diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 68fe9052e6e..d866f1f6d70 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -7,9 +7,9 @@ on: - 'doc/**' env: - QT_VERSION: 6.5.2 + QT_VERSION: 6.6.0 MACOS_DEPLOYMENT_TARGET: 10.15 - CLANG_VERSION: 16.0.2 + CLANG_VERSION: 17.0.1 ELFUTILS_VERSION: 0.175 CMAKE_VERSION: 3.21.1 NINJA_VERSION: 1.10.2 @@ -201,7 +201,7 @@ jobs: set(url_os "linux_x64") set(qt_package_arch_suffix "gcc_64") set(qt_dir_prefix "${qt_version}/gcc_64") - set(qt_package_suffix "-Linux-RHEL_8_4-GCC-Linux-RHEL_8_4-X86_64") + set(qt_package_suffix "-Linux-RHEL_8_6-GCC-Linux-RHEL_8_6-X86_64") elseif ("${{ runner.os }}" STREQUAL "macOS") set(url_os "mac_x64") set(qt_package_arch_suffix "clang_64") @@ -374,7 +374,7 @@ jobs: set(libclang "libclang-release_${clang_version}-based-windows-vs2019_32.7z") endif() elseif ("${{ runner.os }}" STREQUAL "Linux") - set(libclang "libclang-release_${clang_version}-based-linux-Ubuntu20.04-gcc9.3-x86_64.7z") + set(libclang "libclang-release_${clang_version}-based-linux-Ubuntu22.04-gcc11.4-x86_64.7z") elseif ("${{ runner.os }}" STREQUAL "macOS") set(libclang "libclang-release_${clang_version}-based-macos-universal.7z") endif() @@ -663,8 +663,8 @@ jobs: if (NOT result EQUAL 0) string(REGEX MATCH "[0-9]+% tests.*[0-9.]+ sec.*$" test_results "${output}") string(REPLACE "\n" "%0A" test_results "${test_results}") - # Do not fail on ctest failure - message("::warning::${test_results}") + message("::error::${test_results}") + message(FATAL_ERROR "Tests failed") endif() - name: Upload diff --git a/.gitignore b/.gitignore index 6cbeb2503d5..7c56e80373d 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ CMakeLists.txt.user* /share/qtcreator/qmldesigner/QtProject/ /src/app/Info.plist /src/plugins/**/*.json +!/src/plugins/**/wizard.json /src/plugins/coreplugin/ide_version.h /src/libs/qt-breakpad/bin /.cmake/ diff --git a/README.md b/README.md index 79a797fcfaa..37ff3eecabf 100644 --- a/README.md +++ b/README.md @@ -998,3 +998,36 @@ SQLite (https://www.sqlite.org) is in the Public Domain. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +### RSTParser + + RSTParser is an open-source C++ library for parsing reStructuredText + + https://github.com/vitaut-archive/rstparser + + License + ------- + + Copyright (c) 2013, Victor Zverovich + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/cmake/Findyaml-cpp.cmake b/cmake/Findyaml-cpp.cmake index 0b0e81418a2..87fbbcd63eb 100644 --- a/cmake/Findyaml-cpp.cmake +++ b/cmake/Findyaml-cpp.cmake @@ -37,6 +37,7 @@ else() ${YAML_SOURCE_DIR}/include/yaml-cpp ${YAML_SOURCE_DIR}/include/yaml-cpp/anchor.h ${YAML_SOURCE_DIR}/include/yaml-cpp/binary.h + ${YAML_SOURCE_DIR}/include/yaml-cpp/depthguard.h ${YAML_SOURCE_DIR}/include/yaml-cpp/dll.h ${YAML_SOURCE_DIR}/include/yaml-cpp/emitfromevents.h ${YAML_SOURCE_DIR}/include/yaml-cpp/emitter.h @@ -46,10 +47,10 @@ else() ${YAML_SOURCE_DIR}/include/yaml-cpp/eventhandler.h ${YAML_SOURCE_DIR}/include/yaml-cpp/exceptions.h ${YAML_SOURCE_DIR}/include/yaml-cpp/mark.h + ${YAML_SOURCE_DIR}/include/yaml-cpp/noexcept.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node ${YAML_SOURCE_DIR}/include/yaml-cpp/node/convert.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/detail - ${YAML_SOURCE_DIR}/include/yaml-cpp/node/detail/bool_type.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/detail/impl.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/detail/iterator.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/detail/iterator_fwd.h @@ -65,7 +66,6 @@ else() ${YAML_SOURCE_DIR}/include/yaml-cpp/node/parse.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/ptr.h ${YAML_SOURCE_DIR}/include/yaml-cpp/node/type.h - ${YAML_SOURCE_DIR}/include/yaml-cpp/noncopyable.h ${YAML_SOURCE_DIR}/include/yaml-cpp/null.h ${YAML_SOURCE_DIR}/include/yaml-cpp/ostream_wrapper.h ${YAML_SOURCE_DIR}/include/yaml-cpp/parser.h @@ -75,6 +75,7 @@ else() ${YAML_SOURCE_DIR}/src/binary.cpp ${YAML_SOURCE_DIR}/src/collectionstack.h ${YAML_SOURCE_DIR}/src/convert.cpp + ${YAML_SOURCE_DIR}/src/depthguard.cpp ${YAML_SOURCE_DIR}/src/directives.cpp ${YAML_SOURCE_DIR}/src/directives.h ${YAML_SOURCE_DIR}/src/emit.cpp diff --git a/cmake/QtCreatorAPI.cmake b/cmake/QtCreatorAPI.cmake index 60a47b4fc93..5ae0e25bdc3 100644 --- a/cmake/QtCreatorAPI.cmake +++ b/cmake/QtCreatorAPI.cmake @@ -37,13 +37,14 @@ list(APPEND DEFAULT_DEFINES # use CMAKE_CURRENT_FUNCTION_LIST_DIR when we can require CMake 3.17 set(_THIS_MODULE_BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") +qtc_env_with_default("QTC_WITH_CCACHE_SUPPORT" ENV_WITH_CCACHE_SUPPORT OFF) option(BUILD_PLUGINS_BY_DEFAULT "Build plugins by default. This can be used to build all plugins by default, or none." ON) option(BUILD_EXECUTABLES_BY_DEFAULT "Build executables by default. This can be used to build all executables by default, or none." ON) option(BUILD_LIBRARIES_BY_DEFAULT "Build libraries by default. This can be used to build all libraries by default, or none." ON) option(BUILD_TESTS_BY_DEFAULT "Build tests by default. This can be used to build all tests by default, or none." ON) option(QTC_SEPARATE_DEBUG_INFO "Extract debug information from binary files." OFF) option(WITH_SCCACHE_SUPPORT "Enables support for building with SCCACHE and separate debug info with MSVC, which SCCACHE normally doesn't support." OFF) -option(WITH_CCACHE_SUPPORT "Enables support for building with CCACHE and separate debug info with MSVC, which CCACHE normally doesn't support." OFF) +option(WITH_CCACHE_SUPPORT "Enables support for building with CCACHE and separate debug info with MSVC, which CCACHE normally doesn't support." "${ENV_WITH_CCACHE_SUPPORT}") option(QTC_STATIC_BUILD "Builds libraries and plugins as static libraries" OFF) # If we provide a list of plugins, executables, libraries, then the BUILD__BY_DEFAULT will be set to OFF @@ -322,7 +323,7 @@ endfunction(add_qtc_library) function(add_qtc_plugin target_name) cmake_parse_arguments(_arg "SKIP_INSTALL;INTERNAL_ONLY;SKIP_TRANSLATION;EXPORT;SKIP_PCH" - "VERSION;COMPAT_VERSION;PLUGIN_JSON_IN;PLUGIN_PATH;PLUGIN_NAME;OUTPUT_NAME;BUILD_DEFAULT;PLUGIN_CLASS" + "VERSION;COMPAT_VERSION;PLUGIN_PATH;PLUGIN_NAME;OUTPUT_NAME;BUILD_DEFAULT;PLUGIN_CLASS" "CONDITION;DEPENDS;PUBLIC_DEPENDS;DEFINES;PUBLIC_DEFINES;INCLUDES;SYSTEM_INCLUDES;PUBLIC_INCLUDES;PUBLIC_SYSTEM_INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;EXTRA_TRANSLATIONS;PLUGIN_DEPENDS;PLUGIN_RECOMMENDS;PLUGIN_TEST_DEPENDS;PROPERTIES" ${ARGN} ) @@ -420,29 +421,20 @@ function(add_qtc_plugin target_name) ) string(APPEND _arg_DEPENDENCY_STRING "\n ]") - set(IDE_PLUGIN_DEPENDENCY_STRING ${_arg_DEPENDENCY_STRING}) + set(IDE_PLUGIN_DEPENDENCIES ${_arg_DEPENDENCY_STRING}) ### Configure plugin.json file: if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.json.in") list(APPEND _arg_SOURCES ${name}.json.in) file(READ "${name}.json.in" plugin_json_in) - string(REPLACE "\\\"" "\"" plugin_json_in ${plugin_json_in}) - string(REPLACE "\\'" "'" plugin_json_in ${plugin_json_in}) - string(REPLACE "$$QTCREATOR_VERSION" "\${IDE_VERSION}" plugin_json_in ${plugin_json_in}) - string(REPLACE "$$QTCREATOR_COMPAT_VERSION" "\${IDE_VERSION_COMPAT}" plugin_json_in ${plugin_json_in}) - string(REPLACE "$$QTCREATOR_COPYRIGHT_YEAR" "\${IDE_COPYRIGHT_YEAR}" plugin_json_in ${plugin_json_in}) - string(REPLACE "$$QTC_PLUGIN_REVISION" "\${QTC_PLUGIN_REVISION}" plugin_json_in ${plugin_json_in}) - string(REPLACE "$$dependencyList" "\${IDE_PLUGIN_DEPENDENCY_STRING}" plugin_json_in ${plugin_json_in}) - if(_arg_PLUGIN_JSON_IN) - #e.g. UPDATEINFO_EXPERIMENTAL_STR=true - string(REGEX REPLACE "=.*$" "" json_key ${_arg_PLUGIN_JSON_IN}) - string(REGEX REPLACE "^.*=" "" json_value ${_arg_PLUGIN_JSON_IN}) - string(REPLACE "$$${json_key}" "${json_value}" plugin_json_in ${plugin_json_in}) + if(plugin_json_in MATCHES "\\$\\$dependencyList") + message(FATAL_ERROR "Found $$dependencyList in ${name}.json.in. " + "This is no longer supported. " + "Use \${IDE_PLUGIN_DEPENDENCIES}, \${IDE_VERSION} " + "and other CMake variables directly. " + "Also remove escaping of quotes.") endif() - string(CONFIGURE "${plugin_json_in}" plugin_json) - file(GENERATE - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.json" - CONTENT "${plugin_json}") + configure_file(${name}.json.in "${CMAKE_CURRENT_BINARY_DIR}/${name}.json") endif() if (QTC_STATIC_BUILD) @@ -824,7 +816,7 @@ function(extend_qtc_executable name) endfunction() function(add_qtc_test name) - cmake_parse_arguments(_arg "GTEST;MANUALTEST;EXCLUDE_FROM_PRECHECK" "TIMEOUT" + cmake_parse_arguments(_arg "GTEST;MANUALTEST;EXCLUDE_FROM_PRECHECK;NEEDS_GUI" "TIMEOUT" "DEFINES;DEPENDS;INCLUDES;SOURCES;EXPLICIT_MOC;SKIP_AUTOMOC;SKIP_PCH;CONDITION;PROPERTIES" ${ARGN}) if (${_arg_UNPARSED_ARGUMENTS}) @@ -862,7 +854,7 @@ function(add_qtc_test name) endif() endforeach() - set(TEST_DEFINES SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") + set(TEST_DEFINES WITH_TESTS SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") # relax cast requirements for tests get_default_defines(default_defines_copy YES) @@ -891,8 +883,12 @@ function(add_qtc_test name) enable_pch(${name}) endif() + if (_arg_NEEDS_GUI) + set(EXTRA_ARGUMENTS "-platform" "minimal") + endif() + if (NOT _arg_GTEST AND NOT _arg_MANUALTEST) - add_test(NAME ${name} COMMAND ${name}) + add_test(NAME ${name} COMMAND ${name} ${EXTRA_ARGUMENTS}) if (_arg_EXCLUDE_FROM_PRECHECK) set_tests_properties(${name} PROPERTIES LABELS exclude_from_precheck) endif() diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index 27e64a5a954..95fb83207d4 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -21,7 +21,7 @@ include(FeatureSummary) list(APPEND DEFAULT_DEFINES QT_CREATOR QT_NO_JAVA_STYLE_ITERATORS - QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII QT_RESTRICTED_CAST_FROM_ASCII QT_NO_FOREACH QT_DISABLE_DEPRECATED_BEFORE=0x050900 QT_USE_QSTRINGBUILDER ) @@ -30,9 +30,7 @@ if (WIN32) list(APPEND DEFAULT_DEFINES UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS) if (NOT BUILD_WITH_PCH) - # Windows 8 0x0602 list(APPEND DEFAULT_DEFINES - WINVER=0x0602 _WIN32_WINNT=0x0602 WIN32_LEAN_AND_MEAN) endif() endif() @@ -49,7 +47,7 @@ if (APPLE) set(_IDE_LIBRARY_BASE_PATH "Frameworks") set(_IDE_LIBRARY_PATH "${_IDE_OUTPUT_PATH}/${_IDE_LIBRARY_BASE_PATH}") - set(_IDE_PLUGIN_PATH "${_IDE_OUTPUT_PATH}/PlugIns") + set(_IDE_PLUGIN_PATH "${_IDE_OUTPUT_PATH}/PlugIns/qtcreator") set(_IDE_LIBEXEC_PATH "${_IDE_OUTPUT_PATH}/Resources/libexec") set(_IDE_DATA_PATH "${_IDE_OUTPUT_PATH}/Resources") set(_IDE_DOC_PATH "${_IDE_OUTPUT_PATH}/Resources/doc") @@ -554,3 +552,12 @@ function(extend_qtc_target target_name) set_source_files_properties(${_arg_SOURCES} PROPERTIES ${_arg_SOURCES_PROPERTIES}) endif() endfunction() + +function (qtc_env_with_default envName varToSet default) + if(DEFINED ENV{${envName}}) + set(${varToSet} $ENV{${envName}} PARENT_SCOPE) + else() + set(${varToSet} ${default} PARENT_SCOPE) + endif() +endfunction() + diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index 80338e4d8e2..5bb17f228bd 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "11.0.3") # The IDE version. -set(IDE_VERSION_COMPAT "11.0.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "11.0.3") # The IDE display version. +set(IDE_VERSION "11.0.84") # The IDE version. +set(IDE_VERSION_COMPAT "11.0.84") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "12.0.0-rc1") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2023") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. @@ -16,6 +16,6 @@ set(IDE_DOC_FILE_ONLINE "qtcreator/qtcreator-online.qdocconf") # Absolute, or relative to /src/app # Should contain qtcreator.ico, qtcreator.xcassets set(IDE_ICON_PATH "") -# Absolute, or relative to /src/plugins/coreplugin +# Absolute, or relative to /src/app # Should contain images/logo/(16|24|32|48|64|128|256|512)/QtProject-qtcreator.png set(IDE_LOGO_PATH "") diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake index d684b972648..b1be1ccf105 100644 --- a/cmake/Utils.cmake +++ b/cmake/Utils.cmake @@ -32,7 +32,7 @@ function(setup_dependencies_component) set(_ide_app_target \"\${_default_app_target}\") if (NOT EXISTS \"\${_ide_app_target}\") # The component CPack generators (WIX, NSIS64, IFW) install every component with their own CMAKE_INSTALL_PREFIX - # directory and since deployqt.py needs the path to IDE_APP_TARGET the line below is needeed + # directory and since deploy.py needs the path to IDE_APP_TARGET the line below is needeed string(REPLACE \"Dependencies\" \"${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}\" _ide_app_target \"\${_ide_app_target}\") endif() if (NOT EXISTS \"\${_ide_app_target}\") @@ -41,7 +41,8 @@ function(setup_dependencies_component) endif() execute_process(COMMAND \"${Python3_EXECUTABLE}\" - \"${CMAKE_CURRENT_LIST_DIR}/scripts/deployqt.py\" + \"-u\" + \"${CMAKE_CURRENT_LIST_DIR}/scripts/deploy.py\" ${_llvm_arg} ${_elfutils_arg} \"\${_ide_app_target}\" diff --git a/coin/instructions/build.yaml b/coin/instructions/build.yaml index fd96cb5bf6d..1972965b512 100644 --- a/coin/instructions/build.yaml +++ b/coin/instructions/build.yaml @@ -16,7 +16,7 @@ instructions: maxTimeBetweenOutput: 360 userMessageOnFailure: "Failed to extract elfutils package, check logs." - type: ExecuteCommand - command: "curl --fail -L --retry 5 --retry-delay 5 -o {{.AgentWorkingDir}}/build/qt_temp/libclang.7z {{.Env.LLVM_BASE_URL}}-linux-Rhel8.2-gcc9.2-x86_64.7z" + command: "curl --fail -L --retry 5 --retry-delay 5 -o {{.AgentWorkingDir}}/build/qt_temp/libclang.7z {{.Env.LLVM_BASE_URL}}-linux-Rhel8.4-gcc10.3-x86_64.7z" maxTimeInSeconds: 3600 maxTimeBetweenOutput: 360 userMessageOnFailure: "Failed to download LLVM package, check logs." diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index 20d6247ea9c..0f651d187a8 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -7,10 +7,10 @@ instructions: variableValue: "RelWithDebInfo" - type: EnvironmentVariable variableName: LLVM_BASE_URL - variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_16.0.2-based + variableValue: https://ci-files02-hki.ci.qt.io/packages/jenkins/qtcreator_libclang/libclang-release_17.0.1-based - type: EnvironmentVariable variableName: QTC_QT_BASE_URL - variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.5/6.5.2-released/Qt" + variableValue: "https://ci-files02-hki.ci.qt.io/packages/jenkins/archive/qt/6.6/6.6.0-released/Qt" - type: EnvironmentVariable variableName: QTC_QT_MODULES variableValue: "qt5compat qtbase qtdeclarative qtimageformats qtquick3d qtquickcontrols2 qtquicktimeline qtserialport qtshadertools qtsvg qttools qttranslations qtwebengine" @@ -39,7 +39,7 @@ instructions: instructions: - type: EnvironmentVariable variableName: QTC_QT_POSTFIX - variableValue: "-Linux-RHEL_8_4-GCC-Linux-RHEL_8_4-X86_64.7z" + variableValue: "-Linux-RHEL_8_6-GCC-Linux-RHEL_8_6-X86_64.7z" - type: EnvironmentVariable variableName: QTC_SDKTOOL_QT_EXT variableValue: ".tar.xz" diff --git a/dist/changelog/changes-11.0.3.md b/dist/changelog/changes-11.0.3.md new file mode 100644 index 00000000000..b7f435b47f8 --- /dev/null +++ b/dist/changelog/changes-11.0.3.md @@ -0,0 +1,123 @@ +Qt Creator 11.0.3 +================= + +Qt Creator version 11.0.3 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/v11.0.2..v11.0.3 + +Editing +------- + +### C++ + +* Fixed a crash with constructs that look similar to a function with initializer + ([QTCREATORBUG-29386](https://bugreports.qt.io/browse/QTCREATORBUG-29386)) +* Fixed an issue with Clang headers + ([QTCREATORBUG-29571](https://bugreports.qt.io/browse/QTCREATORBUG-29571)) +* Fixed missing lightbulbs for Clangd refactoring actions + ([QTCREATORBUG-29493](https://bugreports.qt.io/browse/QTCREATORBUG-29493)) + +### QML + +* Fixed wrong M16 warnings + ([QTCREATORBUG-28468](https://bugreports.qt.io/browse/QTCREATORBUG-28468)) + +### Language Server Protocol + +* Fixed the loading of client settings + ([QTCREATORBUG-29477](https://bugreports.qt.io/browse/QTCREATORBUG-29477)) + +Projects +-------- + +* Fixed that issue descriptions were cut off + ([QTCREATORBUG-29458](https://bugreports.qt.io/browse/QTCREATORBUG-29458)) +* Fixed an issue when running in terminal + ([QTCREATORBUG-29503](https://bugreports.qt.io/browse/QTCREATORBUG-29503)) + +### CMake + +* Fixed a crash when loading a project + ([QTCREATORBUG-29587](https://bugreports.qt.io/browse/QTCREATORBUG-29587)) +* Fixed that `Stage for installation` was enabled by default for iOS Simulator + and Bare Metal configurations + ([QTCREATORBUG-29293](https://bugreports.qt.io/browse/QTCREATORBUG-29293), + [QTCREATORBUG-29475](https://bugreports.qt.io/browse/QTCREATORBUG-29475)) +* Fixed adding and removing files from modified CMake files + ([QTCREATORBUG-29550](https://bugreports.qt.io/browse/QTCREATORBUG-29550)) + +### qmake + +* Fixed the ABI setting in the qmake build step + ([QTCREATORBUG-29552](https://bugreports.qt.io/browse/QTCREATORBUG-29552)) + +### Python + +* Fixed that `.pyw` files were missing from the target information + +Debugging +--------- + +* Fixed the debugging in terminal + ([QTCREATORBUG-29463](https://bugreports.qt.io/browse/QTCREATORBUG-29463), + [QTCREATORBUG-29497](https://bugreports.qt.io/browse/QTCREATORBUG-29497), + [QTCREATORBUG-29554](https://bugreports.qt.io/browse/QTCREATORBUG-29554)) +* Improved the pretty printer of `std::string` for `libc++` + ([QTCREATORBUG-29526](https://bugreports.qt.io/browse/QTCREATORBUG-29526)) + +Analyzer +-------- + +### CTF Visualizer + +* Fixed a crash when loading invalid JSON + +Terminal +-------- + +* Fixed the default environment variables + ([QTCREATORBUG-29515](https://bugreports.qt.io/browse/QTCREATORBUG-29515)) +* Fixed `gnome-terminal` and `xdg-terminal` for the external terminal + ([QTCREATORBUG-29488](https://bugreports.qt.io/browse/QTCREATORBUG-29488)) + +Test Integration +---------------- + +### CTest + +* Fixed the update of target information after a change in the kit + ([QTCREATORBUG-29477](https://bugreports.qt.io/browse/QTCREATORBUG-29477)) + +Platforms +--------- + +### Remote Linux + +* Fixed that SFTP was used (and failed) for deployment when the source is remote + ([QTCREATORBUG-29524](https://bugreports.qt.io/browse/QTCREATORBUG-29524)) +* Fixed deployment to the device root directory + ([QTCREATORBUG-29597](https://bugreports.qt.io/browse/QTCREATORBUG-29597)) + +### Docker + +* Fixed the registration of sysroots by the installer + ([QTCREATORBUG-29523](https://bugreports.qt.io/browse/QTCREATORBUG-29523)) + +Credits for these changes go to: +-------------------------------- +Alessandro Portale +Alexandre Laurent +André Pönitz +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Marcus Tillmanns +Miikka Heikkinen +Semih Yavuz diff --git a/dist/changelog/changes-12.0.0.md b/dist/changelog/changes-12.0.0.md new file mode 100644 index 00000000000..6e96ada76bb --- /dev/null +++ b/dist/changelog/changes-12.0.0.md @@ -0,0 +1,395 @@ +Qt Creator 12 +============= + +Qt Creator version 12 contains bug fixes and new features. + +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/11.0..v12.0.0 + +What's new? +----------- + +* Integrated [Compiler Explorer (https://godbolt.org)](https://godbolt.org) +* CMake debugging and the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/) +* Screen recording + +### Compiler Explorer + +Use [Compiler Explorer (https://godbolt.org)](https://godbolt.org) in Qt Creator +and enter example code to explore the capabilities of your compilers and +interpreters. + +To enable the CompilerExplorer plugin, select +`Help > About Plugins > Utilities > CompilerExplorer`. Then select +`Restart Now` to restart Qt Creator and load the plugin. + +Select `File > New File` and select one of the new `Compiler Explorer` templates +to get started. + +Alternatively, you can open a new `Compiler Explorer` editor via +`Tools > Compiler Explorer > Open Compiler Explorer`. + +Every language, compiler, and library that is supported at +is also supported in Qt Creator. You can save your Compiler Explorer session as +a `.qtce` file (JSON-based). + +([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-explore-compiler-code.html)) + +### CMake Debugging and the Debug Adapter Protocol + +Set breakpoints in a CMake file and select +`Debug > Start Debugging > Start CMake Debugging` to start debugging. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-debug-cmake-files.html)) + + +### Screen Recording + +Use `Tools > Record Screen` to record a part of your screen. Requires an +installation of [FFmpeg](https://ffmpeg.org). + +To enable the ScreenRecorder plugin, select +`Help > About Plugins > Utilities > ScreenRecorder`. Then select +`Restart Now` to restart Qt Creator and load the plugin. + +([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-record-screens.html)) + +General +------- + +* Improved the startup performance significantly on various setups +* Added the `Sort results` check box for configuring the `md` locator filter in + `Edit > Preferences > Environment > Locator` to keep the sorting from the tool + used for the file system index locator filter + ([QTCREATORBUG-27789](https://bugreports.qt.io/browse/QTCREATORBUG-27789)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-editor-locator.html#locating-files-from-global-file-system-index)) +* Added the `View > Show Menu Bar` option to hide the menu bar on platforms + without a unified menu bar + ([QTCREATORBUG-29498](https://bugreports.qt.io/browse/QTCREATORBUG-29498)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-show-and-hide-main-menu.html)) +* Changed the `Enable high DPI scaling` setting to a `DPI rounding policy` + setting, which fits Qt's settings better + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-set-high-dpi-scaling.html)) +* Fixed an issue with growing session files +* Fixed that the shortcuts for the navigation views could be stuck to opening a + view in the right side bar + ([QTCREATORBUG-29770](https://bugreports.qt.io/browse/QTCREATORBUG-29770)) +* Fixed that the shortcut for Locator switched to the main window + ([QTCREATORBUG-29741](https://bugreports.qt.io/browse/QTCREATORBUG-29741)) + +Help +---- + +* Added the `Edit > Preferences > Help > General > Antialias` check box for + setting the anti-aliasing of text + ([QTCREATORBUG-12177](https://bugreports.qt.io/browse/QTCREATORBUG-12177)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-get-help.html#change-the-font)) + +Editing +------- + +* Added the count of selected characters to line and column information + on the `Edit` mode toolbar + ([QTCREATORBUG-29381](https://bugreports.qt.io/browse/QTCREATORBUG-29381)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-coding-navigating.html#navigating-between-open-files-and-symbols)) +* Added an indenter, auto-brace and auto-quote for JSON + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-enclose-code-in-characters.html)) +* Improved the performance of searching in big documents +* Fixed that the historical order of open documents was not restored +* Fixed that suggestions were rendered with the wrong tab size + ([QTCREATORBUG-29483](https://bugreports.qt.io/browse/QTCREATORBUG-29483)) + +### C++ + +* Updated to LLVM 17.0.1 +* Added `Tools > C++ > Fold All Comment Blocks` and `Unfold All Comment Blocks` + ([QTCREATORBUG-2449](https://bugreports.qt.io/browse/QTCREATORBUG-2449)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-highlighting.html#folding-blocks)) +* Added the `Convert Comment to C Style` and `Convert Comment to C++ Style` + refactoring actions for converting comments between C++-style and + C-style + ([QTCREATORBUG-27501](https://bugreports.qt.io/browse/QTCREATORBUG-27501)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-editor-quick-fixes.html#refactoring-c-code)) +* Added the `Move Function Documentation to Declaration` and + `Move Function Documentation to Definition` refactoring actions for moving + documentation between function declaration and definition + ([QTCREATORBUG-13877](https://bugreports.qt.io/browse/QTCREATORBUG-13877)) +* Extended the application of renaming operations to documentation comments + ([QTCREATORBUG-12051](https://bugreports.qt.io/browse/QTCREATORBUG-12051), + [QTCREATORBUG-15425](https://bugreports.qt.io/browse/QTCREATORBUG-15425)) +* Fixed that code inserted by refactoring actions was not formatted according + to the Clang Format settings + ([QTCREATORBUG-10807](https://bugreports.qt.io/browse/QTCREATORBUG-10807), + [QTCREATORBUG-19158](https://bugreports.qt.io/browse/QTCREATORBUG-19158)) +* Fixed that automatically created functions could be added between another + function and its documentation + ([QTCREATORBUG-6934](https://bugreports.qt.io/browse/QTCREATORBUG-6934)) +* Fixed that scope names were considered when searching for `C++ Symbols` with + advanced find + ([QTCREATORBUG-29133](https://bugreports.qt.io/browse/QTCREATORBUG-29133)) +* Clangd + * Added the `Completion ranking model` option for choosing the order of + completion results + ([QTCREATORBUG-29013](https://bugreports.qt.io/browse/QTCREATORBUG-29013)) + * Fixed that the refactoring actions from Clangd were not available in the + context menu + * Fixed that renaming symbols could rename them in generated files + ([QTCREATORBUG-29778](https://bugreports.qt.io/browse/QTCREATORBUG-29778)) +* Clang Format + * Fixed the style settings for Clang Format 16 and later + ([QTCREATORBUG-29434](https://bugreports.qt.io/browse/QTCREATORBUG-29434)) + +### QML + +* Fixed invalid `M325` warnings + ([QTCREATORBUG-29601](https://bugreports.qt.io/browse/QTCREATORBUG-29601)) +* Language Server + * Fixed the shortcut for applying refactoring actions + ([QTCREATORBUG-29557](https://bugreports.qt.io/browse/QTCREATORBUG-29557)) + +### Python + +* Fixed duplicate code when renaming + ([QTCREATORBUG-29389](https://bugreports.qt.io/browse/QTCREATORBUG-29389)) + +### Language Server Protocol + +* Added support for Language servers that request creating, renaming, or deleting + of files + ([QTCREATORBUG-29542](https://bugreports.qt.io/browse/QTCREATORBUG-29542)) + +### Widget Designer + +* Fixed that renaming layouts in the property editor switched to edit mode + ([QTCREATORBUG-29644](https://bugreports.qt.io/browse/QTCREATORBUG-29644)) + +### Copilot + +* Added support for proxies + ([QTCREATORBUG-29485](https://bugreports.qt.io/browse/QTCREATORBUG-29485)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-copilot.html)) +* Fixed the auto-detection of `agent.js` + ([QTCREATORBUG-29750](https://bugreports.qt.io/browse/QTCREATORBUG-29750)) + +### TODO + +* Added the `\todo` keyword to the default + +### Markdown + +* Added buttons and configurable shortcuts for text styles + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-markdown-editor.html)) + +### Images + +* Fixed that animations could not be restarted + ([QTCREATORBUG-29606](https://bugreports.qt.io/browse/QTCREATORBUG-29606)) +* Fixed that looping animations did not loop + ([QTCREATORBUG-29606](https://bugreports.qt.io/browse/QTCREATORBUG-29606)) + +Projects +-------- + +* Project specific settings + * Added C++ file naming settings + ([QTCREATORBUG-22033](https://bugreports.qt.io/browse/QTCREATORBUG-22033)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-set-cpp-file-naming.html)) + * Added documentation comment settings + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-how-to-document-code.html)) +* Added an option for the Doxygen command prefix + ([QTCREATORBUG-8096](https://bugreports.qt.io/browse/QTCREATORBUG-8096)) +* Improved performance of filtering the target setup page + ([QTCREATORBUG-29494](https://bugreports.qt.io/browse/QTCREATORBUG-29494)) +* Fixed that run configurations were removed when the corresponding target + vanishes (even temporarily) + ([QTCREATORBUG-23163](https://bugreports.qt.io/browse/QTCREATORBUG-23163), + [QTCREATORBUG-28273](https://bugreports.qt.io/browse/QTCREATORBUG-28273)) +* Fixed issues with recursive symbolic links + ([QTCREATORBUG-29663](https://bugreports.qt.io/browse/QTCREATORBUG-29663)) + +### CMake + +* Removed support for extra generators +* Added `Follow Symbol Under Cursor` for functions, macros, targets and packages + ([QTCREATORBUG-25523](https://bugreports.qt.io/browse/QTCREATORBUG-25523), + [QTCREATORBUG-25524](https://bugreports.qt.io/browse/QTCREATORBUG-25524)) +* Added support for `CMAKE_SOURCE_DIR` and similar variables for + `Jump to File Under Cursor` + ([QTCREATORBUG-29467](https://bugreports.qt.io/browse/QTCREATORBUG-29467)) +* Added code completion for various aspects of CMake (local functions and + variables, cache variables, `ENV`, targets, packages, variables added by + `find_package`) +* Added support for `CMAKE_UNITY_BUILD` + ([QTCREATORBUG-23635](https://bugreports.qt.io/browse/QTCREATORBUG-23635), + [QTCREATORBUG-26822](https://bugreports.qt.io/browse/QTCREATORBUG-26822), + [QTCREATORBUG-29080](https://bugreports.qt.io/browse/QTCREATORBUG-29080)) +* Added support for `cmake-format` configuration files + ([QTCREATORBUG-28969](https://bugreports.qt.io/browse/QTCREATORBUG-28969)) +* Added help tooltips + ([QTCREATORBUG-25780](https://bugreports.qt.io/browse/QTCREATORBUG-25780)) +* Extended context help for variables, properties and modules +* Improved performance when switching sessions + ([QTCREATORBUG-27729](https://bugreports.qt.io/browse/QTCREATORBUG-27729)) +* Fixed issues with the subdirectory structure of the project tree + ([QTCREATORBUG-23942](https://bugreports.qt.io/browse/QTCREATORBUG-23942), + [QTCREATORBUG-29105](https://bugreports.qt.io/browse/QTCREATORBUG-29105)) +* Presets + * Fixed that variables were not expanded for `cmakeExecutable` + ([QTCREATORBUG-29643](https://bugreports.qt.io/browse/QTCREATORBUG-29643)) + * Fixed unnecessary restrictions on the preset name + +([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-project-cmake.html)) + +### Python + +* Added auto-detection of PySide from the installer + ([PYSIDE-2153](https://bugreports.qt.io/browse/PYSIDE-2153)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-python-development.html#set-up-pyside6)) +* Added the option to forward the display for remote Linux + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-run-settings.html#specifying-run-settings-for-linux-based-devices)) +* Fixed PySide wheels installation on macOS + +### qmake + +* Fixed the project tree structure in case of some subfolder structures + ([QTCREATORBUG-29733](https://bugreports.qt.io/browse/QTCREATORBUG-29733)) + +### vcpkg + +* Added the generation of code for `CMakeLists.txt` +* Added parsing the dependencies from `vcpkg.json` manifest files +* Improved the addition of dependencies to `vcpkg.json` + +### Qt Safe Renderer + +* Added a wizard for Qt Safe Renderer 2.1 and later + ([Documentation](https://doc.qt.io/QtSafeRenderer/qtsr-safety-project.html#using-qt-safe-renderer-project-template-in-qt-creator)) + +Debugging +--------- + +### C++ + +* Added support for remote Linux debugging with LLDB +* Fixed warnings about index cache permissions + ([QTCREATORBUG-29556](https://bugreports.qt.io/browse/QTCREATORBUG-29556)) + +Analyzer +-------- + +### Clang + +* Fixed that error messages were not shown + ([QTCREATORBUG-29298](https://bugreports.qt.io/browse/QTCREATORBUG-29298)) +* Fixed that `-mno-direct-extern-access` could be passed to `clang-tidy` which + does not support it + +### CTF Visualizer + +* Fixed that process and thread IDs could not be strings +* Fixed the computation of nesting levels +* Fixed a crash when zooming with a touch pad + +Terminal +-------- + +* Added mouse support +* Added support for Windows Terminal schemes +* Fixed `Ctrl+C/V` on Windows + +([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-reference-terminal-view.html)) + +Version Control Systems +----------------------- + +### Git + +* Added the `Ignore whitespace changes` and `Ignore line moves` options to + `Preferences > Version Control > Git > Instant Blame` + ([QTCREATORBUG-29378](https://bugreports.qt.io/browse/QTCREATORBUG-29378)) + ([Documentation](https://doc-snapshots.qt.io/qtcreator-12.0/creator-vcs-git.html)) + +### CVS + +* Disabled by default + +Test Integration +---------------- + +* Added an option for the number of threads used for scanning + ([QTCREATORBUG-29301](https://bugreports.qt.io/browse/QTCREATORBUG-29301)) +* Improved the wizards for `GTest` and `Catch2` + +Platforms +--------- + +### Android + +* Fixed issues when `LIBRARY_OUTPUT_DIRECTORY` is set in the CMake build files + ([QTCREATORBUG-26479](https://bugreports.qt.io/browse/QTCREATORBUG-26479)) + +### iOS + +* Known Issue: iOS 17 devices are not supported + +### Docker + +* Fixed the check for commands that are built-ins of the shell + +Credits for these changes go to: +-------------------------------- +Aleksei German +Alessandro Portale +Ali Kianian +Amr Essam +Andre Hartmann +André Pönitz +Andreas Loth +Artem Sokolovskii +Brook Cronin +Burak Hancerli +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Dominic Ernst +Eike Ziller +Esa Törmänen +Friedemann Kleint +Henning Gruendl +Jaroslaw Kobus +Johanna Vanhatapio +Johnny Jazeix +Jonas Karlsson +Jussi Witick +Knud Dollereder +Leena Miettinen +Ludovic Le Brun +Mahmoud Badri +Marco Bubke +Marcus Tillmanns +Mats Honkamaa +Mehdi Salem +Miikka Heikkinen +Olivier De Cannière +Olivier Delaune +Orgad Shaneh +Pranta Dastider +Robert Löhning +Sami Shalayel +Samuel Ghinet +Samuli Piippo +Semih Yavuz +Tasuku Suzuki +Thiago Macieira +Thomas Hartmann +Tim Jenssen +Tim Jenßen +Tor Arne Vestbø +Vikas Pachdha +Xavier Besson +Yasser Grimes diff --git a/dist/installer/mac/README.md b/dist/installer/mac/README.md new file mode 100644 index 00000000000..65bfa95ea2f --- /dev/null +++ b/dist/installer/mac/README.md @@ -0,0 +1,8 @@ +This folder contains various files used to build the Mac installer. + +# Entitlements + +The entitlements here will be picked up by [scripts/build.py](../../../scripts/build.py) +via [scripts/common.py](../../../scripts/common.py) `codesign_executable()` based on their name. +If you need a new application to be signed with specific entitlements, you can simply add a file +called `your-app-name.entitlements` to this folder. diff --git a/dist/installer/mac/ios_qt.conf b/dist/installer/mac/ios_qt.conf deleted file mode 100644 index 249df1d74cc..00000000000 --- a/dist/installer/mac/ios_qt.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Paths] -Plugins = ../../../PlugIns diff --git a/dist/installer/mac/libexec_qt.conf b/dist/installer/mac/libexec_qt.conf deleted file mode 100644 index 7004d4bec38..00000000000 --- a/dist/installer/mac/libexec_qt.conf +++ /dev/null @@ -1,5 +0,0 @@ -[Paths] -Prefix = ../.. -Imports = Imports/qtquick1 -Qml2Imports = Imports/qtquick2 -Plugins = PlugIns diff --git a/dist/installer/mac/qt.conf b/dist/installer/mac/qt.conf deleted file mode 100644 index 6111f0628ef..00000000000 --- a/dist/installer/mac/qt.conf +++ /dev/null @@ -1,4 +0,0 @@ -[Paths] -Binaries = MacOS -Qml2Imports = Imports/qtquick2 -Plugins = PlugIns diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index f806b1dff18..f881062d136 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -39,6 +39,7 @@ macro.QOI = "Qt Online Installer" macro.QMT = "Qt Maintenance Tool" macro.qtcversion = $QTC_VERSION macro.param = "\\e" +macro.preferences = "\\l{Find preferences}{Preferences}" macro.raisedaster.HTML = "*" macro.rarrow.HTML = "→" macro.reg.HTML = "®" diff --git a/doc/qtcreator/config/qtcreator-project.qdocconf b/doc/qtcreator/config/qtcreator-project.qdocconf index ec98529c141..d36bba7595c 100644 --- a/doc/qtcreator/config/qtcreator-project.qdocconf +++ b/doc/qtcreator/config/qtcreator-project.qdocconf @@ -34,6 +34,7 @@ depends += qtwidgets \ qthelp \ qtquicktimeline \ qtlinguist \ + qtpositioning \ qtscxml \ qtsensors \ qttestlib \ diff --git a/doc/qtcreator/config/style/qt5-sidebar.html b/doc/qtcreator/config/style/qt5-sidebar.html index 9f272e3a7a8..7ba89695cb1 100644 --- a/doc/qtcreator/config/style/qt5-sidebar.html +++ b/doc/qtcreator/config/style/qt5-sidebar.html @@ -81,31 +81,34 @@ diff --git a/doc/qtcreator/images/icons/camera.png b/doc/qtcreator/images/icons/camera.png new file mode 100644 index 00000000000..34e29256ba1 Binary files /dev/null and b/doc/qtcreator/images/icons/camera.png differ diff --git a/doc/qtcreator/images/icons/detach-view.png b/doc/qtcreator/images/icons/detach-view.png new file mode 100644 index 00000000000..1cc3d8df400 Binary files /dev/null and b/doc/qtcreator/images/icons/detach-view.png differ diff --git a/doc/qtcreator/images/icons/shortcuts.png b/doc/qtcreator/images/icons/shortcuts.png new file mode 100644 index 00000000000..e059023486e Binary files /dev/null and b/doc/qtcreator/images/icons/shortcuts.png differ diff --git a/doc/qtcreator/images/qtcreator-add-library-external.webp b/doc/qtcreator/images/qtcreator-add-library-external.webp new file mode 100644 index 00000000000..faec0561790 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-add-library-external.webp differ diff --git a/doc/qtcreator/images/qtcreator-add-library-internal-project-details.webp b/doc/qtcreator/images/qtcreator-add-library-internal-project-details.webp new file mode 100644 index 00000000000..a53e07fc6cd Binary files /dev/null and b/doc/qtcreator/images/qtcreator-add-library-internal-project-details.webp differ diff --git a/doc/qtcreator/images/qtcreator-add-library-internal-project-location.webp b/doc/qtcreator/images/qtcreator-add-library-internal-project-location.webp new file mode 100644 index 00000000000..1df0227675d Binary files /dev/null and b/doc/qtcreator/images/qtcreator-add-library-internal-project-location.webp differ diff --git a/doc/qtcreator/images/qtcreator-add-library-internal.webp b/doc/qtcreator/images/qtcreator-add-library-internal.webp new file mode 100644 index 00000000000..65950290350 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-add-library-internal.webp differ diff --git a/doc/qtcreator/images/qtcreator-attach-views.webp b/doc/qtcreator/images/qtcreator-attach-views.webp new file mode 100644 index 00000000000..9d978508850 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-attach-views.webp differ diff --git a/doc/qtcreator/images/qtcreator-build-settings-default.webp b/doc/qtcreator/images/qtcreator-build-settings-default.webp new file mode 100644 index 00000000000..32d59fc9d23 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-build-settings-default.webp differ diff --git a/doc/qtcreator/images/qtcreator-cmake-build-steps.png b/doc/qtcreator/images/qtcreator-cmake-build-steps.png deleted file mode 100644 index 9ecce473c5b..00000000000 Binary files a/doc/qtcreator/images/qtcreator-cmake-build-steps.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-cmake-build-steps.webp b/doc/qtcreator/images/qtcreator-cmake-build-steps.webp new file mode 100644 index 00000000000..d90c97cb81f Binary files /dev/null and b/doc/qtcreator/images/qtcreator-cmake-build-steps.webp differ diff --git a/doc/qtcreator/images/qtcreator-cmake-clean-steps.png b/doc/qtcreator/images/qtcreator-cmake-clean-steps.png deleted file mode 100644 index 9e1b9d51232..00000000000 Binary files a/doc/qtcreator/images/qtcreator-cmake-clean-steps.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-cmake-clean-steps.webp b/doc/qtcreator/images/qtcreator-cmake-clean-steps.webp new file mode 100644 index 00000000000..ea074e56718 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-cmake-clean-steps.webp differ diff --git a/doc/qtcreator/images/qtcreator-cmake-generator.png b/doc/qtcreator/images/qtcreator-cmake-generator.png deleted file mode 100644 index 19c883bcb5b..00000000000 Binary files a/doc/qtcreator/images/qtcreator-cmake-generator.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-cmake-generator.webp b/doc/qtcreator/images/qtcreator-cmake-generator.webp new file mode 100644 index 00000000000..1984984499c Binary files /dev/null and b/doc/qtcreator/images/qtcreator-cmake-generator.webp differ diff --git a/doc/qtcreator/images/qtcreator-compiler-explorer-options.webp b/doc/qtcreator/images/qtcreator-compiler-explorer-options.webp new file mode 100644 index 00000000000..d0fc9098850 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-compiler-explorer-options.webp differ diff --git a/doc/qtcreator/images/qtcreator-compiler-explorer.webp b/doc/qtcreator/images/qtcreator-compiler-explorer.webp new file mode 100644 index 00000000000..3fb945df38b Binary files /dev/null and b/doc/qtcreator/images/qtcreator-compiler-explorer.webp differ diff --git a/doc/qtcreator/images/qtcreator-crop-and-trim.webp b/doc/qtcreator/images/qtcreator-crop-and-trim.webp new file mode 100644 index 00000000000..d851b7a408a Binary files /dev/null and b/doc/qtcreator/images/qtcreator-crop-and-trim.webp differ diff --git a/doc/qtcreator/images/qtcreator-debugger-cmake.webp b/doc/qtcreator/images/qtcreator-debugger-cmake.webp new file mode 100644 index 00000000000..5200f2a060b Binary files /dev/null and b/doc/qtcreator/images/qtcreator-debugger-cmake.webp differ diff --git a/doc/qtcreator/images/qtcreator-editor-line-column.webp b/doc/qtcreator/images/qtcreator-editor-line-column.webp new file mode 100644 index 00000000000..e46f50d94d9 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-editor-line-column.webp differ diff --git a/doc/qtcreator/images/qtcreator-examples-kit-selector.webp b/doc/qtcreator/images/qtcreator-examples-kit-selector.webp new file mode 100644 index 00000000000..0f712f9e29f Binary files /dev/null and b/doc/qtcreator/images/qtcreator-examples-kit-selector.webp differ diff --git a/doc/qtcreator/images/qtcreator-examples-open.webp b/doc/qtcreator/images/qtcreator-examples-open.webp new file mode 100644 index 00000000000..81a148ccab8 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-examples-open.webp differ diff --git a/doc/qtcreator/images/qtcreator-gs-build-example-kit-selector.png b/doc/qtcreator/images/qtcreator-gs-build-example-kit-selector.png deleted file mode 100644 index 77c83b0c3e6..00000000000 Binary files a/doc/qtcreator/images/qtcreator-gs-build-example-kit-selector.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-gs-build-example-open.png b/doc/qtcreator/images/qtcreator-gs-build-example-open.png deleted file mode 100644 index 89e377b6459..00000000000 Binary files a/doc/qtcreator/images/qtcreator-gs-build-example-open.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-help-options.png b/doc/qtcreator/images/qtcreator-help-options.png deleted file mode 100644 index b0916ab7b19..00000000000 Binary files a/doc/qtcreator/images/qtcreator-help-options.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-issues.webp b/doc/qtcreator/images/qtcreator-issues.webp index 503e23a5113..d55a32c5ffd 100644 Binary files a/doc/qtcreator/images/qtcreator-issues.webp and b/doc/qtcreator/images/qtcreator-issues.webp differ diff --git a/doc/qtcreator/images/qtcreator-locator-filter-edit-ai.webp b/doc/qtcreator/images/qtcreator-locator-filter-edit-ai.webp new file mode 100644 index 00000000000..ed8f7088046 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-locator-filter-edit-ai.webp differ diff --git a/doc/qtcreator/images/qtcreator-locator-filter-edit-md.webp b/doc/qtcreator/images/qtcreator-locator-filter-edit-md.webp new file mode 100644 index 00000000000..4526245b68e Binary files /dev/null and b/doc/qtcreator/images/qtcreator-locator-filter-edit-md.webp differ diff --git a/doc/qtcreator/images/qtcreator-locator-filter-t.webp b/doc/qtcreator/images/qtcreator-locator-filter-t.webp new file mode 100644 index 00000000000..d201f0672e1 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-locator-filter-t.webp differ diff --git a/doc/qtcreator/images/qtcreator-markdown-editor.webp b/doc/qtcreator/images/qtcreator-markdown-editor.webp index 0943d90fb3b..37debd47c7c 100644 Binary files a/doc/qtcreator/images/qtcreator-markdown-editor.webp and b/doc/qtcreator/images/qtcreator-markdown-editor.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-file.webp b/doc/qtcreator/images/qtcreator-new-file.webp index 4429f37cd3c..9bea2de2f9c 100644 Binary files a/doc/qtcreator/images/qtcreator-new-file.webp and b/doc/qtcreator/images/qtcreator-new-file.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp new file mode 100644 index 00000000000..1dd0962f4f3 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-location.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-location.webp new file mode 100644 index 00000000000..006338957fb Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-location.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-ready.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-ready.webp new file mode 100644 index 00000000000..15f42356867 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-qt-quick-empty-project-ready.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-define-class.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-define-class.webp new file mode 100644 index 00000000000..681df512fbe Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-define-class.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp new file mode 100644 index 00000000000..3890e8e75ba Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-details.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-location.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-location.webp new file mode 100644 index 00000000000..7e0a84b9382 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-project-location.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-ready.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-ready.webp new file mode 100644 index 00000000000..38fdac87502 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-widgets-ready.webp differ diff --git a/doc/qtcreator/images/qtcreator-new-qt-for-python-app-window-ui-uic.webp b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-window-ui-uic.webp new file mode 100644 index 00000000000..2e6368d9141 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-new-qt-for-python-app-window-ui-uic.webp differ diff --git a/doc/qtcreator/images/qtcreator-options-clangd.png b/doc/qtcreator/images/qtcreator-options-clangd.png deleted file mode 100644 index a4251e7be8f..00000000000 Binary files a/doc/qtcreator/images/qtcreator-options-clangd.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-options-texteditor-completion.png b/doc/qtcreator/images/qtcreator-options-texteditor-completion.png deleted file mode 100644 index a891ed6365c..00000000000 Binary files a/doc/qtcreator/images/qtcreator-options-texteditor-completion.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-output-terminal.webp b/doc/qtcreator/images/qtcreator-output-terminal.webp index ac3f444cef2..83668815694 100644 Binary files a/doc/qtcreator/images/qtcreator-output-terminal.webp and b/doc/qtcreator/images/qtcreator-output-terminal.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-clangd.webp b/doc/qtcreator/images/qtcreator-preferences-clangd.webp new file mode 100644 index 00000000000..6d363c02f93 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-clangd.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-cmake-formatter.webp b/doc/qtcreator/images/qtcreator-preferences-cmake-formatter.webp index a61b2d72701..03ff71ace4f 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-cmake-formatter.webp and b/doc/qtcreator/images/qtcreator-preferences-cmake-formatter.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-copilot.webp b/doc/qtcreator/images/qtcreator-preferences-copilot.webp index b98f74e43df..48df99f87b1 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-copilot.webp and b/doc/qtcreator/images/qtcreator-preferences-copilot.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-documentation-comments.webp b/doc/qtcreator/images/qtcreator-preferences-documentation-comments.webp new file mode 100644 index 00000000000..0e65dde2aeb Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-documentation-comments.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-environment-interface.webp b/doc/qtcreator/images/qtcreator-preferences-environment-interface.webp index d6d5098bc63..a03cb3d5794 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-environment-interface.webp and b/doc/qtcreator/images/qtcreator-preferences-environment-interface.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-help-documentation.webp b/doc/qtcreator/images/qtcreator-preferences-help-documentation.webp new file mode 100644 index 00000000000..8e33837f4ef Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-help-documentation.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-help-general.webp b/doc/qtcreator/images/qtcreator-preferences-help-general.webp new file mode 100644 index 00000000000..b13a22430d9 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-help-general.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-screen-recording.webp b/doc/qtcreator/images/qtcreator-preferences-screen-recording.webp new file mode 100644 index 00000000000..b95ecb70a02 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-screen-recording.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-sort.webp b/doc/qtcreator/images/qtcreator-preferences-sort.webp new file mode 100644 index 00000000000..5691024ada2 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-sort.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-terminal.webp b/doc/qtcreator/images/qtcreator-preferences-terminal.webp index 5772f8ee94f..d3165d15874 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-terminal.webp and b/doc/qtcreator/images/qtcreator-preferences-terminal.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-testing-general.webp b/doc/qtcreator/images/qtcreator-preferences-testing-general.webp index 0b92925e6a1..8fb41b64f7d 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-testing-general.webp and b/doc/qtcreator/images/qtcreator-preferences-testing-general.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-texteditor-completion.webp b/doc/qtcreator/images/qtcreator-preferences-texteditor-completion.webp new file mode 100644 index 00000000000..e2b68ec7852 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-preferences-texteditor-completion.webp differ diff --git a/doc/qtcreator/images/qtcreator-preferences-vcs-git.webp b/doc/qtcreator/images/qtcreator-preferences-vcs-git.webp index 1d76dc557bc..bb450df15c9 100644 Binary files a/doc/qtcreator/images/qtcreator-preferences-vcs-git.webp and b/doc/qtcreator/images/qtcreator-preferences-vcs-git.webp differ diff --git a/doc/qtcreator/images/qtcreator-projectpane.webp b/doc/qtcreator/images/qtcreator-projectpane.webp new file mode 100644 index 00000000000..a8391a678d2 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-projectpane.webp differ diff --git a/doc/qtcreator/images/qtcreator-projects-kits.webp b/doc/qtcreator/images/qtcreator-projects-kits.webp new file mode 100644 index 00000000000..6f863df9a50 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-projects-kits.webp differ diff --git a/doc/qtcreator/images/qtcreator-projects-settings-cpp-file-naming.webp b/doc/qtcreator/images/qtcreator-projects-settings-cpp-file-naming.webp new file mode 100644 index 00000000000..9e222cc6e91 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-projects-settings-cpp-file-naming.webp differ diff --git a/doc/qtcreator/images/qtcreator-python-interpreters.png b/doc/qtcreator/images/qtcreator-python-interpreters.png deleted file mode 100644 index 546adb1b2d3..00000000000 Binary files a/doc/qtcreator/images/qtcreator-python-interpreters.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-python-interpreters.webp b/doc/qtcreator/images/qtcreator-python-interpreters.webp new file mode 100644 index 00000000000..901b3427bfe Binary files /dev/null and b/doc/qtcreator/images/qtcreator-python-interpreters.webp differ diff --git a/doc/qtcreator/images/qtcreator-python-wizard-define-python-interpreter.webp b/doc/qtcreator/images/qtcreator-python-wizard-define-python-interpreter.webp index 8621040267a..1a2559c4bc3 100644 Binary files a/doc/qtcreator/images/qtcreator-python-wizard-define-python-interpreter.webp and b/doc/qtcreator/images/qtcreator-python-wizard-define-python-interpreter.webp differ diff --git a/doc/qtcreator/images/qtcreator-qml-js-editing.webp b/doc/qtcreator/images/qtcreator-qml-js-editing.webp index 7e759cf2da6..c0b56264dd1 100644 Binary files a/doc/qtcreator/images/qtcreator-qml-js-editing.webp and b/doc/qtcreator/images/qtcreator-qml-js-editing.webp differ diff --git a/doc/qtcreator/images/qtcreator-record-screen.webp b/doc/qtcreator/images/qtcreator-record-screen.webp new file mode 100644 index 00000000000..b66e9c9e064 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-record-screen.webp differ diff --git a/doc/qtcreator/images/qtcreator-remove-file.webp b/doc/qtcreator/images/qtcreator-remove-file.webp new file mode 100644 index 00000000000..a96ffacad7c Binary files /dev/null and b/doc/qtcreator/images/qtcreator-remove-file.webp differ diff --git a/doc/qtcreator/images/qtcreator-run-settings-linux.png b/doc/qtcreator/images/qtcreator-run-settings-linux.png deleted file mode 100644 index 14e4d5f9695..00000000000 Binary files a/doc/qtcreator/images/qtcreator-run-settings-linux.png and /dev/null differ diff --git a/doc/qtcreator/images/qtcreator-run-settings-remote-linux.webp b/doc/qtcreator/images/qtcreator-run-settings-remote-linux.webp new file mode 100644 index 00000000000..0c2b00e89b0 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-run-settings-remote-linux.webp differ diff --git a/doc/qtcreator/images/qtcreator-screen-recording-options.webp b/doc/qtcreator/images/qtcreator-screen-recording-options.webp new file mode 100644 index 00000000000..1b4385e7a6b Binary files /dev/null and b/doc/qtcreator/images/qtcreator-screen-recording-options.webp differ diff --git a/doc/qtcreator/images/qtcreator-sidebar-help-mode.webp b/doc/qtcreator/images/qtcreator-sidebar-help-mode.webp new file mode 100644 index 00000000000..0dc99612e8a Binary files /dev/null and b/doc/qtcreator/images/qtcreator-sidebar-help-mode.webp differ diff --git a/doc/qtcreator/images/qtcreator-terminal-python.webp b/doc/qtcreator/images/qtcreator-terminal-python.webp index 30fd14cd818..dad8b3b1e9d 100644 Binary files a/doc/qtcreator/images/qtcreator-terminal-python.webp and b/doc/qtcreator/images/qtcreator-terminal-python.webp differ diff --git a/doc/qtcreator/images/qtcreator-welcome-open-projects.webp b/doc/qtcreator/images/qtcreator-welcome-open-projects.webp new file mode 100644 index 00000000000..5281669b407 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-welcome-open-projects.webp differ diff --git a/doc/qtcreator/images/qtcreator-without-menubar.webp b/doc/qtcreator/images/qtcreator-without-menubar.webp new file mode 100644 index 00000000000..0201ea89321 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-without-menubar.webp differ diff --git a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc index 25cc5e944d1..07dd1128a17 100644 --- a/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc +++ b/doc/qtcreator/src/analyze/cpu-usage-analyzer.qdoc @@ -104,7 +104,7 @@ \section1 Specifying Performance Analyzer Settings To specify global settings for the Performance Analyzer, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Analyzer > + \preferences > \uicontrol Analyzer > \uicontrol {CPU Usage}. For each run configuration, you can also use specialized settings. Select \uicontrol Projects > \uicontrol Run, and then select \uicontrol Details next to @@ -219,9 +219,9 @@ snapshots may fail to capture call chains of highly recursive applications or other intense stack usage. - \section2 Adding Command Line Options For Perf + \section2 Adding Command-Line Options for Perf - You can specify additional command line options to be passed to Perf when + You can specify additional command-line options to be passed to Perf when recording data in the \uicontrol {Additional arguments} field. You may want to specify \c{--no-delay} or \c{--no-buffering} to reduce the processing delay. However, those options are not supported by all versions of Perf and diff --git a/doc/qtcreator/src/analyze/creator-axivion.qdoc b/doc/qtcreator/src/analyze/creator-axivion.qdoc index 244991c7834..f188c84151b 100644 --- a/doc/qtcreator/src/analyze/creator-axivion.qdoc +++ b/doc/qtcreator/src/analyze/creator-axivion.qdoc @@ -25,6 +25,8 @@ The experimental Axivion plugin integrates the Axivion dashboard server into \QC. + \note Enable the Axivion plugin to use it. + To use the plugin, you must set up a project in the Axivion dashboard server and link to it from \QC. You can then see style violations in the \uicontrol Edit mode and descriptions and issue counts in the @@ -44,22 +46,12 @@ To view the issue counts, select \inlineimage icons/home.png (\uicontrol {Show Dashboard}). - \section1 Enabling the Axivion Plugin - - To enable the Axivion plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Code Analyzer} > \uicontrol Axivion to enable the plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist - \section1 Connecting to Axivion Dashboard Servers To connect to Axivion: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Axivion. + \li Select \preferences > \uicontrol Axivion. \image qtcreator-preferences-axivion.webp {General tab in Axivion Preferences} \li Select \uicontrol Edit to create a connection to the Axivion dashboard server. @@ -106,4 +98,6 @@ To clear the view, select \inlineimage icons/clean_pane_small.png (\uicontrol Clear). + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/analyze/creator-clang-static-analyzer.qdoc b/doc/qtcreator/src/analyze/creator-clang-static-analyzer.qdoc index 06f5e6275b7..ea2f73b844d 100644 --- a/doc/qtcreator/src/analyze/creator-clang-static-analyzer.qdoc +++ b/doc/qtcreator/src/analyze/creator-clang-static-analyzer.qdoc @@ -111,7 +111,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Analyzer > + \li Select \preferences > \uicontrol Analyzer > \uicontrol {Clang Tools}. \image qtcreator-clang-tools-options.png {Clang Tools preferences} diff --git a/doc/qtcreator/src/analyze/creator-coco.qdoc b/doc/qtcreator/src/analyze/creator-coco.qdoc index 0818e9a5b92..7dcc9a29397 100644 --- a/doc/qtcreator/src/analyze/creator-coco.qdoc +++ b/doc/qtcreator/src/analyze/creator-coco.qdoc @@ -42,15 +42,7 @@ To use the plugin, you must download and install Coco. - \section1 Enabling the Coco Plugin - - To enable the Coco plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol Coco to enable the plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \note Enable the Coco plugin to use it. \section1 Configuring Coco @@ -75,8 +67,8 @@ \section1 Changing Fonts and Colors - To change the default fonts and colors, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol {Font & Colors}. + To change the default fonts and colors, select \preferences > + \uicontrol {Text Editor} > \uicontrol {Font & Colors}. Create your own color scheme and select new fonts and colors for the following results: @@ -93,5 +85,5 @@ \li Implicit Manual Coverage Validation \endlist - For more information, see \l{Specifying Text Editor Settings}. + \sa {Enable and disable plugins}, {Specifying Text Editor Settings} */ diff --git a/doc/qtcreator/src/analyze/creator-cppcheck.qdoc b/doc/qtcreator/src/analyze/creator-cppcheck.qdoc index a77c8dfe7f9..e3cc393d705 100644 --- a/doc/qtcreator/src/analyze/creator-cppcheck.qdoc +++ b/doc/qtcreator/src/analyze/creator-cppcheck.qdoc @@ -15,20 +15,12 @@ The experimental Cppcheck Diagnostics plugin integrates diagnostics that are generated by the Cppcheck tool into the C++ editor. + \note Enable the Cppcheck plugin to use it. + Cppcheck is automatically run on open files. To select the files to check in the currently active project, select \uicontrol Analyze > \uicontrol Cppcheck. - \section1 Enabling the Cppcheck Plugin - - To enable the Cppcheck plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Code Analyzer} > \uicontrol Cppcheck to enable the - plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist \section1 Running Cppcheck on Selected Files @@ -72,6 +64,8 @@ marks or annotations. To specify the settings above for the automatically run checks, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Analyzer + select \preferences > \uicontrol Analyzer > \uicontrol Cppcheck. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/analyze/creator-heob.qdoc b/doc/qtcreator/src/analyze/creator-heob.qdoc index 3fbd2d62285..449d3cc86ab 100644 --- a/doc/qtcreator/src/analyze/creator-heob.qdoc +++ b/doc/qtcreator/src/analyze/creator-heob.qdoc @@ -92,7 +92,7 @@ In the \uicontrol {Handle exceptions} list, select \uicontrol Off to use the standard exception handler and have the debugger automatically attached if the application crashes. This works only if you register \QC is as a - post-mortem debugger by selecting \uicontrol Edit > \uicontrol Preferences > + post-mortem debugger by selecting \preferences > \uicontrol Debugger > \uicontrol General > \uicontrol {Use Qt Creator for post-mortem debugging}. diff --git a/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc b/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc index 9a2f914648f..bd3d21f7516 100644 --- a/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc +++ b/doc/qtcreator/src/analyze/creator-valgrind-overview.qdoc @@ -36,8 +36,8 @@ For more information about analyzing applications for which you do not have a project, see \l{Running Valgrind Tools on External Applications}. - To set preferences for the Valgrind tools, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Analyzer. You can override the general + To set preferences for the Valgrind tools, select \preferences > + \uicontrol Analyzer. You can override the general settings for each project in the \uicontrol {Run Settings} for the project. The following sections describe how to use the Valgrind tools: diff --git a/doc/qtcreator/src/analyze/creator-valgrind.qdoc b/doc/qtcreator/src/analyze/creator-valgrind.qdoc index f905cb1766a..537f1acfea6 100644 --- a/doc/qtcreator/src/analyze/creator-valgrind.qdoc +++ b/doc/qtcreator/src/analyze/creator-valgrind.qdoc @@ -81,18 +81,17 @@ separately for each project in the \l{Specifying Run Settings}{run settings} of the project. - To specify global settings for Valgrind, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Analyzer. The \uicontrol - {Memcheck Memory Analysis Options} group has Memcheck options. + To specify global settings for Valgrind, select \preferences > + \uicontrol Analyzer. The \uicontrol {Memcheck Memory Analysis Options} + group has Memcheck options. In \uicontrol {Extra Memcheck arguments}, specify additional arguments for launching the executable. Stack traces can get quite large and confusing, and therefore, reading them from the bottom up can help. If the stack trace is not big enough or it is - too big, select \uicontrol Edit > \uicontrol Preferences > \uicontrol Analyzer - and define the length of the stack trace in the - \uicontrol {Backtrace frame count} field. + too big, select \preferences > \uicontrol Analyzer and define the length of + the stack trace in the \uicontrol {Backtrace frame count} field. \image qtcreator-valgrind-memcheck-options.png "Memory Analysis options" @@ -268,9 +267,9 @@ separately for each project in the \l{Specifying Run Settings}{run settings} of the project. - To specify global settings for Valgrind, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Analyzer. The \uicontrol - {Callgrind Profiling Options} group has Callgrind options. + To specify global settings for Valgrind, select \preferences > + \uicontrol Analyzer. The \uicontrol {Callgrind Profiling Options} + group has Callgrind options. \image qtcreator-valgrind-callgrind-options.png "Valgrind options" @@ -324,6 +323,7 @@ \endlist + \sa {Detach views} */ /*! diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc index a171476b13c..c16c80d4e70 100644 --- a/doc/qtcreator/src/android/androiddev.qdoc +++ b/doc/qtcreator/src/android/androiddev.qdoc @@ -21,7 +21,7 @@ highlighting, function tooltips, and navigating in code, add a \l{Java Language Server}{Java language server}. - \QC integrates the Android Debug Bridge (\c adb) command line tool for + \QC integrates the Android Debug Bridge (\c adb) command-line tool for deploying applications to Android devices, running them, and reading their logs. The \c adb tool includes a client and server that run on the development host and a daemon that runs on the emulator or device. @@ -55,10 +55,7 @@ To set up the development environment for Android: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > - \uicontrol Android on Windows and Linux or \uicontrol {\QC} > - \uicontrol Preferences > \uicontrol Devices > \uicontrol Android on - \macos. + \li Select \preferences > \uicontrol Devices > \uicontrol Android. \image qtcreator-options-android-main.png {Android preferences} \li In the \uicontrol {JDK location} field, set the path to the JDK. \QC checks the JDK installation and reports errors. @@ -142,10 +139,8 @@ \section2 Managing Android NDK Packages - To view the installed \l{Android NDK} versions, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Devices > \uicontrol Android on Windows and - Linux or \uicontrol {\QC} > \uicontrol Preferences > \uicontrol Devices > - \uicontrol Android on \macos. + To view the installed \l{Android NDK} versions, select \preferences > + \uicontrol Devices > \uicontrol Android. \image qtcreator-options-android-sdk-tools.png {Android NDK and SDK checks} @@ -162,7 +157,7 @@ To add custom NDK paths manually to the global list of NDKs, select \uicontrol Add. This creates custom tool chains and debuggers associated to that NDK. However, you have to manually create a kit that uses the - custom NDK. For more information, see \l{Adding Kits}. + custom NDK. For more information, see \l{Add kits}. \section2 Managing Android SDK Packages @@ -172,11 +167,8 @@ installing, updating, and removing SDK packages. You can still use \c sdkmanager for advanced SDK management. - To view the installed Android SDK packages, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Devices > \uicontrol Android > - \uicontrol {SDK Manager} on Windows and Linux or \uicontrol {\QC} > - \uicontrol Preferences > \uicontrol Devices > \uicontrol Android > - \uicontrol {SDK Manager} on \macos. + To view the installed Android SDK packages, select \preferences > + \uicontrol Devices > \uicontrol Android > \uicontrol {SDK Manager}. \image qtcreator-android-sdk-manager.webp {Android SDK Manager} @@ -200,10 +192,8 @@ \section1 Managing Android Virtual Devices (AVD) - To view the available AVDs, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol Devices on Windows and Linux or \uicontrol {\QC} > - \uicontrol Preferences > \uicontrol Devices > on \macos. You can add more - AVDs. + To view the available AVDs, select \preferences > \uicontrol Devices. + You can add more AVDs. \image qtcreator-android-avd-manager.png {Android device in Devices} @@ -239,10 +229,8 @@ To create new virtual devices: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > - \uicontrol Add > \uicontrol {Android Device} on Windows and Linux - or \uicontrol {\QC} > \uicontrol Preferences > \uicontrol Devices > - \uicontrol Add > \uicontrol {Android Device} on \macos to open the + \li Select \preferences > \uicontrol Devices > + \uicontrol Add > \uicontrol {Android Device} to open the \uicontrol {Create New AVD} dialog. \image qtcreator-android-create-avd.png {Create New AVD dialog} \li Set the name, definition, architecture, target API level, and diff --git a/doc/qtcreator/src/baremetal/creator-baremetal-dev.qdoc b/doc/qtcreator/src/baremetal/creator-baremetal-dev.qdoc index 5d943b7ed7f..722cf8d89ba 100644 --- a/doc/qtcreator/src/baremetal/creator-baremetal-dev.qdoc +++ b/doc/qtcreator/src/baremetal/creator-baremetal-dev.qdoc @@ -54,23 +54,12 @@ ST-Link and J-Link debug server providers can be used together with the \l {uVision IDE}. - \section1 Enabling the Bare Metal Device Plugin - - To enable the Bare Metal Device plugin: - - \list 1 - - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Device Support} > \uicontrol {Bare Metal}. - - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - - \endlist + \note Enable the Bare Metal plugin to use it. \section1 Specifying Settings for Debug Server Providers To create connections to bare metal devices using a debug server provider, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices + select \preferences > \uicontrol Devices > \uicontrol {Bare Metal} > \uicontrol Add > \uicontrol Default. The available settings depend on the debug server provider. @@ -251,7 +240,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices + \li Select \preferences > \uicontrol Devices > \uicontrol Add > \uicontrol {Bare Metal Device} > \uicontrol {Start Wizard}. @@ -265,12 +254,14 @@ \section1 Building for and Running on Bare Metal Devices To add a kit for building applications and running them on bare metal - devices, select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits - > \uicontrol Add. For more information, see \l{Adding Kits}. + devices, select \preferences > \uicontrol Kits + > \uicontrol Add. For more information, see \l{Add kits}. \image qtcreator-baremetal-kit.png "Kit preferences for Bare Metal" You can build applications for and run them on bare metal devices in the same way as for and on the desktop. For more information, see \l{Building for Multiple Platforms} and \l{Running on Multiple Platforms}. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/cmake/creator-how-to-debug-cmake-files.qdoc b/doc/qtcreator/src/cmake/creator-how-to-debug-cmake-files.qdoc new file mode 100644 index 00000000000..ac3ffb12a90 --- /dev/null +++ b/doc/qtcreator/src/cmake/creator-how-to-debug-cmake-files.qdoc @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-debug-cmake-files.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-debug + \ingroup creator-how-to-build-with-cmake + + \title Debug CMake project files + + With the \QC implementation of the + \l {https://microsoft.github.io/debug-adapter-protocol/} + {Debug Adapter Protocol (DAP)}, you can debug CMake project files. + + \note You need CMake 3.27 or later for DAP. + + To debug CMake project files: + + \list 1 + \li In the \uicontrol Edit mode, set breakpoints in a CMake file. + \li Select \uicontrol Debug > \uicontrol {Start Debugging} > + \uicontrol {Start CMake Debugging}. + \image qtcreator-debugger-cmake.webp {DAP CMake Preset view in the Debug mode} + \endlist + + When the application stops at a breakpoint, you can examine data in the + \uicontrol Debug mode views. + + \sa {Debugging},{Examining Data},{Debug Mode Views},{Setting Breakpoints}, + {CMake} +*/ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index 2c302e5fcc5..f8c8842b1f5 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -137,9 +137,8 @@ To view all variables, select the \uicontrol Advanced check box. - To view all variables by default, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol CMake > \uicontrol General > - \uicontrol {Show advanced options by default}. + To view all variables by default, select \preferences > \uicontrol CMake > + \uicontrol General > \uicontrol {Show advanced options by default}. \image qtcreator-preferences-cmake-general.webp "General tab in CMake Preferences" @@ -152,8 +151,8 @@ stored in the CMakeLists.txt.user file, so deleting a build directory does not delete the initial configuration. - To be asked before \QC resets the changes, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol CMake > \uicontrol General > + To be asked before \QC resets the changes, select \preferences > + \uicontrol CMake > \uicontrol General > \uicontrol {Ask before re-configuring with initial parameters}. \section1 Viewing CMake Output @@ -193,11 +192,17 @@ You can add arguments to pass to CMake and the generator and targets for the build command in \uicontrol {Build Steps}. - \image qtcreator-cmake-build-steps.png {CMake build steps} + \image qtcreator-cmake-build-steps.webp {CMake build steps} \note While the other CMake generators are installed together with Qt, you usually need to install Ninja yourself. + To install the application to a staging directory when cross-compiling, + select the \uicontrol {Stage for installation} check box and specify + the path to a directory in the \uicontrol {Staging directory} field. + The packaging tool constructs the package from the contents of the + directory. + \section1 Using Ninja as a CMake Generator To use \l {https://ninja-build.org/}{Ninja} with CMake, you must install it @@ -212,7 +217,7 @@ \image qtcreator-cmake-kit-configuration.png {Kit CMake Configuration dialog} \li Select \uicontrol Change next to the \uicontrol {CMake generator} field to open the \uicontrol {CMake Generator} dialog. - \image qtcreator-cmake-generator.png {CMake Generator dialog} + \image qtcreator-cmake-generator.webp {CMake Generator dialog} \li In \uicontrol Generator, select \uicontrol Ninja. \li Select \uicontrol OK to save your changes and close the dialog. \li Select \uicontrol Close to close the @@ -230,7 +235,7 @@ \QC can automatically set up the \l {Setting Up Conan} {Conan package manager} for use with CMake. - Select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake + Select \preferences > \uicontrol CMake \uicontrol General > \uicontrol {Package manager auto setup} to set the value of the \c CMAKE_PROJECT_INCLUDE_BEFORE variable to the path to a CMake script that installs dependencies from a \c conanfile.txt, @@ -248,7 +253,7 @@ When building with CMake, you can add arguments to pass to CMake and the generator and targets for the clean command in \uicontrol {Clean Steps}. - \image qtcreator-cmake-clean-steps.png + \image qtcreator-cmake-clean-steps.webp {CMake clean steps} The build errors and warnings are parsed and displayed in \l Issues. */ diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc index 587790fa0dc..3a51034199e 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-presets.qdoc @@ -20,7 +20,7 @@ enforce version checking. It reads and uses all the fields from version 3 if present. It does not support test presets. - You can import the presets the first time you \l {Opening Projects} + You can import the presets the first time you \l {Open projects} {open a project}, when no \c CMakeLists.txt.user file exists or you have disabled all kits in the project. @@ -141,7 +141,7 @@ \endcode This example assumes that the CMake executable path is set in - \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > + \preferences > \uicontrol CMake > \uicontrol Tools. \section1 MSVC Example diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index d9809f09a38..3d2babbc53c 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -8,85 +8,143 @@ // ********************************************************************** /*! - \previouspage creator-project-other.html \page creator-project-cmake.html - \nextpage creator-project-qbs.html + \previouspage creator-reference.html - \title Setting Up CMake + \ingroup creator-reference-build-systems - CMake automates the configuration of build systems. It controls the software - compilation process by using simple + \title CMake + + \brief CMake is an alternative to qmake for automating the generation of + build systems. + + \l{https://cmake.org/}{CMake} automates the configuration of build systems. + It controls the software compilation process by using simple configuration files, called \c {CMakeLists.txt} files. CMake generates 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, as well - as mobile and embedded devices. You can also build single files to test - your changes. + as mobile and embedded devices. Or, 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}. - - CMake documentation is installed in Qt help file format (.qch) when you - install CMake. It is automatically registered by \QC, and you can view it - in the Help mode. - - \QC automatically runs CMake to refresh project information when you edit - a \c CMakeLists.txt configuration file in a project. Project information is - also automatically refreshed when you build the project. To disable this - behavior, select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake - > \uicontrol General, and then deselect the \uicontrol {Autorun CMake} - check box. - - \image qtcreator-projects-view-edit.png {CMake project in Projects view} - - If \QC cannot load the CMake project, the \l Projects view shows a - \uicontrol {} project node to avoid scanning the file - system and load the project faster. The node shows the same files - as the \l {File System} view. Select \uicontrol Build > - \uicontrol {Clear CMake Configuration}, and then select \uicontrol Build - > \uicontrol {Run CMake} to reconfigure the project. - - The \uicontrol Projects view shows the names of the subfolders where the - source files are located. To hide the subfolder names and arrange the files - only according to their source group, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol CMake > \uicontrol General, and then - deselect the \uicontrol {Show subfolders inside source group folders} check - box. The change takes effect after you select \uicontrol Build > - \uicontrol {Run CMake}. - - \section1 Adding CMake Tools + \section1 Supported CMake Versions \QC requires CMake's \l{https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html} {file-based API}, and therefore you'll need CMake version 3.14, or later. For systems with older versions of CMake, only workarounds are available: + \list - - \li For CMake version 3.5 or later it is possible to generate a - \l{https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html}{compilation database} - and open that in \QC, as described in \l{Using Compilation Databases}. - + \li For CMake version 3.5 or later, generate a + \l{CMake: CMAKE_EXPORT_COMPILE_COMMANDS}{compilation database} and + open that in \QC, as described in \l{Using Compilation Databases}. \li Create an ad-hoc project file for a qmake build using - \c{qmake -project} and \l{Opening Projects}{open} that in \QC. - Be aware that this is typically - not compilable without further manual changes. - + \c{qmake -project} and \l{Open projects}{open} that in \QC. + Typically, you cannot compile such projects without manual changes. \li Manually create an ad-hoc project file for a \l{Setting Up a Generic Project}{generic project} and - open that in \QC. Be aware this is typically - not compilable without further manual changes. - + open that in \QC. + Typically, you cannot compile such projects without manual changes. \endlist + \sa {Build with CMake}{How To: Build with CMake}, {Open projects}, + {CMake Build Configuration}, {Debug CMake project files}, + {Specifying Build Settings}, {Specifying Run Settings}, + {Deploying to Remote Linux} +*/ - To view and specify settings for CMake: +/*! + \page creator-how-to-view-cmake-project-contents.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + + \title View CMake project contents + + The \l Projects view visualizes the build system structure of the project as + a tree and lists all files that are part of the project. + \QC automatically runs CMake to refresh project information in the + view when you edit a \c CMakeLists.txt configuration file + in a project. Project information is also automatically refreshed when + you build the project. + + \image qtcreator-projects-view-edit.png {CMake project in Projects view} + + To disable this + behavior, select \preferences > \uicontrol CMake > \uicontrol General, and + then deselect the \uicontrol {Autorun CMake} check box. + + \image qtcreator-preferences-cmake-general.webp {General tab in CMake Preferences} + + \section1 Re-configure CMake projects + + If \QC cannot load the CMake project, the \uicontrol Projects view shows a + \uicontrol {} project node to avoid scanning the file + system and load the project faster. The node shows the same files + as the \l {File System} view. + + To re-configure the project: + + \list 1 + \li Select \uicontrol Build > \uicontrol {Clear CMake Configuration}. + \li Select \uicontrol Build > \uicontrol {Run CMake}. + \endlist + + \section1 Hide subfolder names in Projects view + + The \uicontrol Projects view shows the names of the subfolders where the + source files are located. To hide the subfolder names and arrange the files + only according to their source group, select \preferences > + \uicontrol CMake > \uicontrol General, and then + deselect the \uicontrol {Show subfolders inside source group folders} check + box. The change takes effect after you select \uicontrol Build > + \uicontrol {Run CMake}. + + \sa {CMake}, {Manage files in CMake projects}, {Open projects}, + {File System}, {Projects} +*/ + +/*! + \page creator-how-to-read-cmake-documentation.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + \ingroup creator-how-to-get-help + + \title Read CMake documentation + + CMake documentation is installed in Qt help file format (.qch) when you + install CMake. It is automatically registered by \QC, and you can view it by: + + \list + \li Hovering the mouse over a function, variable, property, policy, + environment variable, or CMake find or include module to show + tooltips + \li Selecting any of the above elements and pressing \key F1 to show + its documentation + \li Switching to the Help mode + \endlist + + \sa {CMake}, {Read Documentation}{How To: Read Documentation} +*/ + +/*! + \page creator-how-to-add-cmake-tools.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + + \title Add CMake tools + + \QC automatically detects the CMake executable that you specify 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}. + + To see the CMake installations that \QC automatically detects: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > - \uicontrol Tools. + \li Select \preferences > \uicontrol CMake > \uicontrol Tools. \image qtcreator-preferences-cmake-tools.webp {Tools tab in CMake Preferences} @@ -99,31 +157,42 @@ \li The \uicontrol {Help file} field displays the path to the CMake help file (.qch) that comes with CMake. - \li Select \uicontrol Apply to save your changes. - \endlist + \section2 Add or remove CMake tools + To add a path to a CMake executable that \QC does not detect automatically, and to specify settings for it, select \uicontrol Add. To make changes to automatically detected installations, select \uicontrol Clone. + To remove the selected CMake executable from the list, select + \uicontrol Remove. + + \section2 Set the default CMake tool + \QC uses the \e {default CMake} if it does not have enough information to choose the CMake to use. To set the selected CMake executable as the default, select \uicontrol {Make Default}. - To remove the selected CMake executable from the list, select - \uicontrol Remove. + \section2 Add CMake tools to kits - To add the CMake tool to a build and run kit, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits. + To add the CMake tool to a build and run kit, select \preferences > + \uicontrol Kits. The kit also specifies the CMake generator that is used for producing project files for \QC and the initial configuration parameters: \image qtcreator-kits-cmake.png {Kits preferences} - For more information, see \l {Adding Kits}. + \sa {CMake}, {Add kits}, {Kits} +*/ - \section1 Editing CMake Configuration Files +/*! + \page creator-how-to-edit-cmake-config-files.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + + \title Edit CMake configuration files To open a CMakeLists.txt file for editing, right-click it in the \uicontrol Projects view and select \uicontrol {Open With} > @@ -138,11 +207,22 @@ \list - \li Pressing \key F2 when the cursor is on a filename to open the file + \li Pressing \key F2 when the cursor is on a: + \list + \li Filename - to open the file + \li CMake function, macro, option, target, CMake's Find or + Include module, local variable created by \c set or \c list, + or package - to go to that item + \endlist \li Keyword completion - \li Code completion + \li Code completion for local functions and variables, cache variables, + \c ENV, targets, packages, and variables that \c find_package adds + + \li Pre-defined code snippets for setting CMake print properties and + variables, as well as creating Qt console and GUI applications and + sample Find modules \li Path completion @@ -154,23 +234,57 @@ Warnings and errors are displayed in \l {Issues}. - \section1 Formatting CMake Files + \sa {CMake}, {Add libraries to CMake projects}, {Complete CMake code}, + {Completing Code Snippets}, {Format CMake files} +*/ - You can use the \c {cmake-format} tool to format any text in CMake files that +/*! + \page creator-how-to-complete-cmake-code.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + + \title Complete CMake code + + \QC uses \l{Generic Highlighting}{generic highlighting} to provide + code completion specific arguments for the CMake commands. For + example, only source file properties are suggested for the + \l {CMake: set_source_files_properties command}, not the test or + target properties. + + The following CMake-specific trigger tokens are supported: + + \list + \li \c{$} for variables + \li \c{$<} for generator expressions + \li \c{$ENV} for environment variables + \endlist + + \sa {CMake}, {Completing Code}, {Completing Code Snippets}, + {Edit CMake configuration files} +*/ + +/*! + \page creator-how-to-format-cmake-files.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build-with-cmake + + \title Format CMake files + + You can use the \l {cmake-format} tool and local \c .cmake-format, \c.py, or + \c .json configuration files to format any text in CMake files that you do not guard with a pair of fences. You must install the tool and tell - \QC where you installed it. For more information about the tool and how to - install it, see \l{https://cmake-format.readthedocs.io/en/latest/index.html} - {cmake language tools}. + \QC where you installed it. To automatically format CMake files upon file save: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > - \uicontrol Formatter. + \li Select \preferences > \uicontrol CMake > \uicontrol Formatter. \image qtcreator-preferences-cmake-formatter.webp {Formatter tab in CMake Preferences} \li In \uicontrol {CMakeFormat command}, enter the path to \c {cmake-format.exe}. - \li Select \uicontrol {Enable auto format on file save} to automatically + \li Select \uicontrol {Automatic formatting on file save} to automatically format CMake files when you save them. \li In \uicontrol {Restrict to MIME types}, add the \l{Editing MIME Types} {MIME types} of the files to format, separated by semicolons. The @@ -181,43 +295,86 @@ current project. \endlist - \section1 Managing Files + \sa {CMake},{Complete CMake code}, {Edit CMake configuration files}, + {Editing MIME Types} +*/ - When you use project wizard templates to \l{Creating Files}{add files} to - a project, \QC automatically adds them to the \c {qt_add_executable()}, - \c {add_executable()}, or \c {qt_add_library()} function in the - CMakeLists.txt file. +/*! + \page creator-how-to-manage-files-in-cmake-projects.html + \previouspage creator-how-tos.html - If you use custom API, \QC uses \c {target_sources()} to add the files. + \ingroup creator-how-to-build-with-cmake - For Qt Quick projects, the files are added to the \c {qt_add_qml_module()} + \title Manage files in CMake projects + + When you use project wizard templates to \l{Create files}{add files} to + a project, \QC automatically adds them to the \l {qt_add_executable}, + \l{CMake: add_executable command}{add_executable}, or \l {qt_add_library} + function in the CMakeLists.txt file. + + If you use custom API, \QC uses the \l{CMake: target_sources command} + {target_sources} function to add the files. + + For Qt Quick projects, the files are added to the \l {qt_add_qml_module} function, prefixed with the \c QML_FILES, \c SOURCES, or \c RESOURCES function argument. When you rename or remove files in the \l {Projects} or \l {File System} view, \QC renames them in the CMakeLists.txt file or removes them from it. - \section1 Adding External Libraries to CMake Projects + \sa {CMake}, {Add libraries to CMake projects}, {Create files}, + {Edit CMake configuration files}, {View CMake project contents}, + {File System}, {Projects} +*/ + +/*! + \page creator-how-to-add-external-libraries-to-cmake-projects.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects + \ingroup creator-how-to-build-with-cmake + + \title Add libraries to CMake projects + + \include creator-projects-libraries.qdoc libraries + + \section1 Add your own libraries + + Use the \l qt_add_library command to create a library and link against it in + the CMakeLists.txt file, as instructed in \l{Structuring projects}. + + Specify whether the library is statically or dynamically linked. + For a statically linked internal library, add the + \l{CMake: target_link_libraries command} to the CMakeLists.txt + project file to specify dependencies. + + \section1 Add external libraries Through external libraries, \QC can support code completion and syntax - highlighting as if they were part of the current project or the Qt library. + highlighting as if the code were a part of the current project or the + Qt library. - \QC detects the external libraries using the \c {find_package()} - macro. Some libraries come with the CMake installation. You can find those + \QC detects the external libraries using the \l{CMake: find_package command} + command. Some libraries come with the CMake installation. You can find those in the \c {Modules} directory of your CMake installation. - For more information, see - \l{https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html} - {cmake-packages(7)}. + For more information, see \l{CMake: cmake-packages(7)}. - Syntax completion and highlighting work once your project successfully - builds and links against the external library. + \section1 Use local CMake Find packages - \section1 Related Topics + For CMake projects that have external dependencies, use + \l{CMake: Find Modules}{Find.cmake} modules that + expose imported targets. You can use the pre-defined \c sample_find_module + \l{Completing Code Snippets}{code snippet} to add sample commands + to a \c .cmake file. You can then change the commands as necessary. - \list - \li \l {Opening Projects} - \li \l {CMake Build Configuration} - \li \l {Specifying Run Settings} - \li \l {Deploying to Remote Linux} - \endlist + Place Find modules in the \c ${CMAKE_CURRENT_SOURCE_DIR}/cmake directory, and + append the directory name to the CMAKE_MODULE_PATH list variable. + For example: + + \code + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + \endcode + + \sa {CMake}, {Edit CMake configuration files}, {Complete CMake code}, + {Completing Code Snippets} */ diff --git a/doc/qtcreator/src/conan/creator-projects-conan.qdoc b/doc/qtcreator/src/conan/creator-projects-conan.qdoc index d91ce1cc575..3136e2a5888 100644 --- a/doc/qtcreator/src/conan/creator-projects-conan.qdoc +++ b/doc/qtcreator/src/conan/creator-projects-conan.qdoc @@ -3,11 +3,14 @@ /*! \page creator-project-conan.html - \previouspage creator-project-incredibuild.html - \nextpage creator-vcpkg.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up Conan + \brief The experimental Conan plugin integrates the Conan package manager. + \l {https://conan.io}{Conan} is a C/C++ package manager that speeds up the integration of C or C++ libraries into your own project. It's available on all the \l{Supported Platforms}{supported development platforms}. @@ -32,9 +35,7 @@ your operating system has. For example, on Windows, you can use the \c {choco install conan} or \c {pip install conan} command. - To enable the experimental Conan plugin, select \uicontrol Help > - \uicontrol {About Plugins} > \uicontrol Utilities > \uicontrol Conan. - Then select \uicontrol {Restart Now} to restart \QC and load the plugin. + \note Enable the Conan plugin to use it. For each project, you must write a \l{https://docs.conan.io/en/latest/reference/conanfile.html}{conanfile.py} @@ -46,4 +47,6 @@ Alternatively, you can automatically set up the Conan package manager for use with CMake. For more information, see \l{Using CMake with Conan}. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/debugger/creator-debug-views.qdoc b/doc/qtcreator/src/debugger/creator-debug-views.qdoc index a963701d555..149ac127a8c 100644 --- a/doc/qtcreator/src/debugger/creator-debug-views.qdoc +++ b/doc/qtcreator/src/debugger/creator-debug-views.qdoc @@ -218,9 +218,8 @@ \if defined(qtcreator) \section1 Specifying Breakpoint Settings - You can specify settings for breakpoints in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger. For more information, - see \l{Debugger Preferences}. + You can specify settings for breakpoints in \preferences > + \uicontrol Debugger. For more information, see \l{Debugger Preferences}. \image qtcreator-debugger-general-options.png {General tab in Debugger preferences} @@ -348,6 +347,10 @@ is unlikely that the used addresses will stay the same at the next application launch. If you really want a data breakpoint to be active again, re-enable it manually. + + \if defined(qtcreator) + \sa {Debug CMake project files} + \endif */ /*! diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc index ab0aff0f548..06b8d05bc52 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-settings.qdoc @@ -8,9 +8,9 @@ \title Debugger Preferences - To specify settings for managing debugger processes, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol Debugger. In the \uicontrol General tab, - you can specify settings that are common to all debuggers. + To specify settings for managing debugger processes, select \preferences > + \uicontrol Debugger. In the \uicontrol General tab,you can specify settings + that are common to all debuggers. \image qtcreator-debugger-general-options.png "Debugger General preferences" @@ -50,8 +50,8 @@ \section1 Specifying GDB Settings - To specify settings for managing the GDB process, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol GDB. + To specify settings for managing the GDB process, select \preferences > + \uicontrol Debugger > \uicontrol GDB. \image qtcreator-gdb-options.png "GDB preferences" @@ -136,8 +136,8 @@ \section1 Specifying CDB Settings - To specify settings for managing the CDB process, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol CDB. + To specify settings for managing the CDB process, select \preferences > + \uicontrol Debugger > \uicontrol CDB. \image qtcreator-cdb-options.png "CDB preferences" @@ -187,8 +187,7 @@ to the symbol search path of the debugger: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > - \uicontrol {CDB Paths}. + \li Select \preferences > \uicontrol Debugger > \uicontrol {CDB Paths}. \image qtcreator-debugger-cdb-paths.png \li In the \uicontrol {Symbol Paths} group, select \uicontrol Insert. \li Select the directory where you want to store the cached information. diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc index 8c2c61a413a..39fd8013715 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc @@ -16,9 +16,9 @@ \title Setting Up Debugger The main debugger preferences are associated with the - \l{Adding Kits}{kit} you build and run your project with. To + \l{Kits}{kit} you build and run your project with. To specify the debugger and compiler to use for each kit, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits. + \preferences > \uicontrol Kits. \image qtcreator-kits.png {Kits preferences} @@ -30,13 +30,13 @@ installed replacement instead. To change the debugger in an automatically detected kit, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \preferences > \uicontrol Kits > \uicontrol Clone to create a copy of the kit, and change the parameters in the cloned kit. Make sure to enable the cloned kit for your project. If the debugger you want to use is not automatically detected, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \preferences > \uicontrol Kits > \uicontrol Debuggers > \uicontrol Add to add it. \image qtcreator-preferences-kits-debuggers.webp {Debuggers tab in Kits preferences} @@ -178,7 +178,7 @@ 32-bit executable with a 64-bit debugger can result in a stack trace of the WOW64 emulator 32-bit emulation layer being displayed. - \QC extends the command line debugger by loading the + \QC extends the command-line debugger by loading the \c qtcreatorcdbext.dll extension library into it. The library must be available in the \c {libs\qtcreatorcdbext64} and \c {libs\qtcreatorcdbext32} folder. To install it there, @@ -214,7 +214,7 @@ \l{https://docs.python.org/3/library/pdb.html}{PDB} is a source code debugger for Python applications. You can use it to debug projects that have a - \l {Creating Widget-Based Qt for Python Applications}{.pyproject} + \l {Creating a Qt for Python Application with Qt Widgets}{.pyproject} configuration file. You must install Python and set the interpreter to use in \uicontrol Projects diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 5b3e0797f71..fe61cd19eae 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -116,6 +116,7 @@ solutions to them. \endlist + \sa {Debug CMake project files} */ @@ -132,19 +133,18 @@ \QC checks whether the compiled application is up-to-date, and rebuilds and deploys it if you set the \uicontrol {Build before deploying} field to - build the whole project or the application to run and select he - \uicontrol {Always deploy before running} check box in - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > - \uicontrol General. To debug the application without deploying - it, select \uicontrol Debug > \uicontrol {Start Debugging} > - \uicontrol {Start Debugging Without Deployment}. + build the whole project or the application to run and select the + \uicontrol {Always deploy before running} check box in \preferences > + \uicontrol {Build & Run} > \uicontrol General. To debug the application + without deploying it, select \uicontrol Debug > \uicontrol {Start Debugging} + > \uicontrol {Start Debugging Without Deployment}. The debugger then takes over and starts the application with suitable parameters. When using GDB or CDB as debug backend, you can specify additional commands to execute before and after the backend and debugged application are started or - attached in \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > + attached in \preferences > \uicontrol Debugger > \uicontrol GDB and \uicontrol CDB. For more information, see \l{Debugger Preferences}. @@ -156,7 +156,7 @@ a long time. Typically, in the range of several seconds to minutes if you use complex features. - For \l {Creating Widget-Based Qt for Python Applications}{Python} projects, + For \l {Creating a Qt for Python Application with Qt Widgets}{Python} projects, start debugging the \c main.py file. If you encounter problems, check the active build target in the \l{Building for Multiple Platforms}{kit selector}. @@ -209,7 +209,7 @@ If a console application does not start up properly in the configured console and the subsequent attach fails, you can diagnose the issue by - using CDB's native console. Select \uicontrol Edit > \uicontrol Preferences > + using CDB's native console. Select \preferences > \uicontrol Debugger > \uicontrol CDB > \uicontrol {Use CDB console} to override the console set in the Windows system environment variables. Note that the native console does not prompt on application exit. @@ -363,7 +363,7 @@ The \QC installation program asks you whether you want to register \QC as a post-mortem debugger. To change the setting, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > + \preferences > \uicontrol Debugger > \uicontrol General > \uicontrol {Use \QC for post-mortem debugging}. You can launch the debugger in the post-mortem mode if an application @@ -390,7 +390,7 @@ \endlist - For more information, see \l{Using Command Line Options}. + For more information, see \l{Command-Line Options}. */ /*! @@ -486,7 +486,7 @@ By default, a non-responsive GDB process is terminated after 20 seconds. To increase the timeout in the \uicontrol {GDB timeout} field, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > + \preferences > \uicontrol Debugger > \uicontrol GDB. For more information about settings that you can specify to manage the GDB process, see \l{Specifying GDB Settings}. @@ -497,14 +497,14 @@ \section1 Using CDB In remote mode, the local CDB process talks to a CDB process that runs on - the remote machine. The process is started with special command line options + the remote machine. The process is started with special command-line options that switch it into server mode. The remote CDB process must load the \QC CDB extension library that is shipped with \QC: \list 1 \li Install the \e{Debugging Tools for Windows} on the remote machine. - The installation folder has the CDB command line executable + The installation folder has the CDB command-line executable (\c cdb.exe). \li Copy the \QC CDB extension library and the dependencies from the Qt installation @@ -559,9 +559,9 @@ \endlist - To specify settings for managing the CDB process, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol CDB. For more - information, see \l{Specifying CDB Settings}. + To specify settings for managing the CDB process, select \preferences > + \uicontrol Debugger > \uicontrol CDB. For more information, see + \l{Specifying CDB Settings}. */ @@ -705,8 +705,7 @@ \section1 Customizing Debug Views To change the appearance and behavior of the debug views, set preferences - in \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > - \uicontrol General. + in \preferences > \uicontrol Debugger > \uicontrol General. \image qtcreator-debugger-general-options.png {General tab in Debugger preferences} @@ -799,8 +798,8 @@ select \uicontrol {Close Editor Tooltips} in the context menu in the \uicontrol Locals or \uicontrol Expressions view. - To disable tooltips for performance reasons, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol General > + To disable tooltips for performance reasons, select \preferences > + \uicontrol Debugger > \uicontrol General > \uicontrol {Use tooltips in main editor when debugging}. \section1 Examining Complex Values in Debug Views @@ -847,9 +846,8 @@ classes in a useful way. To change the number of array elements that are - requested when expanding entries, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Debugger} > - \uicontrol {Locals & Expressions} > \uicontrol {Default array size}. + requested when expanding entries, select \preferences > \uicontrol {Debugger} + > \uicontrol {Locals & Expressions} > \uicontrol {Default array size}. \section1 Stepping Through Code @@ -947,9 +945,8 @@ value display format. The available options depend on the type of the current items, and are provided by the debugging helpers. - To force a plain C-like display of structures, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > - \uicontrol {Locals & Expressions}, and then deselect the + To force a plain C-like display of structures, select \preferences > + \uicontrol Debugger > \uicontrol {Locals & Expressions}, and then deselect the \uicontrol {Use Debugging Helpers} check box. This still uses the Python scripts, but generates more basic output. To force the plain display for a single object or for all objects of a given type, select @@ -989,9 +986,8 @@ If an instance of a class is derived from QObject, you can find all other objects connected to this object's slots with Qt's signals and slots - mechanism. Select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Debugger} > \uicontrol {Locals & Expressions} > - \uicontrol {Use Debugging Helpers}. + mechanism. Select \preferences > \uicontrol {Debugger} > + \uicontrol {Locals & Expressions} > \uicontrol {Use Debugging Helpers}. \image qtcreator-debugging-helper-options.webp {Locals & Expressions preferences} @@ -1005,9 +1001,8 @@ switch off the debugging helpers to make low-level structures visible. To switch off the debugging helpers, deselect - \uicontrol {Use Debugging Helpers} in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > - \uicontrol {Locals & Expressions}. + \uicontrol {Use Debugging Helpers} in \preferences > + \uicontrol Debugger > \uicontrol {Locals & Expressions}. \omit \section2 Creating Snapshots @@ -1111,8 +1106,8 @@ When using CDB as debug backend, you can specify that the debugger should break when application modules are loaded or unloaded. To enable breaking - for the specified modules, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Debugger > \uicontrol CDB. + for the specified modules, select \preferences > \uicontrol Debugger > + \uicontrol CDB. \image qtcreator-cdb-options.png {CDB tab in Debugger preferences} @@ -1149,8 +1144,7 @@ To enable the debugger to step into the code and display the source code when using a copy of the source tree at a location different from the one at which the libraries were built, you can map source paths to target - paths in \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > - \uicontrol General: + paths in \preferences > \uicontrol Debugger > \uicontrol General: \image qtcreator-debugger-general-options.png {General tab in Debugger preferences} @@ -1263,8 +1257,8 @@ \image qtcreator-debugger-log-view.webp {Debugger Log view} - If debug output is sent to the system log, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol General > + If debug output is sent to the system log, select \preferences > + \uicontrol Debugger > \uicontrol General > \uicontrol {Force logging to console} check box. Right-click the view to select the following actions: @@ -1314,7 +1308,7 @@ \image qtcreator-debugger-disassembler-view.webp {Disassembler view} By default, GDB shows AT&T style disassembly. To switch to the Intel style, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > + select \preferences > \uicontrol Debugger > \uicontrol GDB > \uicontrol {Use Intel style disassembly}. To open the \uicontrol Disassembler view: @@ -1370,7 +1364,7 @@ least one of the three supported backends is available. To use the default GDB pretty printers installed in your system or linked - to the libraries your application uses, select \uicontrol Preferences > + to the libraries your application uses, select \preferences > \uicontrol Debugger > \uicontrol GDB > \uicontrol {Load system GDB pretty printers}. For more information, see \l{Specifying GDB Settings}. @@ -1380,9 +1374,9 @@ You can have commands executed after built-in debugging helpers have been loaded and fully initialized. To load additional debugging helpers or - modify existing ones, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Debugger > \uicontrol {Locals & Expressions}, and enter the - commands in the \uicontrol {Debugging Helper Customization} field. + modify existing ones, select \preferences > \uicontrol Debugger > + \uicontrol {Locals & Expressions}, and enter the commands in the + \uicontrol {Debugging Helper Customization} field. \image qtcreator-debugging-helper-options.webp {Locals & Expressions preferences} @@ -1402,9 +1396,8 @@ \endcode To display a message box as soon as your application receives a signal - during debugging, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Debugger > \uicontrol GDB > \uicontrol {Show a message box - when receiving a signal}. + during debugging, select \preferences > \uicontrol Debugger > \uicontrol GDB > + \uicontrol {Show a message box when receiving a signal}. \section2 Adding Custom Debugging Helpers @@ -1415,8 +1408,8 @@ To add debugging helpers for custom types, add debugging helper implementations to the startup file of the native debuggers (for example, \c{~/.gdbinit} or \c{~/.lldbinit}) or specify them directly in the - \uicontrol {Additional Startup Commands} in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Debugger > \uicontrol GDB. + \uicontrol {Additional Startup Commands} in \preferences > + \uicontrol Debugger > \uicontrol GDB. To get started with implementing debugging helpers for your own data types, you can put their implementation into the file @@ -1449,9 +1442,8 @@ update your \QC installation (when updating your Qt installation, for example), copy it to a safe location outside the \QC installation in your file system and specify the location in - \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > - \uicontrol {Locals & Expressions} > - \uicontrol {Extra Debugging Helper}. + \preferences > \uicontrol Debugger > \uicontrol {Locals & Expressions} + > \uicontrol {Extra Debugging Helper}. \endlist The custom debugging helpers will be automatically picked up from @@ -1592,7 +1584,7 @@ common situations. When using CDB as debugger backend, you can enable the Python dumper by - selecting \uicontrol Edit > \uicontrol Preferences > \uicontrol Debugger > + selecting \preferences > \uicontrol Debugger > \uicontrol CDB > \uicontrol {Use Python dumper}. \image qtcreator-cdb-options.png {CDB preferences} @@ -2041,9 +2033,8 @@ When using GDB as backend, you can automatically save a copy of its symbol index in a cache on disk and retrieve it from there - when loading the same binary in the future. Select \uicontrol Edit - > \uicontrol Preferences > \uicontrol Debugger > \uicontrol GDB > - \uicontrol {Use automatic symbol cache}. + when loading the same binary in the future. Select \preferences > + \uicontrol Debugger > \uicontrol GDB > \uicontrol {Use automatic symbol cache}. \image qtcreator-gdb-options.png {GDB preferences} diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index 739095b5a03..56cefd70e62 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -91,11 +91,10 @@ \section2 Using Default Values - You can enable or disable QML debugging globally in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > - \uicontrol {Default Build Properties}. + You can enable or disable QML debugging globally in \preferences > + \uicontrol {Build & Run} > \uicontrol {Default Build Properties}. - \image qtcreator-build-settings-default.png "Default Build Properties tab in Build & Run Preferences" + \image qtcreator-build-settings-default.webp "Default Build Properties tab in Build & Run Preferences" The value of the \uicontrol {QML debugging} field determines what happens when creating new build configurations. The values \uicontrol Enable @@ -120,7 +119,7 @@ \QC, it will be kept as you want it. There are some known issues in the interaction between the global setting - in \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > + in \preferences > \uicontrol {Build & Run} > \uicontrol {Default Build Properties} and the build configuration. For example, for qmake the global setting only affects build configurations that are automatically created when enabling a kit. Also, CMake ignores the diff --git a/doc/qtcreator/src/docker/creator-docker.qdoc b/doc/qtcreator/src/docker/creator-docker.qdoc index 33c358a5bed..ae1dced8cea 100644 --- a/doc/qtcreator/src/docker/creator-docker.qdoc +++ b/doc/qtcreator/src/docker/creator-docker.qdoc @@ -18,8 +18,8 @@ To build, run, and debug applications on Docker devices, you must install and configure \c docker-cli on the development host. \QC automatically detects - \l{Adding Kits}{build and run kit} items, such \l{Adding Debuggers} - {debuggers} and \l{Adding Qt Versions}{Qt versions}, in the Docker container + \l{Kits}{build and run kit} items, such \l{Add debuggers} + {debuggers} and \l{Add Qt versions}{Qt versions}, in the Docker container and creates kits for the devices. You can use CMake or qmake to build applications in the Docker container. @@ -33,7 +33,7 @@ To add a Docker image as a device: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices + \li Select \preferences > \uicontrol Devices > \uicontrol Docker and enter the path to the Docker CLI in the \uicontrol Command field. \image qtcreator-preferences-devices-docker.webp "Docker tab in Devices preferences" @@ -145,7 +145,7 @@ \section1 Editing Docker Device Kits - Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits to check + Select \preferences > \uicontrol Kits to check that the automatically generated kits point to the appropriate kit items. To specify build settings: diff --git a/doc/qtcreator/src/editors/creator-code-completion.qdoc b/doc/qtcreator/src/editors/creator-code-completion.qdoc index a7a1b17c4a2..b3d1830640b 100644 --- a/doc/qtcreator/src/editors/creator-code-completion.qdoc +++ b/doc/qtcreator/src/editors/creator-code-completion.qdoc @@ -28,10 +28,10 @@ \section1 Specifying Completion Settings - To specify settings for code completion, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Completion. + To specify settings for code completion, select \preferences > + \uicontrol {Text Editor} > \uicontrol Completion. - \image qtcreator-options-texteditor-completion.png "Text Editor Completion preferences" + \image qtcreator-preferences-texteditor-completion.webp "Text Editor Completion preferences" By default, code completion does not consider case. To apply full or first-letter case-sensitivity, select \uicontrol Full or @@ -153,7 +153,7 @@ Code snippets specify code constructs. You can add, modify, and remove snippets in the snippet editor. To open the editor, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + \preferences > \uicontrol {Text Editor} > \uicontrol Snippets. \if defined(qtcreator) @@ -296,8 +296,10 @@ completion. To use Nimsuggest, you must install it on the development PC. Then select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Nim > \uicontrol Tools, + \preferences > \uicontrol Nim > \uicontrol Tools, and enter the path to the tool executable in the \uicontrol Path field. + + \sa {Document code} \else \include qtcreator-variables.qdocinc qtcreator variables \endif diff --git a/doc/qtcreator/src/editors/creator-code-indentation.qdoc b/doc/qtcreator/src/editors/creator-code-indentation.qdoc index 8e175856347..6dcb32a4aa6 100644 --- a/doc/qtcreator/src/editors/creator-code-indentation.qdoc +++ b/doc/qtcreator/src/editors/creator-code-indentation.qdoc @@ -2,30 +2,23 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-completing-code.html \page creator-indenting-code.html - \nextpage qt-quick-toolbars.html + \if defined(qtdesignstudio) + \previouspage creator-completing-code.html + \nextpage creator-preferences-text-editor-behavior.html + \else + \previouspage creator-how-tos.html + \endif - \title Indenting Text or Code + \ingroup creator-how-to-edit + + \title Indent text or code When you type text or code, it is indented automatically according to the selected text editor or code style preferences. Select a block to indent it when you press \key Tab. Press \key {Shift+Tab} to decrease the indentation. You can disable automatic indentation. - You can specify indentation for: - - \list - \if defined(qtcreator) - \li C++ files - \endif - \li QML files - \if defined(qtcreator) - \li Nim files - \endif - \li Other text files - \endlist - To fix the indentation in the file currently open in the editor, select options in the \uicontrol Edit > \uicontrol Advanced menu or use \l{Keyboard Shortcuts}{keyboard shortcuts}: @@ -47,30 +40,31 @@ select \uicontrol {Clean Whitespace}. \endlist - \section1 Specifying Indentation Settings - - You can also specify indentation separately for each project. You can - specify several sets of code style settings and easily switch between them. - In addition, you can import and export code style settings. + \section1 Automatically fix indentation To automatically fix indentation according to the indentation settings - when you save the file, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Text Editor} > \uicontrol Behavior > - \uicontrol {Clean whitespace} > \uicontrol {Clean indentation}. Select + when you save the file, select \preferences > \uicontrol {Text Editor} > + \uicontrol Behavior > \uicontrol {Clean whitespace} > + \uicontrol {Clean indentation}. Select the \uicontrol {Skip clean whitespace for file types} check box to exclude the specified file types. \image qtcreator-options-text-editor-behavior.png {Text Editor Behavior preferences} - To visualize whitespace in the editor, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Display > - \uicontrol {Visualize whitespace}. To visualize indentation, select - \uicontrol {Visualize Indent}. To adjust the color of the visualization, - change the value of the Visual Whitespace setting of the editor color scheme - in \uicontrol {Font & Colors}. + \section1 Show whitespace in editor + + To visualize whitespace in the editor, select \preferences > + \uicontrol {Text Editor} > \uicontrol Display > + \uicontrol {Visualize whitespace}. + + To visualize indentation, select \uicontrol {Visualize Indent}. To adjust the + color of the visualization, change the value of the Visual Whitespace setting + of the editor color scheme in \uicontrol {Font & Colors}. \image qtcreator-options-text-editor-display.png {Text Editor Display preferences} + \section1 Display right margin + To help you keep line length at a particular number of characters, set the number of characters in the \uicontrol {Display right margin at column} field. To use a different color for the margin area, select the @@ -83,242 +77,8 @@ Then, use the ClangFormat \c ColumnLimit option to set the margin, for example. - \section1 Indenting C++ Files - - \QC uses the Clang \l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat} - library to automatically format and indent C++ code. It enforces a coding - style for a project or the whole organization. - - To specify indentation settings for the C++ editor: - - \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol {C++}. - \image qtcreator-code-style-clang-format-global.webp {Code Style preferences} - \li In \uicontrol {Formatting mode}, select \uicontrol {Indenting Only} - to only indent code. Select \uicontrol {Full Formatting} to use the - \key {Ctrl+I} keyboard shortcut to format code instead of indenting. - Select \uicontrol Disable to turn off ClangFormat. - \li To apply the formatting while you type, select - \uicontrol {Format while typing}. - \li To apply the formatting to the edited code when you save the file, - select \uicontrol {Format edited code on file save}. - \li To change the ClangFormat style globally for all projects, - select \uicontrol {Override ClangFormat configuration file}. - \li In the \uicontrol {Current settings} field, select the settings to - modify and click \uicontrol Copy. - \li Give a name to the settings and click \uicontrol OK. - \li Click \uicontrol Edit to set - \l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html} - {ClangFormat Style Options}. - \endlist - - In the other tabs, you can specify how to: - - \list - \li Interpret the \key Tab and \key Backspace key presses. - \li Indent the contents of classes, functions, blocks, and namespaces. - \li Indent braces in classes, namespaces, enums, functions, and blocks. - \li Control switch statements and their contents. - \li Align continuation lines. - \li Bind pointers (*) and references (&) in types and declarations to - identifiers, type names, or left or right \c const or \c volatile - keywords. - \li Name getter functions. - \endlist - - Use the live preview to see how the preferences change the indentation. - - \section2 Creating Project-Specific ClangFormat Files - - To override the \c {.clang-format} file for a particular project, create a - copy of the built-in style and edit its settings by selecting - \uicontrol Projects > \uicontrol {Project Settings} > - \uicontrol {Code Style} > \uicontrol Copy > \uicontrol Edit > - \uicontrol {ClangFormat} > - \uicontrol {Override ClangFormat configuration file}. - - \section2 Creating ClangFormat Files from Command Line - - You can create \c {.clang-format} files that have the configuration - options of a certain predefined style from the command line. For example, - to create a format file for the LLVM style, enter the following command: - - \badcode - clang-format -style=llvm -dump-config > .clang-format - \endcode - + \sa {C++ Code Style} \endif - \section1 Indenting QML Files - - To specify settings for the Qt Quick editor: - - \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Qt Quick}. - \li In the \uicontrol {Current settings} field, select the settings to - modify and click \uicontrol Copy. - \image qtcreator-options-code-style-qml.png {QML Code Style preferences} - \li Give a name to the settings and click \uicontrol OK. - \li Click \uicontrol Edit to specify code style settings for the project. - \image qtcreator-code-style-settings-edit-qtquick.png {Edit Code Style dialog} - \endlist - - You can specify how to interpret the \key Tab key presses and how to align - continuation lines. - - In \uicontrol {Line length}, you can adjust the maximum line length for - code lines. - - To specify different settings for a particular project, select - \uicontrol Projects > \uicontrol {Code Style}. - - \if defined(qtcreator) - \section1 Indenting Nim Files - - To specify settings for the Nim editor (experimental): - - \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Nim. - \li In the \uicontrol {Current settings} field, select the settings to - modify and click \uicontrol Copy. - \image qtcreator-options-code-style-nim.png {Nim Code Style preferences} - \li Give a name to the settings and click \uicontrol OK. - \li Click \uicontrol Edit to specify code style settings for the project. - \image qtcreator-code-style-settings-edit-nim.png {Edit Code Style dialog} - \endlist - - You can specify how to interpret the \key Tab key presses and how to align - continuation lines. - - To specify different settings for a particular project, select - \uicontrol Projects > \uicontrol {Code Style}. - \endif - - \section1 Indenting Other Text Files - - To specify indentation settings for text files that do not have C++ or - QML code (such as Python code files), select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior. - - \image qtcreator-indentation.png {Text Editor Behavior preferences} - - To specify different settings for a particular project, select - \uicontrol Projects > \uicontrol Editor. - - You can specify how to interpret the \key Tab and \key Backspace key - presses and how to align continuation lines. - - \section1 Specifying Tab Settings - - You can specify tab settings at the following levels: - - \list - \if defined(qtcreator) - \li For all C++ files - \endif - \li For all QML files - \li For all other text files - \if defined(qtcreator) - \li For C++ files in a project - \endif - \li For QML files in a project - \li For other text files in a project - \endlist - - \section2 Specifying Tabs and Indentation - - You can specify tab policy and tab size in the - \uicontrol {Tabs and Indentation} group. In the \uicontrol {Tab policy} - field, select whether to use only spaces or only tabs for indentation, - or to use a mixture of them. - - By default, the tab length in code editor is 8 spaces and the indent size is - 4 spaces. You can specify the tab length and indent size separately for each - project and for different types of files. - - You can have continuation lines aligned with the previous line. In the - \uicontrol {Align continuation lines} field, select - \uicontrol {Not at all} to disable automatic alignment and indent - continuation lines to the logical depth. To always use spaces for alignment, - select \uicontrol {With Spaces}. To follow the \uicontrol {Tab policy}, - select \uicontrol {With Regular Indent}. - - \section1 Setting Typing Preferences - - When you type text or code, it is indented automatically according to the - selected text editor or code style preferences. To set typing preferences, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Behavior > \uicontrol Typing. - - To disable automatic indentation, deselect the - \uicontrol {Enable automatic indentation} check box. - - You can specify how the indentation is decreased when you press - \uicontrol Backspace in the \uicontrol {Backspace indentation} field. To go - back one space at a time, select \uicontrol None. To decrease indentation - in leading white space by one level, select - \uicontrol {Follows Previous Indents}. To move back one tab length if the - character to the left of the cursor is a space, select - \uicontrol Unindents. - - You can specify whether the \key Tab key automatically indents text when you - press it. To automatically indent text, select \uicontrol Always in the - \uicontrol {Tab key performs auto-indent} field. To only indent text when - the cursor is located within leading white space, select \uicontrol {In - Leading White Space}. - - Your highlight definition file can have definitions for both multi and - single line comments. To apply the single line comment definition when - commenting out a selection, select \uicontrol {Prefer single line comments}. - - \if defined(qtcreator) - \section1 Specifying Settings for Content - - You can indent public, protected, and private statements and declarations - related to them within classes. - - You can also indent statements within functions and blocks and declarations - within namespaces. - - \image qtcreator-code-style-content.png {Content preferences} - - \section1 Specifying Settings for Braces - - You can indent class, namespace, enum and function declarations and code - blocks. - - \image qtcreator-code-style-braces.png {Braces preferences} - - \section1 Specifying Settings for Switch Statements - - You can indent case or default statements, or statements or blocks related - to them within switch statements. - - \image qtcreator-code-style-switch.png {Switch preferences} - - \section1 Specifying Alignment - - To align continuation lines to tokens after assignments, such as \c = or - \c +=, select the \uicontrol {Align after assignments} check box. You can - specify additional settings for aligning continuation lines in the - \uicontrol General tab. - - You can also add spaces to conditional statements, so that they are not - aligned with the following line. Usually, this only affects \c if - statements. - - \image qtcreator-code-style-alignment.png {Alignment preferences} - - \section1 Binding Pointers and References - - To bind pointers (\c *) and references (\c &) in types and declarations to - identifiers, type names, or left or right \c const or \c volatile keywords, - select the check boxes in the \uicontrol {Pointers and References} tab. - - The \c * and \c & characters are automatically bound to identifiers of - pointers to functions and pointers to arrays. - - \image qtcreator-pointers-references.png {Pointers and References preferences} - \endif + \sa {Behavior}, {Qt Quick Code Style}, {Keyboard Shortcuts} */ diff --git a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc index a9ff5a20b5e..37f45c5b259 100644 --- a/doc/qtcreator/src/editors/creator-code-refactoring.qdoc +++ b/doc/qtcreator/src/editors/creator-code-refactoring.qdoc @@ -22,7 +22,7 @@ \l{Applying Refactoring Actions}. By default, the refactored files are saved automatically. To disable - this feature, deselect \uicontrol Edit > \uicontrol Preferences > + this feature, deselect \preferences > \uicontrol Environment > \uicontrol System > \uicontrol {Auto-save files after refactoring}. \if defined(qtcreator) diff --git a/doc/qtcreator/src/editors/creator-code-syntax.qdoc b/doc/qtcreator/src/editors/creator-code-syntax.qdoc index 87203f8bd3a..ad86edbf2d9 100644 --- a/doc/qtcreator/src/editors/creator-code-syntax.qdoc +++ b/doc/qtcreator/src/editors/creator-code-syntax.qdoc @@ -39,7 +39,7 @@ of the line annotations. To modify the colors used for underlining errors and warnings, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + \preferences > \uicontrol {Text Editor} > \uicontrol {Font & Colors} > \uicontrol Copy, and select new colors for \uicontrol Error and \uicontrol Warning. @@ -55,7 +55,7 @@ \section1 Specifying Line Annotation Positions To specify the position where the annotations are displayed, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + \preferences > \uicontrol {Text Editor} > \uicontrol Display > \uicontrol {Line annotations}, and then select whether to display the annotations directly next to the code, aligned to the right of the code, or in the right margin. Showing annotations @@ -701,9 +701,9 @@ \section1 Enabling and Disabling Messages - To enable and disable QML and JavaScript messages, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} - > \uicontrol {Use customized static analyzer}. + To enable and disable QML and JavaScript messages, select \preferences > + \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} > + \uicontrol {Use customized static analyzer}. \image qtcreator-preferences-qtquick-qmljs-editing.webp {QML/JS Editing tab in Qt Quick preferences} @@ -712,6 +712,8 @@ \uicontrol {Disable messages for non Qt Quick UI}. You cannot enable messages just for non-Qt Quick UI files. + \sa {QML Language Server} + \section1 Resetting the Code Model If you change the build and run kit when you have QML files open in the code @@ -739,8 +741,8 @@ \section1 Automatically Formatting QML/JS Files - To automatically format QML/JS files upon saving, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} > + To automatically format QML/JS files upon saving, select \preferences > + \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} > \uicontrol {Enable auto format on file save}. To only format files that belong to the current project, select \uicontrol {Restrict to files contained in the current project}. @@ -763,9 +765,8 @@ This action expands all C++ macros to their actual code and removes code that is guarded by a currently inactive \c {#ifdef} statements. If you deselect the \uicontrol {Use built-in preprocessor to show pre-processed files} check - box in \uicontrol Edit > \uicontrol Preferences > \uicontrol C++ > - \uicontrol {Code Model}, this action also expands all - \c {"#include "} statements to their actual contents. + box in \preferences > \uicontrol C++ > \uicontrol {Code Model}, this action + also expands all \c {"#include "} statements to their actual contents. \image qtcreator-preferences-code-model.webp {C++ Code Model preferences} diff --git a/doc/qtcreator/src/editors/creator-coding.qdoc b/doc/qtcreator/src/editors/creator-coding.qdoc index ce095492875..b74b3b627da 100644 --- a/doc/qtcreator/src/editors/creator-coding.qdoc +++ b/doc/qtcreator/src/editors/creator-coding.qdoc @@ -90,13 +90,6 @@ \l{https://github.com/features/copilot}{GitHub Copilot} into \QC. You can view suggestions from Copilot in the code editor. - \li \l{Developing Qt for Python Applications} - - To support developing Qt for Python applications, \QC lets you - set up Qt for Python, use project wizards to create Qt for - Python applications, write Python code, and run and debug the - applications. - \li \l{Editing MIME Types} \QC uses the MIME type of a file to determine which mode and editor @@ -114,7 +107,7 @@ You can use \QC to create applications that embed state machines. A project wizard creates \l{https://www.w3.org/TR/scxml/} {State Chart XML (SCXML)} files with boilerplate code that you can - edit using an experimental SCXML editor. You can use the classes in + edit using an SCXML editor. You can use the classes in the Qt SCXML module to embed state machines created from the files in Qt applications. diff --git a/doc/qtcreator/src/editors/creator-diff-editor.qdoc b/doc/qtcreator/src/editors/creator-diff-editor.qdoc index 2cc78bbfbaf..92e25c5dcac 100644 --- a/doc/qtcreator/src/editors/creator-diff-editor.qdoc +++ b/doc/qtcreator/src/editors/creator-diff-editor.qdoc @@ -100,7 +100,7 @@ \section1 Changing the Colors - To change the default colors, select \uicontrol Edit > \uicontrol Preferences > + To change the default colors, select \preferences > \uicontrol {Text Editor} > \uicontrol {Font & Colors}. Create your own color scheme and select new colors for the following items: diff --git a/doc/qtcreator/src/editors/creator-editors-options-text.qdoc b/doc/qtcreator/src/editors/creator-editors-options-text.qdoc index e4779703627..a9352a5bc36 100644 --- a/doc/qtcreator/src/editors/creator-editors-options-text.qdoc +++ b/doc/qtcreator/src/editors/creator-editors-options-text.qdoc @@ -23,9 +23,8 @@ \endif Set the font preferences and apply color schemes for syntax highlighting, - diff editor, and code analysis results in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol {Font & Colors}. + diff editor, and code analysis results in \preferences > + \uicontrol {Text Editor} > \uicontrol {Font & Colors}. \image qtcreator-font-colors.png "Text editor preferences" @@ -35,9 +34,8 @@ percentage for viewing the text. You can also zoom in or out by pressing \key {Ctrl++} or \key {Ctrl+-}, or by pressing \key Ctrl and rolling the mouse button up or down. To disable the mouse wheel function, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Behavior and deselect the - \uicontrol {Enable scroll wheel zooming} check box. + \preferences > \uicontrol {Text Editor} > \uicontrol Behavior and deselect + the \uicontrol {Enable scroll wheel zooming} check box. To improve the readability of text in the editor, adjust the line spacing in the \uicontrol {Line spacing} field. @@ -55,9 +53,8 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Text Editor} > \uicontrol {Fonts & Color} > - \uicontrol Copy. + \li Select \preferences > \uicontrol {Text Editor} > + \uicontrol {Fonts & Color} > \uicontrol Copy. \li Enter a name for the color scheme and click \uicontrol OK. @@ -86,7 +83,7 @@ \section2 Exporting and Importing Color Schemes To share color schemes with others, export and import them as XML files. - To export a color scheme, select \uicontrol Edit > \uicontrol Preferences > + To export a color scheme, select \preferences > \uicontrol {Text Editor} > \uicontrol {Fonts & Color} > \uicontrol Export, and then select the filename and location for the XML file. @@ -101,8 +98,8 @@ \section2 File Encoding - To define the default file encoding, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior, and + To define the default file encoding, select \preferences > + \uicontrol {Text Editor} > \uicontrol Behavior, and then select a suitable option in \uicontrol {Default encoding}. \image qtcreator-options-texteditor-behavior-file-encodings.png "File encoding preferences" diff --git a/doc/qtcreator/src/editors/creator-editors-options.qdoc b/doc/qtcreator/src/editors/creator-editors-options.qdoc index e74f3d1d31a..595c79b81c3 100644 --- a/doc/qtcreator/src/editors/creator-editors-options.qdoc +++ b/doc/qtcreator/src/editors/creator-editors-options.qdoc @@ -19,7 +19,7 @@ \title Configuring the Editor You can configure the text editor to suit your specific needs by selecting - \uicontrol Edit > \uicontrol Preferences > \uicontrol{Text Editor}. + \preferences > \uicontrol{Text Editor}. \image qtcreator-font-colors.png "Text Editor preferences" @@ -31,12 +31,12 @@ \image qtcreator-editor-settings.png "Editor settings" \if defined(qtcreator) - For more information, see \l{Specifying Editor Settings}. + For more information, see \l{Specify editor settings}. \endif You can also specify indentation settings separately for C++ and QML files either globally or for the open project. For more information, see - \l{Indenting Text or Code}. + \l{Indent text or code}. You can perform the following configuration actions: @@ -54,12 +54,11 @@ {definition files for syntax highlighting} for other types of files than C++ or QML in \uicontrol {Generic Highlighter}. - \li Set \l{Indenting Text or Code}{tabs, indentation, the handling of + \li Set \l{Indent text or code}{tabs, indentation, the handling of whitespace, and mouse operations} in \uicontrol Behavior. - \li Set various display properties, such as - \l{Highlighting and folding blocks} - {highlighting and folding blocks} or text + \li Set various display properties, such as \l{Highlighting Blocks} + {highlighting} or \l{Folding Blocks}{folding} blocks or text wrapping in \uicontrol Display. \li Add, modify, and remove \l{Editing Code Snippets}{code snippets} in diff --git a/doc/qtcreator/src/editors/creator-editors-writing-code.qdoc b/doc/qtcreator/src/editors/creator-editors-writing-code.qdoc index 133f0b7686d..78f9fbd84a5 100644 --- a/doc/qtcreator/src/editors/creator-editors-writing-code.qdoc +++ b/doc/qtcreator/src/editors/creator-editors-writing-code.qdoc @@ -51,7 +51,7 @@ \QC anticipates what you are going to write and completes code and code snippets for elements, properties, and IDs. - \li \l{Indenting Text or Code} + \li \l{Indent text or code} \QC indents text and code according to rules that you specify separately for files that have C++, QML, or diff --git a/doc/qtcreator/src/editors/creator-locator.qdoc b/doc/qtcreator/src/editors/creator-locator.qdoc index c7add7c980f..44cdc361bc7 100644 --- a/doc/qtcreator/src/editors/creator-locator.qdoc +++ b/doc/qtcreator/src/editors/creator-locator.qdoc @@ -104,10 +104,22 @@ or use the handle next to the locator window to increase the window width. \if defined(qtcreator) - If the locator does not find some files, see \l{Specifying Project Contents} + If the locator does not find some files, see \l{Specify project contents} for how to make them known to the locator. \endif + \section2 Locating Files from Global File System Index + + You can install tools such as Spotlight, Locate, or Everything and use them + to locate files from a global file system index. Use the \c md locator + filter to locate the files. + + To use the sorting from the selected tool instead of from \QC, + deselect the \uicontrol {Sort results} check box in the \c md + locator filter configuration. + + \image qtcreator-locator-filter-edit-md.webp {Filter Configuration dialog} + \section2 Locating Lines and Columns To move directly to a particular line and column in the document when you @@ -183,6 +195,8 @@ example, you can change the filter prefix and restrict the search to items that match the filter. + \image qtcreator-locator-filter-edit-ai.webp {Filter Configuration dialog} + To configure a locator filter: \list 1 @@ -198,8 +212,7 @@ \li To implicitly include the filter even when not typing a prefix as a part of the search string, select \uicontrol {Include by default}. - \li Set other available preferences. For more information, see - \l{Adding Web Search Engines}. + \li Set other available preferences. \endlist @@ -218,8 +231,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol Locator > + \li Select \preferences > \uicontrol Environment > \uicontrol Locator > \uicontrol {Web Search} > \uicontrol Edit. \li Select \uicontrol Add to add a new entry to the list. @@ -294,8 +306,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol Locator. + \li Select \preferences > \uicontrol Environment > \uicontrol Locator. \li In \uicontrol {Refresh interval}, define new time in minutes. diff --git a/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc b/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc index 8d310917373..84eef9dcc0f 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-beautifier.qdoc @@ -32,6 +32,12 @@ specified in the Beautifier preferences. You can use a predefined style or define your own style. + \note Enable the Beautifier plugin to use it. Since \QC 10.0.0, the + ClangFormat plugin is enabled by default. Select \preferences > + \uicontrol {C++} > \uicontrol {Formatting mode} > \uicontrol Disable + to turn off ClangFormat if you enable Beautifier because combining + them can lead to unexpected results. + To use the Beautifier plugin: \list 1 @@ -48,18 +54,7 @@ You might have to build the tools from sources for some platforms. - \li Select \uicontrol Help > \uicontrol {About Plugins} > \uicontrol {C++} > - \uicontrol Beautifier to enable the plugin. - - \note Since \QC 10.0.0, the ClangFormat plugin is enabled by - default. Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {C++} > \uicontrol {Formatting mode} > \uicontrol Disable - to turn off ClangFormat if you enable Beautifier because combining - them can lead to unexpected results. - - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol Beautifier to specify settings for beautifying files. \li Select the \uicontrol {Enable auto format on file save} check box to @@ -105,7 +100,7 @@ \li Select \uicontrol {Use file *.astylerc defined in project files} or \uicontrol {Use file uncrustify.cfg defined in project files}, - to use the configuration file \l{Specifying Project Contents} + to use the configuration file \l{Specify project contents} {defined in the project file} as the configuration file for the selected tool. @@ -148,7 +143,7 @@ \endlist - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Beautifier + \li Select \preferences > \uicontrol Beautifier > \uicontrol {Artistic Style}, \uicontrol ClangFormat, or \uicontrol Uncrustify > \uicontrol {Format Current File} to format the currently open file. @@ -168,4 +163,6 @@ select it when no text is selected, the whole file is formatted by default. To disable this behavior, deselect the \uicontrol {Format entire file if no text was selected} check box. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc b/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc index f9ce09b9551..0edfdcc22c7 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-clang-codemodel.qdoc @@ -33,7 +33,7 @@ \li Diagnostics - \li \l{Viewing Function Tooltips}{Tooltips} + \li \l{View function tooltips}{Tooltips} \li \l{Finding Symbols}{Finding and renaming symbols} @@ -82,7 +82,7 @@ \endlist - To use the built-in code model instead, select \uicontrol Edit > \uicontrol Preferences > + To use the built-in code model instead, select \preferences > \uicontrol C++ > \uicontrol clangd, and deselect the \uicontrol {Use clangd} check box. This setting also exists on the project level, so that you can have the clang-based services generally enabled, but switch them off for certain projects, or vice versa. @@ -100,7 +100,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol C++ > + \li Select \preferences > \uicontrol C++ > \uicontrol {Code Model}. \image qtcreator-preferences-code-model.webp {C++ Code Model preferences} @@ -136,7 +136,7 @@ The clangd \e index provides exact and complete results for services such as finding references, following symbols under cursor, and using the - locator, even for complex constructs. When you \l{Opening Projects} + locator, even for complex constructs. When you \l{Open projects} {open a project}, clangd scans the source files to generate the index. For large projects, this can take a while, but the index is persistent and re-scanning is incremental, so nothing is lost by closing and re-starting @@ -159,9 +159,9 @@ To specify settings for clangd: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol C++ > + \li Select \preferences > \uicontrol C++ > \uicontrol Clangd > \uicontrol {Use clangd}. - \image qtcreator-options-clangd.png "clangd preferences" + \image qtcreator-preferences-clangd.webp "Clangd preferences" \li In \uicontrol {Path to executable}, enter the path to clangd version 14, or later. \li In the \uicontrol {Background indexing} field, select \uicontrol Off @@ -169,15 +169,25 @@ by default. You can set the indexing priority depending on whether the accuracy of results or speed is more important to you during global symbol searches. + \li In \uicontrol {Header/source switch mode}, select the C/C++ + backend for switching between header and source files. While the + clangd implementation has more capabilities than the built-in + code model, it tends to find false positives. \uicontrol {Try Both} + uses clangd if the built-in code model does not find anything. \li By default, clangd attempts to use all unused cores. You can set a fixed number of cores to use in \uicontrol {Worker thread count}. Background indexing also uses this many worker threads. - \li Select \uicontrol {Insert header files on completion} to allow - clangd to insert header files as part of symbol completion. \li Set the number of \uicontrol {Completion results} if you regularly miss important results during code completion. Set it to 0 to remove the limit on the number of completion results. Setting this to 0 or a very high number can make code completion slow. + \li In \uicontrol {Completion ranking model}, select the clangd model to + use for ranking completion suggestions. This determines their order + in the selection list. The \uicontrol {Decision Forest} model + (\uicontrol Default) results from pre-trained machine learning + and usually provides better results than the hand-crafted + \uicontrol Heuristic model. Select the latter if the completion + suggestions stray too much from your expectations for your code base. \li In \uicontrol {Document update threshold}, specify the amount of time \QC waits before sending document changes to the server. If the document changes again while waiting, this timeout is reset. diff --git a/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc b/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc index e12c5e29fdf..7e77b016b4c 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-code-pasting.qdoc @@ -23,8 +23,7 @@ To specify settings for the code pasting service: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Code Pasting}. + \li Select \preferences > \uicontrol {Code Pasting}. \image qtcreator-code-pasting-options.png "Code Pasting preferences" \li In the \uicontrol {Default protocol} field, select a code pasting service to use by default. diff --git a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc index c17613f38f5..6a649e96db7 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc @@ -17,8 +17,8 @@ \image qtcreator-editortoolbar-symbols.webp {Edit mode toolbar} - To add more space around the toolbar items, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Environment > \uicontrol Interface, and + To add more space around the toolbar items, select \preferences > + \uicontrol Environment > \uicontrol Interface, and then select \uicontrol Relaxed in the \uicontrol {Toolbar style} field. \image qtcreator-preferences-environment-interface.webp {Interface tab in Environment preferences} @@ -56,6 +56,11 @@ \l{Searching with the Locator}{locator}. Enter the line number and column number in the locator, separated by a colon (:). + The line and column indicator shows more information about the current + cursor positions, including the length of the selection for selected text. + + \image qtcreator-editor-line-column.webp {Line and column indicator on the Edit mode toolbar} + \note You can also use the \l{Show and hide sidebars}{sidebars} to move around in \QC. @@ -76,7 +81,7 @@ \section2 Changing Text Encoding To show the file encoding of the current file on the editor toolbar (5), - select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + select \preferences > \uicontrol {Text Editor} > \uicontrol Display > \uicontrol {Display file encoding}. To change the text encoding, click it on the toolbar and select new @@ -92,18 +97,18 @@ To switch between Windows line endings (CRLF) and Unix line endings (LF), select the ending style on the editor toolbar (6). To hide this field, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} + select \preferences > \uicontrol {Text Editor} > \uicontrol Display, and deselect \uicontrol {Display file line ending}. To set the line endings to use for all projects by default, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + \preferences > \uicontrol {Text Editor} > \uicontrol Behavior, and then select the ending style in the \uicontrol {Default line endings} field. To set the line endings to use for a project, select \uicontrol Projects > \uicontrol {Project Settings} > \uicontrol Editor. - For more information, see \l {Specifying Editor Settings}. + For more information, see \l {Specify editor settings}. \section2 Managing Language Servers @@ -264,9 +269,8 @@ splits opened, you can open the link in the next split by holding \key Ctrl and \key Alt while clicking the symbol. - To enable this moving function, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior > - \uicontrol {Enable mouse navigation}. + To enable this moving function, select \preferences > \uicontrol {Text Editor} + > \uicontrol Behavior > \uicontrol {Enable mouse navigation}. There are several additional ways of moving between symbol definitions and declarations. All the functions described below are also available from the @@ -302,8 +306,8 @@ split, prepend \key {Ctrl+E} to the shortcut. For example, press \key {Ctrl+E,F2} to follow the symbol in the next split. If necessary, the view is automatically split. To change the default behavior, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Display > \uicontrol {Always open links in another split}. + \preferences > \uicontrol {Text Editor} > \uicontrol Display > + \uicontrol {Always open links in another split}. Additional symbols are displayed and switching between definition and declaration is done in another split. If you change the default behavior, the shortcuts for opening @@ -338,4 +342,6 @@ \QC underlines semantic errors in olive in the C++ code editor. To check the correct paths for includes that are not resolved or that are resolved to the wrong file, select \uicontrol {Project Parts} > \uicontrol {Header Paths}. + + \sa {Detach views} */ diff --git a/doc/qtcreator/src/editors/creator-only/creator-compilation-database.qdocinc b/doc/qtcreator/src/editors/creator-only/creator-compilation-database.qdocinc index 8b779607368..d7e74fc9128 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-compilation-database.qdocinc +++ b/doc/qtcreator/src/editors/creator-only/creator-compilation-database.qdocinc @@ -25,6 +25,8 @@ the files in a compilation database with access to all the editing features of the Clang code model. + \note Enable the Compilation Database Project Manager plugin to use it. + To switch between header and source files, select \uicontrol Tools > \uicontrol C++ > \uicontrol {Switch Header/Source}. @@ -32,9 +34,7 @@ database projects in the \uicontrol Projects mode. For more information, see \l{Adding Custom Build Steps} and \l {Specifying Run Settings}. - To enable the plugin, select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Build Systems} > \uicontrol {Compilation Database Project Manager}. - Then select \uicontrol {Restart Now} to restart \QC and load the plugin. + \sa {Enable and disable plugins} //! [using compilation databases] */ diff --git a/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc b/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc index 4c3d1d220dc..6717a526199 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc @@ -4,7 +4,7 @@ /*! \previouspage creator-language-servers.html \page creator-copilot.html - \nextpage creator-python-development.html + \nextpage creator-mime-types.html \title Using GitHub Copilot @@ -26,19 +26,9 @@ \list \li An active \l{https://docs.github.com/en/billing/managing-billing-for-github-copilot/about-billing-for-github-copilot} {GitHub Copilot subscription}. - \li GitHub Copilot Neovim plugin installed, as described in - \l{https://docs.github.com/en/copilot/getting-started-with-github-copilot?tool=neovim} - {About GitHub Copilot and Neovim}. - \endlist - - \section1 Enabling the Copilot Plugin - - To enable the Copilot plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol Copilot to enable the plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. + \li GitHub Copilot Neovim plugin installed (requires Node.js), as described in + \l{https://github.com/github/copilot.vim/blob/release/README.md} + {Copilot.vim/Readme.md}. \endlist \section1 Setting Copilot Preferences @@ -46,8 +36,7 @@ To set preferences for using Copilot: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Copilot. + \li Select \preferences > \uicontrol Copilot. \image qtcreator-preferences-copilot.webp {Copilot tab in Preferences} \li Select the \uicontrol {Enable Copilot} check box to use Copilot. \li Select \uicontrol {Sign In} to sign into your subscription, activate @@ -57,17 +46,33 @@ \li In the \uicontrol {Node.js path} field, enter the full path to the Node.js executable. \li In the \uicontrol {Path to agent.js} field, enter the path to - agent.js in the Copilot Neovim plugin. - \li Select the \uicontrol {Request completions automatically} checkbox to - receive suggestions for the current text cursor position when you - make changes. + agent.js in the Copilot Neovim plugin installation folder. + \li Select the \uicontrol {Auto request} check box to receive suggestions + for the current text cursor position when you make changes. + \li Select the \uicontrol {Use proxy} check box to use a proxy server to + connect to Copilot servers. + \li In the \uicontrol {Proxy host} field, enter the host name of the + proxy server. + \li In the \uicontrol {Proxy port} field, enter the port number of the + proxy server. + \li Select the \uicontrol {Reject unauthorized} check box to prevent the + security risk presented by accepting unauthorized certificates from + the proxy server. + \li In the \uicontrol {Proxy user} field, enter the user name to + authenticate to the proxy server. + \li Select the \uicontrol {Save proxy password} check box to save the + password to authenticate to the proxy server. + \note The password is saved insecurely. + \li In the \uicontrol {Proxy password} field, enter the password to save. + To see the password as you type, select the \inlineimage icons/original-size.png + button. \endlist \section1 Receiving Suggestions When you write code in the \l {Working in Edit Mode}{Edit} mode and - \uicontrol {Request completions automatically} is enabled, Copilot - automatically makes suggestions when you type. + \uicontrol {Auto request} is enabled, Copilot automatically makes + suggestions when you type. \image qtcreator-copilot.gif {Receiving suggestions from Copilot in the editor} @@ -95,11 +100,12 @@ To enable or disable Copilot suggestions globally, select the \inlineimage icons/copilot.png (\uicontrol {Toggle Copilot}) button. This also sets the value of the - \uicontrol {Enable Copilot} check box in \uicontrol Edit > - \uicontrol Preferences accordingly. + \uicontrol {Enable Copilot} check box in \preferences accordingly. To enable or disable Copilot suggestions for a particular project, select \uicontrol Projects > \uicontrol {Project Settings} > \uicontrol Copilot, and then select or deselect the \uicontrol {Enable Copilot} check box. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc index dba4740fd69..59c613a8fbe 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-fakevim.qdoc @@ -66,7 +66,7 @@ \li Wrap line movement, such as \c gj, \c gk, \c g0, \c g^, \c g$ \endlist - \section2 Command Line Mode + \section2 Command-Line Mode \list \li \c :map, \c :unmap, \c :inoremap, and so on @@ -93,7 +93,7 @@ \section2 Plugin Emulation FakeVim also emulates some popular vim plugins. To enable plugin emulation - for particular plugins, select \uicontrol Edit > \uicontrol Preferences > + for particular plugins, select \preferences > \uicontrol FakeVim > \uicontrol General > \uicontrol {Plugin Emulation}. \image qtcreator-fakevim-options-general-plugin-emulation.png "FakeVim Plugin Emulation preferences" @@ -269,8 +269,8 @@ \section1 Mapping FakeVim Commands To map commands entered on the \uicontrol FakeVim command line to - \QC functions, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol FakeVim > \uicontrol {Ex Command Mapping}. + \QC functions, select \preferences > \uicontrol FakeVim > + \uicontrol {Ex Command Mapping}. Enter a string in the \uicontrol Filter field to search for a specific \QC function. @@ -284,17 +284,17 @@ To reset the trigger expressions for all functions, select \uicontrol {Reset All}. - To map \e {user commands} to keyboard shortcuts, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol FakeVim > - \uicontrol {User Command Mapping}. The user command mapped to the shortcut - is executed by FakeVim as if you were typing it (as when replaying a macro). + To map \e {user commands} to keyboard shortcuts, select \preferences > + \uicontrol FakeVim > \uicontrol {User Command Mapping}. The user command + mapped to the shortcut is executed by FakeVim as if you were typing it + (as when replaying a macro). \image qtcreator-fakevim-options-user-command-mapping.png "FakeVim User Command Mapping preferences" \section1 Setting FakeVim Preferences - To make changes to the Vim-style settings, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol FakeVim > \uicontrol General. + To make changes to the Vim-style settings, select \preferences > + \uicontrol FakeVim > \uicontrol General. \image qtcreator-fakevim-options.png "FakeVim preferences" @@ -304,13 +304,13 @@ select \uicontrol {Set Plain Style}. You can then change any of the preselected settings. - To use a Vim-style color scheme, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol {Fonts & Color}. + To use a Vim-style color scheme, select \preferences > + \uicontrol {Text Editor} > \uicontrol {Fonts & Color}. In the \uicontrol {Color Scheme} list, select \uicontrol {Vim (dark)}. \section1 Quitting FakeVim Mode - To quit the FakeVim mode, deselect \uicontrol Edit > \uicontrol Preferences > + To quit the FakeVim mode, deselect \preferences > \uicontrol FakeVim > \uicontrol {Use FakeVim} or press \key {Alt+V,Alt+V}. You can temporarily escape FakeVim mode to access the normal \QC keyboard diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc index 33a14ee3be2..8ebbaa74d4b 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc @@ -18,10 +18,9 @@ \li \l{Completing Code}{Code completion} \li Sending document formatting requests to the language server to automatically format documents using the settings specified in - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} - > \uicontrol Behavior + \preferences > \uicontrol {Text Editor} > \uicontrol Behavior \li Highlighting the symbol under cursor - \li \l{Viewing Function Tooltips}{Viewing function tooltips} + \li \l{View function tooltips} \li \l{Semantic Highlighting}{Semantic highlighting}, as defined in \l{https://github.com/microsoft/vscode-languageserver-node/pull/367} {Proposal of the semantic highlighting protocol extension} @@ -62,10 +61,8 @@ server is added by default and you can edit its preferences. For other languages, you can add generic stdIO language servers. - To add language servers, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Language Client} > \uicontrol Add (or \uicontrol {Qt Creator} > - \uicontrol Preferences > \uicontrol {Language Client} > \uicontrol Add - on \macos). + To add language servers, select \preferences > + \uicontrol {Language Client} > \uicontrol Add. To enable a language server, select the check box next to the language server name and set server preferences. @@ -77,9 +74,8 @@ To add a generic language server: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Language Client} > \uicontrol Add > - \uicontrol {Generic StdIO Language Server} + \li Select \preferences > \uicontrol {Language Client} > + \uicontrol Add > \uicontrol {Generic StdIO Language Server} to add a generic language server. \image qtcreator-language-server-generic-stdio.png \li In the \uicontrol Name field, enter a name for the language server. @@ -100,7 +96,7 @@ request. \li In the \uicontrol Executable field, enter the path to the language server executable. - \li In the \uicontrol Arguments field, enter any required command line + \li In the \uicontrol Arguments field, enter any required command-line arguments. Select \uicontrol Variables to use variables as arguments. \endlist @@ -110,9 +106,9 @@ To add a Java language server: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Language Client} > \uicontrol Add > - \uicontrol {Java Language Server} to add a Java language server. + \li Select \preferences > \uicontrol {Language Client} > + \uicontrol Add > \uicontrol {Java Language Server} to add + a Java language server. \image qtcreator-language-client-options-java.png "Java language server preferences" \li In the \uicontrol Name field, enter a name for the language server. Select the \inlineimage icons/replace.png @@ -130,8 +126,8 @@ To set preferences for Python language servers: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Python > \uicontrol {Language Server Configuration} to + \li Select \preferences> \uicontrol Python > + \uicontrol {Language Server Configuration} to select the Python language server plugins to use. \image qtcreator-python-plugins.png "Python Language Server Configuration" \li Select \uicontrol Advanced to configure the plugins. @@ -147,15 +143,22 @@ \section2 QML Language Server - Since Qt 6.4, the \c qmlls language server offers code completion and - issues warnings for QML. To enable QML language server support, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick} > - \uicontrol {QML/JS Editing} > \uicontrol {Use qmlls (EXPERIMENTAL!)}. + Since Qt 6.4, the QML language server offers code completion and + issues warnings for QML. To use it, select \preferences > + \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} > + \uicontrol {Enable QML Language Server}. + + To use advanced features, such as renaming and finding usages, select the + \uicontrol {Use QML Language Server advanced features} check box. + To use the latest version of the language server installed on your - system, select the \uicontrol {Always use latest qmlls} check box. + system, select the + \uicontrol {Use QML Language Server from latest Qt version} check box. \image qtcreator-qml-js-editing.webp {QML/JS Editing preferences} + \sa {Enabling and Disabling Messages} + \section1 Supported Locator Filters The locator enables you to browse not only files, but any items defined by diff --git a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc index 9a3d2444e61..95b31c2fd15 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-markdown-editor.qdoc @@ -19,4 +19,10 @@ To hide and show the views, select \uicontrol {Show Preview} and \uicontrol {Show Editor}. To swap the places of the views, select \uicontrol {Swap Views}. + + \section1 Formatting Text + + Use the buttons on the editor toolbar to format text as italic (i), bold (b), + or inline code (`), and to create links to web sites + (\inlineimage icons/linkicon.png). */ diff --git a/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc b/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc index 17fb6d32768..4e66db714c1 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc @@ -8,7 +8,7 @@ // ********************************************************************** /*! - \previouspage creator-python-development.html + \previouspage creator-copilot.html \page creator-mime-types.html \nextpage creator-modeling.html @@ -46,7 +46,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol Environment > \uicontrol {MIME Types}. \image qtcreator-mime-types.png "MIME Types" diff --git a/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc b/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc new file mode 100644 index 00000000000..44622426af8 --- /dev/null +++ b/doc/qtcreator/src/editors/creator-only/creator-preferences-cpp-code-style.qdoc @@ -0,0 +1,128 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-preferences-cpp-code-style.html + \previouspage creator-reference.html + + \ingroup creator-reference-preferences-cpp + + \title C++ Code Style + + \brief Set global code style for C++ files. + + \QC uses the Clang \l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat} + library to automatically format and indent C++ code. It enforces a coding + style for a project or the whole organization. + + To specify global indentation settings for the C++ editor: + + \list 1 + \li Select \preferences > \uicontrol {C++}. + \image qtcreator-code-style-clang-format-global.webp {Code Style preferences} + \li In \uicontrol {Formatting mode}, select: + \list + \li \uicontrol {Indenting Only} to only indent code. + \li \uicontrol {Full Formatting} to use the \key {Ctrl+I} + keyboard shortcut to format code instead of indenting. + \li \uicontrol Disable to turn off ClangFormat. + \endlist + \li To apply the formatting while you type, select + \uicontrol {Format while typing}. + \li To apply the formatting to the edited code when you save the file, + select \uicontrol {Format edited code on file save}. + \li To change the ClangFormat style globally for all projects, + select \uicontrol {Override ClangFormat configuration file}. + \li In the \uicontrol {Current settings} field, select the settings to + modify and click \uicontrol Copy. + \li Give a name to the settings and click \uicontrol OK. + \li Click \uicontrol Edit to set + \l{https://clang.llvm.org/docs/ClangFormatStyleOptions.html} + {ClangFormat Style Options}. + \endlist + + In the other tabs, you can specify how to: + + \list + \li Interpret the \key Tab and \key Backspace key presses. + \li Indent the contents of classes, functions, blocks, and namespaces. + \li Indent braces in classes, namespaces, enums, functions, and blocks. + \li Control switch statements and their contents. + \li Align continuation lines. + \li Bind pointers (*) and references (&) in types and declarations to + identifiers, type names, or left or right \c const or \c volatile + keywords. + \li Name getter functions. + \endlist + + Use the live preview to see how the preferences change the indentation. + + \section1 Specifying Settings for Content + + You can indent public, protected, and private statements and declarations + related to them within classes. + + You can also indent statements within functions and blocks and declarations + within namespaces. + + \image qtcreator-code-style-content.png {Content preferences} + + \section1 Specifying Settings for Braces + + You can indent class, namespace, enum and function declarations and code + blocks. + + \image qtcreator-code-style-braces.png {Braces preferences} + + \section1 Specifying Settings for Switch Statements + + You can indent case or default statements, or statements or blocks related + to them within switch statements. + + \image qtcreator-code-style-switch.png {Switch preferences} + + \section1 Specifying Alignment + + To align continuation lines to tokens after assignments, such as \c = or + \c +=, select the \uicontrol {Align after assignments} check box. You can + specify additional settings for aligning continuation lines in the + \uicontrol General tab. + + You can also add spaces to conditional statements, so that they are not + aligned with the following line. Usually, this only affects \c if + statements. + + \image qtcreator-code-style-alignment.png {Alignment preferences} + + \section1 Binding Pointers and References + + To bind pointers (\c *) and references (\c &) in types and declarations to + identifiers, type names, or left or right \c const or \c volatile keywords, + select the check boxes in the \uicontrol {Pointers and References} tab. + + The \c * and \c & characters are automatically bound to identifiers of + pointers to functions and pointers to arrays. + + \image qtcreator-pointers-references.png {Pointers and References preferences} + + \section1 Creating Project-Specific ClangFormat Files + + To override the \c {.clang-format} file for a particular project, create a + copy of the built-in style and edit its settings by selecting + \uicontrol Projects > \uicontrol {Project Settings} > + \uicontrol {Code Style} > \uicontrol Copy > \uicontrol Edit > + \uicontrol {ClangFormat} > + \uicontrol {Override ClangFormat configuration file}. + + \section1 Creating ClangFormat Files from Command Line + + You can create \c {.clang-format} files that have the configuration + options of a certain predefined style from the command line. For example, + to create a format file for the LLVM style, enter the following command: + + \badcode + clang-format -style=llvm -dump-config > .clang-format + \endcode + + \sa {Indent text or code}, {Behavior}, {Qt Quick Code Style}, {Nim} +*/ diff --git a/doc/qtcreator/src/editors/creator-only/creator-preferences-nim.qdoc b/doc/qtcreator/src/editors/creator-only/creator-preferences-nim.qdoc new file mode 100644 index 00000000000..a72320b71fe --- /dev/null +++ b/doc/qtcreator/src/editors/creator-only/creator-preferences-nim.qdoc @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-preferences-nim.html + \previouspage creator-reference.html + + \ingroup creator-reference-preferences + + \title Nim + + \brief Set Nim code style. + + To specify settings for the Nim editor (experimental): + + \list 1 + \li Select \preferences > \uicontrol Nim. + \li In the \uicontrol {Current settings} field, select the settings to + modify and click \uicontrol Copy. + \image qtcreator-options-code-style-nim.png {Nim Code Style preferences} + \li Give a name to the settings and click \uicontrol OK. + \li Click \uicontrol Edit to specify code style settings for the project. + \image qtcreator-code-style-settings-edit-nim.png {Edit Code Style dialog} + \endlist + + You can specify how to interpret the \key Tab key presses and how to align + continuation lines. + + To specify different settings for a particular project, select + \uicontrol Projects > \uicontrol {Code Style}. + + \sa {Find preferences}, {Indent text or code}, + {Specify code style}, {Setting Up Nimble} +*/ diff --git a/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc b/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc index fbfbd3df294..93ac2b8de6e 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-scxml.qdoc @@ -22,12 +22,11 @@ \e event, but also on earlier events. With state charts, you can easily share this information. - \QC has a project wizard for adding \l{https://www.w3.org/TR/scxml/} - {State Chart XML (SCXML)} files with boilerplate code to projects and an - experimental SCXML editor for editing the state charts. You can use the - SCXML editor to add \e states and \e transitions to the files. You can then - use the classes in the Qt SCXML module to embed the state machines created - from the files in Qt applications. + With the \QC project wizard you can add \l{https://www.w3.org/TR/scxml/} + {State Chart XML (SCXML)} files with boilerplate code to projects. Edit + the state charts with the SCXML editor to add \e states and \e transitions + to the files. Then, use the classes in the Qt SCXML module to embed the + state machines created from the files in Qt applications. \image qtcreator-scxml-editor.png SXCML Editor @@ -83,11 +82,6 @@ \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Modeling > \uicontrol ScxmlEditor. - - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {Files and Classes} > \uicontrol Modeling > \uicontrol {State Chart} > \uicontrol Choose to create an empty diff --git a/doc/qtcreator/src/editors/creator-only/creator-text-editing-macros.qdoc b/doc/qtcreator/src/editors/creator-only/creator-text-editing-macros.qdoc index 6687e0241ae..1ea89d69d8f 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-text-editing-macros.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-text-editing-macros.qdoc @@ -24,12 +24,12 @@ \uicontrol {Text Editing Macros} > \uicontrol {Save Last Macro}. To assign a keyboard shortcut to a text editing macro, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > + \preferences > \uicontrol Environment > \uicontrol Keyboard. For more information, see \l{Assign keyboard shortcuts}. You can also use the \c rm locator filter to run a macro. For more information, see \l{Searching with the Locator}. - To view and remove saved macros, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Macros. + To view and remove saved macros, select \preferences > + \uicontrol {Text Editor} > \uicontrol Macros. */ diff --git a/doc/qtcreator/src/editors/creator-preferences-text-editor-behavior.qdoc b/doc/qtcreator/src/editors/creator-preferences-text-editor-behavior.qdoc new file mode 100644 index 00000000000..aeaf45def30 --- /dev/null +++ b/doc/qtcreator/src/editors/creator-preferences-text-editor-behavior.qdoc @@ -0,0 +1,96 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-preferences-text-editor-behavior.html + \if defined(qtdesignstudio) + \previouspage creator-indenting-code.html + \nextpage creator-preferences-qtquick-code-style.html + \else + \previouspage creator-reference.html + \endif + + \ingroup creator-reference-preferences-text-editor + + \title Behavior + + \brief Set preferences for the behavior of the text editor. + + To specify indentation settings for text files that do not have C++ or + QML code (such as Python code files), select \preferences > + \uicontrol {Text Editor} > \uicontrol Behavior. + + \image qtcreator-indentation.png {Text Editor Behavior preferences} + + You can specify how to interpret the \key Tab and \key Backspace key + presses and how to align continuation lines. + + \section1 Specifying Tab Settings + + You can specify tab settings at the following levels: + + \list + \if defined(qtcreator) + \li For all C++ files + \endif + \li For all QML files + \li For all other text files + \if defined(qtcreator) + \li For C++ files in a project + \endif + \li For QML files in a project + \li For other text files in a project + \endlist + + \section2 Specifying Tabs and Indentation + + You can specify tab policy and tab size in the + \uicontrol {Tabs and Indentation} group. In the \uicontrol {Tab policy} + field, select whether to use only spaces or only tabs for indentation, + or to use a mixture of them. + + By default, the tab length in code editor is 8 spaces and the indent size is + 4 spaces. You can specify the tab length and indent size separately for each + project and for different types of files. + + You can have continuation lines aligned with the previous line. In the + \uicontrol {Align continuation lines} field, select + \uicontrol {Not at all} to disable automatic alignment and indent + continuation lines to the logical depth. To always use spaces for alignment, + select \uicontrol {With Spaces}. To follow the \uicontrol {Tab policy}, + select \uicontrol {With Regular Indent}. + + \section1 Setting Typing Preferences + + When you type text or code, it is indented automatically according to the + selected text editor or code style preferences. To set typing preferences, + select \preferences > \uicontrol {Text Editor} > + \uicontrol Behavior > \uicontrol Typing. + + To disable automatic indentation, deselect the + \uicontrol {Enable automatic indentation} check box. + + You can specify how the indentation is decreased when you press + \uicontrol Backspace in the \uicontrol {Backspace indentation} field. To go + back one space at a time, select \uicontrol None. To decrease indentation + in leading white space by one level, select + \uicontrol {Follows Previous Indents}. To move back one tab length if the + character to the left of the cursor is a space, select + \uicontrol Unindents. + + You can specify whether the \key Tab key automatically indents text when you + press it. To automatically indent text, select \uicontrol Always in the + \uicontrol {Tab key performs auto-indent} field. To only indent text when + the cursor is located within leading white space, select \uicontrol {In + Leading White Space}. + + Your highlight definition file can have definitions for both multi and + single line comments. To apply the single line comment definition when + commenting out a selection, select \uicontrol {Prefer single line comments}. + + \if defined(qtcreator) + \sa {Find preferences}, {C++ Code Style}, {Nim} + \endif + + \sa {Indent text or code}, {Qt Quick Code Style} +*/ diff --git a/doc/qtcreator/src/editors/creator-quick-fixes.qdoc b/doc/qtcreator/src/editors/creator-quick-fixes.qdoc index 0f6e7fd58af..ed482dfcedc 100644 --- a/doc/qtcreator/src/editors/creator-quick-fixes.qdoc +++ b/doc/qtcreator/src/editors/creator-quick-fixes.qdoc @@ -35,7 +35,8 @@ virtual functions of base classes, create getter and setter functions, and generate constructors. You can specify settings for generating the functions either globally for all projects or separately for each project - in the \l{Specifying Settings}{build and run} settings of the project. + in the \l{Overriding Global Preferences}{build and run} settings of the + project. \section2 Implementing Member Functions @@ -82,8 +83,7 @@ You can specify settings for the refactoring actions either globally for all projects or separately for each project. To specify global options, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol C++ > - \uicontrol {Quick Fixes}. + select \preferences > \uicontrol C++ > \uicontrol {Quick Fixes}. To specify custom settings for a particular project, select \uicontrol Projects > \uicontrol {Project Settings} > @@ -727,7 +727,7 @@ By default, \QC uses the \c auto variable type when creating the variable. To label the variable with its actual type, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol C++ > + \preferences > \uicontrol C++ > \uicontrol {Quick Fixes}, and then deselect the \uicontrol {Use type "auto" when creating new variables} check box. @@ -828,6 +828,18 @@ \li Convert connect() to Qt 5 Style \li Converts a Qt 4 QObject::connect() to Qt 5 style. \li QObject::connect() (Qt 4 style) + \row + \li Convert Comment to C/C++ Style + \li Converts C-style comments into C++-style comments, and vice + versa. Tries to preserve \e pretty layout and takes Doxygen and + qdoc formatting into consideration, but you might need to clean + up the results. + \li Code comment + \row + \li Move Function Documentation to Declaration/Definition + \li Moves the documentation comment for a function between its + declaration and definition. + \li Documentation comment for a function \endtable \section2 Refactoring QML Code diff --git a/doc/qtcreator/src/editors/creator-search.qdoc b/doc/qtcreator/src/editors/creator-search.qdoc index 502a87367aa..a5fd8c9e15a 100644 --- a/doc/qtcreator/src/editors/creator-search.qdoc +++ b/doc/qtcreator/src/editors/creator-search.qdoc @@ -95,7 +95,7 @@ The locations of search hits, breakpoints, and bookmarks in your document are highlighted on the editor scroll bar. To turn highlighting off, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > + \preferences > \uicontrol {Text Editor} > \uicontrol Display > \uicontrol {Highlight search results on the scrollbar}. To search using more advanced options, select \uicontrol Advanced. @@ -122,7 +122,7 @@ \if defined(qtcreator) If you cannot find some files, see - \l{Specifying Project Contents} for how + \l{Specify project contents} for how to declare them as a part of the project. \endif @@ -219,7 +219,9 @@ \section1 Enabling Silver Searcher You can use Silver Searcher as a search engine in \QC if you install - Silver Searcher on the development PC and enable the experimental plugin. + Silver Searcher on the development PC. + + \note Enable the SilverSearcher plugin to use it. To use Silver Searcher: @@ -232,23 +234,17 @@ You might have to build Silver Searcher from sources for some platforms. - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Utilities} > \uicontrol {SilverSearcher} to enable the - plugin. - - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \li When searching, select \uicontrol {Silver Searcher} in the \uicontrol {Search engine} field. \li If Silver Searcher is not found, you might have installed it in a location that is not found via the \c{PATH} environment variable. - Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > - \uicontrol System (or \uicontrol {\QC} > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System on macOS), then select - \uicontrol Change in the \uicontrol Environment field and add an entry - \c{PATH=/path/to/bin:${PATH}}. + Select \preferences > \uicontrol Environment > \uicontrol System, + then select \uicontrol Change in the \uicontrol Environment field, + and add the entry \c{PATH=/path/to/bin:${PATH}}. \endlist + + \sa {Enable and disable plugins} \endif */ diff --git a/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc b/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc index 41e8c1ea961..23dfc01a4f0 100644 --- a/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc +++ b/doc/qtcreator/src/editors/creator-semantic-highlighting.qdoc @@ -24,8 +24,7 @@ \endlist To specify the color scheme to use for semantic highlighting, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol {Fonts & Color}. + \preferences > \uicontrol {Text Editor} > \uicontrol {Fonts & Color}. \QC supports syntax highlighting also for other types of files than C++, QML, or JavaScript. @@ -48,7 +47,7 @@ If more than one highlight definition is available for the file that you open for editing, the editor asks you to select the one to use. To save the selection, select \uicontrol {Remember My Choice}. To reset the - remembered definitions, select \uicontrol Edit > \uicontrol Preferences > + remembered definitions, select \preferences > \uicontrol {Text Editor} > \uicontrol {Generic Highlighter} > \uicontrol {Reset Remembered Definitions}. @@ -58,10 +57,9 @@ To view information about the downloaded files, open the \l{View output} {General Messages} view. - To suppress the message for a particular file pattern, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} - > \uicontrol {Generic Highlighter} and add the pattern to the - \uicontrol {Ignored file patterns} field. + To suppress the message for a particular file pattern, select \preferences > + \uicontrol {Text Editor} > \uicontrol {Generic Highlighter} and add the + pattern to the \uicontrol {Ignored file patterns} field. \image qtcreator-syntax-highlighter.png "Generic Highlighter preferences" @@ -71,7 +69,7 @@ apply the changes you make to the definition files, select \uicontrol {Reload Definitions}. - \section1 Highlighting and Folding Blocks + \section1 Highlighting Blocks Use block highlighting to visually separate parts of the code that belong together. For example, when you place the cursor within the braces, the code @@ -79,24 +77,36 @@ \image qtcreator-blockhighlighting.png - To enable block highlighting, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Display > - \uicontrol {Highlight blocks}. + To enable block highlighting, select \preferences > \uicontrol {Text Editor} + > \uicontrol Display > \uicontrol {Highlight blocks}. + + \section1 Folding Blocks Use the folding markers to collapse and expand blocks of code within braces. Click the folding marker to collapse or expand a block. In the figure above, the folding markers are located between the line number and the text pane. - To show the folding markers, select \uicontrol Edit > \uicontrol Preferences > + To show the folding markers, select \preferences > \uicontrol {Text Editor} > \uicontrol Display > \uicontrol {Display folding markers}. This option is enabled by default. \image qtcreator-options-text-editor-display.png "Text Editor Display preferences" - When the cursor is on a brace, the matching brace is animated by default. To - turn off the animation and just highlight the block and the braces, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Display and deselect \uicontrol {Animate matching parentheses}. + \section2 Folding All Comment Blocks + + To fold all comment blocks, select \uicontrol Tools > \uicontrol C++ > + \uicontrol {Fold All Comment Blocks}. To unfold all comment blocks, select + \uicontrol {Unfold All Comment Blocks}. + + \section1 Animating Matching Braces + + When the cursor is on a brace, the matching brace is animated by default. + + To turn off the animation and just highlight the block and the braces, select + \preferences > \uicontrol {Text Editor} > \uicontrol Display and deselect + \uicontrol {Animate matching parentheses}. + + \section1 Moving Between Blocks You can use keyboard shortcuts to move within and between blocks. To go to block end, press \key {Ctrl+]} and to go to block start, press @@ -104,11 +114,15 @@ or beginning of the block, press \key {Ctrl+Shift+]} and \key {Ctrl+Shift+[}, respectively. + \section1 Selecting the Current Block + To select the current block, press \key Ctrl+U. A second key press extends the selection to the parent block. To undo the last selection, press - \key {Ctrl+Alt+Shift+U}. To enable smart block selection, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Behavior > \uicontrol {Enable smart selection changing}. + \key {Ctrl+Alt+Shift+U}. + + To enable smart block selection, select \preferences + > \uicontrol {Text Editor} > \uicontrol Behavior > + \uicontrol {Enable smart selection changing}. \image qtcreator-options-text-editor-behavior.png "Text Editor Behavior preferences" */ diff --git a/doc/qtcreator/src/external-resources/external-resources.qdoc b/doc/qtcreator/src/external-resources/external-resources.qdoc index 5d21be4257c..e3295245df2 100644 --- a/doc/qtcreator/src/external-resources/external-resources.qdoc +++ b/doc/qtcreator/src/external-resources/external-resources.qdoc @@ -101,6 +101,18 @@ \externalpage https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html \title CMake: cmake-variables(7) */ +/*! + \externalpage https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html + \title CMake: cmake-packages(7) +*/ +/*! + \externalpage https://cmake.org/cmake/help/latest/command/add_executable.html + \title CMake: add_executable command +*/ +/*! + \externalpage https://cmake.org/cmake/help/latest/command/find_package.html + \title CMake: find_package command +*/ /*! \externalpage https://cmake.org/cmake/help/latest/command/install.html \title CMake: install command @@ -109,6 +121,10 @@ \externalpage https://cmake.org/cmake/help/latest/command/set_property.html \title CMake: set_property command */ +/*! + \externalpage https://cmake.org/cmake/help/latest/command/set_source_files_properties.html + \title CMake: set_source_files_properties command +*/ /*! \externalpage https://cmake.org/cmake/help/latest/command/target_compile_definitions.html \title CMake: target_compile_definitions command @@ -121,6 +137,10 @@ \externalpage https://cmake.org/cmake/help/latest/command/target_sources.html \title CMake: target_sources command */ +/*! + \externalpage https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#find-modules + \title CMake: Find Modules +*/ /*! \externalpage https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html \title CMake: CMAKE_OSX_DEPLOYMENT_TARGET @@ -129,6 +149,14 @@ \externalpage https://cmake.org/cmake/help/latest/prop_sf/HEADER_FILE_ONLY.html \title CMake: HEADER_FILE_ONLY */ +/*! + \externalpage https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html + \title CMake: CMAKE_EXPORT_COMPILE_COMMANDS +*/ +/*! + \externalpage https://cmake-format.readthedocs.io/en/latest/cmake-format.html + \title cmake-format +*/ /*! \externalpage https://microsoft.github.io/language-server-protocol/ \title Language Server Protocol diff --git a/doc/qtcreator/src/howto/creator-external-tools.qdoc b/doc/qtcreator/src/howto/creator-external-tools.qdoc index 8e003556cf8..53ba24a3f4b 100644 --- a/doc/qtcreator/src/howto/creator-external-tools.qdoc +++ b/doc/qtcreator/src/howto/creator-external-tools.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -13,11 +13,12 @@ \previouspage quick-converting-ui-projects.html \nextpage studio-on-mcus.html \else - \previouspage creator-keyboard-shortcuts.html - \nextpage creator-task-lists.html + \previouspage creator-how-tos.html \endif - \title Using External Tools + \ingroup creator-how-to-use + + \title Use external tools You can use external tools directly from \QC. Qt Linguist, QML preview tools, and the default text editor for your system are preconfigured @@ -27,62 +28,10 @@ To run the tools, select \uicontrol Tools > \uicontrol External, or use the \c x filter in the locator. - \section1 Using Qt Linguist - - When you \l{Creating Projects}{create a new project}, you can automatically - generate a translation source file (TS) for one language. You can add other - languages later by editing the project file. - - You can use the Qt Linguist release manager tools, lupdate and lrelease, - directly from \QC. The lupdate tool is used to synchronize source - code and translations. The lrelease tool is used to create run-time - translation files for use by the released application. - - \note Running the tools from \QC is supported only when using qmake as the - build system. For more information about using the tools with CMake, see - \l{https://doc.qt.io/qt-6/cmake-command-reference.html#qt6-linguisttools} - {Qt6::LinguistTools}. - - To synchronize TS files from a translator with the - application code, select \uicontrol Tools > \uicontrol External > - \uicontrol Linguist > \uicontrol {Update Translations (lupdate)}. - - To generate from the TS files Qt message (QM) files that can be used by an - application, select \uicontrol Tools > \uicontrol External > - \uicontrol Linguist > \uicontrol {Release Translations (lrelease)}. - - By default, the project .pro file is passed to the tools as an argument. To - specify other command line arguments for the tools, select \uicontrol Tools > - \uicontrol External > \uicontrol Configure. - - To open TS files in Qt Linguist, right-click a TS file in the - \uicontrol Projects or \uicontrol {File System} view and select - \uicontrol {Open With} > \uicontrol {Qt Linguist} in the context menu. - For more information about Qt Linguist, see \l{Qt Linguist Manual}. - - \section1 Running QML Files - - You can test the current QML document while you are developing an application. - - To run the currently active QML file, select \uicontrol Tools > - \uicontrol External > \uicontrol {Qt Quick} > \uicontrol {QML Runtime}. - - \section1 Using External Text Editors - - You can open files for editing in the default text editor for your system: - Notepad on Windows and vi on Linux and \macos. - To open the file you are currently viewing in an external editor, select - \uicontrol Tools > \uicontrol External > \uicontrol Text > - \uicontrol {Edit with Notepad} or \uicontrol {Edit with vi}, depending on - your system. - - \QC looks for the editor path in the PATH environment variable - of your operating system. - - \section1 Configuring External Tools + \section1 Configure external tools You can change the configuration of preconfigured tools and configure - additional tools in \QC \uicontrol Preferences. + additional tools in \QC \preferences. \image qtcreator-external-tools.png @@ -141,7 +90,7 @@ To globally change the system environment from the one in which - \QC is started, select \uicontrol Edit > \uicontrol Preferences > + \QC is started, select \preferences > \uicontrol Environment > \uicontrol System, and then select \uicontrol Change in the \uicontrol Environment field. \if defined(qtcreator) @@ -171,4 +120,102 @@ in Windows. To share a configuration with other users, copy an XML configuration file to the folder. + \sa {Run QML files}, {Use external text editors}, {Use Qt Linguist} +*/ + +/*! + \page creator-how-to-use-qtlinguist.html + \if defined(qtdesignstudio) + \previouspage creator-editor-external.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-use + + \title Use Qt Linguist + + When you \l{Creating Projects}{create a new project}, you can automatically + generate a translation source file (TS) for one language. You can add other + languages later by editing the project file. + + To open TS files in Qt Linguist, right-click a TS file in the + \uicontrol Projects or \uicontrol {File System} view and select + \uicontrol {Open With} > \uicontrol {Qt Linguist} in the context menu. + For more information about Qt Linguist, see \l{Qt Linguist Manual}. + + \section1 Use lupdate and lrelease + + You can use the Qt Linguist release manager tools, lupdate and lrelease, + directly from \QC. The lupdate tool synchronizes source + code and translations. The lrelease tool creates run-time + translation files for use by the released application. + + \note Running the tools from \QC is supported only when using qmake as the + build system. For more information about using the tools with CMake, see + \l{https://doc.qt.io/qt-6/cmake-command-reference.html#qt6-linguisttools} + {Qt6::LinguistTools}. + + By default, the project .pro file is passed to the tools as an argument. To + specify other command-line arguments for the tools, select \uicontrol Tools > + \uicontrol External > \uicontrol Configure. + + \section2 Synchronize TS files + + To synchronize TS files from a translator with the + application code, select \uicontrol Tools > \uicontrol External > + \uicontrol Linguist > \uicontrol {Update Translations (lupdate)}. + + \section2 Generate QM files + + To generate from the TS files Qt message (QM) files that can be used by an + application, select \uicontrol Tools > \uicontrol External > + \uicontrol Linguist > \uicontrol {Release Translations (lrelease)}. + + \sa {Use external tools} +*/ + +/*! + \page creator-how-to-run-qml-files.html + \if defined(qtdesignstudio) + \previouspage creator-editor-external.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-design + + \title Run QML files + + You can test the current QML document while you are developing an application. + + To run the currently active QML file, select \uicontrol Tools > + \uicontrol External > \uicontrol {Qt Quick} > \uicontrol {QML Runtime}. + + \sa {Use external tools} +*/ + +/*! + \page creator-how-to-use-external-text-editors.html + \if defined(qtdesignstudio) + \previouspage creator-editor-external.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-use + + \title Use external text editors + + You can open files for editing in the default text editor for your system: + Notepad on Windows and vi on Linux and \macos. + To open the file you are currently viewing in an external editor, select + \uicontrol Tools > \uicontrol External > \uicontrol Text > + \uicontrol {Edit with Notepad} or \uicontrol {Edit with vi}, depending on + your system. + + \QC looks for the editor path in the PATH environment variable + of your operating system. + + \sa {Use external tools} */ diff --git a/doc/qtcreator/src/howto/creator-help.qdoc b/doc/qtcreator/src/howto/creator-help.qdoc index 7bdea9afacf..434ee6fe9b2 100644 --- a/doc/qtcreator/src/howto/creator-help.qdoc +++ b/doc/qtcreator/src/howto/creator-help.qdoc @@ -1,24 +1,25 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \page creator-help.html + \page creator-how-to-get-help.html \if defined(qtdesignstudio) \previouspage studio-help.html \nextpage studio-faq.html \else - \previouspage creator-help-overview.html - \nextpage creator-faq.html + \previouspage creator-how-tos.html \endif - \title Using the Help Mode + \ingroup creator-how-to-get-help + + \title Get help \QC comes fully integrated with Qt documentation and examples using the Qt Help plugin. \list - \li To view documentation, switch to \uicontrol Help mode. + \li To view documentation, switch to the \uicontrol Help mode. \li To view context sensitive help on a Qt class or function as a tooltip, move the mouse cursor over the class or function. If help @@ -35,38 +36,46 @@ space, in the fullscreen \uicontrol Help mode. \li To select and configure how the documentation is displayed in the - \uicontrol Help mode, select \uicontrol Edit > \uicontrol Preferences > \uicontrol Help. + \uicontrol Help mode, select \preferences > \uicontrol Help. \endlist The following image displays the context sensitive help in the \uicontrol Edit mode. - \image qtcreator-context-sensitive-help.png "Context-sensitive help in Edit mode" + \image qtcreator-context-sensitive-help.png {Context-sensitive help in Edit mode} + + \section1 Change the font If the help HTML file does not use a style sheet, you can change the font - family, style, and size in \uicontrol Edit > \uicontrol Preferences > - \uicontrol Help > \uicontrol General. + family, style, and size in \preferences > \uicontrol Help > + \uicontrol General. - \image qtcreator-help-options.png "General tab in Help preferences" + \image qtcreator-preferences-help-general.webp {General tab in Help preferences} You can set the default zoom level in the \uicontrol Zoom field. When viewing help pages, you can use the mouse scroll wheel to zoom them. To disable this feature, deselect the \uicontrol {Enable scroll wheel zooming} check box. + To disable antialiasing, deselect the \uicontrol Antialiasing check box. + + \section1 Return to the editor + To switch to the editor context when you close the last help page, select the \uicontrol {Return to editor on closing the last page} check box. + \section1 Select help viewer backend + The help viewer backend determines the style sheet that is used to display the help files. The default help viewer backend that is based on litehtml is recommended for viewing Qt documentation. You can choose another help viewer backend in the \uicontrol {Viewer backend} field. To take the new backend to use, reload the help page. - \section1 Viewing Function Tooltips + \section1 View function tooltips - To hide function tooltips by default, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior > + To hide function tooltips by default, select \preferences > + \uicontrol {Text Editor} > \uicontrol Behavior > \uicontrol {Show help tooltips using the mouse} > \uicontrol {On Shift+Mouseover}. You can still view the tooltips by pressing and holding down the \key Shift key. @@ -74,18 +83,41 @@ To use a keyboard shortcut for viewing help tooltips, select \uicontrol {Show help tooltips using keyboard shortcut (Alt)}. - \section1 Finding Information in Qt Documentation + \sa {Find information in Qt documentation}, {Filter documentation}, + {Search from documentation} + + \sa {Add external documentation}, {Detach the help window}, + {Filter documentation}, {Find information in Qt documentation}, + {Select the help start page} +*/ + +/*! + \page creator-how-to-find-info-in-documentation.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Find information in Qt documentation \QC, \QSDK and other Qt deliverables have documentation as .qch files. All the documentation is accessible in the \uicontrol Help mode. By default, \QC registers only the latest available version of the documentation for each installed Qt module. To register all installed - documentation, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits > \uicontrol {Qt Versions} > - \uicontrol {Register documentation}. + documentation, select \preferences > \uicontrol Kits > + \uicontrol {Qt Versions} > \uicontrol {Register documentation}. - To find information in the documentation, select: + \image qtcreator-qt-versions.png {Register documentation field in Qt Versions tab in Kit Preferences} + + \section1 Help mode sidebar views + + \image qtcreator-sidebar-help-mode.webp {Sidebar views in the Help mode} + + To find information in the documentation in the \uicontrol Help mode, select: \list @@ -105,14 +137,27 @@ \endlist - \section2 Adding Bookmarks to Help Pages + \sa {Add bookmarks to help pages}, {Search from documentation} +*/ + +/*! + \page creator-how-to-add-bookmarks-to-help-pages.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Add bookmarks to help pages You can add bookmarks to useful help pages to easily find them later in the \uicontrol Bookmarks view. You can either use the page title as the bookmark or change it to any text. You can organize the bookmarks in folders in the view. - \image qtcreator-help-add-bookmark-dlg.png "Add Bookmark dialog" + \image qtcreator-help-add-bookmark-dlg.png {Add Bookmark dialog} To add a bookmark to an open help page: @@ -126,13 +171,29 @@ \endlist - To import and export bookmarks, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol Help > \uicontrol General > \uicontrol {Import Bookmarks} or - \uicontrol {Export Bookmarks}. + \section1 Import and export bookmarks - \section2 Full-text Search + To import and export bookmarks, select \preferences > + \uicontrol Help > \uicontrol General > \uicontrol {Import Bookmarks} or + \uicontrol {Export Bookmarks}. - In the \uicontrol Search pane, you can use full-text search for finding a + \sa {Find information in Qt documentation} +*/ + +/*! + \page creator-how-to-search-from-docs.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Search from documentation + + In the \uicontrol Help mode \uicontrol Search pane, you can use full-text + search for finding a particular word in all the installed documents. Enter the term you are looking for, and select the \uicontrol Search button. All documents that have the specified term are listed. The list is sorted by document @@ -140,7 +201,7 @@ the number of search hits that the documents have. Select a document in the list to open it. - \image qtcreator-help-search.png "Search pane" + \image qtcreator-help-search.png {Help mode Search pane} The following are examples of common search patterns: @@ -173,10 +234,23 @@ punctuation, such as domain names, use the asterisk as a wild card. For example, to find \c {Pastebin.Com}, enter the search term \c {Pastebin*}. - \section1 Adding External Documentation + \sa {Find information in Qt documentation} +*/ + +/*! + \page creator-how-to-add-external-documentation.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Add external documentation You can display external documentation in the \uicontrol Help mode. - To augment or replace the documentation that ships with \QC and Qt: + To add documentation or replace the documentation that ships with \QC and Qt: \list 1 @@ -185,52 +259,105 @@ For information on how to prepare your documentation and create a .qch file, see \l{The Qt Help Framework}. - \li To add the .qch file to \QC, select \uicontrol Edit > \uicontrol Preferences > + \li To add the .qch file to \QC, select \preferences > \uicontrol Help > \uicontrol Documentation > \uicontrol Add. + \image qtcreator-preferences-help-documentation.webp {Documentation tab in Help Preferences} + \endlist - \section1 Detaching the Help Window + \sa {Get help} +*/ + +/*! + \page creator-how-to-detach-help-window.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Detach the help window By default, context-sensitive help is opened in a window next to the code editor when you press \key F1. If there is not enough vertical space, the help opens in the full-screen help mode. - You can specify that the help always opens in full-screen mode or - is detached to an external window. Select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Help > \uicontrol General and specify - settings for displaying context-sensitive help + \image qtcreator-context-sensitive-help.png {Context-sensitive help in Edit mode} + + To specify that the help always opens in full-screen mode or + is detached to an external window, select \preferences > \uicontrol Help > + \uicontrol General. + + \image qtcreator-preferences-help-general.webp {General tab in Help preferences} + + Set preferences for displaying context-sensitive help in the \uicontrol {On context help} field. To detach the help window, select \uicontrol {Always Show in External Window}. To change this setting in a help view, select the \inlineimage icons/linkicon.png toolbar button. - \section1 Selecting the Start Page + \sa {Get help} +*/ + +/*! + \page creator-how-to-set-help-start-page.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Select the help start page You can select the page to display when you open the \uicontrol Help mode in the - \uicontrol Edit > \uicontrol Preferences > \uicontrol Help > \uicontrol General - > \uicontrol {On help start} field. - To display the page and help views that were open when you exited the mode, - select the \uicontrol {Show My Tabs from Last Session} option. However, Web pages - are not opened because loading them would slow down opening the \uicontrol Help - mode. + \preferences > \uicontrol Help > \uicontrol General > + \uicontrol {On help start} field. - To display a particular page, select \uicontrol {Show My Home Page}, and specify - the page in the \uicontrol {Home Page} field. + \image qtcreator-preferences-help-general.webp {General tab in Help preferences} - To display a blank page, select the \uicontrol {Show a Blank Page} option. You can - also select the \uicontrol {Use Blank Page} button to set a blank page as your - home page. + \list + \li To display the page and help views that were open when you exited the mode, + select the \uicontrol {Show My Tabs from Last Session} option. However, Web pages + are not opened because loading them would slow down opening the \uicontrol Help + mode. - \section1 Using Documentation Filters + \li To display a particular page, select \uicontrol {Show My Home Page}, and specify + the page in the \uicontrol {Home Page} field. + + \li To display a blank page, select the \uicontrol {Show a Blank Page} option. You can + also select the \uicontrol {Use Blank Page} button to set a blank page as your + home page. + \endlist + + \sa {Get help} +*/ + +/*! + \page creator-how-to-filter-documentation.html + \if defined(qtdesignstudio) + \previouspage creator-how-to-get-help.html + \else + \previouspage creator-how-tos.html + \endif + + \ingroup creator-how-to-get-help + + \title Filter documentation You can filter the documents displayed in the \uicontrol Help mode to find - relevant information faster. Select a filter from a list of filters (1). The + relevant information faster. Select a filter from a list of filters. The contents of the \uicontrol Index and \uicontrol Contents view in the sidebar change accordingly. - \image qtcreator-help-filters.png "Filters field on the Help mode toolbar" + \image qtcreator-help-filters.png {Filters field on the Help mode toolbar} + + \section1 Add filters You can define your own filters to display documentation for a set of Qt modules and versions. @@ -239,10 +366,9 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Help > - \uicontrol Filters. + \li Select \preferences > \uicontrol Help > \uicontrol Filters. - \image qtcreator-help-filter-attributes.png "Filters tab in Help preferences" + \image qtcreator-help-filter-attributes.png {Filters tab in Help preferences} \li Select \inlineimage icons/plus.png to add a new filter in the \uicontrol {Add Filter} dialog. @@ -264,11 +390,17 @@ \endlist + \section1 Change filters + To modify the selected filter, add and remove Qt modules and versions, and then select \uicontrol Apply. To rename the selected filter, select \uicontrol Rename. + \section1 Remove filters + To remove the selected filter select \inlineimage icons/minus.png . + + \sa {Get help} */ diff --git a/doc/qtcreator/src/howto/creator-how-to-find-preferences.qdoc b/doc/qtcreator/src/howto/creator-how-to-find-preferences.qdoc new file mode 100644 index 00000000000..b59e9709212 --- /dev/null +++ b/doc/qtcreator/src/howto/creator-how-to-find-preferences.qdoc @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-find-preferences.html + \if defined(qtcreator) + \previouspage creator-how-tos.html + \else + \previouspage creator-quick-tour.html + \endif + + \ingroup creator-how-to-ui + \ingroup studio-how-to + + \title Find preferences + + \QC uses standard names and locations on Linux, \macos, and Windows for + standard features, such as \e preferences. + + \table + \header + \li Linux and Windows + \li \macos + \row + \li \uicontrol Edit > \uicontrol Preferences + \li \uicontrol {\QC} > \uicontrol Preferences + \endtable + + \section1 Sort preference categories + + To sort the preference categories in alphabetic order, select the + \uicontrol {Sort categories} check box. + + \image qtcreator-preferences-sort.webp {Preference categories in alphabetic order} + + \section1 Filter preferences + + To find a particular preference, use the filter located at the top left of + the \uicontrol Preferences dialog. + + \image qtcreator-preferences.webp {Filtering preferences} + + \section1 Go to tabs in Preferences + + To go to a tab in the \uicontrol Preferences dialog from anywhere in \QC, + use the \c t \l{Searching with the Locator}{locator} filter. For example, + to open the \uicontrol Interface tab, enter \c {t preferences interface} + in the locator. + + \image qtcreator-locator-filter-t.webp {Using the locator to open a tab in Preferences} +*/ diff --git a/doc/qtcreator/src/howto/creator-how-to-macos.qdoc b/doc/qtcreator/src/howto/creator-how-to-macos.qdoc deleted file mode 100644 index 30e5815efd9..00000000000 --- a/doc/qtcreator/src/howto/creator-how-to-macos.qdoc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \page creator-how-to-macos.html - \if defined(qtcreator) - \previouspage creator-how-tos.html - \else - \previouspage creator-quick-tour.html - \endif - - \ingroup creator-how-to-ui - \ingroup studio-how-to - - \title Find menu items on \macos - - \QC uses standard names and locations for standard features, such as - \e preferences. In this manual, the names and locations on - Windows and Linux are usually used to keep the instructions short. Here are - some places to check if you cannot find a function, dialog, or keyboard - shortcut on \macos when following the instructions: - - \table - \header - \li For - \li Look In - \row - \li \uicontrol Edit > \uicontrol Preferences - \li \uicontrol {\QC} > \uicontrol Preferences - \row - \li \uicontrol Help > \uicontrol {About Plugins} - \li \uicontrol {\QC} > \uicontrol {About Plugins} - \row - \li Keyboard shortcuts - \li \uicontrol {\QC} > \uicontrol Preferences > \uicontrol Environment > - \uicontrol Keyboard - \endtable -*/ diff --git a/doc/qtcreator/src/howto/creator-how-to-set-high-dpi-scaling.qdoc b/doc/qtcreator/src/howto/creator-how-to-set-high-dpi-scaling.qdoc index 2beda1e8f5d..103565ef95d 100644 --- a/doc/qtcreator/src/howto/creator-how-to-set-high-dpi-scaling.qdoc +++ b/doc/qtcreator/src/howto/creator-how-to-set-high-dpi-scaling.qdoc @@ -15,27 +15,30 @@ \title Set high DPI scaling The operating systems that \QC supports implement high dots-per-inch (DPI) - scaling at varying levels. Therefore, \QC handles high DPI scaling - differently on different operating systems: + scaling at varying levels. Therefore, \QC handles \l{High DPI}{high DPI} + scaling differently on different operating systems: \list \li On \macos, \QC forces high DPI scaling, which means that it allows Qt to use the system scaling factor as the \QC scaling factor. - \li On Windows, if you do not set \l{High DPI} - {scaling environment variables}, \QC instructs Qt to detect the - scaling factor and use it as the \QC scaling factor. \li On Linux, \QC leaves it to the user to enable high DPI scaling because the process varies so much on different distributions and windowing systems that it cannot be reliably done automatically. \endlist - To override the default approach and always enable high DPI scaling: + Setting the scale factor or DPI to the exact physical display DPI may not + give good visual results due to the fractional scaling involved. + \l {Qt::HighDpiScaleFactorRoundingPolicy}{Rounding} the scale factor to 25% + increments can improve the results. + + To set the DPI rounding policy: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol Environment > \uicontrol Interface. \image qtcreator-preferences-environment-interface.webp {Interface tab in Environment preferences} - \li Select \uicontrol {Enable high DPI scaling}. + \li In \uicontrol {DPI rounding policy}, select an option to round + DPI up or down. \li Restart \QC to have the change take effect. \endlist */ diff --git a/doc/qtcreator/src/howto/creator-how-to-switch-ui-themes.qdoc b/doc/qtcreator/src/howto/creator-how-to-switch-ui-themes.qdoc index 69272441a38..41146ea2c62 100644 --- a/doc/qtcreator/src/howto/creator-how-to-switch-ui-themes.qdoc +++ b/doc/qtcreator/src/howto/creator-how-to-switch-ui-themes.qdoc @@ -20,7 +20,7 @@ To switch themes: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol Environment > \uicontrol Interface. \image qtcreator-preferences-environment-interface.webp {Interface preferences} \li In \uicontrol Theme, select a theme. diff --git a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc index 878a0f6cca6..bbc5942cae4 100644 --- a/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc +++ b/doc/qtcreator/src/howto/creator-keyboard-shortcuts.qdoc @@ -13,18 +13,18 @@ \previouspage creator-project-managing-sessions.html \nextpage studio-projects.html \else - \previouspage creator-cli.html - \nextpage creator-editor-external.html + \previouspage creator-reference.html \endif \ingroup creator-reference \title Keyboard Shortcuts + \brief Default keyboard shortcuts. + \QC has various keyboard shortcuts that speed up your development process. To view all \QC functions in and their keyboard shortcuts, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > - \uicontrol Keyboard. + \preferences > \uicontrol Environment > \uicontrol Keyboard. \image qtcreator-keyboard-shortcuts.png @@ -46,7 +46,7 @@ To override the platform default value that determines whether keyboard shortcuts are shown in the labels of context menu items, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > + select \preferences > \uicontrol Environment > \uicontrol Interface. The label of the \uicontrol {Show keyboard shortcuts in context menus} check box indicates whether the platform default value is \c on or \c off. @@ -217,8 +217,8 @@ By default, \QC exits without asking for confirmation, unless there are unsaved changes in open files. To always be asked, select the \uicontrol {Ask for confirmation before exiting} - check box in \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + check box in \preferences > \uicontrol Environment > + \uicontrol System. \li Ctrl+Q \endtable @@ -315,9 +315,8 @@ \li Select the current block The second press extends the selection to the parent block. To - enable this behavior, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > - \uicontrol Behavior > + enable this behavior, select \preferences > + \uicontrol {Text Editor} > \uicontrol Behavior > \uicontrol {Enable smart selection changing}. \li Ctrl+U \row @@ -372,7 +371,8 @@ \li Follow symbol under cursor Works with namespaces, classes, functions, variables, include - statements, and macros. Also, opens URLs in the default browser + statements, and macros, as well as CMake functions, macros, + targets, and packages. Also, opens URLs in the default browser \if defined(qtcreator) and Qt resource files (.qrc) in the \l{Resource Files} {resource editor} @@ -454,7 +454,11 @@ You can specify shortcuts for executing actions in a way that is familiar to \l{https://www.gnu.org/software/emacs/manual/html_node/emacs/index.html} {Emacs} editor users. The actions are not bound to any key combinations by - default. The following actions are available: + default. + + \note Enable the EmacsKeys plugin to use the shortcuts. + + The following actions are available: \list \li Copy @@ -791,7 +795,11 @@ \endif \sa {Assign keyboard shortcuts}, {Find keyboard shortcuts}, - {Import and export keyboard shortcuts} + {Import and export keyboard shortcuts} + + \if defined(qtcreator) + \sa {Enable and disable plugins} + \endif */ /*! @@ -818,8 +826,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment - > \uicontrol Keyboard. + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} \li Select a command from the list. @@ -876,8 +883,7 @@ To look up keyboard shortcuts: \list 1 - \li Select uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol Keyboard. + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} \li Start typing the name of a function or shortcut in the \uicontrol Filter field. @@ -908,8 +914,7 @@ \list 1 - \li Select uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol Keyboard. + \li Select \preferences > \uicontrol Environment > \uicontrol Keyboard. \image qtcreator-keyboard-shortcuts.png {Keyboard preferences} \li To import a keyboard shortcut mapping scheme, click \uicontrol Import diff --git a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc index dab584171bc..8d72d3ba896 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc @@ -39,7 +39,7 @@ By default, \QC does not handle build system based tests to avoid interference with code based parsers. To enable build system based tests, - select the respective test tool in \uicontrol Preferences > \uicontrol Testing + select the respective test tool in \preferences > \uicontrol Testing > \uicontrol General. The detection of tests is usually much faster for build system based @@ -341,8 +341,8 @@ with the build system. If a test takes more than a minute to execute, the default timeout might - stop the test execution. To increase the timeout, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Testing} > \uicontrol General. + stop the test execution. To increase the timeout, select \preferences > + \uicontrol {Testing} > \uicontrol General. \section2 Selecting Tests to Run @@ -389,8 +389,7 @@ \section1 Specifying Test Settings To customize the handling of tests, test frameworks, and test tools, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Testing} > - \uicontrol General. + \preferences > \uicontrol {Testing} > \uicontrol General. \image qtcreator-preferences-testing-general.webp {General tab in Testing preferences} @@ -407,6 +406,10 @@ \uicontrol {Active Test Frameworks} list. By default, \QC groups tests that are in the same directory. + By default, \QC uses a quarter of the available logical CPUs when + scanning for tests. You can set the amount of worker threads used + in \uicontrol {Scan threads}. + \QC omits internal messages and run configuration warnings for deduced configurations by default. To view them, deselect the \uicontrol {Omit internal messages} and @@ -444,8 +447,8 @@ The code inside a benchmark test is measured, and possibly also repeated several times in order to get an accurate measurement. This depends on the measurement back-end that you can select in the - \uicontrol {Benchmark Metrics} group in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Testing} > \uicontrol {Qt Test}: + \uicontrol {Benchmark Metrics} group in \preferences > \uicontrol {Testing} > + \uicontrol {Qt Test}: walltime, CPU tick counter, event counter, Valgrind Callgrind, and Linux Perf. For more information, see \l{Creating a Benchmark}. @@ -472,8 +475,8 @@ \section2 Specifying Settings for Running Google Tests - To specify settings for running Google tests, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Testing} > \uicontrol {Google Test}. + To specify settings for running Google tests, select \preferences > + \uicontrol {Testing} > \uicontrol {Google Test}. \image qtcreator-preferences-testing-googletest.webp {Gooble Test tab in Testing preferences} @@ -500,9 +503,8 @@ \section2 Specifying Settings for Running Boost Tests \list 1 - \li To specify settings for running Boost tests, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Testing} > - \uicontrol {Boost Test}. + \li To specify settings for running Boost tests, select \preferences > + \uicontrol {Testing} > \uicontrol {Boost Test}. \image qtcreator-preferences-testing-boosttest.webp {Boost Test tab in Testing preferences} \li In the \uicontrol {Log format} field, select the error report format to specify the type of events to record in the @@ -526,8 +528,7 @@ \section2 Specifying Settings for Running Catch2 Tests \list 1 \li To specify settings for running Catch2 tests, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Testing} > - \uicontrol {Catch Test}. + \preferences > \uicontrol {Testing} > \uicontrol {Catch Test}. \image qtcreator-preferences-testing-catchtest.webp {Catch Test tab in Testing preferences} \li Select the \uicontrol {Show success} check box to show succeeding expressions as well. By default Catch2 will print only fails. @@ -557,8 +558,7 @@ \section2 Specifying Settings for Running CTest-Based Tests \list 1 \li To specify settings for running CTest-based tests, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Testing} > - \uicontrol {CTest}. + \preferences > \uicontrol {Testing} > \uicontrol {CTest}. \image qtcreator-preferences-testing-ctest.webp {CTest tab in Testing preferences} \li Select the \uicontrol {Output on failure} check box to show test specific output if a test fails. Contrary to the CTest default @@ -595,8 +595,8 @@ the lost information when viewing the results as plain text. To view the - results of Qt and Qt Quick tests as plain text, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Testing} > \uicontrol {Qt Test}, and + results of Qt and Qt Quick tests as plain text, select \preferences > + \uicontrol {Testing} > \uicontrol {Qt Test}, and then deselect the \uicontrol {Use XML output} check box. Then select the \inlineimage icons/text.png (\uicontrol {Switch Between Visual and Text Display}) button in diff --git a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc index 96c63abfd1d..b11cab2a52e 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-cli.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,58 +8,21 @@ // ********************************************************************** /*! - \previouspage creator-vcpkg.html \page creator-cli.html - \nextpage creator-keyboard-shortcuts.html + \previouspage creator-reference.html - \title Using Command Line Options + \ingroup creator-reference - You can start \QC and specify some options from the command line. For - example, you can open a file to any line and column. + \title Command-Line Options - To specify command line options, enter the following command in the \QC - installation or build directory: + \brief Options for starting \QC from the command line. - \c {qtcreator [option] [filename[:line_number[:column_number]]]} + To specify command-line options, enter the following command in the + directory that contains the \QC executable or specify the path to + \QC as a part of the command: - \note You can use either a colon (:) or a plus sign (+) as a separator - between the filename and line number and the line number and the column - number. You can also use a space between the separator and the line number. - - For example, on Windows: - - \list - - \li \c {C:\qtcreator\bin>qtcreator -help} - - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100:2} - - \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100+2} - - \endlist - - On \macos: - - \list - - \li \c {Qt\ Creator.app/Contents/MacOS/Qt\ Creator -help} - - \endlist - - To open a project that is located in a particular folder, you can pass on - the folder name as a command line argument. \QC looks for a session that - matches the folder name and loads it. Or it looks for a project file in the - folder and opens it. For example: - - \c {qtcreator .} - - \note To run a self-built \QC from the command line on Windows, make sure - that the Qt installation directory is included in the PATH environment - variable. You can enter the following command on the command line to add Qt - to the path: - - \code - set PATH=\mingw\bin;c:\bin;%PATH% + \badcode + qtcreator [option] [filename[:line_number[:column_number]]] \endcode The following table summarizes the available options: @@ -71,7 +34,7 @@ \row \li -help - \li Display help on command line options. + \li Display help on command-line options. \row \li -version @@ -223,7 +186,7 @@ \li -customwizard-verbose \li ProjectExplorer plugin: display additional information when loading custom wizards. For more information about custom - wizards, see \l{Adding New Custom Wizards} + wizards, see \l{Custom Wizards} \row \li -ensure-kit-for-binary @@ -249,7 +212,7 @@ \section1 Using Custom Styles \QC is a \l{QApplication}{Qt application}, and therefore, it accepts the - command line options that all Qt applications accept. For example, you can + command-line options that all Qt applications accept. For example, you can use the \c {-style} and \c {-stylesheet} options to apply custom styles and \l{Qt Style Sheets}{stylesheets}. The styling is only applied during the current session. @@ -262,4 +225,5 @@ You can also switch to a dark theme to customize the appearance of widgets, colors, and icons without using stylesheets. + \sa {Run Qt Creator from the command line} */ diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-to-document-code.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-to-document-code.qdoc new file mode 100644 index 00000000000..d56a1f288cb --- /dev/null +++ b/doc/qtcreator/src/howto/creator-only/creator-how-to-document-code.qdoc @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-document-code.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-edit + + \title Document code + + When you write documentation comments in the \uicontrol Edit mode, you can + enable Doxygen code blocks and add brief descriptions and leading asterisks + automatically. Also, you can select the Doxygen command prefix to use. + + To set preferences for documentation comments for a particular project, + select \uicontrol Projects > \uicontrol {Project Settings} > + \uicontrol {Documentation Comments}, and deselect the + \uicontrol {Use global settings} check box. + + \image qtcreator-preferences-documentation-comments.webp {Documentation Comments settings} + + To set global preferences, select \preferences > + \uicontrol {Text Editor} > \uicontrol {Documentation Comments}. + + \sa {Configuring Projects} +*/ diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-to-enable-plugins.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-to-enable-plugins.qdoc index 6cf11eff11f..ad5b4e45938 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-how-to-enable-plugins.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-how-to-enable-plugins.qdoc @@ -9,6 +9,18 @@ \title Enable and disable plugins + \QC uses standard names and locations on Linux, \macos, and Windows for + standard features, such as \e about dialogs. + + \table + \header + \li Linux and Windows + \li \macos + \row + \li \uicontrol Help > \uicontrol {About Plugins} + \li \uicontrol {\QC} > \uicontrol {About Plugins} + \endtable + New \QC plugins are often introduced as \e {experimental plugins} to let you try them out before they are fully supported. Experimental plugins are disabled by default and you must enable them for them to become visible @@ -24,7 +36,7 @@ To enable and disable plugins: \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins}. + \li Go to \uicontrol {About Plugins}. \li Start typing in the \uicontrol Filter field to find a plugin. \image qtcreator-installed-plugins.webp {Installed Plugins dialog} \li Select the \uicontrol Load check box to enable a plugin, or deselect @@ -34,5 +46,5 @@ take effect. \endlist - \sa {Install plugins}{How-to: Install plugins} + \sa {Install plugins} */ diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-to-install-plugins.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-to-install-plugins.qdoc index 711e036c519..a0f0bb9adb8 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-how-to-install-plugins.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-how-to-install-plugins.qdoc @@ -22,8 +22,7 @@ To install plugins: \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Install Plugins}. + \li Go to \uicontrol {About Plugins} > \uicontrol {Install Plugins}. \li In the \uicontrol Source dialog, enter the path to the archive or library that has the plugin. \image qtcreator-install-plugin-source.png @@ -39,5 +38,5 @@ \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. \endlist - \sa {Enable and disable plugins}{How-to: Enable and disable plugins} + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-to-record-screens.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-to-record-screens.qdoc new file mode 100644 index 00000000000..c8a624622d3 --- /dev/null +++ b/doc/qtcreator/src/howto/creator-only/creator-how-to-record-screens.qdoc @@ -0,0 +1,149 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-record-screens.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-use + + \title Record screens + + With \l{https://ffmpeg.org/download.html}{FFmpeg}, you can record your + screens and save the recordings as animated images or videos. + + To record screens: + + \list 1 + \li Select \uicontrol Tools > \uicontrol {Screen Recording}. + \image qtcreator-record-screen.webp {Record Screen dialog} + \li Select \inlineimage icons/settings.png + to select the screen to record from and to set the recorded + screen area. + \li Select \inlineimage icons/recordfill.png + to start recording. + \li Select \inlineimage icons/stop_small.png + when you are done recording. + \li Select \uicontrol {Crop and Trim} to edit the recording. + \li Select \uicontrol Export to save the recording as an animated image + or a video. + \endlist + + \note Enable the Screen Recorder plugin to use it. + + \section1 Set the screen and area to record + + Set the screen and the area to record in the + \uicontrol {Screen Recording Options} dialog. + + \image qtcreator-screen-recording-options.webp {Screen Recording Options dialog} + + To select a screen and area: + + \list 1 + \li In \uicontrol Display, select the display to record. + \li In \uicontrol {Recorded screen area}, drag the guides to set the + \uicontrol x and \uicontrol y coordinates of the starting point for + the recording area, as well as the width and height of the area. + \li Select \uicontrol OK to return to the \uicontrol {Record Screen} + dialog. + \endlist + + Select \inlineimage icons/reset.png + to reset the area. + + \section1 Edit recordings + + You can crop and trim the recording in the \uicontrol {Crop and Trim} dialog. + + \image qtcreator-crop-and-trim.webp {Crop and Trim} + + To crop a recording, in \uicontrol Cropping, drag the guides to set the + \uicontrol x and \uicontrol y coordinates of the starting point of + the recorded area, as well as the width and height of the area. + + To save a cropped version of the current frame as a file, select + \inlineimage icons/savefile.png + . + + To copy a cropped version of the current frame to the clipboard, select + \inlineimage icons/camera.png + . + + To trim a recording: + + \list 1 + \li Click the slider to show a handle and drag it to the frame where + you want the recording to start. + \li Click \uicontrol Start to set the first frame. + \li Click the slider again and drag the handle to the frame where you + want the recording to end. + \li Click \uicontrol End to set the last frame. + \endlist + + Select \inlineimage icons/reset.png + to reset the crop area and trim range. + + \sa {Screen Recording}, {Enable and disable plugins} +*/ + +/*! + \page creator-preferences-screen-recording.html + \previouspage creator-reference.html + + \ingroup creator-reference-preferences + + \title Screen Recording + + \brief Set preferences for recording screens with FFmpeg. + + With \l{https://ffmpeg.org/download.html}{FFmpeg}, you can record your + screens and save the recordings as animated images or videos. You have + to download and install FFmpeg and FFprobe to use the screen recorder. + + \note Enable the Screen Recorder plugin to use it. + + To set up screen recording: + + \list 1 + \li Select \preferences > \uicontrol Help > \uicontrol {Screen Recording}. + \image qtcreator-preferences-screen-recording.webp {Screen Recording preferences} + \li In \uicontrol {ffmpeg tool}, set the path to the FFmpeg executable. + \li In \uicontrol {ffprobe tool}, set the path to the FFprobe executable. + \li Set preferences for recording screens and saving the recordings. + \endlist + + The following table lists the available preferences. + + \table + \header + \li Setting + \li Value + \row + \li \uicontrol {Capture the mouse cursor} + \li Whether to show the mouse cursor in the recording. + \row + \li \uicontrol {Capture device/filter} + \li The grabbing device or filter to use. If the recorder shows + error messages, try another grabbing device. + \row + \li \uicontrol {Size limit for intermediate output file} + \li To prevent huge files on your hard disk if you forget to stop + recording, for example, recording automatically stops when this + limit is reached. + \row + \li \uicontrol {RAM buffer for real-time frames} + \li The maximum size of a RAM buffer for storing real-time frames. + Increase the limit if frames are dropped during the recording. + \row + \li \uicontrol {Export animated images as infinite loop} + \li Whether to export animated images as inifite loops. Deselect + this check box to only play the animation once. + \row + \li \uicontrol {Write command line of FFmpeg calls to General Messages} + \li Shows the options used to run FFmpeg in the + \uicontrol {General Messages} view. + \endtable + + \sa {Record screens}, {Enable and disable plugins}, {View output} +*/ diff --git a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc index 9e2dc2afc40..dfcba68e12b 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-how-tos.qdoc @@ -12,65 +12,65 @@ \page creator-how-tos.html \nextpage creator-reference.html - \title How-to + \title How To The following topics describe how to use \QC to perform a particular task. - \section1 Use the UI + \section1 Build and Run - \list - \li \l {Assign keyboard shortcuts} - \li \l {Find a particular preference} - \li \l {Find keyboard shortcuts} - \li \l {Find menu items on \macos} - \li \l {Import and export keyboard shortcuts} - \li \l {Set high DPI scaling} - \li \l {Set the number of recent files shown} - \li \l {Show and hide sidebars} - \li \l {Switch between modes} - \li \l {Switch UI themes} - \li \l {View output} - \endlist + \generatelist creator-how-to-build - \section1 Edit Code + \section2 Build with CMake - \list - \li \l {Add code snippets to the auto-complete menu} - \li \l {Enclose selected code in curly braces, parentheses, or double quotes} - \li \l {Jump to a function in QML code} - \li \l {Locate files using the keyboard} - \li \l {Move between open files} - \li \l {Move to symbols} - \li \l {Paste text from clipboard history} - \li \l {Perform calculations} - \li \l {Search and replace across files using a regular expression} - \li \l {Select the enclosing block in C++} - \li \l {Sort lines alphabetically} - \li \l {Switch to Edit mode} - \li \l {Write down notes} - \endlist + \generatelist creator-how-to-build-with-cmake + + \section2 Build with qmake + + \generatelist creator-how-to-build-with-qmake + + \section1 Debug + + \generatelist creator-how-to-debug \section1 Design UIs - \list - \li \l {Export SVG images} - \li \l {View images} - \endlist + \generatelist creator-how-to-design + + \section1 Edit Code + + \generatelist creator-how-to-edit + + \section1 Manage Kits + + \generatelist creator-how-to-manage-kits \section1 Manage Projects - \list - \li \l {Add a license header template for C++ code} - \endlist + \generatelist creator-how-to-projects + + \section2 Create Projects + + \generatelist creator-how-to-projects-create + + \section2 Create Files + + \generatelist creator-how-to-projects-files + + \section2 Configure Projects + + \generatelist creator-how-to-projects-configure + + \section1 Read Documentation + + \generatelist creator-how-to-get-help \section1 Use \QC - \list - \li \l {Enable and disable plugins} - \li \l {Find settings files} - \li \l {Install plugins} - \li \l {Run \QC from the command line} - \endlist + \generatelist creator-how-to-use + + \section1 Use the UI + + \generatelist creator-how-to-ui */ /*! @@ -115,20 +115,6 @@ \endlist */ -/*! - \page creator-how-to-find-preferences.html - \previouspage creator-how-tos.html - - \ingroup creator-how-to-ui - - \title Find a particular preference - - To find a particular preference in \uicontrol Edit > \uicontrol Preferences, - use the filter located at the top left of the \uicontrol Preferences dialog. - - \image qtcreator-preferences.webp {Filtering preferences} -*/ - /*! \page creator-how-to-run-from-cli.html \previouspage creator-how-tos.html @@ -137,14 +123,55 @@ \title Run \QC from the command line - You can launch \QC from the command line using the name of an - existing \l{Managing Sessions}{session} or project file by entering - the name as the command argument. + You can start \QC and specify some options from the command line. For + example, you can open a file to any line and column. - For example, running \c {qtcreator somesession}, launches \QC and - loads the session called \e somesession. + To use command-line options, enter the following command in the + directory that contains the \QC executable or specify the path to + \QC as a part of the command: - For more information, see \l{Using Command Line Options}. + \badcode + qtcreator [option] [filename[:line_number[:column_number]]] + \endcode + + \note You can use either a colon (:) or a plus sign (+) as a separator + between the filename and line number and the line number and the column + number. You can also use a space between the separator and the line number. + + \section1 Examples of CLI commands + + On Windows: + + \list + + \li \c {C:\qtcreator\bin>qtcreator -help} + + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp:100:2} + + \li \c {C:\qtcreator\bin>qtcreator C:\TextFinder\textfinder.cpp +100+2} + + \endlist + + On \macos: + + \list + + \li \c {Qt\ Creator.app/Contents/MacOS/Qt\ Creator -help} + + \endlist + + \section1 Open a project from a directory + + To open a project that is located in a particular directory, you can + pass on the directory name as a command-line argument. \QC looks for + a \l{Managing Sessions}{session} that matches the directory name and + loads it. Or it looks for a project file in the directory and opens it. + + For example, on Windows: + + \c {C:\qtcreator\bin>qtcreator.exe C:\Examples\alarms} + + \sa {Command-Line Options} */ /*! @@ -187,7 +214,7 @@ create your own locator filters. That way you can locate files in a directory structure you have defined. - To create locator filters, select \uicontrol Edit > \uicontrol Preferences > + To create locator filters, select \preferences > \uicontrol Environment > \uicontrol Locator > \uicontrol Add. \image qtcreator-locator-customize.webp "Locator preferences" @@ -228,13 +255,12 @@ \page creator-how-to-add-license-header-templates.html \previouspage creator-how-tos.html - \ingroup creator-how-to-projects + \ingroup creator-how-to-projects-files \title Add a license header template for C++ code - Specify a file that has a license header for C++ in \uicontrol Edit > - \uicontrol Preferences > \uicontrol C++ > \uicontrol {File Naming} > - \uicontrol {License template}. + Specify a file that has a license header for C++ in \preferences > + \uicontrol C++ > \uicontrol {File Naming} > \uicontrol {License template}. \image qtcreator-options-cpp-files.png "File Naming preferences" @@ -300,10 +326,11 @@ \endlist To specify whether to automatically insert matching characters, - select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Text Editor} > \uicontrol Completion. + select \preferences > \uicontrol {Text Editor} > \uicontrol Completion. - \image qtcreator-options-texteditor-completion.png "Completion preferences" + \image qtcreator-preferences-texteditor-completion.webp "Completion preferences" + + \sa {Completing Code} */ /*! @@ -326,8 +353,8 @@ \title Add code snippets to the auto-complete menu Add, modify, and remove snippets in the snippet editor. - To open the editor, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Text Editor} > \uicontrol Snippets. + To open the editor, select \preferences > \uicontrol {Text Editor} > + \uicontrol Snippets. \image qtcreator-snippet-modifiers.png "Snippets preferences" @@ -375,8 +402,7 @@ \uicontrol {Recent Files}: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + \li Select \preferences > \uicontrol Environment > \uicontrol System. \image qtcreator-options-environment-system.png {System preferences} \li In \uicontrol {Maximum number of entries in "Recent Files"}, set the number of files to show. diff --git a/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc b/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc index 38e4b92d1ce..75ec01e5e56 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-logging-viewer.qdoc @@ -1,12 +1,13 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page creator-logging-viewer.html - \previouspage creator-task-lists.html - \nextpage creator-telemetry.html + \previouspage creator-how-tos.html - \title Inspecting Internal Logs + \ingroup creator-how-to-use + + \title Inspect internal logs You can inspect internal log messages of \QC at runtime without having to restart it or configure the logging rules. Log messages are helpful when @@ -25,7 +26,7 @@ \note Messages are not cached, so the viewer displays only messages that are recorded after you enabled a category. - \section1 Viewing Logs + \section1 View logs To enable logging categories, select them in \uicontrol Category. \uicontrol Type specifies the minimum level of messages to display from diff --git a/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc b/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc index 25b2d54dad5..4284085e721 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc @@ -42,22 +42,14 @@ To use the plugin, you must download and install Squish, create a connection to the Squish Server, and specify AUTs to run. - \section1 Enabling the Squish Plugin - - To enable the Squish plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol Squish to enable the plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \note Enable the Squish plugin to use it. \section1 Specifying a Squish Server To specify a Squish Server to run: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Squish. + \li Select \preferences > \uicontrol Squish. \image qtcreator-squish-preferences.png "Squish general preferences" \li In the \uicontrol {Squish path} field, specify the path to the Squish installation directory. @@ -328,4 +320,6 @@ output, \uicontrol {Runner/Server Log} tab. \image qtcreator-squish-output-runner-server-log.png "Runner and Server Log output" + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc b/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc index 4cc7ecda418..e540a62c3f1 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-task-lists.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,11 +8,12 @@ // ********************************************************************** /*! - \previouspage creator-editor-external.html \page creator-task-lists.html - \nextpage creator-logging-viewer.html + \previouspage creator-how-tos.html - \title Showing Task List Files in Issues + \ingroup creator-how-to-ui + + \title Show task list files in issues You can use code scanning and analysis tools to examine source code. These tools report issues for you to fix. \QC enables you to load lists of @@ -25,7 +26,7 @@ lines of code and matches them against regular expressions to generate a task list, see \c{scripts\mytasks.pl} in the \QC repository. - \section1 Managing Task List Entries + \section1 Manage task list entries To open task list files in \uicontrol Issues, choose \uicontrol File > \uicontrol Open. Right-click a task list entry to open a context menu that @@ -36,7 +37,7 @@ To keep the current entries in a task list, but stop checking for changes, select \uicontrol {Stop Monitoring}. - \section1 Task List File Format + \section1 Task list file format The filename extension must be .tasks for \QC to recognize a file as a task list file. @@ -79,4 +80,6 @@ If the file path is given as a relative path, it is based on the parent directory of the task list file. + + \sa {Issues}, {View output} */ diff --git a/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc b/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc index 84b3580bd69..dd498be5db8 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-vcpkg.qdoc @@ -3,40 +3,33 @@ /*! \page creator-vcpkg.html - \previouspage creator-project-conan.html - \nextpage creator-cli.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Managing Packages with vcpkg - The experimental vcpkg plugin integrates the \l {https://vcpkg.io/en/}{vcpkg} - C/C++ package manager into \QC. It's available on all the + \brief The experimental vcpkg plugin integrates the vcpkg C/C++ package + manager into \QC. + + \l {https://vcpkg.io/en/}{vcpkg} is available on all the \l{Supported Platforms}{supported development platforms}. You can use \QC to create and edit vcpkg.json files to specify packages to build as part of your project when using CMake as the build system. - \section1 Enabling the vcpkg Plugin - - To enable the vcpkg plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol Vcpkg to enable the plugin. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \note Enable the Vcpkg plugin to use it. \section1 Setting vcpkg Preferences By default, vcpkg is automatically set up for use with CMake. To disable - automatic setup, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol CMake > \uicontrol General > - \uicontrol {Package manager auto setup}. + automatic setup, select \preferences > \uicontrol CMake > \uicontrol General + > \uicontrol {Package manager auto setup}. \image qtcreator-preferences-cmake-general.webp {General tab in CMake Preferences} To set the \uicontrol Path to the directory where you installed vcpkg, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > - \uicontrol Vcpkg. + \preferences > \uicontrol CMake > \uicontrol Vcpkg. \image qtcreator-preferences-cmake-vcpkg.webp {Vcpkg tab in CMake Preferences} @@ -85,4 +78,6 @@ To set the path to the directory where you installed vcpkg, select \inlineimage icons/settings.png (\uicontrol Configure) on the editor toolbar. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index 7cf86218e50..beadc0eb86a 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -2,15 +2,17 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-help.html \page creator-faq.html - \nextpage creator-how-tos.html + \previouspage creator-reference.html + + \ingroup creator-reference \title FAQ - This section has answers to some frequently asked questions about \QC. + \brief Answers to some frequently asked questions about \QC. + You might also find answers to your questions in the - \l{Known Issues} and \l{How-to} sections, or the Troubleshooting + \l{Known Issues} and \l{How To} sections, or the Troubleshooting sections for a special area, such as \l{Troubleshooting Debugger}{debugging}. @@ -32,7 +34,7 @@ compiler. What should I do?} Make sure that the application is in your system PATH when starting \QC. - Also select \uicontrol Edit > \uicontrol Preferences to check the settings + Also select \preferences to check the settings specified for the application. Many plugins specify either the path to the tool they need or the environment they run in. @@ -43,7 +45,7 @@ \QC has been localized into several languages. If the system language is one of the supported languages, it is automatically selected. - To change the language, select \uicontrol Edit > \uicontrol Preferences > + To change the language, select \preferences > \uicontrol Environment and select a language in the \uicontrol Language field. Select \uicontrol {Restart Now} to restart \QC and have the change take effect. @@ -143,9 +145,9 @@ \uicontrol Help mode. To view the documentation that is available and to add documentation, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Help > + select \preferences > \uicontrol Help > \uicontrol Documentation. For more information, see - \l{Adding External Documentation}. + \l{Add external documentation}. \section1 Debugger Questions @@ -230,9 +232,9 @@ For console applications that require input, select \uicontrol Projects > \uicontrol {Run Settings} > \uicontrol {Run in terminal}. To specify the - terminal to use, select \uicontrol Edit > \uicontrol Preferences > + terminal to use, select \preferences > \uicontrol Environment > \uicontrol System. To use an \l{Terminal} - {internal terminal}, select \uicontrol Edit > \uicontrol Preferences + {internal terminal}, select \preferences > \uicontrol Terminal > \uicontrol {Use internal terminal}. \b {On Windows:} Output is displayed differently for \e{console diff --git a/doc/qtcreator/src/howto/creator-sessions.qdoc b/doc/qtcreator/src/howto/creator-sessions.qdoc index 203df259f28..1201810e09f 100644 --- a/doc/qtcreator/src/howto/creator-sessions.qdoc +++ b/doc/qtcreator/src/howto/creator-sessions.qdoc @@ -96,7 +96,7 @@ When you start \QC from the command line, you can give the name of a session as an argument and \QC will start with this session. - For more information, see \l{Using Command Line Options}. + For more information, see \l{Command-Line Options}. \endif */ diff --git a/doc/qtcreator/src/howto/creator-telemetry.qdoc b/doc/qtcreator/src/howto/creator-telemetry.qdoc index b3cad6bf004..672ef719eb0 100644 --- a/doc/qtcreator/src/howto/creator-telemetry.qdoc +++ b/doc/qtcreator/src/howto/creator-telemetry.qdoc @@ -1,16 +1,18 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page creator-telemetry.html \if defined(qtdesignstudio) \previouspage creator-quick-ui-forms.html - \else - \previouspage creator-logging-viewer.html - \endif \nextpage collecting-usage-statistics.html + \else + \previouspage creator-how-tos.html + \endif - \title Managing Data Collection + \ingroup creator-how-to-use + + \title Manage data collection \if defined (qtcreator) When you install \QC as a part of Qt installation, you are asked whether @@ -24,15 +26,17 @@ mode for data collection to begin. - See \l {Collecting Usage Statistics} for more information about the data - transmitted by the telemetry plugin and \l {Specifying Telemetry Settings} + See \l {Collect usage statistics} for more information about the data + transmitted by the telemetry plugin and \l {Specify telemetry settings} {specifying telemetry settings}. \else To enable the use of the telemetry plugin, you need to select \uicontrol {Enable Usage Statistics} in the splash screen that appears when you first launch \QDS. If the splash screen does not appear, you can enable the telemetry plugin by selecting \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol UsageStatistic. + \uicontrol Utilities > \uicontrol UsageStatistic on Linux and Windows (or + \uicontrol {\QDS} > \uicontrol {About Plugins} > \uicontrol Utilities > + \uicontrol UsageStatistic on \macos). \image studio-usage-statistics.png "Enabling Usage Statistics" \endif @@ -40,13 +44,13 @@ See below for more information about the collected data: \list - \li \l {Collecting Usage Statistics} + \li \l {Collect usage statistics} \li \l {Collecting User Feedback} \li \l {Reporting Crashes} \endlist \endif - \section1 Principles of Data Collection + \section1 Principles of data collection No personal data, such as names, IP addresses, MAC addresses, or project and path names are collected. However, QUuid objects are used to identify @@ -56,18 +60,22 @@ For more information about Qt privacy policy, select \l{https://www.qt.io/terms-conditions/#privacy} {Legal Notice and Privacy Policy}. + + \sa {Collect usage statistics} */ /*! \page collecting-usage-statistics.html - \previouspage creator-telemetry.html \if defined(qtdesignstudio) + \previouspage creator-telemetry.html \nextpage studio-user-feedback.html \else - \nextpage creator-help-overview.html + \previouspage creator-how-tos.html \endif - \title Collecting Usage Statistics + \ingroup creator-how-to-use + + \title Collect usage statistics The telemetry plugin uses the \l{https://api.kde.org/frameworks/kuserfeedback/html/index.html} @@ -79,12 +87,12 @@ connection. The storage is located in the same Heroku backend as the \QOI backend. Physically, data is stored in the Amazon cloud. - \section1 Specifying Telemetry Settings + \section1 Specify telemetry settings To determine what data is transmitted to the backend storage: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Telemetry + \li Select \preferences > \uicontrol Telemetry > \uicontrol {Usage Statistics}. \image qtcreator-telemetry-settings.png "Telemetry settings" \li In the \uicontrol {Telemetry mode} list, select the mode that @@ -93,4 +101,6 @@ exactly what data is collected. Deselect check boxes for data that you do not want to transmit to the backend storage. \endlist + + \sa {Manage data collection} */ diff --git a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc index 2d892cb1086..92ff58fec36 100644 --- a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc +++ b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild-building.qdoc @@ -32,7 +32,7 @@ The build errors and warnings are parsed and displayed in \l Issues. Select the \uicontrol {Keep original jobs number} check box to stop - IncrediBuild from overriding the \c {-j} command line switch, which + IncrediBuild from overriding the \c {-j} command-line switch, which controls the number of processes that the build tools executed by \QC run in parallel. The default value set by IncrediBuild is 200. diff --git a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild.qdoc b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild.qdoc index 02281f3c3b6..4cd352fb289 100644 --- a/doc/qtcreator/src/incredibuild/creator-projects-incredibuild.qdoc +++ b/doc/qtcreator/src/incredibuild/creator-projects-incredibuild.qdoc @@ -1,13 +1,16 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-meson.html \page creator-project-incredibuild.html - \nextpage creator-project-conan.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up IncrediBuild + \brief IncrediBuild decreases the time it takes to build C++ code. + \l{https://www.incredibuild.com/}{IncrediBuild} accelerates process execution and thus shortens the time you spend on building C++ code. In addition, you can view the build progress in the graphical Build diff --git a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc index 789b090ed65..def1727a1fc 100644 --- a/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc +++ b/doc/qtcreator/src/linux-mobile/b2qtdev.qdoc @@ -18,7 +18,7 @@ To be able to run and debug applications on Boot2Qt devices, you must add devices and select them in the \QC - \l{Adding Kits}{kit}. + \l{Kits}{kit}. \section1 Enabling the Boot2Qt Plugin @@ -34,7 +34,7 @@ \section1 Adding Boot2Qt Devices If \QC does not automatically detect a device you connected with USB, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > + \preferences > \uicontrol Devices > \uicontrol Devices > \uicontrol Add > \uicontrol {Boot2Qt Device} to create either a network connection or a USB connection to it. @@ -49,7 +49,7 @@ \l{https://doc.qt.io/Boot2Qt/b2qt-requirements-x11.html#setting-up-usb-access-to-embedded-devices} {Boot2Qt: Setting Up USB Access to Embedded Devices}. - You can edit the settings later in \uicontrol Edit > \uicontrol Preferences > + You can edit the settings later in \preferences > \uicontrol Devices > \uicontrol Devices. To reboot the selected device, select \uicontrol {Reboot Device}. @@ -74,7 +74,7 @@ need to enter the password upon every connection to the device, or if caching is enabled, at every \QC restart. If you frequently run into the timeout, consider using key-based authentication. On \macos and Linux, you - can also select \uicontrol Preferences > \uicontrol Devices > \uicontrol SSH + can also select \preferences > \uicontrol Devices > \uicontrol SSH and increase the time (in minutes) to use the same SSH connection in the \uicontrol {Connection sharing timeout} field. Windows does not support shared connections. @@ -97,10 +97,9 @@ \list 1 \li Check that you can reach the IP address of the device, or use USB to connect it. - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > - \uicontrol {Qt Versions} > \uicontrol Add to add the Qt version - for Boot2Qt. - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol {Qt Versions} > + \uicontrol Add to add the Qt version for Boot2Qt. + \li Select \preferences > \uicontrol Kits > \uicontrol Compilers > \uicontrol Add to add the compiler for building the applications. \li Select \uicontrol Tools > \uicontrol {Flash Boot to Qt Device} @@ -109,16 +108,15 @@ parameters for connecting to the devices over the network (\QC automatically detects devices connected with USB): \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Devices > \uicontrol Devices > \uicontrol Add > - \uicontrol Boot2Qt. + \li Select \preferences > \uicontrol Devices > + \uicontrol Devices > \uicontrol Add > \uicontrol Boot2Qt. \image qtcreator-devices-boot2qt.png {Boot2Qt Network Device Setup wizard} \li In the \uicontrol {Device name} field, enter a name for the connection. \li In the \uicontrol {Device address} field, enter the host name or IP address of the device. This value will be available in the \c %{Device:HostAddress} variable. - \li Click > \uicontrol {Finish} to test the connection and + \li Click \uicontrol {Finish} to test the connection and add the device. You can edit the connection parameters in the @@ -131,7 +129,7 @@ \uicontrol {Boot2Qt Device} in the pull-down menu of the \uicontrol Add button. \endlist - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol Add to add a kit for building applications for the device. Select the Qt version, compiler, and device that you added above, and choose \uicontrol Boot2Qt as the device type. diff --git a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc index 0944c02a953..38c6823fa30 100644 --- a/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-deployment-embedded-linux.qdoc @@ -35,7 +35,7 @@ \section1 Adding Missing Files The process to add files to deploy depends on the build system you use. - For more information, see \l{Specifying Project Contents}. + For more information, see \l{Specify project contents}. \section2 CMake Builds diff --git a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc index 51746e8f901..1d6ec78e30e 100644 --- a/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc +++ b/doc/qtcreator/src/linux-mobile/creator-embedded-platforms.qdoc @@ -3,11 +3,14 @@ /*! \page creator-embedded-platforms.html - \previouspage creator-desktop-platforms.html - \nextpage creator-mobile-platforms.html + \previouspage creator-reference.html + + \ingroup creator-reference \title Embedded Platforms + \brief Embedded platforms that you can develop applications for. + You can develop applications for the following embedded platforms: \list @@ -95,7 +98,7 @@ \section1 QNX - The QNX Neutrino RTOS has more command line tools + The QNX Neutrino RTOS has more command-line tools and services, as described in \l {Qt for QNX}. \note In Qt 6, \QC support for QNX is considered experimental. @@ -110,4 +113,6 @@ \li \l{Running on QNX Devices} \li \l{Qt for QNX} \endlist + + \sa {Supported Platforms} */ diff --git a/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-linux.qdocinc b/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-linux.qdocinc index 017163c3eeb..ea58383ea44 100644 --- a/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-linux.qdocinc +++ b/doc/qtcreator/src/linux-mobile/creator-projects-settings-run-linux.qdocinc @@ -21,10 +21,13 @@ command in the \uicontrol {Alternate executable on device} field and select the \uicontrol {Use this command instead} check box. - \image qtcreator-run-settings-linux.png "Run settings for Linux-based devices" + \image qtcreator-run-settings-remote-linux.webp {Run settings for Linux-based devices} You can specify arguments to pass to your application in the \uicontrol {Command line arguments} field. + Select the \uicontrol {Forward to local display} check box to show a remotely + running X11 client on a local display. + //! [run settings linux] */ diff --git a/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdocinc b/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdocinc index 172c4a74feb..f3582f23736 100644 --- a/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdocinc +++ b/doc/qtcreator/src/linux-mobile/linuxdev-keys.qdocinc @@ -19,8 +19,8 @@ development PC. To tell \QC where it can find the tools, specify the paths to the - directories where the tools are installed in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Devices > \uicontrol SSH: + directories where the tools are installed in \preferences > + \uicontrol Devices > \uicontrol SSH: \image qtcreator-ssh-options.png "SSH preferences" @@ -60,7 +60,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices + \li Select \preferences > \uicontrol Devices > \uicontrol Devices > \uicontrol {Create New}. \image qtcreator-ssh-key-configuration.png "SSH Key Configuration dialog" diff --git a/doc/qtcreator/src/linux-mobile/linuxdev-processes.qdocinc b/doc/qtcreator/src/linux-mobile/linuxdev-processes.qdocinc index 2992d6b2ecc..1c9c2d0a4ab 100644 --- a/doc/qtcreator/src/linux-mobile/linuxdev-processes.qdocinc +++ b/doc/qtcreator/src/linux-mobile/linuxdev-processes.qdocinc @@ -7,7 +7,7 @@ \section2 Managing Device Processes You can view processes running on devices and kill them. Select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > + \preferences > \uicontrol Devices > \uicontrol Devices > \uicontrol {Show Running Processes}. You can filter the processes by name or ID in the diff --git a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc index 41820632d36..401fdbd8c1e 100644 --- a/doc/qtcreator/src/linux-mobile/linuxdev.qdoc +++ b/doc/qtcreator/src/linux-mobile/linuxdev.qdoc @@ -22,7 +22,7 @@ {kit}. You use a wizard to create the connections. You can edit the settings later - in \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > + in \preferences > \uicontrol Devices > \uicontrol Devices. \image qtcreator-preferences-devices-remote-linux.webp "Remote Linux Device in the Devices tab" @@ -49,11 +49,11 @@ \li Make sure that your device can be reached via an IP address. - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol {Qt Versions} > \uicontrol Add to add the Qt version for embedded Linux. - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol Compilers > \uicontrol Add to add the compiler for building the applications. @@ -62,7 +62,7 @@ \list a - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol Devices > \uicontrol Devices > \uicontrol Add > \uicontrol {Remote Linux Device} > \uicontrol {Start Wizard}. @@ -111,7 +111,7 @@ \uicontrol {Add Remote Linux Device} in the pull-down menu of the \uicontrol Add button. - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol Add to add a kit for building for the device. Select the Qt version, compiler, and device that you added above, and select \uicontrol {Remote Linux Device} in \uicontrol {Run device type}. diff --git a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc index f2ad72b6402..65fba314e28 100644 --- a/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc +++ b/doc/qtcreator/src/mcu/creator-mcu-dev.qdoc @@ -60,8 +60,7 @@ \section2 Specifying MCU Settings To configure a connection between \QC and your MCU board, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > - \uicontrol MCU: + \preferences > \uicontrol Devices > \uicontrol MCU: \image qtcreator-mcu-options.png "MCU preferences" @@ -121,9 +120,8 @@ \image qtcreator-mcu-device.png "MCU devices" - To add MCU devices, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Devices > \uicontrol Add > \uicontrol {MCU Device} > - \uicontrol {Start Wizard}: + To add MCU devices, select \preferences > \uicontrol Devices > \uicontrol Add + > \uicontrol {MCU Device} > \uicontrol {Start Wizard}: \list 1 \li In the \uicontrol Name field, give the device a name. @@ -141,8 +139,7 @@ \image qtcreator-mcu-kit.png "MCU kits" - You can edit and/or remove individual kits in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits. + You can edit and/or remove individual kits in \preferences > \uicontrol Kits. However, for adding new kits you should use the \uicontrol {Create Kit} button in the {\QMCU} settings tab. This method adds the paths to diff --git a/doc/qtcreator/src/meson/creator-projects-meson.qdoc b/doc/qtcreator/src/meson/creator-projects-meson.qdoc index ffc9a0d4c7b..44b25eed93d 100644 --- a/doc/qtcreator/src/meson/creator-projects-meson.qdoc +++ b/doc/qtcreator/src/meson/creator-projects-meson.qdoc @@ -1,28 +1,29 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-nimble.html \page creator-project-meson.html - \nextpage creator-project-incredibuild.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up Meson + \brief Use Meson to build native desktop applications. + \l{https://mesonbuild.com/}{Meson} is an open source and multi-platform build system generator using Ninja as main backend. Build definitions are located in \c {meson.build} files while build options are located in \c {meson_options.txt}. - Meson build support in \QC is not mature yet, you can only use it to build - native desktop applications. Many features available with Meson build or - usually available from \QC are missing. + \note Many features available with Meson build or usually available from \QC + are missing. \QC automatically detects the Meson and Ninja executables specified in the \c PATH. You can add paths to other Meson or Ninja executables and use them in different build and run \l{glossary-buildandrun-kit}{kits}. - \note Meson build plugin is disabled by default, see - \l{Enable and disable plugins}{How-to: Enable and disable plugins}. + \note Enable the Meson plugin to use it. \section1 Adding Meson Tools @@ -33,7 +34,7 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Meson > + \li Select \preferences > \uicontrol Meson > \uicontrol Tools > \uicontrol Add. \image qtcreator-mesonexecutable.png @@ -48,12 +49,12 @@ \endlist Select the - \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > \uicontrol Kits + \preferences > \uicontrol Kits > \uicontrol Kits tab to add the Meson and Ninja tools to a build and run kit: \image qtcreator-kits-meson.png "Setting Meson executable in Kit preferences" - For more information, see \l {Adding Kits}. + For more information, see \l {Kits}. \section1 Editing Meson Build Descriptions @@ -84,11 +85,6 @@ \li Adding files to Meson projects from \QC. \endlist - \section1 Related Topics - - \list - \li \l {Opening Projects} - \li \l {Meson Build Configuration} - \li \l {Specifying Run Settings} - \endlist + \sa {Enable and disable plugins}, {Open projects}, + {Meson Build Configuration}, {Specifying Run Settings} */ diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index 37c105a22ad..68476eb9e4c 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -9,10 +9,18 @@ /*! \page creator-acknowledgements.html + \if defined(qtdesignstudio) \previouspage technical-support.html + \else + \previouspage creator-reference.html + \endif + + \ingroup creator-reference \title Acknowledgements + \brief Third-party components in \QC. + \section1 Credits We would like to thank our contributors, who are listed in the \QC change @@ -942,6 +950,43 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \endcode + \li \b RSTParser + + RSTParser is an open-source C++ library for parsing reStructuredText + + \list + \li \l https://github.com/vitaut-archive/rstparser + \endlist + + \badcode + License + ------- + + Copyright (c) 2013, Victor Zverovich + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + \endcode + \endlist */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc b/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc deleted file mode 100644 index 661c72f9f06..00000000000 --- a/doc/qtcreator/src/overview/creator-only/creator-advanced.qdoc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -// ********************************************************************** -// NOTE: the sections are not ordered by their logical order to avoid -// reshuffling the file each time the index order changes (i.e., often). -// Run the fixnavi.pl script to adjust the links to the index order. -// ********************************************************************** - -/*! - \previouspage creator-squish.html - \page creator-advanced.html - \nextpage creator-os-supported-platforms.html - - \title Advanced Use - - \image front-advanced.png - - \QC attempts to meet your development needs, whether you are an - experienced Qt developer or a newcomer to Qt. When you install \QC - as a part of \QSDK, the default configuration allows you to start coding, - building, running and debugging applications with very little effort. - - However, you can easily change or extend the default configuration, by - choosing a different build system or integrating external tools. - - You can also use special options to start \QC from the command line and use - it mainly from the keyboard. - - \list - - \li \l{Supported Platforms} - - You can install and run \QC on several operating systems to create - applications for multiple desktop and \l{glossary-device}{device} - platforms. - - \li \l{Build Systems} - - \QC is integrated with cross-platform systems for build automation: - qmake, Qbs, CMake, and Autotools. In addition, you can import - generic projects that do not use those systems, and specify that \QC - ignores your build system. - - \li \l{Using Command Line Options} - - You can start \QC and specify some options for running it from the - command line. - - \li \l{Keyboard Shortcuts} - - \QC has keyboard shortcuts that speed up your development - process. You can change the keyboard shortcuts, as well as import - and export keyboard shortcut mapping schemes. - - \li \l{Using External Tools} - - You can use external tools directly from \QC. Qt Linguist, - QML utilities, the default text editor for your system, and the - \c sort tool are preconfigured for use. You can change their default - configurations and configure new tools. - - \endlist - - \section1 Related Topics - - \list - - \li \l{Showing Task List Files in Issues} - - You can load report files created by code scanning and analysis - tools to \l Issues. You can navigate to the corresponding source - code by clicking the error message or by using keyboard shortcuts. - - \li \l{Inspecting Internal Logs} - - You can inspect internal log messages of \QC. They may be - helpful when developing \QC or investigating problems. - - \li \l{Managing Data Collection} - - If you agreed to pseudonymous data collection during \QC - installation, you can turn it on and determine what type - of data is collected and transmitted to the backend storage. - - \endlist - -*/ diff --git a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc index 456777f1542..e891bd8ce89 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-configuring.qdoc @@ -45,25 +45,24 @@ available in \QC. If it does not, you must add the kits yourself to tell \QC where everything is. - To add kits, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits > \uicontrol Add. + To add kits, select \preferences > \uicontrol Kits > \uicontrol Add. - For more information, see \l{Adding Kits}. + For more information, see \l{Add kits}. Each kit consists of a set of values that define one environment, such as a - \l{glossary-device}{device}, compiler, and Qt version. If \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits > \uicontrol {Qt Versions} does not - show all the installed Qt versions, select \uicontrol {Link with Qt}. + \l{glossary-device}{device}, compiler, and Qt version. If \preferences > + \uicontrol Kits > \uicontrol {Qt Versions} does not show all the installed + Qt versions, select \uicontrol {Link with Qt}. If \uicontrol Auto-detected still does not show the Qt version, select \uicontrol {Add} to add it manually. - For more information, see \l{Adding Qt Versions}. + For more information, see \l{Add Qt versions}. - Also check that \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + Also check that \preferences > \uicontrol Kits > \uicontrol {Compilers} shows your compiler. - For more information, see \l{Adding Compilers}. + For more information, see \l{Add compilers}. You can connect devices to the development PC to run, debug, and analyze applications on them from \QC. You can connect the device to the @@ -71,8 +70,8 @@ over a WLAN. You must also configure a connection between \QC and the development PC and specify the device in a kit. - To add devices, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Devices > \uicontrol Devices > \uicontrol Add. + To add devices, select \preferences > \uicontrol Devices > + \uicontrol Devices > \uicontrol Add. For more information, see \l{Connecting Devices}. @@ -81,7 +80,8 @@ You can use \QC with your favorite keyboard shortcuts. To view and edit all keyboard shortcuts defined in \QC, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > + \preferences > + \uicontrol Environment > \uicontrol Keyboard. For more information, see \l{Keyboard Shortcuts}. \section1 Changing Color Schemes @@ -89,9 +89,8 @@ Themes enable you to customize the appearance of the \QC UI: widgets, colors, and icons. - To switch themes, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment, and then select a theme - in the \uicontrol Theme field. + To switch themes, select \preferences > \uicontrol Environment, and then + select a theme in the \uicontrol Theme field. You can use the \QC text and code editors with your favorite color scheme that defines how to highlight code elements and which background color to @@ -99,7 +98,7 @@ ones. The color schemes apply to highlighting C++ files, QML files, and generic files. - To change the color scheme, select \uicontrol Edit > \uicontrol Preferences > + To change the color scheme, select \preferences > \uicontrol {Text Editor} > \uicontrol {Fonts & Color}. For more information, see \l{Defining Color Schemes}. @@ -109,8 +108,8 @@ highlighting engine for Kate syntax definitions. \QC comes with most of the commonly used syntax files, and you can download additional files. - To download and use highlight definition files, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol {Generic Highlighter}. + To download and use highlight definition files, select \preferences > + \uicontrol {Text Editor} > \uicontrol {Generic Highlighter}. For more information, see \l{Generic Highlighting}. @@ -121,7 +120,7 @@ the statement currently under your cursor. You can add, modify, and remove snippets in the snippet editor. - To open the snippet editor, select \uicontrol Edit > \uicontrol Preferences > + To open the snippet editor, select \preferences > \uicontrol {Text Editor} > \uicontrol Snippets. For more information, see \l{Editing Code Snippets}. @@ -133,11 +132,11 @@ \QC. However, some configuration options are available and you can set them in - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Version Control} > - \uicontrol General. + \preferences > + \uicontrol {Version Control} > \uicontrol General. For more information about the supported functions, see - \l{Using Version Control Systems}. + \l{Version Control Systems}. \section1 Managing Plugins diff --git a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc index 91696e03c1c..8e1722f59d0 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc @@ -3,11 +3,15 @@ /*! \page creator-desktop-platforms.html - \previouspage creator-os-supported-platforms.html - \nextpage creator-embedded-platforms.html + \previouspage creator-reference.html + + \ingroup creator-reference \title Desktop Platforms + \brief Requirements for operating systems that you can install and run \QC + on. + \QC is available in binary packages for the following operating systems: \list @@ -66,4 +70,6 @@ To build \QC from the source, see the requirements and instructions in the readme file that is located in the source repository. + + \sa {Supported Platforms} */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc b/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc index c6cc117b1b0..ac5e1cae6b4 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,12 +8,15 @@ // ********************************************************************** /*! - \previouspage creator-known-issues.html \page creator-glossary.html - \nextpage technical-support.html + \previouspage creator-reference.html + + \ingroup creator-reference \title Glossary + \brief \QC terms and concepts. + \table \header \li Term @@ -75,7 +78,7 @@ cross-platform development easier. Each kit consists of a set of values that define one environment, such as a \e {device}, tool chain, Qt version, and debugger command to use. Configure kits at - \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits. + \preferences > \uicontrol Kits. \row \li Mode diff --git a/doc/qtcreator/src/overview/creator-only/creator-help-overview.qdoc b/doc/qtcreator/src/overview/creator-only/creator-help-overview.qdoc deleted file mode 100644 index bf792f5564d..00000000000 --- a/doc/qtcreator/src/overview/creator-only/creator-help-overview.qdoc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -// ********************************************************************** -// NOTE: the sections are not ordered by their logical order to avoid -// reshuffling the file each time the index order changes (i.e., often). -// Run the fixnavi.pl script to adjust the links to the index order. -// ********************************************************************** - -/*! - \page creator-help-overview.html - \previouspage collecting-usage-statistics.html - \nextpage creator-help.html - - \title Getting Help - - \image front-help.png - - \list - - \li \l{Using the Help Mode} - - \QC comes fully integrated with Qt documentation and examples using - the Qt Help plugin. You can add external documentation to the - \uicontrol Help mode and filter the documents displayed to find relevant - information faster. In addition, you can add bookmarks to help - pages. - - \li \l{FAQ} - - Has answers to some frequently asked questions about \QC. - - \li \l{How-to} - - Lists useful \QC features. - - \li \l{Reference} - - Lists the sidebar views and the output views. - - \li \l{Known Issues} - - Lists known issues in \QC version \qtcversion. The development team - is aware of them, and therefore, you do not need to report them as - bugs. - - \li \l{Glossary} - - Lists special terms used in \QC. - - \endlist - - \section1 Related Topics - - \list - - \li \l{Technical Support} - - Lists Qt support sites and other useful sites. - - \li \l{Acknowledgements} - - Lists the third-party components in \QC. - - \endlist - -*/ diff --git a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc index ca3b44304f7..db2af2c5ebf 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-issues.qdoc @@ -74,7 +74,7 @@ \QC or the conflicting shortcut in ibus. To set another \l {Keyboard Shortcuts}{keyboard shortcut} - in \QC, select \uicontrol Edit > \uicontrol Preferences > + in \QC, select \preferences > \uicontrol Environment > \uicontrol Keyboard. To change the ibus shortcut, enter the following command on the @@ -102,7 +102,7 @@ \li If error messages displayed in \l {Compile Output} have paths where slashes are missing (for example, C:QtSDK), check your PATH variable. For more information, see - \l{Troubleshooting MinGW Compilation Errors}. + \l{Troubleshoot MinGW compilation errors}. \endlist diff --git a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc index 58ad65844ac..5b3fbeaecc8 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-mobile-platforms.qdoc @@ -3,11 +3,14 @@ /*! \page creator-mobile-platforms.html - \previouspage creator-embedded-platforms.html - \nextpage creator-project-other.html + \previouspage creator-reference.html + + \ingroup creator-reference \title Mobile Platforms + \brief Mobile platforms that you can develop applications for. + You can develop applications for the following mobile platforms: \list @@ -60,4 +63,5 @@ \li \l{Qt for iOS} \endlist + \sa {Supported Platforms} */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-reference.qdoc b/doc/qtcreator/src/overview/creator-only/creator-reference.qdoc index 22dfff65739..2408b7f1828 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-reference.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-reference.qdoc @@ -8,15 +8,36 @@ \title Reference - The following topics describe the different parts of the UI in detail. + \annotatedlist creator-reference - \section1 Sidebar Views + \section1 Build Systems - \annotatedlist creator-reference-sidebar-views + Most \QC project wizards enable you to choose the build system to use for + building the project: qmake, CMake, Meson, or Qbs. qmake is installed and + configured when you install Qt. To use one of the other supported build + systems, you need to set it up. - \section1 Output Views + \annotatedlist creator-reference-build-systems + + \section1 Preferences + + \annotatedlist creator-reference-preferences + + \section2 C++ + + \annotatedlist creator-reference-preferences-cpp + + \section2 Text Editor + + \annotatedlist creator-reference-preferences-text-editor + + \section1 Views + + \section2 Output Views \annotatedlist creator-reference-output-views - \sa {User Interface}, {Show and hide sidebars}, {View output} + \section2 Sidebar Views + + \annotatedlist creator-reference-sidebar-views */ diff --git a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc index e7da4b12c7e..5ea6d94a0cf 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-supported-platforms.qdoc @@ -1,13 +1,17 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-advanced.html + \page creator-os-supported-platforms.html - \nextpage creator-desktop-platforms.html + \previouspage creator-reference.html + + \ingroup creator-reference \title Supported Platforms + \brief Summary of development and target platforms. + You can install and run \QC on several operating systems to create applications for multiple desktop, embedded, and mobile device platforms, as well as web browsers (experimental). @@ -69,14 +73,7 @@ To develop for UWP using Qt 5, use \QC 7.0, or earlier. \QC automatically runs scheduled checks for updates based on the settings - specified in \uicontrol Edit > \uicontrol Preferences \uicontrol Environment > - \uicontrol Update. + specified in \preferences > \uicontrol Environment > \uicontrol Update. - For more information on the requirements for each platform, see: - - \list - \li \l {Desktop Platforms} - \li \l {Embedded Platforms} - \li \l {Mobile Platforms} - \endlist + \sa {Desktop Platforms}, {Embedded Platforms}, {Mobile Platforms} */ diff --git a/doc/qtcreator/src/overview/creator-tech-support.qdoc b/doc/qtcreator/src/overview/creator-tech-support.qdoc index af87b414bcc..aacee5595f1 100644 --- a/doc/qtcreator/src/overview/creator-tech-support.qdoc +++ b/doc/qtcreator/src/overview/creator-tech-support.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -11,13 +11,18 @@ \page technical-support.html \if defined(qtdesignstudio) \previouspage studio-platforms.html - \else - \previouspage creator-glossary.html - \endif \nextpage creator-acknowledgements.html + \else + \previouspage creator-reference.html + + \endif + + \ingroup creator-reference \title Technical Support + \brief Qt support sites and other useful sites. + The following table lists Qt support sites and other useful links. diff --git a/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc b/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc index e0f2d11ac10..2d427ee15aa 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -8,18 +8,25 @@ \title qmake Build Configuration - \image qtcreator-projectpane.png "qmake build settings" + Specify build settings for the selected \l{glossary-buildandrun-kit} + {build and run kit} in \uicontrol Projects > \uicontrol {Build & Run} + > \uicontrol Build > \uicontrol {Build Settings}. + + \image qtcreator-projectpane.webp {qmake build settings} By default, \QC builds qmake projects in a separate directory from the source directory, as \l{glossary-shadow-build} {shadow builds}. This - keeps the files generated for each \l{glossary-buildandrun-kit} - {build and run kit} separate. If you only build and run with a single - \l{glossary-buildandrun-kit}{kit}, you can deselect the - \uicontrol {Shadow build} checkbox. Select the build directory in the - \uicontrol {Build Directory} field. + keeps the files generated for each kit separate. If you only build and + run with a single kit, you can deselect the \uicontrol {Shadow build} + checkbox. + + Select the build directory in the \uicontrol {Build Directory} field. You + can use the \l{Using Qt Creator Variables}{variables} that are listed when + you select the \inlineimage icons/replace.png (\uicontrol {Variables}) + button. To make in-source builds the default option for all projects, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > + \preferences > \uicontrol {Build & Run} > \uicontrol {Default Build Properties}, and enter a period (.) in the \uicontrol {Default build directory} field. @@ -46,17 +53,22 @@ \section1 Global qmake Settings - To specify settings for all qmake builds, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Build & Run} > - \uicontrol Qmake. + To specify settings for all qmake builds, select \preferences > + \uicontrol {Build & Run} > \uicontrol Qmake. \image qtcreator-preferences-build-run-qmake.png "Qmake tab in Build & Run Preferences" - To set the default build properties, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Build & Run} > - \uicontrol {Default Build Properties}. + To set the default build properties, select \preferences > + \uicontrol {Build & Run} > \uicontrol {Default Build Properties}. - \image qtcreator-build-settings-default.png "Default Build Properties tab in Build & Run Preferences" + \image qtcreator-build-settings-default.webp {Default Build Properties tab in Build & Run Preferences} + + In the \uicontrol {Default build directory} field, you can use the variables + that are listed when you select \inlineimage icons/replace.png, as well as + variables that are available for a particular kit. Those you can see when + you select the button in the \uicontrol Projects > \uicontrol {Build & Run} > + \uicontrol Build > \uicontrol {Build Settings} > \uicontrol {Build Directory} + field for a kit. \section1 Compiling QML @@ -70,9 +82,8 @@ select \uicontrol Enable in the \uicontrol {Qt Quick Compiler} field. To use default settings, select \uicontrol {Leave at Default}. - You can specify default behavior for compiling QML code in \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol Qmake > - \uicontrol {Use qmlcachegen}. + You can specify default behavior for compiling QML code in \preferences > + \uicontrol {Build & Run} > \uicontrol Qmake > \uicontrol {Use qmlcachegen}. \section1 qmake Build Steps diff --git a/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc b/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc index f973cc13b11..d07124bf309 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-custom-output-parser.qdoc @@ -8,12 +8,11 @@ \title Using Custom Output Parsers - Custom output parsers scan command line output for error + Custom output parsers scan command-line output for error and warning patterns that you specify and create entries for found patterns in \l Issues. - To view or add custom output parsers, select - \uicontrol Edit > \uicontrol Preferences > + To view or add custom output parsers, select \preferences > \uicontrol {Build & Run} > \uicontrol {Custom Output Parsers}. \image qtcreator-custom-parser-list.png @@ -26,7 +25,7 @@ You can activate custom output parsers in the \uicontrol {Custom Output Parsers} section of the \uicontrol Build and \uicontrol Run settings, - as well as in the \l{Adding Custom Compilers} + as well as in the \l{Add custom compilers} {custom compiler settings}. \section1 Specifying Settings for Custom Output Parsers @@ -40,9 +39,8 @@ To create a custom output parser: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Build & Run} > \uicontrol {Custom Output Parsers} - > \uicontrol Add. + \li Select \preferences > \uicontrol {Build & Run} > + \uicontrol {Custom Output Parsers} > \uicontrol Add. \li In the \uicontrol {Error message capture pattern} field, specify a regular expression to define what is an error. The custom parser matches the compile output line by line against the diff --git a/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc index 11319133271..21c189a6986 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-files-creating.qdoc @@ -2,121 +2,232 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-creating.html \page creator-file-creating.html - \nextpage creator-project-opening.html + \previouspage creator-how-tos.html - \title Creating Files + \ingroup creator-how-to-projects-files + + \title Create files + + Use wizard templates to add individual files to your \l{Creating Projects} + {projects}. + + To add files to projects, select \uicontrol File > \uicontrol {New File}, and + then select the type of the file. \image qtcreator-new-file.webp {New File wizard} - Use wizard templates to add individual files to your \l{Creating Projects} - {projects}. Select \uicontrol File > \uicontrol {New File} and - select the type of the file: + \note You can also use the \c f locator filter to create a new + file and open it in the editor. For more information, see + \l {Creating Files and Directories from Locator}. However, you must + manually add the created file to a project. - \list - \li \uicontrol {C/C++}: header and source files for new classes. - \li \uicontrol {Modeling}: State Chart XML (SCXML) files, - Universal Modeling Language (UML) style \l {Modeling}{models}, - and scratch models that use a temporary file. - \li \uicontrol {Qt}: source and header files for item, table, + \section1 Select file type + + The following table lists the types of wizard templates that you can use + for creating files. The \uicontrol {New File} dialog shows detailed information about each file + wizard template. + + \table + \header + \li Category + \li Purpose + \row + \li \uicontrol {C/C++} + \li Header and source files for new classes. + \row + \li \uicontrol {Modeling} + \li State Chart XML (SCXML) files, Universal Modeling Language (UML) + style \l {Modeling}{models}, and scratch models that use a temporary + file. + \row + \li \uicontrol {Qt} + \li Source and header files for item, table, or list models, \QD forms and a matching classes for Qt Widgets projects, Qt resource files, as well as QML and JavaScript files for Qt Quick projects. - \li \uicontrol {GLSL}: fragment and vertex shaders. - \li \uicontrol {General}: markdown files, empty files that you can save - with any filename extension, and scratch buffers that use temporary - files. - \li \uicontrol {Java}: class files. - \li \uicontrol {Python}: class and script files for Python projects. - \li \uicontrol {vcpkg} (experimental): \l {Managing Packages with vcpkg} - {vcpkg package manager} manifest files (vcpkg.json). - \li \uicontrol {Nim} (experimental): empty Nim source and script files. + \row + \li \uicontrol {Compiler Explorer} + \li Example setup for using Compiler Explorer to compile C++ or Python + code. + \row + \li \uicontrol {GLSL} + \li OpenGL fragment and vertex shaders. + \row + \li \uicontrol {General} + \li Markdown files, empty files that you can save with any filename + extension, and scratch buffers that use temporary files. + \row + \li \uicontrol {Java} + \li Class files. + \row + \li \uicontrol {Python} + \li Class and script files for Python projects. + \row + \li \uicontrol {vcpkg} (experimental) + \li \l {Managing Packages with vcpkg}{vcpkg package manager} manifest + files (vcpkg.json). + \row + \li \uicontrol {Nim} (experimental) + \li Empty Nim source and script files. + \endtable + + \sa {Create compiler explorer sessions}, {Create C++ classes}, + {Create OpenGL fragment and vertex shaders}, {Create resource files}, + {Use project wizards} +*/ + +/*! + \page creator-how-to-create-cpp-classes.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-files + + \title Create C++ classes + + To create a C++ header and source file for a new class that you can add to a + C++ project: + + \list 1 + \li Select \uicontrol File > \uicontrol {New File} > + \uicontrol {C++ Class Wizard} > \uicontrol Choose. + \li Specify the class name, base class, and header and source files for + the class. + \image qtcreator-cpp-class-wizard.png {Enter Class Name dialog} \endlist - The \uicontrol {New File} dialog shows detailed information about each file - wizard template. - - \section1 C++ Classes - - The \uicontrol {C++ Class Wizard} allows you to create a C++ header and source - file for a new class that you can add to a C++ project. Specify the class - name, base class, and header and source files for the class. - The wizard supports namespaces. To use a namespace, enter a qualified class name in the \uicontrol {Class name} field. For example: \c MyNamespace::MySubNamespace::MyClass. The wizard suggests existing namespaces and class names as you type. - \image qtcreator-cpp-class-wizard.png "Enter Class Name dialog" + You can also create your own project and class wizards. - The names of the header and source file are based on the class name. To - change the default suffix of a file, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {C++} > \uicontrol {File Naming}. + \sa {Create files}, {Set C++ file naming preferences}, {Use project wizards}, + {Custom Wizards} +*/ - \image qtcreator-options-cpp-files.png "C++ File Naming preferences" +/*! + \page creator-how-to-set-cpp-file-naming.html + \previouspage creator-how-tos.html - In the \uicontrol {License template} field, you can use + \ingroup creator-how-to-projects-files + + \title Set C++ file naming preferences + + When you use the new file wizard to create a C++ header and source file for a + new class in a C++ project, the names of the header and source file are based + on the class name. To change the default suffix of a file for a project, + select \uicontrol Projects > \uicontrol {Project Settings} > + \uicontrol {C++ File Naming}. + + \image qtcreator-projects-settings-cpp-file-naming.webp {C++ File Naming settings for a project} + + In the \uicontrol {License template} field, enter \l{Using Variables in Wizards}{predefined wizard variables} to specify the path and filename of the license to use in the source and header files. - You can create your own project and class wizards. For more information, - see \l{Adding New Custom Wizards}. + To globally change the preferences, select \preferences > \uicontrol {C++} > + \uicontrol {File Naming}. - \section1 Resource Files + \sa {Create C++ classes}, {Use project wizards} +*/ - \QC supports the \l{The Qt Resource System}{Qt Resource System}, which is a - platform-independent mechanism for storing files in the application's - executable. +/*! + \page creator-how-to-create-resource-files.html + \previouspage creator-how-tos.html - \image qtcreator-add-resource-wizard.png "New File dialog" + \ingroup creator-how-to-projects-files - The wizard creates a resource collection file (.qrc) that you can manage in - the resource editor. + \title Create resource files - \image qtcreator-add-resource.png "Editing resource files" + The \l{The Qt Resource System}{Qt Resource System} is a platform-independent + mechanism for storing files in the application's executable. - Select \uicontrol {Add Files} to locate and add individual - files. + To create a resource file: + + \list 1 + \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {Qt} > + \uicontrol {Qt Resource File} > \uicontrol Choose. + \image qtcreator-add-resource-wizard.png {New File dialog} + \li Follow the instructions of the wizard to create a resource + collection file (.qrc). + \li Open the resource file in the resource editor. + \image qtcreator-add-resource.png "Editing resource files" + \li Select \uicontrol {Add Files} to locate and add individual files. + \endlist + + \section1 Sort resource files To list the folders and files in ascending alphabetic order in the source tree, select \uicontrol {Sort Alphabetically} in the context menu. - By default, resources are accessible in the application under the same file + \section1 Set a path prefix + + By default, resources in the application are accessible under the same file name as they have in the source tree, with a \c{:/} prefix, or by a URL with a \c qrc scheme. To specify a path prefix for all files in the \c .qrc file, select \uicontrol {Add Prefix} and enter the prefix in the \uicontrol Prefix field. + \section1 Set a locale + Some resources need to change based on the user's locale, such as translation files or icons. You can specify a locale in the \uicontrol Language field. + Use a lowercase, two-letter \l {https://www.iso.org/iso-639-language-codes.html} + {ISO 639 language code}, such as \e de, \e en, or \e fr. + + \section1 Remove resource files + Select \uicontrol Remove to remove the selected file from the resource - collection. In the \uicontrol {Remove File} dialog, select the + collection. + + In the \uicontrol {Remove File} dialog, select the \uicontrol {Delete file permanently} check box to remove the file from - the file system. To remove files that cannot be found in the file system, + the file system. + + \image qtcreator-remove-file.webp {Remove File dialog} + + To remove files that cannot be found in the file system, select \uicontrol {Remove Missing Files}. The above functions are also available in the context menu in the - \uicontrol Projects view. + \l Projects view. - \section1 OpenGL Fragment and Vertex Shaders + \sa {Create files}, {Use project wizards}, {QLocale} +*/ - Qt supports integration with OpenGL implementations on all - platforms, which allows you to display hardware accelerated 3D graphics - alongside a more conventional user interface. For more information, see - \l{Qt GUI}. +/*! + \page creator-how-to-create-opengl-shaders.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-files + + \title Create OpenGL fragment and vertex shaders + + Qt supports integration with OpenGL implementations on all platforms, so you + can display hardware accelerated 3D graphics alongside a more conventional + user interface. For more information, see \l{Qt GUI}. You can use the QOpenGLShader class to compile OpenGL shaders written in the OpenGL Shading Language (GLSL) and in the OpenGL/ES Shading Language (GLSL/ES). QOpenGLShader and QOpenGLShaderProgram shelter you from the - details of - compiling and linking vertex and fragment shaders. + details of compiling and linking vertex and fragment shaders. - You can use \QC code editor to write fragment and vertex shaders - in GLSL or GLSL/ES. The code editor offers syntax highlighting and code - completion for the files. + To create OpenGL shaders: - \image qtcreator-new-opengl-file.png "New OpenGL file wizard" + \list 1 + \li Select \uicontrol File > \uicontrol {New File} > \uicontrol {GLSL}. + \image qtcreator-new-opengl-file.png {New OpenGL file wizard} + \li Select the type of the shader to create, and then select + \uicontrol Choose. + \li Follow the instructions of the wizard to create the shader file. + \li Open the shader file in the code editor, and write the shader code + in GLSL or GLSL/ES. The code editor offers syntax highlighting and + code completion for the files. + \endlist + + \sa {Create files}, {Use project wizards} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc b/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc new file mode 100644 index 00000000000..1181b3a9cdb --- /dev/null +++ b/doc/qtcreator/src/projects/creator-only/creator-how-to-add-wizards.qdoc @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-add-wizards.html + \previouspage creator-project-qmake-libraries.html + \nextpage creator-version-control.html + + \ingroup creator-how-to-projects-create + + \title Add wizards + + \QC has wizards for adding classes, files, and projects that you can copy + and edit to create your own wizards. Create the wizard directory in the + \c {templates/wizards/} directory in your \l{Locating Wizards} + {user settings directory}. + + \image qtcreator-cpp-class-wizard.png + + To create a new wizard: + + \list 1 + + \li Start \QC with the \c {-customwizard-verbose} argument to receive + feedback during wizard development. For more information, see + \l {Verbose Output}. + + \li Set keyboard shortcuts for the \uicontrol Inspect and + \uicontrol {Factory.Reset} actions, as described in + \l {Tips for Wizard Development}. + + \li Copy a directory that contains a wizard and rename it. For example, + copy \c {share/qtcreator/templates/wizards/classes/cpp} as + \c {$HOME/.config/QtProject/qtcreator/templates/wizards/classes/mycpp}. + + \li Use the \uicontrol {Factory.Reset} action to make the wizard appear + in \uicontrol File > \uicontrol {New File} without + restarting \QC. + + \li Open the wizard configuration file, \c {wizard.json} for editing, as + described in \l {Custom Wizards}. + + \li Change the \c id to something unique. Wizards are sorted by the ID in + alphabetic order within a \c category. You can use a leading letter + to specify the position of the wizard. For example, \c B.MyClass. + \endlist + +\sa {Custom Wizards}, {Find settings files} +*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-how-to-create-compiler-explorer-setup.qdoc b/doc/qtcreator/src/projects/creator-only/creator-how-to-create-compiler-explorer-setup.qdoc new file mode 100644 index 00000000000..08bc4764434 --- /dev/null +++ b/doc/qtcreator/src/projects/creator-only/creator-how-to-create-compiler-explorer-setup.qdoc @@ -0,0 +1,101 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-create-compiler-explorer-sessions.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-files + + \title Create compiler explorer sessions + + To create a compiler explorer session for a C++ or Python project: + + \list 1 + \li Select \uicontrol File > \uicontrol {New File} > + \uicontrol {Compiler Explorer} > \uicontrol C++ or \uicontrol Python + > \uicontrol Choose. + \li Follow the instructions of the wizard to create an example + compiler explorer session as a JSON-based \c .qtce file and to open + it. + \endlist + + \note Enable the Compiler Explorer plugin to use it. + + \sa {Create files}, {Enable and disable plugins}, {Explore compiler code} +*/ + +/*! + \page creator-how-to-explore-compiler-code.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build + + \title Explore compiler code + + With the \l{https://godbolt.org/}{Compiler Explorer}, you can enter code to + explore the capabilities of your compilers and interpreters. \QC supports + all languages, compilers, and libraries that Compiler Explorer supports. You + can save your compiler explorer session as a JSON-based \c .qtce file. + + \image qtcreator-compiler-explorer.webp {Python code in the compiler explorer} + + \note Enable the Compiler Explorer plugin to use it. + + To check how a compiler sees C++ or Python code: + + \list 1 + \li Select \uicontrol Tools > \uicontrol {Compiler Explorer} > + \uicontrol {Open Compiler Explorer}. + \li In the \uicontrol Language field, select the language to compile. + \li In the \uicontrol Compiler field, select a compiler to see the + assembly code. + \li Enter code to see the resulting assembly code. + \endlist + + You can also see the application status and output. + + To explore several compilers, select \uicontrol {Add Compiler}. + + \section1 Set compiler options + + Select \inlineimage icons/settings.png + to set options for the selected compiler. + + \image qtcreator-compiler-explorer-options.webp {Compiler Explorer options} + + \table + \header + \li Setting + \li Value + \row + \li Compiler options + \li Arguments passed to the compiler. Add options to enable optimization + and to change other aspects of the compilation. For example, the + value \c -O3 enables the optimizer with aggressive optimization. + \row + \li Libraries + \li Select \uicontrol Edit to add the libraries that your code links + against. Start typing in the field to find a particular library. + The selection is saved only after you select the version of the + library next to the library name. + \row + \li Execute the code + \li Execute the resulting executable. + \row + \li Compile to binary object + \li Convert the source code to a binary executable. + \row + \li Intel asm syntax + \li Use the Intel ASM syntax for writing the assembly code. + \row + \li Demangle identifiers + \li Extract and demangle identifiers from mangled symbols. + \e {Name mangling} adds descriptive data to a function's identifier + at link time. The data indicates which namespace and object a + function belongs to and which arguments it handles in which order. + \endtable + + \sa {Create compiler explorer sessions}, {Enable and disable plugins}, + {Add compilers}, {Add custom compilers} +*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-how-to-edit-qbs-profiles.qdoc b/doc/qtcreator/src/projects/creator-only/creator-how-to-edit-qbs-profiles.qdoc new file mode 100644 index 00000000000..a5037be5b91 --- /dev/null +++ b/doc/qtcreator/src/projects/creator-only/creator-how-to-edit-qbs-profiles.qdoc @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-edit-qbs-profiles.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-build + + \title Edit Qbs profiles + + To view the Qbs profile associated with a kit, select \preferences > + \uicontrol Qbs > \uicontrol Profiles. + + \image creator-qbs-profiles.png "Qbs Profiles tab" + + You can add keys and values to the profile or remove them from it, as well + as modify existing values. For a list of available keys and values, see + \l{http://doc.qt.io/qbs/list-of-modules.html}{List of Modules} in the Qbs + Manual. + + To edit the Qbs profile associated with a kit: + + \list 1 + \li In \preferences > \uicontrol Kits, select + the kit, and then select \uicontrol Change next to the + \uicontrol {Additional Qbs Profile Settings} field to open the + \uicontrol {Custom Properties} dialog. + \image qtcreator-qbs-profile-settings.png "Custom Properties dialog" + \li Double-click an empty cell in the \uicontrol Key column to specify + the key to add or modify as: \c .. + \li Double-click the cell on the same row in the \uicontrol Value column + to specify a value as a JSON literal. + \li Select \uicontrol Add to add the key-value pair. + \li Click \uicontrol OK. + \endlist + + To modify an existing value, double-click it in the \uicontrol Value field. + + To remove the selected property, select \uicontrol Remove. + + \sa {Activate kits for a project}, {Add kits}, {Kits} +*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-how-to-select-build-systems.qdoc b/doc/qtcreator/src/projects/creator-only/creator-how-to-select-build-systems.qdoc new file mode 100644 index 00000000000..38d0cb73d72 --- /dev/null +++ b/doc/qtcreator/src/projects/creator-only/creator-how-to-select-build-systems.qdoc @@ -0,0 +1,60 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \previouspage creator-project-creating.html + \page creator-how-to-select-build-system.html + \nextpage creator-file-creating.html + + \ingroup creator-how-to-projects-create + + \title Select the build system + + You can use several build systems to build your projects: + + \list + \li \l {Build with CMake}{CMake} is a cross-platform system for build + automation that helps simplify the build process for development + projects across different platforms. It automates the generation of + build configurations. For more information, see \l {CMake}. + \li \l{qmake Manual}{qmake} is an alternative to CMake for automating the + generation of build configurations. Qt installers install and + configure qmake. To use one of the other supported build systems, + you need to set it up. + \li \l {https://mesonbuild.com/}{Meson} is a fast and user-friendly + open-source build system that aims to minimize the time developers spend + writing or debugging build definitions and waiting for the build system + to start compiling code. For more information, see \l {Setting Up Meson}. + \li \l{Qbs Manual}{Qbs} is an all-in-one build tool that generates a build graph + from a high-level project description (like qmake or CMake do) and executes + the commands in the low-level build graph (like make does). For more + information, see \l{Setting Up Qbs}. + \endlist + + Typically, you select the build system when you create a project. + + \section1 Migrate to another build system + + To export a project to some other build system, such as Microsoft Visual + Studio, select \uicontrol Build > \uicontrol {Run Generator}, and select + a generator in the list. \QC generates the build files, such as .vcxproj, + in the project's build directory. + + The tool that you use to build the project (qmake or CMake) provides the + generators. Their availability depends on the version of the build tool, + host platform, and properties of the host system. + Also, a JSON compilation database generator is available if the + \l{Parsing C++ Files with the Clang Code Model}{Clang Code Model plugin} + is enabled (default). + + \section1 Set preferences for building and running + + To change the location of the project directory, and to specify settings + for building and running projects, select \preferences > + \uicontrol {Build & Run} > \uicontrol General. + + Specify build and run settings for different target platforms, in the + \uicontrol Projects mode. + + \sa {Specifying Build Settings}, {Build Systems}, {Use project wizards} +*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-autotools.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-autotools.qdoc index 31cc7ba2987..7cf87b25684 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-autotools.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-autotools.qdoc @@ -1,18 +1,20 @@ -// Copyright (C) 2018 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-qbs.html \page creator-projects-autotools.html - \nextpage creator-project-generic.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up an Autotools Project - The AutotoolsProjectManager is a plugin for autotools support. It is - disabled by default. To enable the plugin, select \uicontrol Help > - \uicontrol {About Plugins} > \uicontrol {Build Systems} > - \uicontrol AutotoolsProjectManager. Then select \uicontrol {Restart Now} - to restart \QC and load the plugin. + \brief \QC can open projects that use the Autotools build system. You can + build and run the projects directly from \QC. + + The AutotoolsProjectManager is a plugin for autotools support. + + \note Enable the Autotools plugin to use it. To work with your Autotools project in \QC: @@ -43,4 +45,6 @@ \image qtcreator-autotools-buildsettings.png \endlist + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc index 66bd2ce2b73..77e9263a323 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-build-run-tutorial.qdoc @@ -34,10 +34,10 @@ \li In the \uicontrol Welcome mode, select \uicontrol Examples (1). - \image qtcreator-gs-build-example-open.png "Selecting an example" + \image qtcreator-examples-open.webp {Selecting an example} If you cannot see any examples, check that the list of - \l{Adding Qt Versions}{Qt versions} (2) is not empty. If + \l{Add Qt versions}{Qt versions} (2) is not empty. If you select a Qt for Android or iOS, you can only see the examples that run on Android or iOS. @@ -52,11 +52,11 @@ \l{glossary-buildandrun-kit}{kit} for the device. - \image qtcreator-gs-build-example-kit-selector.png "Selecting a kit to build with" + \image qtcreator-examples-kit-selector.webp {Selecting a kit to build with} If you installed \QC as part of a Qt installation, it should have automatically detected the installed kit. If you cannot see any kits, - see \l{Adding Kits}. + see \l{Add kits}. \li Click \inlineimage icons/run_small.png (\uicontrol Run) to build and run the application. @@ -65,7 +65,7 @@ \l {Compile Output}. If build errors occur, check that you have a Qt version, a - \l{Adding Compilers}{compiler}, and the necessary kits installed. If + \l{Add compilers}{compiler}, and the necessary kits installed. If you are building for an \l{Connecting Android Devices}{Android device} or \l{Connecting iOS Devices}{iOS device}, check that you set up the development environment correctly. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc deleted file mode 100644 index 05a340d0df2..00000000000 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-build-systems.qdocinc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -// ********************************************************************** -// NOTE: the sections are not ordered by their logical order to avoid -// reshuffling the file each time the index order changes (i.e., often). -// Run the fixnavi.pl script to adjust the links to the index order. -// ********************************************************************** - -/*! -//! [build systems] - - \section1 Selecting the Build System - - You can use several build systems to build your projects: - - \list - - \li \l{qmake Manual}{qmake} is a cross-platform system for build automation - that helps simplify the build process for development projects across - different platforms. qmake automates the generation of build configurations - so that you need only a few lines of information to create each - configuration. Qt installers install and configure qmake. - To use one of the other supported build systems, you need to set it up. - - \li \l {Build with CMake}{CMake} is an alternative to qmake for automating the - generation of build configurations. For more information, see - \l {Setting Up CMake}. - - \li \l {https://mesonbuild.com/}{Meson} is a fast and user-friendly - open-source build system that aims to minimize the time developers spend - writing or debugging build definitions and waiting for the build system - to start compiling code. For more information, see \l {Setting Up Meson}. - - \li \l{Qbs Manual}{Qbs} is an all-in-one build tool that generates a build graph - from a high-level project description (like qmake or CMake do) and executes - the commands in the low-level build graph (like make does). For more - information, see \l{Setting Up Qbs}. - - \endlist - - To export a project to some other build system, such as Microsoft Visual - Studio, select \uicontrol Build > \uicontrol {Run Generator}, and select - a generator in the list. \QC generates the build files, such as .vcxproj, - in the project's build directory. The tool that you use to build the project - (qmake or CMake) provides the generators. Their availability depends on the - version of the build tool, host platform, and properties of the host system. - Also, a JSON compilation database generator is available if the - \l{Parsing C++ Files with the Clang Code Model}{Clang Code Model plugin} - is enabled (default). - - To change the location of the project directory, and to specify settings - for building and running projects, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General. - - Specify build and run settings for different target platforms, in the - \uicontrol Projects mode. For more information on the options you have, - see \l{Specifying Build Settings}. - -//! [build systems] -*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc index acf362877d0..6a5c085fd47 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc @@ -98,21 +98,20 @@ \section1 Building with CMake \QC automatically runs CMake when you make changes to \c {CMakeLists.txt} - files. To disable this feature, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol CMake > \uicontrol General. Select the - CMake executable to edit, and then deselect the \uicontrol {Autorun CMake} - check box. + files. To disable this feature, select \preferences > \uicontrol CMake > + \uicontrol General. Select the CMake executable to edit, and then deselect + the \uicontrol {Autorun CMake} check box. \image qtcreator-preferences-cmake-tools.webp "Tools tab in CMake Preferences" - For more information, see \l {Setting Up CMake}. + For more information, see \l {CMake}. \section1 Building with qmake To prevent failures on incremental builds, it might make sense to always run qmake before building, even though it means that - building will take more time. To enable this option, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol Qmake > + building will take more time. To enable this option, select \preferences > + \uicontrol {Build & Run} > \uicontrol Qmake > \uicontrol {Run qmake on every build}. \image qtcreator-preferences-build-run-qmake.png "qmake tab in Build & Run Preferences" diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-builds-customizing.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-builds-customizing.qdoc index 0a7c1df2ac6..48f1bf87611 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-builds-customizing.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-builds-customizing.qdoc @@ -15,8 +15,7 @@ \title Customizing the Build Process To configure how projects are built, deployed, and run, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} - > \uicontrol General. + \preferences > \uicontrol {Build & Run} > \uicontrol General. \image qtcreator-project-options-deploy.png "Project General preferences" diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc index b156fef7ac0..b95dfa284dd 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,21 +8,23 @@ // ********************************************************************** /*! - \previouspage creator-project-qmake.html \page creator-tool-chains.html - \nextpage creator-debuggers.html + \previouspage creator-how-tos.html - \title Adding Compilers + \ingroup creator-how-to-manage-kits - Qt is supported on a variety of 32-bit and 64-bit platforms, and can - usually be built on each platform with GCC, a vendor-supplied compiler, or - a third party compiler. In \QC, a \l{glossary-buildandrun-kit}{kit} - specifies the compiler and other necessary tools for building an application - for and running it on a particular platform. + \title Add compilers + + You can develop Qt applications on several 32-bit and 64-bit platforms. + Usually, you can build Qt applications on each platform with GCC, a + vendor-supplied compiler, or a third party compiler. In \QC, a + \l{glossary-buildandrun-kit}{kit} specifies the compiler and other + necessary tools for building an application for and running it on a + particular platform. \QC automatically detects the compilers that your system or \QOI - registers and lists them in \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits > \uicontrol Compilers. + registers and lists them in \preferences > \uicontrol Kits > + \uicontrol Compilers. \image qtcreator-toolchains.png @@ -32,65 +34,7 @@ \l{https://trac.macports.org/wiki/howto/ccache}{How to enable ccache} in the MacPorts wiki. - You can add the following compilers to build applications by using other - compilers or by using additional versions of the automatically detected - compilers: - - \list - - \li Clang is a C, C++, Objective C, and Objective C++ front-end for the - LLVM compiler for Windows, Linux, and \macos. - - \li \l{https://clang.llvm.org/docs/UsersManual.html#clang-cl}{clang-cl} - is an alternative command-line interface to Clang that is compatible - with the Visual C++ compiler, \c cl.exe. - - \li GNU Compiler Collection (GCC) is a compiler for Linux and - \macos. - - \li ICC (Intel C++ Compiler) is a group of C and C++ compilers. - Only the GCC-compatible variant, available for Linux and \macos, - is currently supported by \QC. - - \li \MinGW (Minimalist GNU for Windows) is a native software port of GCC - and GNU Binutils for use in the development of native Microsoft - Windows applications on Windows. \MinGW is distributed together with - \QC and Qt for Windows. - - \li MSVC (Microsoft Visual C++ Compiler) is a C++ compiler that is - installed with Microsoft Visual Studio. - - \li Nim is the Nim Compiler for Windows, Linux, and \macos. - - \li QCC is the interface for compiling C++ applications for QNX. - - \endlist - - In addition, the \QC Bare Metal Device plugin supports the - following compilers: - - \list - - \li \l{https://www.iar.com/iar-embedded-workbench/}{IAREW} is a group of - C and C++ bare-metal compilers from the various IAR Embedded Workbench - development environments. - Currently supported architectures are \c 8051, \c AVR, \c ARM, - \c STM8, and \c MSP430. - - \li \l{https://www.keil.com}{KEIL} is a group of C and C++ bare-metal - compilers from the various KEIL development environments. - Currently supported architectures are \c 8051 and \c ARM. - - \li \l{http://sdcc.sourceforge.net}{SDCC} is an optimizing - C bare-metal compiler for various architectures. - Currently supported architectures are \c 8051 and \c STM8. - - \endlist - - The emscripten compiler is tool chain for compiling to - \l{Building Applications for the Web}{WebAssembly}. - - \section1 Re-detecting Compilers + \section1 Re-detect compilers When \QC finds an x86_64 GCC compiler, it sets up an instance for the native x86_64 target. If you plan to create also 32-bit x86 binaries without using @@ -102,132 +46,93 @@ To remove manually added compilers, select \uicontrol Remove or \uicontrol {Remove All}. - \section1 Specifying Compiler Settings + \section1 Add C or C++ compilers - To build an application using GCC, \MinGW, Clang, or QCC, specify the path - to the directory where the compiler is located and select - the application binary interface (ABI) version from the list of available - versions. You can also create a custom ABI definition. - For QCC, also specify the path to the QNX Software Development Platform - (SDP) in the \uicontrol {SPD path} field. - - To enable Microsoft Visual C++ Compilers (MSVC) and clang-cl to find system - headers, libraries, and the linker, \QC executes them inside a command - prompt where you set up the environment using \c {vcvarsall.bat}. For - these compilers, you also specify the path to the script that sets up the - command prompt in the \uicontrol Initialization field. - - You specify the compiler to use for each kit in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits. - - To add a C or C++ compiler, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits > \uicontrol Compilers > \uicontrol Add. Select a compiler - in the list, and then select \uicontrol C or \uicontrol C++. - - To clone the selected compiler, select \uicontrol Clone. - - The settings to specify depend on the compiler: - - \list - - \li In the \uicontrol Name field, enter a name for the compiler to - identify it in \QC. - - \image qtcreator-options-cpp-compilers.png "Adding a clang-cl compiler" - - \li In the \uicontrol Initialization field, select the - \c {vcvarsall.bat} file for setting up the command - prompt to use. - - \li In the \uicontrol {Compiler path} field, enter the path to the - directory where the compiler is located. - - \li In the \uicontrol {Platform codegen flags} field, check the flags passed - to the compiler that specify the architecture on the target - platform. - - \li In the \uicontrol {Platform linker flags} field, check the flags passed to - the linker that specify the architecture on the target platform. - The linker flags are used only when building with Qbs. - - \image qtcreator-options-clang-compilers.png "Adding a Clang compiler" - - \li In the \uicontrol {Parent toolchain} field, select a \MinGW - compiler, which is needed because Clang does not have its own - standard library. - - \li In the \uicontrol {SPD path} field, specify the path to the QNX - Software Development Platform (SDP). - - \image qtcreator-options-qcc-compilers.png "Adding a QCC compiler" - - \li In the \uicontrol ABI field, enter an identifier for the - target architecture. This is used to warn about ABI mismatches - within the kits. - - \li In the \uicontrol {Target triple} field, specify the GCC target - architecture. If code model services fail because - Clang does not understand the target architecture, select - \uicontrol {Override for code model}. - - \image qtcreator-compilers-target-triple.png "Target triple field" - - \endlist - - \section1 Adding Nim Compilers - - To build an application using the Nim Compiler, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits > \uicontrol Compilers > - \uicontrol Add > \uicontrol Nim, and specify the path to the directory where - the compiler is located. - - \section1 Adding Custom Compilers - - To add a compiler that is not listed above or a remote compiler, use the - \uicontrol Custom option and specify the paths to the directories where the - compiler and make tool are located and options for the compiler. - - \image creator-compilers-custom.png - - To add other compilers: + To add a C or C++ compiler: \list 1 - - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > - \uicontrol Compilers > \uicontrol Add > \uicontrol Custom > - \uicontrol C or \uicontrol C++. - - \li In the \uicontrol Name field, enter a name for the compiler. - - \li In the \uicontrol {Compiler path} field, enter the path to the directory - where the compiler is located. - - \li In the \uicontrol {Make path} field, enter the path to the directory where - the make tool is located. - - \li In the \uicontrol ABI field, specify the ABI version. - - \li In the \uicontrol {Predefined macros} field, specify the macros that the - compiler enables by default. Specify each macro on a separate line, - in the following format: MACRO[=value]. - - \li In the \uicontrol {Header paths} field, specify the paths to directories - that the compiler checks for headers. Specify each path on a - separate line. - - \li In the \uicontrol {C++11 flags} field, specify the flags that turn on - C++11 support in the compiler. - - \li In the \uicontrol {Qt mkspecs} field, specify the path to the directory - where mkspecs are located. Usually, the path is specified relative - to the Qt mkspecs directory. - - \li In the \uicontrol {Error parser} field, select the error parser to use. - You can add custom output parsers to the list. For more information, - see \l{Using Custom Output Parsers}. + \li Select \preferences > \uicontrol Kits > \uicontrol Compilers. + \li Select \uicontrol Add to add a new compiler or \uicontrol Clone to + add another version of the selected compiler. + \li Select a compiler in the list. + \li Select \uicontrol C or \uicontrol C++. + \li Set \l{Compilers}{preferences} according to the selected compiler. \endlist - \section1 Troubleshooting \MinGW Compilation Errors + \section1 Supported compilers + + You can add the following compilers to build applications by using other + compilers or by using other versions of the automatically detected + compilers. + + \table + \header + \li Compiler + \li Description + \row + \li Clang + \li A C, C++, Objective C, and Objective C++ front-end for the + LLVM compiler for Windows, Linux, and \macos. + \row + \li \l{https://clang.llvm.org/docs/UsersManual.html#clang-cl}{clang-cl} + \li An alternative command-line interface to Clang that is compatible + with the Visual C++ compiler, \c cl.exe. + \row + \li GCC (GNU Compiler Collection) + \li A compiler for Linux and \macos. + \row + \li ICC (Intel C++ Compiler) + \li A group of C and C++ compilers. Only the GCC-compatible variant, + available for Linux and \macos, is currently supported by \QC. + \row + \li MinGW (Minimalist GNU for Windows) + \li A native software port of GCC and GNU Binutils for use in the + development of native Microsoft Windows applications on Windows. + \MinGW is distributed together with \QC and Qt for Windows. + \row + \li MSVC (Microsoft Visual C++ Compiler) + \li A C++ compiler that is installed with Microsoft Visual Studio. + \row + \li Nim + \li The Nim Compiler for Windows, Linux, and \macos. + \row + \li QCC + \li The interface for compiling C++ applications for QNX. + \row + \li Custom + \li Other than the listed compilers and remote compilers. + \endtable + + The emscripten compiler is tool chain for compiling to + \l{Building Applications for the Web}{WebAssembly}. + + \section2 Bare-metal compilers + + In addition, the \QC Bare Metal Device plugin supports the + following compilers. + + \table + \header + \li Compiler + \li Description + \row + \li \l{https://www.iar.com/iar-embedded-workbench/}{IAREW} + \li A group of C and C++ bare-metal compilers from the various + IAR Embedded Workbench development environments. + Currently supported architectures are \c 8051, \c AVR, \c ARM, + \c STM8, and \c MSP430. + \row + \li \l{https://www.keil.com}{KEIL} + \li A group of C and C++ bare-metal compilers from the various KEIL + development environments. + Currently supported architectures are \c 8051 and \c ARM. + \row + \li \l{http://sdcc.sourceforge.net}{SDCC} + \li An optimizing C bare-metal compiler for various architectures. + Currently supported architectures are \c 8051 and \c STM8. + \endtable + + \section1 Troubleshoot \MinGW compilation errors If error messages displayed in \l {Compile Output} contain paths where slashes are missing (for example, \c {C:QtSDK}), @@ -263,4 +168,160 @@ You can use the shell link to run the tools in the third-party tool chains. + \sa {Compilers}, {Add Nim compilers}, {Add custom compilers}, + {Connecting Bare Metal Devices}, {Supported Platforms} +*/ + +/*! + \page creator-preferences-kits-compilers.html + \previouspage creator-reference.html + + \ingroup creator-reference-preferences + + \title Compilers + + \brief Lists the registered compilers. You can add custom compilers to the + list. + + To build an application using GCC, \MinGW, Clang, or QCC, specify the path + to the directory where the compiler is located and select + the application binary interface (ABI) version from the list of available + versions. You can also create a custom ABI definition. + For QCC, also specify the path to the QNX Software Development Platform + (SDP) in the \uicontrol {SPD path} field. + + To enable Microsoft Visual C++ Compilers (MSVC) and clang-cl to find system + headers, libraries, and the linker, \QC executes them inside a command + prompt where you set up the environment using \c {vcvarsall.bat}. For + these compilers, you also specify the path to the script that sets up the + command prompt in the \uicontrol Initialization field. + + You specify the compiler to use for each kit in \preferences > + \uicontrol Kits. + + To set compiler preferences according to the compiler type, select + \preferences > \uicontrol Kits > \uicontrol Compilers: + + \list + + \li In the \uicontrol Name field, enter a name for the compiler to + identify it in \QC. + + \image qtcreator-options-cpp-compilers.png {Adding a clang-cl compiler} + \caption Adding a clang-cl compiler. + + \li In the \uicontrol Initialization field, select the + \c {vcvarsall.bat} file for setting up the command + prompt to use. + + \li In the \uicontrol {Compiler path} field, enter the path to the + directory where the compiler is located. + + \li In the \uicontrol {Platform codegen flags} field, check the flags passed + to the compiler that specify the architecture on the target + platform. + + \image qtcreator-options-clang-compilers.png {Adding a Clang compiler} + \caption Adding a Clang compiler. + + \li \b {When building with Qbs}: In the \uicontrol {Platform linker flags} + field, check the flags passed to the linker that specify the + architecture on the target platform. + + \li In the \uicontrol {Parent toolchain} field, select a \MinGW + compiler, which is needed because Clang does not have its own + standard library. + + \li In the \uicontrol {SPD path} field, specify the path to the QNX + Software Development Platform (SDP). + + \image qtcreator-options-qcc-compilers.png {Adding a QCC compiler} + \caption Adding a QCC compiler. + + \li In the \uicontrol ABI field, enter an identifier for the + target architecture. This is used to warn about ABI mismatches + within the kits. + + \li In the \uicontrol {Target triple} field, specify the GCC target + architecture. If code model services fail because + Clang does not understand the target architecture, select + \uicontrol {Override for code model}. + + \image qtcreator-compilers-target-triple.png {Target triple field} + \caption Target triple field. + \endlist + + \sa {Add compilers}, {Add custom compilers}, {Add Nim compilers}, + {Supported Platforms} +*/ + +/*! + \page creator-how-to-add-nim-compilers.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-manage-kits + + \title Add Nim compilers + + To build an application using the Nim Compiler, select \preferences > + \uicontrol Kits > \uicontrol Compilers > \uicontrol Add > \uicontrol Nim, + and specify the path to the directory where the compiler is located. + + \sa {Add compilers}, {Compilers} +*/ + +/*! + \page creator-how-to-add-custom-compilers.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-manage-kits + + \title Add custom compilers + + To add a compiler that is not listed \preferences > + \uicontrol Kits > \uicontrol Compilers or to add a remote compiler, use the + \uicontrol Custom option. Specify the paths to the directories where the + compiler and make tool are located and set preferences for the compiler. + + \image creator-compilers-custom.png + + To add other compilers: + + \list 1 + + \li Select \preferences > \uicontrol Kits > + \uicontrol Compilers > \uicontrol Add > \uicontrol Custom > + \uicontrol C or \uicontrol C++. + + \li In the \uicontrol Name field, enter a name for the compiler. + + \li In the \uicontrol {Compiler path} field, enter the path to the directory + where the compiler is located. + + \li In the \uicontrol {Make path} field, enter the path to the directory where + the make tool is located. + + \li In the \uicontrol ABI field, specify the ABI version. + + \li In the \uicontrol {Predefined macros} field, specify the macros that the + compiler enables by default. Specify each macro on a separate line, + in the following format: MACRO[=value]. + + \li In the \uicontrol {Header paths} field, specify the paths to directories + that the compiler checks for headers. Specify each path on a + separate line. + + \li In the \uicontrol {C++11 flags} field, specify the flags that turn on + C++11 support in the compiler. + + \li In the \uicontrol {Qt mkspecs} field, specify the path to the directory + where mkspecs are located. Usually, the path is specified relative + to the Qt mkspecs directory. + + \li In the \uicontrol {Error parser} field, select the error parser to use. + You can add custom output parsers to the list. + \endlist + + \sa {Add compilers}, {Add Nim compilers}, {Compilers}, + {Using Custom Output Parsers} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc index 77164799119..c94870b6db1 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc @@ -10,11 +10,13 @@ /*! \previouspage creator-project-managing.html \page creator-project-creating.html - \nextpage creator-file-creating.html + \nextpage creator-project-qmake-libraries.html + + \ingroup creator-explanation-projects \title Creating Projects - Creating a project enables you to: + Create projects to: \list @@ -28,32 +30,55 @@ \endlist + To set up a project, you first have to decide what kind of an application + you want to develop: do you want a user interface based on Qt Quick or + Qt widgets. Second, you have to choose the language to implement the + application logic: C++, JavaScript, or Python. + When you set up a new project in \QC, a wizard guides you step-by-step through the process. The wizard templates prompt you to enter the settings - that you need for that particular type of project and create - the necessary files for you. You can add your own custom wizards to - standardize the way of adding subprojects and classes to a project. + that you need for that particular type of project and create the necessary + files for you. - Most \QC project wizards enable you to choose the build system to use for + \image qtcreator-project-qt-quick.webp {New Project dialog} + + You can add your own custom wizards to standardize the way of adding + subprojects and classes to a project. In the \uicontrol {New Project} and + \uicontrol {New File} dialogs you can see an icon (1), a display name (2), + and a description (3) of the wizard. + + \image qtcreator-custom-wizard.png {Wizard details in the New Project dialog} + + In most project wizards, you can choose the build system to use for building the project: qmake, CMake, or Qbs. If you do not get to choose, the project uses qmake as the build system. + \image qtcreator-new-project-build-system-qt-gui.png {Define Build System dialog} + You can use wizards also to create plain C or C++ projects that use qmake, Qbs, or CMake, but do not use the Qt library. In addition, you can import projects as \e {generic projects} that do not - use qmake, Qbs, or CMake. This enables you to use \QC as a code editor and - to fully control the steps and commands used to build the project. + use qmake, Qbs, or CMake. Use \QC as a code editor and fully control the + steps and commands used to build the project. - You can install tools for \l{glossary-device}{devices} as part of Qt distributions. + To test applications on \l{glossary-device}{devices}, you can install + toolchains for mobile and embedded development as part of Qt distributions. The installers create \l{glossary-buildandrun-kit}{kits} and specify build and run settings for the installed device types. However, you might need to install and configure some additional software on the devices to be able to \l{Connecting Devices}{connect} to them from the development PC. - \include creator-projects-build-systems.qdocinc build systems + \sa {Manage Projects}{How To: Manage Projects}, {Custom Wizards} +*/ - \section1 Using Project Wizards +/*! + \page creator-how-to-use-project-wizards.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-create + + \title Use project wizards To create a new project, select \uicontrol File > \uicontrol{New Project} and select the type of your project. The contents of the wizard dialogs depend @@ -75,7 +100,7 @@ For example, if you choose to create a Qt Quick application, \QC generates a QML file that you can modify in the \uicontrol Edit mode. - \section1 Selecting Project Type + \section1 Select project type The following table lists the types of wizard templates that you can use for creating projects. The \uicontrol {New Project} dialog shows detailed @@ -101,7 +126,7 @@ widgets or widget collections, \l{Qt Quick UI Projects}{Qt Quick UI projects}, \l {Creating Tests}{auto-test projects}, - \l{Adding Subprojects to Projects}{subprojects}, + \l{Add subprojects to projects}{subprojects}, empty qmake projects, or qmake projects for testing code snippets. \row @@ -110,7 +135,7 @@ {Nim or Nimble} applications (experimental) \row \li Imported project - \li Import projects from a supported \l{Using Version Control Systems} + \li Import projects from a supported \l{Version Control Systems} {version control system}, such as Bazaar, CVS, Git, Mercurial, or Subversion. @@ -133,9 +158,28 @@ For more information about creating Qt Quick projects, see \l {Creating Qt Quick Projects}. - \include creator-python-project.qdocinc python project wizards + \section1 Bind keyboard shortcuts to wizards - \section1 Specifying Project Contents + If you use a wizard regularly, you can bind a custom keyboard shortcut to + it. Triggering this keyboard shortcut directly opens the wizard, so you do + not need to navigate to \uicontrol File > \uicontrol {New File} or + \uicontrol {New Project}. + + Set keyboard shortcuts for wizards in \preferences > \uicontrol Environment > + \uicontrol Keyboard > \uicontrol Wizard. All wizard actions start with + \uicontrol Impl there. + + \sa {Assign keyboard shortcuts}, {Activate kits for a project}, + {Create files}, {Creating Projects} +*/ + +/*! + \page creator-how-to-specify-project-contents.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-configure + + \title Specify project contents A project can have files that should be: @@ -148,8 +192,8 @@ to \QC \endlist - \QC displays all files that you declare to be part of the project by the - project files in the \l Projects view. It sorts the files into categories + The \l Projects view shows all files that you declare to be part of the + project in the project files. It sorts the files into categories by file type (.cpp, .h, .qrc, and so on). To display additional files, edit the project file. Alternatively, you can see all the files in a project directory in the \l {File System} view. @@ -158,7 +202,7 @@ \l{Searching with the Locator}{locator} and \l{Advanced Search} {project-wide search}. - \section2 CMake Projects + \section1 CMake Projects When using CMake, you can specify additional files for a project by either adding them as sources or installing them. @@ -179,7 +223,7 @@ Alternatively, to install the files, use the \l {CMake: install command} {install} command with the \c FILES or \c DIRECTORY property. - \section2 qmake Projects + \section1 qmake Projects Use the following variables in the .pro file: @@ -199,24 +243,33 @@ \endcode - \section1 Adding Subprojects to Projects + \sa {Creating Projects}, {Use project wizards}, {Projects} - In addition to Qt libraries, you can link your application to other - libraries, such as system libraries or your own libraries. Further, your - own libraries might link to other libraries. To be able to compile your - project, you must add the libraries to your project. This also enables - code completion and syntax highlighting for the libraries. - The procedure of adding a library to a project depends on the build +*/ + +/*! + \page creator-how-to-add-subprojects-to-projects.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-create + + \title Add subprojects to projects + + You can link your application to Qt libraries and other libraries, such as + system libraries or your own libraries. Further, your own libraries might + link to other libraries. To compile your project and benefit from services + such as code completion and syntax highlighting, add the libraries to your + project. The process of adding a library to a project depends on the build system that you use. - \section2 CMake Projects + \section1 CMake projects - You can add CMakeLists.txt files to any project by using the + To add CMakeLists.txt files to any project, use the \l{https://cmake.org/cmake/help/latest/command/add_subdirectory.html} {add_subdirectory} command. The files can define complete projects that you include into the top-level project or any other CMake commands. - \section2 qmake Projects + \section1 qmake projects When you create a new project and select qmake as the build system, you can add it to another project as a subproject in the @@ -224,19 +277,26 @@ must specify that qmake uses the \c subdirs \l{TEMPLATE}{template} to build the project. - To create a root project, select \uicontrol File > - \uicontrol {New Project} > \uicontrol {Other Project} > - \uicontrol {Subdirs Project} > \uicontrol Choose. + To create a root project: - On the \uicontrol Summary page, select \uicontrol {Finish & Add Subproject} to create - the root project and to add another project, such as a C++ library. + \list 1 + \li Select \uicontrol File > \uicontrol {New Project} > + \uicontrol {Other Project} > \uicontrol {Subdirs Project} > + \uicontrol Choose. + + \li On the \uicontrol Summary page, select + \uicontrol {Finish & Add Subproject} to create + the root project and to add another project, such as a C++ library. + \endlist The wizard creates a project file (.pro) that defines a \c subdirs template and the subproject that you add as a value of the \l{Variables#subdirs} {SUBDIRS variable}. It also adds all the necessary files for the subproject. + \section2 Add subprojects to the root project + To create more subprojects, right-click the project name in the - \uicontrol Projects view to open the context menu, and select + \l Projects view to open the context menu, and select \uicontrol {New Subproject}. Follow the steps in the \uicontrol {New Subproject} wizard to create a subproject. @@ -246,31 +306,15 @@ \uicontrol {Add Existing Projects} in the context menu. In the file browser dialog, locate your subproject. + \section2 Remove subprojects + To remove subprojects, right-click the project name in the \uicontrol Projects view, and select \uicontrol {Remove Subproject} in the context menu. - To specify dependencies, use the \uicontrol{Add Library} wizard. For more - information, see \l{Adding Libraries to Projects}. + \section2 Specify dependencies - \section1 Binding Keyboard Shortcuts to Wizards - - If you use a wizard regularly, you can bind a custom keyboard shortcut to - it. Triggering this keyboard shortcut directly opens the wizard, so you do - not need to navigate to \uicontrol File > \uicontrol {New File} or - \uicontrol {New Project}. - - Set keyboard shortcuts for wizards in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Environment > \uicontrol Keyboard > - \uicontrol Wizard. All wizard actions start with \uicontrol Impl there. - - \section1 Related Topics - - \list - \li \l{Creating Files} - \li \l{Opening Projects} - \li \l{Adding Libraries to Projects} - \li \l{Adding New Custom Wizards} - \li \l{Build Systems} - \endlist + To specify dependencies, use the \uicontrol{Add Library} wizard. + \sa {Creating Projects}, {Use project wizards}, + {Add libraries to qmake projects}, {Add libraries to CMake projects} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc index d4c0009db17..d9cd6573c36 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards-json.qdocinc @@ -5,21 +5,22 @@ //! [json wizards] \section1 Integrating Wizards into Builds - To integrate the wizard into \QC and to deliver it as part of the \QC build, - place the wizard files in the \QC sources. Then select \uicontrol Build > - \uicontrol {Run CMake} or \uicontrol {Run qmake}, depending on the build - system you use. This ensures that the new files you added for your wizard are - actually copied from the \QC source directory into the \QC build directory + If you are a \QC developer or build your own \QC version for delivery to + others, you can integrate the wizard into \QC. To deliver the wizard as + part of the \QC build, place the wizard files in the shared directory in + the \QC sources. Then select \uicontrol Build > \uicontrol {Run CMake}. + This ensures that the new files you added for your wizard are actually + copied from the \QC source directory into the \QC build directory as part of the next \QC build. - If you do not run CMake or qmake, your new wizard will not show up because + If you do not run CMake, your new wizard will not show up because it does not exist in the build directory you run your newly built \QC from. - It never got copied there because CMake or qmake did not inform the - build tool, such as make or ninja, about the new files in the source tree. + It never got copied there because CMake did not inform the + build tool, such as make or Ninja, about the new files in the source tree. - Basically, CMake and qmake generate a fixed list of files to copy from the + Basically, CMake generates a fixed list of files to copy from the source directory to the subdirectory of the build directory that is checked - for wizards at runtime. Therefore, you need to run CMake or qmake or execute + for wizards at runtime. Therefore, you need to run CMake or execute the \uicontrol {Factory.Reset} function each time the names or locations of the files change. @@ -89,7 +90,7 @@ \li Make a copy of \c {share/qtcreator/templates/wizards/classes/cpp} and rename it. For example, - \c {share/qtcreator/templates/wizards/classes/mycpp} + \c {$HOME/.config/QtProject/qtcreator/templates/wizards/classes/mycpp}. \li Use the \uicontrol {Factory.Reset} action to make the wizard appear in \uicontrol File > \uicontrol {New File} without @@ -792,16 +793,24 @@ \list - \li \c trText specifies the default text to display. + \li \c trText specifies the translatable default text to display. - \li \c trDisabledText specifies the text to display in a disabled field. + \li \c text specifies the non-translatable default text to display. + + \li \c trDisabledText specifies the translatable text to display in a + disabled field. + + \li \c disabledText specifies the non-translatable text to display in + a disabled field. \li \c completion lists existing \c namespaces for the class name line edit and existing \c classes for the base class line edit. This value replaces the history completer that is usually available for such fields. - \li \c trPlaceholder specifies the placeholder text. + \li \c trPlaceholder specifies the translatable placeholder text. + + \li \c placeholder specifies the non-translatable placeholder text. \li \c validator specifies a QRegularExpression to validate the line edit against. @@ -885,10 +894,15 @@ \list - \li \c trText specifies the text to display. + \li \c trText specifies the translatable text to display. - \li \c trDisabledText specifies the text to display when the text edit - is disabled. + \li \c text specifies the non-translatable text to display. + + \li \c trDisabledText specifies the translatable text to display when + the text edit is disabled. + + \li \c disabledText specifies the non-translatable text to display when + the text edit is disabled. \li \c richText is set to \c true for rich text, otherwise \c{false}. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards.qdoc index f2dae9f9ceb..3abd3a1ab6a 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-custom-wizards.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,11 +8,14 @@ // ********************************************************************** /*! - \previouspage creator-project-qmake-libraries.html \page creator-project-wizards.html - \nextpage creator-version-control.html + \previouspage creator-reference.html - \title Adding New Custom Wizards + \ingroup creator-reference + + \title Custom Wizards + + \brief Wizard types and JSON wizard format. If you have a team working on a large application or several applications, you might want to standardize the way the team members create projects and @@ -24,9 +27,10 @@ has sections that specify information about the wizard, variables that you can use, wizard pages, and generators for creating files. - To create a customized wizard, copy a template directory to the shared - directory or the local user's settings directory under a new name. Then - change the wizard id in the \c {wizard.json} file. + To create a customized wizard, copy a template directory to the + \c {templates/wizards/} directory in the local user's settings + directory under a new name. Then change the wizard id in the + \c {wizard.json} file. You can create a subdirectory for the templates in the settings directory. \QC organizes the standard wizards into subdirectories by type, but you can @@ -57,14 +61,14 @@ \list - \li Shared directory: + \li Predefined wizards in the shared directory: \list \li On Windows: \c {share\qtcreator\templates\wizards} \li On Linux: \c {share/qtcreator/templates/wizards} \li On \macos: \c{Qt Creator.app/Contents/Resources/templates/wizards} \endlist - \li Local user's settings directory: + \li Your custom wizards in the local user's settings directory: \list \li On Windows: \c {%APPDATA%\QtProject\qtcreator\templates\wizards} @@ -81,9 +85,8 @@ \QC has some actions that can improve the wizard development process. They don't have keyboard shortcuts by default, so you cannot trigger them. To - enable them, assign keyboard shortcuts in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Environment > \uicontrol Keyboard > - \uicontrol Wizard. + enable them, assign keyboard shortcuts in \preferences > + \uicontrol Environment > \uicontrol Keyboard > \uicontrol Wizard. The following actions can help with wizard development: @@ -142,7 +145,7 @@ /global/genericfilewizard.png" not found. \endcode - See \l{Using Command Line Options} for more information about command line + See \l{Command-Line Options} for more information about command-line arguments. \include creator-projects-custom-wizards-json.qdocinc json wizards diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-debuggers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-debuggers.qdoc index a7d0aeb7b5e..8762a9e97b1 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-debuggers.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-debuggers.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,11 +8,12 @@ // ********************************************************************** /*! - \previouspage creator-tool-chains.html \page creator-debuggers.html - \nextpage creator-build-settings.html + \previouspage creator-how-tos.html - \title Adding Debuggers + \ingroup creator-how-to-manage-kits + + \title Add debuggers The \QC debugger plugin acts as an interface between the \QC core and external native debuggers such as the GNU Symbolic Debugger (GDB), @@ -21,21 +22,16 @@ The debugger plugin automatically selects a suitable native debugger for each \l{glossary-buildandrun-kit}{kit} from the ones found on your system. - To override this choice, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits. - - For more information about setting up the debugger, see - \l {Setting Up Debugger}. If you encounter problems, see - \l {Troubleshooting Debugger}. + To override this choice, select \preferences > \uicontrol Kits. To add debuggers: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol Debuggers > \uicontrol Add. - \image qtcreator-preferences-kits-debuggers.webp "Debuggers tab in Kits preferences" + \image qtcreator-preferences-kits-debuggers.webp {Debuggers tab in Kits preferences} \li In the \uicontrol Name field, give a descriptive name for the debugger. @@ -73,7 +69,11 @@ \endlist + \section1 Remove debuggers + To remove the selected manually added debugger, select \uicontrol Remove. The debugger disappears from the list when you select \uicontrol Apply. Until then, you can cancel the deletion by clicking \uicontrol Restore. + + \sa {Debugging}, {Setting Up Debugger}, {Troubleshooting Debugger} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc index c4ebbdb3e62..094526334b5 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-generic.qdoc @@ -8,12 +8,18 @@ // ********************************************************************** /*! - \previouspage creator-projects-autotools.html \page creator-project-generic.html - \nextpage creator-project-nimble.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up a Generic Project + \brief \QC supports generic projects, so you can import existing projects + that do not use qmake or CMake. This enables you to use \QC as a + code editor and to fully control the steps and commands used to + build the project. + Generic project support allows you to use \QC as a code editor. You can change the way your project is built by modifying the \c make command in the \uicontrol{Projects} mode under \uicontrol{Build Settings}. @@ -121,7 +127,7 @@ \section1 Forwarding Flags to Clang Code Model - The \c {.cxxflags} and \c {.cflags} files have command line flags for the + The \c {.cxxflags} and \c {.cflags} files have command-line flags for the Clang code model on a single line. For example, specify the \c {-std=c++11} to set the language version diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc similarity index 66% rename from doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc rename to doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc index 08a6d988207..3e1f9fcd62e 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-targets.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-kits.qdoc @@ -8,77 +8,93 @@ // ********************************************************************** /*! - \previouspage creator-configuring-projects.html \page creator-targets.html - \nextpage creator-project-qmake.html + \previouspage creator-how-tos.html - \title Adding Kits + \ingroup creator-how-to-manage-kits - \QC groups settings used for building and running projects as kits + \title Add kits + + \QC groups settings used for building and running projects as \e kits to make cross-platform and cross-configuration development easier. Each kit consists of a set of values that define one environment, such as a \l{glossary-device}{device}, compiler, Qt version, and debugger command to use, and some metadata, such as an icon and a name for the kit. Once you have defined kits, you can select them to build and run projects. - \QC supports development for the desktop and for the following types of - devices: + You can add kits for the desktop and for the following types of devices: \list - \li \l{Connecting Android Devices}{Android Device} - \li \l{Connecting Bare Metal Devices}{Bare Metal Device} + \li \l{Connecting Android Devices}{Android} + \li \l{Connecting Bare Metal Devices}{Bare Metal} \li \l{https://doc.qt.io/Boot2Qt/b2qt-installation-guides.html} - {Boot2Qt Device} (commercial only) - \li \l{Emulator}{Boot2Qt Emulator Device} (commercial only) - \li \l{Adding Docker Devices}{Docker Device} (experimental) - \li \l{Connecting iOS Devices}{iOS Device} + {Boot2Qt} (commercial only) + \li \l{Emulator}{Boot2Qt Emulator} (commercial only) + \li \l{Adding Docker Devices}{Docker} (experimental) + \li \l{Connecting iOS Devices}{iOS} \li iOS Simulator - \li \l{Connecting MCUs}{MCU Device} (commercial only) - \li \l{Connecting QNX Devices}{QNX Device} - \li \l{Connecting Remote Linux Devices}{Remote Linux Device} + \li \l{Connecting MCUs}{MCU} (commercial only) + \li \l{Connecting QNX Devices}{QNX} + \li \l{Connecting Remote Linux Devices}{Remote Linux} \li \l{Building Applications for the Web}{WebAssembly Runtime} \endlist - \section1 Filtering Kit Settings - - Typically, only a subset of the kit settings is relevant for a particular - setup. Therefore, \QC plugins register sets of relevant settings that you - can view and modify in \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits. For example, if you use CMake to build all your projects, - you can hide Qbs and qmake settings by default. - - \image qtcreator-kits.png - - To hide and show settings in the \uicontrol Kits tab for the - current kit, select \uicontrol {Settings Filter}. To view and - modify the settings displayed when you add a new kit, select - \uicontrol {Default Settings Filter}. - - \section1 Specifying Kit Settings - To add kits: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > - \uicontrol Add. - - To clone the selected kit, select \uicontrol Clone. - - \li Specify kit settings. The settings to specify depend on the build - system and device type. + \li Select \preferences > \uicontrol Kits. + \li Select \uicontrol Add to start from an empty kit or \uicontrol Clone + to clone the selected kit and edit its preferences. + \image qtcreator-kits.png + \li Set \l{Kits}{kit preferences} according to the build system and + device type. \li Select \uicontrol OK to create the kit. \endlist + \section1 Set the default kit + \QC uses the \e {default kit} if it does not have enough information to choose the kit to use. To set the selected kit as the default kit, select \uicontrol {Make Default}. - \section2 Kit Settings + \sa {Activate kits for a project}, {Add debuggers}, {Add Qt versions}, + {Open projects}, {Compilers}, {Kits} +*/ - The following table summarizes the available kit settings. +/*! + \page creator-preferences-kits.html + \previouspage creator-reference.html + + \ingroup creator-reference-preferences + + \title Kits + + \brief Sets kit preferences. A kit consists of a set of values that define + one environment, such as a \e {device}, tool chain, Qt version, and debugger + command to use. + + Typically, only a subset of the kit preferences is relevant for a particular + setup. Therefore, \QC plugins register sets of relevant preferences that you + can view and modify in \preferences > + \uicontrol Kits. For example, if you use CMake to build all your projects, + you can hide Qbs and qmake preferences by default. + + \image qtcreator-kits.png + + \section1 Filtering Kit Preferences + + To hide and show preferences in the \uicontrol Kits tab for the + current kit, select \uicontrol {Settings Filter}. + + To view and modify the preferences displayed when you add a new kit, select + \uicontrol {Default Settings Filter}. + + \section1 Kit Preferences + + The following table summarizes the available kit preferences. \table \header @@ -125,7 +141,7 @@ \li C or C++ compiler that you use to build the project. You can add compilers to the list if they are installed on the development PC, but were not detected automatically. For more information, see - \l{Adding Compilers}. + \l{Add compilers}. This setting is used to tell the code model which compiler is used. If your project type and build tool support it, \QC also tells the @@ -146,7 +162,7 @@ \li Debugger to debug the project on the target platform. \QC automatically detects available debuggers and displays a suitable debugger in the field. You can add debuggers to the list. - For more information, see \l{Adding Debuggers}. + For more information, see \l{Add debuggers}. For Android kits, the \uicontrol {Android GDB server} field will display the path to GDB server executable. @@ -154,7 +170,7 @@ \li \uicontrol {Qt version} \li Qt version to use for building the project. You can add Qt versions that \QC did not detect automatically. For more information, see - \l{Adding Qt Versions}. + \l{Add Qt versions}. \QC checks the directories listed in the \c{PATH} environment variable for the qmake executable. It refers to the qmake executable @@ -167,12 +183,12 @@ \row \li \uicontrol {Additional Qbs profile settings} \li Select \uicontrol Change to add settings to Qbs build profiles. - For more information, see \l {Editing Qbs Profiles}. + For more information, see \l {Edit Qbs profiles}. \row \li \uicontrol {CMake Tool} \li CMake executable to use for building the project. Select \uicontrol Manage to add installed CMake executables to - the list. For more information, see \l{Adding CMake Tools}. + the list. For more information, see \l{Add CMake Tools}. \row \li \uicontrol {CMake generator} \li Select \uicontrol Change to edit the CMake Generator to use for @@ -196,43 +212,7 @@ \uicontrol Manage to add installed Ninja tools to the list. \endtable - \section1 Editing Qbs Profiles - - To view the Qbs profile associated with the kit, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Qbs > \uicontrol Profiles. - - \image creator-qbs-profiles.png "Qbs Profiles tab" - - You can add keys and values to the profile or remove them from it, as well - as modify existing values. For a list of available keys and values, see - \l{http://doc.qt.io/qbs/list-of-modules.html}{List of Modules} in the Qbs - Manual. - - To edit the Qbs profile associated with the kit: - - \list 1 - - \li In \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits, select - the kit, and then select \uicontrol Change next to the - \uicontrol {Additional Qbs Profile Settings} field to open the - \uicontrol {Custom Properties} dialog. - - \image qtcreator-qbs-profile-settings.png "Custom Properties dialog" - - \li Double-click an empty cell in the \uicontrol Key column to specify - the key to add or modify as: \c .. - - \li Double-click the cell on the same row in the \uicontrol Value column - to specify a value as a JSON literal. - - \li Select \uicontrol Add to add the key-value pair. - - \li Click \uicontrol OK. - - \endlist - - To modify an existing value, double-click it in the \uicontrol Value field. - - To remove the selected property, select \uicontrol Remove. - + \sa {Activate kits for a project}, {Open projects}, {Add CMake Tools}, + {Add compilers}, {Add debuggers}, {Add kits}, {Add Qt versions}, + {Edit Qbs profiles} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-libraries.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-libraries.qdoc index c63cf151d1d..9d03ea4e892 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-libraries.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-libraries.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,19 +8,43 @@ // ********************************************************************** /*! - \previouspage creator-project-opening.html \page creator-project-qmake-libraries.html - \nextpage creator-project-wizards.html + \previouspage creator-how-tos.html - \title Adding Libraries to Projects + \ingroup creator-how-to-projects-create + \ingroup creator-how-to-build-with-qmake + \title Add libraries to qmake projects + + //! [libraries} In addition to Qt libraries, you can add other libraries to your projects. - The process depends on the type and location of the - library. You can add a system library, your own library, or a 3rd party - library. The library can be located either in the build tree of the - current project or in another build tree. + The process depends on the type and location of the library: - \image qtcreator-add-library-wizard.png "Add Library wizard" + \list + \li A system library + \li Your own library + \li A 3rd party library + \endlist + + \QC supports code completion and syntax highlighting for the added + libraries once your project successfully builds and links to them. + //! [libraries} + + The library can be located either in the build tree of the current project or + in another build tree. + + To add libraries to projects that you build with qmake: + + \list 1 + \li In the \l Projects view, right-click the project name to open the + context menu and select \uicontrol {Add Library}. + \image qtcreator-add-library-wizard.png {Add Library wizard} + \li Specify settings for the library. + \image qtcreator-add-library-external.webp {Adding an external library} + The settings depend on the library type. + \endlist + + \section1 Library location Because system libraries do not typically change and are often found by default, you do not need to specify the path to the library or to its @@ -32,13 +56,18 @@ but you need to check it and modify it if necessary. \QC automatically adds the include path for an internal library. + \section1 Target platform + For all libraries, select the target platforms for the application, library, or plugin. + \section1 Linking + Specify whether the library is statically or dynamically linked. For a - statically linked internal library, \QC adds dependencies - (\l{CMake: target_link_libraries command}{target_link_libraries} when using - CMake or \l PRE_TARGETDEPS when using qmake) in the project file. + statically linked internal library, \QC adds dependencies as the value of + the \l PRE_TARGETDEPS qmake variable in the project file (.pro). + + \section1 Development platform Depending on the development platform, \QC might detect some options automatically. For example, on \macos, it detects the library type @@ -59,52 +88,89 @@ If the library name ends in \e d, deselect the \uicontrol {Remove "d" suffix for release version} option. - \QC supports code completion and syntax highlighting for the added - libraries once your project successfully builds and links to them. - - \section1 To Add Libraries - - \list 1 - - \li In the \uicontrol Projects view, right-click the project name to open the - context menu and select - \uicontrol {Add Library}. - - \li Follow the instructions of the wizard. - - \endlist - For more information about the project file settings, see - \l{Declaring Other Libraries}. + \l{Declaring Other Libraries}{qmake Manual: Declaring Other Libraries}. - \section1 Example of Adding Internal Libraries + \sa {Adding an Internal Library to a qmake Project}{Tutorial: Adding an Internal Library to a qmake Project}, + {Add subprojects to projects}, {Add libraries to CMake projects}, + {Use project wizards}, {Creating Projects} +*/ - To add an internal library to your project: +/*! + \page creator-tutorial-adding-internal-libraries-to-projects.html + \previouspage creator-tutorials.html + \nextpage creator-project-managing.html + + \ingroup creator-tutorials + + \title Adding an Internal Library to a qmake Project + + \brief How to create your own library and link your application against it + when using qmake as the build system. + + You can add a library into a \e subdirs project. Use wizards to create the + project and the library and to link the library against the project. + + \note This tutorial only applies when you select qmake as the the build + system for the subdirs project. + + \section1 Creating a shared library + + To create a shared library: \list 1 \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol Library > \uicontrol {C++ Library}. + \uicontrol Library > \uicontrol {C++ Library}. If your top level + project is a subdirs project or contains one, you may add the library + to the project. However, this does not link other libraries from + your project against it. \li Select \uicontrol Choose to open the \uicontrol {Project Location} dialog. - \image qtcreator-add-library-wizard-ex-1.png "Project Location dialog" + \image qtcreator-add-library-internal-project-location.webp {Project Location dialog} \li In the \uicontrol Name field, give a name for the library. For example, - \b mylib. + \e MyLibrary. - \li Follow the instructions of the wizard until you get to the + \li Select \uicontrol Next (on Windows and Linux) or \uicontrol Continue + (on \macos) to open the \uicontrol {Define Build System} dialog. + + \li Select \uicontrol Next or \uicontrol Continue to use CMake as the + build system. + + The \uicontrol {Define Project Details} dialog opens. + + \image qtcreator-add-library-internal-project-details.webp {Define Project Details dialog} + + \li Select the library type and enter information about the classes for + which you want to generate source code files: class name, Qt module, + and source and header file names. + + \li Select \uicontrol Next or \uicontrol Continue until you get to the \uicontrol {Project Management} dialog. In the \uicontrol {Add as a subproject to project} - list, select a project. For example, \b myapp. + list, select a subdirs project. For example, \e MyApplication. + \endlist - \li In the \uicontrol Projects view, right-click the project name to open the - context menu and select - \uicontrol {Add Library} > \uicontrol {Internal Library} > - \uicontrol Next. + \section1 Linking an application to the library - \li In the \uicontrol Library field, select \b mylib, and then select + To link a project to the shared library: + + \list 1 + + \li In the \l Projects view, right-click the project name to open + the context menu and select \uicontrol {Add Library} > + \uicontrol {Internal Library} > \uicontrol Next. + + The wizard instructs the build system to link an existing application + project or a library project against the selected library. Here, you + add the library that you created above. + + \image qtcreator-add-library-internal.webp {Adding an internal library} + + \li In the \uicontrol Library field, select \e mylibrary, and then select \uicontrol Next. \li Select \uicontrol Finish to add the library declaration to the @@ -116,7 +182,7 @@ CMakeLists.txt file: \badcode - target_link_libraries(myapp PRIVATE mylib) + target_link_libraries(myapplication PRIVATE mylibrary) \endcode When using qmake, the following library declaration is added to the .pro @@ -132,4 +198,8 @@ else:win32:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../../../projects/mylib/debug/mylib.lib else:unix: PRE_TARGETDEPS += $$OUT_PWD/../../../projects/mylib/libmylib.a \endcode + + \sa {Add libraries to qmake projects}, {Add libraries to CMake projects}, + {Add subprojects to projects}, {Select the build system}, + {Use project wizards}, {Creating Projects} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-nimble.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-nimble.qdoc index b183eec8917..4062cf775e5 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-nimble.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-nimble.qdoc @@ -2,21 +2,22 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-generic.html \page creator-project-nimble.html - \nextpage creator-project-meson.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up Nimble + \brief The Nimble package manager generates Nim application + executables that are supported on Windows, Linux, and \macos. + \l {https://github.com/nim-lang/nimble#readme}{Nimble} is a package manager for the Nim programming language. It is delivered with \l{https://nim-lang.org/}{Nim} and uses the Nim compiler to generate executables that are supported on Windows, Linux, and \macos. - To use \QC for Nim development, you need to enable the experimental - Nim plugin. Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Other Languages} > \uicontrol Nim. Then select - \uicontrol {Restart Now} to restart \QC and load the plugin. + \note Enable the Nim plugin to use Nimble and Nim. In addition, you have to download and install Nim and set up a Nim kit in \QC. @@ -28,7 +29,7 @@ To configure \QC to build Nim executables: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits + \li Select \preferences > \uicontrol Kits > \uicontrol Compilers > \uicontrol Add > \uicontrol Nim to specify the path to the Nim compiler. \li Select \uicontrol Apply to add the compiler. @@ -58,4 +59,6 @@ \image qtcreator-project-nimble.png \li Select \uicontrol Next to create the project. \endlist + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc index 85d04903921..ccbe2316b47 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc @@ -1,5 +1,5 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -9,91 +9,100 @@ // ********************************************************************** /*! - \previouspage creator-file-creating.html \page creator-project-opening.html - \nextpage creator-project-qmake-libraries.html + \previouspage creator-how-tos.html - \title Opening Projects + \ingroup creator-how-to-projects-configure + + \title Open projects + + To open a project, open the project file for the project: + \e {CMakeLists.txt} (CMake), \e {.pro} (qmake), \e {.qbs} (Qbs), + \e {meson.build} (Meson), \e {pyproject} (Python), or \e {Makefile.am} + (Autotools, experimental). + + You can open a project in the following ways: + + \list + \li Select \uicontrol File > \uicontrol {Open File or Project}. + \li In the \uicontrol Welcome mode, \uicontrol Projects tab, select + \uicontrol {Open Project} (1). + \image qtcreator-welcome-open-projects.webp {Ways to open projects in the Welcome mode Projects tab} + \li In the \uicontrol Projects tab, select a project in the list of + recently opened projects (2). + \li In the \l Projects view, right-click to open a context + menu and select \uicontrol {Load Project} to open the + \uicontrol {Load Project} dialog, where you can select + a project file. + \li Use the following keyboard shortcuts, depending on the mode you are + currently in: + \list + \li In all modes, press \key Ctrl+O (\key Cmd+O on \macos) to open the + \uicontrol {Open File} dialog, where you can select a project file. + \li On Windows and Linux, in all modes except the \uicontrol Help mode, + press \key Ctrl+Shift+O to open the \uicontrol {Load Project} dialog. + \li In the \uicontrol Welcome mode, \uicontrol Projects tab, press + \key Ctrl+Shift+number (\key Cmd+Shift+number on \macos), where + the number is the number of a project in the list of recently opened + projects (3). + \endlist + \endlist + + \section1 Re-configure projects \QC stores information that it needs to build projects in a .user file. If \QC cannot find the file when you open an existing project, it prompts you - to enter the information. If you used another \QC instance to create the - project, \QC asks whether you want to use the old settings. The settings - are specific to the development environment, and you should not copy them from - one environment to another. Therefore, we recommend that you select \uicontrol No - and enter the information again in the \uicontrol {Configure Project} tab. + to enter the information. - The \uicontrol {Configure Project} tab displays a list of \l{glossary-buildandrun-kit}{kits} - for building and running projects, that you install on the development PC and - configure in \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits. - Select the kits that you want to build and run the project with. + If you used another \QC instance to create the project, \QC asks whether you + want to use the old settings. The settings are specific to the development + environment, and you should not copy them from one environment to another. + Therefore, we recommend that you select \uicontrol No and enter the + information again in the \uicontrol {Configure Project} tab. - \image qtcreator-open-project-kits.png "Configure Project tab" + To re-configure projects: + + \list 1 + \li In the \uicontrol {Configure Project} tab, select + \l{glossary-buildandrun-kit}{kits} for building + and running your project. + \image qtcreator-open-project-kits.png {Configure Project tab} + \li Select \uicontrol {Configure Project}. + \endlist + + The \uicontrol {Configure Project} tab displays a list of kits that you + install on the development PC and configure in \preferences > \uicontrol Kits. Even if you do not intend to build the project, the C++ and QML code models need a Qt version and compiler to offer code completion. To specify them, - select the \uicontrol Preferences link, or select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits. + select the \uicontrol Preferences link, or select \preferences > + \uicontrol Kits. Qt for Python projects rely on the \l{Using Language Servers} {language server client} for code completion, highlighting, and other useful features. - If \QC cannot find an existing build for a particular \l{glossary-buildandrun-kit}{kit}, - it starts out - from a clean slate, and creates new debug and release build configurations - in the specified directory. \QC suggests a name - and location for the directory that you can change. + If \QC cannot find an existing build for a particular kit, it starts out + from a clean slate and creates new debug and release build configurations + in the specified directory. \QC suggests a name and location for the + directory that you can change. + + \section1 Import builds If you have built the project before, \QC can use the existing build configuration to make the exact same build available to you. To import a - build, specify a directory in the - \uicontrol {Import Build From} section and select \uicontrol {Import}. + build, specify a directory in the \uicontrol {Import Build From} section + of the \uicontrol {Configure Project} tab and select \uicontrol {Import}. You can edit the build configuration later. For more information, see \l{Editing Build Configurations}. - To open a project: - - \list 1 - - \li Select \uicontrol File > \uicontrol {Open File or Project} - (\key Ctrl+O or \key Cmd+O on \macos) and select the project file - for the project to open: \e {.pro} (qmake), \e {CMakeLists.txt} - (CMake), \e {.qbs} (Qbs), \e {meson.build} (Meson), \e {pyproject} (Python), or - \e {Makefile.am} (Autotools, experimental). - - \li In the \uicontrol {Configure Project} tab, select kits for building - and running your project. - - \li Select \uicontrol {Configure Project}. - - \endlist - - You can use the following keyboard shortcuts to open projects, depending on - the mode you are currently in: - - \list - - \li In all modes, press \key Ctrl+O (\key Cmd+O on \macos) to open the - \uicontrol {Open File} dialog, where you can select a project file - to open a project. - - \li In all modes, except the \uicontrol Help mode, press - \key Ctrl+Shift+O (\key Cmd+Shift+O on \macos) to open the - \uicontrol {Load Project} dialog, where you can select a project - file to open a project. - - \li In the \uicontrol Welcome mode, \uicontrol Projects tab, press - \key Ctrl+Shift+number (\key Cmd+Shift+number on \macos), where - the number is the number of a project in the list of recently opened - projects. - - \endlist + \section1 Show progress information \QC parses all the source files in the project and performs a semantic analysis to build up the information that it needs for functions such as navigation and finding usages. A progress bar is displayed during parsing. + To show or hide detailed progress information, select \uicontrol {Toggle Progress Details} (1). @@ -102,4 +111,6 @@ You can drag the progress bar to another position. The position is saved for later. Select the \inlineimage icons/pin.png (\uicontrol Pin) button to pin the progress bar back to the toggle button. + + \sa {Activate kits for a project}, {Add kits}, {Kits}, */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc deleted file mode 100644 index 232bb7d4fa1..00000000000 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-other.qdoc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -// ********************************************************************** -// NOTE: the sections are not ordered by their logical order to avoid -// reshuffling the file each time the index order changes (i.e., often). -// Run the fixnavi.pl script to adjust the links to the index order. -// ********************************************************************** - -/*! - \previouspage creator-os-supported-platforms.html - \page creator-project-other.html - \nextpage creator-project-cmake.html - - \title Build Systems - - Most \QC project wizards enable you to choose the build system to use for - building the project: qmake, CMake, Meson, or Qbs. qmake is installed and - configured when you install Qt. To use one of the other supported build - systems, you need to set it up, as described in the following sections: - - \list - - \li \l{Setting Up CMake} - - CMake is an alternative to qmake for automating the generation of - build configurations. - - \li \l{Setting Up Qbs} - - \l{Qbs Manual}{Qbs} is an all-in-one build - tool that generates a build graph from a high-level project - description (like qmake or CMake do) and executes the commands in - the low-level build graph (like make does). - - \li \l{Setting Up an Autotools Project} - - \QC can open projects that use the Autotools build system. You can - build and run the projects directly from \QC. - - \li \l{Setting Up a Generic Project} - - \QC supports generic projects, so you can import existing projects - that do not use qmake or CMake. This enables you to use \QC as a - code editor and to fully control the steps and commands used to - build the project. - - \li \l{Setting Up Nimble} - - The experimental \l{https://nim-lang.org/}{Nim} plugin integrates - the Nimble package manager for generating Nim application - executables that are supported on Windows, Linux, and \macos. - - \li \l{Setting Up Meson} - - Meson is an open source build system meant to be both extremely fast, - and, even more importantly, as user friendly as possible. - - \li \l{Setting Up IncrediBuild} - - IncrediBuild decreases the time it takes to build C++ code. - - \li \l{Setting Up Conan} - - The experimental Conan plugin integrates the Conan package manager - that speeds up the integration of C or C++ libraries into your - project. You can use Conan with most build systems integrated into - \QC. - - \li \l{Managing Packages with vcpkg} - - The experimental vcpkg plugin integrates the - \l {https://vcpkg.io/en/}{vcpkg} C/C++ package manager into \QC. - Create and edit vcpkg.json files to specify packages to build as - part of your project when using CMake as the build system. - \endlist - -*/ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-overview.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-overview.qdoc index 7be0b1ba4d9..ce94263b593 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-overview.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -16,9 +16,9 @@ \image front-projects.png - One of the major advantages of \QC is that it allows a team of designers and - developers to share a project across different development platforms with a - common tool for design, development, and debugging. + You can share projects with other designers and developers across different + development platforms with a common tool for design, development, and + debugging. \list @@ -30,7 +30,7 @@ language to implement the application logic: C++, JavaScript, or Python. - \li \l{Using Version Control Systems} + \li \l{Version Control Systems} The recommended way to set up a project is to use a version control system. Store and edit only project source files and configuration @@ -40,7 +40,7 @@ Installation programs and project wizards create default configurations for \QC and your projects. You can modify - the settings in the Projects mode. + the settings in the \uicontrol Projects mode. \li \l{Managing Sessions} @@ -50,10 +50,6 @@ \endlist - \section1 Related Topics - - \list - \li \l{Build Systems} - \endlist + \sa {Build Systems} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-qbs.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-qbs.qdoc index 9d7504b85af..bf88413305f 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-qbs.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-qbs.qdoc @@ -1,13 +1,18 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-project-cmake.html \page creator-project-qbs.html - \nextpage creator-projects-autotools.html + \previouspage creator-reference.html + + \ingroup creator-reference-build-systems \title Setting Up Qbs + \brief \l{Qbs Manual}{Qbs} is an all-in-one build tool that generates a build + graph from a high-level project description (like qmake or CMake do) and + executes the commands in the low-level build graph (like make does). + To use Qbs to build a project, you must create a .qbs file for the project. You can use \QC to create a C or C++ project that is built with Qbs. For more information about Qbs, see @@ -17,8 +22,8 @@ with the build and run kit. \QC automatically creates a Qbs profile for each kit. You can edit the build profiles by adding new keys and values. - To check which Qbs version is being used, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Qbs > \uicontrol General. + To check which Qbs version is being used, select \preferences > + \uicontrol Qbs > \uicontrol General. \section1 Building Qbs @@ -44,7 +49,7 @@ To specify settings for Qbs: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Qbs. + \li Select \preferences > \uicontrol Qbs. \image qtcreator-options-qbs.png "Qbs preferences" \li Deselect the \uicontrol {Use \QC settings directory for Qbs} check box to store Qbs profiles in the Qbs settings directory. @@ -59,14 +64,14 @@ \image creator-qbs-profiles.png "Qbs Profiles tab" \li In the \uicontrol Kit field, select a build and run kit to view the properties of the associated profile. To modify the properties, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits. - For more information, see \l{Editing Qbs Profiles}. + select \preferences > \uicontrol Kits. + For more information, see \l{Edit Qbs profiles}. \endlist \section1 Related Topics \list - \li \l {Opening Projects} + \li \l {Open projects} \li \l {Qbs Build Configuration} \li \l {Specifying Run Settings} \endlist diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc index 8cddb9375eb..63090c65c87 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-qt-versions.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,42 +8,43 @@ // ********************************************************************** /*! - \previouspage creator-targets.html \page creator-project-qmake.html - \nextpage creator-tool-chains.html + \previouspage creator-how-tos.html - \title Adding Qt Versions + \ingroup creator-how-to-manage-kits - You can install multiple versions of Qt development PC and use them to build - your projects. For example, \l{glossary-device}{device} manufacturers offer - special Qt versions for developing applications for their devices. + \title Add Qt versions - \section1 Registering Installed Qt Versions + You can install multiple versions of Qt on the development PC and use them to + build your projects. For example, \l{glossary-device}{device} manufacturers + offer special Qt versions for developing applications for their devices. - The \uicontrol {Qt Versions} tab lists the installed Qt versions. To view - detailed information about each Qt version, select it in the list and select - \uicontrol Details in the \uicontrol {Qt version for} section. + To view the installed Qt versions, select \preferences > \uicontrol Kits > + \uicontrol {Qt Versions}. - \image qtcreator-qt-versions.png "Qt Versions tab in Kit preferences" + \image qtcreator-qt-versions.png {Qt Versions tab in Kit preferences} + + To view detailed information about each Qt version, select it in the list and + select \uicontrol Details in the \uicontrol {Qt version for} section. To remove invalid Qt versions, select \uicontrol {Clean Up}. + \section1 Register installed Qt versions + You can link to a Qt that \QOI installed to automatically detect the installed Qt versions. However, you cannot link to a Qt that the system installed with some other package manager, such as your Linux distribution, brew on \macos, or Chocolatey on Windows, nor a self-built Qt. In those cases, select \uicontrol {Add} in the \uicontrol {Qt Versions} tab to add the Qt version manually, as - instructed in \l{Setting Up New Qt Versions}. + instructed in \l{Set up new Qt versions}. To link to a Qt installation: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > - (or \uicontrol {Qt Creator} > \uicontrol Preferences on - \macos) > \uicontrol Kits > \uicontrol {Qt Versions} > + \li Select \preferences > \uicontrol Kits > \uicontrol {Qt Versions} > \uicontrol {Link with Qt}. - \image qtcreator-link-with-qt.png "Choose Qt Installation dialog" + \image qtcreator-link-with-qt.png {Choose Qt Installation dialog} \li In the \uicontrol {Qt installation path} field, enter the path to the directory where you installed Qt. \li Select \uicontrol {Link with Qt} to automatically register Qt @@ -55,21 +56,19 @@ \uicontrol {Remove Link}. If the \uicontrol {Qt Versions} tab does not show a Qt version - under \uicontrol Auto-detected, set it up manually, as described - in the following section. + under \uicontrol Auto-detected, set it up manually. You specify the Qt version to use for each \l{glossary-buildandrun-kit} - {kit} for building and running your projects - in \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits, as described - in \l{Specifying Kit Settings}. + {kit} for building and running your projects in \preferences > + \uicontrol Kits. - \section1 Setting Up New Qt Versions + \section1 Set up new Qt versions To add a Qt version: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > + \li Select \preferences > \uicontrol Kits > \uicontrol {Qt Versions} > \uicontrol Add. \li Select the qmake executable for the Qt version to add. @@ -90,7 +89,7 @@ To remove a Qt version that you added manually, select it in the \uicontrol Manual list and then select \uicontrol Remove. - \section1 Registering Documentation + \section1 Register documentation By default, \QC registers only the latest available version of the documentation for each installed Qt module. @@ -100,7 +99,7 @@ To register no Qt documentation at all, choose \uicontrol{None}. The default behavior is \uicontrol{Highest Version Only}. - \section1 Troubleshooting Qt Installations + \section1 Troubleshoot Qt installations If \QC detects problems in the installation of a Qt version, it displays warnings and errors beside the name of the Qt version in the list. Select @@ -115,13 +114,13 @@ Installer, run \QMT to check for updates or to reinstall the Qt version. - \section1 Minimum Requirements + \section1 Minimum requirements If your build of Qt is incomplete but you still want to use qmake as build system, you need to ensure the following minimum requirements to use that setup with \QC. \list 1 - \li qmake is an executable that understands the \c -query command line + \li qmake is an executable that understands the \c -query command-line argument. \li The \c bin and \c include directories have to exist. \QC fetches these directories by running \c{qmake -query}. @@ -131,5 +130,6 @@ If your Qt version has no \c libQtCore.so, \QC cannot detect the ABI. + \sa {Kits} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc index 69315c8a4d0..3a54e8e4e7e 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build.qdoc @@ -35,7 +35,8 @@ \section1 Managing Build Configurations - Specify build settings in \uicontrol Projects > \uicontrol {Build & Run} + Specify build settings for the selected \l{glossary-buildandrun-kit} + {build and run kit} in \uicontrol Projects > \uicontrol {Build & Run} > \uicontrol Build > \uicontrol {Build Settings}. \image qtcreator-build-configurations.png "Build Settings" @@ -90,7 +91,7 @@ \QC executes external processes to accomplish tasks such as building and running applications. To execute the processes, \QC uses shell commands that are native to the system. It constructs the commands from - an executable name and optional command line arguments. + an executable name and optional command-line arguments. The executable name is specified in the executable fields: \uicontrol qmake, \uicontrol Make, \uicontrol Command, or \uicontrol Executable. It is either @@ -116,7 +117,7 @@ \section1 Build Steps - \image qtcreator-cmake-build-steps.png "CMake build steps" + \image qtcreator-cmake-build-steps.webp {CMake build steps} In \uicontrol{Build Steps}, you can change the settings for the build system selected for building the project: diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc index 083fd14fc38..bd90079372f 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-code-style.qdoc @@ -8,52 +8,44 @@ // ********************************************************************** /*! - \previouspage creator-editor-settings.html \page creator-code-style-settings.html - \nextpage creator-build-dependencies.html + \previouspage creator-how-tos.html - \title Specifying Code Style + \ingroup creator-how-to-edit - \QC uses the \l{Editing MIME Types}{MIME type} of the file to - determine which mode and editor to use for opening the file. - \QC opens C++ files in \uicontrol Edit mode in the C++ code editor and - QML files in the Qt Quick editor. + \title Specify code style - \QC uses ClangFormat to enforce the C++ code style specified in a - \c {.clang-format} file. It uses the - \l{https://clang.llvm.org/docs/LibFormat.html}{LibFormat} library for - automatic code formatting and indentation. For more information, see - \l {Indenting C++ Files}. + The \l{Editing MIME Types}{MIME type} of the file determines which mode + and editor the file opens in. \QC opens C++ files in \uicontrol Edit mode + in the C++ code editor and QML files in the Qt Quick editor. - \image qtcreator-code-style-clang-format-project.webp {Code Style settings in Projects mode} + You can specify indentation for: - In rare cases, ClangFormat can trip over a code construct and - trigger a \QC crash. If that happens for your project, select - \uicontrol {Formatting mode} > \uicontrol Disable to switch - ClangFormat off for the project. If you can reproduce the crash, - please select \uicontrol Help > \uicontrol {Report Bug} to report - the bug and and attach the code that triggers the crash. + \list + \li C++ files + \li QML files + \li Nim files + \li Other text files + \endlist - To specify a global code style for C++ files, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol C++. + You can specify code style either globally or separately for each project. + You can specify several sets of code style settings and easily switch between + them. In addition, you can import and export code style settings. - \image qtcreator-code-style-clang-format-global.webp {Code Style preferences} - - To specify a global code style for QML files, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick}. - - \image qtcreator-code-style-settings-edit-qtquick.png {Code Style view} - - To configure the editor behavior for the current project: + To override the global code style for the current project: \list 1 \li Select \uicontrol Projects > \uicontrol {Project Settings} > \uicontrol {Code Style}. + \image qtcreator-code-style-clang-format-project.webp {Code Style settings in Projects mode} + \li In the \uicontrol Language field, select \uicontrol C++, \uicontrol {Qt Quick}, or \uicontrol Nim. + \li Deselect the \uicontrol {Use global settings} check box. + \li In the \uicontrol {Current settings} field, select the settings to modify and click \uicontrol Copy. @@ -63,6 +55,12 @@ \endlist - For more information about the settings, see \l{Indenting Text or Code}. + In rare cases, ClangFormat can trip over a code construct and + trigger a \QC crash. If that happens for your project, select + \uicontrol Disable as the formatting mode to switch + ClangFormat off for the project. If you can reproduce the crash, + please select \uicontrol Help > \uicontrol {Report Bug} to report + the bug and and attach the code that triggers the crash. + \sa {Indent text or code}, {C++ Code Style}, {Qt Quick Code Style}, {Nim} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-dependencies.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-dependencies.qdoc index bda2c5b5e71..0fb7729d887 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-dependencies.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-dependencies.qdoc @@ -9,20 +9,20 @@ /*! \page creator-build-dependencies.html - \previouspage creator-code-style-settings.html - \nextpage creator-project-settings-environment.html + \previouspage creator-how-tos.html - \title Specifying Dependencies + \ingroup creator-how-to-projects-configure + + \title Specify dependencies If you have multiple projects loaded in a session, you can define the order in which they are built. For example, if project A depends on project B, project B must be built first. \note The build order is stored as a property of a session, not a project. - You must open the session for these settings to take effect. For more - information, see \l{Managing Sessions}. + You must open the session for these settings to take effect. - \image qtcreator-build-dependencies.png "Dependencies view" + \image qtcreator-build-dependencies.png {Dependencies settings in Projects mode} To define the build order of projects within a session: @@ -45,7 +45,7 @@ specify for the projects loaded in the session. \note You cannot use this view to specify subprojects for projects. - For more information on how to add subprojects, see \l{Adding Subprojects - to Projects}. + + \sa {Add subprojects to projects}, {Managing Sessions} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-editor.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-editor.qdoc index e97ceeb098d..684a960ed35 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-editor.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-editor.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,21 +8,17 @@ // ********************************************************************** /*! - \previouspage creator-run-settings.html \page creator-editor-settings.html - \nextpage creator-code-style-settings.html + \previouspage creator-how-tos.html - \title Specifying Editor Settings + \ingroup creator-how-to-projects-configure + + \title Specify editor settings \QC uses the \l{Editing MIME Types}{MIME type} of the file to determine which mode and editor to use for opening the file. For example, \QC opens .txt files in \uicontrol Edit mode in the text editor. - You can configure the text editor according to your needs. You can specify - editor behavior either globally for all projects or separately for each - project. To specify global editor behavior, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Text Editor} > \uicontrol Behavior. - To configure the text editor behavior for the current project: \list 1 @@ -40,24 +36,8 @@ Click \uicontrol {Restore Global} to revert to the global settings. - For more information about the settings, see: - - \list - - \li \l{Indenting Text or Code} - - \li \l{File Encoding} - - \li \l{Selecting Line Ending Style} - - \li \l{Moving to Symbol Definition or Declaration} - - \li \l{Configuring Fonts} - - \li \l{Highlighting and Folding Blocks} - - \li \l{Viewing Function Tooltips} - - \endlist - + \sa {Configuring Fonts}, {File Encoding}, + {Moving to Symbol Definition or Declaration}, {Indent text or code}, + {Selecting Line Ending Style}, {Semantic Highlighting}, + {View function tooltips} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc index 1d99a0bf650..dd3534bb904 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc @@ -19,7 +19,7 @@ based on your project requirements. To globally change the system environment from the one in which - \QC is started, select \uicontrol Edit > \uicontrol Preferences > + \QC is started, select \preferences > \uicontrol Environment > \uicontrol System, and then select \uicontrol Change in the \uicontrol Environment field. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc index 0b27f1e9316..6a15f763200 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-overview.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,31 +8,70 @@ // ********************************************************************** /*! - \previouspage creator-vcs-subversion.html + \previouspage creator-version-control.html \page creator-configuring-projects.html - \nextpage creator-targets.html + \nextpage creator-project-managing-sessions.html \title Configuring Projects When you install Qt for a target platform, such as Android or QNX, \l{https://www.qt.io/download-qt-installer}{\QOI} creates \l{glossary-buildandrun-kit}{kits} for the development - targets. Select the kits to use in the \uicontrol {Configure Projects} + targets. + + Select the kits to use in the \uicontrol {Configure Projects} view when you open a project for the first time. At least one kit must be - active. For more information about selecting the initial kit, see - \l{Opening Projects}. + active. To maintain the list of active kits for a currently open project, switch to the \uicontrol Projects mode by pressing \key Ctrl+5. - \section1 Activating Kits for a Project + \image qtcreator-projects-kits.webp {Sidebar in the Projects mode} - The \uicontrol {Build & Run} section of the sidebar lists the kits that are - compatible with your project. To activate one or more kits, click them. + To specify build or run settings for a kit, select \uicontrol Build or + \uicontrol Run below the kit. + + \section1 Overriding Global Preferences + + In \uicontrol {Project Settings}, you can override global preferences for + the project: + + \list + \li \l{Parsing C++ Files with the Clang Code Model}{Clangd} + \li \l{Using Clang Tools}{Clang Tools} + \li \l{Specify code style}{C++ Code Style} + \li \l{Set C++ file naming preferences}{C++ File Naming} + \li \l{Using Custom Output Parsers}{Custom Output Parsers} + \li \l{Specify dependencies}{Dependencies} + \li \l{Document code}{Documentation Comments} + \li \l{Specify editor settings}{Editor} + \li \l{Specifying Environment Settings}{Environment} + \li \l{Applying Refactoring Actions}{Quick Fixes} + \li \l{To-Do Entries}{To-Do} (experimental) + \endlist + + If you have multiple projects open in \QC, select the project to configure + in \uicontrol {Active Project}. + + \sa {Activate kits for a project}, {Open projects}, + {Specifying Build Settings}, {Specifying Run Settings} +*/ + +/*! + \page creator-how-to-activate-kits.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-configure + + \title Activate kits for a project + + The \uicontrol {Build & Run} section of the \uicontrol Projects mode sidebar + lists the kits that are compatible with your project. To activate one or more + kits, click them. \image qtcreator-project-kits.png - The list displays kits from \uicontrol Edit > \uicontrol Preferences > + The list displays kits from \preferences > \uicontrol Kits. Warning and error icons indicate that the kit configuration is not suitable for the project type. To view the warning and error messages, move the mouse pointer over the kit name. @@ -45,13 +84,14 @@ configuration to use a currently installed Qt version and save the kit under a new name. - To modify kit configuration or to add kits to the list or to remove - them from it, select \uicontrol {Manage Kits}. For more information - about managing kits, see \l{Adding Kits}. + \section1 Manage kits + + To modify kit configuration or to \l{Add kits}{add kits} to the list or to + remove them from it, select \uicontrol {Manage Kits}. Each kit consists of a set of values that define one environment, such as a - \l{glossary-device}{device}, compiler, and Qt version. For more information, - see \l{Adding Qt Versions}, \l{Adding Compilers}, and \l{Adding Debuggers}. + \l{glossary-device}{device}, \l{Add compilers}{compiler}, + \l{Add debuggers}{debugger}, and \l{Add Qt versions}{Qt version}. To copy the build and run settings for a kit to another kit, select \uicontrol {Copy Steps from Other Kit} in the context menu. @@ -65,38 +105,5 @@ To import an existing build for the project, select \uicontrol {Import Existing Build}. - \section1 Specifying Settings - - To specify build or run settings for a kit, select \uicontrol Build or - \uicontrol Run below the kit. For more information, see - \l{Specifying Build Settings} and \l{Specifying Run Settings}. - - In addition, you can modify the following global settings for each project: - - \list - - \li \l{Specifying Editor Settings}{Editor} - - \li \l{Specifying Code Style}{Code Style} - - \li \l{Specifying Dependencies}{Dependencies} - - \li \l{Specifying Environment Settings}{Environment} - - \li \l{Using Custom Output Parsers}{Custom Output Parsers} - - \li \l{Applying Refactoring Actions}{Quick Fixes} - - \li \l{Using Clang Tools}{Clang Tools} - - \li \l{To-Do Entries}{To-Do} (experimental) - - \li \l{Parsing C++ Files with the Clang Code Model} - {Clangd} - - \endlist - - If you have multiple projects open in \QC, select the project to configure - in the list of projects. - + \sa {Add kits}, {Configuring Projects}, {Kits} */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-analyze.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-analyze.qdocinc index f7adfa4cd2d..e54035966d5 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-analyze.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-analyze.qdocinc @@ -47,8 +47,8 @@ Click \uicontrol {Restore Global} to revert to the global settings. - To specify global Valgrind settings, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Analyzer. + To specify global Valgrind settings, select \preferences > + \uicontrol Analyzer. //! [settings valgrind] */ diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc index c3f3688e892..caadbe3b463 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc @@ -6,7 +6,7 @@ \section1 Specifying Run Settings for Desktop Device Types - You can specify command line arguments to be passed to the executable + You can specify command-line arguments to be passed to the executable and the working directory to use. The working directory defaults to the directory of the build result. @@ -20,10 +20,9 @@ \image qtcreator-settings-run-desktop.webp {Run Settings} For console applications, check the \uicontrol{Run in terminal} check box. - To specify the terminal to use on Linux and \macos, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol Environment > \uicontrol System. To use - an \l{Terminal}{internal terminal}, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Terminal > + To specify the terminal to use on Linux and \macos, select \preferences > + \uicontrol Environment > \uicontrol System. To use an \l{Terminal} + {internal terminal}, select \preferences > \uicontrol Terminal > \uicontrol {Use internal terminal}. To run with special environment variables set up, select them in the @@ -40,8 +39,8 @@ To disable library linking for the current project, deselect the \uicontrol {Add build library search path to PATH} check box. To disable - library linking for all projects, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run}, and then deselect the + library linking for all projects, select \preferences > + \uicontrol {Build & Run}, and then deselect the \uicontrol {Add linker library search paths to run environment} check box. The \uicontrol {Use debug version of frameworks (DYLD_IMAGE_SUFFIX=_debug)} option diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc index 6f489f4b301..e574c00a4c3 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run.qdoc @@ -24,7 +24,7 @@ \image qtcreator-settings-run.webp {Run Settings} To prevent \QC from automatically creating run configurations, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run}, + \preferences > \uicontrol {Build & Run}, and then deselect the \uicontrol {Create suitable run configurations automatically} check box. @@ -127,7 +127,7 @@ run configuration for your project. For example, when working on a library, you can run a test application that links against the library. - Specify the executable to run, command line arguments, working directory, + Specify the executable to run, command-line arguments, working directory, and environment variables to use. \image qmldesigner-run-custom-exe.png {Run settings for custom executables} diff --git a/doc/qtcreator/src/projects/creator-projects-running.qdoc b/doc/qtcreator/src/projects/creator-projects-running.qdoc index a6311d58d0a..81756d4a408 100644 --- a/doc/qtcreator/src/projects/creator-projects-running.qdoc +++ b/doc/qtcreator/src/projects/creator-projects-running.qdoc @@ -25,7 +25,7 @@ To run executable files without deploying them first, select \uicontrol Build > \uicontrol {Run Without Deployment}. To make this the default option, deselect the - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > + \preferences > \uicontrol {Build & Run} > \uicontrol General > \uicontrol {Always deploy project before running it} check box. diff --git a/doc/qtcreator/src/python/creator-python-development.qdoc b/doc/qtcreator/src/python/creator-python-development.qdoc index 75865bd6798..550172738ec 100644 --- a/doc/qtcreator/src/python/creator-python-development.qdoc +++ b/doc/qtcreator/src/python/creator-python-development.qdoc @@ -3,27 +3,27 @@ /*! \page creator-python-development.html - \previouspage creator-copilot.html - \nextpage creator-mime-types.html + \previouspage creator-how-tos.html - \title Developing Qt for Python Applications + \ingroup creator-how-to-projects - \l {https://doc.qt.io/qtforpython/index.html}{Qt for Python} enables you - to use Qt 6 API in Python applications. You can use the PySide6 modules - to gain access to individual Qt modules, such as \l {Qt Core}, \l {Qt GUI}, - and \l {Qt Widgets}. + \title Develop Qt for Python applications + + With \l {https://doc.qt.io/qtforpython/index.html}{Qt for Python}, you can + use Qt 6 API in Python applications. Use the PySide6 modules to gain access + to individual Qt modules, such as \l {Qt Core}, \l {Qt GUI}, and + \l {Qt Widgets}. The following sections describe using \QC for developing with Qt for Python: \list - \li \l{Creating Widget-Based Qt for Python Applications} - {Creating Qt for Python Applications} - \li \l{Setting Up PySide6} - \li \l{Selecting the Python Interpreter} - \li \l{Creating a Virtual Environment} - \li \l{Using Python Interactive Shell} + \li \l{Set up PySide6} + \li \l{Create Qt for Python applications} + \li \l{Select the Python interpreter} + \li \l{Create a virtual environment} + \li \l{Use Python interactive shell} \li \l{Python Language Server} - \li \l{Running Python Projects} + \li \l{Running Python projects} \li \l{Specifying Run Settings for Python Projects} \li \l{PDB} \li \l{Launching the Debugger} @@ -33,7 +33,7 @@ limitations, see \l {https://doc.qt.io/qtforpython/index.html} {Qt for Python}. - \section1 Setting Up PySide6 + \section1 Set up PySide6 If you have not installed the required version of PySide6, \QC prompts you to do so when you open a .py file. @@ -45,12 +45,38 @@ and annotations. Select \uicontrol Install to install PySide6 and the language server. - \section1 Selecting the Python Interpreter + \section1 Create Qt for Python applications + + You can use wizards to create Qt for Python application projects. The wizards + generate a project file, \c {.pyproject}, that lists the files in the Python + project. They also generate a \c {.py} file that has some boilerplate code. + In addition, the widget-based UI wizard creates a \c {.ui} file that has a + \QD form, and the Qt Quick Application wizard creates a \c {.qml} file that + imports Qt Quick controls. + + \note Before importing UI classes and after editing them, create the Python + code from your UI form. In PySide6, run \c{pyside6-uic form.ui -o ui_form.py} + in the \l Terminal view. + + \image qtcreator-new-qt-for-python-app-window-ui-uic.webp {Creating Python code in Terminal} + + The \uicontrol Window wizard adds code to the source file, without the UI + file. + + The \uicontrol Empty wizard adds code to the source file, but it + does not add any classes, so you need to add and instantiate them yourself. + + The \c{.pyproject} files are JSON-based configuration files that replace + the previously used \c {.pyqtc} configuration files. You can still open and + use \c {.pyqtc} files, but we recommend that you choose \c{.pyproject} files + for new projects. + + \section1 Select the Python interpreter You select the initial Python interpreter when you use the Qt for Python Application wizard templates to create Python projects. - \image qtcreator-python-wizard-define-python-interpreter.webp {Define Python Interpreter wizard page} + \image qtcreator-new-qt-for-python-app-widgets-project-details.webp {Define Project Details dialog} You can see the current Python interpreter on the \uicontrol Edit mode toolbar. @@ -64,33 +90,35 @@ To see the available interpreters and choose another interpreter, select the current interpreter, and then select \uicontrol {Manage Python Interpreters}. - Or, select \uicontrol Edit > \uicontrol Preferences > \uicontrol Python > - \uicontrol Interpreters. + Or, select \preferences > \uicontrol Python > \uicontrol Interpreters. - \image qtcreator-python-interpreters.png {Python Interpreters in Preferences} + \image qtcreator-python-interpreters.webp {Python Interpreters in Preferences} You can add and remove interpreters and clean up references to interpreters that you uninstalled, but that still appear in the list. In addition, you can set the interpreter to use by default. - \section1 Creating a Virtual Environment + \section1 Create a virtual environment - To create a virtual environment (\c venv) when you use the Qt for - Python Application wizard templates to create Python projects, select - the \uicontrol {Create new virtual environment} check box on the - \uicontrol {Define Python Interpreter} wizard page. Specify the - directory where to create the environment in + To use a clean \l{https://docs.python.org/3/library/venv.html}{Python} + virtual environment (\c venv) that is independent of your global Python + installation for a Qt for Python project, select the + \uicontrol {Create new virtual environment} check box in the project wizard. + Set the directory where to create the environment in \uicontrol {Path to virtual environment}. - \section1 Using Python Interactive Shell + \section1 Use Python interactive shell - You can write Python code in the Edit mode. Select \uicontrol REPL on the - toolbar to start the \l{https://pythonprogramminglanguage.com/repl/} - {Python interactive shell} in the \l Terminal pane. + You can write Python code in the \uicontrol Edit mode. Select \uicontrol REPL + on the toolbar to start the \l{https://pythonprogramminglanguage.com/repl/} + {Python interactive shell} in the \l Terminal view. - \image qtcreator-terminal-python.webp {Python shell on the taskbar} + \image qtcreator-terminal-python.webp {Python shell in the Terminal view} To start the shell and import the current file as a module, select \uicontrol {REPL Import File}. To also import all functions from the file, select \uicontrol {REPL Import *}. + + \sa {Creating a Qt for Python Application with Qt Widgets}, + {Creating a Qt for Python Application with Qt Quick} */ diff --git a/doc/qtcreator/src/python/creator-python-project.qdocinc b/doc/qtcreator/src/python/creator-python-project.qdocinc deleted file mode 100644 index 80887fc9788..00000000000 --- a/doc/qtcreator/src/python/creator-python-project.qdocinc +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! -//! [python project wizards] - - \section2 Creating Widget-Based Qt for Python Applications - - The Qt for Python Application wizards generate a \c {.pyproject} file that - lists the files in the \l{Developing Qt for Python Applications}{Python} - project and a \c {.py} file that has some boilerplate code. In addition, the - widget-based UI wizard creates a \c {.ui} file that has a \QD form, and the - Qt Quick Application wizard creates a \c {.qml} file that imports Qt Quick - controls. - - The \c{.pyproject} files are JSON-based configuration files that replace - the previously used \c {.pyqtc} configuration files. You can still open and - use \c {.pyqtc} files, but we recommend that you choose \c{.pyproject} files - for new projects. - - The \uicontrol {Window UI} wizard enables you to - create a Python project that has the source file for a class. Specify - the PySide version, class name, base class, and source file for the - class. - - \image qtcreator-python-wizard-app-window.png {Define Class wizard page} - - The wizard adds the imports to the source file for - access to the QApplication, the base class you selected in the Qt - Widgets module, and Qt UI tools: - - \badcode - import sys - - from PySide6.QtWidgets import QApplication, QWidget - \endcode - - \note It is important that you first create the Python code - from your UI form. In PySide6, you can do this by executing - \c{pyside6-uic form.ui -o ui_form.py} on a terminal. This - enables you to import the class that represents your UI - from that Python file. - - Once you generate the Python code from the UI file, - you can import the class: - - \badcode - from ui_form import Ui_Widget - \endcode - - The wizard also adds a main class with the specified name that - inherits from the specified base class: - - \badcode - class Widget(QWidget): - def __init__(self, parent=None): - super().__init__(parent) - \endcode - - The following lines in the main class instantiate the generated Python class from - your UI file, and set up the interface for the current class. - - \badcode - self.ui = Ui_Widget() - self.ui.setupUi(self) - \endcode - - \note You can access the UI elements of the new class as member variables. - For example, if you have a button called \e{button1}, you - can interact with it using \c{self.ui.button1}. - - Next, the wizard adds a main function, where it creates a - QApplication instance. As Qt can receive arguments from the command line, - you can pass any arguments to the QApplication object. Usually, you do not - need to pass any arguments, and you can use the following approach: - - \badcode - if __name__ == "__main__": - app = QApplication(sys.argv) - \endcode - - Next, the wizard instantiates the \c MainWindow class and shows it: - - \badcode - widget = Widget() - widget.show() - ... - \endcode - - Finally, the wizard calls the \c app.exec() method to enter the Qt - main loop and start executing the Qt code: - - \badcode - sys.exit(app.exec()) - \endcode - - You can now modify the boilerplate code in the Edit mode to develop your - Python application. Always regenerate the Python code after modifying a - UI file. - - Open the .ui file in the \uicontrol Design mode to create a widget-based UI - in \QD. - - The \uicontrol Window wizard adds similar code to the source file, without - the UI bits. - - The \uicontrol Empty wizard adds similar code to the source file, but it - does not add any classes, so you need to add and instantiate them yourself. - - For more information about the - \uicontrol {Qt for Python - Qt Quick Application - Empty} wizard, see - \l {Qt Quick Based Python Applications}. - - For examples of creating Qt for Python applications, see - \l {https://doc.qt.io/qtforpython/tutorials/index.html} - {Qt for Python Examples and Tutorials}. - -//! [python project wizards] - - -//! [python qml project wizards] - - \section1 Qt Quick Based Python Applications - - The \uicontrol {Qt for Python - Qt Quick Application - Empty} wizard enables - you to create a Python project that has a main QML file. Specify the - minimum PySide version to run the application. - - \image qtcreator-python-wizard-qml.png {Qt for Python wizard for creating an empty Qt Quick application} - - The wizard adds the following imports to the source file for access - to QGuiApplication and QQmlApplicationEngine: - - \badcode - import sys - from pathlib import Path - - from PySide6.QtGui import QGuiApplication - from PySide6.QtQml import QQmlApplicationEngine - \endcode - - The wizard also adds a main function, where it creates a QGuiApplication - instance and passes system arguments to the QGuiApplication object: - - \badcode - if __name__ == "__main__": - app = QGuiApplication(sys.argv) - ... - \endcode - - The following lines in the main class create a QQmlApplicationEngine - instance and load the generated QML file to the engine object: - - \badcode - engine = QQmlApplicationEngine() - qml_file = Path(__file__).resolve().parent / "main.qml" - engine.load(qml_file) - \endcode - - Finally, the wizard adds code that checks whether the file was successfully - loaded. If loading the file fails, the application exits with an error code. - If loading succeeds, the wizard calls the \c app.exec() method to enter the - Qt main loop and start executing the Qt code: - - \badcode - if not engine.rootObjects(): - sys.exit(-1) - sys.exit(app.exec()) - \endcode - - Open the .qml file in the \uicontrol Edit mode to design a Qt Quick UI, or - use \l{Qt Design Studio Manual}{\QDS}. - -//! [python qml project wizards] -*/ diff --git a/doc/qtcreator/src/python/creator-python-run.qdocinc b/doc/qtcreator/src/python/creator-python-run.qdocinc index 2356607a837..445b32a577b 100644 --- a/doc/qtcreator/src/python/creator-python-run.qdocinc +++ b/doc/qtcreator/src/python/creator-python-run.qdocinc @@ -6,7 +6,7 @@ \section1 Running Python Projects You can execute Qt for Python applications directly from \QC. If you - used the \l{Using Project Wizards}{new project wizard} + used the \l{Use project wizards}{new project wizard} to create the application project, the \c main.py file is automatically executed when you select the \uicontrol Run button. @@ -34,7 +34,7 @@ \li In the \uicontrol Script field, you can see the path to the main file of the project that will be run. \li In the \uicontrol {Command line arguments} field, specify - command line arguments to be passed to the executable. + command-line arguments to be passed to the executable. \endlist If you want to run some other Python file than \c main.py, create a custom diff --git a/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc b/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc new file mode 100644 index 00000000000..98e26bf8b83 --- /dev/null +++ b/doc/qtcreator/src/python/creator-tutorial-python-application-qt-widgets.qdoc @@ -0,0 +1,173 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page tutorial-python-application-qt-widgets.html + \previouspage creator-tutorials.html + \nextpage creator-project-managing.html + + \ingroup creator-tutorials + + \title Creating a Qt for Python Application with Qt Widgets + + \brief How to develop a Qt widget-based application with Python. + + First, create a Qt for Python application project. Then, edit the boilerplate + code to develop a small application that uses Qt widgets to display the text + \e {Hello World} in several languages. + + \image qtcreator-new-qt-for-python-app-widgets-ready.webp {A small Qt Widgets application} + + For more examples of creating Qt for Python applications, see + \l {https://doc.qt.io/qtforpython/tutorials/index.html} + {Qt for Python Examples and Tutorials}. + + \section1 Creating an Empty Window Project + + To create a Qt for Python application that has the source file for a main + class: + + \list 1 + \li Select \uicontrol File > \uicontrol {New Project} > + \uicontrol {Application (Qt for Python)} > \uicontrol {Empty Window} + > \uicontrol Choose. + + The \uicontrol {Project Location} dialog opens. + \image qtcreator-new-qt-for-python-app-widgets-project-location.webp {Project Location dialog} + \li In \uicontrol {Name}, enter the project name. For example, + \e {hello_world}. + \li In \uicontrol {Create in}, enter the path for the project files. + For example, \c {C:\Qt\examples}. + \li Select \uicontrol{Next} (on Windows and Linux) or \uicontrol Continue + (on \macos) to open the \uicontrol {Define Class} dialog. + \image qtcreator-new-qt-for-python-app-widgets-define-class.webp {Define Class dialog} + \li In \uicontrol {Class name}, type \b {MyWidget} as the class + name. + \li In \uicontrol {Base class}, select \b {QWidget} as the base class. + \note The \uicontrol {Source file} field is automatically updated to + match the name of the class. + \li In \uicontrol {Project file}, enter a name for the project file. + \li Select \uicontrol{Next} or \uicontrol Continue to open the + \uicontrol {Define Project Details} dialog. + \image qtcreator-new-qt-for-python-app-widgets-project-details.webp {Define Project Details dialog} + \li In \uicontrol {PySide version}, select the PySide version of the + generated code. + \li In \uicontrol {Interpreter}, select the Python interpreter to use for + the project. + \li Select the \uicontrol {Create new virtual environment} check box to + use a clean \l{https://docs.python.org/3/library/venv.html}{Python} + environment that is independent of your global Python installation. + \li In \uicontrol {Path to virtual environment}, specify the directory + where to create the environment. + \li Select \uicontrol{Next} or \uicontrol Continue. + \li Review the project settings, and select \uicontrol {Finish} (on + Windows and Linux) or \uicontrol Done (on \macos) to create the + project. + \endlist + + The wizard generates the following files: + + \list + \li \c {hellow_world.pyproject}, which lists the files in the Python + project. + \li \c {mywidget.py}, which has some boilerplate code for a class. + \endlist + + \section1 Adding Qt Widgets Imports + + The wizard adds the imports to the \c mywidget.py source file for access to + the QApplication and the base class you selected in the Qt Widgets module, + QWidget. In addition, you need to import \c random and QtCore for randomly + selecting the language of the displayed text and QtWidgets for adding UI + elements: + + \badcode + import sys + import random + from PySide6.QtWidgets import QApplication, QWidget + from PySide6 import QtCore, QtWidgets + \endcode + + \section1 Adding a Widgets-Based UI + + The wizard adds a main class with the specified name that inherits from the + specified base class: + + \badcode + class MyWidget(QWidget): + def __init__(self, parent=None): + super().__init__(parent) + ... + \endcode + + Add button, label, and layout widgets to create UI elements: + + \badcode + ... + self.hello = ["Hallo Welt", "Hei maailma", "Hola Mundo", "Привет мир"] + + self.button = QtWidgets.QPushButton("Click me!") + self.text = QtWidgets.QLabel("Hello World", + alignment=QtCore.Qt.AlignCenter) + + self.layout = QtWidgets.QVBoxLayout(self) + self.layout.addWidget(self.text) + self.layout.addWidget(self.button) + ... + \endcode + + \section1 Adding Signals and Slots + + Then, add a signal and a slot to implement the random function: + + \badcode + ... + self.button.clicked.connect(self.magic) + + @QtCore.Slot() + def magic(self): + self.text.setText(random.choice(self.hello)) + \endcode + + \section1 Adding a Main Function + + The wizard adds a main function, where it creates a QApplication instance. As + Qt can receive arguments from the command line, you can pass any arguments to + the QApplication object. Usually, you do not need to pass any arguments, and + you can use the following approach: + + \badcode + if __name__ == "__main__": + app = QApplication(sys.argv) + ... + \endcode + + \section1 Instantiating the MainWindow Class + + The wizard instantiates the \c MainWindow class and shows it: + + \badcode + ... + widget = MyWidget() + widget.show() + ... + \endcode + + \section1 Executing the Qt Code + + Finally, the wizard calls the \c app.exec() method to enter the Qt + main loop and start executing the Qt code: + + \badcode + ... + sys.exit(app.exec()) + \endcode + + \section1 Running the Application + + Select the \inlineimage icons/run_small.png + button to run the application. + + \sa {Creating a Qt for Python Application with Qt Quick}, + {Develop Qt for Python Applications} +*/ diff --git a/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc b/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc new file mode 100644 index 00000000000..ab9cf0df510 --- /dev/null +++ b/doc/qtcreator/src/python/creator-tutorial-python-application-qtquick.qdoc @@ -0,0 +1,187 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-tutorial-python-application-qt-quick.html + \previouspage creator-tutorials.html + \nextpage creator-project-managing.html + + \ingroup creator-tutorials + + \title Creating a Qt for Python Application with Qt Quick + + \brief How to develop a Qt Quick application with Python. + + First, create a Qt for Python application project. Then, edit the boilerplate + code to develop a small application that uses Qt Quick to display the text + \e {Hello World} in several languages. + + \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-ready.webp {A small Qt Quick application} + + \section1 Creating an Empty Project + + To create a Qt for Python application that has a main QML file: + + \list 1 + \li Select \uicontrol File > \uicontrol {New Project} > + \uicontrol {Application (Qt for Python)} > + \uicontrol {Qt Quick Application - Empty} > \uicontrol Choose. + + The \uicontrol {Project Location} dialog opens. + \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-location.webp {Project Location dialog} + \li In \uicontrol {Name}, enter the project name. For example, + \e {hello_world_quick}. + \li In \uicontrol {Create in}, enter the path for the project files. + For example, \c {C:\Qt\examples}. + \li Select \uicontrol{Next} (on Windows and Linux) or \uicontrol Continue + (on \macos) to open the \uicontrol {Define Project Details} dialog. + \image qtcreator-new-qt-for-python-app-qt-quick-empty-project-details.webp {Define Project Details dialog} + \li In \uicontrol {PySide version}, select the PySide version of + the generated code. + \li In \uicontrol {Interpreter}, select the Python interpreter to use for + the project. + \li Select the \uicontrol {Create new virtual environment} check box to + use a clean \l{https://docs.python.org/3/library/venv.html}{Python} + environment that is independent of your global Python installation. + \li In \uicontrol {Path to virtual environment}, specify the directory + where to create the environment. + \li Select \uicontrol{Next} or \uicontrol Continue. + \li Review the project settings, and select \uicontrol {Finish} (on + Windows and Linux) or \uicontrol Done (on \macos) to create the + project. + \endlist + + The wizard generates the following files: + + \list + \li \c {hello_world_quick.pyproject}, which lists the files in the Python + project. + \li \c {main.py}, which has some boilerplate code. + \li \c {main.qml}, which imports Qt Quick controls. + \endlist + + \section1 Adding Qt Quick Imports + + The wizard adds the following imports to the \c {main.py} source file for + access to QGuiApplication and QQmlApplicationEngine: + + \badcode + import sys + from pathlib import Path + + from PySide6.QtGui import QGuiApplication + from PySide6.QtQml import QQmlApplicationEngine + \endcode + + \section1 Adding a Main Function + + The wizard also adds a main function, where it creates a QGuiApplication + instance and passes system arguments to the QGuiApplication object: + + \badcode + if __name__ == "__main__": + app = QGuiApplication(sys.argv) + ... + \endcode + + \section1 Loading the QML File + + The following lines in the main class create a QQmlApplicationEngine + instance and load the generated QML file to the engine object: + + \badcode + ... + engine = QQmlApplicationEngine() + qml_file = Path(__file__).resolve().parent / "main.qml" + engine.load(qml_file) + ... + \endcode + + Finally, the wizard adds code that checks whether the file was successfully + loaded. If loading the file fails, the application exits with an error code. + If loading succeeds, the wizard calls the \c app.exec() method to enter the + Qt main loop and start executing the Qt code: + + \badcode + ... + if not engine.rootObjects(): + sys.exit(-1) + sys.exit(app.exec()) + ... + \endcode + + \section1 Designing the UI + + Open the \c {main.qml} file in the \uicontrol Edit mode to design a + Qt Quick UI. + + \section2 Adding Imports + + Add imports for Qt Quick Controls and Layouts: + + \badcode + import QtQuick + import QtQuick.Window + import QtQuick.Controls + import QtQuick.Layouts + \endcode + + \section2 Adding Properties and Functions + + The wizard adds a main window: + + \badcode + Window { + width: 640 + height: 480 + visible: true + title: qsTr("Hello World") + } + \endcode + + Add a property and function to randomly select the language of the displayed + text: + + \badcode + ... + readonly property list texts: ["Hallo Welt", "Hei maailma", + "Hola Mundo", "Привет мир"] + + function setText() { + var i = Math.round(Math.random() * 3) + text.text = texts[i] + } + \endcode + + \section2 Adding Qt Quick Controls + + Add \l {Text} and \l {Button} QML types within a \l {ColumnLayout} type to + design the UI: + + \badcode + ColumnLayout { + anchors.fill: parent + + Text { + id: text + text: "Hello World" + Layout.alignment: Qt.AlignHCenter + } + Button { + text: "Click me" + Layout.alignment: Qt.AlignHCenter + onClicked: setText() + } + } + \endcode + + You can also use \l{Qt Design Studio Manual}{\QDS} to design Qt Quick UIs. + + \section1 Running the Application + + Select the \inlineimage icons/run_small.png + button to run the application. + + \sa {Creating a Qt for Python Application with Qt Widgets}, + {Develop Qt for Python Applications} +*/ diff --git a/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc b/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc index 6f5361c2c4d..d7ec1e64177 100644 --- a/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc +++ b/doc/qtcreator/src/qnx/creator-developing-qnx.qdoc @@ -11,7 +11,7 @@ You can connect QNX devices to the development PC to deploy, run and debug applications on them from within \QC. The QNX Neutrino RTOS has additional - command line tools and services, as described in \l {Qt for QNX}. + command-line tools and services, as described in \l {Qt for QNX}. \note In Qt 6, \QC support for QNX is considered experimental. @@ -22,10 +22,11 @@ you need to select \uicontrol {QNX Device} in the \uicontrol {Device Configuration} wizard. - \section1 Adding Kits for QNX Devices + \section1 Add kits for QNX Devices - To view QNX device settings, select \uicontrol Edit > \uicontrol Preferences > + To view QNX device settings, select \preferences > \uicontrol Devices > \uicontrol QNX. Select the \uicontrol {Generate kits} - check box to allow \QC to generate kits for QNX development. For more - information about how to create the kits manually, see \l {Adding Kits}. + check box to allow \QC to generate kits for QNX development. + + \sa {Add kits} */ diff --git a/doc/qtcreator/src/qnx/creator-projects-running-qnx.qdocinc b/doc/qtcreator/src/qnx/creator-projects-running-qnx.qdocinc index 4413be5d1dd..0d9bc2c43d5 100644 --- a/doc/qtcreator/src/qnx/creator-projects-running-qnx.qdocinc +++ b/doc/qtcreator/src/qnx/creator-projects-running-qnx.qdocinc @@ -28,7 +28,7 @@ \section2 Troubleshooting Errors To support running, debugging, and stopping applications from \QC, the QNX - Neutrino RTOS has additional command line tools and services, as described + Neutrino RTOS has additional command-line tools and services, as described in \l {Qt for QNX}. \section3 Debug Output Cannot Be Shown diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index f39905835fc..fce5024189b 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -10,7 +10,7 @@ in the \QC Manual. Use your browser's page search to find a link to a particular topic in the list. For a more extensive search, use the \uicontrol Search function in the \l{https://doc.qt.io/qtcreator/} - {Qt documentation} portal or in the \l {Using the Help Mode}{Help} mode. + {Qt documentation} portal or in the \l {Get help}{Help} mode. \list \li \l{Getting Started} @@ -24,30 +24,8 @@ \li \l{Managing Projects} \list \li \l{Creating Projects} - \list - \li \l{Creating Files} - \li \l{Opening Projects} - \li \l{Adding Libraries to Projects} - \li \l{Adding New Custom Wizards} - \endlist - \li \l{Using Version Control Systems} - \list - \li \l{Using Bazaar} - \li \l{Using ClearCase} - \li \l{Using CVS} - \li \l{Using Fossil} - \li \l{Using Git} - \li \l{Using GitLab} - \li \l{Using Mercurial} - \li \l{Using Perforce} - \li \l{Using Subversion} - \endlist \li \l{Configuring Projects} \list - \li \l{Adding Kits} - \li \l{Adding Qt Versions} - \li \l{Adding Compilers} - \li \l{Adding Debuggers} \li \l{Specifying Build Settings} \list \li \l{Cmake Build Configuration} @@ -61,9 +39,6 @@ \li \l{Conan Build Configuration} \endlist \li \l{Specifying Run Settings} - \li \l{Specifying Editor Settings} - \li \l{Specifying Code Style} - \li \l{Specifying Dependencies} \li \l{Specifying Environment Settings} \li \l{Using Custom Output Parsers} \li \l{Sharing Project Settings} @@ -94,7 +69,6 @@ \li \l{Semantic Highlighting} \li \l{Checking Code Syntax} \li \l{Completing Code} - \li \l{Indenting Text or Code} \li \l{Using Qt Quick Toolbars} \li \l{Pasting and Fetching Code Snippets} \li \l{Using Text Editing Macros} @@ -117,7 +91,6 @@ \li \l{Editing Markdown Files} \li \l{Using Language Servers} \li \l{Using GitHub Copilot} - \li \l{Developing Qt for Python Applications} \li \l{Editing MIME Types} \li \l{Modeling} \li \l{Editing State Charts} @@ -172,7 +145,6 @@ \li \l{Viewing and Editing Register State} \li \l{Debugger Log} \li \l{Viewing Disassembled Code} - \li \endlist \li \l{Stopping Applications} \li \l{Examining Data} @@ -204,115 +176,61 @@ \li \l{Running Autotests} \li \l{Using Squish} \endlist - \li \l{Advanced Use} + \li \l{How To} \list - \li \l{Supported Platforms} + \li Build and Run + \generatelist creator-how-to-build + \li Build with CMake + \generatelist creator-how-to-build-with-cmake + \li Build with qmake + \generatelist creator-how-to-build-with-qmake + \li Debug + \generatelist creator-how-to-debug + \li Design UIs + \generatelist creator-how-to-design + \li Edit Code + \generatelist creator-how-to-edit + \li Manage Kits + \generatelist creator-how-to-manage-kits + \li Manage Projects + \generatelist creator-how-to-projects \list - \li \l {Desktop Platforms} - \li \l {Embedded Platforms} - \li \l {Mobile Platforms} + \li Create Projects + \generatelist creator-how-to-projects-create + \li Create Files + \generatelist creator-how-to-projects-files + \li Configure Projects + \generatelist creator-how-to-projects-configure \endlist - \li \l{Build Systems} - \list - \li \l{Setting Up CMake} - \li \l{Setting Up Qbs} - \li \l{Setting Up an Autotools Project} - \li \l{Setting Up a Generic Project} - \li \l{Setting Up Nimble} - \li \l{Setting Up Meson} - \li \l{Setting Up IncrediBuild} - \li \l{Setting Up Conan} - \li \l{Managing Packages with vcpkg} + \li Read Documentation + \generatelist creator-how-to-get-help + \li Use \QC + \generatelist creator-how-to-use + \li Use the UI + \generatelist creator-how-to-ui \endlist - \li \l{Using Command Line Options} - \li \l{Keyboard Shortcuts} - \li \l{Using External Tools} - \li \l{Showing Task List Files in Issues} - \li \l{Inspecting Internal Logs} - \li \l{Managing Data Collection} - \list - \li \l {Collecting Usage Statistics} - \endlist - \endlist - \li \l{Getting Help} - \list - \li \l{Using the Help Mode} - \li \l{FAQ} - \li \l{How-to} - \list - \li Use the UI - \list - \li \l {Assign keyboard shortcuts} - \li \l {Find a particular preference} - \li \l {Find keyboard shortcuts} - \li \l {Find menu items on \macos} - \li \l {Import and export keyboard shortcuts} - \li \l {Set high DPI scaling} - \li \l {Set the number of recent files shown} - \li \l {Show and hide sidebars} - \li \l {Switch between modes} - \li \l {Switch UI themes} - \li \l {View output} - \endlist - \li Edit Code - \list - \li \l {Add code snippets to the auto-complete menu} - \li \l {Enclose selected code in curly braces, parentheses, or double quotes} - \li \l {Jump to a function in QML code} - \li \l {Locate files using the keyboard} - \li \l {Move between open files} - \li \l {Move to symbols} - \li \l {Paste text from clipboard history} - \li \l {Perform calculations} - \li \l {Search and replace across files using a regular expression} - \li \l {Select the enclosing block in C++} - \li \l {Sort lines alphabetically} - \li \l {Switch to Edit mode} - \li \l {Write down notes} - \endlist - \li Design UIs - \list - \li \l {Export SVG images} - \li \l {View images} - \endlist - \li Manage Projects - \list - \li \l {Add a license header template for C++ code} - \endlist - \li Use \QC - \list - \li \l {Enable and disable plugins} - \li \l {Find settings files} - \li \l {Install plugins} - \li \l {Run \QC from the command line} - \endlist - \endlist \li \l{Reference} + \generatelist creator-reference \list - \li Sidebar Views + \li Build Systems + \generatelist creator-reference-build-systems + \li Preferences + \generatelist creator-reference-preferences \list - \li \l {Call Hierarchy} - \li \l {Class View} - \li \l {File System} - \li \l {Include Hierarchy} - \li \l {Open Documents} - \li \l {Outline} - \li \l {Projects} - \li \l {Type Hierarchy} - \endlist - \li Output Views - \list - \li \l {Application Output} - \li \l {Compile Output} - \li \l {Issues} - \li \l {Search Results} - \li \l {To-Do Entries} + \li C++ + \generatelist creator-reference-preferences-cpp + \li Text editor + \generatelist creator-reference-preferences-text-editor \endlist + \li Version Control Systems + \generatelist creator-reference-vcs + \li Views + \list + \li Output Views + \generatelist creator-reference-output-views + \li Sidebar Views + \generatelist creator-reference-sidebar-views + \endlist \endlist - \li \l{Known Issues} - \li \l{Glossary} - \li \l{Technical Support} - \li \l{Acknowledgements} - \endlist \endlist */ diff --git a/doc/qtcreator/src/qtcreator.qdoc b/doc/qtcreator/src/qtcreator.qdoc index d1aba95665d..fc48d434c86 100644 --- a/doc/qtcreator/src/qtcreator.qdoc +++ b/doc/qtcreator/src/qtcreator.qdoc @@ -51,7 +51,7 @@ \li \b {\l{Managing Projects}} \list \li \l{Creating Projects} - \li \l{Using Version Control Systems} + \li \l{Version Control Systems} \li \l{Configuring Projects} \li \l{Managing Sessions} \endlist @@ -73,8 +73,8 @@ \row \li \inlineimage front-preview.png \li \inlineimage front-testing.png - \li \inlineimage front-advanced.png \li \inlineimage front-help.png + \li \inlineimage front-advanced.png \row \li \b {\l{Building and Running}} \list @@ -91,22 +91,25 @@ \li \l{Running Autotests} \li \l{Using Squish} \endlist - \li \b {\l{Advanced Use}} + \li \b {\l{How To}} \list - \li \l{Supported Platforms} - \li \l{Build Systems} - \li \l{Using Command Line Options} - \li \l{Keyboard Shortcuts} - \li \l{Using External Tools} + \li \l{Design UIs} + \li \l{Edit Code} + \li \l{Manage Projects} + \li \l{Build and Run} + \li \l{Read Documentation} + \li \l{Use \QC} + \li \l{Use the UI} \endlist - \li \b {\l{Getting Help}} + \li \b {\l{Reference}} \list - \li \l{Using the Help Mode} - \li \l{FAQ} - \li \l{How-to} - \li \l{Reference} - \li \l{Known Issues} - \li \l{Glossary} + \li \l {Build Systems} + \li \l {Command-Line Options} + \li \l {Custom Wizards} + \li \l {Keyboard Shortcuts} + \li \l {Preferences} + \li \l {Supported Platforms} + \li \l {Reference}{See More...} \endlist \row \li {4,1} \b {Contact Us} diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc index 9bc2c37c1ce..42e3b987c2c 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc @@ -33,7 +33,7 @@ \QC creates the necessary boilerplate files. Some of the files are specific to a particular target platform. You can use wizard templates - also to \l{Creating Files}{add files} to the projects. + also to \l{Create files}{add files} to the projects. \section1 Qt Quick Applications @@ -67,7 +67,7 @@ \QDS, select \uicontrol {Create a project that you can open in \QDS}. \li In the \uicontrol {Build system} field, select the build system to - use for building and running the project: \l {Setting Up CMake} + use for building and running the project: \l {CMake} {CMake} or \l {Setting Up Qbs}{Qbs}. \li Select \uicontrol Next to open the @@ -92,11 +92,9 @@ \li Select \l{glossary-buildandrun-kit}{kits} for the platforms that you want to build the application for. - \note Kits are listed if they have been specified in \uicontrol Edit - > \uicontrol Preferences > \uicontrol Kits (on Windows and Linux) - or in \uicontrol {\QC} > \uicontrol Preferences > - \uicontrol Kits (on \macos). - For more information, see \l {Adding Kits}. + \note Kits are listed if they have been specified in \preferences > + \uicontrol Kits. + For more information, see \l {Add kits} and \l {Kits}. \li Select \uicontrol Next to open the \uicontrol {Project Management} dialog. @@ -110,7 +108,7 @@ \QC creates a QML file, \e Main.qml, that you can modify in the \uicontrol Edit mode. - \include creator-python-project.qdocinc python qml project wizards + \sa {Creating a Qt for Python Application with Qt Quick} \section1 Qt Quick UI Projects @@ -170,11 +168,9 @@ \li Select \l{glossary-buildandrun-kit}{kits} for the platforms that you want to build the application for. - \note Kits are listed if they have been specified in \uicontrol Edit - > \uicontrol Preferences > \uicontrol Kits (on Windows and Linux) - or in \uicontrol {\QC} > \uicontrol Preferences > - \uicontrol Kits (on \macos). - For more information, see \l {Adding Kits}. + \note Kits are listed if they have been specified in \preferences > + \uicontrol Kits. + For more information, see \l {Add kits} and \l {Kits}. \li Select \uicontrol Next to open the \uicontrol {Project Management} dialog. diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-designer-plugin.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-designer-plugin.qdoc index 7e95c03bb20..9d90b78ace5 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-designer-plugin.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-designer-plugin.qdoc @@ -21,13 +21,5 @@ For more information about using \QMLD, see \l{Qt Design Studio Manual}. - \section1 Enabling the \QMLD Plugin - - To enable the \QMLD plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Qt Quick} > \uicontrol {QmlDesigner}. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc b/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc index 8e439c3ce27..1abd280e56b 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-tutorial-create-empty-project.qdocinc @@ -42,11 +42,9 @@ applications for mobile devices, select kits also for Android and iOS. - \note The list shows kits that you specify in \uicontrol Edit - > \uicontrol Preferences > \uicontrol Kits (on Windows and Linux) - or in \uicontrol {\QC} > \uicontrol Preferences > - \uicontrol Kits (on \macos). - For more information, see \l {Adding Kits}. + \note The list shows kits that you specify in \preferences > + \uicontrol Kits. + For more information, see \l {Add kits} and \l {Kits}. \li Select \uicontrol Next to open the \uicontrol {Project Management} dialog. diff --git a/doc/qtcreator/src/qtquick/creator-preferences-qtquick-code-style.qdoc b/doc/qtcreator/src/qtquick/creator-preferences-qtquick-code-style.qdoc new file mode 100644 index 00000000000..6ea13251620 --- /dev/null +++ b/doc/qtcreator/src/qtquick/creator-preferences-qtquick-code-style.qdoc @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-preferences-qtquick-code-style.html + \if defined(qtdesignstudio) + \previouspage creator-preferences-text-editor-behavior.html + \nextpage qt-quick-toolbars.html + \else + \previouspage creator-reference.html + \endif + + \ingroup creator-reference-preferences + + \title Qt Quick Code Style + + \brief Set QML code style. + + To specify QML code style globally: + + \list 1 + \li Select \preferences > \uicontrol {Qt Quick}. + \li In the \uicontrol {Current settings} field, select the settings to + modify and click \uicontrol Copy. + \image qtcreator-options-code-style-qml.png {QML Code Style preferences} + \li Give a name to the settings and click \uicontrol OK. + \li Click \uicontrol Edit to specify code style settings for the project. + \image qtcreator-code-style-settings-edit-qtquick.png {Edit Code Style dialog} + \endlist + + You can specify how to interpret the \key Tab key presses and how to align + continuation lines. + + In \uicontrol {Line length}, you can adjust the maximum line length for + code lines. + + To override the global preferences for a particular project, select + \uicontrol Projects > \uicontrol {Code Style}. + + \sa {Indent text or code} + + \if defined(qtcreator) + \sa {Find preferences}, {Specify code style} + \endif +*/ diff --git a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc index 61033ee685d..0fd232d8b9c 100644 --- a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc @@ -124,7 +124,7 @@ You can specify flushing settings for the QML Profiler either globally for all projects or separately for each project. To specify global settings, - select \uicontrol Edit > \uicontrol Preferences > \uicontrol Analyzer. + select \preferences > \uicontrol Analyzer. To specify custom QML Profiler settings for a particular project, select \uicontrol Projects > \uicontrol Run and then select \uicontrol Custom in diff --git a/doc/qtcreator/src/qtquick/qtquick-toolbars.qdoc b/doc/qtcreator/src/qtquick/qtquick-toolbars.qdoc index a1eea8edfb2..2363e10a56b 100644 --- a/doc/qtcreator/src/qtquick/qtquick-toolbars.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-toolbars.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,11 +8,12 @@ // ********************************************************************** /*! - \previouspage creator-indenting-code.html \page qt-quick-toolbars.html \if defined(qtdesignstudio) + \previouspage creator-preferences-qtquick-code-style.html \nextpage creator-diff-editor.html \else + \previouspage creator-completing-code.html \nextpage creator-editor-codepasting.html \endif @@ -23,7 +24,7 @@ . Select the icon to open the toolbar. To open toolbars immediately when you select a QML type, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Qt Quick} > + \preferences > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} > \uicontrol {Always show Qt Quick Toolbar}. \image qtcreator-qml-js-editing.webp {QML/JS Editing preferences} diff --git a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc index b2a9f0dfe1d..2b4ddbdb942 100644 --- a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc @@ -32,8 +32,8 @@ in the menu (1). Select \uicontrol Home to move to the user's home directory. Further, you can select a project to move to an open project or \uicontrol Projects to move to the directory specified in the - \uicontrol {Projects directory} field in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General. + \uicontrol {Projects directory} field in \preferences > + \uicontrol {Build & Run} > \uicontrol General. The file that is currently active in the editor determines which folder to display in the \uicontrol {File System} view: @@ -62,12 +62,11 @@ \li Show the file or directory in the file explorer. \li Open a terminal window in the selected directory or in the directory that has the file. To specify the terminal to use on Linux and - \macos, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + \macos, select \preferences > \uicontrol Environment > + \uicontrol System. \if defined(qtcreator) - To use an \l{Terminal} {internal terminal}, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Terminal > - \uicontrol {Use internal terminal}. + To use an \l{Terminal} {internal terminal}, select \preferences > + \uicontrol Terminal > \uicontrol {Use internal terminal}. \endif \li Search from the selected directory. \li View file properties, such as name, path, MIME type, default editor, @@ -77,7 +76,7 @@ \if defined(qtdesignstudio) \l{Adding Files to Projects}. \else - \l{Creating Files}. + \l{Create files}. \endif \li Rename existing files. To move the file to another directory, enter the relative or absolute path to its new location in addition to the @@ -111,5 +110,7 @@ To stop the synchronization with the file currently open in the editor, deselect \inlineimage icons/linkicon.png (\uicontrol {Synchronize with Editor}). + + \sa {View CMake project contents}, {Projects} \endif */ diff --git a/doc/qtcreator/src/user-interface/creator-how-to-view-output.qdoc b/doc/qtcreator/src/user-interface/creator-how-to-view-output.qdoc index a6e4746e7c8..b83a54dbe39 100644 --- a/doc/qtcreator/src/user-interface/creator-how-to-view-output.qdoc +++ b/doc/qtcreator/src/user-interface/creator-how-to-view-output.qdoc @@ -52,8 +52,8 @@ If the text in the output is not displayed correctly, \QC might be using a different codec from the one used by the tools that generate - the output. To specify the codec to use, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Environment > \uicontrol Interface, and + the output. To specify the codec to use, select \preferences > + \uicontrol Environment > \uicontrol Interface, and then select the codec in the \uicontrol {Text codec for tools} field. \image qtcreator-preferences-environment-interface.webp {Interface tab in Environment preferences} diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-detach-views.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-detach-views.qdoc new file mode 100644 index 00000000000..3534e055ec8 --- /dev/null +++ b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-detach-views.qdoc @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-detach-views.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-ui + + \title Detach views + + You can detach \QC views that have the \inlineimage icons/detach-view.png + icon. You can then move them to other places on the screen or to other + screens. + + To detach views: + + \list + \li Double-click the title bar of the view. + \li Grab the title bar of the view with the mouse and start moving the + view to another position on the screen. + \li Select \inlineimage icons/detach-view.png + . + \endlist + + To show the title bars of views, select \uicontrol View > \uicontrol Views, + and deselect the \uicontrol {Automatically Hide Title Bars} check box. + + \section1 Attach views + + To attach views, drag them to a highlighted dock area. + + \image qtcreator-attach-views.webp {Attaching a view to a dock area} + + \section1 Revert changes + + To open closed views, select \uicontrol View > \uicontrol Views. + + To revert the changes, select \uicontrol View > \uicontrol Views > + \uicontrol {Reset to Default Layout}. +*/ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-main-menu.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-main-menu.qdoc new file mode 100644 index 00000000000..22d57b98c27 --- /dev/null +++ b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-main-menu.qdoc @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page creator-how-to-show-and-hide-main-menu.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-ui + + \title Show and hide the main menu + + On Linux and Windows, you can hide the main menu bar to save space on the + screen. Select \uicontrol View, and deselect the \uicontrol {Show Menu Bar} + check box. + + \image qtcreator-without-menubar.webp {Qt Creator without the main menu} + \caption \QC without the main menu bar. + + To show the main menu again, press \key {Ctrl+Alt+M}. + + \sa {Show and hide sidebars},{Switch between modes} +*/ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-sidebars.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-sidebars.qdoc index 52ecb03c011..230ce2adde6 100644 --- a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-sidebars.qdoc +++ b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-show-and-hide-sidebars.qdoc @@ -35,5 +35,5 @@ In some views, right-clicking opens a context menu that has functions for managing the objects listed in the view. - \sa {Sidebar Views} + \sa {Sidebar Views},{Show and hide the main menu},{Switch between modes} */ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-switch-between-modes.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-switch-between-modes.qdoc index fb023f1f3c1..a9c4e26dcbc 100644 --- a/doc/qtcreator/src/user-interface/creator-only/creator-how-to-switch-between-modes.qdoc +++ b/doc/qtcreator/src/user-interface/creator-only/creator-how-to-switch-between-modes.qdoc @@ -60,7 +60,7 @@ \li \uicontrol Help \li \key Ctrl+6 \li Read documentation. - \li \l{Using the Help Mode} + \li \l{Get help} \endtable Some actions in \QC trigger a mode change. For example, @@ -72,4 +72,6 @@ \uicontrol View > \uicontrol {Mode Selector Style} > \uicontrol Hidden. To only show icons on the mode selector, select the \uicontrol {Icons Only} style. + + \sa {Show and hide sidebars},{Show and hide the main menu} */ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc index abfae8ba8b2..2be2fd8a2c3 100644 --- a/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc @@ -15,13 +15,12 @@ application or the \uicontrol {Open Terminal} button to open a terminal, it opens as an output view. - To open the terminal in a separate window, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Terminal, and deselet the - \uicontrol {Use internal terminal} check box. + To open the terminal in a separate window, select \preferences > + \uicontrol Terminal, and deselet the \uicontrol {Use internal terminal} + check box. On Linux and \macos, you can set the terminal to open by selecting - \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + \preferences > \uicontrol Environment > \uicontrol System. \image qtcreator-output-terminal.webp {Terminal pane} @@ -53,6 +52,8 @@ \li To open terminal preferences, select \inlineimage icons/settings.png (\uicontrol Configure). + \li To keep \QC keyboard shortcuts from interfering in the terminal, + select \inlineimage icons/shortcuts.png. \endlist Most of the \QC keyboard shortcuts are disabled in the terminal, except the @@ -63,9 +64,9 @@ \section1 Terminal Preferences - To set preferences for the internal terminal, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Terminal, or select the - \uicontrol Configure button in the \uicontrol Terminal pane. + To set preferences for the internal terminal, select \preferences > + \uicontrol Terminal, or select the \uicontrol Configure button in + the \uicontrol Terminal pane. \image qtcreator-preferences-terminal.webp {Terminal tab in Preferences} @@ -80,12 +81,18 @@ \row \li \uicontrol {Send escape key to terminal} \li Send the escape key to the terminal instead of closing the terminal. + \row + \li \uicontrol {Block shortcuts in terminal} + \li Keep \QC keyboard shortcuts from interfering in the terminal. \row \li \uicontrol {Audible bell} \li Play an audible bell when the a bell character is received. \row \li \uicontrol {Allow blinking cursor} \li Allow the cursor to blink. + \row + \li \uicontrol {Enable mouse tracking} + \li Enable mouse tracking in the terminal. \row \li \uicontrol {Font} \li Select the \uicontrol {Font family} and \uicontrol Size for the text @@ -97,6 +104,10 @@ \li Set colors for the \uicontrol Terminal pane \uicontrol Foreground, \uicontrol Background, \uicontrol Selection, and \uicontrol {Find match}. + + To use an existing color scheme, select \uicontrol {Load Theme}. + + To revert color changes, select \uicontrol {Reset Theme}. \row \li \uicontrol {Default shell} \li Set the full path to the default terminal executable in @@ -104,8 +115,5 @@ in \uicontrol {Shell arguments}. \endtable - To use an existing color scheme, select \uicontrol {Load Theme}. To revert - color changes, select \uicontrol {Reset Theme}. - - \sa {View output} + \sa {View output}, {Keyboard Shortcuts} */ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc index fb1b40f2633..e6ca1c51815 100644 --- a/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-only/creator-reference-to-do-entries-view.qdoc @@ -9,6 +9,8 @@ \title To-Do Entries + \note Enable the Todo plugin to use it. + \brief Lists the BUG, FIXME, NOTE, TODO, and WARNING keywords from the current file, from all project files, or from a subproject. @@ -19,22 +21,11 @@ You can also open task list files generated by code scanning and analysis tools in \l Issues. For more information, see - \l{Showing Task List Files in Issues}. - - \section1 Enabling the To-Do Plugin - - The Todo plugin is disabled by default. To enable it: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol Utilities > \uicontrol Todo. - \li Select \uicontrol OK. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \l{Show task list files in Issues}. \section1 To-Do Preferences - To add keywords, select \uicontrol Edit > \uicontrol Preferences > + To add keywords, select \preferences > \uicontrol {To-Do} > \uicontrol Add. Set an icon and a line background color for the keyword. diff --git a/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc b/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc index 0ad0967dc0c..e758c6c2b27 100644 --- a/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-open-documents-view.qdoc @@ -36,8 +36,7 @@ \section1 Setting Preferences for Opening Files To set preferences for opening files and handling open files, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > - \uicontrol System: + \preferences > \uicontrol Environment > \uicontrol System: \image qtcreator-options-environment-system.png {System tab in Environment preferences} diff --git a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc index 529abfed348..4c1e9e9c54d 100644 --- a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc @@ -73,7 +73,7 @@ \if defined(qtdesignstudio) \l{Adding Files to Projects}. \else - \l{Creating Files}. + \l{Create files}. \endif \li Rename existing files. If you change the base name of a file, \QC displays a list of other files with the same base name @@ -85,19 +85,18 @@ {generic projects}. \li Add existing files and directories. \li Add libraries. For more information, see - \l{Adding Libraries to Projects}. + \l{Add libraries to qmake projects}. \li Add and remove subprojects. \li Find unused functions. \endif \li Search from the selected directory. \li Open a terminal window in the project directory. To specify the - terminal to use on Linux and \macos, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Environment > \uicontrol System. + terminal to use on Linux and \macos, select \preferences > + \uicontrol Environment > \uicontrol System. \if defined(qtcreator) - To use an \l{Terminal}{internal terminal}, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Terminal > - \uicontrol {Use internal terminal}. + To use an \l{Terminal}{internal terminal}, select \preferences > + \uicontrol Terminal > \uicontrol {Use internal terminal}. \endif \li Open a terminal window in the project directory that you configured for building or running the project. @@ -106,9 +105,9 @@ \li Close all files in a project. \li Close the selected project or all projects except the selected one. By default, this closes all files in the projects. To keep - them open, deselect the \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General - > \uicontrol {Close source files along with project} check box. + them open, deselect the \preferences > \uicontrol {Build & Run} > + \uicontrol General > \uicontrol {Close source files along with project} + check box. \endlist For managing files and directories, use the same functions as in @@ -146,11 +145,13 @@ enables you to specify exactly where to place a new file in the build system. If you cannot see some files, you might have to declare them as part of the - project. For more information, see \l{Specifying Project Contents}. + project. For more information, see \l{Specify project contents}. If the project is under version control, you might see information from the version control system in brackets after the project name. \QC currently implements this for Git (the view displays the branch name or a tag) and ClearCase (the view displays the branch name). + + \sa {View CMake project contents}, {File System} \endif */ diff --git a/doc/qtcreator/src/user-interface/creator-reference-output-views.qdoc b/doc/qtcreator/src/user-interface/creator-reference-output-views.qdoc index 011a4bdfee7..523906cbc75 100644 --- a/doc/qtcreator/src/user-interface/creator-reference-output-views.qdoc +++ b/doc/qtcreator/src/user-interface/creator-reference-output-views.qdoc @@ -27,72 +27,14 @@ and presents the issues in an organized way. To further filter the output by type, select \inlineimage icons/filtericon.png - (\uicontrol {Filter Tree}) and then select a filter: + (\uicontrol {Filter Tree}) and then select a filter. See the tooltips for + more information about each filter. - \list - - \if defined(qtdesignstudio) - \li \uicontrol {Asset Export} - Errors and warnings encountered - while exporting assets. - - \li \uicontrol {Asset Importer Error} - Errors and warnings encountered - while importing assets from a design tool. - \else - \li \uicontrol Autotests - Errors and warnings encountered while running - tests. - \endif - - \li \uicontrol {Build System} - Errors and warnings encountered when - opening and managing projects. - - \if defined(qtcreator) - \li \uicontrol {Clang Code Model} - - \l {Parsing C++ Files with the Clang Code Model} - {Errors and warnings from the current editor}. - \li \uicontrol {Clang Tools} - Errors and warnings from - \l {Using Clang Tools}{Clang-Tidy and Clazy} - \endif - - \li \uicontrol Compile - Selected output from the compiler. Open - \uicontrol {Compile Output} for more detailed information. - - \li \uicontrol{Debug Information} - Lists debug information packages that might - be missing. - - \if defined(qtcreator) - \li \uicontrol Debugger - Errors encountered while running the - \l{Analyzing Code}{Valgrind code analysis tools}. - \endif - - \li \uicontrol{Debugger Runtime} - Errors encountered when starting \QC. For - example, information about missing DLLs. - - \li \uicontrol Deployment - Errors encountered between building an application - successfully and starting it on a \l{glossary-device}{device}. - - \if defined(qtcreator) - \li \uicontrol {My Tasks} - Entries from a task list file (.tasks) generated - by \l{Showing Task List Files in Issues} - {code scanning and analysis tools}. - - \li \uicontrol Python - Runtime errors and exceptions of Python scripts. - \endif - - \li \uicontrol QML and \uicontrol {QML Analysis} - - \l{JavaScript and QML Error Codes} - {QML and JavaScript syntax errors}. - - \if defined(qtcreator) - \li \uicontrol Sanitizer - Tasks created when you run an application if - you used an \e {address sanitizer} to detect memory handling issues. - \endif - \endlist + \image qtcreator-issues.webp {Issues} To find output in the view, enter search criteria in the \uicontrol Filter field. - \image qtcreator-issues.webp {Issues} - Select one or several lines to apply context-menu actions to their contents. You can remove the selected lines or copy their contents to the clipboard. For single lines, you can search the Internet for a solution using the @@ -115,9 +57,14 @@ or press \key F6 and \key Shift+F6. By default, a new build clears the \uicontrol Issues view. To keep - the issues from the previous build rounds, deselect \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General > + the issues from the previous build rounds, deselect \preferences > + \uicontrol {Build & Run} > \uicontrol General > \uicontrol {Clear issues list on new build}. + + \sa {View output} + \if defined(qtcreator) + \sa {Show task list files in Issues} + \endif */ /*! @@ -148,6 +95,8 @@ For more information about the different search options, see \l {Finding and Replacing}. + + \sa {View output} */ /*! @@ -170,7 +119,7 @@ \image qtcreator-application-output.webp {Application Output} \if defined(qtcreator) - If you specify command line arguments in the run settings that are passed + If you specify command-line arguments in the run settings that are passed to the application when running it, they are displayed as a part of the application output. For more information, see \l{Specifying Run Settings for Desktop Device Types}. @@ -184,8 +133,8 @@ To set preferences for displaying application output: \list - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Build & Run} > \uicontrol {Application Output}. + \li Select \preferences > \uicontrol {Build & Run} > + \uicontrol {Application Output}. \li Select the \inlineimage icons/settings.png (\uicontrol {Open Settings Page}) button. \endlist @@ -195,6 +144,8 @@ You can select whether to open \uicontrol{Application Output} on output when running or debugging applications, to clear old output on a new run, to word-wrap output, and to limit output to the specified number of lines. + + \sa {View output} */ /*! @@ -239,8 +190,8 @@ \li In the \uicontrol {Compile Output} view, select \inlineimage icons/settings.png (\uicontrol {Open Settings Page}). - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Build & Run} > \uicontrol {Compile Output}. + \li Select \preferences > \uicontrol {Build & Run} > + \uicontrol {Compile Output}. \endlist \image qtcreator-preferences-compile-output.webp {Compile Output tab in Preferences} \li Select the \uicontrol {Open Compile Output when building} check box. @@ -273,4 +224,6 @@ the new output to the old output. \li Select \uicontrol OK to start parsing. \endlist + + \sa {View output} */ diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc index 220d9af132a..bf4bb2c455d 100644 --- a/doc/qtcreator/src/user-interface/creator-ui.qdoc +++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc @@ -35,7 +35,7 @@ \li Kit selector \li Select the appropriate \l{glossary-buildandrun-kit}{kit} for building the project and running it on particular hardware. - \li \l{Activating Kits for a Project} + \li \l{Activate kits for a project} \row \li \inlineimage numbers/03.png \li Run button @@ -76,7 +76,7 @@ For information about new features and bug fixes in each \QC release, select \uicontrol Help > \uicontrol {Change Log}. - \sa {Use the UI}{How-to: Use the UI}, {Reference} + \sa {Use the UI}{How To: Use the UI}, {Reference} \else @@ -103,7 +103,7 @@ To toggle the visibility of these menu items: \list 1 - \li Go to \uicontrol Edit > \uicontrol Preferences. + \li Go to \preferences. \li On the \uicontrol Environment tab, select \uicontrol{Qt Design Studio Configuration}. \li Clear the checkbox for the items that you want to be visible. @@ -118,7 +118,7 @@ The following topics describe how to customize the UI: \list - \li \l {Find menu items on \macos} + \li \l {Find preferences} \li \l {Set high DPI scaling} \li \l {Switch UI themes} \li \l {View output} diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-bazaar.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-bazaar.qdoc index bc2eda804ba..d5d2b9a06a8 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-bazaar.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-bazaar.qdoc @@ -8,16 +8,19 @@ // ********************************************************************** /*! - \previouspage creator-version-control.html \page creator-vcs-bazaar.html - \nextpage creator-vcs-clearcase.html + \previouspage creator-reference.html - \title Using Bazaar + \ingroup creator-reference-vcs + + \title Bazaar + + \brief Additional Bazaar functions. Bazaar is a free version control system sponsored by Canonical. In addition to the standard version control system functions described in - \l {Using Common Functions}, you can select \uicontrol Tools > + \l {Use common VCS Functions}, you can select \uicontrol Tools > \uicontrol Bazaar > \uicontrol Pull to turn a branch into a mirror of another branch. To update the mirror of the branch, select \uicontrol Push. @@ -43,13 +46,13 @@ \section1 Bazaar Preferences - To set Bazaar preferences, select \uicontrol Edit > \uicontrol Preferences > + To set Bazaar preferences, select \preferences > \uicontrol {Version Control} > \uicontrol Bazaar: \image qtcreator-preferences-vcs-bazaar.webp {Bazaar preferences} \list - \li \uicontrol Command specifies the path to the command line client + \li \uicontrol Command specifies the path to the command-line client executable. \li \uicontrol {Default username} and \uicontrol {Default email} specify the username and email address to use by default when @@ -58,4 +61,7 @@ have. \li \uicontrol Timeout sets a timeout for version control operations. \endlist + + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-clearcase.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-clearcase.qdoc index dfed2c5ba68..740eabe5a8b 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-clearcase.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-clearcase.qdoc @@ -8,11 +8,14 @@ // ********************************************************************** /*! - \previouspage creator-vcs-bazaar.html \page creator-vcs-clearcase.html - \nextpage creator-vcs-cvs.html + \previouspage creator-reference.html - \title Using ClearCase + \ingroup creator-reference-vcs + + \title ClearCase + + \brief Additional ClearCase functions. IBM Rational ClearCase is a version control, workspace management, parallel development support, and build automation solution developed by IBM. Use the @@ -28,8 +31,8 @@ \li Download \l{http://gnuwin32.sourceforge.net/packages/diffutils.htm} {Diffutils} and extract it to a directory in your PATH. - \li Select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Version Control} > \uicontrol ClearCase. + \li Select \preferences > \uicontrol {Version Control} > + \uicontrol ClearCase. \image qtcreator-preferences-vcs-clearcase.webp {ClearCase preferences} @@ -44,14 +47,14 @@ \section1 Checking out and Checking in In addition to the standard version control system functions described in - \l {Using Common Functions}, you can check out and check in files. + \l {Use common VCS Functions}, you can check out and check in files. To create a writable copy of a file, select \uicontrol Tools > \uicontrol ClearCase > \uicontrol {Check Out}. If you check out files in a Unified Change Management (UCM) view, they are added to the change set of a UCM activity. By default, the activities are automatically assigned names. - To disable this functionality, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Version Control} > \uicontrol ClearCase, and then deselect the + To disable this functionality, select \preferences > + \uicontrol {Version Control} > \uicontrol ClearCase, and then deselect the \uicontrol {Auto assign activity names} check box. To automatically check out files when you edit them, select the @@ -68,15 +71,14 @@ To create a permanent new version of the current file or all files in the versioned object base (VOB), select \uicontrol Tools > \uicontrol {ClearCase} > \uicontrol {Check In}. To confirm - that you want to check in the files, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol ClearCase, + that you want to check in the files, select \preferences > + \uicontrol {Version Control} > \uicontrol ClearCase, and then select the \uicontrol {Prompt on check-in} check box. By default, you have to enter a comment when checking files out or in. - To suppress this prompt, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Version Control} > \uicontrol ClearCase, and then select the - \uicontrol {Do not prompt for comment during checkout or check-in} check - box. + To suppress this prompt, select \preferences > \uicontrol {Version Control} > + \uicontrol ClearCase, and then select the + \uicontrol {Do not prompt for comment during checkout or check-in} check box. If you change the read-only attribute of a file that is loaded into a snapshot view and modify the file without checking it out, you \e hijack the @@ -84,12 +86,15 @@ \uicontrol Tools > \uicontrol ClearCase > \uicontrol {Undo Hijack}. By default, the files in the VOBs are indexed for quick access to their - statuses. To disable indexing, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Version Control} > \uicontrol ClearCase, and then select the + statuses. To disable indexing, select \preferences > + \uicontrol {Version Control} > \uicontrol ClearCase, and then select the \uicontrol {Disable indexer} check box. To only have some VOBs indexed, specify them in the \uicontrol {Index only VOBs} field. Specify the number of event records to show in \uicontrol {History count}. Set a timeout for version control operations in \uicontrol Timeout. + + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-cvs.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-cvs.qdoc index 440b8846933..54e9b67eb91 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-cvs.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-cvs.qdoc @@ -8,16 +8,19 @@ // ********************************************************************** /*! - \previouspage creator-vcs-clearcase.html \page creator-vcs-cvs.html - \nextpage creator-vcs-fossil.html + \previouspage creator-reference.html - \title Using CVS + \ingroup creator-reference-vcs + + \title CVS + + \brief Additional CVS functions. CVS is an open source version control system. In addition to the standard version control system functions described in - \l {Using Common Functions}, you can select \uicontrol Tools > + \l {Use common VCS Functions}, you can select \uicontrol Tools > \uicontrol CVS > \uicontrol Edit to set a file as writable, notify watchers that the file is being edited, and watch for actions performed on the file by other users. @@ -31,13 +34,13 @@ \section1 CVS Preferences - To set CVS preferences, select \uicontrol Edit > \uicontrol Preferences > + To set CVS preferences, select \preferences > \uicontrol {Version Control} > \uicontrol CVS: \image qtcreator-preferences-vcs-cvs.webp {CVS preferences} \list - \li \uicontrol {CVS Command} specifies the path to the command line + \li \uicontrol {CVS Command} specifies the path to the command-line client executable. \li \uicontrol {CVS root} specifies the CVS root. \li \uicontrol Timeout sets a timeout for version control operations. @@ -45,4 +48,7 @@ \li \uicontrol {Describe all files matching commit id} annotates all files that belong to the commit. \endlist + + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-fossil.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-fossil.qdoc index f3ce2565461..f8707657876 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-fossil.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-fossil.qdoc @@ -3,10 +3,13 @@ /*! \page creator-vcs-fossil.html - \previouspage creator-vcs-cvs.html - \nextpage creator-vcs-git.html + \previouspage creator-reference.html - \title Using Fossil + \ingroup creator-reference-vcs + + \title Fossil + + \brief Additional Fossil functions. Fossil is an open source distributed version control system, designed and developed by the creator of SQLite. A stand-alone Fossil executable @@ -26,7 +29,7 @@ \li Create or designate a directory to store local Fossil repositories and remote clones. For example: \c ~/fossils/qt. - \li Select \uicontrol Edit > \uicontrol Preferences > + \li Select \preferences > \uicontrol {Version Control} > \uicontrol Fossil, and set the designated directory in the \uicontrol {Default path} field. @@ -44,7 +47,7 @@ \section1 Additional Fossil Functions In addition to the standard version control system functions described in - \l {Using Version Control Systems}, the \uicontrol Fossil submenu has + \l {Version Control Systems}, the \uicontrol Fossil submenu has the following items: \table @@ -68,11 +71,11 @@ \section1 Fossil Preferences - To set Fossil preferences, select \uicontrol Edit > \uicontrol Preferences > + To set Fossil preferences, select \preferences > \uicontrol {Version Control} > \uicontrol Fossil: \list - \li \uicontrol Command specifies the path to the command line client + \li \uicontrol Command specifies the path to the command-line client executable. \li \uicontrol {Default path} sets the path to the directory to store local repositories by default. @@ -89,4 +92,7 @@ a commit or update and automatic push after a commit or tag or branch creation. \endlist + + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-gitlab.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-gitlab.qdoc index c458d5d0079..72dc3dacea2 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-gitlab.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-gitlab.qdoc @@ -1,35 +1,35 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-vcs-git.html \page creator-vcs-gitlab.html - \nextpage creator-vcs-mercurial.html + \previouspage creator-reference.html - \title Using GitLab + \ingroup creator-reference-vcs + + \title GitLab + + \brief Additional GitLab functions. GitLab is a DevOps tool developed by GitLab. You can clone projects from - GitLab servers and use \l{Using Git}{Git} to manage your local and remote + GitLab servers and use \l{Git} to manage your local and remote repositories. - To enable the experimental GitLab plugin, select \uicontrol Help > - \uicontrol {About Plugins} > \uicontrol {Version Control} > - \uicontrol GitLab. Then select \uicontrol {Restart Now} to - restart \QC and load the plugin. - To use GitLab, you must create a connection to the GitLab server and clone the projects you want to work on. You can also link previously cloned projects to GitLab in the project settings. This enables you to receive event notifications in the \l {View output}{Version Control} pane. + \note Enable the GitLab plugin to use it. + \section1 Connecting to GitLab Servers To connect to a GitLab server, you need to specify the server host name and port number, as well as an access token that you create in GitLab for \QC. The permission scope of the token must be at least \c read_api or \c api. - To specify connections to GitLab servers, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol GitLab: + To specify connections to GitLab servers, select \preferences > + \uicontrol {Version Control} > \uicontrol GitLab: \image qtcreator-gitlab-preferences.png @@ -92,7 +92,7 @@ open, select \uicontrol File > \uicontrol {New Project} > \uicontrol {Import Project} > \uicontrol {Import Existing Project} to import the project as a generic project. For more information, see - \l {Using Project Wizards}. + \l {Use project wizards}. \section1 Linking Projects with GitLab @@ -121,4 +121,7 @@ To stop the reception of event notifications, select \uicontrol {Unlink from GitLab}. + + \sa {Enable and disable plugins}, {Set up version control systems}, + {Use common VCS functions}, {Version Control Systems}, {Git} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-mercurial.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-mercurial.qdoc index 1380761f79c..624e00a080a 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-mercurial.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-mercurial.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2018 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,16 +8,19 @@ // ********************************************************************** /*! - \previouspage creator-vcs-gitlab.html \page creator-vcs-mercurial.html - \nextpage creator-vcs-perforce.html + \previouspage creator-reference.html - \title Using Mercurial + \ingroup creator-reference-vcs + + \title Mercurial + + \brief Additional Mercurial functions. Mercurial is a free, distributed source control management tool. In addition to the standard version control system functions described in - \l {Using Common Functions}, you can select the following functions in the + \l {Use common VCS Functions}, you can select the following functions in the \uicontrol Tools > \uicontrol Mercurial submenu: \table @@ -45,13 +48,13 @@ \section1 Mercurial Preferences - To set preferences for Mercurial, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol Mercurial: + To set preferences for Mercurial, select \preferences > + \uicontrol {Version Control} > \uicontrol Mercurial: \image qtcreator-preferences-vcs-mercurial.webp {Mercurial preferences} \list - \li \uicontrol Command specifies the path to the command line client + \li \uicontrol Command specifies the path to the command-line client executable. \li \uicontrol {Default username} and \uicontrol {Default email} specify the username and email address to use by default when @@ -61,4 +64,6 @@ \li \uicontrol Timeout sets a timeout for version control operations. \endlist + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc index 1ef25bc7f9a..443aa40be45 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-perforce.qdoc @@ -8,33 +8,28 @@ // ********************************************************************** /*! - \previouspage creator-vcs-mercurial.html \page creator-vcs-perforce.html - \nextpage creator-vcs-subversion.html + \previouspage creator-reference.html - \title Using Perforce + \ingroup creator-reference-vcs + + \title Perforce + + \brief Additional Perforce functions. Perforce is a fast software configuration management system developed by Perforce Software. - \section1 Enabling the Perforce Plugin - - To enable the Perforce plugin: - - \list 1 - \li Select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Version Control} > \uicontrol Perforce. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist + \note Enable the Perforce plugin to use it. \section1 Configuring Perforce - To set Perforce preferences, select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Version Control} > \uicontrol Perforce: + To set Perforce preferences, select \preferences > + \uicontrol {Version Control} > \uicontrol Perforce: \image qtcreator-preferences-vcs-perforce.webp {Perforce preferences} - In \uicontrol {P4 command}, set the path to the command line client + In \uicontrol {P4 command}, set the path to the command-line client executable. Set workspace details in \uicontrol {P4 user}, \uicontrol {P4 client}, and @@ -49,11 +44,11 @@ \section1 Editing Files In addition to the standard version control system functions described in - \l {Using Common Functions}, you can select \uicontrol Tools > + \l {Use common VCS Functions}, you can select \uicontrol Tools > \uicontrol Perforce > \uicontrol {Edit File} to open a file for editing within the client workspace. By default, files are automatically opened for - editing. To disable this feature, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol Perforce, + editing. To disable this feature, select \preferences > + \uicontrol {Version Control} > \uicontrol Perforce, and then deselect the \uicontrol {Automatically open files when editing} check box. @@ -65,4 +60,7 @@ To view information about change lists and the files in them, select \uicontrol Tools > \uicontrol Perforce > \uicontrol Describe. + + \sa {Enable and disable plugins}, {Set up version control systems}, + {Use common VCS functions}, {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-options.qdocinc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-preferences.qdoc similarity index 84% rename from doc/qtcreator/src/vcs/creator-only/creator-vcs-options.qdocinc rename to doc/qtcreator/src/vcs/creator-only/creator-vcs-preferences.qdoc index 98276ab9bba..2bf02fbe4c8 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-options.qdocinc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-preferences.qdoc @@ -2,16 +2,18 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! + \page creator-how-to-set-up-vcs.html + \previouspage creator-how-tos.html -//! [vcs options] + \ingroup creator-how-to-use - \section1 Setting Up Version Control Systems + \title Set up version control systems - \QC uses the version control system's command line clients to access your - repositories. Make sure that the command line clients are in the \c{PATH} - environment variable, or specify the path to the command line client - executable in the version control system specific tab in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control}. + \QC uses the version control system's command-line clients to access your + repositories. Make sure that the command-line clients are in the \c{PATH} + environment variable, or specify the path to the command-line client + executable in the version control system specific tab in \preferences > + \uicontrol {Version Control}. \image qtcreator-preferences-vcs-bazaar.webp {Bazaar preferences} @@ -23,9 +25,9 @@ For more information on using Git for Windows, see \l {Using Git for Windows}. - \section1 Setting Up General Options + \section1 General VCS preferences - Select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Version Control} + Select \preferences > \uicontrol {Version Control} > \uicontrol General to specify settings for submit messages: \image qtcreator-preferences-vcs-general.webp {General tab in Version Control preferences} @@ -72,5 +74,5 @@ from the command line, for example. \endlist -//! [vcs options] + \sa {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs-subversion.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs-subversion.qdoc index 32ebbcff706..b4bbe897aaf 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs-subversion.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs-subversion.qdoc @@ -8,16 +8,19 @@ // ********************************************************************** /*! - \previouspage creator-vcs-perforce.html \page creator-vcs-subversion.html - \nextpage creator-configuring-projects.html + \previouspage creator-reference.html - \title Using Subversion + \ingroup creator-reference-vcs + + \title Subversion + + \brief Additional Subversion functions. Subversion is an open source version control system. In addition to the standard version control system functions described in - \l {Using Common Functions}, you can select \uicontrol Tools > + \l {Use common VCS Functions}, you can select \uicontrol Tools > \uicontrol Subversion > \uicontrol Describe to display commit log messages for a revision. @@ -26,9 +29,8 @@ \section1 Subversion Preferences - To set Subversion preferences, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > - \uicontrol Subversion: + To set Subversion preferences, select \preferences > + \uicontrol {Version Control} > \uicontrol Subversion: \image qtcreator-preferences-vcs-subversion.webp {Subversion preferences} @@ -44,4 +46,7 @@ \li \uicontrol {Ignore whitespace changes in annotation} hides whitespace changes in annotation views. \endlist + + \sa {Set up version control systems}, {Use common VCS functions}, + {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc b/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc index ce4dce91095..d483e54ca9d 100644 --- a/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc +++ b/doc/qtcreator/src/vcs/creator-only/creator-vcs.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,71 +8,79 @@ // ********************************************************************** /*! - \previouspage creator-project-wizards.html \page creator-version-control.html - \nextpage creator-vcs-bazaar.html + \previouspage creator-reference.html - \title Using Version Control Systems + \ingroup creator-reference - Version control systems supported by \QC are: + \title Version Control Systems + + \brief Version control systems that you can use from \QC. + + The recommended way to set up a project is to use a + \e {version control system}. Store and edit only project source files and + configuration files. Do not store generated files. To use version control + systems from \QC, you need to set them up. + + \QC integrates the following version control systems: \table \header \li Version Control System \li Address \li Notes \row - \li \l{Using Bazaar}{Bazaar} + \li \l{Bazaar} \li \l{http://bazaar.canonical.com/} \li \row - \li \l{Using ClearCase}{ClearCase} + \li \l{ClearCase} \li \l{http://www-01.ibm.com/software/awdtools/clearcase/} - \li Disabled by default. To enable the plugin, select - \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Version Control} > \uicontrol ClearCase. - Then select \uicontrol {Restart Now} to restart \QC - and load the plugin. + \li Enable the plugin to use it. \row - \li \l{Using CVS}{CVS} + \li \l{CVS} \li \l{http://www.nongnu.org/cvs/} - \li + \li Enable the plugin to use it. \row - \li \l{Using Fossil}{Fossil} + \li \l{Fossil} \li \l{https://fossil-scm.org/index.html/doc/trunk/www/index.wiki} - \li Disabled by default. To enable the plugin, select - \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Version Control} > \uicontrol Fossil. + \li Enable the plugin to use it. \row - \li \l{Using Git}{Git} + \li \l{Git} \li \l{http://git-scm.com/} \li Git version 1.9.0, or later Gerrit version 2.6, or later \row - \li \l{Using GitLab}{GitLab} + \li \l{GitLab} \li \l{http://gitlab.com/} - \li Experimental + \li Enable the plugin to use it. \row - \li \l{Using Mercurial}{Mercurial} + \li \l{Mercurial} \li \l{http://mercurial.selenic.com/} \li \row - \li \l{Using Perforce}{Perforce} + \li \l{Perforce} \li \l{http://www.perforce.com} \li Server version 2006.1 and later - Disabled by default. To enable the plugin, select - \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Version Control} > \uicontrol Perforce. + Enable the plugin to use it. \row - \li \l{Using Subversion}{Subversion} + \li \l{Subversion} \li \l{http://subversion.apache.org/} \li Subversion version 1.7.0 and later \endtable - \include creator-vcs-options.qdocinc vcs options + \sa {Create VCS repositories for new projects}, {Enable and disable plugins}, + {Set up version control systems}, {Use common VCS functions} +*/ - \section1 Creating VCS Repositories for New Projects +/*! + \page creator-how-to-create-vcs-repositories.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-projects-create + + \title Create VCS repositories for new projects \QC allows you to create repositories for version control systems that support local repository creation, such as Git, Mercurial, or Bazaar. @@ -87,33 +95,33 @@ and select the version control system that you use. Follow the instructions of the wizard to import the project. - \section1 Using Common Functions + \sa {Creating Projects}, {Use project wizards}, {Version Control Systems} +*/ + +/*! + \page creator-how-to-use-common-vcs-functions.html + \previouspage creator-how-tos.html + + \ingroup creator-how-to-use + + \title Use common VCS functions The \uicontrol{Tools} menu has a submenu for each supported version control system. This section describes using the functions that are - available for all the supported version control systems. For more - information about the additional functions and options available for a - particular version control system, see: + available for all the supported version control systems. The additional + functions and options available for a particular version control system + are described in separate topics. - \list - \li \l{Using Bazaar} - \li \l{Using ClearCase} - \li \l{Using CVS} - \li \l{Using Fossil} - \li \l{Using Git} - \li \l{Using GitLab} - \li \l{Using Mercurial} - \li \l{Using Perforce} - \li \l{Using Subversion} - \endlist + \sa {Bazaar}, {ClearCase}, {CVS}, {Fossil}, {Git}, {GitLab}, {Mercurial}, + {Perforce}, {Subversion} - \uicontrol{Version Control} displays the commands that are + The \uicontrol{Version Control} view displays the commands that are executed, a timestamp, and the relevant output. Select \uicontrol View > \uicontrol Output > \uicontrol {Version Control} to open the view. \image qtcreator-vcs-pane.png - \section2 Adding Files + \section1 Add files When you create a new file or a new project, the wizard displays a page asking whether the files should be added to a version control system. @@ -122,7 +130,7 @@ for example, Perforce and Subversion. Alternatively, you can add files later by using the version control tool menus. - \section2 Viewing Diff Output + \section1 View diff output All version control systems have menu options to \e{diff} the current file or project: to compare it with the latest version stored in the @@ -141,13 +149,13 @@ unstage chunks or selected lines, as well as send chunks to a code pasting service. - \section2 Viewing Versioning History and Change Details + \section1 View versioning history and change details Display the versioning history of a file by selecting \uicontrol{Log} or \uicontrol{Filelog}. Typically, the log output has the date, commit message, and a change or revision identifier. - \section2 Annotating Files + \section1 Annotate files To open annotation views, select \uicontrol {Annotate} or \uicontrol {Blame}. They show the lines of the file and the identifier of the change the lines @@ -163,14 +171,14 @@ The same context menu is available when right-clicking on a version identifier in the file log view of a single file. - \section2 Committing Changes + \section1 Commit changes Once you have finished making changes, submit them to the version control system by choosing \uicontrol{Commit} or \uicontrol{Submit}. \QC displays a commit page that has a text editor where you can enter your commit message and a checkable list of modified files to include. - \section2 Reverting Changes + \section1 Revert changes All supported version control systems support reverting your project to known states. This functionality is generally called \e reverting. @@ -180,18 +188,20 @@ A version control system can replace the \uicontrol Revert menu option with other options. - \section2 Viewing Status + \section1 View status You can select \uicontrol{Status} to view the status of the project or repository. - \section2 Updating the Working Tree + \section1 Update the working tree You can select \uicontrol Update to update your working tree with the latest changes from the branch. Some version control systems allow you to choose between updating the current project and updating all projects. - \section2 Deleting Files + \section1 Delete files You can select \uicontrol Delete to delete obsolete files from the repository. + + \sa {Set up version control systems}, {Version Control Systems} */ diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index cc59f24c917..9f5f79f3910 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -12,12 +12,17 @@ \if defined(qtdesignstudio) \previouspage studio-developer-topics.html \nextpage studio-porting-projects.html - \else - \previouspage creator-vcs-fossil.html - \nextpage creator-vcs-gitlab.html - \endif \title Using Git + \else + \previouspage creator-reference.html + + \ingroup creator-reference-vcs + + \title Git + + \brief Additional Git functions. + \endif \l{http://git-scm.com/}{Git} is a fast decentralized version control system. Git is available for Windows, Linux, and \macos. @@ -25,6 +30,10 @@ You can use the \l{http://code.google.com/p/gerrit/}{Gerrit} code review tool for projects that use Git. + \if defined(qtdesignstudio) + \include creator-vcs-options.qdocinc vcs options + \endif + \if defined(qtcreator) \section1 Using Git for Windows @@ -36,9 +45,8 @@ you run Git from a Windows command prompt, it looks for the SSH keys in its installation directory, and therefore, the authorization fails. - You can set the \c HOME environment variable from \QC. Select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Version Control} > - \uicontrol Git, and then select the + You can set the \c HOME environment variable from \QC. Select \preferences > + \uicontrol {Version Control} > \uicontrol Git, and then select the \uicontrol {Set "HOME" environment variable} check box. \c HOME is set to \c %HOMEDRIVE%%HOMEPATH% when the Git executable is run and authorization works as it would with \c {git bash}. @@ -84,9 +92,8 @@ The log output has the date, the commit message, and a commit identifier. - You can set the maximum number of log entries to show in \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol Git > - \uicontrol {Log count}. + You can set the maximum number of log entries to show in \preferences > + \uicontrol {Version Control} > \uicontrol Git > \uicontrol {Log count}. Click on the commit identifier to view commit details. @@ -135,11 +142,15 @@ \image qtcreator-git-blame.webp {Git Blame view} By default, each line is annotated in the editor when you scroll through - the file. To disable this feature, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol Git, and - deselect \uicontrol {Add instant blame annotations to editor}. To view - annotations for the current line, select \uicontrol Tools > \uicontrol Git > - \uicontrol {Current File} > \uicontrol {Instant Blame}. + the file. To disable this feature, select \preferences > + \uicontrol {Version Control} > \uicontrol Git, and + deselect \uicontrol {Instant Blame}. To find the commit that introduced + the last real code change, select \uicontrol {Ignore whitespace changes}. + To find the commit that introduced a line before it was moved, select + \uicontrol {Ignore line moves}. + + To view annotations for the current line, select \uicontrol Tools > + \uicontrol Git > \uicontrol {Current File} > \uicontrol {Instant Blame}. Click the commit identifier to show a detailed description of the change. @@ -481,7 +492,7 @@ To pull changes from the remote repository, select \uicontrol Pull. If there are locally modified files, you are prompted to stash the changes. Select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Version Control} > + \preferences > \uicontrol {Version Control} > \uicontrol Git and then select the \uicontrol {Pull with rebase} check box to perform a rebase operation while pulling. @@ -570,8 +581,8 @@ To refresh the list of changes, select \uicontrol Refresh. The \uicontrol Remote field lists the remotes of the current repository that - are detected as Gerrit servers. Select \uicontrol Edit > \uicontrol Preferences - > \uicontrol {Version Control} > \uicontrol Gerrit to specify a fallback + are detected as Gerrit servers. Select \preferences > + \uicontrol {Version Control} > \uicontrol Gerrit to specify a fallback connection to a Gerrit server over SSH. The Gerrit REST interface and the \l{https://curl.haxx.se/}{curl} tool are used for HTTP connections. @@ -587,7 +598,7 @@ \note On \macos, the default Git installation does not have Git Gui. To use Git Gui, install it separately. To start Git Gui from \QC, select - \uicontrol Preferences > \uicontrol {Version Control} > \uicontrol Git, and + \preferences > \uicontrol {Version Control} > \uicontrol Git, and set the path to the environment that has Git Gui in the \uicontrol {Prepend to PATH} field. @@ -595,12 +606,11 @@ \uicontrol Git > \uicontrol {Git Tools} > \uicontrol Gitk. You can also start the tool to view commits in the current document or in the folder that has the current document. To specify arguments for running Gitk, select - \uicontrol Edit > \uicontrol Preferences > \uicontrol {Version Control} > - \uicontrol Git. + \preferences > \uicontrol {Version Control} > \uicontrol Git. To use some other application for viewing Git history, such as GitX or - QGit viewer, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol {Version Control} > \uicontrol Git and specify the path to the + QGit viewer, select \preferences > \uicontrol {Version Control} > + \uicontrol Git and specify the path to the application executable in the \uicontrol {Command} field. To start the application, select \uicontrol Tools > \uicontrol Git > \uicontrol {Git Tools} > \uicontrol {Repository Browser}. @@ -611,4 +621,8 @@ To resolve merge conflicts, select \uicontrol Tools > \uicontrol Git > \uicontrol {Git Tools} > \uicontrol {Merge Tool}. This menu item is visible only when you have merge conflicts to resolve. + + \if defined(qtcreator) + \sa {Set up version control systems}, {Version Control Systems} + \endif */ diff --git a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc index 64f8cd0e75e..f53830d29f8 100644 --- a/doc/qtcreator/src/webassembly/creator-webassembly.qdoc +++ b/doc/qtcreator/src/webassembly/creator-webassembly.qdoc @@ -21,6 +21,8 @@ You can change the web browser in the project's \l{Specifying Run Settings} {run settings}. + \note Enable the WebAssembly plugin to use it. + To build applications for the web and run them in a web browser, you need to install Qt for WebAssembly and the tool chain for compiling to WebAssembly. @@ -56,30 +58,19 @@ {Install Emscripten}. \endlist - \section2 Enabling the WebAssembly Plugin - - To enable the plugin: - - \list 1 - \li In \QC, select \uicontrol Help > \uicontrol {About Plugins} > - \uicontrol {Device Support} > \uicontrol {WebAssembly}. - \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. - \endlist - \section2 Specifying WebAssembly Settings To configure \QC for building Qt apps for the web: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Devices > - \uicontrol WebAssembly. + \li Select \preferences > \uicontrol Devices > \uicontrol WebAssembly. \li In the \uicontrol {Emscripten SDK path} field, enter the root directory where \c emsdk is installed. \li \QC configures the \uicontrol {Emscripten SDK environment} for you if the \c emsdk is supported by the Qt for WebAssembly version that you will use for developing the application. \image qtcreator-webassembly-options.png "Qt for WebAssembly device preferences" - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits. + \li Select \preferences > \uicontrol Kits. \image qtcreator-kit-webassembly.png "Qt for WebAssembly kit" \li In the \uicontrol Compiler group, \uicontrol {Emscripten Compiler} should have been automatically detected for both C++ and C. If not, @@ -92,8 +83,7 @@ to \QC. To add kits: \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > - \uicontrol Add. + \li Select \preferences > \uicontrol Kits > \uicontrol Add. \li In the \uicontrol Name field, specify a name for the kit. \li In the \uicontrol {Run device type} field, select \uicontrol {WebAssembly Runtime}. @@ -121,4 +111,6 @@ You can now build Qt applications in WebAssembly format and run them in a web browser as described in \l {Building for Multiple Platforms} and \l{Running on Multiple Platforms}. + + \sa {Enable and disable plugins} */ diff --git a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc index d7cdd609455..1bcfb832d7f 100644 --- a/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc +++ b/doc/qtcreator/src/widgets/qtdesigner-app-tutorial.qdoc @@ -44,7 +44,7 @@ \image qtcreator-new-project-build-system-qt-gui.png {Define Build System dialog} - \li In the \uicontrol {Build system} field, select \l {Setting Up CMake} + \li In the \uicontrol {Build system} field, select \l {CMake} {CMake} as the build system to use for building the project. \li Select \uicontrol Next or \uicontrol Continue to open the @@ -67,7 +67,7 @@ \image qtcreator-new-qt-gui-application-translationfile.png {Translation File dialog} \li In the \uicontrol Language field, you can select a language that you - plan to \l {Using Qt Linguist}{translate} the application to. This + plan to \l {Use Qt Linguist}{translate} the application to. This sets up localization support for the application. You can add other languages later by editing the project file. diff --git a/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc b/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc index 390db7f2b89..71e53130bc6 100644 --- a/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc +++ b/doc/qtcreator/src/widgets/qtdesigner-overview.qdoc @@ -60,7 +60,7 @@ \list - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Designer. + \li Select \preferences > \uicontrol Designer. \li Specify embedded device profiles, that determine style, font, and screen resolution, for example, in \uicontrol{Embedded Design}. @@ -91,7 +91,7 @@ \list 1 - \li \uicontrol Edit > \uicontrol Preferences > \uicontrol Designer. + \li \preferences > \uicontrol Designer. \image qtdesigner-embedded-design.png "Qt Designer Embedded Design preferences" \li In \uicontrol {Embedded Design}, select \inlineimage icons/plus.png to open the \uicontrol {Add Profile} dialog. diff --git a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc index f782199c379..ddd3266df60 100644 --- a/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc +++ b/doc/qtcreator/src/widgets/qtdesigner-plugins.qdoc @@ -47,7 +47,8 @@ located in \c {\\bin} in the Qt installation directory. It fetches plugins from the \c {\plugins\designer} subdirectory of \c bin. To check which plugins were loaded successfully and which failed, - choose \uicontrol Help > \uicontrol {About Plugins}. + choose \uicontrol Help > \uicontrol {About Plugins} on Linux and Windows + (or \uicontrol {\QC} > \uicontrol {About Plugins} on \macos). \section1 Matching Build Keys diff --git a/doc/qtcreatordev/examples/exampleplugin/Example.json.in b/doc/qtcreatordev/examples/exampleplugin/Example.json.in index 3a52e318c55..c5faf0a2559 100644 --- a/doc/qtcreatordev/examples/exampleplugin/Example.json.in +++ b/doc/qtcreatordev/examples/exampleplugin/Example.json.in @@ -1,17 +1,17 @@ { //! [1] - \"Name\" : \"Example\", - \"Version\" : \"0.0.1\", - \"CompatVersion\" : \"0.0.1\", + "Name" : "Example", + "Version" : "0.0.1", + "CompatVersion" : "0.0.1", //! [1] //! [2] - \"Vendor\" : \"MyCompany\", - \"Copyright\" : \"(C) MyCompany\", - \"License\" : \"Put short license information here\", - \"Description\" : \"Put a short description of your plugin here\", - \"Url\" : \"https://www.mycompany.com\", + "Vendor" : "MyCompany", + "Copyright" : "(C) MyCompany", + "License" : "Put short license information here", + "Description" : "Put a short description of your plugin here", + "Url" : "https://www.mycompany.com", //! [2] //! [3] - $$dependencyList + ${IDE_PLUGIN_DEPENDENCIES} //! [3] } diff --git a/doc/qtcreatordev/src/external-tool-spec.qdoc b/doc/qtcreatordev/src/external-tool-spec.qdoc index 084f39b6718..0186b742e14 100644 --- a/doc/qtcreatordev/src/external-tool-spec.qdoc +++ b/doc/qtcreatordev/src/external-tool-spec.qdoc @@ -139,7 +139,7 @@ finds. Required. \row \li arguments - \li Command line arguments for the executable. Specify the string in the + \li Command-line arguments for the executable. Specify the string in the same format (with respect to quoting and argument splitting, for example) as you would specify it on the command line of the platform the tool runs on. Optional. diff --git a/doc/qtcreatordev/src/first-plugin.qdoc b/doc/qtcreatordev/src/first-plugin.qdoc index 96b993066c7..0e45e17759f 100644 --- a/doc/qtcreatordev/src/first-plugin.qdoc +++ b/doc/qtcreatordev/src/first-plugin.qdoc @@ -254,7 +254,7 @@ \snippet exampleplugin/Example.json.in 3 - The \c {$$dependencyList} variable is automatically replaced by the + The \c {IDE_PLUGIN_DEPENDENCIES} variable is automatically replaced by the dependency information in \c {QTC_PLUGIN_DEPENDS} and \c {QTC_PLUGIN_RECOMMENDS} from your plugin's \c {.pro} file. diff --git a/doc/qtcreatordev/src/plugin-metadata.qdoc b/doc/qtcreatordev/src/plugin-metadata.qdoc index fc38ac0f60a..4af331c0bae 100644 --- a/doc/qtcreatordev/src/plugin-metadata.qdoc +++ b/doc/qtcreatordev/src/plugin-metadata.qdoc @@ -215,7 +215,7 @@ \section3 Test Dependencies - When the user runs the application with the \c{-test} command line argument, only + When the user runs the application with the \c{-test} command-line argument, only the specified plugins and their dependencies are loaded. This is done in order to speed up the execution of tests by avoiding the loading of unneeded plugins. @@ -225,18 +225,18 @@ This type of dependency is not transitive. - \section2 Command Line Arguments + \section2 Command-Line Arguments - Plugins can register command line arguments that the user can give - when starting the application. These command line arguments are shown + Plugins can register command-line arguments that the user can give + when starting the application. These command-line arguments are shown with a one-line description when the user runs the application with - the \c{-help} command line argument, and the plugin manager does its command + the \c{-help} command-line argument, and the plugin manager does its command line parsing and sanity checks based on that information. - If the plugin manager finds matching command line arguments for a plugin, + If the plugin manager finds matching command-line arguments for a plugin, it passes them on to the plugin's \l{ExtensionSystem::IPlugin::initialize()}{initialize()} function. - Command line arguments are defined through the key \c Arguments, which contains an array + Command-line arguments are defined through the key \c Arguments, which contains an array of argument objects. Each individual argument object has the required key \c Name, and optional keys \c Parameter and \c Description. @@ -248,7 +248,7 @@ \row \li Arguments \li Array of argument objects - \li Describes the command line arguments that the plugin wants to handle. + \li Describes the command-line arguments that the plugin wants to handle. \endtable An argument object is a JSON object with the following keys: @@ -260,19 +260,19 @@ \row \li Name \li String - \li The command line argument itself, including the \c - prefix, e.g. + \li The command-line argument itself, including the \c - prefix, e.g. \c{-my-parameter}. \row \li Parameter \li String - \li Optional. If this is given, the command line argument expects an + \li Optional. If this is given, the command-line argument expects an additional parameter, e.g. \c{-my-parameter somevalue}. The value of this attribute is used as a very short description of the parameter for the user. \row \li Description \li String - \li Optional. A (one-line) description of the argument for the command line argument help. + \li Optional. A (one-line) description of the argument for the command-line argument help. \endtable \section2 Example \c Test.json diff --git a/doc/qtcreatordev/src/plugin-tests.qdoc b/doc/qtcreatordev/src/plugin-tests.qdoc index d3a4dd35a56..e59f6a497cd 100644 --- a/doc/qtcreatordev/src/plugin-tests.qdoc +++ b/doc/qtcreatordev/src/plugin-tests.qdoc @@ -31,7 +31,7 @@ test. Plugin tests are executed by starting \QC with the \c{-test } - command line argument. \QC then fully loads your plugin and all the plugins + command-line argument. \QC then fully loads your plugin and all the plugins that it depends on, going through the normal \l{Plugin Life Cycle}. After your plugin and all dependencies are fully initialized, your tests are executed. Afterwards, \QC automatically closes. Therefore, your plugin diff --git a/doc/qtcreatordev/src/pluginmanager.qdoc b/doc/qtcreatordev/src/pluginmanager.qdoc index ceaf84d8640..1d9047ffa04 100644 --- a/doc/qtcreatordev/src/pluginmanager.qdoc +++ b/doc/qtcreatordev/src/pluginmanager.qdoc @@ -16,7 +16,7 @@ The plugin manager handles all the details regarding finding plugins, reading their description files, resolving plugin dependencies, loading and initializing all plugins - in the right order, and passing on command line arguments. + in the right order, and passing on command-line arguments. In addition, the plugin manager manages an \e{object pool}, where objects can be registered and retrieved depending on different criteria. diff --git a/doc/qtcreatordev/src/qtcreator-dev.qdoc b/doc/qtcreatordev/src/qtcreator-dev.qdoc index 554d5dbb19a..f6f4c68ceec 100644 --- a/doc/qtcreatordev/src/qtcreator-dev.qdoc +++ b/doc/qtcreatordev/src/qtcreator-dev.qdoc @@ -156,7 +156,7 @@ \list \li \l{https://doc.qt.io/qtcreator/creator-editor-external.html} - {Using External Tools} + {Use external tools} \li \l{External Tool Specification Files} \endlist @@ -180,7 +180,7 @@ configure the tool in \QC, you can add an options page for it. \list - \li \l{https://doc.qt.io/qtcreator/creator-editor-external.html}{Using External Tools} + \li \l{https://doc.qt.io/qtcreator/creator-editor-external.html}{Use external tools} \li \l{External Tool Specification Files} \li \l{Creating Plugins} \li \l{Qt Creator Coding Rules} @@ -206,7 +206,7 @@ \list \li \l{https://doc.qt.io/qtcreator/creator-task-lists.html} - {Showing Task List Files in Issues} + {Show task list files in Issues} \li \l{Creating Plugins} \li \l{Qt Creator Coding Rules} \omit diff --git a/doc/qtcreatordev/src/qtcreator-documentation.qdoc b/doc/qtcreatordev/src/qtcreator-documentation.qdoc index 58ccbb3c88e..8b9e3bbacca 100644 --- a/doc/qtcreatordev/src/qtcreator-documentation.qdoc +++ b/doc/qtcreatordev/src/qtcreator-documentation.qdoc @@ -622,7 +622,7 @@ the \QC \uicontrol Help mode. For more information about adding the help files to \QC, see \l{http://doc.qt.io/qtcreator/creator-help.html#adding-external-documentation} - {Adding External Documentation}. + {Add external documentation}. \section2 Additional Build Commands diff --git a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc index fbc362f6c8c..7c11a4a7800 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-advanced.qdoc @@ -23,7 +23,7 @@ Some of the wizard templates create projects that contain UI files. You should always edit UI files in the \l {2D} and \l Properties view, to avoid breaking the code. - \li \l{Managing Data Collection} + \li \l{Manage Data Collection} You can enable \QDS to report crashes automatically. If you enable the telemetry plugin, you can turn on the pseudonymous user diff --git a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc index da1799439a4..7a96a9a41e4 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-developer-topics.qdoc @@ -29,7 +29,7 @@ application development in Qt Creator, you have to convert them to Qt Quick Application projects that contain .pro, .cpp, and .qrc files. - \li \l{Using External Tools} + \li \l{Use external tools} You can use external tools directly from \QDS. Qt Linguist, QML utilities, the default text editor for your system, and the diff --git a/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc index c7815bd349a..f7dec4de039 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-faq.qdoc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \previouspage creator-help.html + \previouspage creator-how-to-get-help.html \page studio-faq.html \nextpage studio-platforms.html @@ -11,7 +11,7 @@ This section contains answers to some frequently asked questions about \QDS grouped by categories. You might also find answers to your questions in the product documentation by searching or browsing the index in the - \l{Using the Help Mode}{Help mode}. Many questions are also answered by the + \l{Get help}{Help mode}. Many questions are also answered by the \l{Examples}{examples} and \l{Tutorials}{video tutorials}. \list diff --git a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc index cacd95b80e0..be9e6b4ed55 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-help-overview.qdoc @@ -4,7 +4,7 @@ /*! \page studio-help.html \previouspage studio-connecting-mcus-with-creator.html - \nextpage creator-help.html + \nextpage creator-how-to-get-help.html \title Help @@ -17,7 +17,7 @@ \list - \li \l{Using the Help Mode} + \li \l{Get help} \QDS comes fully integrated with documentation. You can use search and index functions to find particular topics in the helps, or diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 48512d5eade..20b6df7c41c 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -204,7 +204,9 @@ \li \l{Semantic Highlighting} \li \l{Checking Code Syntax} \li \l{Completing Code} - \li \l{Indenting Text or Code} + \li \l{Indent text or code} + \li \l{Behavior} + \li \l{Qt Quick Code Style} \li \l{Using Qt Quick Toolbars} \li \l{Comparing Files} \endlist @@ -239,9 +241,9 @@ \li Extending Component Functionality \endomit \li \l{UI Files} - \li \l{Managing Data Collection} + \li \l{Manage Data Collection} \list - \li \l {Collecting Usage Statistics} + \li \l {Collect Usage Statistics} \li \l {Collecting User Feedback} \li \l {Reporting Crashes} \endlist @@ -252,7 +254,7 @@ \li \l{Using Git} \li \l{Converting Qt 5 Projects into Qt 6 Projects} \li \l{Converting UI Projects to Applications} - \li \l{Using External Tools} + \li \l{Use external tools} \li \l{\QDS on MCUs} \list \li \l {\QMCU Framework} @@ -266,7 +268,17 @@ \endlist \li \l Help \list - \li \l{Using the Help Mode} + \li \l{Get help} + \list + \li \l {Add bookmarks to help pages} + \li \l {Add external documentation} + \li \l {Detach the help window} + \li \l {Filter documentation} + \li \l {Find information in Qt documentation} + \li \l {Get help} + \li \l {Search from documentation} + \li \l {Select the help start page} + \endlist \li \l{Frequently Asked Questions} \li \l{Supported Platforms} \endlist diff --git a/doc/qtdesignstudio/src/qtdesignstudio.qdoc b/doc/qtdesignstudio/src/qtdesignstudio.qdoc index 94ad24b6cc5..600bc9427a2 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio.qdoc @@ -72,7 +72,7 @@ \li \b {\l{Advanced Designer Topics}} \list \li \l{UI Files} - \li \l {Managing Data Collection} + \li \l {Manage Data Collection} \li \l{Packaging Applications} \endlist \li \b {\l{Developer Topics}} @@ -80,12 +80,12 @@ \li \l{Using Git} \li \l{Converting Qt 5 Projects into Qt 6 Projects} \li \l{Converting UI Projects to Applications} - \li \l{Using External Tools} + \li \l{Use external tools}{Using External Tools} \li \l{\QDS on MCUs} \endlist \li \b {\l Help} \list - \li \l{Using the Help Mode} + \li \l{Get help}{Getting Help} \li \l{Frequently Asked Questions} \li \l{Supported Platforms} \endlist diff --git a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc index 3b07e787ecc..948acd09c55 100644 --- a/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-text-editor.qdoc @@ -27,7 +27,7 @@ \li \l{Semantic Highlighting} \li \l{Checking Code Syntax} \li \l{Completing Code} - \li \l{Indenting Text or Code} + \li \l{Indent text or code} \li \l{Using Qt Quick Toolbars} \endlist */ diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 7b9b46538c1..771d16566c0 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -23,6 +23,7 @@ set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) set(CPACK_DEBIAN_COMPRESSION_TYPE lzma) set(CPACK_DEBIAN_PACKAGE_RELEASE 1) set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Qt Project ") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libdouble-conversion3,libxcb-cursor0") # Make CMAKE_INSTALL_DEFAULT_COMPONENT_NAME the first component to install get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) diff --git a/qbs/imports/QtcAutotest.qbs b/qbs/imports/QtcAutotest.qbs index 91427f75115..3d1f3a50ec6 100644 --- a/qbs/imports/QtcAutotest.qbs +++ b/qbs/imports/QtcAutotest.qbs @@ -2,21 +2,21 @@ import qbs import qbs.FileInfo QtcProduct { - type: ["application", "autotest"] - - Depends { name: "autotest" } - Depends { name: "Qt.testlib" } - Depends { name: "copyable_resource" } - targetName: "tst_" + name.split(' ').join("") + condition: qtc.withAutotests // This needs to be absolute, because it is passed to one of the source files. destinationDirectory: project.buildDirectory + '/' + FileInfo.relativePath(project.ide_source_tree, sourceDirectory) - cpp.rpaths: [ - project.buildDirectory + '/' + qtc.ide_library_path, - project.buildDirectory + '/' + qtc.ide_plugin_path - ] + targetName: "tst_" + name.split(' ').join("") + type: ["application", "autotest"] + + installTags: [] + + Depends { name: "autotest" } + Depends { name: "Qt.testlib" } + Depends { name: "copyable_resource" } + cpp.defines: { var defines = base.filter(function(d) { return d !== "QT_RESTRICTED_CAST_FROM_ASCII"; }); var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, @@ -25,11 +25,10 @@ QtcProduct { defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"'); return defines; } - - Group { - fileTagsFilter: product.type - qbs.install: false - } + cpp.rpaths: [ + project.buildDirectory + '/' + qtc.ide_library_path, + project.buildDirectory + '/' + qtc.ide_plugin_path + ] // The following would be conceptually right, but does not work currently as some autotests // (e.g. extensionsystem) do not work when installed, because they want hardcoded diff --git a/qbs/imports/QtcCommercialPlugin.qbs b/qbs/imports/QtcCommercialPlugin.qbs index 712ad9f1574..f26af46f2b5 100644 --- a/qbs/imports/QtcCommercialPlugin.qbs +++ b/qbs/imports/QtcCommercialPlugin.qbs @@ -1,5 +1,3 @@ -import qbs - QtcPlugin { Depends { name: "LicenseChecker"; required: false } cpp.defines: base.concat(LicenseChecker.present ? ["LICENSECHECKER"] : []) diff --git a/qbs/imports/QtcDocumentation.qbs b/qbs/imports/QtcDocumentation.qbs index c1b10310d26..3017114c8c4 100644 --- a/qbs/imports/QtcDocumentation.qbs +++ b/qbs/imports/QtcDocumentation.qbs @@ -1,38 +1,38 @@ -import qbs import qbs.FileInfo Product { builtByDefault: false type: [isOnlineDoc ? "qdoc-output" : "qch"] + Depends { name: "Qt.core" } Depends { name: "qtc" } - property path mainDocConfFile property bool isOnlineDoc + property path mainDocConfFile + property string versionTag: qtc.qtcreator_version.replace(/\.|-/g, "") + + Qt.core.qdocEnvironment: [ + "IDE_DISPLAY_NAME=" + qtc.ide_display_name, + "IDE_CASED_ID=" + qtc.ide_cased_id, + "IDE_ID=" + qtc.ide_id, + "QTCREATOR_COPYRIGHT_YEAR=" + qtc.qtcreator_copyright_year, + "QTC_VERSION=" + qtc.qtcreator_version, + "QTC_VERSION_TAG=" + qtc.qtcreator_version, + "QT_INSTALL_DOCS=" + Qt.core.docPath, + "QDOC_INDEX_DIR=" + Qt.core.docPath, + "SRCDIR=" + sourceDirectory, + "VERSION_TAG=" + versionTag, + ] Group { name: "main qdocconf file" prefix: product.sourceDirectory + '/' - files: [mainDocConfFile] - fileTags: ["qdocconf-main"] + files: mainDocConfFile + fileTags: "qdocconf-main" } - property string versionTag: qtc.qtcreator_version.replace(/\.|-/g, "") - Qt.core.qdocEnvironment: [ - "IDE_DISPLAY_NAME=" + qtc.ide_display_name, - "IDE_ID=" + qtc.ide_id, - "IDE_CASED_ID=" + qtc.ide_cased_id, - "QTCREATOR_COPYRIGHT_YEAR=" + qtc.qtcreator_copyright_year, - "QTC_VERSION=" + qtc.qtcreator_version, - "QTC_VERSION_TAG=" + qtc.qtcreator_version, - "SRCDIR=" + sourceDirectory, - "QT_INSTALL_DOCS=" + Qt.core.docPath, - "QDOC_INDEX_DIR=" + Qt.core.docPath, - "VERSION_TAG=" + versionTag - ] - Group { - fileTagsFilter: ["qch"] + fileTagsFilter: "qch" qbs.install: !qbs.targetOS.contains("macos") qbs.installDir: qtc.ide_doc_path } diff --git a/qbs/imports/QtcLibrary.qbs b/qbs/imports/QtcLibrary.qbs index 887a79dbb5d..a0b8b7c8176 100644 --- a/qbs/imports/QtcLibrary.qbs +++ b/qbs/imports/QtcLibrary.qbs @@ -1,19 +1,14 @@ -import qbs 1.0 import qbs.FileInfo import QtcFunctions QtcProduct { type: ["dynamiclibrary", "dynamiclibrary_symlink"] - installDir: qtc.ide_library_path - installTags: ["dynamiclibrary", "dynamiclibrary_symlink", "debuginfo_dll"] - useNonGuiPchFile: true - Depends { - condition: qtc.testsEnabled - name: "Qt.testlib" - } - - targetName: QtcFunctions.qtLibraryName(qbs, name) destinationDirectory: FileInfo.joinPaths(project.buildDirectory, qtc.ide_library_path) + targetName: QtcFunctions.qtLibraryName(qbs, name) + + installDir: qtc.ide_library_path + installTags: type.concat("debuginfo_dll") + useNonGuiPchFile: true cpp.linkerFlags: { var flags = base; @@ -23,17 +18,13 @@ QtcProduct { flags.push("-compatibility_version", qtc.qtcreator_compat_version); return flags; } - cpp.sonamePrefix: qbs.targetOS.contains("macos") - ? "@rpath" - : undefined + cpp.sonamePrefix: qbs.targetOS.contains("macos") ? "@rpath" : undefined cpp.rpaths: qbs.targetOS.contains("macos") ? ["@loader_path/../Frameworks"] : ["$ORIGIN", "$ORIGIN/.."] - property string libIncludeBase: ".." // #include - cpp.includePaths: [libIncludeBase] Export { Depends { name: "cpp" } - cpp.includePaths: [exportingProduct.libIncludeBase] + cpp.includePaths: project.ide_source_tree + "/src/libs" } } diff --git a/qbs/imports/QtcManualtest.qbs b/qbs/imports/QtcManualTest.qbs similarity index 91% rename from qbs/imports/QtcManualtest.qbs rename to qbs/imports/QtcManualTest.qbs index cfa1abc1d8f..7a388b81366 100644 --- a/qbs/imports/QtcManualtest.qbs +++ b/qbs/imports/QtcManualTest.qbs @@ -1,23 +1,23 @@ -import qbs import qbs.FileInfo QtcProduct { - type: ["application"] + condition: qtc.withAutotests + destinationDirectory: project.buildDirectory + '/' + + FileInfo.relativePath(project.ide_source_tree, sourceDirectory) + targetName: "tst_" + name.split(' ').join("") + type: "application" + + install: false Depends { name: "Qt.testlib" } Depends { name: "copyable_resource" } - targetName: "tst_" + name.split(' ').join("") - cpp.rpaths: [ - project.buildDirectory + '/' + qtc.ide_library_path, - project.buildDirectory + '/' + qtc.ide_plugin_path - ] cpp.defines: { var defines = base.filter(function(d) { return d !== "QT_RESTRICTED_CAST_FROM_ASCII"; }); return defines; } - - destinationDirectory: project.buildDirectory + '/' - + FileInfo.relativePath(project.ide_source_tree, sourceDirectory) - install: false + cpp.rpaths: [ + project.buildDirectory + '/' + qtc.ide_library_path, + project.buildDirectory + '/' + qtc.ide_plugin_path + ] } diff --git a/qbs/imports/QtcPlugin.qbs b/qbs/imports/QtcPlugin.qbs index 9e5a743e12a..d06f99385e4 100644 --- a/qbs/imports/QtcPlugin.qbs +++ b/qbs/imports/QtcPlugin.qbs @@ -1,39 +1,25 @@ -import qbs 1.0 import qbs.FileInfo import QtcFunctions QtcProduct { + destinationDirectory: FileInfo.joinPaths(project.buildDirectory, qtc.ide_plugin_path) + name: project.name + targetName: QtcFunctions.qtLibraryName(qbs, name) type: ["dynamiclibrary", "pluginSpec"] + installDir: qtc.ide_plugin_path installTags: ["dynamiclibrary", "debuginfo_dll"] useGuiPchFile: true - property var pluginJsonReplacements - property var pluginRecommends: [] - property var pluginTestDepends: [] - - targetName: QtcFunctions.qtLibraryName(qbs, name) - destinationDirectory: FileInfo.joinPaths(project.buildDirectory, qtc.ide_plugin_path) + property stringList pluginRecommends: [] + property stringList pluginTestDepends: [] + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } Depends { name: "ExtensionSystem" } Depends { name: "pluginjson" } - pluginjson.useVcsData: false - Depends { - condition: qtc.testsEnabled - name: "Qt.testlib" - } - Properties { - condition: qbs.targetOS.contains("unix") - cpp.internalVersion: "" - } cpp.defines: base.concat([name.toUpperCase() + "_LIBRARY"]) - cpp.sonamePrefix: qbs.targetOS.contains("macos") - ? "@rpath" - : undefined - cpp.rpaths: qbs.targetOS.contains("macos") - ? ["@loader_path/../Frameworks", "@loader_path/../PlugIns"] - : ["$ORIGIN", "$ORIGIN/.."] + Properties { cpp.internalVersion: ""; condition: qbs.targetOS.contains("unix") } cpp.linkerFlags: { var flags = base; if (qbs.buildVariant == "debug" && qbs.toolchain.contains("msvc")) @@ -42,20 +28,24 @@ QtcProduct { flags.push("-compatibility_version", qtc.qtcreator_compat_version); return flags; } - - property string pluginIncludeBase: ".." // #include - cpp.includePaths: [pluginIncludeBase] + cpp.rpaths: qbs.targetOS.contains("macos") + ? ["@loader_path/../Frameworks", "@loader_path/../PlugIns"] + : ["$ORIGIN", "$ORIGIN/.."] + cpp.sonamePrefix: qbs.targetOS.contains("macos") + ? "@rpath" + : undefined + pluginjson.useVcsData: false Group { name: "PluginMetaData" - prefix: product.sourceDirectory + '/' - files: [ product.name + ".json.in" ] - fileTags: ["pluginJsonIn"] + prefix: sourceDirectory + '/' + files: product.name + ".json.in" + fileTags: "pluginJsonIn" } Export { - Depends { name: "ExtensionSystem" } Depends { name: "cpp" } - cpp.includePaths: [exportingProduct.pluginIncludeBase] + Depends { name: "ExtensionSystem" } + cpp.includePaths: ".." } } diff --git a/qbs/imports/QtcProduct.qbs b/qbs/imports/QtcProduct.qbs index e5f341704df..adf7314d0b2 100644 --- a/qbs/imports/QtcProduct.qbs +++ b/qbs/imports/QtcProduct.qbs @@ -1,16 +1,13 @@ -import qbs 1.0 import qbs.FileInfo import qbs.Utilities -import QtcFunctions Product { - name: project.name version: qtc.qtcreator_version + property bool install: true property string installDir property string installSourceBase: destinationDirectory property stringList installTags: type - property string fileName: FileInfo.fileName(sourceDirectory) + ".qbs" property bool useNonGuiPchFile: false property bool useGuiPchFile: false property bool useQt: true @@ -20,15 +17,6 @@ Product { property bool sanitizable: true Depends { name: "cpp" } - Depends { name: "qtc" } - Depends { - name: product.name + " dev headers"; - required: false - Properties { - condition: Utilities.versionCompare(qbs.version, "1.13") >= 0 - enableFallback: false - } - } Depends { name: "Qt" condition: useQt @@ -36,10 +24,7 @@ Product { versionAtLeast: "6.2.0" } - // TODO: Should fall back to what came from Qt.core for Qt < 5.7, but we cannot express that - // atm. Conditionally pulling in a module that sets the property is also not possible, - // because conflicting scalar values would be reported (QBS-1225 would fix that). - cpp.minimumMacosVersion: project.minimumMacosVersion + Depends { name: "qtc" } cpp.cxxFlags: { var flags = []; @@ -62,7 +47,8 @@ Product { } return flags; } - + cpp.cxxLanguageVersion: "c++17" + cpp.defines: qtc.generalDefines Properties { condition: sanitizable && qbs.toolchain.contains("gcc") cpp.driverFlags: { @@ -76,10 +62,6 @@ Product { return flags; } } - - cpp.cxxLanguageVersion: "c++17" - cpp.defines: qtc.generalDefines - cpp.minimumWindowsVersion: "6.1" cpp.useCxxPrecompiledHeader: useQt && (useNonGuiPchFile || useGuiPchFile) cpp.visibility: "minimal" @@ -94,15 +76,15 @@ Product { name: "standard pch file (non-gui)" condition: useNonGuiPchFile prefix: pathToSharedSources + '/' - files: ["qtcreator_pch.h"] - fileTags: ["cpp_pch_src"] + files: "qtcreator_pch.h" + fileTags: "cpp_pch_src" } Group { name: "standard pch file (gui)" condition: useGuiPchFile prefix: pathToSharedSources + '/' - files: ["qtcreator_gui_pch.h"] - fileTags: ["cpp_pch_src"] + files: "qtcreator_gui_pch.h" + fileTags: "cpp_pch_src" } } diff --git a/qbs/imports/QtcTestApp.qbs b/qbs/imports/QtcTestApp.qbs index 8411ea9b9e4..bca15924c7b 100644 --- a/qbs/imports/QtcTestApp.qbs +++ b/qbs/imports/QtcTestApp.qbs @@ -1,4 +1,3 @@ -import qbs import qbs.FileInfo CppApplication { diff --git a/qbs/imports/QtcTestFiles.qbs b/qbs/imports/QtcTestFiles.qbs index ab27a8df8a1..0dbf377e1f9 100644 --- a/qbs/imports/QtcTestFiles.qbs +++ b/qbs/imports/QtcTestFiles.qbs @@ -1,6 +1,4 @@ -import qbs 1.0 - Group { name: "Unit tests" - condition: qtc.testsEnabled + condition: qtc.withPluginTests } diff --git a/qbs/imports/QtcTool.qbs b/qbs/imports/QtcTool.qbs index 1b389686502..e93829cc630 100644 --- a/qbs/imports/QtcTool.qbs +++ b/qbs/imports/QtcTool.qbs @@ -1,11 +1,11 @@ -import qbs import qbs.FileInfo QtcProduct { - type: ["application"] consoleApplication: true + type: "application" + installDir: qtc.ide_libexec_path - installTags: base.concat(["debuginfo_app"]) + installTags: type.concat("debuginfo_app") useNonGuiPchFile: true cpp.rpaths: { diff --git a/qbs/modules/pluginjson/pluginjson.qbs b/qbs/modules/pluginjson/pluginjson.qbs index e210731f0bf..43d5e7f0012 100644 --- a/qbs/modules/pluginjson/pluginjson.qbs +++ b/qbs/modules/pluginjson/pluginjson.qbs @@ -8,9 +8,10 @@ Module { Depends { id: qtcore; name: "Qt.core" } Depends { name: "qtc" } + property var replacements + // TODO: Wrap the VCS specific stuff in a dedicated module - property bool hasVcs: Utilities.versionCompare(qbs.version, "1.10") >= 0 - property bool useVcsData: hasVcs + property bool useVcsData: true Depends { name: "vcs"; condition: useVcsData } Properties { condition: useVcsData @@ -52,7 +53,7 @@ Module { var cmd = new JavaScriptCommand(); cmd.description = "prepare " + FileInfo.fileName(output.filePath); cmd.highlight = "codegen"; - cmd.pluginJsonReplacements = product.pluginJsonReplacements; + cmd.pluginJsonReplacements = product.pluginjson.replacements; cmd.plugin_depends = []; var deps = product.dependencies; for (var d in deps) { @@ -72,17 +73,15 @@ Module { var vars = pluginJsonReplacements || {}; var inf = new TextFile(input.filePath); var all = inf.readAll(); - // replace quoted quotes - all = all.replace(/\\\"/g, '"'); // replace config vars var qtcVersion = product.moduleProperty("qtc", "qtcreator_version"); - vars['QTCREATOR_VERSION'] = qtcVersion; - vars['QTCREATOR_COMPAT_VERSION'] + vars['IDE_VERSION'] = qtcVersion; + vars['IDE_VERSION_COMPAT'] = product.moduleProperty("qtc", "qtcreator_compat_version"); vars['IDE_VERSION_MAJOR'] = product.moduleProperty("qtc", "ide_version_major"); vars['IDE_VERSION_MINOR'] = product.moduleProperty("qtc", "ide_version_minor"); vars['IDE_VERSION_RELEASE'] = product.moduleProperty("qtc", "ide_version_release"); - vars['QTCREATOR_COPYRIGHT_YEAR'] + vars['IDE_COPYRIGHT_YEAR'] = product.moduleProperty("qtc", "qtcreator_copyright_year") if (!vars['QTC_PLUGIN_REVISION']) vars['QTC_PLUGIN_REVISION'] = product.vcs ? (product.vcs.repoState || "") : ""; @@ -97,9 +96,9 @@ Module { deplist.push(" { \"Name\" : \"" + plugin_test_depends[i] + "\", \"Version\" : \"" + qtcVersion + "\", \"Type\" : \"test\" }"); } deplist = deplist.join(",\n") - vars['dependencyList'] = "\"Dependencies\" : [\n" + deplist + "\n ]"; + vars['IDE_PLUGIN_DEPENDENCIES'] = "\"Dependencies\" : [\n" + deplist + "\n ]"; for (i in vars) { - all = all.replace(new RegExp('\\\$\\\$' + i + '(?!\w)', 'g'), vars[i]); + all = all.replace(new RegExp('\\\$\\{' + i + '(?!\w)\\}', 'g'), vars[i]); } var file = new TextFile(output.filePath, TextFile.WriteOnly); file.truncate(); diff --git a/qbs/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs/modules/qbsbuildconfig/qbsbuildconfig.qbs index eca4f33508e..1ee8cef7a16 100644 --- a/qbs/modules/qbsbuildconfig/qbsbuildconfig.qbs +++ b/qbs/modules/qbsbuildconfig/qbsbuildconfig.qbs @@ -34,7 +34,10 @@ Module { ? ["@loader_path/" + FileInfo.relativePath('/' + appInstallDir, '/' + libInstallDir)] : ["$ORIGIN/..", "$ORIGIN/../" + qtc.ide_library_path] property string resourcesInstallDir: qtc.ide_data_path + "/qbs" - property string pluginsInstallDir: qtc.ide_plugin_path + "/qbs/plugins" + property string pluginsInstallBaseDir: qbs.targetOS.contains("darwin") + ? qtc.ide_plugin_path + "/.." + : qtc.ide_library_path + "/.." + property string pluginsInstallDir: pluginsInstallBaseDir + "/qbs/plugins" property string qmlTypeDescriptionsInstallDir: qtc.ide_data_path + "/qml-type-descriptions" property string appInstallDir: qtc.ide_bin_path property string libexecInstallDir: qtc.ide_libexec_path @@ -44,7 +47,7 @@ Module { property string relativeLibexecPath: FileInfo.relativePath('/' + appInstallDir, '/' + libexecInstallDir) property string relativePluginsPath: FileInfo.relativePath('/' + appInstallDir, - '/' + qtc.ide_plugin_path) + '/' + pluginsInstallBaseDir) property string relativeSearchPath: FileInfo.relativePath('/' + appInstallDir, '/' + resourcesInstallDir) } diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 8ae16817d17..b8e5aac0e35 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -4,18 +4,16 @@ import qbs.FileInfo import qbs.Utilities Module { - Depends { name: "cpp"; required: false } - - property string qtcreator_display_version: '11.0.3' + property string qtcreator_display_version: '12.0.0-rc1' property string ide_version_major: '11' property string ide_version_minor: '0' - property string ide_version_release: '3' + property string ide_version_release: '84' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '11' property string ide_compat_version_minor: '0' - property string ide_compat_version_release: '0' + property string ide_compat_version_release: '84' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release @@ -26,6 +24,7 @@ Module { property string ide_id: 'qtcreator' property string ide_cased_id: 'QtCreator' property string ide_bundle_identifier: 'org.qt-project.qtcreator' + property string ide_user_file_extension: '.user' property string libDirName: "lib" property string ide_app_path: qbs.targetOS.contains("macos") ? "" : "bin" @@ -40,7 +39,7 @@ Module { } property string ide_plugin_path: { if (qbs.targetOS.contains("macos")) - return ide_app_target + ".app/Contents/PlugIns" + return ide_app_target + ".app/Contents/PlugIns/qtcreator" else if (qbs.targetOS.contains("windows")) return libDirName + "/qtcreator/plugins" else @@ -73,12 +72,10 @@ Module { property bool preferSystemSyntaxHighlighting: true - property bool make_dev_package: false + property bool withPluginTests: Environment.getEnv("TEST") || qbs.buildVariant === "debug" + property bool testsEnabled: withPluginTests // TODO: compat, remove + property bool withAutotests: project.withAutotests // FIXME: withPluginTests - // Will be replaced when creating modules from products - property string export_data_base: project.ide_source_tree + "/share/qtcreator" - - property bool testsEnabled: Environment.getEnv("TEST") || qbs.buildVariant === "debug" property stringList generalDefines: [ "QT_CREATOR", 'IDE_LIBRARY_BASENAME="' + libDirName + '"', @@ -91,18 +88,9 @@ Module { 'RELATIVE_DOC_PATH="' + FileInfo.relativePath('/' + ide_bin_path, '/' + ide_doc_path) + '"', "QT_NO_CAST_TO_ASCII", "QT_RESTRICTED_CAST_FROM_ASCII", + "QT_NO_FOREACH", "QT_DISABLE_DEPRECATED_BEFORE=0x050900", "QT_USE_QSTRINGBUILDER", - ].concat(testsEnabled ? ["WITH_TESTS"] : []) + ].concat(withPluginTests ? ["WITH_TESTS"] : []) .concat(qbs.toolchain.contains("msvc") ? ["_CRT_SECURE_NO_WARNINGS"] : []) - .concat((qbs.toolchain.contains("msvc") && Utilities.versionCompare(qbs.version, "1.23.2") < 0) - ? ["_ENABLE_EXTENDED_ALIGNED_STORAGE"] : []) - - Properties { - condition: cpp.present && qbs.toolchain.contains("msvc") && product.Qt - && Utilities.versionCompare(product.Qt.core.version, "6.3") >= 0 - && Utilities.versionCompare(cpp.compilerVersion, "19.10") >= 0 - && Utilities.versionCompare(qbs.version, "1.23") < 0 - cpp.cxxFlags: "/permissive-" - } } diff --git a/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs b/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs index 7b1d663cf0e..4a8299ba516 100644 --- a/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs +++ b/qbs/modules/qtc_gtest_gmock/qtc_gtest_gmock.qbs @@ -19,7 +19,7 @@ Module { property bool hasRepo configure: { - repoDir = FileInfo.cleanPath(path + "/../../../tests/unit/3rdparty/googletest"); + repoDir = FileInfo.cleanPath(path + "/../../../src/libs/3rdparty/googletest"); gtestDir = FileInfo.joinPaths(repoDir, "googletest"); gmockDir = FileInfo.joinPaths(repoDir, "googlemock"); hasRepo = File.exists(gtestDir); diff --git a/qtcreator.qbs b/qtcreator.qbs index c0779f4af10..21f610de563 100644 --- a/qtcreator.qbs +++ b/qtcreator.qbs @@ -1,12 +1,7 @@ -import qbs 1.0 -import qbs.Environment -import qbs.FileInfo - Project { name: "Qt Creator" - minimumQbsVersion: "1.19.0" - property string minimumMacosVersion: "10.15" - property bool withAutotests: qbs.buildVariant === "debug" + minimumQbsVersion: "2.0.0" + property bool withAutotests: qbs.buildVariant === "debug" // TODO: compat, remove property path ide_source_tree: path property pathList additionalPlugins: [] property pathList additionalLibs: [] @@ -23,31 +18,6 @@ Project { "tests/tests.qbs" ] - Product { - name: "qbs_imports_modules" - Depends { name: "qtc" } - Group { - prefix: "qbs/" - files: ["**/*"] - qbs.install: qtc.make_dev_package - qbs.installDir: qtc.ide_qbs_resources_path - qbs.installSourceBase: "qbs" - } - } - - Product { - name: "qmake project files" - files: { - var list = ["**/*.pr[io]"]; - var props = [additionalPlugins, additionalLibs, additionalTools, additionalAutotests]; - for (var i = 0; i < props.length; ++i) { - for (var j = 0; j < props[i].length; ++j) - list.push(props[i][j] + "/**/*.pr[io]"); - } - return list; - } - } - Product { name: "cmake project files" files: { @@ -63,32 +33,4 @@ Project { return list; } } - - AutotestRunner { - Depends { name: "Qt.core" } - Depends { name: "qtc" } - environment: { - var env = base; - if (!qbs.hostOS.contains("windows") || !qbs.targetOS.contains("windows")) - return env; - var path = ""; - for (var i = 0; i < env.length; ++i) { - if (env[i].startsWith("PATH=")) { - path = env[i].substring(5); - break; - } - } - var fullQtcInstallDir = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix); - var fullLibInstallDir = FileInfo.joinPaths(fullQtcInstallDir, qtc.ide_library_path); - var fullPluginInstallDir = FileInfo.joinPaths(fullQtcInstallDir, qtc.ide_plugin_path); - path = Qt.core.binPath + ";" + fullLibInstallDir + ";" + fullPluginInstallDir - + ";" + path; - var arrayElem = "PATH=" + path; - if (i < env.length) - env[i] = arrayElem; - else - env.push(arrayElem); - return env; - } - } } diff --git a/scripts/build.py b/scripts/build.py index 2fced683b1b..72c7d3571ad 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -146,6 +146,8 @@ def common_cmake_arguments(args): # Qt otherwise adds dependencies on libGLX and libOpenGL cmake_args += ['-DOpenGL_GL_PREFERENCE=LEGACY'] + cmake_args += args.config_args + return cmake_args def build_qtcreator(args, paths): @@ -194,8 +196,6 @@ def build_qtcreator(args, paths): if common.is_linux_platform(): cmake_args += ['-DCPACK_INSTALL_PREFIX=/opt/qt-creator'] - cmake_args += args.config_args - common.check_print_call(cmake_args + [paths.src], paths.build) build_args = ['cmake', '--build', '.'] if args.make_args: diff --git a/scripts/deployqt.py b/scripts/deploy.py similarity index 51% rename from scripts/deployqt.py rename to scripts/deploy.py index 79aea111bdd..5dd6198eca6 100755 --- a/scripts/deployqt.py +++ b/scripts/deploy.py @@ -19,14 +19,10 @@ encoding = locale.getdefaultlocale()[1] def get_args(): parser = argparse.ArgumentParser(description='Deploy Qt Creator dependencies for packaging') - parser.add_argument('-i', '--ignore-errors', help='For backward compatibility', - action='store_true', default=False) parser.add_argument('--elfutils-path', help='Path to elfutils installation for use by perfprofiler (Windows, Linux)') - # TODO remove defaulting to LLVM_INSTALL_DIR when we no longer build qmake based packages parser.add_argument('--llvm-path', - help='Path to LLVM installation', - default=os.environ.get('LLVM_INSTALL_DIR')) + help='Path to LLVM installation') parser.add_argument('qtcreator_binary', help='Path to Qt Creator binary (or the app bundle on macOS)') parser.add_argument('qmake_binary', help='Path to qmake binary') @@ -53,8 +49,8 @@ def get_args(): return args -def usage(): - print("Usage: %s [qmake_path]" % os.path.basename(sys.argv[0])) +def with_exe_ext(filepath): + return filepath + '.exe' if common.is_windows_platform() else filepath def which(program): def is_exe(fpath): @@ -112,12 +108,15 @@ def is_ignored_windows_file(use_debug, basepath, filename): def ignored_qt_lib_files(path, filenames): # Qt ships some unneeded object files in the qml plugins # On Windows we also do not want to ship the wrong debug/release .dlls or .lib files etc - if not common.is_windows_platform(): + # And get rid of debug info directories (.dSYM) on macOS + if common.is_linux_platform(): return [fn for fn in filenames if fn.endswith('.cpp.o')] + if common.is_mac_platform(): + return [fn for fn in filenames if fn.endswith('.dylib.dSYM') or fn.startswith('objects-')] return [fn for fn in filenames if fn.endswith('.cpp.obj') or is_ignored_windows_file(debug_build, path, fn)] -def copy_qt_libs(target_qt_prefix_path, qt_bin_dir, qt_libs_dir, qt_plugin_dir, qt_qml_dir, plugins): +def copy_qt_libs(target_qt_prefix_path, qt_bin_dir, qt_libs_dir): print("copying Qt libraries...") if common.is_windows_platform(): @@ -147,30 +146,81 @@ def copy_qt_libs(target_qt_prefix_path, qt_bin_dir, qt_libs_dir, qt_plugin_dir, else: shutil.copy(library, lib_dest) + +def deploy_qtdiag(qtc_binary_path, qt_install): + print("Copying qtdiag") + qtdiag_src = os.path.join(qt_install.bin, with_exe_ext('qtdiag')) + destdir = (qtc_binary_path if common.is_windows_platform() + else os.path.join(qtc_binary_path, 'Contents', 'MacOS') if common.is_mac_platform() + else os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'bin')) + if not os.path.exists(destdir): + os.makedirs(destdir) + shutil.copy(qtdiag_src, destdir) + if common.is_mac_platform(): + # fix RPATHs + qtdiag_dest = os.path.join(destdir, 'qtdiag') + subprocess.check_call(['xcrun', 'install_name_tool', '-add_rpath', '@loader_path/../Frameworks', qtdiag_dest]) + subprocess.check_call(['xcrun', 'install_name_tool', '-delete_rpath', '@loader_path/../lib', qtdiag_dest]) + + +def deploy_plugins(qtc_binary_path, qt_install): + plugins = ['assetimporters', 'accessible', 'codecs', 'designer', 'iconengines', 'imageformats', 'platformthemes', + 'platforminputcontexts', 'platforms', 'printsupport', 'qmltooling', 'sqldrivers', 'styles', + 'xcbglintegrations', + 'wayland-decoration-client', + 'wayland-graphics-integration-client', + 'wayland-shell-integration', + 'tls' + ] print("Copying plugins:", plugins) + destdir = (os.path.join(qtc_binary_path, 'plugins') if common.is_windows_platform() + else os.path.join(qtc_binary_path, 'Contents', 'PlugIns') if common.is_mac_platform() + else os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'plugins')) for plugin in plugins: - target = os.path.join(target_qt_prefix_path, 'plugins', plugin) + target = os.path.join(destdir, plugin) if (os.path.exists(target)): shutil.rmtree(target) - pluginPath = os.path.join(qt_plugin_dir, plugin) + pluginPath = os.path.join(qt_install.plugins, plugin) if (os.path.exists(pluginPath)): print('{0} -> {1}'.format(pluginPath, target)) common.copytree(pluginPath, target, ignore=ignored_qt_lib_files, symlinks=True) - if (os.path.exists(qt_qml_dir)): - print("Copying qt quick 2 imports") - target = os.path.join(target_qt_prefix_path, 'qml') - if (os.path.exists(target)): - shutil.rmtree(target) - print('{0} -> {1}'.format(qt_qml_dir, target)) - common.copytree(qt_qml_dir, target, ignore=ignored_qt_lib_files, symlinks=True) - print("Copying qtdiag") - bin_dest = target_qt_prefix_path if common.is_windows_platform() else os.path.join(target_qt_prefix_path, 'bin') - qtdiag_src = os.path.join(qt_bin_dir, 'qtdiag.exe' if common.is_windows_platform() else 'qtdiag') - if not os.path.exists(bin_dest): - os.makedirs(bin_dest) - shutil.copy(qtdiag_src, bin_dest) +def deploy_imports(qtc_binary_path, qt_install): + print("Copying qt quick 2 imports") + destdir = (os.path.join(qtc_binary_path, 'qml') if common.is_windows_platform() + else os.path.join(qtc_binary_path, 'Contents', 'Imports', 'qtquick2') if common.is_mac_platform() + else os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'qml')) + if (os.path.exists(destdir)): + shutil.rmtree(destdir) + print('{0} -> {1}'.format(qt_install.qml, destdir)) + common.copytree(qt_install.qml, destdir, ignore=ignored_qt_lib_files, symlinks=True) + + +def qt_conf_contents(): + if common.is_linux_platform(): + return '''[Paths] +Prefix={0} +Binaries=bin +Libraries=lib +Plugins=plugins +Qml2Imports=qml +''' + if common.is_windows_platform(): + return '''[Paths] +Prefix={0} +Binaries=. +Libraries=. +Plugins=plugins +Qml2Imports=qml +''' + return '''[Paths] +Prefix={0} +Binaries=MacOS +Libraries=Frameworks +Plugins=PlugIns +Qml2Imports=Imports/qtquick2 +''' def add_qt_conf(target_path, qt_prefix_path): @@ -178,22 +228,36 @@ def add_qt_conf(target_path, qt_prefix_path): prefix_path = os.path.relpath(qt_prefix_path, target_path).replace('\\', '/') print('Creating qt.conf in "{0}":'.format(qtconf_filepath)) f = open(qtconf_filepath, 'w') - f.write('[Paths]\n') - f.write('Prefix={0}\n'.format(prefix_path)) - f.write('Binaries={0}\n'.format('bin' if common.is_linux_platform() else '.')) - f.write('Libraries={0}\n'.format('lib' if common.is_linux_platform() else '.')) - f.write('Plugins=plugins\n') - f.write('Qml2Imports=qml\n') + f.write(qt_conf_contents().format(prefix_path)) f.close() -def copy_translations(install_dir, qt_tr_dir): - translations = glob(os.path.join(qt_tr_dir, '*.qm')) - tr_dir = os.path.join(install_dir, 'share', 'qtcreator', 'translations') - print("copying translations...") +def deploy_qt_conf_files(qtc_binary_path): + if common.is_linux_platform(): + qt_prefix_path = os.path.join(qtc_binary_path, '..', 'lib', 'Qt') + add_qt_conf(os.path.join(qtc_binary_path, '..', 'libexec', 'qtcreator'), qt_prefix_path) + add_qt_conf(os.path.join(qtc_binary_path, '..', 'lib', 'Qt', 'bin'), qt_prefix_path) # qtdiag + add_qt_conf(qtc_binary_path, qt_prefix_path) # QtC itself + if common.is_windows_platform(): + add_qt_conf(qtc_binary_path, qtc_binary_path) # QtC itself, libexec, and qtdiag etc + if common.is_mac_platform(): + qt_prefix_path = os.path.join(qtc_binary_path, 'Contents') + qtc_resources_path = os.path.join(qtc_binary_path, 'Contents', 'Resources') + add_qt_conf(os.path.join(qtc_resources_path, 'libexec'), qt_prefix_path) + add_qt_conf(os.path.join(qtc_resources_path, 'libexec', 'ios'), qt_prefix_path) + # The Prefix path for a qt.conf in Contents/Resources/ is handled specially/funny by Qt, + # relative paths are resolved relative to Contents/, so we must enforces Prefix=. + add_qt_conf(qtc_resources_path, qtc_resources_path) # QtC itself + + +def deploy_translations(qtc_binary_path, qt_install): + print("Copying translations...") + translations = glob(os.path.join(qt_install.translations, '*.qm')) + destdir = (os.path.join(qtc_binary_path, 'Contents', 'Resources', 'translations') if common.is_mac_platform() + else os.path.join(qtc_binary_path, '..', 'share', 'qtcreator', 'translations')) for translation in translations: - print(translation, '->', tr_dir) - shutil.copy(translation, tr_dir) + print(translation, '->', destdir) + shutil.copy(translation, destdir) def copyPreservingLinks(source, destination): if os.path.islink(source): @@ -205,47 +269,52 @@ def copyPreservingLinks(source, destination): else: shutil.copy(source, destination) -def deploy_clang(install_dir, llvm_install_dir, chrpath_bin): +def deploy_clang(qtc_binary_path, llvm_install_dir, chrpath_bin): + print("Copying clang...") + # contains pairs of (source, target directory) deployinfo = [] resourcesource = os.path.join(llvm_install_dir, 'lib', 'clang') if common.is_windows_platform(): - clangbindirtarget = os.path.join(install_dir, 'bin', 'clang', 'bin') - if not os.path.exists(clangbindirtarget): - os.makedirs(clangbindirtarget) - clanglibdirtarget = os.path.join(install_dir, 'bin', 'clang', 'lib') - if not os.path.exists(clanglibdirtarget): - os.makedirs(clanglibdirtarget) - for binary in ['clangd', 'clang-tidy', 'clazy-standalone', 'clang-format']: - binary_filepath = os.path.join(llvm_install_dir, 'bin', binary + '.exe') - if os.path.exists(binary_filepath): - deployinfo.append((binary_filepath, clangbindirtarget)) - resourcetarget = os.path.join(clanglibdirtarget, 'clang') - else: - # clang binaries -> clang libexec - clangbinary_targetdir = os.path.join(install_dir, 'libexec', 'qtcreator', 'clang', 'bin') - if not os.path.exists(clangbinary_targetdir): - os.makedirs(clangbinary_targetdir) - for binary in ['clangd', 'clang-tidy', 'clazy-standalone', 'clang-format']: - binary_filepath = os.path.join(llvm_install_dir, 'bin', binary) - if os.path.exists(binary_filepath): - deployinfo.append((binary_filepath, clangbinary_targetdir)) - # add link target if binary is actually a symlink (to a binary in the same directory) - if os.path.islink(binary_filepath): - linktarget = os.readlink(binary_filepath) - deployinfo.append((os.path.join(os.path.dirname(binary_filepath), linktarget), - os.path.join(clangbinary_targetdir, linktarget))) - clanglibs_targetdir = os.path.join(install_dir, 'libexec', 'qtcreator', 'clang', 'lib') - # support libraries (for clazy) -> clang libexec + clang_targetdir = os.path.join(qtc_binary_path, 'clang') + clangbinary_targetdir = os.path.join(clang_targetdir, 'bin') + resourcetarget = os.path.join(clang_targetdir, 'lib', 'clang') + elif common.is_linux_platform(): + clang_targetdir = os.path.join(qtc_binary_path, '..', 'libexec', 'qtcreator', 'clang') + clangbinary_targetdir = os.path.join(clang_targetdir, 'bin') + resourcetarget = os.path.join(clang_targetdir, 'lib', 'clang') + # ClazyPlugin. On RHEL ClazyPlugin is in lib64, so check both + clanglibs_targetdir = os.path.join(clang_targetdir, 'lib') if not os.path.exists(clanglibs_targetdir): os.makedirs(clanglibs_targetdir) - # on RHEL ClazyPlugin is in lib64 for lib_pattern in ['lib64/ClazyPlugin.so', 'lib/ClazyPlugin.so']: for lib in glob(os.path.join(llvm_install_dir, lib_pattern)): deployinfo.append((lib, clanglibs_targetdir)) - resourcetarget = os.path.join(install_dir, 'libexec', 'qtcreator', 'clang', 'lib', 'clang') + else: + clang_targetdir = os.path.join(qtc_binary_path, 'Contents', 'Resources', 'libexec', 'clang') + clanglibs_targetdir = os.path.join(clang_targetdir, 'lib') + clangbinary_targetdir = os.path.join(clang_targetdir, 'bin') + resourcetarget = os.path.join(clang_targetdir, 'lib', 'clang') + # ClazyPlugin + clanglibs_targetdir = os.path.join(clang_targetdir, 'lib') + if not os.path.exists(clanglibs_targetdir): + os.makedirs(clanglibs_targetdir) + clazy_plugin = os.path.join(llvm_install_dir, 'lib', 'ClazyPlugin.dylib') + deployinfo.append((clazy_plugin, clanglibs_targetdir)) + + # collect binaries + if not os.path.exists(clangbinary_targetdir): + os.makedirs(clangbinary_targetdir) + for binary in ['clangd', 'clang-tidy', 'clazy-standalone', 'clang-format']: + binary_filepath = os.path.join(llvm_install_dir, 'bin', with_exe_ext(binary)) + if os.path.exists(binary_filepath): + deployinfo.append((binary_filepath, clangbinary_targetdir)) + # add link target if binary is actually a symlink (to a binary in the same directory) + if not common.is_windows_platform() and os.path.islink(binary_filepath): + linktarget = os.readlink(binary_filepath) + deployinfo.append((os.path.join(os.path.dirname(binary_filepath), linktarget), + os.path.join(clangbinary_targetdir, linktarget))) - print("copying clang...") for source, target in deployinfo: print(source, '->', target) copyPreservingLinks(source, target) @@ -254,7 +323,6 @@ def deploy_clang(install_dir, llvm_install_dir, chrpath_bin): # libclang was statically compiled, so there is no need for the RPATHs # and they are confusing when fixing RPATHs later in the process. # Also fix clazy-standalone RPATH. - print("fixing Clang RPATHs...") for source, target in deployinfo: filename = os.path.basename(source) targetfilepath = target if not os.path.isdir(target) else os.path.join(target, filename) @@ -264,6 +332,12 @@ def deploy_clang(install_dir, llvm_install_dir, chrpath_bin): targetfilepath = target if not os.path.isdir(target) else os.path.join(target, os.path.basename(source)) subprocess.check_call([chrpath_bin, '-d', targetfilepath]) + if common.is_mac_platform(): + # fix RPATH of clazy-standalone + clazy_dest = os.path.join(clangbinary_targetdir, 'clazy-standalone') + subprocess.call(['xcrun', 'install_name_tool', '-add_rpath', '@loader_path/../lib', clazy_dest], stderr=subprocess.DEVNULL) + subprocess.call(['xcrun', 'install_name_tool', '-delete_rpath', '/Users/qt/work/build/libclang/lib', clazy_dest], stderr=subprocess.DEVNULL) + print(resourcesource, '->', resourcetarget) if (os.path.exists(resourcetarget)): shutil.rmtree(resourcetarget) @@ -317,18 +391,54 @@ def deploy_elfutils(qtc_install_dir, chrpath_bin, args): print(file, '->', backends_install_path) shutil.copy(file, backends_install_path) -def deploy_mac(args): - (_, qt_install) = get_qt_install_info(args.qmake_binary) +def deploy_qt_mac(qtc_binary_path, qt_install): + # This runs macdeployqt + # Collect things to pass via -executable + libexec_path = os.path.join(qtc_binary_path, 'Contents', 'Resources', 'libexec') + bin_path = os.path.join(qtc_binary_path, 'Contents', 'MacOS') + plugins_path = os.path.join(qtc_binary_path, 'Contents', 'PlugIns') + frameworks_path = os.path.join(qtc_binary_path, 'Contents', 'Frameworks') + additional_paths = [] + # Qbs + apps = ['qbs', 'qbs-config', 'qbs-config-ui', 'qbs-setup-android', 'qbs-setup-qt', + 'qbs-setup-toolchains', 'qbs-create-project'] + for app in apps: + additional_paths.append(os.path.join(bin_path, app)) + additional_paths.append(os.path.join(libexec_path, 'qbs_processlauncher')) + # qml2puppet + puppets = glob(os.path.join(libexec_path, 'qml2puppet*')) + for puppet in puppets: + additional_paths.append(puppet) + # qtdiag + additional_paths.append(os.path.join(bin_path, 'qtdiag')) + # other libexec + additional_paths.append(os.path.join(libexec_path, 'sdktool')) + additional_paths.append(os.path.join(libexec_path, 'qtpromaker')) + additional_paths.append(os.path.join(libexec_path, 'buildoutputparser')) + additional_paths.append(os.path.join(libexec_path, 'cpaster')) + additional_paths.append(os.path.join(libexec_path, 'ios', 'iostool')) - env = dict(os.environ) - if args.llvm_path: - env['LLVM_INSTALL_DIR'] = args.llvm_path + existing_additional_paths = [p for p in additional_paths if os.path.exists(p)] + macdeployqt = os.path.join(qt_install.bin, 'macdeployqt') + print('Running macdeployqt (' + macdeployqt + ')') + print('- with additional paths:', existing_additional_paths) + executable_args = ['-executable='+path for path in existing_additional_paths] + subprocess.check_call([macdeployqt, qtc_binary_path] + executable_args) + + # clean up some things that might have been deployed, but we don't want + to_remove = [ + os.path.join(plugins_path, 'tls', 'libqopensslbackend.dylib'), + os.path.join(plugins_path, 'sqldrivers', 'libqsqlpsql.dylib'), + os.path.join(plugins_path, 'sqldrivers', 'libqsqlodbc.dylib'), + ] + to_remove.extend(glob(os.path.join(frameworks_path, 'libpq.*dylib'))) + to_remove.extend(glob(os.path.join(frameworks_path, 'libssl.*dylib'))) + to_remove.extend(glob(os.path.join(frameworks_path, 'libcrypto.*dylib'))) + for path in to_remove: + if os.path.isfile(path): + print('- Removing ' + path) + os.remove(path) - script_path = os.path.dirname(os.path.realpath(__file__)) - deployqtHelper_mac = os.path.join(script_path, 'deployqtHelper_mac.sh') - common.check_print_call([deployqtHelper_mac, args.qtcreator_binary, qt_install.bin, - qt_install.translations, qt_install.plugins, qt_install.qml], - env=env) def get_qt_install_info(qmake_binary): qt_install_info = common.get_qt_install_info(qmake_binary) @@ -343,19 +453,6 @@ def get_qt_install_info(qmake_binary): def main(): args = get_args() - if common.is_mac_platform(): - deploy_mac(args) - return - - (qt_install_info, qt_install) = get_qt_install_info(args.qmake_binary) - - qtcreator_binary_path = os.path.dirname(args.qtcreator_binary) - install_dir = os.path.abspath(os.path.join(qtcreator_binary_path, '..')) - if common.is_linux_platform(): - qt_deploy_prefix = os.path.join(install_dir, 'lib', 'Qt') - else: - qt_deploy_prefix = os.path.join(install_dir, 'bin') - chrpath_bin = None if common.is_linux_platform(): chrpath_bin = which('chrpath') @@ -363,35 +460,43 @@ def main(): print("Cannot find required binary 'chrpath'.") sys.exit(2) - plugins = ['assetimporters', 'accessible', 'codecs', 'designer', 'iconengines', 'imageformats', 'platformthemes', - 'platforminputcontexts', 'platforms', 'printsupport', 'qmltooling', 'sqldrivers', 'styles', - 'xcbglintegrations', - 'wayland-decoration-client', - 'wayland-graphics-integration-client', - 'wayland-shell-integration', - 'tls' - ] - if common.is_windows_platform(): global debug_build debug_build = is_debug(args.qtcreator_binary) - if common.is_windows_platform(): - copy_qt_libs(qt_deploy_prefix, qt_install.bin, qt_install.bin, qt_install.plugins, qt_install.qml, plugins) - else: - copy_qt_libs(qt_deploy_prefix, qt_install.bin, qt_install.lib, qt_install.plugins, qt_install.qml, plugins) - copy_translations(install_dir, qt_install.translations) - if args.llvm_path: - deploy_clang(install_dir, args.llvm_path, chrpath_bin) + (qt_install_info, qt_install) = get_qt_install_info(args.qmake_binary) + # /bin for Win/Lin, /.app for macOS + qtcreator_binary_path = (args.qtcreator_binary if common.is_mac_platform() + else os.path.dirname(args.qtcreator_binary)) + + deploy_qtdiag(qtcreator_binary_path, qt_install) + deploy_plugins(qtcreator_binary_path, qt_install) + deploy_imports(qtcreator_binary_path, qt_install) + deploy_translations(qtcreator_binary_path, qt_install) + deploy_qt_conf_files(qtcreator_binary_path) + if args.llvm_path: + deploy_clang(qtcreator_binary_path, args.llvm_path, chrpath_bin) + + if common.is_mac_platform(): + deploy_qt_mac(qtcreator_binary_path, qt_install) + else: + install_dir = os.path.abspath(os.path.join(qtcreator_binary_path, '..')) + if common.is_linux_platform(): + qt_deploy_prefix = os.path.join(install_dir, 'lib', 'Qt') + else: + qt_deploy_prefix = os.path.join(install_dir, 'bin') + + if common.is_windows_platform(): + copy_qt_libs(qt_deploy_prefix, qt_install.bin, qt_install.bin) + else: + copy_qt_libs(qt_deploy_prefix, qt_install.bin, qt_install.lib) + + if args.elfutils_path: + deploy_elfutils(install_dir, chrpath_bin, args) + if not common.is_windows_platform(): + print("fixing rpaths...") + common.fix_rpaths(install_dir, os.path.join(qt_deploy_prefix, 'lib'), qt_install_info, chrpath_bin) - if args.elfutils_path: - deploy_elfutils(install_dir, chrpath_bin, args) - if not common.is_windows_platform(): - print("fixing rpaths...") - common.fix_rpaths(install_dir, os.path.join(qt_deploy_prefix, 'lib'), qt_install_info, chrpath_bin) - add_qt_conf(os.path.join(install_dir, 'libexec', 'qtcreator'), qt_deploy_prefix) # e.g. for qml2puppet - add_qt_conf(os.path.join(qt_deploy_prefix, 'bin'), qt_deploy_prefix) # e.g. qtdiag - add_qt_conf(os.path.join(install_dir, 'bin'), qt_deploy_prefix) if __name__ == "__main__": main() diff --git a/scripts/deployqtHelper_mac.sh b/scripts/deployqtHelper_mac.sh deleted file mode 100755 index 6ef3fcdb8d1..00000000000 --- a/scripts/deployqtHelper_mac.sh +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/bash - -# Copyright (C) 2016 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -[ $# -lt 5 ] && echo "Usage: $(basename $0) " && exit 2 -[ $(uname -s) != "Darwin" ] && echo "Run this script on Mac OS X" && exit 2; - -app_path="$1" -resource_path="$app_path/Contents/Resources" -libexec_path="$app_path/Contents/Resources/libexec" -bin_src="$2" -translation_src="$3" -plugin_src="$4" -quick2_src="$5" - -echo "Deploying Qt" - -# copy qtdiag -echo "- Copying qtdiag" -cp "$bin_src/qtdiag" "$app_path/Contents/MacOS/" || exit 1 -# fix rpath if qtdiag was from binary Qt package -( xcrun install_name_tool -delete_rpath "@loader_path/../lib" "$app_path/Contents/MacOS/qtdiag" && - xcrun install_name_tool -add_rpath "@loader_path/../Frameworks" "$app_path/Contents/MacOS/qtdiag" ) || true - - -# collect designer plugins -designerDestDir="$app_path/Contents/PlugIns/designer" -if [ ! -d "$designerDestDir" ]; then - echo "- Copying designer plugins" - mkdir -p "$designerDestDir" - for plugin in "$plugin_src"/designer/*.dylib; do - cp "$plugin" "$designerDestDir"/ || exit 1 - done -fi - -# collect 3d assetimporter plugins -assetimporterDestDir="$app_path/Contents/PlugIns/assetimporters" -assetimporterSrcDir="$plugin_src/assetimporters" -if [ -d "$assetimporterSrcDir" ]; then - if [ ! -d "$assetimporterDestDir" ]; then - echo "- Copying 3d assetimporter plugins" - mkdir -p "$assetimporterDestDir" - find "$assetimporterSrcDir" -iname "*.dylib" -maxdepth 1 -exec cp {} "$assetimporterDestDir"/ \; - fi -fi - -# copy Qt Quick 2 imports -imports2Dir="$app_path/Contents/Imports/qtquick2" -if [ -d "$quick2_src" ]; then - if [ ! -d "$imports2Dir" ]; then - echo "- Copying Qt Quick 2 imports" - mkdir -p "$imports2Dir" - cp -R "$quick2_src"/ "$imports2Dir"/ - find "$imports2Dir" -path "*.dylib.dSYM*" -delete - fi -fi - -# copy qt creator qt.conf -if [ ! -f "$resource_path/qt.conf" ]; then - echo "- Copying qt.conf" - cp -f "$(dirname "${BASH_SOURCE[0]}")/../dist/installer/mac/qt.conf" "$resource_path/qt.conf" || exit 1 -fi - -# copy libexec tools' qt.conf -if [ ! -f "$libexec_path/qt.conf" ]; then - echo "- Copying libexec/qt.conf" - cp -f "$(dirname "${BASH_SOURCE[0]}")/../dist/installer/mac/libexec_qt.conf" "$libexec_path/qt.conf" || exit 1 -fi - -# copy ios tools' qt.conf -if [ ! -f "$libexec_path/ios/qt.conf" ]; then - echo "- Copying libexec/ios/qt.conf" - cp -f "$(dirname "${BASH_SOURCE[0]}")/../dist/installer/mac/ios_qt.conf" "$libexec_path/ios/qt.conf" || exit 1 -fi - -# copy Qt translations -# check for known existing translation to avoid copying multiple times -if [ ! -f "$resource_path/translations/qt_de.qm" ]; then - echo "- Copying Qt translations" - cp "$translation_src"/*.qm "$resource_path/translations/" || exit 1 -fi - -# copy clang if needed -if [ $LLVM_INSTALL_DIR ]; then - if [ "$LLVM_INSTALL_DIR"/bin/clangd -nt "$libexec_path"/clang/bin/clangd ]; then - echo "- Copying clang" - mkdir -p "$app_path/Contents/Frameworks" || exit 1 - # use recursive copy to make it copy symlinks as symlinks - mkdir -p "$libexec_path/clang/bin" - mkdir -p "$libexec_path/clang/lib" - cp -Rf "$LLVM_INSTALL_DIR"/lib/clang "$libexec_path/clang/lib/" || exit 1 - cp -Rf "$LLVM_INSTALL_DIR"/lib/ClazyPlugin.dylib "$libexec_path/clang/lib/" || exit 1 - clangdsource="$LLVM_INSTALL_DIR"/bin/clangd - cp -Rf "$clangdsource" "$libexec_path/clang/bin/" || exit 1 - clangtidysource="$LLVM_INSTALL_DIR"/bin/clang-tidy - cp -Rf "$clangtidysource" "$libexec_path/clang/bin/" || exit 1 - clangformatsource="$LLVM_INSTALL_DIR"/bin/clang-format - cp -Rf "$clangformatsource" "$libexec_path/clang/bin/" || exit 1 - clazysource="$LLVM_INSTALL_DIR"/bin/clazy-standalone - cp -Rf "$clazysource" "$libexec_path/clang/bin/" || exit 1 - install_name_tool -add_rpath "@executable_path/../lib" "$libexec_path/clang/bin/clazy-standalone" 2> /dev/null - install_name_tool -delete_rpath "/Users/qt/work/build/libclang/lib" "$libexec_path/clang/bin/clazy-standalone" 2> /dev/null - fi -fi - -#### macdeployqt - -if [ ! -d "$app_path/Contents/Frameworks/QtCore.framework" ]; then - - echo "- Running macdeployqt ($bin_src/macdeployqt)" - - qbsapp="$app_path/Contents/MacOS/qbs" - if [ -f "$qbsapp" ]; then - qbsArguments=("-executable=$qbsapp" \ - "-executable=$qbsapp-config" \ - "-executable=$qbsapp-config-ui" \ - "-executable=$qbsapp-setup-android" \ - "-executable=$qbsapp-setup-qt" \ - "-executable=$qbsapp-setup-toolchains" \ - "-executable=$qbsapp-create-project" \ - "-executable=$libexec_path/qbs_processlauncher") - fi - - qml2puppetapp="$libexec_path/qml2puppet" - if [ -f "$qml2puppetapp" ]; then - qml2puppetArgument="-executable=$qml2puppetapp" - fi - sdktoolapp="$libexec_path/sdktool" - if [ -f "$sdktoolapp" ]; then - sdktoolArgument="-executable=$sdktoolapp" - fi - - "$bin_src/macdeployqt" "$app_path" \ - "-executable=$app_path/Contents/MacOS/qtdiag" \ - "-executable=$libexec_path/qtpromaker" \ - "$sdktoolArgument" \ - "-executable=$libexec_path/ios/iostool" \ - "-executable=$libexec_path/buildoutputparser" \ - "-executable=$libexec_path/cpaster" \ - "${qbsArguments[@]}" \ - "$qml2puppetArgument" || exit 1 - -fi - -# clean up unneeded object files that are part of Qt for some static libraries -find "$app_path" -ipath "*/objects-*" -delete - -# clean up after macdeployqt -# it deploys some plugins (and libs for these) that interfere with what we want -echo "Cleaning up after macdeployqt..." -toRemove=(\ - "Contents/PlugIns/tls/libqopensslbackend.dylib" \ - "Contents/PlugIns/sqldrivers/libqsqlpsql.dylib" \ - "Contents/PlugIns/sqldrivers/libqsqlodbc.dylib" \ - "Contents/Frameworks/libpq.*dylib" \ - "Contents/Frameworks/libssl.*dylib" \ - "Contents/Frameworks/libcrypto.*dylib" \ -) -for f in "${toRemove[@]}"; do - echo "- removing \"$app_path/$f\"" - rm "$app_path"/$f 2> /dev/null; done -exit 0 diff --git a/scripts/generateClangFormatChecksLayout.py b/scripts/generateClangFormatChecksLayout.py index 03f8c7830d7..85bd82257cc 100755 --- a/scripts/generateClangFormatChecksLayout.py +++ b/scripts/generateClangFormatChecksLayout.py @@ -17,10 +17,10 @@ def parse_arguments(): def full_header_content(header_code): - return '''// Copyright (C) 2022 The Qt Company Ltd. + return '''// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! +// THIS FILE IS AUTOMATICALLY GENERATED by generateClangFormatChecksLayout. DO NOT EDIT! #pragma once @@ -53,10 +53,10 @@ private: def full_source_content(source_code, layout_code): - return '''// Copyright (C) 2022 The Qt Company Ltd. + return '''// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! +// THIS FILE IS AUTOMATICALLY GENERATED by generateClangFormatChecksLayout. DO NOT EDIT! #include "clangformatchecks.h" @@ -70,8 +70,6 @@ def full_source_content(source_code, layout_code): #include #include -using namespace Utils; - using namespace ClangFormat; ClangFormatChecks::ClangFormatChecks(QWidget *parent) @@ -105,8 +103,8 @@ def combobox_source(name, values, offset): def combobox_source_bool(name, offset): return combobox_source(name, ["Default", "true", "false"], offset) -def combobox_layout(name, offset): - layout = " new QLabel(\"" + offset + name + "\"), m_" + name + ", br,\n" +def combobox_layout(name, field_name, offset): + layout = " new QLabel(\"" + offset + field_name + "\"), m_" + name + ", br,\n" return layout # String UI @@ -121,11 +119,10 @@ def string_source(name, offset): source += " m_" + name + "->setObjectName(\"" + name + "\");\n" source += " m_set" + name + " = new QPushButton(\"Set\", this);\n\n" source += " m_set" + name + "->setObjectName(\"set" + name + "\");\n" -# source += "m_" + name + "->setObjectName(\"" + offset + name + "\");\n\n" return source -def string_layout(name, offset): - layout = " new QLabel(\"" + offset + name + "\"), Row {m_" + name + ", m_set" + name + "}, br,\n" +def string_layout(name, field_name, offset): + layout = " new QLabel(\"" + offset + field_name + "\"), Row {m_" + name + ", m_set" + name + "}, br,\n" return layout # Vector UI @@ -144,8 +141,8 @@ def vector_source(name, offset): # source += "m_" + name + "->setObjectName(\"" + offset + name + "\");\n\n" return source -def vector_layout(name, offset): - layout = " new QLabel(\"" + offset + name + "\"), Row {m_" + name + ", m_set" + name + "}, br,\n" +def vector_layout(name, field_name, offset): + layout = " new QLabel(\"" + offset + field_name + "\"), Row {m_" + name + ", m_set" + name + "}, br,\n" return layout # Struct Layout @@ -160,7 +157,7 @@ def in_list(list, type): return element; return -def create_private_variables(variables, enums, structs, offset = ""): +def create_private_variables(variables, enums, structs, offset = "", parent_name = ""): header = "" source = "" layout = "" @@ -169,7 +166,7 @@ def create_private_variables(variables, enums, structs, offset = ""): if offset == "": header += combobox_header("BasedOnStyle") source += combobox_source("BasedOnStyle", ["LLVM", "Google", "Chromium", "Mozilla", "WebKit", "Microsoft", "GNU"], offset) - layout += combobox_layout("BasedOnStyle", offset) + layout += combobox_layout("BasedOnStyle", "BasedOnStyle", offset) for variable in variables: if "doxygen" in variable.keys(): @@ -177,31 +174,32 @@ def create_private_variables(variables, enums, structs, offset = ""): continue; type = variable["type"] - name = variable["name"] + field_name = variable["name"] + variable_name = parent_name + variable["name"] enum = in_list(enums, type) struct = in_list(structs, type) if enum: - header += combobox_header(name) - source += combobox_source(name, [value["name"].split("_")[1] for value in enum["values"]], offset) - layout += combobox_layout(name, offset) + header += combobox_header(variable_name) + source += combobox_source(variable_name, [value["name"].split("_")[1] for value in enum["values"]], offset) + layout += combobox_layout(variable_name, field_name, offset) elif struct: - layout += struct_layout(name, offset) - header_tmp, source_tmp, layout_tmp = create_private_variables(struct["properties"]["public"], enums, structs, " ") + layout += struct_layout(variable_name, offset) + header_tmp, source_tmp, layout_tmp = create_private_variables(struct["properties"]["public"], enums, structs, " ", variable_name) header += header_tmp source += source_tmp layout += layout_tmp elif "std::string" == type or "unsigned" == type or "int" == type: - header += string_header(name) - source += string_source(name, offset) - layout += string_layout(name, offset) - elif "std::vector" == type: - header += vector_header(name) - source += vector_source(name, offset) - layout += vector_layout(name, offset) + header += string_header(variable_name) + source += string_source(variable_name, offset) + layout += string_layout(variable_name, field_name, offset) + elif "std::vector" == type: + header += vector_header(variable_name) + source += vector_source(variable_name, offset) + layout += vector_layout(variable_name, field_name, offset) elif "bool" == type: - header += combobox_header(name) - source += combobox_source_bool(name, offset) - layout += combobox_layout(name, offset); + header += combobox_header(variable_name) + source += combobox_source_bool(variable_name, offset) + layout += combobox_layout(variable_name, field_name, offset); return header, source, layout diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py index a11377b0db5..24c16ce0094 100644 --- a/share/qtcreator/debugger/creatortypes.py +++ b/share/qtcreator/debugger/creatortypes.py @@ -81,6 +81,11 @@ def qdump__Utils__Id(d, value): d.putPlainChildren(value) +def qdump__Utils__Key(d, value): + d.putByteArrayValue(value["data"]) + d.putBetterType(value.type) + + def qdump__Debugger__Internal__GdbMi(d, value): val = d.encodeString(value["m_name"]) + "3a002000" \ + d.encodeString(value["m_data"]) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index b25c139f10f..d61160eed3b 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -1409,7 +1409,13 @@ class Dumper(DumperBase): 'file="%s",line="%s",module="%s",language="c"}') % (i, pc, functionName, fileName, line, objfile)) - frame = frame.older() + try: + # This may fail with something like + # gdb.error: DW_FORM_addr_index used without .debug_addr section + #[in module /data/dev/qt-6/qtbase/lib/libQt6Widgets.so.6] + frame = frame.older() + except: + break i += 1 self.put(']}') self.reportResult(self.takeOutput(), args) diff --git a/share/qtcreator/debugger/libcpp_stdtypes.py b/share/qtcreator/debugger/libcpp_stdtypes.py index 23b26f798e3..bce83a4fe68 100644 --- a/share/qtcreator/debugger/libcpp_stdtypes.py +++ b/share/qtcreator/debugger/libcpp_stdtypes.py @@ -404,7 +404,19 @@ def qdump__std____1__weak_ptr(d, value): def qdump__std____1__unique_ptr(d, value): - qdump__std__unique_ptr(d, value) + if value.type.size() == d.ptrSize(): + p = d.extractPointer(value) + else: + _, p = value.split("pp"); # For custom deleters. + if p == 0: + d.putValue("(null)") + else: + try: + d.putItem(value["__value_"]) + d.putValue(d.currentValue.value, d.currentValue.encoding) + except: + d.putItem(d.createValue(p, value.type[0])) + d.putBetterType(value.type) def qform__std____1__unordered_map(): diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 3cef9f22c9d..7b5c002b432 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -971,20 +971,57 @@ class Dumper(DumperBase): self.debugger.GetListener(), self.remoteChannel_, None, error) else: - f = lldb.SBFileSpec() - f.SetFilename(self.executable_) + if self.platform_ == "remote-macosx": + self.report("Connecting to remote target: connect://%s" % self.remoteChannel_) + self.process = self.target.ConnectRemote( + self.debugger.GetListener(), + "connect://" + self.remoteChannel_, None, error) - launchInfo = lldb.SBLaunchInfo(self.processArgs_) - #launchInfo.SetWorkingDirectory(self.workingDirectory_) - launchInfo.SetWorkingDirectory('/tmp') - if self.platform_ == 'remote-android': - launchInfo.SetWorkingDirectory('/data/local/tmp') - launchInfo.SetEnvironmentEntries(self.environment_, False) - launchInfo.SetExecutableFile(f, True) + if not error.Success(): + self.report("Failed to connect to remote target: %s" % error.GetCString()) + self.reportState('enginerunfailed') + return - DumperBase.warn("TARGET: %s" % self.target) - self.process = self.target.Launch(launchInfo, error) - DumperBase.warn("PROCESS: %s" % self.process) + if self.breakOnMain_: + self.createBreakpointAtMain() + + DumperBase.warn("PROCESS: %s (%s)" % (self.process, error.Success() and "Success" or error.GetCString())) + elif self.platform_ == "remote-linux": + self.report("Connecting to remote target: connect://%s" % self.remoteChannel_) + + platform = self.target.GetPlatform() + url = "connect://" + self.remoteChannel_ + conOptions = lldb.SBPlatformConnectOptions(url) + error = platform.ConnectRemote(conOptions) + + if not error.Success(): + self.report("Failed to connect to remote target (%s): %s" % (url, error.GetCString())) + self.reportState('enginerunfailed') + return + + f = lldb.SBFileSpec() + f.SetFilename(self.executable_) + launchInfo = lldb.SBLaunchInfo(self.processArgs_) + launchInfo.SetWorkingDirectory(self.workingDirectory_) + launchInfo.SetWorkingDirectory('/tmp') + launchInfo.SetEnvironmentEntries(self.environment_, False) + launchInfo.SetExecutableFile(f, True) + self.process = self.target.Launch(launchInfo, error) + + if not error.Success(): + self.report("Failed to launch remote target: %s" % (error.GetCString())) + self.reportState('enginerunfailed') + return + else: + self.report("Process has launched.") + + if self.breakOnMain_: + self.createBreakpointAtMain() + + else: + self.report("Unsupported platform: %s" % self.platform_) + self.reportState('enginerunfailed') + return if not error.Success(): self.report(self.describeError(error)) @@ -1479,8 +1516,9 @@ class Dumper(DumperBase): self.reportState("stopped") if self.firstStop_: self.firstStop_ = False - if self.useTerminal_: - # When using a terminal, the process will be interrupted on startup. + if self.useTerminal_ or self.platform_ == "remote-macosx": + # When using a terminal or remote debugging macosx apps, + # the process will be interrupted on startup. # We therefore need to continue it here. self.process.Continue() else: @@ -1798,11 +1836,11 @@ class Dumper(DumperBase): self.process.SetSelectedThreadByID(int(args['id'])) self.reportResult('', args) - def fetchFullBacktrace(self, _=None): + def fetchFullBacktrace(self, args): command = 'thread backtrace all' result = lldb.SBCommandReturnObject() self.debugger.GetCommandInterpreter().HandleCommand(command, result) - self.reportResult(self.hexencode(result.GetOutput()), {}) + self.reportResult('fulltrace="%s"' % self.hexencode(result.GetOutput()), args) def executeDebuggerCommand(self, args): self.reportToken(args) @@ -2030,20 +2068,23 @@ class Tester(Dumper): lldb.SBDebugger.Destroy(self.debugger) +if 'QT_CREATOR_LLDB_PROCESS' in os.environ: + # Initialize Qt Creator dumper + try: + theDumper = Dumper() + except Exception as error: + print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error)) + # ------------------------------ For use in LLDB ------------------------------ +debug = print if 'QT_LLDB_SUMMARY_PROVIDER_DEBUG' in os.environ \ + else lambda *a, **k: None -from pprint import pprint - -__module__ = sys.modules[__name__] -DEBUG = False if not hasattr(__module__, 'DEBUG') else DEBUG - +debug(f"Loading lldbbridge.py from {__file__}") class LogMixin(): @staticmethod def log(message='', log_caller=False, frame=1, args=''): - if not DEBUG: - return if log_caller: message = ": " + message if len(message) else '' # FIXME: Compute based on first frame not in this class? @@ -2052,7 +2093,7 @@ class LogMixin(): localz = frame.f_locals instance = str(localz["self"]) + "." if 'self' in localz else '' message = "%s%s(%s)%s" % (instance, fn, args, message) - print(message) + debug(message) @staticmethod def log_fn(arg_str=''): @@ -2116,14 +2157,7 @@ class SummaryDumper(Dumper, LogMixin): return # Don't mess up lldb output def dump_summary(self, valobj, expanded=False): - try: - from pygdbmi import gdbmiparser - except ImportError: - print("Qt summary provider requires the pygdbmi module, " - "please install using 'sudo /usr/bin/easy_install pygdbmi', " - "and then restart Xcode.") - lldb.debugger.HandleCommand('type category delete Qt') - return None + from pygdbmi import gdbmiparser value = self.fromNativeValue(valobj) @@ -2396,9 +2430,51 @@ class SyntheticChildrenProvider(SummaryProvider): self.valobj = self.create_value(dereference_child) self.update() +def ensure_gdbmiparser(): + try: + from pygdbmi import gdbmiparser + return True + except ImportError: + try: + if not 'QT_LLDB_SUMMARY_PROVIDER_NO_AUTO_INSTALL' in os.environ: + print("Required module 'pygdbmi' not installed. Installing automatically...") + import subprocess + python3 = os.path.join(sys.exec_prefix, 'bin', 'python3') + process = subprocess.run([python3, '-m', 'pip', + '--disable-pip-version-check', + 'install', '--user', 'pygdbmi' ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + print(process.stdout.decode('utf-8').strip()) + process.check_returncode() + from importlib import invalidate_caches + invalidate_caches() + from pygdbmi import gdbmiparser + return True + except Exception as e: + print(e) + + print("Qt summary provider requires the pygdbmi module. Please install\n" \ + "manually using '/usr/bin/pip3 install pygdbmi', and restart Xcode.") + return False + def __lldb_init_module(debugger, internal_dict): # Module is being imported in an LLDB session + if 'QT_CREATOR_LLDB_PROCESS' in os.environ: + # Let Qt Creator take care of its own dumper + return + + debug("Initializing module with", debugger) + + if not ensure_gdbmiparser(): + return + + if not __name__ == 'qt': + # Make available under global 'qt' name for consistency, + # and so we can refer to SyntheticChildrenProvider below. + internal_dict['qt'] = internal_dict[__name__] + dumper = SummaryDumper.initialize() type_category = 'Qt' @@ -2424,17 +2500,6 @@ def __lldb_init_module(debugger, internal_dict): # Synthetic children debugger.HandleCommand("type synthetic add -x '^Q.*$' -l %s -w %s" - % ("lldbbridge.SyntheticChildrenProvider", type_category)) + % ("qt.SyntheticChildrenProvider", type_category)) debugger.HandleCommand('type category enable %s' % type_category) - - if not __name__ == 'qt': - # Make available under global 'qt' name for consistency - internal_dict['qt'] = internal_dict[__name__] - - -if __name__ == "lldbbridge": - try: - theDumper = Dumper() - except Exception as error: - print('@\nstate="enginesetupfailed",error="{}"@\n'.format(error)) diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 5a406c03190..784691e81a7 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -328,10 +328,16 @@ def qdump__QDateTime(d, value): isValid = status & 0x08 else: dptr = d.extractPointer(value) - (msecs, status, offsetFromUtc, ref, timeZone) = d.split('qIIIp', dptr) + (_, status, msecs, offsetFromUtc, _, timeZone) = d.split('iIqII{QTimeZone}', dptr) spec = (status & 0x30) >> 4 isValid = True - + tzD = d.extractPointer(timeZone) + if tzD == 0: + timeZone = 'UTC' + else: + idAddr = tzD + 2 * d.ptrSize() + tzBa = d.encodeByteArray(idAddr, limit=100) + timeZone = tzBa d.putValue( '%s/%s/%s/%s/%s/%s' % (msecs, @@ -365,7 +371,7 @@ def qdump__QDateTime(d, value): tz = '' else: idBase = tzp + 2 * d.ptrSize() # [QSharedData] + [vptr] - elided, tz = d.encodeByteArray(idBase, limit=100) + tz = d.encodeByteArray(idBase, limit=100) d.putValue('%s/%s/%s/%s/%s/%s' % (msecs, spec, offset, tz, status, 0), 'datetimeinternal') else: diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 622bec4e5c1..5b8b699165c 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -646,7 +646,14 @@ def qdump__std__unique_ptr(d, value): if p == 0: d.putValue("(null)") else: - d.putItem(d.createValue(p, value.type[0])) + if d.isMsvcTarget(): + try: + d.putItem(value["_Mypair"]["_Myval2"]) + d.putValue(d.currentValue.value, d.currentValue.encoding) + except: + d.putItem(d.createValue(p, value.type[0])) + else: + d.putItem(d.createValue(p, value.type[0])) d.putBetterType(value.type) diff --git a/share/qtcreator/qml-type-descriptions/qbs-bundle.json b/share/qtcreator/qml-type-descriptions/qbs-bundle.json index 7f0c6886911..9b2f233a4c3 100644 --- a/share/qtcreator/qml-type-descriptions/qbs-bundle.json +++ b/share/qtcreator/qml-type-descriptions/qbs-bundle.json @@ -17,6 +17,7 @@ "qbs.Environment", "qbs.File", "qbs.FileInfo", + "qbs.Host", "qbs.ModUtils", "qbs.PathTools", "qbs.PkgConfig", diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml index 9d753700df1..b9729ba14e7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick3D/SpatialAudio/SpatialSoundSection.qml @@ -138,7 +138,7 @@ Section { PropertyLabel { text: qsTr("Manual Attenuation") - tooltip: qsTr("Set the manual attenuation factor if distanceModel is set to ManualAttenuation.") + tooltip: qsTr("Set the manual attenuation factor if distanceModel is set to ManualAttenuation.") } SecondColumnLayout { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml index e1def65263a..135c0f5cfa1 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/PaddingSection.qml @@ -67,7 +67,7 @@ Section { PropertyLabel { text: qsTr("Horizontal") - tooltip: qsTr("Sets the paddding on the left and right sides of the item.") + tooltip: qsTr("Sets the padding on the left and right sides of the item.") blockedByTemplate: !backendValues.leftPadding.isAvailable && !backendValues.rightPadding.isAvailable } diff --git a/share/qtcreator/qmldesigner/stateseditor/Main.qml b/share/qtcreator/qmldesigner/stateseditor/Main.qml index 1e9c3dccde0..b160bd5e86f 100644 --- a/share/qtcreator/qmldesigner/stateseditor/Main.qml +++ b/share/qtcreator/qmldesigner/stateseditor/Main.qml @@ -579,7 +579,7 @@ Rectangle { anchors.topMargin: root.topMargin anchors.leftMargin: root.leftMargin - ScrollBar.horizontal: StudioControls.TransientScrollBar { + Basic.ScrollBar.horizontal: StudioControls.TransientScrollBar { id: horizontalBar style: StudioTheme.Values.viewStyle parent: scrollView @@ -594,7 +594,7 @@ Rectangle { otherInUse: verticalBar.inUse } - ScrollBar.vertical: StudioControls.TransientScrollBar { + Basic.ScrollBar.vertical: StudioControls.TransientScrollBar { id: verticalBar style: StudioTheme.Values.viewStyle parent: scrollView diff --git a/share/qtcreator/snippets/cmake.xml b/share/qtcreator/snippets/cmake.xml new file mode 100644 index 00000000000..96b1d563ac6 --- /dev/null +++ b/share/qtcreator/snippets/cmake.xml @@ -0,0 +1,123 @@ + + + include(CMakePrintHelpers) +cmake_print_properties(TARGETS $targets$ PROPERTIES $properties$) + include(CMakePrintHelpers) +cmake_print_variables($variables$) + # https://doc.qt.io/qt-6/cmake-get-started.html +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 REQUIRED COMPONENTS Core) +qt_standard_project_setup() + +qt_add_executable($executable$ + main.cpp +) + +target_link_libraries($executable$ PRIVATE Qt6::Core) + # https://doc.qt.io/qt-6/cmake-get-started.html +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 REQUIRED COMPONENTS Widgets) +qt_standard_project_setup() + +qt_add_executable($executable$ + mainwindow.ui + mainwindow.cpp + main.cpp +) + +target_link_libraries($executable$ PRIVATE Qt6::Widgets) + +set_target_properties($executable$ PROPERTIES + WIN32_EXECUTABLE ON + MACOSX_BUNDLE ON +) + # https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#a-sample-find-module + +#[=======================================================================[.rst: +Find$package$ +------- + +Finds the $package$ library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``$package$::$package$`` + The $package$ library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``$package$_FOUND`` + True if the system has the $package$ library. +``$package$_VERSION`` + The version of the $package$ library which was found. +``$package$_INCLUDE_DIRS`` + Include directories needed to use $package$. +``$package$_LIBRARIES`` + Libraries needed to link to $package$. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``$package$_INCLUDE_DIR`` + The directory containing ``$header$.h``. +``$package$_LIBRARY`` + The path to the $library$ library. + +#]=======================================================================] + +find_package(PkgConfig) +pkg_check_modules(PC_$package$ QUIET $package$) + +find_path($package$_INCLUDE_DIR + NAMES $header$.h + PATHS \\${PC_$package$_INCLUDE_DIRS} + PATH_SUFFIXES $package$ +) + +find_library($package$_LIBRARY + NAMES $library$ + PATHS \\${PC_$package$_LIBRARY_DIRS} +) + +set($package$_VERSION \\${PC_$package$_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args($package$ + FOUND_VAR $package$_FOUND + REQUIRED_VARS + $package$_LIBRARY + $package$_INCLUDE_DIR + VERSION_VAR $package$_VERSION +) + +if($package$_FOUND) + set($package$_LIBRARIES \\${$package$_LIBRARY}) + set($package$_INCLUDE_DIRS \\${$package$_INCLUDE_DIR}) +endif() + +if($package$_FOUND AND NOT TARGET $package$::$package$) + add_library($package$::$package$ UNKNOWN IMPORTED) + set_target_properties($package$::$package$ PROPERTIES + IMPORTED_LOCATION "\\${$package$_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "\\${PC_$package$_CFLAGS_OTHER}" + INTERFACE_INCLUDE_DIRECTORIES "\\${$package$_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + $package$_INCLUDE_DIR + $package$_LIBRARY +) + diff --git a/share/qtcreator/snippets/test.xml b/share/qtcreator/snippets/test.xml new file mode 100644 index 00000000000..4c7f8d1c39b --- /dev/null +++ b/share/qtcreator/snippets/test.xml @@ -0,0 +1,58 @@ + + +TestCase { + name: "$TestCaseName$" + + function test_$TestFunctionName$() { + $$ + } +} + +TEST($TestSuite$, $TestName$) +{ + $$ +} + +TEST_F($TestFixtureName$, $TestName$) +{ + $$ +} + +INSTANTIATE_TEST_SUITE_P($InstantiationName$, $TestFixtureName$, $ParameterGenerator$); + +TEST_P($TestFixtureName$, $TestName$) +{ + $$ +} + +BOOST_AUTO_TEST_CASE($TestName$) +{ + $$ +} + +BOOST_AUTO_TEST_SUITE($SuiteName$) +BOOST_AUTO_TEST_CASE($TestName$) +{ + $$ +} +BOOST_AUTO_TEST_SUITE_END() + +TEST_CASE("$TestCaseName$") { + SECTION("$SectionName$") { + $$ + } +} + +SCENARIO("$ScenarioName$") { + GIVEN("$Initial$") { + $$ + WHEN("$Condition$") { + $$ + THEN("$Expectation$") { + $$ + } + } + } +} + + diff --git a/share/qtcreator/templates/wizards/autotest/boosttest/wizard.json b/share/qtcreator/templates/wizards/autotest/boosttest/wizard.json new file mode 100644 index 00000000000..01b79081268 --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/boosttest/wizard.json @@ -0,0 +1,207 @@ +{ + "version": 1, + "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], + "id": "M.BoostAutoTest", + "category": "I.TestProject", + "trDescription": "Creates a new unit test project using Boost. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", + "trDisplayName": "Boost Test Project", + "trDisplayCategory": "Test Project", + "icon": "../autotest.png", + "iconKind": "Themed", + "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", + + "options": + [ + { "key": "ProjectFilePath", + "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" + }, + { "key": "ProFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" + }, + { + "key": "QbsFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" + }, + { + "key": "CMakeFileName", + "value": "%{ProjectDirectory}/CMakeLists.txt" + }, + { "key": "IsTopLevelProject", + "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" + }, + { "key": "MainCppName", + "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" + }, + { + "key": "GUARD", + "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" + }, + { + "key": "TestCaseFileWithCppSuffix", + "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" + } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": + { + "trDescription": "This wizard creates a simple unit test project using Boost." + } + }, + { + "trDisplayName": "Project and Test Information", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "TestFrameWork", + "trDisplayName": "Test framework:", + "type": "ComboBox", + "data": + { + "index": 0, + "items": + [ + { + "trKey": "Boost Test (header only)", + "value": "BoostTest" + }, + { + "trKey": "Boost Test (shared libraries)", + "value": "BoostTest_dyn" + } + ] + } + }, + { + "name": "TestSuiteName", + "trDisplayName": "Test suite name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "TestCaseName", + "trDisplayName": "Test case name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "BoostIncDir", + "trDisplayName": "Boost include directory (optional):", + "visible": "%{JS: value('TestFrameWork') == 'BoostTest'}", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "BoostInstallDir", + "trDisplayName": "Boost install directory (optional):", + "visible": "%{JS: value('TestFrameWork') == 'BoostTest_dyn'}", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "CMake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + }, + { + "trKey": "Qbs", + "value": "qbs", + "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { + "projectFilePath": "%{ProjectFilePath}", + "requiredFeatures": [ "%{JS: value('BuildSystem') === 'qmake' ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' }" ] + } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../files/tst.pro", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.qbs", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.txt", + "target": "CMakeLists.txt", + "condition": "%{JS: value('BuildSystem') == 'cmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst_main.cpp", + "target": "%{MainCppName}", + "openInEditor": true + }, + { + "source": "../files/tst_src_boost.cpp", + "target": "%{TestCaseFileWithCppSuffix}", + "condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}" + }, + { + "source": "../../projects/git.ignore", + "target": ".gitignore", + "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" + } + ] + } + ] +} diff --git a/share/qtcreator/templates/wizards/autotest/catch/wizard.json b/share/qtcreator/templates/wizards/autotest/catch/wizard.json new file mode 100644 index 00000000000..1aa35ed0ec3 --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/catch/wizard.json @@ -0,0 +1,230 @@ +{ + "version": 1, + "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], + "id": "R.CatchAutoTest", + "category": "I.TestProject", + "trDescription": "Creates a new unit test project using Catch2. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", + "trDisplayName": "Catch2 Test Project", + "trDisplayCategory": "Test Project", + "icon": "../autotest.png", + "iconKind": "Themed", + "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", + + "options": + [ + { "key": "ProjectFilePath", + "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" + }, + { "key": "ProFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" + }, + { + "key": "QbsFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" + }, + { + "key": "CMakeFileName", + "value": "%{ProjectDirectory}/CMakeLists.txt" + }, + { "key": "IsTopLevelProject", + "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" + }, + { "key": "MainCppName", + "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" + }, + { + "key": "GUARD", + "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" + }, + { + "key": "TestCaseFileWithCppSuffix", + "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" + } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": + { + "trDescription": "This wizard creates a simple unit test project using Catch2." + } + }, + { + "trDisplayName": "Project and Test Information", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "TestFrameWork", + "trDisplayName": "Test framework:", + "type": "ComboBox", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "Catch2 v2 (header only)", + "value": "Catch2" + }, + { + "trKey": "Catch2 v3 (shared libraries)", + "value": "Catch2_dyn" + } + ] + } + }, + { + "name": "TestCaseName", + "trDisplayName": "Test case name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "CatchIncDir", + "trDisplayName": "Catch2 include directory (optional):", + "visible": "%{JS: value('TestFrameWork') === 'Catch2'}", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "CatchInstallDir", + "trDisplayName": "Catch2 install directory (optional):", + "visible": "%{JS: value('TestFrameWork') === 'Catch2_dyn'}", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "Catch2Main", + "trDisplayName": "Use own main", + "visible": "%{JS: '%{TestFrameWork}' === 'Catch2_dyn'}", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "Catch2NeedsQt", + "trDisplayName": "Use Qt libraries", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "CMake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + }, + { + "trKey": "Qbs", + "value": "qbs", + "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { + "projectFilePath": "%{ProjectFilePath}", + "requiredFeatures": [ "%{JS: (value('Catch2NeedsQt') == 'true' || value('BuildSystem') === 'qmake') ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' }" ] + } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../files/tst.pro", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/catch-common.pri", + "target": "catch-common.pri", + "openInEditor": false, + "condition": "%{JS: value('BuildSystem') == 'qmake' && '%{TestFrameWork}' == 'Catch2_dyn'}" + }, + { + "source": "../files/tst.qbs", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/catchCommon.js", + "target": "catchCommon.js", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false + }, + { + "source": "../files/tst.txt", + "target": "CMakeLists.txt", + "condition": "%{JS: value('BuildSystem') == 'cmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst_main.cpp", + "condition": "%{JS: '%{TestFrameWork}' == 'Catch2' || value('Catch2Main') == 'true'}", + "target": "%{MainCppName}", + "openInEditor": true + }, + { + "source": "../files/catch2_tst.cpp", + "target": "%{TestCaseFileWithCppSuffix}", + "openInEditor": true + }, + { + "source": "../../projects/git.ignore", + "target": ".gitignore", + "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" + } + ] + } + ] +} diff --git a/share/qtcreator/templates/wizards/autotest/files/catch-common.pri b/share/qtcreator/templates/wizards/autotest/files/catch-common.pri new file mode 100644 index 00000000000..69676992182 --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/files/catch-common.pri @@ -0,0 +1,21 @@ +isEmpty(CATCH2_INSTALL_DIR):CATCH2_INSTALL_DIR=$$(CATCH2_INSTALL_DIR) + +isEmpty(CATCH2_INSTALL_DIR) { + CATCH2_INSTALL_DIR = "%{CatchInstallDir}" # set by QC + !isEmpty(CATCH2_INSTALL_DIR) { + warning("Using Catch2 installation specified at Qt Creator wizard.") + message("Set CATCH2_INSTALL_DIR as environment variable or qmake variable to get rid of this message") + } else { + message("Using Catch2 from system - set CATCH2_INSTALL_DIR is it cannot be found automatically.") + } +} + +!isEmpty(CATCH2_INSTALL_DIR): { + INCLUDEPATH *= "$$CATCH2_INSTALL_DIR/include" + + equals(CATCH2_MAIN, 0): LIBS *= -L"$$CATCH2_INSTALL_DIR/lib" -lCatch2Main -lCatch2 + else: LIBS *= -L"$$CATCH2_INSTALL_DIR/lib" -lCatch2 +} else { + equals(CATCH2_MAIN, 0): LIBS *= -lCatch2Main -lCatch2 + else: LIBS *= -lCatch2 +} diff --git a/share/qtcreator/templates/wizards/autotest/files/catch2_tst.cpp b/share/qtcreator/templates/wizards/autotest/files/catch2_tst.cpp index 604b339e1cc..191f8fdeeaa 100644 --- a/share/qtcreator/templates/wizards/autotest/files/catch2_tst.cpp +++ b/share/qtcreator/templates/wizards/autotest/files/catch2_tst.cpp @@ -1,4 +1,8 @@ +@if "%{TestFrameWork}" == "Catch2" #include +@else +#include +@endif TEST_CASE("My first test with Catch2", "[fancy]") { diff --git a/share/qtcreator/templates/wizards/autotest/files/catchCommon.js b/share/qtcreator/templates/wizards/autotest/files/catchCommon.js new file mode 100644 index 00000000000..c8f633ab799 --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/files/catchCommon.js @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +var File = require("qbs.File") +var FileInfo = require("qbs.FileInfo") + +function getChildPath(qbs, baseFolder, childFolder) +{ + if (!baseFolder) + return []; + + var childPath = FileInfo.joinPaths(baseFolder, childFolder); + if (File.exists(childPath)) + return [childPath]; + return []; +} diff --git a/share/qtcreator/templates/wizards/autotest/files/googlecommon.js b/share/qtcreator/templates/wizards/autotest/files/googlecommon.js index 7f754cc9e86..6e09b3ad5e9 100644 --- a/share/qtcreator/templates/wizards/autotest/files/googlecommon.js +++ b/share/qtcreator/templates/wizards/autotest/files/googlecommon.js @@ -16,6 +16,17 @@ var File = require("qbs.File") var FileInfo = require("qbs.FileInfo") +function getChildPath(qbs, baseFolder, childFolder) +{ + if (!baseFolder) + return []; + + var childPath = FileInfo.joinPaths(baseFolder, childFolder); + if (File.exists(childPath)) + return [childPath]; + return []; +} + function getGTestDir(qbs, str) { if (!str) { if (qbs.hostOS.contains("linux") && File.exists("/usr/src/gtest")) diff --git a/share/qtcreator/templates/wizards/autotest/files/gtest_dependency.pri b/share/qtcreator/templates/wizards/autotest/files/gtest_dependency.pri index c064ef6242a..8d0e458c105 100644 --- a/share/qtcreator/templates/wizards/autotest/files/gtest_dependency.pri +++ b/share/qtcreator/templates/wizards/autotest/files/gtest_dependency.pri @@ -1,16 +1,17 @@ isEmpty(GOOGLETEST_DIR):GOOGLETEST_DIR=$$(GOOGLETEST_DIR) isEmpty(GOOGLETEST_DIR) { - GOOGLETEST_DIR = %{GTestRepository} + GOOGLETEST_DIR = "%{GTestBaseFolder}" !isEmpty(GOOGLETEST_DIR) { warning("Using googletest src dir specified at Qt Creator wizard") message("set GOOGLETEST_DIR as environment variable or qmake variable to get rid of this message") } } +@if "%{TestFrameWork}" == "GTest" !isEmpty(GOOGLETEST_DIR): { - GTEST_SRCDIR = $$GOOGLETEST_DIR/googletest - GMOCK_SRCDIR = $$GOOGLETEST_DIR/googlemock + GTEST_SRCDIR = "$$GOOGLETEST_DIR/googletest" + GMOCK_SRCDIR = "$$GOOGLETEST_DIR/googlemock" } else: unix { exists(/usr/src/gtest):GTEST_SRCDIR=/usr/src/gtest exists(/usr/src/gmock):GMOCK_SRCDIR=/usr/src/gmock @@ -19,11 +20,6 @@ isEmpty(GOOGLETEST_DIR) { requires(exists($$GTEST_SRCDIR):exists($$GMOCK_SRCDIR)) -@if "%{GTestCXX11}" == "true" -DEFINES += \\ - GTEST_LANG_CXX11 -@endif - !isEmpty(GTEST_SRCDIR) { INCLUDEPATH *= \\ $$GTEST_SRCDIR \\ @@ -41,3 +37,13 @@ DEFINES += \\ SOURCES += \\ $$GMOCK_SRCDIR/src/gmock-all.cc } +@endif +@if "%{TestFrameWork}" == "GTest_dyn" +!isEmpty(GOOGLETEST_DIR): { + INCLUDEPATH *= "$$GOOGLETEST_DIR/include" + + LIBS *= -L"$$GOOGLETEST_DIR/lib" -lgtest -lgmock +} else { + LIBS *= -lgtest -lgmock +} +@endif diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.pro b/share/qtcreator/templates/wizards/autotest/files/tst.pro index 1a4b00a5e8e..075d87443df 100644 --- a/share/qtcreator/templates/wizards/autotest/files/tst.pro +++ b/share/qtcreator/templates/wizards/autotest/files/tst.pro @@ -33,15 +33,11 @@ SOURCES += \\ setup.cpp @endif @endif -@if "%{TestFrameWork}" == "GTest" +@if "%{TestFrameWork}" == "GTest" || "%{TestFrameWork}" == "GTest_dyn" include(gtest_dependency.pri) TEMPLATE = app -@if "%{GTestCXX11}" == "true" -CONFIG += console c++11 -@else -CONFIG += console -@endif +CONFIG += console c++14 CONFIG -= app_bundle CONFIG += thread CONFIG -= qt @@ -99,7 +95,7 @@ SOURCES += \\ %{MainCppName} \\ %{TestCaseFileWithCppSuffix} @endif -@if "%{TestFrameWork}" == "Catch2" +@if "%{TestFrameWork}" == "Catch2" || "%{TestFrameWork}" == "Catch2_dyn" TEMPLATE = app @if "%{Catch2NeedsQt}" == "true" QT += gui @@ -110,7 +106,8 @@ CONFIG += console @endif CONFIG += c++11 - +@endif +@if "%{TestFrameWork}" == "Catch2" isEmpty(CATCH_INCLUDE_DIR): CATCH_INCLUDE_DIR=$$(CATCH_INCLUDE_DIR) @if "%{CatchIncDir}" != "" # set by Qt Creator wizard @@ -123,6 +120,21 @@ isEmpty(CATCH_INCLUDE_DIR): { } SOURCES += \\ - main.cpp \\ + %{MainCppName} \\ %{TestCaseFileWithCppSuffix} @endif +@if "%{TestFrameWork}" == "Catch2_dyn" +@if "%{Catch2Main}" == "true" +SOURCES = %{TestCaseFileWithCppSuffix} \\ + %{MainCppName} + +CATCH2_MAIN=0 + +@else +SOURCES = %{TestCaseFileWithCppSuffix} + +CATCH2_MAIN=1 + +@endif +include(catch-common.pri) +@endif diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.qbs b/share/qtcreator/templates/wizards/autotest/files/tst.qbs index dda4df02cc9..990219a32f5 100644 --- a/share/qtcreator/templates/wizards/autotest/files/tst.qbs +++ b/share/qtcreator/templates/wizards/autotest/files/tst.qbs @@ -1,5 +1,5 @@ import qbs -@if "%{TestFrameWork}" == "GTest" +@if "%{TestFrameWork}" == "GTest" || "%{TestFrameWork}" == "GTest_dyn" import qbs.Environment import "googlecommon.js" as googleCommon @endif @@ -16,6 +16,12 @@ import qbs.FileInfo import qbs.Environment import qbs.File @endif +@if "%{TestFrameWork}" == "Catch2_dyn" +import qbs.Environment +import qbs.File + +import "catchCommon.js" as catchCommon +@endif CppApplication { @if "%{TestFrameWork}" == "QtTest" @@ -32,34 +38,36 @@ CppApplication { consoleApplication: true @endif -@if "%{TestFrameWork}" == "GTest" +@if "%{TestFrameWork}" == "GTest" || "%{TestFrameWork}" == "GTest_dyn" property string googletestDir: { if (typeof Environment.getEnv("GOOGLETEST_DIR") === 'undefined') { - if ("%{GTestRepository}" === "" && googleCommon.getGTestDir(qbs, undefined) !== "") { + if ("%{GTestBaseFolder}" === "" && googleCommon.getGTestDir(qbs, undefined) !== "") { console.warn("Using googletest from system") } else { console.warn("Using googletest src dir specified at Qt Creator wizard") console.log("set GOOGLETEST_DIR as environment variable or Qbs property to get rid of this message") } - return "%{GTestRepository}" + return "%{GTestBaseFolder}" } else { return Environment.getEnv("GOOGLETEST_DIR") } } -@if "%{GTestCXX11}" == "true" - cpp.cxxLanguageVersion: "c++11" - cpp.defines: [ "GTEST_LANG_CXX11" ] -@endif + cpp.cxxLanguageVersion: "c++14" cpp.dynamicLibraries: { +@if "%{TestFrameWork}" == "GTest" + var tmp = []; +@else + var tmp = ["gtest", "gmock"]; +@endif if (qbs.hostOS.contains("windows")) { - return []; + return tmp; } else { - return [ "pthread" ]; + return tmp.concat([ "pthread" ]); } } - - +@endif +@if "%{TestFrameWork}" == "GTest" cpp.includePaths: [].concat(googleCommon.getGTestIncludes(qbs, googletestDir)) .concat(googleCommon.getGMockIncludes(qbs, googletestDir)) @@ -69,6 +77,15 @@ CppApplication { ].concat(googleCommon.getGTestAll(qbs, googletestDir)) .concat(googleCommon.getGMockAll(qbs, googletestDir)) @endif +@if "%{TestFrameWork}" == "GTest_dyn" + cpp.includePaths: [].concat(googleCommon.getChildPath(qbs, googletestDir, "include")); + cpp.libraryPaths: googleCommon.getChildPath(qbs, googletestDir, "lib") + + files: [ + "%{MainCppName}", + "%{TestCaseFileGTestWithCppSuffix}", + ] +@endif @if "%{TestFrameWork}" == "QtQuickTest" Depends { name: "cpp" } Depends { name: "Qt.core" } @@ -186,5 +203,40 @@ CppApplication { "%{TestCaseFileWithCppSuffix}", ] @endif +@if "%{TestFrameWork}" == "Catch2_dyn" + property string catch2Dir: { + if (typeof Environment.getEnv("CATCH_INSTALL_DIR") === 'undefined') { + if ("%{CatchInstallDir}" === "") { + console.warn("Using Catch2 from system") + } else { + console.warn("Using Catch2 install dir specified at Qt Creator wizard") + console.log("set CATCH_INSTALL_DIR as environment variable or Qbs property to get rid of this message") + return "%{CatchInstallDir}"; + } + return ""; + } else { + return Environment.getEnv("CATCH_INSTALL_DIR"); + } + } + + Properties { + condition: catch2Dir !== "" && File.exists(catch2Dir) + cpp.includePaths: [].concat(catchCommon.getChildPath(qbs, catch2Dir, "include")); + cpp.libraryPaths: catchCommon.getChildPath(qbs, catch2Dir, "lib") + } +@if "%{Catch2Main}" == "false" + cpp.dynamicLibraries: base.concat(["Catch2Main", "Catch2"]) +@else + cpp.dynamicLibraries: base.concat(["Catch2"]) +@endif + + files: [ +@if "%{Catch2Main}" == "true" + "%{MainCppName}", +@endif + "%{TestCaseFileWithCppSuffix}", + ] + +@endif } diff --git a/share/qtcreator/templates/wizards/autotest/files/tst.txt b/share/qtcreator/templates/wizards/autotest/files/tst.txt index 739ec2c26a8..f0eeb813392 100644 --- a/share/qtcreator/templates/wizards/autotest/files/tst.txt +++ b/share/qtcreator/templates/wizards/autotest/files/tst.txt @@ -70,50 +70,67 @@ target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::QuickTest) @endif @if "%{TestFrameWork}" == "GTest" - -@if "%{GTestCXX11}" == "true" -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_definitions(-DGTEST_LANGUAGE_CXX11) -@endif + +if ($ENV{GOOGLETEST_DIR}) + get_filename_component(_GTEST_BASE $ENV{GOOGLETEST_DIR} REALPATH) +elseif (EXISTS "%{GTestBaseFolder}") # set by QC + get_filename_component(_GTEST_BASE "%{GTestBaseFolder}" REALPATH) # set by QC +elseif (UNIX AND EXISTS "/usr/src/gtest") + set(_GTEST_BASE "/usr/src/gtest") +endif() + +if (_GTEST_BASE AND NOT EXISTS "${_GTEST_BASE}/googletest/src/gtest-all.cc" + OR NOT EXISTS "${_GTEST_BASE}/googlemock/src/gmock-all.cc") + message(ERROR "Missing source components (gtest-all.cc and/or gmock-all.cc).") +endif() find_package(Threads REQUIRED) -if ($ENV{GOOGLETEST_DIR}) - set(GOOGLETEST_DIR $ENV{GOOGLETEST_DIR}) -else () - if (NOT "%{GTestRepository}" STREQUAL "") - message(WARNING "Using googletest src dir specified at Qt Creator wizard") - endif () - set(GOOGLETEST_DIR "%{GTestRepository}") -endif () -if (EXISTS ${GOOGLETEST_DIR}) - set(GTestSrc ${GOOGLETEST_DIR}/googletest) - set(GMockSrc ${GOOGLETEST_DIR}/googlemock) -elseif (UNIX AND EXISTS /usr/src/gtest) - set(GTestSrc /usr/src/gtest) - message(WARNING "Using gtest from system") - if (EXISTS /usr/src/gmock) - set(GMockSrc /usr/src/gmock) - endif () -else () - message( FATAL_ERROR "No googletest src dir found - set GOOGLETEST_DIR to enable!") -endif () - -set(GTestFiles ${GTestSrc}/src/gtest-all.cc) -set(GTestIncludes ${GTestSrc} ${GTestSrc}/include) -if (NOT ${GMockSrc} STREQUAL "") - list(APPEND GTestFiles ${GMockSrc}/src/gmock-all.cc) - list(APPEND GTestIncludes ${GMockSrc} ${GMockSrc}/include) -endif () - -include_directories(${GTestIncludes}) - +include_directories( + ${_GTEST_BASE}/googletest ${_GTEST_BASE}/googletest/include + ${_GTEST_BASE}/googlemock ${_GTEST_BASE}/googlemock/include +) add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix} - ${GTestFiles}) + ${_GTEST_BASE}/googletest/src/gtest-all.cc + ${_GTEST_BASE}/googlemock/src/gmock-all.cc + ) + add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) target_link_libraries(%{TestCaseName} PRIVATE Threads::Threads) +@endif +@if "%{TestFrameWork}" == "GTest_dyn" +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +if ($ENV{GOOGLETEST_DIR}) + get_filename_component(_GTEST_BASE $ENV{GOOGLETEST_DIR} REALPATH) +elseif (EXISTS "%{GTestBaseFolder}") # set by QC + get_filename_component(_GTEST_BASE "%{GTestBaseFolder}" REALPATH) # set by QC +endif() + +if (NOT GTEST_ROOT) + if (_GTEST_BASE) + message("Setting GTEST_ROOT to ${_GTEST_BASE}") + set(GTEST_ROOT ${_GTEST_BASE}) + else() + message("No GTEST_ROOT specified - using system defaults.") + endif() +endif() + +find_package(GTest REQUIRED) +if (NOT GTest_FOUND) + message (FATAL_ERROR "No GTest Found") +endif() + +add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix}) +add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) + +target_link_libraries(%{TestCaseName} PRIVATE GTest::GTest) +if (GMock_FOUND) + target_link_libraries(%{TestCaseName} INTERFACE GTest::GMock) +endif() @endif @if "%{TestFrameWork}" == "BoostTest" set(CMAKE_CXX_STANDARD 11) @@ -153,7 +170,7 @@ find_package(Boost COMPONENTS unit_test_framework REQUIRED) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileGTestWithCppSuffix}) +add_executable(%{TestCaseName} %{MainCppName} %{TestCaseFileWithCppSuffix}) add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) if (Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) @@ -168,7 +185,8 @@ find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) @endif -add_executable(${PROJECT_NAME} %{TestCaseFileWithCppSuffix} main.cpp) +add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix} %{MainCppName}) +add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) @if "%{Catch2NeedsQt}" == "true" target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui) @@ -185,3 +203,43 @@ elseif (EXISTS ${CATCH_INCLUDE_DIR}) include_directories(${CATCH_INCLUDE_DIR}) endif () @endif +@if "%{TestFrameWork}" == "Catch2_dyn" +SET(CMAKE_CXX_STANDARD 11) + +if ($ENV{CATCH2_INSTALL_DIR}) + get_filename_component(_CATCH2_BASE $ENV{CATCH2_INSTALL_DIR} REALPATH) +elseif (EXISTS "%{CatchInstallDir}") # set by QC + get_filename_component(_CATCH2_BASE "%{CatchInstallDir}" REALPATH) # set by QC +endif() + +if (NOT Catch2_DIR) + if (_CATCH2_BASE) + if (EXISTS "${_CATCH2_BASE}/lib/cmake/Catch2") + set(_CATCH2_BASE "${_CATCH2_BASE}/lib/cmake/Catch2") + endif() + message("Setting Catch2_DIR to ${_CATCH2_BASE}") + set(Catch2_DIR ${_CATCH2_BASE}) + else() + message("No Catch2_DIR specified - using system defaults.") + endif() +endif() + +find_package(Catch2 3 REQUIRED) +@if "%{Catch2NeedsQt}" == "true" +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Gui) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui) +@endif + +@if "%{Catch2Main}" == "false" +add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix}) +target_link_libraries(%{TestCaseName} PRIVATE Catch2::Catch2WithMain) +@else +add_executable(%{TestCaseName} %{TestCaseFileWithCppSuffix} %{MainCppName}) +target_link_libraries(%{TestCaseName} PRIVATE Catch2::Catch2) +@endif +@if "%{Catch2NeedsQt}" == "true" +target_link_libraries(%{TestCaseName} PRIVATE Qt${QT_VERSION_MAJOR}::Gui) +@endif + +add_test(NAME %{TestCaseName} COMMAND %{TestCaseName}) +@endif diff --git a/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp b/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp index 1c915089d6f..e2aabc9a87b 100644 --- a/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp +++ b/share/qtcreator/templates/wizards/autotest/files/tst_main.cpp @@ -9,7 +9,7 @@ QUICK_TEST_MAIN_WITH_SETUP(example, Setup) QUICK_TEST_MAIN(example) @endif @endif -@if "%{TestFrameWork}" == "GTest" +@if "%{TestFrameWork}" == "GTest" || "%{TestFrameWork}" == "GTest_dyn" %{Cpp:LicenseTemplate}\ #include @@ -55,3 +55,22 @@ int main(int argc, char** argv) } @endif @endif +@if "%{TestFrameWork}" == "Catch2_dyn" && "%{Catch2Main}" == "true" +#include +@if "%{Catch2NeedsQt}" == "true" +#include +@endif + +int main( int argc, char* argv[] ) { + // your setup ... +@if "%{Catch2NeedsQt}" == "true" + QGuiApplication app(argc, argv); +@endif + + int result = Catch::Session().run( argc, argv ); + + // your clean-up... + + return result; +} +@endif diff --git a/share/qtcreator/templates/wizards/autotest/gtest/wizard.json b/share/qtcreator/templates/wizards/autotest/gtest/wizard.json new file mode 100644 index 00000000000..7d8b88b552e --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/gtest/wizard.json @@ -0,0 +1,223 @@ +{ + "version": 1, + "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], + "id": "G.AutoTest", + "category": "I.TestProject", + "trDescription": "Creates a new unit test project using Google Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", + "trDisplayName": "Google Test Project", + "trDisplayCategory": "Test Project", + "icon": "../autotest.png", + "iconKind": "Themed", + "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", + + "options": + [ + { "key": "ProjectFilePath", + "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" + }, + { "key": "ProFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" + }, + { + "key": "QbsFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" + }, + { + "key": "CMakeFileName", + "value": "%{ProjectDirectory}/CMakeLists.txt" + }, + { "key": "IsTopLevelProject", + "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" + }, + { "key": "MainCppName", + "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" + }, + { + "key": "TestCaseFileGTestWithCppSuffix", + "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" + }, + { + "key": "GTestBaseFolder", + "value": "%{JS: value('TestFrameWork') === 'GTest' ? value('GTestRepository') : value('GTestInstallFolder')}" + }, + { + "key": "GUARD", + "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" + } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": + { + "trDescription": "This wizard creates a simple unit test project using Google Test." + } + }, + { + "trDisplayName": "Project and Test Information", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "TestFrameWork", + "trDisplayName": "Test framework:", + "type": "ComboBox", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "Google Test (header only)", + "value": "GTest" + }, + { + "trKey": "Google Test (shared libraries)", + "value": "GTest_dyn" + } + ] + } + }, + { + "name": "TestSuiteName", + "trDisplayName": "Test suite name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "TestCaseName", + "trDisplayName": "Test case name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "GTestRepository", + "visible": "%{JS: '%{TestFrameWork}' === 'GTest'}", + "trDisplayName": "Googletest source directory (optional):", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "GTestInstallFolder", + "visible": "%{JS: '%{TestFrameWork}' === 'GTest_dyn'}", + "trDisplayName": "Googletest install directory (optional):", + "mandatory": false, + "type": "PathChooser", + "data": { + "kind": "existingDirectory" + } + }, + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "CMake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + }, + { + "trKey": "Qbs", + "value": "qbs", + "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { + "projectFilePath": "%{ProjectFilePath}", + "requiredFeatures": [ "%{JS: value('BuildSystem') === 'qmake' ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' }" ] + } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../files/gtest_dependency.pri", + "target": "gtest_dependency.pri", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false + }, + { + "source": "../files/googlecommon.js", + "target": "googlecommon.js", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false + }, + { + "source": "../files/tst.pro", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.qbs", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.txt", + "target": "CMakeLists.txt", + "condition": "%{JS: value('BuildSystem') == 'cmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst_src_gt.cpp", + "target": "%{TestCaseFileGTestWithCppSuffix}", + "openInEditor": true + }, + { + "source": "../files/tst_main.cpp", + "target": "%{MainCppName}", + "openInEditor": true + }, + { + "source": "../../projects/git.ignore", + "target": ".gitignore", + "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" + } + ] + } + ] +} diff --git a/share/qtcreator/templates/wizards/autotest/qttest/wizard.json b/share/qtcreator/templates/wizards/autotest/qttest/wizard.json new file mode 100644 index 00000000000..0236a24a5ab --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/qttest/wizard.json @@ -0,0 +1,179 @@ +{ + "version": 1, + "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], + "id": "A.QtTestAutoTest", + "category": "I.TestProject", + "trDescription": "Creates a new unit test project using Qt Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", + "trDisplayName": "Qt Test Project", + "trDisplayCategory": "Test Project", + "icon": "../autotest.png", + "iconKind": "Themed", + "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", + + "options": + [ + { "key": "TestFrameWork", + "value": "QtTest" + }, + { "key": "ProjectFilePath", + "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" + }, + { "key": "ProFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" + }, + { + "key": "QbsFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" + }, + { + "key": "CMakeFileName", + "value": "%{ProjectDirectory}/CMakeLists.txt" + }, + { "key": "IsTopLevelProject", + "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" + }, + { + "key": "GUARD", + "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" + }, + { + "key": "TestCaseFileWithCppSuffix", + "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" + } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": + { + "trDescription": "This wizard creates a simple unit test project using Qt Test." + } + }, + { + "trDisplayName": "Project and Test Information", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "TestCaseName", + "trDisplayName": "Test case name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "RequireApplication", + "trDisplayName": "Requires QApplication", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "RequireGUI", + "trDisplayName": "GUI Application", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "GenerateInitAndCleanup", + "trDisplayName": "Generate initialization and cleanup code", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "CMake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + }, + { + "trKey": "Qbs", + "value": "qbs", + "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { + "projectFilePath": "%{ProjectFilePath}", + "requiredFeatures": [ "QtSupport.Wizards.FeatureQt", "DeviceType.Desktop" ] + } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../files/tst.pro", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.qbs", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.txt", + "target": "CMakeLists.txt", + "condition": "%{JS: value('BuildSystem') == 'cmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst_src.cpp", + "target": "%{TestCaseFileWithCppSuffix}", + "openInEditor": true + }, + { + "source": "../../projects/git.ignore", + "target": ".gitignore", + "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" + } + ] + } + ] +} diff --git a/share/qtcreator/templates/wizards/autotest/quicktest/wizard.json b/share/qtcreator/templates/wizards/autotest/quicktest/wizard.json new file mode 100644 index 00000000000..bbcf7c41479 --- /dev/null +++ b/share/qtcreator/templates/wizards/autotest/quicktest/wizard.json @@ -0,0 +1,191 @@ +{ + "version": 1, + "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], + "id": "C.QuickAutoTest", + "category": "I.TestProject", + "trDescription": "Creates a new unit test project using Qt Quick Test. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", + "trDisplayName": "Qt Quick Test Project", + "trDisplayCategory": "Test Project", + "icon": "../autotest.png", + "iconKind": "Themed", + "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", + + "options": + [ + { "key": "TestFrameWork", + "value": "QtQuickTest" + }, + { "key": "ProjectFilePath", + "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" + }, + { "key": "ProFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" + }, + { + "key": "QbsFileName", + "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" + }, + { + "key": "CMakeFileName", + "value": "%{ProjectDirectory}/CMakeLists.txt" + }, + { "key": "IsTopLevelProject", + "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" + }, + { "key": "MainCppName", + "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" + }, + { + "key": "GUARD", + "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" + }, + { + "key": "TestCaseFileWithQmlSuffix", + "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml' }" + } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": + { + "trDescription": "This wizard creates a simple unit test project using Qt Quick Test." + } + }, + { + "trDisplayName": "Project and Test Information", + "trShortTitle": "Details", + "typeId": "Fields", + "data": + [ + { + "name": "TestCaseName", + "trDisplayName": "Test case name:", + "mandatory": true, + "type": "LineEdit", + "data": { "validator": "^[a-zA-Z_0-9]+$" } + }, + { + "name": "UseSetupCode", + "trDisplayName": "Generate setup code", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "GenerateInitAndCleanup", + "trDisplayName": "Generate initialization and cleanup code", + "type": "CheckBox", + "data": { + "checked": false + } + }, + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "CMake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + }, + { + "trKey": "Qbs", + "value": "qbs", + "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{IsTopLevelProject}", + "data": { + "projectFilePath": "%{ProjectFilePath}", + "requiredFeatures": [ "QtSupport.Wizards.FeatureQtQuick.2", "DeviceType.Desktop" ] + } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "../files/tst.pro", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.qbs", + "target": "%{ProjectFilePath}", + "condition": "%{JS: value('BuildSystem') == 'qbs'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst.txt", + "target": "CMakeLists.txt", + "condition": "%{JS: value('BuildSystem') == 'cmake'}", + "openInEditor": false, + "openAsProject": true + }, + { + "source": "../files/tst_main.cpp", + "target": "%{MainCppName}", + "openInEditor": true + }, + { + "source": "../files/tst_qml.tmpl", + "target": "%{TestCaseFileWithQmlSuffix}", + "openInEditor": true + }, + { + "source": "../files/setup.cpp", + "target": "setup.cpp", + "condition": "%{JS: value('UseSetupCode')}", + "openInEditor": true + }, + { + "source": "../files/setup.h", + "target": "setup.h", + "condition": "%{JS: value('UseSetupCode')}", + "openInEditor": true + }, + { + "source": "../../projects/git.ignore", + "target": ".gitignore", + "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" + } + ] + } + ] +} diff --git a/share/qtcreator/templates/wizards/autotest/wizard.json b/share/qtcreator/templates/wizards/autotest/wizard.json deleted file mode 100644 index 6f19d679eac..00000000000 --- a/share/qtcreator/templates/wizards/autotest/wizard.json +++ /dev/null @@ -1,357 +0,0 @@ -{ - "version": 1, - "supportedProjectTypes": [ "CMakeProjectManager.CMakeProject", "Qbs.QbsProject", "Qt4ProjectManager.Qt4Project" ], - "id": "R.AutoTest", - "category": "H.Project", - "trDescription": "Creates a new unit test project. Unit tests allow you to verify that the code is fit for use and that there are no regressions.", - "trDisplayName": "Auto Test Project", - "trDisplayCategory": "Other Project", - "icon": "autotest.png", - "iconKind": "Themed", - "featuresRequired": [ "QtSupport.Wizards.FeatureDesktop" ], - "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", - - "options": - [ - { "key": "ProjectFilePath", - "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFileName') : (value('BuildSystem') == 'qbs' ? value('QbsFileName') : value('CMakeFileName')) }" - }, - { "key": "ProFileName", - "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" - }, - { - "key": "QbsFileName", - "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'qbs')}" - }, - { - "key": "CMakeFileName", - "value": "%{ProjectDirectory}/CMakeLists.txt" - }, - { "key": "IsTopLevelProject", - "value": "%{JS: !'%{Exists:ProjectExplorer.Profile.Ids}' }" - }, - { "key": "MainCppName", - "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src') }" - }, - { - "key": "TestCaseFileGTestWithCppSuffix", - "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" - }, - { - "key": "GUARD", - "value": "%{JS: value('TestCaseFileWithHeaderSuffix').toUpperCase().replace('.', '_') }" - }, - { - "key": "TestCaseFileWithCppSuffix", - "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src') }" - }, - { - "key": "TestCaseFileWithQmlSuffix", - "value": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml' }" - } - ], - - "pages": - [ - { - "trDisplayName": "Project Location", - "trShortTitle": "Location", - "typeId": "Project", - "data": - { - "trDescription": "This wizard creates a simple unit test project." - } - }, - { - "trDisplayName": "Project and Test Information", - "trShortTitle": "Details", - "typeId": "Fields", - "data": - [ - { - "name": "TestFrameWork", - "trDisplayName": "Test framework:", - "type": "ComboBox", - "data": - { - "index": 0, - "items": - [ - { - "trKey": "Qt Test", - "value": "QtTest" - }, - { - "trKey": "Google Test", - "value": "GTest" - }, - { - "trKey": "Qt Quick Test", - "value": "QtQuickTest" - }, - { - "trKey": "Boost Test (header only)", - "value": "BoostTest" - }, - { - "trKey": "Boost Test (shared libraries)", - "value": "BoostTest_dyn" - }, - { - "trKey": "Catch2", - "value": "Catch2" - } - - ] - } - }, - { - "name": "RequireGUI", - "trDisplayName": "GUI Application", - "visible": "%{JS: value('TestFrameWork') === 'QtTest'}", - "type": "CheckBox", - "data": { - "checked": false - } - }, - { - "name": "TestSuiteName", - "trDisplayName": "Test suite name:", - "visible": "%{JS: ['BoostTest', 'BoostTest_dyn', 'GTest'].indexOf(value('TestFrameWork')) >= 0}", - "mandatory": true, - "type": "LineEdit", - "data": { "validator": "^[a-zA-Z_0-9]+$" } - }, - { - "name": "TestCaseName", - "trDisplayName": "Test case name:", - "mandatory": true, - "type": "LineEdit", - "data": { "validator": "^[a-zA-Z_0-9]+$" } - }, - { - "name": "RequireApplication", - "trDisplayName": "Requires QApplication", - "visible": "%{JS: value('TestFrameWork') === 'QtTest'}", - "type": "CheckBox", - "data": { - "checked": false - } - }, - { - "name": "UseSetupCode", - "trDisplayName": "Generate setup code", - "visible": "%{JS: value('TestFrameWork') === 'QtQuickTest'}", - "type": "CheckBox", - "data": { - "checked": false - } - }, - { - "name": "GenerateInitAndCleanup", - "trDisplayName": "Generate initialization and cleanup code", - "visible": "%{JS: [ 'QtTest', 'QtQuickTest' ].indexOf(value('TestFrameWork')) >= 0 }", - "type": "CheckBox", - "data": { - "checked": false - } - }, - { - "name": "GTestCXX11", - "trDisplayName": "Enable C++11", - "visible": "%{JS: value('TestFrameWork') === 'GTest'}", - "type": "CheckBox", - "data": { - "checked": false - } - }, - { - "name": "GTestRepository", - "trDisplayName": "Googletest source directory (optional):", - "visible": "%{JS: value('TestFrameWork') === 'GTest'}", - "mandatory": false, - "type": "PathChooser", - "data": { - "kind": "existingDirectory" - } - }, - { - "name": "BoostIncDir", - "trDisplayName": "Boost include directory (optional):", - "visible": "%{JS: value('TestFrameWork') == 'BoostTest'}", - "mandatory": false, - "type": "PathChooser", - "data": { - "kind": "existingDirectory" - } - }, - { - "name": "BoostInstallDir", - "trDisplayName": "Boost install directory (optional):", - "visible": "%{JS: value('TestFrameWork') == 'BoostTest_dyn'}", - "mandatory": false, - "type": "PathChooser", - "data": { - "kind": "existingDirectory" - } - }, - { - "name": "CatchIncDir", - "trDisplayName": "Catch2 include directory (optional):", - "visible": "%{JS: value('TestFrameWork') === 'Catch2'}", - "mandatory": false, - "type": "PathChooser", - "data": { - "kind": "existingDirectory" - } - }, - { - "name": "Catch2NeedsQt", - "trDisplayName": "Use Qt libraries", - "visible": "%{JS: '%{TestFrameWork}' === 'Catch2'}", - "type": "CheckBox", - "data": { - "checked": true - } - }, - { - "name": "BuildSystem", - "trDisplayName": "Build system:", - "type": "ComboBox", - "persistenceKey": "BuildSystemType", - "data": - { - "index": 1, - "items": - [ - { - "trKey": "qmake", - "value": "qmake", - "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" - }, - { - "trKey": "CMake", - "value": "cmake", - "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" - }, - { - "trKey": "Qbs", - "value": "qbs", - "condition": "%{JS: value('Plugins').indexOf('QbsProjectManager') >= 0}" - } - ] - } - } - ] - }, - { - "trDisplayName": "Kit Selection", - "trShortTitle": "Kits", - "typeId": "Kits", - "enabled": "%{IsTopLevelProject}", - "data": { - "projectFilePath": "%{ProjectFilePath}", - "requiredFeatures": [ "%{JS: (value('TestFrameWork') === 'QtQuickTest' ? 'QtSupport.Wizards.FeatureQtQuick.2' : ((value('BuildSystem') === 'qmake' || value('TestFrameWork') === 'QtTest') ? 'QtSupport.Wizards.FeatureQt' : 'DeviceType.Desktop' )) }" ] - } - }, - { - "trDisplayName": "Project Management", - "trShortTitle": "Summary", - "typeId": "Summary" - } - ], - "generators": - [ - { - "typeId": "File", - "data": - [ - { - "source": "files/gtest_dependency.pri", - "target": "gtest_dependency.pri", - "condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qmake'}", - "openInEditor": false - }, - { - "source": "files/googlecommon.js", - "target": "googlecommon.js", - "condition": "%{JS: value('TestFrameWork') == 'GTest' && value('BuildSystem') == 'qbs'}", - "openInEditor": false - }, - { - "source": "files/tst.pro", - "target": "%{ProjectFilePath}", - "condition": "%{JS: value('BuildSystem') == 'qmake'}", - "openInEditor": false, - "openAsProject": true - }, - { - "source": "files/tst.qbs", - "target": "%{ProjectFilePath}", - "condition": "%{JS: value('BuildSystem') == 'qbs'}", - "openInEditor": false, - "openAsProject": true - }, - { - "source": "files/tst.txt", - "target": "CMakeLists.txt", - "condition": "%{JS: value('BuildSystem') == 'cmake'}", - "openInEditor": false, - "openAsProject": true - }, - { - "source": "files/tst_src_gt.cpp", - "target": "%{TestCaseFileGTestWithCppSuffix}", - "condition": "%{JS: value('TestFrameWork') == 'GTest'}", - "openInEditor": true - }, - { - "source": "files/tst_src.cpp", - "target": "%{TestCaseFileWithCppSuffix}", - "condition": "%{JS: value('TestFrameWork') == 'QtTest'}", - "openInEditor": true - }, - { - "source": "files/tst_main.cpp", - "target": "%{MainCppName}", - "condition": "%{JS: ['GTest', 'QtQuickTest', 'BoostTest', 'BoostTest_dyn', 'Catch2'].indexOf(value('TestFrameWork')) >= 0}", - "openInEditor": true - }, - { - "source": "files/tst_src_boost.cpp", - "target": "%{TestCaseFileWithCppSuffix}", - "condition": "%{JS: value('TestFrameWork') === 'BoostTest_dyn'}" - }, - { - "source": "files/tst_qml.tmpl", - "target": "%{TestCaseFileWithQmlSuffix}", - "condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}", - "openInEditor": true - }, - { - "source": "files/setup.cpp", - "target": "setup.cpp", - "condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}", - "openInEditor": true - }, - { - "source": "files/setup.h", - "target": "setup.h", - "condition": "%{JS: value('TestFrameWork') === 'QtQuickTest'}", - "openInEditor": true - }, - { - "source": "files/catch2_tst.cpp", - "target": "%{TestCaseFileWithCppSuffix}", - "condition": "%{JS: '%{TestFrameWork}' === 'Catch2'}", - "openInEditor": true - }, - { - "source": "../projects/git.ignore", - "target": ".gitignore", - "condition": "%{JS: ( %{IsTopLevelProject} && value('VersionControl') === 'G.Git' )}" - } - ] - } - ] -} diff --git a/share/qtcreator/templates/wizards/classes/cpp/file.cpp b/share/qtcreator/templates/wizards/classes/cpp/file.cpp index 8ebbf1d26b4..760f19c2e7b 100644 --- a/share/qtcreator/templates/wizards/classes/cpp/file.cpp +++ b/share/qtcreator/templates/wizards/classes/cpp/file.cpp @@ -1,4 +1,4 @@ -%{Cpp:LicenseTemplate}\ +%{JS: Cpp.licenseTemplate()}\ #include "%{JS: Util.relativeFilePath('%{Path}/%{HdrFileName}', '%{Path}' + '/' + Util.path('%{SrcFileName}'))}" %{JS: Cpp.openNamespaces('%{Class}')} @if '%{IncludeQSharedData}' diff --git a/share/qtcreator/templates/wizards/classes/cpp/file.h b/share/qtcreator/templates/wizards/classes/cpp/file.h index ad2b178e8ff..6c12d054de7 100644 --- a/share/qtcreator/templates/wizards/classes/cpp/file.h +++ b/share/qtcreator/templates/wizards/classes/cpp/file.h @@ -1,12 +1,12 @@ -%{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' +%{JS: Cpp.licenseTemplate()}\ +@if '%{JS: Cpp.usePragmaOnce()}' === 'true' #pragma once @else #ifndef %{GUARD} #define %{GUARD} @endif -%{JS: Cpp.includeStatement('%{Base}', Util.preferredSuffix('text/x-c++hdr'), ['QObject', 'QWidget', 'QMainWindow', 'QQuickItem', 'QSharedData'], '%{TargetPath}')}\ +%{JS: Cpp.includeStatement('%{Base}', Cpp.cxxHeaderSuffix(), ['QObject', 'QWidget', 'QMainWindow', 'QQuickItem', 'QSharedData'], '%{TargetPath}')}\ %{JS: QtSupport.qtIncludes([ ( '%{IncludeQObject}' ) ? 'QtCore/%{IncludeQObject}' : '', ( '%{IncludeQWidget}' ) ? 'QtGui/%{IncludeQWidget}' : '', ( '%{IncludeQMainWindow}' ) ? 'QtGui/%{IncludeQMainWindow}' : '', @@ -65,6 +65,6 @@ private: @endif }; %{JS: Cpp.closeNamespaces('%{Class}')} -@if ! '%{Cpp:PragmaOnce}' +@if '%{JS: Cpp.usePragmaOnce()}' === 'false' #endif // %{GUARD} @endif diff --git a/share/qtcreator/templates/wizards/classes/cpp/wizard.json b/share/qtcreator/templates/wizards/classes/cpp/wizard.json index 9317c175981..12b7eefbbeb 100644 --- a/share/qtcreator/templates/wizards/classes/cpp/wizard.json +++ b/share/qtcreator/templates/wizards/classes/cpp/wizard.json @@ -58,8 +58,8 @@ "mandatory": false, "data": { - "trText": "%{BaseCB}", - "trDisabledText": "%{BaseCB}", + "text": "%{BaseCB}", + "disabledText": "%{BaseCB}", "completion": "classes" } }, @@ -167,14 +167,14 @@ "type": "LineEdit", "trDisplayName": "Header file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Cpp.cxxHeaderSuffix())}" } }, { "name": "SrcFileName", "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Cpp.cxxSourceSuffix())}" } }, { "name": "Path", diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.cpp b/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.cpp index 71f9074a69b..be4af3270e2 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.cpp +++ b/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.cpp @@ -1,4 +1,4 @@ -%{Cpp:LicenseTemplate}\ +%{JS: Cpp.licenseTemplate()}\ #include "%{JS: Util.relativeFilePath('%{Path}/%{HdrFileName}', '%{Path}' + '/' + Util.path('%{SrcFileName}'))}" %{JS: Cpp.openNamespaces('%{Class}')}\ diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.h b/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.h index 4aa7ea11fc7..a9b104c03e7 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.h +++ b/share/qtcreator/templates/wizards/classes/itemmodel/itemmodel.h @@ -1,5 +1,5 @@ -%{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' +%{JS: Cpp.licenseTemplate()}\ +@if '%{JS: Cpp.usePragmaOnce()}' === 'true' #pragma once @else #ifndef %{GUARD} @@ -66,6 +66,6 @@ public: private: }; %{JS: Cpp.closeNamespaces('%{Class}')} -@if ! '%{Cpp:PragmaOnce}' +@if '%{JS: Cpp.usePragmaOnce()}' === 'false' #endif // %{GUARD} @endif diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.cpp b/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.cpp index 9b08462ef4e..86dc009c144 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.cpp +++ b/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.cpp @@ -1,4 +1,4 @@ -%{Cpp:LicenseTemplate}\ +%{JS: Cpp.licenseTemplate()}\ #include "%{JS: Util.relativeFilePath('%{Path}/%{HdrFileName}', '%{Path}' + '/' + Util.path('%{SrcFileName}'))}" %{JS: Cpp.openNamespaces('%{Class}')}\ diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.h b/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.h index fe1ef78cc61..339c582563b 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.h +++ b/share/qtcreator/templates/wizards/classes/itemmodel/listmodel.h @@ -1,5 +1,5 @@ -%{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' +%{JS: Cpp.licenseTemplate()}\ +@if '%{JS: Cpp.usePragmaOnce()}' === 'true' #pragma once @else #ifndef %{GUARD} @@ -59,6 +59,6 @@ public: private: }; %{JS: Cpp.closeNamespaces('%{Class}')} -@if ! '%{Cpp:PragmaOnce}' +@if '%{JS: Cpp.usePragmaOnce()}' === 'false' #endif // %{GUARD} @endif diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.cpp b/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.cpp index 794510501fa..c86cfe64871 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.cpp +++ b/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.cpp @@ -1,4 +1,4 @@ -%{Cpp:LicenseTemplate}\ +%{JS: Cpp.licenseTemplate()}\ #include "%{JS: Util.relativeFilePath('%{Path}/%{HdrFileName}', '%{Path}' + '/' + Util.path('%{SrcFileName}'))}" %{JS: Cpp.openNamespaces('%{Class}')}\ diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.h b/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.h index b6293563064..2be8f5ddcb8 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.h +++ b/share/qtcreator/templates/wizards/classes/itemmodel/tablemodel.h @@ -1,5 +1,5 @@ -%{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' +%{JS: Cpp.licenseTemplate()}\ +@if '%{JS: Cpp.usePragmaOnce()}' == 'true' #pragma once @else #ifndef %{GUARD} @@ -62,6 +62,6 @@ public: private: }; %{JS: Cpp.closeNamespaces('%{Class}')} -@if ! '%{Cpp:PragmaOnce}' +@if '%{JS: Cpp.usePragmaOnce()}' === 'false' #endif // %{GUARD} @endif diff --git a/share/qtcreator/templates/wizards/classes/itemmodel/wizard.json b/share/qtcreator/templates/wizards/classes/itemmodel/wizard.json index 1e0cd06e197..786fa8e70c5 100644 --- a/share/qtcreator/templates/wizards/classes/itemmodel/wizard.json +++ b/share/qtcreator/templates/wizards/classes/itemmodel/wizard.json @@ -14,7 +14,7 @@ { "key": "HdrPath", "value": "%{Path}/%{HdrFileName}" }, { "key": "SrcPath", "value": "%{Path}/%{SrcFileName}" }, { "key": "CN", "value": "%{JS: Cpp.className(value('Class'))}" }, - { "key": "GUARD", "value": "%{JS: Cpp.classToHeaderGuard(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } + { "key": "GUARD", "value": "%{JS: Cpp.classToHeaderGuard(value('Class'), Cpp.cxxHeaderSuffix())}" } ], "pages": @@ -92,14 +92,14 @@ "type": "LineEdit", "trDisplayName": "Header file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Cpp.cxxHeaderSuffix())}" } }, { "name": "SrcFileName", "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Cpp.cxxSourceSuffix())}" } }, { "name": "Path", diff --git a/share/qtcreator/templates/wizards/classes/python/wizard.json b/share/qtcreator/templates/wizards/classes/python/wizard.json index d42d8f0ab52..381718c054f 100644 --- a/share/qtcreator/templates/wizards/classes/python/wizard.json +++ b/share/qtcreator/templates/wizards/classes/python/wizard.json @@ -58,7 +58,7 @@ "type": "LineEdit", "enabled": "%{JS: value('BaseCB') === ''}", "mandatory": false, - "data": { "trText": "%{BaseCB}" } + "data": { "text": "%{BaseCB}" } }, @@ -115,7 +115,7 @@ "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Util.fileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } + "data": { "text": "%{JS: Util.fileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } }, { "name": "TargetPath", diff --git a/share/qtcreator/templates/wizards/codesnippet/wizard.json b/share/qtcreator/templates/wizards/codesnippet/wizard.json index c64ad36822e..ddcd41295fc 100644 --- a/share/qtcreator/templates/wizards/codesnippet/wizard.json +++ b/share/qtcreator/templates/wizards/codesnippet/wizard.json @@ -12,7 +12,7 @@ "options": [ { "key": "ProjectFile", "value": "%{ProjectDirectory}/CMakeLists.txt" }, - { "key": "CppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src')}" }, + { "key": "CppFileName", "value": "%{JS: 'main.' + Cpp.cxxSourceSuffix()}" }, { "key": "MacOSBundleValue", "value": "%{JS: %{MacOSBundle} ? 'TRUE' : 'FALSE' }" } ], "pages": @@ -38,7 +38,7 @@ "type": "TextEdit", "data": { - "trText": "int main(int argc, char *argv[])\n{\n return 0;\n}" + "text": "int main(int argc, char *argv[])\n{\n return 0;\n}" } }, { diff --git a/share/qtcreator/templates/wizards/files/cppheader/file.h b/share/qtcreator/templates/wizards/files/cppheader/file.h index e92e2c0aad7..f92c0d7d75f 100644 --- a/share/qtcreator/templates/wizards/files/cppheader/file.h +++ b/share/qtcreator/templates/wizards/files/cppheader/file.h @@ -1,5 +1,5 @@ -%{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' +%{JS: Cpp.licenseTemplate()}\ +@if '%{JS: Cpp.usePragmaOnce()}' === 'true' #pragma once @else #ifndef %{JS: Cpp.headerGuard('%{FileName}')} diff --git a/share/qtcreator/templates/wizards/files/cppheader/wizard.json b/share/qtcreator/templates/wizards/files/cppheader/wizard.json index 36956636a3f..a4bbb5ccc66 100644 --- a/share/qtcreator/templates/wizards/files/cppheader/wizard.json +++ b/share/qtcreator/templates/wizards/files/cppheader/wizard.json @@ -10,7 +10,7 @@ "enabled": "%{JS: value('Plugins').indexOf('CppEditor') >= 0}", "options": [ - { "key": "DefaultSuffix", "value": "%{JS: Util.preferredSuffix('text/x-c++hdr')}" }, + { "key": "DefaultSuffix", "value": "%{JS: Cpp.cxxHeaderSuffix()}" }, { "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}" } ], diff --git a/share/qtcreator/templates/wizards/files/cppsource/file.cpp b/share/qtcreator/templates/wizards/files/cppsource/file.cpp index b6e652a44a6..70ffeadc39b 100644 --- a/share/qtcreator/templates/wizards/files/cppsource/file.cpp +++ b/share/qtcreator/templates/wizards/files/cppsource/file.cpp @@ -1 +1 @@ -%{Cpp:LicenseTemplate}\ +%{JS: Cpp.licenseTemplate()}\ diff --git a/share/qtcreator/templates/wizards/files/cppsource/wizard.json b/share/qtcreator/templates/wizards/files/cppsource/wizard.json index c75600ff8af..707351da501 100644 --- a/share/qtcreator/templates/wizards/files/cppsource/wizard.json +++ b/share/qtcreator/templates/wizards/files/cppsource/wizard.json @@ -11,7 +11,7 @@ "options": [ { "key": "FileName", "value": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}" }, - { "key": "DefaultSuffix", "value": "%{JS: Util.preferredSuffix('text/x-c++src')}" } + { "key": "DefaultSuffix", "value": "%{JS: Cpp.cxxSourceSuffix()}" } ], "pages" : diff --git a/share/qtcreator/templates/wizards/files/testing/file.cpp b/share/qtcreator/templates/wizards/files/testing/file.cpp index e515339e2db..de5539dc022 100644 --- a/share/qtcreator/templates/wizards/files/testing/file.cpp +++ b/share/qtcreator/templates/wizards/files/testing/file.cpp @@ -1,4 +1,4 @@ -%{Cpp:LicenseTemplate} +%{JS: Cpp.licenseTemplate()} @if "%{TestFrameWork}" == "GTest" #include #include @@ -24,8 +24,12 @@ BOOST_AUTO_TEST_CASE( %{TestCaseName} ) BOOST_AUTO_TEST_SUITE_END() @endif -@if "%{TestFrameWork}" == "Catch2" +@if "%{TestFrameWork}" == "Catch2" +@if "%{Catch2Version}" == "V2" #include +@else +#include +@endif TEST_CASE("Another test with Catch2", "[fancy]") { diff --git a/share/qtcreator/templates/wizards/files/testing/wizard.json b/share/qtcreator/templates/wizards/files/testing/wizard.json index 34170434bea..e15997abf0a 100644 --- a/share/qtcreator/templates/wizards/files/testing/wizard.json +++ b/share/qtcreator/templates/wizards/files/testing/wizard.json @@ -13,7 +13,7 @@ "options": [ { "key": "TargetPath", "value": "%{Path}" }, { "key": "QmlFileName", "value": "%{JS: Util.fileName(value('QmlSrcFile').startsWith('tst_') ? value('QmlSrcFile') : 'tst_' + value('QmlSrcFile'), '.qml')}" }, - { "key": "CppFileName", "value": "%{JS: Util.fileName(value('CppSrcFile'), Util.preferredSuffix('text/x-c++src'))}" } + { "key": "CppFileName", "value": "%{JS: Util.fileName(value('CppSrcFile'), Cpp.cxxSourceSuffix())}" } ], "pages" : @@ -61,6 +61,26 @@ ] } }, + { + "name": "Catch2Version", + "trDisplayName": "Catch2 version:", + "visible": "%{JS: value('TestFrameWork') == 'Catch2'}", + "type": "ComboBox", + "data": { + "index": 1, + "items": + [ + { + "trKey": "2.x", + "value": "V2" + }, + { + "trKey": "3.x", + "value": "V3" + } + ] + } + }, { "name": "TestSuiteName", "trDisplayName": "Test suite name:", @@ -91,7 +111,7 @@ "trDisplayName": "Source file:", "mandatory": true, "visible": "%{JS: value('TestFrameWork') !== 'QtQuickTest' }", - "data": { "trText": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Util.preferredSuffix('text/x-c++src')}" } + "data": { "text": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.' + Cpp.cxxSourceSuffix()}" } }, { "name": "QmlSrcFile", @@ -99,7 +119,7 @@ "trDisplayName": "Source file:", "mandatory": true, "visible": "%{JS: value('TestFrameWork') === 'QtQuickTest' }", - "data": { "trText": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml'}" } + "data": { "text": "%{JS: 'tst_' + value('TestCaseName').toLowerCase() + '.qml'}" } }, { "name": "Path", diff --git a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json index 28e2242f7de..df4c59edbfd 100644 --- a/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json +++ b/share/qtcreator/templates/wizards/projects/cpplibrary/wizard.json @@ -138,7 +138,7 @@ "data": { "validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)", - "trText": "%{JS: value('Type') === 'qtplugin' ? value('BaseClassName').slice(1) : (value('ProjectName').charAt(0).toUpperCase() + value('ProjectName').slice(1))}" + "text": "%{JS: value('Type') === 'qtplugin' ? value('BaseClassName').slice(1) : (value('ProjectName').charAt(0).toUpperCase() + value('ProjectName').slice(1))}" } }, { @@ -269,14 +269,14 @@ "type": "LineEdit", "trDisplayName": "Header file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } }, { "name": "SrcFileName", "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } } ] }, diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json index 9156caaffd8..7ed2498faf8 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json @@ -38,7 +38,7 @@ "data": { "validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)", - "trText": "%{JS: value('BaseCB') ? value('BaseCB').slice(1) : 'MyClass'}" + "text": "%{JS: value('BaseCB') ? value('BaseCB').slice(1) : 'MyClass'}" } }, { @@ -56,14 +56,14 @@ "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } }, { "name": "ProjectFileName", "type": "LineEdit", "trDisplayName": "Project file:", "mandatory": true, - "data": { "trText": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" } + "data": { "text": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" } } ] }, diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json index 6251eaf3341..a2005335668 100644 --- a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/widget/wizard.json @@ -38,7 +38,7 @@ "data": { "validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)", - "trText": "%{JS: value('BaseCB') ? value('BaseCB').slice(1) : 'MyClass'}" + "text": "%{JS: value('BaseCB') ? value('BaseCB').slice(1) : 'MyClass'}" } }, @@ -56,14 +56,14 @@ "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-python'))}" } }, { "name": "ProjectFileName", "type": "LineEdit", "trDisplayName": "Project file:", "mandatory": true, - "data": { "trText": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" } + "data": { "text": "%{JS: Util.fileName('%{ProjectName}', 'pyproject')}" } } ] }, diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/Main.qml.tpl b/share/qtcreator/templates/wizards/projects/qtquickapplication/Main.qml.tpl index e1a6ccc8c31..378557b3df0 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/Main.qml.tpl +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/Main.qml.tpl @@ -1,5 +1,4 @@ import QtQuick -import QtQuick.Window @if %{UseVirtualKeyboard} import QtQuick.VirtualKeyboard @endif diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json index 806787d4591..2e885c2bb63 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/wizard.json @@ -21,6 +21,7 @@ { "key": "HasFailureSignal", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.3' }"}, { "key": "UsesAutoResourcePrefix", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.4' }"}, { "key": "HasLoadFromModule", "value": "%{JS: value('MinimumSupportedQtVersion') > '6.4' && value('UsesAutoResourcePrefix') }"}, + { "key": "FeatureQt", "value": "QtSupport.Wizards.FeatureQt.%{MinimumSupportedQtVersion}"}, { "key": "QdsWizardPath", "value": "%{IDE:ResourcePath}/qmldesigner/studio_templates/projects" }, { "key": "NoQdsProjectStyle", "value": "%{JS: !%{QdsProjectStyle} }" }, @@ -54,7 +55,7 @@ [ { "name": "QdsProjectStyle", - "trDisplayName": "Creates a project that you can open in Qt Design Studio.", + "trDisplayName": "Creates a project that you can open in Qt Design Studio", "trToolTip": "Creates a project with a structure that is compatible both with Qt Design Studio (via .qmlproject) and with Qt Creator (via CMakeLists.txt). It contains a .ui.qml form that you can visually edit in Qt Design Studio.", "type": "CheckBox", "span": true, @@ -77,7 +78,7 @@ }, { "name": "MinimumSupportedQtVersion", - "trDisplayName": "The minimum version of Qt you want to build the application for.", + "trDisplayName": "The minimum version of Qt you want to build the application for", "type": "ComboBox", "data": { @@ -98,7 +99,7 @@ "enabled": "%{JS: !value('IsSubproject')}", "data": { "projectFilePath": "%{ProjectFile}", - "requiredFeatures": [ "QtSupport.Wizards.FeatureQtQmlCMakeApi" ] + "requiredFeatures": [ "QtSupport.Wizards.FeatureQtQmlCMakeApi", "%{FeatureQt}" ] } }, { diff --git a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json index 58abd94344d..d4c7602c487 100644 --- a/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json +++ b/share/qtcreator/templates/wizards/projects/qtwidgetsapplication/wizard.json @@ -102,7 +102,7 @@ "data": { "validator": "(?:(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*|)", - "trText": "%{JS: value('BaseClass') ? value('BaseClass').slice(1) : 'MyClass'}" + "text": "%{JS: value('BaseClass') ? value('BaseClass').slice(1) : 'MyClass'}" } }, { @@ -123,14 +123,14 @@ "type": "LineEdit", "trDisplayName": "Header file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++hdr'))}" } }, { "name": "SrcFileName", "type": "LineEdit", "trDisplayName": "Source file:", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), Util.preferredSuffix('text/x-c++src'))}" } }, { "name": "GenerateForm", @@ -144,7 +144,7 @@ "trDisplayName": "Form file:", "enabled": "%{GenerateForm}", "mandatory": true, - "data": { "trText": "%{JS: Cpp.classToFileName(value('Class'), 'ui')}" } + "data": { "text": "%{JS: Cpp.classToFileName(value('Class'), 'ui')}" } } ] }, diff --git a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json index 8a20a1ef2e8..101a73dc64c 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json @@ -70,7 +70,7 @@ "trIncompleteMessage": "\"%{JS: Util.toNativeSeparators('%{TargetPath}')}\" exists in the filesystem.", "data": { - "trText": "%{defaultDir}" + "text": "%{defaultDir}" } }, { diff --git a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json index 2967a51f31c..ab193990fc1 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json @@ -82,7 +82,7 @@ "trIncompleteMessage": "\"%{JS: Util.toNativeSeparators('%{TargetPath}')}\" exists in the filesystem.", "data": { - "trText": "%{defaultDir}" + "text": "%{defaultDir}" } }, { diff --git a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json index cb7e5ab4f10..84ec489c3ae 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json @@ -69,7 +69,7 @@ "trIncompleteMessage": "\"%{JS: Util.toNativeSeparators('%{TargetPath}')}\" exists in the filesystem.", "data": { - "trText": "%{defaultDir}" + "text": "%{defaultDir}" } } ] diff --git a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json index 1ced38032e0..5ef76615509 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json @@ -69,7 +69,7 @@ "trIncompleteMessage": "\"%{JS: Util.toNativeSeparators('%{TargetPath}')}\" exists in the filesystem.", "data": { - "trText": "%{defaultDir}" + "text": "%{defaultDir}" } }, { diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/MyPlugin.json.in b/share/qtcreator/templates/wizards/qtcreatorplugin/MyPlugin.json.in index 24e580808bf..2e62c447394 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/MyPlugin.json.in +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/MyPlugin.json.in @@ -1,11 +1,11 @@ { - \\"Name\\" : \\"%{PluginName}\\", - \\"Version\\" : \\"0.0.1\\", - \\"CompatVersion\\" : \\"0.0.1\\", - \\"Vendor\\" : \\"%{VendorName}\\", - \\"Copyright\\" : \\"%{Copyright}\\", - \\"License\\" : \\"%{License}\\", - \\"Description\\" : \\"%{Description}\\", - \\"Url\\" : \\"%{Url}\\", - $$dependencyList + \"Name\" : \"%{PluginName}\", + \"Version\" : \"0.0.1\", + \"CompatVersion\" : \"0.0.1\", + \"Vendor\" : \"%{VendorName}\", + \"Copyright\" : \"%{Copyright}\", + \"License\" : \"%{License}\", + \"Description\" : \"%{Description}\", + \"Url\" : \"%{Url}\", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.cpp b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.cpp index 47ab9ebcc50..e25c6a22c33 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.cpp +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.cpp @@ -13,8 +13,7 @@ #include #include -namespace %{PluginName} { -namespace Internal { +namespace %{PluginName}::Internal { %{CN}::%{CN}() { @@ -27,7 +26,7 @@ namespace Internal { // Delete members } -bool %{CN}::initialize(const QStringList &arguments, QString *errorString) +void %{CN}::initialize() { // Register objects in the plugin manager's object pool // Load settings @@ -36,8 +35,9 @@ bool %{CN}::initialize(const QStringList &arguments, QString *errorString) // In the initialize function, a plugin can be sure that the plugins it // depends on have initialized their members. - Q_UNUSED(arguments) - Q_UNUSED(errorString) + // If you need access to command line arguments or to report errors, use the + // bool IPlugin::initialize(const QStringList &arguments, QString *errorString) + // overload. auto action = new QAction(tr("%{PluginName} Action"), this); Core::Command *cmd = Core::ActionManager::registerAction(action, Constants::ACTION_ID, @@ -49,8 +49,6 @@ bool %{CN}::initialize(const QStringList &arguments, QString *errorString) menu->menu()->setTitle(tr("%{PluginName}")); menu->addAction(cmd); Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu); - - return true; } void %{CN}::extensionsInitialized() @@ -75,5 +73,4 @@ void %{CN}::triggerAction() tr("This is an action from %{PluginName}.")); } -} // namespace Internal -} // namespace %{PluginName} +} // namespace %{PluginName}::Internal diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.h b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.h index 3851fa7c657..90051d00dbd 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.h +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin.h @@ -1,16 +1,10 @@ -@if '%{Cpp:PragmaOnce}' #pragma once -@else -#ifndef %{GUARD} -#define %{GUARD} -@endif #include "%{GlobalHdrFileName}" #include -namespace %{PluginName} { -namespace Internal { +namespace %{PluginName}::Internal { class %{CN} : public ExtensionSystem::IPlugin { @@ -21,7 +15,7 @@ public: %{CN}(); ~%{CN}() override; - bool initialize(const QStringList &arguments, QString *errorString) override; + void initialize() override; void extensionsInitialized() override; ShutdownFlag aboutToShutdown() override; @@ -29,9 +23,4 @@ private: void triggerAction(); }; -} // namespace Internal -} // namespace %{PluginName} - -@if ! '%{Cpp:PragmaOnce}' -#endif // %{GUARD} -@endif +} // namespace %{PluginName}::Internal diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h index ad863f17945..a81c8fcee0d 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/myplugin_global.h @@ -1,10 +1,5 @@ %{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' #pragma once -@else -#ifndef %{GLOBAL_GUARD} -#define %{GLOBAL_GUARD} -@endif #include @@ -13,7 +8,3 @@ #else # define %{LibraryExport} Q_DECL_IMPORT #endif -@if ! '%{Cpp:PragmaOnce}' - -#endif // %{GLOBAL_GUARD} -@endif diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/mypluginconstants.h b/share/qtcreator/templates/wizards/qtcreatorplugin/mypluginconstants.h index 699e49b2954..a5822a337e7 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/mypluginconstants.h +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/mypluginconstants.h @@ -1,20 +1,9 @@ %{Cpp:LicenseTemplate}\ -@if '%{Cpp:PragmaOnce}' #pragma once -@else -#ifndef %{CONSTANTS_GUARD} -#define %{CONSTANTS_GUARD} -@endif -namespace %{PluginName} { -namespace Constants { +namespace %{PluginName}::Constants { const char ACTION_ID[] = "%{PluginName}.Action"; const char MENU_ID[] = "%{PluginName}.Menu"; -} // namespace Constants -} // namespace %{PluginName} - -@if ! '%{Cpp:PragmaOnce}' -#endif // %{CONSTANTS_GUARD} -@endif +} // namespace %{PluginName}::Constants diff --git a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json index 95549b2cd99..0d3e20ef5e2 100644 --- a/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json +++ b/share/qtcreator/templates/wizards/qtcreatorplugin/wizard.json @@ -23,10 +23,7 @@ { "key": "GlobalHdrFileName", "value": "%{JS: Util.fileName(value('PluginNameLower') + '_global', Util.preferredSuffix('text/x-c++hdr'))}" }, { "key": "ConstantsHdrFileName", "value": "%{JS: Util.fileName(value('PluginNameLower') + 'constants', Util.preferredSuffix('text/x-c++hdr'))}" }, { "key": "CN", "value": "%{JS: Cpp.className(value('PluginName') + 'Plugin')}" }, - { "key": "GUARD", "value": "%{JS: Cpp.classToHeaderGuard(value('CN'), Util.suffix(value('HdrFileName')))}" }, - { "key": "HasTranslation", "value": "%{JS: value('TsFileName') !== ''}" }, - { "key": "GLOBAL_GUARD", "value": "%{JS: Cpp.headerGuard(value('GlobalHdrFileName'))}" }, - { "key": "CONSTANTS_GUARD", "value": "%{JS: Cpp.headerGuard(value('ConstantsHdrFileName'))}" } + { "key": "HasTranslation", "value": "%{JS: value('TsFileName') !== ''}" } ], "pages": @@ -60,7 +57,7 @@ "data": { "validator": "[a-zA-Z_][a-zA-Z_0-9]*", - "trText": "%{JS: value('ProjectName').charAt(0).toUpperCase() + value('ProjectName').slice(1)}" + "text": "%{JS: value('ProjectName').charAt(0).toUpperCase() + value('ProjectName').slice(1)}" } }, { @@ -112,7 +109,7 @@ "type": "LineEdit", "data": { - "trText": "https://www.%{JS: encodeURIComponent(value('VendorName').toLowerCase())}.com" + "text": "https://www.%{JS: encodeURIComponent(value('VendorName').toLowerCase())}.com" } }, { diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index adec8cd6285..e5b312ed159 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -175,6 +175,7 @@ DSsliderHandleInteraction=ff2aafd3 DSscrollBarTrack=dawnGrey DSscrollBarHandle=offBlack +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=midnightGrey @@ -265,6 +266,7 @@ DStoolbarBackground=midnightGrey ;DS controls theme END + BackgroundColorAlternate=alternateBackground BackgroundColorDark=shadowBackground BackgroundColorHover=hoverBackground @@ -475,26 +477,26 @@ QmlDesigner_FormeditorBackgroundColor=qmlDesignerButtonColor QmlDesigner_AlternateBackgroundColor=qmlDesignerButtonColor QmlDesigner_ScrollBarHandleColor=ff505050 -TerminalForeground=ffffffff +TerminalForeground=ffffff TerminalBackground=normalBackground -TerminalSelection=7fffffff +TerminalSelection=3fd1d1d1 TerminalFindMatch=7fffff00 -TerminalAnsi0=000000 -TerminalAnsi1=8b1b10 -TerminalAnsi2=4aa32e -TerminalAnsi3=9a9a2f -TerminalAnsi4=0058D1 +TerminalAnsi0=151515 +TerminalAnsi1=ac4142 +TerminalAnsi2=7e8e50 +TerminalAnsi3=e5b567 +TerminalAnsi4=6c99bb TerminalAnsi5=a320ac -TerminalAnsi6=49a3b0 -TerminalAnsi7=bfbfbf -TerminalAnsi8=666666 -TerminalAnsi9=d22d1f -TerminalAnsi10=62d63f -TerminalAnsi11=e5e54b -TerminalAnsi12=003EFF -TerminalAnsi13=d22dde -TerminalAnsi14=69e2e4 -TerminalAnsi15=e5e5e6 +TerminalAnsi6=7dd6cf +TerminalAnsi7=d0d0d0 +TerminalAnsi8=505050 +TerminalAnsi9=d05e5b +TerminalAnsi10=a7b773 +TerminalAnsi11=ffd184 +TerminalAnsi12=94c8ea +TerminalAnsi13=f257fb +TerminalAnsi14=a1fcf7 +TerminalAnsi15=ffffff [Flags] ComboBoxDrawTextShadow=false diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index 06f61831843..241502d1e53 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -48,7 +48,8 @@ highlightHover=ff74CBFC [Colors] ;DS controls theme START -;NEW FOR QtDS 4.0 +;NEW FOR QtDS 4 +;4.0 DScontrolBackground_toolbarIdle=offWhite DScontrolBackground_toolbarHover=offWhite DScontrolBackground_topToolbarHover=concreteGrey @@ -118,12 +119,12 @@ DSgreenLight=ff5cdc68 DSamberLight=ffffbf00 DSredLight=ffff0401 -DSinteraction=ff2aafd3 +DSinteraction=highlightBlue DSerrorColor=ffdf3a3a DSwarningColor=warning DSdisabledColor=ff8e8e8e -DSinteractionHover=ff74cbfc +DSinteractionHover=highlightHover DScontrolBackground=ffeaeaea DScontrolBackgroundInteraction=ffc9c9c9 @@ -135,7 +136,6 @@ DScontrolOutline=ffcecccc DScontrolOutlineInteraction=ff2aafd3 DScontrolOutlineDisabled=ff707070 -DStextColor=ff262626 DStextColorDisabled=ff707070 DStextSelectionColor=ff2aafd3 DStextSelectedTextColor=ff000000 @@ -168,8 +168,9 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ffb5b4b4 -DSscrollBarHandle=ff9b9b9b +DSscrollBarTrack=smokeGrey +DSscrollBarHandle=shadowGrey +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=ffd8d8d8 @@ -179,7 +180,7 @@ DSstateBackgroundColor=ffe0e0e0 DSstatePreviewOutline=ff363636 DSstatePanelBackground=ffdadada -DSstateHighlight=offWhite +DSstateHighlight=ff8d8d8d DSchangedStateText=ff99ccff @@ -256,7 +257,6 @@ DSUnimportedModuleColor=ffe33c2e DSBackgroundColorAlternate=ffeaeaea DSBackgroundColorNormal=ffd8d8d8 - ;DS controls theme END BackgroundColorAlternate=ff3d3d3d diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index fbf5241fa83..52fbf12ee87 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -537,26 +537,26 @@ PaletteTextDisabled=textDisabled PaletteMid=ffafafaf PalettePlaceholderText=ff808081 -TerminalForeground=ffffffff +TerminalForeground=ffffff TerminalBackground=normalBackground -TerminalSelection=7fffffff +TerminalSelection=3fd1d1d1 TerminalFindMatch=7fffff00 -TerminalAnsi0=000000 -TerminalAnsi1=8b1b10 -TerminalAnsi2=4aa32e -TerminalAnsi3=9a9a2f -TerminalAnsi4=0058D1 +TerminalAnsi0=151515 +TerminalAnsi1=ac4142 +TerminalAnsi2=7e8e50 +TerminalAnsi3=e5b567 +TerminalAnsi4=6c99bb TerminalAnsi5=a320ac -TerminalAnsi6=49a3b0 -TerminalAnsi7=bfbfbf -TerminalAnsi8=666666 -TerminalAnsi9=d22d1f -TerminalAnsi10=62d63f -TerminalAnsi11=e5e54b -TerminalAnsi12=003EFF -TerminalAnsi13=d22dde -TerminalAnsi14=69e2e4 -TerminalAnsi15=e5e5e6 +TerminalAnsi6=7dd6cf +TerminalAnsi7=d0d0d0 +TerminalAnsi8=505050 +TerminalAnsi9=d05e5b +TerminalAnsi10=a7b773 +TerminalAnsi11=ffd184 +TerminalAnsi12=94c8ea +TerminalAnsi13=f257fb +TerminalAnsi14=a1fcf7 +TerminalAnsi15=ffffff [Flags] ComboBoxDrawTextShadow=false diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index 8f32e7dee9e..d015c149f0b 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -180,6 +180,7 @@ DSsliderHandleInteraction=ff2aafd3 DSscrollBarTrack=dawnGrey DSscrollBarHandle=offBlack +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=midnightGrey @@ -480,26 +481,26 @@ PaletteTextDisabled=textDisabled PaletteMid=ffa0a0a0 PalettePlaceholderText=ff7f7f80 -TerminalForeground=ffffffff +TerminalForeground=ffffff TerminalBackground=normalBackground -TerminalSelection=7fffffff +TerminalSelection=3fd1d1d1 TerminalFindMatch=7fffff00 -TerminalAnsi0=000000 -TerminalAnsi1=8b1b10 -TerminalAnsi2=4aa32e -TerminalAnsi3=9a9a2f -TerminalAnsi4=0058D1 +TerminalAnsi0=151515 +TerminalAnsi1=ac4142 +TerminalAnsi2=7e8e50 +TerminalAnsi3=e5b567 +TerminalAnsi4=6c99bb TerminalAnsi5=a320ac -TerminalAnsi6=49a3b0 -TerminalAnsi7=bfbfbf -TerminalAnsi8=666666 -TerminalAnsi9=d22d1f -TerminalAnsi10=62d63f -TerminalAnsi11=e5e54b -TerminalAnsi12=003EFF -TerminalAnsi13=d22dde -TerminalAnsi14=69e2e4 -TerminalAnsi15=e5e5e6 +TerminalAnsi6=7dd6cf +TerminalAnsi7=d0d0d0 +TerminalAnsi8=505050 +TerminalAnsi9=d05e5b +TerminalAnsi10=a7b773 +TerminalAnsi11=ffd184 +TerminalAnsi12=94c8ea +TerminalAnsi13=f257fb +TerminalAnsi14=a1fcf7 +TerminalAnsi15=ffffff [Flags] ComboBoxDrawTextShadow=false diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index 139dc9c6479..21ded057ece 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -57,7 +57,8 @@ highlightHover=ff74CBFC [Colors] ;DS controls theme START -;NEW FOR QtDS 4.0 +;NEW FOR QtDS 4 +;4.0 DScontrolBackground_toolbarIdle=offWhite DScontrolBackground_toolbarHover=offWhite DScontrolBackground_topToolbarHover=concreteGrey @@ -127,12 +128,12 @@ DSgreenLight=ff5cdc68 DSamberLight=ffffbf00 DSredLight=ffff0401 -DSinteraction=ff2aafd3 +DSinteraction=highlightBlue DSerrorColor=ffdf3a3a DSwarningColor=warning DSdisabledColor=ff8e8e8e -DSinteractionHover=ff74cbfc +DSinteractionHover=highlightHover DScontrolBackground=ffeaeaea DScontrolBackgroundInteraction=ffc9c9c9 @@ -144,7 +145,6 @@ DScontrolOutline=ffcecccc DScontrolOutlineInteraction=ff2aafd3 DScontrolOutlineDisabled=ff707070 -DStextColor=ff262626 DStextColorDisabled=ff707070 DStextSelectionColor=ff2aafd3 DStextSelectedTextColor=ff000000 @@ -177,8 +177,9 @@ DSsliderHandleHover=ff606060 DSsliderHandleFocus=ff0492c9 DSsliderHandleInteraction=ff2aafd3 -DSscrollBarTrack=ffb5b4b4 -DSscrollBarHandle=ff9b9b9b +DSscrollBarTrack=smokeGrey +DSscrollBarHandle=shadowGrey +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=ffd8d8d8 @@ -265,8 +266,6 @@ DSUnimportedModuleColor=ffe33c2e DSBackgroundColorAlternate=ffeaeaea DSBackgroundColorNormal=ffd8d8d8 -DStoolbarBackground=offWhite - ;DS controls theme END BackgroundColorAlternate=alternateBackground diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 9ace50b236c..6dc677d9ff3 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -174,6 +174,7 @@ DSsliderHandleInteraction=ff2aafd3 DSscrollBarTrack=dawnGrey DSscrollBarHandle=offBlack +DSscrollBarHandle_idle=slateGrey DSsectionHeadBackground=midnightGrey @@ -264,6 +265,7 @@ DStoolbarBackground=midnightGrey ;DS controls theme END + BackgroundColorAlternate=alternateBackground BackgroundColorDark=shadowBackground BackgroundColorHover=hoverBackground diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts index d0e98799683..94bbf49d0b3 100644 --- a/share/qtcreator/translations/qtcreator_cs.ts +++ b/share/qtcreator/translations/qtcreator_cs.ts @@ -44,7 +44,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark Přidat záložku @@ -9724,20 +9724,20 @@ se projektu '%2' nepodařilo přidat. Přepnout na sezení - &New - &Nový + &New... + &Nový... - &Rename - &Přejmenovat + &Rename... + &Přejmenovat... - C&lone - Zdvo&jit + C&lone... + Zdvo&jit... - &Delete - &Smazat + &Delete... + &Smazat... &Open @@ -24897,8 +24897,8 @@ Vybere pro napodobovatele a přenosné cíle vhodné verze Qt, jsou-li dostupné '%1' se nepodařilo spustit: %2 - A timeout occurred running '%1' - Překročení času při spuštění '%1' + A timeout occurred running "%1". + Překročení času při spuštění "%1". '%1' crashed. @@ -40281,10 +40281,6 @@ Určuje, jak se chová zpětná klávesa (backspace) co se týče odsazování. Edit... Upravit... - - Remove - Odstranit - Export... Exportovat... @@ -40391,10 +40387,6 @@ Určuje, jak se chová zpětná klávesa (backspace) co se týče odsazování. Group: Skupina: - - Add - Přidat - Revert Built-in Vrátit zpět vestavěný @@ -44826,7 +44818,7 @@ Nainstalujte, prosím, alespoň jedno SDK. - QtC::Bookmarks + QtC::TextEditor Alt+Meta+M Alt+Meta+M @@ -48370,7 +48362,7 @@ nelze najít v cestě. - QtC::Bookmarks + QtC::TextEditor Note text: Text poznámky: diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index a52185b07d4..8421c0a6242 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -751,12 +751,8 @@ Vil du afinstallere den eksisterende pakke? Handlingen kræver indgriben fra brugeren. Brug kommandolinjeværktøjet "sdkmanager". - License command failed. - - - Licens-kommando mislykkedes. - - + License command failed. + Licens-kommando mislykkedes. Android SDK Manager @@ -807,16 +803,12 @@ Vil du afinstallere den eksisterende pakke? Vil du acceptere Android SDK-licensen? - Checking pending licenses... - - Tjekker afventende licenser... - + Checking pending licenses... + Tjekker afventende licenser... - -SDK Manager is busy. - -SDK manager er optaget. + SDK Manager is busy. + SDK manager er optaget. Android SDK Changes @@ -1295,12 +1287,8 @@ Installer en SDK af mindst API version %1. Mislykkedes. - Done - - - Færdig - - + Done + Færdig Installing @@ -2953,8 +2941,8 @@ Lokale commits pushes ikke til master-grenen inden en normal commit udføres.Lokalt filsystem: - For example: 'https://[user[:pass]@]host[:port]/[path]'. - F.eks.: 'https://[bruger[:kode]@]vært[:port]/[sti]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + F.eks.: "https://[bruger[:kode]@]vært[:port]/[sti]". Specify URL: @@ -3454,7 +3442,7 @@ F.eks., vil "Revision: 15" efterlade grenen ved revision 15. - QtC::Bookmarks + QtC::TextEditor Add Bookmark Tilføj bogmærke @@ -7818,8 +7806,8 @@ Vil du dræbe den? Kunne ikke starte "%1": %2 - A timeout occurred running "%1" - Timeout under kørsel af "%1" + A timeout occurred running "%1". + Timeout under kørsel af "%1". "%1" crashed. @@ -19514,8 +19502,8 @@ Fejl: %5 Lokalt filsystem: - For example: 'https://[user[:pass]@]host[:port]/[path]'. - F.eks.: 'https://[bruger[:kode]@]vært[:port]/[sti]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + F.eks.: "https://[bruger[:kode]@]vært[:port]/[sti]". Specify URL: @@ -22699,20 +22687,20 @@ til projektet "%2". Sessionshåndtering - &New - &Ny + &New... + &Ny... - &Rename - &Omdøb + &Rename... + &Omdøb... - C&lone - &Klon + C&lone... + &Klon... - &Delete - &Slet + &Delete... + &Slet... &Open @@ -33663,10 +33651,6 @@ Angiver hvordan backspace interagerer med indrykning. Edit... Rediger... - - Remove - Fjern - Export... Eksportér... diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 396fa4ac6f6..b16f1fe0ac9 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -6385,8 +6385,8 @@ Exporting assets: %2 - A timeout occurred running "%1" - Zeitüberschreitung bei Ausführung von "%1" + A timeout occurred running "%1". + Zeitüberschreitung bei Ausführung von "%1". "%1" crashed. @@ -9410,7 +9410,7 @@ Locked components cannot be modified or selected. Kommandozeilen-Werkzeug für Android-SDK ist installiert. - Android SDK Command-line Tools run. + Android SDK Command-line Tools runs. Kommandozeilen-Werkzeug für Android-SDK startet erfolgreich. @@ -9522,7 +9522,7 @@ Locked components cannot be modified or selected. SDK-Werkzeuge herunterladen - Could not open %1 for writing: %2. + Could not open "%1" for writing: %2. Die Datei "%1" konnte nicht zum Schreiben geöffnet werden: %2. @@ -10240,11 +10240,11 @@ Der vom Kit mindestens benötigte API-Level ist %1. Die Version des Android Build-SDK ist nicht definiert. Überprüfen Sie die Android-Einstellungen. - The Android build folder %1 was not found and could not be created. + The Android build folder "%1" was not found and could not be created. Das Build-Verzeichnis "%1" für Android wurde nicht gefunden und konnte nicht erstellt werden. - Cannot copy the target's lib file %1 to the Android build folder %2. + Cannot copy the target's lib file "%1" to the Android build folder "%2". Die lib-Datei "%1" des Targets konnte nicht in das Build-Verzeichnis "%2" für Android kopiert werden. @@ -10396,22 +10396,16 @@ Installieren Sie diese manuell, nachdem der aktuelle Vorgang abgeschlossen ist. - Checking pending licenses... - - Prüfe auf ausstehende Lizenzen... - + Checking pending licenses... + Prüfe auf ausstehende Lizenzen... - The installation of Android SDK packages may fail if the respective licenses are not accepted. - - Die Installation von Android-SDK-Paketen kann fehlschlagen, wenn die entsprechenden Lizenzen nicht akzeptiert werden. - + The installation of Android SDK packages may fail if the respective licenses are not accepted. + Die Installation von Android-SDK-Paketen kann fehlschlagen, wenn die entsprechenden Lizenzen nicht akzeptiert werden. - -SDK Manager is busy. - -SDK-Manager arbeitet. + SDK Manager is busy. + SDK-Manager arbeitet. Android SDK Changes @@ -10503,12 +10497,8 @@ Breche ausstehende Operationen ab... - License command failed. - - - Lizenzkommando fehlgeschlagen. - - + License command failed. + Lizenzkommando fehlgeschlagen. Updating installed packages. @@ -10519,12 +10509,8 @@ Breche ausstehende Operationen ab... Fehlgeschlagen. - Done - - - Fertig - - + Done + Fertig Installing @@ -10567,7 +10553,7 @@ Breche ausstehende Operationen ab... Anwendungsdaten kopieren - <b>Make install:</b> Copy App Files to %1 + <b>Make install:</b> Copy App Files to "%1" <b>Installieren:</b> Anwendungsdateien nach "%1" kopieren @@ -10850,8 +10836,8 @@ Die Dateien aus dem Quellverzeichnis des Android-Pakets werden in das Verzeichni Klicken zum Auswählen... - Images (*.png *.jpg *.jpeg *.webp *.svg) - Bilder (*.png *.jpg *.jpeg *.webp *.svg) + Images %1 + Bilder %1 %1 has been stopped. @@ -12315,8 +12301,8 @@ Siehe auch die Einstellungen für Google Test. Qt Test - <p>Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable.</p> - <p>Mehrere Testfälle in der gleichen ausführbaren Datei werden nicht offiziell unterstützt. Abhängig von der Implementation werden sie möglicherweise ausgeführt oder auch nicht, aber sie werden niemals explizit auswählbar sein.</p> + Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable. + Mehrere Testfälle in der gleichen ausführbaren Datei werden nicht offiziell unterstützt. Abhängig von der Implementation werden sie möglicherweise ausgeführt oder auch nicht, aber sie werden niemals explizit auswählbar sein. inherited @@ -12484,8 +12470,8 @@ Siehe auch die Einstellungen für Google Test. Zieltreiber: - Starting %1 ... - Starte %1 ... + Starting %1... + Starte %1... Choose the desired startup mode of the GDB server provider. @@ -13260,8 +13246,8 @@ This flag will allow push to proceed. Die Einstellung gestattet es, unter diesen Umständen fortzusetzen. - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Zum Beispiel: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Zum Beispiel: "https://[user[:pass]@]host[:port]/[path]". Ignores differences between branches and overwrites @@ -13927,7 +13913,7 @@ Zum Beispiel bewirkt die Angabe "Revision: 15" dass der Branch auf Rev - QtC::Bookmarks + QtC::TextEditor Bookmarks Lesezeichen @@ -14676,8 +14662,8 @@ Zum Beispiel bewirkt die Angabe "Revision: 15" dass der Branch auf Rev Keine gültige ausführbare CMake-Datei. - Running in %1: %2. - Führe in %1 aus: %2. + Running in "%1": %2. + Führe in "%1" aus: %2. <No CMake Tool available> @@ -16979,8 +16965,8 @@ Der Code wurde in die Zwischenablage kopiert. Anmeldung fehlgeschlagen - The login request failed: - Die Anmeldung ist fehlgeschlagen: + The login request failed: %1 + Die Anmeldung ist fehlgeschlagen: %1 Select Previous Copilot Suggestion @@ -17028,7 +17014,7 @@ Ansonsten müssen Sie den Pfad zur Datei %2 aus dem Copilot-Plugin für Neovim a Fordert Vorschläge für die Cursorposition des aktuellen Editors von Copilot an. - Show next Copilot Suggestion + Show Next Copilot Suggestion Nächsten Copilot-Vorschlag zeigen @@ -17036,7 +17022,7 @@ Ansonsten müssen Sie den Pfad zur Datei %2 aus dem Copilot-Plugin für Neovim a Iteriert über die Vorschläge von Copilot und zeigt den nächsten verfügbaren Vorschlag. - Show previos Copilot Suggestion + Show Previous Copilot Suggestion Vorangehenden Copilot-Vorschlag zeigen @@ -17080,8 +17066,8 @@ Ansonsten müssen Sie den Pfad zur Datei %2 aus dem Copilot-Plugin für Neovim a Pfad zu Node.js - Select path to node.js executable. See https://nodejs.org/en/download/for installation instructions. - Wählen Sie den Pfad zur ausführbaren node.js-Datei. Siehe auch https://nodejs.org/en/download/ für eine Installationsanleitung. + Select path to node.js executable. See %1 for installation instructions. + Wählen Sie den Pfad zur ausführbaren node.js-Datei. Siehe auch %1 für eine Installationsanleitung. Path to agent.js: @@ -17092,8 +17078,8 @@ Ansonsten müssen Sie den Pfad zur Datei %2 aus dem Copilot-Plugin für Neovim a Pfad zu agent.js - Select path to agent.js in Copilot Neovim plugin. See https://github.com/github/copilot.vim#getting-started for installation instructions. - Wählen Sie den Pfad zur agent.js-Datei vom Copilot-Plugin für Neovim. Siehe auch https://github.com/github/copilot.vim#getting-started für eine Installationsanleitung. + Select path to agent.js in Copilot Neovim plugin. See %1 for installation instructions. + Wählen Sie den Pfad zur agent.js-Datei vom Copilot-Plugin für Neovim. Siehe auch %1 für eine Installationsanleitung. Auto Complete @@ -18005,8 +17991,8 @@ Trotzdem fortfahren? "%1" konnte nicht gestartet werden: %2 - A timeout occurred running "%1" - Zeitüberschreitung bei Ausführung von "%1" + A timeout occurred running "%1". + Zeitüberschreitung bei Ausführung von "%1". "%1" crashed. @@ -21886,9 +21872,9 @@ z.B. name = "m_test_foo_": Bestimmt, ob Clangd beim Vervollständigen von Symbolen Header-Dateien einfügen darf. - Defines the amount of time Qt Creator waits before sending document changes to the server. + Defines the amount of time %1 waits before sending document changes to the server. If the document changes again while waiting, this timeout resets. - Bestimmt die Zeit, die Qt Creator vor dem Senden von Dokumentänderungen an den Server wartet. + Bestimmt die Zeit, die %1 vor dem Senden von Dokumentänderungen an den Server wartet. Falls sich das Dokument innerhalb dieser Zeit wieder ändert, wird erneut die volle Zeit gewartet. @@ -27591,8 +27577,8 @@ Versuchen Sie, das Projekt neu zu erstellen. Shell in Container öffnen - Docker daemon appears to be not running. Verify daemon is up and running and reset the Docker daemon in Docker device preferences or restart Qt Creator. - Der Docker-Daemon scheint nicht zu laufen. Stellen Sie sicher, dass der Daemon ausgeführt wird, und setzen Sie den Docker-Daemon in den Einstellungen des Docker-Geräts zurück oder starten Sie Qt Creator neu. + Docker daemon appears to be not running. Verify daemon is up and running and reset the Docker daemon in Docker device preferences or restart %1. + Der Docker-Daemon scheint nicht zu laufen. Stellen Sie sicher, dass der Daemon ausgeführt wird, und setzen Sie den Docker-Daemon in den Einstellungen des Docker-Geräts zurück oder starten Sie %1 neu. Docker Image Selection @@ -27607,10 +27593,8 @@ Versuchen Sie, das Projekt neu zu erstellen. Lade... - Running "%1" - - Führe "%1" aus - + Running "%1" + Führe "%1" aus Unexpected result: %1 @@ -27689,8 +27673,8 @@ Versuchen Sie, das Projekt neu zu erstellen. Die Liste der Quellverzeichnisse sollte nicht leer sein. - Host directories to mount into the container - Host-Verzeichnisse die in den Container eingehängt werden sollen + Host directories to mount into the container. + Host-Verzeichnisse die in den Container eingehängt werden sollen. Maps paths in this list one-to-one to the docker container. @@ -28249,12 +28233,8 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins: Hilfe > Plugins - If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. - - - Wenn Sie %1 vorübergehend deaktivieren, werden die folgenden Plugins, die davon abhängen, auch deaktiviert: %2. - - + If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. + Wenn Sie %1 vorübergehend deaktivieren, werden die folgenden Plugins, die davon abhängen, auch deaktiviert: %2. Disable plugins permanently in %1. @@ -28420,7 +28400,7 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins: In FakeVim nicht implementiert. - Type Meta-Shift-Y, Meta-Shift-Y to quit FakeVim mode. + Type Control-Shift-Y, Control-Shift-Y to quit FakeVim mode. These are the names of the actual keys on my german Apple keyboard https://support.apple.com/de-de/HT201236 Benutzen sie Control-Umschalt-Y, Control-Umschalt-Y, um den FakeVim-Modus zu verlassen. @@ -34345,8 +34325,8 @@ Beispiel: *.cpp%1*.h Nutzerdaten abfragen - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Zum Beispiel: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Zum Beispiel: "https://[user[:pass]@]host[:port]/[path]". Commit Editor @@ -36791,20 +36771,20 @@ Title of a the cloned RunConfiguration window, text of the window Sitzungsverwaltung - &New - &Neu + &New... + &Neu... - &Rename - &Umbenennen + &Rename... + &Umbenennen... - C&lone - &Duplizieren + C&lone... + &Duplizieren... - &Delete - &Löschen + &Delete... + &Löschen... Restore last session on startup @@ -39864,8 +39844,8 @@ Sie sollten nicht mehrere Test-Frameworks im selben Projekt mischen.Nimble-Anwendung - Creates a project that you can open in Qt Design Studio. - Erstellt ein Projekt, das in Qt Design Studio geöffnet werden kann. + Creates a project that you can open in Qt Design Studio + Erstellt ein Projekt, das in Qt Design Studio geöffnet werden kann Creates a project with a structure that is compatible both with Qt Design Studio (via .qmlproject) and with Qt Creator (via CMakeLists.txt). It contains a .ui.qml form that you can visually edit in Qt Design Studio. @@ -39880,8 +39860,8 @@ Sie sollten nicht mehrere Test-Frameworks im selben Projekt mischen.Qt 6.5 - The minimum version of Qt you want to build the application for. - Die niedrigste Qt-Version, die Sie zum Bauen der Anwendung benutzen wollen. + The minimum version of Qt you want to build the application for + Die niedrigste Qt-Version, die Sie zum Bauen der Anwendung benutzen wollen Creates a CMake-based test project for which a code snippet can be entered. @@ -47127,22 +47107,16 @@ Zusätzlich wird die Verbindung zum Gerät getestet. Überprüfe, ob "%1" funktioniert... - Failed to start "%1": %2 - - "%1" konnte nicht gestartet werden: %2 - + Failed to start "%1": %2 + "%1" konnte nicht gestartet werden: %2 - "%1" crashed. - - "%1" ist abgestürzt. - + "%1" crashed. + "%1" ist abgestürzt. - "%1" failed with exit code %2: %3 - - "%1" ist mit Rückgabewert %2 fehlgeschlagen: %3 - + "%1" failed with exit code %2: %3 + "%1" ist mit Rückgabewert %2 fehlgeschlagen: %3 "%1" is functional. @@ -47151,10 +47125,8 @@ Zusätzlich wird die Verbindung zum Gerät getestet. - "%1" will be used for deployment, because "%2" and "%3" are not available. - - "%1" wird für das Deployment benutzt, da "%2" und "%3" nicht verfügbar sind. - + "%1" will be used for deployment, because "%2" and "%3" are not available. + "%1" wird für das Deployment benutzt, da "%2" und "%3" nicht verfügbar sind. Checking if required commands are available... @@ -47177,10 +47149,8 @@ Zusätzlich wird die Verbindung zum Gerät getestet. %1 nicht gefunden. - Deployment to this device will not work out of the box. - - Deployment auf dieses Gerät wird nicht von Anfang an funktionieren. - + Deployment to this device will not work out of the box. + Deployment auf dieses Gerät wird nicht von Anfang an funktionieren. Checking if specified ports are available... @@ -47446,10 +47416,6 @@ Der Kontrollprozess konnte nicht gestartet werden. "%1" failed to start: %2 "%1" konnte nicht gestartet werden: %2 - - "%1" crashed. - "%1" ist abgestürzt. - "sftp" binary "%1" does not exist. Ausführbare "sftp"-Datei "%1" existiert nicht. @@ -48677,655 +48643,664 @@ Zeile: %4, Spalte: %5 QtC::Squish Details - Details + Details Adjust references to the removed symbolic name to point to: - + Referenzen zum entfernten symbolischen Namen ändern auf: Remove the symbolic name (invalidates names referencing it) - + Symbolischen Namen entfernen (macht darauf referenzierende Namen ungültig) Remove the symbolic name and all names referencing it - + Symbolischen Namen und alle darauf referenzierende Namen entfernen The Symbolic Name <span style='white-space: nowrap'>"%1"</span> you want to remove is used in Multi Property Names. Select the action to apply to references in these Multi Property Names. - + Der symbolische Name <span style='white-space: nowrap'>"%1"</span>, den Sie entfernen wollen, wird in Multi-Property-Namen benutzt. Wählen Sie die Aktion, die Sie auf Referenzen in diesen Multi-Property-Namen anwenden wollen. Failed to write "%1" - + "%1" konnte nicht geschrieben werden Incomplete Squish settings. Missing Squish installation path. - + Unvollständige Squish-Einstellungen. Das Installationsverzeichnis von Squish fehlt. objectmaptool not found. - + "objectmaptool" wurde nicht gefunden. Failure while parsing objects.map content. - + Fehler beim Auswerten von objects.map. Squish Object Map Editor - + Squish Object-Map-Editor New - Neu + Neu Remove - + Entfernen Jump to Symbolic Name - + Gehe zu symbolischem Namen Symbolic Names - + Symbolische Namen Cut - Ausschneiden + Ausschneiden Copy - Kopieren + Kopieren Paste - Einfügen + Einfügen Delete - + Löschen Copy Real Name - + Tatsächlichen Namen kopieren Properties: - Eigenschaften: + Eigenschaften: The properties of the Multi Property Name associated with the selected Symbolic Name. (use \\ for a literal \ in the value) - + Die Eigenschaften des Multi-Property-Namens, der mit dem ausgewählten symbolischen Namen assoziiert ist. (Benutzen Sie \\ für ein \-Literal im Wert) The Hierarchical Name associated with the selected Symbolic Name. - + Der hierarchische Name, der mit dem ausgewählten symbolischen Namen assoziiert ist. Remove Symbolic Name - + Symbolischen Namen entfernen Do you really want to remove "%1"? - + Möchten Sie "%1" wirklich entfernen? Ambiguous Property Name - + Mehrdeutiger Eigenschaftsname Ambiguous Symbolic Name - + Mehrdeutiger symbolischer Name %1 "%2" already exists. Specify a unique name. - + %1 "%2" existiert bereits. Geben Sie einen eindeutigen Namen an. Property - Eigenschaft + Eigenschaft Symbolic Name - + Symbolischer Name CopyOf - + Prefix for copies of a name + KopieVon Open Squish Test Suites - + Squish Test-Suites öffnen Select All - + Alle auswählen Deselect All - Alles abwählen + Alle abwählen Base directory: - + Basisverzeichnis: Test suites: - + Test-Suites: Name - Name + Name Operator - Operator + Operator Value - Wert + Wert Application: - + Anwendung: <No Application> - + <Keine Anwendung> Arguments: - Argumente: + Argumente: Recording Settings - + Aufnahme-Einstellungen Suite Already Open - + Suite bereits geöffnet A test suite with the name "%1" is already open. Close the opened test suite and replace it with the new one? - + Eine Test-Suite mit dem Namen "%1" ist bereits geöffnet. +Möchten Sie die Test-Suite schließen und mit der neuen ersetzen? Confirm Delete - Löschen Bestätigen + Löschen Bestätigen Are you sure you want to delete Test Case "%1" from the file system? - + Möchten Sie den Testfall "%1" vom Dateisystem löschen? Deletion of Test Case failed. - + Löschen des Testfalls fehlgeschlagen. Test Suite Path Not Accessible - + Pfad der Test-Suite nicht lesbar The path "%1" does not exist or is not accessible. Refusing to run test case "%2". - + Der Pfad "%1" existiert nicht oder ist nicht lesbar. +Testfall "%2" wird nicht ausgeführt. The path "%1" does not exist or is not accessible. Refusing to run test cases. - + Der Pfad "%1" existiert nicht oder ist nicht lesbar. +Testfälle werden nicht ausgeführt. No Test Cases Defined - + Kein Testfall definiert Test suite "%1" does not contain any test cases. - + Test-Suite "%1" enthält keine Testfälle. The path "%1" does not exist or is not accessible. Refusing to record test case "%2". - + Der Pfad "%1" existiert nicht oder ist nicht lesbar. +Testfall "%2" wird nicht aufgezeichnet. Select Global Script Folder - + Globales Script-Verzeichnis auswählen Error - Fehler + Fehler Squish Tools in unexpected state (%1). - + Squish-Werkzeuge sind in unerwartetem Zustand (%1). Failed to open objects.map file at "%1". - + Die "objects.map"-Datei "%1" konnte nicht geöffnet werden. Squish - + Squish Run This Test Case - + Diesen Testfall ausführen Delete Test Case - + Testfall löschen Run This Test Suite - + Diese Test-Suite ausführen Add New Test Case... - + Neuen Testfall hinzufügen... Close Test Suite - + Test-Suite schließen Delete Shared File - + Gemeinsame Datei löschen Add Shared File - + Gemeinsame Datei hinzufügen Remove Shared Folder - + Gemeinsames Verzeichnis löschen Open Squish Suites... - + Squish-Suites öffnen... Create New Test Suite... - + Neue Test-Suite erstellen... Close All Test Suites - + Alle Test-Suites schließen Close all test suites? - + Alle Test-Suites schließen? Add Shared Folder... - + Gemeinsames Verzeichnis hinzufügen... Remove All Shared Folders - + Alle gemeinsamen Verzeichnisse entfernen Test Suites - + Test-Suites Remove "%1" from the list of shared folders? - + "%1" aus der Liste der gemeinsamen Verzeichnisse entfernen? Remove all shared folders? - + Alle gemeinsamen Verzeichnisse entfernen? Record Test Case - + Testfall aufnehmen Do you want to record over the test case "%1"? The existing content will be overwritten by the recorded script. - + Möchten Sie den Testfall "%1" neu aufnehmen? Der existierende Inhalt wird durch die Aufnahme überschrieben. Set up a valid Squish path to be able to create a new test case. (Edit > Preferences > Squish) - + Setzen Sie einen gültigen Pfad zu Squish, um einen neuen Testfall zu erstellen. +(Bearbeiten > Einstellungen > Squish) Test Results - Testergebnisse + Testergebnisse Runner/Server Log - + Runner/Server Log <b>Test summary:</b>&nbsp;&nbsp; %1 passes, %2 fails, %3 fatals, %4 errors, %5 warnings. - + <b>Zusammenfassung des Tests:</b>&nbsp;&nbsp; %1 bestanden, %2 durchgefallen, %3 fatal, %4 Fehler, %5 Warnungen. Expand All - Alle aufklappen + Alle aufklappen Collapse All - Alle einklappen + Alle einklappen Filter Test Results - Testergebnisse filtern + Testergebnisse filtern Pass - Bestanden + Bestanden Fail - Durchgefallen + Durchgefallen Expected Fail - Erwartetes Scheitern + Erwartet durchgefallen Unexpected Pass - Unerwartet bestanden + Unerwartet bestanden Warning Messages - Warnungsnachrichten + Warnungsnachrichten Log Messages - + Protokollnachrichten Check All Filters - Alle Filter auswählen + Alle Filter auswählen Control Bar - + Kontrollleiste Stop Recording - Aufnahme beenden + Aufnahme beenden Ends the recording session, saving all commands to the script file. - + Beendet die Aufnahmesitzung und speichert alle Kommandos in der Script-Datei. Interrupt - Anhalten + Anhalten Step Into - Einzelschritt herein + Einzelschritt herein Step Over - Einzelschritt über + Einzelschritt über Step Out - Einzelschritt heraus + Einzelschritt heraus Inspect - + Untersuchen Type - Typ + Typ Squish Locals - + Lokale Squish-Variablen Object - + Objekt Squish Objects - + Squish-Objekte Squish Object Properties - + Squish Objekteigenschaften Continue - Fortsetzen + Fortsetzen &Squish - + &Squish &Server Settings... - + &Servereinstellungen... Result - Ergebnis + Ergebnis Message - + Nachricht Time - Zeit + Zeit Squish path: - + Squish-Pfad: Path to Squish installation - + Pfad zur Squish-Installation Path does not contain server executable at its default location. - + Pfad enthält am Standardort keine ausführbare Serverdatei. License path: - + Lizenzpfad: Local Server - + Lokaler Server Server host: - + Server-Host: Server Port - + Server-Port Verbose log - + Ausführliches Protokoll Minimize IDE - + IDE minimieren Minimize IDE automatically while running or recording test cases. - + Minimiert die IDE automatisch, während Testfälle ausgeführt oder aufgenommen werden. General - Allgemein + Allgemein Maximum startup time: - + Maximale Anlaufzeit: Specifies how many seconds Squish should wait for a reply from the AUT directly after starting it. - + Legt fest, wie viele Sekunden Squish auf eine Antwort von der AUT direkt nach dessen Start wartet. Maximum response time: - + Maximale Antwortzeit: Specifies how many seconds Squish should wait for a reply from the hooked up AUT before raising a timeout error. - + Legt fest, wie viele Sekunden Squish auf eine Antwort von der angeschlossenen AUT wartet, bevor ein Zeitüberschreitungsfehler ausgelöst wird. Maximum post-mortem wait time: - + Maximale Post-Mortem-Wartezeit: Specifies how many seconds Squish should wait after the the first AUT process has exited. - + Legt fest, wie viele Sekunden Squish wartet, nachdem der erste AUT-Prozess beendet ist. Animate mouse cursor: - + Mauszeiger animieren: Name: - Name: + Name: Host: - Host: + Host: Port: - Port: + Port: Add Attachable AUT - + Anhängbare AUT hinzufügen Add - Hinzufügen + Hinzufügen Edit - + Bearbeiten Mapped AUTs - + Zugeordnete AUTs AUT Paths - + AUT-Pfade Attachable AUTs - + Anhängbare AUTs Select Application to test - + Wählen Sie die zu testende Anwendung Select Application Path - + Wählen Sie den Pfad zur Anwendung Squish Server Settings - + Squish-Servereinstellungen Failed to write configuration changes. Squish server finished with process error %1. - + Das Schreiben der Konfigurationsänderungen ist fehlgeschlagen. +Der Squish-Server wurde mit dem Fehler %1 beendet. Run Test Suite - + Test-Suite ausführen Object Map - + Object Map Run Test Case - + Testfall ausführen Shared Folders - + Gemeinsame Verzeichnisse %1 (none) - %1 (keine) + %1 (keine) Could not create test results folder. Canceling test run. - + Das Verzeichnis für die Testergebnisse konnte nicht erstellt werden. Testausführung wird abgebrochen. Squish Server Error - + Squish-Serverfehler "%1" could not be found or is not executable. Check the settings. - + "%1" konnte nicht gefunden werden oder ist nicht ausführbar. +Überprüfen Sie die Einstellungen. Recording test case - + Nehme Testfall auf Could not get Squish license from server. - + Die Lizenz konnte nicht vom Server abgerufen werden. Test run finished. - + Testausführung beendet. Test record finished. - + Testaufnahme beendet. Squish could not find the AUT "%1" to start. Make sure it has been added as a Mapped AUT in the squishserver settings. (Tools > Squish > Server Settings...) - + Squish konnte die zu startende AUT "%1" nicht finden. Stellen Sie sicher, dass sie in den Einstellungen des Squish-Servers als zugeordnete AUT hinzugefügt wurde. +(Extras > Squish > Servereinstellungen...) Refusing to run a test case. - + Ausführung des Testfalls verweigert. Refusing to execute server query. - + Ausführung einer Serveranfrage verweigert. Refusing to record a test case. - + Aufnahme eines Testfalls verweigert. Refusing to write configuration changes. - + Schreiben von Konfigurationsänderungen verweigert. Running test case - + Führe Testfall aus User stop initiated. - + Anhalten durch Benutzer angefordert. Squish Server Already Running - + Squish-Server läuft bereits There is still an old Squish server instance running. @@ -49333,95 +49308,107 @@ This will cause problems later on. If you continue, the old instance will be terminated. Do you want to continue? - + Es wird bereits eine alte Instanz des Squish-Servers ausgeführt. +Dies wird später zu Problemen führen. + +Wenn Sie den Vorgang fortsetzen, wird die alte Instanz beendet. +Wollen Sie fortfahren? Unexpected state or request while starting Squish server. (state: %1, request: %2) - + Unerwarteter Zustand oder Anfrage beim Starten des Squish-Servers. (Status: %1, Anfrage: %2) No Squish Server - + Kein Squish-Server Squish server does not seem to be running. (state: %1, request: %2) Try again. - + Der Squish-Server scheint nicht zu laufen. +(Status: %1, Anfrage: %2) +Versuchen Sie es erneut. No Squish Server Port - + Kein Squish-Serverport Failed to get the server port. (state: %1, request: %2) Try again. - + Der Serverport konnte nicht erhalten werden. +(Status: %1, Anfrage: %2) +Versuchen Sie es erneut. Squish Runner Running - + Squish-Runner läuft Squish runner seems to be running already. (state: %1, request: %2) Wait until it has finished and try again. - + Der Squish-Runner scheint bereits zu laufen. +(Status: %1, Anfrage: %2) +Warten Sie, bis er beendet ist, und versuchen Sie es dann erneut. Squish Runner Error - + Squish-Runner-Fehler Squish runner failed to start within given timeframe. - + Squish-Runner konnte im angegebenen Zeitrahmen nicht gestartet werden. Create New Squish Test Suite - + Neue Squish Test-Suite erstellen Available GUI toolkits: - + Verfügbare GUI-Toolkits: Invalid Squish settings. Configure Squish installation path inside Preferences... > Squish > General to use this wizard. - + Ungültige Squish-Einstellungen. Stellen Sie in Einstellungen... > Squish > Allgemein den Squish-Installationspfad ein, um diesen Assistenten zu benutzen. Available languages: - + Verfügbare Sprachen: <None> - <Kein> + <Kein> Key is not an object. - Schlüssel ist kein Objekt. + Schlüssel ist kein Objekt. Key 'mode' is not set. - + Schlüssel 'mode' ist nicht gesetzt. Unsupported mode: - + Modus nicht unterstützt: Could not merge results into single results.xml. Destination file "%1" already exists. - + Die Ergebnisse konnten nicht in einer einzelnen 'results.xml'-Datei zusammengeführt werden. +Zieldatei "%1" existiert bereits. Could not merge results into single results.xml. -Failed to open file "%1" - +Failed to open file "%1". + Die Ergebnisse konnten nicht in einer einzelnen 'results.xml'-Datei zusammengeführt werden. +Die Datei "%1" konnte nicht geöffnet werden. Error while parsing first test result. - + Fehler beim Auswerten des ersten Testergebnisses. @@ -49694,8 +49681,8 @@ Failed to open file "%1" Einstellungen... - Sends Esc to terminal instead of Qt Creator. - Sendet Escape zum Terminal statt zu Qt Creator. + Sends Esc to terminal instead of %1. + Sendet Escape zum Terminal statt zu %1. Press %1 to send Esc to terminal. @@ -49814,7 +49801,7 @@ Failed to open file "%1" Escape-Taste zum Terminal senden - Sends the escape key to the terminal when pressedinstead of closing the terminal. + Sends the escape key to the terminal when pressed instead of closing the terminal. Sendet die Escape-Taste zum Terminal, anstatt das Terminal zu schliessen. @@ -52972,8 +52959,8 @@ Die Trace-Daten sind verloren. Ausschl&ussmuster: - List of comma separated wildcard filters. - Kommaseparierte Liste von Platzhalter-Filtern. + List of comma separated wildcard filters. + Kommaseparierte Liste von Platzhalter-Filtern. Files with file name or full file path matching any filter are included. @@ -58315,7 +58302,7 @@ Are you sure you want to remove the material? Vcpkg Manifest-Editor - Name: + Name: Name: diff --git a/share/qtcreator/translations/qtcreator_es.ts b/share/qtcreator/translations/qtcreator_es.ts index a4dc26ed74f..623ffc7e0ae 100644 --- a/share/qtcreator/translations/qtcreator_es.ts +++ b/share/qtcreator/translations/qtcreator_es.ts @@ -85,7 +85,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark Agregar marcador @@ -130,10 +130,6 @@ You are going to delete a Folder which will also<br>remove its content. Are you sure you would like to continue? Se dispone a eliminar un directorio y todo su contenido. ¿Está seguro? - - New Folder - Nuevo directorio - Show Bookmark Mostrar marcador @@ -9376,10 +9372,6 @@ The following encodings are likely to fit: Copy... - - Delete - Suprimir - Line in current document Línea en el documento actual diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index 5650594b136..673d54af1ea 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -6336,8 +6336,8 @@ Export des ressources : %2 Impossible de générer le fichier de ressource : %1 - A timeout occurred running "%1" - Un dépassement de délai s’est produit lors de l’exécution de « %1 » + A timeout occurred running "%1". + Un dépassement de délai s’est produit lors de l’exécution de « %1 ». "%1" crashed. @@ -9058,22 +9058,16 @@ Installez-les manuellement après que l'opération en cours soit finie. L'installation du SDK Android ne contient pas certains paquets nécessaires. Souhaitez-vous installer les paquets manquants ? - Checking pending licenses... - - Vérification des licences en cours… - + Checking pending licenses... + Vérification des licences en cours… - The installation of Android SDK packages may fail if the respective licenses are not accepted. - - L'installation des paquets du SDK Android peut échouer si les licences respectives ne sont pas acceptées. - + The installation of Android SDK packages may fail if the respective licenses are not accepted. + L'installation des paquets du SDK Android peut échouer si les licences respectives ne sont pas acceptées. - -SDK Manager is busy. - -Le gestionnaire de SDK est occupé. + SDK Manager is busy. + Le gestionnaire de SDK est occupé. %n Android SDK packages shall be updated. @@ -9643,12 +9637,12 @@ Le niveau minimum d'API nécessaire par le kit est %1. Le fichier des paramètres de déploiement Android est introuvable. Aucune construction d'APK. - The Android build folder %1 was not found and could not be created. - Le dossier de compilation Android %1 n'a pas été trouvé ou ne peut être créé. + The Android build folder "%1" was not found and could not be created. + Le dossier de compilation Android « %1 » n'a pas été trouvé ou ne peut être créé. - Cannot copy the target's lib file %1 to the Android build folder %2. - Impossible de copier le fichier cible de la bibliothèque %1 dans le répertoire de compilation Android %2. + Cannot copy the target's lib file "%1" to the Android build folder "%2". + Impossible de copier le fichier cible de la bibliothèque « %1 » dans le répertoire de compilation Android « %2 ». Cannot copy file "%1" to Android build libs folder "%2". @@ -9989,8 +9983,8 @@ Cela ne peut être annulé. Copie des données de l’application - <b>Make install:</b> Copy App Files to %1 - <b>Make install :</b> Copie des fichiers de l'application vers %1 + <b>Make install:</b> Copy App Files to "%1" + <b>Make install :</b> Copie des fichiers de l'application vers « %1 » "%1" step has an invalid C++ toolchain. @@ -10484,8 +10478,8 @@ Les fichiers du répertoire source du paquet Android sont copiés dans le réper Cliquer pour sélectionner… - Images (*.png *.jpg *.jpeg *.webp *.svg) - Images (*.png *.jpg *.jpeg *.webp *.svg) + Images %1 + Images %1 Deploy to Android Device @@ -10644,8 +10638,8 @@ Les fichiers du répertoire source du paquet Android sont copiés dans le réper Télécharger les outils du SDK - Could not open %1 for writing: %2. - Impossible d'ouvrir %1 en écriture : %2. + Could not open "%1" for writing: %2. + Impossible d'ouvrir « %1 » en écriture : %2. Downloading Android SDK Tools from URL %1 has failed: %2. @@ -10672,12 +10666,8 @@ Les fichiers du répertoire source du paquet Android sont copiés dans le réper Échec. - Done - - - Fini - - + Done + Fini Installing @@ -10700,12 +10690,8 @@ Les fichiers du répertoire source du paquet Android sont copiés dans le réper - License command failed. - - - Échec de la commande de licence. - - + License command failed. + Échec de la commande de licence. Revision @@ -11611,8 +11597,8 @@ Avertissement : l'activation de cette fonctionnalité augmente signifi Métriques du benchmark - <p>Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable.</p> - <p>La présence de plusieurs cas de tests dans un unique exécutable n'est officiellement pas supportée. Suivant l'implémentation, ils peuvent être exécutés ou non mais il ne seront jamais sélectionnables explicitement.</p> + Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable. + La présence de plusieurs cas de tests dans un unique exécutable n'est officiellement pas supportée. Suivant l'implémentation, ils peuvent être exécutés ou non mais il ne seront jamais sélectionnables explicitement. inherited @@ -12745,8 +12731,8 @@ Avertissement : fonctionnalité expérimentale pouvant entraîner un échec Pilote cible : - Starting %1 ... - Démarrage de %1 … + Starting %1... + Démarrage de %1… Version @@ -12999,9 +12985,9 @@ Les commits locaux ne sont pas envoyés vers la branche master jusqu'à ce Boite de dialogue - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Should we translate the URL? - Par exemple : 'https://[user[:pass]@]host[:port]/[path]'. + Par exemple : « https://[user[:pass]@]host[:port]/[path] ». Ignores differences between branches and overwrites @@ -13722,7 +13708,7 @@ Par exemple, « Revision : 15 » laissera la branche à la révis - QtC::Bookmarks + QtC::TextEditor Bookmarks Signets @@ -14427,8 +14413,8 @@ Par exemple, « Revision : 15 » laissera la branche à la révis Exécutable CMake invalide. - Running in %1: %2. - Exécution dans %1 : %2. + Running in "%1": %2. + Exécution dans « %1 » : %2. Failed to open %1 for reading. @@ -16703,8 +16689,8 @@ Le code a été copié dans votre presse-papiers. Identification échouée - The login request failed: - La requête d'identification a échouée : + The login request failed: %1 + La requête d'identification a échouée : %1 Copilot @@ -16739,7 +16725,7 @@ Le code a été copié dans votre presse-papiers. Requête de suggestion Copilot à la position actuelle du curseur de l'éditeur. - Show next Copilot Suggestion + Show Next Copilot Suggestion Afficher la suggestion Copilot suivante @@ -16747,7 +16733,7 @@ Le code a été copié dans votre presse-papiers. Alterner parmi les suggestions Copilot reçues pour afficher la suggestion suivante disponible. - Show previos Copilot Suggestion + Show Previous Copilot Suggestion Afficher la suggestion Copilot précédente @@ -16787,8 +16773,8 @@ Le code a été copié dans votre presse-papiers. Chemin Node.js - Select path to node.js executable. See https://nodejs.org/en/download/for installation instructions. - Sélectionner le chemin menant à l'exécutable node.js. Voir https://nodejs.org/fr/download/ pour les instructions d'installation. + Select path to node.js executable. See %1 for installation instructions. + Sélectionner le chemin menant à l'exécutable node.js. Voir %1 pour les instructions d'installation. Path to agent.js: @@ -16799,8 +16785,8 @@ Le code a été copié dans votre presse-papiers. Chemin Agent.js - Select path to agent.js in Copilot Neovim plugin. See https://github.com/github/copilot.vim#getting-started for installation instructions. - Sélectionner le chemin vers agent.js dans le plug-in Copilot Neovim Voir https://github.com/github/copilot.vim#getting-started pour les instructions d'installation. + Select path to agent.js in Copilot Neovim plugin. See %1 for installation instructions. + Sélectionner le chemin vers agent.js dans le plug-in Copilot Neovim Voir %1 pour les instructions d'installation. Auto Complete @@ -20044,8 +20030,8 @@ Double-cliquez pour modifier l’élément. Impossible de lancer « %1 » : %2 - A timeout occurred running "%1" - Un dépassement de délai s’est produit lors de l’exécution de « %1 » + A timeout occurred running "%1". + Un dépassement de délai s’est produit lors de l’exécution de « %1 ». "%1" crashed. @@ -20295,8 +20281,8 @@ Double-cliquez pour modifier l’élément. C++ - <p>If background indexing is enabled, global symbol searches will yield more accurate results, at the cost of additional CPU load when the project is first opened. The indexing result is persisted in the project's build directory. If you disable background indexing, a faster, but less accurate, built-in indexer is used instead. The thread priority for building the background index can be adjusted since clangd 15.</p><p>Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' cores unused.</p><p>Normal Priority: Reduced priority compared to interactive work.</p>Low Priority: Same priority as other clangd work. - <p>Si l’indexation en arrière-plan est activée, les recherches globales de symboles donneront des résultats plus précis, au prix d’une charge de travail supplémentaire du CPU lors de la première ouverture du projet. Le résultat de l’indexation est conservé dans le répertoire de construction du projet. Si vous désactivez l’indexation en arrière-plan, un indexeur intégré plus rapide, mais moins précis, est utilisé à la place. La priorité des threads pour la construction de l’index d’arrière-plan peut être ajustée depuis clangd 15.</p><p>Priorité d’arrière-plan : priorité minimale, s’exécute sur les processeurs inactifs. Peut laisser les cœurs de « performance » inutilisés.</p><p>Priorité normale : priorité réduite par rapport au travail interactif.</p>Priorité basse : même priorité que les autres travaux de clangd. + <p>If background indexing is enabled, global symbol searches will yield more accurate results, at the cost of additional CPU load when the project is first opened. The indexing result is persisted in the project's build directory. If you disable background indexing, a faster, but less accurate, built-in indexer is used instead. The thread priority for building the background index can be adjusted since clangd 15.</p><p>Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' cores unused.</p><p>Normal Priority: Reduced priority compared to interactive work.</p><p>Low Priority: Same priority as other clangd work.</p> + <p>Si l’indexation en arrière-plan est activée, les recherches globales de symboles donneront des résultats plus précis, au prix d’une charge de travail supplémentaire du CPU lors de la première ouverture du projet. Le résultat de l’indexation est conservé dans le répertoire de construction du projet. Si vous désactivez l’indexation en arrière-plan, un indexeur intégré plus rapide, mais moins précis, est utilisé à la place. La priorité des threads pour la construction de l’index d’arrière-plan peut être ajustée depuis clangd 15.</p><p>Priorité d’arrière-plan : priorité minimale, s’exécute sur les processeurs inactifs. Peut laisser les cœurs de « performance » inutilisés.</p><p>Priorité normale : priorité réduite par rapport au travail interactif.</p><p>Priorité basse : même priorité que les autres travaux de clangd.</p> <p>Which C/C++ backend to use when switching between header and source file.<p>The clangd implementation has more capabilities, but also has some bugs not present in the built-in variant.<p>When "Try Both" is selected, clangd will be employed only if the built-in variant does not find anything. @@ -20311,9 +20297,9 @@ Double-cliquez pour modifier l’élément. Contrôle si clangd peut insérer des fichiers d’en-tête dans le cadre de la complétion de symboles. - Defines the amount of time Qt Creator waits before sending document changes to the server. + Defines the amount of time %1 waits before sending document changes to the server. If the document changes again while waiting, this timeout resets. - Définit le temps d’attente de Qt Creator avant d’envoyer les modifications du document au serveur. + Définit le temps d’attente de %1 avant d’envoyer les modifications du document au serveur. Si le document est à nouveau modifié pendant l’attente, ce délai est réinitialisé. @@ -27336,8 +27322,8 @@ La recompilation du projet peut aider. Ouvrir un shell dans le conteneur - Docker daemon appears to be not running. Verify daemon is up and running and reset the Docker daemon in Docker device preferences or restart Qt Creator. - Le démon Docker ne semble pas fonctionner. Vérifiez que le démon est présent et en cours d'exécution et réinitialisez le démon docker dans les paramètres du périphérique docker ou redémarrez Qt Creator. + Docker daemon appears to be not running. Verify daemon is up and running and reset the Docker daemon in Docker device preferences or restart %1. + Le démon Docker ne semble pas fonctionner. Vérifiez que le démon est présent et en cours d'exécution et réinitialisez le démon docker dans les paramètres du périphérique docker ou redémarrez %1. Docker Image Selection @@ -27352,10 +27338,8 @@ La recompilation du projet peut aider. Chargement … - Running "%1" - - Exécution de « %1 » - + Running "%1" + Exécution de « %1 » Unexpected result: %1 @@ -27434,8 +27418,8 @@ La recompilation du projet peut aider. La liste des répertoires sources ne doit pas être vide. - Host directories to mount into the container - Répertoires hôtes à monter dans le conteneur + Host directories to mount into the container. + Répertoires hôtes à monter dans le conteneur. Maps paths in this list one-to-one to the docker container. @@ -27852,12 +27836,8 @@ Raison : %3 Aide > À propos des greffons - If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. - - - Si vous désactivez temporairement %1, les greffons suivants qui en dépendent seront également désactivés : %2. - - + If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. + Si vous désactivez temporairement %1, les greffons suivants qui en dépendent seront également désactivés : %2. Disable plugins permanently in %1. @@ -28049,8 +28029,8 @@ Raison : %3 La marque « %1 » n'est pas définie. - Type Meta-Shift-Y, Meta-Shift-Y to quit FakeVim mode. - Tapez Meta-Maj-Y, Meta-Maj-Y pour quitter le mode FakeVim. + Type Control-Shift-Y, Control-Shift-Y to quit FakeVim mode. + Tapez Contrôle-Maj-Y, Contrôle-Maj-Y pour quitter le mode FakeVim. Type Alt-Y, Alt-Y to quit FakeVim mode. @@ -34096,7 +34076,7 @@ Exemple : *.cpp%1*.h Spécifier l’URL : - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Par exemple : « https://[utilisateur[:motdepasse]@]hôte[:port]/[chemin] ». @@ -36382,20 +36362,20 @@ Title of a the cloned RunConfiguration window, text of the window Gestionnaire de session - &New - &Nouveau + &New... + &Nouveau... - &Rename - &Renommer + &Rename... + &Renommer... - C&lone - C&lone + C&lone... + C&lone... - &Delete - &Supprimer + &Delete... + &Supprimer... What is a Session? @@ -38491,8 +38471,8 @@ Pour développer une application complête, créer un projet Qt Quick Applicatio Génère le code d’initialisation et de nettoyage - Creates a project that you can open in Qt Design Studio. - Génère un projet qui peut être ouvert dans Qt Design Studio. + Creates a project that you can open in Qt Design Studio + Génère un projet qui peut être ouvert dans Qt Design Studio Creates a project with a structure that is compatible both with Qt Design Studio (via .qmlproject) and with Qt Creator (via CMakeLists.txt). It contains a .ui.qml form that you can visually edit in Qt Design Studio. @@ -38507,8 +38487,8 @@ Pour développer une application complête, créer un projet Qt Quick Applicatio Qt 6.5 - The minimum version of Qt you want to build the application for. - La verison minimale de Qt pour laquelle vous voulez construire votre application. + The minimum version of Qt you want to build the application for + La verison minimale de Qt pour laquelle vous voulez construire votre application Creates a Qt Quick application that can have both QML and C++ code. You can build the application and deploy it to desktop, embedded, and mobile target platforms. @@ -46947,34 +46927,20 @@ Le processus de contrôle n'a pas pu démarrer. - Failed to start "%1": %2 - - Échec du démarrage de « %1 » : %2 - + Failed to start "%1": %2 + Échec du démarrage de « %1 » : %2 - "%1" crashed. - - « %1 » a planté. - + "%1" failed with exit code %2: %3 + « %1 » a échoué avec le code de sortie %2 : %3 - "%1" failed with exit code %2: %3 - - « %1 » a échoué avec le code de sortie %2 : %3 - + "%1" will be used for deployment, because "%2" and "%3" are not available. + « %1 » sera utilisé pour le déploiement, car « %2 » et « %3 » ne sont pas disponibles. - "%1" will be used for deployment, because "%2" and "%3" are not available. - - « %1 » sera utilisé pour le déploiement, car « %2 » et « %3 » ne sont pas disponibles. - - - - Deployment to this device will not work out of the box. - - Le déploiement vers ce périphérique ne fonctionnera pas directement. - + Deployment to this device will not work out of the box. + Le déploiement vers ce périphérique ne fonctionnera pas directement. %1... @@ -49104,9 +49070,9 @@ Le fichier de destination « %1 » existe déjà. Could not merge results into single results.xml. -Failed to open file "%1" +Failed to open file "%1". Impossible de fusionner les résultats dans un unique fichier results.xml. -Impossible d'ouvrir le fichier « %1 » +Impossible d'ouvrir le fichier « %1 ». Error while parsing first test result. @@ -49383,8 +49349,8 @@ Impossible d'ouvrir le fichier « %1 » Configurer… - Sends Esc to terminal instead of Qt Creator. - Envoie Échap au terminal au lieu d'à Qt Creator. + Sends Esc to terminal instead of %1. + Envoie Échap au terminal au lieu d'à %1. Press %1 to send Esc to terminal. @@ -49503,7 +49469,7 @@ Impossible d'ouvrir le fichier « %1 » Envoyer la touche d'échappement au terminal - Sends the escape key to the terminal when pressedinstead of closing the terminal. + Sends the escape key to the terminal when pressed instead of closing the terminal. Envoi la touche d'échappement au terminal lorsque appuyé au lieu de fermer le terminal. @@ -52725,8 +52691,8 @@ Les données de la trace sont perdues. Motif d’excl&usion : - List of comma separated wildcard filters. - Liste de filtres joker séparés par des virgules. + List of comma separated wildcard filters. + Liste de filtres joker séparés par des virgules. Files with file name or full file path matching any filter are included. @@ -54734,8 +54700,8 @@ Vérifiez les paramètres pour vous assurer que Valgrind est installé et dispon Éditeur de Manifest Vcpkg - Name: - Nom: + Name: + Nom : Version: diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index bdc95b998f8..5cb6489b7ed 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -874,16 +874,12 @@ Želiš li prihvatiti licencu za Android SDK? - Checking pending licenses... - - Provjera neriješenih licenca … - + Checking pending licenses... + Provjera neriješenih licenca … - -SDK Manager is busy. - -Upravljač za SDK je zauzet. + SDK Manager is busy. + Upravljač za SDK je zauzet. Android SDK Changes @@ -1428,8 +1424,8 @@ Lokalne obveze se ne guraju u glavnu granu sve dok se ne izvrši normalna obveza Lokalni datotečni sustav: - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Na primjer: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Na primjer: "https://[user[:pass]@]host[:port]/[path]". Specify URL: @@ -4824,8 +4820,8 @@ Greška: %5 Lokalni datotečni sustav: - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Na primjer: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Na primjer: "https://[user[:pass]@]host[:port]/[path]". Specify URL: @@ -5564,20 +5560,20 @@ Greška: %5 Upravljač za sesije - &New - &Novi + &New... + &Novi... - &Rename - &Preimenuj + &Rename... + &Preimenuj... - C&lone - K&loniraj + C&lone... + K&loniraj... - &Delete - &Ukloni + &Delete... + &Ukloni... &Open @@ -8805,7 +8801,7 @@ should a repository require SSH-authentication (see documentation on SSH and the - QtC::Bookmarks + QtC::TextEditor Add Bookmark Dodaj knjižnu oznaku @@ -13630,9 +13626,7 @@ Do you want to uninstall the existing package? - License command failed. - - + License command failed. @@ -13644,9 +13638,7 @@ Do you want to uninstall the existing package? Neuspjelo. - Done - - + Done @@ -15383,15 +15375,11 @@ Check the test environment. - QtC::Bookmarks + QtC::TextEditor Bookmark Knjižna oznaka - - Remove - Ukloni - Deleting a folder also removes its content.<br>Do you want to continue? Brisanjem mape se uklanja i njen sadržaj.<br>Želiš li nastaviti? @@ -18580,8 +18568,8 @@ Do you want to kill it? Nije moguće pokrenuti „%1”: %2 - A timeout occurred running "%1" - Došlo je do prekoračenja vremena prilikom pokretanja „%1” + A timeout occurred running "%1". + Došlo je do prekoračenja vremena prilikom pokretanja „%1”. "%1" crashed. @@ -36253,7 +36241,7 @@ What do you want to do? - QtC::Bookmarks + QtC::TextEditor Show Bookmark Prikaži knjižnu oznaku diff --git a/share/qtcreator/translations/qtcreator_hu.ts b/share/qtcreator/translations/qtcreator_hu.ts index 843d55c8525..9f29949c84d 100644 --- a/share/qtcreator/translations/qtcreator_hu.ts +++ b/share/qtcreator/translations/qtcreator_hu.ts @@ -77,7 +77,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark Künyvjelző hozzáadása diff --git a/share/qtcreator/translations/qtcreator_it.ts b/share/qtcreator/translations/qtcreator_it.ts index 8e092a00d77..549bf8f77b7 100644 --- a/share/qtcreator/translations/qtcreator_it.ts +++ b/share/qtcreator/translations/qtcreator_it.ts @@ -93,7 +93,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark Aggiungi un Segnalibro @@ -138,10 +138,6 @@ You are going to delete a Folder which will also<br>remove its content. Are you sure you would like to continue? Stai per cancellare una Cartella, questo cancellerà<br>anche il suo contenuto. Sei sicuro di volerlo fare? - - New Folder - Nuova Cartella - Show Bookmark Mostra il Segnalibro @@ -9219,10 +9215,6 @@ Queste codifiche dovrebbero andare bene: Copy... - - Delete - Elimina - Line %1 Riga %1 diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts index 7bba62d3666..71665c23d51 100644 --- a/share/qtcreator/translations/qtcreator_ja.ts +++ b/share/qtcreator/translations/qtcreator_ja.ts @@ -982,8 +982,8 @@ This flag will allow push to proceed. サーバーへプッシュ - For example: 'https://[user[:pass]@]host[:port]/[path]'. - 例: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + 例: "https://[user[:pass]@]host[:port]/[path]". Ignores differences between branches and overwrites @@ -3821,8 +3821,8 @@ Add, modify, and remove document filters, which determine the documentation set 認証情報を確認する - For example: 'https://[user[:pass]@]host[:port]/[path]'. - 例: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + 例: "https://[user[:pass]@]host[:port]/[path]". @@ -4383,20 +4383,20 @@ Add, modify, and remove document filters, which determine the documentation set セッションマネージャ - &New - 新規作成(&N) + &New... + 新規作成(&N)... - &Rename - 名前を変更(&R) + &Rename... + 名前を変更(&R)... - C&lone - 複製(&L) + C&lone... + 複製(&L)... - &Delete - 削除(&D) + &Delete... + 削除(&D)... Restore last session on startup @@ -7092,7 +7092,7 @@ SSH 認証が必要とされるリポジトリで使用されます(SSH の SSH_ - QtC::Bookmarks + QtC::TextEditor Add Bookmark ブックマークの追加 @@ -12201,7 +12201,7 @@ in the system's browser for manual download. - QtC::Bookmarks + QtC::TextEditor Move Up 上に移動 @@ -30728,7 +30728,7 @@ When a problem is detected, the application is interrupted and can be debugged.< - QtC::Bookmarks + QtC::TextEditor Show Bookmark ブックマークを開く @@ -30745,10 +30745,6 @@ When a problem is detected, the application is interrupted and can be debugged.< Rename Bookmark ブックマークの名前変更 - - Remove - 削除 - Deleting a folder also removes its content.<br>Do you want to continue? フォルダを削除すると中身も削除されます。<br>続行しますか? @@ -32563,8 +32559,8 @@ API バージョンが %1 以上の SDK をインストールしてください "%1" を起動できません: %2 - A timeout occurred running "%1" - "%1" を実行中にタイムアウトが発生しました + A timeout occurred running "%1". + "%1" を実行中にタイムアウトが発生しました。 "%1" crashed. @@ -46504,8 +46500,8 @@ Stepping into the module or setting breakpoints by file and line is expected to QmlDesigner::GenerateResource - A timeout occurred running "%1" - "%1" を実行中にタイムアウトが発生しました + A timeout occurred running "%1". + "%1" を実行中にタイムアウトが発生しました。 "%1" crashed. diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index fc280a71457..fb3b9a36e3f 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -883,20 +883,20 @@ Zarządzanie sesjami - &New - &Nowa sesja + &New... + &Nowa sesja... - &Rename - Z&mień nazwę + &Rename... + Z&mień nazwę... - C&lone - S&klonuj + C&lone... + S&klonuj... - &Delete - &Usuń + &Delete... + &Usuń... &Open @@ -1301,9 +1301,6 @@ Zoom: Powiększenie: - - - QtC::Bookmarks Add Bookmark Dodaj zakładkę @@ -1872,7 +1869,7 @@ Przyczyna: %3 - QtC::Bookmarks + QtC::TextEditor Move Up Przenieś do góry @@ -9431,7 +9428,7 @@ Nie zostanie zastosowane do białych znaków w komentarzach i ciągach znakowych - QtC::Bookmarks + QtC::TextEditor Show Bookmark Pokaż zakładkę @@ -9774,7 +9771,7 @@ Możesz odłożyć zmiany lub je porzucić. Pytaj o listy uwierzytelniające - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Na przykład: "https://[użytkownik[:hasło]@]host[:port]/[ścieżka]". @@ -13333,7 +13330,7 @@ This flag will allow push to proceed. - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Na przykład: "https://[użytkownik[:hasło]@]host[:port]/[ścieżka]". @@ -19038,10 +19035,6 @@ Ustala, jak klawisz "Backspace" reaguje na wcięcia. Edit... Modyfikuj... - - Remove - Usuń - Export... Eksportuj... @@ -20027,7 +20020,7 @@ Wersje Qt można dodać w: Opcje > Budowanie i uruchamianie > Wersje Qt. - QtC::Bookmarks + QtC::TextEditor Alt+Meta+M Alt+Meta+M @@ -23455,7 +23448,7 @@ Więcej informacji w dokumentacji "Checking Code Syntax". - QtC::Bookmarks + QtC::TextEditor Note text: Tekst notatki: @@ -28630,8 +28623,8 @@ Do you want to check them out now? Nie można uruchomić "%1": %2 - A timeout occurred running "%1" - Przekroczono limit czasu oczekiwania na odpowiedź od uruchomionego "%1" + A timeout occurred running "%1". + Przekroczono limit czasu oczekiwania na odpowiedź od uruchomionego "%1". "%1" crashed. diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 0c4dd669944..ed5d78ba7a2 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -578,8 +578,8 @@ The minimum API level required by the kit is %1. Установка на устройство - <b>Make install:</b> Copy App Files to %1 - <b>Make install:</b> Копирование файлов приложения в %1 + <b>Make install:</b> Copy App Files to "%1" + <b>Make install:</b> Копирование файлов приложения в «%1» Activity manager start options: @@ -1378,8 +1378,8 @@ Do you want to uninstall the existing package? Загрузка SDK Tools - Could not open %1 for writing: %2. - Не удалось открыть %1 для записи: %2. + Could not open "%1" for writing: %2. + Не удалось открыть «%1» для записи: %2. Downloading Android SDK Tools from URL %1 has failed: %2. @@ -1398,12 +1398,8 @@ Do you want to uninstall the existing package? Операция требует вмешательства пользователя. Используйте «sdkmanager» в командной строке. - License command failed. - - - Команда License завершилась с ошибкой. - - + License command failed. + Команда License завершилась с ошибкой. Android SDK Manager @@ -1446,16 +1442,12 @@ Do you want to uninstall the existing package? Принимаете условия лицензии Android SDK? - Checking pending licenses... - - Проверка ожидающих лицензий... - + Checking pending licenses... + Проверка ожидающих лицензий... - -SDK Manager is busy. - -SDK Manager занят. + SDK Manager is busy. + SDK Manager занят. Android SDK Changes @@ -2081,12 +2073,8 @@ To hide a sticky splash screen, invoke QtAndroid::hideSplashScreen(). Ошибка. - Done - - - Готово - - + Done + Готово Installing @@ -4428,7 +4416,7 @@ This flag will allow push to proceed. Этот флаг позволяет выполнить эту операцию. - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Например: «https://[имя[:пароль]@]адрес[:порт]/[путь]». @@ -4874,7 +4862,7 @@ For example, "Revision: 15" will leave the branch at revision 15. - QtC::Bookmarks + QtC::TextEditor Add Bookmark Добавить закладку @@ -10572,8 +10560,8 @@ Double-click to edit item. Не удалось запустить «%1»: %2 - A timeout occurred running "%1" - Истекло время работы «%1» + A timeout occurred running "%1". + Истекло время работы «%1». "%1" crashed. @@ -24645,7 +24633,7 @@ Error: %5 Спрашивать имя пользователя и пароль - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". Например: «https://[имя[:пароль]@]адрес[:порт]/[путь]». @@ -29135,20 +29123,20 @@ to project "%2". Управление сессиями - &New - &Новая + &New... + &Новая... - &Rename - &Переименовать + &Rename... + &Переименовать... - C&lone - &Копировать + C&lone... + &Копировать... - &Delete - &Удалить + &Delete... + &Удалить... What is a Session? @@ -34855,8 +34843,8 @@ Neither the path to the library nor the path to its includes is added to the .pr Не удалось создать файл ресурсов: %1 - A timeout occurred running "%1" - Истекло время работы «%1» + A timeout occurred running "%1". + Истекло время работы «%1». "%1" crashed. @@ -40173,10 +40161,8 @@ If you do not have a private key yet, you can also create one here. - Deployment to this device will not work out of the box. - - Развёртывание на это устройство не работает «из коробки». - + Deployment to this device will not work out of the box. + Развёртывание на это устройство не работает «из коробки». rsync is functional. @@ -43005,10 +42991,6 @@ Specifies how backspace interacts with indentation. Edit... Изменить... - - Remove - Удалить - Export... Экспорт... diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts index 8292124dfe6..e5b3b87b02b 100644 --- a/share/qtcreator/translations/qtcreator_sl.ts +++ b/share/qtcreator/translations/qtcreator_sl.ts @@ -102,7 +102,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark Dodaj zaznamek @@ -6752,20 +6752,20 @@ enojen »Vstopi« za oddajo signala pa vas bo privedel neposredno do ustrezne pr Upravljalnik sej - &New - &Nova + &New... + &Nova... - &Rename - Pre&imenuj + &Rename... + Pre&imenuj... - C&lone - &Podvoji + C&lone... + &Podvoji... - &Delete - &Izbriši + &Delete... + &Izbriši... &Open @@ -15893,8 +15893,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi.Ni moč zagnati »%1«: %2 - A timeout occurred running '%1' - Med poganjanjem »%1« je potekel čas + A timeout occurred running "%1". + Med poganjanjem »%1« je potekel čas. '%1' crashed. @@ -18441,14 +18441,6 @@ Potreben je Qt 4.7.4 ali novejši in nabor komponent za vašo različico Qt.Group: Skupina: - - Add - Dodaj - - - Remove - Odstrani - Revert Built-in Povrni vgrajene diff --git a/share/qtcreator/translations/qtcreator_uk.ts b/share/qtcreator/translations/qtcreator_uk.ts index 47c7b8a0931..432f5ab3a24 100644 --- a/share/qtcreator/translations/qtcreator_uk.ts +++ b/share/qtcreator/translations/qtcreator_uk.ts @@ -764,8 +764,8 @@ This flag will allow push to proceed. - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Наприклад: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Наприклад: "https://[user[:pass]@]host[:port]/[path]". Ignores differences between branches and overwrites @@ -795,7 +795,7 @@ Local pulls are not applied to the master branch. - QtC::Bookmarks + QtC::TextEditor Add Bookmark Додати закладку @@ -852,10 +852,6 @@ Local pulls are not applied to the master branch. Rename Bookmark Перейменувати закладку - - Add - Додати - Move Up Пересунути вгору @@ -10794,8 +10790,8 @@ Ids must begin with a lowercase letter. - For example: 'https://[user[:pass]@]host[:port]/[path]'. - Наприклад: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". + Наприклад: "https://[user[:pass]@]host[:port]/[path]". @@ -13203,20 +13199,20 @@ to project "%2". Менеджер сесій - &New - &Нова + &New... + &Нова... - &Rename - Перей&менувати + &Rename... + Перей&менувати... - C&lone - &Клонувати + C&lone... + &Клонувати... - &Delete - Ви&далити + &Delete... + Ви&далити... &Open @@ -21652,10 +21648,6 @@ Specifies how backspace interacts with indentation. Edit... Редагувати... - - Remove - Видалити - Export... Експортувати... @@ -25113,7 +25105,7 @@ To add the Qt versions, select Options > Build & Run > Qt Versions. - QtC::Bookmarks + QtC::TextEditor Alt+Meta+M Alt+Meta+M @@ -28986,7 +28978,7 @@ cannot be found in the path. - QtC::Bookmarks + QtC::TextEditor Note text: Текст примітки: @@ -35653,8 +35645,8 @@ Install an SDK of at least API version %1. Неможливо запустити "%1": %2 - A timeout occurred running "%1" - Час очікування вичерпано для "%1" + A timeout occurred running "%1". + Час очікування вичерпано для "%1". "%1" crashed. diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index 5d038593cc8..ff7a8e061ce 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -597,12 +597,12 @@ The minimum API level required by the kit is %1. 安卓部署设定文件不存在,未构建 APK。 - The Android build folder %1 was not found and could not be created. - 未找到安卓构建文件夹%1,并无法创建。 + The Android build folder "%1" was not found and could not be created. + 未找到安卓构建文件夹“%1“,并无法创建。 - Cannot copy the target's lib file %1 to the Android build folder %2. - 无法复制目标的库文件%1到安卓构建目录%2。 + Cannot copy the target's lib file "%1" to the Android build folder "%2". + 无法复制目标的库文件“%1“到安卓构建目录“%2“。 Cannot copy file "%1" to Android build libs folder "%2". @@ -1083,8 +1083,8 @@ This cannot be undone. 单击以选择... - Images (*.png *.jpg *.jpeg *.webp *.svg) - 图片(*.png *.jpg *.jpeg *.webp *.svg) + Images %1 + 图片 %1 Include default permissions for Qt modules. @@ -1211,8 +1211,8 @@ This cannot be undone. 服务无效。无法保存 Manifest。请在保存前修正服务定义。 - <b>Make install:</b> Copy App Files to %1 - <b>Make install:</b> 拷贝应用到 %1 + <b>Make install:</b> Copy App Files to "%1" + <b>Make install:</b> 拷贝应用到 “%1“ "%1" step has an invalid C++ toolchain. @@ -1308,8 +1308,8 @@ This cannot be undone. 无法为 %1 创建文件 “%2” - A timeout occurred running "%1" - 运行 “%1” 时超时 + A timeout occurred running "%1". + 运行 “%1” 时超时。 Crash while creating file for %1 "%2" @@ -1432,8 +1432,8 @@ This cannot be undone. 下载 SDK 工具 - Could not open %1 for writing: %2. - 无法打开 %1 以写入:%2。 + Could not open "%1" for writing: %2. + 无法打开 “%1“ 以写入:%2。 Downloading Android SDK Tools from URL %1 has failed: %2. @@ -1460,12 +1460,8 @@ This cannot be undone. 失败. - Done - - - 完成 - - + Done + 完成 Installing @@ -1488,12 +1484,8 @@ This cannot be undone. 安卓 SDK 管理器 - License command failed. - - - 许可命令失败。 - - + License command failed. + 许可命令失败。 Android SDK Manager @@ -1580,21 +1572,16 @@ Install them manually after the current operation is done. 缺失安卓 SDK 安装所必要的包。你想安装这些缺失包吗? - Checking pending licenses... - - 正在检查待处理的许可证... - + Checking pending licenses... + 正在检查待处理的许可证... - The installation of Android SDK packages may fail if the respective licenses are not accepted. - + The installation of Android SDK packages may fail if the respective licenses are not accepted. 如果不接受相应的许可证,安卓 SDK 包安装可能会失败。 - -SDK Manager is busy. - -SDK 管理器繁忙。 + SDK Manager is busy. + SDK 管理器繁忙。 %n Android SDK packages shall be updated. @@ -2999,8 +2986,8 @@ Warning: Plain text misses some information, such as duration. 基准测试指标 - <p>Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable.</p> - <p>官方不支持单一可执行程序中的多个测试用例,根据具体实现,它可能会被执行,但绝不会被明确选择</p> + Multiple testcases inside a single executable are not officially supported. Depending on the implementation they might get executed or not, but never will be explicitly selectable. + 官方不支持单一可执行程序中的多个测试用例,根据具体实现,它可能会被执行,但绝不会被明确选择 inherited @@ -4127,7 +4114,7 @@ Warning: this is an experimental feature and might lead to failing to execute th 目标驱动器: - Starting %1 ... + Starting %1... 启动 %1... @@ -4718,7 +4705,7 @@ For example, "Revision: 15" will leave the branch at revision 15.未提交... - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". 例如:“https://[user[:pass]@]host[:port]/[path]”。 @@ -5199,7 +5186,7 @@ Local pulls are not applied to the master branch. - QtC::Bookmarks + QtC::TextEditor Bookmarks 书签 @@ -8551,7 +8538,7 @@ Set a valid executable first. - A timeout occurred running "%1" + A timeout occurred running "%1". @@ -11567,7 +11554,7 @@ to version control (%2) - Defines the amount of time Qt Creator waits before sending document changes to the server. + Defines the amount of time %1 waits before sending document changes to the server. If the document changes again while waiting, this timeout resets. @@ -18093,8 +18080,7 @@ Rebuilding the project might help. - Running "%1" - + Running "%1" @@ -18159,7 +18145,7 @@ Rebuilding the project might help. - Host directories to mount into the container + Host directories to mount into the container. @@ -18519,9 +18505,7 @@ Rebuilding the project might help. - If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. - - + If you temporarily disable %1, the following plugins that depend on it are also disabled: %2. @@ -18911,7 +18895,7 @@ will also disable the following plugins: - Type Meta-Shift-Y, Meta-Shift-Y to quit FakeVim mode. + Type Control-Shift-Y, Control-Shift-Y to quit FakeVim mode. @@ -24591,7 +24575,7 @@ Error: %5 Alt+G,Alt+C - For example: 'https://[user[:pass]@]host[:port]/[path]'. + For example: "https://[user[:pass]@]host[:port]/[path]". @@ -28204,16 +28188,16 @@ to project "%2". 会话管理器 - &New - 新建(&N) + &New... + 新建(&N)... - C&lone - 克隆(&L) + C&lone... + 克隆(&L)... - &Delete - 删除(&D) + &Delete... + 删除(&D)... &Open @@ -33343,7 +33327,7 @@ The following files or directories are missing: - A timeout occurred running "%1" + A timeout occurred running "%1". @@ -39240,18 +39224,11 @@ Control process failed to start. - Failed to start "%1": %2 - + Failed to start "%1": %2 - "%1" crashed. - - - - - "%1" failed with exit code %2: %3 - + "%1" failed with exit code %2: %3 @@ -39265,8 +39242,7 @@ Control process failed to start. - Deployment to this device will not work out of the box. - + Deployment to this device will not work out of the box. @@ -41397,7 +41373,7 @@ Destination file "%1" already exists. Could not merge results into single results.xml. -Failed to open file "%1" +Failed to open file "%1". diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts index f5c68598b06..d21d4d9bca2 100644 --- a/share/qtcreator/translations/qtcreator_zh_TW.ts +++ b/share/qtcreator/translations/qtcreator_zh_TW.ts @@ -32,7 +32,7 @@ - QtC::Bookmarks + QtC::TextEditor Add Bookmark 新增書籤 @@ -6472,19 +6472,19 @@ Add, modify, and remove document filters, which determine the documentation set 工作階段管理器 - &New - 新增(&N) + &New... + 新增(&N)... - &Rename - 重新命名(&R) + &Rename... + 重新命名(&R)... - C&lone - 複製(&L) + C&lone... + 複製(&L)... - &Delete + &Delete... 刪除(&D) @@ -13569,8 +13569,8 @@ Requires <b>Qt 4.7.4</b> or newer. 無法啟動 '%1':%2 - A timeout occurred running '%1' - 執行 '%1' 發生逾時 + A timeout occurred running "%1". + 執行 '%1' 發生逾時。 '%1' crashed. @@ -25150,10 +25150,6 @@ Specifies how backspace interacts with indentation. Edit... 編輯... - - Remove - 移除 - Export... 匯出... @@ -25244,10 +25240,6 @@ Specifies how backspace interacts with indentation. Group: 群組: - - Add - 新增 - Revert Built-in 回復到內建 @@ -28363,7 +28355,7 @@ Please choose a valid package name for your application (e.g. "org.example. - QtC::Bookmarks + QtC::TextEditor Alt+Meta+M diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 940ae30a596..8e19bacc680 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -4,17 +4,17 @@ if(NOT IS_ABSOLUTE "${IDE_ICON_PATH}") set(IDE_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${IDE_ICON_PATH}") endif() -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/app_version.h - DESTINATION ${IDE_HEADER_INSTALL_PATH}/src/app - COMPONENT Devel EXCLUDE_FROM_ALL -) +if (NOT IS_ABSOLUTE ${IDE_LOGO_PATH}) + set(IDE_LOGO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${IDE_LOGO_PATH}") +endif() +configure_file(app_logo.qrc.cmakein app_logo_cmake.qrc) add_qtc_executable(qtcreator DEFINES IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\" DEPENDS Aggregation ExtensionSystem Qt::Core Qt::Widgets Utils shared_qtsingleapplication app_version SOURCES main.cpp + ${CMAKE_CURRENT_BINARY_DIR}/app_logo_cmake.qrc ../tools/qtcreatorcrashhandler/crashhandlersetup.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.h PROPERTIES WIN32_EXECUTABLE ON @@ -151,3 +151,13 @@ if(BUILD_WITH_CRASHPAD) DESTINATION "${IDE_LIBEXEC_PATH}" ) endif() + +if ((NOT WIN32) AND (NOT APPLE)) + # install logo + foreach(size 16 24 32 48 64 128 256 512) + install( + FILES ${IDE_LOGO_PATH}/images/logo/${size}/QtProject-qtcreator.png + DESTINATION share/icons/hicolor/${size}x${size}/apps + ) + endforeach() +endif() diff --git a/src/app/app.qbs b/src/app/app.qbs index a0b30ae2ae4..5c293781c29 100644 --- a/src/app/app.qbs +++ b/src/app/app.qbs @@ -40,7 +40,6 @@ QtcProduct { : ["$ORIGIN/../" + qtc.libDirName + "/qtcreator"] cpp.includePaths: [ project.sharedSourcesDir + "/qtsingleapplication", - project.sharedSourcesDir + "/qtlockedfile", ] cpp.frameworks: base.concat(qbs.targetOS.contains("macos") ? ["Foundation"] : []) @@ -52,13 +51,13 @@ QtcProduct { files: [ "app-Info.plist", + "app_logo.qrc", "main.cpp", "qtcreator.xcassets", "../shared/qtsingleapplication/qtsingleapplication.h", "../shared/qtsingleapplication/qtsingleapplication.cpp", "../shared/qtsingleapplication/qtlocalpeer.h", "../shared/qtsingleapplication/qtlocalpeer.cpp", - "../shared/qtlockedfile/qtlockedfile.cpp", "../tools/qtcreatorcrashhandler/crashhandlersetup.cpp", "../tools/qtcreatorcrashhandler/crashhandlersetup.h" ] @@ -84,22 +83,6 @@ QtcProduct { qbs.installDir: "bin" } - Group { - name: "QtLockedFile_unix" - condition: qbs.targetOS.contains("unix") - files: [ - "../shared/qtlockedfile/qtlockedfile_unix.cpp" - ] - } - - Group { - name: "QtLockedFile_win" - condition: qbs.targetOS.contains("windows") - files: [ - "../shared/qtlockedfile/qtlockedfile_win.cpp" - ] - } - Group { name: "main_macos" condition: qbs.targetOS.contains("macos") diff --git a/src/plugins/coreplugin/core_logo.qrc b/src/app/app_logo.qrc similarity index 100% rename from src/plugins/coreplugin/core_logo.qrc rename to src/app/app_logo.qrc diff --git a/src/plugins/coreplugin/core_logo.qrc.cmakein b/src/app/app_logo.qrc.cmakein similarity index 100% rename from src/plugins/coreplugin/core_logo.qrc.cmakein rename to src/app/app_logo.qrc.cmakein diff --git a/src/app/app_version.h.cmakein b/src/app/app_version.h.cmakein index 5b9098f056e..6c1fb9bb297 100644 --- a/src/app/app_version.h.cmakein +++ b/src/app/app_version.h.cmakein @@ -28,5 +28,8 @@ const char IDE_REVISION_URL[] = "${IDE_REVISION_URL}"; // changes the path where the settings are saved to const char IDE_SETTINGSVARIANT_STR[] = "${IDE_SETTINGSVARIANT}"; +// added extension for project user settings +const char IDE_PROJECT_USER_FILE_EXTENSION[] = "${PROJECT_USER_FILE_EXTENSION}"; + } // Constants } // Core diff --git a/src/app/app_version.h.in b/src/app/app_version.h.in deleted file mode 100644 index 605016b3762..00000000000 --- a/src/app/app_version.h.in +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace Core { -namespace Constants { - -#define STRINGIFY_INTERNAL(x) #x -#define STRINGIFY(x) STRINGIFY_INTERNAL(x) - -const char IDE_DISPLAY_NAME[] = \"$${IDE_DISPLAY_NAME}\"; -const char IDE_ID[] = \"$${IDE_ID}\"; -const char IDE_CASED_ID[] = \"$${IDE_CASED_ID}\"; - -#define IDE_VERSION $${QTCREATOR_VERSION} -#define IDE_VERSION_STR STRINGIFY(IDE_VERSION) -#define IDE_VERSION_DISPLAY_DEF $${QTCREATOR_DISPLAY_VERSION} -#define IDE_VERSION_COMPAT_DEF $${QTCREATOR_COMPAT_VERSION} - -#define IDE_VERSION_MAJOR $$replace(QTCREATOR_VERSION, "^(\\d+)\\.\\d+\\.\\d+(-.*)?$", \\1) -#define IDE_VERSION_MINOR $$replace(QTCREATOR_VERSION, "^\\d+\\.(\\d+)\\.\\d+(-.*)?$", \\1) -#define IDE_VERSION_RELEASE $$replace(QTCREATOR_VERSION, "^\\d+\\.\\d+\\.(\\d+)(-.*)?$", \\1) - -const char IDE_VERSION_LONG[] = IDE_VERSION_STR; -const char IDE_VERSION_DISPLAY[] = STRINGIFY(IDE_VERSION_DISPLAY_DEF); -const char IDE_VERSION_COMPAT[] = STRINGIFY(IDE_VERSION_COMPAT_DEF); -const char IDE_AUTHOR[] = \"The Qt Company Ltd\"; -const char IDE_YEAR[] = \"$${QTCREATOR_COPYRIGHT_YEAR}\"; - -#ifdef IDE_REVISION -const char IDE_REVISION_STR[] = STRINGIFY(IDE_REVISION); -#else -const char IDE_REVISION_STR[] = \"\"; -#endif - -const char IDE_REVISION_URL[] = \"$${IDE_REVISION_URL}\"; - -// changes the path where the settings are saved to -#ifdef IDE_SETTINGSVARIANT -const char IDE_SETTINGSVARIANT_STR[] = STRINGIFY(IDE_SETTINGSVARIANT); -#else -const char IDE_SETTINGSVARIANT_STR[] = \"QtProject\"; -#endif - -#undef IDE_VERSION_COMPAT_DEF -#undef IDE_VERSION_DISPLAY_DEF -#undef IDE_VERSION -#undef IDE_VERSION_STR -#undef STRINGIFY -#undef STRINGIFY_INTERNAL - -} // Constants -} // Core diff --git a/src/app/app_version_header.qbs b/src/app/app_version_header.qbs index 59cd014df7c..dace34d4ede 100644 --- a/src/app/app_version_header.qbs +++ b/src/app/app_version_header.qbs @@ -6,15 +6,10 @@ Product { type: "hpp" Group { - files: ["app_version.h.in"] + files: ["app_version.h.cmakein"] fileTags: ["hpp.in"] } - Group { - name: "other" - files: "app_version.h.cmakein" - } - Depends { name: "qtc" } Rule { @@ -31,32 +26,36 @@ Product { cmd.sourceCode = function() { var file = new TextFile(input.filePath); var content = file.readAll(); - // replace quoted quotes - content = content.replace(/\\\"/g, '"'); // replace Windows line endings if (onWindows) content = content.replace(/\r\n/g, "\n"); // replace the magic qmake incantations - content = content.replace(/(\n#define IDE_VERSION_DISPLAY_DEF) .+\n/, "$1 " - + product.moduleProperty("qtc", "qtcreator_display_version") + "\n"); - content = content.replace(/(\n#define IDE_VERSION_COMPAT_DEF) .+\n/, "$1 " - + product.moduleProperty("qtc", "qtcreator_compat_version") + "\n"); - content = content.replace(/(\n#define IDE_VERSION) .+\n/, "$1 " - + product.moduleProperty("qtc", "qtcreator_version") + "\n"); - content = content.replace(/(\n#define IDE_VERSION_MAJOR) .+\n/, "$1 " - + product.moduleProperty("qtc", "ide_version_major") + "\n"); - content = content.replace(/(\n#define IDE_VERSION_MINOR) .+\n/, "$1 " - + product.moduleProperty("qtc", "ide_version_minor") + "\n"); - content = content.replace(/(\n#define IDE_VERSION_RELEASE) .+\n/, "$1 " - + product.moduleProperty("qtc", "ide_version_release") + "\n"); - content = content.replace("$${QTCREATOR_COPYRIGHT_YEAR}", + content = content.replace("${IDE_VERSION_DISPLAY}", + product.moduleProperty("qtc", "qtcreator_display_version")); + content = content.replace("${IDE_VERSION_COMPAT}", + product.moduleProperty("qtc", "qtcreator_compat_version")); + content = content.replace("${PROJECT_VERSION}", + product.moduleProperty("qtc", "qtcreator_version")); + content = content.replace("${PROJECT_VERSION_MAJOR}", + product.moduleProperty("qtc", "ide_version_major")); + content = content.replace("${PROJECT_VERSION_MINOR}", + product.moduleProperty("qtc", "ide_version_minor")); + content = content.replace("${PROJECT_VERSION_PATCH}", + product.moduleProperty("qtc", "ide_version_release")); + content = content.replace("${IDE_COPYRIGHT_YEAR}", product.moduleProperty("qtc", "qtcreator_copyright_year")); - content = content.replace("$${IDE_DISPLAY_NAME}", + content = content.replace("${IDE_DISPLAY_NAME}", product.moduleProperty("qtc", "ide_display_name")); - content = content.replace("$${IDE_ID}", + content = content.replace("${IDE_ID}", product.moduleProperty("qtc", "ide_id")); - content = content.replace("$${IDE_CASED_ID}", + content = content.replace("${IDE_CASED_ID}", product.moduleProperty("qtc", "ide_cased_id")); + content = content.replace(/\n#cmakedefine IDE_REVISION\n/, ""); + content = content.replace("${IDE_REVISION_STR}", ""); + content = content.replace("${IDE_REVISION_URL}", ""); + content = content.replace("${PROJECT_USER_FILE_EXTENSION}", + product.moduleProperty("qtc", "ide_user_file_extension")); + content = content.replace("${IDE_SETTINGSVARIANT}", "QtProject"); file = new TextFile(output.filePath, TextFile.WriteOnly); file.truncate(); file.write(content); diff --git a/src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png b/src/app/images/logo/128/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/128/QtProject-qtcreator.png rename to src/app/images/logo/128/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png b/src/app/images/logo/16/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/16/QtProject-qtcreator.png rename to src/app/images/logo/16/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png b/src/app/images/logo/24/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/24/QtProject-qtcreator.png rename to src/app/images/logo/24/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png b/src/app/images/logo/256/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/256/QtProject-qtcreator.png rename to src/app/images/logo/256/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png b/src/app/images/logo/32/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/32/QtProject-qtcreator.png rename to src/app/images/logo/32/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png b/src/app/images/logo/48/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/48/QtProject-qtcreator.png rename to src/app/images/logo/48/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png b/src/app/images/logo/512/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/512/QtProject-qtcreator.png rename to src/app/images/logo/512/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png b/src/app/images/logo/64/QtProject-qtcreator.png similarity index 100% rename from src/plugins/coreplugin/images/logo/64/QtProject-qtcreator.png rename to src/app/images/logo/64/QtProject-qtcreator.png diff --git a/src/plugins/coreplugin/images/logo/logo.qbs b/src/app/images/logo/logo.qbs similarity index 100% rename from src/plugins/coreplugin/images/logo/logo.qbs rename to src/app/images/logo/logo.qbs diff --git a/src/app/main.cpp b/src/app/main.cpp index 46233ba19ca..036d169c063 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -11,12 +11,15 @@ #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -278,16 +281,18 @@ static Utils::QtcSettings *createUserSettings() static void setHighDpiEnvironmentVariable() { - if (Utils::HostOsInfo::isMacHost() || qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY")) + if (Utils::StyleHelper::defaultHighDpiScaleFactorRoundingPolicy() + == Qt::HighDpiScaleFactorRoundingPolicy::Unset + || qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY")) return; - std::unique_ptr settings(createUserSettings()); + std::unique_ptr settings(createUserSettings()); - const bool defaultValue = Utils::HostOsInfo::isWindowsHost(); - const bool enableHighDpiScaling = settings->value("Core/EnableHighDpiScaling", defaultValue).toBool(); - const auto policy = enableHighDpiScaling ? Qt::HighDpiScaleFactorRoundingPolicy::PassThrough - : Qt::HighDpiScaleFactorRoundingPolicy::Floor; - QGuiApplication::setHighDpiScaleFactorRoundingPolicy(policy); + using Policy = Qt::HighDpiScaleFactorRoundingPolicy; + const Policy defaultPolicy = Utils::StyleHelper::defaultHighDpiScaleFactorRoundingPolicy(); + const Policy userPolicy = settings->value("Core/HighDpiScaleFactorRoundingPolicy", + int(defaultPolicy)).value(); + QGuiApplication::setHighDpiScaleFactorRoundingPolicy(userPolicy); } void setPixmapCacheLimit() @@ -411,7 +416,7 @@ QStringList lastSessionArgument() // and src\tools\qml2puppet\qml2puppet\qmlpuppet.cpp -> QString crashReportsPath() QString crashReportsPath() { - std::unique_ptr settings(createUserSettings()); + std::unique_ptr settings(createUserSettings()); QFileInfo(settings->fileName()).path() + "/crashpad_reports"; if (Utils::HostOsInfo::isMacHost()) return QFileInfo(createUserSettings()->fileName()).path() + "/crashpad_reports"; @@ -520,8 +525,6 @@ int main(int argc, char **argv) } qputenv("QSG_RHI_BACKEND", "opengl"); - QGuiApplication::setHighDpiScaleFactorRoundingPolicy( - Qt::HighDpiScaleFactorRoundingPolicy::Round); if (qEnvironmentVariableIsSet("QTCREATOR_DISABLE_NATIVE_MENUBAR") || qgetenv("XDG_CURRENT_DESKTOP").startsWith("Unity")) { @@ -607,11 +610,20 @@ int main(int argc, char **argv) // Re-setup install settings for real setupInstallSettings(options.installSettingsPath); Utils::QtcSettings *settings = createUserSettings(); - Utils::QtcSettings *globalSettings + Utils::QtcSettings *installSettings = new Utils::QtcSettings(QSettings::IniFormat, QSettings::SystemScope, QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), QLatin1String(Core::Constants::IDE_CASED_ID)); + // warn if -installsettings points to a place where no install settings are located + if (!options.installSettingsPath.isEmpty() && !QFileInfo::exists(installSettings->fileName())) { + displayError(QLatin1String("The install settings \"%1\" do not exist. The %2 option must " + "point to a path with existing settings, excluding the %3 part " + "of the path.") + .arg(QDir::toNativeSeparators(installSettings->fileName()), + INSTALL_SETTINGS_OPTION, + Core::Constants::IDE_SETTINGSVARIANT_STR)); + } Utils::TerminalCommand::setSettings(settings); setPixmapCacheLimit(); loadFonts(); @@ -640,13 +652,27 @@ int main(int argc, char **argv) PluginManager pluginManager; PluginManager::setPluginIID(QLatin1String("org.qt-project.Qt.QtCreatorPlugin")); - PluginManager::setGlobalSettings(globalSettings); + PluginManager::setInstallSettings(installSettings); PluginManager::setSettings(settings); + PluginManager::startProfiling(); + + Utils::BaseAspect::setQtcSettings(settings); + + using namespace Core; + Utils::AppInfo info; + info.author = Constants::IDE_AUTHOR; + info.year = Constants::IDE_YEAR; + info.displayVersion = Constants::IDE_VERSION_DISPLAY; + info.id = Constants::IDE_ID; + info.revision = Constants::IDE_REVISION_STR; + info.revisionUrl = Constants::IDE_REVISION_URL; + info.userFileExtension = Constants::IDE_PROJECT_USER_FILE_EXTENSION; + Utils::Internal::setAppInfo(info); QTranslator translator; QTranslator qtTranslator; QStringList uiLanguages = QLocale::system().uiLanguages(); - QString overrideLanguage = settings->value(QLatin1String("General/OverrideLanguage")).toString(); + QString overrideLanguage = settings->value("General/OverrideLanguage").toString(); if (!overrideLanguage.isEmpty()) uiLanguages.prepend(overrideLanguage); if (!options.uiLanguage.isEmpty()) @@ -678,7 +704,7 @@ int main(int argc, char **argv) if (!overrideCodecForLocale.isEmpty()) QTextCodec::setCodecForLocale(QTextCodec::codecForName(overrideCodecForLocale)); - app.setDesktopFileName("org.qt-project.qtcreator.desktop"); + app.setDesktopFileName("org.qt-project.qtcreator"); // Make sure we honor the system's proxy settings QNetworkProxyFactory::setUseSystemConfiguration(true); diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp index 62f4c570c79..848f61285cb 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp @@ -105,6 +105,35 @@ int TranslationUnit::commentCount() const const Token &TranslationUnit::commentAt(int index) const { return _comments->at(index); } +std::vector TranslationUnit::allTokens() const +{ + std::vector all; + int tokIndex = 0; + int commentIndex = 0; + while (true) { + if (tokIndex == tokenCount()) { + for (int i = commentIndex; i < commentCount(); ++i) + all.push_back(commentAt(i)); + break; + } + if (commentIndex == commentCount()) { + for (int i = tokIndex; i < tokenCount(); ++i) + all.push_back(tokenAt(i)); + break; + } + const Token &tok = tokenAt(tokIndex); + const Token &comment = commentAt(commentIndex); + if (tok.utf16charsBegin() < comment.utf16charsBegin()) { + all.push_back(tok); + ++tokIndex; + } else { + all.push_back(comment); + ++commentIndex; + } + } + return all; +} + const Identifier *TranslationUnit::identifier(int index) const { return tokenAt(index).identifier; } @@ -379,35 +408,57 @@ int TranslationUnit::findColumnNumber(int utf16CharOffset, int lineNumber) const return utf16CharOffset - _lineOffsets[lineNumber]; } -void TranslationUnit::getTokenPosition(int index, - int *line, - int *column, - const StringLiteral **fileName) const -{ return getPosition(tokenAt(index).utf16charsBegin(), line, column, fileName); } - int TranslationUnit::getTokenPositionInDocument(int index, const QTextDocument *doc) const { - int line, column; - getTokenPosition(index, &line, &column); - return Utils::Text::positionInText(doc, line, column); + return getTokenPositionInDocument(_tokens->at(index), doc); } int TranslationUnit::getTokenEndPositionInDocument(int index, const QTextDocument *doc) const { - int line, column; - getTokenEndPosition(index, &line, &column); - return Utils::Text::positionInText(doc, line, column); + return getTokenEndPositionInDocument(_tokens->at(index), doc); } -void TranslationUnit::getTokenStartPosition(int index, int *line, - int *column, - const StringLiteral **fileName) const -{ return getPosition(tokenAt(index).utf16charsBegin(), line, column, fileName); } +void TranslationUnit::getTokenPosition(int index, int *line, + int *column, + const StringLiteral **fileName) const +{ + return getTokenPosition(_tokens->at(index), line, column, fileName); +} void TranslationUnit::getTokenEndPosition(int index, int *line, int *column, const StringLiteral **fileName) const -{ return getPosition(tokenAt(index).utf16charsEnd(), line, column, fileName); } +{ + return getTokenEndPosition(_tokens->at(index), line, column, fileName); +} + +void TranslationUnit::getTokenPosition(const Token &token, int *line, int *column, + const StringLiteral **fileName) const +{ + return getPosition(token.utf16charsBegin(), line, column, fileName); +} + +void TranslationUnit::getTokenEndPosition(const Token &token, int *line, + int *column, const StringLiteral **fileName) const +{ + return getPosition(token.utf16charsEnd(), line, column, fileName); +} + +int TranslationUnit::getTokenPositionInDocument(const Token token, + const QTextDocument *doc) const +{ + int line, column; + getTokenPosition(token, &line, &column); + return Utils::Text::positionInText(doc, line, column); +} + +int TranslationUnit::getTokenEndPositionInDocument(const Token &token, + const QTextDocument *doc) const +{ + int line, column; + getTokenEndPosition(token, &line, &column); + return Utils::Text::positionInText(doc, line, column); +} void TranslationUnit::getPosition(int utf16charOffset, int *line, diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.h b/src/libs/3rdparty/cplusplus/TranslationUnit.h index 9694177a752..40f79d0091d 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.h +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.h @@ -65,6 +65,9 @@ public: int commentCount() const; const Token &commentAt(int index) const; + // Including comments. + std::vector allTokens() const; + int matchingBrace(int index) const; const Identifier *identifier(int index) const; const Literal *literal(int index) const; @@ -110,27 +113,27 @@ public: void resetAST(); void release(); - void getTokenStartPosition(int index, int *line, - int *column = nullptr, - const StringLiteral **fileName = nullptr) const; - + void getTokenPosition(int index, int *line, + int *column = nullptr, + const StringLiteral **fileName = nullptr) const; void getTokenEndPosition(int index, int *line, int *column = nullptr, const StringLiteral **fileName = nullptr) const; - void getPosition(int utf16charOffset, int *line, int *column = nullptr, const StringLiteral **fileName = nullptr) const; - void getTokenPosition(int index, - int *line, - int *column = nullptr, - const StringLiteral **fileName = nullptr) const; - int getTokenPositionInDocument(int index, const QTextDocument *doc) const; int getTokenEndPositionInDocument(int index, const QTextDocument *doc) const; + void getTokenPosition(const Token &token, int *line, int *column = nullptr, + const StringLiteral **fileName = nullptr) const; + void getTokenEndPosition(const Token &token, int *line, int *column = nullptr, + const StringLiteral **fileName = nullptr) const; + int getTokenPositionInDocument(const Token token, const QTextDocument *doc) const; + int getTokenEndPositionInDocument(const Token &token, const QTextDocument *doc) const; + void pushLineOffset(int offset); void pushPreprocessorLine(int utf16charOffset, int line, diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.cpp b/src/libs/3rdparty/libptyqt/conptyprocess.cpp index cb18b332066..6464dbeb347 100644 --- a/src/libs/3rdparty/libptyqt/conptyprocess.cpp +++ b/src/libs/3rdparty/libptyqt/conptyprocess.cpp @@ -12,6 +12,76 @@ #define READ_INTERVAL_MSEC 500 +//ConPTY is available only on Windows 10 released after 1903 (19H1) Windows release +class WindowsContext +{ +private: + WindowsContext() {} + +public: + typedef HRESULT (*CreatePseudoConsolePtr)( + COORD size, // ConPty Dimensions + HANDLE hInput, // ConPty Input + HANDLE hOutput, // ConPty Output + DWORD dwFlags, // ConPty Flags + HPCON* phPC); // ConPty Reference + + typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size); + + typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC); + + static WindowsContext &instance() + { + static WindowsContext ctx; + return ctx; + } + + bool init() + { + //already initialized + if (createPseudoConsole) + return true; + + //try to load symbols from library + //if it fails -> we can't use ConPty API + HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0); + + if (kernel32Handle != nullptr) + { + createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole"); + resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole"); + closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole"); + if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL) + { + m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions"); + return false; + } + } + else + { + m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32"); + return false; + } + + return true; + } + + QString lastError() + { + return m_lastError; + } + +public: + //vars + CreatePseudoConsolePtr createPseudoConsole{nullptr}; + ResizePseudoConsolePtr resizePseudoConsole{nullptr}; + ClosePseudoConsolePtr closePseudoConsole{nullptr}; + +private: + QString m_lastError; +}; + + HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows) { HRESULT hr{ E_UNEXPECTED }; @@ -23,7 +93,7 @@ HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0)) { // Create the Pseudo Console of the required size, attached to the PTY-end of the pipes - hr = m_winContext.createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC); + hr = WindowsContext::instance().createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC); // Note: We can close the handles to the PTY-end of the pipes here // because the handles are dup'ed into the ConHost and will be released @@ -108,7 +178,7 @@ bool ConPtyProcess::startProcess(const QString &executable, qint16 rows) { if (!isAvailable()) { - m_lastError = m_winContext.lastError(); + m_lastError = WindowsContext::instance().lastError(); return false; } @@ -228,7 +298,7 @@ bool ConPtyProcess::resize(qint16 cols, qint16 rows) return false; } - bool res = SUCCEEDED(m_winContext.resizePseudoConsole(m_ptyHandler, {cols, rows})); + bool res = SUCCEEDED(WindowsContext::instance().resizePseudoConsole(m_ptyHandler, {cols, rows})); if (res) { @@ -248,7 +318,7 @@ bool ConPtyProcess::kill() m_aboutToDestruct = true; // Close ConPTY - this will terminate client process if running - m_winContext.closePseudoConsole(m_ptyHandler); + WindowsContext::instance().closePseudoConsole(m_ptyHandler); // Clean-up the pipes if (INVALID_HANDLE_VALUE != m_hPipeOut) @@ -334,7 +404,7 @@ bool ConPtyProcess::isAvailable() qint32 buildNumber = QSysInfo::kernelVersion().split(".").last().toInt(); if (buildNumber < CONPTY_MINIMAL_WINDOWS_VERSION) return false; - return m_winContext.init(); + return WindowsContext::instance().init(); } void ConPtyProcess::moveToThread(QThread *targetThread) diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.h b/src/libs/3rdparty/libptyqt/conptyprocess.h index d4ffd62b7ee..aaf56fe76f1 100644 --- a/src/libs/3rdparty/libptyqt/conptyprocess.h +++ b/src/libs/3rdparty/libptyqt/conptyprocess.h @@ -25,80 +25,6 @@ typedef VOID* HPCON; class QWinEventNotifier; -template -std::vector vectorFromString(const std::basic_string &str) -{ - return std::vector(str.begin(), str.end()); -} - -//ConPTY available only on Windows 10 releazed after 1903 (19H1) Windows release -class WindowsContext -{ -public: - typedef HRESULT (*CreatePseudoConsolePtr)( - COORD size, // ConPty Dimensions - HANDLE hInput, // ConPty Input - HANDLE hOutput, // ConPty Output - DWORD dwFlags, // ConPty Flags - HPCON* phPC); // ConPty Reference - - typedef HRESULT (*ResizePseudoConsolePtr)(HPCON hPC, COORD size); - - typedef VOID (*ClosePseudoConsolePtr)(HPCON hPC); - - WindowsContext() - : createPseudoConsole(nullptr) - , resizePseudoConsole(nullptr) - , closePseudoConsole(nullptr) - { - - } - - bool init() - { - //already initialized - if (createPseudoConsole) - return true; - - //try to load symbols from library - //if it fails -> we can't use ConPty API - HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0); - - if (kernel32Handle != nullptr) - { - createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole"); - resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole"); - closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole"); - if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL) - { - m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions"); - return false; - } - } - else - { - m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32"); - return false; - } - - return true; - } - - QString lastError() - { - return m_lastError; - } - -public: - //vars - CreatePseudoConsolePtr createPseudoConsole; - ResizePseudoConsolePtr resizePseudoConsole; - ClosePseudoConsolePtr closePseudoConsole; - -private: - QString m_lastError; -}; - class PtyBuffer : public QIODevice { friend class ConPtyProcess; @@ -141,7 +67,7 @@ public: virtual QIODevice *notifier(); virtual QByteArray readAll(); virtual qint64 write(const QByteArray &byteArray); - bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: @@ -149,7 +75,6 @@ private: HRESULT initializeStartupInfoAttachedToPseudoConsole(STARTUPINFOEX* pStartupInfo, HPCON hPC); private: - WindowsContext m_winContext; HPCON m_ptyHandler{INVALID_HANDLE_VALUE}; HANDLE m_hPipeIn{INVALID_HANDLE_VALUE}, m_hPipeOut{INVALID_HANDLE_VALUE}; diff --git a/src/libs/3rdparty/libptyqt/iptyprocess.h b/src/libs/3rdparty/libptyqt/iptyprocess.h index 3d974908c86..6005b0efde1 100644 --- a/src/libs/3rdparty/libptyqt/iptyprocess.h +++ b/src/libs/3rdparty/libptyqt/iptyprocess.h @@ -11,6 +11,8 @@ class IPtyProcess { public: enum PtyType { UnixPty = 0, WinPty = 1, ConPty = 2, AutoPty = 3 }; + enum PtyInputFlag { None = 0x0, InputModeHidden = 0x1, }; + Q_DECLARE_FLAGS(PtyInputFlags, PtyInputFlag) IPtyProcess() = default; IPtyProcess(const IPtyProcess &) = delete; @@ -32,7 +34,6 @@ public: virtual QIODevice *notifier() = 0; virtual QByteArray readAll() = 0; virtual qint64 write(const QByteArray &byteArray) = 0; - virtual bool isAvailable() = 0; virtual void moveToThread(QThread *targetThread) = 0; qint64 pid() { return m_pid; } QPair size() { return m_size; } @@ -44,6 +45,8 @@ public: return m_trace; } + PtyInputFlags inputFlags() { return m_inputFlags; } + protected: QString m_shellPath; QString m_lastError; @@ -51,6 +54,9 @@ protected: int m_exitCode{0}; QPair m_size; //cols / rows bool m_trace{false}; + PtyInputFlags m_inputFlags; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(IPtyProcess::PtyInputFlags) + #endif // IPTYPROCESS_H diff --git a/src/libs/3rdparty/libptyqt/ptyqt.cpp b/src/libs/3rdparty/libptyqt/ptyqt.cpp index b3e7aa1b164..06fe4498190 100644 --- a/src/libs/3rdparty/libptyqt/ptyqt.cpp +++ b/src/libs/3rdparty/libptyqt/ptyqt.cpp @@ -10,6 +10,15 @@ #include "unixptyprocess.h" #endif +bool PtyQt::isUsingConPTY() +{ +#ifdef Q_OS_WIN + if (ConPtyProcess::isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty()) + return true; +#endif + + return false; +} IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) { @@ -34,7 +43,7 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) } #ifdef Q_OS_WIN - if (ConPtyProcess().isAvailable() && qgetenv("QTC_USE_WINPTY").isEmpty()) + if (isUsingConPTY()) return new ConPtyProcess(); else return new WinPtyProcess(); @@ -43,3 +52,5 @@ IPtyProcess *PtyQt::createPtyProcess(IPtyProcess::PtyType ptyType) return new UnixPtyProcess(); #endif } + + diff --git a/src/libs/3rdparty/libptyqt/ptyqt.h b/src/libs/3rdparty/libptyqt/ptyqt.h index 23b80d346bb..85f27b33a9b 100644 --- a/src/libs/3rdparty/libptyqt/ptyqt.h +++ b/src/libs/3rdparty/libptyqt/ptyqt.h @@ -7,6 +7,8 @@ class PtyQt { public: static IPtyProcess *createPtyProcess(IPtyProcess::PtyType ptyType); + + static bool isUsingConPTY(); }; #endif // PTYQT_H diff --git a/src/libs/3rdparty/libptyqt/ptyqt.qbs b/src/libs/3rdparty/libptyqt/ptyqt.qbs index 7ff4da9f560..3b56dd8e465 100644 --- a/src/libs/3rdparty/libptyqt/ptyqt.qbs +++ b/src/libs/3rdparty/libptyqt/ptyqt.qbs @@ -1,45 +1,40 @@ -import qbs - -Project { +QtcLibrary { name: "ptyqt" + type: "staticlibrary" - QtcLibrary { - Depends { name: "Qt.core" } - Depends { name: "Qt.network"; condition: qbs.targetOS.contains("windows") } - Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") } + Depends { name: "Qt.core" } + Depends { name: "Qt.network"; condition: qbs.targetOS.contains("windows") } + Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") } - type: "staticlibrary" + files: [ + "iptyprocess.h", + "ptyqt.cpp", + "ptyqt.h", + ] + Group { + name: "ptyqt UNIX files" + condition: qbs.targetOS.contains("unix") files: [ - "iptyprocess.h", - "ptyqt.cpp", - "ptyqt.h", + "unixptyprocess.cpp", + "unixptyprocess.h", ] + } - Group { - name: "ptyqt UNIX files" - condition: qbs.targetOS.contains("unix") - files: [ - "unixptyprocess.cpp", - "unixptyprocess.h", - ] - } + Group { + name: "ptyqt Windows files" + condition: qbs.targetOS.contains("windows") + files: [ + "conptyprocess.cpp", + "conptyprocess.h", + "winptyprocess.cpp", + "winptyprocess.h", + ] + } - Group { - name: "ptyqt Windows files" - condition: qbs.targetOS.contains("windows") - files: [ - "conptyprocess.cpp", - "conptyprocess.h", - "winptyprocess.cpp", - "winptyprocess.h", - ] - } - - Export { - Depends { name: "cpp" } - Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") } - cpp.includePaths: base.concat(exportingProduct.sourceDirectory) - } + Export { + Depends { name: "cpp" } + Depends { name: "winpty"; condition: qbs.targetOS.contains("windows") } + cpp.includePaths: exportingProduct.sourceDirectory } } diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp index a72712c1eb4..e9ec1d590f6 100644 --- a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp +++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp @@ -172,6 +172,12 @@ bool UnixPtyProcess::startProcess(const QString &shellPath, static std::array buffer; int len = ::read(m_shellProcess.m_handleMaster, buffer.data(), buffer.size()); + + struct termios termAttributes; + tcgetattr(m_shellProcess.m_handleMaster, &termAttributes); + const bool isPasswordEntry = !(termAttributes.c_lflag & ECHO) && (termAttributes.c_lflag & ICANON); + m_inputFlags.setFlag(PtyInputFlag::InputModeHidden, isPasswordEntry); + if (len > 0) { m_shellReadBuffer.append(buffer.data(), len); m_shellProcess.emitReadyRead(); @@ -185,14 +191,12 @@ bool UnixPtyProcess::startProcess(const QString &shellPath, }); QStringList varNames; - foreach (QString line, environment) { + for (const QString &line : std::as_const(environment)) varNames.append(line.split("=").first()); - } QProcessEnvironment envFormat; - foreach (QString line, environment) { + for (const QString &line : std::as_const(environment)) envFormat.insert(line.split("=").first(), line.split("=").last()); - } m_shellProcess.setWorkingDirectory(workingDir); m_shellProcess.setProcessEnvironment(envFormat); diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.h b/src/libs/3rdparty/libptyqt/unixptyprocess.h index e4df0d2f74d..f7e3a2e5281 100644 --- a/src/libs/3rdparty/libptyqt/unixptyprocess.h +++ b/src/libs/3rdparty/libptyqt/unixptyprocess.h @@ -54,7 +54,7 @@ public: virtual QIODevice *notifier(); virtual QByteArray readAll(); virtual qint64 write(const QByteArray &byteArray); - virtual bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.cpp b/src/libs/3rdparty/libptyqt/winptyprocess.cpp index 0509bb77c37..7a781bc1953 100644 --- a/src/libs/3rdparty/libptyqt/winptyprocess.cpp +++ b/src/libs/3rdparty/libptyqt/winptyprocess.cpp @@ -77,7 +77,7 @@ bool WinPtyProcess::startProcess(const QString &executable, //env std::wstringstream envBlock; - foreach (QString line, environment) + for (const QString &line : environment) { envBlock << line.toStdWString() << L'\0'; } diff --git a/src/libs/3rdparty/libptyqt/winptyprocess.h b/src/libs/3rdparty/libptyqt/winptyprocess.h index 0bfb27c02c4..ee91fbdf8d6 100644 --- a/src/libs/3rdparty/libptyqt/winptyprocess.h +++ b/src/libs/3rdparty/libptyqt/winptyprocess.h @@ -26,7 +26,7 @@ public: QIODevice *notifier(); QByteArray readAll(); qint64 write(const QByteArray &byteArray); - bool isAvailable(); + static bool isAvailable(); void moveToThread(QThread *targetThread); private: diff --git a/src/libs/3rdparty/libvterm/include/vterm.h b/src/libs/3rdparty/libvterm/include/vterm.h index cb16ff2a044..d08dd382db3 100644 --- a/src/libs/3rdparty/libvterm/include/vterm.h +++ b/src/libs/3rdparty/libvterm/include/vterm.h @@ -13,6 +13,7 @@ extern "C" { #define VTERM_VERSION_MAJOR 0 #define VTERM_VERSION_MINOR 3 +#define VTERM_VERSION_PATCH 3 #define VTERM_CHECK_VERSION \ vterm_check_version(VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR) @@ -258,6 +259,7 @@ typedef enum { VTERM_PROP_REVERSE, // bool VTERM_PROP_CURSORSHAPE, // number VTERM_PROP_MOUSE, // number + VTERM_PROP_FOCUSREPORT, // bool VTERM_N_PROPS } VTermProp; diff --git a/src/libs/3rdparty/libvterm/src/mouse.c b/src/libs/3rdparty/libvterm/src/mouse.c index bd713f8106a..f74abc3d9d2 100644 --- a/src/libs/3rdparty/libvterm/src/mouse.c +++ b/src/libs/3rdparty/libvterm/src/mouse.c @@ -93,7 +93,7 @@ void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod) if(button < 4) { output_mouse(state, button-1, pressed, mod, state->mouse_col, state->mouse_row); } - else if(button < 6) { + else if(button < 8) { output_mouse(state, button-4 + 0x40, pressed, mod, state->mouse_col, state->mouse_row); } } diff --git a/src/libs/3rdparty/libvterm/src/screen.c b/src/libs/3rdparty/libvterm/src/screen.c index 9d1028e67ae..6c549094a9a 100644 --- a/src/libs/3rdparty/libvterm/src/screen.c +++ b/src/libs/3rdparty/libvterm/src/screen.c @@ -595,8 +595,15 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new new_row_start, new_row_end, old_row_start, old_row_end, width); #endif - if(new_row_start < 0) + if(new_row_start < 0) { + if(old_row_start <= old_cursor.row && old_cursor.row < old_row_end) { + new_cursor.row = 0; + new_cursor.col = old_cursor.col; + if(new_cursor.col >= new_cols) + new_cursor.col = new_cols-1; + } break; + } for(new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) { int count = width >= new_cols ? new_cols : width; @@ -660,8 +667,9 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) { /* Push spare lines to scrollback buffer */ - for(int row = 0; row <= old_row; row++) - sb_pushline_from_row(screen, row); + if(screen->callbacks && screen->callbacks->sb_pushline) + for(int row = 0; row <= old_row; row++) + sb_pushline_from_row(screen, row); if(active) statefields->pos.row -= (old_row + 1); } diff --git a/src/libs/3rdparty/libvterm/src/state.c b/src/libs/3rdparty/libvterm/src/state.c index 313e746e77c..ce8e0342370 100644 --- a/src/libs/3rdparty/libvterm/src/state.c +++ b/src/libs/3rdparty/libvterm/src/state.c @@ -801,6 +801,7 @@ static void set_dec_mode(VTermState *state, int num, int val) break; case 1004: + settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val); state->mode.report_focus = val; break; @@ -949,6 +950,7 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha switch(intermed[0]) { case ' ': + case '!': case '"': case '$': case '\'': @@ -1311,8 +1313,10 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha break; case LEADER('?', 0x68): // DEC private mode set - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 1); + for(int i = 0; i < argcount; i++) { + if(!CSI_ARG_IS_MISSING(args[i])) + set_dec_mode(state, CSI_ARG(args[i]), 1); + } break; case 0x6a: // HPB - ECMA-48 8.3.58 @@ -1333,8 +1337,10 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha break; case LEADER('?', 0x6c): // DEC private mode reset - if(!CSI_ARG_IS_MISSING(args[0])) - set_dec_mode(state, CSI_ARG(args[0]), 0); + for(int i = 0; i < argcount; i++) { + if(!CSI_ARG_IS_MISSING(args[i])) + set_dec_mode(state, CSI_ARG(args[i]), 0); + } break; case 0x6d: // SGR - ECMA-48 8.3.117 @@ -1386,7 +1392,7 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha break; - case LEADER('!', 0x70): // DECSTR - DEC soft terminal reset + case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset vterm_state_reset(state, 0); break; @@ -1652,8 +1658,18 @@ static void osc_selection(VTermState *state, VTermStringFragment frag) frag.len--; } - if(!frag.len) + if(!frag.len) { + /* Clear selection if we're already finished but didn't do anything */ + if(frag.final && state->selection.callbacks->set) { + (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){ + .str = NULL, + .len = 0, + .initial = state->tmp.selection.state != SELECTION_SET, + .final = true, + }, state->selection.user); + } return; + } if(state->tmp.selection.state == SELECTION_SELECTED) { if(frag.str[0] == '?') { @@ -1671,6 +1687,9 @@ static void osc_selection(VTermState *state, VTermStringFragment frag) return; } + if(state->tmp.selection.state == SELECTION_INVALID) + return; + if(state->selection.callbacks->set) { size_t bufcur = 0; char *buffer = state->selection.buffer; @@ -1706,11 +1725,21 @@ static void osc_selection(VTermState *state, VTermStringFragment frag) uint8_t b = unbase64one(frag.str[0]); if(b == 0xFF) { DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]); + + state->tmp.selection.state = SELECTION_INVALID; + if(state->selection.callbacks->set) { + (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){ + .str = NULL, + .len = 0, + .initial = true, + .final = true, + }, state->selection.user); + } + break; } - else { - x = (x << 6) | b; - n++; - } + + x = (x << 6) | b; + n++; frag.str++, frag.len--; if(n == 4) { @@ -1730,7 +1759,7 @@ static void osc_selection(VTermState *state, VTermStringFragment frag) .str = state->selection.buffer, .len = bufcur, .initial = state->tmp.selection.state == SELECTION_SET_INITIAL, - .final = frag.final, + .final = frag.final && !frag.len, }, state->selection.user); state->tmp.selection.state = SELECTION_SET; } @@ -1842,7 +1871,7 @@ static void request_status_string(VTermState *state, VTermStringFragment frag) case ' '|('q'<<8): { // Query DECSCUSR - int reply = 2; + int reply; switch(state->mode.cursor_shape) { case VTERM_PROP_CURSORSHAPE_BLOCK: reply = 2; break; case VTERM_PROP_CURSORSHAPE_UNDERLINE: reply = 4; break; @@ -2196,6 +2225,9 @@ int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val) if(val->number == VTERM_PROP_MOUSE_MOVE) state->mouse_flags |= MOUSE_WANT_MOVE; return 1; + case VTERM_PROP_FOCUSREPORT: + state->mode.report_focus = val->boolean; + return 1; case VTERM_N_PROPS: return 0; diff --git a/src/libs/3rdparty/libvterm/src/vterm.c b/src/libs/3rdparty/libvterm/src/vterm.c index 0997887f5fb..e1f676f5b62 100644 --- a/src/libs/3rdparty/libvterm/src/vterm.c +++ b/src/libs/3rdparty/libvterm/src/vterm.c @@ -295,6 +295,7 @@ VTermValueType vterm_get_prop_type(VTermProp prop) case VTERM_PROP_REVERSE: return VTERM_VALUETYPE_BOOL; case VTERM_PROP_CURSORSHAPE: return VTERM_VALUETYPE_INT; case VTERM_PROP_MOUSE: return VTERM_VALUETYPE_INT; + case VTERM_PROP_FOCUSREPORT: return VTERM_VALUETYPE_BOOL; case VTERM_N_PROPS: return 0; } diff --git a/src/libs/3rdparty/libvterm/src/vterm_internal.h b/src/libs/3rdparty/libvterm/src/vterm_internal.h index ad61bff8b0f..5d934aff071 100644 --- a/src/libs/3rdparty/libvterm/src/vterm_internal.h +++ b/src/libs/3rdparty/libvterm/src/vterm_internal.h @@ -158,6 +158,7 @@ struct VTermState SELECTION_QUERY, SELECTION_SET_INITIAL, SELECTION_SET, + SELECTION_INVALID, } state : 8; uint32_t recvpartial; uint32_t sendpartial; diff --git a/src/libs/3rdparty/libvterm/vterm.qbs b/src/libs/3rdparty/libvterm/vterm.qbs index 18ccb638aab..e35658908da 100644 --- a/src/libs/3rdparty/libvterm/vterm.qbs +++ b/src/libs/3rdparty/libvterm/vterm.qbs @@ -1,34 +1,35 @@ -Project { - QtcLibrary { - name: "vterm" - type: "staticlibrary" +QtcLibrary { + name: "vterm" + type: "staticlibrary" + useQt: false + + Depends { name: "cpp" } + + cpp.includePaths: base.concat("include") + cpp.warningLevel: "none" + + Group { + prefix: "src/" + files: [ + "encoding.c", + "fullwidth.inc", + "keyboard.c", + "mouse.c", + "parser.c", + "pen.c", + "rect.h", + "screen.c", + "state.c", + "unicode.c", + "utf8.h", + "vterm.c", + "vterm_internal.h", + ] + } + + Export { Depends { name: "cpp" } - cpp.includePaths: base.concat("include") - cpp.warningLevel: "none" - - Group { - prefix: "src/" - files: [ - "encoding.c", - "fullwidth.inc", - "keyboard.c", - "mouse.c", - "parser.c", - "pen.c", - "rect.h", - "screen.c", - "state.c", - "unicode.c", - "utf8.h", - "vterm.c", - "vterm_internal.h", - ] - } - - Export { - Depends { name: "cpp" } - cpp.includePaths: base.concat("include") - } + cpp.includePaths: "include" } } diff --git a/src/libs/3rdparty/tl_expected/include/tl/expected.hpp b/src/libs/3rdparty/tl_expected/include/tl/expected.hpp index d8ed472bdd1..afee404d43e 100644 --- a/src/libs/3rdparty/tl_expected/include/tl/expected.hpp +++ b/src/libs/3rdparty/tl_expected/include/tl/expected.hpp @@ -1,6 +1,6 @@ /// // expected - An implementation of std::expected with extensions -// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama) +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) // // Documentation available at http://tl.tartanllama.xyz/ // @@ -17,8 +17,8 @@ #define TL_EXPECTED_HPP #define TL_EXPECTED_VERSION_MAJOR 1 -#define TL_EXPECTED_VERSION_MINOR 0 -#define TL_EXPECTED_VERSION_PATCH 1 +#define TL_EXPECTED_VERSION_MINOR 1 +#define TL_EXPECTED_VERSION_PATCH 0 #include #include @@ -51,6 +51,16 @@ #define TL_EXPECTED_GCC55 #endif +#if !defined(TL_ASSERT) +//can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +#if (__cplusplus > 201103L) && !defined(TL_EXPECTED_GCC49) +#include +#define TL_ASSERT(x) assert(x) +#else +#define TL_ASSERT(x) +#endif +#endif + #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions @@ -66,30 +76,30 @@ #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector -// for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && \ - !defined(__clang__)) +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX namespace tl { - namespace detail { - template - struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +namespace detail { +template +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; #ifdef _GLIBCXX_VECTOR - template - struct is_trivially_copy_constructible> - : std::false_type{}; +template +struct is_trivially_copy_constructible> : std::false_type {}; #endif - } -} +} // namespace detail +} // namespace tl #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible #else #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::is_trivially_copy_constructible @@ -138,6 +148,17 @@ public: constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + template ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(args)...) {} + constexpr const E &value() const & { return m_val; } TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } @@ -147,6 +168,10 @@ private: E m_val; }; +#ifdef __cpp_deduction_guides +template unexpected(E) -> unexpected; +#endif + template constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { return lhs.value() == rhs.value(); @@ -183,16 +208,17 @@ struct unexpect_t { static constexpr unexpect_t unexpect{}; namespace detail { -template +template [[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(e); + throw std::forward(e); #else - #ifdef _MSC_VER - __assume(0); - #else - __builtin_unreachable(); - #endif + (void)e; +#ifdef _MSC_VER + __assume(0); +#else + __builtin_unreachable(); +#endif #endif } @@ -213,7 +239,7 @@ template struct conjunction : std::true_type {}; template struct conjunction : B {}; template struct conjunction - : std::conditional, B>::type {}; + : std::conditional, B>::type {}; #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L #define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND @@ -223,45 +249,52 @@ struct conjunction // which results in a hard-error when using it in a noexcept expression // in some cases. This is a check to workaround the common failing case. #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND -template struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func : std::false_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template -struct is_pointer_to_non_const_member_func : std::true_type {}; +struct is_pointer_to_non_const_member_func + : std::true_type {}; template struct is_const_or_const_ref : std::false_type {}; -template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : std::true_type {}; template struct is_const_or_const_ref : std::true_type {}; #endif // std::invoke from C++17 // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template ::value - && is_const_or_const_ref::value)>, + typename = enable_if_t::value && + is_const_or_const_ref::value)>, #endif - typename = enable_if_t>::value>, - int = 0> - constexpr auto invoke(Fn && f, Args && ... args) noexcept( + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { + -> decltype(std::mem_fn(f)(std::forward(args)...)) { return std::mem_fn(f)(std::forward(args)...); } template >::value>> - constexpr auto invoke(Fn && f, Args && ... args) noexcept( + typename = enable_if_t>::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { + -> decltype(std::forward(f)(std::forward(args)...)) { return std::forward(f)(std::forward(args)...); } @@ -270,9 +303,11 @@ template struct invoke_result_impl; template struct invoke_result_impl< - F, decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = decltype(detail::invoke(std::declval(), std::declval()...)); + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); }; template @@ -289,69 +324,67 @@ template struct is_nothrow_swappable : std::true_type {}; #else // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept namespace swap_adl_tests { - // if swap ADL finds this then it would call std::swap otherwise (same - // signature) - struct tag {}; +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; - template tag swap(T&, T&); - template tag swap(T(&a)[N], T(&b)[N]); +template tag swap(T &, T &); +template tag swap(T (&a)[N], T (&b)[N]); - // helper functions to test if an unqualified swap is possible, and if it - // becomes std::swap - template std::false_type can_swap(...) noexcept(false); - template (), std::declval()))> - std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); - template std::false_type uses_std(...); - template - std::is_same(), std::declval())), tag> - uses_std(int); +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); - template - struct is_std_swap_noexcept +template +struct is_std_swap_noexcept : std::integral_constant::value&& - std::is_nothrow_move_assignable::value> {}; + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value> {}; - template - struct is_std_swap_noexcept : is_std_swap_noexcept {}; +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; - template - struct is_adl_swap_noexcept +template +struct is_adl_swap_noexcept : std::integral_constant(0))> {}; } // namespace swap_adl_tests template struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; template struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype( - detail::swap_adl_tests::uses_std(0))::value || - is_swappable::value)> {}; + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; template struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value - && detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> { -}; + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; #endif #endif @@ -393,14 +426,10 @@ using is_move_constructible_or_void = is_void_or>; template -using is_copy_assignable_or_void = - is_void_or>; - +using is_copy_assignable_or_void = is_void_or>; template -using is_move_assignable_or_void = - is_void_or>; - +using is_move_assignable_or_void = is_void_or>; } // namespace detail @@ -423,19 +452,19 @@ struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -470,19 +499,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; @@ -511,19 +540,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -556,19 +585,19 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr expected_storage_base(in_place_t, Args &&... args) + constexpr expected_storage_base(in_place_t, Args &&...args) : m_val(std::forward(args)...), m_has_val(true) {} template &, Args &&...>::value> * = nullptr> constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_val(il, std::forward(args)...), m_has_val(true) {} template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -594,7 +623,13 @@ template struct expected_storage_base { // `T` is `void`, `E` is trivially-destructible template struct expected_storage_base { - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {} + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else + TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} constexpr expected_storage_base(in_place_t) : m_has_val(true) {} @@ -602,7 +637,7 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() = default; @@ -632,7 +667,7 @@ template struct expected_storage_base { template ::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) : m_unexpect(std::forward(args)...), m_has_val(false) {} template struct expected_storage_base { E, std::initializer_list &, Args &&...>::value> * = nullptr> constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : m_unexpect(il, std::forward(args)...), m_has_val(false) {} ~expected_storage_base() { @@ -662,7 +697,7 @@ template struct expected_operations_base : expected_storage_base { using expected_storage_base::expected_storage_base; - template void construct(Args &&... args) noexcept { + template void construct(Args &&...args) noexcept { new (std::addressof(this->m_val)) T(std::forward(args)...); this->m_has_val = true; } @@ -672,13 +707,13 @@ struct expected_operations_base : expected_storage_base { this->m_has_val = true; } - template void construct_error(Args &&... args) noexcept { + template void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; } - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED // These assign overloads ensure that the most efficient assignment // implementation is used while maintaining the strong exception guarantee. @@ -778,7 +813,7 @@ struct expected_operations_base : expected_storage_base { } } - #else +#else // If exceptions are disabled then we can just copy-construct void assign(const expected_operations_base &rhs) noexcept { @@ -795,11 +830,11 @@ struct expected_operations_base : expected_storage_base { geterr().~unexpected(); construct(std::move(rhs).get()); } else { - assign_common(rhs); + assign_common(std::move(rhs)); } } - #endif +#endif // The common part of move/copy assigning template void assign_common(Rhs &&rhs) { @@ -807,7 +842,7 @@ struct expected_operations_base : expected_storage_base { if (rhs.m_has_val) { get() = std::forward(rhs).get(); } else { - destroy_val(); + destroy_val(); construct_error(std::forward(rhs).geterr()); } } else { @@ -839,9 +874,7 @@ struct expected_operations_base : expected_storage_base { } #endif - TL_EXPECTED_11_CONSTEXPR void destroy_val() { - get().~T(); - } + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; // This base class provides some handy member functions which can be used in @@ -858,7 +891,7 @@ struct expected_operations_base : expected_storage_base { this->m_has_val = true; } - template void construct_error(Args &&... args) noexcept { + template void construct_error(Args &&...args) noexcept { new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...); this->m_has_val = false; @@ -895,7 +928,7 @@ struct expected_operations_base : expected_storage_base { #endif TL_EXPECTED_11_CONSTEXPR void destroy_val() { - //no-op + // no-op } }; @@ -1220,14 +1253,17 @@ class expected : private detail::expected_move_assign_base, "T must not be in_place_t"); static_assert(!std::is_same::type>::value, "T must not be unexpect_t"); - static_assert(!std::is_same>::type>::value, - "T must not be unexpected"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); static_assert(!std::is_reference::value, "E must not be a reference"); T *valptr() { return std::addressof(this->m_val); } - const T *valptr() const { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } unexpected *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } template ::value> * = nullptr> @@ -1272,24 +1308,26 @@ public: #else template TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(std::declval(), std::forward(f))) { + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { return and_then_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } template - constexpr auto and_then(F &&f) const & -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr auto and_then(F &&f) const && -> decltype( - and_then_impl(std::declval(), std::forward(f))) { + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } #endif @@ -1297,7 +1335,7 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { @@ -1311,14 +1349,14 @@ public: } #else template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) map(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) map(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } @@ -1341,7 +1379,7 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { @@ -1354,15 +1392,15 @@ public: return expected_map_impl(std::move(*this), std::forward(f)); } #else - template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) transform(F &&f) & { return expected_map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) transform(F &&f) && { return expected_map_impl(std::move(*this), std::forward(f)); } @@ -1385,54 +1423,87 @@ public: #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template - TL_EXPECTED_11_CONSTEXPR auto transform_or(F &&f) & - { - return transform_or_impl(*this, std::forward(f)); + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); } - template - TL_EXPECTED_11_CONSTEXPR auto transform_or(F &&f) && - { - return transform_or_impl(std::move(*this), std::forward(f)); + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); } - template - constexpr auto transform_or(F &&f) const & - { - return transform_or_impl(*this, std::forward(f)); + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); } - template - constexpr auto transform_or(F &&f) const && - { - return transform_or_impl(std::move(*this), std::forward(f)); + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); } #else - template - TL_EXPECTED_11_CONSTEXPR decltype(transform_or_impl(std::declval(), - std::declval())) - transform_or(F &&f) & - { - return transform_or_impl(*this, std::forward(f)); + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); } - template - TL_EXPECTED_11_CONSTEXPR decltype(transform_or_impl(std::declval(), - std::declval())) - transform_or(F &&f) && - { - return transform_or_impl(std::move(*this), std::forward(f)); + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); } - template - constexpr decltype(transform_or_impl(std::declval(), std::declval())) - transform_or(F &&f) const & - { - return transform_or_impl(*this, std::forward(f)); + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); } #ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(transform_or_impl(std::declval(), std::declval())) - transform_or(F &&f) const && - { - return transform_or_impl(std::move(*this), std::forward(f)); + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); } #endif #endif @@ -1462,14 +1533,14 @@ public: template ::value> * = nullptr> - constexpr expected(in_place_t, Args &&... args) + constexpr expected(in_place_t, Args &&...args) : impl_base(in_place, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&... args) + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) : impl_base(in_place, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} @@ -1482,7 +1553,7 @@ public: : impl_base(unexpect, e.value()), ctor_base(detail::default_constructor_tag{}) {} - template < + template < class G = E, detail::enable_if_t::value> * = nullptr, @@ -1500,7 +1571,7 @@ public: : impl_base(unexpect, std::move(e.value())), ctor_base(detail::default_constructor_tag{}) {} - template < + template < class G = E, detail::enable_if_t::value> * = nullptr, detail::enable_if_t::value> * = nullptr> @@ -1512,15 +1583,15 @@ public: template ::value> * = nullptr> - constexpr explicit expected(unexpect_t, Args &&... args) + constexpr explicit expected(unexpect_t, Args &&...args) : impl_base(unexpect, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} - template &, Args &&...>::value> * = nullptr> constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&... args) + Args &&...args) : impl_base(unexpect, il, std::forward(args)...), ctor_base(detail::default_constructor_tag{}) {} @@ -1535,11 +1606,11 @@ public: if (rhs.has_value()) { this->construct(*rhs); } else { - this->construct_error(rhs.error()); + this->construct_error(rhs.error()); } } - template ::value && std::is_convertible::value)> * = nullptr, @@ -1550,8 +1621,8 @@ public: if (rhs.has_value()) { this->construct(*rhs); } else { - this->construct_error(rhs.error()); - } + this->construct_error(rhs.error()); + } } template < @@ -1564,11 +1635,11 @@ public: if (rhs.has_value()) { this->construct(std::move(*rhs)); } else { - this->construct_error(std::move(rhs.error())); - } + this->construct_error(std::move(rhs.error())); + } } - template < + template < class U, class G, detail::enable_if_t<(std::is_convertible::value && std::is_convertible::value)> * = nullptr, @@ -1578,8 +1649,8 @@ public: if (rhs.has_value()) { this->construct(std::move(*rhs)); } else { - this->construct_error(std::move(rhs.error())); - } + this->construct_error(std::move(rhs.error())); + } } template < @@ -1589,7 +1660,7 @@ public: explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward(v)) {} - template < + template < class U = T, detail::enable_if_t::value> * = nullptr, detail::expected_enable_forward_value * = nullptr> @@ -1620,7 +1691,7 @@ public: return *this; } - template < + template < class U = T, class G = T, detail::enable_if_t::value> * = nullptr, @@ -1639,7 +1710,7 @@ public: auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(std::forward(v)); this->m_has_val = true; @@ -1647,10 +1718,10 @@ public: err() = std::move(tmp); throw; } - #else - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - #endif +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif } return *this; @@ -1688,26 +1759,27 @@ public: template ::value> * = nullptr> - void emplace(Args &&... args) { + void emplace(Args &&...args) { if (has_value()) { - val() = T(std::forward(args)...); + val().~T(); } else { err().~unexpected(); - ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; } + ::new (valptr()) T(std::forward(args)...); } - template ::value> * = nullptr> - void emplace(Args &&... args) { + void emplace(Args &&...args) { if (has_value()) { - val() = T(std::forward(args)...); + val().~T(); + ::new (valptr()) T(std::forward(args)...); } else { auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; @@ -1715,17 +1787,17 @@ public: err() = std::move(tmp); throw; } - #else +#else ::new (valptr()) T(std::forward(args)...); this->m_has_val = true; - #endif +#endif } } template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&... args) { + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -1736,10 +1808,10 @@ public: } } - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&... args) { + void emplace(std::initializer_list il, Args &&...args) { if (has_value()) { T t(il, std::forward(args)...); val() = std::move(t); @@ -1747,7 +1819,7 @@ public: auto tmp = std::move(err()); err().~unexpected(); - #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(il, std::forward(args)...); this->m_has_val = true; @@ -1755,10 +1827,10 @@ public: err() = std::move(tmp); throw; } - #else +#else ::new (valptr()) T(il, std::forward(args)...); this->m_has_val = true; - #endif +#endif } } @@ -1770,7 +1842,7 @@ private: using e_is_nothrow_move_constructible = std::true_type; using move_constructing_e_can_throw = std::false_type; - void swap_where_both_have_value(expected &/*rhs*/ , t_is_void) noexcept { + void swap_where_both_have_value(expected & /*rhs*/, t_is_void) noexcept { // swapping void is a no-op } @@ -1828,12 +1900,12 @@ private: void swap_where_only_one_has_value_and_t_is_not_void( expected &rhs, move_constructing_t_can_throw, - t_is_nothrow_move_constructible) { + e_is_nothrow_move_constructible) { auto temp = std::move(rhs.err()); rhs.err().~unexpected_type(); #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED try { - ::new (rhs.valptr()) T(val()); + ::new (rhs.valptr()) T(std::move(val())); val().~T(); ::new (errptr()) unexpected_type(std::move(temp)); std::swap(this->m_has_val, rhs.m_has_val); @@ -1842,7 +1914,7 @@ private: throw; } #else - ::new (rhs.valptr()) T(val()); + ::new (rhs.valptr()) T(std::move(val())); val().~T(); ::new (errptr()) unexpected_type(std::move(temp)); std::swap(this->m_has_val, rhs.m_has_val); @@ -1872,27 +1944,37 @@ public: } } - constexpr const T *operator->() const { return valptr(); } - TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + constexpr const T *operator->() const { + TL_ASSERT(has_value()); + return valptr(); + } + TL_EXPECTED_11_CONSTEXPR T *operator->() { + TL_ASSERT(has_value()); + return valptr(); + } template ::value> * = nullptr> constexpr const U &operator*() const & { + TL_ASSERT(has_value()); return val(); } template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); return val(); } template ::value> * = nullptr> constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); return std::move(val()); } template ::value> * = nullptr> TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + TL_ASSERT(has_value()); return std::move(val()); } @@ -1928,10 +2010,22 @@ public: return std::move(val()); } - constexpr const E &error() const & { return err().value(); } - TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } - constexpr const E &&error() const && { return std::move(err().value()); } - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + constexpr const E &error() const & { + TL_ASSERT(!has_value()); + return err().value(); + } + TL_EXPECTED_11_CONSTEXPR E &error() & { + TL_ASSERT(!has_value()); + return err().value(); + } + constexpr const E &&error() const && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } + TL_EXPECTED_11_CONSTEXPR E &&error() && { + TL_ASSERT(!has_value()); + return std::move(err().value()); + } template constexpr T value_or(U &&v) const & { static_assert(std::is_copy_constructible::value && @@ -2001,7 +2095,7 @@ constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { #ifdef TL_EXPECTED_CXX14 template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2013,7 +2107,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2038,7 +2132,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> auto expected_map_impl(Exp &&exp, F &&f) { @@ -2049,10 +2143,10 @@ auto expected_map_impl(Exp &&exp, F &&f) { } return result(unexpect, std::forward(exp).error()); -} +} #else template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2067,7 +2161,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval(), *std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2082,7 +2176,7 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected> { } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2095,7 +2189,7 @@ constexpr auto expected_map_impl(Exp &&exp, F &&f) } template >::value> * = nullptr, + detail::enable_if_t>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval())), detail::enable_if_t::value> * = nullptr> @@ -2106,31 +2200,29 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected> { } return unexpected>(std::forward(exp).error()); -} +} #endif #if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto transform_or_impl(Exp &&exp, F &&f) -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { using result = expected, detail::decay_t>; return exp.has_value() ? result(*std::forward(exp)) : result(unexpect, detail::invoke(std::forward(f), std::forward(exp).error())); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto transform_or_impl(Exp &&exp, F &&f) -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { using result = expected, monostate>; if (exp.has_value()) { return result(*std::forward(exp)); @@ -2139,26 +2231,24 @@ auto transform_or_impl(Exp &&exp, F &&f) detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto transform_or_impl(Exp &&exp, F &&f) -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { using result = expected, detail::decay_t>; return exp.has_value() ? result() : result(unexpect, detail::invoke(std::forward(f), std::forward(exp).error())); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto transform_or_impl(Exp &&exp, F &&f) -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { using result = expected, monostate>; if (exp.has_value()) { return result(); @@ -2168,13 +2258,13 @@ auto transform_or_impl(Exp &&exp, F &&f) return result(unexpect, monostate{}); } #else -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto transform_or_impl(Exp &&exp, F &&f) -> expected, detail::decay_t> -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { using result = expected, detail::decay_t>; return exp.has_value() @@ -2183,13 +2273,12 @@ constexpr auto transform_or_impl(Exp &&exp, F &&f) -> expected, detai std::forward(exp).error())); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto transform_or_impl(Exp &&exp, F &&f) -> expected, monostate> -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { using result = expected, monostate>; if (exp.has_value()) { return result(*std::forward(exp)); @@ -2199,13 +2288,13 @@ auto transform_or_impl(Exp &&exp, F &&f) -> expected, monostate> return result(unexpect, monostate{}); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto transform_or_impl(Exp &&exp, F &&f) -> expected, detail::decay_t> -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { using result = expected, detail::decay_t>; return exp.has_value() @@ -2214,13 +2303,12 @@ constexpr auto transform_or_impl(Exp &&exp, F &&f) -> expected, detai std::forward(exp).error())); } -template>::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto transform_or_impl(Exp &&exp, F &&f) -> expected, monostate> -{ +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { using result = expected, monostate>; if (exp.has_value()) { return result(); @@ -2228,7 +2316,7 @@ auto transform_or_impl(Exp &&exp, F &&f) -> expected, monostate> detail::invoke(std::forward(f), std::forward(exp).error()); return result(unexpect, monostate{}); -} +} #endif #ifdef TL_EXPECTED_CXX14 @@ -2238,9 +2326,9 @@ template ::value> * = nullptr> constexpr auto or_else_impl(Exp &&exp, F &&f) { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template ().error())), detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); } #else template (), std::declval().error())), - detail::enable_if_t::value> * = nullptr> + detail::enable_if_t::value> * = nullptr> auto or_else_impl(Exp &&exp, F &&f) -> Ret { static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); } template (), std::declval().error())), - detail::enable_if_t::value> * = nullptr> + detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); } #endif } // namespace detail @@ -2292,6 +2380,20 @@ constexpr bool operator!=(const expected &lhs, ? true : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); } +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} template constexpr bool operator==(const expected &x, const U &v) { diff --git a/src/libs/3rdparty/winpty/winpty.qbs b/src/libs/3rdparty/winpty/winpty.qbs index f6160fe9e62..35d56f92655 100644 --- a/src/libs/3rdparty/winpty/winpty.qbs +++ b/src/libs/3rdparty/winpty/winpty.qbs @@ -5,7 +5,6 @@ Project { name: "Winpty" condition: qbs.targetOS.contains("windows") - Product { name: "winpty_genversion_header" type: "hpp" @@ -199,8 +198,8 @@ Project { Export { Depends { name: "cpp" } - cpp.defines: base.concat("COMPILING_WINPTY_DLL") - cpp.includePaths: base.concat(exportingProduct.sourceDirectory + "/src/include") + cpp.defines: "COMPILING_WINPTY_DLL" + cpp.includePaths: exportingProduct.sourceDirectory + "/src/include" } } } diff --git a/src/libs/3rdparty/yaml-cpp/README.md b/src/libs/3rdparty/yaml-cpp/README.md index f33d3503a15..70c231445d3 100644 --- a/src/libs/3rdparty/yaml-cpp/README.md +++ b/src/libs/3rdparty/yaml-cpp/README.md @@ -1,51 +1,58 @@ -# yaml-cpp [![Build Status](https://travis-ci.org/jbeder/yaml-cpp.svg?branch=master)](https://travis-ci.org/jbeder/yaml-cpp) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/) +# yaml-cpp ![Build Status](https://github.com/jbeder/yaml-cpp/actions/workflows/build.yml/badge.svg) [![Documentation](https://codedocs.xyz/jbeder/yaml-cpp.svg)](https://codedocs.xyz/jbeder/yaml-cpp/) -yaml-cpp is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html). +`yaml-cpp` is a [YAML](http://www.yaml.org/) parser and emitter in C++ matching the [YAML 1.2 spec](http://www.yaml.org/spec/1.2/spec.html). -To get a feel for how it can be used, see the [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial) or [How to Emit YAML](https://github.com/jbeder/yaml-cpp/wiki/How-To-Emit-YAML). For the old API (version < 0.5.0), see [How To Parse A Document](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)). +## Usage -# Problems? # +See [Tutorial](https://github.com/jbeder/yaml-cpp/wiki/Tutorial) and [How to Emit YAML](https://github.com/jbeder/yaml-cpp/wiki/How-To-Emit-YAML) for reference. For the old API (until 0.5.0), see [How To Parse A Document](https://github.com/jbeder/yaml-cpp/wiki/How-To-Parse-A-Document-(Old-API)). + +## Any Problems? If you find a bug, post an [issue](https://github.com/jbeder/yaml-cpp/issues)! If you have questions about how to use yaml-cpp, please post it on http://stackoverflow.com and tag it [`yaml-cpp`](http://stackoverflow.com/questions/tagged/yaml-cpp). -# How to Build # +## How to Build -yaml-cpp uses [CMake](http://www.cmake.org) to support cross-platform building. The basic steps to build are: +`yaml-cpp` uses [CMake](http://www.cmake.org) to support cross-platform building. Install [CMake](http://www.cmake.org) _(Resources -> Download)_ before proceeding. The basic steps to build are: -1. Download and install [CMake](http://www.cmake.org) (Resources -> Download). +**Note:** If you don't use the provided installer for your platform, make sure that you add `CMake`'s bin folder to your path. -**Note:** If you don't use the provided installer for your platform, make sure that you add CMake's bin folder to your path. +#### 1. Navigate into the source directory, create build folder and run `CMake`: -2. Navigate into the source directory, and type: - -``` +```sh mkdir build cd build +cmake [-G generator] [-DYAML_BUILD_SHARED_LIBS=on|OFF] .. ``` -3. Run CMake. The basic syntax is: + * The `generator` option is the build system you'd like to use. Run `cmake` without arguments to see a full list of available generators. + * On Windows, you might use "Visual Studio 12 2013" (VS 2013 32-bits), or "Visual Studio 14 2015 Win64" (VS 2015 64-bits). + * On OS X, you might use "Xcode". + * On a UNIX-like system, omit the option (for a Makefile). -``` -cmake [-G generator] [-DBUILD_SHARED_LIBS=ON|OFF] .. -``` - - * The `generator` is whatever type of build system you'd like to use. To see a full list of generators on your platform, just run `cmake` (with no arguments). For example: - * On Windows, you might use "Visual Studio 12 2013" to generate a Visual Studio 2013 solution or "Visual Studio 14 2015 Win64" to generate a 64-bit Visual Studio 2015 solution. - * On OS X, you might use "Xcode" to generate an Xcode project - * On a UNIX-y system, simply omit the option to generate a makefile - - * yaml-cpp defaults to building a static library, but you may build a shared library by specifying `-DBUILD_SHARED_LIBS=ON`. + * `yaml-cpp` builds a static library by default, you may want to build a shared library by specifying `-DYAML_BUILD_SHARED_LIBS=ON`. * For more options on customizing the build, see the [CMakeLists.txt](https://github.com/jbeder/yaml-cpp/blob/master/CMakeLists.txt) file. -4. Build it! +#### 2. Build it! + * The command you'll need to run depends on the generator you chose earlier. -5. To clean up, just remove the `build` directory. +**Note:** To clean up, just remove the `build` directory. -# Recent Release # +## Recent Releases -[yaml-cpp 0.6.0](https://github.com/jbeder/yaml-cpp/releases/tag/yaml-cpp-0.6.0) has been released! This release requires C++11, and no longer depends on Boost. +[yaml-cpp 0.6.0](https://github.com/jbeder/yaml-cpp/releases/tag/yaml-cpp-0.6.0) released! This release requires C++11, and no longer depends on Boost. [yaml-cpp 0.3.0](https://github.com/jbeder/yaml-cpp/releases/tag/release-0.3.0) is still available if you want the old API. **The old API will continue to be supported, and will still receive bugfixes!** The 0.3.x and 0.4.x versions will be old API releases, and 0.5.x and above will all be new API releases. + +# API Documentation + +The autogenerated API reference is hosted on [CodeDocs](https://codedocs.xyz/jbeder/yaml-cpp/index.html) + +# Third Party Integrations + +The following projects are not officially supported: + +- [Qt wrapper](https://gist.github.com/brcha/d392b2fe5f1e427cc8a6) +- [UnrealEngine Wrapper](https://github.com/jwindgassen/UnrealYAML) diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h index 06759c724d2..f46d1d79dd8 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/anchor.h @@ -10,7 +10,7 @@ #include namespace YAML { -typedef std::size_t anchor_t; +using anchor_t = std::size_t; const anchor_t NullAnchor = 0; } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h index 29d5dbd027a..1050dae98c3 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/binary.h @@ -19,9 +19,13 @@ YAML_CPP_API std::vector DecodeBase64(const std::string &input); class YAML_CPP_API Binary { public: - Binary() : m_unownedData(0), m_unownedSize(0) {} Binary(const unsigned char *data_, std::size_t size_) - : m_unownedData(data_), m_unownedSize(size_) {} + : m_data{}, m_unownedData(data_), m_unownedSize(size_) {} + Binary() : Binary(nullptr, 0) {} + Binary(const Binary &) = default; + Binary(Binary &&) = default; + Binary &operator=(const Binary &) = default; + Binary &operator=(Binary &&) = default; bool owned() const { return !m_unownedData; } std::size_t size() const { return owned() ? m_data.size() : m_unownedSize; } @@ -35,7 +39,7 @@ class YAML_CPP_API Binary { rhs.clear(); rhs.resize(m_unownedSize); std::copy(m_unownedData, m_unownedData + m_unownedSize, rhs.begin()); - m_unownedData = 0; + m_unownedData = nullptr; m_unownedSize = 0; } else { m_data.swap(rhs); @@ -62,6 +66,6 @@ class YAML_CPP_API Binary { const unsigned char *m_unownedData; std::size_t m_unownedSize; }; -} +} // namespace YAML #endif // BASE64_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h new file mode 100644 index 00000000000..8ca61ac6ccc --- /dev/null +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/depthguard.h @@ -0,0 +1,77 @@ +#ifndef DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000 +#define DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +#include "exceptions.h" + +namespace YAML { + +/** + * @brief The DeepRecursion class + * An exception class which is thrown by DepthGuard. Ideally it should be + * a member of DepthGuard. However, DepthGuard is a templated class which means + * that any catch points would then need to know the template parameters. It is + * simpler for clients to not have to know at the catch point what was the + * maximum depth. + */ +class DeepRecursion : public ParserException { +public: + virtual ~DeepRecursion() = default; + + DeepRecursion(int depth, const Mark& mark_, const std::string& msg_); + + // Returns the recursion depth when the exception was thrown + int depth() const { + return m_depth; + } + +private: + int m_depth = 0; +}; + +/** + * @brief The DepthGuard class + * DepthGuard takes a reference to an integer. It increments the integer upon + * construction of DepthGuard and decrements the integer upon destruction. + * + * If the integer would be incremented past max_depth, then an exception is + * thrown. This is ideally geared toward guarding against deep recursion. + * + * @param max_depth + * compile-time configurable maximum depth. + */ +template +class DepthGuard final { +public: + DepthGuard(int & depth_, const Mark& mark_, const std::string& msg_) : m_depth(depth_) { + ++m_depth; + if ( max_depth <= m_depth ) { + throw DeepRecursion{m_depth, mark_, msg_}; + } + } + + DepthGuard(const DepthGuard & copy_ctor) = delete; + DepthGuard(DepthGuard && move_ctor) = delete; + DepthGuard & operator=(const DepthGuard & copy_assign) = delete; + DepthGuard & operator=(DepthGuard && move_assign) = delete; + + ~DepthGuard() { + --m_depth; + } + + int current_depth() const { + return m_depth; + } + +private: + int & m_depth; +}; + +} // namespace YAML + +#endif // DEPTH_GUARD_H_00000000000000000000000000000000000000000000000000000000 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h index 897f1533df6..eabdda1d95c 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h @@ -1,42 +1,61 @@ #ifndef DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 #define DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#if defined(_MSC_VER) || \ - (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ - (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 -#pragma once -#endif +// Definition YAML_CPP_STATIC_DEFINE using to building YAML-CPP as static +// library (definition created by CMake or defined manually) -// The following ifdef block is the standard way of creating macros which make -// exporting from a DLL simpler. All files within this DLL are compiled with the -// yaml_cpp_EXPORTS symbol defined on the command line. This symbol should not -// be defined on any project that uses this DLL. This way any other project -// whose source files include this file see YAML_CPP_API functions as being -// imported from a DLL, whereas this DLL sees symbols defined with this macro as -// being exported. -#undef YAML_CPP_API +// Definition yaml_cpp_EXPORTS using to building YAML-CPP as dll/so library +// (definition created by CMake or defined manually) -#ifdef YAML_CPP_DLL // Using or Building YAML-CPP DLL (definition defined - // manually) - -#if defined(_WIN32) || defined(WIN32) -# define YAML_CPP_API_IMPORT __declspec(dllimport) -# define YAML_CPP_API_EXPORT __declspec(dllexport) +#ifdef YAML_CPP_STATIC_DEFINE +# define YAML_CPP_API +# define YAML_CPP_NO_EXPORT #else -# define YAML_CPP_API_IMPORT __attribute__((visibility("default"))) -# define YAML_CPP_API_EXPORT __attribute__((visibility("default"))) +# if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) +# ifndef YAML_CPP_API +# ifdef yaml_cpp_EXPORTS + /* We are building this library */ +# pragma message( "Defining YAML_CPP_API for DLL export" ) +# define YAML_CPP_API __declspec(dllexport) +# else + /* We are using this library */ +# pragma message( "Defining YAML_CPP_API for DLL import" ) +# define YAML_CPP_API __declspec(dllimport) +# endif +# endif +# ifndef YAML_CPP_NO_EXPORT +# define YAML_CPP_NO_EXPORT +# endif +# else /* No _MSC_VER */ +# ifndef YAML_CPP_API +# ifdef yaml_cpp_EXPORTS + /* We are building this library */ +# define YAML_CPP_API __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define YAML_CPP_API __attribute__((visibility("default"))) +# endif +# endif +# ifndef YAML_CPP_NO_EXPORT +# define YAML_CPP_NO_EXPORT __attribute__((visibility("hidden"))) +# endif +# endif /* _MSC_VER */ +#endif /* YAML_CPP_STATIC_DEFINE */ + +#ifndef YAML_CPP_DEPRECATED +# ifdef _MSC_VER +# define YAML_CPP_DEPRECATED __declspec(deprecated) +# else +# define YAML_CPP_DEPRECATED __attribute__ ((__deprecated__)) +# endif #endif -#ifdef yaml_cpp_EXPORTS // Building YAML-CPP DLL (definition created by CMake - // or defined manually) -// #pragma message( "Defining YAML_CPP_API for DLL export" ) -#define YAML_CPP_API YAML_CPP_API_EXPORT -#else // yaml_cpp_EXPORTS -// #pragma message( "Defining YAML_CPP_API for DLL import" ) -#define YAML_CPP_API YAML_CPP_API_IMPORT -#endif // yaml_cpp_EXPORTS -#else // YAML_CPP_DLL -#define YAML_CPP_API -#endif // YAML_CPP_DLL +#ifndef YAML_CPP_DEPRECATED_EXPORT +# define YAML_CPP_DEPRECATED_EXPORT YAML_CPP_API YAML_CPP_DEPRECATED +#endif -#endif // DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 +#ifndef YAML_CPP_DEPRECATED_NO_EXPORT +# define YAML_CPP_DEPRECATED_NO_EXPORT YAML_CPP_NO_EXPORT YAML_CPP_DEPRECATED +#endif + +#endif /* DLL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 */ diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h index f14b051ab0e..1f389c5a138 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitfromevents.h @@ -24,21 +24,21 @@ class EmitFromEvents : public EventHandler { public: EmitFromEvents(Emitter& emitter); - virtual void OnDocumentStart(const Mark& mark); - virtual void OnDocumentEnd(); + void OnDocumentStart(const Mark& mark) override; + void OnDocumentEnd() override; - virtual void OnNull(const Mark& mark, anchor_t anchor); - virtual void OnAlias(const Mark& mark, anchor_t anchor); - virtual void OnScalar(const Mark& mark, const std::string& tag, - anchor_t anchor, const std::string& value); + void OnNull(const Mark& mark, anchor_t anchor) override; + void OnAlias(const Mark& mark, anchor_t anchor) override; + void OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value) override; - virtual void OnSequenceStart(const Mark& mark, const std::string& tag, - anchor_t anchor, EmitterStyle::value style); - virtual void OnSequenceEnd(); + void OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) override; + void OnSequenceEnd() override; - virtual void OnMapStart(const Mark& mark, const std::string& tag, - anchor_t anchor, EmitterStyle::value style); - virtual void OnMapEnd(); + void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) override; + void OnMapEnd() override; private: void BeginNode(); diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h index ef92cc4035b..210b1ec9744 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emitter.h @@ -7,16 +7,18 @@ #pragma once #endif +#include #include +#include #include #include #include +#include #include "yaml-cpp/binary.h" #include "yaml-cpp/dll.h" #include "yaml-cpp/emitterdef.h" #include "yaml-cpp/emittermanip.h" -#include "yaml-cpp/noncopyable.h" #include "yaml-cpp/null.h" #include "yaml-cpp/ostream_wrapper.h" @@ -28,10 +30,12 @@ struct _Null; namespace YAML { class EmitterState; -class YAML_CPP_API Emitter : private noncopyable { +class YAML_CPP_API Emitter { public: Emitter(); explicit Emitter(std::ostream& stream); + Emitter(const Emitter&) = delete; + Emitter& operator=(const Emitter&) = delete; ~Emitter(); // output @@ -46,6 +50,7 @@ class YAML_CPP_API Emitter : private noncopyable { bool SetOutputCharset(EMITTER_MANIP value); bool SetStringFormat(EMITTER_MANIP value); bool SetBoolFormat(EMITTER_MANIP value); + bool SetNullFormat(EMITTER_MANIP value); bool SetIntBase(EMITTER_MANIP value); bool SetSeqFormat(EMITTER_MANIP value); bool SetMapFormat(EMITTER_MANIP value); @@ -54,6 +59,7 @@ class YAML_CPP_API Emitter : private noncopyable { bool SetPostCommentIndent(std::size_t n); bool SetFloatPrecision(std::size_t n); bool SetDoublePrecision(std::size_t n); + void RestoreGlobalModifiedSettings(); // local setters Emitter& SetLocalValue(EMITTER_MANIP value); @@ -119,6 +125,7 @@ class YAML_CPP_API Emitter : private noncopyable { void SpaceOrIndentTo(bool requireSpace, std::size_t indent); const char* ComputeFullBoolName(bool b) const; + const char* ComputeNullName() const; bool CanEmitNewline() const; private: @@ -152,7 +159,27 @@ inline Emitter& Emitter::WriteStreamable(T value) { std::stringstream stream; SetStreamablePrecision(stream); - stream << value; + + bool special = false; + if (std::is_floating_point::value) { + if ((std::numeric_limits::has_quiet_NaN || + std::numeric_limits::has_signaling_NaN) && + std::isnan(value)) { + special = true; + stream << ".nan"; + } else if (std::numeric_limits::has_infinity && std::isinf(value)) { + special = true; + if (std::signbit(value)) { + stream << "-.inf"; + } else { + stream << ".inf"; + } + } + } + + if (!special) { + stream << value; + } m_stream << stream.str(); StartedScalar(); @@ -249,6 +276,6 @@ inline Emitter& operator<<(Emitter& emitter, _Indent indent) { inline Emitter& operator<<(Emitter& emitter, _Precision precision) { return emitter.SetLocalPrecision(precision); } -} +} // namespace YAML #endif // EMITTER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h index 89f7256714e..976d14950fc 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/emittermanip.h @@ -19,6 +19,7 @@ enum EMITTER_MANIP { // output character set EmitNonAscii, EscapeNonAscii, + EscapeAsJson, // string manipulators // Auto, // duplicate @@ -26,6 +27,12 @@ enum EMITTER_MANIP { DoubleQuoted, Literal, + // null manipulators + LowerNull, + UpperNull, + CamelNull, + TildeNull, + // bool manipulators YesNoBool, // yes, no TrueFalseBool, // true, false @@ -74,14 +81,14 @@ struct _Alias { std::string content; }; -inline _Alias Alias(const std::string content) { return _Alias(content); } +inline _Alias Alias(const std::string& content) { return _Alias(content); } struct _Anchor { _Anchor(const std::string& content_) : content(content_) {} std::string content; }; -inline _Anchor Anchor(const std::string content) { return _Anchor(content); } +inline _Anchor Anchor(const std::string& content) { return _Anchor(content); } struct _Tag { struct Type { @@ -96,11 +103,11 @@ struct _Tag { Type::value type; }; -inline _Tag VerbatimTag(const std::string content) { +inline _Tag VerbatimTag(const std::string& content) { return _Tag("", content, _Tag::Type::Verbatim); } -inline _Tag LocalTag(const std::string content) { +inline _Tag LocalTag(const std::string& content) { return _Tag("", content, _Tag::Type::PrimaryHandle); } @@ -108,7 +115,7 @@ inline _Tag LocalTag(const std::string& prefix, const std::string content) { return _Tag(prefix, content, _Tag::Type::NamedHandle); } -inline _Tag SecondaryTag(const std::string content) { +inline _Tag SecondaryTag(const std::string& content) { return _Tag("", content, _Tag::Type::NamedHandle); } @@ -117,7 +124,7 @@ struct _Comment { std::string content; }; -inline _Comment Comment(const std::string content) { return _Comment(content); } +inline _Comment Comment(const std::string& content) { return _Comment(content); } struct _Precision { _Precision(int floatPrecision_, int doublePrecision_) diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h index efe381c6218..7242fe1f5ba 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/eventhandler.h @@ -17,7 +17,7 @@ struct Mark; class EventHandler { public: - virtual ~EventHandler() {} + virtual ~EventHandler() = default; virtual void OnDocumentStart(const Mark& mark) = 0; virtual void OnDocumentEnd() = 0; @@ -34,7 +34,12 @@ class EventHandler { virtual void OnMapStart(const Mark& mark, const std::string& tag, anchor_t anchor, EmitterStyle::value style) = 0; virtual void OnMapEnd() = 0; + + virtual void OnAnchor(const Mark& /*mark*/, + const std::string& /*anchor_name*/) { + // empty default implementation for compatibility + } }; -} +} // namespace YAML #endif // EVENTHANDLER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h index eae31968b7f..f6b2602ae1c 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h @@ -8,13 +8,12 @@ #endif #include "yaml-cpp/mark.h" +#include "yaml-cpp/noexcept.h" #include "yaml-cpp/traits.h" #include #include #include -#define YAML_CPP_NOEXCEPT noexcept - namespace YAML { // error messages namespace ErrorMsg { @@ -66,7 +65,7 @@ const char* const ZERO_INDENT_IN_BLOCK = const char* const CHAR_IN_BLOCK = "unexpected character in block scalar"; const char* const AMBIGUOUS_ANCHOR = "cannot assign the same alias to multiple nodes"; -const char* const UNKNOWN_ANCHOR = "the referenced anchor is not defined"; +const char* const UNKNOWN_ANCHOR = "the referenced anchor is not defined: "; const char* const INVALID_NODE = "invalid node; this may result from using a map iterator as a sequence " @@ -101,6 +100,12 @@ inline const std::string KEY_NOT_FOUND_WITH_KEY(const std::string& key) { return stream.str(); } +inline const std::string KEY_NOT_FOUND_WITH_KEY(const char* key) { + std::stringstream stream; + stream << KEY_NOT_FOUND << ": " << key; + return stream.str(); +} + template inline const std::string KEY_NOT_FOUND_WITH_KEY( const T& key, typename enable_if>::type* = 0) { @@ -108,13 +113,48 @@ inline const std::string KEY_NOT_FOUND_WITH_KEY( stream << KEY_NOT_FOUND << ": " << key; return stream.str(); } + +template +inline const std::string BAD_SUBSCRIPT_WITH_KEY( + const T&, typename disable_if>::type* = nullptr) { + return BAD_SUBSCRIPT; } +inline const std::string BAD_SUBSCRIPT_WITH_KEY(const std::string& key) { + std::stringstream stream; + stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")"; + return stream.str(); +} + +inline const std::string BAD_SUBSCRIPT_WITH_KEY(const char* key) { + std::stringstream stream; + stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")"; + return stream.str(); +} + +template +inline const std::string BAD_SUBSCRIPT_WITH_KEY( + const T& key, typename enable_if>::type* = nullptr) { + std::stringstream stream; + stream << BAD_SUBSCRIPT << " (key: \"" << key << "\")"; + return stream.str(); +} + +inline const std::string INVALID_NODE_WITH_KEY(const std::string& key) { + std::stringstream stream; + if (key.empty()) { + return INVALID_NODE; + } + stream << "invalid node; first invalid key: \"" << key << "\""; + return stream.str(); +} +} // namespace ErrorMsg + class YAML_CPP_API Exception : public std::runtime_error { public: Exception(const Mark& mark_, const std::string& msg_) : std::runtime_error(build_what(mark_, msg_)), mark(mark_), msg(msg_) {} - virtual ~Exception() YAML_CPP_NOEXCEPT; + ~Exception() YAML_CPP_NOEXCEPT override; Exception(const Exception&) = default; @@ -125,7 +165,7 @@ class YAML_CPP_API Exception : public std::runtime_error { static const std::string build_what(const Mark& mark, const std::string& msg) { if (mark.is_null()) { - return msg.c_str(); + return msg; } std::stringstream output; @@ -140,7 +180,7 @@ class YAML_CPP_API ParserException : public Exception { ParserException(const Mark& mark_, const std::string& msg_) : Exception(mark_, msg_) {} ParserException(const ParserException&) = default; - virtual ~ParserException() YAML_CPP_NOEXCEPT; + ~ParserException() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API RepresentationException : public Exception { @@ -148,7 +188,7 @@ class YAML_CPP_API RepresentationException : public Exception { RepresentationException(const Mark& mark_, const std::string& msg_) : Exception(mark_, msg_) {} RepresentationException(const RepresentationException&) = default; - virtual ~RepresentationException() YAML_CPP_NOEXCEPT; + ~RepresentationException() YAML_CPP_NOEXCEPT override; }; // representation exceptions @@ -157,7 +197,7 @@ class YAML_CPP_API InvalidScalar : public RepresentationException { InvalidScalar(const Mark& mark_) : RepresentationException(mark_, ErrorMsg::INVALID_SCALAR) {} InvalidScalar(const InvalidScalar&) = default; - virtual ~InvalidScalar() YAML_CPP_NOEXCEPT; + ~InvalidScalar() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API KeyNotFound : public RepresentationException { @@ -167,7 +207,7 @@ class YAML_CPP_API KeyNotFound : public RepresentationException { : RepresentationException(mark_, ErrorMsg::KEY_NOT_FOUND_WITH_KEY(key_)) { } KeyNotFound(const KeyNotFound&) = default; - virtual ~KeyNotFound() YAML_CPP_NOEXCEPT; + ~KeyNotFound() YAML_CPP_NOEXCEPT override; }; template @@ -175,7 +215,7 @@ class YAML_CPP_API TypedKeyNotFound : public KeyNotFound { public: TypedKeyNotFound(const Mark& mark_, const T& key_) : KeyNotFound(mark_, key_), key(key_) {} - virtual ~TypedKeyNotFound() YAML_CPP_NOEXCEPT {} + ~TypedKeyNotFound() YAML_CPP_NOEXCEPT override = default; T key; }; @@ -188,10 +228,11 @@ inline TypedKeyNotFound MakeTypedKeyNotFound(const Mark& mark, class YAML_CPP_API InvalidNode : public RepresentationException { public: - InvalidNode() - : RepresentationException(Mark::null_mark(), ErrorMsg::INVALID_NODE) {} + InvalidNode(const std::string& key) + : RepresentationException(Mark::null_mark(), + ErrorMsg::INVALID_NODE_WITH_KEY(key)) {} InvalidNode(const InvalidNode&) = default; - virtual ~InvalidNode() YAML_CPP_NOEXCEPT; + ~InvalidNode() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API BadConversion : public RepresentationException { @@ -199,7 +240,7 @@ class YAML_CPP_API BadConversion : public RepresentationException { explicit BadConversion(const Mark& mark_) : RepresentationException(mark_, ErrorMsg::BAD_CONVERSION) {} BadConversion(const BadConversion&) = default; - virtual ~BadConversion() YAML_CPP_NOEXCEPT; + ~BadConversion() YAML_CPP_NOEXCEPT override; }; template @@ -213,15 +254,16 @@ class YAML_CPP_API BadDereference : public RepresentationException { BadDereference() : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_DEREFERENCE) {} BadDereference(const BadDereference&) = default; - virtual ~BadDereference() YAML_CPP_NOEXCEPT; + ~BadDereference() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API BadSubscript : public RepresentationException { public: - BadSubscript() - : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_SUBSCRIPT) {} + template + BadSubscript(const Mark& mark_, const Key& key) + : RepresentationException(mark_, ErrorMsg::BAD_SUBSCRIPT_WITH_KEY(key)) {} BadSubscript(const BadSubscript&) = default; - virtual ~BadSubscript() YAML_CPP_NOEXCEPT; + ~BadSubscript() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API BadPushback : public RepresentationException { @@ -229,7 +271,7 @@ class YAML_CPP_API BadPushback : public RepresentationException { BadPushback() : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_PUSHBACK) {} BadPushback(const BadPushback&) = default; - virtual ~BadPushback() YAML_CPP_NOEXCEPT; + ~BadPushback() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API BadInsert : public RepresentationException { @@ -237,7 +279,7 @@ class YAML_CPP_API BadInsert : public RepresentationException { BadInsert() : RepresentationException(Mark::null_mark(), ErrorMsg::BAD_INSERT) {} BadInsert(const BadInsert&) = default; - virtual ~BadInsert() YAML_CPP_NOEXCEPT; + ~BadInsert() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API EmitterException : public Exception { @@ -245,17 +287,17 @@ class YAML_CPP_API EmitterException : public Exception { EmitterException(const std::string& msg_) : Exception(Mark::null_mark(), msg_) {} EmitterException(const EmitterException&) = default; - virtual ~EmitterException() YAML_CPP_NOEXCEPT; + ~EmitterException() YAML_CPP_NOEXCEPT override; }; class YAML_CPP_API BadFile : public Exception { public: - BadFile() : Exception(Mark::null_mark(), ErrorMsg::BAD_FILE) {} + explicit BadFile(const std::string& filename) + : Exception(Mark::null_mark(), + std::string(ErrorMsg::BAD_FILE) + ": " + filename) {} BadFile(const BadFile&) = default; - virtual ~BadFile() YAML_CPP_NOEXCEPT; + ~BadFile() YAML_CPP_NOEXCEPT override; }; -} - -#undef YAML_CPP_NOEXCEPT +} // namespace YAML #endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h index 45a878ab0c0..d0eb450f731 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/convert.h @@ -8,12 +8,20 @@ #endif #include +#include #include #include #include +#include #include +#include +#include #include +#if __cplusplus >= 201703L +#include +#endif + #include "yaml-cpp/binary.h" #include "yaml-cpp/node/impl.h" #include "yaml-cpp/node/iterator.h" @@ -21,6 +29,7 @@ #include "yaml-cpp/node/type.h" #include "yaml-cpp/null.h" + namespace YAML { class Binary; struct _Null; @@ -71,14 +80,33 @@ struct convert { // C-strings can only be encoded template <> struct convert { - static Node encode(const char*& rhs) { return Node(rhs); } + static Node encode(const char* rhs) { return Node(rhs); } +}; + +template <> +struct convert { + static Node encode(const char* rhs) { return Node(rhs); } }; template -struct convert { - static Node encode(const char(&rhs)[N]) { return Node(rhs); } +struct convert { + static Node encode(const char* rhs) { return Node(rhs); } }; +#if __cplusplus >= 201703L +template <> +struct convert { + static Node encode(std::string_view rhs) { return Node(std::string(rhs)); } + + static bool decode(const Node& node, std::string_view& rhs) { + if (!node.IsScalar()) + return false; + rhs = node.Scalar(); + return true; + } +}; +#endif + template <> struct convert<_Null> { static Node encode(const _Null& /* rhs */) { return Node(); } @@ -88,42 +116,98 @@ struct convert<_Null> { } }; -#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \ - template <> \ - struct convert { \ - static Node encode(const type& rhs) { \ - std::stringstream stream; \ - stream.precision(std::numeric_limits::digits10 + 1); \ - stream << rhs; \ - return Node(stream.str()); \ - } \ - \ - static bool decode(const Node& node, type& rhs) { \ - if (node.Type() != NodeType::Scalar) \ - return false; \ - const std::string& input = node.Scalar(); \ - std::stringstream stream(input); \ - stream.unsetf(std::ios::dec); \ - if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) \ - return true; \ - if (std::numeric_limits::has_infinity) { \ - if (conversion::IsInfinity(input)) { \ - rhs = std::numeric_limits::infinity(); \ - return true; \ - } else if (conversion::IsNegativeInfinity(input)) { \ - rhs = negative_op std::numeric_limits::infinity(); \ - return true; \ - } \ - } \ - \ - if (std::numeric_limits::has_quiet_NaN && \ - conversion::IsNaN(input)) { \ - rhs = std::numeric_limits::quiet_NaN(); \ - return true; \ - } \ - \ - return false; \ - } \ +namespace conversion { +template +typename std::enable_if< std::is_floating_point::value, void>::type +inner_encode(const T& rhs, std::stringstream& stream){ + if (std::isnan(rhs)) { + stream << ".nan"; + } else if (std::isinf(rhs)) { + if (std::signbit(rhs)) { + stream << "-.inf"; + } else { + stream << ".inf"; + } + } else { + stream << rhs; + } +} + +template +typename std::enable_if::value, void>::type +inner_encode(const T& rhs, std::stringstream& stream){ + stream << rhs; +} + +template +typename std::enable_if<(std::is_same::value || + std::is_same::value), bool>::type +ConvertStreamTo(std::stringstream& stream, T& rhs) { + int num; + if ((stream >> std::noskipws >> num) && (stream >> std::ws).eof()) { + if (num >= (std::numeric_limits::min)() && + num <= (std::numeric_limits::max)()) { + rhs = static_cast(num); + return true; + } + } + return false; +} + +template +typename std::enable_if::value || + std::is_same::value), bool>::type +ConvertStreamTo(std::stringstream& stream, T& rhs) { + if ((stream >> std::noskipws >> rhs) && (stream >> std::ws).eof()) { + return true; + } + return false; +} +} + +#define YAML_DEFINE_CONVERT_STREAMABLE(type, negative_op) \ + template <> \ + struct convert { \ + \ + static Node encode(const type& rhs) { \ + std::stringstream stream; \ + stream.precision(std::numeric_limits::max_digits10); \ + conversion::inner_encode(rhs, stream); \ + return Node(stream.str()); \ + } \ + \ + static bool decode(const Node& node, type& rhs) { \ + if (node.Type() != NodeType::Scalar) { \ + return false; \ + } \ + const std::string& input = node.Scalar(); \ + std::stringstream stream(input); \ + stream.unsetf(std::ios::dec); \ + if ((stream.peek() == '-') && std::is_unsigned::value) { \ + return false; \ + } \ + if (conversion::ConvertStreamTo(stream, rhs)) { \ + return true; \ + } \ + if (std::numeric_limits::has_infinity) { \ + if (conversion::IsInfinity(input)) { \ + rhs = std::numeric_limits::infinity(); \ + return true; \ + } else if (conversion::IsNegativeInfinity(input)) { \ + rhs = negative_op std::numeric_limits::infinity(); \ + return true; \ + } \ + } \ + \ + if (std::numeric_limits::has_quiet_NaN) { \ + if (conversion::IsNaN(input)) { \ + rhs = std::numeric_limits::quiet_NaN(); \ + return true; \ + } \ + } \ + \ + return false; \ + } \ } #define YAML_DEFINE_CONVERT_STREAMABLE_SIGNED(type) \ @@ -162,81 +246,104 @@ struct convert { }; // std::map -template -struct convert> { - static Node encode(const std::map& rhs) { +template +struct convert> { + static Node encode(const std::map& rhs) { Node node(NodeType::Map); - for (typename std::map::const_iterator it = rhs.begin(); - it != rhs.end(); ++it) - node.force_insert(it->first, it->second); + for (const auto& element : rhs) + node.force_insert(element.first, element.second); return node; } - static bool decode(const Node& node, std::map& rhs) { + static bool decode(const Node& node, std::map& rhs) { if (!node.IsMap()) return false; rhs.clear(); - for (const_iterator it = node.begin(); it != node.end(); ++it) + for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: - rhs[it->first.template as()] = it->second.template as(); + rhs[element.first.template as()] = element.second.template as(); #else - rhs[it->first.as()] = it->second.as(); + rhs[element.first.as()] = element.second.as(); +#endif + return true; + } +}; + +// std::unordered_map +template +struct convert> { + static Node encode(const std::unordered_map& rhs) { + Node node(NodeType::Map); + for (const auto& element : rhs) + node.force_insert(element.first, element.second); + return node; + } + + static bool decode(const Node& node, std::unordered_map& rhs) { + if (!node.IsMap()) + return false; + + rhs.clear(); + for (const auto& element : node) +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs[element.first.template as()] = element.second.template as(); +#else + rhs[element.first.as()] = element.second.as(); #endif return true; } }; // std::vector -template -struct convert> { - static Node encode(const std::vector& rhs) { +template +struct convert> { + static Node encode(const std::vector& rhs) { Node node(NodeType::Sequence); - for (typename std::vector::const_iterator it = rhs.begin(); - it != rhs.end(); ++it) - node.push_back(*it); + for (const auto& element : rhs) + node.push_back(element); return node; } - static bool decode(const Node& node, std::vector& rhs) { + static bool decode(const Node& node, std::vector& rhs) { if (!node.IsSequence()) return false; rhs.clear(); - for (const_iterator it = node.begin(); it != node.end(); ++it) + for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: - rhs.push_back(it->template as()); + rhs.push_back(element.template as()); #else - rhs.push_back(it->as()); + rhs.push_back(element.as()); #endif return true; } }; // std::list -template -struct convert> { - static Node encode(const std::list& rhs) { +template +struct convert> { + static Node encode(const std::list& rhs) { Node node(NodeType::Sequence); - for (typename std::list::const_iterator it = rhs.begin(); - it != rhs.end(); ++it) - node.push_back(*it); + for (const auto& element : rhs) + node.push_back(element); return node; } - static bool decode(const Node& node, std::list& rhs) { + static bool decode(const Node& node, std::list& rhs) { if (!node.IsSequence()) return false; rhs.clear(); - for (const_iterator it = node.begin(); it != node.end(); ++it) + for (const auto& element : node) #if defined(__GNUC__) && __GNUC__ < 4 // workaround for GCC 3: - rhs.push_back(it->template as()); + rhs.push_back(element.template as()); #else - rhs.push_back(it->as()); + rhs.push_back(element.as()); #endif return true; } @@ -275,6 +382,37 @@ struct convert> { } }; + +// std::valarray +template +struct convert> { + static Node encode(const std::valarray& rhs) { + Node node(NodeType::Sequence); + for (const auto& element : rhs) { + node.push_back(element); + } + return node; + } + + static bool decode(const Node& node, std::valarray& rhs) { + if (!node.IsSequence()) { + return false; + } + + rhs.resize(node.size()); + for (auto i = 0u; i < node.size(); ++i) { +#if defined(__GNUC__) && __GNUC__ < 4 + // workaround for GCC 3: + rhs[i] = node[i].template as(); +#else + rhs[i] = node[i].as(); +#endif + } + return true; + } +}; + + // std::pair template struct convert> { diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/bool_type.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/bool_type.h deleted file mode 100644 index 2c80705c9ae..00000000000 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/bool_type.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#define NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 - -#if defined(_MSC_VER) || \ - (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ - (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 -#pragma once -#endif - -namespace YAML { -namespace detail { -struct unspecified_bool { - struct NOT_ALLOWED; - static void true_value(NOT_ALLOWED*) {} -}; -typedef void (*unspecified_bool_type)(unspecified_bool::NOT_ALLOWED*); -} -} - -#define YAML_CPP_OPERATOR_BOOL() \ - operator YAML::detail::unspecified_bool_type() const { \ - return this->operator!() ? 0 \ - : &YAML::detail::unspecified_bool::true_value; \ - } - -#endif // NODE_DETAIL_BOOL_TYPE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h index 09e55f838c2..b38038dfd22 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/impl.h @@ -9,6 +9,8 @@ #include "yaml-cpp/node/detail/node.h" #include "yaml-cpp/node/detail/node_data.h" + +#include #include namespace YAML { @@ -17,7 +19,7 @@ template struct get_idx { static node* get(const std::vector& /* sequence */, const Key& /* key */, shared_memory_holder /* pMemory */) { - return 0; + return nullptr; } }; @@ -27,13 +29,13 @@ struct get_idx::value>::type> { static node* get(const std::vector& sequence, const Key& key, shared_memory_holder /* pMemory */) { - return key < sequence.size() ? sequence[key] : 0; + return key < sequence.size() ? sequence[key] : nullptr; } static node* get(std::vector& sequence, const Key& key, shared_memory_holder pMemory) { - if (key > sequence.size() || (key > 0 && !sequence[key-1]->is_defined())) - return 0; + if (key > sequence.size() || (key > 0 && !sequence[key - 1]->is_defined())) + return nullptr; if (key == sequence.size()) sequence.push_back(&pMemory->create_node()); return sequence[key]; @@ -46,13 +48,51 @@ struct get_idx::value>::type> { shared_memory_holder pMemory) { return key >= 0 ? get_idx::get( sequence, static_cast(key), pMemory) - : 0; + : nullptr; } static node* get(std::vector& sequence, const Key& key, shared_memory_holder pMemory) { return key >= 0 ? get_idx::get( sequence, static_cast(key), pMemory) - : 0; + : nullptr; + } +}; + +template +struct remove_idx { + static bool remove(std::vector&, const Key&, std::size_t&) { + return false; + } +}; + +template +struct remove_idx< + Key, typename std::enable_if::value && + !std::is_same::value>::type> { + + static bool remove(std::vector& sequence, const Key& key, + std::size_t& seqSize) { + if (key >= sequence.size()) { + return false; + } else { + sequence.erase(sequence.begin() + key); + if (seqSize > key) { + --seqSize; + } + return true; + } + } +}; + +template +struct remove_idx::value>::type> { + + static bool remove(std::vector& sequence, const Key& key, + std::size_t& seqSize) { + return key >= 0 ? remove_idx::remove( + sequence, static_cast(key), seqSize) + : false; } }; @@ -66,7 +106,11 @@ inline bool node::equals(const T& rhs, shared_memory_holder pMemory) { } inline bool node::equals(const char* rhs, shared_memory_holder pMemory) { - return equals(rhs, pMemory); + std::string lhs; + if (convert::decode(Node(*this, std::move(pMemory)), lhs)) { + return lhs == rhs; + } + return false; } // indexing @@ -78,22 +122,20 @@ inline node* node_data::get(const Key& key, break; case NodeType::Undefined: case NodeType::Null: - return NULL; + return nullptr; case NodeType::Sequence: if (node* pNode = get_idx::get(m_sequence, key, pMemory)) return pNode; - return NULL; + return nullptr; case NodeType::Scalar: - throw BadSubscript(); + throw BadSubscript(m_mark, key); } - for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->equals(key, pMemory)) { - return it->second; - } - } + auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) { + return m.first->equals(key, pMemory); + }); - return NULL; + return it != m_map.end() ? it->second : nullptr; } template @@ -112,13 +154,15 @@ inline node& node_data::get(const Key& key, shared_memory_holder pMemory) { convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(); + throw BadSubscript(m_mark, key); } - for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->equals(key, pMemory)) { - return *it->second; - } + auto it = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) { + return m.first->equals(key, pMemory); + }); + + if (it != m_map.end()) { + return *it->second; } node& k = convert_to_node(key, pMemory); @@ -129,20 +173,26 @@ inline node& node_data::get(const Key& key, shared_memory_holder pMemory) { template inline bool node_data::remove(const Key& key, shared_memory_holder pMemory) { - if (m_type != NodeType::Map) - return false; - - for (kv_pairs::iterator it = m_undefinedPairs.begin(); - it != m_undefinedPairs.end();) { - kv_pairs::iterator jt = std::next(it); - if (it->first->equals(key, pMemory)) - m_undefinedPairs.erase(it); - it = jt; + if (m_type == NodeType::Sequence) { + return remove_idx::remove(m_sequence, key, m_seqSize); } - for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->equals(key, pMemory)) { - m_map.erase(it); + if (m_type == NodeType::Map) { + kv_pairs::iterator it = m_undefinedPairs.begin(); + while (it != m_undefinedPairs.end()) { + kv_pairs::iterator jt = std::next(it); + if (it->first->equals(key, pMemory)) { + m_undefinedPairs.erase(it); + } + it = jt; + } + + auto iter = std::find_if(m_map.begin(), m_map.end(), [&](const kv_pair m) { + return m.first->equals(key, pMemory); + }); + + if (iter != m_map.end()) { + m_map.erase(iter); return true; } } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h index 0ea3bc3f0e3..997c69a14c5 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator.h @@ -8,12 +8,13 @@ #endif #include "yaml-cpp/dll.h" +#include "yaml-cpp/node/detail/node_iterator.h" #include "yaml-cpp/node/node.h" #include "yaml-cpp/node/ptr.h" -#include "yaml-cpp/node/detail/node_iterator.h" #include #include + namespace YAML { namespace detail { struct iterator_value; @@ -25,7 +26,7 @@ class iterator_base { template friend class iterator_base; struct enabler {}; - typedef node_iterator base_type; + using base_type = node_iterator; struct proxy { explicit proxy(const V& x) : m_ref(x) {} @@ -40,7 +41,7 @@ class iterator_base { using value_type = V; using difference_type = std::ptrdiff_t; using pointer = V*; - using reference = V&; + using reference = V; public: iterator_base() : m_iterator(), m_pMemory() {} @@ -89,7 +90,7 @@ class iterator_base { base_type m_iterator; shared_memory_holder m_pMemory; }; -} -} +} // namespace detail +} // namespace YAML #endif // VALUE_DETAIL_ITERATOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h index 5f1ffe7436d..75c9de086c0 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/iterator_fwd.h @@ -20,8 +20,8 @@ template class iterator_base; } -typedef detail::iterator_base iterator; -typedef detail::iterator_base const_iterator; +using iterator = detail::iterator_base; +using const_iterator = detail::iterator_base; } #endif // VALUE_DETAIL_ITERATOR_FWD_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h index 8f2bc2657a2..e881545bf2f 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/memory.h @@ -22,11 +22,12 @@ namespace YAML { namespace detail { class YAML_CPP_API memory { public: + memory() : m_nodes{} {} node& create_node(); void merge(const memory& rhs); private: - typedef std::set Nodes; + using Nodes = std::set; Nodes m_nodes; }; @@ -40,7 +41,7 @@ class YAML_CPP_API memory_holder { private: shared_memory m_pMemory; }; -} -} +} // namespace detail +} // namespace YAML #endif // VALUE_DETAIL_MEMORY_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h index 8a776f62a9e..acf60ffb6df 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node.h @@ -7,18 +7,24 @@ #pragma once #endif -#include "yaml-cpp/emitterstyle.h" #include "yaml-cpp/dll.h" -#include "yaml-cpp/node/type.h" -#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/emitterstyle.h" #include "yaml-cpp/node/detail/node_ref.h" +#include "yaml-cpp/node/ptr.h" +#include "yaml-cpp/node/type.h" #include +#include namespace YAML { namespace detail { class node { + private: + struct less { + bool operator ()(const node* l, const node* r) const {return l->m_index < r->m_index;} + }; + public: - node() : m_pRef(new node_ref) {} + node() : m_pRef(new node_ref), m_dependencies{}, m_index{} {} node(const node&) = delete; node& operator=(const node&) = delete; @@ -42,9 +48,8 @@ class node { return; m_pRef->mark_defined(); - for (nodes::iterator it = m_dependencies.begin(); - it != m_dependencies.end(); ++it) - (*it)->mark_defined(); + for (node* dependency : m_dependencies) + dependency->mark_defined(); m_dependencies.clear(); } @@ -109,6 +114,7 @@ class node { void push_back(node& input, shared_memory_holder pMemory) { m_pRef->push_back(input, pMemory); input.add_dependency(*this); + m_index = m_amount.fetch_add(1); } void insert(node& key, node& value, shared_memory_holder pMemory) { m_pRef->insert(key, value, pMemory); @@ -120,7 +126,7 @@ class node { template node* get(const Key& key, shared_memory_holder pMemory) const { // NOTE: this returns a non-const node so that the top-level Node can wrap - // it, and returns a pointer so that it can be NULL (if there is no such + // it, and returns a pointer so that it can be nullptr (if there is no such // key). return static_cast(*m_pRef).get(key, pMemory); } @@ -137,7 +143,7 @@ class node { node* get(node& key, shared_memory_holder pMemory) const { // NOTE: this returns a non-const node so that the top-level Node can wrap - // it, and returns a pointer so that it can be NULL (if there is no such + // it, and returns a pointer so that it can be nullptr (if there is no such // key). return static_cast(*m_pRef).get(key, pMemory); } @@ -160,10 +166,12 @@ class node { private: shared_node_ref m_pRef; - typedef std::set nodes; + using nodes = std::set; nodes m_dependencies; + size_t m_index; + static YAML_CPP_API std::atomic m_amount; }; -} -} +} // namespace detail +} // namespace YAML #endif // NODE_DETAIL_NODE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h index 50bcd74352d..07cf81aa099 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_data.h @@ -60,8 +60,8 @@ class YAML_CPP_API node_data { node_iterator end(); // sequence - void push_back(node& node, shared_memory_holder pMemory); - void insert(node& key, node& value, shared_memory_holder pMemory); + void push_back(node& node, const shared_memory_holder& pMemory); + void insert(node& key, node& value, const shared_memory_holder& pMemory); // indexing template @@ -71,9 +71,9 @@ class YAML_CPP_API node_data { template bool remove(const Key& key, shared_memory_holder pMemory); - node* get(node& key, shared_memory_holder pMemory) const; - node& get(node& key, shared_memory_holder pMemory); - bool remove(node& key, shared_memory_holder pMemory); + node* get(node& key, const shared_memory_holder& pMemory) const; + node& get(node& key, const shared_memory_holder& pMemory); + bool remove(node& key, const shared_memory_holder& pMemory); // map template @@ -81,7 +81,7 @@ class YAML_CPP_API node_data { shared_memory_holder pMemory); public: - static std::string empty_scalar; + static const std::string& empty_scalar(); private: void compute_seq_size() const; @@ -91,8 +91,8 @@ class YAML_CPP_API node_data { void reset_map(); void insert_map_pair(node& key, node& value); - void convert_to_map(shared_memory_holder pMemory); - void convert_sequence_to_map(shared_memory_holder pMemory); + void convert_to_map(const shared_memory_holder& pMemory); + void convert_sequence_to_map(const shared_memory_holder& pMemory); template static node& convert_to_node(const T& rhs, shared_memory_holder pMemory); @@ -108,17 +108,17 @@ class YAML_CPP_API node_data { std::string m_scalar; // sequence - typedef std::vector node_seq; + using node_seq = std::vector; node_seq m_sequence; mutable std::size_t m_seqSize; // map - typedef std::vector> node_map; + using node_map = std::vector>; node_map m_map; - typedef std::pair kv_pair; - typedef std::list kv_pairs; + using kv_pair = std::pair; + using kv_pairs = std::list; mutable kv_pairs m_undefinedPairs; }; } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h index 692afca3289..49dcf958dbb 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/detail/node_iterator.h @@ -24,11 +24,11 @@ struct iterator_type { template struct node_iterator_value : public std::pair { - typedef std::pair kv; + using kv = std::pair; - node_iterator_value() : kv(), pNode(0) {} + node_iterator_value() : kv(), pNode(nullptr) {} explicit node_iterator_value(V& rhs) : kv(), pNode(&rhs) {} - explicit node_iterator_value(V& key, V& value) : kv(&key, &value), pNode(0) {} + explicit node_iterator_value(V& key, V& value) : kv(&key, &value), pNode(nullptr) {} V& operator*() const { return *pNode; } V& operator->() const { return *pNode; } @@ -36,19 +36,19 @@ struct node_iterator_value : public std::pair { V* pNode; }; -typedef std::vector node_seq; -typedef std::vector> node_map; +using node_seq = std::vector; +using node_map = std::vector>; template struct node_iterator_type { - typedef node_seq::iterator seq; - typedef node_map::iterator map; + using seq = node_seq::iterator; + using map = node_map::iterator; }; template struct node_iterator_type { - typedef node_seq::const_iterator seq; - typedef node_map::const_iterator map; + using seq = node_seq::const_iterator; + using map = node_map::const_iterator; }; template @@ -65,13 +65,13 @@ class node_iterator_base { }; public: - typedef typename node_iterator_type::seq SeqIter; - typedef typename node_iterator_type::map MapIter; using iterator_category = std::forward_iterator_tag; using value_type = node_iterator_value; using difference_type = std::ptrdiff_t; using pointer = node_iterator_value*; - using reference = node_iterator_value&; + using reference = node_iterator_value; + using SeqIter = typename node_iterator_type::seq; + using MapIter = typename node_iterator_type::map; node_iterator_base() : m_type(iterator_type::NoneType), m_seqIt(), m_mapIt(), m_mapEnd() {} @@ -173,8 +173,8 @@ class node_iterator_base { MapIter m_mapIt, m_mapEnd; }; -typedef node_iterator_base node_iterator; -typedef node_iterator_base const_node_iterator; +using node_iterator = node_iterator_base; +using const_node_iterator = node_iterator_base; } } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h index 20c487a687f..312281f18cc 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/impl.h @@ -7,18 +7,21 @@ #pragma once #endif -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/exceptions.h" #include "yaml-cpp/node/detail/memory.h" #include "yaml-cpp/node/detail/node.h" -#include "yaml-cpp/exceptions.h" +#include "yaml-cpp/node/iterator.h" +#include "yaml-cpp/node/node.h" +#include #include namespace YAML { -inline Node::Node() : m_isValid(true), m_pNode(NULL) {} +inline Node::Node() + : m_isValid(true), m_invalidKey{}, m_pMemory(nullptr), m_pNode(nullptr) {} inline Node::Node(NodeType::value type) : m_isValid(true), + m_invalidKey{}, m_pMemory(new detail::memory_holder), m_pNode(&m_pMemory->create_node()) { m_pNode->set_type(type); @@ -27,6 +30,7 @@ inline Node::Node(NodeType::value type) template inline Node::Node(const T& rhs) : m_isValid(true), + m_invalidKey{}, m_pMemory(new detail::memory_holder), m_pNode(&m_pMemory->create_node()) { Assign(rhs); @@ -34,24 +38,26 @@ inline Node::Node(const T& rhs) inline Node::Node(const detail::iterator_value& rhs) : m_isValid(rhs.m_isValid), + m_invalidKey(rhs.m_invalidKey), m_pMemory(rhs.m_pMemory), m_pNode(rhs.m_pNode) {} -inline Node::Node(const Node& rhs) - : m_isValid(rhs.m_isValid), - m_pMemory(rhs.m_pMemory), - m_pNode(rhs.m_pNode) {} +inline Node::Node(const Node&) = default; -inline Node::Node(Zombie) : m_isValid(false), m_pNode(NULL) {} +inline Node::Node(Zombie) + : m_isValid(false), m_invalidKey{}, m_pMemory{}, m_pNode(nullptr) {} + +inline Node::Node(Zombie, const std::string& key) + : m_isValid(false), m_invalidKey(key), m_pMemory{}, m_pNode(nullptr) {} inline Node::Node(detail::node& node, detail::shared_memory_holder pMemory) - : m_isValid(true), m_pMemory(pMemory), m_pNode(&node) {} + : m_isValid(true), m_invalidKey{}, m_pMemory(pMemory), m_pNode(&node) {} -inline Node::~Node() {} +inline Node::~Node() = default; inline void Node::EnsureNodeExists() const { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); if (!m_pNode) { m_pMemory.reset(new detail::memory_holder); m_pNode = &m_pMemory->create_node(); @@ -68,14 +74,14 @@ inline bool Node::IsDefined() const { inline Mark Node::Mark() const { if (!m_isValid) { - throw InvalidNode(); + throw InvalidNode(m_invalidKey); } return m_pNode ? m_pNode->mark() : Mark::null_mark(); } inline NodeType::value Node::Type() const { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); return m_pNode ? m_pNode->type() : NodeType::Null; } @@ -104,6 +110,8 @@ struct as_if { const Node& node; std::string operator()(const S& fallback) const { + if (node.Type() == NodeType::Null) + return "null"; if (node.Type() != NodeType::Scalar) return fallback; return node.Scalar(); @@ -132,6 +140,8 @@ struct as_if { const Node& node; std::string operator()() const { + if (node.Type() == NodeType::Null) + return "null"; if (node.Type() != NodeType::Scalar) throw TypedBadConversion(node.Mark()); return node.Scalar(); @@ -142,7 +152,7 @@ struct as_if { template inline T Node::as() const { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); return as_if(*this)(); } @@ -155,32 +165,28 @@ inline T Node::as(const S& fallback) const { inline const std::string& Node::Scalar() const { if (!m_isValid) - throw InvalidNode(); - return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar; + throw InvalidNode(m_invalidKey); + return m_pNode ? m_pNode->scalar() : detail::node_data::empty_scalar(); } inline const std::string& Node::Tag() const { if (!m_isValid) - throw InvalidNode(); - return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar; + throw InvalidNode(m_invalidKey); + return m_pNode ? m_pNode->tag() : detail::node_data::empty_scalar(); } inline void Node::SetTag(const std::string& tag) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); m_pNode->set_tag(tag); } inline EmitterStyle::value Node::Style() const { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); return m_pNode ? m_pNode->style() : EmitterStyle::Default; } inline void Node::SetStyle(EmitterStyle::value style) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); m_pNode->set_style(style); } @@ -188,7 +194,7 @@ inline void Node::SetStyle(EmitterStyle::value style) { // assignment inline bool Node::is(const Node& rhs) const { if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); if (!m_pNode || !rhs.m_pNode) return false; return m_pNode->is(*rhs.m_pNode); @@ -196,15 +202,20 @@ inline bool Node::is(const Node& rhs) const { template inline Node& Node::operator=(const T& rhs) { - if (!m_isValid) - throw InvalidNode(); Assign(rhs); return *this; } +inline Node& Node::operator=(const Node& rhs) { + if (is(rhs)) + return *this; + AssignNode(rhs); + return *this; +} + inline void Node::reset(const YAML::Node& rhs) { if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); m_pMemory = rhs.m_pMemory; m_pNode = rhs.m_pNode; } @@ -212,44 +223,27 @@ inline void Node::reset(const YAML::Node& rhs) { template inline void Node::Assign(const T& rhs) { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); AssignData(convert::encode(rhs)); } template <> inline void Node::Assign(const std::string& rhs) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); m_pNode->set_scalar(rhs); } inline void Node::Assign(const char* rhs) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); m_pNode->set_scalar(rhs); } inline void Node::Assign(char* rhs) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); m_pNode->set_scalar(rhs); } -inline Node& Node::operator=(const Node& rhs) { - if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); - if (is(rhs)) - return *this; - AssignNode(rhs); - return *this; -} - inline void Node::AssignData(const Node& rhs) { - if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); EnsureNodeExists(); rhs.EnsureNodeExists(); @@ -258,8 +252,8 @@ inline void Node::AssignData(const Node& rhs) { } inline void Node::AssignNode(const Node& rhs) { - if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); + if (!m_isValid) + throw InvalidNode(m_invalidKey); rhs.EnsureNodeExists(); if (!m_pNode) { @@ -276,7 +270,7 @@ inline void Node::AssignNode(const Node& rhs) { // size/iterator inline std::size_t Node::size() const { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); return m_pNode ? m_pNode->size() : 0; } @@ -309,13 +303,11 @@ inline iterator Node::end() { template inline void Node::push_back(const T& rhs) { if (!m_isValid) - throw InvalidNode(); + throw InvalidNode(m_invalidKey); push_back(Node(rhs)); } inline void Node::push_back(const Node& rhs) { - if (!m_isValid || !rhs.m_isValid) - throw InvalidNode(); EnsureNodeExists(); rhs.EnsureNodeExists(); @@ -323,99 +315,49 @@ inline void Node::push_back(const Node& rhs) { m_pMemory->merge(*rhs.m_pMemory); } -// helpers for indexing -namespace detail { -template -struct to_value_t { - explicit to_value_t(const T& t_) : t(t_) {} - const T& t; - typedef const T& return_type; - - const T& operator()() const { return t; } -}; - -template <> -struct to_value_t { - explicit to_value_t(const char* t_) : t(t_) {} - const char* t; - typedef std::string return_type; - - const std::string operator()() const { return t; } -}; - -template <> -struct to_value_t { - explicit to_value_t(char* t_) : t(t_) {} - const char* t; - typedef std::string return_type; - - const std::string operator()() const { return t; } -}; - -template -struct to_value_t { - explicit to_value_t(const char* t_) : t(t_) {} - const char* t; - typedef std::string return_type; - - const std::string operator()() const { return t; } -}; - -// converts C-strings to std::strings so they can be copied -template -inline typename to_value_t::return_type to_value(const T& t) { - return to_value_t(t)(); -} +template +std::string key_to_string(const Key& key) { + return streamable_to_string::value>().impl(key); } // indexing template inline const Node Node::operator[](const Key& key) const { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); - detail::node* value = static_cast(*m_pNode) - .get(detail::to_value(key), m_pMemory); + detail::node* value = + static_cast(*m_pNode).get(key, m_pMemory); if (!value) { - return Node(ZombieNode); + return Node(ZombieNode, key_to_string(key)); } return Node(*value, m_pMemory); } template inline Node Node::operator[](const Key& key) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); - detail::node& value = m_pNode->get(detail::to_value(key), m_pMemory); + detail::node& value = m_pNode->get(key, m_pMemory); return Node(value, m_pMemory); } template inline bool Node::remove(const Key& key) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); - return m_pNode->remove(detail::to_value(key), m_pMemory); + return m_pNode->remove(key, m_pMemory); } inline const Node Node::operator[](const Node& key) const { - if (!m_isValid || !key.m_isValid) - throw InvalidNode(); EnsureNodeExists(); key.EnsureNodeExists(); m_pMemory->merge(*key.m_pMemory); detail::node* value = static_cast(*m_pNode).get(*key.m_pNode, m_pMemory); if (!value) { - return Node(ZombieNode); + return Node(ZombieNode, key_to_string(key)); } return Node(*value, m_pMemory); } inline Node Node::operator[](const Node& key) { - if (!m_isValid || !key.m_isValid) - throw InvalidNode(); EnsureNodeExists(); key.EnsureNodeExists(); m_pMemory->merge(*key.m_pMemory); @@ -424,8 +366,6 @@ inline Node Node::operator[](const Node& key) { } inline bool Node::remove(const Node& key) { - if (!m_isValid || !key.m_isValid) - throw InvalidNode(); EnsureNodeExists(); key.EnsureNodeExists(); return m_pNode->remove(*key.m_pNode, m_pMemory); @@ -434,15 +374,12 @@ inline bool Node::remove(const Node& key) { // map template inline void Node::force_insert(const Key& key, const Value& value) { - if (!m_isValid) - throw InvalidNode(); EnsureNodeExists(); - m_pNode->force_insert(detail::to_value(key), detail::to_value(value), - m_pMemory); + m_pNode->force_insert(key, value, m_pMemory); } // free functions inline bool operator==(const Node& lhs, const Node& rhs) { return lhs.is(rhs); } -} +} // namespace YAML #endif // NODE_IMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h index 366a9c807fe..1fcf6e400ff 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/iterator.h @@ -15,10 +15,13 @@ #include #include +// Assert in place so gcc + libc++ combination properly builds +static_assert(std::is_constructible::value, "Node must be copy constructable"); + namespace YAML { namespace detail { struct iterator_value : public Node, std::pair { - iterator_value() {} + iterator_value() = default; explicit iterator_value(const Node& rhs) : Node(rhs), std::pair(Node(Node::ZombieNode), Node(Node::ZombieNode)) {} diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h index 1ded7d27b72..c9e9a0a4bc1 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/node.h @@ -8,11 +8,11 @@ #endif #include +#include #include "yaml-cpp/dll.h" #include "yaml-cpp/emitterstyle.h" #include "yaml-cpp/mark.h" -#include "yaml-cpp/node/detail/bool_type.h" #include "yaml-cpp/node/detail/iterator_fwd.h" #include "yaml-cpp/node/ptr.h" #include "yaml-cpp/node/type.h" @@ -38,8 +38,8 @@ class YAML_CPP_API Node { template friend struct as_if; - typedef YAML::iterator iterator; - typedef YAML::const_iterator const_iterator; + using iterator = YAML::iterator; + using const_iterator = YAML::const_iterator; Node(); explicit Node(NodeType::value type); @@ -58,7 +58,7 @@ class YAML_CPP_API Node { bool IsMap() const { return Type() == NodeType::Map; } // bool conversions - YAML_CPP_OPERATOR_BOOL() + explicit operator bool() const { return IsDefined(); } bool operator!() const { return !IsDefined(); } // access @@ -116,6 +116,7 @@ class YAML_CPP_API Node { private: enum Zombie { ZombieNode }; explicit Node(Zombie); + explicit Node(Zombie, const std::string&); explicit Node(detail::node& node, detail::shared_memory_holder pMemory); void EnsureNodeExists() const; @@ -130,6 +131,8 @@ class YAML_CPP_API Node { private: bool m_isValid; + // String representation of invalid key, if the node is invalid. + std::string m_invalidKey; mutable detail::shared_memory_holder m_pMemory; mutable detail::node* m_pNode; }; diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h index ce085dd5cd8..f55d95ed9ca 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/node/ptr.h @@ -7,7 +7,6 @@ #pragma once #endif -#include "yaml-cpp/dll.h" #include namespace YAML { @@ -18,11 +17,11 @@ class node_data; class memory; class memory_holder; -typedef std::shared_ptr shared_node; -typedef std::shared_ptr shared_node_ref; -typedef std::shared_ptr shared_node_data; -typedef std::shared_ptr shared_memory_holder; -typedef std::shared_ptr shared_memory; +using shared_node = std::shared_ptr; +using shared_node_ref = std::shared_ptr; +using shared_node_data = std::shared_ptr; +using shared_memory_holder = std::shared_ptr; +using shared_memory = std::shared_ptr; } } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h new file mode 100644 index 00000000000..6aac63516f3 --- /dev/null +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noexcept.h @@ -0,0 +1,18 @@ +#ifndef NOEXCEPT_H_768872DA_476C_11EA_88B8_90B11C0C0FF8 +#define NOEXCEPT_H_768872DA_476C_11EA_88B8_90B11C0C0FF8 + +#if defined(_MSC_VER) || \ + (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ + (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 +#pragma once +#endif + +// This is here for compatibility with older versions of Visual Studio +// which don't support noexcept. +#if defined(_MSC_VER) && _MSC_VER < 1900 + #define YAML_CPP_NOEXCEPT _NOEXCEPT +#else + #define YAML_CPP_NOEXCEPT noexcept +#endif + +#endif diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noncopyable.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noncopyable.h deleted file mode 100644 index a261040739b..00000000000 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/noncopyable.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 -#define NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 - -#if defined(_MSC_VER) || \ - (defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \ - (__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4 -#pragma once -#endif - -#include "yaml-cpp/dll.h" - -namespace YAML { -// this is basically boost::noncopyable -class YAML_CPP_API noncopyable { - protected: - noncopyable() {} - ~noncopyable() {} - - private: - noncopyable(const noncopyable&); - const noncopyable& operator=(const noncopyable&); -}; -} - -#endif // NONCOPYABLE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h index 09d45f39b78..cf89741d093 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/ostream_wrapper.h @@ -17,6 +17,10 @@ class YAML_CPP_API ostream_wrapper { public: ostream_wrapper(); explicit ostream_wrapper(std::ostream& stream); + ostream_wrapper(const ostream_wrapper&) = delete; + ostream_wrapper(ostream_wrapper&&) = delete; + ostream_wrapper& operator=(const ostream_wrapper&) = delete; + ostream_wrapper& operator=(ostream_wrapper&&) = delete; ~ostream_wrapper(); void write(const std::string& str); @@ -26,7 +30,7 @@ class YAML_CPP_API ostream_wrapper { const char* str() const { if (m_pStream) { - return 0; + return nullptr; } else { m_buffer[m_pos] = '\0'; return &m_buffer[0]; @@ -52,7 +56,7 @@ class YAML_CPP_API ostream_wrapper { template inline ostream_wrapper& operator<<(ostream_wrapper& stream, - const char(&str)[N]) { + const char (&str)[N]) { stream.write(str, N - 1); return stream; } @@ -67,6 +71,6 @@ inline ostream_wrapper& operator<<(ostream_wrapper& stream, char ch) { stream.write(&ch, 1); return stream; } -} +} // namespace YAML #endif // OSTREAM_WRAPPER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h index ceac22d0268..2f403c35048 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/parser.h @@ -11,7 +11,6 @@ #include #include "yaml-cpp/dll.h" -#include "yaml-cpp/noncopyable.h" namespace YAML { class EventHandler; @@ -24,11 +23,16 @@ struct Token; * A parser turns a stream of bytes into one stream of "events" per YAML * document in the input stream. */ -class YAML_CPP_API Parser : private noncopyable { +class YAML_CPP_API Parser { public: /** Constructs an empty parser (with no input. */ Parser(); + Parser(const Parser&) = delete; + Parser(Parser&&) = delete; + Parser& operator=(const Parser&) = delete; + Parser& operator=(Parser&&) = delete; + /** * Constructs a parser from the given input stream. The input stream must * live as long as the parser. @@ -81,6 +85,6 @@ class YAML_CPP_API Parser : private noncopyable { std::unique_ptr m_pScanner; std::unique_ptr m_pDirectives; }; -} +} // namespace YAML #endif // PARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h index 06780c861f1..210a2f64e6a 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/stlemitter.h @@ -16,8 +16,8 @@ namespace YAML { template inline Emitter& EmitSeq(Emitter& emitter, const Seq& seq) { emitter << BeginSeq; - for (typename Seq::const_iterator it = seq.begin(); it != seq.end(); ++it) - emitter << *it; + for (const auto& v : seq) + emitter << v; emitter << EndSeq; return emitter; } @@ -39,10 +39,9 @@ inline Emitter& operator<<(Emitter& emitter, const std::set& v) { template inline Emitter& operator<<(Emitter& emitter, const std::map& m) { - typedef typename std::map map; emitter << BeginMap; - for (typename map::const_iterator it = m.begin(); it != m.end(); ++it) - emitter << Key << it->first << Value << it->second; + for (const auto& v : m) + emitter << Key << v.first << Value << v.second; emitter << EndMap; return emitter; } diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h index f33d0e1f637..ffe9999f191 100644 --- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h +++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/traits.h @@ -7,6 +7,11 @@ #pragma once #endif +#include +#include +#include +#include + namespace YAML { template struct is_numeric { @@ -79,7 +84,7 @@ struct is_numeric { template struct enable_if_c { - typedef T type; + using type = T; }; template @@ -90,7 +95,7 @@ struct enable_if : public enable_if_c {}; template struct disable_if_c { - typedef T type; + using type = T; }; template @@ -100,4 +105,31 @@ template struct disable_if : public disable_if_c {}; } +template +struct is_streamable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...) -> std::false_type; + + static const bool value = decltype(test(0))::value; +}; + +template +struct streamable_to_string { + static std::string impl(const Key& key) { + std::stringstream ss; + ss << key; + return ss.str(); + } +}; + +template +struct streamable_to_string { + static std::string impl(const Key&) { + return ""; + } +}; #endif // TRAITS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch b/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch index 93bdc64f64f..218b9e1ef79 100644 --- a/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch +++ b/src/libs/3rdparty/yaml-cpp/patches/0001-yaml-cpp-Strip-unneeded-sources.patch @@ -1,1667 +1,1556 @@ -From dd3d5fa32be556e54d2ee2247a06e1fc7e5c8026 Mon Sep 17 00:00:00 2001 -From: Nikolai Kosjar -Date: Thu, 22 Aug 2019 15:00:03 +0200 +From 484b421d0004ce37d7abd63bd0043819ad3a9d9f Mon Sep 17 00:00:00 2001 +From: Eike Ziller +Date: Fri, 25 Aug 2023 13:00:39 +0200 Subject: [PATCH] yaml-cpp: Strip unneeded sources -Change-Id: I2f863b06c1cd42abadc64897cd6282c4a2c8a8e3 --- - src/libs/3rdparty/yaml-cpp/.codedocs | 50 - - src/libs/3rdparty/yaml-cpp/.gitignore | 1 - - src/libs/3rdparty/yaml-cpp/.travis.yml | 28 - - src/libs/3rdparty/yaml-cpp/CMakeLists.txt | 365 - - src/libs/3rdparty/yaml-cpp/CONTRIBUTING.md | 26 - - .../include/yaml-cpp/contrib/anchordict.h | 39 - - .../include/yaml-cpp/contrib/graphbuilder.h | 149 - - src/libs/3rdparty/yaml-cpp/install.txt | 24 - - .../yaml-cpp/src/contrib/graphbuilder.cpp | 17 - - .../src/contrib/graphbuilderadapter.cpp | 94 - - .../src/contrib/graphbuilderadapter.h | 79 - - .../3rdparty/yaml-cpp/test/CMakeLists.txt | 44 - - .../yaml-cpp/test/create-emitter-tests.py | 211 - - .../yaml-cpp/test/gtest-1.8.0/.gitignore | 2 - - .../yaml-cpp/test/gtest-1.8.0/.travis.yml | 46 - - .../yaml-cpp/test/gtest-1.8.0/CMakeLists.txt | 16 - - .../yaml-cpp/test/gtest-1.8.0/README.md | 142 - - .../yaml-cpp/test/gtest-1.8.0/appveyor.yml | 71 - - .../test/gtest-1.8.0/googlemock/CHANGES | 126 - - .../gtest-1.8.0/googlemock/CMakeLists.txt | 202 - - .../test/gtest-1.8.0/googlemock/CONTRIBUTORS | 40 - - .../test/gtest-1.8.0/googlemock/LICENSE | 28 - - .../test/gtest-1.8.0/googlemock/README.md | 333 - - .../gtest-1.8.0/googlemock/build-aux/.keep | 0 - .../test/gtest-1.8.0/googlemock/configure.ac | 146 - - .../gtest-1.8.0/googlemock/docs/CheatSheet.md | 562 - - .../gtest-1.8.0/googlemock/docs/CookBook.md | 3675 ------- - .../gtest-1.8.0/googlemock/docs/DesignDoc.md | 280 - - .../gtest-1.8.0/googlemock/docs/DevGuide.md | 132 - - .../googlemock/docs/Documentation.md | 12 - - .../gtest-1.8.0/googlemock/docs/ForDummies.md | 439 - - .../docs/FrequentlyAskedQuestions.md | 628 -- - .../googlemock/docs/KnownIssues.md | 19 - - .../googlemock/docs/v1_5/CheatSheet.md | 525 - - .../googlemock/docs/v1_5/CookBook.md | 3250 ------ - .../googlemock/docs/v1_5/Documentation.md | 11 - - .../googlemock/docs/v1_5/ForDummies.md | 439 - - .../docs/v1_5/FrequentlyAskedQuestions.md | 624 -- - .../googlemock/docs/v1_6/CheatSheet.md | 534 - - .../googlemock/docs/v1_6/CookBook.md | 3342 ------ - .../googlemock/docs/v1_6/Documentation.md | 12 - - .../googlemock/docs/v1_6/ForDummies.md | 439 - - .../docs/v1_6/FrequentlyAskedQuestions.md | 628 -- - .../googlemock/docs/v1_7/CheatSheet.md | 556 - - .../googlemock/docs/v1_7/CookBook.md | 3432 ------ - .../googlemock/docs/v1_7/Documentation.md | 12 - - .../googlemock/docs/v1_7/ForDummies.md | 439 - - .../docs/v1_7/FrequentlyAskedQuestions.md | 628 -- - .../googlemock/include/gmock/gmock-actions.h | 1205 -- - .../include/gmock/gmock-cardinalities.h | 147 - - .../include/gmock/gmock-generated-actions.h | 2377 ---- - .../gmock/gmock-generated-actions.h.pump | 794 -- - .../gmock/gmock-generated-function-mockers.h | 1095 -- - .../gmock-generated-function-mockers.h.pump | 291 - - .../include/gmock/gmock-generated-matchers.h | 2179 ---- - .../gmock/gmock-generated-matchers.h.pump | 672 -- - .../gmock/gmock-generated-nice-strict.h | 397 - - .../gmock/gmock-generated-nice-strict.h.pump | 161 - - .../googlemock/include/gmock/gmock-matchers.h | 4399 -------- - .../include/gmock/gmock-more-actions.h | 246 - - .../include/gmock/gmock-more-matchers.h | 58 - - .../include/gmock/gmock-spec-builders.h | 1847 ---- - .../googlemock/include/gmock/gmock.h | 94 - - .../internal/custom/gmock-generated-actions.h | 8 - - .../custom/gmock-generated-actions.h.pump | 10 - - .../gmock/internal/custom/gmock-matchers.h | 39 - - .../gmock/internal/custom/gmock-port.h | 46 - - .../internal/gmock-generated-internal-utils.h | 279 - - .../gmock-generated-internal-utils.h.pump | 136 - - .../gmock/internal/gmock-internal-utils.h | 511 - - .../include/gmock/internal/gmock-port.h | 91 - - .../googlemock/msvc/2005/gmock_config.vsprops | 15 - - .../googlemock/msvc/2010/gmock_config.props | 19 - - .../googlemock/msvc/2015/gmock_config.props | 19 - - .../googlemock/scripts/fuse_gmock_files.py | 240 - + .codedocs | 50 - + .github/workflows/build.yml | 72 - + .gitignore | 3 - + BUILD.bazel | 21 - + CMakeLists.txt | 207 - + CONTRIBUTING.md | 26 - + SECURITY.md | 13 - + WORKSPACE | 10 - + cmake_uninstall.cmake.in | 21 - + docs/Breaking-Changes.md | 52 - + docs/How-To-Emit-YAML.md | 230 - + docs/How-To-Parse-A-Document-(Old-API).md | 265 - + docs/Strings.md | 18 - + docs/Tutorial.md | 201 - + docs/_config.yml | 1 - + docs/index.md | 1 - + include/yaml-cpp/contrib/anchordict.h | 40 - + include/yaml-cpp/contrib/graphbuilder.h | 149 - + install.txt | 24 - + src/contrib/graphbuilder.cpp | 16 - + src/contrib/graphbuilderadapter.cpp | 94 - + src/contrib/graphbuilderadapter.h | 86 - + src/contrib/yaml-cpp.natvis | 32 - + src/contrib/yaml-cpp.natvis.md | 9 - + test/BUILD.bazel | 14 - + test/CMakeLists.txt | 56 - + test/binary_test.cpp | 14 - + test/create-emitter-tests.py | 223 - + test/gtest-1.11.0/.clang-format | 4 - + .../.github/ISSUE_TEMPLATE/00-bug_report.md | 43 - + .../ISSUE_TEMPLATE/10-feature_request.md | 24 - + .../.github/ISSUE_TEMPLATE/config.yml | 1 - + test/gtest-1.11.0/.gitignore | 84 - + test/gtest-1.11.0/BUILD.bazel | 190 - + test/gtest-1.11.0/CMakeLists.txt | 32 - + test/gtest-1.11.0/CONTRIBUTING.md | 130 - + test/gtest-1.11.0/CONTRIBUTORS | 63 - + test/gtest-1.11.0/LICENSE | 28 - + test/gtest-1.11.0/README.md | 140 - + test/gtest-1.11.0/WORKSPACE | 24 - + test/gtest-1.11.0/ci/linux-presubmit.sh | 126 - + test/gtest-1.11.0/ci/macos-presubmit.sh | 73 - + test/gtest-1.11.0/docs/_config.yml | 1 - + test/gtest-1.11.0/docs/_data/navigation.yml | 43 - + test/gtest-1.11.0/docs/_layouts/default.html | 58 - + test/gtest-1.11.0/docs/_sass/main.scss | 200 - + test/gtest-1.11.0/docs/advanced.md | 2318 ---- + test/gtest-1.11.0/docs/assets/css/style.scss | 5 - + .../docs/community_created_documentation.md | 7 - + test/gtest-1.11.0/docs/faq.md | 693 -- + test/gtest-1.11.0/docs/gmock_cheat_sheet.md | 241 - + test/gtest-1.11.0/docs/gmock_cook_book.md | 4301 ------- + test/gtest-1.11.0/docs/gmock_faq.md | 390 - + test/gtest-1.11.0/docs/gmock_for_dummies.md | 700 -- + test/gtest-1.11.0/docs/index.md | 22 - + test/gtest-1.11.0/docs/pkgconfig.md | 148 - + test/gtest-1.11.0/docs/platforms.md | 35 - + test/gtest-1.11.0/docs/primer.md | 482 - + test/gtest-1.11.0/docs/quickstart-bazel.md | 161 - + test/gtest-1.11.0/docs/quickstart-cmake.md | 156 - + test/gtest-1.11.0/docs/reference/actions.md | 115 - + .../gtest-1.11.0/docs/reference/assertions.md | 633 -- + test/gtest-1.11.0/docs/reference/matchers.md | 283 - + test/gtest-1.11.0/docs/reference/mocking.md | 587 - + test/gtest-1.11.0/docs/reference/testing.md | 1431 --- + test/gtest-1.11.0/docs/samples.md | 22 - + test/gtest-1.11.0/googlemock/CMakeLists.txt | 218 - + test/gtest-1.11.0/googlemock/README.md | 44 - + .../gtest-1.11.0/googlemock/cmake/gmock.pc.in | 10 - + .../googlemock/cmake/gmock_main.pc.in | 10 - + test/gtest-1.11.0/googlemock/docs/README.md | 4 - + .../googlemock/include/gmock/gmock-actions.h | 1687 --- + .../include/gmock/gmock-cardinalities.h | 157 - + .../include/gmock/gmock-function-mocker.h | 479 - + .../googlemock/include/gmock/gmock-matchers.h | 5392 --------- + .../include/gmock/gmock-more-actions.h | 573 - + .../include/gmock/gmock-more-matchers.h | 92 - + .../include/gmock/gmock-nice-strict.h | 261 - + .../include/gmock/gmock-spec-builders.h | 2038 ---- + .../googlemock/include/gmock/gmock.h | 98 - + .../include/gmock/internal/custom/README.md | 16 - + .../internal/custom/gmock-generated-actions.h | 6 - + .../gmock/internal/custom/gmock-matchers.h | 36 - + .../gmock/internal/custom/gmock-port.h | 39 - + .../gmock/internal/gmock-internal-utils.h | 459 - + .../include/gmock/internal/gmock-port.h | 87 - + .../include/gmock/internal/gmock-pp.h | 279 - + .../gtest-1.11.0/googlemock/scripts/README.md | 5 - + .../googlemock/scripts/fuse_gmock_files.py | 256 - .../googlemock/scripts/generator/LICENSE | 203 - - .../googlemock/scripts/generator/README | 35 - + .../googlemock/scripts/generator/README | 34 - .../scripts/generator/README.cppclean | 115 - .../scripts/generator/cpp/__init__.py | 0 - .../googlemock/scripts/generator/cpp/ast.py | 1733 --- - .../scripts/generator/cpp/gmock_class.py | 227 - - .../scripts/generator/cpp/gmock_class_test.py | 448 - - .../scripts/generator/cpp/keywords.py | 59 - - .../scripts/generator/cpp/tokenize.py | 287 - - .../googlemock/scripts/generator/cpp/utils.py | 41 - - .../googlemock/scripts/generator/gmock_gen.py | 31 - - .../googlemock/scripts/gmock-config.in | 303 - - .../googlemock/scripts/gmock_doctor.py | 640 -- - .../gtest-1.8.0/googlemock/scripts/upload.py | 1387 --- - .../googlemock/scripts/upload_gmock.py | 78 - - .../gtest-1.8.0/googlemock/src/gmock-all.cc | 47 - - .../googlemock/src/gmock-cardinalities.cc | 156 - - .../googlemock/src/gmock-internal-utils.cc | 174 - - .../googlemock/src/gmock-matchers.cc | 498 - - .../googlemock/src/gmock-spec-builders.cc | 823 -- - .../test/gtest-1.8.0/googlemock/src/gmock.cc | 183 - - .../gtest-1.8.0/googlemock/src/gmock_main.cc | 54 - - .../googlemock/test/gmock-actions_test.cc | 1411 --- - .../test/gmock-cardinalities_test.cc | 428 - - .../test/gmock-generated-actions_test.cc | 1228 --- - .../gmock-generated-function-mockers_test.cc | 622 -- - .../gmock-generated-internal-utils_test.cc | 127 - - .../test/gmock-generated-matchers_test.cc | 1286 --- - .../test/gmock-internal-utils_test.cc | 699 -- - .../googlemock/test/gmock-matchers_test.cc | 5652 ---------- - .../test/gmock-more-actions_test.cc | 708 -- - .../googlemock/test/gmock-nice-strict_test.cc | 424 - - .../googlemock/test/gmock-port_test.cc | 43 - - .../test/gmock-spec-builders_test.cc | 2644 ----- - .../googlemock/test/gmock_all_test.cc | 51 - - .../googlemock/test/gmock_ex_test.cc | 81 - - .../googlemock/test/gmock_leak_test.py | 108 - - .../googlemock/test/gmock_leak_test_.cc | 100 - - .../googlemock/test/gmock_link2_test.cc | 40 - - .../googlemock/test/gmock_link_test.cc | 40 - - .../googlemock/test/gmock_link_test.h | 669 -- - .../googlemock/test/gmock_output_test.py | 180 - - .../googlemock/test/gmock_output_test_.cc | 291 - - .../test/gmock_output_test_golden.txt | 310 - - .../googlemock/test/gmock_stress_test.cc | 322 - - .../gtest-1.8.0/googlemock/test/gmock_test.cc | 220 - - .../googlemock/test/gmock_test_utils.py | 112 - - .../test/gtest-1.8.0/googletest/.gitignore | 2 - - .../test/gtest-1.8.0/googletest/CHANGES | 157 - - .../gtest-1.8.0/googletest/CMakeLists.txt | 286 - - .../test/gtest-1.8.0/googletest/CONTRIBUTORS | 37 - - .../test/gtest-1.8.0/googletest/LICENSE | 28 - - .../test/gtest-1.8.0/googletest/README.md | 280 - - .../gtest-1.8.0/googletest/build-aux/.keep | 0 - .../googletest/cmake/internal_utils.cmake | 254 - - .../googletest/codegear/gtest.cbproj | 138 - - .../googletest/codegear/gtest.groupproj | 54 - - .../googletest/codegear/gtest_all.cc | 38 - - .../googletest/codegear/gtest_link.cc | 40 - - .../googletest/codegear/gtest_main.cbproj | 82 - - .../googletest/codegear/gtest_unittest.cbproj | 88 - - .../test/gtest-1.8.0/googletest/configure.ac | 68 - - .../googletest/docs/AdvancedGuide.md | 2182 ---- - .../gtest-1.8.0/googletest/docs/DevGuide.md | 126 - - .../googletest/docs/Documentation.md | 14 - - .../test/gtest-1.8.0/googletest/docs/FAQ.md | 1087 -- - .../gtest-1.8.0/googletest/docs/Primer.md | 502 - - .../gtest-1.8.0/googletest/docs/PumpManual.md | 177 - - .../gtest-1.8.0/googletest/docs/Samples.md | 14 - - .../googletest/docs/V1_5_AdvancedGuide.md | 2096 ---- - .../googletest/docs/V1_5_Documentation.md | 12 - - .../gtest-1.8.0/googletest/docs/V1_5_FAQ.md | 886 -- - .../googletest/docs/V1_5_Primer.md | 497 - - .../googletest/docs/V1_5_PumpManual.md | 177 - - .../googletest/docs/V1_5_XcodeGuide.md | 93 - - .../googletest/docs/V1_6_AdvancedGuide.md | 2178 ---- - .../googletest/docs/V1_6_Documentation.md | 14 - - .../gtest-1.8.0/googletest/docs/V1_6_FAQ.md | 1038 -- - .../googletest/docs/V1_6_Primer.md | 501 - - .../googletest/docs/V1_6_PumpManual.md | 177 - - .../googletest/docs/V1_6_Samples.md | 14 - - .../googletest/docs/V1_6_XcodeGuide.md | 93 - - .../googletest/docs/V1_7_AdvancedGuide.md | 2181 ---- - .../googletest/docs/V1_7_Documentation.md | 14 - - .../gtest-1.8.0/googletest/docs/V1_7_FAQ.md | 1082 -- - .../googletest/docs/V1_7_Primer.md | 501 - - .../googletest/docs/V1_7_PumpManual.md | 177 - - .../googletest/docs/V1_7_Samples.md | 14 - - .../googletest/docs/V1_7_XcodeGuide.md | 93 - - .../gtest-1.8.0/googletest/docs/XcodeGuide.md | 93 - - .../include/gtest/gtest-death-test.h | 294 - - .../googletest/include/gtest/gtest-message.h | 250 - - .../include/gtest/gtest-param-test.h | 1444 --- - .../include/gtest/gtest-param-test.h.pump | 510 - - .../googletest/include/gtest/gtest-printers.h | 993 -- - .../googletest/include/gtest/gtest-spi.h | 232 - - .../include/gtest/gtest-test-part.h | 179 - - .../include/gtest/gtest-typed-test.h | 263 - - .../googletest/include/gtest/gtest.h | 2236 ---- - .../include/gtest/gtest_pred_impl.h | 358 - - .../googletest/include/gtest/gtest_prod.h | 58 - - .../gtest/internal/custom/gtest-port.h | 69 - + .../googlemock/scripts/generator/cpp/ast.py | 1773 --- + .../scripts/generator/cpp/gmock_class.py | 247 - + .../scripts/generator/cpp/gmock_class_test.py | 570 - + .../scripts/generator/cpp/keywords.py | 56 - + .../scripts/generator/cpp/tokenize.py | 284 - + .../googlemock/scripts/generator/cpp/utils.py | 37 - + .../googlemock/scripts/generator/gmock_gen.py | 30 - + test/gtest-1.11.0/googlemock/src/gmock-all.cc | 46 - + .../googlemock/src/gmock-cardinalities.cc | 155 - + .../googlemock/src/gmock-internal-utils.cc | 200 - + .../googlemock/src/gmock-matchers.cc | 459 - + .../googlemock/src/gmock-spec-builders.cc | 908 -- + test/gtest-1.11.0/googlemock/src/gmock.cc | 213 - + .../gtest-1.11.0/googlemock/src/gmock_main.cc | 72 - + test/gtest-1.11.0/googlemock/test/BUILD.bazel | 118 - + .../googlemock/test/gmock-actions_test.cc | 1583 --- + .../test/gmock-cardinalities_test.cc | 429 - + .../test/gmock-function-mocker_test.cc | 986 -- + .../test/gmock-internal-utils_test.cc | 720 -- + .../googlemock/test/gmock-matchers_test.cc | 8562 -------------- + .../test/gmock-more-actions_test.cc | 1547 --- + .../googlemock/test/gmock-nice-strict_test.cc | 539 - + .../googlemock/test/gmock-port_test.cc | 42 - + .../googlemock/test/gmock-pp-string_test.cc | 206 - + .../googlemock/test/gmock-pp_test.cc | 83 - + .../test/gmock-spec-builders_test.cc | 2775 ----- + .../googlemock/test/gmock_all_test.cc | 46 - + .../googlemock/test/gmock_ex_test.cc | 80 - + .../googlemock/test/gmock_leak_test.py | 104 - + .../googlemock/test/gmock_leak_test_.cc | 99 - + .../googlemock/test/gmock_link2_test.cc | 39 - + .../googlemock/test/gmock_link_test.cc | 39 - + .../googlemock/test/gmock_link_test.h | 690 -- + .../googlemock/test/gmock_output_test.py | 183 - + .../googlemock/test/gmock_output_test_.cc | 309 - + .../test/gmock_output_test_golden.txt | 317 - + .../googlemock/test/gmock_stress_test.cc | 240 - + .../googlemock/test/gmock_test.cc | 181 - + .../googlemock/test/gmock_test_utils.py | 108 - + test/gtest-1.11.0/googletest/CMakeLists.txt | 323 - + test/gtest-1.11.0/googletest/README.md | 215 - + .../googletest/cmake/Config.cmake.in | 9 - + .../gtest-1.11.0/googletest/cmake/gtest.pc.in | 9 - + .../googletest/cmake/gtest_main.pc.in | 10 - + .../googletest/cmake/internal_utils.cmake | 344 - + .../googletest/cmake/libgtest.la.in | 21 - + test/gtest-1.11.0/googletest/docs/README.md | 4 - + .../include/gtest/gtest-death-test.h | 346 - + .../googletest/include/gtest/gtest-matchers.h | 930 -- + .../googletest/include/gtest/gtest-message.h | 219 - + .../include/gtest/gtest-param-test.h | 507 - + .../googletest/include/gtest/gtest-printers.h | 1029 -- + .../googletest/include/gtest/gtest-spi.h | 238 - + .../include/gtest/gtest-test-part.h | 184 - + .../include/gtest/gtest-typed-test.h | 329 - + .../googletest/include/gtest/gtest.h | 2495 ----- + .../include/gtest/gtest_pred_impl.h | 359 - + .../googletest/include/gtest/gtest_prod.h | 61 - + .../include/gtest/internal/custom/README.md | 56 - + .../gtest/internal/custom/gtest-port.h | 37 - .../gtest/internal/custom/gtest-printers.h | 42 - - .../include/gtest/internal/custom/gtest.h | 41 - - .../internal/gtest-death-test-internal.h | 319 - - .../include/gtest/internal/gtest-filepath.h | 206 - - .../include/gtest/internal/gtest-internal.h | 1238 --- - .../include/gtest/internal/gtest-linked_ptr.h | 243 - - .../internal/gtest-param-util-generated.h | 5146 --------- - .../gtest-param-util-generated.h.pump | 286 - - .../include/gtest/internal/gtest-param-util.h | 731 -- - .../include/gtest/internal/gtest-port-arch.h | 93 - - .../include/gtest/internal/gtest-port.h | 2554 ----- - .../include/gtest/internal/gtest-string.h | 167 - - .../include/gtest/internal/gtest-tuple.h | 1020 -- - .../include/gtest/internal/gtest-tuple.h.pump | 347 - - .../include/gtest/internal/gtest-type-util.h | 3331 ------ - .../gtest/internal/gtest-type-util.h.pump | 297 - - .../gtest-1.8.0/googletest/m4/acx_pthread.m4 | 363 - - .../test/gtest-1.8.0/googletest/m4/gtest.m4 | 74 - - .../googletest/samples/prime_tables.h | 123 - - .../gtest-1.8.0/googletest/samples/sample1.cc | 68 - - .../gtest-1.8.0/googletest/samples/sample1.h | 43 - - .../googletest/samples/sample10_unittest.cc | 144 - - .../googletest/samples/sample1_unittest.cc | 153 - - .../gtest-1.8.0/googletest/samples/sample2.cc | 56 - - .../gtest-1.8.0/googletest/samples/sample2.h | 85 - - .../googletest/samples/sample2_unittest.cc | 109 - + .../include/gtest/internal/custom/gtest.h | 37 - + .../internal/gtest-death-test-internal.h | 304 - + .../include/gtest/internal/gtest-filepath.h | 211 - + .../include/gtest/internal/gtest-internal.h | 1560 --- + .../include/gtest/internal/gtest-param-util.h | 947 -- + .../include/gtest/internal/gtest-port-arch.h | 114 - + .../include/gtest/internal/gtest-port.h | 2389 ---- + .../include/gtest/internal/gtest-string.h | 175 - + .../include/gtest/internal/gtest-type-util.h | 183 - + .../googletest/samples/prime_tables.h | 126 - + .../googletest/samples/sample1.cc | 66 - + .../gtest-1.11.0/googletest/samples/sample1.h | 41 - + .../googletest/samples/sample10_unittest.cc | 139 - + .../googletest/samples/sample1_unittest.cc | 151 - + .../googletest/samples/sample2.cc | 54 - + .../gtest-1.11.0/googletest/samples/sample2.h | 80 - + .../googletest/samples/sample2_unittest.cc | 107 - .../googletest/samples/sample3-inl.h | 172 - - .../googletest/samples/sample3_unittest.cc | 151 - - .../gtest-1.8.0/googletest/samples/sample4.cc | 46 - - .../gtest-1.8.0/googletest/samples/sample4.h | 53 - - .../googletest/samples/sample4_unittest.cc | 45 - - .../googletest/samples/sample5_unittest.cc | 199 - - .../googletest/samples/sample6_unittest.cc | 224 - - .../googletest/samples/sample7_unittest.cc | 130 - - .../googletest/samples/sample8_unittest.cc | 173 - - .../googletest/samples/sample9_unittest.cc | 160 - - .../gtest-1.8.0/googletest/scripts/common.py | 83 - + .../googletest/samples/sample3_unittest.cc | 149 - + .../googletest/samples/sample4.cc | 54 - + .../gtest-1.11.0/googletest/samples/sample4.h | 53 - + .../googletest/samples/sample4_unittest.cc | 53 - + .../googletest/samples/sample5_unittest.cc | 196 - + .../googletest/samples/sample6_unittest.cc | 217 - + .../googletest/samples/sample7_unittest.cc | 117 - + .../googletest/samples/sample8_unittest.cc | 154 - + .../googletest/samples/sample9_unittest.cc | 156 - + .../gtest-1.11.0/googletest/scripts/README.md | 5 - + .../gtest-1.11.0/googletest/scripts/common.py | 83 - .../googletest/scripts/fuse_gtest_files.py | 253 - - .../googletest/scripts/gen_gtest_pred_impl.py | 730 -- + .../googletest/scripts/gen_gtest_pred_impl.py | 733 -- .../googletest/scripts/gtest-config.in | 274 - - .../gtest-1.8.0/googletest/scripts/pump.py | 855 -- .../googletest/scripts/release_docs.py | 158 - - .../gtest-1.8.0/googletest/scripts/upload.py | 1387 --- + .../googletest/scripts/run_with_path.py | 32 - + .../gtest-1.11.0/googletest/scripts/upload.py | 1402 --- .../googletest/scripts/upload_gtest.py | 78 - - .../gtest-1.8.0/googletest/src/gtest-all.cc | 48 - - .../googletest/src/gtest-death-test.cc | 1342 --- - .../googletest/src/gtest-filepath.cc | 387 - - .../googletest/src/gtest-internal-inl.h | 1183 -- - .../gtest-1.8.0/googletest/src/gtest-port.cc | 1259 --- - .../googletest/src/gtest-printers.cc | 373 - - .../googletest/src/gtest-test-part.cc | 110 - - .../googletest/src/gtest-typed-test.cc | 118 - - .../test/gtest-1.8.0/googletest/src/gtest.cc | 5388 --------- - .../gtest-1.8.0/googletest/src/gtest_main.cc | 38 - - .../test/gtest-death-test_ex_test.cc | 93 - - .../googletest/test/gtest-death-test_test.cc | 1427 --- - .../googletest/test/gtest-filepath_test.cc | 662 -- - .../googletest/test/gtest-linked_ptr_test.cc | 154 - - .../googletest/test/gtest-listener_test.cc | 311 - - .../googletest/test/gtest-message_test.cc | 159 - - .../googletest/test/gtest-options_test.cc | 215 - - .../googletest/test/gtest-param-test2_test.cc | 65 - - .../googletest/test/gtest-param-test_test.cc | 1055 -- - .../googletest/test/gtest-param-test_test.h | 57 - - .../googletest/test/gtest-port_test.cc | 1304 --- - .../googletest/test/gtest-printers_test.cc | 1635 --- - .../googletest/test/gtest-test-part_test.cc | 208 - - .../googletest/test/gtest-tuple_test.cc | 320 - - .../googletest/test/gtest-typed-test2_test.cc | 45 - - .../googletest/test/gtest-typed-test_test.cc | 380 - - .../googletest/test/gtest-typed-test_test.h | 66 - - .../test/gtest-unittest-api_test.cc | 341 - - .../googletest/test/gtest_all_test.cc | 47 - - .../test/gtest_break_on_failure_unittest.py | 212 - - .../test/gtest_break_on_failure_unittest_.cc | 88 - - .../test/gtest_catch_exceptions_test.py | 237 - - .../test/gtest_catch_exceptions_test_.cc | 311 - - .../googletest/test/gtest_color_test.py | 130 - - .../googletest/test/gtest_color_test_.cc | 71 - - .../googletest/test/gtest_env_var_test.py | 117 - - .../googletest/test/gtest_env_var_test_.cc | 126 - - .../googletest/test/gtest_environment_test.cc | 192 - - .../googletest/test/gtest_filter_unittest.py | 636 -- - .../googletest/test/gtest_filter_unittest_.cc | 140 - + test/gtest-1.11.0/googletest/src/gtest-all.cc | 48 - + .../googletest/src/gtest-death-test.cc | 1644 --- + .../googletest/src/gtest-filepath.cc | 369 - + .../googletest/src/gtest-internal-inl.h | 1221 -- + .../googletest/src/gtest-matchers.cc | 97 - + .../gtest-1.11.0/googletest/src/gtest-port.cc | 1433 --- + .../googletest/src/gtest-printers.cc | 533 - + .../googletest/src/gtest-test-part.cc | 108 - + .../googletest/src/gtest-typed-test.cc | 107 - + test/gtest-1.11.0/googletest/src/gtest.cc | 6746 ----------- + .../gtest-1.11.0/googletest/src/gtest_main.cc | 54 - + test/gtest-1.11.0/googletest/test/BUILD.bazel | 590 - + .../googletest-break-on-failure-unittest.py | 208 - + .../googletest-break-on-failure-unittest_.cc | 86 - + .../test/googletest-catch-exceptions-test.py | 236 - + .../test/googletest-catch-exceptions-test_.cc | 293 - + .../googletest/test/googletest-color-test.py | 127 - + .../googletest/test/googletest-color-test_.cc | 62 - + .../test/googletest-death-test-test.cc | 1542 --- + .../test/googletest-death-test_ex_test.cc | 92 - + .../test/googletest-env-var-test.py | 120 - + .../test/googletest-env-var-test_.cc | 132 - + .../test/googletest-failfast-unittest.py | 410 - + .../test/googletest-failfast-unittest_.cc | 167 - + .../test/googletest-filepath-test.cc | 649 -- + .../test/googletest-filter-unittest.py | 639 -- + .../test/googletest-filter-unittest_.cc | 137 - + .../googletest-global-environment-unittest.py | 72 - + ...googletest-global-environment-unittest_.cc | 58 - + .../test/googletest-json-outfiles-test.py | 191 - + .../test/googletest-json-output-unittest.py | 848 -- + .../test/googletest-list-tests-unittest.py | 205 - + .../test/googletest-list-tests-unittest_.cc | 156 - + .../test/googletest-listener-test.cc | 518 - + .../test/googletest-message-test.cc | 158 - + .../test/googletest-options-test.cc | 219 - + .../googletest-output-test-golden-lin.txt | 1180 -- + .../googletest/test/googletest-output-test.py | 346 - + .../test/googletest-output-test_.cc | 1108 -- + ...oogletest-param-test-invalid-name1-test.py | 63 - + ...ogletest-param-test-invalid-name1-test_.cc | 50 - + ...oogletest-param-test-invalid-name2-test.py | 62 - + ...ogletest-param-test-invalid-name2-test_.cc | 55 - + .../test/googletest-param-test-test.cc | 1119 -- + .../test/googletest-param-test-test.h | 51 - + .../test/googletest-param-test2-test.cc | 61 - + .../googletest/test/googletest-port-test.cc | 1276 --- + .../test/googletest-printers-test.cc | 1962 ---- + .../test/googletest-setuptestsuite-test.py | 54 - + .../test/googletest-setuptestsuite-test_.cc | 49 - + .../test/googletest-shuffle-test.py | 323 - + .../test/googletest-shuffle-test_.cc | 101 - + .../test/googletest-test-part-test.cc | 230 - + .../test/googletest-throw-on-failure-test.py | 168 - + .../test/googletest-throw-on-failure-test_.cc | 71 - + .../test/googletest-uninitialized-test.py | 67 - + .../test/googletest-uninitialized-test_.cc | 42 - + .../googletest/test/gtest-typed-test2_test.cc | 40 - + .../googletest/test/gtest-typed-test_test.cc | 437 - + .../googletest/test/gtest-typed-test_test.h | 60 - + .../test/gtest-unittest-api_test.cc | 328 - + .../googletest/test/gtest_all_test.cc | 46 - + .../test/gtest_assert_by_exception_test.cc | 116 - + .../googletest/test/gtest_environment_test.cc | 188 - .../googletest/test/gtest_help_test.py | 172 - - .../googletest/test/gtest_help_test_.cc | 46 - - .../test/gtest_list_tests_unittest.py | 207 - - .../test/gtest_list_tests_unittest_.cc | 157 - - .../googletest/test/gtest_main_unittest.cc | 45 - - .../googletest/test/gtest_no_test_unittest.cc | 56 - - .../googletest/test/gtest_output_test.py | 340 - - .../googletest/test/gtest_output_test_.cc | 1062 -- - .../test/gtest_output_test_golden_lin.txt | 743 -- - .../test/gtest_pred_impl_unittest.cc | 2427 ---- - .../test/gtest_premature_exit_test.cc | 127 - - .../googletest/test/gtest_prod_test.cc | 57 - - .../googletest/test/gtest_repeat_test.cc | 253 - - .../googletest/test/gtest_shuffle_test.py | 325 - - .../googletest/test/gtest_shuffle_test_.cc | 103 - - .../googletest/test/gtest_sole_header_test.cc | 57 - - .../googletest/test/gtest_stress_test.cc | 256 - - .../googletest/test/gtest_test_utils.py | 320 - - .../test/gtest_throw_on_failure_ex_test.cc | 92 - - .../test/gtest_throw_on_failure_test.py | 171 - - .../test/gtest_throw_on_failure_test_.cc | 72 - - .../test/gtest_uninitialized_test.py | 70 - - .../test/gtest_uninitialized_test_.cc | 43 - - .../googletest/test/gtest_unittest.cc | 7706 ------------- - .../test/gtest_xml_outfile1_test_.cc | 49 - - .../test/gtest_xml_outfile2_test_.cc | 49 - - .../test/gtest_xml_outfiles_test.py | 132 - - .../test/gtest_xml_output_unittest.py | 308 - - .../test/gtest_xml_output_unittest_.cc | 181 - - .../googletest/test/gtest_xml_test_utils.py | 194 - - .../gtest-1.8.0/googletest/test/production.cc | 36 - - .../gtest-1.8.0/googletest/test/production.h | 55 - - .../xcode/Config/DebugProject.xcconfig | 30 - - .../xcode/Config/FrameworkTarget.xcconfig | 17 - - .../googletest/xcode/Config/General.xcconfig | 41 - - .../xcode/Config/ReleaseProject.xcconfig | 32 - - .../xcode/Config/StaticLibraryTarget.xcconfig | 18 - - .../xcode/Config/TestTarget.xcconfig | 8 - - .../googletest/xcode/Resources/Info.plist | 30 - - .../xcode/Samples/FrameworkSample/Info.plist | 28 - - .../WidgetFramework.xcodeproj/project.pbxproj | 457 - - .../xcode/Samples/FrameworkSample/runtests.sh | 62 - - .../xcode/Samples/FrameworkSample/widget.cc | 63 - - .../xcode/Samples/FrameworkSample/widget.h | 59 - - .../Samples/FrameworkSample/widget_test.cc | 68 - - .../googletest/xcode/Scripts/runtests.sh | 65 - - .../xcode/Scripts/versiongenerate.py | 100 - - .../xcode/gtest.xcodeproj/project.pbxproj | 1135 -- - .../yaml-cpp/test/gtest-1.8.0/travis.sh | 15 - - .../3rdparty/yaml-cpp/test/handler_test.h | 32 - - .../test/integration/emitter_test.cpp | 1038 -- - .../test/integration/encoding_test.cpp | 182 - - .../test/integration/gen_emitter_test.cpp | 9759 ----------------- - .../test/integration/handler_spec_test.cpp | 1611 --- - .../test/integration/handler_test.cpp | 76 - - .../test/integration/load_node_test.cpp | 241 - - .../test/integration/node_spec_test.cpp | 1131 -- - src/libs/3rdparty/yaml-cpp/test/main.cpp | 6 - - .../yaml-cpp/test/mock_event_handler.h | 26 - - .../3rdparty/yaml-cpp/test/node/node_test.cpp | 517 - - .../yaml-cpp/test/ostream_wrapper_test.cpp | 66 - - .../3rdparty/yaml-cpp/test/regex_test.cpp | 177 - - .../3rdparty/yaml-cpp/test/specexamples.h | 846 -- - .../3rdparty/yaml-cpp/util/CMakeLists.txt | 14 - - src/libs/3rdparty/yaml-cpp/util/api.cpp | 137 - - src/libs/3rdparty/yaml-cpp/util/parse.cpp | 61 - - src/libs/3rdparty/yaml-cpp/util/read.cpp | 103 - - src/libs/3rdparty/yaml-cpp/util/sandbox.cpp | 36 - - .../yaml-cpp/yaml-cpp-config-version.cmake.in | 11 - - .../yaml-cpp/yaml-cpp-config.cmake.in | 14 - - 331 files changed, 167784 deletions(-) - delete mode 100644 src/libs/3rdparty/yaml-cpp/.codedocs - delete mode 100644 src/libs/3rdparty/yaml-cpp/.gitignore - delete mode 100644 src/libs/3rdparty/yaml-cpp/.travis.yml - delete mode 100644 src/libs/3rdparty/yaml-cpp/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/CONTRIBUTING.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/anchordict.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/graphbuilder.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/install.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilder.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/create-emitter-tests.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.gitignore - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.travis.yml - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/README.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/appveyor.yml - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CHANGES - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CONTRIBUTORS - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/LICENSE - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/README.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/build-aux/.keep - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/configure.ac - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CheatSheet.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CookBook.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DesignDoc.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DevGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/ForDummies.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/FrequentlyAskedQuestions.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/KnownIssues.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CheatSheet.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CookBook.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/ForDummies.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CheatSheet.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CookBook.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/ForDummies.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CheatSheet.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CookBook.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/ForDummies.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-actions.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-cardinalities.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-matchers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-actions.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-matchers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-spec-builders.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-matchers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-port.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-internal-utils.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-port.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2005/gmock_config.vsprops - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2010/gmock_config.props - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2015/gmock_config.props - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/fuse_gmock_files.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/LICENSE - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README.cppclean - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/__init__.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/ast.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class_test.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/keywords.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/tokenize.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/utils.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/gmock_gen.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock-config.in - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock_doctor.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload_gmock.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-all.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-cardinalities.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-internal-utils.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-matchers.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-spec-builders.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock_main.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-actions_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-cardinalities_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-actions_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-function-mockers_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-internal-utils_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-matchers_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-internal-utils_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-matchers_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-more-actions_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-nice-strict_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-port_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-spec-builders_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_all_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_ex_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link2_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.h - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_golden.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_stress_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test_utils.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/.gitignore - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CHANGES - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CONTRIBUTORS - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/LICENSE - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/README.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/build-aux/.keep - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/cmake/internal_utils.cmake - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.cbproj - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.groupproj - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_all.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_link.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_main.cbproj - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_unittest.cbproj - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/configure.ac - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/AdvancedGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/DevGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/FAQ.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Primer.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/PumpManual.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Samples.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_AdvancedGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_FAQ.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Primer.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_PumpManual.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_XcodeGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_AdvancedGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_FAQ.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Primer.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_PumpManual.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Samples.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_XcodeGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_AdvancedGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Documentation.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_FAQ.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Primer.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_PumpManual.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Samples.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_XcodeGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/XcodeGuide.md - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-death-test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-message.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-printers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-spi.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-test-part.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-typed-test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_pred_impl.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_prod.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-port.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-printers.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-death-test-internal.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-filepath.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-internal.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-linked_ptr.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port-arch.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-string.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h.pump - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/acx_pthread.m4 - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/gtest.m4 - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/prime_tables.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample10_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3-inl.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample5_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample6_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample7_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample8_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample9_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/common.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/fuse_gtest_files.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gen_gtest_pred_impl.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gtest-config.in - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/pump.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/release_docs.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload_gtest.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-all.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-death-test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-filepath.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-internal-inl.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-port.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-printers.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-test-part.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-typed-test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest_main.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_ex_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-filepath_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-linked_ptr_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-listener_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-message_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-options_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test2_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-port_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-printers_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-test-part_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-tuple_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test2_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-unittest-api_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_all_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_environment_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_main_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_no_test_unittest.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_golden_lin.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_pred_impl_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_premature_exit_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_prod_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_repeat_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_sole_header_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_stress_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_test_utils.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_ex_test.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_unittest.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile1_test_.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile2_test_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfiles_test.py - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest_.cc - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_test_utils.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/DebugProject.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/FrameworkTarget.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/General.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/ReleaseProject.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/StaticLibraryTarget.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/TestTarget.xcconfig - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Resources/Info.plist - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/Info.plist - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/runtests.sh - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget_test.cc - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/runtests.sh - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/versiongenerate.py - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/gtest.xcodeproj/project.pbxproj - delete mode 100755 src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/travis.sh - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/handler_test.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/emitter_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/encoding_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/gen_emitter_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/handler_spec_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/handler_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/load_node_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/integration/node_spec_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/main.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/mock_event_handler.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/node/node_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/ostream_wrapper_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/regex_test.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/test/specexamples.h - delete mode 100644 src/libs/3rdparty/yaml-cpp/util/CMakeLists.txt - delete mode 100644 src/libs/3rdparty/yaml-cpp/util/api.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/util/parse.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/util/read.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/util/sandbox.cpp - delete mode 100644 src/libs/3rdparty/yaml-cpp/yaml-cpp-config-version.cmake.in - delete mode 100644 src/libs/3rdparty/yaml-cpp/yaml-cpp-config.cmake.in + .../googletest/test/gtest_help_test_.cc | 45 - + .../googletest/test/gtest_json_test_utils.py | 60 - + .../test/gtest_list_output_unittest.py | 286 - + .../test/gtest_list_output_unittest_.cc | 77 - + .../googletest/test/gtest_main_unittest.cc | 44 - + .../googletest/test/gtest_no_test_unittest.cc | 54 - + .../test/gtest_pred_impl_unittest.cc | 2422 ---- + .../test/gtest_premature_exit_test.cc | 126 - + .../googletest/test/gtest_prod_test.cc | 56 - + .../googletest/test/gtest_repeat_test.cc | 233 - + .../test/gtest_skip_check_output_test.py | 59 - + ...test_skip_environment_check_output_test.py | 54 - + .../gtest_skip_in_environment_setup_test.cc | 49 - + .../googletest/test/gtest_skip_test.cc | 55 - + .../googletest/test/gtest_sole_header_test.cc | 56 - + .../googletest/test/gtest_stress_test.cc | 248 - + .../gtest_test_macro_stack_footprint_test.cc | 89 - + .../googletest/test/gtest_test_utils.py | 312 - + .../googletest/test/gtest_testbridge_test.py | 63 - + .../googletest/test/gtest_testbridge_test_.cc | 43 - + .../test/gtest_throw_on_failure_ex_test.cc | 90 - + .../googletest/test/gtest_unittest.cc | 7784 ------------- + .../test/gtest_xml_outfile1_test_.cc | 43 - + .../test/gtest_xml_outfile2_test_.cc | 43 - + .../test/gtest_xml_outfiles_test.py | 135 - + .../test/gtest_xml_output_unittest.py | 415 - + .../test/gtest_xml_output_unittest_.cc | 193 - + .../googletest/test/gtest_xml_test_utils.py | 197 - + .../googletest/test/production.cc | 35 - + .../gtest-1.11.0/googletest/test/production.h | 54 - + test/gtest-1.11.0/library.json | 62 - + test/handler_test.h | 32 - + test/integration/emitter_test.cpp | 1740 --- + test/integration/encoding_test.cpp | 182 - + test/integration/error_messages_test.cpp | 61 - + test/integration/gen_emitter_test.cpp | 9936 ----------------- + test/integration/handler_spec_test.cpp | 1686 --- + test/integration/handler_test.cpp | 76 - + test/integration/load_node_test.cpp | 364 - + test/integration/node_spec_test.cpp | 1136 -- + test/main.cpp | 6 - + test/mock_event_handler.h | 30 - + test/node/node_test.cpp | 853 -- + test/ostream_wrapper_test.cpp | 66 - + test/parser_test.cpp | 64 - + test/regex_test.cpp | 177 - + test/specexamples.h | 868 -- + util/CMakeLists.txt | 32 - + util/api.cpp | 137 - + util/parse.cpp | 46 - + util/read.cpp | 103 - + util/sandbox.cpp | 36 - + yaml-cpp-config.cmake.in | 22 - + yaml-cpp.pc.in | 11 - + 309 files changed, 133651 deletions(-) + delete mode 100644 .codedocs + delete mode 100644 .github/workflows/build.yml + delete mode 100644 .gitignore + delete mode 100644 BUILD.bazel + delete mode 100644 CMakeLists.txt + delete mode 100644 CONTRIBUTING.md + delete mode 100644 SECURITY.md + delete mode 100644 WORKSPACE + delete mode 100644 cmake_uninstall.cmake.in + delete mode 100644 docs/Breaking-Changes.md + delete mode 100644 docs/How-To-Emit-YAML.md + delete mode 100644 docs/How-To-Parse-A-Document-(Old-API).md + delete mode 100644 docs/Strings.md + delete mode 100644 docs/Tutorial.md + delete mode 100644 docs/_config.yml + delete mode 100644 docs/index.md + delete mode 100644 include/yaml-cpp/contrib/anchordict.h + delete mode 100644 include/yaml-cpp/contrib/graphbuilder.h + delete mode 100644 install.txt + delete mode 100644 src/contrib/graphbuilder.cpp + delete mode 100644 src/contrib/graphbuilderadapter.cpp + delete mode 100644 src/contrib/graphbuilderadapter.h + delete mode 100644 src/contrib/yaml-cpp.natvis + delete mode 100644 src/contrib/yaml-cpp.natvis.md + delete mode 100644 test/BUILD.bazel + delete mode 100644 test/CMakeLists.txt + delete mode 100644 test/binary_test.cpp + delete mode 100644 test/create-emitter-tests.py + delete mode 100644 test/gtest-1.11.0/.clang-format + delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md + delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md + delete mode 100644 test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml + delete mode 100644 test/gtest-1.11.0/.gitignore + delete mode 100644 test/gtest-1.11.0/BUILD.bazel + delete mode 100644 test/gtest-1.11.0/CMakeLists.txt + delete mode 100644 test/gtest-1.11.0/CONTRIBUTING.md + delete mode 100644 test/gtest-1.11.0/CONTRIBUTORS + delete mode 100644 test/gtest-1.11.0/LICENSE + delete mode 100644 test/gtest-1.11.0/README.md + delete mode 100644 test/gtest-1.11.0/WORKSPACE + delete mode 100644 test/gtest-1.11.0/ci/linux-presubmit.sh + delete mode 100644 test/gtest-1.11.0/ci/macos-presubmit.sh + delete mode 100644 test/gtest-1.11.0/docs/_config.yml + delete mode 100644 test/gtest-1.11.0/docs/_data/navigation.yml + delete mode 100644 test/gtest-1.11.0/docs/_layouts/default.html + delete mode 100644 test/gtest-1.11.0/docs/_sass/main.scss + delete mode 100644 test/gtest-1.11.0/docs/advanced.md + delete mode 100644 test/gtest-1.11.0/docs/assets/css/style.scss + delete mode 100644 test/gtest-1.11.0/docs/community_created_documentation.md + delete mode 100644 test/gtest-1.11.0/docs/faq.md + delete mode 100644 test/gtest-1.11.0/docs/gmock_cheat_sheet.md + delete mode 100644 test/gtest-1.11.0/docs/gmock_cook_book.md + delete mode 100644 test/gtest-1.11.0/docs/gmock_faq.md + delete mode 100644 test/gtest-1.11.0/docs/gmock_for_dummies.md + delete mode 100644 test/gtest-1.11.0/docs/index.md + delete mode 100644 test/gtest-1.11.0/docs/pkgconfig.md + delete mode 100644 test/gtest-1.11.0/docs/platforms.md + delete mode 100644 test/gtest-1.11.0/docs/primer.md + delete mode 100644 test/gtest-1.11.0/docs/quickstart-bazel.md + delete mode 100644 test/gtest-1.11.0/docs/quickstart-cmake.md + delete mode 100644 test/gtest-1.11.0/docs/reference/actions.md + delete mode 100644 test/gtest-1.11.0/docs/reference/assertions.md + delete mode 100644 test/gtest-1.11.0/docs/reference/matchers.md + delete mode 100644 test/gtest-1.11.0/docs/reference/mocking.md + delete mode 100644 test/gtest-1.11.0/docs/reference/testing.md + delete mode 100644 test/gtest-1.11.0/docs/samples.md + delete mode 100644 test/gtest-1.11.0/googlemock/CMakeLists.txt + delete mode 100644 test/gtest-1.11.0/googlemock/README.md + delete mode 100644 test/gtest-1.11.0/googlemock/cmake/gmock.pc.in + delete mode 100644 test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in + delete mode 100644 test/gtest-1.11.0/googlemock/docs/README.md + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/gmock.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h + delete mode 100644 test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h + delete mode 100644 test/gtest-1.11.0/googlemock/scripts/README.md + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py + delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/LICENSE + delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/README + delete mode 100644 test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py + delete mode 100755 test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-all.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-matchers.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock.cc + delete mode 100644 test/gtest-1.11.0/googlemock/src/gmock_main.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/BUILD.bazel + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-port_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_all_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc + delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_leak_test.py + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_link_test.h + delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_output_test.py + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc + delete mode 100644 test/gtest-1.11.0/googlemock/test/gmock_test.cc + delete mode 100755 test/gtest-1.11.0/googlemock/test/gmock_test_utils.py + delete mode 100644 test/gtest-1.11.0/googletest/CMakeLists.txt + delete mode 100644 test/gtest-1.11.0/googletest/README.md + delete mode 100644 test/gtest-1.11.0/googletest/cmake/Config.cmake.in + delete mode 100644 test/gtest-1.11.0/googletest/cmake/gtest.pc.in + delete mode 100644 test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in + delete mode 100644 test/gtest-1.11.0/googletest/cmake/internal_utils.cmake + delete mode 100644 test/gtest-1.11.0/googletest/cmake/libgtest.la.in + delete mode 100644 test/gtest-1.11.0/googletest/docs/README.md + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-message.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h + delete mode 100644 test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/prime_tables.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample10_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample1_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample2_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample3-inl.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample3_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4.h + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample4_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample5_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample6_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample7_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample8_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/samples/sample9_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/scripts/README.md + delete mode 100644 test/gtest-1.11.0/googletest/scripts/common.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/gtest-config.in + delete mode 100755 test/gtest-1.11.0/googletest/scripts/release_docs.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/run_with_path.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/upload.py + delete mode 100755 test/gtest-1.11.0/googletest/scripts/upload_gtest.py + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-all.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-death-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-filepath.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-internal-inl.h + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-matchers.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-port.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-printers.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-test-part.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest-typed-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest.cc + delete mode 100644 test/gtest-1.11.0/googletest/src/gtest_main.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/BUILD.bazel + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-color-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-color-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-env-var-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-listener-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-message-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-options-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-output-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-output-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test-test.h + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-port-test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-printers-test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_all_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_environment_test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_help_test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_help_test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_prod_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_skip_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_stress_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_test_utils.py + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_unittest.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py + delete mode 100644 test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc + delete mode 100755 test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py + delete mode 100644 test/gtest-1.11.0/googletest/test/production.cc + delete mode 100644 test/gtest-1.11.0/googletest/test/production.h + delete mode 100644 test/gtest-1.11.0/library.json + delete mode 100644 test/handler_test.h + delete mode 100644 test/integration/emitter_test.cpp + delete mode 100644 test/integration/encoding_test.cpp + delete mode 100644 test/integration/error_messages_test.cpp + delete mode 100644 test/integration/gen_emitter_test.cpp + delete mode 100644 test/integration/handler_spec_test.cpp + delete mode 100644 test/integration/handler_test.cpp + delete mode 100644 test/integration/load_node_test.cpp + delete mode 100644 test/integration/node_spec_test.cpp + delete mode 100644 test/main.cpp + delete mode 100644 test/mock_event_handler.h + delete mode 100644 test/node/node_test.cpp + delete mode 100644 test/ostream_wrapper_test.cpp + delete mode 100644 test/parser_test.cpp + delete mode 100644 test/regex_test.cpp + delete mode 100644 test/specexamples.h + delete mode 100644 util/CMakeLists.txt + delete mode 100644 util/api.cpp + delete mode 100644 util/parse.cpp + delete mode 100644 util/read.cpp + delete mode 100644 util/sandbox.cpp + delete mode 100644 yaml-cpp-config.cmake.in + delete mode 100644 yaml-cpp.pc.in -diff --git a/src/libs/3rdparty/yaml-cpp/.codedocs b/src/libs/3rdparty/yaml-cpp/.codedocs +diff --git a/.codedocs b/.codedocs deleted file mode 100644 -index 02e438213a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/.gitignore b/src/libs/3rdparty/yaml-cpp/.gitignore +index 02e4382..0000000 +diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 -index 567609b123..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/.travis.yml b/src/libs/3rdparty/yaml-cpp/.travis.yml +index a408a9d..0000000 +diff --git a/.gitignore b/.gitignore deleted file mode 100644 -index d0b6a04efe..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/CMakeLists.txt +index 2f9d10f..0000000 +diff --git a/BUILD.bazel b/BUILD.bazel deleted file mode 100644 -index d2d8810288..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/CONTRIBUTING.md b/src/libs/3rdparty/yaml-cpp/CONTRIBUTING.md +index 23e847e..0000000 +diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 -index cd09a1aca8..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/anchordict.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/anchordict.h +index 46dc180..0000000 +diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 -index 78db9ec928..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/graphbuilder.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/contrib/graphbuilder.h +index 5705fe2..0000000 +diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 -index f0a38f2887..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/install.txt b/src/libs/3rdparty/yaml-cpp/install.txt +index 06a1751..0000000 +diff --git a/WORKSPACE b/WORKSPACE deleted file mode 100644 -index 939236249b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilder.cpp b/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilder.cpp +index d5ecc0b..0000000 +diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in deleted file mode 100644 -index 416c1359db..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.cpp b/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.cpp +index c2d34d4..0000000 +diff --git a/docs/Breaking-Changes.md b/docs/Breaking-Changes.md deleted file mode 100644 -index 02a3d972a5..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.h b/src/libs/3rdparty/yaml-cpp/src/contrib/graphbuilderadapter.h +index 959adea..0000000 +diff --git a/docs/How-To-Emit-YAML.md b/docs/How-To-Emit-YAML.md deleted file mode 100644 -index 0d1e579208..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/test/CMakeLists.txt +index 9340701..0000000 +diff --git a/docs/How-To-Parse-A-Document-(Old-API).md b/docs/How-To-Parse-A-Document-(Old-API).md deleted file mode 100644 -index 3633da578b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/create-emitter-tests.py b/src/libs/3rdparty/yaml-cpp/test/create-emitter-tests.py +index 82fac71..0000000 +diff --git a/docs/Strings.md b/docs/Strings.md deleted file mode 100644 -index 7a03c41e1b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.gitignore b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.gitignore +index f2328a1..0000000 +diff --git a/docs/Tutorial.md b/docs/Tutorial.md deleted file mode 100644 -index ce310bc357..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.travis.yml b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/.travis.yml +index a7b0e21..0000000 +diff --git a/docs/_config.yml b/docs/_config.yml deleted file mode 100644 -index 3204dfac17..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/CMakeLists.txt +index c741881..0000000 +diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 -index 8d2b552ef7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/README.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/README.md +index 17f1315..0000000 +diff --git a/include/yaml-cpp/contrib/anchordict.h b/include/yaml-cpp/contrib/anchordict.h deleted file mode 100644 -index 076484e4fa..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/appveyor.yml b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/appveyor.yml +index 1b7809b..0000000 +diff --git a/include/yaml-cpp/contrib/graphbuilder.h b/include/yaml-cpp/contrib/graphbuilder.h deleted file mode 100644 -index d613fd6027..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CHANGES b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CHANGES +index dbffd92..0000000 +diff --git a/install.txt b/install.txt deleted file mode 100644 -index d6f2f760e3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CMakeLists.txt +index 9392362..0000000 +diff --git a/src/contrib/graphbuilder.cpp b/src/contrib/graphbuilder.cpp deleted file mode 100644 -index beb259a2e9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CONTRIBUTORS b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/CONTRIBUTORS +index 0352054..0000000 +diff --git a/src/contrib/graphbuilderadapter.cpp b/src/contrib/graphbuilderadapter.cpp deleted file mode 100644 -index 6e9ae362b6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/LICENSE b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/LICENSE +index c386a92..0000000 +diff --git a/src/contrib/graphbuilderadapter.h b/src/contrib/graphbuilderadapter.h deleted file mode 100644 -index 1941a11f8c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/README.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/README.md +index c1cbcff..0000000 +diff --git a/src/contrib/yaml-cpp.natvis b/src/contrib/yaml-cpp.natvis deleted file mode 100644 -index 332beab388..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/build-aux/.keep b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/build-aux/.keep +index d5c222b..0000000 +diff --git a/src/contrib/yaml-cpp.natvis.md b/src/contrib/yaml-cpp.natvis.md deleted file mode 100644 -index e69de29bb2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/configure.ac b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/configure.ac +index f1d68a8..0000000 +diff --git a/test/BUILD.bazel b/test/BUILD.bazel deleted file mode 100644 -index 3b740f205e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CheatSheet.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CheatSheet.md +index d30fa73..0000000 +diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 -index ef4451b878..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CookBook.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/CookBook.md +index 351b03f..0000000 +diff --git a/test/binary_test.cpp b/test/binary_test.cpp deleted file mode 100644 -index c52f1009d1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DesignDoc.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DesignDoc.md +index 7b17823..0000000 +diff --git a/test/create-emitter-tests.py b/test/create-emitter-tests.py deleted file mode 100644 -index 3f515c3b6d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DevGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/DevGuide.md +index 7295544..0000000 +diff --git a/test/gtest-1.11.0/.clang-format b/test/gtest-1.11.0/.clang-format deleted file mode 100644 -index f4bab75ca7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/Documentation.md +index 5b9bfe6..0000000 +diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/00-bug_report.md deleted file mode 100644 -index 444151ee9e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/ForDummies.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/ForDummies.md +index 0f7e8b5..0000000 +diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/10-feature_request.md deleted file mode 100644 -index 0da4cbe27b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/FrequentlyAskedQuestions.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/FrequentlyAskedQuestions.md +index 70a3a20..0000000 +diff --git a/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml b/test/gtest-1.11.0/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 -index 5eac83f4b9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/KnownIssues.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/KnownIssues.md +index 3ba13e0..0000000 +diff --git a/test/gtest-1.11.0/.gitignore b/test/gtest-1.11.0/.gitignore deleted file mode 100644 -index adadf5144b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CheatSheet.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CheatSheet.md +index f08cb72..0000000 +diff --git a/test/gtest-1.11.0/BUILD.bazel b/test/gtest-1.11.0/BUILD.bazel deleted file mode 100644 -index 3c7bed4c6f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CookBook.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/CookBook.md +index 965c518..0000000 +diff --git a/test/gtest-1.11.0/CMakeLists.txt b/test/gtest-1.11.0/CMakeLists.txt deleted file mode 100644 -index 26e153c6ba..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/Documentation.md +index ea81ab1..0000000 +diff --git a/test/gtest-1.11.0/CONTRIBUTING.md b/test/gtest-1.11.0/CONTRIBUTING.md deleted file mode 100644 -index 315b0a2989..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/ForDummies.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/ForDummies.md +index da45e44..0000000 +diff --git a/test/gtest-1.11.0/CONTRIBUTORS b/test/gtest-1.11.0/CONTRIBUTORS deleted file mode 100644 -index fcc3b56174..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_5/FrequentlyAskedQuestions.md +index 76db0b4..0000000 +diff --git a/test/gtest-1.11.0/LICENSE b/test/gtest-1.11.0/LICENSE deleted file mode 100644 -index 7593243c3a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CheatSheet.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CheatSheet.md +index 1941a11..0000000 +diff --git a/test/gtest-1.11.0/README.md b/test/gtest-1.11.0/README.md deleted file mode 100644 -index 91de1d210e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CookBook.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/CookBook.md +index 7d872a5..0000000 +diff --git a/test/gtest-1.11.0/WORKSPACE b/test/gtest-1.11.0/WORKSPACE deleted file mode 100644 -index f5975a0035..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/Documentation.md +index 614f557..0000000 +diff --git a/test/gtest-1.11.0/ci/linux-presubmit.sh b/test/gtest-1.11.0/ci/linux-presubmit.sh deleted file mode 100644 -index dcc9156c2a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/ForDummies.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/ForDummies.md +index 6bea1cd..0000000 +diff --git a/test/gtest-1.11.0/ci/macos-presubmit.sh b/test/gtest-1.11.0/ci/macos-presubmit.sh deleted file mode 100644 -index 19ee63ab0c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_6/FrequentlyAskedQuestions.md +index d6423fa..0000000 +diff --git a/test/gtest-1.11.0/docs/_config.yml b/test/gtest-1.11.0/docs/_config.yml deleted file mode 100644 -index f74715d2e3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CheatSheet.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CheatSheet.md +index d12867e..0000000 +diff --git a/test/gtest-1.11.0/docs/_data/navigation.yml b/test/gtest-1.11.0/docs/_data/navigation.yml deleted file mode 100644 -index db421e51be..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CookBook.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/CookBook.md +index 9f33327..0000000 +diff --git a/test/gtest-1.11.0/docs/_layouts/default.html b/test/gtest-1.11.0/docs/_layouts/default.html deleted file mode 100644 -index 419a001071..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/Documentation.md +index dcb42d9..0000000 +diff --git a/test/gtest-1.11.0/docs/_sass/main.scss b/test/gtest-1.11.0/docs/_sass/main.scss deleted file mode 100644 -index d9181f28e1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/ForDummies.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/ForDummies.md +index 92edc87..0000000 +diff --git a/test/gtest-1.11.0/docs/advanced.md b/test/gtest-1.11.0/docs/advanced.md deleted file mode 100644 -index ee03c5b989..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/docs/v1_7/FrequentlyAskedQuestions.md +index 8dff5ba..0000000 +diff --git a/test/gtest-1.11.0/docs/assets/css/style.scss b/test/gtest-1.11.0/docs/assets/css/style.scss deleted file mode 100644 -index fa21233aa2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-actions.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-actions.h +index bb30f41..0000000 +diff --git a/test/gtest-1.11.0/docs/community_created_documentation.md b/test/gtest-1.11.0/docs/community_created_documentation.md deleted file mode 100644 -index b3f654af34..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-cardinalities.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-cardinalities.h +index 4569075..0000000 +diff --git a/test/gtest-1.11.0/docs/faq.md b/test/gtest-1.11.0/docs/faq.md deleted file mode 100644 -index fc315f92ab..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h +index 9042da1..0000000 +diff --git a/test/gtest-1.11.0/docs/gmock_cheat_sheet.md b/test/gtest-1.11.0/docs/gmock_cheat_sheet.md deleted file mode 100644 -index b5a889c0c3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-actions.h.pump +index 17ed7a5..0000000 +diff --git a/test/gtest-1.11.0/docs/gmock_cook_book.md b/test/gtest-1.11.0/docs/gmock_cook_book.md deleted file mode 100644 -index 66d9f9d551..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h +index c08958e..0000000 +diff --git a/test/gtest-1.11.0/docs/gmock_faq.md b/test/gtest-1.11.0/docs/gmock_faq.md deleted file mode 100644 -index 4fa5ca9484..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-function-mockers.h.pump +index 2cd9b3f..0000000 +diff --git a/test/gtest-1.11.0/docs/gmock_for_dummies.md b/test/gtest-1.11.0/docs/gmock_for_dummies.md deleted file mode 100644 -index 811502d0ce..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h +index 1f4cc24..0000000 +diff --git a/test/gtest-1.11.0/docs/index.md b/test/gtest-1.11.0/docs/index.md deleted file mode 100644 -index 57056fd91d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-matchers.h.pump +index b162c74..0000000 +diff --git a/test/gtest-1.11.0/docs/pkgconfig.md b/test/gtest-1.11.0/docs/pkgconfig.md deleted file mode 100644 -index de30c2c92b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h +index 768e9b4..0000000 +diff --git a/test/gtest-1.11.0/docs/platforms.md b/test/gtest-1.11.0/docs/platforms.md deleted file mode 100644 -index 4095f4d5bc..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-generated-nice-strict.h.pump +index eba6ef8..0000000 +diff --git a/test/gtest-1.11.0/docs/primer.md b/test/gtest-1.11.0/docs/primer.md deleted file mode 100644 -index 3ee1ce7f30..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-matchers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-matchers.h +index 6d8fdf4..0000000 +diff --git a/test/gtest-1.11.0/docs/quickstart-bazel.md b/test/gtest-1.11.0/docs/quickstart-bazel.md deleted file mode 100644 -index 33b37a7a5d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-actions.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-actions.h +index 362ee6d..0000000 +diff --git a/test/gtest-1.11.0/docs/quickstart-cmake.md b/test/gtest-1.11.0/docs/quickstart-cmake.md deleted file mode 100644 -index 3d387b6b7d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-matchers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-more-matchers.h +index 420f1d3..0000000 +diff --git a/test/gtest-1.11.0/docs/reference/actions.md b/test/gtest-1.11.0/docs/reference/actions.md deleted file mode 100644 -index 3db899f429..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-spec-builders.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock-spec-builders.h +index 166d2a8..0000000 +diff --git a/test/gtest-1.11.0/docs/reference/assertions.md b/test/gtest-1.11.0/docs/reference/assertions.md deleted file mode 100644 -index fed7de66bc..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/gmock.h +index 7bf03a3..0000000 +diff --git a/test/gtest-1.11.0/docs/reference/matchers.md b/test/gtest-1.11.0/docs/reference/matchers.md deleted file mode 100644 -index 6735c71bf8..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h +index 9e40cab..0000000 +diff --git a/test/gtest-1.11.0/docs/reference/mocking.md b/test/gtest-1.11.0/docs/reference/mocking.md deleted file mode 100644 -index 7dc3b1ad54..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h.pump +index c29f716..0000000 +diff --git a/test/gtest-1.11.0/docs/reference/testing.md b/test/gtest-1.11.0/docs/reference/testing.md deleted file mode 100644 -index d26c8a08a4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-matchers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-matchers.h +index 554d6c9..0000000 +diff --git a/test/gtest-1.11.0/docs/samples.md b/test/gtest-1.11.0/docs/samples.md deleted file mode 100644 -index f2efef91db..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-port.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/custom/gmock-port.h +index 2d97ca5..0000000 +diff --git a/test/gtest-1.11.0/googlemock/CMakeLists.txt b/test/gtest-1.11.0/googlemock/CMakeLists.txt deleted file mode 100644 -index 9ce8bfe06b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h +index e7df8ec..0000000 +diff --git a/test/gtest-1.11.0/googlemock/README.md b/test/gtest-1.11.0/googlemock/README.md deleted file mode 100644 -index 7811e43f87..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-generated-internal-utils.h.pump +index ead6883..0000000 +diff --git a/test/gtest-1.11.0/googlemock/cmake/gmock.pc.in b/test/gtest-1.11.0/googlemock/cmake/gmock.pc.in deleted file mode 100644 -index 800af17c1d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-internal-utils.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-internal-utils.h +index 23c67b5..0000000 +diff --git a/test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in b/test/gtest-1.11.0/googlemock/cmake/gmock_main.pc.in deleted file mode 100644 -index e2ddb05c91..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-port.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/include/gmock/internal/gmock-port.h +index 66ffea7..0000000 +diff --git a/test/gtest-1.11.0/googlemock/docs/README.md b/test/gtest-1.11.0/googlemock/docs/README.md deleted file mode 100644 -index 63f4a6802e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2005/gmock_config.vsprops b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2005/gmock_config.vsprops +index 1bc57b7..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-actions.h deleted file mode 100644 -index 9b5ff7f38a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2010/gmock_config.props b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2010/gmock_config.props +index f2393bd..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-cardinalities.h deleted file mode 100644 -index 77bc95b192..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2015/gmock_config.props b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/msvc/2015/gmock_config.props +index fc7f803..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-function-mocker.h deleted file mode 100644 -index 77bc95b192..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/fuse_gmock_files.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/fuse_gmock_files.py +index 0fc6f6f..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-matchers.h +deleted file mode 100644 +index 86be9c1..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-actions.h +deleted file mode 100644 +index fd29335..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-more-matchers.h +deleted file mode 100644 +index dfc77e3..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-nice-strict.h +deleted file mode 100644 +index b03b770..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock-spec-builders.h +deleted file mode 100644 +index 41323c1..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/gmock.h b/test/gtest-1.11.0/googlemock/include/gmock/gmock.h +deleted file mode 100644 +index 12469bc..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/README.md +deleted file mode 100644 +index f6c93f6..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-generated-actions.h +deleted file mode 100644 +index 63f8999..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-matchers.h +deleted file mode 100644 +index 6384294..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/custom/gmock-port.h +deleted file mode 100644 +index 1437869..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-internal-utils.h +deleted file mode 100644 +index 317544a..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-port.h +deleted file mode 100644 +index 367a44d..0000000 +diff --git a/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h b/test/gtest-1.11.0/googlemock/include/gmock/internal/gmock-pp.h +deleted file mode 100644 +index 94d61c0..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/README.md b/test/gtest-1.11.0/googlemock/scripts/README.md +deleted file mode 100644 +index a3301e5..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py b/test/gtest-1.11.0/googlemock/scripts/fuse_gmock_files.py deleted file mode 100755 -index cb7fdf2f78..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/LICENSE b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/LICENSE +index 7fa9b3a..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/LICENSE b/test/gtest-1.11.0/googlemock/scripts/generator/LICENSE deleted file mode 100644 -index 87ea063651..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README +index 87ea063..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/README b/test/gtest-1.11.0/googlemock/scripts/generator/README deleted file mode 100644 -index d6f95974b6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README.cppclean b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/README.cppclean +index 01fd463..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean b/test/gtest-1.11.0/googlemock/scripts/generator/README.cppclean deleted file mode 100644 -index 65431b6175..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/__init__.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/__init__.py +index 65431b6..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/__init__.py deleted file mode 100755 -index e69de29bb2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/ast.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/ast.py +index e69de29..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/ast.py deleted file mode 100755 -index 11cbe9126a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class.py +index 0e77016..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class.py deleted file mode 100755 -index f9966cbb4b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/gmock_class_test.py +index 3e21022..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/gmock_class_test.py deleted file mode 100755 -index 018f90a650..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/keywords.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/keywords.py +index eff475f..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/keywords.py deleted file mode 100755 -index f694450e37..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/tokenize.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/tokenize.py +index e428271..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/tokenize.py deleted file mode 100755 -index 359d5562d7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/utils.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/cpp/utils.py +index a75edcb..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py b/test/gtest-1.11.0/googlemock/scripts/generator/cpp/utils.py deleted file mode 100755 -index eab36eec33..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/gmock_gen.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/generator/gmock_gen.py +index 6f5fc09..0000000 +diff --git a/test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py b/test/gtest-1.11.0/googlemock/scripts/generator/gmock_gen.py deleted file mode 100755 -index 8cc0d135d6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock-config.in b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock-config.in +index 9d528a5..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock-all.cc b/test/gtest-1.11.0/googlemock/src/gmock-all.cc +deleted file mode 100644 +index e43c9b7..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc b/test/gtest-1.11.0/googlemock/src/gmock-cardinalities.cc +deleted file mode 100644 +index 7463f43..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc b/test/gtest-1.11.0/googlemock/src/gmock-internal-utils.cc +deleted file mode 100644 +index e5b5479..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock-matchers.cc b/test/gtest-1.11.0/googlemock/src/gmock-matchers.cc +deleted file mode 100644 +index dded437..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc b/test/gtest-1.11.0/googlemock/src/gmock-spec-builders.cc +deleted file mode 100644 +index c7266a3..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock.cc b/test/gtest-1.11.0/googlemock/src/gmock.cc +deleted file mode 100644 +index 7bcdb0b..0000000 +diff --git a/test/gtest-1.11.0/googlemock/src/gmock_main.cc b/test/gtest-1.11.0/googlemock/src/gmock_main.cc +deleted file mode 100644 +index 18c500f..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/BUILD.bazel b/test/gtest-1.11.0/googlemock/test/BUILD.bazel +deleted file mode 100644 +index efb7306..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-actions_test.cc +deleted file mode 100644 +index e1ca7fe..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-cardinalities_test.cc +deleted file mode 100644 +index ca97cae..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-function-mocker_test.cc +deleted file mode 100644 +index cf76fa9..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-internal-utils_test.cc +deleted file mode 100644 +index 0d15e8f..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-matchers_test.cc +deleted file mode 100644 +index 1f48a76..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-more-actions_test.cc +deleted file mode 100644 +index 53bb029..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-nice-strict_test.cc +deleted file mode 100644 +index 25558eb..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-port_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-port_test.cc +deleted file mode 100644 +index a2c2be2..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-pp-string_test.cc +deleted file mode 100644 +index 6f66cf1..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-pp_test.cc +deleted file mode 100644 +index 5d1566e..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc b/test/gtest-1.11.0/googlemock/test/gmock-spec-builders_test.cc +deleted file mode 100644 +index fa97411..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_all_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_all_test.cc +deleted file mode 100644 +index fffbb8b..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_ex_test.cc +deleted file mode 100644 +index 72eb43f..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_leak_test.py b/test/gtest-1.11.0/googlemock/test/gmock_leak_test.py deleted file mode 100755 -index 2baefe94d6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock_doctor.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/gmock_doctor.py +index 7e4b1ee..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc b/test/gtest-1.11.0/googlemock/test/gmock_leak_test_.cc +deleted file mode 100644 +index 2e095ab..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_link2_test.cc +deleted file mode 100644 +index d27ce17..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_link_test.cc +deleted file mode 100644 +index e7c54cc..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_link_test.h b/test/gtest-1.11.0/googlemock/test/gmock_link_test.h +deleted file mode 100644 +index 5734b2e..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test.py b/test/gtest-1.11.0/googlemock/test/gmock_output_test.py deleted file mode 100755 -index 74992bc744..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload.py +index 25f99f2..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc b/test/gtest-1.11.0/googlemock/test/gmock_output_test_.cc +deleted file mode 100644 +index 3955c73..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt b/test/gtest-1.11.0/googlemock/test/gmock_output_test_golden.txt +deleted file mode 100644 +index 4846c12..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_stress_test.cc +deleted file mode 100644 +index 20725d6..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_test.cc b/test/gtest-1.11.0/googlemock/test/gmock_test.cc +deleted file mode 100644 +index e9840a3..0000000 +diff --git a/test/gtest-1.11.0/googlemock/test/gmock_test_utils.py b/test/gtest-1.11.0/googlemock/test/gmock_test_utils.py deleted file mode 100755 -index 6e6f9a1471..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload_gmock.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/scripts/upload_gmock.py +index 7dc4e11..0000000 +diff --git a/test/gtest-1.11.0/googletest/CMakeLists.txt b/test/gtest-1.11.0/googletest/CMakeLists.txt +deleted file mode 100644 +index abdd98b..0000000 +diff --git a/test/gtest-1.11.0/googletest/README.md b/test/gtest-1.11.0/googletest/README.md +deleted file mode 100644 +index 1f8b349..0000000 +diff --git a/test/gtest-1.11.0/googletest/cmake/Config.cmake.in b/test/gtest-1.11.0/googletest/cmake/Config.cmake.in +deleted file mode 100644 +index 12be449..0000000 +diff --git a/test/gtest-1.11.0/googletest/cmake/gtest.pc.in b/test/gtest-1.11.0/googletest/cmake/gtest.pc.in +deleted file mode 100644 +index b4148fa..0000000 +diff --git a/test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in b/test/gtest-1.11.0/googletest/cmake/gtest_main.pc.in +deleted file mode 100644 +index 38c88c5..0000000 +diff --git a/test/gtest-1.11.0/googletest/cmake/internal_utils.cmake b/test/gtest-1.11.0/googletest/cmake/internal_utils.cmake +deleted file mode 100644 +index 8d8d60a..0000000 +diff --git a/test/gtest-1.11.0/googletest/cmake/libgtest.la.in b/test/gtest-1.11.0/googletest/cmake/libgtest.la.in +deleted file mode 100644 +index 840c838..0000000 +diff --git a/test/gtest-1.11.0/googletest/docs/README.md b/test/gtest-1.11.0/googletest/docs/README.md +deleted file mode 100644 +index 1bc57b7..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-death-test.h +deleted file mode 100644 +index 9b4d4d1..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-matchers.h +deleted file mode 100644 +index 9fa34a0..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-message.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-message.h +deleted file mode 100644 +index becfd49..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-param-test.h +deleted file mode 100644 +index 804e702..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-printers.h +deleted file mode 100644 +index 076c9de..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-spi.h +deleted file mode 100644 +index eacef44..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-test-part.h +deleted file mode 100644 +index 203fdf9..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h b/test/gtest-1.11.0/googletest/include/gtest/gtest-typed-test.h +deleted file mode 100644 +index 9fdc6be..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest.h b/test/gtest-1.11.0/googletest/include/gtest/gtest.h +deleted file mode 100644 +index 7a5d057..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h b/test/gtest-1.11.0/googletest/include/gtest/gtest_pred_impl.h +deleted file mode 100644 +index 5029a9b..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h b/test/gtest-1.11.0/googletest/include/gtest/gtest_prod.h +deleted file mode 100644 +index 38b9d85..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/README.md +deleted file mode 100644 +index ff391fb..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-port.h +deleted file mode 100644 +index db02881..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest-printers.h +deleted file mode 100644 +index b9495d8..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h b/test/gtest-1.11.0/googletest/include/gtest/internal/custom/gtest.h +deleted file mode 100644 +index afaaf17..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-death-test-internal.h +deleted file mode 100644 +index 490296d..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-filepath.h +deleted file mode 100644 +index 0c033ab..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-internal.h +deleted file mode 100644 +index f8cbdbd..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-param-util.h +deleted file mode 100644 +index c2ef6e3..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port-arch.h +deleted file mode 100644 +index dd84591..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-port.h +deleted file mode 100644 +index 0953a78..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-string.h +deleted file mode 100644 +index 10f774f..0000000 +diff --git a/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h b/test/gtest-1.11.0/googletest/include/gtest/internal/gtest-type-util.h +deleted file mode 100644 +index b87a2e2..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/prime_tables.h b/test/gtest-1.11.0/googletest/samples/prime_tables.h +deleted file mode 100644 +index 3a10352..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample1.cc b/test/gtest-1.11.0/googletest/samples/sample1.cc +deleted file mode 100644 +index 1d42759..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample1.h b/test/gtest-1.11.0/googletest/samples/sample1.h +deleted file mode 100644 +index ba392cf..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample10_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample10_unittest.cc +deleted file mode 100644 +index 36cdac2..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample1_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample1_unittest.cc +deleted file mode 100644 +index cb08b61..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample2.cc b/test/gtest-1.11.0/googletest/samples/sample2.cc +deleted file mode 100644 +index d8e8723..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample2.h b/test/gtest-1.11.0/googletest/samples/sample2.h +deleted file mode 100644 +index 0f98689..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample2_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample2_unittest.cc +deleted file mode 100644 +index 41e31c1..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample3-inl.h b/test/gtest-1.11.0/googletest/samples/sample3-inl.h +deleted file mode 100644 +index 659e0f0..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample3_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample3_unittest.cc +deleted file mode 100644 +index b19416d..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample4.cc b/test/gtest-1.11.0/googletest/samples/sample4.cc +deleted file mode 100644 +index b0ee609..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample4.h b/test/gtest-1.11.0/googletest/samples/sample4.h +deleted file mode 100644 +index 0c4ed92..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample4_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample4_unittest.cc +deleted file mode 100644 +index d5144c0..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample5_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample5_unittest.cc +deleted file mode 100644 +index 0a21dd2..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample6_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample6_unittest.cc +deleted file mode 100644 +index da317ee..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample7_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample7_unittest.cc +deleted file mode 100644 +index e0efc29..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample8_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample8_unittest.cc +deleted file mode 100644 +index 10488b0..0000000 +diff --git a/test/gtest-1.11.0/googletest/samples/sample9_unittest.cc b/test/gtest-1.11.0/googletest/samples/sample9_unittest.cc +deleted file mode 100644 +index e502d08..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/README.md b/test/gtest-1.11.0/googletest/scripts/README.md +deleted file mode 100644 +index fa359fe..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/common.py b/test/gtest-1.11.0/googletest/scripts/common.py +deleted file mode 100644 +index 3c0347a..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py b/test/gtest-1.11.0/googletest/scripts/fuse_gtest_files.py deleted file mode 100755 -index 5dc484b391..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-all.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-all.cc -deleted file mode 100644 -index 7aebce7afe..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-cardinalities.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-cardinalities.cc -deleted file mode 100644 -index 50ec7286ee..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-internal-utils.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-internal-utils.cc -deleted file mode 100644 -index fb5308018a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-matchers.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-matchers.cc -deleted file mode 100644 -index e7424510fc..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-spec-builders.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock-spec-builders.cc -deleted file mode 100644 -index 9551342070..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock.cc -deleted file mode 100644 -index eac3d842ba..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock_main.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/src/gmock_main.cc -deleted file mode 100644 -index bd5be03be2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-actions_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-actions_test.cc -deleted file mode 100644 -index f470de4c55..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-cardinalities_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-cardinalities_test.cc -deleted file mode 100644 -index 64815e57a3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-actions_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-actions_test.cc -deleted file mode 100644 -index 5ca5bc7892..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-function-mockers_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-function-mockers_test.cc -deleted file mode 100644 -index a86a613578..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-internal-utils_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-internal-utils_test.cc -deleted file mode 100644 -index e0a535a346..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-matchers_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-generated-matchers_test.cc -deleted file mode 100644 -index 0e9f77f5eb..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-internal-utils_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-internal-utils_test.cc -deleted file mode 100644 -index 9d5ec60927..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-matchers_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-matchers_test.cc -deleted file mode 100644 -index 9f62c3d826..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-more-actions_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-more-actions_test.cc -deleted file mode 100644 -index 77e15bd586..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-nice-strict_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-nice-strict_test.cc -deleted file mode 100644 -index d0adcbbed8..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-port_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-port_test.cc -deleted file mode 100644 -index d6a8d44466..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-spec-builders_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock-spec-builders_test.cc -deleted file mode 100644 -index 59ea87c894..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_all_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_all_test.cc -deleted file mode 100644 -index 56d6c49ccc..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_ex_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_ex_test.cc -deleted file mode 100644 -index 3afed86ab9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test.py +index d0dd464..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py b/test/gtest-1.11.0/googletest/scripts/gen_gtest_pred_impl.py deleted file mode 100755 -index 997680ce1a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_leak_test_.cc -deleted file mode 100644 -index 1d27d22f62..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link2_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link2_test.cc -deleted file mode 100644 -index 4c310c3d83..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.cc -deleted file mode 100644 -index 61e97d10ca..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_link_test.h -deleted file mode 100644 -index 1f55f5bd7f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test.py +index e09a6e0..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/gtest-config.in b/test/gtest-1.11.0/googletest/scripts/gtest-config.in deleted file mode 100755 -index eced8a81f2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_.cc -deleted file mode 100644 -index 44cba342ad..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_golden.txt b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_output_test_golden.txt -deleted file mode 100644 -index 689d5eeb03..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_stress_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_stress_test.cc -deleted file mode 100644 -index 0e97aeed0b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test.cc -deleted file mode 100644 -index d8d0c57b16..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test_utils.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googlemock/test/gmock_test_utils.py +index 780f843..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/release_docs.py b/test/gtest-1.11.0/googletest/scripts/release_docs.py deleted file mode 100755 -index 20e3d3d446..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/.gitignore b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/.gitignore -deleted file mode 100644 -index 4b7be4b91b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CHANGES b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CHANGES -deleted file mode 100644 -index 0552132421..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CMakeLists.txt -deleted file mode 100644 -index 621d0f0421..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CONTRIBUTORS b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/CONTRIBUTORS -deleted file mode 100644 -index feae2fc044..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/LICENSE b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/LICENSE -deleted file mode 100644 -index 1941a11f8c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/README.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/README.md -deleted file mode 100644 -index edd4408054..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/build-aux/.keep b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/build-aux/.keep -deleted file mode 100644 -index e69de29bb2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/cmake/internal_utils.cmake b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/cmake/internal_utils.cmake -deleted file mode 100644 -index 777b91ed4b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.cbproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.cbproj -deleted file mode 100644 -index 285bb2a87b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.groupproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest.groupproj -deleted file mode 100644 -index 849f4c4b81..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_all.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_all.cc -deleted file mode 100644 -index ba7ad68ad1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_link.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_link.cc -deleted file mode 100644 -index b955ebf2f9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_main.cbproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_main.cbproj -deleted file mode 100644 -index fae32cb29b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_unittest.cbproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/codegear/gtest_unittest.cbproj -deleted file mode 100644 -index 33f7056346..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/configure.ac b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/configure.ac -deleted file mode 100644 -index cc592e1583..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/AdvancedGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/AdvancedGuide.md -deleted file mode 100644 -index 93a65200da..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/DevGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/DevGuide.md -deleted file mode 100644 -index 06467a3277..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Documentation.md -deleted file mode 100644 -index 8ca1aac759..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/FAQ.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/FAQ.md -deleted file mode 100644 -index 5fd6cb7238..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Primer.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Primer.md -deleted file mode 100644 -index 474c1d2ab6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/PumpManual.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/PumpManual.md -deleted file mode 100644 -index 8184f153ca..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Samples.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/Samples.md -deleted file mode 100644 -index f21d200567..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_AdvancedGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_AdvancedGuide.md -deleted file mode 100644 -index 34e19c26fd..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Documentation.md -deleted file mode 100644 -index 46bba2ec86..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_FAQ.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_FAQ.md -deleted file mode 100644 -index e870aff000..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Primer.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_Primer.md -deleted file mode 100644 -index 6960d2ce4c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_PumpManual.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_PumpManual.md -deleted file mode 100644 -index 15710789dd..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_XcodeGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_5_XcodeGuide.md -deleted file mode 100644 -index bf24bf51bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_AdvancedGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_AdvancedGuide.md -deleted file mode 100644 -index 78864b1667..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Documentation.md -deleted file mode 100644 -index ca924660a3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_FAQ.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_FAQ.md -deleted file mode 100644 -index 2b7f784077..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Primer.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Primer.md -deleted file mode 100644 -index 8d840ef45b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_PumpManual.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_PumpManual.md -deleted file mode 100644 -index 8184f153ca..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Samples.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_Samples.md -deleted file mode 100644 -index f21d200567..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_XcodeGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_6_XcodeGuide.md -deleted file mode 100644 -index bf24bf51bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_AdvancedGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_AdvancedGuide.md -deleted file mode 100644 -index dd4af8f366..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Documentation.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Documentation.md -deleted file mode 100644 -index 282697a50b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_FAQ.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_FAQ.md -deleted file mode 100644 -index 3dd914dcf1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Primer.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Primer.md -deleted file mode 100644 -index b1827c7355..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_PumpManual.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_PumpManual.md -deleted file mode 100644 -index 8184f153ca..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Samples.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_Samples.md -deleted file mode 100644 -index f21d200567..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_XcodeGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/V1_7_XcodeGuide.md -deleted file mode 100644 -index bf24bf51bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/XcodeGuide.md b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/docs/XcodeGuide.md -deleted file mode 100644 -index bf24bf51bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-death-test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-death-test.h -deleted file mode 100644 -index 957a69c6a9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-message.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-message.h -deleted file mode 100644 -index fe879bca79..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h -deleted file mode 100644 -index 038f9ba79e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-param-test.h.pump -deleted file mode 100644 -index 3078d6d2a1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-printers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-printers.h -deleted file mode 100644 -index 8a33164cb3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-spi.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-spi.h -deleted file mode 100644 -index f63fa9a1b2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-test-part.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-test-part.h -deleted file mode 100644 -index 77eb844839..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-typed-test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest-typed-test.h -deleted file mode 100644 -index 5f69d5678e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest.h -deleted file mode 100644 -index f846c5bd66..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_pred_impl.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_pred_impl.h -deleted file mode 100644 -index 30ae712f50..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_prod.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/gtest_prod.h -deleted file mode 100644 -index da80ddc6c7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-port.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-port.h -deleted file mode 100644 -index 7e744bd3bb..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-printers.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest-printers.h -deleted file mode 100644 -index 60c1ea050b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/custom/gtest.h -deleted file mode 100644 -index c27412a898..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-death-test-internal.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-death-test-internal.h -deleted file mode 100644 -index 2b3a78f5bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-filepath.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-filepath.h -deleted file mode 100644 -index 7a13b4b0de..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-internal.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-internal.h -deleted file mode 100644 -index ebd1cf615d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-linked_ptr.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-linked_ptr.h -deleted file mode 100644 -index 3602942217..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h -deleted file mode 100644 -index 4d1d81d20f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util-generated.h.pump -deleted file mode 100644 -index 5c7c47af0b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-param-util.h -deleted file mode 100644 -index 82cab9b020..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port-arch.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port-arch.h -deleted file mode 100644 -index 74ab949057..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-port.h -deleted file mode 100644 -index 0094ed5077..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-string.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-string.h -deleted file mode 100644 -index 97f1a7fdd2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h -deleted file mode 100644 -index e9b405340a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-tuple.h.pump -deleted file mode 100644 -index 429ddfeeca..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h -deleted file mode 100644 -index e46f7cfcb4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h.pump b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/include/gtest/internal/gtest-type-util.h.pump -deleted file mode 100644 -index 251fdf025b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/acx_pthread.m4 b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/acx_pthread.m4 -deleted file mode 100644 -index 2cf20de144..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/gtest.m4 b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/m4/gtest.m4 -deleted file mode 100644 -index 6598ba75a4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/prime_tables.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/prime_tables.h -deleted file mode 100644 -index 92ce16a014..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.cc -deleted file mode 100644 -index f171e2609d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1.h -deleted file mode 100644 -index 3dfeb98c45..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample10_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample10_unittest.cc -deleted file mode 100644 -index 0051cd5dcd..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample1_unittest.cc -deleted file mode 100644 -index aefc4f1d86..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.cc -deleted file mode 100644 -index 5f763b9bdf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2.h -deleted file mode 100644 -index cb485c70fb..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample2_unittest.cc -deleted file mode 100644 -index 4fa19b71c7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3-inl.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3-inl.h -deleted file mode 100644 -index 7e3084d638..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample3_unittest.cc -deleted file mode 100644 -index bf3877d013..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.cc -deleted file mode 100644 -index ae44bda6f1..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4.h -deleted file mode 100644 -index cd60f0dd2d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample4_unittest.cc -deleted file mode 100644 -index fa5afc7d5a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample5_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample5_unittest.cc -deleted file mode 100644 -index 43d8e57775..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample6_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample6_unittest.cc -deleted file mode 100644 -index 8f2036a516..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample7_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample7_unittest.cc -deleted file mode 100644 -index 1b651a21d6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample8_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample8_unittest.cc -deleted file mode 100644 -index 7274334067..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample9_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/samples/sample9_unittest.cc -deleted file mode 100644 -index b2e2079bf3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/common.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/common.py -deleted file mode 100644 -index 3c0347a75b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/fuse_gtest_files.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/fuse_gtest_files.py +index 8d24f28..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/run_with_path.py b/test/gtest-1.11.0/googletest/scripts/run_with_path.py deleted file mode 100755 -index 3f3e9f36d6..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gen_gtest_pred_impl.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gen_gtest_pred_impl.py +index d46ab4d..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/upload.py b/test/gtest-1.11.0/googletest/scripts/upload.py deleted file mode 100755 -index 3e7ab042ea..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gtest-config.in b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/gtest-config.in +index eba5711..0000000 +diff --git a/test/gtest-1.11.0/googletest/scripts/upload_gtest.py b/test/gtest-1.11.0/googletest/scripts/upload_gtest.py deleted file mode 100755 -index 780f8432ef..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/pump.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/pump.py +index be19ae8..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-all.cc b/test/gtest-1.11.0/googletest/src/gtest-all.cc +deleted file mode 100644 +index ad29290..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-death-test.cc b/test/gtest-1.11.0/googletest/src/gtest-death-test.cc +deleted file mode 100644 +index bf4f633..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-filepath.cc b/test/gtest-1.11.0/googletest/src/gtest-filepath.cc +deleted file mode 100644 +index 0b56294..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-internal-inl.h b/test/gtest-1.11.0/googletest/src/gtest-internal-inl.h +deleted file mode 100644 +index 6d8cecb..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-matchers.cc b/test/gtest-1.11.0/googletest/src/gtest-matchers.cc +deleted file mode 100644 +index 65104eb..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-port.cc b/test/gtest-1.11.0/googletest/src/gtest-port.cc +deleted file mode 100644 +index 53a4d37..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-printers.cc b/test/gtest-1.11.0/googletest/src/gtest-printers.cc +deleted file mode 100644 +index 1b68fcb..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-test-part.cc b/test/gtest-1.11.0/googletest/src/gtest-test-part.cc +deleted file mode 100644 +index a938683..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest-typed-test.cc b/test/gtest-1.11.0/googletest/src/gtest-typed-test.cc +deleted file mode 100644 +index c02c3df..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest.cc b/test/gtest-1.11.0/googletest/src/gtest.cc +deleted file mode 100644 +index 21c611a..0000000 +diff --git a/test/gtest-1.11.0/googletest/src/gtest_main.cc b/test/gtest-1.11.0/googletest/src/gtest_main.cc +deleted file mode 100644 +index 46b27c3..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/BUILD.bazel b/test/gtest-1.11.0/googletest/test/BUILD.bazel +deleted file mode 100644 +index b06a00a..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest.py deleted file mode 100755 -index 5efb653c20..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/release_docs.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/release_docs.py +index a5dfbc6..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-break-on-failure-unittest_.cc +deleted file mode 100644 +index f84957a..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py b/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test.py deleted file mode 100755 -index 1291347f67..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload.py +index 94a5b33..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-catch-exceptions-test_.cc +deleted file mode 100644 +index 8c127d4..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-color-test.py b/test/gtest-1.11.0/googletest/test/googletest-color-test.py deleted file mode 100755 -index 6e6f9a1471..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload_gtest.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/scripts/upload_gtest.py +index f3b7c99..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-color-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-color-test_.cc +deleted file mode 100644 +index 220a3a0..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc b/test/gtest-1.11.0/googletest/test/googletest-death-test-test.cc +deleted file mode 100644 +index c0b3d1f..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc b/test/gtest-1.11.0/googletest/test/googletest-death-test_ex_test.cc +deleted file mode 100644 +index 7219680..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-env-var-test.py b/test/gtest-1.11.0/googletest/test/googletest-env-var-test.py deleted file mode 100755 -index be19ae8091..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-all.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-all.cc +index 02c3655..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-env-var-test_.cc deleted file mode 100644 -index 0a9cee5223..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-death-test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-death-test.cc -deleted file mode 100644 -index a01a369830..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-filepath.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-filepath.cc -deleted file mode 100644 -index 0292dc1195..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-internal-inl.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-internal-inl.h -deleted file mode 100644 -index ed8a682a96..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-port.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-port.cc -deleted file mode 100644 -index e5bf3dd2be..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-printers.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-printers.cc -deleted file mode 100644 -index a2df412f8a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-test-part.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-test-part.cc -deleted file mode 100644 -index fb0e35425e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-typed-test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest-typed-test.cc -deleted file mode 100644 -index df1eef4754..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest.cc -deleted file mode 100644 -index d882ab2e36..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest_main.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/src/gtest_main.cc -deleted file mode 100644 -index f302822552..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_ex_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_ex_test.cc -deleted file mode 100644 -index b50a13d5e2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-death-test_test.cc -deleted file mode 100644 -index bb4a3d1b38..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-filepath_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-filepath_test.cc -deleted file mode 100644 -index da72986926..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-linked_ptr_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-linked_ptr_test.cc -deleted file mode 100644 -index 6fcf5124a8..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-listener_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-listener_test.cc -deleted file mode 100644 -index 90747685f0..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-message_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-message_test.cc -deleted file mode 100644 -index 175238ef4e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-options_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-options_test.cc -deleted file mode 100644 -index 5586dc3b1b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test2_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test2_test.cc -deleted file mode 100644 -index 4a782fe708..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.cc -deleted file mode 100644 -index 8b278bb94b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-param-test_test.h -deleted file mode 100644 -index 26ea122b10..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-port_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-port_test.cc -deleted file mode 100644 -index 6ea607bc70..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-printers_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-printers_test.cc -deleted file mode 100644 -index 3e97cc24ab..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-test-part_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-test-part_test.cc -deleted file mode 100644 -index ca8ba933ae..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-tuple_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-tuple_test.cc -deleted file mode 100644 -index bfaa3e0ac4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test2_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test2_test.cc -deleted file mode 100644 -index c284700b02..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.cc -deleted file mode 100644 -index 93628ba080..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-typed-test_test.h -deleted file mode 100644 -index 41d75704cf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-unittest-api_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest-unittest-api_test.cc -deleted file mode 100644 -index b1f51688af..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_all_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_all_test.cc -deleted file mode 100644 -index 955aa62828..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest.py +index 52f9586..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest.py deleted file mode 100755 -index 78f3e0f53b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_break_on_failure_unittest_.cc +index 3aeb2df..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-failfast-unittest_.cc deleted file mode 100644 -index dd07478c07..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test.py +index 0b2c951..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc b/test/gtest-1.11.0/googletest/test/googletest-filepath-test.cc +deleted file mode 100644 +index aafad36..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-filter-unittest.py deleted file mode 100755 -index e6fc22fd1f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_catch_exceptions_test_.cc +index 6b32f2d..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-filter-unittest_.cc deleted file mode 100644 -index d0fc82c998..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test.py +index d30ec9c..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest.py +deleted file mode 100644 +index 32ba628..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-global-environment-unittest_.cc +deleted file mode 100644 +index f401b2f..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py b/test/gtest-1.11.0/googletest/test/googletest-json-outfiles-test.py +deleted file mode 100644 +index 8ef47b8..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-json-output-unittest.py +deleted file mode 100644 +index 41c8565..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py b/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest.py deleted file mode 100755 -index d02a53ed85..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_color_test_.cc +index 81423a3..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc b/test/gtest-1.11.0/googletest/test/googletest-list-tests-unittest_.cc deleted file mode 100644 -index f61ebb89b8..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test.py +index 493c6f0..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-listener-test.cc b/test/gtest-1.11.0/googletest/test/googletest-listener-test.cc +deleted file mode 100644 +index 10457af..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-message-test.cc b/test/gtest-1.11.0/googletest/test/googletest-message-test.cc +deleted file mode 100644 +index 962d519..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-options-test.cc b/test/gtest-1.11.0/googletest/test/googletest-options-test.cc +deleted file mode 100644 +index 11fb1f2..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt b/test/gtest-1.11.0/googletest/test/googletest-output-test-golden-lin.txt +deleted file mode 100644 +index 3fab3b9..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test.py b/test/gtest-1.11.0/googletest/test/googletest-output-test.py deleted file mode 100755 -index 424075cfa3..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_env_var_test_.cc +index 09028f6..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-output-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-output-test_.cc deleted file mode 100644 -index 539afc9683..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_environment_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_environment_test.cc +index 074f64e..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test.py deleted file mode 100644 -index 3cff19e70e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest.py +index 2a08477..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name1-test_.cc +deleted file mode 100644 +index 955d699..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test.py +deleted file mode 100644 +index ab838f4..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-invalid-name2-test_.cc +deleted file mode 100644 +index 76371df..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test-test.cc +deleted file mode 100644 +index 023aa46..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test-test.h b/test/gtest-1.11.0/googletest/test/googletest-param-test-test.h +deleted file mode 100644 +index 8919375..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc b/test/gtest-1.11.0/googletest/test/googletest-param-test2-test.cc +deleted file mode 100644 +index 2a29fb1..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-port-test.cc b/test/gtest-1.11.0/googletest/test/googletest-port-test.cc +deleted file mode 100644 +index 1e0c861..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-printers-test.cc b/test/gtest-1.11.0/googletest/test/googletest-printers-test.cc +deleted file mode 100644 +index e1e8e1c..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py b/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test.py deleted file mode 100755 -index ec0b151b11..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_filter_unittest_.cc +index c82162f..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-setuptestsuite-test_.cc deleted file mode 100644 -index 77deffc38f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test.py +index a4bc4ef..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py b/test/gtest-1.11.0/googletest/test/googletest-shuffle-test.py deleted file mode 100755 -index 093c838d9e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_help_test_.cc +index 573cc5e..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-shuffle-test_.cc deleted file mode 100644 -index 31f78c2441..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest.py +index 4505663..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc b/test/gtest-1.11.0/googletest/test/googletest-test-part-test.cc +deleted file mode 100644 +index 44cf7ca..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py b/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test.py deleted file mode 100755 -index f2d2fd1b1c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_list_tests_unittest_.cc +index ea627c4..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-throw-on-failure-test_.cc deleted file mode 100644 -index 907c176ba9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_main_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_main_unittest.cc -deleted file mode 100644 -index ecd9bb876f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_no_test_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_no_test_unittest.cc -deleted file mode 100644 -index 292599af8d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test.py +index 83bb914..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py b/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test.py deleted file mode 100755 -index 06dbee0980..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_.cc +index 69595a0..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc b/test/gtest-1.11.0/googletest/test/googletest-uninitialized-test_.cc deleted file mode 100644 -index 1070a9f26f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_golden_lin.txt b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_output_test_golden_lin.txt +index b4434d5..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc b/test/gtest-1.11.0/googletest/test/gtest-typed-test2_test.cc deleted file mode 100644 -index 2223d560e2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_pred_impl_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_pred_impl_unittest.cc +index e83ca2e..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc b/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.cc deleted file mode 100644 -index a84eff860a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_premature_exit_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_premature_exit_test.cc +index 5fc678c..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h b/test/gtest-1.11.0/googletest/test/gtest-typed-test_test.h deleted file mode 100644 -index 3b4dc7d43f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_prod_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_prod_test.cc +index 8ce559c..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc b/test/gtest-1.11.0/googletest/test/gtest-unittest-api_test.cc deleted file mode 100644 -index 060abce187..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_repeat_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_repeat_test.cc +index 8ef5058..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_all_test.cc b/test/gtest-1.11.0/googletest/test/gtest_all_test.cc deleted file mode 100644 -index 481012adc2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test.py +index 615b29b..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc b/test/gtest-1.11.0/googletest/test/gtest_assert_by_exception_test.cc +deleted file mode 100644 +index ada4cb3..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_environment_test.cc b/test/gtest-1.11.0/googletest/test/gtest_environment_test.cc +deleted file mode 100644 +index 064bfc5..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_help_test.py b/test/gtest-1.11.0/googletest/test/gtest_help_test.py deleted file mode 100755 -index 30d0303d19..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_shuffle_test_.cc +index 8d953bb..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_help_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_help_test_.cc deleted file mode 100644 -index 6fb441bd4d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_sole_header_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_sole_header_test.cc +index 750ae6c..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_json_test_utils.py deleted file mode 100644 -index ccd091a281..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_stress_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_stress_test.cc +index 62bbfc2..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py b/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest.py deleted file mode 100644 -index e7daa430df..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_test_utils.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_test_utils.py +index a442fc1..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc b/test/gtest-1.11.0/googletest/test/gtest_list_output_unittest_.cc +deleted file mode 100644 +index 92b9d4f..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_main_unittest.cc +deleted file mode 100644 +index eddedea..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_no_test_unittest.cc +deleted file mode 100644 +index d4f88db..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_pred_impl_unittest.cc +deleted file mode 100644 +index bbef994..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc b/test/gtest-1.11.0/googletest/test/gtest_premature_exit_test.cc +deleted file mode 100644 +index 1d1187e..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_prod_test.cc b/test/gtest-1.11.0/googletest/test/gtest_prod_test.cc +deleted file mode 100644 +index ede81a0..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc b/test/gtest-1.11.0/googletest/test/gtest_repeat_test.cc +deleted file mode 100644 +index 7da4a15..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py b/test/gtest-1.11.0/googletest/test/gtest_skip_check_output_test.py deleted file mode 100755 -index 4acd36c975..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_ex_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_ex_test.cc -deleted file mode 100644 -index 8d46c76f16..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test.py +index 14e63ab..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py b/test/gtest-1.11.0/googletest/test/gtest_skip_environment_check_output_test.py deleted file mode 100755 -index 3e7740cabb..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_throw_on_failure_test_.cc +index 6e79155..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc b/test/gtest-1.11.0/googletest/test/gtest_skip_in_environment_setup_test.cc deleted file mode 100644 -index 2b88fe3d9b..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test.py +index 9372310..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_skip_test.cc b/test/gtest-1.11.0/googletest/test/gtest_skip_test.cc +deleted file mode 100644 +index 4a23004..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc b/test/gtest-1.11.0/googletest/test/gtest_sole_header_test.cc +deleted file mode 100644 +index 1d94ac6..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_stress_test.cc b/test/gtest-1.11.0/googletest/test/gtest_stress_test.cc +deleted file mode 100644 +index 8434819..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc b/test/gtest-1.11.0/googletest/test/gtest_test_macro_stack_footprint_test.cc +deleted file mode 100644 +index a48db05..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_test_utils.py deleted file mode 100755 -index 4358370097..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_uninitialized_test_.cc -deleted file mode 100644 -index 44316987fb..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_unittest.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_unittest.cc -deleted file mode 100644 -index 88e94134b9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile1_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile1_test_.cc -deleted file mode 100644 -index 531ced49d4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile2_test_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfile2_test_.cc -deleted file mode 100644 -index 7b400b2760..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfiles_test.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_outfiles_test.py +index d0c2446..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py b/test/gtest-1.11.0/googletest/test/gtest_testbridge_test.py deleted file mode 100755 -index 524e437e6c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest.py +index 87ffad7..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_testbridge_test_.cc +deleted file mode 100644 +index 24617b2..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc b/test/gtest-1.11.0/googletest/test/gtest_throw_on_failure_ex_test.cc +deleted file mode 100644 +index 1d95adb..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_unittest.cc b/test/gtest-1.11.0/googletest/test/gtest_unittest.cc +deleted file mode 100644 +index 1730e8b..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_outfile1_test_.cc +deleted file mode 100644 +index 19aa252..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_outfile2_test_.cc +deleted file mode 100644 +index f9a2a6e..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py b/test/gtest-1.11.0/googletest/test/gtest_xml_outfiles_test.py deleted file mode 100755 -index bcd5975991..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest_.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_output_unittest_.cc -deleted file mode 100644 -index 48b8771b52..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_test_utils.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/gtest_xml_test_utils.py +index ac66feb..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py b/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest.py deleted file mode 100755 -index 341956b54d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.cc +index eade7aa..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc b/test/gtest-1.11.0/googletest/test/gtest_xml_output_unittest_.cc deleted file mode 100644 -index 8b8a40b44e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/test/production.h -deleted file mode 100644 -index 98fd5e476c..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/DebugProject.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/DebugProject.xcconfig -deleted file mode 100644 -index 3d68157d5d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/FrameworkTarget.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/FrameworkTarget.xcconfig -deleted file mode 100644 -index 357b1c8fbf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/General.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/General.xcconfig -deleted file mode 100644 -index f23e322272..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/ReleaseProject.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/ReleaseProject.xcconfig -deleted file mode 100644 -index 5349f0a04a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/StaticLibraryTarget.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/StaticLibraryTarget.xcconfig -deleted file mode 100644 -index 3922fa51d5..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/TestTarget.xcconfig b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Config/TestTarget.xcconfig -deleted file mode 100644 -index e6652ba859..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Resources/Info.plist b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Resources/Info.plist -deleted file mode 100644 -index 9dd28ea148..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/Info.plist b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/Info.plist -deleted file mode 100644 -index f3852edea2..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/WidgetFramework.xcodeproj/project.pbxproj -deleted file mode 100644 -index 497617eb68..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/runtests.sh b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/runtests.sh -deleted file mode 100644 -index 4a0d413e52..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.cc -deleted file mode 100644 -index bfc4e7fcfd..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.h b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget.h -deleted file mode 100644 -index 0c55cdc8cf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget_test.cc b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Samples/FrameworkSample/widget_test.cc -deleted file mode 100644 -index 8725994218..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/runtests.sh b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/runtests.sh -deleted file mode 100644 -index 3fc229f1d4..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/versiongenerate.py b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/Scripts/versiongenerate.py +index c0036aa..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py b/test/gtest-1.11.0/googletest/test/gtest_xml_test_utils.py deleted file mode 100755 -index 81de8c96ac..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/gtest.xcodeproj/project.pbxproj b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/googletest/xcode/gtest.xcodeproj/project.pbxproj +index ec42c62..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/production.cc b/test/gtest-1.11.0/googletest/test/production.cc deleted file mode 100644 -index aefaa88b05..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/travis.sh b/src/libs/3rdparty/yaml-cpp/test/gtest-1.8.0/travis.sh -deleted file mode 100755 -index bdecbd964d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/handler_test.h b/src/libs/3rdparty/yaml-cpp/test/handler_test.h +index 0f69f6d..0000000 +diff --git a/test/gtest-1.11.0/googletest/test/production.h b/test/gtest-1.11.0/googletest/test/production.h deleted file mode 100644 -index 668a58d01e..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/emitter_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/emitter_test.cpp +index 41a5472..0000000 +diff --git a/test/gtest-1.11.0/library.json b/test/gtest-1.11.0/library.json deleted file mode 100644 -index 27808380d5..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/encoding_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/encoding_test.cpp +index f61bf00..0000000 +diff --git a/test/handler_test.h b/test/handler_test.h deleted file mode 100644 -index 9bd6586378..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/gen_emitter_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/gen_emitter_test.cpp +index 668a58d..0000000 +diff --git a/test/integration/emitter_test.cpp b/test/integration/emitter_test.cpp deleted file mode 100644 -index e44eee6da7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/handler_spec_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/handler_spec_test.cpp +index b277d57..0000000 +diff --git a/test/integration/encoding_test.cpp b/test/integration/encoding_test.cpp deleted file mode 100644 -index d142a0d302..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/handler_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/handler_test.cpp +index 9bd6586..0000000 +diff --git a/test/integration/error_messages_test.cpp b/test/integration/error_messages_test.cpp deleted file mode 100644 -index 6011460713..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/load_node_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/load_node_test.cpp +index 64ab6b9..0000000 +diff --git a/test/integration/gen_emitter_test.cpp b/test/integration/gen_emitter_test.cpp deleted file mode 100644 -index 02bb8fe58d..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/integration/node_spec_test.cpp b/src/libs/3rdparty/yaml-cpp/test/integration/node_spec_test.cpp +index 3536144..0000000 +diff --git a/test/integration/handler_spec_test.cpp b/test/integration/handler_spec_test.cpp deleted file mode 100644 -index aedf38b2bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/main.cpp b/src/libs/3rdparty/yaml-cpp/test/main.cpp +index 8cba902..0000000 +diff --git a/test/integration/handler_test.cpp b/test/integration/handler_test.cpp deleted file mode 100644 -index 443e2dbb3f..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/mock_event_handler.h b/src/libs/3rdparty/yaml-cpp/test/mock_event_handler.h +index 6011460..0000000 +diff --git a/test/integration/load_node_test.cpp b/test/integration/load_node_test.cpp deleted file mode 100644 -index 49d1f0c334..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/node/node_test.cpp b/src/libs/3rdparty/yaml-cpp/test/node/node_test.cpp +index 9d0c790..0000000 +diff --git a/test/integration/node_spec_test.cpp b/test/integration/node_spec_test.cpp deleted file mode 100644 -index 485ad09e1a..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/ostream_wrapper_test.cpp b/src/libs/3rdparty/yaml-cpp/test/ostream_wrapper_test.cpp +index bfc8578..0000000 +diff --git a/test/main.cpp b/test/main.cpp deleted file mode 100644 -index cdc1f05083..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/regex_test.cpp b/src/libs/3rdparty/yaml-cpp/test/regex_test.cpp +index 443e2db..0000000 +diff --git a/test/mock_event_handler.h b/test/mock_event_handler.h deleted file mode 100644 -index 7589d2e4bf..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/test/specexamples.h b/src/libs/3rdparty/yaml-cpp/test/specexamples.h +index 0b7e7da..0000000 +diff --git a/test/node/node_test.cpp b/test/node/node_test.cpp deleted file mode 100644 -index 3c81c77791..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/util/CMakeLists.txt b/src/libs/3rdparty/yaml-cpp/util/CMakeLists.txt +index 5f41ef2..0000000 +diff --git a/test/ostream_wrapper_test.cpp b/test/ostream_wrapper_test.cpp deleted file mode 100644 -index 22866273c7..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/util/api.cpp b/src/libs/3rdparty/yaml-cpp/util/api.cpp +index ff4f635..0000000 +diff --git a/test/parser_test.cpp b/test/parser_test.cpp deleted file mode 100644 -index 8ae5ff2978..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/util/parse.cpp b/src/libs/3rdparty/yaml-cpp/util/parse.cpp +index e5002a4..0000000 +diff --git a/test/regex_test.cpp b/test/regex_test.cpp deleted file mode 100644 -index f3f0279ce5..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/util/read.cpp b/src/libs/3rdparty/yaml-cpp/util/read.cpp +index 658db9e..0000000 +diff --git a/test/specexamples.h b/test/specexamples.h deleted file mode 100644 -index fc88f1f9b9..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/util/sandbox.cpp b/src/libs/3rdparty/yaml-cpp/util/sandbox.cpp +index 46e2c4c..0000000 +diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt deleted file mode 100644 -index 1df25bb242..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/yaml-cpp-config-version.cmake.in b/src/libs/3rdparty/yaml-cpp/yaml-cpp-config-version.cmake.in +index 87ea4f9..0000000 +diff --git a/util/api.cpp b/util/api.cpp deleted file mode 100644 -index 80b9c79add..0000000000 -diff --git a/src/libs/3rdparty/yaml-cpp/yaml-cpp-config.cmake.in b/src/libs/3rdparty/yaml-cpp/yaml-cpp-config.cmake.in +index 8ae5ff2..0000000 +diff --git a/util/parse.cpp b/util/parse.cpp deleted file mode 100644 -index 7b41e3f30c..0000000000 +index ea4295a..0000000 +diff --git a/util/read.cpp b/util/read.cpp +deleted file mode 100644 +index 3455ea3..0000000 +diff --git a/util/sandbox.cpp b/util/sandbox.cpp +deleted file mode 100644 +index f21490e..0000000 +diff --git a/yaml-cpp-config.cmake.in b/yaml-cpp-config.cmake.in +deleted file mode 100644 +index 799b9b4..0000000 +diff --git a/yaml-cpp.pc.in b/yaml-cpp.pc.in +deleted file mode 100644 +index d02dc9e..0000000 -- -2.17.1 +2.42.0 diff --git a/src/libs/3rdparty/yaml-cpp/patches/0002-yaml-cpp-Make-dll.h-compatible-with-fvisibility-hidd.patch b/src/libs/3rdparty/yaml-cpp/patches/0002-yaml-cpp-Make-dll.h-compatible-with-fvisibility-hidd.patch deleted file mode 100644 index 90d684b9c1d..00000000000 --- a/src/libs/3rdparty/yaml-cpp/patches/0002-yaml-cpp-Make-dll.h-compatible-with-fvisibility-hidd.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 72546402c08fc548efb248761870a83e78eb5ea3 Mon Sep 17 00:00:00 2001 -From: Nikolai Kosjar -Date: Wed, 31 Jul 2019 09:08:55 +0200 -Subject: [PATCH] yaml-cpp: Make dll.h compatible with -fvisibility=hidden - -Change-Id: Ic09ace43e37102294d290768b3a7994c7a6b42c6 ---- - src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h | 13 +++++++++++-- - 1 file changed, 11 insertions(+), 2 deletions(-) - -diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h -index a32c06b2e3..897f1533df 100644 ---- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h -+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/dll.h -@@ -18,13 +18,22 @@ - - #ifdef YAML_CPP_DLL // Using or Building YAML-CPP DLL (definition defined - // manually) -+ -+#if defined(_WIN32) || defined(WIN32) -+# define YAML_CPP_API_IMPORT __declspec(dllimport) -+# define YAML_CPP_API_EXPORT __declspec(dllexport) -+#else -+# define YAML_CPP_API_IMPORT __attribute__((visibility("default"))) -+# define YAML_CPP_API_EXPORT __attribute__((visibility("default"))) -+#endif -+ - #ifdef yaml_cpp_EXPORTS // Building YAML-CPP DLL (definition created by CMake - // or defined manually) - // #pragma message( "Defining YAML_CPP_API for DLL export" ) --#define YAML_CPP_API __declspec(dllexport) -+#define YAML_CPP_API YAML_CPP_API_EXPORT - #else // yaml_cpp_EXPORTS - // #pragma message( "Defining YAML_CPP_API for DLL import" ) --#define YAML_CPP_API __declspec(dllimport) -+#define YAML_CPP_API YAML_CPP_API_IMPORT - #endif // yaml_cpp_EXPORTS - #else // YAML_CPP_DLL - #define YAML_CPP_API --- -2.17.1 - diff --git a/src/libs/3rdparty/yaml-cpp/patches/0003-yaml-cpp-MSVC-Fix-unknown-override-specifier-for-_NO.patch b/src/libs/3rdparty/yaml-cpp/patches/0003-yaml-cpp-MSVC-Fix-unknown-override-specifier-for-_NO.patch deleted file mode 100644 index ab7929f964e..00000000000 --- a/src/libs/3rdparty/yaml-cpp/patches/0003-yaml-cpp-MSVC-Fix-unknown-override-specifier-for-_NO.patch +++ /dev/null @@ -1,52 +0,0 @@ -From b6f98df7d1ebdd788e7b5029c3884dcf38a6f17d Mon Sep 17 00:00:00 2001 -From: Nikolai Kosjar -Date: Tue, 27 Aug 2019 12:01:03 +0200 -Subject: [PATCH] yaml-cpp: MSVC: Fix "unknown override specifier" for - _NOEXCEPT - -Change-Id: If07000f7b3e8c4c1b87b683ff9cd29e57d43ab60 ---- - src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h | 8 +------- - src/libs/3rdparty/yaml-cpp/src/exceptions.cpp | 8 +------- - 2 files changed, 2 insertions(+), 14 deletions(-) - -diff --git a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h -index 9c96859b2c..eae31968b7 100644 ---- a/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h -+++ b/src/libs/3rdparty/yaml-cpp/include/yaml-cpp/exceptions.h -@@ -13,13 +13,7 @@ - #include - #include - --// This is here for compatibility with older versions of Visual Studio --// which don't support noexcept --#ifdef _MSC_VER -- #define YAML_CPP_NOEXCEPT _NOEXCEPT --#else -- #define YAML_CPP_NOEXCEPT noexcept --#endif -+#define YAML_CPP_NOEXCEPT noexcept - - namespace YAML { - // error messages -diff --git a/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp -index 9b6d8912c1..d5e10b23c1 100644 ---- a/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp -+++ b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp -@@ -1,12 +1,6 @@ - #include "yaml-cpp/exceptions.h" - --// This is here for compatibility with older versions of Visual Studio --// which don't support noexcept --#ifdef _MSC_VER -- #define YAML_CPP_NOEXCEPT _NOEXCEPT --#else -- #define YAML_CPP_NOEXCEPT noexcept --#endif -+#define YAML_CPP_NOEXCEPT noexcept - - namespace YAML { - --- -2.17.1 - diff --git a/src/libs/3rdparty/yaml-cpp/src/binary.cpp b/src/libs/3rdparty/yaml-cpp/src/binary.cpp index a7e51301b82..d27762a2436 100644 --- a/src/libs/3rdparty/yaml-cpp/src/binary.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/binary.cpp @@ -1,5 +1,7 @@ #include "yaml-cpp/binary.h" +#include + namespace YAML { static const char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -64,7 +66,7 @@ static const unsigned char decoding[] = { }; std::vector DecodeBase64(const std::string &input) { - typedef std::vector ret_type; + using ret_type = std::vector; if (input.empty()) return ret_type(); @@ -72,22 +74,27 @@ std::vector DecodeBase64(const std::string &input) { unsigned char *out = &ret[0]; unsigned value = 0; - for (std::size_t i = 0; i < input.size(); i++) { - unsigned char d = decoding[static_cast(input[i])]; + for (std::size_t i = 0, cnt = 0; i < input.size(); i++) { + if (std::isspace(static_cast(input[i]))) { + // skip newlines + continue; + } + unsigned char d = decoding[static_cast(input[i])]; if (d == 255) return ret_type(); value = (value << 6) | d; - if (i % 4 == 3) { + if (cnt % 4 == 3) { *out++ = value >> 16; if (i > 0 && input[i - 1] != '=') *out++ = value >> 8; if (input[i] != '=') *out++ = value; } + ++cnt; } ret.resize(out - &ret[0]); return ret; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/collectionstack.h b/src/libs/3rdparty/yaml-cpp/src/collectionstack.h index 2302786e037..9feba967951 100644 --- a/src/libs/3rdparty/yaml-cpp/src/collectionstack.h +++ b/src/libs/3rdparty/yaml-cpp/src/collectionstack.h @@ -7,8 +7,8 @@ #pragma once #endif -#include #include +#include namespace YAML { struct CollectionType { @@ -17,6 +17,7 @@ struct CollectionType { class CollectionStack { public: + CollectionStack() : collectionStack{} {} CollectionType::value GetCurCollectionType() const { if (collectionStack.empty()) return CollectionType::NoCollection; @@ -28,12 +29,13 @@ class CollectionStack { } void PopCollectionType(CollectionType::value type) { assert(type == GetCurCollectionType()); + (void)type; collectionStack.pop(); } private: std::stack collectionStack; }; -} +} // namespace YAML #endif // COLLECTIONSTACK_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/convert.cpp b/src/libs/3rdparty/yaml-cpp/src/convert.cpp index ec05b77826b..9658b360355 100644 --- a/src/libs/3rdparty/yaml-cpp/src/convert.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/convert.cpp @@ -16,11 +16,7 @@ std::string tolower(const std::string& str) { template bool IsEntirely(const std::string& str, T func) { - for (std::size_t i = 0; i < str.size(); i++) - if (!func(str[i])) - return false; - - return true; + return std::all_of(str.begin(), str.end(), [=](char ch) { return func(ch); }); } // IsFlexibleCase @@ -39,7 +35,7 @@ bool IsFlexibleCase(const std::string& str) { std::string rest = str.substr(1); return firstcaps && (IsEntirely(rest, IsLower) || IsEntirely(rest, IsUpper)); } -} +} // namespace namespace YAML { bool convert::decode(const Node& node, bool& rhs) { @@ -52,19 +48,22 @@ bool convert::decode(const Node& node, bool& rhs) { static const struct { std::string truename, falsename; } names[] = { - {"y", "n"}, {"yes", "no"}, {"true", "false"}, {"on", "off"}, + {"y", "n"}, + {"yes", "no"}, + {"true", "false"}, + {"on", "off"}, }; if (!IsFlexibleCase(node.Scalar())) return false; - for (unsigned i = 0; i < sizeof(names) / sizeof(names[0]); i++) { - if (names[i].truename == tolower(node.Scalar())) { + for (const auto& name : names) { + if (name.truename == tolower(node.Scalar())) { rhs = true; return true; } - if (names[i].falsename == tolower(node.Scalar())) { + if (name.falsename == tolower(node.Scalar())) { rhs = false; return true; } @@ -72,4 +71,4 @@ bool convert::decode(const Node& node, bool& rhs) { return false; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp b/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp new file mode 100644 index 00000000000..5bf6cdf03bf --- /dev/null +++ b/src/libs/3rdparty/yaml-cpp/src/depthguard.cpp @@ -0,0 +1,9 @@ +#include "yaml-cpp/depthguard.h" + +namespace YAML { + +DeepRecursion::DeepRecursion(int depth, const Mark& mark_, + const std::string& msg_) + : ParserException(mark_, msg_), m_depth(depth) {} + +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/directives.cpp b/src/libs/3rdparty/yaml-cpp/src/directives.cpp index 963bd2cd379..4c1dc201d7f 100644 --- a/src/libs/3rdparty/yaml-cpp/src/directives.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/directives.cpp @@ -1,16 +1,11 @@ #include "directives.h" namespace YAML { -Directives::Directives() { - // version - version.isDefault = true; - version.major = 1; - version.minor = 2; -} +Directives::Directives() : version{true, 1, 2}, tags{} {} -const std::string Directives::TranslateTagHandle( +std::string Directives::TranslateTagHandle( const std::string& handle) const { - std::map::const_iterator it = tags.find(handle); + auto it = tags.find(handle); if (it == tags.end()) { if (handle == "!!") return "tag:yaml.org,2002:"; @@ -19,4 +14,4 @@ const std::string Directives::TranslateTagHandle( return it->second; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/directives.h b/src/libs/3rdparty/yaml-cpp/src/directives.h index 333af26e374..18d14c9c0b7 100644 --- a/src/libs/3rdparty/yaml-cpp/src/directives.h +++ b/src/libs/3rdparty/yaml-cpp/src/directives.h @@ -19,7 +19,7 @@ struct Version { struct Directives { Directives(); - const std::string TranslateTagHandle(const std::string& handle) const; + std::string TranslateTagHandle(const std::string& handle) const; Version version; std::map tags; diff --git a/src/libs/3rdparty/yaml-cpp/src/emit.cpp b/src/libs/3rdparty/yaml-cpp/src/emit.cpp index 51bc791533d..b0efb8401cc 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emit.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/emit.cpp @@ -1,7 +1,7 @@ #include "yaml-cpp/node/emit.h" +#include "nodeevents.h" #include "yaml-cpp/emitfromevents.h" #include "yaml-cpp/emitter.h" -#include "nodeevents.h" namespace YAML { Emitter& operator<<(Emitter& out, const Node& node) { diff --git a/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp b/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp index 4832649f3c7..2e97187b900 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/emitfromevents.cpp @@ -16,10 +16,11 @@ std::string ToString(YAML::anchor_t anchor) { stream << anchor; return stream.str(); } -} +} // namespace namespace YAML { -EmitFromEvents::EmitFromEvents(Emitter& emitter) : m_emitter(emitter) {} +EmitFromEvents::EmitFromEvents(Emitter& emitter) + : m_emitter(emitter), m_stateStack{} {} void EmitFromEvents::OnDocumentStart(const Mark&) {} @@ -58,6 +59,8 @@ void EmitFromEvents::OnSequenceStart(const Mark&, const std::string& tag, default: break; } + // Restore the global settings to eliminate the override from node style + m_emitter.RestoreGlobalModifiedSettings(); m_emitter << BeginSeq; m_stateStack.push(State::WaitingForSequenceEntry); } @@ -82,6 +85,8 @@ void EmitFromEvents::OnMapStart(const Mark&, const std::string& tag, default: break; } + // Restore the global settings to eliminate the override from node style + m_emitter.RestoreGlobalModifiedSettings(); m_emitter << BeginMap; m_stateStack.push(State::WaitingForKey); } @@ -116,4 +121,4 @@ void EmitFromEvents::EmitProps(const std::string& tag, anchor_t anchor) { if (anchor) m_emitter << Anchor(ToString(anchor)); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/emitter.cpp b/src/libs/3rdparty/yaml-cpp/src/emitter.cpp index ebeb059554e..4d483075bde 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitter.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/emitter.cpp @@ -11,12 +11,12 @@ namespace YAML { class Binary; struct _Null; -Emitter::Emitter() : m_pState(new EmitterState) {} +Emitter::Emitter() : m_pState(new EmitterState), m_stream{} {} Emitter::Emitter(std::ostream& stream) : m_pState(new EmitterState), m_stream(stream) {} -Emitter::~Emitter() {} +Emitter::~Emitter() = default; const char* Emitter::c_str() const { return m_stream.str(); } @@ -49,6 +49,10 @@ bool Emitter::SetBoolFormat(EMITTER_MANIP value) { return ok; } +bool Emitter::SetNullFormat(EMITTER_MANIP value) { + return m_pState->SetNullFormat(value, FmtScope::Global); +} + bool Emitter::SetIntBase(EMITTER_MANIP value) { return m_pState->SetIntFormat(value, FmtScope::Global); } @@ -86,6 +90,10 @@ bool Emitter::SetDoublePrecision(std::size_t n) { return m_pState->SetDoublePrecision(n, FmtScope::Global); } +void Emitter::RestoreGlobalModifiedSettings() { + m_pState->RestoreGlobalModifiedSettings(); +} + // SetLocalValue // . Either start/end a group, or set a modifier locally Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) { @@ -197,6 +205,7 @@ void Emitter::EmitBeginSeq() { void Emitter::EmitEndSeq() { if (!good()) return; + FlowType::value originalType = m_pState->CurGroupFlowType(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); @@ -205,8 +214,12 @@ void Emitter::EmitEndSeq() { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); - if (m_pState->CurGroupChildCount() == 0) + if (originalType == FlowType::Block) { m_stream << "["; + } else { + if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) + m_stream << "["; + } m_stream << "]"; } @@ -227,6 +240,7 @@ void Emitter::EmitBeginMap() { void Emitter::EmitEndMap() { if (!good()) return; + FlowType::value originalType = m_pState->CurGroupFlowType(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); @@ -235,8 +249,12 @@ void Emitter::EmitEndMap() { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); - if (m_pState->CurGroupChildCount() == 0) + if (originalType == FlowType::Block) { m_stream << "{"; + } else { + if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) + m_stream << "{"; + } m_stream << "}"; } @@ -285,10 +303,8 @@ void Emitter::PrepareTopNode(EmitterNodeType::value child) { if (child == EmitterNodeType::NoType) return; - if (m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) { - if (child != EmitterNodeType::NoType) - EmitBeginDoc(); - } + if (m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) + EmitBeginDoc(); switch (child) { case EmitterNodeType::NoType: @@ -488,6 +504,9 @@ void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); + if (m_pState->HasAlias()) { + m_stream << " "; + } m_stream << ":"; } @@ -514,7 +533,8 @@ void Emitter::BlockMapPrepareNode(EmitterNodeType::value child) { if (m_pState->GetMapKeyFormat() == LongKey) m_pState->SetLongKey(); if (child == EmitterNodeType::BlockSeq || - child == EmitterNodeType::BlockMap) + child == EmitterNodeType::BlockMap || + child == EmitterNodeType::Property) m_pState->SetLongKey(); if (m_pState->CurGroupLongKey()) @@ -558,6 +578,8 @@ void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child) { break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: + if (m_pState->HasBegunContent()) + m_stream << "\n"; break; } } @@ -581,8 +603,12 @@ void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child) { case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: + SpaceOrIndentTo(true, curIndent + 1); + break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: + if (m_pState->HasBegunContent()) + m_stream << "\n"; SpaceOrIndentTo(true, curIndent + 1); break; } @@ -621,6 +647,9 @@ void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child) { const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent(); if (!m_pState->HasBegunNode()) { + if (m_pState->HasAlias()) { + m_stream << " "; + } m_stream << ":"; } @@ -674,16 +703,29 @@ void Emitter::StartedScalar() { m_pState->StartedScalar(); } // ******************************************************************************************* // overloads of Write +StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) { + switch (emitterManip) { + case EscapeNonAscii: + return StringEscaping::NonAscii; + case EscapeAsJson: + return StringEscaping::JSON; + default: + return StringEscaping::None; + break; + } +} + Emitter& Emitter::Write(const std::string& str) { if (!good()) return *this; - const bool escapeNonAscii = m_pState->GetOutputCharset() == EscapeNonAscii; + StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset()); + const StringFormat::value strFormat = Utils::ComputeStringFormat(str, m_pState->GetStringFormat(), - m_pState->CurGroupFlowType(), escapeNonAscii); + m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii); - if (strFormat == StringFormat::Literal) + if (strFormat == StringFormat::Literal || str.size() > 1024) m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local); PrepareNode(EmitterNodeType::Scalar); @@ -696,7 +738,7 @@ Emitter& Emitter::Write(const std::string& str) { Utils::WriteSingleQuotedString(m_stream, str); break; case StringFormat::DoubleQuoted: - Utils::WriteDoubleQuotedString(m_stream, str, escapeNonAscii); + Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping); break; case StringFormat::Literal: Utils::WriteLiteralString(m_stream, str, @@ -766,6 +808,21 @@ const char* Emitter::ComputeFullBoolName(bool b) const { // these answers } +const char* Emitter::ComputeNullName() const { + switch (m_pState->GetNullFormat()) { + case LowerNull: + return "null"; + case UpperNull: + return "NULL"; + case CamelNull: + return "Null"; + case TildeNull: + // fallthrough + default: + return "~"; + } +} + Emitter& Emitter::Write(bool b) { if (!good()) return *this; @@ -787,8 +844,10 @@ Emitter& Emitter::Write(char ch) { if (!good()) return *this; + + PrepareNode(EmitterNodeType::Scalar); - Utils::WriteChar(m_stream, ch); + Utils::WriteChar(m_stream, ch, GetStringEscapingStyle(m_pState->GetOutputCharset())); StartedScalar(); return *this; @@ -812,6 +871,8 @@ Emitter& Emitter::Write(const _Alias& alias) { StartedScalar(); + m_pState->SetAlias(); + return *this; } @@ -889,7 +950,7 @@ Emitter& Emitter::Write(const _Null& /*null*/) { PrepareNode(EmitterNodeType::Scalar); - m_stream << "~"; + m_stream << ComputeNullName(); StartedScalar(); @@ -908,4 +969,4 @@ Emitter& Emitter::Write(const Binary& binary) { return *this; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp b/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp index 3542aaf5071..3dbe4011084 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/emitterstate.cpp @@ -6,29 +6,35 @@ namespace YAML { EmitterState::EmitterState() : m_isGood(true), + m_lastError{}, + // default global manipulators + m_charset(EmitNonAscii), + m_strFmt(Auto), + m_boolFmt(TrueFalseBool), + m_boolLengthFmt(LongBool), + m_boolCaseFmt(LowerCase), + m_nullFmt(TildeNull), + m_intFmt(Dec), + m_indent(2), + m_preCommentIndent(2), + m_postCommentIndent(1), + m_seqFmt(Block), + m_mapFmt(Block), + m_mapKeyFmt(Auto), + m_floatPrecision(std::numeric_limits::max_digits10), + m_doublePrecision(std::numeric_limits::max_digits10), + // + m_modifiedSettings{}, + m_globalModifiedSettings{}, + m_groups{}, m_curIndent(0), m_hasAnchor(false), + m_hasAlias(false), m_hasTag(false), m_hasNonContent(false), - m_docCount(0) { - // set default global manipulators - m_charset.set(EmitNonAscii); - m_strFmt.set(Auto); - m_boolFmt.set(TrueFalseBool); - m_boolLengthFmt.set(LongBool); - m_boolCaseFmt.set(LowerCase); - m_intFmt.set(Dec); - m_indent.set(2); - m_preCommentIndent.set(2); - m_postCommentIndent.set(1); - m_seqFmt.set(Block); - m_mapFmt.set(Block); - m_mapKeyFmt.set(Auto); - m_floatPrecision.set(std::numeric_limits::digits10 + 1); - m_doublePrecision.set(std::numeric_limits::digits10 + 1); -} + m_docCount(0) {} -EmitterState::~EmitterState() {} +EmitterState::~EmitterState() = default; // SetLocalValue // . We blindly tries to set all possible formatters to this value @@ -39,6 +45,7 @@ void EmitterState::SetLocalValue(EMITTER_MANIP value) { SetBoolFormat(value, FmtScope::Local); SetBoolCaseFormat(value, FmtScope::Local); SetBoolLengthFormat(value, FmtScope::Local); + SetNullFormat(value, FmtScope::Local); SetIntFormat(value, FmtScope::Local); SetFlowType(GroupType::Seq, value, FmtScope::Local); SetFlowType(GroupType::Map, value, FmtScope::Local); @@ -47,6 +54,8 @@ void EmitterState::SetLocalValue(EMITTER_MANIP value) { void EmitterState::SetAnchor() { m_hasAnchor = true; } +void EmitterState::SetAlias() { m_hasAlias = true; } + void EmitterState::SetTag() { m_hasTag = true; } void EmitterState::SetNonContent() { m_hasNonContent = true; } @@ -81,6 +90,7 @@ void EmitterState::StartedNode() { } m_hasAnchor = false; + m_hasAlias = false; m_hasTag = false; m_hasNonContent = false; } @@ -90,15 +100,13 @@ EmitterNodeType::value EmitterState::NextGroupType( if (type == GroupType::Seq) { if (GetFlowType(type) == Block) return EmitterNodeType::BlockSeq; - else - return EmitterNodeType::FlowSeq; - } else { - if (GetFlowType(type) == Block) - return EmitterNodeType::BlockMap; - else - return EmitterNodeType::FlowMap; + return EmitterNodeType::FlowSeq; } + if (GetFlowType(type) == Block) + return EmitterNodeType::BlockMap; + return EmitterNodeType::FlowMap; + // can't happen assert(false); return EmitterNodeType::NoType; @@ -152,9 +160,15 @@ void EmitterState::EndedGroup(GroupType::value type) { if (m_groups.empty()) { if (type == GroupType::Seq) { return SetError(ErrorMsg::UNEXPECTED_END_SEQ); - } else { - return SetError(ErrorMsg::UNEXPECTED_END_MAP); } + return SetError(ErrorMsg::UNEXPECTED_END_MAP); + } + + if (m_hasTag) { + SetError(ErrorMsg::INVALID_TAG); + } + if (m_hasAnchor) { + SetError(ErrorMsg::INVALID_ANCHOR); } // get rid of the current group @@ -176,6 +190,9 @@ void EmitterState::EndedGroup(GroupType::value type) { m_globalModifiedSettings.restore(); ClearModifiedSettings(); + m_hasAnchor = false; + m_hasTag = false; + m_hasNonContent = false; } EmitterNodeType::value EmitterState::CurGroupNodeType() const { @@ -216,11 +233,16 @@ std::size_t EmitterState::LastIndent() const { void EmitterState::ClearModifiedSettings() { m_modifiedSettings.clear(); } +void EmitterState::RestoreGlobalModifiedSettings() { + m_globalModifiedSettings.restore(); +} + bool EmitterState::SetOutputCharset(EMITTER_MANIP value, FmtScope::value scope) { switch (value) { case EmitNonAscii: case EscapeNonAscii: + case EscapeAsJson: _Set(m_charset, value, scope); return true; default: @@ -278,6 +300,19 @@ bool EmitterState::SetBoolCaseFormat(EMITTER_MANIP value, } } +bool EmitterState::SetNullFormat(EMITTER_MANIP value, FmtScope::value scope) { + switch (value) { + case LowerNull: + case UpperNull: + case CamelNull: + case TildeNull: + _Set(m_nullFmt, value, scope); + return true; + default: + return false; + } +} + bool EmitterState::SetIntFormat(EMITTER_MANIP value, FmtScope::value scope) { switch (value) { case Dec: @@ -349,7 +384,7 @@ bool EmitterState::SetMapKeyFormat(EMITTER_MANIP value, FmtScope::value scope) { } bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) { - if (value > std::numeric_limits::digits10 + 1) + if (value > std::numeric_limits::max_digits10) return false; _Set(m_floatPrecision, value, scope); return true; @@ -357,9 +392,9 @@ bool EmitterState::SetFloatPrecision(std::size_t value, FmtScope::value scope) { bool EmitterState::SetDoublePrecision(std::size_t value, FmtScope::value scope) { - if (value > std::numeric_limits::digits10 + 1) + if (value > std::numeric_limits::max_digits10) return false; _Set(m_doublePrecision, value, scope); return true; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterstate.h b/src/libs/3rdparty/yaml-cpp/src/emitterstate.h index 0937f000d9f..8f379ca9524 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitterstate.h +++ b/src/libs/3rdparty/yaml-cpp/src/emitterstate.h @@ -43,6 +43,7 @@ class EmitterState { // node handling void SetAnchor(); + void SetAlias(); void SetTag(); void SetNonContent(); void SetLongKey(); @@ -65,6 +66,7 @@ class EmitterState { std::size_t LastIndent() const; std::size_t CurIndent() const { return m_curIndent; } bool HasAnchor() const { return m_hasAnchor; } + bool HasAlias() const { return m_hasAlias; } bool HasTag() const { return m_hasTag; } bool HasBegunNode() const { return m_hasAnchor || m_hasTag || m_hasNonContent; @@ -72,6 +74,7 @@ class EmitterState { bool HasBegunContent() const { return m_hasAnchor || m_hasTag; } void ClearModifiedSettings(); + void RestoreGlobalModifiedSettings(); // formatters void SetLocalValue(EMITTER_MANIP value); @@ -91,6 +94,9 @@ class EmitterState { bool SetBoolCaseFormat(EMITTER_MANIP value, FmtScope::value scope); EMITTER_MANIP GetBoolCaseFormat() const { return m_boolCaseFmt.get(); } + bool SetNullFormat(EMITTER_MANIP value, FmtScope::value scope); + EMITTER_MANIP GetNullFormat() const { return m_nullFmt.get(); } + bool SetIntFormat(EMITTER_MANIP value, FmtScope::value scope); EMITTER_MANIP GetIntFormat() const { return m_intFmt.get(); } @@ -131,6 +137,7 @@ class EmitterState { Setting m_boolFmt; Setting m_boolLengthFmt; Setting m_boolCaseFmt; + Setting m_nullFmt; Setting m_intFmt; Setting m_indent; Setting m_preCommentIndent, m_postCommentIndent; @@ -145,7 +152,12 @@ class EmitterState { struct Group { explicit Group(GroupType::value type_) - : type(type_), indent(0), childCount(0), longKey(false) {} + : type(type_), + flowType{}, + indent(0), + childCount(0), + longKey(false), + modifiedSettings{} {} GroupType::value type; FlowType::value flowType; @@ -177,6 +189,7 @@ class EmitterState { std::vector> m_groups; std::size_t m_curIndent; bool m_hasAnchor; + bool m_hasAlias; bool m_hasTag; bool m_hasNonContent; std::size_t m_docCount; @@ -198,6 +211,6 @@ void EmitterState::_Set(Setting& fmt, T value, FmtScope::value scope) { assert(false); } } -} +} // namespace YAML #endif // EMITTERSTATE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp b/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp index 147738ad8a1..6cdf6de7e29 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/emitterutils.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -8,8 +9,8 @@ #include "regeximpl.h" #include "stringsource.h" #include "yaml-cpp/binary.h" // IWYU pragma: keep -#include "yaml-cpp/ostream_wrapper.h" #include "yaml-cpp/null.h" +#include "yaml-cpp/ostream_wrapper.h" namespace YAML { namespace Utils { @@ -134,12 +135,12 @@ void WriteCodePoint(ostream_wrapper& out, int codePoint) { if (codePoint < 0 || codePoint > 0x10FFFF) { codePoint = REPLACEMENT_CHARACTER; } - if (codePoint < 0x7F) { + if (codePoint <= 0x7F) { out << static_cast(codePoint); - } else if (codePoint < 0x7FF) { + } else if (codePoint <= 0x7FF) { out << static_cast(0xC0 | (codePoint >> 6)) << static_cast(0x80 | (codePoint & 0x3F)); - } else if (codePoint < 0xFFFF) { + } else if (codePoint <= 0xFFFF) { out << static_cast(0xE0 | (codePoint >> 12)) << static_cast(0x80 | ((codePoint >> 6) & 0x3F)) << static_cast(0x80 | (codePoint & 0x3F)); @@ -173,13 +174,13 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, // then check until something is disallowed static const RegEx& disallowed_flow = - Exp::EndScalarInFlow() || (Exp::BlankOrBreak() + Exp::Comment()) || - Exp::NotPrintable() || Exp::Utf8_ByteOrderMark() || Exp::Break() || - Exp::Tab(); + Exp::EndScalarInFlow() | (Exp::BlankOrBreak() + Exp::Comment()) | + Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | + Exp::Tab() | Exp::Ampersand(); static const RegEx& disallowed_block = - Exp::EndScalar() || (Exp::BlankOrBreak() + Exp::Comment()) || - Exp::NotPrintable() || Exp::Utf8_ByteOrderMark() || Exp::Break() || - Exp::Tab(); + Exp::EndScalar() | (Exp::BlankOrBreak() + Exp::Comment()) | + Exp::NotPrintable() | Exp::Utf8_ByteOrderMark() | Exp::Break() | + Exp::Tab() | Exp::Ampersand(); const RegEx& disallowed = flowType == FlowType::Flow ? disallowed_flow : disallowed_block; @@ -199,15 +200,10 @@ bool IsValidPlainScalar(const std::string& str, FlowType::value flowType, bool IsValidSingleQuotedScalar(const std::string& str, bool escapeNonAscii) { // TODO: check for non-printable characters? - for (std::size_t i = 0; i < str.size(); i++) { - if (escapeNonAscii && (0x80 <= static_cast(str[i]))) { - return false; - } - if (str[i] == '\n') { - return false; - } - } - return true; + return std::none_of(str.begin(), str.end(), [=](char ch) { + return (escapeNonAscii && (0x80 <= static_cast(ch))) || + (ch == '\n'); + }); } bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType, @@ -217,28 +213,39 @@ bool IsValidLiteralScalar(const std::string& str, FlowType::value flowType, } // TODO: check for non-printable characters? - for (std::size_t i = 0; i < str.size(); i++) { - if (escapeNonAscii && (0x80 <= static_cast(str[i]))) { - return false; - } - } - return true; + return std::none_of(str.begin(), str.end(), [=](char ch) { + return (escapeNonAscii && (0x80 <= static_cast(ch))); + }); } -void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint) { +std::pair EncodeUTF16SurrogatePair(int codePoint) { + const uint32_t leadOffset = 0xD800 - (0x10000 >> 10); + + return { + leadOffset | (codePoint >> 10), + 0xDC00 | (codePoint & 0x3FF), + }; +} + +void WriteDoubleQuoteEscapeSequence(ostream_wrapper& out, int codePoint, StringEscaping::value stringEscapingStyle) { static const char hexDigits[] = "0123456789abcdef"; out << "\\"; int digits = 8; - if (codePoint < 0xFF) { + if (codePoint < 0xFF && stringEscapingStyle != StringEscaping::JSON) { out << "x"; digits = 2; } else if (codePoint < 0xFFFF) { out << "u"; digits = 4; - } else { + } else if (stringEscapingStyle != StringEscaping::JSON) { out << "U"; digits = 8; + } else { + auto surrogatePair = EncodeUTF16SurrogatePair(codePoint); + WriteDoubleQuoteEscapeSequence(out, surrogatePair.first, stringEscapingStyle); + WriteDoubleQuoteEscapeSequence(out, surrogatePair.second, stringEscapingStyle); + return; } // Write digits into the escape sequence @@ -258,7 +265,7 @@ bool WriteAliasName(ostream_wrapper& out, const std::string& str) { } return true; } -} +} // namespace StringFormat::value ComputeStringFormat(const std::string& str, EMITTER_MANIP strFormat, @@ -310,7 +317,7 @@ bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str) { } bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, - bool escapeNonAscii) { + StringEscaping::value stringEscaping) { out << "\""; int codePoint; for (std::string::const_iterator i = str.begin(); @@ -334,16 +341,19 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, case '\b': out << "\\b"; break; + case '\f': + out << "\\f"; + break; default: if (codePoint < 0x20 || (codePoint >= 0x80 && codePoint <= 0xA0)) { // Control characters and non-breaking space - WriteDoubleQuoteEscapeSequence(out, codePoint); + WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping); } else if (codePoint == 0xFEFF) { // Byte order marks (ZWNS) should be // escaped (YAML 1.2, sec. 5.2) - WriteDoubleQuoteEscapeSequence(out, codePoint); - } else if (escapeNonAscii && codePoint > 0x7E) { - WriteDoubleQuoteEscapeSequence(out, codePoint); + WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping); + } else if (stringEscaping == StringEscaping::NonAscii && codePoint > 0x7E) { + WriteDoubleQuoteEscapeSequence(out, codePoint, stringEscaping); } else { WriteCodePoint(out, codePoint); } @@ -356,37 +366,41 @@ bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, bool WriteLiteralString(ostream_wrapper& out, const std::string& str, std::size_t indent) { out << "|\n"; - out << IndentTo(indent); int codePoint; for (std::string::const_iterator i = str.begin(); GetNextCodePointAndAdvance(codePoint, i, str.end());) { if (codePoint == '\n') { - out << "\n" << IndentTo(indent); + out << "\n"; } else { + out<< IndentTo(indent); WriteCodePoint(out, codePoint); } } return true; } -bool WriteChar(ostream_wrapper& out, char ch) { +bool WriteChar(ostream_wrapper& out, char ch, StringEscaping::value stringEscapingStyle) { if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) { out << ch; } else if (ch == '\"') { - out << "\"\\\"\""; + out << R"("\"")"; } else if (ch == '\t') { - out << "\"\\t\""; + out << R"("\t")"; } else if (ch == '\n') { - out << "\"\\n\""; + out << R"("\n")"; } else if (ch == '\b') { - out << "\"\\b\""; + out << R"("\b")"; + } else if (ch == '\r') { + out << R"("\r")"; + } else if (ch == '\f') { + out << R"("\f")"; } else if (ch == '\\') { - out << "\"\\\\\""; - } else if ((0x20 <= ch && ch <= 0x7e) || ch == ' ') { + out << R"("\\")"; + } else if (0x20 <= ch && ch <= 0x7e) { out << "\"" << ch << "\""; } else { out << "\""; - WriteDoubleQuoteEscapeSequence(out, ch); + WriteDoubleQuoteEscapeSequence(out, ch, stringEscapingStyle); out << "\""; } return true; @@ -401,8 +415,8 @@ bool WriteComment(ostream_wrapper& out, const std::string& str, for (std::string::const_iterator i = str.begin(); GetNextCodePointAndAdvance(codePoint, i, str.end());) { if (codePoint == '\n') { - out << "\n" << IndentTo(curIndent) << "#" - << Indentation(postCommentIndent); + out << "\n" + << IndentTo(curIndent) << "#" << Indentation(postCommentIndent); out.set_comment(); } else { WriteCodePoint(out, codePoint); @@ -476,8 +490,8 @@ bool WriteTagWithPrefix(ostream_wrapper& out, const std::string& prefix, bool WriteBinary(ostream_wrapper& out, const Binary& binary) { WriteDoubleQuotedString(out, EncodeBase64(binary.data(), binary.size()), - false); + StringEscaping::None); return true; } -} -} +} // namespace Utils +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/emitterutils.h b/src/libs/3rdparty/yaml-cpp/src/emitterutils.h index 6cc73191476..3a7d5982521 100644 --- a/src/libs/3rdparty/yaml-cpp/src/emitterutils.h +++ b/src/libs/3rdparty/yaml-cpp/src/emitterutils.h @@ -24,6 +24,10 @@ struct StringFormat { enum value { Plain, SingleQuoted, DoubleQuoted, Literal }; }; +struct StringEscaping { + enum value { None, NonAscii, JSON }; +}; + namespace Utils { StringFormat::value ComputeStringFormat(const std::string& str, EMITTER_MANIP strFormat, @@ -32,10 +36,11 @@ StringFormat::value ComputeStringFormat(const std::string& str, bool WriteSingleQuotedString(ostream_wrapper& out, const std::string& str); bool WriteDoubleQuotedString(ostream_wrapper& out, const std::string& str, - bool escapeNonAscii); + StringEscaping::value stringEscaping); bool WriteLiteralString(ostream_wrapper& out, const std::string& str, std::size_t indent); -bool WriteChar(ostream_wrapper& out, char ch); +bool WriteChar(ostream_wrapper& out, char ch, + StringEscaping::value stringEscapingStyle); bool WriteComment(ostream_wrapper& out, const std::string& str, std::size_t postCommentIndent); bool WriteAlias(ostream_wrapper& out, const std::string& str); diff --git a/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp index d5e10b23c1c..43a7976e907 100644 --- a/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/exceptions.cpp @@ -1,25 +1,20 @@ #include "yaml-cpp/exceptions.h" - -#define YAML_CPP_NOEXCEPT noexcept +#include "yaml-cpp/noexcept.h" namespace YAML { // These destructors are defined out-of-line so the vtable is only emitted once. -Exception::~Exception() YAML_CPP_NOEXCEPT {} -ParserException::~ParserException() YAML_CPP_NOEXCEPT {} -RepresentationException::~RepresentationException() YAML_CPP_NOEXCEPT {} -InvalidScalar::~InvalidScalar() YAML_CPP_NOEXCEPT {} -KeyNotFound::~KeyNotFound() YAML_CPP_NOEXCEPT {} -InvalidNode::~InvalidNode() YAML_CPP_NOEXCEPT {} -BadConversion::~BadConversion() YAML_CPP_NOEXCEPT {} -BadDereference::~BadDereference() YAML_CPP_NOEXCEPT {} -BadSubscript::~BadSubscript() YAML_CPP_NOEXCEPT {} -BadPushback::~BadPushback() YAML_CPP_NOEXCEPT {} -BadInsert::~BadInsert() YAML_CPP_NOEXCEPT {} -EmitterException::~EmitterException() YAML_CPP_NOEXCEPT {} -BadFile::~BadFile() YAML_CPP_NOEXCEPT {} -} - -#undef YAML_CPP_NOEXCEPT - - +Exception::~Exception() YAML_CPP_NOEXCEPT = default; +ParserException::~ParserException() YAML_CPP_NOEXCEPT = default; +RepresentationException::~RepresentationException() YAML_CPP_NOEXCEPT = default; +InvalidScalar::~InvalidScalar() YAML_CPP_NOEXCEPT = default; +KeyNotFound::~KeyNotFound() YAML_CPP_NOEXCEPT = default; +InvalidNode::~InvalidNode() YAML_CPP_NOEXCEPT = default; +BadConversion::~BadConversion() YAML_CPP_NOEXCEPT = default; +BadDereference::~BadDereference() YAML_CPP_NOEXCEPT = default; +BadSubscript::~BadSubscript() YAML_CPP_NOEXCEPT = default; +BadPushback::~BadPushback() YAML_CPP_NOEXCEPT = default; +BadInsert::~BadInsert() YAML_CPP_NOEXCEPT = default; +EmitterException::~EmitterException() YAML_CPP_NOEXCEPT = default; +BadFile::~BadFile() YAML_CPP_NOEXCEPT = default; +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/exp.cpp b/src/libs/3rdparty/yaml-cpp/src/exp.cpp index 695440aec0e..992620ff94b 100644 --- a/src/libs/3rdparty/yaml-cpp/src/exp.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/exp.cpp @@ -12,8 +12,7 @@ namespace YAML { namespace Exp { unsigned ParseHex(const std::string& str, const Mark& mark) { unsigned value = 0; - for (std::size_t i = 0; i < str.size(); i++) { - char ch = str[i]; + for (char ch : str) { int digit = 0; if ('a' <= ch && ch <= 'f') digit = ch - 'a' + 10; @@ -55,14 +54,16 @@ std::string Escape(Stream& in, int codeLength) { // now break it up into chars if (value <= 0x7F) return Str(value); - else if (value <= 0x7FF) + + if (value <= 0x7FF) return Str(0xC0 + (value >> 6)) + Str(0x80 + (value & 0x3F)); - else if (value <= 0xFFFF) + + if (value <= 0xFFFF) return Str(0xE0 + (value >> 12)) + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F)); - else - return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) + - Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F)); + + return Str(0xF0 + (value >> 18)) + Str(0x80 + ((value >> 12) & 0x3F)) + + Str(0x80 + ((value >> 6) & 0x3F)) + Str(0x80 + (value & 0x3F)); } // Escape @@ -104,7 +105,7 @@ std::string Escape(Stream& in) { case 'e': return "\x1B"; case ' ': - return "\x20"; + return R"( )"; case '\"': return "\""; case '\'': @@ -132,5 +133,5 @@ std::string Escape(Stream& in) { std::stringstream msg; throw ParserException(in.mark(), std::string(ErrorMsg::INVALID_ESCAPE) + ch); } -} -} +} // namespace Exp +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/exp.h b/src/libs/3rdparty/yaml-cpp/src/exp.h index 7c02cf6e451..c8837f0f825 100644 --- a/src/libs/3rdparty/yaml-cpp/src/exp.h +++ b/src/libs/3rdparty/yaml-cpp/src/exp.h @@ -33,15 +33,15 @@ inline const RegEx& Tab() { return e; } inline const RegEx& Blank() { - static const RegEx e = Space() || Tab(); + static const RegEx e = Space() | Tab(); return e; } inline const RegEx& Break() { - static const RegEx e = RegEx('\n') || RegEx("\r\n"); + static const RegEx e = RegEx('\n') | RegEx("\r\n") | RegEx('\r'); return e; } inline const RegEx& BlankOrBreak() { - static const RegEx e = Blank() || Break(); + static const RegEx e = Blank() | Break(); return e; } inline const RegEx& Digit() { @@ -49,29 +49,29 @@ inline const RegEx& Digit() { return e; } inline const RegEx& Alpha() { - static const RegEx e = RegEx('a', 'z') || RegEx('A', 'Z'); + static const RegEx e = RegEx('a', 'z') | RegEx('A', 'Z'); return e; } inline const RegEx& AlphaNumeric() { - static const RegEx e = Alpha() || Digit(); + static const RegEx e = Alpha() | Digit(); return e; } inline const RegEx& Word() { - static const RegEx e = AlphaNumeric() || RegEx('-'); + static const RegEx e = AlphaNumeric() | RegEx('-'); return e; } inline const RegEx& Hex() { - static const RegEx e = Digit() || RegEx('A', 'F') || RegEx('a', 'f'); + static const RegEx e = Digit() | RegEx('A', 'F') | RegEx('a', 'f'); return e; } // Valid Unicode code points that are not part of c-printable (YAML 1.2, sec. // 5.1) inline const RegEx& NotPrintable() { static const RegEx e = - RegEx(0) || - RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) || - RegEx(0x0E, 0x1F) || - (RegEx('\xC2') + (RegEx('\x80', '\x84') || RegEx('\x86', '\x9F'))); + RegEx(0) | + RegEx("\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x7F", REGEX_OR) | + RegEx(0x0E, 0x1F) | + (RegEx('\xC2') + (RegEx('\x80', '\x84') | RegEx('\x86', '\x9F'))); return e; } inline const RegEx& Utf8_ByteOrderMark() { @@ -82,19 +82,19 @@ inline const RegEx& Utf8_ByteOrderMark() { // actual tags inline const RegEx& DocStart() { - static const RegEx e = RegEx("---") + (BlankOrBreak() || RegEx()); + static const RegEx e = RegEx("---") + (BlankOrBreak() | RegEx()); return e; } inline const RegEx& DocEnd() { - static const RegEx e = RegEx("...") + (BlankOrBreak() || RegEx()); + static const RegEx e = RegEx("...") + (BlankOrBreak() | RegEx()); return e; } inline const RegEx& DocIndicator() { - static const RegEx e = DocStart() || DocEnd(); + static const RegEx e = DocStart() | DocEnd(); return e; } inline const RegEx& BlockEntry() { - static const RegEx e = RegEx('-') + (BlankOrBreak() || RegEx()); + static const RegEx e = RegEx('-') + (BlankOrBreak() | RegEx()); return e; } inline const RegEx& Key() { @@ -106,36 +106,40 @@ inline const RegEx& KeyInFlow() { return e; } inline const RegEx& Value() { - static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx()); + static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx()); return e; } inline const RegEx& ValueInFlow() { - static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx(",}", REGEX_OR)); + static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx(",]}", REGEX_OR)); return e; } inline const RegEx& ValueInJSONFlow() { static const RegEx e = RegEx(':'); return e; } +inline const RegEx& Ampersand() { + static const RegEx e = RegEx('&'); + return e; +} inline const RegEx Comment() { static const RegEx e = RegEx('#'); return e; } inline const RegEx& Anchor() { - static const RegEx e = !(RegEx("[]{},", REGEX_OR) || BlankOrBreak()); + static const RegEx e = !(RegEx("[]{},", REGEX_OR) | BlankOrBreak()); return e; } inline const RegEx& AnchorEnd() { - static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) || BlankOrBreak(); + static const RegEx e = RegEx("?:,]}%@`", REGEX_OR) | BlankOrBreak(); return e; } inline const RegEx& URI() { - static const RegEx e = Word() || RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) || + static const RegEx e = Word() | RegEx("#;/?:@&=+$,_.!~*'()[]", REGEX_OR) | (RegEx('%') + Hex() + Hex()); return e; } inline const RegEx& Tag() { - static const RegEx e = Word() || RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) || + static const RegEx e = Word() | RegEx("#;/?:@&=+$_.~*'()", REGEX_OR) | (RegEx('%') + Hex() + Hex()); return e; } @@ -148,34 +152,34 @@ inline const RegEx& Tag() { // space. inline const RegEx& PlainScalar() { static const RegEx e = - !(BlankOrBreak() || RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) || - (RegEx("-?:", REGEX_OR) + (BlankOrBreak() || RegEx()))); + !(BlankOrBreak() | RegEx(",[]{}#&*!|>\'\"%@`", REGEX_OR) | + (RegEx("-?:", REGEX_OR) + (BlankOrBreak() | RegEx()))); return e; } inline const RegEx& PlainScalarInFlow() { static const RegEx e = - !(BlankOrBreak() || RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) || - (RegEx("-:", REGEX_OR) + Blank())); + !(BlankOrBreak() | RegEx("?,[]{}#&*!|>\'\"%@`", REGEX_OR) | + (RegEx("-:", REGEX_OR) + (Blank() | RegEx()))); return e; } inline const RegEx& EndScalar() { - static const RegEx e = RegEx(':') + (BlankOrBreak() || RegEx()); + static const RegEx e = RegEx(':') + (BlankOrBreak() | RegEx()); return e; } inline const RegEx& EndScalarInFlow() { static const RegEx e = - (RegEx(':') + (BlankOrBreak() || RegEx() || RegEx(",]}", REGEX_OR))) || + (RegEx(':') + (BlankOrBreak() | RegEx() | RegEx(",]}", REGEX_OR))) | RegEx(",?[]{}", REGEX_OR); return e; } inline const RegEx& ScanScalarEndInFlow() { - static const RegEx e = (EndScalarInFlow() || (BlankOrBreak() + Comment())); + static const RegEx e = (EndScalarInFlow() | (BlankOrBreak() + Comment())); return e; } inline const RegEx& ScanScalarEnd() { - static const RegEx e = EndScalar() || (BlankOrBreak() + Comment()); + static const RegEx e = EndScalar() | (BlankOrBreak() + Comment()); return e; } inline const RegEx& EscSingleQuote() { @@ -192,8 +196,8 @@ inline const RegEx& ChompIndicator() { return e; } inline const RegEx& Chomp() { - static const RegEx e = (ChompIndicator() + Digit()) || - (Digit() + ChompIndicator()) || ChompIndicator() || + static const RegEx e = (ChompIndicator() + Digit()) | + (Digit() + ChompIndicator()) | ChompIndicator() | Digit(); return e; } diff --git a/src/libs/3rdparty/yaml-cpp/src/memory.cpp b/src/libs/3rdparty/yaml-cpp/src/memory.cpp index e5f8a9d3f8c..676e4c7f152 100644 --- a/src/libs/3rdparty/yaml-cpp/src/memory.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/memory.cpp @@ -22,5 +22,5 @@ node& memory::create_node() { void memory::merge(const memory& rhs) { m_nodes.insert(rhs.m_nodes.begin(), rhs.m_nodes.end()); } -} -} +} // namespace detail +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/node.cpp b/src/libs/3rdparty/yaml-cpp/src/node.cpp index 2088e13c9ae..badc3110ecc 100644 --- a/src/libs/3rdparty/yaml-cpp/src/node.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/node.cpp @@ -9,4 +9,4 @@ Node Clone(const Node& node) { events.Emit(builder); return builder.Root(); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/node_data.cpp b/src/libs/3rdparty/yaml-cpp/src/node_data.cpp index 77cd4657806..8f5422ae6e1 100644 --- a/src/libs/3rdparty/yaml-cpp/src/node_data.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/node_data.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include #include @@ -12,15 +13,24 @@ namespace YAML { namespace detail { +YAML_CPP_API std::atomic node::m_amount{0}; -std::string node_data::empty_scalar; +const std::string& node_data::empty_scalar() { + static const std::string svalue; + return svalue; +} node_data::node_data() : m_isDefined(false), m_mark(Mark::null_mark()), m_type(NodeType::Null), + m_tag{}, m_style(EmitterStyle::Default), - m_seqSize(0) {} + m_scalar{}, + m_sequence{}, + m_seqSize(0), + m_map{}, + m_undefinedPairs{} {} void node_data::mark_defined() { if (m_type == NodeType::Undefined) @@ -100,9 +110,9 @@ void node_data::compute_seq_size() const { } void node_data::compute_map_size() const { - kv_pairs::iterator it = m_undefinedPairs.begin(); + auto it = m_undefinedPairs.begin(); while (it != m_undefinedPairs.end()) { - kv_pairs::iterator jt = std::next(it); + auto jt = std::next(it); if (it->first->is_defined() && it->second->is_defined()) m_undefinedPairs.erase(it); it = jt; @@ -111,7 +121,7 @@ void node_data::compute_map_size() const { const_node_iterator node_data::begin() const { if (!m_isDefined) - return const_node_iterator(); + return {}; switch (m_type) { case NodeType::Sequence: @@ -119,13 +129,13 @@ const_node_iterator node_data::begin() const { case NodeType::Map: return const_node_iterator(m_map.begin(), m_map.end()); default: - return const_node_iterator(); + return {}; } } node_iterator node_data::begin() { if (!m_isDefined) - return node_iterator(); + return {}; switch (m_type) { case NodeType::Sequence: @@ -133,13 +143,13 @@ node_iterator node_data::begin() { case NodeType::Map: return node_iterator(m_map.begin(), m_map.end()); default: - return node_iterator(); + return {}; } } const_node_iterator node_data::end() const { if (!m_isDefined) - return const_node_iterator(); + return {}; switch (m_type) { case NodeType::Sequence: @@ -147,13 +157,13 @@ const_node_iterator node_data::end() const { case NodeType::Map: return const_node_iterator(m_map.end(), m_map.end()); default: - return const_node_iterator(); + return {}; } } node_iterator node_data::end() { if (!m_isDefined) - return node_iterator(); + return {}; switch (m_type) { case NodeType::Sequence: @@ -161,12 +171,13 @@ node_iterator node_data::end() { case NodeType::Map: return node_iterator(m_map.end(), m_map.end()); default: - return node_iterator(); + return {}; } } // sequence -void node_data::push_back(node& node, shared_memory_holder /* pMemory */) { +void node_data::push_back(node& node, + const shared_memory_holder& /* pMemory */) { if (m_type == NodeType::Undefined || m_type == NodeType::Null) { m_type = NodeType::Sequence; reset_sequence(); @@ -178,7 +189,8 @@ void node_data::push_back(node& node, shared_memory_holder /* pMemory */) { m_sequence.push_back(&node); } -void node_data::insert(node& key, node& value, shared_memory_holder pMemory) { +void node_data::insert(node& key, node& value, + const shared_memory_holder& pMemory) { switch (m_type) { case NodeType::Map: break; @@ -188,27 +200,28 @@ void node_data::insert(node& key, node& value, shared_memory_holder pMemory) { convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(); + throw BadSubscript(m_mark, key); } insert_map_pair(key, value); } // indexing -node* node_data::get(node& key, shared_memory_holder /* pMemory */) const { +node* node_data::get(node& key, + const shared_memory_holder& /* pMemory */) const { if (m_type != NodeType::Map) { - return NULL; + return nullptr; } - for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->is(key)) - return it->second; + for (const auto& it : m_map) { + if (it.first->is(key)) + return it.second; } - return NULL; + return nullptr; } -node& node_data::get(node& key, shared_memory_holder pMemory) { +node& node_data::get(node& key, const shared_memory_holder& pMemory) { switch (m_type) { case NodeType::Map: break; @@ -218,12 +231,12 @@ node& node_data::get(node& key, shared_memory_holder pMemory) { convert_to_map(pMemory); break; case NodeType::Scalar: - throw BadSubscript(); + throw BadSubscript(m_mark, key); } - for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->is(key)) - return *it->second; + for (const auto& it : m_map) { + if (it.first->is(key)) + return *it.second; } node& value = pMemory->create_node(); @@ -231,15 +244,26 @@ node& node_data::get(node& key, shared_memory_holder pMemory) { return value; } -bool node_data::remove(node& key, shared_memory_holder /* pMemory */) { +bool node_data::remove(node& key, const shared_memory_holder& /* pMemory */) { if (m_type != NodeType::Map) return false; - for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) { - if (it->first->is(key)) { - m_map.erase(it); - return true; - } + for (auto it = m_undefinedPairs.begin(); it != m_undefinedPairs.end();) { + auto jt = std::next(it); + if (it->first->is(key)) + m_undefinedPairs.erase(it); + it = jt; + } + + auto it = + std::find_if(m_map.begin(), m_map.end(), + [&](std::pair j) { + return (j.first->is(key)); + }); + + if (it != m_map.end()) { + m_map.erase(it); + return true; } return false; @@ -262,7 +286,7 @@ void node_data::insert_map_pair(node& key, node& value) { m_undefinedPairs.emplace_back(&key, &value); } -void node_data::convert_to_map(shared_memory_holder pMemory) { +void node_data::convert_to_map(const shared_memory_holder& pMemory) { switch (m_type) { case NodeType::Undefined: case NodeType::Null: @@ -280,7 +304,7 @@ void node_data::convert_to_map(shared_memory_holder pMemory) { } } -void node_data::convert_sequence_to_map(shared_memory_holder pMemory) { +void node_data::convert_sequence_to_map(const shared_memory_holder& pMemory) { assert(m_type == NodeType::Sequence); reset_map(); @@ -296,5 +320,5 @@ void node_data::convert_sequence_to_map(shared_memory_holder pMemory) { reset_sequence(); m_type = NodeType::Map; } -} -} +} // namespace detail +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp index 093d2efeb77..bbaefac8a60 100644 --- a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.cpp @@ -1,4 +1,3 @@ -#include #include #include "nodebuilder.h" @@ -11,11 +10,16 @@ namespace YAML { struct Mark; NodeBuilder::NodeBuilder() - : m_pMemory(new detail::memory_holder), m_pRoot(0), m_mapDepth(0) { - m_anchors.push_back(0); // since the anchors start at 1 + : m_pMemory(new detail::memory_holder), + m_pRoot(nullptr), + m_stack{}, + m_anchors{}, + m_keys{}, + m_mapDepth(0) { + m_anchors.push_back(nullptr); // since the anchors start at 1 } -NodeBuilder::~NodeBuilder() {} +NodeBuilder::~NodeBuilder() = default; Node NodeBuilder::Root() { if (!m_pRoot) @@ -88,7 +92,7 @@ void NodeBuilder::Push(detail::node& node) { m_stack.push_back(&node); if (needsKey) - m_keys.push_back(PushedKey(&node, false)); + m_keys.emplace_back(&node, false); } void NodeBuilder::Pop() { @@ -127,4 +131,4 @@ void NodeBuilder::RegisterAnchor(anchor_t anchor, detail::node& node) { m_anchors.push_back(&node); } } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h index a6a47f007bb..c580d40e29b 100644 --- a/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h +++ b/src/libs/3rdparty/yaml-cpp/src/nodebuilder.h @@ -27,25 +27,29 @@ class Node; class NodeBuilder : public EventHandler { public: NodeBuilder(); - virtual ~NodeBuilder(); + NodeBuilder(const NodeBuilder&) = delete; + NodeBuilder(NodeBuilder&&) = delete; + NodeBuilder& operator=(const NodeBuilder&) = delete; + NodeBuilder& operator=(NodeBuilder&&) = delete; + ~NodeBuilder() override; Node Root(); - virtual void OnDocumentStart(const Mark& mark); - virtual void OnDocumentEnd(); + void OnDocumentStart(const Mark& mark) override; + void OnDocumentEnd() override; - virtual void OnNull(const Mark& mark, anchor_t anchor); - virtual void OnAlias(const Mark& mark, anchor_t anchor); - virtual void OnScalar(const Mark& mark, const std::string& tag, - anchor_t anchor, const std::string& value); + void OnNull(const Mark& mark, anchor_t anchor) override; + void OnAlias(const Mark& mark, anchor_t anchor) override; + void OnScalar(const Mark& mark, const std::string& tag, + anchor_t anchor, const std::string& value) override; - virtual void OnSequenceStart(const Mark& mark, const std::string& tag, - anchor_t anchor, EmitterStyle::value style); - virtual void OnSequenceEnd(); + void OnSequenceStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) override; + void OnSequenceEnd() override; - virtual void OnMapStart(const Mark& mark, const std::string& tag, - anchor_t anchor, EmitterStyle::value style); - virtual void OnMapEnd(); + void OnMapStart(const Mark& mark, const std::string& tag, + anchor_t anchor, EmitterStyle::value style) override; + void OnMapEnd() override; private: detail::node& Push(const Mark& mark, anchor_t anchor); @@ -57,14 +61,14 @@ class NodeBuilder : public EventHandler { detail::shared_memory_holder m_pMemory; detail::node* m_pRoot; - typedef std::vector Nodes; + using Nodes = std::vector; Nodes m_stack; Nodes m_anchors; - typedef std::pair PushedKey; + using PushedKey = std::pair; std::vector m_keys; std::size_t m_mapDepth; }; -} +} // namespace YAML #endif // NODE_NODEBUILDER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp b/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp index 82261feb058..b1774fef3e4 100644 --- a/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/nodeevents.cpp @@ -13,14 +13,14 @@ void NodeEvents::AliasManager::RegisterReference(const detail::node& node) { anchor_t NodeEvents::AliasManager::LookupAnchor( const detail::node& node) const { - AnchorByIdentity::const_iterator it = m_anchorByIdentity.find(node.ref()); + auto it = m_anchorByIdentity.find(node.ref()); if (it == m_anchorByIdentity.end()) return 0; return it->second; } NodeEvents::NodeEvents(const Node& node) - : m_pMemory(node.m_pMemory), m_root(node.m_pNode) { + : m_pMemory(node.m_pMemory), m_root(node.m_pNode), m_refCount{} { if (m_root) Setup(*m_root); } @@ -32,13 +32,12 @@ void NodeEvents::Setup(const detail::node& node) { return; if (node.type() == NodeType::Sequence) { - for (detail::const_node_iterator it = node.begin(); it != node.end(); ++it) - Setup(**it); + for (auto element : node) + Setup(*element); } else if (node.type() == NodeType::Map) { - for (detail::const_node_iterator it = node.begin(); it != node.end(); - ++it) { - Setup(*it->first); - Setup(*it->second); + for (auto element : node) { + Setup(*element.first); + Setup(*element.second); } } } @@ -77,17 +76,15 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler, break; case NodeType::Sequence: handler.OnSequenceStart(Mark(), node.tag(), anchor, node.style()); - for (detail::const_node_iterator it = node.begin(); it != node.end(); - ++it) - Emit(**it, handler, am); + for (auto element : node) + Emit(*element, handler, am); handler.OnSequenceEnd(); break; case NodeType::Map: handler.OnMapStart(Mark(), node.tag(), anchor, node.style()); - for (detail::const_node_iterator it = node.begin(); it != node.end(); - ++it) { - Emit(*it->first, handler, am); - Emit(*it->second, handler, am); + for (auto element : node) { + Emit(*element.first, handler, am); + Emit(*element.second, handler, am); } handler.OnMapEnd(); break; @@ -95,7 +92,7 @@ void NodeEvents::Emit(const detail::node& node, EventHandler& handler, } bool NodeEvents::IsAliased(const detail::node& node) const { - RefCount::const_iterator it = m_refCount.find(node.ref()); + auto it = m_refCount.find(node.ref()); return it != m_refCount.end() && it->second > 1; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/nodeevents.h b/src/libs/3rdparty/yaml-cpp/src/nodeevents.h index 49c18eb854a..efca9149ed3 100644 --- a/src/libs/3rdparty/yaml-cpp/src/nodeevents.h +++ b/src/libs/3rdparty/yaml-cpp/src/nodeevents.h @@ -26,13 +26,17 @@ class Node; class NodeEvents { public: explicit NodeEvents(const Node& node); + NodeEvents(const NodeEvents&) = delete; + NodeEvents(NodeEvents&&) = delete; + NodeEvents& operator=(const NodeEvents&) = delete; + NodeEvents& operator=(NodeEvents&&) = delete; void Emit(EventHandler& handler); private: class AliasManager { public: - AliasManager() : m_curAnchor(0) {} + AliasManager() : m_anchorByIdentity{}, m_curAnchor(0) {} void RegisterReference(const detail::node& node); anchor_t LookupAnchor(const detail::node& node) const; @@ -41,7 +45,7 @@ class NodeEvents { anchor_t _CreateNewAnchor() { return ++m_curAnchor; } private: - typedef std::map AnchorByIdentity; + using AnchorByIdentity = std::map; AnchorByIdentity m_anchorByIdentity; anchor_t m_curAnchor; @@ -56,9 +60,9 @@ class NodeEvents { detail::shared_memory_holder m_pMemory; detail::node* m_root; - typedef std::map RefCount; + using RefCount = std::map; RefCount m_refCount; }; -} +} // namespace YAML #endif // NODE_NODEEVENTS_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/null.cpp b/src/libs/3rdparty/yaml-cpp/src/null.cpp index d12dd08ce4b..db7daebf1d2 100644 --- a/src/libs/3rdparty/yaml-cpp/src/null.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/null.cpp @@ -7,4 +7,4 @@ bool IsNullString(const std::string& str) { return str.empty() || str == "~" || str == "null" || str == "Null" || str == "NULL"; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp b/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp index 357fc0094c4..047a9f7c8ea 100644 --- a/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/ostream_wrapper.cpp @@ -7,16 +7,21 @@ namespace YAML { ostream_wrapper::ostream_wrapper() : m_buffer(1, '\0'), - m_pStream(0), + m_pStream(nullptr), m_pos(0), m_row(0), m_col(0), m_comment(false) {} ostream_wrapper::ostream_wrapper(std::ostream& stream) - : m_pStream(&stream), m_pos(0), m_row(0), m_col(0), m_comment(false) {} + : m_buffer{}, + m_pStream(&stream), + m_pos(0), + m_row(0), + m_col(0), + m_comment(false) {} -ostream_wrapper::~ostream_wrapper() {} +ostream_wrapper::~ostream_wrapper() = default; void ostream_wrapper::write(const std::string& str) { if (m_pStream) { @@ -26,8 +31,8 @@ void ostream_wrapper::write(const std::string& str) { std::copy(str.begin(), str.end(), m_buffer.begin() + m_pos); } - for (std::size_t i = 0; i < str.size(); i++) { - update_pos(str[i]); + for (char ch : str) { + update_pos(ch); } } @@ -54,4 +59,4 @@ void ostream_wrapper::update_pos(char ch) { m_comment = false; } } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/parse.cpp b/src/libs/3rdparty/yaml-cpp/src/parse.cpp index 0b2ae4a4f6e..262536b85a4 100644 --- a/src/libs/3rdparty/yaml-cpp/src/parse.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/parse.cpp @@ -3,10 +3,10 @@ #include #include -#include "yaml-cpp/node/node.h" -#include "yaml-cpp/node/impl.h" -#include "yaml-cpp/parser.h" #include "nodebuilder.h" +#include "yaml-cpp/node/impl.h" +#include "yaml-cpp/node/node.h" +#include "yaml-cpp/parser.h" namespace YAML { Node Load(const std::string& input) { @@ -30,9 +30,9 @@ Node Load(std::istream& input) { } Node LoadFile(const std::string& filename) { - std::ifstream fin(filename.c_str()); + std::ifstream fin(filename); if (!fin) { - throw BadFile(); + throw BadFile(filename); } return Load(fin); } @@ -51,7 +51,7 @@ std::vector LoadAll(std::istream& input) { std::vector docs; Parser parser(input); - while (1) { + while (true) { NodeBuilder builder; if (!parser.HandleNextDocument(builder)) { break; @@ -63,9 +63,9 @@ std::vector LoadAll(std::istream& input) { } std::vector LoadAllFromFile(const std::string& filename) { - std::ifstream fin(filename.c_str()); + std::ifstream fin(filename); if (!fin) { - throw BadFile(); + throw BadFile(filename); } return LoadAll(fin); } diff --git a/src/libs/3rdparty/yaml-cpp/src/parser.cpp b/src/libs/3rdparty/yaml-cpp/src/parser.cpp index cd69f39fcec..b8b78ebabc5 100644 --- a/src/libs/3rdparty/yaml-cpp/src/parser.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/parser.cpp @@ -11,15 +11,13 @@ namespace YAML { class EventHandler; -Parser::Parser() {} +Parser::Parser() : m_pScanner{}, m_pDirectives{} {} -Parser::Parser(std::istream& in) { Load(in); } +Parser::Parser(std::istream& in) : Parser() { Load(in); } -Parser::~Parser() {} +Parser::~Parser() = default; -Parser::operator bool() const { - return m_pScanner.get() && !m_pScanner->empty(); -} +Parser::operator bool() const { return m_pScanner && !m_pScanner->empty(); } void Parser::Load(std::istream& in) { m_pScanner.reset(new Scanner(in)); @@ -27,7 +25,7 @@ void Parser::Load(std::istream& in) { } bool Parser::HandleNextDocument(EventHandler& eventHandler) { - if (!m_pScanner.get()) + if (!m_pScanner) return false; ParseDirectives(); @@ -43,11 +41,7 @@ bool Parser::HandleNextDocument(EventHandler& eventHandler) { void Parser::ParseDirectives() { bool readDirective = false; - while (1) { - if (m_pScanner->empty()) { - break; - } - + while (!m_pScanner->empty()) { Token& token = m_pScanner->peek(); if (token.type != Token::DIRECTIVE) { break; @@ -113,17 +107,13 @@ void Parser::HandleTagDirective(const Token& token) { } void Parser::PrintTokens(std::ostream& out) { - if (!m_pScanner.get()) { + if (!m_pScanner) { return; } - while (1) { - if (m_pScanner->empty()) { - break; - } - + while (!m_pScanner->empty()) { out << m_pScanner->peek() << "\n"; m_pScanner->pop(); } } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h b/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h index 955aebd8d56..d58de04cb62 100644 --- a/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h +++ b/src/libs/3rdparty/yaml-cpp/src/ptr_vector.h @@ -12,15 +12,17 @@ #include #include -#include "yaml-cpp/noncopyable.h" - namespace YAML { // TODO: This class is no longer needed template -class ptr_vector : private YAML::noncopyable { +class ptr_vector { public: - ptr_vector() {} + ptr_vector() : m_data{} {} + ptr_vector(const ptr_vector&) = delete; + ptr_vector(ptr_vector&&) = default; + ptr_vector& operator=(const ptr_vector&) = delete; + ptr_vector& operator=(ptr_vector&&) = default; void clear() { m_data.clear(); } @@ -38,6 +40,6 @@ class ptr_vector : private YAML::noncopyable { private: std::vector> m_data; }; -} +} // namespace YAML #endif // PTR_VECTOR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp index 20b772051d2..bf1784b41d5 100644 --- a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.cpp @@ -2,18 +2,16 @@ namespace YAML { // constructors -RegEx::RegEx() : m_op(REGEX_EMPTY) {} -RegEx::RegEx(REGEX_OP op) : m_op(op) {} +RegEx::RegEx(REGEX_OP op) : m_op(op), m_a(0), m_z(0), m_params{} {} +RegEx::RegEx() : RegEx(REGEX_EMPTY) {} -RegEx::RegEx(char ch) : m_op(REGEX_MATCH), m_a(ch) {} +RegEx::RegEx(char ch) : m_op(REGEX_MATCH), m_a(ch), m_z(0), m_params{} {} -RegEx::RegEx(char a, char z) : m_op(REGEX_RANGE), m_a(a), m_z(z) {} +RegEx::RegEx(char a, char z) : m_op(REGEX_RANGE), m_a(a), m_z(z), m_params{} {} -RegEx::RegEx(const std::string& str, REGEX_OP op) : m_op(op) { - for (std::size_t i = 0; i < str.size(); i++) - m_params.push_back(RegEx(str[i])); -} +RegEx::RegEx(const std::string& str, REGEX_OP op) + : m_op(op), m_a(0), m_z(0), m_params(str.begin(), str.end()) {} // combination constructors RegEx operator!(const RegEx& ex) { @@ -22,14 +20,14 @@ RegEx operator!(const RegEx& ex) { return ret; } -RegEx operator||(const RegEx& ex1, const RegEx& ex2) { +RegEx operator|(const RegEx& ex1, const RegEx& ex2) { RegEx ret(REGEX_OR); ret.m_params.push_back(ex1); ret.m_params.push_back(ex2); return ret; } -RegEx operator&&(const RegEx& ex1, const RegEx& ex2) { +RegEx operator&(const RegEx& ex1, const RegEx& ex2) { RegEx ret(REGEX_AND); ret.m_params.push_back(ex1); ret.m_params.push_back(ex2); @@ -42,4 +40,4 @@ RegEx operator+(const RegEx& ex1, const RegEx& ex2) { ret.m_params.push_back(ex2); return ret; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h index 8f28b852a29..c70ab60dcc1 100644 --- a/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h +++ b/src/libs/3rdparty/yaml-cpp/src/regex_yaml.h @@ -31,14 +31,14 @@ enum REGEX_OP { class YAML_CPP_API RegEx { public: RegEx(); - RegEx(char ch); + explicit RegEx(char ch); RegEx(char a, char z); RegEx(const std::string& str, REGEX_OP op = REGEX_SEQ); - ~RegEx() {} + ~RegEx() = default; friend YAML_CPP_API RegEx operator!(const RegEx& ex); - friend YAML_CPP_API RegEx operator||(const RegEx& ex1, const RegEx& ex2); - friend YAML_CPP_API RegEx operator&&(const RegEx& ex1, const RegEx& ex2); + friend YAML_CPP_API RegEx operator|(const RegEx& ex1, const RegEx& ex2); + friend YAML_CPP_API RegEx operator&(const RegEx& ex1, const RegEx& ex2); friend YAML_CPP_API RegEx operator+(const RegEx& ex1, const RegEx& ex2); bool Matches(char ch) const; @@ -53,7 +53,7 @@ class YAML_CPP_API RegEx { int Match(const Source& source) const; private: - RegEx(REGEX_OP op); + explicit RegEx(REGEX_OP op); template bool IsValidSource(const Source& source) const; @@ -77,10 +77,11 @@ class YAML_CPP_API RegEx { private: REGEX_OP m_op; - char m_a, m_z; + char m_a{}; + char m_z{}; std::vector m_params; }; -} +} // namespace YAML #include "regeximpl.h" diff --git a/src/libs/3rdparty/yaml-cpp/src/regeximpl.h b/src/libs/3rdparty/yaml-cpp/src/regeximpl.h index 709124f0088..a742cdc3050 100644 --- a/src/libs/3rdparty/yaml-cpp/src/regeximpl.h +++ b/src/libs/3rdparty/yaml-cpp/src/regeximpl.h @@ -8,8 +8,8 @@ #endif #include "stream.h" -#include "stringsource.h" #include "streamcharsource.h" +#include "stringsource.h" namespace YAML { // query matches @@ -106,9 +106,8 @@ inline int RegEx::MatchOpEmpty(const Source& source) const { template <> inline int RegEx::MatchOpEmpty( const StringCharSource& source) const { - return !source - ? 0 - : -1; // the empty regex only is successful on the empty string + return !source ? 0 : -1; // the empty regex only is successful on the empty + // string } // MatchOperator @@ -130,8 +129,8 @@ inline int RegEx::MatchOpRange(const Source& source) const { // OrOperator template inline int RegEx::MatchOpOr(const Source& source) const { - for (std::size_t i = 0; i < m_params.size(); i++) { - int n = m_params[i].MatchUnchecked(source); + for (const RegEx& param : m_params) { + int n = param.MatchUnchecked(source); if (n >= 0) return n; } @@ -169,11 +168,11 @@ inline int RegEx::MatchOpNot(const Source& source) const { template inline int RegEx::MatchOpSeq(const Source& source) const { int offset = 0; - for (std::size_t i = 0; i < m_params.size(); i++) { - int n = m_params[i].Match(source + offset); // note Match, not - // MatchUnchecked because we - // need to check validity after - // the offset + for (const RegEx& param : m_params) { + int n = param.Match(source + offset); // note Match, not + // MatchUnchecked because we + // need to check validity after + // the offset if (n == -1) return -1; offset += n; @@ -181,6 +180,6 @@ inline int RegEx::MatchOpSeq(const Source& source) const { return offset; } -} +} // namespace YAML #endif // REGEXIMPL_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/scanner.cpp b/src/libs/3rdparty/yaml-cpp/src/scanner.cpp index b5cfcc12b22..ea5511a114c 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scanner.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/scanner.cpp @@ -9,12 +9,17 @@ namespace YAML { Scanner::Scanner(std::istream& in) : INPUT(in), + m_tokens{}, m_startedStream(false), m_endedStream(false), m_simpleKeyAllowed(false), - m_canBeJSONFlow(false) {} + m_canBeJSONFlow(false), + m_simpleKeys{}, + m_indents{}, + m_indentRefs{}, + m_flows{} {} -Scanner::~Scanner() {} +Scanner::~Scanner() = default; bool Scanner::empty() { EnsureTokensInQueue(); @@ -46,7 +51,7 @@ Token& Scanner::peek() { Mark Scanner::mark() const { return INPUT.mark(); } void Scanner::EnsureTokensInQueue() { - while (1) { + while (true) { if (!m_tokens.empty()) { Token& token = m_tokens.front(); @@ -83,7 +88,7 @@ void Scanner::ScanNextToken() { return StartStream(); } - // get rid of whitespace, etc. (in between tokens it should be irrelevent) + // get rid of whitespace, etc. (in between tokens it should be irrelevant) ScanToNextToken(); // maybe need to end some blocks @@ -169,7 +174,7 @@ void Scanner::ScanNextToken() { } void Scanner::ScanToNextToken() { - while (1) { + while (true) { // first eat whitespace while (INPUT && IsWhitespaceToBeEaten(INPUT.peek())) { if (InBlockContext() && Exp::Tab().Matches(INPUT)) { @@ -282,7 +287,7 @@ Scanner::IndentMarker* Scanner::PushIndentTo(int column, IndentMarker::INDENT_TYPE type) { // are we in flow? if (InFlowContext()) { - return 0; + return nullptr; } std::unique_ptr pIndent(new IndentMarker(column, type)); @@ -291,12 +296,12 @@ Scanner::IndentMarker* Scanner::PushIndentTo(int column, // is this actually an indentation? if (indent.column < lastIndent.column) { - return 0; + return nullptr; } if (indent.column == lastIndent.column && !(indent.type == IndentMarker::SEQ && lastIndent.type == IndentMarker::MAP)) { - return 0; + return nullptr; } // push a start token diff --git a/src/libs/3rdparty/yaml-cpp/src/scanner.h b/src/libs/3rdparty/yaml-cpp/src/scanner.h index 7bb2ccc71a5..4af938e69c3 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scanner.h +++ b/src/libs/3rdparty/yaml-cpp/src/scanner.h @@ -9,9 +9,7 @@ #include #include -#include #include -#include #include #include @@ -49,7 +47,7 @@ class Scanner { enum INDENT_TYPE { MAP, SEQ, NONE }; enum STATUS { VALID, INVALID, UNKNOWN }; IndentMarker(int column_, INDENT_TYPE type_) - : column(column_), type(type_), status(VALID), pStartToken(0) {} + : column(column_), type(type_), status(VALID), pStartToken(nullptr) {} int column; INDENT_TYPE type; diff --git a/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp b/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp index 10e359d4466..be57b1cd5d5 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/scanscalar.cpp @@ -47,7 +47,8 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { if (INPUT.column() == 0 && Exp::DocIndicator().Matches(INPUT)) { if (params.onDocIndicator == BREAK) { break; - } else if (params.onDocIndicator == THROW) { + } + if (params.onDocIndicator == THROW) { throw ParserException(INPUT.mark(), ErrorMsg::DOC_IN_SCALAR); } } @@ -183,7 +184,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { case FOLD_FLOW: if (nextEmptyLine) { scalar += "\n"; - } else if (!emptyLine && !nextEmptyLine && !escapedNewline) { + } else if (!emptyLine && !escapedNewline) { scalar += " "; } break; @@ -203,7 +204,7 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { // post-processing if (params.trimTrailingSpaces) { - std::size_t pos = scalar.find_last_not_of(' '); + std::size_t pos = scalar.find_last_not_of(" \t"); if (lastEscapedChar != std::string::npos) { if (pos < lastEscapedChar || pos == std::string::npos) { pos = lastEscapedChar; @@ -247,4 +248,4 @@ std::string ScanScalar(Stream& INPUT, ScanScalarParams& params) { return scalar; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/scanscalar.h b/src/libs/3rdparty/yaml-cpp/src/scanscalar.h index c3a574ad9b6..296b885a515 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scanscalar.h +++ b/src/libs/3rdparty/yaml-cpp/src/scanscalar.h @@ -57,7 +57,7 @@ struct ScanScalarParams { bool leadingSpaces; }; -std::string ScanScalar(Stream& INPUT, ScanScalarParams& info); +std::string ScanScalar(Stream& INPUT, ScanScalarParams& params); } #endif // SCANSCALAR_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/scantag.cpp b/src/libs/3rdparty/yaml-cpp/src/scantag.cpp index c5b39652ad0..176cc5c711c 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scantag.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/scantag.cpp @@ -78,4 +78,4 @@ const std::string ScanTagSuffix(Stream& INPUT) { return tag; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp b/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp index fd8758d7815..1a94ab1d7d0 100644 --- a/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/scantoken.cpp @@ -37,7 +37,7 @@ void Scanner::ScanDirective() { token.value += INPUT.get(); // read parameters - while (1) { + while (true) { // first get rid of whitespace while (Exp::Blank().Matches(INPUT)) INPUT.eat(1); @@ -171,7 +171,7 @@ void Scanner::ScanBlockEntry() { // Key void Scanner::ScanKey() { - // handle keys diffently in the block context (and manage indents) + // handle keys differently in the block context (and manage indents) if (InBlockContext()) { if (!m_simpleKeyAllowed) throw ParserException(INPUT.mark(), ErrorMsg::MAP_KEY); @@ -199,7 +199,7 @@ void Scanner::ScanValue() { // seems fine) m_simpleKeyAllowed = false; } else { - // handle values diffently in the block context (and manage indents) + // handle values differently in the block context (and manage indents) if (InBlockContext()) { if (!m_simpleKeyAllowed) throw ParserException(INPUT.mark(), ErrorMsg::MAP_VALUE); @@ -338,7 +338,7 @@ void Scanner::ScanQuotedScalar() { // setup the scanning parameters ScanScalarParams params; - RegEx end = (single ? RegEx(quote) && !Exp::EscSingleQuote() : RegEx(quote)); + RegEx end = (single ? RegEx(quote) & !Exp::EscSingleQuote() : RegEx(quote)); params.end = &end; params.eatEnd = true; params.escape = (single ? '\'' : '\\'); @@ -434,4 +434,4 @@ void Scanner::ScanBlockScalar() { token.value = scalar; m_tokens.push(token); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/setting.h b/src/libs/3rdparty/yaml-cpp/src/setting.h index b78d40e2e85..4960bbf75c4 100644 --- a/src/libs/3rdparty/yaml-cpp/src/setting.h +++ b/src/libs/3rdparty/yaml-cpp/src/setting.h @@ -7,17 +7,24 @@ #pragma once #endif +#include "yaml-cpp/noexcept.h" #include +#include #include -#include "yaml-cpp/noncopyable.h" namespace YAML { -class SettingChangeBase; + +class SettingChangeBase { + public: + virtual ~SettingChangeBase() = default; + virtual void pop() = 0; +}; template class Setting { public: Setting() : m_value() {} + Setting(const T& value) : m_value() { set(value); } const T get() const { return m_value; } std::unique_ptr set(const T& value); @@ -27,21 +34,19 @@ class Setting { T m_value; }; -class SettingChangeBase { - public: - virtual ~SettingChangeBase() {} - virtual void pop() = 0; -}; - template class SettingChange : public SettingChangeBase { public: - SettingChange(Setting* pSetting) : m_pCurSetting(pSetting) { - // copy old setting to save its state - m_oldSetting = *pSetting; - } + SettingChange(Setting* pSetting) + : m_pCurSetting(pSetting), + m_oldSetting(*pSetting) // copy old setting to save its state + {} + SettingChange(const SettingChange&) = delete; + SettingChange(SettingChange&&) = delete; + SettingChange& operator=(const SettingChange&) = delete; + SettingChange& operator=(SettingChange&&) = delete; - virtual void pop() { m_pCurSetting->restore(m_oldSetting); } + void pop() override { m_pCurSetting->restore(m_oldSetting); } private: Setting* m_pCurSetting; @@ -55,28 +60,13 @@ inline std::unique_ptr Setting::set(const T& value) { return pChange; } -class SettingChanges : private noncopyable { +class SettingChanges { public: - SettingChanges() {} - ~SettingChanges() { clear(); } - - void clear() { - restore(); - m_settingChanges.clear(); - } - - void restore() { - for (setting_changes::const_iterator it = m_settingChanges.begin(); - it != m_settingChanges.end(); ++it) - (*it)->pop(); - } - - void push(std::unique_ptr pSettingChange) { - m_settingChanges.push_back(std::move(pSettingChange)); - } - - // like std::unique_ptr - assignment is transfer of ownership - SettingChanges& operator=(SettingChanges&& rhs) { + SettingChanges() : m_settingChanges{} {} + SettingChanges(const SettingChanges&) = delete; + SettingChanges(SettingChanges&&) YAML_CPP_NOEXCEPT = default; + SettingChanges& operator=(const SettingChanges&) = delete; + SettingChanges& operator=(SettingChanges&& rhs) YAML_CPP_NOEXCEPT { if (this == &rhs) return *this; @@ -85,11 +75,26 @@ class SettingChanges : private noncopyable { return *this; } + ~SettingChanges() { clear(); } + + void clear() YAML_CPP_NOEXCEPT { + restore(); + m_settingChanges.clear(); + } + + void restore() YAML_CPP_NOEXCEPT { + for (const auto& setting : m_settingChanges) + setting->pop(); + } + + void push(std::unique_ptr pSettingChange) { + m_settingChanges.push_back(std::move(pSettingChange)); + } private: - typedef std::vector> setting_changes; + using setting_changes = std::vector>; setting_changes m_settingChanges; }; -} +} // namespace YAML #endif // SETTING_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp b/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp index 70f56b6ae42..67c2d712efe 100644 --- a/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/simplekey.cpp @@ -5,7 +5,11 @@ namespace YAML { struct Mark; Scanner::SimpleKey::SimpleKey(const Mark& mark_, std::size_t flowLevel_) - : mark(mark_), flowLevel(flowLevel_), pIndent(0), pMapStart(0), pKey(0) {} + : mark(mark_), + flowLevel(flowLevel_), + pIndent(nullptr), + pMapStart(nullptr), + pKey(nullptr) {} void Scanner::SimpleKey::Validate() { // Note: pIndent will *not* be garbage here; @@ -125,4 +129,4 @@ void Scanner::PopAllSimpleKeys() { while (!m_simpleKeys.empty()) m_simpleKeys.pop(); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp b/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp index a27c1c3b04d..22913d198c2 100644 --- a/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/singledocparser.cpp @@ -7,6 +7,7 @@ #include "singledocparser.h" #include "tag.h" #include "token.h" +#include "yaml-cpp/depthguard.h" #include "yaml-cpp/emitterstyle.h" #include "yaml-cpp/eventhandler.h" #include "yaml-cpp/exceptions.h" // IWYU pragma: keep @@ -18,9 +19,10 @@ SingleDocParser::SingleDocParser(Scanner& scanner, const Directives& directives) : m_scanner(scanner), m_directives(directives), m_pCollectionStack(new CollectionStack), + m_anchors{}, m_curAnchor(0) {} -SingleDocParser::~SingleDocParser() {} +SingleDocParser::~SingleDocParser() = default; // HandleDocument // . Handles the next document @@ -46,6 +48,8 @@ void SingleDocParser::HandleDocument(EventHandler& eventHandler) { } void SingleDocParser::HandleNode(EventHandler& eventHandler) { + DepthGuard<500> depthguard(depth, m_scanner.mark(), ErrorMsg::BAD_FILE); + // an empty node *is* a possibility if (m_scanner.empty()) { eventHandler.OnNull(m_scanner.mark(), NullAnchor); @@ -71,20 +75,31 @@ void SingleDocParser::HandleNode(EventHandler& eventHandler) { } std::string tag; + std::string anchor_name; anchor_t anchor; - ParseProperties(tag, anchor); + ParseProperties(tag, anchor, anchor_name); - const Token& token = m_scanner.peek(); + if (!anchor_name.empty()) + eventHandler.OnAnchor(mark, anchor_name); - if (token.type == Token::PLAIN_SCALAR && IsNullString(token.value)) { + // after parsing properties, an empty node is again a possibility + if (m_scanner.empty()) { eventHandler.OnNull(mark, anchor); - m_scanner.pop(); return; } + const Token& token = m_scanner.peek(); + // add non-specific tags if (tag.empty()) tag = (token.type == Token::NON_PLAIN_SCALAR ? "!" : "?"); + + if (token.type == Token::PLAIN_SCALAR + && tag.compare("?") == 0 && IsNullString(token.value)) { + eventHandler.OnNull(mark, anchor); + m_scanner.pop(); + return; + } // now split based on what kind of node we should be switch (token.type) { @@ -152,7 +167,7 @@ void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) { m_scanner.pop(); m_pCollectionStack->PushCollectionType(CollectionType::BlockSeq); - while (1) { + while (true) { if (m_scanner.empty()) throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ); @@ -166,10 +181,10 @@ void SingleDocParser::HandleBlockSequence(EventHandler& eventHandler) { // check for null if (!m_scanner.empty()) { - const Token& token = m_scanner.peek(); - if (token.type == Token::BLOCK_ENTRY || - token.type == Token::BLOCK_SEQ_END) { - eventHandler.OnNull(token.mark, NullAnchor); + const Token& nextToken = m_scanner.peek(); + if (nextToken.type == Token::BLOCK_ENTRY || + nextToken.type == Token::BLOCK_SEQ_END) { + eventHandler.OnNull(nextToken.mark, NullAnchor); continue; } } @@ -185,7 +200,7 @@ void SingleDocParser::HandleFlowSequence(EventHandler& eventHandler) { m_scanner.pop(); m_pCollectionStack->PushCollectionType(CollectionType::FlowSeq); - while (1) { + while (true) { if (m_scanner.empty()) throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_SEQ_FLOW); @@ -238,7 +253,7 @@ void SingleDocParser::HandleBlockMap(EventHandler& eventHandler) { m_scanner.pop(); m_pCollectionStack->PushCollectionType(CollectionType::BlockMap); - while (1) { + while (true) { if (m_scanner.empty()) throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP); @@ -277,7 +292,7 @@ void SingleDocParser::HandleFlowMap(EventHandler& eventHandler) { m_scanner.pop(); m_pCollectionStack->PushCollectionType(CollectionType::FlowMap); - while (1) { + while (true) { if (m_scanner.empty()) throw ParserException(m_scanner.mark(), ErrorMsg::END_OF_MAP_FLOW); @@ -356,11 +371,13 @@ void SingleDocParser::HandleCompactMapWithNoKey(EventHandler& eventHandler) { // ParseProperties // . Grabs any tag or anchor tokens and deals with them. -void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor) { +void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor, + std::string& anchor_name) { tag.clear(); + anchor_name.clear(); anchor = NullAnchor; - while (1) { + while (true) { if (m_scanner.empty()) return; @@ -369,7 +386,7 @@ void SingleDocParser::ParseProperties(std::string& tag, anchor_t& anchor) { ParseTag(tag); break; case Token::ANCHOR: - ParseAnchor(anchor); + ParseAnchor(anchor, anchor_name); break; default: return; @@ -387,11 +404,12 @@ void SingleDocParser::ParseTag(std::string& tag) { m_scanner.pop(); } -void SingleDocParser::ParseAnchor(anchor_t& anchor) { +void SingleDocParser::ParseAnchor(anchor_t& anchor, std::string& anchor_name) { Token& token = m_scanner.peek(); if (anchor) throw ParserException(token.mark, ErrorMsg::MULTIPLE_ANCHORS); + anchor_name = token.value; anchor = RegisterAnchor(token.value); m_scanner.pop(); } @@ -405,10 +423,13 @@ anchor_t SingleDocParser::RegisterAnchor(const std::string& name) { anchor_t SingleDocParser::LookupAnchor(const Mark& mark, const std::string& name) const { - Anchors::const_iterator it = m_anchors.find(name); - if (it == m_anchors.end()) - throw ParserException(mark, ErrorMsg::UNKNOWN_ANCHOR); + auto it = m_anchors.find(name); + if (it == m_anchors.end()) { + std::stringstream ss; + ss << ErrorMsg::UNKNOWN_ANCHOR << name; + throw ParserException(mark, ss.str()); + } return it->second; } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/singledocparser.h b/src/libs/3rdparty/yaml-cpp/src/singledocparser.h index 2b92067cddb..f484eb1f957 100644 --- a/src/libs/3rdparty/yaml-cpp/src/singledocparser.h +++ b/src/libs/3rdparty/yaml-cpp/src/singledocparser.h @@ -12,10 +12,10 @@ #include #include "yaml-cpp/anchor.h" -#include "yaml-cpp/noncopyable.h" namespace YAML { class CollectionStack; +template class DepthGuard; // depthguard.h class EventHandler; class Node; class Scanner; @@ -23,9 +23,13 @@ struct Directives; struct Mark; struct Token; -class SingleDocParser : private noncopyable { +class SingleDocParser { public: SingleDocParser(Scanner& scanner, const Directives& directives); + SingleDocParser(const SingleDocParser&) = delete; + SingleDocParser(SingleDocParser&&) = delete; + SingleDocParser& operator=(const SingleDocParser&) = delete; + SingleDocParser& operator=(SingleDocParser&&) = delete; ~SingleDocParser(); void HandleDocument(EventHandler& eventHandler); @@ -43,23 +47,25 @@ class SingleDocParser : private noncopyable { void HandleCompactMap(EventHandler& eventHandler); void HandleCompactMapWithNoKey(EventHandler& eventHandler); - void ParseProperties(std::string& tag, anchor_t& anchor); + void ParseProperties(std::string& tag, anchor_t& anchor, + std::string& anchor_name); void ParseTag(std::string& tag); - void ParseAnchor(anchor_t& anchor); + void ParseAnchor(anchor_t& anchor, std::string& anchor_name); anchor_t RegisterAnchor(const std::string& name); anchor_t LookupAnchor(const Mark& mark, const std::string& name) const; private: + int depth = 0; Scanner& m_scanner; const Directives& m_directives; std::unique_ptr m_pCollectionStack; - typedef std::map Anchors; + using Anchors = std::map; Anchors m_anchors; anchor_t m_curAnchor; }; -} +} // namespace YAML #endif // SINGLEDOCPARSER_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/stream.cpp b/src/libs/3rdparty/yaml-cpp/src/stream.cpp index 3b013cfa7d3..b1aa092f693 100644 --- a/src/libs/3rdparty/yaml-cpp/src/stream.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/stream.cpp @@ -111,24 +111,15 @@ static UtfIntroState s_introTransitions[][uictMax] = { static char s_introUngetCount[][uictMax] = { // uict00, uictBB, uictBF, uictEF, uictFE, uictFF, uictAscii, uictOther - {0, 1, 1, 0, 0, 0, 0, 1}, - {0, 2, 2, 2, 2, 2, 2, 2}, - {3, 3, 3, 3, 0, 3, 3, 3}, - {4, 4, 4, 4, 4, 0, 4, 4}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {2, 2, 2, 2, 2, 0, 2, 2}, - {2, 2, 2, 2, 0, 2, 2, 2}, - {0, 1, 1, 1, 1, 1, 1, 1}, - {0, 2, 2, 2, 2, 2, 2, 2}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 1, 1}, - {0, 2, 2, 2, 2, 2, 2, 2}, - {0, 3, 3, 3, 3, 3, 3, 3}, - {4, 4, 4, 4, 4, 4, 4, 4}, - {2, 0, 2, 2, 2, 2, 2, 2}, - {3, 3, 0, 3, 3, 3, 3, 3}, - {1, 1, 1, 1, 1, 1, 1, 1}, + {0, 1, 1, 0, 0, 0, 0, 1}, {0, 2, 2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 0, 3, 3, 3}, {4, 4, 4, 4, 4, 0, 4, 4}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 0, 2, 2}, {2, 2, 2, 2, 0, 2, 2, 2}, + {0, 1, 1, 1, 1, 1, 1, 1}, {0, 2, 2, 2, 2, 2, 2, 2}, + {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, + {0, 2, 2, 2, 2, 2, 2, 2}, {0, 3, 3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4, 4, 4}, {2, 0, 2, 2, 2, 2, 2, 2}, + {3, 3, 0, 3, 3, 3, 3, 3}, {1, 1, 1, 1, 1, 1, 1, 1}, }; inline UtfIntroCharType IntroCharTypeOf(std::istream::int_type ch) { @@ -160,7 +151,8 @@ inline UtfIntroCharType IntroCharTypeOf(std::istream::int_type ch) { inline char Utf8Adjust(unsigned long ch, unsigned char lead_bits, unsigned char rshift) { - const unsigned char header = ((1 << lead_bits) - 1) << (8 - lead_bits); + const unsigned char header = + static_cast(((1 << lead_bits) - 1) << (8 - lead_bits)); const unsigned char mask = (0xFF >> (lead_bits + 1)); return static_cast( static_cast(header | ((ch >> rshift) & mask))); @@ -192,17 +184,20 @@ inline void QueueUnicodeCodepoint(std::deque& q, unsigned long ch) { Stream::Stream(std::istream& input) : m_input(input), + m_mark{}, + m_charSet{}, + m_readahead{}, m_pPrefetched(new unsigned char[YAML_PREFETCH_SIZE]), m_nPrefetchedAvailable(0), m_nPrefetchedUsed(0) { - typedef std::istream::traits_type char_traits; + using char_traits = std::istream::traits_type; if (!input) return; // Determine (or guess) the character-set by reading the BOM, if any. See // the YAML specification for the determination algorithm. - char_traits::int_type intro[4]; + char_traits::int_type intro[4]{}; int nIntroUsed = 0; UtfIntroState state = uis_start; for (; !s_introFinalState[state];) { @@ -279,9 +274,11 @@ char Stream::get() { // . Extracts 'n' characters from the stream and updates our position std::string Stream::get(int n) { std::string ret; - ret.reserve(n); - for (int i = 0; i < n; i++) - ret += get(); + if (n > 0) { + ret.reserve(static_cast(n)); + for (int i = 0; i < n; i++) + ret += get(); + } return ret; } @@ -332,7 +329,7 @@ bool Stream::_ReadAheadTo(size_t i) const { void Stream::StreamInUtf8() const { unsigned char b = GetNextByte(); if (m_input.good()) { - m_readahead.push_back(b); + m_readahead.push_back(static_cast(b)); } } @@ -353,7 +350,9 @@ void Stream::StreamInUtf16() const { // Trailing (low) surrogate...ugh, wrong order QueueUnicodeCodepoint(m_readahead, CP_REPLACEMENT_CHARACTER); return; - } else if (ch >= 0xD800 && ch < 0xDC00) { + } + + if (ch >= 0xD800 && ch < 0xDC00) { // ch is a leading (high) surrogate // Four byte UTF-8 code point @@ -378,11 +377,10 @@ void Stream::StreamInUtf16() const { // Easiest case: queue the codepoint and return QueueUnicodeCodepoint(m_readahead, ch); return; - } else { - // Start the loop over with the new high surrogate - ch = chLow; - continue; } + // Start the loop over with the new high surrogate + ch = chLow; + continue; } // Select the payload bits from the high surrogate @@ -445,4 +443,4 @@ void Stream::StreamInUtf32() const { QueueUnicodeCodepoint(m_readahead, ch); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/stream.h b/src/libs/3rdparty/yaml-cpp/src/stream.h index 42d542d5b16..2bc7a152165 100644 --- a/src/libs/3rdparty/yaml-cpp/src/stream.h +++ b/src/libs/3rdparty/yaml-cpp/src/stream.h @@ -7,7 +7,6 @@ #pragma once #endif -#include "yaml-cpp/noncopyable.h" #include "yaml-cpp/mark.h" #include #include @@ -17,11 +16,18 @@ #include namespace YAML { -class Stream : private noncopyable { + +class StreamCharSource; + +class Stream { public: friend class StreamCharSource; Stream(std::istream& input); + Stream(const Stream&) = delete; + Stream(Stream&&) = delete; + Stream& operator=(const Stream&) = delete; + Stream& operator=(Stream&&) = delete; ~Stream(); operator bool() const; @@ -71,6 +77,6 @@ inline bool Stream::ReadAheadTo(size_t i) const { return true; return _ReadAheadTo(i); } -} +} // namespace YAML #endif // STREAM_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h b/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h index 624599e65da..826ba5347ee 100644 --- a/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h +++ b/src/libs/3rdparty/yaml-cpp/src/streamcharsource.h @@ -7,16 +7,20 @@ #pragma once #endif -#include "yaml-cpp/noncopyable.h" +#include "yaml-cpp/noexcept.h" +#include "stream.h" #include namespace YAML { + class StreamCharSource { public: StreamCharSource(const Stream& stream) : m_offset(0), m_stream(stream) {} - StreamCharSource(const StreamCharSource& source) - : m_offset(source.m_offset), m_stream(source.m_stream) {} - ~StreamCharSource() {} + StreamCharSource(const StreamCharSource& source) = default; + StreamCharSource(StreamCharSource&&) YAML_CPP_NOEXCEPT = default; + StreamCharSource& operator=(const StreamCharSource&) = delete; + StreamCharSource& operator=(StreamCharSource&&) = delete; + ~StreamCharSource() = default; operator bool() const; char operator[](std::size_t i) const { return m_stream.CharAt(m_offset + i); } @@ -27,8 +31,6 @@ class StreamCharSource { private: std::size_t m_offset; const Stream& m_stream; - - StreamCharSource& operator=(const StreamCharSource&); // non-assignable }; inline StreamCharSource::operator bool() const { @@ -38,11 +40,11 @@ inline StreamCharSource::operator bool() const { inline const StreamCharSource StreamCharSource::operator+(int i) const { StreamCharSource source(*this); if (static_cast(source.m_offset) + i >= 0) - source.m_offset += i; + source.m_offset += static_cast(i); else source.m_offset = 0; return source; } -} +} // namespace YAML #endif // STREAMCHARSOURCE_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/src/tag.cpp b/src/libs/3rdparty/yaml-cpp/src/tag.cpp index 51435520e46..35a1b465604 100644 --- a/src/libs/3rdparty/yaml-cpp/src/tag.cpp +++ b/src/libs/3rdparty/yaml-cpp/src/tag.cpp @@ -6,7 +6,8 @@ #include "token.h" namespace YAML { -Tag::Tag(const Token& token) : type(static_cast(token.data)) { +Tag::Tag(const Token& token) + : type(static_cast(token.data)), handle{}, value{} { switch (type) { case VERBATIM: value = token.value; @@ -28,7 +29,7 @@ Tag::Tag(const Token& token) : type(static_cast(token.data)) { } } -const std::string Tag::Translate(const Directives& directives) { +std::string Tag::Translate(const Directives& directives) { switch (type) { case VERBATIM: return value; @@ -46,4 +47,4 @@ const std::string Tag::Translate(const Directives& directives) { } throw std::runtime_error("yaml-cpp: internal error, bad tag type"); } -} +} // namespace YAML diff --git a/src/libs/3rdparty/yaml-cpp/src/tag.h b/src/libs/3rdparty/yaml-cpp/src/tag.h index ac30673b9e8..c811f395597 100644 --- a/src/libs/3rdparty/yaml-cpp/src/tag.h +++ b/src/libs/3rdparty/yaml-cpp/src/tag.h @@ -23,7 +23,7 @@ struct Tag { }; Tag(const Token& token); - const std::string Translate(const Directives& directives); + std::string Translate(const Directives& directives); TYPE type; std::string handle, value; diff --git a/src/libs/3rdparty/yaml-cpp/src/token.h b/src/libs/3rdparty/yaml-cpp/src/token.h index ad0b7d0a005..9c9a5b77982 100644 --- a/src/libs/3rdparty/yaml-cpp/src/token.h +++ b/src/libs/3rdparty/yaml-cpp/src/token.h @@ -14,10 +14,11 @@ namespace YAML { const std::string TokenNames[] = { - "DIRECTIVE", "DOC_START", "DOC_END", "BLOCK_SEQ_START", "BLOCK_MAP_START", - "BLOCK_SEQ_END", "BLOCK_MAP_END", "BLOCK_ENTRY", "FLOW_SEQ_START", - "FLOW_MAP_START", "FLOW_SEQ_END", "FLOW_MAP_END", "FLOW_MAP_COMPACT", - "FLOW_ENTRY", "KEY", "VALUE", "ANCHOR", "ALIAS", "TAG", "SCALAR"}; + "DIRECTIVE", "DOC_START", "DOC_END", "BLOCK_SEQ_START", + "BLOCK_MAP_START", "BLOCK_SEQ_END", "BLOCK_MAP_END", "BLOCK_ENTRY", + "FLOW_SEQ_START", "FLOW_MAP_START", "FLOW_SEQ_END", "FLOW_MAP_END", + "FLOW_MAP_COMPACT", "FLOW_ENTRY", "KEY", "VALUE", + "ANCHOR", "ALIAS", "TAG", "SCALAR"}; struct Token { // enums @@ -48,12 +49,12 @@ struct Token { // data Token(TYPE type_, const Mark& mark_) - : status(VALID), type(type_), mark(mark_), data(0) {} + : status(VALID), type(type_), mark(mark_), value{}, params{}, data(0) {} friend std::ostream& operator<<(std::ostream& out, const Token& token) { out << TokenNames[token.type] << std::string(": ") << token.value; - for (std::size_t i = 0; i < token.params.size(); i++) - out << std::string(" ") << token.params[i]; + for (const std::string& param : token.params) + out << std::string(" ") << param; return out; } @@ -64,6 +65,6 @@ struct Token { std::vector params; int data; }; -} +} // namespace YAML #endif // TOKEN_H_62B23520_7C8E_11DE_8A39_0800200C9A66 diff --git a/src/libs/3rdparty/yaml-cpp/yaml-cpp.pc.cmake b/src/libs/3rdparty/yaml-cpp/yaml-cpp.pc.cmake deleted file mode 100644 index 3db7962eaf5..00000000000 --- a/src/libs/3rdparty/yaml-cpp/yaml-cpp.pc.cmake +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/@INCLUDE_INSTALL_ROOT_DIR@ -libdir=${exec_prefix}/@LIB_INSTALL_DIR@ - -Name: Yaml-cpp -Description: A YAML parser and emitter for C++ -Version: @YAML_CPP_VERSION@ -Requires: -Libs: -L${libdir} -lyaml-cpp -Cflags: -I${includedir} diff --git a/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs b/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs index ca39a4864f9..d6558880f8e 100644 --- a/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs +++ b/src/libs/3rdparty/yaml-cpp/yaml-cpp.qbs @@ -1,105 +1,102 @@ -import qbs 1.0 +QtcLibrary { + name: "yaml-cpp" -Project { - QtcLibrary { - name: "yaml-cpp" + cpp.defines: base.concat(["YAML_CPP_DLL", "yaml_cpp_EXPORTS"]) + cpp.includePaths: [product.sourceDirectory + "/include/"] - cpp.defines: base.concat(["YAML_CPP_DLL", "yaml_cpp_EXPORTS"]) - cpp.includePaths: [product.sourceDirectory + "/include/"] + files: [ + "include/yaml-cpp/anchor.h", + "include/yaml-cpp/binary.h", + "include/yaml-cpp/depthguard.h", + "include/yaml-cpp/dll.h", + "include/yaml-cpp/emitfromevents.h", + "include/yaml-cpp/emitter.h", + "include/yaml-cpp/emitterdef.h", + "include/yaml-cpp/emittermanip.h", + "include/yaml-cpp/emitterstyle.h", + "include/yaml-cpp/eventhandler.h", + "include/yaml-cpp/exceptions.h", + "include/yaml-cpp/mark.h", + "include/yaml-cpp/noexcept.h", + "include/yaml-cpp/node", + "include/yaml-cpp/node/convert.h", + "include/yaml-cpp/node/detail", + "include/yaml-cpp/node/detail/impl.h", + "include/yaml-cpp/node/detail/iterator.h", + "include/yaml-cpp/node/detail/iterator_fwd.h", + "include/yaml-cpp/node/detail/memory.h", + "include/yaml-cpp/node/detail/node.h", + "include/yaml-cpp/node/detail/node_data.h", + "include/yaml-cpp/node/detail/node_iterator.h", + "include/yaml-cpp/node/detail/node_ref.h", + "include/yaml-cpp/node/emit.h", + "include/yaml-cpp/node/impl.h", + "include/yaml-cpp/node/iterator.h", + "include/yaml-cpp/node/node.h", + "include/yaml-cpp/node/parse.h", + "include/yaml-cpp/node/ptr.h", + "include/yaml-cpp/node/type.h", + "include/yaml-cpp/null.h", + "include/yaml-cpp/ostream_wrapper.h", + "include/yaml-cpp/parser.h", + "include/yaml-cpp/stlemitter.h", + "include/yaml-cpp/traits.h", + "include/yaml-cpp/yaml.h", + "src/binary.cpp", + "src/collectionstack.h", + "src/convert.cpp", + "src/depthguard.cpp", + "src/directives.cpp", + "src/directives.h", + "src/emit.cpp", + "src/emitfromevents.cpp", + "src/emitter.cpp", + "src/emitterstate.cpp", + "src/emitterstate.h", + "src/emitterutils.cpp", + "src/emitterutils.h", + "src/exceptions.cpp", + "src/exp.cpp", + "src/exp.h", + "src/indentation.h", + "src/memory.cpp", + "src/node.cpp", + "src/node_data.cpp", + "src/nodebuilder.cpp", + "src/nodebuilder.h", + "src/nodeevents.cpp", + "src/nodeevents.h", + "src/null.cpp", + "src/ostream_wrapper.cpp", + "src/parse.cpp", + "src/parser.cpp", + "src/ptr_vector.h", + "src/regex_yaml.cpp", + "src/regex_yaml.h", + "src/regeximpl.h", + "src/scanner.cpp", + "src/scanner.h", + "src/scanscalar.cpp", + "src/scanscalar.h", + "src/scantag.cpp", + "src/scantag.h", + "src/scantoken.cpp", + "src/setting.h", + "src/simplekey.cpp", + "src/singledocparser.cpp", + "src/singledocparser.h", + "src/stream.cpp", + "src/stream.h", + "src/streamcharsource.h", + "src/stringsource.h", + "src/tag.cpp", + "src/tag.h", + "src/token.h", + ] - files: [ - "include/yaml-cpp/anchor.h", - "include/yaml-cpp/binary.h", - "include/yaml-cpp/dll.h", - "include/yaml-cpp/emitfromevents.h", - "include/yaml-cpp/emitter.h", - "include/yaml-cpp/emitterdef.h", - "include/yaml-cpp/emittermanip.h", - "include/yaml-cpp/emitterstyle.h", - "include/yaml-cpp/eventhandler.h", - "include/yaml-cpp/exceptions.h", - "include/yaml-cpp/mark.h", - "include/yaml-cpp/node", - "include/yaml-cpp/node/convert.h", - "include/yaml-cpp/node/detail", - "include/yaml-cpp/node/detail/bool_type.h", - "include/yaml-cpp/node/detail/impl.h", - "include/yaml-cpp/node/detail/iterator.h", - "include/yaml-cpp/node/detail/iterator_fwd.h", - "include/yaml-cpp/node/detail/memory.h", - "include/yaml-cpp/node/detail/node.h", - "include/yaml-cpp/node/detail/node_data.h", - "include/yaml-cpp/node/detail/node_iterator.h", - "include/yaml-cpp/node/detail/node_ref.h", - "include/yaml-cpp/node/emit.h", - "include/yaml-cpp/node/impl.h", - "include/yaml-cpp/node/iterator.h", - "include/yaml-cpp/node/node.h", - "include/yaml-cpp/node/parse.h", - "include/yaml-cpp/node/ptr.h", - "include/yaml-cpp/node/type.h", - "include/yaml-cpp/noncopyable.h", - "include/yaml-cpp/null.h", - "include/yaml-cpp/ostream_wrapper.h", - "include/yaml-cpp/parser.h", - "include/yaml-cpp/stlemitter.h", - "include/yaml-cpp/traits.h", - "include/yaml-cpp/yaml.h", - "src/binary.cpp", - "src/collectionstack.h", - "src/convert.cpp", - "src/directives.cpp", - "src/directives.h", - "src/emit.cpp", - "src/emitfromevents.cpp", - "src/emitter.cpp", - "src/emitterstate.cpp", - "src/emitterstate.h", - "src/emitterutils.cpp", - "src/emitterutils.h", - "src/exceptions.cpp", - "src/exp.cpp", - "src/exp.h", - "src/indentation.h", - "src/memory.cpp", - "src/node.cpp", - "src/node_data.cpp", - "src/nodebuilder.cpp", - "src/nodebuilder.h", - "src/nodeevents.cpp", - "src/nodeevents.h", - "src/null.cpp", - "src/ostream_wrapper.cpp", - "src/parse.cpp", - "src/parser.cpp", - "src/ptr_vector.h", - "src/regex_yaml.cpp", - "src/regex_yaml.h", - "src/regeximpl.h", - "src/scanner.cpp", - "src/scanner.h", - "src/scanscalar.cpp", - "src/scanscalar.h", - "src/scantag.cpp", - "src/scantag.h", - "src/scantoken.cpp", - "src/setting.h", - "src/simplekey.cpp", - "src/singledocparser.cpp", - "src/singledocparser.h", - "src/stream.cpp", - "src/stream.h", - "src/streamcharsource.h", - "src/stringsource.h", - "src/tag.cpp", - "src/tag.h", - "src/token.h", - ] - - Export { - Depends { name: "cpp" } - cpp.includePaths: [exportingProduct.sourceDirectory + "/include/"] - cpp.defines: base.concat(["YAML_CPP_DLL"]) - } + Export { + Depends { name: "cpp" } + cpp.includePaths: [exportingProduct.sourceDirectory + "/include/"] + cpp.defines: "YAML_CPP_DLL" } } diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt index 73a554bae8b..536e3734949 100644 --- a/src/libs/CMakeLists.txt +++ b/src/libs/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(3rdparty) +add_subdirectory(nanotrace) add_subdirectory(advanceddockingsystem) add_subdirectory(aggregation) add_subdirectory(cplusplus) @@ -8,7 +9,6 @@ add_subdirectory(glsl) add_subdirectory(languageserverprotocol) add_subdirectory(languageutils) add_subdirectory(modelinglib) -add_subdirectory(nanotrace) add_subdirectory(qmldebug) add_subdirectory(qmleditorwidgets) add_subdirectory(qmljs) diff --git a/src/libs/advanceddockingsystem/CMakeLists.txt b/src/libs/advanceddockingsystem/CMakeLists.txt index 66608852137..f993a9f1431 100644 --- a/src/libs/advanceddockingsystem/CMakeLists.txt +++ b/src/libs/advanceddockingsystem/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_library(AdvancedDockingSystem + CONDITION TARGET Qt::QuickWidgets DEPENDS Qt::Widgets Qt::Core Qt::Gui Qt::Xml Qt::QuickWidgets Utils SOURCES ads_globals.cpp ads_globals.h diff --git a/src/libs/advanceddockingsystem/ads_globals.cpp b/src/libs/advanceddockingsystem/ads_globals.cpp index 664f10ca8a5..5642651e717 100644 --- a/src/libs/advanceddockingsystem/ads_globals.cpp +++ b/src/libs/advanceddockingsystem/ads_globals.cpp @@ -17,7 +17,6 @@ #if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) #include #include -#include #endif namespace ADS { diff --git a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs index e603a039827..c04b8e0fe33 100644 --- a/src/libs/advanceddockingsystem/advanceddockingsystem.qbs +++ b/src/libs/advanceddockingsystem/advanceddockingsystem.qbs @@ -7,7 +7,7 @@ QtcLibrary { cpp.defines: base.concat("ADVANCEDDOCKINGSYSTEM_LIBRARY") cpp.includePaths: base.concat([".", linux.prefix]) - Depends { name: "Qt"; submodules: ["widgets", "xml"] } + Depends { name: "Qt"; submodules: ["quickwidgets", "widgets", "xml"] } Depends { name: "Utils" } Group { diff --git a/src/libs/advanceddockingsystem/autohidetab.cpp b/src/libs/advanceddockingsystem/autohidetab.cpp index 1d8f7a50a65..dd3f3f6c820 100644 --- a/src/libs/advanceddockingsystem/autohidetab.cpp +++ b/src/libs/advanceddockingsystem/autohidetab.cpp @@ -4,6 +4,7 @@ #include "autohidetab.h" #include "ads_globals_p.h" +#include "advanceddockingsystemtr.h" #include "autohidedockcontainer.h" #include "autohidesidebar.h" #include "dockareawidget.h" @@ -335,7 +336,7 @@ void AutoHideTab::contextMenuEvent(QContextMenuEvent *event) const bool isFloatable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetFloatable); QMenu menu(this); - QAction *detachAction = menu.addAction(tr("Detach")); + QAction *detachAction = menu.addAction(Tr::tr("Detach")); detachAction->connect(detachAction, &QAction::triggered, this, @@ -345,17 +346,17 @@ void AutoHideTab::contextMenuEvent(QContextMenuEvent *event) auto isPinnable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetPinnable); detachAction->setEnabled(isPinnable); - auto pinToMenu = menu.addMenu(tr("Pin To...")); + auto pinToMenu = menu.addMenu(Tr::tr("Pin To...")); pinToMenu->setEnabled(isPinnable); - d->createAutoHideToAction(tr("Top"), SideBarTop, pinToMenu); - d->createAutoHideToAction(tr("Left"), SideBarLeft, pinToMenu); - d->createAutoHideToAction(tr("Right"), SideBarRight, pinToMenu); - d->createAutoHideToAction(tr("Bottom"), SideBarBottom, pinToMenu); + d->createAutoHideToAction(Tr::tr("Top"), SideBarTop, pinToMenu); + d->createAutoHideToAction(Tr::tr("Left"), SideBarLeft, pinToMenu); + d->createAutoHideToAction(Tr::tr("Right"), SideBarRight, pinToMenu); + d->createAutoHideToAction(Tr::tr("Bottom"), SideBarBottom, pinToMenu); - QAction *unpinAction = menu.addAction(tr("Unpin (Dock)")); + QAction *unpinAction = menu.addAction(Tr::tr("Unpin (Dock)")); unpinAction->connect(unpinAction, &QAction::triggered, this, &AutoHideTab::unpinDockWidget); menu.addSeparator(); - QAction *closeAction = menu.addAction(tr("Close")); + QAction *closeAction = menu.addAction(Tr::tr("Close")); closeAction->connect(closeAction, &QAction::triggered, this, diff --git a/src/libs/advanceddockingsystem/dockareatitlebar.cpp b/src/libs/advanceddockingsystem/dockareatitlebar.cpp index 234916d530d..7a2e6770621 100644 --- a/src/libs/advanceddockingsystem/dockareatitlebar.cpp +++ b/src/libs/advanceddockingsystem/dockareatitlebar.cpp @@ -656,7 +656,8 @@ void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event) QMenu menu(this); if (!isTopLevelArea) { - QAction *detachAction = menu.addAction(isAutoHide ? tr("Detach") : tr("Detach Group")); + QAction *detachAction = menu.addAction(isAutoHide ? Tr::tr("Detach") + : Tr::tr("Detach Group")); detachAction->connect(detachAction, &QAction::triggered, this, @@ -665,7 +666,8 @@ void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event) d->m_dockArea->features().testFlag(DockWidget::DockWidgetFloatable)); if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) { - QAction *pinAction = menu.addAction(isAutoHide ? tr("Unpin (Dock)") : tr("Pin Group")); + QAction *pinAction = menu.addAction(isAutoHide ? Tr::tr("Unpin (Dock)") + : Tr::tr("Pin Group")); pinAction->connect(pinAction, &QAction::triggered, this, @@ -674,17 +676,17 @@ void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event) auto areaIsPinnable = d->m_dockArea->features().testFlag(DockWidget::DockWidgetPinnable); pinAction->setEnabled(areaIsPinnable); if (!isAutoHide) { - auto tmp = menu.addMenu(tr("Pin Group To...")); + auto tmp = menu.addMenu(Tr::tr("Pin Group To...")); tmp->setEnabled(areaIsPinnable); - d->createAutoHideToAction(tr("Top"), SideBarTop, tmp); - d->createAutoHideToAction(tr("Left"), SideBarLeft, tmp); - d->createAutoHideToAction(tr("Right"), SideBarRight, tmp); - d->createAutoHideToAction(tr("Bottom"), SideBarBottom, tmp); + d->createAutoHideToAction(Tr::tr("Top"), SideBarTop, tmp); + d->createAutoHideToAction(Tr::tr("Left"), SideBarLeft, tmp); + d->createAutoHideToAction(Tr::tr("Right"), SideBarRight, tmp); + d->createAutoHideToAction(Tr::tr("Bottom"), SideBarBottom, tmp); } } menu.addSeparator(); } - QAction *closeAction = menu.addAction(isAutoHide ? tr("Close") : tr("Close Group")); + QAction *closeAction = menu.addAction(isAutoHide ? Tr::tr("Close") : Tr::tr("Close Group")); closeAction->connect(closeAction, &QAction::triggered, this, @@ -692,7 +694,7 @@ void DockAreaTitleBar::contextMenuEvent(QContextMenuEvent *event) closeAction->setEnabled(d->m_dockArea->features().testFlag(DockWidget::DockWidgetClosable)); if (!isAutoHide && !isTopLevelArea) { - QAction *closeOthersAction = menu.addAction(tr("Close Other Groups")); + QAction *closeOthersAction = menu.addAction(Tr::tr("Close Other Groups")); closeOthersAction->connect(closeOthersAction, &QAction::triggered, d->m_dockArea, @@ -717,22 +719,22 @@ QString DockAreaTitleBar::titleBarButtonToolTip(eTitleBarButton button) const switch (button) { case TitleBarButtonAutoHide: if (d->m_dockArea->isAutoHide()) - return tr("Unpin (Dock)"); + return Tr::tr("Unpin (Dock)"); if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideButtonTogglesArea)) - return tr("Pin Group"); + return Tr::tr("Pin Group"); else - return tr("Pin Active Tab (Press Ctrl to Pin Group)"); + return Tr::tr("Pin Active Tab (Press Ctrl to Pin Group)"); break; case TitleBarButtonClose: if (d->m_dockArea->isAutoHide()) - return tr("Close"); + return Tr::tr("Close"); if (DockManager::testConfigFlag(DockManager::DockAreaCloseButtonClosesTab)) - return tr("Close Active Tab"); + return Tr::tr("Close Active Tab"); else - return tr("Close Group"); + return Tr::tr("Close Group"); break; default: diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp index 088db38fbdf..15e7a9dbb15 100644 --- a/src/libs/advanceddockingsystem/dockmanager.cpp +++ b/src/libs/advanceddockingsystem/dockmanager.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -91,7 +92,7 @@ public: Workspace m_workspace; bool m_workspaceLocked = false; - QSettings *m_settings = nullptr; + QtcSettings *m_settings = nullptr; bool m_modeChangeState = false; bool m_workspaceOrderDirty = false; @@ -441,7 +442,7 @@ int DockManager::startDragDistance() return static_cast(QApplication::startDragDistance() * 1.5); } -void DockManager::setSettings(QSettings *settings) +void DockManager::setSettings(QtcSettings *settings) { d->m_settings = settings; } @@ -739,7 +740,7 @@ QList DockManager::splitterSizes(DockAreaWidget *containedArea) const return splitter->sizes(); } - return QList(); + return {}; } void DockManager::setSplitterSizes(DockAreaWidget *containedArea, const QList &sizes) @@ -1372,13 +1373,13 @@ expected_str DockManager::exportWorkspace(const QString &targetFilePath // Check if the target directory exists if (!targetFile.parentDir().exists()) return make_unexpected( - Tr::tr("Directory does not exist\"%1\".").arg(targetFile.parentDir().toUserOutput())); + Tr::tr("The directory \"%1\" does not exist.").arg(targetFile.parentDir().toUserOutput())); // Check if the workspace exists const FilePath workspaceFile = userDirectory().pathAppended(sourceFileName); if (!workspaceFile.exists()) return make_unexpected( - Tr::tr("Workspace does not exist \"%1\"").arg(workspaceFile.toUserOutput())); + Tr::tr("The workspace \"%1\" does not exist ").arg(workspaceFile.toUserOutput())); // Finally copy the workspace to the target const expected_str copyResult = workspaceFile.copyFile(targetFile); @@ -1573,7 +1574,7 @@ void DockManager::syncWorkspacePresets() // Try do create the 'workspaces' directory if it doesn't exist already if (!userDirectory().ensureWritableDir()) { - qWarning() << QString("Could not make directory '%1')").arg(userDirectory().toString()); + qWarning() << QString("Could not make directory '%1')").arg(userDirectory().toUserOutput()); return; } @@ -1601,8 +1602,8 @@ void DockManager::syncWorkspacePresets() userDirectory().pathAppended(filePath.fileName())); if (!copyResult) qWarning() << QString("Could not copy '%1' to '%2' due to %3") - .arg(filePath.toString(), - userDirectory().toString(), + .arg(filePath.toUserOutput(), + userDirectory().toUserOutput(), copyResult.error()); } } diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h index 31621449d58..c11bd6ebb16 100644 --- a/src/libs/advanceddockingsystem/dockmanager.h +++ b/src/libs/advanceddockingsystem/dockmanager.h @@ -23,9 +23,10 @@ QT_BEGIN_NAMESPACE class QMenu; -class QSettings; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace ADS { namespace Constants { @@ -277,7 +278,7 @@ public: /** * Set the QtCreator settings. */ - void setSettings(QSettings *settings); + void setSettings(Utils::QtcSettings *settings); /** * Set the path to the workspace presets folder. diff --git a/src/libs/advanceddockingsystem/dockwidgettab.cpp b/src/libs/advanceddockingsystem/dockwidgettab.cpp index aa4aba6aee3..efea3d0b202 100644 --- a/src/libs/advanceddockingsystem/dockwidgettab.cpp +++ b/src/libs/advanceddockingsystem/dockwidgettab.cpp @@ -473,7 +473,7 @@ void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event) QMenu menu(this); if (!isTopLevelArea) { - QAction *detachAction = menu.addAction(tr("Detach")); + QAction *detachAction = menu.addAction(Tr::tr("Detach")); detachAction->connect(detachAction, &QAction::triggered, this, @@ -481,7 +481,7 @@ void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event) detachAction->setEnabled(isDetachable); if (DockManager::testAutoHideConfigFlag(DockManager::AutoHideFeatureEnabled)) { - QAction *pinAction = menu.addAction(tr("Pin")); + QAction *pinAction = menu.addAction(Tr::tr("Pin")); pinAction->connect(pinAction, &QAction::triggered, this, @@ -490,23 +490,23 @@ void DockWidgetTab::contextMenuEvent(QContextMenuEvent *event) auto isPinnable = d->m_dockWidget->features().testFlag(DockWidget::DockWidgetPinnable); pinAction->setEnabled(isPinnable); - auto subMenu = menu.addMenu(tr("Pin To...")); + auto subMenu = menu.addMenu(Tr::tr("Pin To...")); subMenu->setEnabled(isPinnable); - d->createAutoHideToAction(tr("Top"), SideBarTop, subMenu); - d->createAutoHideToAction(tr("Left"), SideBarLeft, subMenu); - d->createAutoHideToAction(tr("Right"), SideBarRight, subMenu); - d->createAutoHideToAction(tr("Bottom"), SideBarBottom, subMenu); + d->createAutoHideToAction(Tr::tr("Top"), SideBarTop, subMenu); + d->createAutoHideToAction(Tr::tr("Left"), SideBarLeft, subMenu); + d->createAutoHideToAction(Tr::tr("Right"), SideBarRight, subMenu); + d->createAutoHideToAction(Tr::tr("Bottom"), SideBarBottom, subMenu); } } menu.addSeparator(); - QAction *closeAction = menu.addAction(tr("Close")); + QAction *closeAction = menu.addAction(Tr::tr("Close")); closeAction->connect(closeAction, &QAction::triggered, this, &DockWidgetTab::closeRequested); closeAction->setEnabled(isClosable()); if (d->m_dockArea->openDockWidgetsCount() > 1) { - QAction *closeOthersAction = menu.addAction(tr("Close Others")); + QAction *closeOthersAction = menu.addAction(Tr::tr("Close Others")); closeOthersAction->connect(closeOthersAction, &QAction::triggered, this, diff --git a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp index 699a0086fb9..1e02e47ebdc 100644 --- a/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp +++ b/src/libs/advanceddockingsystem/linux/floatingwidgettitlebar.cpp @@ -174,7 +174,7 @@ void FloatingWidgetTitleBar::mouseMoveEvent(QMouseEvent *event) // Move floating window if (DraggingFloatingWidget == d->m_dragState) { if (d->m_floatingWidget->isMaximized()) - d->m_floatingWidget->showNormal(true); + d->m_floatingWidget->showNormal(); d->m_floatingWidget->moveFloating(); Super::mouseMoveEvent(event); diff --git a/src/libs/advanceddockingsystem/workspaceview.cpp b/src/libs/advanceddockingsystem/workspaceview.cpp index 1b4cfc7e270..26983c2bcd7 100644 --- a/src/libs/advanceddockingsystem/workspaceview.cpp +++ b/src/libs/advanceddockingsystem/workspaceview.cpp @@ -46,6 +46,7 @@ WorkspaceView::WorkspaceView(DockManager *manager, QWidget *parent) , m_manager(manager) , m_workspaceModel(manager) { + setUniformRowHeights(false); setItemDelegate(new RemoveItemFocusDelegate(this)); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::SingleSelection); diff --git a/src/libs/aggregation/aggregate.h b/src/libs/aggregation/aggregate.h index 4934191d444..0e210451e71 100644 --- a/src/libs/aggregation/aggregate.h +++ b/src/libs/aggregation/aggregate.h @@ -83,14 +83,14 @@ template T *query(QObject *obj) template QList query_all(Aggregate *obj) { if (!obj) - return QList(); + return {}; return obj->template components(); } template QList query_all(QObject *obj) { if (!obj) - return QList(); + return {}; QReadLocker locker(&Aggregate::lock()); Aggregate *parentAggregation = Aggregate::parentAggregate(obj); QList results; diff --git a/src/libs/aggregation/aggregation.qbs b/src/libs/aggregation/aggregation.qbs index d8f1097f756..873294e92f5 100644 --- a/src/libs/aggregation/aggregation.qbs +++ b/src/libs/aggregation/aggregation.qbs @@ -1,17 +1,12 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "Aggregation" + Depends { name: "Qt.core" } + cpp.defines: base.concat("AGGREGATION_LIBRARY") - QtcLibrary { - Depends { name: "Qt.core" } - cpp.defines: base.concat("AGGREGATION_LIBRARY") - - files: [ - "aggregate.cpp", - "aggregate.h", - "aggregation_global.h", - ] - } + files: [ + "aggregate.cpp", + "aggregate.h", + "aggregation_global.h", + ] } diff --git a/src/libs/cplusplus/CMakeLists.txt b/src/libs/cplusplus/CMakeLists.txt index c99a66c1f89..2fe5c63fe17 100644 --- a/src/libs/cplusplus/CMakeLists.txt +++ b/src/libs/cplusplus/CMakeLists.txt @@ -32,6 +32,7 @@ add_qtc_library(CPlusPlus TypeOfExpression.cpp TypeOfExpression.h TypePrettyPrinter.cpp TypePrettyPrinter.h cppmodelmanagerbase.cpp cppmodelmanagerbase.h + declarationcomments.cpp declarationcomments.h findcdbbreakpoint.cpp findcdbbreakpoint.h pp-cctype.h pp-engine.cpp pp-engine.h pp-scanner.cpp diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp index 07714c0fe2f..6dc59bf1bf1 100644 --- a/src/libs/cplusplus/CppDocument.cpp +++ b/src/libs/cplusplus/CppDocument.cpp @@ -774,10 +774,8 @@ QSet Snapshot::allIncludesForDocument(const FilePath &filePath) const if (Document::Ptr doc = document(file)) { const FilePaths includedFiles = doc->includedFiles(Document::Duplicates::Keep); for (const FilePath &inc : includedFiles) { - if (!result.contains(inc)) { - result.insert(inc); + if (Utils::insert(result, inc)) files.push(inc); - } } } } diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h index 00bebf277d9..8834c7538a7 100644 --- a/src/libs/cplusplus/CppDocument.h +++ b/src/libs/cplusplus/CppDocument.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/src/libs/cplusplus/FastPreprocessor.cpp b/src/libs/cplusplus/FastPreprocessor.cpp index b66d214ed27..580334d0744 100644 --- a/src/libs/cplusplus/FastPreprocessor.cpp +++ b/src/libs/cplusplus/FastPreprocessor.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include using namespace Utils; @@ -65,9 +67,7 @@ void FastPreprocessor::sourceNeeded(int line, const FilePath &filePath, IncludeT void FastPreprocessor::mergeEnvironment(const FilePath &filePath) { - if (! _merged.contains(filePath)) { - _merged.insert(filePath); - + if (Utils::insert(_merged, filePath)) { if (Document::Ptr doc = _snapshot.document(filePath)) { const QList includes = doc->resolvedIncludes(); for (const Document::Include &i : includes) diff --git a/src/libs/cplusplus/LookupContext.cpp b/src/libs/cplusplus/LookupContext.cpp index 64669409b52..d4bf5b970d5 100644 --- a/src/libs/cplusplus/LookupContext.cpp +++ b/src/libs/cplusplus/LookupContext.cpp @@ -78,8 +78,7 @@ static bool isNestedInstantiationEnclosingTemplate( { QSet processed; while (enclosingTemplateClassInstantiation - && !processed.contains(enclosingTemplateClassInstantiation)) { - processed.insert(enclosingTemplateClassInstantiation); + && Utils::insert(processed, enclosingTemplateClassInstantiation)) { if (enclosingTemplateClassInstantiation == nestedClassOrNamespaceInstantiation) return false; enclosingTemplateClassInstantiation = enclosingTemplateClassInstantiation->parent(); @@ -389,11 +388,10 @@ ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope, } if (const NamedType *namedTy = d->type()->asNamedType()) { // Stop on recursive typedef declarations - if (typedefsBeingResolved.contains(d)) + if (!Utils::insert(typedefsBeingResolved, d)) return nullptr; return lookupType(namedTy->name(), scope, nullptr, - QSet(typedefsBeingResolved) - << d); + typedefsBeingResolved); } } } @@ -504,9 +502,8 @@ QList LookupContext::lookup(const Name *name, Scope *scope) const // try find this name in parent class QSet processed; while (candidates.isEmpty() && (binding = binding->parent())) { - if (processed.contains(binding)) + if (!Utils::insert(processed, binding)) break; - processed.insert(binding); candidates = binding->find(name); } @@ -683,9 +680,8 @@ QList ClassOrNamespace::lookup_helper(const Name *name, bool searchI for (ClassOrNamespace *parentBinding = binding->parent(); parentBinding && !match; parentBinding = parentBinding->parent()) { - if (processed.contains(parentBinding)) + if (!Utils::insert(processed, parentBinding)) break; - processed.insert(parentBinding); match = parentBinding->lookupInScope(fullName); } @@ -704,9 +700,8 @@ QList ClassOrNamespace::lookup_helper(const Name *name, bool searchI QSet processedOwnParents; ClassOrNamespace *binding = this; do { - if (processedOwnParents.contains(binding)) + if (!Utils::insert(processedOwnParents, binding)) break; - processedOwnParents.insert(binding); lookup_helper(name, binding, &result, &processed, /*templateId = */ nullptr); binding = binding->_parent; } while (searchInEnclosingScope && binding); @@ -720,9 +715,7 @@ void ClassOrNamespace::lookup_helper(const Name *name, ClassOrNamespace *binding QSet *processed, const TemplateNameId *templateId) { - if (binding && ! processed->contains(binding)) { - processed->insert(binding); - + if (binding && Utils::insert(*processed, binding)) { const Identifier *nameId = name->identifier(); const QList symbols = binding->symbols(); @@ -902,9 +895,8 @@ ClassOrNamespace *ClassOrNamespace::findBlock_helper(Block *block, bool searchInEnclosingScope) { for (ClassOrNamespace *binding = this; binding; binding = binding->_parent) { - if (processed->contains(binding)) + if (!Utils::insert(*processed, binding)) break; - processed->insert(binding); binding->flush(); auto end = binding->_blocks.end(); auto citBlock = binding->_blocks.find(block); @@ -977,9 +969,7 @@ ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name, return nullptr; - } else if (! processed->contains(this)) { - processed->insert(this); - + } else if (Utils::insert(*processed, this)) { if (name->asNameId() || name->asTemplateNameId() || name->asAnonymousNameId()) { flush(); @@ -1260,9 +1250,8 @@ ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name, QSet otherProcessed; while (!origin->_symbols.isEmpty() && origin->_symbols[0]->asBlock()) { - if (otherProcessed.contains(origin)) + if (!Utils::insert(otherProcessed, origin)) break; - otherProcessed.insert(origin); origin = origin->parent(); } @@ -1489,9 +1478,8 @@ void ClassOrNamespace::NestedClassInstantiator::instantiate(ClassOrNamespace *en { if (_alreadyConsideredNestedClassInstantiations.size() >= 3) return; - if (_alreadyConsideredNestedClassInstantiations.contains(enclosingTemplateClass)) + if (!Utils::insert(_alreadyConsideredNestedClassInstantiations, enclosingTemplateClass)) return; - _alreadyConsideredNestedClassInstantiations.insert(enclosingTemplateClass); ClassOrNamespace::Table::const_iterator cit = enclosingTemplateClass->_classOrNamespaces.begin(); for (; cit != enclosingTemplateClass->_classOrNamespaces.end(); ++cit) { const Name *nestedName = cit->first; @@ -1720,9 +1708,7 @@ void CreateBindings::process(Document::Ptr doc) return; if (Namespace *globalNamespace = doc->globalNamespace()) { - if (! _processed.contains(globalNamespace)) { - _processed.insert(globalNamespace); - + if (Utils::insert(_processed, globalNamespace)) { const QList includes = doc->resolvedIncludes(); for (const Document::Include &i : includes) { if (Document::Ptr incl = _snapshot.document(i.resolvedFileName())) diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp index 7d9ccf6891b..a120d5b736f 100644 --- a/src/libs/cplusplus/ResolveExpression.cpp +++ b/src/libs/cplusplus/ResolveExpression.cpp @@ -6,7 +6,6 @@ #include "LookupContext.h" #include "Overview.h" #include "DeprecatedGenTemplateInstance.h" -#include "CppRewriter.h" #include "TypeOfExpression.h" #include @@ -20,6 +19,8 @@ #include #include +#include + #include #include #include @@ -142,9 +143,8 @@ private: for (const LookupItem &it : namedTypeItems) { Symbol *declaration = it.declaration(); if (declaration && declaration->isTypedef()) { - if (visited.contains(declaration)) + if (!Utils::insert(visited, declaration)) break; - visited.insert(declaration); // continue working with the typedefed type and scope if (type->type()->asPointerType()) { @@ -231,7 +231,7 @@ QList ResolveExpression::reference(ExpressionAST *ast, Scope *scope) QList ResolveExpression::resolve(ExpressionAST *ast, Scope *scope, bool ref) { if (! scope) - return QList(); + return {}; std::swap(_scope, scope); std::swap(_reference, ref); diff --git a/src/libs/cplusplus/SnapshotSymbolVisitor.cpp b/src/libs/cplusplus/SnapshotSymbolVisitor.cpp index 452c61531ad..512a4cffe7d 100644 --- a/src/libs/cplusplus/SnapshotSymbolVisitor.cpp +++ b/src/libs/cplusplus/SnapshotSymbolVisitor.cpp @@ -5,6 +5,8 @@ #include +#include + using namespace CPlusPlus; SnapshotSymbolVisitor::SnapshotSymbolVisitor(const Snapshot &snapshot) @@ -20,9 +22,7 @@ void SnapshotSymbolVisitor::accept(Document::Ptr doc) void SnapshotSymbolVisitor::accept(Document::Ptr doc, QSet *processed) { - if (doc && doc->globalNamespace() && ! processed->contains(doc->filePath().path())) { - processed->insert(doc->filePath().path()); - + if (doc && doc->globalNamespace() && Utils::insert(*processed, doc->filePath().path())) { const QList includes = doc->resolvedIncludes(); for (const Document::Include &i : includes) { if (Document::Ptr incl = _snapshot.document(i.resolvedFileName())) diff --git a/src/libs/cplusplus/TypeOfExpression.cpp b/src/libs/cplusplus/TypeOfExpression.cpp index 2776c19dd04..0cedec36b23 100644 --- a/src/libs/cplusplus/TypeOfExpression.cpp +++ b/src/libs/cplusplus/TypeOfExpression.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include using namespace Utils; @@ -134,9 +136,7 @@ ExpressionAST *TypeOfExpression::expressionAST() const void TypeOfExpression::processEnvironment(Document::Ptr doc, Environment *env, QSet *processed) const { - if (doc && ! processed->contains(doc->filePath().path())) { - processed->insert(doc->filePath().path()); - + if (doc && Utils::insert(*processed, doc->filePath().path())) { const QList includes = doc->resolvedIncludes(); for (const Document::Include &incl : includes) processEnvironment(m_snapshot.document(incl.resolvedFileName()), env, processed); diff --git a/src/libs/cplusplus/cplusplus.qbs b/src/libs/cplusplus/cplusplus.qbs index 0aed0ab438f..7e230504530 100644 --- a/src/libs/cplusplus/cplusplus.qbs +++ b/src/libs/cplusplus/cplusplus.qbs @@ -1,134 +1,131 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "CPlusPlus" - QtcLibrary { - cpp.includePaths: base.concat("../3rdparty") - cpp.defines: base.concat([ - "NDEBUG", - "CPLUSPLUS_BUILD_LIB" - ]) - cpp.optimization: "fast" + cpp.includePaths: base.concat("../3rdparty") + cpp.defines: base.concat([ + "NDEBUG", + "CPLUSPLUS_BUILD_LIB" + ]) + cpp.optimization: "fast" - Depends { name: "Qt.widgets" } - Depends { name: "Utils" } + Depends { name: "Qt.widgets" } + Depends { name: "Utils" } - Group { - name: "ThirdPartyCPlusPlus" - prefix: "../3rdparty/cplusplus/" - files: [ - "AST.cpp", - "AST.h", - "ASTClone.cpp", - "ASTMatch0.cpp", - "ASTMatcher.cpp", - "ASTMatcher.h", - "ASTPatternBuilder.h", - "ASTVisit.cpp", - "ASTVisitor.cpp", - "ASTVisitor.h", - "ASTfwd.h", - "Bind.cpp", - "Bind.h", - "CPlusPlus.h", - "Control.cpp", - "Control.h", - "CoreTypes.cpp", - "CoreTypes.h", - "DiagnosticClient.cpp", - "DiagnosticClient.h", - "FullySpecifiedType.cpp", - "FullySpecifiedType.h", - "Keywords.cpp", - "Keywords.kwgen", - "Lexer.cpp", - "Lexer.h", - "LiteralTable.h", - "Literals.cpp", - "Literals.h", - "Matcher.cpp", - "Matcher.h", - "MemoryPool.cpp", - "MemoryPool.h", - "Name.cpp", - "Name.h", - "NameVisitor.cpp", - "NameVisitor.h", - "Names.cpp", - "Names.h", - "ObjectiveCAtKeywords.cpp", - "ObjectiveCTypeQualifiers.cpp", - "ObjectiveCTypeQualifiers.h", - "Parser.cpp", - "Parser.h", - "QtContextKeywords.cpp", - "QtContextKeywords.h", - "SafeMatcher.cpp", - "SafeMatcher.h", - "Scope.cpp", - "Scope.h", - "Symbol.cpp", - "Symbol.h", - "SymbolVisitor.h", - "Symbols.cpp", - "Symbols.h", - "Templates.cpp", - "Templates.h", - "Token.cpp", - "Token.h", - "TranslationUnit.cpp", - "TranslationUnit.h", - "Type.cpp", - "Type.h", - "TypeVisitor.cpp", - "TypeVisitor.h", - ] - } + Group { + name: "ThirdPartyCPlusPlus" + prefix: "../3rdparty/cplusplus/" + files: [ + "AST.cpp", + "AST.h", + "ASTClone.cpp", + "ASTMatch0.cpp", + "ASTMatcher.cpp", + "ASTMatcher.h", + "ASTPatternBuilder.h", + "ASTVisit.cpp", + "ASTVisitor.cpp", + "ASTVisitor.h", + "ASTfwd.h", + "Bind.cpp", + "Bind.h", + "CPlusPlus.h", + "Control.cpp", + "Control.h", + "CoreTypes.cpp", + "CoreTypes.h", + "DiagnosticClient.cpp", + "DiagnosticClient.h", + "FullySpecifiedType.cpp", + "FullySpecifiedType.h", + "Keywords.cpp", + "Keywords.kwgen", + "Lexer.cpp", + "Lexer.h", + "LiteralTable.h", + "Literals.cpp", + "Literals.h", + "Matcher.cpp", + "Matcher.h", + "MemoryPool.cpp", + "MemoryPool.h", + "Name.cpp", + "Name.h", + "NameVisitor.cpp", + "NameVisitor.h", + "Names.cpp", + "Names.h", + "ObjectiveCAtKeywords.cpp", + "ObjectiveCTypeQualifiers.cpp", + "ObjectiveCTypeQualifiers.h", + "Parser.cpp", + "Parser.h", + "QtContextKeywords.cpp", + "QtContextKeywords.h", + "SafeMatcher.cpp", + "SafeMatcher.h", + "Scope.cpp", + "Scope.h", + "Symbol.cpp", + "Symbol.h", + "SymbolVisitor.h", + "Symbols.cpp", + "Symbols.h", + "Templates.cpp", + "Templates.h", + "Token.cpp", + "Token.h", + "TranslationUnit.cpp", + "TranslationUnit.h", + "Type.cpp", + "Type.h", + "TypeVisitor.cpp", + "TypeVisitor.h", + ] + } - Group { - name: "General" - files: [ - "AlreadyConsideredClassContainer.h", - "ASTParent.cpp", "ASTParent.h", - "ASTPath.cpp", "ASTPath.h", - "BackwardsScanner.cpp", "BackwardsScanner.h", - "CppDocument.cpp", "CppDocument.h", - "CppRewriter.cpp", "CppRewriter.h", - "cppmodelmanagerbase.cpp", "cppmodelmanagerbase.h", - "DependencyTable.cpp", "DependencyTable.h", - "DeprecatedGenTemplateInstance.cpp", "DeprecatedGenTemplateInstance.h", - "ExpressionUnderCursor.cpp", "ExpressionUnderCursor.h", - "FastPreprocessor.cpp", "FastPreprocessor.h", - "FindUsages.cpp", "FindUsages.h", - "Icons.cpp", "Icons.h", - "LookupContext.cpp", "LookupContext.h", - "LookupItem.cpp", "LookupItem.h", - "Macro.cpp", "Macro.h", - "MatchingText.cpp", "MatchingText.h", - "NamePrettyPrinter.cpp", "NamePrettyPrinter.h", - "Overview.cpp", "Overview.h", - "PPToken.cpp", "PPToken.h", - "PreprocessorClient.cpp", "PreprocessorClient.h", - "PreprocessorEnvironment.cpp", "PreprocessorEnvironment.h", - "ResolveExpression.cpp", "ResolveExpression.h", - "SimpleLexer.cpp", "SimpleLexer.h", - "SnapshotSymbolVisitor.cpp", "SnapshotSymbolVisitor.h", - "SymbolNameVisitor.cpp", "SymbolNameVisitor.h", - "TypeOfExpression.cpp", "TypeOfExpression.h", - "TypePrettyPrinter.cpp", "TypePrettyPrinter.h", - "findcdbbreakpoint.cpp", "findcdbbreakpoint.h", - "pp-cctype.h", - "pp-engine.cpp", "pp-engine.h", - "pp-scanner.cpp", "pp-scanner.h", - "pp.h", - ] - } + Group { + name: "General" + files: [ + "AlreadyConsideredClassContainer.h", + "ASTParent.cpp", "ASTParent.h", + "ASTPath.cpp", "ASTPath.h", + "BackwardsScanner.cpp", "BackwardsScanner.h", + "CppDocument.cpp", "CppDocument.h", + "CppRewriter.cpp", "CppRewriter.h", + "cppmodelmanagerbase.cpp", "cppmodelmanagerbase.h", + "declarationcomments.cpp", "declarationcomments.h", + "DependencyTable.cpp", "DependencyTable.h", + "DeprecatedGenTemplateInstance.cpp", "DeprecatedGenTemplateInstance.h", + "ExpressionUnderCursor.cpp", "ExpressionUnderCursor.h", + "FastPreprocessor.cpp", "FastPreprocessor.h", + "FindUsages.cpp", "FindUsages.h", + "Icons.cpp", "Icons.h", + "LookupContext.cpp", "LookupContext.h", + "LookupItem.cpp", "LookupItem.h", + "Macro.cpp", "Macro.h", + "MatchingText.cpp", "MatchingText.h", + "NamePrettyPrinter.cpp", "NamePrettyPrinter.h", + "Overview.cpp", "Overview.h", + "PPToken.cpp", "PPToken.h", + "PreprocessorClient.cpp", "PreprocessorClient.h", + "PreprocessorEnvironment.cpp", "PreprocessorEnvironment.h", + "ResolveExpression.cpp", "ResolveExpression.h", + "SimpleLexer.cpp", "SimpleLexer.h", + "SnapshotSymbolVisitor.cpp", "SnapshotSymbolVisitor.h", + "SymbolNameVisitor.cpp", "SymbolNameVisitor.h", + "TypeOfExpression.cpp", "TypeOfExpression.h", + "TypePrettyPrinter.cpp", "TypePrettyPrinter.h", + "findcdbbreakpoint.cpp", "findcdbbreakpoint.h", + "pp-cctype.h", + "pp-engine.cpp", "pp-engine.h", + "pp-scanner.cpp", "pp-scanner.h", + "pp.h", + ] + } - Export { - cpp.includePaths: [ - exportingProduct.sourceDirectory + "/../3rdparty" - ] - } + Export { + cpp.includePaths: [ + exportingProduct.sourceDirectory + "/../3rdparty" + ] } } diff --git a/src/libs/cplusplus/cppmodelmanagerbase.cpp b/src/libs/cplusplus/cppmodelmanagerbase.cpp index c869fd61a0f..410244ab96f 100644 --- a/src/libs/cplusplus/cppmodelmanagerbase.cpp +++ b/src/libs/cplusplus/cppmodelmanagerbase.cpp @@ -3,48 +3,60 @@ #include "cppmodelmanagerbase.h" -namespace CPlusPlus { +#include +#include -static CppModelManagerBase *g_instance = nullptr; +using namespace Utils; -CppModelManagerBase::CppModelManagerBase(QObject *parent) - : QObject(parent) +namespace CPlusPlus::CppModelManagerBase { + +static bool (*setExtraDiagnosticsCallback) + (const FilePath &, const QString &, const QList &) = nullptr; + +static CPlusPlus::Snapshot (*snapshotCallback)() = nullptr; + + +bool trySetExtraDiagnostics(const FilePath &filePath, const QString &kind, + const QList &diagnostics) { - Q_ASSERT(!g_instance); - g_instance = this; + if (!setExtraDiagnosticsCallback) + return false; + return setExtraDiagnosticsCallback(filePath, kind, diagnostics); } -CppModelManagerBase::~CppModelManagerBase() +bool setExtraDiagnostics(const FilePath &filePath, const QString &kind, + const QList &diagnostics) { - Q_ASSERT(g_instance == this); - g_instance = nullptr; + QTC_ASSERT(setExtraDiagnosticsCallback, return false); + return setExtraDiagnosticsCallback(filePath, kind, diagnostics); } -CppModelManagerBase *CppModelManagerBase::instance() +Snapshot snapshot() { - return g_instance; + QTC_ASSERT(snapshotCallback, return {}); + return snapshotCallback(); } -bool CppModelManagerBase::trySetExtraDiagnostics(const QString &fileName, const QString &kind, - const QList &diagnostics) +bool hasSnapshots() { - if (CppModelManagerBase *mm = instance()) - return mm->setExtraDiagnostics(fileName, kind, diagnostics); - return false; + return snapshotCallback; } -bool CppModelManagerBase::setExtraDiagnostics(const QString &fileName, const QString &kind, - const QList &diagnostics) +// Installation + +void registerSetExtraDiagnosticsCallback( + bool (*callback)(const FilePath &, const QString &, const QList &)) { - Q_UNUSED(fileName) - Q_UNUSED(kind) - Q_UNUSED(diagnostics) - return false; + QTC_ASSERT(callback, return); + QTC_CHECK(!setExtraDiagnosticsCallback); // bark when used twice + setExtraDiagnosticsCallback = callback; } -CPlusPlus::Snapshot CppModelManagerBase::snapshot() const +void registerSnapshotCallback(Snapshot (*callback)()) { - return CPlusPlus::Snapshot(); + QTC_ASSERT(callback, return); + QTC_CHECK(!snapshotCallback); // bark when used twice + snapshotCallback = callback; } -} // namespace CPlusPlus +} // CPlusPlus::CppModelManagerBase diff --git a/src/libs/cplusplus/cppmodelmanagerbase.h b/src/libs/cplusplus/cppmodelmanagerbase.h index d003fcf1dec..0a8e18e733f 100644 --- a/src/libs/cplusplus/cppmodelmanagerbase.h +++ b/src/libs/cplusplus/cppmodelmanagerbase.h @@ -5,29 +5,26 @@ #include -#include -#include +namespace Utils { class FilePath; } -QT_BEGIN_NAMESPACE -class QString; -QT_END_NAMESPACE +namespace CPlusPlus::CppModelManagerBase { -namespace CPlusPlus { +CPLUSPLUS_EXPORT bool trySetExtraDiagnostics + (const Utils::FilePath &filePath, const QString &, const QList &); -class CPLUSPLUS_EXPORT CppModelManagerBase : public QObject -{ - Q_OBJECT -public: - CppModelManagerBase(QObject *parent = nullptr); - ~CppModelManagerBase(); +CPLUSPLUS_EXPORT bool setSetExtraDiagnostics + (const Utils::FilePath &, const QString &, const QList &); - static CppModelManagerBase *instance(); - static bool trySetExtraDiagnostics(const QString &fileName, const QString &kind, - const QList &diagnostics); +CPLUSPLUS_EXPORT bool hasSnapshots(); - virtual bool setExtraDiagnostics(const QString &fileName, const QString &kind, - const QList &diagnostics); - virtual CPlusPlus::Snapshot snapshot() const; -}; +CPLUSPLUS_EXPORT CPlusPlus::Snapshot snapshot(); -} // namespace CPlusPlus + +// These callback are provided by the CppEditor plugin. + +CPLUSPLUS_EXPORT void registerSnapshotCallback(CPlusPlus::Snapshot (*)(void)); + +CPLUSPLUS_EXPORT void registerSetExtraDiagnosticsCallback( + bool(*)(const Utils::FilePath &, const QString &, const QList &)); + +} // CPlusPlus::CppModelManagerBase diff --git a/src/libs/cplusplus/declarationcomments.cpp b/src/libs/cplusplus/declarationcomments.cpp new file mode 100644 index 00000000000..f67ac1be9d5 --- /dev/null +++ b/src/libs/cplusplus/declarationcomments.cpp @@ -0,0 +1,175 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "declarationcomments.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace CPlusPlus { + +static QString nameFromSymbol(const Symbol *symbol) +{ + const QStringList symbolParts = Overview().prettyName(symbol->name()) + .split("::", Qt::SkipEmptyParts); + if (symbolParts.isEmpty()) + return {}; + return symbolParts.last(); +} + +static QList commentsForDeclaration( + const AST *decl, const QString &symbolName, const QTextDocument &textDoc, + const Document::Ptr &cppDoc, bool isParameter) +{ + if (symbolName.isEmpty()) + return {}; + + // Get the list of all tokens (including comments) and find the declaration start token there. + TranslationUnit * const tu = cppDoc->translationUnit(); + QTC_ASSERT(tu && tu->isParsed(), return {}); + const Token &declToken = tu->tokenAt(decl->firstToken()); + std::vector allTokens = tu->allTokens(); + QTC_ASSERT(!allTokens.empty(), return {}); + int tokenPos = -1; + for (int i = 0; i < int(allTokens.size()); ++i) { + if (allTokens.at(i).byteOffset == declToken.byteOffset) { + tokenPos = i; + break; + } + } + if (tokenPos == -1) + return {}; + + // Go backwards in the token list and collect all associated comments. + struct Comment { + Token token; + QTextBlock startBlock; + QTextBlock endBlock; + }; + QList comments; + Kind commentKind = T_EOF_SYMBOL; + const auto blockForTokenStart = [&](const Token &tok) { + return textDoc.findBlock(tu->getTokenPositionInDocument(tok, &textDoc)); + }; + const auto blockForTokenEnd = [&](const Token &tok) { + return textDoc.findBlock(tu->getTokenEndPositionInDocument(tok, &textDoc)); + }; + bool needsSymbolReference = isParameter; + for (int i = tokenPos - 1; i >= 0; --i) { + const Token &tok = allTokens.at(i); + if (!tok.isComment()) + break; + const QTextBlock tokenEndBlock = blockForTokenEnd(tok); + if (commentKind == T_EOF_SYMBOL) { + if (tokenEndBlock.next() != blockForTokenStart(declToken)) + needsSymbolReference = true; + commentKind = tok.kind(); + } else { + // If it's not the same kind of comment, it's not part of our comment block. + if (tok.kind() != commentKind) + break; + + // If there are empty lines between the comments, we don't consider them as + // belonging together. + if (tokenEndBlock.next() != comments.first().startBlock) + break; + } + + comments.push_front({tok, blockForTokenStart(tok), tokenEndBlock}); + } + + if (comments.isEmpty()) + return {}; + + const auto tokenList = [&] { + return Utils::transform>(comments, &Comment::token); + }; + + // We consider the comment block as associated with the symbol if it + // a) precedes it directly, without any empty lines in between or + // b) the symbol name occurs in it. + // Obviously, this heuristic can yield false positives in the case of very short names, + // but if a symbol is important enough to get documented, it should also have a proper name. + // Note that for function parameters, we always require the name to occur in the comment. + + if (!needsSymbolReference) // a) + return tokenList(); + + // b) + const Kind tokenKind = comments.first().token.kind(); + const bool isDoxygenComment = tokenKind == T_DOXY_COMMENT || tokenKind == T_CPP_DOXY_COMMENT; + const QRegularExpression symbolRegExp(QString("%1\\b%2\\b").arg( + isParameter && isDoxygenComment ? "[\\@]param\\s+" : QString(), symbolName)); + for (const Comment &c : std::as_const(comments)) { + for (QTextBlock b = c.startBlock; b.blockNumber() <= c.endBlock.blockNumber(); + b = b.next()) { + if (b.text().contains(symbolRegExp)) + return tokenList(); + } + } + return {}; +} + + +QList commentsForDeclaration(const Symbol *symbol, const QTextDocument &textDoc, + const Document::Ptr &cppDoc) +{ + QTC_ASSERT(cppDoc->translationUnit() && cppDoc->translationUnit()->isParsed(), return {}); + Utils::Text::Position pos; + cppDoc->translationUnit()->getTokenPosition(symbol->sourceLocation(), &pos.line, &pos.column); + --pos.column; + return commentsForDeclaration(nameFromSymbol(symbol), pos, textDoc, cppDoc); +} + +QList commentsForDeclaration(const QString &symbolName, const Utils::Text::Position &pos, + const QTextDocument &textDoc, const Document::Ptr &cppDoc) +{ + if (symbolName.isEmpty()) + return {}; + + // Find the symbol declaration's AST node. + // We stop at the last declaration node that precedes the symbol, except: + // - For parameter declarations, we just continue, because we are interested in the function. + // - If the declaration node is preceded directly by another one, we choose that one instead, + // because with nested declarations we want the outer one (e.g. templates). + const QList astPath = ASTPath(cppDoc)(pos.line, pos.column + 1); + if (astPath.isEmpty()) + return {}; + const AST *declAst = nullptr; + bool isParameter = false; + for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) { + AST * const node = *it; + if (node->asParameterDeclaration()) { + isParameter = true; + continue; + } + if (node->asDeclaration()) { + declAst = node; + continue; + } + if (declAst) + break; + } + if (!declAst) + return {}; + + return commentsForDeclaration(declAst, symbolName, textDoc, cppDoc, isParameter); +} + +QList commentsForDeclaration(const Symbol *symbol, const AST *decl, + const QTextDocument &textDoc, const Document::Ptr &cppDoc) +{ + return commentsForDeclaration(decl, nameFromSymbol(symbol), textDoc, cppDoc, + symbol->asArgument()); +} + +} // namespace CPlusPlus diff --git a/src/libs/cplusplus/declarationcomments.h b/src/libs/cplusplus/declarationcomments.h new file mode 100644 index 00000000000..7b775ad7549 --- /dev/null +++ b/src/libs/cplusplus/declarationcomments.h @@ -0,0 +1,35 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QTextDocument; +QT_END_NAMESPACE + +namespace Utils { namespace Text { class Position; } } + +namespace CPlusPlus { +class AST; +class Symbol; + +QList CPLUSPLUS_EXPORT commentsForDeclaration(const Symbol *symbol, + const QTextDocument &textDoc, + const Document::Ptr &cppDoc); + +QList CPLUSPLUS_EXPORT commentsForDeclaration(const Symbol *symbol, + const AST *decl, + const QTextDocument &textDoc, + const Document::Ptr &cppDoc); + +QList CPLUSPLUS_EXPORT commentsForDeclaration(const QString &symbolName, + const Utils::Text::Position &pos, + const QTextDocument &textDoc, + const Document::Ptr &cppDoc); + +} // namespace CPlusPlus diff --git a/src/libs/extensionsystem/CMakeLists.txt b/src/libs/extensionsystem/CMakeLists.txt index 0e4e6607a55..edc1daa2455 100644 --- a/src/libs/extensionsystem/CMakeLists.txt +++ b/src/libs/extensionsystem/CMakeLists.txt @@ -26,3 +26,8 @@ extend_qtc_library(ExtensionSystem DEPENDS Qt::Test DEFINES WITH_TESTS ) + +extend_qtc_library(ExtensionSystem + CONDITION TARGET Nanotrace + PUBLIC_DEPENDS Nanotrace +) diff --git a/src/libs/extensionsystem/extensionsystem.qbs b/src/libs/extensionsystem/extensionsystem.qbs index 39e8cbc57c6..8748c27ba69 100644 --- a/src/libs/extensionsystem/extensionsystem.qbs +++ b/src/libs/extensionsystem/extensionsystem.qbs @@ -1,45 +1,40 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "ExtensionSystem" - QtcLibrary { - cpp.defines: base.concat([ - "EXTENSIONSYSTEM_LIBRARY", - "IDE_TEST_DIR=\".\"" - ]) + cpp.defines: base.concat(["EXTENSIONSYSTEM_LIBRARY", "IDE_TEST_DIR=\".\""]) - Depends { name: "Qt"; submodules: ["core", "widgets"] } - Depends { name: "Aggregation" } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["core", "widgets"] } + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } - files: [ - "extensionsystem_global.h", - "extensionsystemtr.h", - "invoker.cpp", - "invoker.h", - "iplugin.cpp", - "iplugin.h", - "optionsparser.cpp", - "optionsparser.h", - "plugindetailsview.cpp", - "plugindetailsview.h", - "pluginerroroverview.cpp", - "pluginerroroverview.h", - "pluginerrorview.cpp", - "pluginerrorview.h", - "pluginmanager.cpp", - "pluginmanager.h", - "pluginmanager_p.h", - "pluginspec.cpp", - "pluginspec.h", - "pluginspec_p.h", - "pluginview.cpp", - "pluginview.h", - ] + Depends { name: "Aggregation" } + Depends { name: "Utils" } - Export { - Depends { name: "Qt.core" } - } + files: [ + "extensionsystem_global.h", + "extensionsystemtr.h", + "invoker.cpp", + "invoker.h", + "iplugin.cpp", + "iplugin.h", + "optionsparser.cpp", + "optionsparser.h", + "plugindetailsview.cpp", + "plugindetailsview.h", + "pluginerroroverview.cpp", + "pluginerroroverview.h", + "pluginerrorview.cpp", + "pluginerrorview.h", + "pluginmanager.cpp", + "pluginmanager.h", + "pluginmanager_p.h", + "pluginspec.cpp", + "pluginspec.h", + "pluginspec_p.h", + "pluginview.cpp", + "pluginview.h", + ] + + Export { + Depends { name: "Qt.core" } } } diff --git a/src/libs/extensionsystem/extensionsystem_global.h b/src/libs/extensionsystem/extensionsystem_global.h index 85fa8de831c..8e1c1ee463c 100644 --- a/src/libs/extensionsystem/extensionsystem_global.h +++ b/src/libs/extensionsystem/extensionsystem_global.h @@ -14,4 +14,16 @@ # define EXTENSIONSYSTEM_EXPORT Q_DECL_IMPORT #endif +#if defined(WITH_TESTS) +# if defined(EXTENSIONSYSTEM_LIBRARY) +# define EXTENSIONSYSTEM_TEST_EXPORT Q_DECL_EXPORT +# elif defined(EXTENSIONSYSTEM_STATIC_LIBRARY) +# define EXTENSIONSYSTEM_TEST_EXPORT +# else +# define EXTENSIONSYSTEM_TEST_EXPORT Q_DECL_IMPORT +# endif +#else +# define EXTENSIONSYSTEM_TEST_EXPORT +#endif + Q_DECLARE_LOGGING_CATEGORY(pluginLog) diff --git a/src/libs/extensionsystem/iplugin.cpp b/src/libs/extensionsystem/iplugin.cpp index 2af49255735..42ddd05bf68 100644 --- a/src/libs/extensionsystem/iplugin.cpp +++ b/src/libs/extensionsystem/iplugin.cpp @@ -161,47 +161,12 @@ namespace ExtensionSystem { namespace Internal { -class ObjectInitializer -{ -public: - ObjectCreator creator; - ObjectDestructor destructor; - ObjectCreationPolicy policy; -}; - class IPluginPrivate { public: - void tryCreateObjects(); - QList testCreators; - - QList objectInitializers; - QList> objectDestructors; - - // For debugging purposes: - QList createdObjects; // Not owned. }; -void IPluginPrivate::tryCreateObjects() -{ - QList unhandledObjectInitializers; - - for (const ObjectInitializer &initializer : std::as_const(objectInitializers)) { - if (!initializer.policy.dependsOn.isEmpty()) { - qWarning("Initialization dependencies are not supported yet"); - unhandledObjectInitializers.append(initializer); - continue; - } - - void *object = initializer.creator(); - createdObjects.append(object); - objectDestructors.append([initializer, object] { initializer.destructor(object); }); - } - - objectInitializers = unhandledObjectInitializers; -} - } // Internal /*! @@ -217,20 +182,10 @@ IPlugin::IPlugin() */ IPlugin::~IPlugin() { - for (const std::function &dtor : std::as_const(d->objectDestructors)) - dtor(); - delete d; d = nullptr; } -void IPlugin::addManagedHelper(const ObjectCreator &creator, - const ObjectDestructor &destructor, - const ObjectCreationPolicy &policy) -{ - d->objectInitializers.append({creator, destructor, policy}); -} - bool IPlugin::initialize(const QStringList &arguments, QString *errorString) { Q_UNUSED(arguments) @@ -239,14 +194,6 @@ bool IPlugin::initialize(const QStringList &arguments, QString *errorString) return true; } -/*! - \internal -*/ -void IPlugin::tryCreateObjects() -{ - d->tryCreateObjects(); -} - /*! Registers a function object that creates a test object with the owner \a creator. diff --git a/src/libs/extensionsystem/iplugin.h b/src/libs/extensionsystem/iplugin.h index 3e477e42e81..5a8fceb3d6e 100644 --- a/src/libs/extensionsystem/iplugin.h +++ b/src/libs/extensionsystem/iplugin.h @@ -5,8 +5,6 @@ #include "extensionsystem_global.h" -#include - #include #include @@ -17,17 +15,6 @@ namespace Internal { class IPluginPrivate; } using TestCreator = std::function; -using ObjectCreator = std::function; -using ObjectDestructor = std::function; - -struct EXTENSIONSYSTEM_EXPORT ObjectCreationPolicy -{ - // Can be empty if nothing depends on it. - Utils::Id id; - // Objects with empty dependencies are created as soon as possible. - QList dependsOn; -}; - class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject { Q_OBJECT @@ -52,7 +39,6 @@ public: // Deprecated in 10.0, use addTest() virtual QVector createTestObjects() const; - virtual void tryCreateObjects(); protected: virtual void initialize() {} @@ -61,17 +47,6 @@ protected: void addTest(Args && ...args) { addTestCreator([args...] { return new Test(args...); }); } void addTestCreator(const TestCreator &creator); - template - void addManaged(const ObjectCreationPolicy &policy = {}) { - addManagedHelper([]() -> void * { return new Type(); }, - [](void *p) { delete static_cast(p); }, - policy); - } - - void addManagedHelper(const ObjectCreator &creator, - const ObjectDestructor &destructor, - const ObjectCreationPolicy &policy); - signals: void asynchronousShutdownFinished(); diff --git a/src/libs/extensionsystem/optionsparser.cpp b/src/libs/extensionsystem/optionsparser.cpp index 8e3a5d900e7..205442488e6 100644 --- a/src/libs/extensionsystem/optionsparser.cpp +++ b/src/libs/extensionsystem/optionsparser.cpp @@ -22,6 +22,7 @@ const char *OptionsParser::TEST_OPTION = "-test"; const char *OptionsParser::NOTEST_OPTION = "-notest"; const char *OptionsParser::SCENARIO_OPTION = "-scenario"; const char *OptionsParser::PROFILE_OPTION = "-profile"; +const char *OptionsParser::TRACE_OPTION = "-trace"; const char *OptionsParser::NO_CRASHCHECK_OPTION = "-no-crashcheck"; OptionsParser::OptionsParser(const QStringList &args, @@ -60,6 +61,8 @@ bool OptionsParser::parse() continue; if (checkForProfilingOption()) continue; + if (checkForTraceOption()) + continue; if (checkForNoCrashcheckOption()) continue; #ifdef WITH_TESTS @@ -239,7 +242,17 @@ bool OptionsParser::checkForProfilingOption() { if (m_currentArg != QLatin1String(PROFILE_OPTION)) return false; - m_pmPrivate->initProfiling(); + m_pmPrivate->increaseProfilingVerbosity(); + return true; +} + +bool OptionsParser::checkForTraceOption() +{ + if (m_currentArg != QLatin1String(TRACE_OPTION)) + return false; + if (nextToken(RequiredToken)) { + m_pmPrivate->enableTracing(m_currentArg); + } return true; } diff --git a/src/libs/extensionsystem/optionsparser.h b/src/libs/extensionsystem/optionsparser.h index ee11582c3b0..93f3812c8d3 100644 --- a/src/libs/extensionsystem/optionsparser.h +++ b/src/libs/extensionsystem/optionsparser.h @@ -28,6 +28,7 @@ public: static const char *NOTEST_OPTION; static const char *SCENARIO_OPTION; static const char *PROFILE_OPTION; + static const char *TRACE_OPTION; static const char *NO_CRASHCHECK_OPTION; private: @@ -41,6 +42,7 @@ private: bool checkForAppOption(); bool checkForPluginOption(); bool checkForProfilingOption(); + bool checkForTraceOption(); bool checkForNoCrashcheckOption(); bool checkForUnknownOption(); void forceDisableAllPluginsExceptTestedAndForceEnabled(); diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 45b9807958e..1f86693f907 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -10,6 +10,8 @@ #include "pluginspec.h" #include "pluginspec_p.h" +#include + #include #include #include @@ -373,10 +375,8 @@ const QSet PluginManager::pluginsRequiredByPlugin(PluginSpec *spec if (depIt.key().type != PluginDependency::Required) continue; PluginSpec *depSpec = depIt.value(); - if (!recursiveDependencies.contains(depSpec)) { - recursiveDependencies.insert(depSpec); + if (Utils::insert(recursiveDependencies, depSpec)) queue.push(depSpec); - } } } recursiveDependencies.remove(spec); @@ -490,7 +490,7 @@ void PluginManager::setSettings(QtcSettings *settings) default disabled plugins. Needs to be set before the plugin search path is set with setPluginPaths(). */ -void PluginManager::setGlobalSettings(QtcSettings *settings) +void PluginManager::setInstallSettings(QtcSettings *settings) { d->setGlobalSettings(settings); } @@ -723,6 +723,12 @@ void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int d formatOption(str, QLatin1String(OptionsParser::PROFILE_OPTION), QString(), QLatin1String("Profile plugin loading"), optionIndentation, descriptionIndentation); + formatOption(str, + QLatin1String(OptionsParser::TRACE_OPTION), + QLatin1String("file"), + QLatin1String("Write trace file (CTF) for plugin loading"), + optionIndentation, + descriptionIndentation); formatOption(str, QLatin1String(OptionsParser::NO_CRASHCHECK_OPTION), QString(), @@ -882,16 +888,6 @@ PluginManager::ProcessData PluginManager::creatorProcessData() return d->m_creatorProcessData; } -/*! - \internal -*/ - -void PluginManager::profilingReport(const char *what, const PluginSpec *spec) -{ - d->profilingReport(what, spec); -} - - /*! Returns a list of plugins in load order. */ @@ -942,23 +938,30 @@ PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec) return spec->d; } -void PluginManagerPrivate::nextDelayedInitialize() +void PluginManagerPrivate::startDelayedInitialize() { - while (!delayedInitializeQueue.empty()) { - PluginSpec *spec = delayedInitializeQueue.front(); - delayedInitializeQueue.pop(); - profilingReport(">delayedInitialize", spec); - bool delay = spec->d->delayedInitialize(); - profilingReport("name().toStdString(); + delayedInitializeQueue.pop(); + NANOTRACE_SCOPE(specName, specName + "::delayedInitialized"); + profilingReport(">delayedInitialize", spec); + bool delay = spec->d->delayedInitialize(); + profilingReport("d->performanceData.delayedInitialize); + if (delay) // give UI a bit of breathing space, but prevent user interaction + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } + Utils::setMimeStartupPhase(MimeStartupPhase::UpAndRunning); m_isInitializationDone = true; - delete delayedInitializeTimer; - delayedInitializeTimer = nullptr; - profilingSummary(); - emit q->initializationDone(); + if (m_profileTimer) + m_totalStartupMS = m_profileTimer->elapsed(); + printProfilingSummary(); + } + NANOTRACE_SHUTDOWN(); + emit q->initializationDone(); #ifdef WITH_TESTS if (PluginManager::testRunRequested()) startTests(); @@ -971,9 +974,6 @@ void PluginManagerPrivate::nextDelayedInitialize() } } #endif - } else { - delayedInitializeTimer->start(); - } } /*! @@ -1020,12 +1020,12 @@ void PluginManagerPrivate::writeSettings() void PluginManagerPrivate::readSettings() { if (globalSettings) { - defaultDisabledPlugins = globalSettings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList(); - defaultEnabledPlugins = globalSettings->value(QLatin1String(C_FORCEENABLED_PLUGINS)).toStringList(); + defaultDisabledPlugins = globalSettings->value(C_IGNORED_PLUGINS).toStringList(); + defaultEnabledPlugins = globalSettings->value(C_FORCEENABLED_PLUGINS).toStringList(); } if (settings) { - disabledPlugins = settings->value(QLatin1String(C_IGNORED_PLUGINS)).toStringList(); - forceEnabledPlugins = settings->value(QLatin1String(C_FORCEENABLED_PLUGINS)).toStringList(); + disabledPlugins = settings->value(C_IGNORED_PLUGINS).toStringList(); + forceEnabledPlugins = settings->value(C_FORCEENABLED_PLUGINS).toStringList(); } } @@ -1035,11 +1035,7 @@ void PluginManagerPrivate::readSettings() void PluginManagerPrivate::stopAll() { m_isShuttingDown = true; - if (delayedInitializeTimer && delayedInitializeTimer->isActive()) { - delayedInitializeTimer->stop(); - delete delayedInitializeTimer; - delayedInitializeTimer = nullptr; - } + delayedInitializeTimer.stop(); const QVector queue = loadQueue(); for (PluginSpec *spec : queue) @@ -1305,7 +1301,7 @@ void PluginManagerPrivate::addObject(QObject *obj) if (debugLeaks) qDebug() << "PluginManagerPrivate::addObject" << obj << obj->objectName(); - if (m_profilingVerbosity && !m_profileTimer.isNull()) { + if (m_profilingVerbosity > 1 && m_profileTimer) { // Report a timestamp when adding an object. Useful for profiling // its initialization time. const int absoluteElapsedMS = int(m_profileTimer->elapsed()); @@ -1345,34 +1341,45 @@ void PluginManagerPrivate::removeObject(QObject *obj) */ void PluginManagerPrivate::loadPlugins() { + if (m_profilingVerbosity > 0) + qDebug("Profiling started"); + const QVector queue = loadQueue(); Utils::setMimeStartupPhase(MimeStartupPhase::PluginsLoading); - for (PluginSpec *spec : queue) - loadPlugin(spec, PluginSpec::Loaded); + { + NANOTRACE_SCOPE("ExtensionSystem", "Load"); + for (PluginSpec *spec : queue) + loadPlugin(spec, PluginSpec::Loaded); + } Utils::setMimeStartupPhase(MimeStartupPhase::PluginsInitializing); - for (PluginSpec *spec : queue) - loadPlugin(spec, PluginSpec::Initialized); + { + NANOTRACE_SCOPE("ExtensionSystem", "Initialize"); + for (PluginSpec *spec : queue) + loadPlugin(spec, PluginSpec::Initialized); + } - Utils::setMimeStartupPhase(MimeStartupPhase::PluginsDelayedInitializing); - Utils::reverseForeach(queue, [this](PluginSpec *spec) { - loadPlugin(spec, PluginSpec::Running); - if (spec->state() == PluginSpec::Running) { - delayedInitializeQueue.push(spec); - } else { - // Plugin initialization failed, so cleanup after it - spec->d->kill(); - } - }); + { + NANOTRACE_SCOPE("ExtensionSystem", "ExtensionsInitialized"); + Utils::reverseForeach(queue, [this](PluginSpec *spec) { + loadPlugin(spec, PluginSpec::Running); + if (spec->state() == PluginSpec::Running) { + delayedInitializeQueue.push(spec); + } else { + // Plugin initialization failed, so cleanup after it + spec->d->kill(); + } + }); + } emit q->pluginsChanged(); - Utils::setMimeStartupPhase(MimeStartupPhase::UpAndRunning); - delayedInitializeTimer = new QTimer; - delayedInitializeTimer->setInterval(DELAYED_INITIALIZE_INTERVAL); - delayedInitializeTimer->setSingleShot(true); - connect(delayedInitializeTimer, &QTimer::timeout, - this, &PluginManagerPrivate::nextDelayedInitialize); - delayedInitializeTimer->start(); + delayedInitializeTimer.setInterval(DELAYED_INITIALIZE_INTERVAL); + delayedInitializeTimer.setSingleShot(true); + connect(&delayedInitializeTimer, + &QTimer::timeout, + this, + &PluginManagerPrivate::startDelayedInitialize); + delayedInitializeTimer.start(); } /*! @@ -1482,7 +1489,7 @@ public: static std::optional lockedPluginName(PluginManagerPrivate *pm) { const QString lockFilePath = LockFile::filePath(pm); - if (QFile::exists(lockFilePath)) { + if (QFileInfo::exists(lockFilePath)) { QFile f(lockFilePath); if (f.open(QIODevice::ReadOnly)) { const auto pluginName = QString::fromUtf8(f.readLine()).trimmed(); @@ -1533,8 +1540,7 @@ void PluginManagerPrivate::checkForProblematicPlugins() : Tr::tr("Help > About Plugins"); const QString otherPluginsText = Tr::tr("If you temporarily disable %1, the following plugins that depend on " - "it are also disabled: %2.\n\n") - .arg(spec->name(), dependentsList); + "it are also disabled: %2.").arg(spec->name(), dependentsList) + "\n\n"; const QString detailsText = (dependents.isEmpty() ? QString() : otherPluginsText) + Tr::tr("Disable plugins permanently in %1.").arg(pluginsMenu); const QString text = Tr::tr("The last time you started %1, it seems to have closed because " @@ -1589,12 +1595,18 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt if (enableCrashCheck && destState < PluginSpec::Stopped) lockFile.reset(new LockFile(this, spec)); + const std::string specName = spec->name().toStdString(); + switch (destState) { - case PluginSpec::Running: + case PluginSpec::Running: { + NANOTRACE_SCOPE(specName, specName + "::extensionsInitialized"); profilingReport(">initializeExtensions", spec); spec->d->initializeExtensions(); - profilingReport("d->performanceData.extensionsInitialized); return; + } case PluginSpec::Deleted: profilingReport(">delete", spec); spec->d->kill(); @@ -1618,16 +1630,20 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt } } switch (destState) { - case PluginSpec::Loaded: + case PluginSpec::Loaded: { + NANOTRACE_SCOPE(specName, specName + "::load"); profilingReport(">loadLibrary", spec); spec->d->loadLibrary(); - profilingReport("d->performanceData.load); break; - case PluginSpec::Initialized: + } + case PluginSpec::Initialized: { + NANOTRACE_SCOPE(specName, specName + "::initialize"); profilingReport(">initializePlugin", spec); spec->d->initializePlugin(); - profilingReport("d->performanceData.initialize); break; + } case PluginSpec::Stopped: profilingReport(">stop", spec); if (spec->d->stop() == IPlugin::AsynchronousShutdown) { @@ -1762,58 +1778,83 @@ PluginSpec *PluginManagerPrivate::pluginByName(const QString &name) const return Utils::findOrDefault(pluginSpecs, [name](PluginSpec *spec) { return spec->name() == name; }); } -void PluginManagerPrivate::initProfiling() +void PluginManagerPrivate::increaseProfilingVerbosity() { - if (m_profileTimer.isNull()) { - m_profileTimer.reset(new QElapsedTimer); - m_profileTimer->start(); - m_profileElapsedMS = 0; - qDebug("Profiling started"); - } else { - m_profilingVerbosity++; - } + m_profilingVerbosity++; + if (!m_profileTimer) + PluginManager::startProfiling(); } -void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *spec /* = 0 */) +void PluginManagerPrivate::enableTracing(const QString &filePath) { - if (!m_profileTimer.isNull()) { - const int absoluteElapsedMS = int(m_profileTimer->elapsed()); - const int elapsedMS = absoluteElapsedMS - m_profileElapsedMS; + const QString jsonFilePath = filePath.endsWith(".json") ? filePath : filePath + ".json"; +#ifdef NANOTRACE_ENABLED + qDebug() << "Trace event file (CTF) will be saved at" << qPrintable(jsonFilePath); +#endif + NANOTRACE_INIT(QCoreApplication::applicationName().toStdString(), + "Main", + jsonFilePath.toStdString()); +} + +void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *spec, qint64 *target) +{ + if (m_profileTimer) { + const qint64 absoluteElapsedMS = m_profileTimer->elapsed(); + const qint64 elapsedMS = absoluteElapsedMS - m_profileElapsedMS; m_profileElapsedMS = absoluteElapsedMS; - if (spec) - qDebug("%-22s %-22s %8dms (%8dms)", what, qPrintable(spec->name()), absoluteElapsedMS, elapsedMS); - else - qDebug("%-45s %8dms (%8dms)", what, absoluteElapsedMS, elapsedMS); - if (what && *what == '<') { + if (m_profilingVerbosity > 0) { + qDebug("%-22s %-40s %8lldms (%8lldms)", + what, + qPrintable(spec->name()), + absoluteElapsedMS, + elapsedMS); + } + if (target) { QString tc; - if (spec) { - m_profileTotal[spec] += elapsedMS; - tc = spec->name() + '_'; - } + *target = elapsedMS; + tc = spec->name() + '_'; tc += QString::fromUtf8(QByteArray(what + 1)); Utils::Benchmarker::report("loadPlugins", tc, elapsedMS); } } } -void PluginManagerPrivate::profilingSummary() const +QString PluginManagerPrivate::profilingSummary(qint64 *totalOut) const { - if (!m_profileTimer.isNull()) { - QMultiMap sorter; - int total = 0; + QString summary; + const QVector specs = Utils::sorted(pluginSpecs, + [](PluginSpec *s1, PluginSpec *s2) { + return s1->performanceData().total() + < s2->performanceData().total(); + }); + const qint64 total + = std::accumulate(specs.constBegin(), specs.constEnd(), 0, [](qint64 t, PluginSpec *s) { + return t + s->performanceData().total(); + }); + for (PluginSpec *s : specs) { + if (!s->isEffectivelyEnabled()) + continue; + const qint64 t = s->performanceData().total(); + summary += QString("%1 %2ms ( %3% ) (%4)\n") + .arg(s->name(), -34) + .arg(t, 8) + .arg(100.0 * t / total, 5, 'f', 2) + .arg(s->performanceData().summary()); + } + summary += QString("Total plugins: %1ms\n").arg(total, 8); + summary += QString("Total startup: %1ms\n").arg(m_totalStartupMS, 8); + if (totalOut) + *totalOut = total; + return summary; +} - auto totalEnd = m_profileTotal.constEnd(); - for (auto it = m_profileTotal.constBegin(); it != totalEnd; ++it) { - sorter.insert(it.value(), it.key()); - total += it.value(); - } - - auto sorterEnd = sorter.constEnd(); - for (auto it = sorter.constBegin(); it != sorterEnd; ++it) - qDebug("%-22s %8dms ( %5.2f%% )", qPrintable(it.value()->name()), - it.key(), 100.0 * it.key() / total); - qDebug("Total: %8dms", total); - Utils::Benchmarker::report("loadPlugins", "Total", total); +void PluginManagerPrivate::printProfilingSummary() const +{ + if (m_profilingVerbosity > 0) { + qint64 total; + const QString summary = profilingSummary(&total); + qDebug() << qPrintable(summary); + Utils::Benchmarker::report("loadPlugins", "Total", total); } } @@ -1857,4 +1898,11 @@ QObject *PluginManager::getObjectByName(const QString &name) }); } +void PluginManager::startProfiling() +{ + d->m_profileTimer.reset(new QElapsedTimer); + d->m_profileTimer->start(); + d->m_profileElapsedMS = 0; +} + } // ExtensionSystem diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h index ecd0ee70b73..e7c6a315259 100644 --- a/src/libs/extensionsystem/pluginmanager.h +++ b/src/libs/extensionsystem/pluginmanager.h @@ -64,6 +64,7 @@ public: static QObject *getObjectByName(const QString &name); + static void startProfiling(); // Plugin operations static QVector loadQueue(); static void loadPlugins(); @@ -83,7 +84,7 @@ public: // Settings static void setSettings(Utils::QtcSettings *settings); static Utils::QtcSettings *settings(); - static void setGlobalSettings(Utils::QtcSettings *settings); + static void setInstallSettings(Utils::QtcSettings *settings); static Utils::QtcSettings *globalSettings(); static void writeSettings(); @@ -124,8 +125,6 @@ public: static void setCreatorProcessData(const ProcessData &data); static ProcessData creatorProcessData(); - static void profilingReport(const char *what, const PluginSpec *spec = nullptr); - static QString platformName(); static bool isInitializationDone(); diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h index c7a4291a6b7..3de21388bbb 100644 --- a/src/libs/extensionsystem/pluginmanager_p.h +++ b/src/libs/extensionsystem/pluginmanager_p.h @@ -15,13 +15,13 @@ #include #include #include +#include #include #include QT_BEGIN_NAMESPACE class QTime; -class QTimer; class QEventLoop; QT_END_NAMESPACE @@ -38,9 +38,8 @@ namespace Internal { class PluginSpecPrivate; -class EXTENSIONSYSTEM_EXPORT PluginManagerPrivate : public QObject +class EXTENSIONSYSTEM_TEST_EXPORT PluginManagerPrivate : public QObject { - Q_OBJECT public: PluginManagerPrivate(PluginManager *pluginManager); ~PluginManagerPrivate() override; @@ -58,9 +57,11 @@ public: void loadPlugin(PluginSpec *spec, PluginSpec::State destState); void resolveDependencies(); void enableDependenciesIndirectly(); - void initProfiling(); - void profilingSummary() const; - void profilingReport(const char *what, const PluginSpec *spec = nullptr); + void increaseProfilingVerbosity(); + void enableTracing(const QString &filePath); + QString profilingSummary(qint64 *totalOut = nullptr) const; + void printProfilingSummary() const; + void profilingReport(const char *what, const PluginSpec *spec, qint64 *target = nullptr); void setSettings(Utils::QtcSettings *settings); void setGlobalSettings(Utils::QtcSettings *settings); void readSettings(); @@ -97,7 +98,7 @@ public: QStringList disabledPlugins; QStringList forceEnabledPlugins; // delayed initialization - QTimer *delayedInitializeTimer = nullptr; + QTimer delayedInitializeTimer; std::queue delayedInitializeQueue; // ansynchronous shutdown QSet asynchronousPlugins; // plugins that have requested async shutdown @@ -106,8 +107,9 @@ public: QStringList arguments; QStringList argumentsForRestart; QScopedPointer m_profileTimer; - QHash m_profileTotal; - int m_profileElapsedMS = 0; + qint64 m_profileElapsedMS = 0; + qint64 m_totalUntilDelayedInitialize = 0; + qint64 m_totalStartupMS = 0; unsigned m_profilingVerbosity = 0; Utils::QtcSettings *settings = nullptr; Utils::QtcSettings *globalSettings = nullptr; @@ -140,7 +142,7 @@ public: private: PluginManager *q; - void nextDelayedInitialize(); + void startDelayedInitialize(); void readPluginPaths(); bool loadQueue(PluginSpec *spec, diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 62b3c0096f6..bbc4e3541c0 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -402,6 +402,11 @@ QJsonObject PluginSpec::metaData() const return d->metaData; } +const PerformanceData &PluginSpec::performanceData() const +{ + return d->performanceData; +} + /*! Returns a list of descriptions of command line arguments the plugin processes. */ @@ -1119,7 +1124,6 @@ bool PluginSpecPrivate::initializePlugin() hasError = true; return false; } - plugin->tryCreateObjects(); state = PluginSpec::Initialized; return true; } @@ -1146,7 +1150,6 @@ bool PluginSpecPrivate::initializeExtensions() return false; } plugin->extensionsInitialized(); - plugin->tryCreateObjects(); state = PluginSpec::Running; return true; } @@ -1167,7 +1170,6 @@ bool PluginSpecPrivate::delayedInitialize() return false; } const bool res = plugin->delayedInitialize(); - plugin->tryCreateObjects(); return res; } diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index 9abb4bf4475..6030bc568e4 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -53,6 +53,24 @@ struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription QString description; }; +struct EXTENSIONSYSTEM_EXPORT PerformanceData +{ + qint64 load = 0; + qint64 initialize = 0; + qint64 extensionsInitialized = 0; + qint64 delayedInitialize = 0; + + qint64 total() const { return load + initialize + extensionsInitialized + delayedInitialize; } + QString summary() const + { + return QString("l: %1ms, i: %2ms, x: %3ms, d: %4ms") + .arg(load, 3) + .arg(initialize, 3) + .arg(extensionsInitialized, 3) + .arg(delayedInitialize, 3); + } +}; + class EXTENSIONSYSTEM_EXPORT PluginSpec { public: @@ -84,6 +102,7 @@ public: bool isForceDisabled() const; QVector dependencies() const; QJsonObject metaData() const; + const PerformanceData &performanceData() const; using PluginArgumentDescriptions = QVector; PluginArgumentDescriptions argumentDescriptions() const; diff --git a/src/libs/extensionsystem/pluginspec_p.h b/src/libs/extensionsystem/pluginspec_p.h index 568ab943a31..f802902e53c 100644 --- a/src/libs/extensionsystem/pluginspec_p.h +++ b/src/libs/extensionsystem/pluginspec_p.h @@ -22,7 +22,7 @@ class IPlugin; namespace Internal { -class EXTENSIONSYSTEM_EXPORT PluginSpecPrivate : public QObject +class EXTENSIONSYSTEM_TEST_EXPORT PluginSpecPrivate : public QObject { Q_OBJECT @@ -84,6 +84,8 @@ public: bool hasError = false; QString errorString; + PerformanceData performanceData; + static bool isValidVersion(const QString &version); static int versionCompare(const QString &version1, const QString &version2); diff --git a/src/libs/extensionsystem/pluginview.cpp b/src/libs/extensionsystem/pluginview.cpp index 693dd3c471e..f4afedc01ea 100644 --- a/src/libs/extensionsystem/pluginview.cpp +++ b/src/libs/extensionsystem/pluginview.cpp @@ -275,7 +275,6 @@ PluginView::PluginView(QWidget *parent) m_categoryView = new TreeView(this); m_categoryView->setAlternatingRowColors(true); m_categoryView->setIndentation(20); - m_categoryView->setUniformRowHeights(true); m_categoryView->setSortingEnabled(true); m_categoryView->setColumnWidth(LoadedColumn, 40); m_categoryView->header()->setDefaultSectionSize(120); diff --git a/src/libs/glsl/glslsymbol.cpp b/src/libs/glsl/glslsymbol.cpp index 9b2630872c6..fb1324aacf9 100644 --- a/src/libs/glsl/glslsymbol.cpp +++ b/src/libs/glsl/glslsymbol.cpp @@ -52,5 +52,5 @@ Symbol *Scope::lookup(const QString &name) const QList Scope::members() const { - return QList(); + return {}; } diff --git a/src/libs/languageserverprotocol/clientcapabilities.cpp b/src/libs/languageserverprotocol/clientcapabilities.cpp index 321b46ecd2e..acae7ad554a 100644 --- a/src/libs/languageserverprotocol/clientcapabilities.cpp +++ b/src/libs/languageserverprotocol/clientcapabilities.cpp @@ -77,4 +77,46 @@ bool SemanticTokensClientCapabilities::isValid() const && contains(formatsKey); } +const char resourceOperationCreate[] = "create"; +const char resourceOperationRename[] = "rename"; +const char resourceOperationDelete[] = "delete"; + +std::optional> +WorkspaceClientCapabilities::WorkspaceEditCapabilities::resourceOperations() const +{ + if (!contains(resourceOperationsKey)) + return std::nullopt; + QList result; + for (const QJsonValue &value : this->value(resourceOperationsKey).toArray()) { + const QString str = value.toString(); + if (str == resourceOperationCreate) + result << ResourceOperationKind::Create; + else if (str == resourceOperationRename) + result << ResourceOperationKind::Rename; + else if (str == resourceOperationDelete) + result << ResourceOperationKind::Delete; + } + return result; +} + +void WorkspaceClientCapabilities::WorkspaceEditCapabilities::setResourceOperations( + const QList &resourceOperations) +{ + QJsonArray array; + for (const auto &kind : resourceOperations) { + switch (kind) { + case ResourceOperationKind::Create: + array << resourceOperationCreate; + break; + case ResourceOperationKind::Rename: + array << resourceOperationRename; + break; + case ResourceOperationKind::Delete: + array << resourceOperationDelete; + break; + } + } + insert(resourceOperationsKey, array); +} + } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h index 92f78ae0562..584c4b8d539 100644 --- a/src/libs/languageserverprotocol/clientcapabilities.h +++ b/src/libs/languageserverprotocol/clientcapabilities.h @@ -555,6 +555,14 @@ public: void setDocumentChanges(bool documentChanges) { insert(documentChangesKey, documentChanges); } void clearDocumentChanges() { remove(documentChangesKey); } + + enum class ResourceOperationKind { Create, Rename, Delete }; + + // The resource operations the client supports. Clients should at least support 'create', + // 'rename' and 'delete' files and folders. + std::optional> resourceOperations() const; + void setResourceOperations(const QList &resourceOperations); + void clearResourceOperations() { remove(resourceOperationsKey); } }; // Capabilities specific to `WorkspaceEdit`s @@ -628,7 +636,7 @@ public: void clearWorkDoneProgress() { remove(workDoneProgressKey); } private: - constexpr static const char16_t workDoneProgressKey[] = u"workDoneProgress"; + constexpr static const char workDoneProgressKey[] = "workDoneProgress"; }; class LANGUAGESERVERPROTOCOL_EXPORT ClientCapabilities : public JsonObject diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h index 302c57bc209..250b5ca87c1 100644 --- a/src/libs/languageserverprotocol/jsonkeys.h +++ b/src/libs/languageserverprotocol/jsonkeys.h @@ -5,220 +5,227 @@ namespace LanguageServerProtocol { -constexpr char16_t actionsKey[] = u"actions"; -constexpr char16_t activeParameterKey[] = u"activeParameter"; -constexpr char16_t activeParameterSupportKey[] = u"activeParameterSupport"; -constexpr char16_t activeSignatureKey[] = u"activeSignature"; -constexpr char16_t addedKey[] = u"added"; -constexpr char16_t additionalTextEditsKey[] = u"additionalTextEdits"; -constexpr char16_t alphaKey[] = u"alpha"; -constexpr char16_t appliedKey[] = u"applied"; -constexpr char16_t applyEditKey[] = u"applyEdit"; -constexpr char16_t argumentsKey[] = u"arguments"; -constexpr char16_t blueKey[] = u"blue"; -constexpr char16_t callHierarchyKey[] = u"callHierarchy"; -constexpr char16_t callHierarchyProviderKey[] = u"callHierarchyProvider"; -constexpr char16_t cancellableKey[] = u"cancellable"; -constexpr char16_t capabilitiesKey[] = u"capabilities"; -constexpr char16_t chKey[] = u"ch"; -constexpr char16_t changeKey[] = u"change"; -constexpr char16_t changeNotificationsKey[] = u"changeNotifications"; -constexpr char16_t changesKey[] = u"changes"; -constexpr char16_t characterKey[] = u"character"; -constexpr char16_t childrenKey[] = u"children"; -constexpr char16_t clientInfoKey[] = u"clientInfo"; -constexpr char16_t codeActionKey[] = u"codeAction"; -constexpr char16_t codeActionKindKey[] = u"codeActionKind"; -constexpr char16_t codeActionKindsKey[] = u"codeActionKinds"; -constexpr char16_t codeActionLiteralSupportKey[] = u"codeActionLiteralSupport"; -constexpr char16_t codeActionProviderKey[] = u"codeActionProvider"; -constexpr char16_t codeKey[] = u"code"; -constexpr char16_t codeLensKey[] = u"codeLens"; -constexpr char16_t codeLensProviderKey[] = u"codeLensProvider"; -constexpr char16_t colorInfoKey[] = u"colorInfo"; -constexpr char16_t colorKey[] = u"color"; -constexpr char16_t colorProviderKey[] = u"colorProvider"; -constexpr char16_t commandKey[] = u"command"; -constexpr char16_t commandsKey[] = u"commands"; -constexpr char16_t commitCharacterSupportKey[] = u"commitCharacterSupport"; -constexpr char16_t commitCharactersKey[] = u"commitCharacters"; -constexpr char16_t completionItemKey[] = u"completionItem"; -constexpr char16_t completionItemKindKey[] = u"completionItemKind"; -constexpr char16_t completionKey[] = u"completion"; -constexpr char16_t completionProviderKey[] = u"completionProvider"; -constexpr char16_t configurationKey[] = u"configuration"; -constexpr char16_t containerNameKey[] = u"containerName"; -constexpr char16_t contentChangesKey[] = u"contentChanges"; -constexpr char16_t contentFormatKey[] = u"contentFormat"; -constexpr char16_t contentKey[] = u"value"; -constexpr char16_t contentsKey[] = u"contents"; -constexpr char16_t contextKey[] = u"context"; -constexpr char16_t contextSupportKey[] = u"contextSupport"; -constexpr char16_t dataKey[] = u"data"; -constexpr char16_t definitionKey[] = u"definition"; -constexpr char16_t definitionProviderKey[] = u"definitionProvider"; -constexpr char16_t deleteCountKey[] = u"deleteCount"; -constexpr char16_t deltaKey[] = u"delta"; -constexpr char16_t deprecatedKey[] = u"deprecated"; -constexpr char16_t detailKey[] = u"detail"; -constexpr char16_t diagnosticsKey[] = u"diagnostics"; -constexpr char16_t didChangeConfigurationKey[] = u"didChangeConfiguration"; -constexpr char16_t didChangeWatchedFilesKey[] = u"didChangeWatchedFiles"; -constexpr char16_t didSaveKey[] = u"didSave"; -constexpr char16_t documentChangesKey[] = u"documentChanges"; -constexpr char16_t documentFormattingProviderKey[] = u"documentFormattingProvider"; -constexpr char16_t documentHighlightKey[] = u"documentHighlight"; -constexpr char16_t documentHighlightProviderKey[] = u"documentHighlightProvider"; -constexpr char16_t documentLinkKey[] = u"documentLink"; -constexpr char16_t documentLinkProviderKey[] = u"documentLinkProvider"; -constexpr char16_t documentRangeFormattingProviderKey[] = u"documentRangeFormattingProvider"; -constexpr char16_t documentSelectorKey[] = u"documentSelector"; -constexpr char16_t documentSymbolKey[] = u"documentSymbol"; -constexpr char16_t documentSymbolProviderKey[] = u"documentSymbolProvider"; -constexpr char16_t documentationFormatKey[] = u"documentationFormat"; -constexpr char16_t documentationKey[] = u"documentation"; -constexpr char16_t dynamicRegistrationKey[] = u"dynamicRegistration"; -constexpr char16_t editKey[] = u"edit"; -constexpr char16_t editsKey[] = u"edits"; -constexpr char16_t endKey[] = u"end"; -constexpr char16_t errorKey[] = u"error"; -constexpr char16_t eventKey[] = u"event"; -constexpr char16_t executeCommandKey[] = u"executeCommand"; -constexpr char16_t executeCommandProviderKey[] = u"executeCommandProvider"; -constexpr char16_t experimentalKey[] = u"experimental"; -constexpr char16_t filterTextKey[] = u"filterText"; -constexpr char16_t firstTriggerCharacterKey[] = u"firstTriggerCharacter"; -constexpr char16_t formatsKey[] = u"formats"; -constexpr char16_t formattingKey[] = u"formatting"; -constexpr char16_t fromKey[] = u"from"; -constexpr char16_t fromRangesKey[] = u"fromRanges"; -constexpr char16_t fullKey[] = u"full"; -constexpr char16_t greenKey[] = u"green"; -constexpr char16_t hierarchicalDocumentSymbolSupportKey[] = u"hierarchicalDocumentSymbolSupport"; -constexpr char16_t hoverKey[] = u"hover"; -constexpr char16_t hoverProviderKey[] = u"hoverProvider"; -constexpr char16_t idKey[] = u"id"; -constexpr char16_t implementationKey[] = u"implementation"; -constexpr char16_t implementationProviderKey[] = u"implementationProvider"; -constexpr char16_t includeDeclarationKey[] = u"includeDeclaration"; -constexpr char16_t includeTextKey[] = u"includeText"; -constexpr char16_t initializationOptionsKey[] = u"initializationOptions"; -constexpr char16_t insertFinalNewlineKey[] = u"insertFinalNewline"; -constexpr char16_t insertSpaceKey[] = u"insertSpace"; -constexpr char16_t insertTextFormatKey[] = u"insertTextFormat"; -constexpr char16_t insertTextKey[] = u"insertText"; -constexpr char16_t isIncompleteKey[] = u"isIncomplete"; -constexpr char16_t itemKey[] = u"item"; -constexpr char16_t itemsKey[] = u"items"; -constexpr char16_t jsonRpcVersionKey[] = u"jsonrpc"; -constexpr char16_t kindKey[] = u"kind"; -constexpr char16_t labelKey[] = u"label"; -constexpr char16_t languageIdKey[] = u"languageId"; -constexpr char16_t languageKey[] = u"language"; -constexpr char16_t legendKey[] = u"legend"; -constexpr char16_t limitKey[] = u"limit"; -constexpr char16_t lineKey[] = u"line"; -constexpr char16_t linesKey[] = u"lines"; -constexpr char16_t locationKey[] = u"location"; -constexpr char16_t messageKey[] = u"message"; -constexpr char16_t methodKey[] = u"method"; -constexpr char16_t moreTriggerCharacterKey[] = u"moreTriggerCharacter"; -constexpr char16_t multiLineTokenSupportKey[] = u"multiLineTokenSupport"; -constexpr char16_t nameKey[] = u"name"; -constexpr char16_t newNameKey[] = u"newName"; -constexpr char16_t newTextKey[] = u"newText"; -constexpr char16_t onTypeFormattingKey[] = u"onTypeFormatting"; -constexpr char16_t onlyKey[] = u"only"; -constexpr char16_t openCloseKey[] = u"openClose"; -constexpr char16_t optionsKey[] = u"options"; -constexpr char16_t overlappingTokenSupportKey[] = u"overlappingTokenSupport"; -constexpr char16_t parametersKey[] = u"parameters"; -constexpr char16_t paramsKey[] = u"params"; -constexpr char16_t patternKey[] = u"pattern"; -constexpr char16_t percentageKey[] = u"percentage"; -constexpr char16_t placeHolderKey[] = u"placeHolder"; -constexpr char16_t positionKey[] = u"position"; -constexpr char16_t prepareProviderKey[] = u"prepareProvider"; -constexpr char16_t prepareSupportKey[] = u"prepareSupport"; -constexpr char16_t previousResultIdKey[] = u"previousResultId"; -constexpr char16_t processIdKey[] = u"processId"; -constexpr char16_t queryKey[] = u"query"; -constexpr char16_t rangeFormattingKey[] = u"rangeFormatting"; -constexpr char16_t rangeKey[] = u"range"; -constexpr char16_t rangeLengthKey[] = u"rangeLength"; -constexpr char16_t reasonKey[] = u"reason"; -constexpr char16_t redKey[] = u"red"; -constexpr char16_t referencesKey[] = u"references"; -constexpr char16_t referencesProviderKey[] = u"referencesProvider"; -constexpr char16_t refreshSupportKey[] = u"refreshSupport"; -constexpr char16_t registerOptionsKey[] = u"registerOptions"; -constexpr char16_t registrationsKey[] = u"registrations"; -constexpr char16_t removedKey[] = u"removed"; -constexpr char16_t renameKey[] = u"rename"; -constexpr char16_t renameProviderKey[] = u"renameProvider"; -constexpr char16_t requestsKey[] = u"requests"; -constexpr char16_t resolveProviderKey[] = u"resolveProvider"; -constexpr char16_t resultIdKey[] = u"resultId"; -constexpr char16_t resultKey[] = u"result"; -constexpr char16_t retryKey[] = u"retry"; -constexpr char16_t rootPathKey[] = u"rootPath"; -constexpr char16_t rootUriKey[] = u"rootUri"; -constexpr char16_t saveKey[] = u"save"; -constexpr char16_t schemeKey[] = u"scheme"; -constexpr char16_t scopeUriKey[] = u"scopeUri"; -constexpr char16_t sectionKey[] = u"section"; -constexpr char16_t selectionRangeKey[] = u"selectionRange"; -constexpr char16_t semanticTokensKey[] = u"semanticTokens"; -constexpr char16_t semanticTokensProviderKey[] = u"semanticTokensProvider"; -constexpr char16_t serverInfoKey[] = u"serverInfo"; -constexpr char16_t settingsKey[] = u"settings"; -constexpr char16_t severityKey[] = u"severity"; -constexpr char16_t signatureHelpKey[] = u"signatureHelp"; -constexpr char16_t signatureHelpProviderKey[] = u"signatureHelpProvider"; -constexpr char16_t signatureInformationKey[] = u"signatureInformation"; -constexpr char16_t signaturesKey[] = u"signatures"; -constexpr char16_t snippetSupportKey[] = u"snippetSupport"; -constexpr char16_t sortTextKey[] = u"sortText"; -constexpr char16_t sourceKey[] = u"source"; -constexpr char16_t startKey[] = u"start"; -constexpr char16_t supportedKey[] = u"supported"; -constexpr char16_t symbolKey[] = u"symbol"; -constexpr char16_t symbolKindKey[] = u"symbolKind"; -constexpr char16_t syncKindKey[] = u"syncKind"; -constexpr char16_t synchronizationKey[] = u"synchronization"; -constexpr char16_t tabSizeKey[] = u"tabSize"; -constexpr char16_t tagsKey[] = u"tags"; -constexpr char16_t targetKey[] = u"target"; -constexpr char16_t textDocumentKey[] = u"textDocument"; -constexpr char16_t textDocumentSyncKey[] = u"textDocumentSync"; -constexpr char16_t textEditKey[] = u"textEdit"; -constexpr char16_t textKey[] = u"text"; -constexpr char16_t titleKey[] = u"title"; -constexpr char16_t tokenKey[] = u"token"; -constexpr char16_t toKey[] = u"to"; -constexpr char16_t tokenModifiersKey[] = u"tokenModifiers"; -constexpr char16_t tokenTypesKey[] = u"tokenTypes"; -constexpr char16_t traceKey[] = u"trace"; -constexpr char16_t triggerCharacterKey[] = u"triggerCharacter"; -constexpr char16_t triggerCharactersKey[] = u"triggerCharacters"; -constexpr char16_t triggerKindKey[] = u"triggerKind"; -constexpr char16_t trimFinalNewlinesKey[] = u"trimFinalNewlines"; -constexpr char16_t trimTrailingWhitespaceKey[] = u"trimTrailingWhitespace"; -constexpr char16_t typeDefinitionKey[] = u"typeDefinition"; -constexpr char16_t typeDefinitionProviderKey[] = u"typeDefinitionProvider"; -constexpr char16_t typeKey[] = u"type"; -constexpr char16_t unregistrationsKey[] = u"unregistrations"; -constexpr char16_t uriKey[] = u"uri"; -constexpr char16_t valueKey[] = u"value"; -constexpr char16_t valueSetKey[] = u"valueSet"; -constexpr char16_t versionKey[] = u"version"; -constexpr char16_t willSaveKey[] = u"willSave"; -constexpr char16_t willSaveWaitUntilKey[] = u"willSaveWaitUntil"; -constexpr char16_t windowKey[] = u"window"; -constexpr char16_t workDoneProgressKey[] = u"workDoneProgress"; -constexpr char16_t workspaceEditKey[] = u"workspaceEdit"; -constexpr char16_t workspaceFoldersKey[] = u"workspaceFolders"; -constexpr char16_t workspaceKey[] = u"workspace"; -constexpr char16_t workspaceSymbolProviderKey[] = u"workspaceSymbolProvider"; +constexpr char actionsKey[] = "actions"; +constexpr char activeParameterKey[] = "activeParameter"; +constexpr char activeParameterSupportKey[] = "activeParameterSupport"; +constexpr char activeSignatureKey[] = "activeSignature"; +constexpr char addedKey[] = "added"; +constexpr char additionalTextEditsKey[] = "additionalTextEdits"; +constexpr char alphaKey[] = "alpha"; +constexpr char appliedKey[] = "applied"; +constexpr char applyEditKey[] = "applyEdit"; +constexpr char argumentsKey[] = "arguments"; +constexpr char blueKey[] = "blue"; +constexpr char callHierarchyKey[] = "callHierarchy"; +constexpr char callHierarchyProviderKey[] = "callHierarchyProvider"; +constexpr char cancellableKey[] = "cancellable"; +constexpr char capabilitiesKey[] = "capabilities"; +constexpr char chKey[] = "ch"; +constexpr char changeKey[] = "change"; +constexpr char changeNotificationsKey[] = "changeNotifications"; +constexpr char changesKey[] = "changes"; +constexpr char characterKey[] = "character"; +constexpr char childrenKey[] = "children"; +constexpr char clientInfoKey[] = "clientInfo"; +constexpr char codeActionKey[] = "codeAction"; +constexpr char codeActionKindKey[] = "codeActionKind"; +constexpr char codeActionKindsKey[] = "codeActionKinds"; +constexpr char codeActionLiteralSupportKey[] = "codeActionLiteralSupport"; +constexpr char codeActionProviderKey[] = "codeActionProvider"; +constexpr char codeKey[] = "code"; +constexpr char codeLensKey[] = "codeLens"; +constexpr char codeLensProviderKey[] = "codeLensProvider"; +constexpr char colorInfoKey[] = "colorInfo"; +constexpr char colorKey[] = "color"; +constexpr char colorProviderKey[] = "colorProvider"; +constexpr char commandKey[] = "command"; +constexpr char commandsKey[] = "commands"; +constexpr char commitCharacterSupportKey[] = "commitCharacterSupport"; +constexpr char commitCharactersKey[] = "commitCharacters"; +constexpr char completionItemKey[] = "completionItem"; +constexpr char completionItemKindKey[] = "completionItemKind"; +constexpr char completionKey[] = "completion"; +constexpr char completionProviderKey[] = "completionProvider"; +constexpr char configurationKey[] = "configuration"; +constexpr char containerNameKey[] = "containerName"; +constexpr char contentChangesKey[] = "contentChanges"; +constexpr char contentFormatKey[] = "contentFormat"; +constexpr char contentKey[] = "value"; +constexpr char contentsKey[] = "contents"; +constexpr char contextKey[] = "context"; +constexpr char contextSupportKey[] = "contextSupport"; +constexpr char dataKey[] = "data"; +constexpr char definitionKey[] = "definition"; +constexpr char definitionProviderKey[] = "definitionProvider"; +constexpr char deleteCountKey[] = "deleteCount"; +constexpr char deltaKey[] = "delta"; +constexpr char deprecatedKey[] = "deprecated"; +constexpr char detailKey[] = "detail"; +constexpr char diagnosticsKey[] = "diagnostics"; +constexpr char didChangeConfigurationKey[] = "didChangeConfiguration"; +constexpr char didChangeWatchedFilesKey[] = "didChangeWatchedFiles"; +constexpr char didSaveKey[] = "didSave"; +constexpr char documentChangesKey[] = "documentChanges"; +constexpr char documentFormattingProviderKey[] = "documentFormattingProvider"; +constexpr char documentHighlightKey[] = "documentHighlight"; +constexpr char documentHighlightProviderKey[] = "documentHighlightProvider"; +constexpr char documentLinkKey[] = "documentLink"; +constexpr char documentLinkProviderKey[] = "documentLinkProvider"; +constexpr char documentRangeFormattingProviderKey[] = "documentRangeFormattingProvider"; +constexpr char documentSelectorKey[] = "documentSelector"; +constexpr char documentSymbolKey[] = "documentSymbol"; +constexpr char documentSymbolProviderKey[] = "documentSymbolProvider"; +constexpr char documentationFormatKey[] = "documentationFormat"; +constexpr char documentationKey[] = "documentation"; +constexpr char dynamicRegistrationKey[] = "dynamicRegistration"; +constexpr char editKey[] = "edit"; +constexpr char editsKey[] = "edits"; +constexpr char endKey[] = "end"; +constexpr char errorKey[] = "error"; +constexpr char eventKey[] = "event"; +constexpr char executeCommandKey[] = "executeCommand"; +constexpr char executeCommandProviderKey[] = "executeCommandProvider"; +constexpr char experimentalKey[] = "experimental"; +constexpr char filterTextKey[] = "filterText"; +constexpr char firstTriggerCharacterKey[] = "firstTriggerCharacter"; +constexpr char formatsKey[] = "formats"; +constexpr char formattingKey[] = "formatting"; +constexpr char fromKey[] = "from"; +constexpr char fromRangesKey[] = "fromRanges"; +constexpr char fullKey[] = "full"; +constexpr char greenKey[] = "green"; +constexpr char hierarchicalDocumentSymbolSupportKey[] = "hierarchicalDocumentSymbolSupport"; +constexpr char hoverKey[] = "hover"; +constexpr char hoverProviderKey[] = "hoverProvider"; +constexpr char idKey[] = "id"; +constexpr char ignoreIfExistsKey[] = "ignoreIfExists"; +constexpr char ignoreIfNotExistsKey[] = "ignoreIfNotExists"; +constexpr char implementationKey[] = "implementation"; +constexpr char implementationProviderKey[] = "implementationProvider"; +constexpr char includeDeclarationKey[] = "includeDeclaration"; +constexpr char includeTextKey[] = "includeText"; +constexpr char initializationOptionsKey[] = "initializationOptions"; +constexpr char insertFinalNewlineKey[] = "insertFinalNewline"; +constexpr char insertSpaceKey[] = "insertSpace"; +constexpr char insertTextFormatKey[] = "insertTextFormat"; +constexpr char insertTextKey[] = "insertText"; +constexpr char isIncompleteKey[] = "isIncomplete"; +constexpr char itemKey[] = "item"; +constexpr char itemsKey[] = "items"; +constexpr char jsonRpcVersionKey[] = "jsonrpc"; +constexpr char kindKey[] = "kind"; +constexpr char labelKey[] = "label"; +constexpr char languageIdKey[] = "languageId"; +constexpr char languageKey[] = "language"; +constexpr char legendKey[] = "legend"; +constexpr char limitKey[] = "limit"; +constexpr char lineKey[] = "line"; +constexpr char linesKey[] = "lines"; +constexpr char locationKey[] = "location"; +constexpr char messageKey[] = "message"; +constexpr char methodKey[] = "method"; +constexpr char moreTriggerCharacterKey[] = "moreTriggerCharacter"; +constexpr char multiLineTokenSupportKey[] = "multiLineTokenSupport"; +constexpr char nameKey[] = "name"; +constexpr char newNameKey[] = "newName"; +constexpr char newTextKey[] = "newText"; +constexpr char newUriKey[] = "newUri"; +constexpr char oldUriKey[] = "oldUri"; +constexpr char onTypeFormattingKey[] = "onTypeFormatting"; +constexpr char onlyKey[] = "only"; +constexpr char openCloseKey[] = "openClose"; +constexpr char optionsKey[] = "options"; +constexpr char overlappingTokenSupportKey[] = "overlappingTokenSupport"; +constexpr char overwriteKey[] = "overwrite"; +constexpr char parametersKey[] = "parameters"; +constexpr char paramsKey[] = "params"; +constexpr char patternKey[] = "pattern"; +constexpr char percentageKey[] = "percentage"; +constexpr char placeHolderKey[] = "placeHolder"; +constexpr char positionKey[] = "position"; +constexpr char prepareProviderKey[] = "prepareProvider"; +constexpr char prepareSupportKey[] = "prepareSupport"; +constexpr char previousResultIdKey[] = "previousResultId"; +constexpr char processIdKey[] = "processId"; +constexpr char queryKey[] = "query"; +constexpr char rangeFormattingKey[] = "rangeFormatting"; +constexpr char rangeKey[] = "range"; +constexpr char rangeLengthKey[] = "rangeLength"; +constexpr char reasonKey[] = "reason"; +constexpr char recursiveKey[] = "recursive"; +constexpr char redKey[] = "red"; +constexpr char referencesKey[] = "references"; +constexpr char referencesProviderKey[] = "referencesProvider"; +constexpr char refreshSupportKey[] = "refreshSupport"; +constexpr char registerOptionsKey[] = "registerOptions"; +constexpr char registrationsKey[] = "registrations"; +constexpr char removedKey[] = "removed"; +constexpr char renameKey[] = "rename"; +constexpr char renameProviderKey[] = "renameProvider"; +constexpr char requestsKey[] = "requests"; +constexpr char resolveProviderKey[] = "resolveProvider"; +constexpr char resourceOperationsKey[] = "resourceOperations"; +constexpr char resultIdKey[] = "resultId"; +constexpr char resultKey[] = "result"; +constexpr char retryKey[] = "retry"; +constexpr char rootPathKey[] = "rootPath"; +constexpr char rootUriKey[] = "rootUri"; +constexpr char saveKey[] = "save"; +constexpr char schemeKey[] = "scheme"; +constexpr char scopeUriKey[] = "scopeUri"; +constexpr char sectionKey[] = "section"; +constexpr char selectionRangeKey[] = "selectionRange"; +constexpr char semanticTokensKey[] = "semanticTokens"; +constexpr char semanticTokensProviderKey[] = "semanticTokensProvider"; +constexpr char serverInfoKey[] = "serverInfo"; +constexpr char settingsKey[] = "settings"; +constexpr char severityKey[] = "severity"; +constexpr char signatureHelpKey[] = "signatureHelp"; +constexpr char signatureHelpProviderKey[] = "signatureHelpProvider"; +constexpr char signatureInformationKey[] = "signatureInformation"; +constexpr char signaturesKey[] = "signatures"; +constexpr char snippetSupportKey[] = "snippetSupport"; +constexpr char sortTextKey[] = "sortText"; +constexpr char sourceKey[] = "source"; +constexpr char startKey[] = "start"; +constexpr char supportedKey[] = "supported"; +constexpr char symbolKey[] = "symbol"; +constexpr char symbolKindKey[] = "symbolKind"; +constexpr char syncKindKey[] = "syncKind"; +constexpr char synchronizationKey[] = "synchronization"; +constexpr char tabSizeKey[] = "tabSize"; +constexpr char tagsKey[] = "tags"; +constexpr char targetKey[] = "target"; +constexpr char textDocumentKey[] = "textDocument"; +constexpr char textDocumentSyncKey[] = "textDocumentSync"; +constexpr char textEditKey[] = "textEdit"; +constexpr char textKey[] = "text"; +constexpr char titleKey[] = "title"; +constexpr char toKey[] = "to"; +constexpr char tokenKey[] = "token"; +constexpr char tokenModifiersKey[] = "tokenModifiers"; +constexpr char tokenTypesKey[] = "tokenTypes"; +constexpr char traceKey[] = "trace"; +constexpr char triggerCharacterKey[] = "triggerCharacter"; +constexpr char triggerCharactersKey[] = "triggerCharacters"; +constexpr char triggerKindKey[] = "triggerKind"; +constexpr char trimFinalNewlinesKey[] = "trimFinalNewlines"; +constexpr char trimTrailingWhitespaceKey[] = "trimTrailingWhitespace"; +constexpr char typeDefinitionKey[] = "typeDefinition"; +constexpr char typeDefinitionProviderKey[] = "typeDefinitionProvider"; +constexpr char typeKey[] = "type"; +constexpr char unregistrationsKey[] = "unregistrations"; +constexpr char uriKey[] = "uri"; +constexpr char valueKey[] = "value"; +constexpr char valueSetKey[] = "valueSet"; +constexpr char versionKey[] = "version"; +constexpr char willSaveKey[] = "willSave"; +constexpr char willSaveWaitUntilKey[] = "willSaveWaitUntil"; +constexpr char windowKey[] = "window"; +constexpr char workDoneProgressKey[] = "workDoneProgress"; +constexpr char workspaceEditKey[] = "workspaceEdit"; +constexpr char workspaceFoldersKey[] = "workspaceFolders"; +constexpr char workspaceKey[] = "workspace"; +constexpr char workspaceSymbolProviderKey[] = "workspaceSymbolProvider"; } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/jsonobject.cpp b/src/libs/languageserverprotocol/jsonobject.cpp index 2a89f34a6ea..d591e06cb67 100644 --- a/src/libs/languageserverprotocol/jsonobject.cpp +++ b/src/libs/languageserverprotocol/jsonobject.cpp @@ -15,14 +15,14 @@ JsonObject &JsonObject::operator=(JsonObject &&other) return *this; } -QJsonObject::iterator JsonObject::insert(const QStringView key, const JsonObject &object) +QJsonObject::iterator JsonObject::insert(const std::string_view key, const JsonObject &object) { - return m_jsonObject.insert(key, object.m_jsonObject); + return m_jsonObject.insert(QLatin1String(key.data()), object.m_jsonObject); } -QJsonObject::iterator JsonObject::insert(const QStringView key, const QJsonValue &value) +QJsonObject::iterator JsonObject::insert(const std::string_view key, const QJsonValue &value) { - return m_jsonObject.insert(key, value); + return m_jsonObject.insert(QLatin1String(key.data()), value); } } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/jsonobject.h b/src/libs/languageserverprotocol/jsonobject.h index 8f773d283ff..11c764c82c7 100644 --- a/src/libs/languageserverprotocol/jsonobject.h +++ b/src/libs/languageserverprotocol/jsonobject.h @@ -43,97 +43,98 @@ public: const_iterator end() const { return m_jsonObject.end(); } protected: - iterator insert(const QStringView key, const JsonObject &value); - iterator insert(const QStringView key, const QJsonValue &value); + iterator insert(const std::string_view key, const JsonObject &value); + iterator insert(const std::string_view key, const QJsonValue &value); template - iterator insertVariant(const QStringView key, const V &variant); + iterator insertVariant(const std::string_view key, const V &variant); template - iterator insertVariant(const QStringView key, const V &variant); + iterator insertVariant(const std::string_view key, const V &variant); // QJSonObject redirections - QJsonValue value(const QStringView key) const { return m_jsonObject.value(key); } - bool contains(const QStringView key) const { return m_jsonObject.contains(key); } - iterator find(const QStringView key) { return m_jsonObject.find(key); } - const_iterator find(const QStringView key) const { return m_jsonObject.find(key); } - void remove(const QStringView key) { m_jsonObject.remove(key); } + QJsonValue value(const std::string_view key) const { return m_jsonObject.value(QLatin1String(key.data())); } + bool contains(const std::string_view key) const { return m_jsonObject.contains(QLatin1String(key.data())); } + iterator find(const std::string_view key) { return m_jsonObject.find(QLatin1String(key.data())); } + const_iterator find(const std::string_view key) const { return m_jsonObject.find(QLatin1String(key.data())); } + void remove(const std::string_view key) { m_jsonObject.remove(QLatin1String(key.data())); } QStringList keys() const { return m_jsonObject.keys(); } // convenience value access template - T typedValue(const QStringView key) const; + T typedValue(const std::string_view key) const; template - std::optional optionalValue(const QStringView key) const; + std::optional optionalValue(const std::string_view key) const; template - LanguageClientValue clientValue(const QStringView key) const; + LanguageClientValue clientValue(const std::string_view key) const; template - std::optional> optionalClientValue(const QStringView key) const; + std::optional> optionalClientValue(const std::string_view key) const; template - QList array(const QStringView key) const; + QList array(const std::string_view key) const; template - std::optional> optionalArray(const QStringView key) const; + std::optional> optionalArray(const std::string_view key) const; template - LanguageClientArray clientArray(const QStringView key) const; + LanguageClientArray clientArray(const std::string_view key) const; template - std::optional> optionalClientArray(const QStringView key) const; + std::optional> optionalClientArray(const std::string_view key) const; template - void insertArray(const QStringView key, const QList &array); + void insertArray(const std::string_view key, const QList &array); template - void insertArray(const QStringView key, const QList &array); + void insertArray(const std::string_view key, const QList &array); private: QJsonObject m_jsonObject; }; template -JsonObject::iterator JsonObject::insertVariant(const QStringView key, const V &variant) +JsonObject::iterator JsonObject::insertVariant(const std::string_view key, const V &variant) { return std::holds_alternative(variant) ? insert(key, std::get(variant)) : end(); } template -JsonObject::iterator JsonObject::insertVariant(const QStringView key, const V &variant) +JsonObject::iterator JsonObject::insertVariant(const std::string_view key, const V &variant) { auto result = insertVariant(key, variant); return result != end() ? result : insertVariant(key, variant); } template -T JsonObject::typedValue(const QStringView key) const +T JsonObject::typedValue(const std::string_view key) const { return fromJsonValue(value(key)); } template -std::optional JsonObject::optionalValue(const QStringView key) const +std::optional JsonObject::optionalValue(const std::string_view key) const { const QJsonValue &val = value(key); return val.isUndefined() ? std::nullopt : std::make_optional(fromJsonValue(val)); } template -LanguageClientValue JsonObject::clientValue(const QStringView key) const +LanguageClientValue JsonObject::clientValue(const std::string_view key) const { return LanguageClientValue(value(key)); } template -std::optional> JsonObject::optionalClientValue(const QStringView key) const +std::optional> JsonObject::optionalClientValue(const std::string_view key) const { return contains(key) ? std::make_optional(clientValue(key)) : std::nullopt; } template -QList JsonObject::array(const QStringView key) const +QList JsonObject::array(const std::string_view key) const { if (const std::optional> &array = optionalArray(key)) return *array; - qCDebug(conversionLog) << QString("Expected array under %1 in:").arg(key) << *this; + qCDebug(conversionLog) << QString("Expected array under %1 in:") + .arg(QLatin1String(key.data())) << *this; return {}; } template -std::optional> JsonObject::optionalArray(const QStringView key) const +std::optional> JsonObject::optionalArray(const std::string_view key) const { const QJsonValue &jsonValue = value(key); if (jsonValue.isUndefined()) @@ -142,13 +143,13 @@ std::optional> JsonObject::optionalArray(const QStringView key) const } template -LanguageClientArray JsonObject::clientArray(const QStringView key) const +LanguageClientArray JsonObject::clientArray(const std::string_view key) const { return LanguageClientArray(value(key)); } template -std::optional> JsonObject::optionalClientArray(const QStringView key) const +std::optional> JsonObject::optionalClientArray(const std::string_view key) const { const QJsonValue &val = value(key); return !val.isUndefined() ? std::make_optional(LanguageClientArray(value(key))) @@ -156,7 +157,7 @@ std::optional> JsonObject::optionalClientArray(const QStr } template -void JsonObject::insertArray(const QStringView key, const QList &array) +void JsonObject::insertArray(const std::string_view key, const QList &array) { QJsonArray jsonArray; for (const T &item : array) @@ -165,7 +166,7 @@ void JsonObject::insertArray(const QStringView key, const QList &array) } template -void JsonObject::insertArray(const QStringView key, const QList &array) +void JsonObject::insertArray(const std::string_view key, const QList &array) { QJsonArray jsonArray; for (const JsonObject &item : array) diff --git a/src/libs/languageserverprotocol/languagefeatures.cpp b/src/libs/languageserverprotocol/languagefeatures.cpp index 24a160f4b39..b4d124f96be 100644 --- a/src/libs/languageserverprotocol/languagefeatures.cpp +++ b/src/libs/languageserverprotocol/languagefeatures.cpp @@ -125,7 +125,7 @@ QHash FormattingOptions::properties() const for (const QString &key : keys()) { if (key == tabSizeKey || key == insertSpaceKey) continue; - QJsonValue property = value(key); + QJsonValue property = value(key.toStdString()); if (property.isBool()) ret[key] = property.toBool(); if (property.isDouble()) @@ -136,7 +136,7 @@ QHash FormattingOptions::properties() const return ret; } -void FormattingOptions::setProperty(const QString &key, const DocumentFormattingProperty &property) +void FormattingOptions::setProperty(const std::string_view key, const DocumentFormattingProperty &property) { using namespace std; if (auto val = get_if(&property)) diff --git a/src/libs/languageserverprotocol/languagefeatures.h b/src/libs/languageserverprotocol/languagefeatures.h index 2fc3a991f96..b82b893f49d 100644 --- a/src/libs/languageserverprotocol/languagefeatures.h +++ b/src/libs/languageserverprotocol/languagefeatures.h @@ -678,8 +678,8 @@ public: void clearTrimFinalNewlines() { remove(trimFinalNewlinesKey); } QHash properties() const; - void setProperty(const QString &key, const DocumentFormattingProperty &property); - void removeProperty(const QString &key) { remove(key); } + void setProperty(const std::string_view key, const DocumentFormattingProperty &property); + void removeProperty(const std::string_view &key) { remove(key); } bool isValid() const override { return contains(insertSpaceKey) && contains(tabSizeKey); } }; diff --git a/src/libs/languageserverprotocol/languageserverprotocol.qbs b/src/libs/languageserverprotocol/languageserverprotocol.qbs index 163b2d6d4ea..0368ceafee8 100644 --- a/src/libs/languageserverprotocol/languageserverprotocol.qbs +++ b/src/libs/languageserverprotocol/languageserverprotocol.qbs @@ -1,54 +1,50 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "LanguageServerProtocol" - QtcLibrary { - Depends { name: "Utils" } - cpp.defines: base.concat("LANGUAGESERVERPROTOCOL_LIBRARY") + Depends { name: "Utils" } + cpp.defines: base.concat("LANGUAGESERVERPROTOCOL_LIBRARY") - files: [ - "basemessage.cpp", - "basemessage.h", - "callhierarchy.cpp", - "callhierarchy.h", - "client.cpp", - "client.h", - "clientcapabilities.cpp", - "clientcapabilities.h", - "completion.cpp", - "completion.h", - "diagnostics.cpp", - "diagnostics.h", - "initializemessages.cpp", - "initializemessages.h", - "jsonkeys.h", - "jsonobject.cpp", - "jsonobject.h", - "jsonrpcmessages.cpp", - "jsonrpcmessages.h", - "languagefeatures.cpp", - "languagefeatures.h", - "languageserverprotocol_global.h", - "languageserverprotocoltr.h", - "lsptypes.cpp", - "lsptypes.h", - "lsputils.cpp", - "lsputils.h", - "messages.cpp", - "messages.h", - "progresssupport.cpp", - "progresssupport.h", - "semantictokens.cpp", - "semantictokens.h", - "servercapabilities.cpp", - "servercapabilities.h", - "shutdownmessages.cpp", - "shutdownmessages.h", - "textsynchronization.cpp", - "textsynchronization.h", - "workspace.cpp", - "workspace.h", - ] - } + files: [ + "basemessage.cpp", + "basemessage.h", + "callhierarchy.cpp", + "callhierarchy.h", + "client.cpp", + "client.h", + "clientcapabilities.cpp", + "clientcapabilities.h", + "completion.cpp", + "completion.h", + "diagnostics.cpp", + "diagnostics.h", + "initializemessages.cpp", + "initializemessages.h", + "jsonkeys.h", + "jsonobject.cpp", + "jsonobject.h", + "jsonrpcmessages.cpp", + "jsonrpcmessages.h", + "languagefeatures.cpp", + "languagefeatures.h", + "languageserverprotocol_global.h", + "languageserverprotocoltr.h", + "lsptypes.cpp", + "lsptypes.h", + "lsputils.cpp", + "lsputils.h", + "messages.cpp", + "messages.h", + "progresssupport.cpp", + "progresssupport.h", + "semantictokens.cpp", + "semantictokens.h", + "servercapabilities.cpp", + "servercapabilities.h", + "shutdownmessages.cpp", + "shutdownmessages.h", + "textsynchronization.cpp", + "textsynchronization.h", + "workspace.cpp", + "workspace.h", + ] } diff --git a/src/libs/languageserverprotocol/lsptypes.cpp b/src/libs/languageserverprotocol/lsptypes.cpp index 6f0b57c1e35..ddd9b0e66c0 100644 --- a/src/libs/languageserverprotocol/lsptypes.cpp +++ b/src/libs/languageserverprotocol/lsptypes.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "lsptypes.h" + +#include "languageserverprotocoltr.h" #include "lsputils.h" #include @@ -384,7 +386,7 @@ Utils::FilePath DocumentUri::toFilePath(const PathMapper &mapToHostPath) const QTC_ASSERT(mapToHostPath, return serverPath); return mapToHostPath(serverPath); } - return Utils::FilePath(); + return {}; } DocumentUri DocumentUri::fromProtocol(const QString &uri) @@ -413,12 +415,76 @@ LanguageServerProtocol::MarkupKind::operator QJsonValue() const return {}; } -Utils::Text::Replacement TextEdit::toReplacement(QTextDocument *document) const +DocumentChange::DocumentChange(const QJsonValue &value) { - const Range &range = this->range(); - const int start = range.start().toPositionInDocument(document); - const int end = range.end().toPositionInDocument(document); - return Utils::Text::Replacement(start, end - start, newText()); + const QString kind = value["kind"].toString(); + if (kind == "create") + emplace(value); + else if (kind == "rename") + emplace(value); + else if (kind == "delete") + emplace(value); + else + emplace(value); +} + +using DocumentChangeBase = std::variant; + +bool DocumentChange::isValid() const +{ + return std::visit([](const auto &v) { return v.isValid(); }, DocumentChangeBase(*this)); +} + +DocumentChange::operator const QJsonValue () const +{ + return std::visit([](const auto &v) { return QJsonValue(v); }, DocumentChangeBase(*this)); +} + +CreateFileOperation::CreateFileOperation() +{ + insert(kindKey, "create"); +} + +QString CreateFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const +{ + return Tr::tr("Create %1").arg(uri().toFilePath(mapToHostPath).toUserOutput()); +} + +bool LanguageServerProtocol::CreateFileOperation::isValid() const +{ + return contains(uriKey) && value(kindKey) == "create"; +} + +RenameFileOperation::RenameFileOperation() +{ + insert(kindKey, "rename"); +} + +QString RenameFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const +{ + return Tr::tr("Rename %1 to %2") + .arg(oldUri().toFilePath(mapToHostPath).toUserOutput(), + newUri().toFilePath(mapToHostPath).toUserOutput()); +} + +bool RenameFileOperation::isValid() const +{ + return contains(oldUriKey) && contains(newUriKey) && value(kindKey) == "rename"; +} + +DeleteFileOperation::DeleteFileOperation() +{ + insert(kindKey, "delete"); +} + +QString DeleteFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const +{ + return Tr::tr("Delete %1").arg(uri().toFilePath(mapToHostPath).toUserOutput()); +} + +bool DeleteFileOperation::isValid() const +{ + return contains(uriKey) && value(kindKey) == "delete"; } } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/lsptypes.h b/src/libs/languageserverprotocol/lsptypes.h index 3a9adb1b0d8..ae19f36145b 100644 --- a/src/libs/languageserverprotocol/lsptypes.h +++ b/src/libs/languageserverprotocol/lsptypes.h @@ -228,8 +228,6 @@ public: QString newText() const { return typedValue(newTextKey); } void setNewText(const QString &text) { insert(newTextKey, text); } - Utils::Text::Replacement toReplacement(QTextDocument *document) const; - bool isValid() const override { return contains(rangeKey) && contains(newTextKey); } }; @@ -287,6 +285,106 @@ public: bool isValid() const override { return contains(textDocumentKey) && contains(editsKey); } }; +class CreateFileOptions : public JsonObject +{ +public: + using JsonObject::JsonObject; + + std::optional overwrite() const { return optionalValue(overwriteKey); } + void setOverwrite(bool overwrite) { insert(overwriteKey, overwrite); } + void clearOverwrite() { remove(overwriteKey); } + + std::optional ignoreIfExists() const { return optionalValue(ignoreIfExistsKey); } + void setIgnoreIfExists(bool ignoreIfExists) { insert(ignoreIfExistsKey, ignoreIfExists); } + void clearIgnoreIfExists() { remove(ignoreIfExistsKey); } +}; + +class LANGUAGESERVERPROTOCOL_EXPORT CreateFileOperation : public JsonObject +{ +public: + using JsonObject::JsonObject; + CreateFileOperation(); + + DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue(uriKey)); } + void setUri(const DocumentUri &uri) { insert(uriKey, uri); } + + std::optional options() const + { return optionalValue(optionsKey); } + void setOptions(const CreateFileOptions &options) { insert(optionsKey, options); } + void clearOptions() { remove(optionsKey); } + + QString message(const DocumentUri::PathMapper &mapToHostPath) const; + + bool isValid() const override; +}; + +class LANGUAGESERVERPROTOCOL_EXPORT RenameFileOperation : public JsonObject +{ +public: + using JsonObject::JsonObject; + RenameFileOperation(); + + DocumentUri oldUri() const { return DocumentUri::fromProtocol(typedValue(oldUriKey)); } + void setOldUri(const DocumentUri &oldUri) { insert(oldUriKey, oldUri); } + + DocumentUri newUri() const { return DocumentUri::fromProtocol(typedValue(newUriKey)); } + void setNewUri(const DocumentUri &newUri) { insert(newUriKey, newUri); } + + std::optional options() const + { return optionalValue(optionsKey); } + void setOptions(const CreateFileOptions &options) { insert(optionsKey, options); } + void clearOptions() { remove(optionsKey); } + + QString message(const DocumentUri::PathMapper &mapToHostPath) const; + + bool isValid() const override; +}; + +class DeleteFileOptions : public JsonObject +{ +public: + using JsonObject::JsonObject; + + std::optional recursive() const { return optionalValue(recursiveKey); } + void setRecursive(bool recursive) { insert(recursiveKey, recursive); } + void clearRecursive() { remove(recursiveKey); } + + std::optional ignoreIfNotExists() const { return optionalValue(ignoreIfNotExistsKey); } + void setIgnoreIfNotExists(bool ignoreIfNotExists) { insert(ignoreIfNotExistsKey, ignoreIfNotExists); } + void clearIgnoreIfNotExists() { remove(ignoreIfNotExistsKey); } +}; + +class LANGUAGESERVERPROTOCOL_EXPORT DeleteFileOperation : public JsonObject +{ +public: + using JsonObject::JsonObject; + DeleteFileOperation(); + + DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue(uriKey)); } + void setUri(const DocumentUri &uri) { insert(uriKey, uri); } + + std::optional options() const + { return optionalValue(optionsKey); } + void setOptions(const DeleteFileOptions &options) { insert(optionsKey, options); } + void clearOptions() { remove(optionsKey); } + + QString message(const DocumentUri::PathMapper &mapToHostPath) const; + + bool isValid() const override; +}; + +class LANGUAGESERVERPROTOCOL_EXPORT DocumentChange + : public std::variant +{ +public: + using variant::variant; + DocumentChange(const QJsonValue &value); + + bool isValid() const; + + operator const QJsonValue() const; +}; + class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceEdit : public JsonObject { public: @@ -298,17 +396,23 @@ public: void setChanges(const Changes &changes); /* - * An array of `TextDocumentEdit`s to express changes to n different text documents - * where each text document edit addresses a specific version of a text document. - * Whether a client supports versioned document edits is expressed via - * `WorkspaceClientCapabilities.workspaceEdit.documentChanges`. + * Depending on the client capability + * `workspace.workspaceEdit.resourceOperations` document changes are either + * an array of `TextDocumentEdit`s to express changes to n different text + * documents where each text document edit addresses a specific version of + * a text document. Or it can contain above `TextDocumentEdit`s mixed with + * create, rename and delete file / folder operations. * - * Note: If the client can handle versioned document edits and if documentChanges are present, - * the latter are preferred over changes. + * Whether a client supports versioned document edits is expressed via + * `workspace.workspaceEdit.documentChanges` client capability. + * + * If a client neither supports `documentChanges` nor + * `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s + * using the `changes` property are supported. */ - std::optional> documentChanges() const - { return optionalArray(documentChangesKey); } - void setDocumentChanges(const QList &changes) + std::optional> documentChanges() const + { return optionalArray(documentChangesKey); } + void setDocumentChanges(const QList &changes) { insertArray(documentChangesKey, changes); } }; diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp index 1c0d322e6e7..885198d12e8 100644 --- a/src/libs/languageserverprotocol/servercapabilities.cpp +++ b/src/libs/languageserverprotocol/servercapabilities.cpp @@ -51,6 +51,24 @@ void ServerCapabilities::setHoverProvider( insertVariant(hoverProviderKey, hoverProvider); } +std::optional> +ServerCapabilities::definitionProvider() const +{ + using RetType = std::variant; + const QJsonValue &provider = value(definitionProviderKey); + if (provider.isUndefined() || !(provider.isBool() || provider.isObject())) + return std::nullopt; + return std::make_optional(provider.isBool() + ? RetType(provider.toBool()) + : RetType(RegistrationOptions(provider.toObject()))); +} + +void ServerCapabilities::setDefinitionProvider( + const std::variant &definitionProvider) +{ + insertVariant(definitionProviderKey, definitionProvider); +} + std::optional> ServerCapabilities::typeDefinitionProvider() const { diff --git a/src/libs/languageserverprotocol/servercapabilities.h b/src/libs/languageserverprotocol/servercapabilities.h index bacddda19e6..c3b50dd5904 100644 --- a/src/libs/languageserverprotocol/servercapabilities.h +++ b/src/libs/languageserverprotocol/servercapabilities.h @@ -269,13 +269,6 @@ public: { insert(signatureHelpProviderKey, signatureHelpProvider); } void clearSignatureHelpProvider() { remove(signatureHelpProviderKey); } - // The server provides goto definition support. - std::optional definitionProvider() const - { return optionalValue(definitionProviderKey); } - void setDefinitionProvider(bool definitionProvider) - { insert(definitionProviderKey, definitionProvider); } - void clearDefinitionProvider() { remove(definitionProviderKey); } - class LANGUAGESERVERPROTOCOL_EXPORT RegistrationOptions : public JsonObject { public: @@ -298,6 +291,11 @@ public: bool isValid() const override { return contains(documentSelectorKey); } }; + // The server provides goto definition support. + std::optional> definitionProvider() const; + void setDefinitionProvider(const std::variant &typeDefinitionProvider); + void clearDefinitionProvider() { remove(typeDefinitionProviderKey); } + // The server provides Goto Type Definition support. std::optional> typeDefinitionProvider() const; void setTypeDefinitionProvider(const std::variant &typeDefinitionProvider); diff --git a/src/libs/languageserverprotocol/workspace.cpp b/src/libs/languageserverprotocol/workspace.cpp index c9d2aeb4130..2d49be2d5ff 100644 --- a/src/libs/languageserverprotocol/workspace.cpp +++ b/src/libs/languageserverprotocol/workspace.cpp @@ -72,4 +72,11 @@ LanguageServerProtocol::WorkSpaceFolderResult::operator const QJsonValue() const return array; } +std::optional ConfigurationParams::ConfigurationItem::scopeUri() const +{ + if (const std::optional optionalScope = optionalValue(scopeUriKey)) + return std::make_optional(DocumentUri::fromProtocol(*optionalScope)); + return std::nullopt; +} + } // namespace LanguageServerProtocol diff --git a/src/libs/languageserverprotocol/workspace.h b/src/libs/languageserverprotocol/workspace.h index 42cfcf451d2..703310caaa3 100644 --- a/src/libs/languageserverprotocol/workspace.h +++ b/src/libs/languageserverprotocol/workspace.h @@ -92,8 +92,8 @@ public: public: using JsonObject::JsonObject; - std::optional scopeUri() const { return optionalValue(scopeUriKey); } - void setScopeUri(const QString &scopeUri) { insert(scopeUriKey, scopeUri); } + std::optional scopeUri() const; + void setScopeUri(const DocumentUri &scopeUri) { insert(scopeUriKey, scopeUri); } void clearScopeUri() { remove(scopeUriKey); } std::optional section() const { return optionalValue(sectionKey); } @@ -109,8 +109,8 @@ public: bool isValid() const override { return contains(itemsKey); } }; -class LANGUAGESERVERPROTOCOL_EXPORT ConfigurationRequest : public Request< - LanguageClientArray, std::nullptr_t, ConfigurationParams> +class LANGUAGESERVERPROTOCOL_EXPORT ConfigurationRequest + : public Request { public: explicit ConfigurationRequest(const ConfigurationParams ¶ms); @@ -166,7 +166,7 @@ public: QString query() const { return typedValue(queryKey); } void setQuery(const QString &query) { insert(queryKey, query); } - void setLimit(int limit) { insert(u"limit", limit); } // clangd extension + void setLimit(int limit) { insert("limit", limit); } // clangd extension bool isValid() const override { return contains(queryKey); } }; diff --git a/src/libs/languageutils/languageutils.qbs b/src/libs/languageutils/languageutils.qbs index 8b5d6a6e8ad..7a4cefd5ff3 100644 --- a/src/libs/languageutils/languageutils.qbs +++ b/src/libs/languageutils/languageutils.qbs @@ -1,20 +1,16 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "LanguageUtils" - QtcLibrary { - cpp.defines: base.concat(["LANGUAGEUTILS_LIBRARY"]) - cpp.optimization: "fast" + cpp.defines: base.concat(["LANGUAGEUTILS_LIBRARY"]) + cpp.optimization: "fast" - Depends { name: "Qt.core" } + Depends { name: "Qt.core" } - files: [ - "componentversion.cpp", - "componentversion.h", - "fakemetaobject.cpp", - "fakemetaobject.h", - "languageutils_global.h", - ] - } + files: [ + "componentversion.cpp", + "componentversion.h", + "fakemetaobject.cpp", + "fakemetaobject.h", + "languageutils_global.h", + ] } diff --git a/src/libs/modelinglib/qmt/config/configcontroller.h b/src/libs/modelinglib/qmt/config/configcontroller.h index 73efef16f4a..28e6d65afcd 100644 --- a/src/libs/modelinglib/qmt/config/configcontroller.h +++ b/src/libs/modelinglib/qmt/config/configcontroller.h @@ -3,9 +3,10 @@ #pragma once -#include #include "qmt/infrastructure/qmt_global.h" +#include + namespace qmt { class CustomRelation; @@ -15,7 +16,6 @@ class Toolbar; class QMT_EXPORT ConfigController : public QObject { - Q_OBJECT class ConfigControllerPrivate; public: diff --git a/src/libs/modelinglib/qmt/config/textscanner.h b/src/libs/modelinglib/qmt/config/textscanner.h index 341b2559039..d0bec89c93a 100644 --- a/src/libs/modelinglib/qmt/config/textscanner.h +++ b/src/libs/modelinglib/qmt/config/textscanner.h @@ -3,12 +3,12 @@ #pragma once -#include - #include "sourcepos.h" #include "qmt/infrastructure/exceptions.h" +#include + namespace qmt { class ITextSource; @@ -29,7 +29,6 @@ private: class QMT_EXPORT TextScanner : public QObject { - Q_OBJECT class TextScannerPrivate; public: diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.h b/src/libs/modelinglib/qmt/controller/namecontroller.h index b9289fb9cd9..03e3be30bb8 100644 --- a/src/libs/modelinglib/qmt/controller/namecontroller.h +++ b/src/libs/modelinglib/qmt/controller/namecontroller.h @@ -13,8 +13,6 @@ namespace qmt { class QMT_EXPORT NameController : public QObject { - Q_OBJECT - private: explicit NameController(QObject *parent = nullptr); ~NameController() override; diff --git a/src/libs/modelinglib/qmt/controller/undocontroller.h b/src/libs/modelinglib/qmt/controller/undocontroller.h index aaf62c786bd..1bef6ed2081 100644 --- a/src/libs/modelinglib/qmt/controller/undocontroller.h +++ b/src/libs/modelinglib/qmt/controller/undocontroller.h @@ -3,9 +3,10 @@ #pragma once -#include #include "qmt/infrastructure/qmt_global.h" +#include + QT_BEGIN_NAMESPACE class QUndoStack; QT_END_NAMESPACE @@ -16,8 +17,6 @@ class UndoCommand; class QMT_EXPORT UndoController : public QObject { - Q_OBJECT - public: explicit UndoController(QObject *parent = nullptr); ~UndoController() override; diff --git a/src/libs/modelinglib/qmt/diagram_scene/latchcontroller.h b/src/libs/modelinglib/qmt/diagram_scene/latchcontroller.h index b56a303d881..bc726c50eab 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/latchcontroller.h +++ b/src/libs/modelinglib/qmt/diagram_scene/latchcontroller.h @@ -3,11 +3,12 @@ #pragma once -#include #include "qmt/infrastructure/qmt_global.h" #include "capabilities/latchable.h" +#include + QT_BEGIN_NAMESPACE class QGraphicsScene; class QGraphicsSceneMouseEvent; @@ -22,8 +23,6 @@ class AlignLineItem; class QMT_EXPORT LatchController : public QObject { - Q_OBJECT - public: explicit LatchController(QObject *parent = nullptr); ~LatchController() override; @@ -44,7 +43,6 @@ private: void hideLatches(); void applyLatches(); -private: DiagramSceneModel *m_diagramSceneModel = nullptr; AlignLineItem *m_horizontalAlignLine = nullptr; AlignLineItem *m_verticalAlignLine = nullptr; diff --git a/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp b/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp index 3e38aabdf3f..b363decd683 100644 --- a/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp +++ b/src/libs/modelinglib/qmt/model_controller/modelcontroller.cpp @@ -878,7 +878,7 @@ void ModelController::moveRelation(MObject *newOwner, MRelation *relation) QList ModelController::findRelationsOfObject(const MObject *object) const { - QMT_ASSERT(object, return QList()); + QMT_ASSERT(object, return {}); return m_objectRelationsMap.values(object->uid()); } diff --git a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h index 02f02779e5b..0c01cf7de79 100644 --- a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h +++ b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h @@ -3,9 +3,9 @@ #pragma once -#include #include "qmt/infrastructure/qmt_global.h" +#include #include namespace qmt { @@ -14,8 +14,6 @@ class TreeModel; class QMT_EXPORT SortedTreeModel : public QSortFilterProxyModel { - Q_OBJECT - public: explicit SortedTreeModel(QObject *parent = nullptr); ~SortedTreeModel() override; diff --git a/src/libs/modelinglib/qmt/model_ui/treemodelmanager.h b/src/libs/modelinglib/qmt/model_ui/treemodelmanager.h index 0dee6d31f9b..9e0793aba87 100644 --- a/src/libs/modelinglib/qmt/model_ui/treemodelmanager.h +++ b/src/libs/modelinglib/qmt/model_ui/treemodelmanager.h @@ -3,9 +3,10 @@ #pragma once -#include #include "qmt/infrastructure/qmt_global.h" +#include + namespace qmt { class TreeModel; @@ -16,8 +17,6 @@ class MSelection; class QMT_EXPORT TreeModelManager : public QObject { - Q_OBJECT - public: explicit TreeModelManager(QObject *parent = nullptr); ~TreeModelManager() override; diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/classmembersedit.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/classmembersedit.cpp index d9146c038fe..3f14eb93e5d 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/classmembersedit.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/classmembersedit.cpp @@ -429,7 +429,7 @@ QString ClassMembersEdit::build(const QList &members) QList ClassMembersEdit::parse(const QString &text, bool *ok) { - QMT_ASSERT(ok, return QList()); + QMT_ASSERT(ok, {}); *ok = true; QList members; diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesview.h b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesview.h index ba115c03eda..b1255421d02 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesview.h +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesview.h @@ -3,11 +3,11 @@ #pragma once -#include - #include "qmt/infrastructure/qmt_global.h" +#include #include + #include QT_BEGIN_NAMESPACE @@ -28,8 +28,6 @@ class StyleController; class QMT_EXPORT PropertiesView : public QObject { - Q_OBJECT - public: class MView; diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h index 7b94f445b7c..4762803a209 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h @@ -3,8 +3,6 @@ #pragma once -#include - #include "propertiesview.h" #include "qmt/model/mconstvisitor.h" @@ -14,6 +12,7 @@ #include "qmt/style/styleengine.h" #include +#include QT_BEGIN_NAMESPACE class QWidget; @@ -36,8 +35,6 @@ class PaletteBox; class QMT_EXPORT PropertiesView::MView : public QObject, public MConstVisitor, public DConstVisitor { - Q_OBJECT - public: explicit MView(PropertiesView *propertiesView); ~MView() override; diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h index 949f1027a5c..676f9a275a5 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h +++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h @@ -3,11 +3,10 @@ #pragma once -#include - #include "stereotypeicon.h" #include +#include namespace qmt { @@ -17,7 +16,6 @@ class Style; class QMT_EXPORT StereotypeController : public QObject { - Q_OBJECT class StereotypeControllerPrivate; public: diff --git a/src/libs/modelinglib/qmt/style/stylecontroller.h b/src/libs/modelinglib/qmt/style/stylecontroller.h index 6eeeb61ac7f..2627bb2caa2 100644 --- a/src/libs/modelinglib/qmt/style/stylecontroller.h +++ b/src/libs/modelinglib/qmt/style/stylecontroller.h @@ -3,11 +3,10 @@ #pragma once -#include - #include "styleengine.h" #include "qmt/diagram/dobject.h" +#include #include namespace qmt { @@ -21,7 +20,6 @@ class DBoundary; class QMT_EXPORT StyleController : public QObject { - Q_OBJECT class Parameters; public: diff --git a/src/libs/nanotrace/nanotrace.cpp b/src/libs/nanotrace/nanotrace.cpp index 6af2db58bcf..c5adeb4d724 100644 --- a/src/libs/nanotrace/nanotrace.cpp +++ b/src/libs/nanotrace/nanotrace.cpp @@ -73,6 +73,7 @@ struct TraceEvent int64_t oh; int64_t ts; + int64_t dur = 0; std::vector< Nanotrace::Arg > args; @@ -100,7 +101,10 @@ std::ostream& operator<<(std::ostream &stream, const TraceEvent &event) << "{ \"cat\":\"" << event.cat << "\", \"pid\":" << event.pid << ", \"tid\":\"" << event.tid << "\"" - << ", \"ts\":" << event.ts + << ", \"ts\":" << event.ts; + if (event.dur > -1) + stream << ", \"dur\":" << event.dur; + stream << ", \"ph\":\"" << event.ph << "\", \"name\":\"" << event.name << "\", \"args\": { \"overhead\": " << event.oh; @@ -112,7 +116,6 @@ std::ostream& operator<<(std::ostream &stream, const TraceEvent &event) return stream; } - void init(const std::string &process, const std::string &thread, const std::string &path) { auto now = Clock::now(); @@ -125,55 +128,56 @@ void init(const std::string &process, const std::string &thread, const std::stri initEvent.ts = now; events.reserve(eventCount); - events.push_back(TraceEvent { - getProcessId(), - std::this_thread::get_id(), - "process_name", - "", - 'M', - 0, - 0, - {{"name", process}} - } ); + events.push_back(TraceEvent{getProcessId(), + std::this_thread::get_id(), + "process_name", + "", + 'M', + 0, + 0, + -1, + {{"name", process}}}); - events.push_back(TraceEvent { - getProcessId(), - std::this_thread::get_id(), - "thread_name", - "", - 'M', - 0, - 0, - {{"name", thread}} - } ); + events.push_back(TraceEvent{getProcessId(), + std::this_thread::get_id(), + "thread_name", + "", + 'M', + 0, + 0, + -1, + {{"name", thread}}}); if (std::ofstream stream(path, std::ios::trunc); stream.good()) stream << "{ \"traceEvents\": [\n"; else std::cout << "Nanotrace::init: stream not good" << std::endl; - events.push_back(TraceEvent { - getProcessId(), - std::this_thread::get_id(), - "Initialize", - "Initialize", - 'I', - 0, - std::chrono::duration_cast< Units >(Clock::now() - now).count(), - {} - } ); + events.push_back(TraceEvent{getProcessId(), + std::this_thread::get_id(), + "Initialize", + "Initialize", + 'I', + 0, + std::chrono::duration_cast(Clock::now() - now).count(), + -1, + {}}); initEvent.overhead = std::chrono::duration_cast< Units >(Clock::now() - now).count(); } void shutdown() { + if (!initEvent.initialized) + return; + flush(); if (std::ofstream stream(initEvent.filePath, std::ios::app); stream.good()) stream << "\n] }"; else std::cout << "Nanotrace::shutdown: stream not good" << std::endl; + initEvent = {}; } void addTracePoint( @@ -188,16 +192,15 @@ void addTracePoint( auto now = Clock::now(); auto beg = std::chrono::duration_cast< Units >(now - initEvent.ts); - events.push_back( TraceEvent { - getProcessId(), - std::this_thread::get_id(), - name, - cat, - phase, - initEvent.overhead, - beg.count(), - {arguments} - } ); + events.push_back(TraceEvent{getProcessId(), + std::this_thread::get_id(), + name, + cat, + phase, + initEvent.overhead, + beg.count(), + -1, + {arguments}}); if (events.size() >= eventCount - 1) flush(); @@ -238,20 +241,19 @@ ScopeTracer::~ScopeTracer() return; auto now = Clock::now(); - auto beg = std::chrono::duration_cast< Units >(m_start - initEvent.ts); + auto beg = std::chrono::duration_cast(m_start - initEvent.ts); + const int64_t dur = std::chrono::duration_cast(now - m_start).count(); - m_args.push_back(Arg("dur", std::chrono::duration_cast< Units >(now - m_start).count())); - - events.push_back(TraceEvent { - getProcessId(), - std::this_thread::get_id(), - m_name, - m_cat, - 'X', - initEvent.overhead, - beg.count(), - { m_args } - } ); + m_args.push_back(Arg("dur", dur)); + events.push_back(TraceEvent{getProcessId(), + std::this_thread::get_id(), + m_name, + m_cat, + 'X', + initEvent.overhead, + beg.count(), + dur, + {m_args}}); if (events.size() >= eventCount - 1) flush(); diff --git a/src/libs/nanotrace/nanotrace.h b/src/libs/nanotrace/nanotrace.h index fae1892b80b..ed17797e344 100644 --- a/src/libs/nanotrace/nanotrace.h +++ b/src/libs/nanotrace/nanotrace.h @@ -45,7 +45,7 @@ namespace Nanotrace { -using Units = std::chrono::nanoseconds; +using Units = std::chrono::microseconds; using Clock = std::chrono::high_resolution_clock; using TimePoint = std::chrono::time_point< Clock >; diff --git a/src/libs/qlitehtml b/src/libs/qlitehtml index 8e541a22b51..b8f6617f22a 160000 --- a/src/libs/qlitehtml +++ b/src/libs/qlitehtml @@ -1 +1 @@ -Subproject commit 8e541a22b513432ed566fca824af207395ee0c90 +Subproject commit b8f6617f22a7bd0bf3da2e75d1613e1346b974f0 diff --git a/src/libs/qmldebug/qmldebug.qbs b/src/libs/qmldebug/qmldebug.qbs index bad5947682d..387f22b0aa0 100644 --- a/src/libs/qmldebug/qmldebug.qbs +++ b/src/libs/qmldebug/qmldebug.qbs @@ -1,40 +1,36 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "QmlDebug" - QtcLibrary { - cpp.defines: base.concat("QMLDEBUG_LIBRARY") + cpp.defines: base.concat("QMLDEBUG_LIBRARY") - Depends { name: "Qt"; submodules: ["gui", "network"] } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["gui", "network"] } + Depends { name: "Utils" } - files: [ - "baseenginedebugclient.cpp", - "baseenginedebugclient.h", - "basetoolsclient.cpp", - "basetoolsclient.h", - "qdebugmessageclient.cpp", - "qdebugmessageclient.h", - "qmldebug_global.h", - "qmldebugtr.h", - "qmldebugclient.cpp", - "qmldebugclient.h", - "qmldebugcommandlinearguments.h", - "qmldebugconnection.cpp", - "qmldebugconnection.h", - "qmldebugconnectionmanager.cpp", - "qmldebugconnectionmanager.h", - "qmldebugconstants.h", - "qmlenginecontrolclient.cpp", - "qmlenginecontrolclient.h", - "qmlenginedebugclient.h", - "qmloutputparser.cpp", - "qmloutputparser.h", - "qmltoolsclient.cpp", - "qmltoolsclient.h", - "qpacketprotocol.cpp", - "qpacketprotocol.h", - ] - } + files: [ + "baseenginedebugclient.cpp", + "baseenginedebugclient.h", + "basetoolsclient.cpp", + "basetoolsclient.h", + "qdebugmessageclient.cpp", + "qdebugmessageclient.h", + "qmldebug_global.h", + "qmldebugtr.h", + "qmldebugclient.cpp", + "qmldebugclient.h", + "qmldebugcommandlinearguments.h", + "qmldebugconnection.cpp", + "qmldebugconnection.h", + "qmldebugconnectionmanager.cpp", + "qmldebugconnectionmanager.h", + "qmldebugconstants.h", + "qmlenginecontrolclient.cpp", + "qmlenginecontrolclient.h", + "qmlenginedebugclient.h", + "qmloutputparser.cpp", + "qmloutputparser.h", + "qmltoolsclient.cpp", + "qmltoolsclient.h", + "qpacketprotocol.cpp", + "qpacketprotocol.h", + ] } diff --git a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp index 12ec4da3d51..f188d6aa40a 100644 --- a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp +++ b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -260,7 +259,7 @@ void ContextPaneWidgetImage::setProperties(QmlJS::PropertyReader *propertyReader if (propertyReader->hasProperty(QLatin1String("source"))) { QString source = propertyReader->readProperty(QLatin1String("source")).toString(); m_fileWidget->setFileName(source); - if (QFile::exists(m_path + QLatin1Char('/') + source)) + if (QFileInfo::exists(m_path + QLatin1Char('/') + source)) setPixmap(m_path + QLatin1Char('/') + source); else setPixmap(source); @@ -901,11 +900,6 @@ PreviewDialog::PreviewDialog(QWidget *parent) : DragWidget(parent) setZoom(1); - QVBoxLayout *layout = new QVBoxLayout(this); - QHBoxLayout *horizontalLayout = new QHBoxLayout(); - QHBoxLayout *horizontalLayout2 = new QHBoxLayout(); - layout->setContentsMargins(2, 2, 2, 16); - layout->setSpacing(4); QToolButton *toolButton = new QToolButton(this); QIcon icon(style()->standardIcon(QStyle::SP_DockWidgetCloseButton)); toolButton->setIcon(icon); @@ -922,17 +916,14 @@ PreviewDialog::PreviewDialog(QWidget *parent) : DragWidget(parent) m_slider->setFixedWidth(80); m_zoomLabel->setFixedWidth(50); - horizontalLayout->addWidget(toolButton); - horizontalLayout->addSpacing(6); - horizontalLayout->addWidget(m_slider); - horizontalLayout->addSpacing(6); - horizontalLayout->addWidget(m_zoomLabel); - horizontalLayout->addStretch(1); - - layout->addLayout(horizontalLayout); - horizontalLayout2->addSpacing(24); - horizontalLayout2->addWidget(scrollArea); - layout->addLayout(horizontalLayout2); + using namespace Layouting; + Row { + Column { toolButton, st }, + Column { + Row { m_slider, m_zoomLabel, st }, + scrollArea, + } + }.attachTo(this); wheelFilter->setTarget(this); diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt index d166e666e74..4ca15cf7cc2 100644 --- a/src/libs/qmljs/CMakeLists.txt +++ b/src/libs/qmljs/CMakeLists.txt @@ -32,7 +32,6 @@ add_qtc_library(QmlJS qmljsevaluate.cpp qmljsevaluate.h qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h qmljsicons.cpp qmljsicons.h - qmljsicontextpane.h qmljsimportdependencies.cpp qmljsimportdependencies.h qmljsindenter.cpp qmljsindenter.h qmljsinterpreter.cpp qmljsinterpreter.h diff --git a/src/libs/qmljs/jsoncheck.cpp b/src/libs/qmljs/jsoncheck.cpp index b7ce1443fb9..1d7c04e50cf 100644 --- a/src/libs/qmljs/jsoncheck.cpp +++ b/src/libs/qmljs/jsoncheck.cpp @@ -4,19 +4,24 @@ #include "jsoncheck.h" #include + +#include #include #include +#include +#include #include #include #include -using namespace QmlJS; using namespace QmlJS::AST; using namespace QmlJS::StaticAnalysis; using namespace Utils; +namespace QmlJS { + JsonCheck::JsonCheck(Document::Ptr doc) : m_doc(doc) , m_schema(nullptr) @@ -29,7 +34,7 @@ JsonCheck::~JsonCheck() QList JsonCheck::operator ()(JsonSchema *schema) { - QTC_ASSERT(schema, return QList()); + QTC_ASSERT(schema, return {}); m_schema = schema; @@ -384,3 +389,716 @@ JsonCheck::AnalysisData *JsonCheck::analysis() { return &m_analysis.top(); } + + +JsonMemoryPool::~JsonMemoryPool() +{ + for (char *obj : std::as_const(_objs)) { + reinterpret_cast(obj)->~JsonValue(); + delete[] obj; + } +} + +JsonValue::JsonValue(Kind kind) + : m_kind(kind) +{} + +JsonValue::~JsonValue() = default; + +JsonValue *JsonValue::create(const QString &s, JsonMemoryPool *pool) +{ + const QJsonDocument document = QJsonDocument::fromJson(s.toUtf8()); + if (document.isNull()) + return nullptr; + + return build(document.toVariant(), pool); +} + +void *JsonValue::operator new(size_t size, JsonMemoryPool *pool) +{ return pool->allocate(size); } + +void JsonValue::operator delete(void *) +{ } + +void JsonValue::operator delete(void *, JsonMemoryPool *) +{ } + +QString JsonValue::kindToString(JsonValue::Kind kind) +{ + if (kind == String) + return QLatin1String("string"); + if (kind == Double) + return QLatin1String("number"); + if (kind == Int) + return QLatin1String("integer"); + if (kind == Object) + return QLatin1String("object"); + if (kind == Array) + return QLatin1String("array"); + if (kind == Boolean) + return QLatin1String("boolean"); + if (kind == Null) + return QLatin1String("null"); + + return QLatin1String("unknown"); +} + +JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool) +{ + switch (variant.typeId()) { + + case QVariant::List: { + auto newValue = new (pool) JsonArrayValue; + const QList list = variant.toList(); + for (const QVariant &element : list) + newValue->addElement(build(element, pool)); + return newValue; + } + + case QVariant::Map: { + auto newValue = new (pool) JsonObjectValue; + const QVariantMap variantMap = variant.toMap(); + for (QVariantMap::const_iterator it = variantMap.begin(); it != variantMap.end(); ++it) + newValue->addMember(it.key(), build(it.value(), pool)); + return newValue; + } + + case QVariant::String: + return new (pool) JsonStringValue(variant.toString()); + + case QVariant::Int: + return new (pool) JsonIntValue(variant.toInt()); + + case QVariant::Double: + return new (pool) JsonDoubleValue(variant.toDouble()); + + case QVariant::Bool: + return new (pool) JsonBooleanValue(variant.toBool()); + + case QVariant::Invalid: + return new (pool) JsonNullValue; + + default: + break; + } + + return nullptr; +} + + +/////////////////////////////////////////////////////////////////////////////// + +QString JsonSchema::kType() { return QStringLiteral("type"); } +QString JsonSchema::kProperties() { return QStringLiteral("properties"); } +QString JsonSchema::kPatternProperties() { return QStringLiteral("patternProperties"); } +QString JsonSchema::kAdditionalProperties() { return QStringLiteral("additionalProperties"); } +QString JsonSchema::kItems() { return QStringLiteral("items"); } +QString JsonSchema::kAdditionalItems() { return QStringLiteral("additionalItems"); } +QString JsonSchema::kRequired() { return QStringLiteral("required"); } +QString JsonSchema::kDependencies() { return QStringLiteral("dependencies"); } +QString JsonSchema::kMinimum() { return QStringLiteral("minimum"); } +QString JsonSchema::kMaximum() { return QStringLiteral("maximum"); } +QString JsonSchema::kExclusiveMinimum() { return QStringLiteral("exclusiveMinimum"); } +QString JsonSchema::kExclusiveMaximum() { return QStringLiteral("exclusiveMaximum"); } +QString JsonSchema::kMinItems() { return QStringLiteral("minItems"); } +QString JsonSchema::kMaxItems() { return QStringLiteral("maxItems"); } +QString JsonSchema::kUniqueItems() { return QStringLiteral("uniqueItems"); } +QString JsonSchema::kPattern() { return QStringLiteral("pattern"); } +QString JsonSchema::kMinLength() { return QStringLiteral("minLength"); } +QString JsonSchema::kMaxLength() { return QStringLiteral("maxLength"); } +QString JsonSchema::kTitle() { return QStringLiteral("title"); } +QString JsonSchema::kDescription() { return QStringLiteral("description"); } +QString JsonSchema::kExtends() { return QStringLiteral("extends"); } +QString JsonSchema::kRef() { return QStringLiteral("$ref"); } + +JsonSchema::JsonSchema(JsonObjectValue *rootObject, const JsonSchemaManager *manager) + : m_manager(manager) +{ + enter(rootObject); +} + +bool JsonSchema::isTypeConstrained() const +{ + // Simple types + if (JsonStringValue *sv = getStringValue(kType(), currentValue())) + return isCheckableType(sv->value()); + + // Union types + if (JsonArrayValue *av = getArrayValue(kType(), currentValue())) { + QTC_ASSERT(currentIndex() != -1, return false); + QTC_ASSERT(av->elements().at(currentIndex())->kind() == JsonValue::String, return false); + JsonStringValue *sv = av->elements().at(currentIndex())->toString(); + return isCheckableType(sv->value()); + } + + return false; +} + +bool JsonSchema::acceptsType(const QString &type) const +{ + // Simple types + if (JsonStringValue *sv = getStringValue(kType(), currentValue())) + return typeMatches(sv->value(), type); + + // Union types + if (JsonArrayValue *av = getArrayValue(kType(), currentValue())) { + QTC_ASSERT(currentIndex() != -1, return false); + QTC_ASSERT(av->elements().at(currentIndex())->kind() == JsonValue::String, return false); + JsonStringValue *sv = av->elements().at(currentIndex())->toString(); + return typeMatches(sv->value(), type); + } + + return false; +} + +QStringList JsonSchema::validTypes(JsonObjectValue *v) +{ + QStringList all; + + if (JsonStringValue *sv = getStringValue(kType(), v)) + all.append(sv->value()); + + if (JsonObjectValue *ov = getObjectValue(kType(), v)) + return validTypes(ov); + + if (JsonArrayValue *av = getArrayValue(kType(), v)) { + const QList elements = av->elements(); + for (JsonValue *v : elements) { + if (JsonStringValue *sv = v->toString()) + all.append(sv->value()); + else if (JsonObjectValue *ov = v->toObject()) + all.append(validTypes(ov)); + } + } + + return all; +} + +bool JsonSchema::typeMatches(const QString &expected, const QString &actual) +{ + if (expected == QLatin1String("number") && actual == QLatin1String("integer")) + return true; + + return expected == actual; +} + +bool JsonSchema::isCheckableType(const QString &s) +{ + return s == QLatin1String("string") + || s == QLatin1String("number") + || s == QLatin1String("integer") + || s == QLatin1String("boolean") + || s == QLatin1String("object") + || s == QLatin1String("array") + || s == QLatin1String("null"); +} + +QStringList JsonSchema::validTypes() const +{ + return validTypes(currentValue()); +} + +bool JsonSchema::hasTypeSchema() const +{ + return getObjectValue(kType(), currentValue()); +} + +void JsonSchema::enterNestedTypeSchema() +{ + QTC_ASSERT(hasTypeSchema(), return); + + enter(getObjectValue(kType(), currentValue())); +} + +QStringList JsonSchema::properties(JsonObjectValue *v) const +{ + using Members = QHash; + + QStringList all; + + if (JsonObjectValue *ov = getObjectValue(kProperties(), v)) { + const Members members = ov->members(); + const Members::ConstIterator cend = members.constEnd(); + for (Members::ConstIterator it = members.constBegin(); it != cend; ++it) + if (hasPropertySchema(it.key())) + all.append(it.key()); + } + + if (JsonObjectValue *base = resolveBase(v)) + all.append(properties(base)); + + return all; +} + +QStringList JsonSchema::properties() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Object)), return {}); + + return properties(currentValue()); +} + +JsonObjectValue *JsonSchema::propertySchema(const QString &property, + JsonObjectValue *v) const +{ + if (JsonObjectValue *ov = getObjectValue(kProperties(), v)) { + JsonValue *member = ov->member(property); + if (member && member->kind() == JsonValue::Object) + return member->toObject(); + } + + if (JsonObjectValue *base = resolveBase(v)) + return propertySchema(property, base); + + return nullptr; +} + +bool JsonSchema::hasPropertySchema(const QString &property) const +{ + return propertySchema(property, currentValue()); +} + +void JsonSchema::enterNestedPropertySchema(const QString &property) +{ + QTC_ASSERT(hasPropertySchema(property), return); + + JsonObjectValue *schema = propertySchema(property, currentValue()); + + enter(schema); +} + +/*! + * An array schema is allowed to have its \e items specification in the form of + * another schema + * or in the form of an array of schemas [Sec. 5.5]. This functions checks whether this is case + * in which the items are a schema. + * + * Returns whether or not the items from the array are a schema. + */ +bool JsonSchema::hasItemSchema() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); + + return getObjectValue(kItems(), currentValue()); +} + +void JsonSchema::enterNestedItemSchema() +{ + QTC_ASSERT(hasItemSchema(), return); + + enter(getObjectValue(kItems(), currentValue())); +} + +/*! + * An array schema is allowed to have its \e items specification in the form of another schema + * or in the form of an array of schemas [Sec. 5.5]. This functions checks whether this is case + * in which the items are an array of schemas. + * + * Returns whether or not the items from the array are a an array of schemas. + */ +bool JsonSchema::hasItemArraySchema() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); + + return getArrayValue(kItems(), currentValue()); +} + +int JsonSchema::itemArraySchemaSize() const +{ + QTC_ASSERT(hasItemArraySchema(), return false); + + return getArrayValue(kItems(), currentValue())->size(); +} + +/*! + * When evaluating the items of an array it might be necessary to \e enter a + * particular schema, + * since this API assumes that there's always a valid schema in context (the one the user is + * interested on). This shall only happen if the item at the supplied array index is of type + * object, which is then assumed to be a schema. + * + * The function also marks the context as being inside an array evaluation. + * + * Returns whether it was necessary to enter a schema for the supplied + * array \a index, false if index is out of bounds. + */ +bool JsonSchema::maybeEnterNestedArraySchema(int index) +{ + QTC_ASSERT(itemArraySchemaSize(), return false); + QTC_ASSERT(index >= 0 && index < itemArraySchemaSize(), return false); + + JsonValue *v = getArrayValue(kItems(), currentValue())->elements().at(index); + + return maybeEnter(v, Array, index); +} + +/*! + * The type of a schema can be specified in the form of a union type, which is basically an + * array of allowed types for the particular instance [Sec. 5.1]. This function checks whether + * the current schema is one of such. + * + * Returns whether or not the current schema specifies a union type. + */ +bool JsonSchema::hasUnionSchema() const +{ + return getArrayValue(kType(), currentValue()); +} + +int JsonSchema::unionSchemaSize() const +{ + return getArrayValue(kType(), currentValue())->size(); +} + +/*! + * When evaluating union types it might be necessary to enter a particular + * schema, since this + * API assumes that there's always a valid schema in context (the one the user is interested on). + * This shall only happen if the item at the supplied union \a index, which is then assumed to be + * a schema. + * + * The function also marks the context as being inside an union evaluation. + * + * Returns whether or not it was necessary to enter a schema for the + * supplied union index. + */ +bool JsonSchema::maybeEnterNestedUnionSchema(int index) +{ + QTC_ASSERT(unionSchemaSize(), return false); + QTC_ASSERT(index >= 0 && index < unionSchemaSize(), return false); + + JsonValue *v = getArrayValue(kType(), currentValue())->elements().at(index); + + return maybeEnter(v, Union, index); +} + +void JsonSchema::leaveNestedSchema() +{ + QTC_ASSERT(!m_schemas.isEmpty(), return); + + leave(); +} + +bool JsonSchema::required() const +{ + if (JsonBooleanValue *bv = getBooleanValue(kRequired(), currentValue())) + return bv->value(); + + return false; +} + +bool JsonSchema::hasMinimum() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); + + return getDoubleValue(kMinimum(), currentValue()); +} + +double JsonSchema::minimum() const +{ + QTC_ASSERT(hasMinimum(), return 0); + + return getDoubleValue(kMinimum(), currentValue())->value(); +} + +bool JsonSchema::hasExclusiveMinimum() +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); + + if (JsonBooleanValue *bv = getBooleanValue(kExclusiveMinimum(), currentValue())) + return bv->value(); + + return false; +} + +bool JsonSchema::hasMaximum() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); + + return getDoubleValue(kMaximum(), currentValue()); +} + +double JsonSchema::maximum() const +{ + QTC_ASSERT(hasMaximum(), return 0); + + return getDoubleValue(kMaximum(), currentValue())->value(); +} + +bool JsonSchema::hasExclusiveMaximum() +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); + + if (JsonBooleanValue *bv = getBooleanValue(kExclusiveMaximum(), currentValue())) + return bv->value(); + + return false; +} + +QString JsonSchema::pattern() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return QString()); + + if (JsonStringValue *sv = getStringValue(kPattern(), currentValue())) + return sv->value(); + + return QString(); +} + +int JsonSchema::minimumLength() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return -1); + + if (JsonDoubleValue *dv = getDoubleValue(kMinLength(), currentValue())) + return dv->value(); + + return -1; +} + +int JsonSchema::maximumLength() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return -1); + + if (JsonDoubleValue *dv = getDoubleValue(kMaxLength(), currentValue())) + return dv->value(); + + return -1; +} + +bool JsonSchema::hasAdditionalItems() const +{ + QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); + + return currentValue()->member(kAdditionalItems()); +} + +bool JsonSchema::maybeSchemaName(const QString &s) +{ + if (s.isEmpty() || s == QLatin1String("any")) + return false; + + return !isCheckableType(s); +} + +JsonObjectValue *JsonSchema::rootValue() const +{ + QTC_ASSERT(!m_schemas.isEmpty(), return nullptr); + + return m_schemas.first().m_value; +} + +JsonObjectValue *JsonSchema::currentValue() const +{ + QTC_ASSERT(!m_schemas.isEmpty(), return nullptr); + + return m_schemas.last().m_value; +} + +int JsonSchema::currentIndex() const +{ + QTC_ASSERT(!m_schemas.isEmpty(), return 0); + + return m_schemas.last().m_index; +} + +void JsonSchema::evaluate(EvaluationMode eval, int index) +{ + QTC_ASSERT(!m_schemas.isEmpty(), return); + + m_schemas.last().m_eval = eval; + m_schemas.last().m_index = index; +} + +void JsonSchema::enter(JsonObjectValue *ov, EvaluationMode eval, int index) +{ + Context context; + context.m_eval = eval; + context.m_index = index; + context.m_value = resolveReference(ov); + + m_schemas.push_back(context); +} + +bool JsonSchema::maybeEnter(JsonValue *v, EvaluationMode eval, int index) +{ + evaluate(eval, index); + + if (v->kind() == JsonValue::Object) { + enter(v->toObject()); + return true; + } + + if (v->kind() == JsonValue::String) { + const QString &s = v->toString()->value(); + if (maybeSchemaName(s)) { + JsonSchema *schema = m_manager->schemaByName(s); + if (schema) { + enter(schema->rootValue()); + return true; + } + } + } + + return false; +} + +void JsonSchema::leave() +{ + QTC_ASSERT(!m_schemas.isEmpty(), return); + + m_schemas.pop_back(); +} + +JsonObjectValue *JsonSchema::resolveReference(JsonObjectValue *ov) const +{ + if (JsonStringValue *sv = getStringValue(kRef(), ov)) { + JsonSchema *referenced = m_manager->schemaByName(sv->value()); + if (referenced) + return referenced->rootValue(); + } + + return ov; +} + +JsonObjectValue *JsonSchema::resolveBase(JsonObjectValue *ov) const +{ + if (JsonValue *v = ov->member(kExtends())) { + if (v->kind() == JsonValue::String) { + JsonSchema *schema = m_manager->schemaByName(v->toString()->value()); + if (schema) + return schema->rootValue(); + } else if (v->kind() == JsonValue::Object) { + return resolveReference(v->toObject()); + } + } + + return nullptr; +} + +JsonStringValue *JsonSchema::getStringValue(const QString &name, JsonObjectValue *value) +{ + JsonValue *v = value->member(name); + if (!v) + return nullptr; + + return v->toString(); +} + +JsonObjectValue *JsonSchema::getObjectValue(const QString &name, JsonObjectValue *value) +{ + JsonValue *v = value->member(name); + if (!v) + return nullptr; + + return v->toObject(); +} + +JsonBooleanValue *JsonSchema::getBooleanValue(const QString &name, JsonObjectValue *value) +{ + JsonValue *v = value->member(name); + if (!v) + return nullptr; + + return v->toBoolean(); +} + +JsonArrayValue *JsonSchema::getArrayValue(const QString &name, JsonObjectValue *value) +{ + JsonValue *v = value->member(name); + if (!v) + return nullptr; + + return v->toArray(); +} + +JsonDoubleValue *JsonSchema::getDoubleValue(const QString &name, JsonObjectValue *value) +{ + JsonValue *v = value->member(name); + if (!v) + return nullptr; + + return v->toDouble(); +} + + +/////////////////////////////////////////////////////////////////////////////// + +JsonSchemaManager::JsonSchemaManager(const QStringList &searchPaths) + : m_searchPaths(searchPaths) +{ + for (const QString &path : searchPaths) { + QDir dir(path); + if (!dir.exists()) + continue; + dir.setNameFilters(QStringList(QLatin1String("*.json"))); + const QList entries = dir.entryInfoList(); + for (const QFileInfo &fi : entries) + m_schemas.insert(fi.baseName(), JsonSchemaData(fi.absoluteFilePath())); + } +} + +JsonSchemaManager::~JsonSchemaManager() +{ + for (const JsonSchemaData &schemaData : std::as_const(m_schemas)) + delete schemaData.m_schema; +} + +/*! + * Tries to find a JSON schema to validate \a fileName against. According + * to the specification, how the schema/instance association is done is implementation defined. + * Currently we use a quite naive approach which is simply based on file names. Specifically, + * if one opens a foo.json file we'll look for a schema named foo.json. We should probably + * investigate alternative settings later. + * + * Returns a valid schema or 0. + */ +JsonSchema *JsonSchemaManager::schemaForFile(const QString &fileName) const +{ + QString baseName(QFileInfo(fileName).baseName()); + + return schemaByName(baseName); +} + +JsonSchema *JsonSchemaManager::schemaByName(const QString &baseName) const +{ + QHash::iterator it = m_schemas.find(baseName); + if (it == m_schemas.end()) { + for (const QString &path : m_searchPaths) { + QFileInfo candidate(path + baseName + ".json"); + if (candidate.exists()) { + m_schemas.insert(baseName, candidate.absoluteFilePath()); + break; + } + } + } + + it = m_schemas.find(baseName); + if (it == m_schemas.end()) + return nullptr; + + JsonSchemaData *schemaData = &it.value(); + if (!schemaData->m_schema) { + // Schemas are built on-demand. + QFileInfo currentSchema(schemaData->m_absoluteFileName); + Q_ASSERT(currentSchema.exists()); + if (schemaData->m_lastParseAttempt.isNull() + || schemaData->m_lastParseAttempt < currentSchema.lastModified()) { + schemaData->m_schema = parseSchema(currentSchema.absoluteFilePath()); + } + } + + return schemaData->m_schema; +} + +JsonSchema *JsonSchemaManager::parseSchema(const QString &schemaFileName) const +{ + FileReader reader; + if (reader.fetch(FilePath::fromString(schemaFileName), QIODevice::Text)) { + const QString &contents = QString::fromUtf8(reader.data()); + JsonValue *json = JsonValue::create(contents, &m_pool); + if (json && json->kind() == JsonValue::Object) + return new JsonSchema(json->toObject(), this); + } + + return nullptr; +} + +} // QmlJs diff --git a/src/libs/qmljs/jsoncheck.h b/src/libs/qmljs/jsoncheck.h index 6050ff72897..261c349cc30 100644 --- a/src/libs/qmljs/jsoncheck.h +++ b/src/libs/qmljs/jsoncheck.h @@ -9,21 +9,397 @@ #include #include -#include - #include #include - namespace QmlJS { +class JsonStringValue; +class JsonDoubleValue; +class JsonIntValue; +class JsonObjectValue; +class JsonArrayValue; +class JsonBooleanValue; +class JsonNullValue; + +class QMLJS_EXPORT JsonMemoryPool +{ +public: + ~JsonMemoryPool(); + + inline void *allocate(size_t size) + { + auto obj = new char[size]; + _objs.append(obj); + return obj; + } + +private: + QList _objs; +}; + +/*! + * \brief The JsonValue class + */ +class QMLJS_EXPORT JsonValue +{ +public: + enum Kind { + String, + Double, + Int, + Object, + Array, + Boolean, + Null, + Unknown + }; + + virtual ~JsonValue(); + + Kind kind() const { return m_kind; } + static QString kindToString(Kind kind); + + virtual JsonStringValue *toString() { return nullptr; } + virtual JsonDoubleValue *toDouble() { return nullptr; } + virtual JsonIntValue *toInt() { return nullptr; } + virtual JsonObjectValue *toObject() { return nullptr; } + virtual JsonArrayValue *toArray() { return nullptr; } + virtual JsonBooleanValue *toBoolean() { return nullptr; } + virtual JsonNullValue *toNull() { return nullptr; } + + static JsonValue *create(const QString &s, JsonMemoryPool *pool); + void *operator new(size_t size, JsonMemoryPool *pool); + void operator delete(void *); + void operator delete(void *, JsonMemoryPool *); + +protected: + JsonValue(Kind kind); + +private: + static JsonValue *build(const QVariant &varixant, JsonMemoryPool *pool); + + Kind m_kind; +}; + + +/*! + * \brief The JsonStringValue class + */ +class QMLJS_EXPORT JsonStringValue : public JsonValue +{ +public: + JsonStringValue(const QString &value) + : JsonValue(String) + , m_value(value) + {} + + JsonStringValue *toString() override { return this; } + + const QString &value() const { return m_value; } + +private: + QString m_value; +}; + + +/*! + * \brief The JsonDoubleValue class + */ +class QMLJS_EXPORT JsonDoubleValue : public JsonValue +{ +public: + JsonDoubleValue(double value) + : JsonValue(Double) + , m_value(value) + {} + + JsonDoubleValue *toDouble() override { return this; } + + double value() const { return m_value; } + +private: + double m_value; +}; + +/*! + * \brief The JsonIntValue class + */ +class QMLJS_EXPORT JsonIntValue : public JsonValue +{ +public: + JsonIntValue(int value) + : JsonValue(Int) + , m_value(value) + {} + + JsonIntValue *toInt() override { return this; } + + int value() const { return m_value; } + +private: + int m_value; +}; + + +/*! + * \brief The JsonObjectValue class + */ +class QMLJS_EXPORT JsonObjectValue : public JsonValue +{ +public: + JsonObjectValue() + : JsonValue(Object) + {} + + JsonObjectValue *toObject() override { return this; } + + void addMember(const QString &name, JsonValue *value) { m_members.insert(name, value); } + bool hasMember(const QString &name) const { return m_members.contains(name); } + JsonValue *member(const QString &name) const { return m_members.value(name); } + QHash members() const { return m_members; } + bool isEmpty() const { return m_members.isEmpty(); } + +protected: + JsonObjectValue(Kind kind) + : JsonValue(kind) + {} + +private: + QHash m_members; +}; + + +/*! + * \brief The JsonArrayValue class + */ +class QMLJS_EXPORT JsonArrayValue : public JsonValue +{ +public: + JsonArrayValue() + : JsonValue(Array) + {} + + JsonArrayValue *toArray() override { return this; } + + void addElement(JsonValue *value) { m_elements.append(value); } + QList elements() const { return m_elements; } + int size() const { return m_elements.size(); } + +private: + QList m_elements; +}; + + +/*! + * \brief The JsonBooleanValue class + */ +class QMLJS_EXPORT JsonBooleanValue : public JsonValue +{ +public: + JsonBooleanValue(bool value) + : JsonValue(Boolean) + , m_value(value) + {} + + JsonBooleanValue *toBoolean() override { return this; } + + bool value() const { return m_value; } + +private: + bool m_value; +}; + +class QMLJS_EXPORT JsonNullValue : public JsonValue +{ +public: + JsonNullValue() + : JsonValue(Null) + {} + + JsonNullValue *toNull() override { return this; } +}; + +class JsonSchemaManager; + +/*! + * \brief The JsonSchema class + * + * [NOTE: This is an incomplete implementation and a work in progress.] + * + * This class provides an interface for traversing and evaluating a JSON schema, as described + * in the draft http://tools.ietf.org/html/draft-zyp-json-schema-03. + * + * JSON schemas are recursive in concept. This means that a particular attribute from a schema + * might be also another schema. Therefore, the basic working principle of this API is that + * from within some schema, one can investigate its attributes and if necessary "enter" a + * corresponding nested schema. Afterwards, it's expected that one would "leave" such nested + * schema. + * + * All functions assume that the current "context" is a valid schema. Once an instance of this + * class is created the root schema is put on top of the stack. + * + */ +class QMLJS_EXPORT JsonSchema +{ +public: + bool isTypeConstrained() const; + bool acceptsType(const QString &type) const; + QStringList validTypes() const; + + // Applicable on schemas of any type. + bool required() const; + + bool hasTypeSchema() const; + void enterNestedTypeSchema(); + + bool hasUnionSchema() const; + int unionSchemaSize() const; + bool maybeEnterNestedUnionSchema(int index); + + void leaveNestedSchema(); + + // Applicable on schemas of type number/integer. + bool hasMinimum() const; + bool hasMaximum() const; + bool hasExclusiveMinimum(); + bool hasExclusiveMaximum(); + double minimum() const; + double maximum() const; + + // Applicable on schemas of type string. + QString pattern() const; + int minimumLength() const; + int maximumLength() const; + + // Applicable on schemas of type object. + QStringList properties() const; + bool hasPropertySchema(const QString &property) const; + void enterNestedPropertySchema(const QString &property); + + // Applicable on schemas of type array. + bool hasAdditionalItems() const; + + bool hasItemSchema() const; + void enterNestedItemSchema(); + + bool hasItemArraySchema() const; + int itemArraySchemaSize() const; + bool maybeEnterNestedArraySchema(int index); + +private: + friend class JsonSchemaManager; + JsonSchema(JsonObjectValue *rootObject, const JsonSchemaManager *manager); + Q_DISABLE_COPY(JsonSchema) + + enum EvaluationMode { + Normal, + Array, + Union + }; + + void enter(JsonObjectValue *ov, EvaluationMode eval = Normal, int index = -1); + bool maybeEnter(JsonValue *v, EvaluationMode eval, int index); + void evaluate(EvaluationMode eval, int index); + void leave(); + + JsonObjectValue *resolveReference(JsonObjectValue *ov) const; + JsonObjectValue *resolveBase(JsonObjectValue *ov) const; + + JsonObjectValue *currentValue() const; + int currentIndex() const; + + JsonObjectValue *rootValue() const; + + static JsonStringValue *getStringValue(const QString &name, JsonObjectValue *value); + static JsonObjectValue *getObjectValue(const QString &name, JsonObjectValue *value); + static JsonBooleanValue *getBooleanValue(const QString &name, JsonObjectValue *value); + static JsonArrayValue *getArrayValue(const QString &name, JsonObjectValue *value); + static JsonDoubleValue *getDoubleValue(const QString &name, JsonObjectValue *value); + + static QStringList validTypes(JsonObjectValue *v); + static bool typeMatches(const QString &expected, const QString &actual); + static bool isCheckableType(const QString &s); + + QStringList properties(JsonObjectValue *v) const; + JsonObjectValue *propertySchema(const QString &property, JsonObjectValue *v) const; + // TODO: Similar functions for other attributes which require looking into base schemas. + + static bool maybeSchemaName(const QString &s); + + static QString kType(); + static QString kProperties(); + static QString kPatternProperties(); + static QString kAdditionalProperties(); + static QString kItems(); + static QString kAdditionalItems(); + static QString kRequired(); + static QString kDependencies(); + static QString kMinimum(); + static QString kMaximum(); + static QString kExclusiveMinimum(); + static QString kExclusiveMaximum(); + static QString kMinItems(); + static QString kMaxItems(); + static QString kUniqueItems(); + static QString kPattern(); + static QString kMinLength(); + static QString kMaxLength(); + static QString kTitle(); + static QString kDescription(); + static QString kExtends(); + static QString kRef(); + + struct Context + { + JsonObjectValue *m_value; + EvaluationMode m_eval; + int m_index; + }; + + QList m_schemas; + const JsonSchemaManager *m_manager; +}; + + +/*! + * \brief The JsonSchemaManager class + */ +class QMLJS_EXPORT JsonSchemaManager +{ +public: + JsonSchemaManager(const QStringList &searchPaths); + ~JsonSchemaManager(); + + JsonSchema *schemaForFile(const QString &fileName) const; + JsonSchema *schemaByName(const QString &baseName) const; + +private: + struct JsonSchemaData + { + JsonSchemaData(const QString &absoluteFileName, JsonSchema *schema = nullptr) + : m_absoluteFileName(absoluteFileName) + , m_schema(schema) + {} + QString m_absoluteFileName; + JsonSchema *m_schema; + QDateTime m_lastParseAttempt; + }; + + JsonSchema *parseSchema(const QString &schemaFileName) const; + + QStringList m_searchPaths; + mutable QHash m_schemas; + mutable JsonMemoryPool m_pool; +}; + class QMLJS_EXPORT JsonCheck : public AST::Visitor { public: JsonCheck(Document::Ptr doc); ~JsonCheck(); - QList operator()(Utils::JsonSchema *schema); + QList operator()(JsonSchema *schema); private: bool preVisit(AST::Node *) override; @@ -52,14 +428,14 @@ private: }; void processSchema(AST::Node *ast); - bool proceedCheck(Utils::JsonValue::Kind kind, const SourceLocation &location); + bool proceedCheck(JsonValue::Kind kind, const SourceLocation &location); AnalysisData *analysis(); Document::Ptr m_doc; SourceLocation m_firstLoc; - Utils::JsonSchema *m_schema; + JsonSchema *m_schema; QStack m_analysis; }; -} // QmlJs +} // QmlJS diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs index 59eecc55f4c..18434c83bad 100644 --- a/src/libs/qmljs/qmljs.qbs +++ b/src/libs/qmljs/qmljs.qbs @@ -1,84 +1,79 @@ -import qbs 1.0 - -Project { +QtcLibrary { name: "QmlJS" - QtcLibrary { - cpp.defines: base.concat(["QMLJS_LIBRARY"]) - cpp.optimization: "fast" + cpp.defines: base.concat(["QMLJS_LIBRARY"]) + cpp.optimization: "fast" - Depends { name: "ExtensionSystem" } - Depends { name: "Utils" } - Depends { name: "LanguageUtils" } + Depends { name: "ExtensionSystem" } + Depends { name: "Utils" } + Depends { name: "LanguageUtils" } + Depends { name: "CPlusPlus" } + Depends { name: "Qt"; submodules: ["widgets", "xml"] } + + Group { + name: "General" + files: [ + "jsoncheck.cpp", "jsoncheck.h", + "persistenttrie.cpp", "persistenttrie.h", + "qmljs_global.h", + "qmljsbind.cpp", "qmljsbind.h", + "qmljsbundle.cpp", "qmljsbundle.h", + "qmljscheck.cpp", "qmljscheck.h", + "qmljscodeformatter.cpp", "qmljscodeformatter.h", + "qmljscompletioncontextfinder.cpp", "qmljscompletioncontextfinder.h", + "qmljsconstants.h", + "qmljscontext.cpp", "qmljscontext.h", + "qmljsdocument.cpp", "qmljsdocument.h", + "qmljsevaluate.cpp", "qmljsevaluate.h", + "qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h", + "qmljsicons.cpp", "qmljsicons.h", + "qmljsimportdependencies.cpp", "qmljsimportdependencies.h", + "qmljsindenter.cpp", "qmljsindenter.h", + "qmljsinterpreter.cpp", "qmljsinterpreter.h", + "qmljsdialect.cpp", "qmljsdialect.h", + "qmljslineinfo.cpp", "qmljslineinfo.h", + "qmljslink.cpp", "qmljslink.h", + "qmljsmodelmanagerinterface.cpp", "qmljsmodelmanagerinterface.h", + "qmljsplugindumper.cpp", "qmljsplugindumper.h", + "qmljspropertyreader.cpp", "qmljspropertyreader.h", + "qmljsreformatter.cpp", "qmljsreformatter.h", + "qmljsrewriter.cpp", "qmljsrewriter.h", + "qmljsscanner.cpp", "qmljsscanner.h", + "qmljsscopeastpath.cpp", "qmljsscopeastpath.h", + "qmljsscopebuilder.cpp", "qmljsscopebuilder.h", + "qmljsscopechain.cpp", "qmljsscopechain.h", + "qmljssimplereader.cpp", "qmljssimplereader.h", + "qmljsstaticanalysismessage.cpp", "qmljsstaticanalysismessage.h", + "qmljstr.h", + "qmljstypedescriptionreader.cpp", "qmljstypedescriptionreader.h", + "qmljsutils.cpp", "qmljsutils.h", + "qmljsvalueowner.cpp", "qmljsvalueowner.h", + "qmljsviewercontext.h" + ] + } + + Group { + name: "Parser" + prefix: "parser/" + files: [ + "qmldirparser.cpp", "qmldirparser_p.h", + "qmlimportresolver.cpp", "qmlimportresolver_p.h", + "qmljsast.cpp", "qmljsast_p.h", + "qmljsastfwd_p.h", + "qmljsastvisitor.cpp", "qmljsastvisitor_p.h", + "qmljsengine_p.h", + "qmljsglobal_p.h", + "qmljsgrammar.cpp", "qmljsgrammar_p.h", + "qmljskeywords_p.h", + "qmljslexer.cpp", "qmljslexer_p.h", + "qmljsmemorypool_p.h", + "qmljsparser.cpp", "qmljsparser_p.h", + "qmljssourcelocation_p.h", + ] + } + + Export { Depends { name: "CPlusPlus" } - Depends { name: "Qt"; submodules: ["widgets", "xml"] } - - Group { - name: "General" - files: [ - "jsoncheck.cpp", "jsoncheck.h", - "persistenttrie.cpp", "persistenttrie.h", - "qmljs_global.h", - "qmljsbind.cpp", "qmljsbind.h", - "qmljsbundle.cpp", "qmljsbundle.h", - "qmljscheck.cpp", "qmljscheck.h", - "qmljscodeformatter.cpp", "qmljscodeformatter.h", - "qmljscompletioncontextfinder.cpp", "qmljscompletioncontextfinder.h", - "qmljsconstants.h", - "qmljscontext.cpp", "qmljscontext.h", - "qmljsdocument.cpp", "qmljsdocument.h", - "qmljsevaluate.cpp", "qmljsevaluate.h", - "qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h", - "qmljsicons.cpp", "qmljsicons.h", - "qmljsicontextpane.h", - "qmljsimportdependencies.cpp", "qmljsimportdependencies.h", - "qmljsindenter.cpp", "qmljsindenter.h", - "qmljsinterpreter.cpp", "qmljsinterpreter.h", - "qmljsdialect.cpp", "qmljsdialect.h", - "qmljslineinfo.cpp", "qmljslineinfo.h", - "qmljslink.cpp", "qmljslink.h", - "qmljsmodelmanagerinterface.cpp", "qmljsmodelmanagerinterface.h", - "qmljsplugindumper.cpp", "qmljsplugindumper.h", - "qmljspropertyreader.cpp", "qmljspropertyreader.h", - "qmljsreformatter.cpp", "qmljsreformatter.h", - "qmljsrewriter.cpp", "qmljsrewriter.h", - "qmljsscanner.cpp", "qmljsscanner.h", - "qmljsscopeastpath.cpp", "qmljsscopeastpath.h", - "qmljsscopebuilder.cpp", "qmljsscopebuilder.h", - "qmljsscopechain.cpp", "qmljsscopechain.h", - "qmljssimplereader.cpp", "qmljssimplereader.h", - "qmljsstaticanalysismessage.cpp", "qmljsstaticanalysismessage.h", - "qmljstr.h", - "qmljstypedescriptionreader.cpp", "qmljstypedescriptionreader.h", - "qmljsutils.cpp", "qmljsutils.h", - "qmljsvalueowner.cpp", "qmljsvalueowner.h", - "qmljsviewercontext.h" - ] - } - - Group { - name: "Parser" - prefix: "parser/" - files: [ - "qmldirparser.cpp", "qmldirparser_p.h", - "qmlimportresolver.cpp", "qmlimportresolver_p.h", - "qmljsast.cpp", "qmljsast_p.h", - "qmljsastfwd_p.h", - "qmljsastvisitor.cpp", "qmljsastvisitor_p.h", - "qmljsengine_p.h", - "qmljsglobal_p.h", - "qmljsgrammar.cpp", "qmljsgrammar_p.h", - "qmljskeywords_p.h", - "qmljslexer.cpp", "qmljslexer_p.h", - "qmljsmemorypool_p.h", - "qmljsparser.cpp", "qmljsparser_p.h", - "qmljssourcelocation_p.h", - ] - } - - Export { - Depends { name: "CPlusPlus" } - Depends { name: "LanguageUtils" } - } + Depends { name: "LanguageUtils" } } } diff --git a/src/libs/qmljs/qmljsbundle.cpp b/src/libs/qmljs/qmljsbundle.cpp index e4d536a3da2..28f88fca452 100644 --- a/src/libs/qmljs/qmljsbundle.cpp +++ b/src/libs/qmljs/qmljsbundle.cpp @@ -3,7 +3,7 @@ #include "qmljsbundle.h" -#include +#include "jsoncheck.h" #include #include @@ -11,6 +11,7 @@ #include #include + namespace QmlJS { typedef PersistentTrie::Trie Trie; @@ -45,19 +46,16 @@ Trie QmlBundle::implicitImports() const return m_implicitImports; } - Trie QmlBundle::supportedImports() const { return m_supportedImports; } - void QmlBundle::merge(const QmlBundle &o) { *this = mergeF(o); } - void QmlBundle::intersect(const QmlBundle &o) { *this = intersectF(o); @@ -186,7 +184,7 @@ QString QmlBundle::toString(const QString &indent) return res; } -QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, +QStringList QmlBundle::maybeReadTrie(Trie &trie, JsonObjectValue *config, const QString &path, const QString &propertyName, bool required, bool stripVersions) { @@ -198,12 +196,13 @@ QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, path); return res; } - Utils::JsonValue *imp0 = config->member(propertyName); - Utils::JsonArrayValue *imp = ((imp0 != nullptr) ? imp0->toArray() : nullptr); + + JsonValue *imp0 = config->member(propertyName); + JsonArrayValue *imp = ((imp0 != nullptr) ? imp0->toArray() : nullptr); if (imp != nullptr) { - const QList elements = imp->elements(); - for (Utils::JsonValue *v : elements) { - Utils::JsonStringValue *impStr = ((v != nullptr) ? v->toString() : nullptr); + const QList elements = imp->elements(); + for (JsonValue *v : elements) { + JsonStringValue *impStr = ((v != nullptr) ? v->toString() : nullptr); if (impStr != nullptr) { QString value = impStr->value(); if (stripVersions) { @@ -228,9 +227,8 @@ QStringList QmlBundle::maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, bool QmlBundle::readFrom(QString path, bool stripVersions, QStringList *errors) { - Utils::JsonMemoryPool pool; + JsonMemoryPool pool; - using namespace Utils; QFile f(path); if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { if (errors) diff --git a/src/libs/qmljs/qmljsbundle.h b/src/libs/qmljs/qmljsbundle.h index 5d2058eef48..ebc0d49e251 100644 --- a/src/libs/qmljs/qmljsbundle.h +++ b/src/libs/qmljs/qmljsbundle.h @@ -12,10 +12,10 @@ QT_FORWARD_DECLARE_CLASS(QTextStream) -namespace Utils { class JsonObjectValue; } - namespace QmlJS { +class JsonObjectValue; + /* ! \class QmlJS::QmlBundle @@ -59,7 +59,7 @@ public: private: static void printEscaped(QTextStream &s, const QString &str); static void writeTrie(QTextStream &stream, const Trie &t, const QString &indent); - QStringList maybeReadTrie(Trie &trie, Utils::JsonObjectValue *config, const QString &path, + QStringList maybeReadTrie(Trie &trie, JsonObjectValue *config, const QString &path, const QString &propertyName, bool required = false, bool stripVersions = false); @@ -80,4 +80,5 @@ public: private: QHash m_bundles; }; + } // namespace QmlJS diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index feab41f24bf..7ceff0e78de 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1364,71 +1364,6 @@ static bool shouldAvoidNonStrictEqualityCheck(const Value *lhs, const Value *rhs return false; } -static bool equalIsAlwaysFalse(const Value *lhs, const Value *rhs) -{ - if ((lhs->asNullValue() || lhs->asUndefinedValue()) - && (rhs->asNumberValue() || rhs->asBooleanValue() || rhs->asStringValue())) - return true; - return false; -} - -static bool isIntegerValue(const Value *value) -{ - if (value->asNumberValue() || value->asIntValue()) - return true; - if (auto obj = value->asObjectValue()) - return obj->className() == "Number" || obj->className() == "int"; - - return false; -} - -static bool isStringValue(const Value *value) -{ - if (value->asStringValue()) - return true; - if (auto obj = value->asObjectValue()) - return obj->className() == "QString" || obj->className() == "string" || obj->className() == "String"; - - return false; -} - -static bool isBooleanValue(const Value *value) -{ - if (value->asBooleanValue()) - return true; - if (auto obj = value->asObjectValue()) - return obj->className() == "boolean" || obj->className() == "Boolean"; - - return false; -} - -static bool strictCompareConstant(const Value *lhs, const Value *rhs) -{ - // attached properties and working at runtime cases may be undefined at evaluation time - if (lhs->asUndefinedValue() || rhs->asUndefinedValue()) - return false; - if (lhs->asUnknownValue() || rhs->asUnknownValue()) - return false; - if (lhs->asFunctionValue() || rhs->asFunctionValue()) // function evaluation not implemented - return false; - if (isIntegerValue(lhs) && isIntegerValue(rhs)) - return false; - if (isStringValue(lhs) && isStringValue(rhs)) - return false; - if (isBooleanValue(lhs) && isBooleanValue(rhs)) - return false; - if (lhs->asBooleanValue() && !rhs->asBooleanValue()) - return true; - if (lhs->asNumberValue() && !rhs->asNumberValue()) - return true; - if (lhs->asStringValue() && !rhs->asStringValue()) - return true; - if (lhs->asObjectValue() && (!rhs->asObjectValue() || !rhs->asNullValue())) - return true; - return false; -} - - bool Check::visit(BinaryExpression *ast) { const QString source = _doc->source(); @@ -1458,18 +1393,6 @@ bool Check::visit(BinaryExpression *ast) || shouldAvoidNonStrictEqualityCheck(rhsValue, lhsValue)) { addMessage(MaybeWarnEqualityTypeCoercion, ast->operatorToken); } - if (equalIsAlwaysFalse(lhsValue, rhsValue) - || equalIsAlwaysFalse(rhsValue, lhsValue)) - addMessage(WarnLogicalValueDoesNotDependOnValues, ast->operatorToken); - } - if (ast->op == QSOperator::StrictEqual || ast->op == QSOperator::StrictNotEqual) { - Evaluate eval(&_scopeChain); - const Value *lhsValue = eval(ast->left); - const Value *rhsValue = eval(ast->right); - if (strictCompareConstant(lhsValue, rhsValue) - || strictCompareConstant(rhsValue, lhsValue)) { - addMessage(WarnLogicalValueDoesNotDependOnValues, ast->operatorToken); - } } // check odd + ++ combinations diff --git a/src/libs/qmljs/qmljsdialect.cpp b/src/libs/qmljs/qmljsdialect.cpp index 58b9771bcd1..387c06a3ab9 100644 --- a/src/libs/qmljs/qmljsdialect.cpp +++ b/src/libs/qmljs/qmljsdialect.cpp @@ -192,7 +192,7 @@ QList Dialect::companionLanguages() const << Dialect::QmlQtQuick2Ui << Dialect::Qml; break; case Dialect::NoLanguage: - return QList(); // return at least itself? + return {}; // return at least itself? } if (*this != Dialect::AnyLanguage) langs << Dialect::AnyLanguage; diff --git a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp index 7716e77ed5a..36125539776 100644 --- a/src/libs/qmljs/qmljsfindexportedcpptypes.cpp +++ b/src/libs/qmljs/qmljsfindexportedcpptypes.cpp @@ -247,7 +247,7 @@ protected: nameLit = translationUnit()->stringLiteral(nameAst->literal_token); if (!nameLit) { int line, column; - translationUnit()->getTokenStartPosition(nameExp->firstToken(), &line, &column); + translationUnit()->getTokenPosition(nameExp->firstToken(), &line, &column); _messages += Document::DiagnosticMessage( Document::DiagnosticMessage::Warning, _doc->filePath(), @@ -308,7 +308,7 @@ protected: if (packageName.isEmpty()) { packageName = QmlJS::CppQmlTypes::defaultPackage; int line, column; - translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column); + translationUnit()->getTokenPosition(ast->firstToken(), &line, &column); _messages += Document::DiagnosticMessage( Document::DiagnosticMessage::Warning, _doc->filePath(), @@ -346,7 +346,7 @@ protected: // we want to do lookup later, so also store the surrounding scope int line, column; - translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column); + translationUnit()->getTokenPosition(ast->firstToken(), &line, &column); exportedType.scope = _doc->scopeAt(line, column); if (typeId){ @@ -490,7 +490,7 @@ protected: nameLit = translationUnit()->stringLiteral(nameAst->literal_token); if (!nameLit) { int line, column; - translationUnit()->getTokenStartPosition(ast->expression_list->value->firstToken(), &line, &column); + translationUnit()->getTokenPosition(ast->expression_list->value->firstToken(), &line, &column); _messages += Document::DiagnosticMessage( Document::DiagnosticMessage::Warning, _doc->filePath(), @@ -504,9 +504,9 @@ protected: contextProperty.name = QString::fromUtf8(nameLit->chars(), nameLit->size()); contextProperty.expression = stringOf(skipQVariant(ast->expression_list->next->value, translationUnit())); // we want to do lookup later, so also store the line and column of the target scope - translationUnit()->getTokenStartPosition(ast->firstToken(), - &contextProperty.line, - &contextProperty.column); + translationUnit()->getTokenPosition(ast->firstToken(), + &contextProperty.line, + &contextProperty.column); _contextProperties += contextProperty; @@ -827,7 +827,7 @@ FindExportedCppTypes::FindExportedCppTypes(const CPlusPlus::Snapshot &snapshot) QStringList FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &document) { - QTC_ASSERT(!document.isNull(), return QStringList()); + QTC_ASSERT(!document.isNull(), return {}); m_contextProperties.clear(); m_exportedTypes.clear(); @@ -842,8 +842,7 @@ QStringList FindExportedCppTypes::operator()(const CPlusPlus::Document::Ptr &doc FindExportsVisitor finder(document); finder(); static const QString kindKey = QLatin1String("QmlJSTools.ExportedQmlTypesDiagnostic"); - CppModelManagerBase::trySetExtraDiagnostics(document->filePath().toString(), kindKey, - finder.messages()); + CppModelManagerBase::trySetExtraDiagnostics(document->filePath(), kindKey, finder.messages()); // if nothing was found, done const QList contextPropertyDescriptions = finder.contextProperties(); diff --git a/src/libs/qmljs/qmljsicontextpane.h b/src/libs/qmljs/qmljsicontextpane.h deleted file mode 100644 index 4bbc33a7d73..00000000000 --- a/src/libs/qmljs/qmljsicontextpane.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include "qmljs_global.h" - -#include -#include - -namespace TextEditor { class TextEditorWidget; } - -namespace QmlJS { - -class ScopeChain; - -class QMLJS_EXPORT IContextPane : public QObject -{ - Q_OBJECT - -public: - IContextPane(QObject *parent = nullptr) : QObject(parent) {} - virtual ~IContextPane() {} - virtual void apply(TextEditor::TextEditorWidget *editorWidget, Document::Ptr document, const ScopeChain *scopeChain, AST::Node *node, bool update, bool force = false) = 0; - virtual void setEnabled(bool) = 0; - virtual bool isAvailable(TextEditor::TextEditorWidget *editorWidget, Document::Ptr document, AST::Node *node) = 0; - virtual QWidget* widget() = 0; -signals: - void closed(); -}; - -} // namespace QmlJS diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 7a2cd6fa717..63e009607b9 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1321,10 +1321,32 @@ const Function *Function::asFunction() const // typing environment //////////////////////////////////////////////////////////////////////////////// -CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultLibraryObjects; -CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::defaultQtObjects; +CppQmlTypesLoader::BuiltinObjects sDefaultLibraryObjects; +CppQmlTypesLoader::BuiltinObjects sDefaultQtObjects; +std::function CppQmlTypesLoader::defaultObjectsInitializer; -CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInfoList &qmlTypeFiles, QStringList *errors, QStringList *warnings) +CppQmlTypesLoader::BuiltinObjects &CppQmlTypesLoader::defaultQtObjects() +{ + if (defaultObjectsInitializer) { + const std::function init = defaultObjectsInitializer; + defaultObjectsInitializer = {}; + init(); + } + return sDefaultLibraryObjects; +} +CppQmlTypesLoader::BuiltinObjects &CppQmlTypesLoader::defaultLibraryObjects() +{ + if (defaultObjectsInitializer) { + const std::function init = defaultObjectsInitializer; + defaultObjectsInitializer = {}; + init(); + } + return sDefaultQtObjects; +} + +CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInfoList &qmlTypeFiles, + QStringList *errors, + QStringList *warnings) { QHash newObjects; QStringList newDependencies; diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index c0a953d7bd8..8ca9f4ec26c 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -3,10 +3,11 @@ #pragma once +#include "qmljsdocument.h" +#include #include #include #include -#include #include @@ -689,8 +690,8 @@ public: static BuiltinObjects loadQmlTypes(const QFileInfoList &qmltypesFiles, QStringList *errors, QStringList *warnings); - static BuiltinObjects defaultQtObjects; - static BuiltinObjects defaultLibraryObjects; + static BuiltinObjects &defaultQtObjects(); + static BuiltinObjects &defaultLibraryObjects(); // parses the contents of a qmltypes file and fills the newObjects map static void parseQmlTypeDescriptions(const QByteArray &contents, @@ -700,6 +701,8 @@ public: QString *errorMessage, QString *warningMessage, const QString &fileName); + + static std::function defaultObjectsInitializer; }; class QMLJS_EXPORT FakeMetaObjectWithOrigin @@ -1108,12 +1111,20 @@ class QMLJS_EXPORT CustomImportsProvider : public QObject { Q_OBJECT public: + typedef QHash> ImportsPerDocument; explicit CustomImportsProvider(QObject *parent = nullptr); virtual ~CustomImportsProvider(); static const QList allProviders(); - virtual QList imports(ValueOwner *valueOwner, const Document *context) const = 0; + virtual QList imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot = nullptr) const = 0; + virtual void loadBuiltins([[maybe_unused]] ImportsPerDocument *importsPerDocument, + [[maybe_unused]] Imports *imports, + [[maybe_unused]] const Document *context, + [[maybe_unused]] ValueOwner *valueOwner, + [[maybe_unused]] Snapshot *snapshot) {} }; } // namespace QmlJS diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index ed1c506df73..b837037b7c8 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -196,12 +196,12 @@ Context::ImportsPerDocument LinkPrivate::linkImports() m_valueOwner->cppQmlTypes().load(QLatin1String(""), m_builtins.metaObjects()); } else { m_valueOwner->cppQmlTypes().load(QLatin1String(""), - CppQmlTypesLoader::defaultQtObjects); + CppQmlTypesLoader::defaultQtObjects()); } // load library objects shipped with Creator m_valueOwner->cppQmlTypes().load(QLatin1String(""), - CppQmlTypesLoader::defaultLibraryObjects); + CppQmlTypesLoader::defaultLibraryObjects()); if (document) { // do it on document first, to make sure import errors are shown @@ -209,9 +209,16 @@ Context::ImportsPerDocument LinkPrivate::linkImports() // Add custom imports for the opened document for (const auto &provider : CustomImportsProvider::allProviders()) { - const auto providerImports = provider->imports(m_valueOwner, document.data()); + const auto providerImports = provider->imports(m_valueOwner, + document.data(), + &m_snapshot); for (const auto &import : providerImports) importCache.insert(ImportCacheKey(import.info), import); + provider->loadBuiltins(&importsPerDocument, + imports, + document.data(), + m_valueOwner, + &m_snapshot); } populateImportedTypes(imports, document); @@ -656,14 +663,12 @@ void LinkPrivate::loadQmldirComponents(ObjectValue *import, QSet importedTypes; const auto components = libraryInfo.components(); for (const QmlDirParser::Component &component : components) { - if (importedTypes.contains(component.typeName)) - continue; - ComponentVersion componentVersion(component.majorVersion, component.minorVersion); if (version < componentVersion) continue; - importedTypes.insert(component.typeName); + if (!Utils::insert(importedTypes, component.typeName)) + continue; if (Document::Ptr importedDoc = m_snapshot.document( libraryPath.pathAppended(component.fileName))) { if (ObjectValue *v = importedDoc->bind()->rootObjectValue()) diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index 66a670d1b8a..fb0c806fe60 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -279,8 +279,9 @@ void ModelManagerInterface::loadQmlTypeDescriptionsInternal(const QString &resou if (qmlTypesFiles.at(i).baseName() == QLatin1String("builtins")) { QFileInfoList list; list.append(qmlTypesFiles.at(i)); - CppQmlTypesLoader::defaultQtObjects = - CppQmlTypesLoader::loadQmlTypes(list, &errors, &warnings); + CppQmlTypesLoader::defaultQtObjects() = CppQmlTypesLoader::loadQmlTypes(list, + &errors, + &warnings); qmlTypesFiles.removeAt(i); break; } @@ -290,7 +291,7 @@ void ModelManagerInterface::loadQmlTypeDescriptionsInternal(const QString &resou const CppQmlTypesLoader::BuiltinObjects objs = CppQmlTypesLoader::loadQmlTypes(qmlTypesFiles, &errors, &warnings); for (auto it = objs.cbegin(); it != objs.cend(); ++it) - CppQmlTypesLoader::defaultLibraryObjects.insert(it.key(), it.value()); + CppQmlTypesLoader::defaultLibraryObjects().insert(it.key(), it.value()); for (const QString &error : std::as_const(errors)) writeMessageInternal(error); @@ -734,10 +735,9 @@ static void findNewImplicitImports(const Document::Ptr &doc, // scan files that could be implicitly imported // it's important we also do this for JS files, otherwise the isEmpty check will fail if (snapshot.documentsInDirectory(doc->path()).isEmpty()) { - if (!scannedPaths->contains(doc->path())) { + if (Utils::insert(*scannedPaths, doc->path())) { *importedFiles += filesInDirectoryForLanguages(doc->path(), doc->language().companionLanguages()); - scannedPaths->insert(doc->path()); } } } @@ -757,11 +757,10 @@ static void findNewFileImports(const Document::Ptr &doc, *importedFiles += importPath; } else if (import.type() == ImportType::Directory) { if (snapshot.documentsInDirectory(importPath).isEmpty()) { - if (!scannedPaths->contains(importPath)) { + if (Utils::insert(*scannedPaths, importPath)) { *importedFiles += filesInDirectoryForLanguages(importPath, doc->language().companionLanguages()); - scannedPaths->insert(importPath); } } } else if (import.type() == ImportType::QrcFile) { @@ -890,10 +889,9 @@ static bool findNewQmlLibraryInPath(const Utils::FilePath &path, if (!component.fileName.isEmpty()) { const FilePath componentFile = path.pathAppended(component.fileName); const FilePath path = componentFile.absolutePath().cleanPath(); - if (!scannedPaths->contains(path)) { + if (Utils::insert(*scannedPaths, path)) { *importedFiles += filesInDirectoryForLanguages(path, Dialect(Dialect::AnyLanguage) .companionLanguages()); - scannedPaths->insert(path); } } } @@ -904,7 +902,7 @@ static bool findNewQmlLibraryInPath(const Utils::FilePath &path, static FilePath modulePath(const ImportInfo &import, const FilePaths &paths) { if (!import.version().isValid()) - return FilePath(); + return {}; const FilePaths modPaths = modulePaths(import.name(), import.version().toString(), paths); return modPaths.value(0); // first is best match @@ -1110,10 +1108,9 @@ void ModelManagerInterface::importScanAsync(QPromise &promise, const Worki QMutexLocker l(&modelManager->m_mutex); for (const auto &path : paths) { Utils::FilePath cPath = path.path().cleanPath(); - if (!forceRescan && modelManager->m_scannedPaths.contains(cPath)) + if (!forceRescan && !Utils::insert(modelManager->m_scannedPaths, cPath)) continue; pathsToScan.append({cPath, 0, path.language()}); - modelManager->m_scannedPaths.insert(cPath); } } const int maxScanDepth = 5; @@ -1370,13 +1367,12 @@ void ModelManagerInterface::startCppQmlTypeUpdate() return; } - CPlusPlus::CppModelManagerBase *cppModelManager = - CPlusPlus::CppModelManagerBase::instance(); - if (!cppModelManager) + if (!CPlusPlus::CppModelManagerBase::hasSnapshots()) return; m_cppQmlTypesUpdater = Utils::asyncRun(&ModelManagerInterface::updateCppQmlTypes, this, - cppModelManager->snapshot(), m_queuedCppDocuments); + CPlusPlus::CppModelManagerBase::snapshot(), + m_queuedCppDocuments); m_queuedCppDocuments.clear(); } diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp index 761df90ddcd..04913de3ddc 100644 --- a/src/libs/qmljs/qmljsplugindumper.cpp +++ b/src/libs/qmljs/qmljsplugindumper.cpp @@ -387,14 +387,14 @@ Utils::FilePath PluginDumper::buildQmltypesPath(const QString &name) const m_modelManager->importPathsNames()); if (paths.isEmpty()) - return Utils::FilePath(); + return {}; for (const Utils::FilePath &path : paths) { auto qmltypes = path.dirEntries(FileFilter(QStringList{"*.qmltypes"}, QDir::Files)); if (!qmltypes.isEmpty()) return qmltypes.first(); } - return Utils::FilePath(); + return {}; } /*! diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp index 98bff3a4b69..5b15e694281 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp +++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp @@ -229,8 +229,6 @@ StaticAnalysisMessages::StaticAnalysisMessages() Tr::tr("Hit maximum recursion limit when visiting AST.")); newMsg(ErrTypeIsInstantiatedRecursively, Error, Tr::tr("Type cannot be instantiated recursively (%1)."), 1); - newMsg(WarnLogicalValueDoesNotDependOnValues, Warning, - Tr::tr("Logical value does not depend on actual values.")); newMsg(ErrToManyComponentChildren, Error, Tr::tr("Components are only allowed to have a single child element.")); newMsg(WarnComponentRequiresChildren, Warning, diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.h b/src/libs/qmljs/qmljsstaticanalysismessage.h index 01b3b52d698..97dd95c5eb9 100644 --- a/src/libs/qmljs/qmljsstaticanalysismessage.h +++ b/src/libs/qmljs/qmljsstaticanalysismessage.h @@ -109,7 +109,6 @@ enum Type { ErrShorterStringValueExpected = 322, ErrInvalidArrayValueLength = 323, ErrHitMaximumRecursion = 324, - WarnLogicalValueDoesNotDependOnValues = 325, ErrToManyComponentChildren = 326, WarnComponentRequiresChildren = 327, WarnDuplicateImport = 400, diff --git a/src/libs/qmlpuppetcommunication/container/sharedmemory_unix.cpp b/src/libs/qmlpuppetcommunication/container/sharedmemory_unix.cpp index fce49841803..76b93a3c4f9 100644 --- a/src/libs/qmlpuppetcommunication/container/sharedmemory_unix.cpp +++ b/src/libs/qmlpuppetcommunication/container/sharedmemory_unix.cpp @@ -15,7 +15,7 @@ #include #include -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS #include #endif #include @@ -326,7 +326,7 @@ bool SharedMemory::createInternal(QSharedMemory::AccessMode mode, size_t size) { detachInternal(); -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS if (m_fileHandle > -1 && m_createdByMe) { close(m_fileHandle); shm_unlink(m_nativeKey.constData()); diff --git a/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp index cdf921e8f16..e70e316a9d5 100644 --- a/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp +++ b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp @@ -4,7 +4,6 @@ #include "mainwidget.h" #include "ui_mainwidget.h" -#include #include #include @@ -23,7 +22,8 @@ MainWidget::MainWidget(QWidget *parent) : { ui->setupUi(this); - ui->mainWidgetTopLabel.setText(tr("%1 has crashed").arg(Core::Constants::IDE_DISPLAY_NAME)); + const QString applicationName = QApplication::arguments().at(3); + ui->mainWidgetTopLabel.setText(tr("%1 has crashed").arg(applicationName)); connect(ui->restartButton, &QAbstractButton::clicked, this, &MainWidget::restartApplication); connect(ui->quitButton, &QAbstractButton::clicked, this, &MainWidget::quitApplication); diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 8c95cb400b8..07a5493de34 100644 --- a/src/libs/qtcreatorcdbext/CMakeLists.txt +++ b/src/libs/qtcreatorcdbext/CMakeLists.txt @@ -12,6 +12,7 @@ if (NOT QT_CREATOR_API_DEFINED) # standalone build include(QtCreatorIDEBranding) include(QtCreatorAPI) + qtc_handle_compiler_cache_support() endif() if (NOT WIN32 OR NOT MSVC) diff --git a/src/libs/solutions/CMakeLists.txt b/src/libs/solutions/CMakeLists.txt index 2a47fbee5fb..67630f067c7 100644 --- a/src/libs/solutions/CMakeLists.txt +++ b/src/libs/solutions/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(spinner) add_subdirectory(tasking) +add_subdirectory(terminal) diff --git a/src/libs/solutions/solutions.qbs b/src/libs/solutions/solutions.qbs index 3978235666e..d166c43f439 100644 --- a/src/libs/solutions/solutions.qbs +++ b/src/libs/solutions/solutions.qbs @@ -4,5 +4,6 @@ Project { references: [ "spinner/spinner.qbs", "tasking/tasking.qbs", + "terminal/terminal.qbs", ].concat(project.additionalLibs) } diff --git a/src/libs/solutions/spinner/icons/spinner_large.png b/src/libs/solutions/spinner/icons/spinner_large.png index c24ff1b77cf..0a3f894397b 100644 Binary files a/src/libs/solutions/spinner/icons/spinner_large.png and b/src/libs/solutions/spinner/icons/spinner_large.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_large@2x.png b/src/libs/solutions/spinner/icons/spinner_large@2x.png new file mode 100644 index 00000000000..910a11bc756 Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_large@2x.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_medium.png b/src/libs/solutions/spinner/icons/spinner_medium.png index d64cc514e1b..6cb735f18ac 100644 Binary files a/src/libs/solutions/spinner/icons/spinner_medium.png and b/src/libs/solutions/spinner/icons/spinner_medium.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_medium@2x.png b/src/libs/solutions/spinner/icons/spinner_medium@2x.png new file mode 100644 index 00000000000..3cd93d63e24 Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_medium@2x.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_small.png b/src/libs/solutions/spinner/icons/spinner_small.png index 254e9c82fc0..cd6e92a7370 100644 Binary files a/src/libs/solutions/spinner/icons/spinner_small.png and b/src/libs/solutions/spinner/icons/spinner_small.png differ diff --git a/src/libs/solutions/spinner/icons/spinner_small@2x.png b/src/libs/solutions/spinner/icons/spinner_small@2x.png new file mode 100644 index 00000000000..36821a92e19 Binary files /dev/null and b/src/libs/solutions/spinner/icons/spinner_small@2x.png differ diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp index 2dc241db7e1..114fea537da 100644 --- a/src/libs/solutions/spinner/spinner.cpp +++ b/src/libs/solutions/spinner/spinner.cpp @@ -3,7 +3,9 @@ #include "spinner.h" +#include #include +#include #include #include #include @@ -81,7 +83,7 @@ private: int m_rotationStep = 45; int m_rotation = 0; QTimer m_timer; - QPixmap m_pixmap; + mutable QPixmap m_pixmap; UpdateCallback m_callback; }; @@ -98,6 +100,18 @@ static QString imageFileNameForSpinnerSize(SpinnerSize size) return {}; } +static QPixmap themedPixmapForSpinnerSize(SpinnerSize size, qreal dpr) +{ + QImage mask(qt_findAtNxFile(imageFileNameForSpinnerSize(size), dpr)); + mask.invertPixels(); + QImage themedImage(mask.size(), QImage::Format_ARGB32); + themedImage.fill(qApp->palette().text().color()); + themedImage.setAlphaChannel(mask); + QPixmap themedPixmap = QPixmap::fromImage(themedImage); + themedPixmap.setDevicePixelRatio(mask.devicePixelRatio()); + return themedPixmap; +} + SpinnerPainter::SpinnerPainter(SpinnerSize size) { m_timer.setSingleShot(false); @@ -114,11 +128,14 @@ void SpinnerPainter::setSize(SpinnerSize size) m_size = size; m_rotationStep = size == SpinnerSize::Small ? 45 : 30; m_timer.setInterval(size == SpinnerSize::Small ? 100 : 80); - m_pixmap = QPixmap(imageFileNameForSpinnerSize(size)); + m_pixmap = themedPixmapForSpinnerSize(size, qApp->devicePixelRatio()); } void SpinnerPainter::paint(QPainter &painter, const QRect &rect) const { + const qreal dpr = painter.device()->devicePixelRatioF(); + if (!qFuzzyCompare(m_pixmap.devicePixelRatio(), dpr)) + m_pixmap = themedPixmapForSpinnerSize(m_size, dpr); painter.save(); painter.setRenderHint(QPainter::SmoothPixmapTransform); QPoint translate(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); diff --git a/src/libs/solutions/spinner/spinner.h b/src/libs/solutions/spinner/spinner.h index 86dfbee6ebd..16ec6bb71db 100644 --- a/src/libs/solutions/spinner/spinner.h +++ b/src/libs/solutions/spinner/spinner.h @@ -13,7 +13,7 @@ namespace SpinnerSolution { Q_NAMESPACE_EXPORT(SPINNER_EXPORT) enum class SpinnerSize { Small, Medium, Large }; -Q_ENUM_NS(SpinnerSize); +Q_ENUM_NS(SpinnerSize) // TODO: SpinnerOverlay and SpinnerWidget? diff --git a/src/libs/solutions/spinner/spinner.qbs b/src/libs/solutions/spinner/spinner.qbs index cd830d107ec..d02858d0484 100644 --- a/src/libs/solutions/spinner/spinner.qbs +++ b/src/libs/solutions/spinner/spinner.qbs @@ -1,6 +1,8 @@ QtcLibrary { name: "Spinner" - Depends { name: "Qt"; submodules: ["core", "widgets"] } + + Depends { name: "Qt.widgets" } + cpp.defines: base.concat("SPINNER_LIBRARY") files: [ @@ -9,5 +11,10 @@ QtcLibrary { "spinner.qrc", "spinner_global.h", ] + + Export { + Depends { name: "cpp" } + cpp.includePaths: ".." + } } diff --git a/src/libs/solutions/spinner/spinner.qrc b/src/libs/solutions/spinner/spinner.qrc index 5ad85953e8d..0b8a980272e 100644 --- a/src/libs/solutions/spinner/spinner.qrc +++ b/src/libs/solutions/spinner/spinner.qrc @@ -1,7 +1,10 @@ icons/spinner_large.png + icons/spinner_large@2x.png icons/spinner_medium.png + icons/spinner_medium@2x.png icons/spinner_small.png + icons/spinner_small@2x.png diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index 64a59ec8733..71081209f4d 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -41,11 +41,7 @@ public: void start() final { task()->start(); } }; -} // namespace Tasking - -TASKING_DECLARE_TASK(BarrierTask, Tasking::BarrierTaskAdapter); - -namespace Tasking { +using BarrierTask = CustomTask; template class SharedBarrier @@ -70,28 +66,26 @@ using MultiBarrier = TreeStorage>; // alias template deduction only available with C++20. using SingleBarrier = MultiBarrier<1>; -class TASKING_EXPORT WaitForBarrierTask : public BarrierTask +template +GroupItem waitForBarrierTask(const MultiBarrier &sharedBarrier) { -public: - template - WaitForBarrierTask(const MultiBarrier &sharedBarrier) - : BarrierTask([sharedBarrier](Barrier &barrier) { - SharedBarrier *activeBarrier = sharedBarrier.activeStorage(); - if (!activeBarrier) { - qWarning("The barrier referenced from WaitForBarrier element " - "is not reachable in the running tree. " - "It is possible that no barrier was added to the tree, " - "or the storage is not reachable from where it is referenced. " - "The WaitForBarrier task finishes with an error. "); - return SetupResult::StopWithError; - } - Barrier *activeSharedBarrier = activeBarrier->barrier(); - const std::optional result = activeSharedBarrier->result(); - if (result.has_value()) - return result.value() ? SetupResult::StopWithDone : SetupResult::StopWithError; - QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult); - return SetupResult::Continue; - }) {} -}; + return BarrierTask([sharedBarrier](Barrier &barrier) { + SharedBarrier *activeBarrier = sharedBarrier.activeStorage(); + if (!activeBarrier) { + qWarning("The barrier referenced from WaitForBarrier element " + "is not reachable in the running tree. " + "It is possible that no barrier was added to the tree, " + "or the storage is not reachable from where it is referenced. " + "The WaitForBarrier task finishes with an error. "); + return SetupResult::StopWithError; + } + Barrier *activeSharedBarrier = activeBarrier->barrier(); + const std::optional result = activeSharedBarrier->result(); + if (result.has_value()) + return result.value() ? SetupResult::StopWithDone : SetupResult::StopWithError; + QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult); + return SetupResult::Continue; + }); +} } // namespace Tasking diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h index d7799159447..7c0d1cf47ef 100644 --- a/src/libs/solutions/tasking/concurrentcall.h +++ b/src/libs/solutions/tasking/concurrentcall.h @@ -95,6 +95,7 @@ private: std::unique_ptr> m_watcher; }; -} // namespace Tasking +template +using ConcurrentCallTask = CustomTask>; -TASKING_DECLARE_TEMPLATE_TASK(ConcurrentCallTask, Tasking::ConcurrentCallTaskAdapter); +} // namespace Tasking diff --git a/src/libs/solutions/tasking/networkquery.h b/src/libs/solutions/tasking/networkquery.h index faf482df90d..0cac720584a 100644 --- a/src/libs/solutions/tasking/networkquery.h +++ b/src/libs/solutions/tasking/networkquery.h @@ -50,6 +50,6 @@ public: void start() final { task()->start(); } }; -} // namespace Tasking +using NetworkQueryTask = CustomTask; -TASKING_DECLARE_TASK(NetworkQueryTask, Tasking::NetworkQueryTaskAdapter); +} // namespace Tasking diff --git a/src/libs/solutions/tasking/tasking.qbs b/src/libs/solutions/tasking/tasking.qbs index fa0a5ebacc9..fba42b10b1d 100644 --- a/src/libs/solutions/tasking/tasking.qbs +++ b/src/libs/solutions/tasking/tasking.qbs @@ -1,6 +1,8 @@ QtcLibrary { name: "Tasking" - Depends { name: "Qt"; submodules: ["concurrent", "core", "network"] } + + Depends { name: "Qt"; submodules: ["concurrent", "network"] } + cpp.defines: base.concat("TASKING_LIBRARY") files: [ @@ -13,5 +15,10 @@ QtcLibrary { "tasktree.cpp", "tasktree.h", ] + + Export { + Depends { name: "cpp" } + cpp.includePaths: ["..", "../.."] + } } diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 4e6b40ccc1e..5dc743f1324 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,19 @@ private: implement the start() method, and emit the done() signal when the task is finished. Use task() to access the associated \c Task instance. + To use your task adapter inside the task tree, create an alias to the + Tasking::CustomTask template passing your task adapter as a template parameter: + \code + // Defines actual worker + class Worker {...}; + + // Adapts Worker's interface to work with task tree + class WorkerTaskAdapter : public TaskAdapter {...}; + + // Defines WorkerTask as a new task tree element + using WorkerTask = CustomTask; + \endcode + For more information on implementing the custom task adapters, refer to \l {Task Adapters}. \sa start(), done(), task() @@ -333,8 +347,8 @@ private: The CustomTask class template is used inside TaskTree for describing custom task items. - Custom task names are aliased with unique names inside the \l Tasking namespace - via the TASKING_DECLARE_TASK or TASKING_DECLARE_TEMPLATE_TASK macros. + Custom task names are aliased with unique names using the CustomTask template + with a given TaskAdapter subclass as a template parameter. For example, \c ConcurrentCallTask is an alias to the CustomTask that is defined to work with \c ConcurrentCall as an associated task class. The following table contains all the built-in tasks and their associated task classes: @@ -501,26 +515,6 @@ private: \sa onSetup() */ -/*! - \macro TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass) - \relates Tasking - - Registers the new custom task type under a \a CustomTaskName name inside the - Tasking namespace for the passed \a TaskAdapterClass adapter class. - - For more information on implementing the custom task adapters, refer to \l {Task Adapters}. -*/ - -/*! - \macro TASKING_DECLARE_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass) - \relates Tasking - - Registers the new custom task template type under a \a CustomTaskName name inside the - Tasking namespace for the passed \a TaskAdapterClass adapter class template. - - For more information on implementing the custom task adapters, refer to \l {Task Adapters}. -*/ - /*! \enum Tasking::WorkflowPolicy @@ -2117,8 +2111,8 @@ void TaskNode::invokeEndHandler(bool success) TreeStorage storage; const Group root = ...; // storage placed inside root's group and inside handlers TaskTree taskTree(root); - auto initStorage = [](CopyStorage *storage){ - storage->content = "initial content"; + auto initStorage = [](CopyStorage &storage){ + storage.content = "initial content"; }; taskTree.onStorageSetup(storage, initStorage); taskTree.start(); @@ -2137,8 +2131,8 @@ void TaskNode::invokeEndHandler(bool success) TreeStorage storage; const Group root = ...; // storage placed inside root's group and inside handlers TaskTree taskTree(root); - auto collectStorage = [](CopyStorage *storage){ - qDebug() << "final content" << storage->content; + auto collectStorage = [](const CopyStorage &storage){ + qDebug() << "final content" << storage.content; }; taskTree.onStorageDone(storage, collectStorage); taskTree.start(); @@ -2156,10 +2150,10 @@ void TaskNode::invokeEndHandler(bool success) asynchronous task: \code - class TimeoutTaskAdapter : public Tasking::TaskAdapter + class TimerTaskAdapter : public TaskAdapter { public: - TimeoutTaskAdapter() { + TimerTaskAdapter() { task()->setSingleShot(true); task()->setInterval(1000); connect(task(), &QTimer::timeout, this, [this] { emit done(true); }); @@ -2168,7 +2162,7 @@ void TaskNode::invokeEndHandler(bool success) void start() final { task()->start(); } }; - TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter); + using TimerTask = CustomTask; \endcode You must derive the custom adapter from the TaskAdapter class template @@ -2177,28 +2171,27 @@ void TaskNode::invokeEndHandler(bool success) later as an argument to the task's handlers. The instance of this class parameter automatically becomes a member of the TaskAdapter template, and is accessible through the TaskAdapter::task() method. The constructor - of TimeoutTaskAdapter initially configures the QTimer object and connects - to the QTimer::timeout signal. When the signal is triggered, TimeoutTaskAdapter + of TimerTaskAdapter initially configures the QTimer object and connects + to the QTimer::timeout signal. When the signal is triggered, TimerTaskAdapter emits the \c done(true) signal to inform the task tree that the task finished successfully. If it emits \c done(false), the task finished with an error. The TaskAdapter::start() method starts the timer. - To make QTimer accessible inside TaskTree under the \e TimeoutTask name, - register it with TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter). - TimeoutTask becomes a new task type inside Tasking namespace, using TimeoutTaskAdapter. + To make QTimer accessible inside TaskTree under the \e TimerTask name, + define TimerTask to be an alias to the Tasking::CustomTask. + TimerTask becomes a new task type, using TimerTaskAdapter. The new task type is now registered, and you can use it in TaskTree: \code - const auto onTimeoutSetup = [](QTimer &task) { + const auto onTimerSetup = [](QTimer &task) { task.setInterval(2000); }; - const auto onTimeoutDone = [](const QTimer &task) { - qDebug() << "timeout triggered"; + const auto onTimerDone = [](const QTimer &task) { + qDebug() << "timer triggered"; }; - const Group root { - TimeoutTask(onTimeoutSetup, onTimeoutDone) + TimerTask(onTimerSetup, onTimerDone) }; \endcode @@ -2537,7 +2530,7 @@ int TaskTree::progressValue() const Installs a storage setup \a handler for the \a storage to pass the initial data dynamically to the running task tree. - The \c StorageHandler takes the pointer to the \c StorageStruct instance: + The \c StorageHandler takes a reference to the \c StorageStruct instance: \code static void save(const QString &fileName, const QByteArray &array) { ... } @@ -2554,8 +2547,8 @@ int TaskTree::progressValue() const }; TaskTree taskTree(root); - auto initStorage = [](QByteArray *storage){ - *storage = "initial content"; + auto initStorage = [](QByteArray &storage){ + storage = "initial content"; }; taskTree.onStorageSetup(storage, initStorage); taskTree.start(); @@ -2575,10 +2568,10 @@ int TaskTree::progressValue() const /*! \fn template void TaskTree::onStorageDone(const TreeStorage &storage, StorageHandler &&handler) - Installs a storage done \a handler for the \a storage to retrie the final data + Installs a storage done \a handler for the \a storage to retrieve the final data dynamically from the running task tree. - The \c StorageHandler takes the pointer to the \c StorageStruct instance: + The \c StorageHandler takes a const reference to the \c StorageStruct instance: \code static QByteArray load(const QString &fileName) { ... } @@ -2598,8 +2591,8 @@ int TaskTree::progressValue() const }; TaskTree taskTree(root); - auto collectStorage = [](QByteArray *storage){ - qDebug() << "final content" << *storage; + auto collectStorage = [](const QByteArray &storage){ + qDebug() << "final content" << storage; }; taskTree.onStorageDone(storage, collectStorage); taskTree.start(); diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 88f89ef0c51..5e1226eb2c4 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -33,6 +33,9 @@ private: template friend class TaskAdapter; friend class TaskNode; TaskInterface() = default; +#ifdef Q_QDOC +protected: +#endif virtual void start() = 0; }; @@ -41,14 +44,13 @@ class TASKING_EXPORT TreeStorageBase public: bool isValid() const; -protected: +private: using StorageConstructor = std::function; using StorageDestructor = std::function; TreeStorageBase(StorageConstructor ctor, StorageDestructor dtor); void *activeStorageVoid() const; -private: int createStorage() const; void deleteStorage(int id) const; void activateStorage(int id) const; @@ -71,13 +73,15 @@ private: int m_storageCounter = 0; }; QSharedPointer m_storageData; + + template friend class TreeStorage; friend ExecutionContextActivator; friend TaskContainer; friend TaskTreePrivate; }; template -class TreeStorage : public TreeStorageBase +class TreeStorage final : public TreeStorageBase { public: TreeStorage() : TreeStorageBase(TreeStorage::ctor(), TreeStorage::dtor()) {} @@ -190,7 +194,7 @@ protected: static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); } static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); } static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout, - const GroupEndHandler &handler = {}); + const GroupEndHandler &handler = {}); private: Type m_type = Type::Group; @@ -200,7 +204,7 @@ private: TaskHandler m_taskHandler; }; -class TASKING_EXPORT Group : public GroupItem +class TASKING_EXPORT Group final : public GroupItem { public: Group(const QList &children) { addChildren(children); } @@ -267,23 +271,22 @@ TASKING_EXPORT extern const GroupItem stopOnFinished; TASKING_EXPORT extern const GroupItem finishAllAndDone; TASKING_EXPORT extern const GroupItem finishAllAndError; -class TASKING_EXPORT Storage : public GroupItem +class TASKING_EXPORT Storage final : public GroupItem { public: Storage(const TreeStorageBase &storage) : GroupItem(storage) { } }; // Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount() -class TASKING_EXPORT Sync : public Group +class TASKING_EXPORT Sync final : public GroupItem { - public: template - Sync(Function &&function) : Group(init(std::forward(function))) {} + Sync(Function &&function) { addChildren({init(std::forward(function))}); } private: template - static QList init(Function &&function) { + static GroupItem init(Function &&function) { constexpr bool isInvocable = std::is_invocable_v>; static_assert(isInvocable, "Sync element: The synchronous function can't take any arguments."); @@ -292,10 +295,10 @@ private: static_assert(isBool || isVoid, "Sync element: The synchronous function has to return void or bool."); if constexpr (isBool) { - return {onGroupSetup([function] { return function() ? SetupResult::StopWithDone - : SetupResult::StopWithError; })}; + return onGroupSetup([function] { return function() ? SetupResult::StopWithDone + : SetupResult::StopWithError; }); } - return {onGroupSetup([function] { function(); return SetupResult::StopWithDone; })}; + return onGroupSetup([function] { function(); return SetupResult::StopWithDone; }); }; }; @@ -314,10 +317,13 @@ private: }; template -class CustomTask : public GroupItem +class CustomTask final : public GroupItem { public: using Task = typename Adapter::Type; + static_assert(std::is_base_of_v, Adapter>, + "The Adapter type for the CustomTask needs to be derived from " + "TaskAdapter."); using EndHandler = std::function; static Adapter *createAdapter() { return new Adapter; } CustomTask() : GroupItem({&createAdapter}) {} @@ -407,11 +413,21 @@ public: template void onStorageSetup(const TreeStorage &storage, StorageHandler &&handler) { + constexpr bool isInvokable = std::is_invocable_v, + StorageStruct &>; + static_assert(isInvokable, + "Storage setup handler needs to take (Storage &) as an argument. " + "The passed handler doesn't fulfill these requirements."); setupStorageHandler(storage, wrapHandler(std::forward(handler)), {}); } template void onStorageDone(const TreeStorage &storage, StorageHandler &&handler) { + constexpr bool isInvokable = std::is_invocable_v, + const StorageStruct &>; + static_assert(isInvokable, + "Storage done handler needs to take (const Storage &) as an argument. " + "The passed handler doesn't fulfill these requirements."); setupStorageHandler(storage, {}, wrapHandler(std::forward(handler))); } @@ -431,7 +447,7 @@ private: StorageVoidHandler wrapHandler(StorageHandler &&handler) { return [=](void *voidStruct) { StorageStruct *storageStruct = static_cast(voidStruct); - std::invoke(handler, storageStruct); + std::invoke(handler, *storageStruct); }; } @@ -457,16 +473,7 @@ private: std::optional m_timerId; }; +using TaskTreeTask = CustomTask; +using TimeoutTask = CustomTask; + } // namespace Tasking - -#define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\ -namespace Tasking { using CustomTaskName = CustomTask; } - -#define TASKING_DECLARE_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass)\ -namespace Tasking {\ -template \ -using CustomTaskName = CustomTask>;\ -} // namespace Tasking - -TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter); -TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter); diff --git a/src/libs/solutions/terminal/CMakeLists.txt b/src/libs/solutions/terminal/CMakeLists.txt new file mode 100644 index 00000000000..0cf48fefbc7 --- /dev/null +++ b/src/libs/solutions/terminal/CMakeLists.txt @@ -0,0 +1,13 @@ +add_qtc_library(TerminalLib + DEPENDS Qt::Core Qt::Widgets libvterm + SOURCES + celliterator.cpp celliterator.h + glyphcache.cpp glyphcache.h + keys.cpp keys.h + scrollback.cpp scrollback.h + surfaceintegration.h + terminal_global.h + terminal.qrc + terminalsurface.cpp terminalsurface.h + terminalview.cpp terminalview.h +) diff --git a/src/plugins/terminal/celliterator.cpp b/src/libs/solutions/terminal/celliterator.cpp similarity index 96% rename from src/plugins/terminal/celliterator.cpp rename to src/libs/solutions/terminal/celliterator.cpp index 91a70f76ea3..b7053438e08 100644 --- a/src/plugins/terminal/celliterator.cpp +++ b/src/libs/solutions/terminal/celliterator.cpp @@ -7,7 +7,7 @@ #include -namespace Terminal::Internal { +namespace TerminalSolution { CellIterator::CellIterator(const TerminalSurface *surface, QPoint pos) : CellIterator(surface, pos.x() + (pos.y() * surface->liveSize().width())) @@ -91,4 +91,4 @@ CellIterator &CellIterator::operator+=(int n) return *this; } -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/celliterator.h b/src/libs/solutions/terminal/celliterator.h similarity index 94% rename from src/plugins/terminal/celliterator.h rename to src/libs/solutions/terminal/celliterator.h index c246aaa3114..e1fc6efce74 100644 --- a/src/plugins/terminal/celliterator.h +++ b/src/libs/solutions/terminal/celliterator.h @@ -3,15 +3,17 @@ #pragma once +#include "terminal_global.h" + #include #include -namespace Terminal::Internal { +namespace TerminalSolution { class TerminalSurface; -class CellIterator +class TERMINAL_EXPORT CellIterator { public: using iterator_category = std::bidirectional_iterator_tag; @@ -94,4 +96,4 @@ private: mutable std::u32string::value_type m_char; }; -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/glyphcache.cpp b/src/libs/solutions/terminal/glyphcache.cpp similarity index 89% rename from src/plugins/terminal/glyphcache.cpp rename to src/libs/solutions/terminal/glyphcache.cpp index 72a0fd7b9d1..1941f1c0366 100644 --- a/src/plugins/terminal/glyphcache.cpp +++ b/src/libs/solutions/terminal/glyphcache.cpp @@ -5,7 +5,7 @@ #include -namespace Terminal::Internal { +namespace TerminalSolution { size_t qHash(const GlyphCacheKey &key, size_t seed = 0) { @@ -38,11 +38,11 @@ const QGlyphRun *GlyphCache::get(const QFont &font, const QString &text) const auto runs = line.glyphRuns(); if (!runs.isEmpty()) { QGlyphRun *run = new QGlyphRun(layout.lineAt(0).glyphRuns().first()); - insert(key, run); - return run; + if (insert(key, run)) + return run; } } return nullptr; } -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/glyphcache.h b/src/libs/solutions/terminal/glyphcache.h similarity index 90% rename from src/plugins/terminal/glyphcache.h rename to src/libs/solutions/terminal/glyphcache.h index 60701098f5f..a5ebfc21453 100644 --- a/src/plugins/terminal/glyphcache.h +++ b/src/libs/solutions/terminal/glyphcache.h @@ -8,7 +8,7 @@ #include #include -namespace Terminal::Internal { +namespace TerminalSolution { struct GlyphCacheKey { @@ -31,4 +31,4 @@ public: const QGlyphRun *get(const QFont &font, const QString &text); }; -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/libs/solutions/terminal/images/passwordlock.png b/src/libs/solutions/terminal/images/passwordlock.png new file mode 100644 index 00000000000..c548e28cab2 Binary files /dev/null and b/src/libs/solutions/terminal/images/passwordlock.png differ diff --git a/src/plugins/terminal/keys.cpp b/src/libs/solutions/terminal/keys.cpp similarity index 92% rename from src/plugins/terminal/keys.cpp rename to src/libs/solutions/terminal/keys.cpp index ce14cbe5fbc..adbcda10ea7 100644 --- a/src/plugins/terminal/keys.cpp +++ b/src/libs/solutions/terminal/keys.cpp @@ -1,11 +1,9 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 -#include - #include "keys.h" -namespace Terminal::Internal { +namespace TerminalSolution { VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod) { @@ -77,8 +75,9 @@ VTermKey qtKeyToVTerm(Qt::Key key, bool keypad) case Qt::Key_Enter: { VTermKey enterKey = VTERM_KEY_KP_ENTER; - if (Utils::HostOsInfo::isWindowsHost()) - enterKey = VTERM_KEY_ENTER; +#ifdef Q_OS_WIN + enterKey = VTERM_KEY_ENTER; +#endif return keypad ? enterKey : VTERM_KEY_NONE; } @@ -88,4 +87,4 @@ VTermKey qtKeyToVTerm(Qt::Key key, bool keypad) return VTERM_KEY_NONE; } } -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/keys.h b/src/libs/solutions/terminal/keys.h similarity index 83% rename from src/plugins/terminal/keys.h rename to src/libs/solutions/terminal/keys.h index f3df9330013..2f967010db9 100644 --- a/src/plugins/terminal/keys.h +++ b/src/libs/solutions/terminal/keys.h @@ -7,9 +7,9 @@ #include -namespace Terminal::Internal { +namespace TerminalSolution { VTermKey qtKeyToVTerm(Qt::Key key, bool keypad); VTermModifier qtModifierToVTerm(Qt::KeyboardModifiers mod); -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/scrollback.cpp b/src/libs/solutions/terminal/scrollback.cpp similarity index 95% rename from src/plugins/terminal/scrollback.cpp rename to src/libs/solutions/terminal/scrollback.cpp index e22d5fa2436..b3fa9af8433 100644 --- a/src/plugins/terminal/scrollback.cpp +++ b/src/libs/solutions/terminal/scrollback.cpp @@ -8,7 +8,7 @@ #include #include -namespace Terminal::Internal { +namespace TerminalSolution { Scrollback::Line::Line(int cols, const VTermScreenCell *cells) : m_cols(cols) @@ -58,4 +58,4 @@ void Scrollback::clear() m_deque.clear(); } -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/scrollback.h b/src/libs/solutions/terminal/scrollback.h similarity index 95% rename from src/plugins/terminal/scrollback.h rename to src/libs/solutions/terminal/scrollback.h index 9ca71eec615..a03f9891e64 100644 --- a/src/plugins/terminal/scrollback.h +++ b/src/libs/solutions/terminal/scrollback.h @@ -13,7 +13,7 @@ #include #include -namespace Terminal::Internal { +namespace TerminalSolution { class Scrollback { @@ -54,4 +54,4 @@ private: std::deque m_deque; }; -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/libs/solutions/terminal/surfaceintegration.h b/src/libs/solutions/terminal/surfaceintegration.h new file mode 100644 index 00000000000..99b9538de15 --- /dev/null +++ b/src/libs/solutions/terminal/surfaceintegration.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace TerminalSolution { + +class SurfaceIntegration +{ +public: + virtual void onOsc(int cmd, std::string_view str, bool initial, bool final) = 0; + + virtual void onBell() {} + virtual void onTitle(const QString &title) { Q_UNUSED(title); } + + virtual void onSetClipboard(const QByteArray &text) { Q_UNUSED(text); } + virtual void onGetClipboard() {} +}; + +} // namespace TerminalSolution diff --git a/src/libs/solutions/terminal/terminal.qbs b/src/libs/solutions/terminal/terminal.qbs new file mode 100644 index 00000000000..aa1293e3994 --- /dev/null +++ b/src/libs/solutions/terminal/terminal.qbs @@ -0,0 +1,26 @@ +QtcLibrary { + name: "TerminalLib" + + Depends { name: "vterm" } + Depends { name: "Qt.widgets" } + + cpp.defines: base.concat("TERMINALLIB_LIBRARY") + + files: [ + "celliterator.cpp", + "celliterator.h", + "glyphcache.cpp", + "glyphcache.h", + "keys.cpp", + "keys.h", + "scrollback.cpp", + "scrollback.h", + "surfaceintegration.h", + "terminal.qrc", + "terminal_global.h", + "terminalsurface.cpp", + "terminalsurface.h", + "terminalview.cpp", + "terminalview.h", + ] +} diff --git a/src/libs/solutions/terminal/terminal.qrc b/src/libs/solutions/terminal/terminal.qrc new file mode 100644 index 00000000000..0877ca08b9d --- /dev/null +++ b/src/libs/solutions/terminal/terminal.qrc @@ -0,0 +1,5 @@ + + + images/passwordlock.png + + diff --git a/src/libs/solutions/terminal/terminal_global.h b/src/libs/solutions/terminal/terminal_global.h new file mode 100644 index 00000000000..b7fec1c77c5 --- /dev/null +++ b/src/libs/solutions/terminal/terminal_global.h @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +#if defined(TERMINALLIB_LIBRARY) +#define TERMINAL_EXPORT Q_DECL_EXPORT +#elif defined(TERMINALLIB_STATIC_LIBRARY) +#define TERMINAL_EXPORT +#else +#define TERMINAL_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/libs/solutions/terminal/terminalsurface.cpp similarity index 79% rename from src/plugins/terminal/terminalsurface.cpp rename to src/libs/solutions/terminal/terminalsurface.cpp index 698f3589b56..e51c09d8944 100644 --- a/src/plugins/terminal/terminalsurface.cpp +++ b/src/libs/solutions/terminal/terminalsurface.cpp @@ -2,18 +2,17 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "terminalsurface.h" +#include "surfaceintegration.h" #include "keys.h" #include "scrollback.h" -#include - #include #include #include -namespace Terminal::Internal { +namespace TerminalSolution { Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg); @@ -26,13 +25,10 @@ constexpr int batchFlushSize = 256; struct TerminalSurfacePrivate { - TerminalSurfacePrivate(TerminalSurface *surface, - const QSize &initialGridSize, - ShellIntegration *shellIntegration) + TerminalSurfacePrivate(TerminalSurface *surface, const QSize &initialGridSize) : m_vterm(vterm_new(initialGridSize.height(), initialGridSize.width()), vterm_free) , m_vtermScreen(vterm_obtain_screen(m_vterm.get())) - , m_scrollback(std::make_unique(5000)) - , m_shellIntegration(shellIntegration) + , m_scrollback(std::make_unique(5000)) , q(surface) {} @@ -128,7 +124,8 @@ struct TerminalSurfacePrivate }; m_vtermScreenCallbacks.bell = [](void *user) { auto p = static_cast(user); - emit p->q->bell(); + if (p->m_surfaceIntegration) + p->m_surfaceIntegration->onBell(); return 1; }; @@ -145,6 +142,41 @@ struct TerminalSurfacePrivate VTermState *vts = vterm_obtain_state(m_vterm.get()); vterm_state_set_unrecognised_fallbacks(vts, &m_vtermStateFallbacks, this); + + memset(&m_vtermSelectionCallbacks, 0, sizeof(m_vtermSelectionCallbacks)); + + m_vtermSelectionCallbacks.query = [](VTermSelectionMask mask, void *user) { + if (!(mask & 0xF)) + return 0; + + auto p = static_cast(user); + if (p->m_surfaceIntegration) + p->m_surfaceIntegration->onGetClipboard(); + + return 0; + }; + + m_vtermSelectionCallbacks.set = + [](VTermSelectionMask mask, VTermStringFragment frag, void *user) { + if (!(mask & 0xF)) + return 0; + + auto p = static_cast(user); + if (frag.initial) + p->m_selectionBuffer.clear(); + + p->m_selectionBuffer.append(frag.str, frag.len); + if (!frag.final) + return 1; + + if (p->m_surfaceIntegration) + p->m_surfaceIntegration->onSetClipboard(p->m_selectionBuffer); + + return 1; + }; + + vterm_state_set_selection_callbacks(vts, &m_vtermSelectionCallbacks, this, nullptr, 256); + vterm_state_set_bold_highbright(vts, true); VTermColor fg; @@ -273,8 +305,12 @@ struct TerminalSurfacePrivate int osc(int cmd, const VTermStringFragment &fragment) { - if (m_shellIntegration) - m_shellIntegration->onOsc(cmd, fragment); + if (m_surfaceIntegration) { + m_surfaceIntegration->onOsc(cmd, + {fragment.str, fragment.len}, + fragment.initial, + fragment.final); + } return 1; } @@ -303,6 +339,8 @@ struct TerminalSurfacePrivate case VTERM_PROP_ICONNAME: break; case VTERM_PROP_TITLE: + if (m_surfaceIntegration) + m_surfaceIntegration->onTitle(QString::fromUtf8(val->string.str, val->string.len)); break; case VTERM_PROP_ALTSCREEN: m_altscreen = val->boolean; @@ -316,6 +354,8 @@ struct TerminalSurfacePrivate break; case VTERM_N_PROPS: break; + case VTERM_PROP_FOCUSREPORT: + break; } return 1; } @@ -331,8 +371,11 @@ struct TerminalSurfacePrivate const VTermScreenCell *cellAt(int x, int y) { - QTC_ASSERT(y >= 0 && x >= 0, return nullptr); - QTC_ASSERT(y < q->fullSize().height() && x < liveSize().width(), return nullptr); + if (y < 0 || x < 0 || y >= q->fullSize().height() || x >= liveSize().width()) { + qCWarning(log) << "Invalid Parameter for cellAt:" << x << y << "liveSize:" << liveSize() + << "fullSize:" << q->fullSize(); + return nullptr; + } if (!m_altscreen && y < m_scrollback->size()) { const auto &sbl = m_scrollback->line((m_scrollback->size() - 1) - y); @@ -357,24 +400,27 @@ struct TerminalSurfacePrivate VTermScreenCallbacks m_vtermScreenCallbacks; VTermStateFallbacks m_vtermStateFallbacks; + VTermSelectionCallbacks m_vtermSelectionCallbacks; + Cursor m_cursor; QString m_currentCommand; bool m_altscreen{false}; - std::unique_ptr m_scrollback; + std::unique_ptr m_scrollback; - ShellIntegration *m_shellIntegration{nullptr}; + SurfaceIntegration *m_surfaceIntegration{nullptr}; TerminalSurface *q; QTimer m_delayWriteTimer; QByteArray m_writeBuffer; + QByteArray m_selectionBuffer; TerminalSurface::WriteToPty m_writeToPty; }; -TerminalSurface::TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration) - : d(std::make_unique(this, initialGridSize, shellIntegration)) +TerminalSurface::TerminalSurface(QSize initialGridSize) + : d(std::make_unique(this, initialGridSize)) { d->init(); } @@ -426,8 +472,10 @@ TerminalCell TerminalSurface::fetchCell(int x, int y) const QTextCharFormat::NoUnderline, false}; - QTC_ASSERT(y >= 0, return emptyCell); - QTC_ASSERT(y < fullSize().height() && x < fullSize().width(), return emptyCell); + if (y < 0 || y >= fullSize().height() || x >= fullSize().width()) { + qCWarning(log) << "Invalid Parameter for fetchCell:" << x << y << "fullSize:" << fullSize(); + return emptyCell; + } const VTermScreenCell *refCell = d->cellAt(x, y); if (!refCell) @@ -507,8 +555,8 @@ void TerminalSurface::sendKey(const QString &text) void TerminalSurface::sendKey(QKeyEvent *event) { bool keypad = event->modifiers() & Qt::KeypadModifier; - VTermModifier mod = Internal::qtModifierToVTerm(event->modifiers()); - VTermKey key = Internal::qtKeyToVTerm(Qt::Key(event->key()), keypad); + VTermModifier mod = qtModifierToVTerm(event->modifiers()); + VTermKey key = qtKeyToVTerm(Qt::Key(event->key()), keypad); if (key != VTERM_KEY_NONE) { if (mod == VTERM_MOD_SHIFT && (key == VTERM_KEY_ESCAPE || key == VTERM_KEY_BACKSPACE)) @@ -516,21 +564,8 @@ void TerminalSurface::sendKey(QKeyEvent *event) vterm_keyboard_key(d->m_vterm.get(), key, mod); } else if (event->text().length() == 1) { - // This maps to delete word and is way to easy to mistakenly type - // if (event->key() == Qt::Key_Space && mod == VTERM_MOD_SHIFT) - // mod = VTERM_MOD_NONE; - - // Per https://github.com/justinmk/neovim/commit/317d5ca7b0f92ef42de989b3556ca9503f0a3bf6 - // libvterm prefers we send the full keycode rather than sending the - // ctrl modifier. This helps with ncurses applications which otherwise - // do not recognize ctrl+ and in the shell for getting common control characters - // like ctrl+i for tab or ctrl+j for newline. - - // Workaround for "ALT+SHIFT+/" (\ on german mac keyboards) - if (mod == (VTERM_MOD_SHIFT | VTERM_MOD_ALT) && event->key() == Qt::Key_Slash) { - mod = VTERM_MOD_NONE; - } - + // event->text() already contains the correct unicode character based on the modifiers + // used, so we can simply convert it to Ucs4 and send it to the terminal. vterm_keyboard_unichar(d->m_vterm.get(), event->text().toUcs4()[0], VTERM_MOD_NONE); } else if (mod == VTERM_MOD_CTRL && event->key() >= Qt::Key_A && event->key() < Qt::Key_Z) { vterm_keyboard_unichar(d->m_vterm.get(), 'a' + (event->key() - Qt::Key_A), mod); @@ -546,9 +581,63 @@ Cursor TerminalSurface::cursor() const return cursor; } -ShellIntegration *TerminalSurface::shellIntegration() const +SurfaceIntegration *TerminalSurface::surfaceIntegration() const { - return d->m_shellIntegration; + return d->m_surfaceIntegration; +} + +void TerminalSurface::setSurfaceIntegration(SurfaceIntegration *surfaceIntegration) +{ + d->m_surfaceIntegration = surfaceIntegration; +} + +void TerminalSurface::mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers) +{ + vterm_mouse_move(d->m_vterm.get(), pos.y(), pos.x(), qtModifierToVTerm(modifiers)); +} + +void TerminalSurface::mouseButton(Qt::MouseButton button, + bool pressed, + Qt::KeyboardModifiers modifiers) +{ + int btnIdx = 0; + switch (button) { + case Qt::LeftButton: + btnIdx = 1; + break; + case Qt::RightButton: + btnIdx = 3; + break; + case Qt::MiddleButton: + btnIdx = 2; + break; + case Qt::ExtraButton1: + btnIdx = 4; + break; + case Qt::ExtraButton2: + btnIdx = 5; + break; + case Qt::ExtraButton3: + btnIdx = 6; + break; + case Qt::ExtraButton4: + btnIdx = 7; + break; + default: + return; + } + + vterm_mouse_button(d->m_vterm.get(), btnIdx, pressed, qtModifierToVTerm(modifiers)); +} + +void TerminalSurface::sendFocus(bool hasFocus) +{ + VTermState *vts = vterm_obtain_state(d->m_vterm.get()); + + if (hasFocus) + vterm_state_focus_in(vts); + else + vterm_state_focus_out(vts); } void TerminalSurface::setWriteToPty(WriteToPty writeToPty) @@ -597,4 +686,4 @@ std::reverse_iterator TerminalSurface::rIteratorAt(int pos) const return std::make_reverse_iterator(iteratorAt(pos)); } -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/plugins/terminal/terminalsurface.h b/src/libs/solutions/terminal/terminalsurface.h similarity index 81% rename from src/plugins/terminal/terminalsurface.h rename to src/libs/solutions/terminal/terminalsurface.h index 3b737836f6b..a8cc12fa4b0 100644 --- a/src/plugins/terminal/terminalsurface.h +++ b/src/libs/solutions/terminal/terminalsurface.h @@ -3,8 +3,9 @@ #pragma once +#include "terminal_global.h" + #include "celliterator.h" -#include "shellintegration.h" #include #include @@ -12,9 +13,10 @@ #include -namespace Terminal::Internal { +namespace TerminalSolution { class Scrollback; +class SurfaceIntegration; struct TerminalSurfacePrivate; @@ -45,12 +47,12 @@ struct Cursor bool blink{false}; }; -class TerminalSurface : public QObject +class TERMINAL_EXPORT TerminalSurface : public QObject { Q_OBJECT; public: - TerminalSurface(QSize initialGridSize, ShellIntegration *shellIntegration); + TerminalSurface(QSize initialGridSize); ~TerminalSurface(); public: @@ -93,20 +95,25 @@ public: Cursor cursor() const; - ShellIntegration *shellIntegration() const; + SurfaceIntegration *surfaceIntegration() const; + void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration); using WriteToPty = std::function; void setWriteToPty(WriteToPty writeToPty); + + void mouseMove(QPoint pos, Qt::KeyboardModifiers modifiers); + void mouseButton(Qt::MouseButton button, bool pressed, Qt::KeyboardModifiers modifiers); + + void sendFocus(bool hasFocus); signals: void invalidated(QRect grid); void fullSizeChanged(QSize newSize); void cursorChanged(Cursor oldCursor, Cursor newCursor); void altscreenChanged(bool altScreen); void unscroll(); - void bell(); private: std::unique_ptr d; }; -} // namespace Terminal::Internal +} // namespace TerminalSolution diff --git a/src/libs/solutions/terminal/terminalview.cpp b/src/libs/solutions/terminal/terminalview.cpp new file mode 100644 index 00000000000..2244ea62d73 --- /dev/null +++ b/src/libs/solutions/terminal/terminalview.cpp @@ -0,0 +1,1300 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "terminalview.h" +#include "glyphcache.h" +#include "terminalsurface.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(terminalLog, "qtc.terminal", QtWarningMsg) +Q_LOGGING_CATEGORY(selectionLog, "qtc.terminal.selection", QtWarningMsg) +Q_LOGGING_CATEGORY(paintLog, "qtc.terminal.paint", QtWarningMsg) + +namespace TerminalSolution { + +using namespace std::chrono_literals; + +// Minimum time between two refreshes. (30fps) +static constexpr std::chrono::milliseconds minRefreshInterval = 33ms; + +class TerminalViewPrivate +{ +public: + TerminalViewPrivate() + { + m_cursorBlinkTimer.setInterval(750ms); + m_cursorBlinkTimer.setSingleShot(false); + + m_flushDelayTimer.setSingleShot(true); + m_flushDelayTimer.setInterval(minRefreshInterval); + + m_scrollTimer.setSingleShot(false); + m_scrollTimer.setInterval(500ms); + } + + std::optional m_selection; + std::unique_ptr m_surface; + + QSizeF m_cellSize; + + bool m_ignoreScroll{false}; + + QString m_preEditString; + + std::optional m_linkSelection; + + struct + { + QPoint start; + QPoint end; + } m_activeMouseSelect; + + QTimer m_flushDelayTimer; + + QTimer m_scrollTimer; + int m_scrollDirection{0}; + + std::array m_currentColors; + + std::chrono::system_clock::time_point m_lastFlush{std::chrono::system_clock::now()}; + std::chrono::system_clock::time_point m_lastDoubleClick{std::chrono::system_clock::now()}; + bool m_selectLineMode{false}; + Cursor m_cursor; + QTimer m_cursorBlinkTimer; + bool m_cursorBlinkState{true}; + bool m_allowBlinkingCursor{true}; + bool m_allowMouseTracking{true}; + bool m_passwordModeActive{false}; + + SurfaceIntegration *m_surfaceIntegration{nullptr}; +}; + +QString defaultFontFamily() +{ +#ifdef Q_OS_DARWIN + return QLatin1String("Menlo"); +#elif defined(Q_OS_WIN) + return QLatin1String("Consolas"); +#else + return QLatin1String("Monospace"); +#endif +} + +int defaultFontSize() +{ +#ifdef Q_OS_DARWIN + return 12; +#elif defined(Q_OS_WIN) + return 10; +#else + return 9; +#endif +} + +TerminalView::TerminalView(QWidget *parent) + : QAbstractScrollArea(parent) + , d(std::make_unique()) +{ + setupSurface(); + setFont(QFont(defaultFontFamily(), defaultFontSize())); + + connect(&d->m_cursorBlinkTimer, &QTimer::timeout, this, [this]() { + if (hasFocus()) + d->m_cursorBlinkState = !d->m_cursorBlinkState; + else + d->m_cursorBlinkState = true; + updateViewportRect(gridToViewport(QRect{d->m_cursor.position, d->m_cursor.position})); + }); + + setAttribute(Qt::WA_InputMethodEnabled); + setAttribute(Qt::WA_MouseTracking); + setAcceptDrops(true); + + setCursor(Qt::IBeamCursor); + + setViewportMargins(1, 1, 1, 1); + + setFocus(); + setFocusPolicy(Qt::StrongFocus); + + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + connect(&d->m_flushDelayTimer, &QTimer::timeout, this, [this]() { flushVTerm(true); }); + + connect(&d->m_scrollTimer, &QTimer::timeout, this, [this] { + if (d->m_scrollDirection < 0) + verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub); + else if (d->m_scrollDirection > 0) + verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd); + }); +} + +TerminalView::~TerminalView() = default; + +void TerminalView::setSurfaceIntegration(SurfaceIntegration *surfaceIntegration) +{ + d->m_surfaceIntegration = surfaceIntegration; + if (d->m_surface) + d->m_surface->setSurfaceIntegration(d->m_surfaceIntegration); +} + +TerminalSurface *TerminalView::surface() const +{ + return d->m_surface.get(); +} + +void TerminalView::setupSurface() +{ + d->m_surface = std::make_unique(QSize{80, 60}); + + if (d->m_surfaceIntegration) + d->m_surface->setSurfaceIntegration(d->m_surfaceIntegration); + + d->m_surface->setWriteToPty([this](const QByteArray &data) { return writeToPty(data); }); + + connect(d->m_surface.get(), &TerminalSurface::fullSizeChanged, this, [this] { + updateScrollBars(); + }); + connect(d->m_surface.get(), &TerminalSurface::invalidated, this, [this](const QRect &rect) { + setSelection(std::nullopt); + updateViewportRect(gridToViewport(rect)); + verticalScrollBar()->setValue(d->m_surface->fullSize().height()); + }); + connect( + d->m_surface.get(), + &TerminalSurface::cursorChanged, + this, + [this](const Cursor &oldCursor, const Cursor &newCursor) { + int startX = oldCursor.position.x(); + int endX = newCursor.position.x(); + + if (startX > endX) + std::swap(startX, endX); + + int startY = oldCursor.position.y(); + int endY = newCursor.position.y(); + if (startY > endY) + std::swap(startY, endY); + + d->m_cursor = newCursor; + + updateViewportRect(gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}})); + configBlinkTimer(); + }); + connect(d->m_surface.get(), &TerminalSurface::altscreenChanged, this, [this] { + updateScrollBars(); + if (!setSelection(std::nullopt)) + updateViewport(); + }); + connect(d->m_surface.get(), &TerminalSurface::unscroll, this, [this] { + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + }); + + surfaceChanged(); + updateScrollBars(); +} + +void TerminalView::setAllowBlinkingCursor(bool allow) +{ + d->m_allowBlinkingCursor = allow; +} +bool TerminalView::allowBlinkingCursor() const +{ + return d->m_allowBlinkingCursor; +} + +void TerminalView::configBlinkTimer() +{ + bool shouldRun = d->m_cursor.visible && d->m_cursor.blink && hasFocus() + && d->m_allowBlinkingCursor; + if (shouldRun != d->m_cursorBlinkTimer.isActive()) { + if (shouldRun) + d->m_cursorBlinkTimer.start(); + else + d->m_cursorBlinkTimer.stop(); + } +} + +QColor TerminalView::toQColor(std::variant color) const +{ + if (std::holds_alternative(color)) { + int idx = std::get(color); + if (idx >= 0 && idx < 18) + return d->m_currentColors[idx]; + + return d->m_currentColors[(int) WidgetColorIdx::Background]; + } + return std::get(color); +} + +void TerminalView::setColors(const std::array &newColors) +{ + if (d->m_currentColors == newColors) + return; + + d->m_currentColors = newColors; + + updateViewport(); + update(); +} + +void TerminalView::setPasswordMode(bool passwordMode) +{ + if (passwordMode != d->m_passwordModeActive) { + d->m_passwordModeActive = passwordMode; + updateViewport(); + } +} + +void TerminalView::setFont(const QFont &font) +{ + QAbstractScrollArea::setFont(font); + + QFontMetricsF qfm{font}; + qCInfo(terminalLog) << font.family() << font.pointSize() << qfm.averageCharWidth() + << qfm.maxWidth() << viewport()->size(); + + d->m_cellSize = {qfm.averageCharWidth(), (double) qCeil(qfm.height())}; + + QAbstractScrollArea::setFont(font); + + applySizeChange(); +} + +void TerminalView::copyToClipboard() +{ + if (!d->m_selection.has_value()) + return; + + QString text = textFromSelection(); + + qCDebug(selectionLog) << "Copied to clipboard: " << text; + + setClipboard(text); + + clearSelection(); +} + +void TerminalView::pasteFromClipboard() +{ + QClipboard *clipboard = QApplication::clipboard(); + const QString clipboardText = clipboard->text(QClipboard::Clipboard); + + if (clipboardText.isEmpty()) + return; + + d->m_surface->pasteFromClipboard(clipboardText); +} + +void TerminalView::copyLinkToClipboard() +{ + if (d->m_linkSelection) + setClipboard(d->m_linkSelection->link.text); +} + +std::optional TerminalView::selection() const +{ + return d->m_selection; +} + +void TerminalView::clearSelection() +{ + setSelection(std::nullopt); + //d->m_surface->sendKey(Qt::Key_Escape); +} + +void TerminalView::zoomIn() +{ + QFont f = font(); + f.setPointSize(f.pointSize() + 1); + setFont(f); +} + +void TerminalView::zoomOut() +{ + QFont f = font(); + f.setPointSize(qMax(f.pointSize() - 1, 1)); + setFont(f); +} + +void TerminalView::moveCursorWordLeft() +{ + writeToPty("\x1b\x62"); +} + +void TerminalView::moveCursorWordRight() +{ + writeToPty("\x1b\x66"); +} + +void TerminalView::clearContents() +{ + d->m_surface->clearAll(); +} + +void TerminalView::writeToTerminal(const QByteArray &data, bool forceFlush) +{ + d->m_surface->dataFromPty(data); + flushVTerm(forceFlush); +} + +void TerminalView::flushVTerm(bool force) +{ + const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + const std::chrono::milliseconds timeSinceLastFlush + = std::chrono::duration_cast(now - d->m_lastFlush); + + const bool shouldFlushImmediately = timeSinceLastFlush > minRefreshInterval; + if (force || shouldFlushImmediately) { + if (d->m_flushDelayTimer.isActive()) + d->m_flushDelayTimer.stop(); + + d->m_lastFlush = now; + d->m_surface->flush(); + return; + } + + if (!d->m_flushDelayTimer.isActive()) { + const std::chrono::milliseconds timeToNextFlush = (minRefreshInterval - timeSinceLastFlush); + d->m_flushDelayTimer.start(timeToNextFlush.count()); + } +} + +QString TerminalView::textFromSelection() const +{ + if (!d->m_selection) + return {}; + + CellIterator it = d->m_surface->iteratorAt(d->m_selection->start); + CellIterator end = d->m_surface->iteratorAt(d->m_selection->end); + + if (it.position() >= end.position()) { + qCWarning(selectionLog) << "Invalid selection: start >= end"; + return {}; + } + + std::u32string s; + bool previousWasZero = false; + for (; it != end; ++it) { + if (it.gridPos().x() == 0 && !s.empty() && previousWasZero) + s += U'\n'; + + if (*it != 0) { + previousWasZero = false; + s += *it; + } else { + previousWasZero = true; + } + } + + return QString::fromUcs4(s.data(), static_cast(s.size())); +} + +bool TerminalView::setSelection(const std::optional &selection, bool scroll) +{ + qCDebug(selectionLog) << "setSelection" << selection.has_value(); + if (selection.has_value()) + qCDebug(selectionLog) << "start:" << selection->start << "end:" << selection->end + << "final:" << selection->final; + + if (selectionLog().isDebugEnabled()) + updateViewport(); + + if (selection == d->m_selection) + return false; + + d->m_selection = selection; + selectionChanged(d->m_selection); + + if (d->m_selection && d->m_selection->final && scroll) { + QPoint start = d->m_surface->posToGrid(d->m_selection->start); + QPoint end = d->m_surface->posToGrid(d->m_selection->end); + QRect viewRect = gridToViewport(QRect{start, end}); + if (viewRect.y() >= viewport()->height() || viewRect.y() < 0) { + // Selection is outside of the viewport, scroll to it. + verticalScrollBar()->setValue(start.y()); + } + } + + if (!selectionLog().isDebugEnabled()) + updateViewport(); + + return true; +} + +void TerminalView::restart() +{ + setupSurface(); + applySizeChange(); +} + +QPoint TerminalView::viewportToGlobal(QPoint p) const +{ + int y = p.y() - topMargin(); + const double offset = verticalScrollBar()->value() * d->m_cellSize.height(); + y += offset; + + return {p.x(), y}; +} + +QPoint TerminalView::globalToViewport(QPoint p) const +{ + int y = p.y() + topMargin(); + const double offset = verticalScrollBar()->value() * d->m_cellSize.height(); + y -= offset; + + return {p.x(), y}; +} + +QPoint TerminalView::globalToGrid(QPointF p) const +{ + return QPoint(p.x() / d->m_cellSize.width(), p.y() / d->m_cellSize.height()); +} + +QPointF TerminalView::gridToGlobal(QPoint p, bool bottom, bool right) const +{ + QPointF result = QPointF(p.x() * d->m_cellSize.width(), p.y() * d->m_cellSize.height()); + if (bottom || right) + result += {right ? d->m_cellSize.width() : 0, bottom ? d->m_cellSize.height() : 0}; + return result; +} + +qreal TerminalView::topMargin() const +{ + return viewport()->size().height() + - (d->m_surface->liveSize().height() * d->m_cellSize.height()); +} + +static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) +{ + const qreal radiusBase = qMax(qreal(1), maxRadius); + const qreal pWidth = pen.widthF(); + + const QString key = QLatin1String("WaveUnderline-") % pen.color().name() + % QString::number(int(radiusBase), 16) % QString::number(int(pWidth), 16); + + QPixmap pixmap; + if (QPixmapCache::find(key, &pixmap)) + return pixmap; + + const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio + const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod); + const qreal radius = qFloor(radiusBase * 2) / 2.; + + QPainterPath path; + + qreal xs = 0; + qreal ys = radius; + + while (xs < width) { + xs += halfPeriod; + ys = -ys; + path.quadTo(xs - halfPeriod / 2, ys, xs, 0); + } + + pixmap = QPixmap(width, radius * 2); + pixmap.fill(Qt::transparent); + { + QPen wavePen = pen; + wavePen.setCapStyle(Qt::SquareCap); + + // This is to protect against making the line too fat, as happens on macOS + // due to it having a rather thick width for the regular underline. + const qreal maxPenWidth = .8 * radius; + if (wavePen.widthF() > maxPenWidth) + wavePen.setWidthF(maxPenWidth); + + QPainter imgPainter(&pixmap); + imgPainter.setPen(wavePen); + imgPainter.setRenderHint(QPainter::Antialiasing); + imgPainter.translate(0, radius); + imgPainter.drawPath(path); + } + + QPixmapCache::insert(key, pixmap); + + return pixmap; +} + +// Copied from qpainter.cpp +static void drawTextItemDecoration(QPainter &painter, + const QPointF &pos, + QTextCharFormat::UnderlineStyle underlineStyle, + QTextItem::RenderFlags flags, + qreal width, + const QColor &underlineColor, + const QRawFont &font) +{ + if (underlineStyle == QTextCharFormat::NoUnderline + && !(flags & (QTextItem::StrikeOut | QTextItem::Overline))) + return; + + const QPen oldPen = painter.pen(); + const QBrush oldBrush = painter.brush(); + painter.setBrush(Qt::NoBrush); + QPen pen = oldPen; + pen.setStyle(Qt::SolidLine); + pen.setWidthF(font.lineThickness()); + pen.setCapStyle(Qt::FlatCap); + + QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y()); + + const qreal underlineOffset = font.underlinePosition(); + + /*if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { + QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); + if (theme) + underlineStyle = QTextCharFormat::UnderlineStyle( + theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt()); + if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved + underlineStyle = QTextCharFormat::WaveUnderline; + }*/ + + if (underlineStyle == QTextCharFormat::WaveUnderline) { + painter.save(); + painter.translate(0, pos.y() + 1); + qreal maxHeight = font.descent() - qreal(1); + + QColor uc = underlineColor; + if (uc.isValid()) + pen.setColor(uc); + + // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms + const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), + maxHeight / qreal(2.)), + pen); + const int descent = qFloor(maxHeight); + + painter.setBrushOrigin(painter.brushOrigin().x(), 0); + painter.fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave); + painter.restore(); + } else if (underlineStyle != QTextCharFormat::NoUnderline) { + // Deliberately ceil the offset to avoid the underline coming too close to + // the text above it, but limit it to stay within descent. + qreal adjustedUnderlineOffset = std::ceil(underlineOffset) + 0.5; + if (underlineOffset <= font.descent()) + adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, font.descent() - qreal(0.5)); + const qreal underlinePos = pos.y() + adjustedUnderlineOffset; + QColor uc = underlineColor; + if (uc.isValid()) + pen.setColor(uc); + + pen.setStyle((Qt::PenStyle)(underlineStyle)); + painter.setPen(pen); + QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos); + painter.drawLine(underline); + } + + pen.setStyle(Qt::SolidLine); + pen.setColor(oldPen.color()); + + if (flags & QTextItem::StrikeOut) { + QLineF strikeOutLine = line; + strikeOutLine.translate(0., -font.ascent() / 3.); + QColor uc = underlineColor; + if (uc.isValid()) + pen.setColor(uc); + painter.setPen(pen); + painter.drawLine(strikeOutLine); + } + + if (flags & QTextItem::Overline) { + QLineF overline = line; + overline.translate(0., -font.ascent()); + QColor uc = underlineColor; + if (uc.isValid()) + pen.setColor(uc); + painter.setPen(pen); + painter.drawLine(overline); + } + + painter.setPen(oldPen); + painter.setBrush(oldBrush); +} + +bool TerminalView::paintFindMatches(QPainter &p, + QList::const_iterator &it, + const QRectF &cellRect, + const QPoint gridPos) const +{ + if (it == searchHits().constEnd()) + return false; + + const int pos = d->m_surface->gridToPos(gridPos); + while (it != searchHits().constEnd()) { + if (pos < it->start) + return false; + + if (pos >= it->end) { + ++it; + continue; + } + break; + } + + if (it == searchHits().constEnd()) + return false; + + p.fillRect(cellRect, d->m_currentColors[(size_t) WidgetColorIdx::FindMatch]); + + return true; +} + +bool TerminalView::paintSelection(QPainter &p, const QRectF &cellRect, const QPoint gridPos) const +{ + bool isInSelection = false; + const int pos = d->m_surface->gridToPos(gridPos); + + if (d->m_selection) + isInSelection = pos >= d->m_selection->start && pos < d->m_selection->end; + + if (isInSelection) + p.fillRect(cellRect, d->m_currentColors[(size_t) WidgetColorIdx::Selection]); + + return isInSelection; +} + +int TerminalView::paintCell(QPainter &p, + const QRectF &cellRect, + QPoint gridPos, + const TerminalCell &cell, + QFont &f, + QList::const_iterator &searchIt) const +{ + bool paintBackground = !paintSelection(p, cellRect, gridPos) + && !paintFindMatches(p, searchIt, cellRect, gridPos); + + bool isDefaultBg = std::holds_alternative(cell.backgroundColor) + && std::get(cell.backgroundColor) == 17; + + if (paintBackground && !isDefaultBg) + p.fillRect(cellRect, toQColor(cell.backgroundColor)); + + p.setPen(toQColor(cell.foregroundColor)); + + f.setBold(cell.bold); + f.setItalic(cell.italic); + + if (!cell.text.isEmpty()) { + const auto r = GlyphCache::instance().get(f, cell.text); + + if (r) { + const auto brSize = r->boundingRect().size(); + QPointF brOffset; + if (brSize.width() > cellRect.size().width()) + brOffset.setX(-(brSize.width() - cellRect.size().width()) / 2.0); + if (brSize.height() > cellRect.size().height()) + brOffset.setY(-(brSize.height() - cellRect.size().height()) / 2.0); + + QPointF finalPos = cellRect.topLeft() + brOffset; + + p.drawGlyphRun(finalPos, *r); + + bool tempLink = false; + if (d->m_linkSelection) { + int chPos = d->m_surface->gridToPos(gridPos); + tempLink = chPos >= d->m_linkSelection->start && chPos < d->m_linkSelection->end; + } + if (cell.underlineStyle != QTextCharFormat::NoUnderline || cell.strikeOut || tempLink) { + QTextItem::RenderFlags flags; + //flags.setFlag(QTextItem::RenderFlag::Underline, cell.format.fontUnderline()); + flags.setFlag(QTextItem::StrikeOut, cell.strikeOut); + finalPos.setY(finalPos.y() + r->rawFont().ascent()); + drawTextItemDecoration(p, + finalPos, + tempLink ? QTextCharFormat::DashUnderline + : cell.underlineStyle, + flags, + cellRect.size().width(), + {}, + r->rawFont()); + } + } + } + + return cell.width; +} + +void TerminalView::paintCursor(QPainter &p) const +{ + auto cursor = d->m_surface->cursor(); + + const int cursorCellWidth = d->m_surface->cellWidthAt(cursor.position.x(), cursor.position.y()); + + if (!d->m_preEditString.isEmpty()) { + cursor.shape = Cursor::Shape::Underline; + } else if (d->m_passwordModeActive) { + QRectF cursorRect = QRectF(gridToGlobal(cursor.position), + gridToGlobal({cursor.position.x() + cursorCellWidth, + cursor.position.y()}, + true)) + .toAlignedRect(); + + const qreal dpr = p.device()->devicePixelRatioF(); + const QString key = QString("terminalpasswordlock-") + % QString::number(cursorRect.size().height()) + % "@" % QString::number(dpr); + QPixmap px; + if (!QPixmapCache::find(key, &px)) { + const QPixmap lock(":/terminal/images/passwordlock.png"); + px = lock.scaledToHeight(cursorRect.size().height() * dpr, Qt::SmoothTransformation); + px.setDevicePixelRatio(dpr); + QPixmapCache::insert(key, px); + } + + p.drawPixmap(cursorRect.topLeft(), px); + + return; + } + const bool blinkState = !cursor.blink || d->m_cursorBlinkState || !d->m_allowBlinkingCursor + || !d->m_cursorBlinkTimer.isActive(); + + if (cursor.visible && blinkState) { + QRectF cursorRect = QRectF(gridToGlobal(cursor.position), + gridToGlobal({cursor.position.x() + cursorCellWidth, + cursor.position.y()}, + true)) + .toAlignedRect(); + + cursorRect.adjust(1, 1, -1, -1); + + QPen pen(Qt::white, 0, Qt::SolidLine); + p.setPen(pen); + + if (hasFocus()) { + QPainter::CompositionMode oldMode = p.compositionMode(); + p.setCompositionMode(QPainter::RasterOp_NotDestination); + switch (cursor.shape) { + case Cursor::Shape::Block: + p.fillRect(cursorRect, p.pen().brush()); + break; + case Cursor::Shape::Underline: + p.drawLine(cursorRect.bottomLeft(), cursorRect.bottomRight()); + break; + case Cursor::Shape::LeftBar: + p.drawLine(cursorRect.topLeft(), cursorRect.bottomLeft()); + break; + } + p.setCompositionMode(oldMode); + } else { + p.drawRect(cursorRect); + } + } +} + +void TerminalView::paintPreedit(QPainter &p) const +{ + auto cursor = d->m_surface->cursor(); + if (!d->m_preEditString.isEmpty()) { + QRectF rect = QRectF(gridToGlobal(cursor.position), + gridToGlobal({cursor.position.x(), cursor.position.y()}, true, true)); + + rect.setWidth(viewport()->width() - rect.x()); + + p.setPen(toQColor((int) WidgetColorIdx::Foreground)); + QFont f = font(); + f.setUnderline(true); + p.setFont(f); + p.drawText(rect, Qt::TextDontClip | Qt::TextWrapAnywhere, d->m_preEditString); + } +} + +void TerminalView::paintCells(QPainter &p, QPaintEvent *event) const +{ + QFont f = font(); + + const int scrollOffset = verticalScrollBar()->value(); + + const int maxRow = d->m_surface->fullSize().height(); + const int startRow = qFloor((qreal) event->rect().y() / d->m_cellSize.height()) + scrollOffset; + const int endRow = qMin(maxRow, + qCeil((event->rect().y() + event->rect().height()) + / d->m_cellSize.height()) + + scrollOffset); + + QList::const_iterator searchIt + = std::lower_bound(searchHits().constBegin(), + searchHits().constEnd(), + startRow, + [this](const SearchHit &hit, int value) { + return d->m_surface->posToGrid(hit.start).y() < value; + }); + + for (int cellY = startRow; cellY < endRow; ++cellY) { + for (int cellX = 0; cellX < d->m_surface->liveSize().width();) { + const auto cell = d->m_surface->fetchCell(cellX, cellY); + + QRectF cellRect(gridToGlobal({cellX, cellY}), + QSizeF{d->m_cellSize.width() * cell.width, d->m_cellSize.height()}); + + int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f, searchIt); + + cellX += numCells; + } + } +} + +void TerminalView::paintDebugSelection(QPainter &p, const Selection &selection) const +{ + auto s = globalToViewport(gridToGlobal(d->m_surface->posToGrid(selection.start)).toPoint()); + const auto e = globalToViewport( + gridToGlobal(d->m_surface->posToGrid(selection.end), true).toPoint()); + + p.setPen(QPen(Qt::green, 1, Qt::DashLine)); + p.drawLine(s.x(), 0, s.x(), height()); + p.drawLine(0, s.y(), width(), s.y()); + + p.setPen(QPen(Qt::red, 1, Qt::DashLine)); + + p.drawLine(e.x(), 0, e.x(), height()); + p.drawLine(0, e.y(), width(), e.y()); +} + +void TerminalView::paintEvent(QPaintEvent *event) +{ + QElapsedTimer t; + t.start(); + event->accept(); + QPainter p(viewport()); + + p.save(); + + if (paintLog().isDebugEnabled()) + p.fillRect(event->rect(), QColor::fromRgb(rand() % 60, rand() % 60, rand() % 60)); + else + p.fillRect(event->rect(), d->m_currentColors[(size_t) WidgetColorIdx::Background]); + + int scrollOffset = verticalScrollBar()->value(); + int offset = -(scrollOffset * d->m_cellSize.height()); + + qreal margin = topMargin(); + + p.translate(QPointF{0.0, offset + margin}); + + paintCells(p, event); + paintCursor(p); + paintPreedit(p); + + p.restore(); + + p.fillRect(QRectF{{0, 0}, QSizeF{(qreal) width(), topMargin()}}, + d->m_currentColors[(size_t) WidgetColorIdx::Background]); + + if (selectionLog().isDebugEnabled()) { + if (d->m_selection) + paintDebugSelection(p, *d->m_selection); + if (d->m_linkSelection) + paintDebugSelection(p, *d->m_linkSelection); + } + + if (paintLog().isDebugEnabled()) { + QToolTip::showText(this->mapToGlobal(QPoint(width() - 200, 0)), + QString("Paint: %1ms").arg(t.elapsed())); + } +} + +void TerminalView::keyPressEvent(QKeyEvent *event) +{ + // Don't blink during typing + if (d->m_cursorBlinkTimer.isActive()) { + d->m_cursorBlinkTimer.start(); + d->m_cursorBlinkState = true; + } + + if (event->key() == Qt::Key_Control) { + if (!d->m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) { + setCursor(Qt::PointingHandCursor); + } + } + + event->accept(); + + d->m_surface->sendKey(event); +} + +void TerminalView::keyReleaseEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Control && d->m_linkSelection.has_value()) { + d->m_linkSelection.reset(); + setCursor(Qt::IBeamCursor); + updateViewport(); + } +} + +void TerminalView::applySizeChange() +{ + QSize newLiveSize = { + qFloor((qreal) (viewport()->size().width()) / (qreal) d->m_cellSize.width()), + qFloor((qreal) (viewport()->size().height()) / d->m_cellSize.height()), + }; + + if (newLiveSize.height() <= 0) + newLiveSize.setHeight(1); + + if (newLiveSize.width() <= 0) + newLiveSize.setWidth(1); + + resizePty(newLiveSize); + d->m_surface->resize(newLiveSize); + flushVTerm(true); +} + +void TerminalView::updateScrollBars() +{ + int scrollSize = d->m_surface->fullSize().height() - d->m_surface->liveSize().height(); + verticalScrollBar()->setRange(0, scrollSize); + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); + updateViewport(); +} + +void TerminalView::resizeEvent(QResizeEvent *event) +{ + event->accept(); + + // If increasing in size, we'll trigger libvterm to call sb_popline in + // order to pull lines out of the history. This will cause the scrollback + // to decrease in size which reduces the size of the verticalScrollBar. + // That will trigger a scroll offset increase which we want to ignore. + d->m_ignoreScroll = true; + + applySizeChange(); + + setSelection(std::nullopt); + d->m_ignoreScroll = false; +} + +QRect TerminalView::gridToViewport(QRect rect) const +{ + int offset = verticalScrollBar()->value(); + + int startRow = rect.y() - offset; + int numRows = rect.height(); + int numCols = rect.width(); + + QRectF r{rect.x() * d->m_cellSize.width(), + startRow * d->m_cellSize.height(), + numCols * d->m_cellSize.width(), + numRows * d->m_cellSize.height()}; + + r.translate(0, topMargin()); + + return r.toAlignedRect(); +} + +QPoint TerminalView::toGridPos(QMouseEvent *event) const +{ + return globalToGrid(QPointF(event->pos()) + QPointF(0, -topMargin() + 0.5)); +} + +void TerminalView::updateViewport() +{ + viewport()->update(); +} + +void TerminalView::updateViewportRect(const QRect &rect) +{ + if (d->m_passwordModeActive) + viewport()->update(); + else + viewport()->update(rect); +} + +void TerminalView::focusInEvent(QFocusEvent *) +{ + updateViewport(); + configBlinkTimer(); + selectionChanged(d->m_selection); + d->m_surface->sendFocus(true); +} +void TerminalView::focusOutEvent(QFocusEvent *) +{ + updateViewport(); + configBlinkTimer(); + d->m_surface->sendFocus(false); +} + +void TerminalView::inputMethodEvent(QInputMethodEvent *event) +{ + d->m_preEditString = event->preeditString(); + + if (event->commitString().isEmpty()) { + updateViewport(); + return; + } + + d->m_surface->sendKey(event->commitString()); +} + +void TerminalView::mousePressEvent(QMouseEvent *event) +{ + if (d->m_allowMouseTracking) { + d->m_surface->mouseMove(toGridPos(event), event->modifiers()); + d->m_surface->mouseButton(event->button(), true, event->modifiers()); + } + + d->m_scrollDirection = 0; + + d->m_activeMouseSelect.start = viewportToGlobal(event->pos()); + + if (event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier) { + if (d->m_linkSelection) { + if (event->modifiers() & Qt::ShiftModifier) { + copyLinkToClipboard(); + return; + } + + linkActivated(d->m_linkSelection->link); + } + return; + } + + if (event->button() == Qt::LeftButton) { + if (std::chrono::system_clock::now() - d->m_lastDoubleClick < 500ms) { + d->m_selectLineMode = true; + const Selection newSelection{d->m_surface->gridToPos( + {0, + d->m_surface->posToGrid(d->m_selection->start).y()}), + d->m_surface->gridToPos( + {d->m_surface->liveSize().width(), + d->m_surface->posToGrid(d->m_selection->end).y()}), + false}; + setSelection(newSelection); + } else { + d->m_selectLineMode = false; + int pos = d->m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos()))); + setSelection(Selection{pos, pos, false}); + } + event->accept(); + updateViewport(); + } else if (event->button() == Qt::RightButton) { + if (event->modifiers() & Qt::ShiftModifier) { + contextMenuRequested(event->pos()); + } else if (d->m_selection) { + copyToClipboard(); + setSelection(std::nullopt); + } else { + pasteFromClipboard(); + } + } else if (event->button() == Qt::MiddleButton) { + QClipboard *clipboard = QApplication::clipboard(); + if (clipboard->supportsSelection()) { + const QString selectionText = clipboard->text(QClipboard::Selection); + if (!selectionText.isEmpty()) + d->m_surface->pasteFromClipboard(selectionText); + } else { + d->m_surface->pasteFromClipboard(textFromSelection()); + } + } +} +void TerminalView::mouseMoveEvent(QMouseEvent *event) +{ + if (d->m_allowMouseTracking) + d->m_surface->mouseMove(toGridPos(event), event->modifiers()); + + if (d->m_selection && event->buttons() & Qt::LeftButton) { + Selection newSelection = *d->m_selection; + int scrollVelocity = 0; + if (event->pos().y() < 0) { + scrollVelocity = (event->pos().y()); + } else if (event->pos().y() > viewport()->height()) { + scrollVelocity = (event->pos().y() - viewport()->height()); + } + + if ((scrollVelocity != 0) != d->m_scrollTimer.isActive()) { + if (scrollVelocity != 0) + d->m_scrollTimer.start(); + else + d->m_scrollTimer.stop(); + } + + d->m_scrollDirection = scrollVelocity; + + if (d->m_scrollTimer.isActive() && scrollVelocity != 0) { + const std::chrono::milliseconds scrollInterval = 1000ms / qAbs(scrollVelocity); + if (d->m_scrollTimer.intervalAsDuration() != scrollInterval) + d->m_scrollTimer.setInterval(scrollInterval); + } + + QPoint posBoundedToViewport = event->pos(); + posBoundedToViewport.setX(qBound(0, posBoundedToViewport.x(), viewport()->width())); + + int start = d->m_surface->gridToPos(globalToGrid(d->m_activeMouseSelect.start)); + int newEnd = d->m_surface->gridToPos(globalToGrid(viewportToGlobal(posBoundedToViewport))); + + if (start > newEnd) { + std::swap(start, newEnd); + } + if (start < 0) + start = 0; + + if (d->m_selectLineMode) { + newSelection.start = d->m_surface->gridToPos({0, d->m_surface->posToGrid(start).y()}); + newSelection.end = d->m_surface->gridToPos( + {d->m_surface->liveSize().width(), d->m_surface->posToGrid(newEnd).y()}); + } else { + newSelection.start = start; + newSelection.end = newEnd; + } + + setSelection(newSelection); + } else if (event->modifiers() & Qt::ControlModifier) { + checkLinkAt(event->pos()); + } else if (d->m_linkSelection) { + d->m_linkSelection.reset(); + updateViewport(); + } + + if (d->m_linkSelection) { + setCursor(Qt::PointingHandCursor); + } else { + setCursor(Qt::IBeamCursor); + } +} + +void TerminalView::mouseReleaseEvent(QMouseEvent *event) +{ + if (d->m_allowMouseTracking) { + d->m_surface->mouseMove(toGridPos(event), event->modifiers()); + d->m_surface->mouseButton(event->button(), false, event->modifiers()); + } + + d->m_scrollTimer.stop(); + + if (d->m_selection && event->button() == Qt::LeftButton) { + if (d->m_selection->end - d->m_selection->start == 0) + setSelection(std::nullopt); + else + setSelection(Selection{d->m_selection->start, d->m_selection->end, true}); + } +} + +void TerminalView::mouseDoubleClickEvent(QMouseEvent *event) +{ + if (d->m_allowMouseTracking) { + d->m_surface->mouseMove(toGridPos(event), event->modifiers()); + d->m_surface->mouseButton(event->button(), true, event->modifiers()); + d->m_surface->mouseButton(event->button(), false, event->modifiers()); + } + + const auto hit = textAt(event->pos()); + + setSelection(Selection{hit.start, hit.end, true}); + + d->m_lastDoubleClick = std::chrono::system_clock::now(); + + event->accept(); +} + +void TerminalView::wheelEvent(QWheelEvent *event) +{ + verticalScrollBar()->event(event); + + if (!d->m_allowMouseTracking) + return; + + if (event->angleDelta().ry() > 0) + d->m_surface->mouseButton(Qt::ExtraButton1, true, event->modifiers()); + else if (event->angleDelta().ry() < 0) + d->m_surface->mouseButton(Qt::ExtraButton2, true, event->modifiers()); +} + +bool TerminalView::checkLinkAt(const QPoint &pos) +{ + const TextAndOffsets hit = textAt(pos); + + if (hit.text.size() > 0) { + QString t = QString::fromUcs4(hit.text.c_str(), hit.text.size()).trimmed(); + auto newLink = toLink(t); + if (newLink) { + const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, newLink.value()}; + if (!d->m_linkSelection || *d->m_linkSelection != newSelection) { + d->m_linkSelection = newSelection; + updateViewport(); + } + + return true; + } + } + + if (d->m_linkSelection) { + d->m_linkSelection.reset(); + updateViewport(); + } + return false; +} + +TerminalView::TextAndOffsets TerminalView::textAt(const QPoint &pos) const +{ + auto it = d->m_surface->iteratorAt(globalToGrid(viewportToGlobal(pos))); + auto itRev = d->m_surface->rIteratorAt(globalToGrid(viewportToGlobal(pos))); + + std::u32string whiteSpaces = U" \t\x00a0"; + + const bool inverted = whiteSpaces.find(*it) != std::u32string::npos || *it == 0; + + auto predicate = [inverted, whiteSpaces](const std::u32string::value_type &ch) { + if (inverted) + return ch != 0 && whiteSpaces.find(ch) == std::u32string::npos; + else + return ch == 0 || whiteSpaces.find(ch) != std::u32string::npos; + }; + + auto itRight = std::find_if(it, d->m_surface->end(), predicate); + auto itLeft = std::find_if(itRev, d->m_surface->rend(), predicate); + + std::u32string text; + std::copy(itLeft.base(), it, std::back_inserter(text)); + std::copy(it, itRight, std::back_inserter(text)); + std::transform(text.begin(), text.end(), text.begin(), [](const char32_t &ch) { + return ch == 0 ? U' ' : ch; + }); + + return {(itLeft.base()).position(), itRight.position(), text}; +} + +bool TerminalView::event(QEvent *event) +{ + if (event->type() == QEvent::Paint) { + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), + d->m_currentColors[(size_t) WidgetColorIdx::Background]); + return true; + } + + // TODO: Is this necessary? + if (event->type() == QEvent::KeyPress) { + auto k = static_cast(event); + keyPressEvent(k); + return true; + } + if (event->type() == QEvent::KeyRelease) { + auto k = static_cast(event); + keyReleaseEvent(k); + return true; + } + + return QAbstractScrollArea::event(event); +} + +} // namespace TerminalSolution diff --git a/src/libs/solutions/terminal/terminalview.h b/src/libs/solutions/terminal/terminalview.h new file mode 100644 index 00000000000..4745c63ee15 --- /dev/null +++ b/src/libs/solutions/terminal/terminalview.h @@ -0,0 +1,227 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "terminal_global.h" +#include "terminalsurface.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace TerminalSolution { + +class SurfaceIntegration; +class TerminalViewPrivate; + +struct SearchHit +{ + int start{-1}; + int end{-1}; + + bool operator!=(const SearchHit &other) const + { + return start != other.start || end != other.end; + } + bool operator==(const SearchHit &other) const { return !operator!=(other); } +}; + +QString defaultFontFamily(); +int defaultFontSize(); + +class TERMINAL_EXPORT TerminalView : public QAbstractScrollArea +{ + friend class CellIterator; + Q_OBJECT +public: + enum class WidgetColorIdx { + Foreground = ColorIndex::Foreground, + Background = ColorIndex::Background, + Selection, + FindMatch, + }; + + TerminalView(QWidget *parent = nullptr); + ~TerminalView() override; + + void setAllowBlinkingCursor(bool allow); + bool allowBlinkingCursor() const; + + void setFont(const QFont &font); + + void copyToClipboard(); + void pasteFromClipboard(); + void copyLinkToClipboard(); + + struct Selection + { + int start; + int end; + bool final{false}; + + bool operator!=(const Selection &other) const + { + return start != other.start || end != other.end || final != other.final; + } + + bool operator==(const Selection &other) const { return !operator!=(other); } + }; + + std::optional selection() const; + void clearSelection(); + + void zoomIn(); + void zoomOut(); + + void moveCursorWordLeft(); + void moveCursorWordRight(); + + void clearContents(); + + void setSurfaceIntegration(SurfaceIntegration *surfaceIntegration); + void setColors(const std::array &colors); + + void setPasswordMode(bool passwordMode); + + struct Link + { + QString text; + int targetLine = 0; + int targetColumn = 0; + }; + + struct LinkSelection : public Selection + { + Link link; + + bool operator!=(const LinkSelection &other) const + { + return link.text != other.link.text || link.targetLine != other.link.targetLine + || link.targetColumn != other.link.targetColumn || Selection::operator!=(other); + } + }; + + virtual qint64 writeToPty(const QByteArray &data) + { + Q_UNUSED(data); + return 0; + } + void writeToTerminal(const QByteArray &data, bool forceFlush); + + void restart(); + + virtual const QList &searchHits() const + { + static QList noHits; + return noHits; + } + + virtual void resizePty(QSize newSize) { Q_UNUSED(newSize); } + virtual void setClipboard(const QString &text) { Q_UNUSED(text); } + virtual std::optional toLink(const QString &text) + { + Q_UNUSED(text); + return std::nullopt; + } + + virtual void selectionChanged(const std::optional &newSelection) + { + Q_UNUSED(newSelection); + } + virtual void linkActivated(const Link &link) { Q_UNUSED(link); } + virtual void contextMenuRequested(const QPoint &pos) { Q_UNUSED(pos); } + + virtual void surfaceChanged(){}; + + TerminalSurface *surface() const; + +protected: + void paintEvent(QPaintEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void inputMethodEvent(QInputMethodEvent *event) override; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + + bool event(QEvent *event) override; + +protected: + void setupSurface(); + + int paintCell(QPainter &p, + const QRectF &cellRect, + QPoint gridPos, + const TerminalCell &cell, + QFont &f, + QList::const_iterator &searchIt) const; + void paintCells(QPainter &painter, QPaintEvent *event) const; + void paintCursor(QPainter &painter) const; + void paintPreedit(QPainter &painter) const; + bool paintFindMatches(QPainter &painter, + QList::const_iterator &searchIt, + const QRectF &cellRect, + const QPoint gridPos) const; + + bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const; + void paintDebugSelection(QPainter &painter, const Selection &selection) const; + + qreal topMargin() const; + + QPoint viewportToGlobal(QPoint p) const; + QPoint globalToViewport(QPoint p) const; + QPoint globalToGrid(QPointF p) const; + QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const; + QRect gridToViewport(QRect rect) const; + QPoint toGridPos(QMouseEvent *event) const; + + void updateViewport(); + void updateViewportRect(const QRect &rect); + + int textLineFromPixel(int y) const; + std::optional textPosFromPoint(const QTextLayout &textLayout, QPoint p) const; + + std::optional selectionToFormatRange(Selection selection, + const QTextLayout &layout, + int rowOffset) const; + + bool checkLinkAt(const QPoint &pos); + + struct TextAndOffsets + { + int start; + int end; + std::u32string text; + }; + + TextAndOffsets textAt(const QPoint &pos) const; + + void applySizeChange(); + void updateScrollBars(); + + void flushVTerm(bool force); + + bool setSelection(const std::optional &selection, bool scroll = true); + QString textFromSelection() const; + + void configBlinkTimer(); + + QColor toQColor(std::variant color) const; + +private: + std::unique_ptr d; +}; + +} // namespace TerminalSolution diff --git a/src/libs/sqlite/sqlite.qbs b/src/libs/sqlite/sqlite.qbs index f0c58790006..c25bd5f3fc0 100644 --- a/src/libs/sqlite/sqlite.qbs +++ b/src/libs/sqlite/sqlite.qbs @@ -1,9 +1,13 @@ -import qbs 1.0 +import qbs.Utilities QtcLibrary { name: "Sqlite" + Depends { name: "Utils" } Depends { name: "sqlite_sources" } + Depends { name: "Qt.core"; required:false } + condition: Qt.core.present && Utilities.versionCompare(Qt.core.version, "6.4.3") >= 0 + property string exportedIncludeDir: sqlite_sources.includeDir Export { diff --git a/src/libs/tracing/CMakeLists.txt b/src/libs/tracing/CMakeLists.txt index 3e0a7237f6c..e89f31f9cc8 100644 --- a/src/libs/tracing/CMakeLists.txt +++ b/src/libs/tracing/CMakeLists.txt @@ -1,11 +1,3 @@ -if (WITH_TESTS) - set(TEST_SOURCES - runscenegraphtest.cpp runscenegraphtest.h - ) -else() - set(TEST_SOURCES "") -endif() - set(TRACING_CPP_SOURCES flamegraph.cpp flamegraph.h flamegraphattached.h @@ -40,8 +32,6 @@ add_qtc_library(Tracing FEATURE_INFO DEPENDS Utils Qt::Quick PUBLIC_DEPENDS Qt::Widgets Qt::Qml - SOURCES - ${TEST_SOURCES} ) if (NOT TARGET Tracing) diff --git a/src/libs/tracing/qml/CategoryLabel.qml b/src/libs/tracing/qml/CategoryLabel.qml index 58704aa175a..db58b13c0ff 100644 --- a/src/libs/tracing/qml/CategoryLabel.qml +++ b/src/libs/tracing/qml/CategoryLabel.qml @@ -15,7 +15,7 @@ Item { property bool expanded: model && model.expanded property var labels: (expanded && model) ? model.labels : [] - property bool dragging + property bool isDragging property int visualIndex property int dragOffset property Item draggerParent @@ -38,10 +38,10 @@ Item { id: dragArea anchors.fill: txt drag.target: dragger - cursorShape: labelContainer.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor + cursorShape: labelContainer.isDragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor // Account for parent change below - drag.minimumY: labelContainer.dragging ? 0 : -labelContainer.dragOffset - drag.maximumY: labelContainer.visibleHeight - (labelContainer.dragging ? 0 : labelContainer.dragOffset) + drag.minimumY: labelContainer.isDragging ? 0 : -labelContainer.dragOffset + drag.maximumY: labelContainer.visibleHeight - (labelContainer.isDragging ? 0 : labelContainer.dragOffset) drag.axis: Drag.YAxis hoverEnabled: true ToolTip { @@ -221,7 +221,7 @@ Item { MouseArea { anchors.top: dragArea.bottom - anchors.bottom: labelContainer.dragging ? labelContainer.bottom : dragArea.bottom + anchors.bottom: labelContainer.isDragging ? labelContainer.bottom : dragArea.bottom anchors.left: labelContainer.left anchors.right: labelContainer.right cursorShape: dragArea.cursorShape diff --git a/src/libs/tracing/qml/TimelineLabels.qml b/src/libs/tracing/qml/TimelineLabels.qml index 754f36e1902..99f36467875 100644 --- a/src/libs/tracing/qml/TimelineLabels.qml +++ b/src/libs/tracing/qml/TimelineLabels.qml @@ -26,7 +26,7 @@ Flickable { // Dispatch the cursor shape to all labels. When dragging the DropArea receiving // the drag events is not necessarily related to the MouseArea receiving the mouse // events, so we can't use the drag events to determine the cursor shape. - property bool dragging: false + property bool isDragging: false Column { id: categoryContent @@ -75,10 +75,10 @@ Flickable { model: modelData notesModel: categories.modelProxy.notes visualIndex: loader.visualIndex - dragging: categories.dragging + isDragging: categories.isDragging reverseSelect: categories.reverseSelect - onDragStarted: categories.dragging = true - onDragStopped: categories.dragging = false + onDragStarted: categories.isDragging = true + onDragStopped: categories.isDragging = false draggerParent: categories contentY: categories.contentY contentHeight: categories.contentHeight diff --git a/src/libs/tracing/runscenegraphtest.cpp b/src/libs/tracing/runscenegraphtest.cpp deleted file mode 100644 index d88dd03271f..00000000000 --- a/src/libs/tracing/runscenegraphtest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "runscenegraphtest.h" - -#include -#include -#include -#include - -namespace Timeline { - -void runSceneGraphTest(QSGNode *node) -{ - Q_UNUSED(node) - - QSurfaceFormat format; - format.setStencilBufferSize(8); - format.setDepthBufferSize(24); - - QOpenGLContext context; - context.setFormat(format); - QVERIFY(context.create()); - - QOffscreenSurface surface; - surface.setFormat(format); - surface.create(); - - QVERIFY(context.makeCurrent(&surface)); - - context.doneCurrent(); -} - -} // namespace Timeline diff --git a/src/libs/tracing/runscenegraphtest.h b/src/libs/tracing/runscenegraphtest.h deleted file mode 100644 index e76702d6818..00000000000 --- a/src/libs/tracing/runscenegraphtest.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "tracing_global.h" -#include - -namespace Timeline { - -void TRACING_EXPORT runSceneGraphTest(QSGNode *node); - -} // namespace Timeline diff --git a/src/libs/tracing/timelinemodel.cpp b/src/libs/tracing/timelinemodel.cpp index d0c88f55280..46ab37f3f5a 100644 --- a/src/libs/tracing/timelinemodel.cpp +++ b/src/libs/tracing/timelinemodel.cpp @@ -83,6 +83,54 @@ void TimelineModel::computeNesting() } } +/*! + Compute all ranges' nesting level. Sort them into rows by finding the + first row that has "an open spot". +*/ +QList TimelineModel::computeRows(int *maxlevel) const +{ + *maxlevel = 0; + std::list rows; + QList levels; + levels.reserve(d->ranges.count()); + for (int range = 0; range != count(); ++range) { + TimelineModelPrivate::Range ¤t = d->ranges[range]; + // find first row for inserting + int level = 0; + auto rowIt = rows.begin(); + forever { + if (rowIt == rows.end()) { + // didn't find a row, insert new one + rows.push_back(range); + break; + } + TimelineModelPrivate::Range &rowItem = d->ranges[*rowIt]; + if (rowItem.start + rowItem.duration < current.start) { + // We've completely passed the item + // Use this row for the range + *rowIt = range; + break; + } + ++rowIt; + ++level; + } + levels.append(level); + if (level > *maxlevel) + *maxlevel = level; + // remove other rows that we passed + while (rowIt != rows.end()) { + TimelineModelPrivate::Range &rowItem = d->ranges[*rowIt]; + if (rowItem.start + rowItem.duration < current.start) { + // We've completely passed the item, remove + rowIt = rows.erase(rowIt); + } else { + ++rowIt; + } + } + } + return levels; +} + int TimelineModel::collapsedRowCount() const { return d->collapsedRowCount; diff --git a/src/libs/tracing/timelinemodel.h b/src/libs/tracing/timelinemodel.h index dd89fb0c5f4..5720909d563 100644 --- a/src/libs/tracing/timelinemodel.h +++ b/src/libs/tracing/timelinemodel.h @@ -133,6 +133,7 @@ protected: int insertStart(qint64 startTime, int selectionId); void insertEnd(int index, qint64 duration); void computeNesting(); + QList computeRows(int *maxlevel) const; void setCollapsedRowCount(int rows); void setExpandedRowCount(int rows); diff --git a/src/libs/tracing/timelinerenderer.cpp b/src/libs/tracing/timelinerenderer.cpp index 07abb071445..ace3efe3ffe 100644 --- a/src/libs/tracing/timelinerenderer.cpp +++ b/src/libs/tracing/timelinerenderer.cpp @@ -183,23 +183,28 @@ void TimelineRenderer::wheelEvent(QWheelEvent *event) // ctrl-wheel means zoom if (event->modifiers() & Qt::ControlModifier) { event->setAccepted(true); + if (event->angleDelta().y() == 0) + return; TimelineZoomControl *zoom = zoomer(); - int degrees = (event->angleDelta().x() + event->angleDelta().y()) / 8; - const qint64 circle = 360; - qint64 mouseTime = event->position().toPoint().x() * zoom->windowDuration() / width() + - zoom->windowStart(); - qint64 beforeMouse = (mouseTime - zoom->rangeStart()) * (circle - degrees) / circle; - qint64 afterMouse = (zoom->rangeEnd() - mouseTime) * (circle - degrees) / circle; - - qint64 newStart = qBound(zoom->traceStart(), zoom->traceEnd(), mouseTime - beforeMouse); - if (newStart + zoom->minimumRangeLength() > zoom->traceEnd()) - return; // too close to trace end - - qint64 newEnd = qBound(newStart + zoom->minimumRangeLength(), zoom->traceEnd(), - mouseTime + afterMouse); - - zoom->setRange(newStart, newEnd); + // Handle similar to text editor, but avoid floats. + // angleDelta of 120 is considered a 10% change in zoom. + const qint64 delta = event->angleDelta().y(); + const qint64 newDuration = qBound(zoom->minimumRangeLength(), + zoom->rangeDuration() * 1200 / (1200 + delta), + std::max(zoom->minimumRangeLength(), + zoom->traceDuration())); + const qint64 mouseTime = event->position().toPoint().x() * zoom->windowDuration() / width() + + zoom->windowStart(); + // Try to keep mouseTime where it was in relation to the shown range, + // but keep within traceStart/End + const qint64 newStart + = qBound(zoom->traceStart(), + mouseTime + - newDuration * /*rest is mouse time position [0,1] in range:*/ + (mouseTime - zoom->rangeStart()) / zoom->rangeDuration(), + std::max(zoom->traceStart(), zoom->traceEnd() - newDuration)); + zoom->setRange(newStart, newStart + newDuration); } else { TimelineAbstractRenderer::wheelEvent(event); } diff --git a/src/libs/tracing/timelinezoomcontrol.cpp b/src/libs/tracing/timelinezoomcontrol.cpp index f5783d09797..7d9ee28c7e8 100644 --- a/src/libs/tracing/timelinezoomcontrol.cpp +++ b/src/libs/tracing/timelinezoomcontrol.cpp @@ -44,7 +44,7 @@ void TimelineZoomControl::clear() void TimelineZoomControl::setTrace(qint64 start, qint64 end) { - Q_ASSERT(start <= end); + QTC_ASSERT(start <= end, { std::swap(start, end); }); if (start != m_traceStart || end != m_traceEnd) { m_traceStart = start; m_traceEnd = end; @@ -55,7 +55,7 @@ void TimelineZoomControl::setTrace(qint64 start, qint64 end) void TimelineZoomControl::setRange(qint64 start, qint64 end) { - Q_ASSERT(start <= end); + QTC_ASSERT(start <= end, { std::swap(start, end); }); if (m_rangeStart != start || m_rangeEnd != end) { if (m_timer.isActive()) { m_timer.stop(); diff --git a/src/libs/tracing/tracing.qbs b/src/libs/tracing/tracing.qbs index 465a92c8ffe..6876dccd256 100644 --- a/src/libs/tracing/tracing.qbs +++ b/src/libs/tracing/tracing.qbs @@ -1,60 +1,46 @@ -import qbs 1.0 - -import QtcLibrary - -Project { +QtcLibrary { name: "Tracing" - QtcLibrary { - Depends { name: "Qt"; submodules: ["qml", "quick", "gui"] } - Depends { name: "Qt.testlib"; condition: project.withAutotests } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["qml", "quick", "gui"] } + Depends { name: "Qt.testlib"; condition: qtc.withAutotests } + Depends { name: "Utils" } - Group { - name: "General" - files: [ - "README", - "flamegraph.cpp", "flamegraph.h", - "flamegraphattached.h", - "safecastable.h", - "timelineabstractrenderer.cpp", "timelineabstractrenderer.h", - "timelineabstractrenderer_p.h", - "timelineformattime.cpp", "timelineformattime.h", - "timelineitemsrenderpass.cpp", "timelineitemsrenderpass.h", - "timelinemodel.cpp", "timelinemodel.h", "timelinemodel_p.h", - "timelinemodelaggregator.cpp", "timelinemodelaggregator.h", - "timelinenotesmodel.cpp", "timelinenotesmodel.h", "timelinenotesmodel_p.h", - "timelinenotesrenderpass.cpp", "timelinenotesrenderpass.h", - "timelineoverviewrenderer.cpp", "timelineoverviewrenderer.h", - "timelineoverviewrenderer_p.h", - "timelinerenderer.cpp", "timelinerenderer.h", "timelinerenderer_p.h", - "timelinerenderpass.cpp", "timelinerenderpass.h", - "timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h", - "timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h", - "timelinetheme.cpp", "timelinetheme.h", - "timelinetracefile.cpp", "timelinetracefile.h", - "timelinetracemanager.cpp", "timelinetracemanager.h", - "timelinezoomcontrol.cpp", "timelinezoomcontrol.h", - "traceevent.h", "traceeventtype.h", "tracestashfile.h", - "tracingtr.h", - ] - } - - Group { - name: "Qml Files" - Qt.core.resourcePrefix: "qt/qml/QtCreator/Tracing/" - fileTags: "qt.core.resource_data" - files: "qml/**" - } - - Group { - name: "Unit test utilities" - condition: project.withAutotests - files: [ - "runscenegraphtest.cpp", "runscenegraphtest.h" - ] - } - - cpp.defines: base.concat("TRACING_LIBRARY") + Group { + name: "General" + files: [ + "README", + "flamegraph.cpp", "flamegraph.h", + "flamegraphattached.h", + "safecastable.h", + "timelineabstractrenderer.cpp", "timelineabstractrenderer.h", + "timelineabstractrenderer_p.h", + "timelineformattime.cpp", "timelineformattime.h", + "timelineitemsrenderpass.cpp", "timelineitemsrenderpass.h", + "timelinemodel.cpp", "timelinemodel.h", "timelinemodel_p.h", + "timelinemodelaggregator.cpp", "timelinemodelaggregator.h", + "timelinenotesmodel.cpp", "timelinenotesmodel.h", "timelinenotesmodel_p.h", + "timelinenotesrenderpass.cpp", "timelinenotesrenderpass.h", + "timelineoverviewrenderer.cpp", "timelineoverviewrenderer.h", + "timelineoverviewrenderer_p.h", + "timelinerenderer.cpp", "timelinerenderer.h", "timelinerenderer_p.h", + "timelinerenderpass.cpp", "timelinerenderpass.h", + "timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h", + "timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h", + "timelinetheme.cpp", "timelinetheme.h", + "timelinetracefile.cpp", "timelinetracefile.h", + "timelinetracemanager.cpp", "timelinetracemanager.h", + "timelinezoomcontrol.cpp", "timelinezoomcontrol.h", + "traceevent.h", "traceeventtype.h", "tracestashfile.h", + "tracingtr.h", + ] } + + Group { + name: "Qml Files" + Qt.core.resourcePrefix: "qt/qml/QtCreator/Tracing/" + fileTags: "qt.core.resource_data" + files: "qml/**" + } + + cpp.defines: base.concat("TRACING_LIBRARY") } diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 146aeafbe5f..6a890815d07 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -1,16 +1,15 @@ add_qtc_library(Utils - DEPENDS Tasking Qt::Qml Qt::Xml + DEPENDS Tasking Qt::Qml Qt::Xml Spinner PUBLIC_DEPENDS Qt::Concurrent Qt::Core Qt::Network Qt::Gui Qt::Widgets Qt::Core5Compat SOURCES ../3rdparty/span/span.hpp ../3rdparty/tl_expected/include/tl/expected.hpp - QtConcurrentTools algorithm.h ansiescapecodehandler.cpp ansiescapecodehandler.h + appinfo.cpp appinfo.h appmainwindow.cpp appmainwindow.h - archive.cpp archive.h array.h aspects.cpp aspects.h async.cpp async.h @@ -62,11 +61,9 @@ add_qtc_library(Utils filesystemwatcher.cpp filesystemwatcher.h fileutils.cpp fileutils.h filewizardpage.cpp filewizardpage.h - fixedsizeclicklabel.cpp fixedsizeclicklabel.h flowlayout.cpp flowlayout.h fsengine/fsengine.cpp fsengine/fsengine.h fsengine/fileiconprovider.cpp fsengine/fileiconprovider.h - functiontraits.h futuresynchronizer.cpp futuresynchronizer.h fuzzymatcher.cpp fuzzymatcher.h genericconstants.h @@ -78,12 +75,12 @@ add_qtc_library(Utils hostosinfo.cpp hostosinfo.h htmldocextractor.cpp htmldocextractor.h icon.cpp icon.h + iconbutton.cpp iconbutton.h id.cpp id.h indexedcontainerproxyconstiterator.h infobar.cpp infobar.h infolabel.cpp infolabel.h itemviews.cpp itemviews.h - json.cpp json.h jsontreeitem.cpp jsontreeitem.h launcherinterface.cpp launcherinterface.h launcherpackets.cpp launcherpackets.h @@ -93,7 +90,6 @@ add_qtc_library(Utils listmodel.h listutils.h macroexpander.cpp macroexpander.h - mapreduce.h mathutils.cpp mathutils.h mimeutils.h minimizableinfobars.cpp @@ -119,8 +115,10 @@ add_qtc_library(Utils overlaywidget.cpp overlaywidget.h overridecursor.cpp overridecursor.h parameteraction.cpp parameteraction.h + passworddialog.cpp passworddialog.h pathchooser.cpp pathchooser.h pathlisteditor.cpp pathlisteditor.h + persistentcachestore.cpp persistentcachestore.h persistentsettings.cpp persistentsettings.h pointeralgorithm.h port.cpp port.h @@ -143,7 +141,6 @@ add_qtc_library(Utils ranges.h reloadpromptutils.cpp reloadpromptutils.h removefiledialog.cpp removefiledialog.h - runextensions.cpp runextensions.h savefile.cpp savefile.h scopedswap.h scopedtimer.cpp scopedtimer.h @@ -151,7 +148,6 @@ add_qtc_library(Utils set_algorithm.h settingsaccessor.cpp settingsaccessor.h settingsselector.cpp settingsselector.h - settingsutils.h singleton.cpp singleton.h sizedarray.h smallstring.h @@ -166,6 +162,8 @@ add_qtc_library(Utils sortfiltermodel.h span.h statuslabel.cpp statuslabel.h + store.cpp store.h + storekey.h stringtable.cpp stringtable.h stringutils.cpp stringutils.h styleanimator.cpp styleanimator.h @@ -190,6 +188,7 @@ add_qtc_library(Utils transientscroll.cpp transientscroll.h treemodel.cpp treemodel.h treeviewcombobox.cpp treeviewcombobox.h + unarchiver.cpp unarchiver.h uncommentselection.cpp uncommentselection.h uniqueobjectptr.h unixutils.cpp unixutils.h @@ -278,6 +277,11 @@ extend_qtc_library(Utils fsengine/filepathinfocache.h ) +extend_qtc_library(Utils + CONDITION TARGET Nanotrace + DEPENDS Nanotrace +) + if (WIN32) add_qtc_executable(qtcreator_ctrlc_stub DEPENDS user32 shell32 diff --git a/src/libs/utils/QtConcurrentTools b/src/libs/utils/QtConcurrentTools deleted file mode 100644 index bbec0a89f09..00000000000 --- a/src/libs/utils/QtConcurrentTools +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "runextensions.h" diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index 3c0a4b45c1f..dd201b647c7 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -1507,4 +1507,13 @@ void addToHash(QHash *result, const QHash &additionalContents) result->insert(additionalContents); } +// Workaround for missing information from QSet::insert() +// Return type could be a pair like for std::set, but we never use the iterator anyway. +template [[nodiscard]] bool insert(QSet &s, const U &v) +{ + const int oldSize = s.size(); + s.insert(v); + return s.size() > oldSize; +} + } // namespace Utils diff --git a/src/libs/utils/appinfo.cpp b/src/libs/utils/appinfo.cpp new file mode 100644 index 00000000000..465b3a46e46 --- /dev/null +++ b/src/libs/utils/appinfo.cpp @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "appinfo.h" + +Q_GLOBAL_STATIC(Utils::AppInfo, sAppInfo) + +namespace Utils { + +Utils::AppInfo appInfo() +{ + return *sAppInfo; +} + +void Internal::setAppInfo(const AppInfo &info) +{ + *sAppInfo = info; +} + +} // namespace Utils diff --git a/src/libs/utils/appinfo.h b/src/libs/utils/appinfo.h new file mode 100644 index 00000000000..9fd5a3f35e8 --- /dev/null +++ b/src/libs/utils/appinfo.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT AppInfo +{ +public: + QString author; + QString year; + QString displayVersion; + QString id; + QString revision; + QString revisionUrl; + QString userFileExtension; +}; + +QTCREATOR_UTILS_EXPORT AppInfo appInfo(); + +namespace Internal { +QTCREATOR_UTILS_EXPORT void setAppInfo(const AppInfo &info); +} // namespace Internal + +} // namespace Utils diff --git a/src/libs/utils/archive.h b/src/libs/utils/archive.h deleted file mode 100644 index ccf62d3885c..00000000000 --- a/src/libs/utils/archive.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include "commandline.h" - -#include - -namespace Utils { - -class FilePath; -class Process; - -class QTCREATOR_UTILS_EXPORT Archive : public QObject -{ - Q_OBJECT -public: - Archive(const FilePath &src, const FilePath &dest); - ~Archive(); - - bool isValid() const; - void unarchive(); - - static bool supportsFile(const FilePath &filePath, QString *reason = nullptr); - -signals: - void outputReceived(const QString &output); - void finished(bool success); - -private: - CommandLine m_commandLine; - FilePath m_workingDirectory; - std::unique_ptr m_process; -}; - -} // namespace Utils diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index b11e149c0d5..b14a429ed92 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -7,11 +7,15 @@ #include "checkablemessagebox.h" #include "environment.h" #include "fancylineedit.h" +#include "iconbutton.h" #include "layoutbuilder.h" +#include "passworddialog.h" #include "pathchooser.h" +#include "pathlisteditor.h" #include "qtcassert.h" #include "qtcolorbutton.h" #include "qtcsettings.h" +#include "utilsicons.h" #include "utilstr.h" #include "variablechooser.h" @@ -19,40 +23,61 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include #include -#include +#include #include +#include #include +#include using namespace Layouting; namespace Utils { -namespace Internal { -class BaseAspectPrivate +static QtcSettings *theSettings = nullptr; + +void BaseAspect::setQtcSettings(QtcSettings *settings) +{ + theSettings = settings; +} + +QtcSettings *BaseAspect::qtcSettings() +{ + return theSettings; +} + +BaseAspect::Changes::Changes() +{ + memset(this, 0, sizeof(*this)); +} + +class Internal::BaseAspectPrivate { public: + explicit BaseAspectPrivate(AspectContainer *container) : m_container(container) {} + Id m_id; - QVariant m_value; - QVariant m_defaultValue; std::function m_toSettings; std::function m_fromSettings; QString m_displayName; - QString m_settingsKey; // Name of data in settings. + Key m_settingsKey; // Name of data in settings. QString m_tooltip; QString m_labelText; QPixmap m_labelPixmap; QIcon m_icon; - QPointer m_label; // Owned by configuration widget QPointer m_action; // Owned by us. + AspectContainer *m_container = nullptr; // Not owned by us. bool m_visible = true; bool m_enabled = true; @@ -66,9 +91,9 @@ public: BaseAspect::DataCreator m_dataCreator; BaseAspect::DataCloner m_dataCloner; QList m_dataExtractors; -}; -} // Internal + QUndoStack *m_undoStack = nullptr; +}; /*! \class Utils::BaseAspect @@ -95,11 +120,11 @@ public: If \a container is non-null, the aspect is made known to the container. */ BaseAspect::BaseAspect(AspectContainer *container) - : d(new Internal::BaseAspectPrivate) + : d(new Internal::BaseAspectPrivate(container)) { if (container) container->registerAspect(this); - addDataExtractor(this, &BaseAspect::value, &Data::value); + addDataExtractor(this, &BaseAspect::variantValue, &Data::value); } /*! @@ -120,43 +145,47 @@ void BaseAspect::setId(Id id) d->m_id = id; } -QVariant BaseAspect::value() const +QVariant BaseAspect::volatileVariantValue() const { - return d->m_value; + return {}; +} + +QVariant BaseAspect::variantValue() const +{ + return {}; } /*! Sets \a value. - Emits \c changed() if the value changed. + Prefer the typed setValue() of derived classes. */ -void BaseAspect::setValue(const QVariant &value) +void BaseAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce) { - if (setValueQuietly(value)) { - emit changed(); - emitChangedValue(); - } + Q_UNUSED(value) + Q_UNUSED(howToAnnounce) + QTC_CHECK(false); +} + +void BaseAspect::setDefaultVariantValue(const QVariant &value) +{ + Q_UNUSED(value) + QTC_CHECK(false); +} + +bool BaseAspect::isDefaultValue() const +{ + return defaultVariantValue() == variantValue(); +} + +QVariant BaseAspect::defaultVariantValue() const +{ + return {}; } /*! - Sets \a value without emitting \c changed(). + \fn TypedAspect::setDefaultValue(const ValueType &value) - Returns whether the value changed. -*/ -bool BaseAspect::setValueQuietly(const QVariant &value) -{ - if (d->m_value == value) - return false; - d->m_value = value; - return true; -} - -QVariant BaseAspect::defaultValue() const -{ - return d->m_defaultValue; -} - -/*! Sets a default \a value and the current value for this aspect. \note The current value will be set silently to the same value. @@ -165,11 +194,6 @@ QVariant BaseAspect::defaultValue() const Default values will not be stored in settings. */ -void BaseAspect::setDefaultValue(const QVariant &value) -{ - d->m_defaultValue = value; - d->m_value = value; -} void BaseAspect::setDisplayName(const QString &displayName) { @@ -194,31 +218,38 @@ void BaseAspect::setVisible(bool visible) // This may happen during layout building. Explicit setting visibility here // may create a show a toplevel widget for a moment until it is parented // to some non-shown widget. - if (w->parentWidget()) + if (!visible || w->parentWidget()) w->setVisible(visible); } } -void BaseAspect::setupLabel() +QLabel *BaseAspect::createLabel() { - QTC_ASSERT(!d->m_label, delete d->m_label); if (d->m_labelText.isEmpty() && d->m_labelPixmap.isNull()) - return; - d->m_label = new QLabel(d->m_labelText); - d->m_label->setTextInteractionFlags(d->m_label->textInteractionFlags() - | Qt::TextSelectableByMouse); - connect(d->m_label, &QLabel::linkActivated, this, [this](const QString &link) { + return nullptr; + + auto label = new QLabel(d->m_labelText); + label->setTextInteractionFlags(label->textInteractionFlags() | Qt::TextSelectableByMouse); + connect(label, &QLabel::linkActivated, this, [this](const QString &link) { emit labelLinkActivated(link); }); if (!d->m_labelPixmap.isNull()) - d->m_label->setPixmap(d->m_labelPixmap); - registerSubWidget(d->m_label); + label->setPixmap(d->m_labelPixmap); + registerSubWidget(label); + + connect(this, &BaseAspect::labelTextChanged, label, [label, this] { + label->setText(d->m_labelText); + }); + connect(this, &BaseAspect::labelPixmapChanged, label, [label, this] { + label->setPixmap(d->m_labelPixmap); + }); + + return label; } void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget) { - setupLabel(); - if (QLabel *l = label()) { + if (QLabel *l = createLabel()) { l->setBuddy(widget); parent.addItem(l); parent.addItem(Span(std::max(d->m_spanX - 1, 1), LayoutItem(widget))); @@ -234,8 +265,7 @@ void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget) void BaseAspect::setLabelText(const QString &labelText) { d->m_labelText = labelText; - if (d->m_label) - d->m_label->setText(labelText); + emit labelTextChanged(); } /*! @@ -245,8 +275,7 @@ void BaseAspect::setLabelText(const QString &labelText) void BaseAspect::setLabelPixmap(const QPixmap &labelPixmap) { d->m_labelPixmap = labelPixmap; - if (d->m_label) - d->m_label->setPixmap(labelPixmap); + emit labelPixmapChanged(); } void BaseAspect::setIcon(const QIcon &icon) @@ -265,11 +294,6 @@ QString BaseAspect::labelText() const return d->m_labelText; } -QLabel *BaseAspect::label() const -{ - return d->m_label.data(); -} - QString BaseAspect::toolTip() const { return d->m_tooltip; @@ -287,6 +311,16 @@ void BaseAspect::setToolTip(const QString &tooltip) } } +void BaseAspect::setUndoStack(QUndoStack *undoStack) +{ + d->m_undoStack = undoStack; +} + +QUndoStack *BaseAspect::undoStack() const +{ + return d->m_undoStack; +} + bool BaseAspect::isEnabled() const { return d->m_enabled; @@ -294,11 +328,16 @@ bool BaseAspect::isEnabled() const void BaseAspect::setEnabled(bool enabled) { - d->m_enabled = enabled; for (QWidget *w : std::as_const(d->m_subWidgets)) { QTC_ASSERT(w, continue); w->setEnabled(enabled); } + + if (enabled == d->m_enabled) + return; + + d->m_enabled = enabled; + emit enabledChanged(); } /*! @@ -307,9 +346,16 @@ void BaseAspect::setEnabled(bool enabled) void BaseAspect::setEnabler(BoolAspect *checker) { QTC_ASSERT(checker, return); - setEnabled(checker->value()); - connect(checker, &BoolAspect::volatileValueChanged, this, &BaseAspect::setEnabled); - connect(checker, &BoolAspect::valueChanged, this, &BaseAspect::setEnabled); + + auto update = [this, checker] { + BaseAspect::setEnabled(checker->isEnabled() && checker->volatileValue()); + }; + + connect(checker, &BoolAspect::volatileValueChanged, this, update); + connect(checker, &BoolAspect::changed, this, update); + connect(checker, &BaseAspect::enabledChanged, this, update); + + update(); } bool BaseAspect::isReadOnly() const @@ -326,6 +372,8 @@ void BaseAspect::setReadOnly(bool readOnly) lineEdit->setReadOnly(readOnly); else if (auto textEdit = qobject_cast(w)) textEdit->setReadOnly(readOnly); + else if (auto pathChooser = qobject_cast(w)) + pathChooser->setReadOnly(readOnly); } } @@ -366,7 +414,7 @@ void BaseAspect::setConfigWidgetCreator(const ConfigWidgetCreator &configWidgetC \sa setSettingsKey() */ -QString BaseAspect::settingsKey() const +Key BaseAspect::settingsKey() const { return d->m_settingsKey; } @@ -376,7 +424,7 @@ QString BaseAspect::settingsKey() const \sa settingsKey() */ -void BaseAspect::setSettingsKey(const QString &key) +void BaseAspect::setSettingsKey(const Key &key) { d->m_settingsKey = key; } @@ -386,11 +434,27 @@ void BaseAspect::setSettingsKey(const QString &key) \sa settingsKey() */ -void BaseAspect::setSettingsKey(const QString &group, const QString &key) +void BaseAspect::setSettingsKey(const Key &group, const Key &key) { d->m_settingsKey = group + "/" + key; } +/*! + Immediately writes the value of this aspect into its specified + settings, taking a potential container's settings group specification + into account. + + \note This is expensive, so it should only be used with good reason. +*/ +void BaseAspect::writeToSettingsImmediatly() const +{ + QStringList groups; + if (d->m_container) + groups = d->m_container->settingsGroups(); + const SettingsGroupNester nester(groups); + writeSettings(); +} + /*! Returns the string that should be used when this action appears in menus or other places that are typically used with Book style capitalization. @@ -420,6 +484,11 @@ QAction *BaseAspect::action() return d->m_action; } +AspectContainer *BaseAspect::container() const +{ + return d->m_container; +} + /*! Adds the visual representation of this aspect to the layout with the specified \a parent using a layout builder. @@ -442,13 +511,20 @@ void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect) /*! Updates this aspect's value from user-initiated changes in the widget. - This has only an effect if \c isAutoApply is false. + Emits changed() if the value changed. */ void BaseAspect::apply() { - QTC_CHECK(!d->m_autoApply); - if (isDirty()) - setValue(volatileValue()); + // We assume m_buffer to reflect current gui state as invariant after signalling settled down. + // It's an aspect (-subclass) implementation problem if this doesn't hold. Fix it up and bark. + QTC_CHECK(!guiToBuffer()); + + if (!bufferToInternal()) // Nothing to do. + return; + + Changes changes; + changes.internalFromBuffer = true; + announceChanges(changes); } /*! @@ -459,9 +535,8 @@ void BaseAspect::apply() */ void BaseAspect::cancel() { - QTC_CHECK(!d->m_autoApply); - if (!d->m_subWidgets.isEmpty()) - setVolatileValue(d->m_value); + internalToBuffer(); + bufferToGui(); } void BaseAspect::finish() @@ -476,24 +551,24 @@ bool BaseAspect::hasAction() const return d->m_action != nullptr; } -bool BaseAspect::isDirty() const +void BaseAspect::announceChanges(Changes changes, Announcement howToAnnounce) { - QTC_CHECK(!isAutoApply()); - // Aspects that were never shown cannot contain unsaved user changes. - if (d->m_subWidgets.isEmpty()) - return false; - return volatileValue() != d->m_value; + if (howToAnnounce == BeQuiet) + return; + + if (changes.bufferFromInternal || changes.bufferFromOutside || changes.bufferFromGui) + emit volatileValueChanged(); + + if (changes.internalFromOutside || changes.internalFromBuffer) { + emit changed(); + if (hasAction()) + emit action()->triggered(variantValue().toBool()); + } } -QVariant BaseAspect::volatileValue() const +bool BaseAspect::isDirty() { - QTC_CHECK(!isAutoApply()); - return {}; -} - -void BaseAspect::setVolatileValue(const QVariant &val) -{ - Q_UNUSED(val); + return false; } void BaseAspect::registerSubWidget(QWidget *widget) @@ -518,8 +593,14 @@ void BaseAspect::registerSubWidget(QWidget *widget) widget->setVisible(d->m_visible); } -void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value, - const QVariant &defaultValue, const QString &key) +void BaseAspect::forEachSubWidget(const std::function &func) +{ + for (const QPointer &w : d->m_subWidgets) + func(w); +} + +void BaseAspect::saveToMap(Store &data, const QVariant &value, + const QVariant &defaultValue, const Key &key) { if (key.isEmpty()) return; @@ -530,38 +611,53 @@ void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value, } /*! - Retrieves the internal value of this BaseAspect from the QVariantMap \a map. + Retrieves the internal value of this BaseAspect from the Store \a map. */ -void BaseAspect::fromMap(const QVariantMap &map) +void BaseAspect::fromMap(const Store &map) { - const QVariant val = map.value(settingsKey(), toSettingsValue(defaultValue())); - setValue(fromSettingsValue(val)); + if (settingsKey().isEmpty()) + return; + const QVariant val = map.value(settingsKey(), toSettingsValue(defaultVariantValue())); + setVariantValue(fromSettingsValue(val), BeQuiet); } /*! - Stores the internal value of this BaseAspect into the QVariantMap \a map. + Stores the internal value of this BaseAspect into the Store \a map. */ -void BaseAspect::toMap(QVariantMap &map) const -{ - saveToMap(map, toSettingsValue(d->m_value), toSettingsValue(d->m_defaultValue), settingsKey()); -} - -void BaseAspect::readSettings(const QSettings *settings) +void BaseAspect::toMap(Store &map) const { if (settingsKey().isEmpty()) return; - const QVariant &val = settings->value(settingsKey()); - setValue(val.isValid() ? fromSettingsValue(val) : defaultValue()); + saveToMap(map, toSettingsValue(variantValue()), toSettingsValue(defaultVariantValue()), settingsKey()); } -void BaseAspect::writeSettings(QSettings *settings) const +void BaseAspect::volatileToMap(Store &map) const { if (settingsKey().isEmpty()) return; - QtcSettings::setValueWithDefault(settings, - settingsKey(), - toSettingsValue(value()), - toSettingsValue(defaultValue())); + saveToMap(map, + toSettingsValue(volatileVariantValue()), + toSettingsValue(defaultVariantValue()), + settingsKey()); +} + +void BaseAspect::readSettings() +{ + if (settingsKey().isEmpty()) + return; + QTC_ASSERT(theSettings, return); + const QVariant val = theSettings->value(settingsKey()); + setVariantValue(val.isValid() ? fromSettingsValue(val) : defaultVariantValue(), BeQuiet); +} + +void BaseAspect::writeSettings() const +{ + if (settingsKey().isEmpty()) + return; + QTC_ASSERT(theSettings, return); + theSettings->setValueWithDefault(settingsKey(), + toSettingsValue(variantValue()), + toSettingsValue(defaultVariantValue())); } void BaseAspect::setFromSettingsTransformation(const SavedValueTransformation &transform) @@ -590,9 +686,7 @@ class BoolAspectPrivate { public: BoolAspect::LabelPlacement m_labelPlacement = BoolAspect::LabelPlacement::AtCheckBox; - QPointer m_button; // Owned by configuration widget - QPointer m_groupBox; // For BoolAspects handling GroupBox check boxes - bool m_buttonIsAdopted = false; + UndoableValue m_undoable; }; class ColorAspectPrivate @@ -608,7 +702,7 @@ public: SelectionAspect::DisplayStyle m_displayStyle = SelectionAspect::DisplayStyle::RadioButtons; - QVector m_options; + QList m_options; // These are all owned by the configuration widget. QList> m_buttons; @@ -632,55 +726,126 @@ public: QPointer m_listView; }; -class StringAspectPrivate +class CheckableAspectImplementation { public: - StringAspect::DisplayStyle m_displayStyle = StringAspect::LabelDisplay; - StringAspect::CheckBoxPlacement m_checkBoxPlacement - = StringAspect::CheckBoxPlacement::Right; - StringAspect::UncheckedSemantics m_uncheckedSemantics - = StringAspect::UncheckedSemantics::Disabled; - std::function m_displayFilter; - std::unique_ptr m_checker; - - Qt::TextElideMode m_elideMode = Qt::ElideNone; - QString m_placeHolderText; - QString m_prompDialogFilter; - QString m_prompDialogTitle; - QStringList m_commandVersionArguments; - QString m_historyCompleterKey; - PathChooser::Kind m_expectedKind = PathChooser::File; - Environment m_environment; - QPointer m_labelDisplay; - QPointer m_lineEditDisplay; - QPointer m_pathChooserDisplay; - QPointer m_textEditDisplay; - MacroExpanderProvider m_expanderProvider; - FilePath m_baseFileName; - StringAspect::ValueAcceptor m_valueAcceptor; - FancyLineEdit::ValidationFunction m_validator; - std::function m_openTerminal; - - bool m_undoRedoEnabled = true; - bool m_acceptRichText = false; - bool m_showToolTipOnLabel = false; - bool m_fileDialogOnly = false; - bool m_useResetButton = false; - bool m_autoApplyOnEditingFinished = false; - // Used to block recursive editingFinished signals for example when return is pressed, and - // the validation changes focus by opening a dialog - bool m_blockAutoApply = false; - bool m_allowPathFromDevice = true; - bool m_validatePlaceHolder = false; - - template void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w) + void fromMap(const Store &map) { - const bool enabled = !m_checker || m_checker->value(); - if (m_uncheckedSemantics == StringAspect::UncheckedSemantics::Disabled) + if (m_checked) + m_checked->fromMap(map); + } + + void toMap(Store &map) + { + if (m_checked) + m_checked->toMap(map); + } + + void volatileToMap(Store &map) + { + if (m_checked) + m_checked->volatileToMap(map); + } + + template + void updateWidgetFromCheckStatus(BaseAspect *aspect, Widget *w) + { + const bool enabled = !m_checked || m_checked->value(); + if (m_uncheckedSemantics == UncheckedSemantics::Disabled) w->setEnabled(enabled && aspect->isEnabled()); else w->setReadOnly(!enabled || aspect->isReadOnly()); } + + void setUncheckedSemantics(UncheckedSemantics semantics) + { + m_uncheckedSemantics = semantics; + } + + bool isChecked() const + { + QTC_ASSERT(m_checked, return false); + return m_checked->value(); + } + + void setChecked(bool checked) + { + QTC_ASSERT(m_checked, return); + m_checked->setValue(checked); + } + + void makeCheckable(CheckBoxPlacement checkBoxPlacement, const QString &checkerLabel, + const Key &checkerKey, BaseAspect *aspect) + { + QTC_ASSERT(!m_checked, return); + m_checkBoxPlacement = checkBoxPlacement; + m_checked.reset(new BoolAspect); + m_checked->setLabel(checkerLabel, checkBoxPlacement == CheckBoxPlacement::Top + ? BoolAspect::LabelPlacement::InExtraLabel + : BoolAspect::LabelPlacement::AtCheckBox); + m_checked->setSettingsKey(checkerKey); + + QObject::connect(m_checked.get(), &BoolAspect::changed, aspect, [aspect] { + // FIXME: Check. + aspect->internalToBuffer(); + aspect->bufferToGui(); + emit aspect->changed(); + aspect->checkedChanged(); + }); + + QObject::connect(m_checked.get(), &BoolAspect::volatileValueChanged, aspect, [aspect] { + // FIXME: Check. + aspect->internalToBuffer(); + aspect->bufferToGui(); + aspect->checkedChanged(); + }); + + aspect->internalToBuffer(); + aspect->bufferToGui(); + } + + void addToLayoutFirst(LayoutItem &parent) + { + if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Top) { + m_checked->addToLayout(parent); + parent.addItem(br); + } + } + + void addToLayoutLast(LayoutItem &parent) + { + if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Right) + m_checked->addToLayout(parent); + } + + CheckBoxPlacement m_checkBoxPlacement = CheckBoxPlacement::Right; + UncheckedSemantics m_uncheckedSemantics = UncheckedSemantics::Disabled; + std::unique_ptr m_checked; +}; + +class StringAspectPrivate +{ +public: + StringAspect::DisplayStyle m_displayStyle = StringAspect::LabelDisplay; + std::function m_displayFilter; + + Qt::TextElideMode m_elideMode = Qt::ElideNone; + QString m_placeHolderText; + Key m_historyCompleterKey; + MacroExpanderProvider m_expanderProvider; + StringAspect::ValueAcceptor m_valueAcceptor; + std::optional m_validator; + + CheckableAspectImplementation m_checkerImpl; + + bool m_undoRedoEnabled = true; + bool m_acceptRichText = false; + bool m_showToolTipOnLabel = false; + bool m_useResetButton = false; + bool m_autoApplyOnEditingFinished = false; + bool m_validatePlaceHolder = false; + + UndoableValue undoable; }; class IntegerAspectPrivate @@ -714,6 +879,13 @@ class StringListAspectPrivate public: }; +class FilePathListAspectPrivate +{ +public: + UndoableValue undoable; + QString placeHolderText; +}; + class TextDisplayPrivate { public: @@ -747,6 +919,9 @@ public: \value PathChooserDisplay Based on Utils::PathChooser. + \value PasswordLineEditDisplay + Based on QLineEdit, used for password strings + \sa Utils::PathChooser */ @@ -773,13 +948,9 @@ public: */ StringAspect::StringAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::StringAspectPrivate) + : TypedAspect(container), d(new Internal::StringAspectPrivate) { - setDefaultValue(QString()); setSpan(2, 1); // Default: Label + something - - addDataExtractor(this, &StringAspect::value, &Data::value); - addDataExtractor(this, &StringAspect::filePath, &Data::filePath); } /*! @@ -795,103 +966,29 @@ void StringAspect::setValueAcceptor(StringAspect::ValueAcceptor &&acceptor) d->m_valueAcceptor = std::move(acceptor); } -/*! - Returns the value of this StringAspect as an ordinary \c QString. -*/ -QString StringAspect::value() const -{ - return BaseAspect::value().toString(); -} - -/*! - Sets the value, \a val, of this StringAspect from an ordinary \c QString. -*/ -void StringAspect::setValue(const QString &val) -{ - const bool isSame = val == value(); - if (isSame) - return; - - QString processedValue = val; - if (d->m_valueAcceptor) { - const std::optional tmp = d->m_valueAcceptor(value(), val); - if (!tmp) { - update(); // Make sure the original value is retained in the UI - return; - } - processedValue = tmp.value(); - } - - if (BaseAspect::setValueQuietly(QVariant(processedValue))) { - update(); - emit changed(); - emit valueChanged(processedValue); - } -} - -QString StringAspect::defaultValue() const -{ - return BaseAspect::defaultValue().toString(); -} - -void StringAspect::setDefaultValue(const QString &val) -{ - BaseAspect::setDefaultValue(val); -} - /*! \reimp */ -void StringAspect::fromMap(const QVariantMap &map) +void StringAspect::fromMap(const Store &map) { if (!settingsKey().isEmpty()) - BaseAspect::setValueQuietly(map.value(settingsKey(), defaultValue())); - if (d->m_checker) - d->m_checker->fromMap(map); + setValue(map.value(settingsKey(), defaultValue()).toString(), BeQuiet); + d->m_checkerImpl.fromMap(map); } /*! \reimp */ -void StringAspect::toMap(QVariantMap &map) const +void StringAspect::toMap(Store &map) const { saveToMap(map, value(), defaultValue(), settingsKey()); - if (d->m_checker) - d->m_checker->toMap(map); + d->m_checkerImpl.toMap(map); } -/*! - Returns the value of this string aspect as \c Utils::FilePath. - - \note This simply uses \c FilePath::fromUserInput() for the - conversion. It does not use any check that the value is actually - a valid file path. -*/ -FilePath StringAspect::filePath() const +void StringAspect::volatileToMap(Store &map) const { - return FilePath::fromUserInput(value()); -} - -/*! - Sets the value of this string aspect to \a value. - - \note This simply uses \c FilePath::toUserOutput() for the - conversion. It does not use any check that the value is actually - a file path. -*/ -void StringAspect::setFilePath(const FilePath &value) -{ - setValue(value.toUserOutput()); -} - -void StringAspect::setDefaultFilePath(const FilePath &value) -{ - setDefaultValue(value.toUserOutput()); -} - -PathChooser *StringAspect::pathChooser() const -{ - return d->m_pathChooserDisplay.data(); + saveToMap(map, volatileValue(), defaultValue(), settingsKey()); + d->m_checkerImpl.volatileToMap(map); } /*! @@ -900,7 +997,7 @@ PathChooser *StringAspect::pathChooser() const void StringAspect::setShowToolTipOnLabel(bool show) { d->m_showToolTipOnLabel = show; - update(); + bufferToGui(); } /*! @@ -912,27 +1009,6 @@ void StringAspect::setDisplayFilter(const std::functionm_displayFilter = displayFilter; } -/*! - Returns the check box value. - - \sa makeCheckable(), setChecked() -*/ -bool StringAspect::isChecked() const -{ - return !d->m_checker || d->m_checker->value(); -} - -/*! - Sets the check box of this aspect to \a checked. - - \sa makeCheckable(), isChecked() -*/ -void StringAspect::setChecked(bool checked) -{ - QTC_ASSERT(d->m_checker, return); - d->m_checker->setValue(checked); -} - /*! Selects the main display characteristics of the aspect according to \a displayStyle. @@ -951,46 +1027,11 @@ void StringAspect::setDisplayStyle(DisplayStyle displayStyle) */ void StringAspect::setPlaceHolderText(const QString &placeHolderText) { + if (d->m_placeHolderText == placeHolderText) + return; + d->m_placeHolderText = placeHolderText; - if (d->m_lineEditDisplay) - d->m_lineEditDisplay->setPlaceholderText(placeHolderText); - if (d->m_textEditDisplay) - d->m_textEditDisplay->setPlaceholderText(placeHolderText); -} - -void StringAspect::setPromptDialogFilter(const QString &filter) -{ - d->m_prompDialogFilter = filter; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setPromptDialogFilter(filter); -} - -void StringAspect::setPromptDialogTitle(const QString &title) -{ - d->m_prompDialogTitle = title; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setPromptDialogTitle(title); -} - -void StringAspect::setCommandVersionArguments(const QStringList &arguments) -{ - d->m_commandVersionArguments = arguments; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setCommandVersionArguments(arguments); -} - -void StringAspect::setAllowPathFromDevice(bool allowPathFromDevice) -{ - d->m_allowPathFromDevice = allowPathFromDevice; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setAllowPathFromDevice(allowPathFromDevice); -} - -void StringAspect::setValidatePlaceHolder(bool validatePlaceHolder) -{ - d->m_validatePlaceHolder = validatePlaceHolder; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->lineEdit()->setValidatePlaceHolder(validatePlaceHolder); + emit placeholderTextChanged(placeHolderText); } /*! @@ -998,9 +1039,10 @@ void StringAspect::setValidatePlaceHolder(bool validatePlaceHolder) */ void StringAspect::setElideMode(Qt::TextElideMode elideMode) { + if (d->m_elideMode == elideMode) + return; d->m_elideMode = elideMode; - if (d->m_labelDisplay) - d->m_labelDisplay->setElideMode(elideMode); + emit elideModeChanged(elideMode); } /*! @@ -1009,53 +1051,16 @@ void StringAspect::setElideMode(Qt::TextElideMode elideMode) \sa Utils::PathChooser::setExpectedKind() */ -void StringAspect::setHistoryCompleter(const QString &historyCompleterKey) +void StringAspect::setHistoryCompleter(const Key &historyCompleterKey) { d->m_historyCompleterKey = historyCompleterKey; - if (d->m_lineEditDisplay) - d->m_lineEditDisplay->setHistoryCompleter(historyCompleterKey); - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey); -} - -/*! - Sets \a expectedKind as expected kind for path chooser displays. - - \sa Utils::PathChooser::setExpectedKind() -*/ -void StringAspect::setExpectedKind(const PathChooser::Kind expectedKind) -{ - d->m_expectedKind = expectedKind; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setExpectedKind(expectedKind); -} - -void StringAspect::setEnvironment(const Environment &env) -{ - d->m_environment = env; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setEnvironment(env); -} - -void StringAspect::setBaseFileName(const FilePath &baseFileName) -{ - d->m_baseFileName = baseFileName; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setBaseDirectory(baseFileName); -} - -void StringAspect::setUndoRedoEnabled(bool undoRedoEnabled) -{ - d->m_undoRedoEnabled = undoRedoEnabled; - if (d->m_textEditDisplay) - d->m_textEditDisplay->setUndoRedoEnabled(undoRedoEnabled); + emit historyCompleterKeyChanged(historyCompleterKey); } void StringAspect::setAcceptRichText(bool acceptRichText) { d->m_acceptRichText = acceptRichText; - if (d->m_textEditDisplay) - d->m_textEditDisplay->setAcceptRichText(acceptRichText); + emit acceptRichTextChanged(acceptRichText); } void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider) @@ -1076,17 +1081,7 @@ void StringAspect::setUseResetButton() void StringAspect::setValidationFunction(const FancyLineEdit::ValidationFunction &validator) { d->m_validator = validator; - if (d->m_lineEditDisplay) - d->m_lineEditDisplay->setValidationFunction(d->m_validator); - else if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setValidationFunction(d->m_validator); -} - -void StringAspect::setOpenTerminalHandler(const std::function &openTerminal) -{ - d->m_openTerminal = openTerminal; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setOpenTerminalHandler(openTerminal); + emit validationFunctionChanged(validator); } void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished) @@ -1094,25 +1089,9 @@ void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished) d->m_autoApplyOnEditingFinished = applyOnEditingFinished; } -void StringAspect::validateInput() -{ - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->triggerChanged(); - if (d->m_lineEditDisplay) - d->m_lineEditDisplay->validate(); -} - -void StringAspect::setUncheckedSemantics(StringAspect::UncheckedSemantics semantics) -{ - d->m_uncheckedSemantics = semantics; -} - void StringAspect::addToLayout(LayoutItem &parent) { - if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Top) { - d->m_checker->addToLayout(parent); - parent.addItem(br); - } + d->m_checkerImpl.addToLayoutFirst(parent); const auto useMacroExpander = [this](QWidget *w) { if (!d->m_expanderProvider) @@ -1125,203 +1104,196 @@ void StringAspect::addToLayout(LayoutItem &parent) const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value(); switch (d->m_displayStyle) { - case PathChooserDisplay: - d->m_pathChooserDisplay = createSubWidget(); - d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind); + case PasswordLineEditDisplay: + case LineEditDisplay: { + auto lineEditDisplay = createSubWidget(); + lineEditDisplay->setPlaceholderText(d->m_placeHolderText); if (!d->m_historyCompleterKey.isEmpty()) - d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey); + lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey); + + connect(this, + &StringAspect::historyCompleterKeyChanged, + lineEditDisplay, + [lineEditDisplay](const Key &historyCompleterKey) { + lineEditDisplay->setHistoryCompleter(historyCompleterKey); + }); + connect(this, + &StringAspect::placeholderTextChanged, + lineEditDisplay, + &FancyLineEdit::setPlaceholderText); + if (d->m_validator) - d->m_pathChooserDisplay->setValidationFunction(d->m_validator); - d->m_pathChooserDisplay->setEnvironment(d->m_environment); - d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName); - d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal); - d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter); - d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle); - d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments); - d->m_pathChooserDisplay->setAllowPathFromDevice(d->m_allowPathFromDevice); - d->m_pathChooserDisplay->lineEdit()->setValidatePlaceHolder(d->m_validatePlaceHolder); - if (defaultValue() == value()) - d->m_pathChooserDisplay->setDefaultValue(defaultValue()); - else - d->m_pathChooserDisplay->setFilePath(FilePath::fromUserInput(displayedString)); - // do not override default value with placeholder, but use placeholder if default is empty - if (d->m_pathChooserDisplay->lineEdit()->placeholderText().isEmpty()) - d->m_pathChooserDisplay->lineEdit()->setPlaceholderText(d->m_placeHolderText); - d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data()); - addLabeledItem(parent, d->m_pathChooserDisplay); - useMacroExpander(d->m_pathChooserDisplay->lineEdit()); - if (isAutoApply()) { - if (d->m_autoApplyOnEditingFinished) { - const auto setPathChooserValue = [this] { - if (d->m_blockAutoApply) - return; - d->m_blockAutoApply = true; - setValueQuietly(d->m_pathChooserDisplay->filePath().toString()); - d->m_blockAutoApply = false; - }; - connect(d->m_pathChooserDisplay, &PathChooser::editingFinished, this, setPathChooserValue); - connect(d->m_pathChooserDisplay, &PathChooser::browsingFinished, this, setPathChooserValue); - } else { - connect(d->m_pathChooserDisplay, &PathChooser::textChanged, - this, [this](const QString &path) { - setValueQuietly(path); - }); - } - } - break; - case LineEditDisplay: - d->m_lineEditDisplay = createSubWidget(); - d->m_lineEditDisplay->setPlaceholderText(d->m_placeHolderText); - if (!d->m_historyCompleterKey.isEmpty()) - d->m_lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey); - if (d->m_validator) - d->m_lineEditDisplay->setValidationFunction(d->m_validator); - d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString); - d->m_lineEditDisplay->setReadOnly(isReadOnly()); - d->m_lineEditDisplay->setValidatePlaceHolder(d->m_validatePlaceHolder); - d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data()); - addLabeledItem(parent, d->m_lineEditDisplay); - useMacroExpander(d->m_lineEditDisplay); - if (isAutoApply()) { - if (d->m_autoApplyOnEditingFinished) { - connect(d->m_lineEditDisplay, &FancyLineEdit::editingFinished, this, [this] { - if (d->m_blockAutoApply) - return; - d->m_blockAutoApply = true; - setValue(d->m_lineEditDisplay->text()); - d->m_blockAutoApply = false; - }); - } else { - connect(d->m_lineEditDisplay, - &FancyLineEdit::textEdited, - this, - &StringAspect::setValue); - connect(d->m_lineEditDisplay, &FancyLineEdit::editingFinished, this, [this] { - setValue(d->m_lineEditDisplay->text()); - }); - } + lineEditDisplay->setValidationFunction(*d->m_validator); + lineEditDisplay->setTextKeepingActiveCursor(displayedString); + lineEditDisplay->setReadOnly(isReadOnly()); + lineEditDisplay->setValidatePlaceHolder(d->m_validatePlaceHolder); + + d->m_checkerImpl.updateWidgetFromCheckStatus(this, lineEditDisplay); + + if (d->m_checkerImpl.m_checked.get()) { + connect(d->m_checkerImpl.m_checked.get(), + &BoolAspect::volatileValueChanged, + lineEditDisplay, + [this, lineEditDisplay]() { + d->m_checkerImpl.updateWidgetFromCheckStatus(this, lineEditDisplay); + }); } + + addLabeledItem(parent, lineEditDisplay); + useMacroExpander(lineEditDisplay); if (d->m_useResetButton) { auto resetButton = createSubWidget(Tr::tr("Reset")); - resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue()); - connect(resetButton, &QPushButton::clicked, this, [this] { - d->m_lineEditDisplay->setText(defaultValue()); - }); - connect(d->m_lineEditDisplay, &QLineEdit::textChanged, this, [this, resetButton] { - resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue()); + resetButton->setEnabled(lineEditDisplay->text() != defaultValue()); + connect(resetButton, &QPushButton::clicked, lineEditDisplay, [this, lineEditDisplay] { + lineEditDisplay->setText(defaultValue()); }); + connect(lineEditDisplay, + &QLineEdit::textChanged, + resetButton, + [this, lineEditDisplay, resetButton] { + resetButton->setEnabled(lineEditDisplay->text() != defaultValue()); + }); parent.addItem(resetButton); } - break; - case TextEditDisplay: - d->m_textEditDisplay = createSubWidget(); - d->m_textEditDisplay->setPlaceholderText(d->m_placeHolderText); - d->m_textEditDisplay->setUndoRedoEnabled(d->m_undoRedoEnabled); - d->m_textEditDisplay->setAcceptRichText(d->m_acceptRichText); - d->m_textEditDisplay->setTextInteractionFlags(Qt::TextEditorInteraction); - d->m_textEditDisplay->setText(displayedString); - d->m_textEditDisplay->setReadOnly(isReadOnly()); - d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data()); - addLabeledItem(parent, d->m_textEditDisplay); - useMacroExpander(d->m_textEditDisplay); - if (isAutoApply()) { - connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] { - setValue(d->m_textEditDisplay->document()->toPlainText()); + connect(lineEditDisplay, &FancyLineEdit::validChanged, this, &StringAspect::validChanged); + bufferToGui(); + if (isAutoApply() && d->m_autoApplyOnEditingFinished) { + connect(lineEditDisplay, + &FancyLineEdit::editingFinished, + this, + [this, lineEditDisplay]() { + d->undoable.set(undoStack(), lineEditDisplay->text()); + handleGuiChanged(); + }); + } else { + connect(lineEditDisplay, &QLineEdit::textChanged, this, [this, lineEditDisplay]() { + d->undoable.set(undoStack(), lineEditDisplay->text()); + handleGuiChanged(); }); } - break; - case LabelDisplay: - d->m_labelDisplay = createSubWidget(); - d->m_labelDisplay->setElideMode(d->m_elideMode); - d->m_labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse); - d->m_labelDisplay->setText(displayedString); - d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip()); - addLabeledItem(parent, d->m_labelDisplay); + if (d->m_displayStyle == PasswordLineEditDisplay) { + auto showPasswordButton = createSubWidget(); + lineEditDisplay->setEchoMode(QLineEdit::PasswordEchoOnEdit); + parent.addItem(showPasswordButton); + connect(showPasswordButton, + &ShowPasswordButton::toggled, + lineEditDisplay, + [showPasswordButton, lineEditDisplay] { + lineEditDisplay->setEchoMode(showPasswordButton->isChecked() + ? QLineEdit::Normal + : QLineEdit::PasswordEchoOnEdit); + }); + } + + connect(&d->undoable.m_signal, + &UndoSignaller::changed, + lineEditDisplay, + [this, lineEditDisplay] { + if (lineEditDisplay->text() != d->undoable.get()) + lineEditDisplay->setTextKeepingActiveCursor(d->undoable.get()); + + lineEditDisplay->validate(); + }); + break; } + case TextEditDisplay: { + auto textEditDisplay = createSubWidget(); + textEditDisplay->setPlaceholderText(d->m_placeHolderText); + textEditDisplay->setUndoRedoEnabled(false); + textEditDisplay->setAcceptRichText(d->m_acceptRichText); + textEditDisplay->setTextInteractionFlags(Qt::TextEditorInteraction); + textEditDisplay->setText(displayedString); + textEditDisplay->setReadOnly(isReadOnly()); + d->m_checkerImpl.updateWidgetFromCheckStatus(this, textEditDisplay); - validateInput(); + if (d->m_checkerImpl.m_checked) { + connect(d->m_checkerImpl.m_checked.get(), + &BoolAspect::volatileValueChanged, + textEditDisplay, + [this, textEditDisplay]() { + d->m_checkerImpl.updateWidgetFromCheckStatus(this, textEditDisplay); + }); + } - if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Right) - d->m_checker->addToLayout(parent); + addLabeledItem(parent, textEditDisplay); + useMacroExpander(textEditDisplay); + bufferToGui(); + connect(this, + &StringAspect::acceptRichTextChanged, + textEditDisplay, + &QTextEdit::setAcceptRichText); + connect(this, + &StringAspect::placeholderTextChanged, + textEditDisplay, + &QTextEdit::setPlaceholderText); + + connect(textEditDisplay, &QTextEdit::textChanged, this, [this, textEditDisplay]() { + if (textEditDisplay->toPlainText() != d->undoable.get()) { + d->undoable.set(undoStack(), textEditDisplay->toPlainText()); + handleGuiChanged(); + } + }); + + connect(&d->undoable.m_signal, + &UndoSignaller::changed, + textEditDisplay, + [this, textEditDisplay] { + if (textEditDisplay->toPlainText() != d->undoable.get()) + textEditDisplay->setText(d->undoable.get()); + }); + break; + } + case LabelDisplay: { + auto labelDisplay = createSubWidget(); + labelDisplay->setElideMode(d->m_elideMode); + labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse); + labelDisplay->setText(displayedString); + labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip()); + connect(this, &StringAspect::elideModeChanged, labelDisplay, &ElidingLabel::setElideMode); + addLabeledItem(parent, labelDisplay); + + connect(&d->undoable.m_signal, &UndoSignaller::changed, labelDisplay, [this, labelDisplay] { + labelDisplay->setText(d->undoable.get()); + labelDisplay->setToolTip(d->m_showToolTipOnLabel ? d->undoable.get() : toolTip()); + }); + + break; + } + } + + d->m_checkerImpl.addToLayoutLast(parent); } -QVariant StringAspect::volatileValue() const +QString StringAspect::expandedValue() const { - switch (d->m_displayStyle) { - case PathChooserDisplay: - QTC_ASSERT(d->m_pathChooserDisplay, return {}); - if (d->m_pathChooserDisplay->filePath().isEmpty()) - return defaultValue(); - return d->m_pathChooserDisplay->filePath().toString(); - case LineEditDisplay: - QTC_ASSERT(d->m_lineEditDisplay, return {}); - if (d->m_lineEditDisplay->text().isEmpty()) - return defaultValue(); - return d->m_lineEditDisplay->text(); - case TextEditDisplay: - QTC_ASSERT(d->m_textEditDisplay, return {}); - if (d->m_textEditDisplay->document()->isEmpty()) - return defaultValue(); - return d->m_textEditDisplay->document()->toPlainText(); - case LabelDisplay: - break; - } - return {}; + // FIXME: Use macroexpander here later. + return m_internal; } -void StringAspect::setVolatileValue(const QVariant &val) +bool StringAspect::guiToBuffer() { - switch (d->m_displayStyle) { - case PathChooserDisplay: - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setFilePath(FilePath::fromVariant(val)); - break; - case LineEditDisplay: - if (d->m_lineEditDisplay) - d->m_lineEditDisplay->setText(val.toString()); - break; - case TextEditDisplay: - if (d->m_textEditDisplay) - d->m_textEditDisplay->document()->setPlainText(val.toString()); - break; - case LabelDisplay: - break; - } + return updateStorage(m_buffer, d->undoable.get()); } -void StringAspect::emitChangedValue() +bool StringAspect::bufferToInternal() { - emit valueChanged(value()); + if (d->m_valueAcceptor) { + if (const std::optional tmp = d->m_valueAcceptor(m_internal, m_buffer)) + return updateStorage(m_internal, *tmp); + } + return updateStorage(m_internal, m_buffer); } -void StringAspect::update() +bool StringAspect::internalToBuffer() { - const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value(); + const QString val = d->m_displayFilter ? d->m_displayFilter(m_internal) : m_internal; + return updateStorage(m_buffer, val); +} - if (d->m_pathChooserDisplay) { - d->m_pathChooserDisplay->setFilePath(FilePath::fromUserInput(displayedString)); - d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data()); - } - - if (d->m_lineEditDisplay) { - d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString); - d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data()); - } - - if (d->m_textEditDisplay) { - const QString old = d->m_textEditDisplay->document()->toPlainText(); - if (displayedString != old) - d->m_textEditDisplay->setText(displayedString); - d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data()); - } - - if (d->m_labelDisplay) { - d->m_labelDisplay->setText(displayedString); - d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip()); - } - - validateInput(); +void StringAspect::bufferToGui() +{ + d->undoable.setWithoutUndo(m_buffer); } /*! @@ -1332,21 +1304,19 @@ void StringAspect::update() \a checkerKey. */ void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement, - const QString &checkerLabel, const QString &checkerKey) + const QString &checkerLabel, const Key &checkerKey) { - QTC_ASSERT(!d->m_checker, return); - d->m_checkBoxPlacement = checkBoxPlacement; - d->m_checker.reset(new BoolAspect); - d->m_checker->setLabel(checkerLabel, checkBoxPlacement == CheckBoxPlacement::Top - ? BoolAspect::LabelPlacement::InExtraLabel - : BoolAspect::LabelPlacement::AtCheckBox); - d->m_checker->setSettingsKey(checkerKey); + d->m_checkerImpl.makeCheckable(checkBoxPlacement, checkerLabel, checkerKey, this); +} - connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::update); - connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::changed); - connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::checkedChanged); +bool StringAspect::isChecked() const +{ + return d->m_checkerImpl.isChecked(); +} - update(); +void StringAspect::setChecked(bool checked) +{ + return d->m_checkerImpl.setChecked(checked); } @@ -1365,11 +1335,348 @@ void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement, \sa Utils::StringAspect */ +class Internal::FilePathAspectPrivate +{ +public: + std::function m_displayFilter; + + QString m_placeHolderText; + QString m_prompDialogFilter; + QString m_prompDialogTitle; + QStringList m_commandVersionArguments; + Key m_historyCompleterKey; + PathChooser::Kind m_expectedKind = PathChooser::File; + Environment m_environment; + QPointer m_pathChooserDisplay; + MacroExpanderProvider m_expanderProvider; + FilePath m_baseFileName; + StringAspect::ValueAcceptor m_valueAcceptor; + std::optional m_validator; + std::function m_openTerminal; + + CheckableAspectImplementation m_checkerImpl; + + bool m_showToolTipOnLabel = false; + bool m_fileDialogOnly = false; + bool m_autoApplyOnEditingFinished = false; + bool m_allowPathFromDevice = true; + bool m_validatePlaceHolder = false; +}; FilePathAspect::FilePathAspect(AspectContainer *container) - : StringAspect(container) + : TypedAspect(container), d(new Internal::FilePathAspectPrivate) { - setDisplayStyle(PathChooserDisplay); + setSpan(2, 1); // Default: Label + something + + addDataExtractor(this, &FilePathAspect::value, &Data::value); + addDataExtractor(this, &FilePathAspect::operator(), &Data::filePath); +} + +FilePathAspect::~FilePathAspect() = default; + +/*! + Returns the value of this aspect as \c Utils::FilePath. + + \note This simply uses \c FilePath::fromUserInput() for the + conversion. It does not use any check that the value is actually + a valid file path. +*/ + +FilePath FilePathAspect::operator()() const +{ + return FilePath::fromUserInput(TypedAspect::value()); +} + +FilePath FilePathAspect::expandedValue() const +{ + return FilePath::fromUserInput(TypedAspect::value()); +} + +QString FilePathAspect::value() const +{ + return TypedAspect::value(); +} + +/*! + Sets the value of this file path aspect to \a value. + + \note This does not use any check that the value is actually + a file path. +*/ + +void FilePathAspect::setValue(const FilePath &filePath, Announcement howToAnnounce) +{ + TypedAspect::setValue(filePath.toUserOutput(), howToAnnounce); +} + +void FilePathAspect::setValue(const QString &filePath, Announcement howToAnnounce) +{ + TypedAspect::setValue(filePath, howToAnnounce); +} + +void FilePathAspect::setDefaultValue(const QString &filePath) +{ + TypedAspect::setDefaultValue(filePath); +} + +/*! + Adds a check box with a \a checkerLabel according to \a checkBoxPlacement + to the line edit. + + The state of the check box is made persistent when using a non-emtpy + \a checkerKey. +*/ +void FilePathAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement, + const QString &checkerLabel, + const Key &checkerKey) +{ + d->m_checkerImpl.makeCheckable(checkBoxPlacement, checkerLabel, checkerKey, this); +} + +bool FilePathAspect::isChecked() const +{ + return d->m_checkerImpl.isChecked(); +} + +void FilePathAspect::setChecked(bool checked) +{ + return d->m_checkerImpl.setChecked(checked); +} + +void FilePathAspect::setValueAcceptor(ValueAcceptor &&acceptor) +{ + d->m_valueAcceptor = std::move(acceptor); +} + +bool FilePathAspect::guiToBuffer() +{ + if (d->m_pathChooserDisplay) + return updateStorage(m_buffer, d->m_pathChooserDisplay->lineEdit()->text()); + return false; +} + +bool FilePathAspect::bufferToInternal() +{ + if (d->m_valueAcceptor) { + if (const std::optional tmp = d->m_valueAcceptor(m_internal, m_buffer)) + return updateStorage(m_internal, *tmp); + } + return updateStorage(m_internal, m_buffer); +} + +bool FilePathAspect::internalToBuffer() +{ + const QString val = d->m_displayFilter ? d->m_displayFilter(m_internal) : m_internal; + return updateStorage(m_buffer, val); +} + +void FilePathAspect::bufferToGui() +{ + if (d->m_pathChooserDisplay) { + d->m_pathChooserDisplay->lineEdit()->setText(m_buffer); + d->m_checkerImpl.updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data()); + } + + validateInput(); +} + +PathChooser *FilePathAspect::pathChooser() const +{ + return d->m_pathChooserDisplay.data(); +} + +void FilePathAspect::addToLayout(Layouting::LayoutItem &parent) +{ + d->m_checkerImpl.addToLayoutFirst(parent); + + const auto useMacroExpander = [this](QWidget *w) { + if (!d->m_expanderProvider) + return; + const auto chooser = new VariableChooser(w); + chooser->addSupportedWidget(w); + chooser->addMacroExpanderProvider(d->m_expanderProvider); + }; + + const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value(); + + d->m_pathChooserDisplay = createSubWidget(); + d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind); + if (!d->m_historyCompleterKey.isEmpty()) + d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey); + + if (d->m_validator) + d->m_pathChooserDisplay->setValidationFunction(*d->m_validator); + d->m_pathChooserDisplay->setEnvironment(d->m_environment); + d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName); + d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal); + d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter); + d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle); + d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments); + d->m_pathChooserDisplay->setAllowPathFromDevice(d->m_allowPathFromDevice); + d->m_pathChooserDisplay->setReadOnly(isReadOnly()); + d->m_pathChooserDisplay->lineEdit()->setValidatePlaceHolder(d->m_validatePlaceHolder); + if (defaultValue() == value()) + d->m_pathChooserDisplay->setDefaultValue(defaultValue()); + else + d->m_pathChooserDisplay->setFilePath(FilePath::fromUserInput(displayedString)); + // do not override default value with placeholder, but use placeholder if default is empty + if (d->m_pathChooserDisplay->lineEdit()->placeholderText().isEmpty()) + d->m_pathChooserDisplay->lineEdit()->setPlaceholderText(d->m_placeHolderText); + d->m_checkerImpl.updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data()); + addLabeledItem(parent, d->m_pathChooserDisplay); + useMacroExpander(d->m_pathChooserDisplay->lineEdit()); + connect(d->m_pathChooserDisplay, &PathChooser::validChanged, this, &FilePathAspect::validChanged); + bufferToGui(); + if (isAutoApply() && d->m_autoApplyOnEditingFinished) { + connect(d->m_pathChooserDisplay, &PathChooser::editingFinished, + this, &FilePathAspect::handleGuiChanged); + connect(d->m_pathChooserDisplay, &PathChooser::browsingFinished, + this, &FilePathAspect::handleGuiChanged); + } else { + connect(d->m_pathChooserDisplay, &PathChooser::textChanged, + this, &FilePathAspect::handleGuiChanged); + } + + d->m_checkerImpl.addToLayoutLast(parent); +} + +/*! + \reimp +*/ +void FilePathAspect::fromMap(const Store &map) +{ + if (!settingsKey().isEmpty()) + setValue(map.value(settingsKey(), defaultValue()).toString(), BeQuiet); + d->m_checkerImpl.fromMap(map); +} + +/*! + \reimp +*/ +void FilePathAspect::toMap(Store &map) const +{ + saveToMap(map, value(), defaultValue(), settingsKey()); + d->m_checkerImpl.toMap(map); +} + +void FilePathAspect::volatileToMap(Store &map) const +{ + saveToMap(map, volatileValue(), defaultValue(), settingsKey()); + d->m_checkerImpl.volatileToMap(map); +} + +void FilePathAspect::setPromptDialogFilter(const QString &filter) +{ + d->m_prompDialogFilter = filter; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setPromptDialogFilter(filter); +} + +void FilePathAspect::setPromptDialogTitle(const QString &title) +{ + d->m_prompDialogTitle = title; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setPromptDialogTitle(title); +} + +void FilePathAspect::setCommandVersionArguments(const QStringList &arguments) +{ + d->m_commandVersionArguments = arguments; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setCommandVersionArguments(arguments); +} + +void FilePathAspect::setAllowPathFromDevice(bool allowPathFromDevice) +{ + d->m_allowPathFromDevice = allowPathFromDevice; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setAllowPathFromDevice(allowPathFromDevice); +} + +void FilePathAspect::setValidatePlaceHolder(bool validatePlaceHolder) +{ + d->m_validatePlaceHolder = validatePlaceHolder; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->lineEdit()->setValidatePlaceHolder(validatePlaceHolder); +} + +void FilePathAspect::setShowToolTipOnLabel(bool show) +{ + d->m_showToolTipOnLabel = show; + bufferToGui(); +} + +void FilePathAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished) +{ + d->m_autoApplyOnEditingFinished = applyOnEditingFinished; +} + +/*! + Sets \a expectedKind as expected kind for path chooser displays. + + \sa Utils::PathChooser::setExpectedKind() +*/ +void FilePathAspect::setExpectedKind(const PathChooser::Kind expectedKind) +{ + d->m_expectedKind = expectedKind; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setExpectedKind(expectedKind); +} + +void FilePathAspect::setEnvironment(const Environment &env) +{ + d->m_environment = env; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setEnvironment(env); +} + +void FilePathAspect::setBaseFileName(const FilePath &baseFileName) +{ + d->m_baseFileName = baseFileName; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setBaseDirectory(baseFileName); +} + +void FilePathAspect::setPlaceHolderText(const QString &placeHolderText) +{ + d->m_placeHolderText = placeHolderText; +} + +void FilePathAspect::setValidationFunction(const FancyLineEdit::ValidationFunction &validator) +{ + d->m_validator = validator; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setValidationFunction(*d->m_validator); +} + +void FilePathAspect::setDisplayFilter(const std::function &displayFilter) +{ + d->m_displayFilter = displayFilter; +} + +void FilePathAspect::setHistoryCompleter(const Key &historyCompleterKey) +{ + d->m_historyCompleterKey = historyCompleterKey; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey); +} + +void FilePathAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider) +{ + d->m_expanderProvider = expanderProvider; +} + +void FilePathAspect::validateInput() +{ + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->triggerChanged(); +} + +void FilePathAspect::setOpenTerminalHandler(const std::function &openTerminal) +{ + d->m_openTerminal = openTerminal; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setOpenTerminalHandler(openTerminal); } /*! @@ -1384,12 +1691,10 @@ FilePathAspect::FilePathAspect(AspectContainer *container) */ ColorAspect::ColorAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::ColorAspectPrivate) + : TypedAspect(container), d(new Internal::ColorAspectPrivate) { setDefaultValue(QColor::fromRgb(0, 0, 0)); setSpan(1, 1); - - addDataExtractor(this, &ColorAspect::value, &Data::value); } ColorAspect::~ColorAspect() = default; @@ -1399,40 +1704,23 @@ void ColorAspect::addToLayout(Layouting::LayoutItem &parent) QTC_CHECK(!d->m_colorButton); d->m_colorButton = createSubWidget(); parent.addItem(d->m_colorButton.data()); - d->m_colorButton->setColor(value()); - if (isAutoApply()) { - connect(d->m_colorButton.data(), - &QtColorButton::colorChanged, - this, - [this](const QColor &color) { setValue(color); }); - } + + bufferToGui(); + connect(d->m_colorButton.data(), &QtColorButton::colorChanged, + this, &ColorAspect::handleGuiChanged); } -QColor ColorAspect::value() const +bool ColorAspect::guiToBuffer() { - return BaseAspect::value().value(); -} - -void ColorAspect::setValue(const QColor &value) -{ - if (BaseAspect::setValueQuietly(value)) - emit changed(); -} - -QVariant ColorAspect::volatileValue() const -{ - QTC_CHECK(!isAutoApply()); if (d->m_colorButton) - return d->m_colorButton->color(); - QTC_CHECK(false); - return {}; + return updateStorage(m_buffer, d->m_colorButton->color()); + return false; } -void ColorAspect::setVolatileValue(const QVariant &val) +void ColorAspect::bufferToGui() { - QTC_CHECK(!isAutoApply()); if (d->m_colorButton) - d->m_colorButton->setColor(val.value()); + d->m_colorButton->setColor(m_buffer); } /*! @@ -1451,12 +1739,12 @@ void ColorAspect::setVolatileValue(const QVariant &val) BoolAspect::BoolAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::BoolAspectPrivate) + : TypedAspect(container), d(new Internal::BoolAspectPrivate) { setDefaultValue(false); setSpan(2, 1); - addDataExtractor(this, &BoolAspect::value, &Data::value); + d->m_undoable.setSilently(false); } /*! @@ -1464,45 +1752,53 @@ BoolAspect::BoolAspect(AspectContainer *container) */ BoolAspect::~BoolAspect() = default; +void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button) +{ + switch (d->m_labelPlacement) { + case LabelPlacement::Compact: + button->setText(labelText()); + parent.addItem(button); + break; + case LabelPlacement::AtCheckBox: + button->setText(labelText()); + parent.addItem(empty()); + parent.addItem(button); + break; + case LabelPlacement::InExtraLabel: + addLabeledItem(parent, button); + break; + } + + connect(button, &QAbstractButton::clicked, this, [button, this] { + d->m_undoable.set(undoStack(), button->isChecked()); + }); + + connect(&d->m_undoable.m_signal, &UndoSignaller::changed, button, [button, this] { + button->setChecked(d->m_undoable.get()); + handleGuiChanged(); + }); +} + +LayoutItem BoolAspect::adoptButton(QAbstractButton *button) +{ + LayoutItem parent; + + addToLayoutHelper(parent, button); + + bufferToGui(); + return parent; +} + /*! \reimp */ void BoolAspect::addToLayout(Layouting::LayoutItem &parent) { - if (!d->m_buttonIsAdopted) { - QTC_CHECK(!d->m_button); - d->m_button = createSubWidget(); - } - switch (d->m_labelPlacement) { - case LabelPlacement::AtCheckBoxWithoutDummyLabel: - d->m_button->setText(labelText()); - parent.addItem(d->m_button.data()); - break; - case LabelPlacement::AtCheckBox: - d->m_button->setText(labelText()); - parent.addItem(empty()); - parent.addItem(d->m_button.data()); - break; - case LabelPlacement::InExtraLabel: - addLabeledItem(parent, d->m_button); - break; - } - d->m_button->setChecked(value()); - if (isAutoApply()) { - connect(d->m_button.data(), &QAbstractButton::clicked, - this, [this](bool val) { setValue(val); }); - } - connect(d->m_button.data(), &QAbstractButton::clicked, - this, &BoolAspect::volatileValueChanged); -} + QTC_ASSERT(m_buffer == m_internal, m_buffer = m_internal); -void BoolAspect::adoptButton(QAbstractButton *button) -{ - QTC_ASSERT(button, return); - QTC_CHECK(!d->m_button); - d->m_button = button; - d->m_buttonIsAdopted = true; - registerSubWidget(button); + QCheckBox *checkBox = createSubWidget(); + addToLayoutHelper(parent, checkBox); + bufferToGui(); } std::function BoolAspect::groupChecker() @@ -1513,92 +1809,46 @@ std::function BoolAspect::groupChecker() registerSubWidget(groupBox); groupBox->setCheckable(true); groupBox->setChecked(value()); - d->m_groupBox = groupBox; + + connect(groupBox, &QGroupBox::clicked, this, [groupBox, this] { + d->m_undoable.set(undoStack(), groupBox->isChecked()); + }); + + connect(&d->m_undoable.m_signal, &UndoSignaller::changed, groupBox, [groupBox, this] { + groupBox->setChecked(d->m_undoable.get()); + handleGuiChanged(); + }); + bufferToGui(); }; } QAction *BoolAspect::action() { if (hasAction()) - return BaseAspect::action(); - auto act = BaseAspect::action(); // Creates it. + return TypedAspect::action(); + auto act = TypedAspect::action(); // Creates it. act->setCheckable(true); - act->setChecked(value()); + act->setChecked(m_internal); act->setToolTip(toolTip()); connect(act, &QAction::triggered, this, [this](bool newValue) { - // The check would be nice to have in simple conditions, but if we - // have an action that's used both on a settings page and as action - // in a menu like "Use FakeVim", isAutoApply() is false, and yet this - // here can trigger. - //QTC_CHECK(isAutoApply()); setValue(newValue); }); return act; } -QVariant BoolAspect::volatileValue() const +bool BoolAspect::guiToBuffer() { - if (d->m_button) - return d->m_button->isChecked(); - if (d->m_groupBox) - return d->m_groupBox->isChecked(); - QTC_CHECK(false); - return {}; + return updateStorage(m_buffer, d->m_undoable.get()); } -void BoolAspect::setVolatileValue(const QVariant &val) +void BoolAspect::bufferToGui() { - if (d->m_button) - d->m_button->setChecked(val.toBool()); - else if (d->m_groupBox) - d->m_groupBox->setChecked(val.toBool()); -} - -void BoolAspect::emitChangedValue() -{ - emit valueChanged(value()); -} - - -/*! - Returns the value of the boolean aspect as a boolean value. -*/ - -bool BoolAspect::value() const -{ - return BaseAspect::value().toBool(); -} - -void BoolAspect::setValue(bool value) -{ - if (BaseAspect::setValueQuietly(value)) { - if (d->m_button) - d->m_button->setChecked(value); - //qDebug() << "SetValue: Changing" << labelText() << " to " << value; - emit changed(); - //QTC_CHECK(!labelText().isEmpty()); - emit valueChanged(value); - //qDebug() << "SetValue: Changed" << labelText() << " to " << value; - if (hasAction()) { - //qDebug() << "SetValue: Triggering " << labelText() << "with" << value; - emit action()->triggered(value); - } - } -} - -bool BoolAspect::defaultValue() const -{ - return BaseAspect::defaultValue().toBool(); -} - -void BoolAspect::setDefaultValue(bool val) -{ - BaseAspect::setDefaultValue(val); + d->m_undoable.setWithoutUndo(m_buffer); } void BoolAspect::setLabel(const QString &labelText, LabelPlacement labelPlacement) { - BaseAspect::setLabelText(labelText); + TypedAspect::setLabelText(labelText); d->m_labelPlacement = labelPlacement; } @@ -1635,7 +1885,7 @@ CheckableDecider BoolAspect::doNotAskAgainCheckableDecider() */ SelectionAspect::SelectionAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::SelectionAspectPrivate) + : TypedAspect(container), d(new Internal::SelectionAspectPrivate) { setSpan(2, 1); } @@ -1653,6 +1903,7 @@ void SelectionAspect::addToLayout(Layouting::LayoutItem &parent) QTC_CHECK(d->m_buttonGroup == nullptr); QTC_CHECK(!d->m_comboBox); QTC_ASSERT(d->m_buttons.isEmpty(), d->m_buttons.clear()); + QTC_ASSERT(m_buffer == m_internal, m_buffer = m_internal); switch (d->m_displayStyle) { case DisplayStyle::RadioButtons: @@ -1667,59 +1918,50 @@ void SelectionAspect::addToLayout(Layouting::LayoutItem &parent) parent.addItem(button); d->m_buttons.append(button); d->m_buttonGroup->addButton(button, i); - if (isAutoApply()) { - connect(button, &QAbstractButton::clicked, this, [this, i] { - setValue(i); - }); - } } + bufferToGui(); + connect(d->m_buttonGroup, &QButtonGroup::idClicked, + this, &SelectionAspect::handleGuiChanged); break; case DisplayStyle::ComboBox: setLabelText(displayName()); d->m_comboBox = createSubWidget(); for (int i = 0, n = d->m_options.size(); i < n; ++i) d->m_comboBox->addItem(d->m_options.at(i).displayName); - if (isAutoApply()) { - connect(d->m_comboBox.data(), &QComboBox::activated, - this, &SelectionAspect::setValue); - } - connect(d->m_comboBox.data(), &QComboBox::currentIndexChanged, - this, &SelectionAspect::volatileValueChanged); d->m_comboBox->setCurrentIndex(value()); addLabeledItem(parent, d->m_comboBox); + bufferToGui(); + connect(d->m_comboBox.data(), &QComboBox::activated, + this, &SelectionAspect::handleGuiChanged); break; } } -QVariant SelectionAspect::volatileValue() const +bool SelectionAspect::guiToBuffer() { + const int old = m_buffer; switch (d->m_displayStyle) { case DisplayStyle::RadioButtons: - QTC_ASSERT(d->m_buttonGroup, return {}); - return d->m_buttonGroup->checkedId(); - case DisplayStyle::ComboBox: - QTC_ASSERT(d->m_comboBox, return {}); - return d->m_comboBox->currentIndex(); - } - return {}; -} - -void SelectionAspect::setVolatileValue(const QVariant &val) -{ - switch (d->m_displayStyle) { - case DisplayStyle::RadioButtons: { - if (d->m_buttonGroup) { - QAbstractButton *button = d->m_buttonGroup->button(val.toInt()); - QTC_ASSERT(button, return); - button->setChecked(true); - } + if (d->m_buttonGroup) + m_buffer = d->m_buttonGroup->checkedId(); break; - } case DisplayStyle::ComboBox: if (d->m_comboBox) - d->m_comboBox->setCurrentIndex(val.toInt()); + m_buffer = d->m_comboBox->currentIndex(); break; } + return m_buffer != old; +} + +void SelectionAspect::bufferToGui() +{ + if (d->m_buttonGroup) { + QAbstractButton *button = d->m_buttonGroup->button(m_buffer); + QTC_ASSERT(button, return); + button->setChecked(true); + } else if (d->m_comboBox) { + d->m_comboBox->setCurrentIndex(m_buffer); + } } void SelectionAspect::finish() @@ -1735,22 +1977,6 @@ void SelectionAspect::setDisplayStyle(SelectionAspect::DisplayStyle style) d->m_displayStyle = style; } -int SelectionAspect::value() const -{ - return BaseAspect::value().toInt(); -} - -void SelectionAspect::setValue(int value) -{ - if (BaseAspect::setValueQuietly(value)) { - if (d->m_buttonGroup && 0 <= value && value < d->m_buttons.size()) - d->m_buttons.at(value)->setChecked(true); - else if (d->m_comboBox) - d->m_comboBox->setCurrentIndex(value); - emit changed(); - } -} - void SelectionAspect::setStringValue(const QString &val) { const int index = indexForDisplay(val); @@ -1758,20 +1984,15 @@ void SelectionAspect::setStringValue(const QString &val) setValue(index); } -int SelectionAspect::defaultValue() const -{ - return BaseAspect::defaultValue().toInt(); -} - void SelectionAspect::setDefaultValue(int val) { - BaseAspect::setDefaultValue(val); + TypedAspect::setDefaultValue(val); } // Note: This needs to be set after all options are added. void SelectionAspect::setDefaultValue(const QString &val) { - BaseAspect::setDefaultValue(indexForDisplay(val)); + TypedAspect::setDefaultValue(indexForDisplay(val)); } QString SelectionAspect::stringValue() const @@ -1838,7 +2059,7 @@ QVariant SelectionAspect::itemValueForIndex(int index) const */ MultiSelectionAspect::MultiSelectionAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::MultiSelectionAspectPrivate(this)) + : TypedAspect(container), d(new Internal::MultiSelectionAspectPrivate(this)) { setDefaultValue(QStringList()); setSpan(2, 1); @@ -1861,17 +2082,13 @@ void MultiSelectionAspect::addToLayout(LayoutItem &builder) switch (d->m_displayStyle) { case DisplayStyle::ListView: d->m_listView = createSubWidget(); - for (const QString &val : std::as_const(d->m_allValues)) { - auto item = new QListWidgetItem(val, d->m_listView); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable); - item->setCheckState(value().contains(item->text()) ? Qt::Checked : Qt::Unchecked); - } - connect(d->m_listView, &QListWidget::itemChanged, this, - [this](QListWidgetItem *item) { - if (d->setValueSelectedHelper(item->text(), item->checkState() & Qt::Checked)) - emit changed(); - }); + for (const QString &val : std::as_const(d->m_allValues)) + (void) new QListWidgetItem(val, d->m_listView); addLabeledItem(builder, d->m_listView); + + bufferToGui(); + connect(d->m_listView, &QListWidget::itemChanged, + this, &MultiSelectionAspect::handleGuiChanged); } } @@ -1906,27 +2123,34 @@ void MultiSelectionAspect::setDisplayStyle(MultiSelectionAspect::DisplayStyle st d->m_displayStyle = style; } -QStringList MultiSelectionAspect::value() const +void MultiSelectionAspect::bufferToGui() { - return BaseAspect::value().toStringList(); -} - -void MultiSelectionAspect::setValue(const QStringList &value) -{ - if (BaseAspect::setValueQuietly(value)) { - if (d->m_listView) { - const int n = d->m_listView->count(); - QTC_CHECK(n == d->m_allValues.size()); - for (int i = 0; i != n; ++i) { - auto item = d->m_listView->item(i); - item->setCheckState(value.contains(item->text()) ? Qt::Checked : Qt::Unchecked); - } - } else { - emit changed(); + if (d->m_listView) { + const int n = d->m_listView->count(); + QTC_CHECK(n == d->m_allValues.size()); + for (int i = 0; i != n; ++i) { + auto item = d->m_listView->item(i); + item->setCheckState(m_buffer.contains(item->text()) ? Qt::Checked : Qt::Unchecked); } } } +bool MultiSelectionAspect::guiToBuffer() +{ + if (d->m_listView) { + QStringList val; + const int n = d->m_listView->count(); + QTC_CHECK(n == d->m_allValues.size()); + for (int i = 0; i != n; ++i) { + auto item = d->m_listView->item(i); + if (item->checkState() == Qt::Checked) + val.append(item->text()); + } + return updateStorage(m_buffer, val); + } + return false; +} + /*! \class Utils::IntegerAspect @@ -1945,12 +2169,9 @@ void MultiSelectionAspect::setValue(const QStringList &value) // IntegerAspect IntegerAspect::IntegerAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::IntegerAspectPrivate) + : TypedAspect(container), d(new Internal::IntegerAspectPrivate) { - setDefaultValue(qint64(0)); setSpan(2, 1); - - addDataExtractor(this, &IntegerAspect::value, &Data::value); } /*! @@ -1975,48 +2196,21 @@ void IntegerAspect::addToLayout(Layouting::LayoutItem &parent) int(d->m_maximumValue.value() / d->m_displayScaleFactor)); d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange() addLabeledItem(parent, d->m_spinBox); - - if (isAutoApply()) { - connect(d->m_spinBox.data(), &QSpinBox::valueChanged, - this, [this] { setValue(d->m_spinBox->value() * d->m_displayScaleFactor); }); - } + connect(d->m_spinBox.data(), &QSpinBox::valueChanged, + this, &IntegerAspect::handleGuiChanged); } -QVariant IntegerAspect::volatileValue() const +bool IntegerAspect::guiToBuffer() { - QTC_CHECK(!isAutoApply()); - QTC_ASSERT(d->m_spinBox, return {}); - return d->m_spinBox->value() * d->m_displayScaleFactor; -} - -void IntegerAspect::setVolatileValue(const QVariant &val) -{ - QTC_CHECK(!isAutoApply()); if (d->m_spinBox) - d->m_spinBox->setValue(int(val.toLongLong() / d->m_displayScaleFactor)); + return updateStorage(m_buffer, d->m_spinBox->value() * d->m_displayScaleFactor); + return false; } -qint64 IntegerAspect::value() const +void IntegerAspect::bufferToGui() { - return BaseAspect::value().toLongLong(); -} - -void IntegerAspect::setValue(qint64 value) -{ - if (BaseAspect::setValueQuietly(value)) { - if (d->m_spinBox) - d->m_spinBox->setValue(value / d->m_displayScaleFactor); - //qDebug() << "SetValue: Changing" << labelText() << " to " << value; - emit changed(); - //QTC_CHECK(!labelText().isEmpty()); - emit valueChanged(value); - //qDebug() << "SetValue: Changed" << labelText() << " to " << value; - } -} - -qint64 IntegerAspect::defaultValue() const -{ - return BaseAspect::defaultValue().toLongLong(); + if (d->m_spinBox) + d->m_spinBox->setValue(m_buffer / d->m_displayScaleFactor); } void IntegerAspect::setRange(qint64 min, qint64 max) @@ -2050,11 +2244,6 @@ void IntegerAspect::setDisplayScaleFactor(qint64 factor) d->m_displayScaleFactor = factor; } -void IntegerAspect::setDefaultValue(qint64 defaultValue) -{ - BaseAspect::setDefaultValue(defaultValue); -} - void IntegerAspect::setSpecialValueText(const QString &specialText) { d->m_specialValueText = specialText; @@ -2081,7 +2270,7 @@ void IntegerAspect::setSingleStep(qint64 step) */ DoubleAspect::DoubleAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::DoubleAspectPrivate) + : TypedAspect(container), d(new Internal::DoubleAspectPrivate) { setDefaultValue(double(0)); setSpan(2, 1); @@ -2105,42 +2294,23 @@ void DoubleAspect::addToLayout(LayoutItem &builder) d->m_spinBox->setSpecialValueText(d->m_specialValueText); if (d->m_maximumValue && d->m_maximumValue) d->m_spinBox->setRange(d->m_minimumValue.value(), d->m_maximumValue.value()); - d->m_spinBox->setValue(value()); // Must happen after setRange()! + bufferToGui(); // Must happen after setRange()! addLabeledItem(builder, d->m_spinBox); - - if (isAutoApply()) { - connect(d->m_spinBox.data(), &QDoubleSpinBox::valueChanged, - this, [this] { setValue(d->m_spinBox->value()); }); - } + connect(d->m_spinBox.data(), &QDoubleSpinBox::valueChanged, + this, &DoubleAspect::handleGuiChanged); } -QVariant DoubleAspect::volatileValue() const +bool DoubleAspect::guiToBuffer() { - QTC_CHECK(!isAutoApply()); - QTC_ASSERT(d->m_spinBox, return {}); - return d->m_spinBox->value(); -} - -void DoubleAspect::setVolatileValue(const QVariant &val) -{ - QTC_CHECK(!isAutoApply()); if (d->m_spinBox) - d->m_spinBox->setValue(val.toDouble()); + return updateStorage(m_buffer, d->m_spinBox->value()); + return false; } -double DoubleAspect::value() const +void DoubleAspect::bufferToGui() { - return BaseAspect::value().toDouble(); -} - -void DoubleAspect::setValue(double value) -{ - BaseAspect::setValue(value); -} - -double DoubleAspect::defaultValue() const -{ - return BaseAspect::defaultValue().toDouble(); + if (d->m_spinBox) + d->m_spinBox->setValue(m_buffer); } void DoubleAspect::setRange(double min, double max) @@ -2159,11 +2329,6 @@ void DoubleAspect::setSuffix(const QString &suffix) d->m_suffix = suffix; } -void DoubleAspect::setDefaultValue(double defaultValue) -{ - BaseAspect::setDefaultValue(defaultValue); -} - void DoubleAspect::setSpecialValueText(const QString &specialText) { d->m_specialValueText = specialText; @@ -2200,7 +2365,7 @@ TriStateAspect::TriStateAspect(AspectContainer *container, TriState TriStateAspect::value() const { - return TriState::fromVariant(BaseAspect::value()); + return TriState::fromInt(SelectionAspect::value()); } void TriStateAspect::setValue(TriState value) @@ -2210,12 +2375,12 @@ void TriStateAspect::setValue(TriState value) TriState TriStateAspect::defaultValue() const { - return TriState::fromVariant(BaseAspect::defaultValue()); + return TriState::fromInt(SelectionAspect::defaultValue()); } void TriStateAspect::setDefaultValue(TriState value) { - BaseAspect::setDefaultValue(value.toVariant()); + SelectionAspect::setDefaultValue(value.toInt()); } const TriState TriState::Enabled{TriState::EnabledValue}; @@ -2224,7 +2389,11 @@ const TriState TriState::Default{TriState::DefaultValue}; TriState TriState::fromVariant(const QVariant &variant) { - int v = variant.toInt(); + return fromInt(variant.toInt()); +} + +TriState TriState::fromInt(int v) +{ QTC_ASSERT(v == EnabledValue || v == DisabledValue || v == DefaultValue, v = DefaultValue); return TriState(Value(v)); } @@ -2239,7 +2408,7 @@ TriState TriState::fromVariant(const QVariant &variant) */ StringListAspect::StringListAspect(AspectContainer *container) - : BaseAspect(container), d(new Internal::StringListAspectPrivate) + : TypedAspect(container), d(new Internal::StringListAspectPrivate) { setDefaultValue(QStringList()); } @@ -2258,16 +2427,6 @@ void StringListAspect::addToLayout(LayoutItem &parent) // TODO - when needed. } -QStringList StringListAspect::value() const -{ - return BaseAspect::value().toStringList(); -} - -void StringListAspect::setValue(const QStringList &value) -{ - BaseAspect::setValue(value); -} - void StringListAspect::appendValue(const QString &s, bool allowDuplicates) { QStringList val = value(); @@ -2301,6 +2460,116 @@ void StringListAspect::removeValues(const QStringList &values) setValue(val); } +/*! + \class Utils::FilePathListAspect + \inmodule QtCreator + + \brief A filepath list aspect represents a property of some object + that is a list of filepathList. +*/ + +FilePathListAspect::FilePathListAspect(AspectContainer *container) + : TypedAspect(container) + , d(new Internal::FilePathListAspectPrivate) +{ + setDefaultValue(QStringList()); +} + +FilePathListAspect::~FilePathListAspect() = default; + +FilePaths FilePathListAspect::operator()() const +{ + return Utils::transform(m_internal, &FilePath::fromUserInput); +} + +bool FilePathListAspect::guiToBuffer() +{ + const QStringList newValue = d->undoable.get(); + if (newValue != m_buffer) { + m_buffer = newValue; + return true; + } + return false; +} + +void FilePathListAspect::bufferToGui() +{ + d->undoable.setWithoutUndo(m_buffer); +} + +void FilePathListAspect::addToLayout(LayoutItem &parent) +{ + d->undoable.setSilently(value()); + + PathListEditor *editor = new PathListEditor; + editor->setPathList(value()); + connect(editor, &PathListEditor::changed, this, [this, editor] { + d->undoable.set(undoStack(), editor->pathList()); + }); + connect(&d->undoable.m_signal, &UndoSignaller::changed, editor, [this, editor] { + if (editor->pathList() != d->undoable.get()) + editor->setPathList(d->undoable.get()); + + handleGuiChanged(); + }); + + editor->setToolTip(toolTip()); + editor->setMaximumHeight(100); + editor->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + editor->setPlaceholderText(d->placeHolderText); + + registerSubWidget(editor); + + parent.addItem(editor); +} + +void FilePathListAspect::setPlaceHolderText(const QString &placeHolderText) +{ + d->placeHolderText = placeHolderText; + + forEachSubWidget([placeHolderText](QWidget *widget) { + if (auto pathListEditor = qobject_cast(widget)) { + pathListEditor->setPlaceholderText(placeHolderText); + } + }); +} + +void FilePathListAspect::appendValue(const FilePath &path, bool allowDuplicates) +{ + const QString asString = path.toUserOutput(); + QStringList val = value(); + if (allowDuplicates || !val.contains(asString)) + val.append(asString); + setValue(val); +} + +void FilePathListAspect::removeValue(const FilePath &s) +{ + QStringList val = value(); + val.removeAll(s.toUserOutput()); + setValue(val); +} + +void FilePathListAspect::appendValues(const FilePaths &paths, bool allowDuplicates) +{ + QStringList val = value(); + + for (const FilePath &path : paths) { + const QString asString = path.toUserOutput(); + if (allowDuplicates || !val.contains(asString)) + val.append(asString); + } + setValue(val); +} + +void FilePathListAspect::removeValues(const FilePaths &paths) +{ + QStringList val = value(); + for (const FilePath &path : paths) + val.removeAll(path.toUserOutput()); + setValue(val); +} + /*! \class Utils::IntegerListAspect \internal @@ -2311,10 +2580,8 @@ void StringListAspect::removeValues(const QStringList &values) */ IntegersAspect::IntegersAspect(AspectContainer *container) - : BaseAspect(container) -{ - setDefaultValue({}); -} + : TypedAspect(container) +{} /*! \internal @@ -2330,33 +2597,6 @@ void IntegersAspect::addToLayout(Layouting::LayoutItem &parent) // TODO - when needed. } -void IntegersAspect::emitChangedValue() -{ - emit valueChanged(value()); -} - -QList IntegersAspect::value() const -{ - return transform(BaseAspect::value().toList(), - [](QVariant v) { return v.toInt(); }); -} - -void IntegersAspect::setValue(const QList &value) -{ - BaseAspect::setValue(transform(value, [](int i) { return QVariant::fromValue(i); })); -} - -QList IntegersAspect::defaultValue() const -{ - return transform(BaseAspect::defaultValue().toList(), - [](QVariant v) { return v.toInt(); }); -} - -void IntegersAspect::setDefaultValue(const QList &value) -{ - BaseAspect::setDefaultValue(transform(value, [](int i) { return QVariant::fromValue(i); })); -} - /*! \class Utils::TextDisplay @@ -2399,6 +2639,10 @@ void TextDisplay::addToLayout(LayoutItem &parent) // have a QWidget parent yet when used in a LayoutBuilder. if (!isVisible()) d->m_label->setVisible(false); + + connect(this, &TextDisplay::changed, d->m_label, [this] { + d->m_label->setText(d->m_message); + }); } parent.addItem(d->m_label.data()); } @@ -2417,6 +2661,7 @@ void TextDisplay::setIconType(InfoLabel::InfoType t) void TextDisplay::setText(const QString &message) { d->m_message = message; + emit changed(); } /*! @@ -2429,21 +2674,17 @@ void TextDisplay::setText(const QString &message) Sub-aspects ownership can be declared using \a setOwnsSubAspects. */ -namespace Internal { - -class AspectContainerPrivate +class Internal::AspectContainerPrivate { public: QList m_items; // Both owned and non-owned. QList m_ownedItems; // Owned only. - bool m_autoApply = true; QStringList m_settingsGroup; + std::function m_layouter; }; -} // Internal - -AspectContainer::AspectContainer(QObject *parent) - : QObject(parent), d(new Internal::AspectContainerPrivate) +AspectContainer::AspectContainer() + : d(new Internal::AspectContainerPrivate) {} /*! @@ -2459,7 +2700,7 @@ AspectContainer::~AspectContainer() */ void AspectContainer::registerAspect(BaseAspect *aspect, bool takeOwnership) { - aspect->setAutoApply(d->m_autoApply); + aspect->setAutoApply(isAutoApply()); d->m_items.append(aspect); if (takeOwnership) d->m_ownedItems.append(aspect); @@ -2491,12 +2732,22 @@ AspectContainer::const_iterator AspectContainer::end() const return d->m_items.cend(); } +void AspectContainer::setLayouter(const std::function &layouter) +{ + d->m_layouter = layouter; +} + +std::function AspectContainer::layouter() const +{ + return d->m_layouter; +} + const QList &AspectContainer::aspects() const { return d->m_items; } -void AspectContainer::fromMap(const QVariantMap &map) +void AspectContainer::fromMap(const Store &map) { for (BaseAspect *aspect : std::as_const(d->m_items)) aspect->fromMap(map); @@ -2505,34 +2756,30 @@ void AspectContainer::fromMap(const QVariantMap &map) } -void AspectContainer::toMap(QVariantMap &map) const +void AspectContainer::toMap(Store &map) const { for (BaseAspect *aspect : std::as_const(d->m_items)) aspect->toMap(map); } -void AspectContainer::readSettings(QSettings *settings) +void AspectContainer::volatileToMap(Store &map) const { - for (const QString &group : d->m_settingsGroup) - settings->beginGroup(group); - for (BaseAspect *aspect : std::as_const(d->m_items)) - aspect->readSettings(settings); - - for (int i = 0; i != d->m_settingsGroup.size(); ++i) - settings->endGroup(); + aspect->volatileToMap(map); } -void AspectContainer::writeSettings(QSettings *settings) const +void AspectContainer::readSettings() { - for (const QString &group : d->m_settingsGroup) - settings->beginGroup(group); - + const SettingsGroupNester nester(d->m_settingsGroup); for (BaseAspect *aspect : std::as_const(d->m_items)) - aspect->writeSettings(settings); + aspect->readSettings(); +} - for (int i = 0; i != d->m_settingsGroup.size(); ++i) - settings->endGroup(); +void AspectContainer::writeSettings() const +{ + const SettingsGroupNester nester(d->m_settingsGroup); + for (BaseAspect *aspect : std::as_const(d->m_items)) + aspect->writeSettings(); } void AspectContainer::setSettingsGroup(const QString &groupKey) @@ -2545,6 +2792,11 @@ void AspectContainer::setSettingsGroups(const QString &groupKey, const QString & d->m_settingsGroup = QStringList{groupKey, subGroupKey}; } +QStringList AspectContainer::settingsGroups() const +{ + return d->m_settingsGroup; +} + void AspectContainer::apply() { const bool willChange = isDirty(); @@ -2573,17 +2825,18 @@ void AspectContainer::finish() void AspectContainer::reset() { for (BaseAspect *aspect : std::as_const(d->m_items)) - aspect->setValueQuietly(aspect->defaultValue()); + aspect->setVariantValue(aspect->defaultVariantValue()); } void AspectContainer::setAutoApply(bool on) { - d->m_autoApply = on; + BaseAspect::setAutoApply(on); + for (BaseAspect *aspect : std::as_const(d->m_items)) aspect->setAutoApply(on); } -bool AspectContainer::isDirty() const +bool AspectContainer::isDirty() { for (BaseAspect *aspect : std::as_const(d->m_items)) { if (aspect->isDirty()) @@ -2592,10 +2845,18 @@ bool AspectContainer::isDirty() const return false; } +void AspectContainer::setUndoStack(QUndoStack *undoStack) +{ + BaseAspect::setUndoStack(undoStack); + + for (BaseAspect *aspect : std::as_const(d->m_items)) + aspect->setUndoStack(undoStack); +} + bool AspectContainer::equals(const AspectContainer &other) const { // FIXME: Expensive, but should not really be needed in a fully aspectified world. - QVariantMap thisMap, thatMap; + Store thisMap, thatMap; toMap(thisMap); other.toMap(thatMap); return thisMap == thatMap; @@ -2603,7 +2864,7 @@ bool AspectContainer::equals(const AspectContainer &other) const void AspectContainer::copyFrom(const AspectContainer &other) { - QVariantMap map; + Store map; other.toMap(map); fromMap(map); } @@ -2630,6 +2891,55 @@ BaseAspect::Data::Ptr BaseAspect::extractData() const return Data::Ptr(data); } +/* + Mirrors the internal volatile value to the GUI element if they are already + created. + + No-op otherwise. +*/ +void BaseAspect::bufferToGui() +{ +} + +/* + Mirrors the data stored in GUI element if they are already created to + the internal volatile value. + + No-op otherwise. + + \return true when the buffered volatile value changed. +*/ +bool BaseAspect::guiToBuffer() +{ + return false; +} + +/* + Mirrors buffered volatile value to the internal value. + This function is used for \c apply(). + + \return true when the internal value changed. +*/ + +bool BaseAspect::bufferToInternal() +{ + return false; +} + + +bool BaseAspect::internalToBuffer() +{ + return false; +} + +void BaseAspect::handleGuiChanged() +{ + if (guiToBuffer()) + volatileValueChanged(); + if (isAutoApply()) + apply(); +} + void BaseAspect::addDataExtractorHelper(const DataExtractor &extractor) const { d->m_dataExtractors.append(extractor); @@ -2680,4 +2990,430 @@ void BaseAspect::Data::Ptr::operator=(const Ptr &other) m_data = other.m_data->clone(); } +// SettingsGroupNester + +SettingsGroupNester::SettingsGroupNester(const QStringList &groups) + : m_groupCount(groups.size()) +{ + QTC_ASSERT(theSettings, return); + for (const QString &group : groups) + theSettings->beginGroup(keyFromString(group)); +} + +SettingsGroupNester::~SettingsGroupNester() +{ + QTC_ASSERT(theSettings, return); + for (int i = 0; i != m_groupCount; ++i) + theSettings->endGroup(); +} + +class AddItemCommand : public QUndoCommand +{ +public: + AddItemCommand(AspectList *aspect, const std::shared_ptr &item) + : m_aspect(aspect) + , m_item(item) + {} + + void undo() override { m_aspect->actualRemoveItem(m_item); } + void redo() override { m_aspect->actualAddItem(m_item); } + +private: + AspectList *m_aspect; + std::shared_ptr m_item; +}; + +class RemoveItemCommand : public QUndoCommand +{ +public: + RemoveItemCommand(AspectList *aspect, const std::shared_ptr &item) + : m_aspect(aspect) + , m_item(item) + {} + + void undo() override { m_aspect->actualAddItem(m_item); } + void redo() override { m_aspect->actualRemoveItem(m_item); } + +private: + AspectList *m_aspect; + std::shared_ptr m_item; +}; + +class Internal::AspectListPrivate +{ +public: + QList> items; + QList> volatileItems; + AspectList::CreateItem createItem; + AspectList::ItemCallback itemAdded; + AspectList::ItemCallback itemRemoved; +}; + +AspectList::AspectList(Utils::AspectContainer *container) + : Utils::BaseAspect(container) + , d(std::make_unique()) +{} + +AspectList::~AspectList() = default; + +void AspectList::fromMap(const Utils::Store &map) +{ + QTC_ASSERT(!settingsKey().isEmpty(), return); + + QVariantList list = map[settingsKey()].toList(); + d->volatileItems.clear(); + for (const QVariant &entry : list) { + auto item = d->createItem(); + item->setAutoApply(isAutoApply()); + item->setUndoStack(undoStack()); + item->fromMap(Utils::storeFromVariant(entry)); + d->volatileItems.append(item); + } + d->items = d->volatileItems; +} + +QVariantList AspectList::toList(bool v) const +{ + QVariantList list; + const auto &items = v ? d->volatileItems : d->items; + + for (const std::shared_ptr &item : items) { + Utils::Store childStore; + if (v) + item->volatileToMap(childStore); + else + item->toMap(childStore); + + list.append(Utils::variantFromStore(childStore)); + } + + return list; +} + +void AspectList::toMap(Utils::Store &map) const +{ + QTC_ASSERT(!settingsKey().isEmpty(), return); + const Utils::Key key = settingsKey(); + map[key] = toList(false); +} + +void AspectList::volatileToMap(Utils::Store &map) const +{ + QTC_ASSERT(!settingsKey().isEmpty(), return); + const Utils::Key key = settingsKey(); + map[key] = toList(true); +} + +std::shared_ptr AspectList::actualAddItem(const std::shared_ptr &item) +{ + item->setAutoApply(isAutoApply()); + item->setUndoStack(undoStack()); + + d->volatileItems.append(item); + if (d->itemAdded) + d->itemAdded(item); + emit volatileValueChanged(); + if (isAutoApply()) + d->items = d->volatileItems; + return item; +} + +QList> AspectList::items() const +{ + return d->items; +} +QList> AspectList::volatileItems() const +{ + return d->volatileItems; +} + +std::shared_ptr AspectList::createAndAddItem() +{ + return addItem(d->createItem()); +} + +std::shared_ptr AspectList::addItem(const std::shared_ptr &item) +{ + if (undoStack()) + undoStack()->push(new AddItemCommand(this, item)); + else + return actualAddItem(item); + + return item; +} + +void AspectList::actualRemoveItem(const std::shared_ptr &item) +{ + d->volatileItems.removeOne(item); + if (d->itemRemoved) + d->itemRemoved(item); + emit volatileValueChanged(); + if (isAutoApply()) + d->items = d->volatileItems; +} + +void AspectList::removeItem(const std::shared_ptr &item) +{ + if (undoStack()) + undoStack()->push(new RemoveItemCommand(this, item)); + else + actualRemoveItem(item); +} + +void AspectList::clear() +{ + if (undoStack()) { + undoStack()->beginMacro("Clear"); + + for (const std::shared_ptr &item : volatileItems()) + undoStack()->push(new RemoveItemCommand(this, item)); + + undoStack()->endMacro(); + } else { + for (const std::shared_ptr &item : volatileItems()) + actualRemoveItem(item); + } +} + +void AspectList::apply() +{ + d->items = d->volatileItems; + forEachItem([](const std::shared_ptr &aspect) { aspect->apply(); }); + emit changed(); +} + +void AspectList::setCreateItemFunction(CreateItem createItem) +{ + d->createItem = createItem; +} + +void AspectList::setItemAddedCallback(const ItemCallback &callback) +{ + d->itemAdded = callback; +} +void AspectList::setItemRemovedCallback(const ItemCallback &callback) +{ + d->itemRemoved = callback; +} + +qsizetype AspectList::size() const +{ + return d->volatileItems.size(); +} + +bool AspectList::isDirty() +{ + if (d->items != d->volatileItems) + return true; + + for (const std::shared_ptr &item : d->volatileItems) { + if (item->isDirty()) + return true; + } + return false; +} + +class ColoredRow : public QWidget +{ +public: + ColoredRow(int idx, QWidget *parent = nullptr) + : QWidget(parent) + , m_index(idx) + {} + void paintEvent(QPaintEvent *event) + { + QPainter p(this); + QPalette pal = palette(); + if (m_index % 2 == 0) + p.fillRect(event->rect(), pal.base()); + else + p.fillRect(event->rect(), pal.alternateBase()); + } + +private: + int m_index; +}; + +void AspectList::addToLayout(Layouting::LayoutItem &parent) +{ + using namespace Layouting; + + QScrollArea *scrollArea = new QScrollArea; + scrollArea->setWidgetResizable(true); + scrollArea->setMaximumHeight(100); + scrollArea->setMinimumHeight(100); + scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto fill = [this, scrollArea]() mutable { + if (scrollArea->widget()) + delete scrollArea->takeWidget(); + + auto add = new QPushButton(Tr::tr("Add")); + QObject::connect(add, &QPushButton::clicked, scrollArea, [this] { + addItem(d->createItem()); + }); + + Column column{noMargin()}; + + forEachItem([&column, this](const std::shared_ptr &item, int idx) { + auto removeBtn = new IconButton; + removeBtn->setIcon(Utils::Icons::EDIT_CLEAR.icon()); + removeBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QObject::connect(removeBtn, &QPushButton::clicked, removeBtn, [this, item] { + removeItem(item); + }); + ColoredRow *rowWdgt = new ColoredRow(idx); + // clang-format off + auto row = Row { + *item, + removeBtn, + spacing(5), + }; + // clang-format on + row.attachTo(rowWdgt); + column.addItem(rowWdgt); + }); + + ColoredRow *rowWdgt = new ColoredRow(size()); + Row{st, add}.attachTo(rowWdgt); + column.addItem(rowWdgt); + + QWidget *contentWidget = column.emerge(); + contentWidget->layout()->setSpacing(1); + + scrollArea->setWidget(contentWidget); + }; + + fill(); + QObject::connect(this, &AspectList::volatileValueChanged, scrollArea, fill); + + parent.addItem(scrollArea); +} + +StringSelectionAspect::StringSelectionAspect(AspectContainer *container) + : TypedAspect(container) +{} + +QStandardItem *StringSelectionAspect::itemById(const QString &id) +{ + for (int i = 0; i < m_model->rowCount(); ++i) { + auto cur = m_model->item(i); + if (cur->data() == id) + return cur; + } + + return nullptr; +} + +void StringSelectionAspect::bufferToGui() +{ + if (!m_model) { + m_undoable.setSilently(m_buffer); + return; + } + + auto selected = itemById(m_buffer); + if (selected) { + m_undoable.setSilently(selected->data().toString()); + m_selectionModel->setCurrentIndex(selected->index(), + QItemSelectionModel::SelectionFlag::ClearAndSelect); + return; + } + + if (m_model->rowCount() > 0) { + m_undoable.setSilently(m_model->item(0)->data().toString()); + m_selectionModel->setCurrentIndex(m_model->item(0)->index(), + QItemSelectionModel::SelectionFlag::ClearAndSelect); + } else { + m_undoable.setSilently(m_buffer); + m_selectionModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::SelectionFlag::Clear); + } + + handleGuiChanged(); +} + +bool StringSelectionAspect::guiToBuffer() +{ + if (!m_model) + return false; + + auto oldBuffer = m_buffer; + + m_buffer = m_undoable.get(); + + return oldBuffer != m_buffer; +} + +void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent) +{ + QTC_ASSERT(m_fillCallback, return); + + auto cb = [this](const QList &items) { + m_model->clear(); + for (QStandardItem *item : items) + m_model->appendRow(item); + + bufferToGui(); + }; + + if (!m_model) { + m_model = new QStandardItemModel(this); + m_selectionModel = new QItemSelectionModel(m_model); + + connect(this, &StringSelectionAspect::refillRequested, this, [this, cb] { + m_fillCallback(cb); + }); + + m_fillCallback(cb); + } + + QComboBox *comboBox = new QComboBox(); + comboBox->setInsertPolicy(QComboBox::InsertPolicy::NoInsert); + comboBox->setEditable(true); + comboBox->completer()->setCompletionMode(QCompleter::PopupCompletion); + comboBox->completer()->setFilterMode(Qt::MatchContains); + + comboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + comboBox->setCurrentText(value()); + comboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + + comboBox->setModel(m_model); + + connect(m_selectionModel, + &QItemSelectionModel::currentChanged, + comboBox, + [comboBox](QModelIndex currentIdx) { + if (currentIdx.isValid() && comboBox->currentIndex() != currentIdx.row()) + comboBox->setCurrentIndex(currentIdx.row()); + }); + + connect(comboBox, &QComboBox::activated, this, [this](int idx) { + QModelIndex modelIdx = m_model->index(idx, 0); + if (!modelIdx.isValid()) + return; + + QString newValue = m_model->index(idx, 0).data(Qt::UserRole + 1).toString(); + if (newValue.isEmpty()) + return; + + m_undoable.set(undoStack(), newValue); + bufferToGui(); + }); + + connect(&m_undoable.m_signal, &UndoSignaller::changed, comboBox, [this, comboBox]() { + auto item = itemById(m_undoable.get()); + if (item) + m_selectionModel->setCurrentIndex(item->index(), QItemSelectionModel::ClearAndSelect); + else + comboBox->setCurrentText(m_undoable.get()); + + handleGuiChanged(); + }); + + if (m_selectionModel->currentIndex().isValid()) + comboBox->setCurrentIndex(m_selectionModel->currentIndex().row()); + + return addLabeledItem(parent, comboBox); +} + } // namespace Utils diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index 146e89198d3..e46ce5ab9b8 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -8,17 +8,27 @@ #include "infolabel.h" #include "macroexpander.h" #include "pathchooser.h" +#include "qtcsettings.h" +#include "store.h" #include #include #include +#include + QT_BEGIN_NAMESPACE class QAction; class QSettings; +class QUndoStack; +class QStandardItem; +class QStandardItemModel; +class QItemSelectionModel; QT_END_NAMESPACE -namespace Layouting { class LayoutItem; } +namespace Layouting { +class LayoutItem; +} namespace Utils { @@ -32,12 +42,16 @@ class BaseAspectPrivate; class BoolAspectPrivate; class ColorAspectPrivate; class DoubleAspectPrivate; +class FilePathAspectPrivate; +class FilePathListAspectPrivate; class IntegerAspectPrivate; class MultiSelectionAspectPrivate; class SelectionAspectPrivate; class StringAspectPrivate; class StringListAspectPrivate; class TextDisplayPrivate; +class CheckableAspectImplementation; +class AspectListPrivate; } // Internal class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject @@ -51,16 +65,19 @@ public: Id id() const; void setId(Id id); - QVariant value() const; - void setValue(const QVariant &value); - bool setValueQuietly(const QVariant &value); + enum Announcement { DoEmit, BeQuiet }; - QVariant defaultValue() const; - void setDefaultValue(const QVariant &value); + virtual QVariant volatileVariantValue() const; + virtual QVariant variantValue() const; + virtual void setVariantValue(const QVariant &value, Announcement = DoEmit); - QString settingsKey() const; - void setSettingsKey(const QString &settingsKey); - void setSettingsKey(const QString &group, const QString &key); + virtual QVariant defaultVariantValue() const; + virtual void setDefaultVariantValue(const QVariant &value); + virtual bool isDefaultValue() const; + + Key settingsKey() const; + void setSettingsKey(const Key &settingsKey); + void setSettingsKey(const Key &group, const Key &key); QString displayName() const; void setDisplayName(const QString &displayName); @@ -72,7 +89,10 @@ public: void setVisible(bool visible); bool isAutoApply() const; - void setAutoApply(bool on); + virtual void setAutoApply(bool on); + + virtual void setUndoStack(QUndoStack *undoStack); + QUndoStack *undoStack() const; bool isEnabled() const; void setEnabled(bool enabled); @@ -94,18 +114,17 @@ public: virtual QAction *action(); - virtual void fromMap(const QVariantMap &map); - virtual void toMap(QVariantMap &map) const; - virtual void toActiveMap(QVariantMap &map) const { toMap(map); } + AspectContainer *container() const; + + virtual void fromMap(const Store &map); + virtual void toMap(Store &map) const; + virtual void toActiveMap(Store &map) const { toMap(map); } + virtual void volatileToMap(Store &map) const; virtual void addToLayout(Layouting::LayoutItem &parent); - virtual QVariant volatileValue() const; - virtual void setVolatileValue(const QVariant &val); - virtual void emitChangedValue() {} - - virtual void readSettings(const QSettings *settings); - virtual void writeSettings(QSettings *settings) const; + virtual void readSettings(); + virtual void writeSettings() const; using SavedValueTransformation = std::function; void setFromSettingsTransformation(const SavedValueTransformation &transform); @@ -116,9 +135,22 @@ public: virtual void apply(); virtual void cancel(); virtual void finish(); - virtual bool isDirty() const; + virtual bool isDirty(); bool hasAction() const; + struct QTCREATOR_UTILS_EXPORT Changes + { + Changes(); + + unsigned internalFromOutside : 1; + unsigned internalFromBuffer : 1; + unsigned bufferFromOutside : 1; + unsigned bufferFromInternal : 1; + unsigned bufferFromGui : 1; + }; + + void announceChanges(Changes changes, Announcement howToAnnounce = DoEmit); + class QTCREATOR_UTILS_EXPORT Data { public: @@ -162,13 +194,30 @@ public: Data::Ptr extractData() const; + static void setQtcSettings(QtcSettings *settings); + static QtcSettings *qtcSettings(); + + // This is expensive. Do not use without good reason + void writeToSettingsImmediatly() const; + signals: - void changed(); + void changed(); // "internal" + void volatileValueChanged(); void labelLinkActivated(const QString &link); + void checkedChanged(); + void enabledChanged(); + void labelTextChanged(); + void labelPixmapChanged(); protected: - QLabel *label() const; - void setupLabel(); + virtual bool internalToBuffer(); + virtual bool bufferToInternal(); + virtual void bufferToGui(); + virtual bool guiToBuffer(); + + virtual void handleGuiChanged(); + + QLabel *createLabel(); void addLabeledItem(Layouting::LayoutItem &parent, QWidget *widget); void setDataCreatorHelper(const DataCreator &creator) const; @@ -198,17 +247,176 @@ protected: } void registerSubWidget(QWidget *widget); - static void saveToMap(QVariantMap &data, const QVariant &value, - const QVariant &defaultValue, const QString &key); + static void saveToMap(Store &data, const QVariant &value, + const QVariant &defaultValue, const Key &key); + + void forEachSubWidget(const std::function &func); + +protected: + template + static bool updateStorage(Value &target, const Value &val) + { + if (target == val) + return false; + target = val; + return true; + } private: std::unique_ptr d; + friend class Internal::CheckableAspectImplementation; }; QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect); QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect); -class QTCREATOR_UTILS_EXPORT BoolAspect : public BaseAspect +template +class TypedAspect : public BaseAspect +{ +public: + TypedAspect(AspectContainer *container = nullptr) + : BaseAspect(container) + { + addDataExtractor(this, &TypedAspect::value, &Data::value); + } + + struct Data : BaseAspect::Data + { + ValueType value; + }; + + ValueType operator()() const { return m_internal; } + ValueType value() const { return m_internal; } + ValueType defaultValue() const { return m_default; } + ValueType volatileValue() const { return m_buffer; } + + // We assume that this is only used in the ctor and no signalling is needed. + // If it is used elsewhere changes have to be detected and signalled externally. + void setDefaultValue(const ValueType &value) + { + m_default = value; + m_internal = value; + if (internalToBuffer()) // Might be more than a plain copy. + bufferToGui(); + } + + bool isDefaultValue() const override + { + return m_default == m_internal; + } + + void setValue(const ValueType &value, Announcement howToAnnounce = DoEmit) + { + Changes changes; + changes.internalFromOutside = updateStorage(m_internal, value); + if (internalToBuffer()) { + changes.bufferFromInternal = true; + bufferToGui(); + } + announceChanges(changes, howToAnnounce); + } + + void setVolatileValue(const ValueType &value, Announcement howToAnnounce = DoEmit) + { + Changes changes; + if (updateStorage(m_buffer, value)) { + changes.bufferFromOutside = true; + bufferToGui(); + } + if (isAutoApply() && bufferToInternal()) + changes.internalFromBuffer = true; + announceChanges(changes, howToAnnounce); + } + +protected: + bool isDirty() override + { + return m_internal != m_buffer; + } + + bool internalToBuffer() override + { + return updateStorage(m_buffer, m_internal); + } + + bool bufferToInternal() override + { + return updateStorage(m_internal, m_buffer); + } + + QVariant variantValue() const override + { + return QVariant::fromValue(m_internal); + } + + QVariant volatileVariantValue() const override + { + return QVariant::fromValue(m_buffer); + } + + void setVariantValue(const QVariant &value, Announcement howToAnnounce = DoEmit) override + { + setValue(value.value(), howToAnnounce); + } + + QVariant defaultVariantValue() const override + { + return QVariant::fromValue(m_default); + } + + void setDefaultVariantValue(const QVariant &value) override + { + setDefaultValue(value.value()); + } + + ValueType m_default{}; + ValueType m_internal{}; + ValueType m_buffer{}; +}; + +template +class FlexibleTypedAspect : public TypedAspect +{ +public: + using Base = TypedAspect; + using Updater = std::function; + + using Base::Base; + + void setInternalToBuffer(const Updater &updater) { m_internalToBuffer = updater; } + void setBufferToInternal(const Updater &updater) { m_bufferToInternal = updater; } + void setInternalToExternal(const Updater &updater) { m_internalToExternal = updater; } + +protected: + bool internalToBuffer() override + { + if (m_internalToBuffer) + return m_internalToBuffer(Base::m_buffer, Base::m_internal); + return Base::internalToBuffer(); + } + + bool bufferToInternal() override + { + if (m_bufferToInternal) + return m_bufferToInternal(Base::m_internal, Base::m_buffer); + return Base::bufferToInternal(); + } + + ValueType expandedValue() + { + if (!m_internalToExternal) + return Base::m_internal; + ValueType val; + m_internalToExternal(val, Base::m_internal); + return val; + } + + Updater m_internalToBuffer; + Updater m_bufferToInternal; + Updater m_internalToExternal; +}; + +class QTCREATOR_UTILS_EXPORT BoolAspect : public TypedAspect { Q_OBJECT @@ -216,11 +424,6 @@ public: BoolAspect(AspectContainer *container = nullptr); ~BoolAspect() override; - struct Data : BaseAspect::Data - { - bool value; - }; - void addToLayout(Layouting::LayoutItem &parent) override; std::function groupChecker(); @@ -229,32 +432,23 @@ public: QAction *action() override; - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - void emitChangedValue() override; - - bool operator()() const { return value(); } - bool value() const; - void setValue(bool val); - bool defaultValue() const; - void setDefaultValue(bool val); - - enum class LabelPlacement { AtCheckBox, AtCheckBoxWithoutDummyLabel, InExtraLabel }; + enum class LabelPlacement { AtCheckBox, Compact, InExtraLabel }; void setLabel(const QString &labelText, LabelPlacement labelPlacement = LabelPlacement::InExtraLabel); void setLabelPlacement(LabelPlacement labelPlacement); - void adoptButton(QAbstractButton *button); - -signals: - void valueChanged(bool newValue); - void volatileValueChanged(bool newValue); + Layouting::LayoutItem adoptButton(QAbstractButton *button); private: + void addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button); + + void bufferToGui() override; + bool guiToBuffer() override; + std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT ColorAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT ColorAspect : public TypedAspect { Q_OBJECT @@ -262,24 +456,16 @@ public: ColorAspect(AspectContainer *container = nullptr); ~ColorAspect() override; - struct Data : BaseAspect::Data - { - QColor value; - }; - void addToLayout(Layouting::LayoutItem &parent) override; - QColor value() const; - void setValue(const QColor &val); - - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - private: + void bufferToGui() override; + bool guiToBuffer() override; + std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT SelectionAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT SelectionAspect : public TypedAspect { Q_OBJECT @@ -288,20 +474,13 @@ public: ~SelectionAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; void finish() override; - int operator()() const { return value(); } - int value() const; - void setValue(int val); - QString stringValue() const; void setStringValue(const QString &val); - int defaultValue() const; - void setDefaultValue(int val); void setDefaultValue(const QString &val); + void setDefaultValue(int val); QVariant itemValue() const; @@ -327,14 +506,15 @@ public: int indexForItemValue(const QVariant &value) const; QVariant itemValueForIndex(int index) const; -signals: - void volatileValueChanged(int newValue); +protected: + void bufferToGui() override; + bool guiToBuffer() override; private: std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT MultiSelectionAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT MultiSelectionAspect : public TypedAspect { Q_OBJECT @@ -347,17 +527,21 @@ public: enum class DisplayStyle { ListView }; void setDisplayStyle(DisplayStyle style); - QStringList value() const; - void setValue(const QStringList &val); - QStringList allValues() const; void setAllValues(const QStringList &val); +protected: + void bufferToGui() override; + bool guiToBuffer() override; + private: std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT StringAspect : public BaseAspect +enum class UncheckedSemantics { Disabled, ReadOnly }; +enum class CheckBoxPlacement { Top, Right }; + +class QTCREATOR_UTILS_EXPORT StringAspect : public TypedAspect { Q_OBJECT @@ -365,98 +549,132 @@ public: StringAspect(AspectContainer *container = nullptr); ~StringAspect() override; + void addToLayout(Layouting::LayoutItem &parent) override; + + QString operator()() const { return expandedValue(); } + QString expandedValue() const; + + // Hook between UI and StringAspect: + using ValueAcceptor = std::function(const QString &, const QString &)>; + void setValueAcceptor(ValueAcceptor &&acceptor); + + void setShowToolTipOnLabel(bool show); + void setDisplayFilter(const std::function &displayFilter); + void setPlaceHolderText(const QString &placeHolderText); + void setHistoryCompleter(const Key &historyCompleterKey); + void setAcceptRichText(bool acceptRichText); + void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider); + void setUseGlobalMacroExpander(); + void setUseResetButton(); + void setValidationFunction(const FancyLineEdit::ValidationFunction &validator); + void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); + void setElideMode(Qt::TextElideMode elideMode); + + void makeCheckable(CheckBoxPlacement checkBoxPlacement, const QString &optionalLabel, const Key &optionalBaseKey); + bool isChecked() const; + void setChecked(bool checked); + + enum DisplayStyle { + LabelDisplay, + LineEditDisplay, + TextEditDisplay, + PasswordLineEditDisplay, + }; + void setDisplayStyle(DisplayStyle style); + + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; + void volatileToMap(Utils::Store &map) const override; + +signals: + void validChanged(bool validState); + void elideModeChanged(Qt::TextElideMode elideMode); + void historyCompleterKeyChanged(const Key &historyCompleterKey); + void acceptRichTextChanged(bool acceptRichText); + void validationFunctionChanged(const FancyLineEdit::ValidationFunction &validator); + void placeholderTextChanged(const QString &placeholderText); + +protected: + void bufferToGui() override; + bool guiToBuffer() override; + + bool internalToBuffer() override; + bool bufferToInternal() override; + + std::unique_ptr d; +}; + +class QTCREATOR_UTILS_EXPORT FilePathAspect : public TypedAspect +{ + Q_OBJECT + +public: + FilePathAspect(AspectContainer *container = nullptr); + ~FilePathAspect(); + struct Data : BaseAspect::Data { QString value; FilePath filePath; }; - void addToLayout(Layouting::LayoutItem &parent) override; + FilePath operator()() const; + FilePath expandedValue() const; + QString value() const; + void setValue(const FilePath &filePath, Announcement howToAnnounce = DoEmit); + void setValue(const QString &filePath, Announcement howToAnnounce = DoEmit); + void setDefaultValue(const QString &filePath); - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - void emitChangedValue() override; + void setPromptDialogFilter(const QString &filter); + void setPromptDialogTitle(const QString &title); + void setCommandVersionArguments(const QStringList &arguments); + void setAllowPathFromDevice(bool allowPathFromDevice); + void setValidatePlaceHolder(bool validatePlaceHolder); + void setOpenTerminalHandler(const std::function &openTerminal); + void setExpectedKind(const PathChooser::Kind expectedKind); + void setEnvironment(const Environment &env); + void setBaseFileName(const FilePath &baseFileName); + + void setPlaceHolderText(const QString &placeHolderText); + void setValidationFunction(const FancyLineEdit::ValidationFunction &validator); + void setDisplayFilter(const std::function &displayFilter); + void setHistoryCompleter(const Key &historyCompleterKey); + void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider); + void setShowToolTipOnLabel(bool show); + void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); + + void validateInput(); + + void makeCheckable(CheckBoxPlacement checkBoxPlacement, const QString &optionalLabel, const Key &optionalBaseKey); + bool isChecked() const; + void setChecked(bool checked); // Hook between UI and StringAspect: using ValueAcceptor = std::function(const QString &, const QString &)>; void setValueAcceptor(ValueAcceptor &&acceptor); - QString operator()() const { return value(); } - QString value() const; - void setValue(const QString &val); - - QString defaultValue() const; - void setDefaultValue(const QString &val); - - void setShowToolTipOnLabel(bool show); - - void setDisplayFilter(const std::function &displayFilter); - void setPlaceHolderText(const QString &placeHolderText); - void setPromptDialogFilter(const QString &filter); - void setPromptDialogTitle(const QString &title); - void setCommandVersionArguments(const QStringList &arguments); - void setHistoryCompleter(const QString &historyCompleterKey); - void setExpectedKind(const PathChooser::Kind expectedKind); - void setEnvironment(const Environment &env); - void setBaseFileName(const FilePath &baseFileName); - void setUndoRedoEnabled(bool readOnly); - void setAcceptRichText(bool acceptRichText); - void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider); - void setUseGlobalMacroExpander(); - void setUseResetButton(); - void setValidationFunction(const FancyLineEdit::ValidationFunction &validator); - void setOpenTerminalHandler(const std::function &openTerminal); - void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); - void setElideMode(Qt::TextElideMode elideMode); - void setAllowPathFromDevice(bool allowPathFromDevice); - void setValidatePlaceHolder(bool validatePlaceHolder); - - void validateInput(); - - enum class UncheckedSemantics { Disabled, ReadOnly }; - enum class CheckBoxPlacement { Top, Right }; - void setUncheckedSemantics(UncheckedSemantics semantics); - bool isChecked() const; - void setChecked(bool checked); - void makeCheckable(CheckBoxPlacement checkBoxPlacement, const QString &optionalLabel, - const QString &optionalBaseKey); - - enum DisplayStyle { - LabelDisplay, - LineEditDisplay, - TextEditDisplay, - PathChooserDisplay - }; - void setDisplayStyle(DisplayStyle style); - - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; - - FilePath filePath() const; - void setFilePath(const FilePath &value); - void setDefaultFilePath(const FilePath &value); - PathChooser *pathChooser() const; // Avoid to use. + void addToLayout(Layouting::LayoutItem &parent) override; + + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; + void volatileToMap(Utils::Store &map) const override; + signals: - void checkedChanged(); - void valueChanged(const QString &newValue); + void validChanged(bool validState); protected: - void update(); + void bufferToGui() override; + bool guiToBuffer() override; - std::unique_ptr d; + bool internalToBuffer() override; + bool bufferToInternal() override; + + std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT FilePathAspect : public StringAspect -{ -public: - FilePathAspect(AspectContainer *container = nullptr); - - FilePath operator()() const { return filePath(); } -}; - -class QTCREATOR_UTILS_EXPORT IntegerAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT IntegerAspect : public TypedAspect { Q_OBJECT @@ -466,16 +684,6 @@ public: void addToLayout(Layouting::LayoutItem &parent) override; - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - - qint64 operator()() const { return value(); } - qint64 value() const; - void setValue(qint64 val); - - qint64 defaultValue() const; - void setDefaultValue(qint64 defaultValue); - void setRange(qint64 min, qint64 max); void setLabel(const QString &label); // FIXME: Use setLabelText void setPrefix(const QString &prefix); @@ -487,14 +695,15 @@ public: struct Data : BaseAspect::Data { qint64 value = 0; }; -signals: - void valueChanged(int newValue); +protected: + void bufferToGui() override; + bool guiToBuffer() override; private: std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT DoubleAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT DoubleAspect : public TypedAspect { Q_OBJECT @@ -504,22 +713,16 @@ public: void addToLayout(Layouting::LayoutItem &parent) override; - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - - double operator()() const { return value(); } - double value() const; - void setValue(double val); - - double defaultValue() const; - void setDefaultValue(double defaultValue); - void setRange(double min, double max); void setPrefix(const QString &prefix); void setSuffix(const QString &suffix); void setSpecialValueText(const QString &specialText); void setSingleStep(double step); +protected: + void bufferToGui() override; + bool guiToBuffer() override; + private: std::unique_ptr d; }; @@ -534,6 +737,7 @@ public: int toInt() const { return int(m_value); } QVariant toVariant() const { return int(m_value); } + static TriState fromInt(int value); static TriState fromVariant(const QVariant &variant); static const TriState Enabled; @@ -557,6 +761,7 @@ public: const QString &offString = {}, const QString &defaultString = {}); + TriState operator()() const { return value(); } TriState value() const; void setValue(TriState setting); @@ -564,7 +769,7 @@ public: void setDefaultValue(TriState setting); }; -class QTCREATOR_UTILS_EXPORT StringListAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT StringListAspect : public TypedAspect { Q_OBJECT @@ -574,9 +779,6 @@ public: void addToLayout(Layouting::LayoutItem &parent) override; - QStringList value() const; - void setValue(const QStringList &val); - void appendValue(const QString &value, bool allowDuplicates = true); void removeValue(const QString &value); void appendValues(const QStringList &values, bool allowDuplicates = true); @@ -586,7 +788,32 @@ private: std::unique_ptr d; }; -class QTCREATOR_UTILS_EXPORT IntegersAspect : public BaseAspect +class QTCREATOR_UTILS_EXPORT FilePathListAspect : public TypedAspect +{ + Q_OBJECT + +public: + FilePathListAspect(AspectContainer *container = nullptr); + ~FilePathListAspect() override; + + FilePaths operator()() const; + + bool guiToBuffer() override; + void bufferToGui() override; + + void addToLayout(Layouting::LayoutItem &parent) override; + void setPlaceHolderText(const QString &placeHolderText); + + void appendValue(const FilePath &path, bool allowDuplicates = true); + void removeValue(const FilePath &path); + void appendValues(const FilePaths &values, bool allowDuplicates = true); + void removeValues(const FilePaths &values); + +private: + std::unique_ptr d; +}; + +class QTCREATOR_UTILS_EXPORT IntegersAspect : public TypedAspect> { Q_OBJECT @@ -595,17 +822,6 @@ public: ~IntegersAspect() override; void addToLayout(Layouting::LayoutItem &parent) override; - void emitChangedValue() override; - - QList operator()() const { return value(); } - QList value() const; - void setValue(const QList &value); - - QList defaultValue() const; - void setDefaultValue(const QList &value); - -signals: - void valueChanged(const QList &values); }; class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect @@ -646,12 +862,24 @@ private: QList m_data; // Owned. }; -class QTCREATOR_UTILS_EXPORT AspectContainer : public QObject +class QTCREATOR_UTILS_EXPORT SettingsGroupNester +{ + Q_DISABLE_COPY_MOVE(SettingsGroupNester) + +public: + explicit SettingsGroupNester(const QStringList &groups); + ~SettingsGroupNester(); + +private: + const int m_groupCount; +}; + +class QTCREATOR_UTILS_EXPORT AspectContainer : public BaseAspect { Q_OBJECT public: - AspectContainer(QObject *parent = nullptr); + AspectContainer(); ~AspectContainer(); AspectContainer(const AspectContainer &) = delete; @@ -660,32 +888,27 @@ public: void registerAspect(BaseAspect *aspect, bool takeOwnership = false); void registerAspects(const AspectContainer &aspects); - template - Aspect *addAspect(Args && ...args) - { - auto aspect = new Aspect(args...); - registerAspect(aspect, true); - return aspect; - } + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; + void volatileToMap(Utils::Store &map) const override; - void fromMap(const QVariantMap &map); - void toMap(QVariantMap &map) const; - - void readSettings(QSettings *settings); - void writeSettings(QSettings *settings) const; + void readSettings() override; + void writeSettings() const override; void setSettingsGroup(const QString &groupKey); void setSettingsGroups(const QString &groupKey, const QString &subGroupKey); + QStringList settingsGroups() const; - void apply(); - void cancel(); - void finish(); + void apply() override; + void cancel() override; + void finish() override; void reset(); bool equals(const AspectContainer &other) const; void copyFrom(const AspectContainer &other); - void setAutoApply(bool on); - bool isDirty() const; + void setAutoApply(bool on) override; + bool isDirty() override; + void setUndoStack(QUndoStack *undoStack) override; template T *aspect() const { @@ -712,6 +935,9 @@ public: const_iterator begin() const; const_iterator end() const; + void setLayouter(const std::function &layouter); + std::function layouter() const; + signals: void applied(); void changed(); @@ -721,4 +947,170 @@ private: std::unique_ptr d; }; +// Because QObject cannot be a template +class QTCREATOR_UTILS_EXPORT UndoSignaller : public QObject +{ + Q_OBJECT +public: + void emitChanged() { emit changed(); } +signals: + void changed(); +}; + +template +class QTCREATOR_UTILS_EXPORT UndoableValue +{ +public: + class UndoCmd : public QUndoCommand + { + public: + UndoCmd(UndoableValue *value, const T &oldValue, const T &newValue) + : m_value(value) + , m_oldValue(oldValue) + , m_newValue(newValue) + {} + + void undo() override { m_value->setInternal(m_oldValue); } + void redo() override { m_value->setInternal(m_newValue); } + + private: + UndoableValue *m_value; + T m_oldValue; + T m_newValue; + }; + + void set(QUndoStack *stack, const T &value) + { + if (m_value == value) + return; + + if (stack) + stack->push(new UndoCmd(this, m_value, value)); + else + setInternal(value); + } + + void setSilently(const T &value) { m_value = value; } + void setWithoutUndo(const T &value) { setInternal(value); } + + T get() const { return m_value; } + + UndoSignaller m_signal; + +private: + void setInternal(const T &value) + { + m_value = value; + m_signal.emitChanged(); + } + +private: + T m_value; +}; + +class QTCREATOR_UTILS_EXPORT AspectList : public Utils::BaseAspect +{ +public: + using CreateItem = std::function()>; + using ItemCallback = std::function)>; + + AspectList(Utils::AspectContainer *container = nullptr); + ~AspectList() override; + + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; + + void volatileToMap(Utils::Store &map) const override; + QVariantList toList(bool v) const; + + QList> items() const; + QList> volatileItems() const; + + std::shared_ptr createAndAddItem(); + std::shared_ptr addItem(const std::shared_ptr &item); + std::shared_ptr actualAddItem(const std::shared_ptr &item); + + void removeItem(const std::shared_ptr &item); + void actualRemoveItem(const std::shared_ptr &item); + void clear(); + + void apply() override; + + void setCreateItemFunction(CreateItem createItem); + + template + void forEachItem(std::function &)> callback) + { + for (const auto &item : volatileItems()) + callback(std::static_pointer_cast(item)); + } + + template + void forEachItem(std::function &, int)> callback) + { + int idx = 0; + for (const auto &item : volatileItems()) + callback(std::static_pointer_cast(item), idx++); + } + + void setItemAddedCallback(const ItemCallback &callback); + void setItemRemovedCallback(const ItemCallback &callback); + + template + void setItemAddedCallback(const std::function)> &callback) + { + setItemAddedCallback([callback](const std::shared_ptr &item) { + callback(std::static_pointer_cast(item)); + }); + } + + template + void setItemRemovedCallback(const std::function)> &callback) + { + setItemRemovedCallback([callback](const std::shared_ptr &item) { + callback(std::static_pointer_cast(item)); + }); + } + + qsizetype size() const; + bool isDirty() override; + + QVariant volatileVariantValue() const override { return {}; } + + void addToLayout(Layouting::LayoutItem &parent) override; + +private: + std::unique_ptr d; +}; + +class QTCREATOR_UTILS_EXPORT StringSelectionAspect : public Utils::TypedAspect +{ + Q_OBJECT +public: + StringSelectionAspect(Utils::AspectContainer *container = nullptr); + + void addToLayout(Layouting::LayoutItem &parent) override; + + using ResultCallback = std::function items)>; + using FillCallback = std::function; + void setFillCallback(FillCallback callback) { m_fillCallback = callback; } + + void refill() { emit refillRequested(); } + + void bufferToGui() override; + bool guiToBuffer() override; + +signals: + void refillRequested(); + +private: + QStandardItem *itemById(const QString &id); + + FillCallback m_fillCallback; + QStandardItemModel *m_model{nullptr}; + QItemSelectionModel *m_selectionModel{nullptr}; + + Utils::UndoableValue m_undoable; +}; + } // namespace Utils diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h index f8dc6813a7b..64ade0a9e3b 100644 --- a/src/libs/utils/async.h +++ b/src/libs/utils/async.h @@ -210,6 +210,7 @@ public: void start() final { this->task()->start(); } }; -} // namespace Utils +template +using AsyncTask = Tasking::CustomTask>; -TASKING_DECLARE_TEMPLATE_TASK(AsyncTask, Utils::AsyncTaskAdapter); +} // namespace Utils diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp index 14d17257841..8719bfc9b59 100644 --- a/src/libs/utils/basetreeview.cpp +++ b/src/libs/utils/basetreeview.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -92,13 +91,13 @@ public: m_userHandled.clear(); if (m_settings && !m_settingsKey.isEmpty()) { m_settings->beginGroup(m_settingsKey); - QVariantList l = m_settings->value(QLatin1String(ColumnKey)).toList(); - QTC_ASSERT(l.size() % 2 == 0, qDebug() << m_settingsKey; l.append(0)); + QVariantList l = m_settings->value(ColumnKey).toList(); + QTC_ASSERT(l.size() % 2 == 0, qDebug() << m_settingsKey.view(); l.append(0)); for (int i = 0; i < l.size(); i += 2) { int column = l.at(i).toInt(); int width = l.at(i + 1).toInt(); - QTC_ASSERT(column >= 0 && column < 20, qDebug() << m_settingsKey << column; continue); - QTC_ASSERT(width > 0 && width < 10000, qDebug() << m_settingsKey << width; continue); + QTC_ASSERT(column >= 0 && column < 20, qDebug() << m_settingsKey.view() << column; continue); + QTC_ASSERT(width > 0 && width < 10000, qDebug() << m_settingsKey.view() << width; continue); m_userHandled[column] = width; } m_settings->endGroup(); @@ -138,7 +137,7 @@ public: l.append(column); l.append(width); } - QtcSettings::setValueWithDefault(m_settings, ColumnKey, l); + m_settings->setValueWithDefault(ColumnKey, l); m_settings->endGroup(); } } @@ -306,9 +305,9 @@ public: public: BaseTreeView *q; QMap m_userHandled; // column -> width, "not present" means "automatic" - QSettings *m_settings = nullptr; + QtcSettings *m_settings = nullptr; QTimer m_settingsTimer; - QString m_settingsKey; + Key m_settingsKey; bool m_expectUserChanges = false; ProgressIndicator *m_progressIndicator = nullptr; int m_spanColumn = -1; @@ -345,7 +344,6 @@ BaseTreeView::BaseTreeView(QWidget *parent) setRootIsDecorated(false); setIconSize(QSize(16, 16)); setSelectionMode(QAbstractItemView::ExtendedSelection); - setUniformRowHeights(true); setItemDelegate(new BaseTreeViewDelegate(this)); setAlternatingRowColors(false); @@ -561,11 +559,11 @@ void BaseTreeView::refreshSpanColumn() d->rebalanceColumns(); } -void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key) +void BaseTreeView::setSettings(QtcSettings *settings, const QByteArray &key) { QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key); d->m_settings = settings; - d->m_settingsKey = QString::fromLatin1(key); + d->m_settingsKey = key; d->readSettings(); } diff --git a/src/libs/utils/basetreeview.h b/src/libs/utils/basetreeview.h index 9c8096fc4fc..a127d1087a2 100644 --- a/src/libs/utils/basetreeview.h +++ b/src/libs/utils/basetreeview.h @@ -7,12 +7,10 @@ #include "itemviews.h" -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace Utils { +class QtcSettings; + namespace Internal { class BaseTreeViewPrivate; } class QTCREATOR_UTILS_EXPORT BaseTreeView : public TreeView @@ -34,7 +32,7 @@ public: BaseTreeView(QWidget *parent = nullptr); ~BaseTreeView() override; - void setSettings(QSettings *settings, const QByteArray &key); + void setSettings(Utils::QtcSettings *settings, const QByteArray &key); void setModel(QAbstractItemModel *model) override; void mousePressEvent(QMouseEvent *ev) override; diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp index fbdd45455d5..b5d3dc2f93a 100644 --- a/src/libs/utils/changeset.cpp +++ b/src/libs/utils/changeset.cpp @@ -318,6 +318,12 @@ void ChangeSet::apply(QTextCursor *textCursor) m_cursor = nullptr; } +void ChangeSet::apply(QTextDocument *document) +{ + QTextCursor c(document); + apply(&c); +} + QString ChangeSet::textAt(int pos, int length) { if (m_string) { @@ -334,10 +340,8 @@ void ChangeSet::apply_helper() { // convert all ops to replace QList replaceList; - { - while (!m_operationList.isEmpty()) - convertToReplace(m_operationList.takeFirst(), &replaceList); - } + while (!m_operationList.isEmpty()) + convertToReplace(m_operationList.takeFirst(), &replaceList); // execute replaces if (m_cursor) diff --git a/src/libs/utils/changeset.h b/src/libs/utils/changeset.h index a6853c75f5d..4afaefab463 100644 --- a/src/libs/utils/changeset.h +++ b/src/libs/utils/changeset.h @@ -10,6 +10,7 @@ QT_BEGIN_NAMESPACE class QTextCursor; +class QTextDocument; QT_END_NAMESPACE namespace Utils { @@ -76,6 +77,7 @@ public: void apply(QString *s); void apply(QTextCursor *textCursor); + void apply(QTextDocument *document); private: // length-based API. @@ -101,6 +103,8 @@ private: bool m_error; }; +using EditOperations = QList; + inline bool operator<(const ChangeSet::Range &r1, const ChangeSet::Range &r2) { return r1.start < r2.start; diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp index d3ede7d4e7e..8e43debc4f7 100644 --- a/src/libs/utils/checkablemessagebox.cpp +++ b/src/libs/utils/checkablemessagebox.cpp @@ -5,6 +5,7 @@ #include "hostosinfo.h" #include "qtcassert.h" +#include "qtcsettings.h" #include "utilstr.h" #include @@ -12,7 +13,6 @@ #include #include #include -#include #include #include @@ -32,7 +32,7 @@ static const char kDoNotAskAgainKey[] = "DoNotAskAgain"; namespace Utils { -static QSettings *theSettings; +static QtcSettings *theSettings; static QMessageBox::StandardButton exec( QWidget *parent, @@ -53,15 +53,18 @@ static QMessageBox::StandardButton exec( msgBox.setTextFormat(Qt::RichText); msgBox.setTextInteractionFlags(Qt::LinksAccessibleByKeyboard | Qt::LinksAccessibleByMouse); +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) if (HostOsInfo::isMacHost()) { // Message boxes on macOS cannot display links. // If the message contains a link, we need to disable native dialogs. - if (text.contains("= QT_VERSION_CHECK(6, 6, 0) + if (text.contains("beginGroup(QLatin1String(kDoNotAskAgainKey)); + theSettings->beginGroup(kDoNotAskAgainKey); bool shouldNotAsk = theSettings->value(settingsSubKey, false).toBool(); theSettings->endGroup(); return !shouldNotAsk; }; doNotAskAgain = [settingsSubKey] { - theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + theSettings->beginGroup(kDoNotAskAgainKey); theSettings->setValue(settingsSubKey, true); theSettings->endGroup(); }; @@ -160,8 +163,8 @@ QMessageBox::StandardButton CheckableMessageBox::information( void CheckableMessageBox::resetAllDoNotAskAgainQuestions() { QTC_ASSERT(theSettings, return); - theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); - theSettings->remove(QString()); + theSettings->beginGroup(kDoNotAskAgainKey); + theSettings->remove(Key()); theSettings->endGroup(); } @@ -172,7 +175,7 @@ void CheckableMessageBox::resetAllDoNotAskAgainQuestions() bool CheckableMessageBox::hasSuppressedQuestions() { QTC_ASSERT(theSettings, return false); - theSettings->beginGroup(QLatin1String(kDoNotAskAgainKey)); + theSettings->beginGroup(kDoNotAskAgainKey); const bool hasSuppressed = !theSettings->childKeys().isEmpty() || !theSettings->childGroups().isEmpty(); theSettings->endGroup(); @@ -195,7 +198,7 @@ QString CheckableMessageBox::msgDoNotShowAgain() return Tr::tr("Do not &show again"); } -void CheckableMessageBox::initialize(QSettings *settings) +void CheckableMessageBox::initialize(QtcSettings *settings) { theSettings = settings; } diff --git a/src/libs/utils/checkablemessagebox.h b/src/libs/utils/checkablemessagebox.h index 297ff22b623..de5f73ca90a 100644 --- a/src/libs/utils/checkablemessagebox.h +++ b/src/libs/utils/checkablemessagebox.h @@ -8,17 +8,16 @@ #include #include -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace Utils { +class Key; +class QtcSettings; + class QTCREATOR_UTILS_EXPORT CheckableDecider { public: CheckableDecider() = default; - CheckableDecider(const QString &settingsSubKey); + CheckableDecider(const Key &settingsSubKey); CheckableDecider(bool *doNotAskAgain); CheckableDecider(const std::function &should, const std::function &doNot) : shouldAskAgain(should), doNotAskAgain(doNot) @@ -58,7 +57,7 @@ public: static QString msgDoNotAskAgain(); static QString msgDoNotShowAgain(); - static void initialize(QSettings *settings); + static void initialize(QtcSettings *settings); }; } // namespace Utils diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index 60b8d72eed2..8e6bc60277e 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -182,7 +182,7 @@ static QStringList doSplitArgsWin(const QString &args, ProcessArgs::SplitError * if (inquote) { if (err) *err = ProcessArgs::BadQuoting; - return QStringList(); + return {}; } break; } @@ -265,7 +265,7 @@ static QStringList splitArgsWin(const QString &_args, bool abortOnMeta, err = &perr; QString args = prepareArgsWin(_args, &perr, env, pwd).toWindowsArgs(); if (*err != ProcessArgs::SplitOk) - return QStringList(); + return {}; return doSplitArgsWin(args, err); } else { QString args = _args; @@ -470,12 +470,12 @@ static QStringList splitArgsUnix(const QString &args, bool abortOnMeta, quoteerr: if (err) *err = ProcessArgs::BadQuoting; - return QStringList(); + return {}; metaerr: if (err) *err = ProcessArgs::FoundMeta; - return QStringList(); + return {}; } inline static bool isSpecialCharUnix(ushort c) diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index d7fc0a066be..52ff8c5496f 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -8,7 +8,6 @@ #include "filepath.h" #include "hostosinfo.h" -#include #include #include diff --git a/src/libs/utils/delegates.cpp b/src/libs/utils/delegates.cpp index 3b0623027b6..b384faba571 100644 --- a/src/libs/utils/delegates.cpp +++ b/src/libs/utils/delegates.cpp @@ -151,7 +151,7 @@ void PathChooserDelegate::updateEditorGeometry(QWidget *editor, const QStyleOpti editor->setGeometry(option.rect); } -void PathChooserDelegate::setHistoryCompleter(const QString &key) +void PathChooserDelegate::setHistoryCompleter(const Key &key) { m_historyKey = key; } diff --git a/src/libs/utils/delegates.h b/src/libs/utils/delegates.h index ff012152f5f..328798b4b8d 100644 --- a/src/libs/utils/delegates.h +++ b/src/libs/utils/delegates.h @@ -52,12 +52,12 @@ public: void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - void setHistoryCompleter(const QString &key); + void setHistoryCompleter(const Key &key); private: PathChooser::Kind m_kind = PathChooser::ExistingDirectory; QString m_filter; - QString m_historyKey; + Key m_historyKey; }; class QTCREATOR_UTILS_EXPORT CompleterDelegate : public QStyledItemDelegate diff --git a/src/libs/utils/detailsbutton.h b/src/libs/utils/detailsbutton.h index 2f49980dbd5..1e222a0bcd1 100644 --- a/src/libs/utils/detailsbutton.h +++ b/src/libs/utils/detailsbutton.h @@ -15,8 +15,6 @@ QT_END_NAMESPACE namespace Utils { class QTCREATOR_UTILS_EXPORT FadingPanel : public QWidget { - Q_OBJECT - public: FadingPanel(QWidget *parent = nullptr) : QWidget(parent) @@ -27,7 +25,6 @@ public: class QTCREATOR_UTILS_EXPORT FadingWidget : public FadingPanel { - Q_OBJECT public: FadingWidget(QWidget *parent = nullptr); void fadeTo(qreal value) override; diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 07600599884..a38a3617a7e 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -18,10 +18,10 @@ #include #include +#include #include #include #include -#include #ifdef Q_OS_WIN #ifdef QTCREATOR_PCH_H @@ -113,14 +113,22 @@ bool DeviceFileAccess::hasHardLinks(const FilePath &filePath) const return false; } -bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +expected_str DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const { if (isWritableDirectory(filePath)) - return true; - if (exists(filePath)) - return false; + return {}; - return createDirectory(filePath); + if (exists(filePath)) { + return make_unexpected(Tr::tr("Path \"%1\" exists but is not a writable directory.") + .arg(filePath.toUserOutput())); + } + + const bool result = createDirectory(filePath); + if (result) + return {}; + + return make_unexpected( + Tr::tr("Failed to create directory \"%1\".").arg(filePath.toUserOutput())); } bool DeviceFileAccess::ensureExistingFile(const FilePath &filePath) const @@ -169,31 +177,24 @@ expected_str DeviceFileAccess::copyFile(const FilePath &filePath, const Fi expected_str copyRecursively_fallback(const FilePath &src, const FilePath &target) { - QString error; + expected_str result; src.iterateDirectory( - [&target, &src, &error](const FilePath &path) { + [&target, &src, &result](const FilePath &path) { const FilePath relative = path.relativePathFrom(src); const FilePath targetPath = target.pathAppended(relative.path()); - - if (!targetPath.parentDir().ensureWritableDir()) { - error = QString("Could not create directory %1") - .arg(targetPath.parentDir().toUserOutput()); + result = targetPath.parentDir().ensureWritableDir(); + if (!result) return IterationPolicy::Stop; - } - const expected_str result = path.copyFile(targetPath); - if (!result) { - error = result.error(); + result = path.copyFile(targetPath); + if (!result) return IterationPolicy::Stop; - } + return IterationPolicy::Continue; }, {{"*"}, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::Subdirectories}); - if (error.isEmpty()) - return {}; - - return make_unexpected(error); + return result; } expected_str DeviceFileAccess::copyRecursively(const FilePath &src, @@ -203,12 +204,12 @@ expected_str DeviceFileAccess::copyRecursively(const FilePath &src, return make_unexpected( Tr::tr("Cannot copy from \"%1\", it is not a directory.").arg(src.toUserOutput())); } - - if (!target.ensureWritableDir()) { - return make_unexpected( - Tr::tr("Cannot copy \"%1\" to \"%2\", it is not a writable directory.") - .arg(src.toUserOutput()) - .arg(target.toUserOutput())); + const expected_str result = target.ensureWritableDir(); + if (!result) { + return make_unexpected(Tr::tr("Cannot copy \"%1\" to \"%2\": %3") + .arg(src.toUserOutput()) + .arg(target.toUserOutput()) + .arg(result.error())); } #ifdef UTILS_STATIC_LIBRARY @@ -389,7 +390,6 @@ expected_str DeviceFileAccess::createTempFile(const FilePath &filePath Tr::tr("createTempFile is not implemented for \"%1\".").arg(filePath.toUserOutput())); } - // DesktopDeviceFileAccess DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default; @@ -516,15 +516,23 @@ bool DesktopDeviceFileAccess::hasHardLinks(const FilePath &filePath) const return false; } -bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +expected_str DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const { const QFileInfo fi(filePath.path()); if (fi.isDir() && fi.isWritable()) - return true; - if (fi.exists()) - return false; + return {}; - return QDir().mkpath(filePath.path()); + if (fi.exists()) { + return make_unexpected(Tr::tr("Path \"%1\" exists but is not a writable directory.") + .arg(filePath.toUserOutput())); + } + + const bool result = QDir().mkpath(filePath.path()); + if (result) + return {}; + + return make_unexpected( + Tr::tr("Failed to create directory \"%1\".").arg(filePath.toUserOutput())); } bool DesktopDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const @@ -713,10 +721,11 @@ expected_str DesktopDeviceFileAccess::writeFileContents(const FilePath & qint64 res = file.write(data); if (res != data.size()) return make_unexpected( - Tr::tr("Could not write to file \"%1\" (only %2 of %3 bytes written).") + Tr::tr("Could not write to file \"%1\" (only %2 of %n byte(s) written).", + nullptr, + data.size()) .arg(filePath.toUserOutput()) - .arg(res) - .arg(data.size())); + .arg(res)); return res; } @@ -954,7 +963,9 @@ expected_str UnixDeviceFileAccess::copyFile(const FilePath &filePath, if (result.exitCode != 0) { return make_unexpected(Tr::tr("Failed to copy file \"%1\" to \"%2\": %3") - .arg(filePath.toUserOutput(), target.toUserOutput(), QString::fromUtf8(result.stdErr))); + .arg(filePath.toUserOutput(), + target.toUserOutput(), + QString::fromUtf8(result.stdErr))); } return {}; } @@ -1089,7 +1100,8 @@ QStringList UnixDeviceFileAccess::statArgs(const FilePath &filePath, const QString &linuxFormat, const QString &macFormat) const { - return (filePath.osType() == OsTypeMac ? QStringList{"-f", macFormat} : QStringList{"-c", linuxFormat}) + return (filePath.osType() == OsTypeMac ? QStringList{"-f", macFormat} + : QStringList{"-c", linuxFormat}) << "-L" << filePath.path(); } @@ -1182,8 +1194,8 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath, // TODO: Using stat -L will always return the link target, not the link itself. // We may wan't to add the information that it is a link at some point. - const QString statFormat = filePath.osType() == OsTypeMac - ? QLatin1String("-f \"%p %m %z\"") : QLatin1String("-c \"%f %Y %s\""); + const QString statFormat = filePath.osType() == OsTypeMac ? QLatin1String("-f \"%p %m %z\"") + : QLatin1String("-c \"%f %Y %s\""); if (callBack.index() == 1) cmdLine.addArgs(QString(R"(-exec echo -n \"{}\"" " \; -exec stat -L %1 "{}" \;)") @@ -1211,24 +1223,23 @@ bool UnixDeviceFileAccess::iterateWithFind(const FilePath &filePath, const int modeBase = filePath.osType() == OsTypeMac ? 8 : 16; - const auto toFilePath = - [&filePath, &callBack, modeBase](const QString &entry) { - if (callBack.index() == 0) - return std::get<0>(callBack)(filePath.withNewPath(entry)); + const auto toFilePath = [&filePath, &callBack, modeBase](const QString &entry) { + if (callBack.index() == 0) + return std::get<0>(callBack)(filePath.withNewPath(entry)); - const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); - const QString infos = entry.mid(fileName.length() + 3); + const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); + const QString infos = entry.mid(fileName.length() + 3); - const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos, modeBase); - if (!fi.fileFlags) - return IterationPolicy::Continue; + const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos, modeBase); + if (!fi.fileFlags) + return IterationPolicy::Continue; - const FilePath fp = filePath.withNewPath(fileName); - // Do not return the entry for the directory we are searching in. - if (fp.path() == filePath.path()) - return IterationPolicy::Continue; - return std::get<1>(callBack)(fp, fi); - }; + const FilePath fp = filePath.withNewPath(fileName); + // Do not return the entry for the directory we are searching in. + if (fp.path() == filePath.path()) + return IterationPolicy::Continue; + return std::get<1>(callBack)(fp, fi); + }; // Remove the first line, this can be the directory we are searching in. // as long as we do not specify "mindepth > 0" @@ -1247,7 +1258,8 @@ void UnixDeviceFileAccess::findUsingLs(const QString ¤t, const FileFilter &filter, QStringList *found) const { - const RunResult result = runInShell({"ls", {"-1", "-a", "-p", "--", current}, OsType::OsTypeLinux}); + const RunResult result = runInShell( + {"ls", {"-1", "-a", "-p", "--", current}, OsType::OsTypeLinux}); const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); for (QString entry : entries) { const QChar last = entry.back(); @@ -1326,4 +1338,4 @@ Environment UnixDeviceFileAccess::deviceEnvironment() const return Environment(out.split('\n', Qt::SkipEmptyParts), OsTypeLinux); } -} // Utils +} // namespace Utils diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h index 0f8282477f6..6dddbc70045 100644 --- a/src/libs/utils/devicefileaccess.h +++ b/src/libs/utils/devicefileaccess.h @@ -35,7 +35,7 @@ protected: virtual bool isDirectory(const FilePath &filePath) const; virtual bool isSymLink(const FilePath &filePath) const; virtual bool hasHardLinks(const FilePath &filePath) const; - virtual bool ensureWritableDirectory(const FilePath &filePath) const; + virtual expected_str ensureWritableDirectory(const FilePath &filePath) const; virtual bool ensureExistingFile(const FilePath &filePath) const; virtual bool createDirectory(const FilePath &filePath) const; virtual bool exists(const FilePath &filePath) const; @@ -92,7 +92,7 @@ protected: bool isDirectory(const FilePath &filePath) const override; bool isSymLink(const FilePath &filePath) const override; bool hasHardLinks(const FilePath &filePath) const override; - bool ensureWritableDirectory(const FilePath &filePath) const override; + expected_str ensureWritableDirectory(const FilePath &filePath) const override; bool ensureExistingFile(const FilePath &filePath) const override; bool createDirectory(const FilePath &filePath) const override; bool exists(const FilePath &filePath) const override; diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 99ddecdeec9..6d99d399841 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -6,6 +6,7 @@ #include "process.h" #include "processinterface.h" #include "qtcassert.h" +#include "utilstr.h" #include @@ -153,23 +154,13 @@ CommandLine DeviceShell::createFallbackCommand(const CommandLine &cmd) return cmd; } -/*! - * \brief DeviceShell::startupFailed - * - * Override to display custom error messages - */ -void DeviceShell::startupFailed(const CommandLine &cmdLine) -{ - qCWarning(deviceShellLog) << "Failed to start shell via:" << cmdLine.toUserOutput(); -} - /*! * \brief DeviceShell::start * \return Returns true if starting the Shell process succeeded * * \note You have to call this function when deriving from DeviceShell. Current implementations call the function from their constructor. */ -bool DeviceShell::start() +expected_str DeviceShell::start() { m_shellProcess = std::make_unique(); connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), @@ -185,19 +176,21 @@ bool DeviceShell::start() // Moving the process into its own thread ... m_shellProcess->moveToThread(&m_thread); - bool result = false; + expected_str result; QMetaObject::invokeMethod( m_shellProcess.get(), - [this] { - qCDebug(deviceShellLog) << "Starting shell process:" << m_shellProcess->commandLine().toUserOutput(); + [this]() -> expected_str { + qCDebug(deviceShellLog) + << "Starting shell process:" << m_shellProcess->commandLine().toUserOutput(); m_shellProcess->start(); if (!m_shellProcess->waitForStarted()) { closeShellProcess(); - return false; + return make_unexpected(Tr::tr("The process failed to start.")); } - if (installShellScript()) { + auto installResult = installShellScript(); + if (installResult) { connect(m_shellProcess.get(), &Process::readyReadStandardOutput, m_shellProcess.get(), @@ -210,66 +203,67 @@ bool DeviceShell::start() qCWarning(deviceShellLog) << "Received unexpected output on stderr:" << stdErr; }); + + connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), [this] { + if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS + || m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) { + qCWarning(deviceShellLog) << "Shell exited with error code:" + << m_shellProcess->resultData().m_exitCode << "(" + << m_shellProcess->exitMessage() << ")"; + } + }); + + return {}; } else if (m_shellProcess->isRunning()) { m_shellProcess->kill(); - m_shellProcess.reset(); - return false; } + const QString stdErr = m_shellProcess->readAllStandardError(); + m_shellProcess.reset(); - connect(m_shellProcess.get(), &Process::done, m_shellProcess.get(), [this] { - if (m_shellProcess->resultData().m_exitCode != EXIT_SUCCESS - || m_shellProcess->resultData().m_exitStatus != QProcess::NormalExit) { - qCWarning(deviceShellLog) << "Shell exited with error code:" - << m_shellProcess->resultData().m_exitCode << "(" - << m_shellProcess->exitMessage() << ")"; - } - }); - - return true; + return make_unexpected(Tr::tr("Failed to install shell script: %1\n%2") + .arg(installResult.error()) + .arg(stdErr)); }, Qt::BlockingQueuedConnection, &result); - if (!result) { - startupFailed(cmdLine); - } - return result; } -bool DeviceShell::checkCommand(const QByteArray &command) +expected_str DeviceShell::checkCommand(const QByteArray &command) { - const QByteArray checkCmd = "(which " + command + " || echo '')\n"; + const QByteArray checkCmd = "(type " + command + " || echo '')\n"; m_shellProcess->writeRaw(checkCmd); if (!m_shellProcess->waitForReadyRead()) { - qCWarning(deviceShellLog) << "Timeout while trying to check for" << command; - return false; + return make_unexpected( + Tr::tr("Timeout while trying to check for %1.").arg(QString::fromUtf8(command))); } QByteArray out = m_shellProcess->readAllRawStandardOutput(); if (out.contains("")) { m_shellScriptState = State::Failed; - qCWarning(deviceShellLog) << "Command" << command << "was not found"; m_missingFeatures.append(QString::fromUtf8(command)); - return false; + return make_unexpected( + Tr::tr("Command \"%1\" was not found.").arg(QString::fromUtf8(command))); } - return true; + return out; } -bool DeviceShell::installShellScript() +expected_str DeviceShell::installShellScript() { if (m_forceFailScriptInstallation) { m_shellScriptState = State::Failed; - return false; + return make_unexpected(Tr::tr("Script installation was forced to fail.")); } static const QList requiredCommands = {"base64", "cat", "echo", "kill", "mkfifo", "mktemp", "rm"}; for (const QByteArray &command : requiredCommands) { - if (!checkCommand(command)) - return false; + auto checkResult = checkCommand(command); + if (!checkResult) + return make_unexpected(checkResult.error()); } const static QByteArray shellScriptBase64 = FilePath(":/utils/scripts/deviceshell.sh") @@ -286,19 +280,18 @@ bool DeviceShell::installShellScript() while (m_shellScriptState == State::Unknown) { if (!m_shellProcess->waitForReadyRead(5000)) { - qCWarning(deviceShellLog) << "Timeout while waiting for shell script installation"; - return false; + return make_unexpected(Tr::tr("Timeout while waiting for shell script installation.")); } QByteArray out = m_shellProcess->readAllRawStandardError(); if (out.contains("SCRIPT_INSTALLED") && !out.contains("ERROR_INSTALL_SCRIPT")) { m_shellScriptState = State::Succeeded; - return true; + return {}; } if (out.contains("ERROR_INSTALL_SCRIPT")) { m_shellScriptState = State::Failed; - qCWarning(deviceShellLog) << "Failed installing device shell script"; - return false; + return make_unexpected( + Tr::tr("Failed to install shell script: %1").arg(QString::fromUtf8(out))); } if (!out.isEmpty()) { qCWarning(deviceShellLog) @@ -306,7 +299,7 @@ bool DeviceShell::installShellScript() } } - return true; + return {}; } void DeviceShell::closeShellProcess() diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index e5bc4ad7afe..73d9805e104 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -3,9 +3,9 @@ #pragma once -#include "utils_global.h" - +#include "expected.h" #include "fileutils.h" +#include "utils_global.h" #include #include @@ -39,7 +39,7 @@ public: DeviceShell(bool forceFailScriptInstallation = false); virtual ~DeviceShell(); - bool start(); + expected_str start(); RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); @@ -51,7 +51,6 @@ signals: void done(const ProcessResultData &resultData); protected: - virtual void startupFailed(const CommandLine &cmdLine); RunResult run(const CommandLine &cmd, const QByteArray &stdInData = {}); void close(); @@ -60,12 +59,12 @@ private: virtual void setupShellProcess(Process *shellProcess); virtual CommandLine createFallbackCommand(const CommandLine &cmdLine); - bool installShellScript(); + expected_str installShellScript(); void closeShellProcess(); void onReadyRead(); - bool checkCommand(const QByteArray &command); + expected_str checkCommand(const QByteArray &command); private: struct CommandRun : public RunResult diff --git a/src/libs/utils/differ.cpp b/src/libs/utils/differ.cpp index f929efbeeb0..bc7dda6f302 100644 --- a/src/libs/utils/differ.cpp +++ b/src/libs/utils/differ.cpp @@ -13,7 +13,6 @@ publication by Neil Fraser: http://neil.fraser.name/writing/diff/ #include "utilstr.h" -#include #include #include #include @@ -579,7 +578,7 @@ static QList decodeExpandedWhitespace(const QList &input, const int replacementSize = it.value().first; const int reversePosition = diffCount + counter - it.key(); if (reversePosition < replacementSize) - return QList(); // replacement exceeds one Diff + return {}; // replacement exceeds one Diff const QString replacement = it.value().second; const int updatedDiffCount = diff.text.size(); diff.text.replace(updatedDiffCount - reversePosition, @@ -978,7 +977,7 @@ Differ::DiffMode Differ::diffMode() const QList Differ::preprocess1AndDiff(const QString &text1, const QString &text2) { if (text1.isNull() && text2.isNull()) - return QList(); + return {}; if (text1 == text2) { QList diffList; @@ -1075,7 +1074,7 @@ QList Differ::diffMyers(const QString &text1, const QString &text2) if (m_future && m_future->isCanceled()) { delete [] forwardV; delete [] reverseV; - return QList(); + return {}; } // going forward for (int k = qMax(-d, kMinForward + qAbs(d + kMinForward) % 2); diff --git a/src/libs/utils/displayname.cpp b/src/libs/utils/displayname.cpp index b4082a60922..a2f622143c6 100644 --- a/src/libs/utils/displayname.cpp +++ b/src/libs/utils/displayname.cpp @@ -35,13 +35,13 @@ bool DisplayName::usesDefaultValue() const return m_value.isEmpty(); } -void DisplayName::toMap(QVariantMap &map, const QString &key) const +void DisplayName::toMap(Store &map, const Key &key) const { - if (!usesDefaultValue()) + if (m_forceSerialization || !usesDefaultValue()) map.insert(key, m_value); } -void DisplayName::fromMap(const QVariantMap &map, const QString &key) +void DisplayName::fromMap(const Store &map, const Key &key) { m_value = map.value(key).toString(); } diff --git a/src/libs/utils/displayname.h b/src/libs/utils/displayname.h index c1e7f7b10a3..fd4977c7586 100644 --- a/src/libs/utils/displayname.h +++ b/src/libs/utils/displayname.h @@ -5,8 +5,7 @@ #include "utils_global.h" -#include -#include +#include "store.h" namespace Utils { @@ -22,13 +21,15 @@ public: QString value() const; QString defaultValue() const { return m_defaultValue; } bool usesDefaultValue() const; + void forceSerialization() { m_forceSerialization = true; } - void toMap(QVariantMap &map, const QString &key) const; - void fromMap(const QVariantMap &map, const QString &key); + void toMap(Store &map, const Key &key) const; + void fromMap(const Store &map, const Key &key); private: QString m_value; QString m_defaultValue; + bool m_forceSerialization = false; }; bool QTCREATOR_UTILS_EXPORT operator==(const DisplayName &dn1, const DisplayName &dn2); diff --git a/src/libs/utils/dropsupport.cpp b/src/libs/utils/dropsupport.cpp index 6def3fd1aff..7416edf9c37 100644 --- a/src/libs/utils/dropsupport.cpp +++ b/src/libs/utils/dropsupport.cpp @@ -10,7 +10,7 @@ #include #include -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS // for file drops from Finder, working around QTBUG-40449 #include "fileutils_mac.h" #endif @@ -63,7 +63,7 @@ DropSupport::DropSupport(QWidget *parentWidget, const DropFilterFunction &filter QStringList DropSupport::mimeTypesForFilePaths() { - return QStringList("text/uri-list"); + return {"text/uri-list"}; } bool DropSupport::isFileDrop(QDropEvent *event) @@ -181,7 +181,7 @@ void DropMimeData::addFile(const FilePath &filePath, int line, int column) { // standard mime data QList currentUrls = urls(); - currentUrls.append(QUrl::fromLocalFile(filePath.toString())); + currentUrls.append(filePath.toUrl()); setUrls(currentUrls); // special mime data m_files.append(DropSupport::FileSpec(filePath, line, column)); diff --git a/src/libs/utils/elfreader.h b/src/libs/utils/elfreader.h index d1506c81f00..a254b8e221e 100644 --- a/src/libs/utils/elfreader.h +++ b/src/libs/utils/elfreader.h @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include namespace Utils { @@ -130,8 +130,8 @@ public: QByteArray debugLink; QByteArray buildId; DebugSymbolsType symbolsType = UnknownSymbols; - QVector sectionHeaders; - QVector programHeaders; + QList sectionHeaders; + QList programHeaders; }; class QTCREATOR_UTILS_EXPORT ElfReader diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index b9af2dea928..d71d76c8b8a 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -24,7 +24,7 @@ namespace Utils { static QReadWriteLock s_envMutex; Q_GLOBAL_STATIC_WITH_ARGS(Environment, staticSystemEnvironment, (QProcessEnvironment::systemEnvironment().toStringList())) -Q_GLOBAL_STATIC(QVector, environmentProviders) +Q_GLOBAL_STATIC(QList, environmentProviders) Environment::Environment() : m_dict(HostOsInfo::hostOs()) @@ -149,6 +149,11 @@ void Environment::prependOrSetPath(const FilePath &value) prependOrSet("PATH", value.nativePath(), OsSpecificAspects::pathListSeparator(osType())); } +void Environment::prependOrSetPath(const QString &directories) +{ + prependOrSet("PATH", directories, OsSpecificAspects::pathListSeparator(osType())); +} + void Environment::appendOrSet(const QString &key, const QString &value, const QString &sep) { addItem(Item{std::in_place_index_t(), key, value, sep}); @@ -220,11 +225,12 @@ QString Environment::expandedValueForKey(const QString &key) const FilePath Environment::searchInPath(const QString &executable, const FilePaths &additionalDirs, - const FilePathPredicate &filter) const + const FilePathPredicate &filter, + FilePath::MatchScope scope) const { const FilePath exec = FilePath::fromUserInput(expandVariables(executable)); const FilePaths dirs = path() + additionalDirs; - return exec.searchInDirectories(dirs, filter, FilePath::WithAnySuffix); + return exec.searchInDirectories(dirs, filter, scope); } FilePaths Environment::path() const @@ -352,7 +358,7 @@ void EnvironmentProvider::addProvider(EnvironmentProvider &&provider) environmentProviders->append(std::move(provider)); } -const QVector EnvironmentProvider::providers() +const QList EnvironmentProvider::providers() { return *environmentProviders; } diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index 63fe697bd6e..679b28f0e08 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -48,6 +48,7 @@ public: void appendOrSetPath(const FilePath &value); void prependOrSetPath(const FilePath &value); + void prependOrSetPath(const QString &directories); // Could be several ':'/';' separated entries. void prependOrSetLibrarySearchPath(const FilePath &value); void prependOrSetLibrarySearchPaths(const FilePaths &values); @@ -60,7 +61,8 @@ public: FilePath searchInPath(const QString &executable, const FilePaths &additionalDirs = FilePaths(), - const FilePathPredicate &func = {}) const; + const FilePathPredicate &func = {}, + FilePath::MatchScope = FilePath::WithAnySuffix) const; FilePaths path() const; FilePaths pathListValue(const QString &varName) const; @@ -134,7 +136,7 @@ public: std::function environment; static void addProvider(EnvironmentProvider &&provider); - static const QVector providers(); + static const QList providers(); static std::optional provider(const QByteArray &id); }; diff --git a/src/libs/utils/environmentdialog.h b/src/libs/utils/environmentdialog.h index 5c60f3d2a09..335ba7ffe58 100644 --- a/src/libs/utils/environmentdialog.h +++ b/src/libs/utils/environmentdialog.h @@ -7,18 +7,16 @@ #include "environmentfwd.h" #include "namevaluesdialog.h" -#include namespace Utils { class QTCREATOR_UTILS_EXPORT EnvironmentDialog : public NameValuesDialog { - Q_OBJECT public: static std::optional getEnvironmentItems(QWidget *parent = nullptr, - const EnvironmentItems &initial = {}, - const QString &placeholderText = {}, - Polisher polish = {}); + const EnvironmentItems &initial = {}, + const QString &placeholderText = {}, + Polisher polish = {}); }; } // namespace Utils diff --git a/src/libs/utils/environmentfwd.h b/src/libs/utils/environmentfwd.h index 2975cbb64a7..51089eeee19 100644 --- a/src/libs/utils/environmentfwd.h +++ b/src/libs/utils/environmentfwd.h @@ -3,7 +3,7 @@ #pragma once -#include +#include QT_BEGIN_NAMESPACE class QTreeView; @@ -12,7 +12,7 @@ QT_END_NAMESPACE namespace Utils { class NameValueDictionary; class NameValueItem; -using NameValueItems = QVector; +using NameValueItems = QList; class Environment; using EnvironmentItem = NameValueItem; diff --git a/src/libs/utils/environmentmodel.h b/src/libs/utils/environmentmodel.h index 46c16b4e73c..82bf98d43f5 100644 --- a/src/libs/utils/environmentmodel.h +++ b/src/libs/utils/environmentmodel.h @@ -11,8 +11,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT EnvironmentModel : public NameValueModel { - Q_OBJECT - public: Environment baseEnvironment() const; void setBaseEnvironment(const Environment &env); diff --git a/src/libs/utils/execmenu.cpp b/src/libs/utils/execmenu.cpp index d607a8de69a..13d79152953 100644 --- a/src/libs/utils/execmenu.cpp +++ b/src/libs/utils/execmenu.cpp @@ -3,6 +3,8 @@ #include "execmenu.h" +#include "tooltip/tooltip.h" + #include #include #include @@ -41,4 +43,14 @@ QAction *execMenuAtWidget(QMenu *menu, QWidget *widget) return menu->exec(p); } +/*! + Adds tool tips to the menu that show the actions tool tip when hovering over an entry. + */ +void addToolTipsToMenu(QMenu *menu) +{ + QObject::connect(menu, &QMenu::hovered, menu, [menu](QAction *action) { + ToolTip::show(menu->mapToGlobal(menu->actionGeometry(action).topRight()), action->toolTip()); + }); +} + } // namespace Utils diff --git a/src/libs/utils/execmenu.h b/src/libs/utils/execmenu.h index 90fac94c0e8..9ad63d88ead 100644 --- a/src/libs/utils/execmenu.h +++ b/src/libs/utils/execmenu.h @@ -14,5 +14,6 @@ QT_END_NAMESPACE namespace Utils { QTCREATOR_UTILS_EXPORT QAction *execMenuAtWidget(QMenu *menu, QWidget *widget); +QTCREATOR_UTILS_EXPORT void addToolTipsToMenu(QMenu *menu); } // namespace Utils diff --git a/src/libs/utils/faketooltip.h b/src/libs/utils/faketooltip.h index 7df85a816ec..1f8f3acc57c 100644 --- a/src/libs/utils/faketooltip.h +++ b/src/libs/utils/faketooltip.h @@ -11,8 +11,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT FakeToolTip : public QWidget { - Q_OBJECT - public: explicit FakeToolTip(QWidget *parent = nullptr); diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp index 2bf50a70fa1..a3acaad0f44 100644 --- a/src/libs/utils/fancylineedit.cpp +++ b/src/libs/utils/fancylineedit.cpp @@ -7,19 +7,23 @@ #include "execmenu.h" #include "historycompleter.h" #include "hostosinfo.h" +#include "icon.h" #include "qtcassert.h" -#include "utilsicons.h" #include "utilstr.h" +#include + #include +#include #include #include #include -#include -#include #include +#include #include #include +#include +#include #include #include @@ -45,8 +49,7 @@ \li A history completer. - \li The ability to validate the contents of the text field by overriding - virtual \c validate() function in derived clases. + \li The ability to validate the contents of the text field by setting the \a validationFunction. \endlist When invalid, the text color will turn red and a tooltip will @@ -99,7 +102,7 @@ public: bool eventFilter(QObject *obj, QEvent *event) override; FancyLineEdit *m_lineEdit; - IconButton *m_iconbutton[2]; + FancyIconButton *m_iconbutton[2]; HistoryCompleter *m_historyCompleter = nullptr; QShortcut m_completionShortcut; FancyLineEdit::ValidationFunction m_validationFunction = &FancyLineEdit::validateWithValidator; @@ -120,23 +123,35 @@ public: const QColor m_errorTextColor; const QColor m_placeholderTextColor; QString m_errorMessage; + + SpinnerSolution::Spinner *m_spinner = nullptr; + QTimer m_spinnerDelayTimer; + + std::unique_ptr> m_validatorWatcher; }; -FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) : - QObject(parent), - m_lineEdit(parent), - m_completionShortcut(completionShortcut()->key(), parent), - m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)), - m_errorTextColor(creatorTheme()->color(Theme::TextColorError)), - m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText)) - +FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) + : QObject(parent) + , m_lineEdit(parent) + , m_completionShortcut(completionShortcut()->key(), parent) + , m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)) + , m_errorTextColor(creatorTheme()->color(Theme::TextColorError)) + , m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText)) + , m_spinner(new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Small, m_lineEdit)) { + m_spinner->setVisible(false); + m_spinnerDelayTimer.setInterval(200); + m_spinnerDelayTimer.setSingleShot(true); + connect(&m_spinnerDelayTimer, &QTimer::timeout, m_spinner, [spinner = m_spinner] { + spinner->setVisible(true); + }); + m_completionShortcut.setContext(Qt::WidgetShortcut); connect(completionShortcut(), &CompletionShortcut::keyChanged, &m_completionShortcut, &QShortcut::setKey); for (int i = 0; i < 2; ++i) { - m_iconbutton[i] = new IconButton(parent); + m_iconbutton[i] = new FancyIconButton(parent); m_iconbutton[i]->installEventFilter(this); m_iconbutton[i]->hide(); m_iconbutton[i]->setAutoHide(false); @@ -331,7 +346,7 @@ bool FancyLineEdit::hasAutoHideButton(Side side) const return d->m_iconbutton[side]->hasAutoHide(); } -void FancyLineEdit::setHistoryCompleter(const QString &historyKey, bool restoreLastItemFromHistory) +void FancyLineEdit::setHistoryCompleter(const Key &historyKey, bool restoreLastItemFromHistory) { QTC_ASSERT(!d->m_historyCompleter, return); d->m_historyCompleter = new HistoryCompleter(historyKey, this); @@ -413,15 +428,13 @@ void FancyLineEdit::setFiltering(bool on) d->m_isFiltering = on; if (on) { d->m_lastFilterText = text(); - // KDE has custom icons for this. Notice that icon namings are counter intuitive. - // If these icons are not available we use the freedesktop standard name before - // falling back to a bundled resource. - QIcon icon = QIcon::fromTheme(layoutDirection() == Qt::LeftToRight ? - QLatin1String("edit-clear-locationbar-rtl") : - QLatin1String("edit-clear-locationbar-ltr"), - QIcon::fromTheme(QLatin1String("edit-clear"), - Icons::EDIT_CLEAR.icon())); - + // KDE has custom icons for this. The "ltr" and "rtl" suffixes describe the direction + // into which the arrows are pointing. They do not describe which writing direction they + // are intended to be used for. + const QLatin1String pointingWest("edit-clear-locationbar-rtl"); + const QLatin1String pointingEast("edit-clear-locationbar-ltr"); + const QIcon icon = Icon::fromTheme(layoutDirection() == Qt::LeftToRight ? pointingWest + : pointingEast); setButtonIcon(Right, icon); setButtonVisible(Right, true); setPlaceholderText(Tr::tr("Filter")); @@ -475,21 +488,20 @@ void FancyLineEdit::setValidatePlaceHolder(bool on) d->m_validatePlaceHolder = on; } -void FancyLineEdit::validate() +void FancyLineEdit::handleValidationResult(AsyncValidationResult result, const QString &oldText) { - const QString t = text(); + d->m_spinner->setVisible(false); + d->m_spinnerDelayTimer.stop(); - if (d->m_isFiltering){ - if (t != d->m_lastFilterText) { - d->m_lastFilterText = t; - emit filterChanged(t); - } - } + const QString newText = result ? *result : oldText; + if (!result) + d->m_errorMessage = result.error(); + else + d->m_errorMessage.clear(); - d->m_errorMessage.clear(); // Are we displaying the placeholder text? - const bool isDisplayingPlaceholderText = !placeholderText().isEmpty() && t.isEmpty(); - const bool validates = d->m_validationFunction(this, &d->m_errorMessage); + const bool isDisplayingPlaceholderText = !placeholderText().isEmpty() && newText.isEmpty(); + const bool validates = result.has_value(); const State newState = isDisplayingPlaceholderText ? DisplayingPlaceholderText : (validates ? Valid : Invalid); if (!validates || d->m_toolTipSet) { @@ -504,18 +516,20 @@ void FancyLineEdit::validate() d->m_firstChange = false; QPalette p = palette(); - p.setColor(QPalette::Active, QPalette::Text, - newState == Invalid ? d->m_errorTextColor : d->m_okTextColor); - p.setColor(QPalette::Active, QPalette::PlaceholderText, - validates || !d->m_validatePlaceHolder - ? d->m_placeholderTextColor : d->m_errorTextColor); + p.setColor(QPalette::Active, + QPalette::Text, + newState == Invalid ? d->m_errorTextColor : d->m_okTextColor); + p.setColor(QPalette::Active, + QPalette::PlaceholderText, + validates || !d->m_validatePlaceHolder ? d->m_placeholderTextColor + : d->m_errorTextColor); setPalette(p); if (validHasChanged) emit validChanged(newState == Valid); } - const QString fixedString = fixInputString(t); - if (t != fixedString) { + const QString fixedString = fixInputString(newText); + if (newText != fixedString) { const int cursorPos = cursorPosition(); QSignalBlocker blocker(this); setText(fixedString); @@ -523,15 +537,79 @@ void FancyLineEdit::validate() } // Check buttons. - if (d->m_oldText.isEmpty() || t.isEmpty()) { + if (d->m_oldText.isEmpty() || newText.isEmpty()) { for (auto &button : std::as_const(d->m_iconbutton)) { if (button->hasAutoHide()) - button->animateShow(!t.isEmpty()); + button->animateShow(!newText.isEmpty()); } - d->m_oldText = t; + d->m_oldText = newText; } - handleChanged(t); + handleChanged(newText); +} + +void FancyLineEdit::validate() +{ + if (d->m_validationFunction.index() == 0) { + AsyncValidationFunction &validationFunction = std::get<0>(d->m_validationFunction); + if (!validationFunction) + return; + + if (d->m_validatorWatcher) + d->m_validatorWatcher->cancel(); + + const QString oldText = text(); + + if (d->m_isFiltering) { + if (oldText != d->m_lastFilterText) { + d->m_lastFilterText = oldText; + emit filterChanged(oldText); + } + } + + d->m_validatorWatcher = std::make_unique>(); + connect(d->m_validatorWatcher.get(), + &QFutureWatcher::finished, + this, + [this, oldText]() { + FancyLineEdit::AsyncValidationResult result = d->m_validatorWatcher->result(); + + handleValidationResult(result, oldText); + }); + + d->m_spinnerDelayTimer.start(); + + AsyncValidationFuture future = validationFunction(text()); + d->m_validatorWatcher->setFuture(future); + + return; + } + + if (d->m_validationFunction.index() == 1) { + auto &validationFunction = std::get<1>(d->m_validationFunction); + if (!validationFunction) + return; + + const QString t = text(); + + if (d->m_isFiltering) { + if (t != d->m_lastFilterText) { + d->m_lastFilterText = t; + emit filterChanged(t); + } + } + + QString error; + const bool validates = validationFunction(this, &error); + expected_str result; + + if (validates) + result = t; + else + result = make_unexpected(error); + + handleValidationResult(result, t); + } } QString FancyLineEdit::fixInputString(const QString &string) @@ -543,14 +621,14 @@ QString FancyLineEdit::fixInputString(const QString &string) // IconButton - helper class to represent a clickable icon // -IconButton::IconButton(QWidget *parent) - : QAbstractButton(parent), m_autoHide(false) +FancyIconButton::FancyIconButton(QWidget *parent) + : QAbstractButton(parent) { setCursor(Qt::ArrowCursor); setFocusPolicy(Qt::NoFocus); } -void IconButton::paintEvent(QPaintEvent *) +void FancyIconButton::paintEvent(QPaintEvent *) { const qreal pixelRatio = window()->windowHandle()->devicePixelRatio(); const QPixmap iconPixmap = icon().pixmap(sizeHint(), pixelRatio, @@ -577,7 +655,7 @@ void IconButton::paintEvent(QPaintEvent *) } } -void IconButton::animateShow(bool visible) +void FancyIconButton::animateShow(bool visible) { QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity"); animation->setDuration(FADE_TIME); @@ -585,13 +663,13 @@ void IconButton::animateShow(bool visible) animation->start(QAbstractAnimation::DeleteWhenStopped); } -QSize IconButton::sizeHint() const +QSize FancyIconButton::sizeHint() const { QWindow *window = this->window()->windowHandle(); return icon().actualSize(window, QSize(32, 16)); // Find flags icon can be wider than 16px } -void IconButton::keyPressEvent(QKeyEvent *ke) +void FancyIconButton::keyPressEvent(QKeyEvent *ke) { QAbstractButton::keyPressEvent(ke); if (!ke->modifiers() && (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return)) @@ -600,7 +678,7 @@ void IconButton::keyPressEvent(QKeyEvent *ke) ke->accept(); } -void IconButton::keyReleaseEvent(QKeyEvent *ke) +void FancyIconButton::keyReleaseEvent(QKeyEvent *ke) { QAbstractButton::keyReleaseEvent(ke); // do not forward to line edit diff --git a/src/libs/utils/fancylineedit.h b/src/libs/utils/fancylineedit.h index eb54bc8d866..a8bd9501274 100644 --- a/src/libs/utils/fancylineedit.h +++ b/src/libs/utils/fancylineedit.h @@ -6,8 +6,11 @@ #include "utils_global.h" #include "completinglineedit.h" +#include "expected.h" +#include "storekey.h" #include +#include #include @@ -20,13 +23,13 @@ namespace Utils { class FancyLineEditPrivate; -class QTCREATOR_UTILS_EXPORT IconButton: public QAbstractButton +class QTCREATOR_UTILS_EXPORT FancyIconButton : public QAbstractButton { Q_OBJECT Q_PROPERTY(float iconOpacity READ iconOpacity WRITE setIconOpacity) Q_PROPERTY(bool autoHide READ hasAutoHide WRITE setAutoHide) public: - explicit IconButton(QWidget *parent = nullptr); + explicit FancyIconButton(QWidget *parent = nullptr); void paintEvent(QPaintEvent *event) override; float iconOpacity() { return m_iconOpacity; } void setIconOpacity(float value) { m_iconOpacity = value; update(); } @@ -42,8 +45,8 @@ protected: void keyReleaseEvent(QKeyEvent *ke) override; private: - float m_iconOpacity; - bool m_autoHide; + float m_iconOpacity = 1.0f; + bool m_autoHide = false; QIcon m_icon; }; @@ -84,7 +87,7 @@ public: // Completion // Enable a history completer with a history of entries. - void setHistoryCompleter(const QString &historyKey, bool restoreLastItemFromHistory = false); + void setHistoryCompleter(const Utils::Key &historyKey, bool restoreLastItemFromHistory = false); // Sets a completer that is not a history completer. void setSpecialCompleter(QCompleter *completer); @@ -98,7 +101,12 @@ public: // Validation // line edit, (out)errorMessage -> valid? - using ValidationFunction = std::function; + using AsyncValidationResult = Utils::expected_str; + using AsyncValidationFuture = QFuture; + using AsyncValidationFunction = std::function; + using SynchronousValidationFunction = std::function; + using ValidationFunction = std::variant; + enum State { Invalid, DisplayingPlaceholderText, Valid }; State state() const; @@ -138,6 +146,8 @@ protected: private: void iconClicked(FancyLineEdit::Side); + void handleValidationResult(AsyncValidationResult result, const QString &oldText); + static bool validateWithValidator(FancyLineEdit *edit, QString *errorMessage); // Unimplemented, to force the user to make a decision on // whether to use setHistoryCompleter() or setSpecialCompleter(). diff --git a/src/libs/utils/fancymainwindow.cpp b/src/libs/utils/fancymainwindow.cpp index 323e2508eba..ad5e4ed9c3a 100644 --- a/src/libs/utils/fancymainwindow.cpp +++ b/src/libs/utils/fancymainwindow.cpp @@ -5,6 +5,7 @@ #include "algorithm.h" #include "qtcassert.h" +#include "qtcsettings.h" #include "stringutils.h" #include "utilstr.h" @@ -16,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -169,6 +169,11 @@ public: setLayout(layout); setProperty("managed_titlebar", 1); + + connect(parent, &QDockWidget::featuresChanged, this, [this, parent] { + m_closeButton->setVisible(parent->features().testFlag(QDockWidget::DockWidgetClosable)); + m_floatButton->setVisible(parent->features().testFlag(QDockWidget::DockWidgetFloatable)); + }); } void enterEvent(QEnterEvent *event) override @@ -187,8 +192,11 @@ public: { bool clickable = isClickable(); m_titleLabel->setVisible(clickable); - m_floatButton->setVisible(clickable); - m_closeButton->setVisible(clickable); + + m_floatButton->setVisible(clickable + && q->features().testFlag(QDockWidget::DockWidgetFloatable)); + m_closeButton->setVisible(clickable + && q->features().testFlag(QDockWidget::DockWidgetClosable)); } bool isClickable() const @@ -349,7 +357,8 @@ FancyMainWindowPrivate::FancyMainWindowPrivate(FancyMainWindow *parent) : }); QObject::connect(&m_showCentralWidget, &QAction::toggled, q, [this](bool visible) { - q->centralWidget()->setVisible(visible); + if (q->centralWidget()) + q->centralWidget()->setVisible(visible); }); } @@ -434,53 +443,70 @@ void FancyMainWindow::handleVisibilityChanged(bool visible) d->m_handleDockVisibilityChanges = true; } -void FancyMainWindow::saveSettings(QSettings *settings) const +void FancyMainWindow::saveSettings(QtcSettings *settings) const { - const QHash hash = saveSettings(); + const QHash hash = saveSettings(); for (auto it = hash.cbegin(), end = hash.cend(); it != end; ++it) settings->setValue(it.key(), it.value()); } -void FancyMainWindow::restoreSettings(const QSettings *settings) +void FancyMainWindow::restoreSettings(const QtcSettings *settings) { - QHash hash; - const QStringList childKeys = settings->childKeys(); - for (const QString &key : childKeys) + QHash hash; + const KeyList childKeys = settings->childKeys(); + for (const Key &key : childKeys) hash.insert(key, settings->value(key)); restoreSettings(hash); } -QHash FancyMainWindow::saveSettings() const +QHash FancyMainWindow::saveSettings() const { - QHash settings; - settings.insert(QLatin1String(StateKey), saveState(settingsVersion)); - settings.insert(QLatin1String(AutoHideTitleBarsKey), - d->m_autoHideTitleBars.isChecked()); + QHash settings; + settings.insert(StateKey, saveState(settingsVersion)); + settings.insert(AutoHideTitleBarsKey, d->m_autoHideTitleBars.isChecked()); settings.insert(ShowCentralWidgetKey, d->m_showCentralWidget.isChecked()); for (QDockWidget *dockWidget : dockWidgets()) { - settings.insert(dockWidget->objectName(), + settings.insert(keyFromString(dockWidget->objectName()), dockWidget->property(dockWidgetActiveState)); } return settings; } -void FancyMainWindow::restoreSettings(const QHash &settings) +void FancyMainWindow::restoreSettings(const QHash &settings) { - QByteArray ba = settings.value(QLatin1String(StateKey), QByteArray()).toByteArray(); - if (!ba.isEmpty()) - restoreState(ba, settingsVersion); - bool on = settings.value(QLatin1String(AutoHideTitleBarsKey), true).toBool(); + QByteArray ba = settings.value(StateKey, QByteArray()).toByteArray(); + if (!ba.isEmpty()) { + if (!restoreState(ba, settingsVersion)) + qWarning() << "Restoring the state of dock widgets failed."; + } + bool on = settings.value(AutoHideTitleBarsKey, true).toBool(); d->m_autoHideTitleBars.setChecked(on); d->m_showCentralWidget.setChecked(settings.value(ShowCentralWidgetKey, true).toBool()); for (QDockWidget *widget : dockWidgets()) { widget->setProperty(dockWidgetActiveState, - settings.value(widget->objectName(), false)); + settings.value(keyFromString(widget->objectName()), false)); + } +} + +static void findDockChildren(QWidget *parent, QList &result) +{ + for (QObject *child : parent->children()) { + QWidget *childWidget = qobject_cast(child); + if (!childWidget) + continue; + + if (auto dockWidget = qobject_cast(child)) + result.append(dockWidget); + else if (!qobject_cast(child)) + findDockChildren(qobject_cast(child), result); } } const QList FancyMainWindow::dockWidgets() const { - return findChildren(); + QList result; + findDockChildren((QWidget *) this, result); + return result; } bool FancyMainWindow::autoHideTitleBars() const diff --git a/src/libs/utils/fancymainwindow.h b/src/libs/utils/fancymainwindow.h index 19618bb41c4..40066de95f6 100644 --- a/src/libs/utils/fancymainwindow.h +++ b/src/libs/utils/fancymainwindow.h @@ -5,15 +5,14 @@ #include "utils_global.h" -#include +#include "storekey.h" -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include namespace Utils { struct FancyMainWindowPrivate; +class QtcSettings; class QTCREATOR_UTILS_EXPORT FancyMainWindow : public QMainWindow { @@ -30,10 +29,10 @@ public: void setTrackingEnabled(bool enabled); - void saveSettings(QSettings *settings) const; - void restoreSettings(const QSettings *settings); - QHash saveSettings() const; - void restoreSettings(const QHash &settings); + void saveSettings(QtcSettings *settings) const; + void restoreSettings(const QtcSettings *settings); + QHash saveSettings() const; + void restoreSettings(const QHash &settings); // Additional context menu actions QAction *menuSeparator1() const; diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp index 957f694179b..29e0f6d7c2c 100644 --- a/src/libs/utils/fileinprojectfinder.cpp +++ b/src/libs/utils/fileinprojectfinder.cpp @@ -477,7 +477,7 @@ FilePath chooseFileFromList(const FilePaths &candidates) filesMenu.addAction(candidate.toUserOutput()); if (const QAction * const action = filesMenu.exec(QCursor::pos())) return FilePath::fromUserInput(action->text()); - return FilePath(); + return {}; } } // namespace Utils diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 3995ddde3a7..8b898344278 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -225,7 +225,7 @@ bool FilePath::isRootPath() const return true; } - return *this == FilePath::fromString(QDir::rootPath()); + return *this == HostOsInfo::root(); } QString FilePath::encodedHost() const @@ -475,30 +475,6 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, QStrin m_pathLen = path.size(); } -/*! - Returns a bool indicating whether a file or directory with this FilePath exists. -*/ -bool FilePath::exists() const -{ - return fileAccess()->exists(*this); -} - -/*! - Returns a bool indicating whether this is a writable directory. -*/ -bool FilePath::isWritableDir() const -{ - return fileAccess()->isWritableDirectory(*this); -} - -/*! - Returns a bool indicating whether this is a writable file. -*/ -bool FilePath::isWritableFile() const -{ - return fileAccess()->isWritableFile(*this); -} - /*! \brief Re-uses or creates a directory in this location. @@ -506,7 +482,7 @@ bool FilePath::isWritableFile() const \sa createDir() */ -bool FilePath::ensureWritableDir() const +expected_str FilePath::ensureWritableDir() const { return fileAccess()->ensureWritableDirectory(*this); } @@ -516,14 +492,6 @@ bool FilePath::ensureExistingFile() const return fileAccess()->ensureExistingFile(*this); } -/*! - Returns a bool indicating whether this is an executable file. -*/ -bool FilePath::isExecutableFile() const -{ - return fileAccess()->isExecutableFile(*this); -} - /*! Returns a bool indicating on whether a process with this FilePath's native path is likely to start. @@ -540,13 +508,16 @@ std::optional FilePath::refersToExecutableFile(MatchScope matchScope) expected_str FilePath::tmpDir() const { if (needsDevice()) { - const Environment env = deviceEnvironment(); - if (env.hasKey("TMPDIR")) - return withNewPath(env.value("TMPDIR")).cleanPath(); - if (env.hasKey("TEMP")) - return withNewPath(env.value("TEMP")).cleanPath(); - if (env.hasKey("TMP")) - return withNewPath(env.value("TMP")).cleanPath(); + const expected_str env = deviceEnvironmentWithError(); + if (!env) + return make_unexpected(env.error()); + + if (env->hasKey("TMPDIR")) + return withNewPath(env->value("TMPDIR")).cleanPath(); + if (env->hasKey("TEMP")) + return withNewPath(env->value("TEMP")).cleanPath(); + if (env->hasKey("TMP")) + return withNewPath(env->value("TMP")).cleanPath(); if (osType() != OsTypeWindows) return withNewPath("/tmp"); @@ -571,31 +542,6 @@ expected_str FilePath::createTempFile() const return fileAccess()->createTempFile(*this); } -bool FilePath::isReadableFile() const -{ - return fileAccess()->isReadableFile(*this); -} - -bool FilePath::isReadableDir() const -{ - return fileAccess()->isReadableDirectory(*this); -} - -bool FilePath::isFile() const -{ - return fileAccess()->isFile(*this); -} - -bool FilePath::isDir() const -{ - return fileAccess()->isDirectory(*this); -} - -bool FilePath::isSymLink() const -{ - return fileAccess()->isSymLink(*this); -} - bool FilePath::hasHardLinks() const { return fileAccess()->hasHardLinks(*this); @@ -682,11 +628,6 @@ expected_str FilePath::writeFileContents(const QByteArray &data, qint64 return fileAccess()->writeFileContents(*this, data, offset); } -FilePathInfo FilePath::filePathInfo() const -{ - return fileAccess()->filePathInfo(*this); -} - FileStreamHandle FilePath::asyncCopy(const FilePath &target, QObject *context, const CopyContinuation &cont) const { @@ -806,6 +747,11 @@ FilePath FilePath::withExecutableSuffix() const return withNewPath(OsSpecificAspects::withExecutableSuffix(osType(), path())); } +FilePath FilePath::withSuffix(const QString &suffix) const +{ + return withNewPath(path() + suffix); +} + static bool startsWithWindowsDriveLetterAndSlash(QStringView path) { return path.size() > 2 && path[1] == ':' && path[2] == '/' && isWindowsDriveLetter(path[0]); @@ -1005,12 +951,12 @@ FilePath FilePath::parentDir() const { const QString basePath = path(); if (basePath.isEmpty()) - return FilePath(); + return {}; const QString path = basePath + QLatin1String("/.."); const QString parent = doCleanPath(path); if (parent == path) - return FilePath(); + return {}; return withNewPath(parent); } @@ -1043,7 +989,7 @@ const QString &FilePath::specialRootName() const QString &FilePath::specialRootPath() { - static const QString rootPath = QDir::rootPath() + u"__qtc_devices__"; + static const QString rootPath = HostOsInfo::root().path() + u"__qtc_devices__"; return rootPath; } @@ -1055,7 +1001,7 @@ const QString &FilePath::specialDeviceRootName() const QString &FilePath::specialDeviceRootPath() { - static const QString deviceRootPath = QDir::rootPath() + u"__qtc_devices__/device"; + static const QString deviceRootPath = HostOsInfo::root().path() + u"__qtc_devices__/device"; return deviceRootPath; } @@ -1228,13 +1174,115 @@ DeviceFileAccess *FilePath::fileAccess() const static DeviceFileAccess dummy; const expected_str access = getFileAccess(*this); QTC_ASSERT_EXPECTED(access, return &dummy); - return *access ? *access : &dummy; + return *access; } bool FilePath::hasFileAccess() const { const expected_str access = getFileAccess(*this); - return access && access.value(); + return access.has_value(); +} + +FilePathInfo FilePath::filePathInfo() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return {}; + return (*access)->filePathInfo(*this); +} + +/*! + Returns a bool indicating whether a file or directory with this FilePath exists. +*/ +bool FilePath::exists() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->exists(*this); +} + +/*! + Returns a bool indicating whether this is an executable file. +*/ +bool FilePath::isExecutableFile() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isExecutableFile(*this); +} + +/*! + Returns a bool indicating whether this is a writable directory. +*/ +bool FilePath::isWritableDir() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isWritableDirectory(*this); +} + +/*! + Returns a bool indicating whether this is a writable file. +*/ +bool FilePath::isWritableFile() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isWritableFile(*this); +} + + +bool FilePath::isReadableFile() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isReadableFile(*this); +} + +bool FilePath::isReadableDir() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isReadableDirectory(*this); +} + +bool FilePath::isFile() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isFile(*this); +} + +bool FilePath::isDir() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isDirectory(*this); +} + +bool FilePath::isSymLink() const +{ + const expected_str access = getFileAccess(*this); + if (!access) + return false; + + return (*access)->isSymLink(*this); } /*! @@ -1512,11 +1560,6 @@ QString FilePath::calcRelativePath(const QString &absolutePath, const QString &a */ FilePath FilePath::withNewMappedPath(const FilePath &newPath) const { - const bool sameDevice = newPath.scheme() == scheme() && newPath.host() == host(); - if (sameDevice) - return newPath; - // TODO: converting paths between different non local devices is still unsupported - QTC_CHECK(!newPath.needsDevice() || !needsDevice()); FilePath res; res.setParts(scheme(), host(), fileAccess()->mapToDevicePath(newPath.path())); return res; @@ -1670,6 +1713,13 @@ FilePaths FilePath::searchAllInPath(const FilePaths &additionalDirs, } Environment FilePath::deviceEnvironment() const +{ + expected_str env = deviceEnvironmentWithError(); + QTC_ASSERT_EXPECTED(env, return {}); + return *env; +} + +expected_str FilePath::deviceEnvironmentWithError() const { if (needsDevice()) { QTC_ASSERT(s_deviceHooks.environment, return {}); @@ -1751,6 +1801,13 @@ FilePath FilePath::stringAppended(const QString &str) const return FilePath::fromString(toString() + str); } +std::optional FilePath::tailRemoved(const QString &str) const +{ + if (pathView().endsWith(str)) + return withNewPath(pathView().chopped(str.size()).toString()); + return {}; +} + QDateTime FilePath::lastModified() const { return fileAccess()->lastModified(*this); @@ -2124,6 +2181,7 @@ FileFilter::FileFilter(const QStringList &nameFilters, fileFilters(fileFilters), iteratorFlags(flags) { + QTC_CHECK(this->fileFilters != QDir::Filters()); } QStringList FileFilter::asFindArguments(const QString &path) const diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index f313ba5ff92..a35a3a09403 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -97,6 +97,7 @@ public: [[nodiscard]] FilePath pathAppended(const QString &str) const; [[nodiscard]] FilePath stringAppended(const QString &str) const; + [[nodiscard]] std::optional tailRemoved(const QString &str) const; bool startsWith(const QString &s) const; bool endsWith(const QString &s) const; bool contains(const QString &s) const; @@ -108,7 +109,7 @@ public: bool isWritableDir() const; bool isWritableFile() const; - bool ensureWritableDir() const; + expected_str ensureWritableDir() const; bool ensureExistingFile() const; bool isExecutableFile() const; bool isReadableFile() const; @@ -159,9 +160,11 @@ public: [[nodiscard]] FilePath symLinkTarget() const; [[nodiscard]] FilePath resolveSymlinks() const; [[nodiscard]] FilePath withExecutableSuffix() const; + [[nodiscard]] FilePath withSuffix(const QString &suffix) const; [[nodiscard]] FilePath relativeChildPath(const FilePath &parent) const; [[nodiscard]] FilePath relativePathFrom(const FilePath &anchor) const; [[nodiscard]] Environment deviceEnvironment() const; + [[nodiscard]] expected_str deviceEnvironmentWithError() const; [[nodiscard]] FilePaths devicePathEnvironmentVariable() const; [[nodiscard]] FilePath withNewPath(const QString &newPath) const; [[nodiscard]] FilePath withNewMappedPath(const FilePath &newPath) const; @@ -299,7 +302,7 @@ public: std::function(const FilePath &)> fileAccess; std::function deviceDisplayName; std::function ensureReachable; - std::function environment; + std::function(const FilePath &)> environment; std::function isSameDevice; std::function(const FilePath &)> localSource; std::function openTerminal; diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp index a72ddc6ab04..f97934cde40 100644 --- a/src/libs/utils/filesearch.cpp +++ b/src/libs/utils/filesearch.cpp @@ -4,23 +4,201 @@ #include "filesearch.h" #include "algorithm.h" -#include "filepath.h" -#include "mapreduce.h" +#include "async.h" #include "qtcassert.h" -#include "searchresultitem.h" #include "stringutils.h" #include "utilstr.h" #include -#include #include +#include #include -#include - Q_LOGGING_CATEGORY(searchLog, "qtc.utils.filesearch", QtWarningMsg) -using namespace Utils; +namespace Utils { + +const int MAX_LINE_SIZE = 400; + +static QString clippedText(const QString &text, int maxLength) +{ + if (text.length() > maxLength) + return text.left(maxLength) + QChar(0x2026); // '...' + return text; +} + +QTextDocument::FindFlags textDocumentFlagsForFindFlags(FindFlags flags) +{ + QTextDocument::FindFlags textDocFlags; + if (flags & FindBackward) + textDocFlags |= QTextDocument::FindBackward; + if (flags & FindCaseSensitively) + textDocFlags |= QTextDocument::FindCaseSensitively; + if (flags & FindWholeWords) + textDocFlags |= QTextDocument::FindWholeWords; + return textDocFlags; +} + +static SearchResultItems searchWithoutRegExp(const QFuture &future, const QString &searchTerm, + FindFlags flags, const FilePath &filePath, + const QString &contents) +{ + const bool caseSensitive = (flags & QTextDocument::FindCaseSensitively); + const bool wholeWord = (flags & QTextDocument::FindWholeWords); + const QString searchTermLower = searchTerm.toLower(); + const QString searchTermUpper = searchTerm.toUpper(); + const int termMaxIndex = searchTerm.length() - 1; + const QChar *termData = searchTerm.constData(); + const QChar *termDataLower = searchTermLower.constData(); + const QChar *termDataUpper = searchTermUpper.constData(); + + SearchResultItems results; + QString copy = contents; + QTextStream stream(©); + int lineNr = 0; + + while (!stream.atEnd()) { + ++lineNr; + const QString chunk = stream.readLine(); + const int chunkLength = chunk.length(); + const QChar *chunkPtr = chunk.constData(); + const QChar *chunkEnd = chunkPtr + chunkLength - 1; + for (const QChar *regionPtr = chunkPtr; regionPtr + termMaxIndex <= chunkEnd; ++regionPtr) { + const QChar *regionEnd = regionPtr + termMaxIndex; + if ( /* optimization check for start and end of region */ + // case sensitive + (caseSensitive && *regionPtr == termData[0] + && *regionEnd == termData[termMaxIndex]) + || + // case insensitive + (!caseSensitive && (*regionPtr == termDataLower[0] + || *regionPtr == termDataUpper[0]) + && (*regionEnd == termDataLower[termMaxIndex] + || *regionEnd == termDataUpper[termMaxIndex])) + ) { + bool equal = true; + + // whole word check + const QChar *beforeRegion = regionPtr - 1; + const QChar *afterRegion = regionEnd + 1; + if (wholeWord + && (((beforeRegion >= chunkPtr) + && (beforeRegion->isLetterOrNumber() + || ((*beforeRegion) == QLatin1Char('_')))) + || + ((afterRegion <= chunkEnd) + && (afterRegion->isLetterOrNumber() + || ((*afterRegion) == QLatin1Char('_')))) + )) { + equal = false; + } else { + // check all chars + int regionIndex = 1; + for (const QChar *regionCursor = regionPtr + 1; + regionCursor < regionEnd; + ++regionCursor, ++regionIndex) { + if ( // case sensitive + (caseSensitive + && *regionCursor != termData[regionIndex]) + || + // case insensitive + (!caseSensitive + && *regionCursor != termDataLower[regionIndex] + && *regionCursor != termDataUpper[regionIndex]) + ) { + equal = false; + break; + } + } + } + if (equal) { + SearchResultItem result; + result.setFilePath(filePath); + result.setMainRange(lineNr, regionPtr - chunkPtr, termMaxIndex + 1); + result.setDisplayText(clippedText(chunk, MAX_LINE_SIZE)); + result.setUserData(QStringList()); + result.setUseTextEditorFont(true); + results << result; + regionPtr += termMaxIndex; // another +1 done by for-loop + } + } + } + if (future.isCanceled()) + return {}; + } + if (future.isCanceled()) + return {}; + return results; +} + +static SearchResultItems searchWithRegExp(const QFuture &future, const QString &searchTerm, + FindFlags flags, const FilePath &filePath, + const QString &contents) +{ + const QString term = flags & QTextDocument::FindWholeWords + ? QString::fromLatin1("\\b%1\\b").arg(searchTerm) : searchTerm; + const QRegularExpression::PatternOptions patternOptions = (flags & FindCaseSensitively) + ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption; + const QRegularExpression expression = QRegularExpression(term, patternOptions); + if (!expression.isValid()) { + QFuture nonConstFuture = future; + nonConstFuture.cancel(); + return {}; + } + + SearchResultItems results; + QString copy = contents; + QTextStream stream(©); + int lineNr = 0; + + QRegularExpressionMatch match; + while (!stream.atEnd()) { + ++lineNr; + const QString line = stream.readLine(); + const QString resultItemText = clippedText(line, MAX_LINE_SIZE); + int lengthOfLine = line.size(); + int pos = 0; + while ((match = expression.match(line, pos)).hasMatch()) { + pos = match.capturedStart(); + SearchResultItem result; + result.setFilePath(filePath); + result.setMainRange(lineNr, pos, match.capturedLength()); + result.setDisplayText(resultItemText); + result.setUserData(match.capturedTexts()); + result.setUseTextEditorFont(true); + results << result; + if (match.capturedLength() == 0) + break; + pos += match.capturedLength(); + if (pos >= lengthOfLine) + break; + } + if (future.isCanceled()) + return {}; + } + if (future.isCanceled()) + return {}; + return results; +} + +static SearchResultItems searchInContents(const QFuture &future, const QString &searchTerm, + FindFlags flags, const FilePath &filePath, + const QString &contents) +{ + if (flags & FindRegularExpression) + return searchWithRegExp(future, searchTerm, flags, filePath, contents); + return searchWithoutRegExp(future, searchTerm, flags, filePath, contents); +} + +void searchInContents(QPromise &promise, const QString &searchTerm, + FindFlags flags, const FilePath &filePath, const QString &contents) +{ + const QFuture future(promise.future()); + const SearchResultItems results = searchInContents(future, searchTerm, flags, filePath, + contents); + if (!promise.isCanceled()) + promise.addResult(results); +} static inline QString msgCanceled(const QString &searchTerm, int numMatches, int numFilesSearched) { @@ -34,22 +212,8 @@ static inline QString msgFound(const QString &searchTerm, int numMatches, int nu nullptr, numMatches).arg(searchTerm).arg(numFilesSearched); } -namespace { - -const int MAX_LINE_SIZE = 400; - -QString clippedText(const QString &text, int maxLength) -{ - if (text.length() > maxLength) - return text.left(maxLength) + QChar(0x2026); // '...' - return text; -} - -// returns success -static bool getFileContent(const FilePath &filePath, - QTextCodec *encoding, - QString *tempString, - const QMap &fileToContentsMap) +static bool getFileContent(const FilePath &filePath, QTextCodec *encoding, + QString *tempString, const QMap &fileToContentsMap) { if (fileToContentsMap.contains(filePath)) { *tempString = fileToContentsMap.value(filePath); @@ -63,330 +227,133 @@ static bool getFileContent(const FilePath &filePath, return true; } -class FileSearch +static void fileSearch(QPromise &promise, + const FileContainerIterator::Item &item, const QString &searchTerm, + FindFlags flags, const QMap &fileToContentsMap) { -public: - FileSearch(const QString &searchTerm, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap); - void operator()(QFutureInterface &futureInterface, - const FileIterator::Item &item) const; - -private: - QMap fileToContentsMap; - QString searchTermLower; - QString searchTermUpper; - int termMaxIndex; - const QChar *termData; - const QChar *termDataLower; - const QChar *termDataUpper; - bool caseSensitive; - bool wholeWord; -}; - -class FileSearchRegExp -{ -public: - FileSearchRegExp(const QString &searchTerm, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap); - FileSearchRegExp(const FileSearchRegExp &other); - void operator()(QFutureInterface &futureInterface, - const FileIterator::Item &item) const; - -private: - QRegularExpressionMatch doGuardedMatch(const QString &line, int offset) const; - - QMap fileToContentsMap; - QRegularExpression expression; - mutable QMutex mutex; -}; - -FileSearch::FileSearch(const QString &searchTerm, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap) -{ - this->fileToContentsMap = fileToContentsMap; - caseSensitive = (flags & QTextDocument::FindCaseSensitively); - wholeWord = (flags & QTextDocument::FindWholeWords); - searchTermLower = searchTerm.toLower(); - searchTermUpper = searchTerm.toUpper(); - termMaxIndex = searchTerm.length() - 1; - termData = searchTerm.constData(); - termDataLower = searchTermLower.constData(); - termDataUpper = searchTermUpper.constData(); -} - -void FileSearch::operator()(QFutureInterface &futureInterface, - const FileIterator::Item &item) const -{ - if (futureInterface.isCanceled()) + if (promise.isCanceled()) return; qCDebug(searchLog) << "Searching in" << item.filePath; - futureInterface.setProgressRange(0, 1); - futureInterface.setProgressValue(0); - SearchResultItems results; - QString tempString; - if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) { + promise.setProgressRange(0, 1); + promise.setProgressValue(0); + QString contents; + if (!getFileContent(item.filePath, item.encoding, &contents, fileToContentsMap)) { qCDebug(searchLog) << "- failed to get content for" << item.filePath; - futureInterface.cancel(); // failure + promise.future().cancel(); // failure return; } - QTextStream stream(&tempString); - int lineNr = 0; - while (!stream.atEnd()) { - ++lineNr; - const QString chunk = stream.readLine(); - const int chunkLength = chunk.length(); - const QChar *chunkPtr = chunk.constData(); - const QChar *chunkEnd = chunkPtr + chunkLength - 1; - for (const QChar *regionPtr = chunkPtr; regionPtr + termMaxIndex <= chunkEnd; ++regionPtr) { - const QChar *regionEnd = regionPtr + termMaxIndex; - if ( /* optimization check for start and end of region */ - // case sensitive - (caseSensitive && *regionPtr == termData[0] - && *regionEnd == termData[termMaxIndex]) - || - // case insensitive - (!caseSensitive && (*regionPtr == termDataLower[0] - || *regionPtr == termDataUpper[0]) - && (*regionEnd == termDataLower[termMaxIndex] - || *regionEnd == termDataUpper[termMaxIndex])) - ) { - bool equal = true; + const QFuture future(promise.future()); + const SearchResultItems results = searchInContents(future, searchTerm, flags, item.filePath, + contents); + if (!promise.isCanceled()) { + promise.addResult(results); + promise.setProgressValue(1); + } + qCDebug(searchLog) << "- finished searching in" << item.filePath; +} - // whole word check - const QChar *beforeRegion = regionPtr - 1; - const QChar *afterRegion = regionEnd + 1; - if (wholeWord - && (((beforeRegion >= chunkPtr) - && (beforeRegion->isLetterOrNumber() - || ((*beforeRegion) == QLatin1Char('_')))) - || - ((afterRegion <= chunkEnd) - && (afterRegion->isLetterOrNumber() - || ((*afterRegion) == QLatin1Char('_')))) - )) { - equal = false; - } else { - // check all chars - int regionIndex = 1; - for (const QChar *regionCursor = regionPtr + 1; - regionCursor < regionEnd; - ++regionCursor, ++regionIndex) { - if ( // case sensitive - (caseSensitive - && *regionCursor != termData[regionIndex]) - || - // case insensitive - (!caseSensitive - && *regionCursor != termDataLower[regionIndex] - && *regionCursor != termDataUpper[regionIndex]) - ) { - equal = false; - break; - } - } - } - if (equal) { - SearchResultItem result; - result.setFilePath(item.filePath); - result.setMainRange(lineNr, regionPtr - chunkPtr, termMaxIndex + 1); - result.setDisplayText(clippedText(chunk, MAX_LINE_SIZE)); - result.setUserData(QStringList()); - result.setUseTextEditorFont(true); - results << result; - regionPtr += termMaxIndex; // another +1 done by for-loop - } +static void findInFilesImpl(QPromise &promise, const QString &searchTerm, + const FileContainer &container, FindFlags flags, + const QMap &fileToContentsMap) +{ + QEventLoop loop; + // The states transition exactly in this order: + enum State { BelowLimit, AboveLimit, Resumed }; + State state = BelowLimit; + int reportedItemsCount = 0; + int searchedFilesCount = 0; + + const int progressMaximum = container.progressMaximum(); + promise.setProgressRange(0, progressMaximum); + promise.setProgressValueAndText(0, msgFound(searchTerm, 0, 0)); + const int threadsCount = qMax(1, QThread::idealThreadCount() / 2); + QSet *> watchers; + FutureSynchronizer futureSynchronizer; + + const auto cleanup = qScopeGuard([&] { + qDeleteAll(watchers); + const QString message = promise.isCanceled() + ? msgCanceled(searchTerm, reportedItemsCount, searchedFilesCount) + : msgFound(searchTerm, reportedItemsCount, searchedFilesCount); + promise.setProgressValueAndText(progressMaximum, message); + }); + + FileContainerIterator it = container.begin(); + const FileContainerIterator itEnd = container.end(); + if (it == itEnd) + return; + + std::function scheduleNext; + scheduleNext = [&] { + if (promise.isCanceled() || (it == itEnd && watchers.isEmpty())) { + loop.quit(); + return; + } + if (it == itEnd) + return; + + if (state == AboveLimit) + return; + + if (watchers.size() == threadsCount) + return; + + const FileContainerIterator::Item item = *it; + const int progress = it.progressValue(); + ++it; + const QFuture future + = Utils::asyncRun(fileSearch, item, searchTerm, flags, fileToContentsMap); + QFutureWatcher *watcher = new QFutureWatcher; + QObject::connect(watcher, &QFutureWatcherBase::finished, &loop, + [watcher, progress, &searchTerm, &reportedItemsCount, &searchedFilesCount, &state, + &watchers, &promise, &scheduleNext] { + const QFuture future = watcher->future(); + if (future.resultCount()) { + const SearchResultItems items = future.result(); + reportedItemsCount += items.size(); + if (state == BelowLimit && reportedItemsCount > 200000) + state = AboveLimit; + if (!items.isEmpty()) + promise.addResult(items); } - } - if (futureInterface.isPaused()) - futureInterface.waitForResume(); - if (futureInterface.isCanceled()) - break; - } - if (!futureInterface.isCanceled()) { - futureInterface.reportResult(results); - futureInterface.setProgressValue(1); - } - qCDebug(searchLog) << "- finished searching in" << item.filePath; -} + ++searchedFilesCount; + promise.setProgressValueAndText(progress, msgFound(searchTerm, reportedItemsCount, + searchedFilesCount)); + watcher->deleteLater(); + watchers.remove(watcher); + scheduleNext(); + }); + watcher->setFuture(future); + futureSynchronizer.addFuture(future); + watchers.insert(watcher); + scheduleNext(); + }; -FileSearchRegExp::FileSearchRegExp(const QString &searchTerm, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap) -{ - this->fileToContentsMap = fileToContentsMap; - QString term = searchTerm; - if (flags & QTextDocument::FindWholeWords) - term = QString::fromLatin1("\\b%1\\b").arg(term); - const QRegularExpression::PatternOptions patternOptions = (flags & QTextDocument::FindCaseSensitively) - ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption; - expression = QRegularExpression(term, patternOptions); -} + QFutureWatcher watcher; + QObject::connect(&watcher, &QFutureWatcherBase::canceled, &loop, &QEventLoop::quit); + QObject::connect(&watcher, &QFutureWatcherBase::resumed, &loop, [&state, &scheduleNext] { + state = Resumed; + scheduleNext(); + }); -FileSearchRegExp::FileSearchRegExp(const FileSearchRegExp &other) - : fileToContentsMap(other.fileToContentsMap), - expression(other.expression) -{ -} + watcher.setFuture(QFuture(promise.future())); -QRegularExpressionMatch FileSearchRegExp::doGuardedMatch(const QString &line, int offset) const -{ - QMutexLocker lock(&mutex); - return expression.match(line, offset); -} - -void FileSearchRegExp::operator()(QFutureInterface &futureInterface, - const FileIterator::Item &item) const -{ - if (!expression.isValid()) { - futureInterface.cancel(); + if (promise.isCanceled()) return; - } - if (futureInterface.isCanceled()) - return; - qCDebug(searchLog) << "Searching in" << item.filePath; - futureInterface.setProgressRange(0, 1); - futureInterface.setProgressValue(0); - SearchResultItems results; - QString tempString; - if (!getFileContent(item.filePath, item.encoding, &tempString, fileToContentsMap)) { - qCDebug(searchLog) << "- failed to get content for" << item.filePath; - futureInterface.cancel(); // failure - return; - } - QTextStream stream(&tempString); - int lineNr = 0; - QString line; - QRegularExpressionMatch match; - while (!stream.atEnd()) { - ++lineNr; - line = stream.readLine(); - const QString resultItemText = clippedText(line, MAX_LINE_SIZE); - int lengthOfLine = line.size(); - int pos = 0; - while ((match = doGuardedMatch(line, pos)).hasMatch()) { - pos = match.capturedStart(); - SearchResultItem result; - result.setFilePath(item.filePath); - result.setMainRange(lineNr, pos, match.capturedLength()); - result.setDisplayText(resultItemText); - result.setUserData(match.capturedTexts()); - result.setUseTextEditorFont(true); - results << result; - if (match.capturedLength() == 0) - break; - pos += match.capturedLength(); - if (pos >= lengthOfLine) - break; - } - if (futureInterface.isPaused()) - futureInterface.waitForResume(); - if (futureInterface.isCanceled()) - break; - } - if (!futureInterface.isCanceled()) { - futureInterface.reportResult(results); - futureInterface.setProgressValue(1); - } - qCDebug(searchLog) << "- finished searching in" << item.filePath; + QTimer::singleShot(0, &loop, scheduleNext); + loop.exec(QEventLoop::ExcludeUserInputEvents); } -struct SearchState +QFuture findInFiles(const QString &searchTerm, const FileContainer &container, + FindFlags flags, + const QMap &fileToContentsMap) { - SearchState(const QString &term, FileIterator *iterator) : searchTerm(term), files(iterator) {} - QString searchTerm; - FileIterator *files = nullptr; - SearchResultItems cachedResults; - int numFilesSearched = 0; - int numMatches = 0; -}; - -SearchState initFileSearch(QFutureInterface &futureInterface, - const QString &searchTerm, FileIterator *files) -{ - futureInterface.setProgressRange(0, files->maxProgress()); - futureInterface.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, 0, 0)); - return SearchState(searchTerm, files); + return Utils::asyncRun(findInFilesImpl, searchTerm, container, flags, fileToContentsMap); } -void collectSearchResults(QFutureInterface &futureInterface, - SearchState &state, - const SearchResultItems &results) -{ - state.numMatches += results.size(); - state.cachedResults << results; - state.numFilesSearched += 1; - if (futureInterface.isProgressUpdateNeeded() - || futureInterface.progressValue() == 0 /*workaround for regression in Qt*/) { - if (!state.cachedResults.isEmpty()) { - futureInterface.reportResult(state.cachedResults); - state.cachedResults.clear(); - } - futureInterface.setProgressRange(0, state.files->maxProgress()); - futureInterface.setProgressValueAndText(state.files->currentProgress(), - msgFound(state.searchTerm, - state.numMatches, - state.numFilesSearched)); - } -} - -void cleanUpFileSearch(QFutureInterface &futureInterface, - SearchState &state) -{ - if (!state.cachedResults.isEmpty()) { - futureInterface.reportResult(state.cachedResults); - state.cachedResults.clear(); - } - if (futureInterface.isCanceled()) { - futureInterface.setProgressValueAndText(state.files->currentProgress(), - msgCanceled(state.searchTerm, - state.numMatches, - state.numFilesSearched)); - } else { - futureInterface.setProgressValueAndText(state.files->currentProgress(), - msgFound(state.searchTerm, - state.numMatches, - state.numFilesSearched)); - } - delete state.files; -} - -} // namespace - -QFuture Utils::findInFiles(const QString &searchTerm, - FileIterator *files, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap) -{ - return mapReduce(files->begin(), files->end(), - [searchTerm, files](QFutureInterface &futureInterface) { - return initFileSearch(futureInterface, searchTerm, files); - }, - FileSearch(searchTerm, flags, fileToContentsMap), - &collectSearchResults, - &cleanUpFileSearch); -} - -QFuture Utils::findInFilesRegExp( - const QString &searchTerm, - FileIterator *files, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap) -{ - return mapReduce(files->begin(), files->end(), - [searchTerm, files](QFutureInterface &futureInterface) { - return initFileSearch(futureInterface, searchTerm, files); - }, - FileSearchRegExp(searchTerm, flags, fileToContentsMap), - &collectSearchResults, - &cleanUpFileSearch); -} - -QString Utils::expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts) +QString expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts) { // handles \1 \\ \& \t \n $1 $$ $& QString result; @@ -435,9 +402,7 @@ QString Utils::expandRegExpReplacement(const QString &replaceText, const QString return result; } -namespace Utils { -namespace Internal { -QString matchCaseReplacement(const QString &originalText, const QString &replaceText) +static QString matchCaseReplacementHelper(const QString &originalText, const QString &replaceText) { if (originalText.isEmpty() || replaceText.isEmpty()) return replaceText; @@ -472,7 +437,6 @@ QString matchCaseReplacement(const QString &originalText, const QString &replace return replaceText; // mixed } } -} // namespace static QList filtersToRegExps(const QStringList &filters) { @@ -519,7 +483,6 @@ QStringList splitFilterUiText(const QString &text) return Utils::filtered(trimmedPortableParts, [](const QString &s) { return !s.isEmpty(); }); } - QString msgFilePatternLabel() { return Tr::tr("Fi&le pattern:"); @@ -532,7 +495,7 @@ QString msgExclusionPatternLabel() QString msgFilePatternToolTip(InclusionType inclusionType) { - return Tr::tr("List of comma separated wildcard filters. ") + return Tr::tr("List of comma separated wildcard filters.") + " " + (inclusionType == InclusionType::Included ? Tr::tr("Files with file name or full file path matching any filter are included.") : Tr::tr("Files with file name or full file path matching any filter are excluded.")); @@ -559,96 +522,95 @@ QString matchCaseReplacement(const QString &originalText, const QString &replace //keep prefix and suffix, and do actual replacement on the 'middle' of the string return originalText.left(prefixLen) - + Internal::matchCaseReplacement(originalText.mid(prefixLen, originalTextLen - prefixLen - suffixLen), - replaceText.mid(prefixLen, replaceTextLen - prefixLen - suffixLen)) + + matchCaseReplacementHelper(originalText.mid(prefixLen, originalTextLen - prefixLen - suffixLen), + replaceText.mid(prefixLen, replaceTextLen - prefixLen - suffixLen)) + originalText.right(suffixLen); - } -// #pragma mark -- FileIterator - -void FileIterator::advance(FileIterator::const_iterator *it) const +void FileContainerIterator::operator++() { - if (it->m_index < 0) // == end - return; - ++it->m_index; - const_cast(this)->update(it->m_index); - if (it->m_index >= currentFileCount()) - it->m_index = -1; // == end + QTC_ASSERT(m_data.m_container, return); + QTC_ASSERT(m_data.m_index >= 0, return); + QTC_ASSERT(m_data.m_advancer, return); + m_data.m_advancer(&m_data); } -FileIterator::const_iterator FileIterator::begin() const +int FileContainerIterator::progressMaximum() const { - const_cast(this)->update(0); - if (currentFileCount() == 0) - return end(); - return FileIterator::const_iterator(this, 0/*index*/); + return m_data.m_container ? m_data.m_container->progressMaximum() : 0; } -FileIterator::const_iterator FileIterator::end() const +static QList toFileListCache(const FilePaths &fileList, + const QList &encodings) { - return FileIterator::const_iterator(this, -1/*end*/); -} - -// #pragma mark -- FileListIterator - -QList constructItems(const FilePaths &fileList, - const QList &encodings) -{ - QList items; + QList items; items.reserve(fileList.size()); QTextCodec *defaultEncoding = QTextCodec::codecForLocale(); for (int i = 0; i < fileList.size(); ++i) - items.append(FileIterator::Item(fileList.at(i), encodings.value(i, defaultEncoding))); + items.append({fileList.at(i), encodings.value(i, defaultEncoding)}); return items; } -FileListIterator::FileListIterator(const FilePaths &fileList, const QList &encodings) - : m_items(constructItems(fileList, encodings)) +static FileContainerIterator::Advancer fileListAdvancer( + const QList &items) { + return [items](FileContainerIterator::Data *iterator) { + ++iterator->m_index; + if (iterator->m_index >= items.size() || iterator->m_index < 0) { + iterator->m_value = {}; + iterator->m_index = -1; + iterator->m_progressValue = items.size(); + return; + } + iterator->m_value = items.at(iterator->m_index); + iterator->m_progressValue = iterator->m_index; + }; } -void FileListIterator::update(int requestedIndex) +static FileContainer::AdvancerProvider fileListAdvancerProvider(const FilePaths &fileList, + const QList &encodings) { - if (requestedIndex > m_maxIndex) - m_maxIndex = requestedIndex; + const auto initialCache = toFileListCache(fileList, encodings); + return [=] { return fileListAdvancer(initialCache); }; } -int FileListIterator::currentFileCount() const +FileListContainer::FileListContainer(const FilePaths &fileList, + const QList &encodings) + : FileContainer(fileListAdvancerProvider(fileList, encodings), fileList.size()) {} + +const int s_progressMaximum = 1000; + +struct SubDirCache { - return m_items.size(); -} + SubDirCache(const FilePaths &directories, const QStringList &filters, + const QStringList &exclusionFilters, QTextCodec *encoding); -const FileIterator::Item &FileListIterator::itemAt(int index) const -{ - return m_items.at(index); -} + std::optional updateCache(int advanceIntoIndex, + const SubDirCache &initialCache); -int FileListIterator::maxProgress() const -{ - return m_items.size(); -} + std::function m_filterFiles; + QTextCodec *m_encoding = nullptr; + QStack m_dirs; + QSet m_knownDirs; + QStack m_progressValues; + QStack m_processedValues; + qreal m_progress = 0; + QList m_items; + // When forward iterating, we construct some results for the future iterations + // and keep them in m_items cache. Later, when we iterated over all from the cache, + // we don't want to keep the cache anymore, so we are clearing it. + // In order to match the iterator's index with the position inside m_items cache, + // we need to remember how many items were removed from the cache and subtract + // this value from the iterator's index when a new advance comes. + int m_removedItemsCount = 0; +}; -int FileListIterator::currentProgress() const -{ - return m_maxIndex + 1; -} - -// #pragma mark -- SubDirFileIterator - -namespace { - const int MAX_PROGRESS = 1000; -} - -SubDirFileIterator::SubDirFileIterator(const FilePaths &directories, - const QStringList &filters, - const QStringList &exclusionFilters, - QTextCodec *encoding) +SubDirCache::SubDirCache(const FilePaths &directories, const QStringList &filters, + const QStringList &exclusionFilters, QTextCodec *encoding) : m_filterFiles(filterFilesFunction(filters, exclusionFilters)) - , m_progress(0) + , m_encoding(encoding == nullptr ? QTextCodec::codecForLocale() : encoding) { - m_encoding = (encoding == nullptr ? QTextCodec::codecForLocale() : encoding); - qreal maxPer = qreal(MAX_PROGRESS) / directories.count(); + const qreal maxPer = qreal(s_progressMaximum) / directories.count(); for (const FilePath &directoryEntry : directories) { if (!directoryEntry.isEmpty()) { const FilePath canonicalPath = directoryEntry.canonicalPath(); @@ -662,18 +624,22 @@ SubDirFileIterator::SubDirFileIterator(const FilePaths &directories, } } -SubDirFileIterator::~SubDirFileIterator() +std::optional SubDirCache::updateCache(int advanceIntoIndex, + const SubDirCache &initialCache) { - qDeleteAll(m_items); -} + QTC_ASSERT(advanceIntoIndex >= 0, return {}); + if (advanceIntoIndex < m_removedItemsCount) + *this = initialCache; // Regenerate the cache from scratch + const int currentIndex = advanceIntoIndex - m_removedItemsCount; + if (currentIndex < m_items.size()) + return m_items.at(currentIndex); -void SubDirFileIterator::update(int index) -{ - if (index < m_items.size()) - return; - // collect files from the directories until we have enough for the given index - while (!m_dirs.isEmpty() && index >= m_items.size()) { - FilePath dir = m_dirs.pop(); + m_removedItemsCount += m_items.size(); + m_items.clear(); + const int newCurrentIndex = advanceIntoIndex - m_removedItemsCount; + + while (!m_dirs.isEmpty() && newCurrentIndex >= m_items.size()) { + const FilePath dir = m_dirs.pop(); const qreal dirProgressMax = m_progressValues.pop(); const bool processed = m_processedValues.pop(); if (dir.exists()) { @@ -681,8 +647,9 @@ void SubDirFileIterator::update(int index) using CanonicalDir = FilePath; std::vector> subDirs; if (!processed) { - for (const FilePath &entry : - dir.dirEntries(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot)) { + const FilePaths entries = dir.dirEntries(QDir::Dirs | QDir::Hidden + | QDir::NoDotAndDotDot); + for (const FilePath &entry : entries) { const FilePath canonicalDir = entry.canonicalPath(); if (!m_knownDirs.contains(canonicalDir)) subDirs.emplace_back(entry, canonicalDir); @@ -693,11 +660,11 @@ void SubDirFileIterator::update(int index) const FilePaths filePaths = m_filterFiles(allFilePaths); m_items.reserve(m_items.size() + filePaths.size()); Utils::reverseForeach(filePaths, [this](const FilePath &file) { - m_items.append(new Item(file, m_encoding)); + m_items.append({file, m_encoding}); }); m_progress += dirProgressMax; } else { - qreal subProgress = dirProgressMax/(subDirs.size()+1); + const qreal subProgress = dirProgressMax / (subDirs.size() + 1); m_dirs.push(dir); m_progressValues.push(subProgress); m_processedValues.push(true); @@ -713,28 +680,41 @@ void SubDirFileIterator::update(int index) m_progress += dirProgressMax; } } - if (index >= m_items.size()) - m_progress = MAX_PROGRESS; + if (newCurrentIndex < m_items.size()) + return m_items.at(newCurrentIndex); + + m_progress = s_progressMaximum; + return {}; } -int SubDirFileIterator::currentFileCount() const +static FileContainerIterator::Advancer subDirAdvancer(const SubDirCache &initialCache) { - return m_items.size(); + const std::shared_ptr sharedCache(new SubDirCache(initialCache)); + return [=](FileContainerIterator::Data *iterator) { + ++iterator->m_index; + const std::optional item + = sharedCache->updateCache(iterator->m_index, initialCache); + if (!item) { + iterator->m_value = {}; + iterator->m_index = -1; + iterator->m_progressValue = s_progressMaximum; + return; + } + iterator->m_value = *item; + iterator->m_progressValue = qMin(qRound(sharedCache->m_progress), s_progressMaximum); + }; } -const FileIterator::Item &SubDirFileIterator::itemAt(int index) const +static FileContainer::AdvancerProvider subDirAdvancerProvider(const FilePaths &directories, + const QStringList &filters, const QStringList &exclusionFilters, QTextCodec *encoding) { - return *m_items.at(index); + const SubDirCache initialCache(directories, filters, exclusionFilters, encoding); + return [=] { return subDirAdvancer(initialCache); }; } -int SubDirFileIterator::maxProgress() const -{ - return MAX_PROGRESS; -} +SubDirFileContainer::SubDirFileContainer(const FilePaths &directories, const QStringList &filters, + const QStringList &exclusionFilters, QTextCodec *encoding) + : FileContainer(subDirAdvancerProvider(directories, filters, exclusionFilters, encoding), + s_progressMaximum) {} -int SubDirFileIterator::currentProgress() const -{ - return qMin(qRound(m_progress), MAX_PROGRESS); -} - -} +} // namespace Utils diff --git a/src/libs/utils/filesearch.h b/src/libs/utils/filesearch.h index fc6ce62ab0b..2d2155a723c 100644 --- a/src/libs/utils/filesearch.h +++ b/src/libs/utils/filesearch.h @@ -9,8 +9,7 @@ #include "searchresultitem.h" #include -#include -#include +#include #include #include @@ -23,6 +22,22 @@ QT_END_NAMESPACE namespace Utils { +enum FindFlag { + FindBackward = 0x01, + FindCaseSensitively = 0x02, + FindWholeWords = 0x04, + FindRegularExpression = 0x08, + FindPreserveCase = 0x10 +}; +Q_DECLARE_FLAGS(FindFlags, FindFlag) + +QTCREATOR_UTILS_EXPORT +QTextDocument::FindFlags textDocumentFlagsForFindFlags(FindFlags flags); + +QTCREATOR_UTILS_EXPORT +void searchInContents(QPromise &promise, const QString &searchTerm, + FindFlags flags, const FilePath &filePath, const QString &contents); + QTCREATOR_UTILS_EXPORT std::function filterFilesFunction(const QStringList &filters, const QStringList &exclusionFilters); @@ -44,125 +59,105 @@ enum class InclusionType { QTCREATOR_UTILS_EXPORT QString msgFilePatternToolTip(InclusionType inclusionType = InclusionType::Included); -class QTCREATOR_UTILS_EXPORT FileIterator +class FileContainer; + +class QTCREATOR_UTILS_EXPORT FileContainerIterator { public: class Item { public: - Item() = default; - Item(const FilePath &path, QTextCodec *codec) - : filePath(path) - , encoding(codec) - {} - FilePath filePath; + FilePath filePath {}; QTextCodec *encoding = nullptr; }; - using value_type = Item; + class Data; + using Advancer = std::function; - class const_iterator + class Data { public: - using iterator_category = std::forward_iterator_tag; - using value_type = Item; - using difference_type = std::ptrdiff_t; - using pointer = const value_type*; - using reference = const value_type&; - - const_iterator() = default; - const_iterator(const FileIterator *parent, int id) - : m_parent(parent), m_index(id) - {} - const_iterator(const const_iterator &) = default; - const_iterator &operator=(const const_iterator &) = default; - - reference operator*() const { return m_parent->itemAt(m_index); } - pointer operator->() const { return &m_parent->itemAt(m_index); } - void operator++() { m_parent->advance(this); } - bool operator==(const const_iterator &other) const - { - return m_parent == other.m_parent && m_index == other.m_index; - } - bool operator!=(const const_iterator &other) const { return !operator==(other); } - - const FileIterator *m_parent = nullptr; - int m_index = -1; // -1 == end + const FileContainer *m_container = nullptr; + int m_progressValue = 0; + Advancer m_advancer = {}; + int m_index = -1; // end iterator + Item m_value = {}; }; - virtual ~FileIterator() = default; - const_iterator begin() const; - const_iterator end() const; + using value_type = Item; + using pointer = const value_type *; + using reference = const value_type &; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; - virtual int maxProgress() const = 0; - virtual int currentProgress() const = 0; + FileContainerIterator() = default; - void advance(const_iterator *it) const; - virtual const Item &itemAt(int index) const = 0; + reference operator*() const { return m_data.m_value; } + pointer operator->() const { return &m_data.m_value; } + void operator++(); -protected: - virtual void update(int requestedIndex) = 0; - virtual int currentFileCount() const = 0; -}; - -class QTCREATOR_UTILS_EXPORT FileListIterator : public FileIterator -{ -public: - explicit FileListIterator(const FilePaths &fileList = {}, - const QList &encodings = {}); - - int maxProgress() const override; - int currentProgress() const override; - -protected: - void update(int requestedIndex) override; - int currentFileCount() const override; - const Item &itemAt(int index) const override; + bool operator==(const FileContainerIterator &other) const { + return m_data.m_container == other.m_data.m_container + && m_data.m_index == other.m_data.m_index; + } + bool operator!=(const FileContainerIterator &other) const { return !operator==(other); } + int progressValue() const { return m_data.m_progressValue; } + int progressMaximum() const; private: - const QList m_items; - int m_maxIndex = -1; + friend class FileContainer; + FileContainerIterator(const Data &data) : m_data(data) {} + Data m_data; }; -class QTCREATOR_UTILS_EXPORT SubDirFileIterator : public FileIterator +class QTCREATOR_UTILS_EXPORT FileContainer { public: - SubDirFileIterator(const FilePaths &directories, - const QStringList &filters, - const QStringList &exclusionFilters, - QTextCodec *encoding = nullptr); - ~SubDirFileIterator() override; + using AdvancerProvider = std::function; - int maxProgress() const override; - int currentProgress() const override; + FileContainer() = default; + + FileContainerIterator begin() const { + if (!m_provider) + return end(); + const FileContainerIterator::Advancer advancer = m_provider(); + if (!advancer) + return end(); + FileContainerIterator iterator({this, 0, advancer}); + advancer(&iterator.m_data); + return iterator; + } + FileContainerIterator end() const { return FileContainerIterator({this, m_progressMaximum}); } + int progressMaximum() const { return m_progressMaximum; } protected: - void update(int requestedIndex) override; - int currentFileCount() const override; - const Item &itemAt(int index) const override; + FileContainer(const AdvancerProvider &provider, int progressMaximum) + : m_provider(provider) + , m_progressMaximum(progressMaximum) {} private: - std::function m_filterFiles; - QTextCodec *m_encoding; - QStack m_dirs; - QSet m_knownDirs; - QStack m_progressValues; - QStack m_processedValues; - qreal m_progress; - // Use heap allocated objects directly because we want references to stay valid even after resize - QList m_items; + friend class FileContainerIterator; + AdvancerProvider m_provider; + int m_progressMaximum = 0; +}; + +class QTCREATOR_UTILS_EXPORT FileListContainer : public FileContainer +{ +public: + FileListContainer(const FilePaths &fileList, const QList &encodings); +}; + +class QTCREATOR_UTILS_EXPORT SubDirFileContainer : public FileContainer +{ +public: + SubDirFileContainer(const FilePaths &directories, + const QStringList &filters, + const QStringList &exclusionFilters, + QTextCodec *encoding = nullptr); }; QTCREATOR_UTILS_EXPORT QFuture findInFiles(const QString &searchTerm, - FileIterator *files, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap = {}); - -QTCREATOR_UTILS_EXPORT QFuture findInFilesRegExp( - const QString &searchTerm, - FileIterator *files, - QTextDocument::FindFlags flags, - const QMap &fileToContentsMap = {}); + const FileContainer &container, FindFlags flags, const QMap &fileToContentsMap); QTCREATOR_UTILS_EXPORT QString expandRegExpReplacement(const QString &replaceText, const QStringList &capturedTexts); @@ -170,3 +165,6 @@ QTCREATOR_UTILS_EXPORT QString matchCaseReplacement(const QString &originalText, const QString &replaceText); } // namespace Utils + +Q_DECLARE_OPERATORS_FOR_FLAGS(Utils::FindFlags) +Q_DECLARE_METATYPE(Utils::FindFlags) diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp index eb9e057c1a1..24587563019 100644 --- a/src/libs/utils/filestreamer.cpp +++ b/src/libs/utils/filestreamer.cpp @@ -304,12 +304,8 @@ public: void start() override { task()->start(); } }; -} // namespace Utils - -TASKING_DECLARE_TASK(FileStreamReaderTask, Utils::FileStreamReaderAdapter); -TASKING_DECLARE_TASK(FileStreamWriterTask, Utils::FileStreamWriterAdapter); - -namespace Utils { +using FileStreamReaderTask = CustomTask; +using FileStreamWriterTask = CustomTask; static Group sameRemoteDeviceTransferTask(const FilePath &source, const FilePath &destination) { @@ -355,7 +351,7 @@ static Group interDeviceTransferTask(const FilePath &source, const FilePath &des Storage(storage), FileStreamWriterTask(setupWriter), Group { - WaitForBarrierTask(writerReadyBarrier), + waitForBarrierTask(writerReadyBarrier), FileStreamReaderTask(setupReader, finalizeReader, finalizeReader) } }; diff --git a/src/libs/utils/filestreamer.h b/src/libs/utils/filestreamer.h index a5b6ff4f256..828f27be8e1 100644 --- a/src/libs/utils/filestreamer.h +++ b/src/libs/utils/filestreamer.h @@ -58,6 +58,6 @@ public: void start() override { task()->start(); } }; -} // namespace Utils +using FileStreamerTask = Tasking::CustomTask; -TASKING_DECLARE_TASK(FileStreamerTask, Utils::FileStreamerTaskAdapter); +} // namespace Utils diff --git a/src/libs/utils/filesystemmodel.cpp b/src/libs/utils/filesystemmodel.cpp index 9a10f3b91fc..fb0441185d1 100644 --- a/src/libs/utils/filesystemmodel.cpp +++ b/src/libs/utils/filesystemmodel.cpp @@ -1053,10 +1053,8 @@ FileSystemNode *FileSystemModelPrivate::node(const QString &path, bool fetch) co elementPath = host; elementPath.append(separator); } else { - if (!pathElements.at(0).contains(QLatin1Char(':'))) { - QString rootPath = QDir(longPath).rootPath(); - pathElements.prepend(rootPath); - } + if (!pathElements.at(0).contains(QLatin1Char(':'))) + pathElements.prepend(HostOsInfo::root().path()); if (pathElements.at(0).endsWith(QLatin1Char('/'))) pathElements[0].chop(1); } @@ -2520,7 +2518,7 @@ QStringList FileSystemModelPrivate::unwatchPathsAt(const QModelIndex &index) QTC_CHECK(useFileSystemWatcher()); const FileSystemNode *indexNode = node(index); if (indexNode == nullptr) - return QStringList(); + return {}; const Qt::CaseSensitivity caseSensitivity = indexNode->caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive; const QString path = indexNode->fileInfo().absoluteFilePath(); diff --git a/src/libs/utils/filesystemwatcher.cpp b/src/libs/utils/filesystemwatcher.cpp index 155f83fba03..7933d31543e 100644 --- a/src/libs/utils/filesystemwatcher.cpp +++ b/src/libs/utils/filesystemwatcher.cpp @@ -275,7 +275,7 @@ void FileSystemWatcher::addFiles(const QStringList &files, WatchMode wm) d->m_files.insert(file, WatchEntry(file, wm)); const int count = ++d->m_staticData->m_fileCount[file]; - Q_ASSERT(count > 0); + QTC_CHECK(count > 0); if (count == 1) { toAdd << file; @@ -284,7 +284,7 @@ void FileSystemWatcher::addFiles(const QStringList &files, WatchMode wm) if (!fi.exists()) { const QString directory = fi.path(); const int dirCount = ++d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(dirCount > 0); + QTC_CHECK(dirCount > 0); if (dirCount == 1) toAdd << directory; @@ -315,7 +315,7 @@ void FileSystemWatcher::removeFiles(const QStringList &files) d->m_files.erase(it); const int count = --(d->m_staticData->m_fileCount[file]); - Q_ASSERT(count >= 0); + QTC_CHECK(count >= 0); if (!count) { toRemove << file; @@ -324,7 +324,7 @@ void FileSystemWatcher::removeFiles(const QStringList &files) if (!fi.exists()) { const QString directory = fi.path(); const int dirCount = --d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(dirCount >= 0); + QTC_CHECK(dirCount >= 0); if (!dirCount) toRemove << directory; @@ -381,7 +381,7 @@ void FileSystemWatcher::addDirectories(const QStringList &directories, WatchMode d->m_directories.insert(directory, WatchEntry(directory, wm)); const int count = ++d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(count > 0); + QTC_CHECK(count > 0); if (count == 1) toAdd << directory; @@ -411,7 +411,7 @@ void FileSystemWatcher::removeDirectories(const QStringList &directories) d->m_directories.erase(it); const int count = --d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(count >= 0); + QTC_CHECK(count >= 0); if (!count) toRemove << directory; @@ -428,27 +428,23 @@ QStringList FileSystemWatcher::directories() const void FileSystemWatcher::slotFileChanged(const QString &path) { const auto it = d->m_files.find(path); - QStringList toAdd; if (it != d->m_files.end() && it.value().trigger(path)) { qCDebug(fileSystemWatcherLog) << this << "triggers on file" << it.key() << it.value().watchMode << it.value().modifiedTime.toString(Qt::ISODate); - d->fileChanged(path); QFileInfo fi(path); if (!fi.exists()) { const QString directory = fi.path(); const int dirCount = ++d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(dirCount > 0); + QTC_CHECK(dirCount > 0); if (dirCount == 1) - toAdd << directory; + d->m_staticData->m_watcher->addPath(directory); } + d->fileChanged(path); } - - if (!toAdd.isEmpty()) - d->m_staticData->m_watcher->addPaths(toAdd); } void FileSystemWatcher::slotDirectoryChanged(const QString &path) @@ -474,20 +470,17 @@ void FileSystemWatcher::slotDirectoryChanged(const QString &path) for (const QString &rejected : d->m_staticData->m_watcher->addPaths(toReadd)) toReadd.removeOne(rejected); - QStringList toRemove; // If we've successfully added the file, that means it was deleted and replaced. for (const QString &reAdded : std::as_const(toReadd)) { - d->fileChanged(reAdded); const QString directory = QFileInfo(reAdded).path(); const int dirCount = --d->m_staticData->m_directoryCount[directory]; - Q_ASSERT(dirCount >= 0); + QTC_CHECK(dirCount >= 0); if (!dirCount) - toRemove << directory; - } + d->m_staticData->m_watcher->removePath(directory); - if (!toRemove.isEmpty()) - d->m_staticData->m_watcher->removePaths(toRemove); + d->fileChanged(reAdded); + } } } diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 4f67ee8105a..3d8f074962e 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -6,6 +6,7 @@ #include "algorithm.h" #include "devicefileaccess.h" +#include "environment.h" #include "qtcassert.h" #include "utilstr.h" @@ -721,7 +722,7 @@ bool FileUtils::copyRecursively( bool FileUtils::copyIfDifferent(const FilePath &srcFilePath, const FilePath &tgtFilePath) { - QTC_ASSERT(srcFilePath.exists(), return false); + QTC_ASSERT(srcFilePath.exists(), qDebug() << srcFilePath.toUserOutput(); return false); QTC_ASSERT(srcFilePath.isSameDevice(tgtFilePath), return false); if (tgtFilePath.exists()) { @@ -843,4 +844,18 @@ qint64 FileUtils::bytesAvailableFromDFOutput(const QByteArray &dfOutput) return -1; } +FilePaths FileUtils::usefulExtraSearchPaths() +{ + if (HostOsInfo::isMacHost()) { + return {"/opt/homebrew/bin"}; + } else if (HostOsInfo::isWindowsHost()) { + const QString programData = + qtcEnvironmentVariable("ProgramData", + qtcEnvironmentVariable("ALLUSERSPROFILE", "C:/ProgramData")); + return {FilePath::fromUserInput(programData) / "chocolatey/bin"}; + } + + return {}; +} + } // namespace Utils diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index a1a7ffef977..316030b0397 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -85,6 +85,9 @@ public: static FilePathInfo filePathInfoFromTriple(const QString &infos, int modeBase); + //! Returns known paths like /opt/homebrew on macOS that might not be in PATH + static FilePaths usefulExtraSearchPaths(); + #ifdef QT_WIDGETS_LIB static void setDialogParentGetter(const std::function &getter); diff --git a/src/libs/utils/filewizardpage.cpp b/src/libs/utils/filewizardpage.cpp index a883f960566..4d2dba6ab30 100644 --- a/src/libs/utils/filewizardpage.cpp +++ b/src/libs/utils/filewizardpage.cpp @@ -50,9 +50,10 @@ FileWizardPage::FileWizardPage(QWidget *parent) : d->m_defaultSuffixLabel = new QLabel; d->m_nameLabel = new QLabel; d->m_nameLineEdit = new FileNameValidatingLineEdit; - d->m_nameLineEdit->setObjectName("nameLineEdit"); + d->m_nameLineEdit->setObjectName("nameLineEdit"); // used by Squish d->m_pathLabel = new QLabel; d->m_pathChooser = new PathChooser; + d->m_pathChooser->setObjectName("fullPathChooser"); // used by Squish d->m_pathChooser->setExpectedKind(PathChooser::Directory); d->m_nameLabel->setText(Tr::tr("File name:")); diff --git a/src/libs/utils/fixedsizeclicklabel.cpp b/src/libs/utils/fixedsizeclicklabel.cpp deleted file mode 100644 index 090ea1ca4a6..00000000000 --- a/src/libs/utils/fixedsizeclicklabel.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "fixedsizeclicklabel.h" - -#include - -/*! - \class Utils::FixedSizeClickLabel - - \brief The FixedSizeClickLabel class is a label with a size hint derived from a sample text - that can be different to the text that is shown. - - For convenience it also has a clicked signal that is emitted whenever the label receives a mouse - click. - - \inmodule Qt Creator -*/ - -/*! - \fn Utils::FixedSizeClickLabel::clicked() - - This signal is emitted when the label is clicked with the left mouse button. -*/ - -/*! - \property Utils::FixedSizeClickLabel::maxText - - This property holds the text that is used to calculate the label's size hint. -*/ - -namespace Utils { - -/*! - Constructs a FixedSizeClickLabel with the parent \a parent. -*/ -FixedSizeClickLabel::FixedSizeClickLabel(QWidget *parent) - : QLabel(parent) -{ -} - -/*! - Sets the label's text to \a text, and changes the size hint of the label to the size of - \a maxText. - - \sa maxText - \sa setMaxText -*/ -void FixedSizeClickLabel::setText(const QString &text, const QString &maxText) -{ - QLabel::setText(text); - m_maxText = maxText; -} - -/*! - \reimp -*/ -QSize FixedSizeClickLabel::sizeHint() const -{ - return fontMetrics().boundingRect(m_maxText).size(); -} - -QString FixedSizeClickLabel::maxText() const -{ - return m_maxText; -} - -void FixedSizeClickLabel::setMaxText(const QString &maxText) -{ - m_maxText = maxText; -} - -/*! - \reimp -*/ -void FixedSizeClickLabel::mousePressEvent(QMouseEvent *ev) -{ - QLabel::mousePressEvent(ev); - if (ev->button() == Qt::LeftButton) - m_pressed = true; -} - -/*! - \reimp -*/ -void FixedSizeClickLabel::mouseReleaseEvent(QMouseEvent *ev) -{ - QLabel::mouseReleaseEvent(ev); - if (ev->button() != Qt::LeftButton) - return; - if (m_pressed && rect().contains(ev->pos())) - emit clicked(); - m_pressed = false; -} - -} // namespace Utils diff --git a/src/libs/utils/fixedsizeclicklabel.h b/src/libs/utils/fixedsizeclicklabel.h deleted file mode 100644 index b663efc47a8..00000000000 --- a/src/libs/utils/fixedsizeclicklabel.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include - -namespace Utils { - -class QTCREATOR_UTILS_EXPORT FixedSizeClickLabel : public QLabel -{ - Q_OBJECT - Q_PROPERTY(QString maxText READ maxText WRITE setMaxText DESIGNABLE true) - -public: - explicit FixedSizeClickLabel(QWidget *parent = nullptr); - - void setText(const QString &text, const QString &maxText); - using QLabel::setText; - QSize sizeHint() const override; - - QString maxText() const; - void setMaxText(const QString &maxText); - -protected: - void mousePressEvent(QMouseEvent *ev) override; - void mouseReleaseEvent(QMouseEvent *ev) override; - -signals: - void clicked(); - -private: - QString m_maxText; - bool m_pressed = false; -}; - -} // namespace Utils diff --git a/src/libs/utils/fsengine/fileiconprovider.cpp b/src/libs/utils/fsengine/fileiconprovider.cpp index cd2cc060a21..9d2a7c8c050 100644 --- a/src/libs/utils/fsengine/fileiconprovider.cpp +++ b/src/libs/utils/fsengine/fileiconprovider.cpp @@ -85,7 +85,31 @@ public: m_suffixCache.insert(suffix, iconFilePath); } - void registerIconOverlayForMimeType(const QIcon &icon, const Utils::MimeType &mimeType) + void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeName) + { + // avoid accessing the MIME database right away + m_mimeUpdater.append([this, icon, mimeName] { + addIconOverlayForMimeType(icon, Utils::mimeTypeForName(mimeName)); + }); + } + + void registerIconForMimeType(const QIcon &icon, const QString &mimeName) + { + // avoid accessing the MIME database right away + m_mimeUpdater.append( + [this, icon, mimeName] { addIconForMimeType(icon, Utils::mimeTypeForName(mimeName)); }); + } + + void registerIconOverlayForMimeType(const QString &iconFilePath, const QString &mimeName) + { + // avoid accessing the MIME database right away + m_mimeUpdater.append([this, iconFilePath, mimeName] { + addIconOverlayForMimeType(iconFilePath, Utils::mimeTypeForName(mimeName)); + }); + } + +private: + void addIconOverlayForMimeType(const QIcon &icon, const Utils::MimeType &mimeType) { const QStringList suffixes = mimeType.suffixes(); for (const QString &suffix : suffixes) { @@ -99,16 +123,31 @@ public: } } - void registerIconOverlayForMimeType(const QString &iconFilePath, const Utils::MimeType &mimeType) + void addIconOverlayForMimeType(const QString &iconFilePath, const Utils::MimeType &mimeType) { const QStringList suffixes = mimeType.suffixes(); for (const QString &suffix : suffixes) registerIconOverlayForSuffix(iconFilePath, suffix); } + void addIconForMimeType(const QIcon &icon, const Utils::MimeType &mimeType) + { + const QStringList suffixes = mimeType.suffixes(); + for (const QString &suffix : suffixes) + m_suffixCache.insert(suffix, icon); + } + + void ensureMimeOverlays() const + { + for (const std::function &f : m_mimeUpdater) + f(); + m_mimeUpdater.clear(); + } + // Mapping of file suffix to icon. mutable QHash m_suffixCache; mutable QHash m_filenameCache; + mutable QList> m_mimeUpdater; // QAbstractFileIconProvider interface public: @@ -165,11 +204,9 @@ static const QIcon &dirIcon() return icon; } -QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const +QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const { - qCDebug(fileIconProvider) << "FileIconProvider::icon" << fi.absoluteFilePath(); - - const FilePath filePath = FilePath::fromString(fi.filePath()); + qCDebug(fileIconProvider) << "FileIconProvider::icon" << filePath.absoluteFilePath(); if (filePath.isEmpty()) return unknownFileIcon(); @@ -181,17 +218,19 @@ QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const return dirIcon(); } - bool isDir = fi.isDir(); + const bool isDir = filePath.isDir(); // Check for cached overlay icons by file suffix. - const QString filename = !isDir ? fi.fileName() : QString(); + const QString filename = !isDir ? filePath.fileName() : QString(); if (!filename.isEmpty()) { const std::optional icon = getIcon(m_filenameCache, filename); if (icon) return *icon; } - const QString suffix = !isDir ? fi.suffix() : QString(); + ensureMimeOverlays(); + + const QString suffix = !isDir ? filePath.suffix() : QString(); if (!suffix.isEmpty()) { const std::optional icon = getIcon(m_suffixCache, suffix); if (icon) @@ -213,11 +252,11 @@ QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const return icon; } -QIcon FileIconProviderImplementation::icon(const FilePath &filePath) const +QIcon FileIconProviderImplementation::icon(const QFileInfo &fi) const { - qCDebug(fileIconProvider) << "FileIconProvider::icon" << filePath.absoluteFilePath(); + qCDebug(fileIconProvider) << "FileIconProvider::icon" << fi.absoluteFilePath(); - return icon(QFileInfo(filePath.toFSPathString())); + return icon(FilePath::fromString(fi.filePath())); } /*! @@ -278,7 +317,12 @@ void registerIconOverlayForSuffix(const QString &path, const QString &suffix) */ void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeType) { - instance()->registerIconOverlayForMimeType(icon, Utils::mimeTypeForName(mimeType)); + instance()->registerIconOverlayForMimeType(icon, mimeType); +} + +void registerIconForMimeType(const QIcon &icon, const QString &mimeType) +{ + instance()->registerIconForMimeType(icon, mimeType); } /*! @@ -287,7 +331,7 @@ void registerIconOverlayForMimeType(const QIcon &icon, const QString &mimeType) */ void registerIconOverlayForMimeType(const QString &path, const QString &mimeType) { - instance()->registerIconOverlayForMimeType(path, Utils::mimeTypeForName(mimeType)); + instance()->registerIconOverlayForMimeType(path, mimeType); } void registerIconOverlayForFilename(const QString &path, const QString &filename) diff --git a/src/libs/utils/fsengine/fileiconprovider.h b/src/libs/utils/fsengine/fileiconprovider.h index 60479220cd8..5db70e62587 100644 --- a/src/libs/utils/fsengine/fileiconprovider.h +++ b/src/libs/utils/fsengine/fileiconprovider.h @@ -37,6 +37,8 @@ QTCREATOR_UTILS_EXPORT void registerIconOverlayForMimeType(const QIcon &icon, QTCREATOR_UTILS_EXPORT QIcon directoryIcon(const QString &overlay); +QTCREATOR_UTILS_EXPORT void registerIconForMimeType(const QIcon &icon, const QString &mimeType); + } // namespace FileIconProvider } // namespace Utils diff --git a/src/libs/utils/fsengine/fileiteratordevicesappender.h b/src/libs/utils/fsengine/fileiteratordevicesappender.h index 9aec917d6b7..a28f1190806 100644 --- a/src/libs/utils/fsengine/fileiteratordevicesappender.h +++ b/src/libs/utils/fsengine/fileiteratordevicesappender.h @@ -4,6 +4,7 @@ #pragma once #include "../filepath.h" +#include "../hostosinfo.h" #include @@ -97,7 +98,7 @@ private: // We only need QDir::cleanPath here, as the path is always // a fs engine path and will not contain scheme:// etc. const QString p = QDir::cleanPath(path()); - if (p.compare(QDir::rootPath(), Qt::CaseInsensitive) == 0) + if (p.compare(HostOsInfo::root().path(), Qt::CaseInsensitive) == 0) m_status = State::IteratingRoot; ((*m_baseIterator).*get(QAFEITag()))(p); diff --git a/src/libs/utils/fsengine/fsenginehandler.cpp b/src/libs/utils/fsengine/fsenginehandler.cpp index e020eebddac..2f828537d19 100644 --- a/src/libs/utils/fsengine/fsenginehandler.cpp +++ b/src/libs/utils/fsengine/fsenginehandler.cpp @@ -10,6 +10,7 @@ #include "fsengine.h" #include "../algorithm.h" +#include "../hostosinfo.h" namespace Utils::Internal { @@ -33,6 +34,18 @@ static FilePath removeDoubleSlash(const QString &fileName) return FilePath::fromString(result); } +static bool isRootPath(const QString &fileName) +{ + if (HostOsInfo::isWindowsHost()) { + static const QChar lowerDriveLetter = HostOsInfo::root().path().front().toUpper(); + static const QChar upperDriveLetter = HostOsInfo::root().path().front().toLower(); + return fileName.size() == 3 && fileName[1] == ':' && fileName[2] == '/' + && (fileName[0] == lowerDriveLetter || fileName[0] == upperDriveLetter); + } + + return fileName.size() == 1 && fileName[0] == '/'; +} + QAbstractFileEngine *FSEngineHandler::create(const QString &fileName) const { if (fileName.startsWith(':')) @@ -71,7 +84,7 @@ QAbstractFileEngine *FSEngineHandler::create(const QString &fileName) const return new FSEngineImpl(removeDoubleSlash(fileName)); } - if (fixedFileName.compare(QDir::rootPath(), Qt::CaseInsensitive) == 0) + if (isRootPath(fixedFileName)) return new RootInjectFSEngine(fileName); return nullptr; diff --git a/src/libs/utils/functiontraits.h b/src/libs/utils/functiontraits.h deleted file mode 100644 index 8b052d3e882..00000000000 --- a/src/libs/utils/functiontraits.h +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace Utils { - -/* - struct functionTraits - { - using ResultType; // Return type of Function - struct argument - { - using type; // type of Function argument at index (starting with 0) - } - } - - struct functionTakesArgument; - - Is derived from std::true_type if Function takes an argument of type ArgumentType at index. - Otherwise derived from std::false_type. -*/ - -//////////////////// -// functionTraits -//////////////////// - -// for callables. defined below. -template -struct functionTraits; - -// function -template -struct functionTraits -{ - using ResultType = Result; - static const unsigned arity = sizeof...(Args); // TODO const -> constexpr with MSVC2015 - - template - struct argument - { - using type = typename std::tuple_element>::type; - }; -}; - -// function pointer -template -struct functionTraits : public functionTraits -{ -}; - -// const function pointer -template -struct functionTraits : public functionTraits -{ -}; - -// member function -template -struct functionTraits : public functionTraits -{ -}; - -// const member function -template -struct functionTraits : public functionTraits -{ -}; - -// const pointer to member function -template -struct functionTraits : public functionTraits -{ -}; - -// const pointer to const member function -template -struct functionTraits : public functionTraits -{ -}; - -// TODO: enable lvalue and rvalue ref member function later (MSVC 2015?) -//// lvalue ref member function -//template -//struct functionTraits : public functionTraits -//{ -//}; - -//// const lvalue ref member function -//template -//struct functionTraits : public functionTraits -//{ -//}; - -//// rvalue ref member function -//template -//struct functionTraits : public functionTraits -//{ -//}; - -// callables. only works if operator() is not overloaded. -template -struct functionTraits -{ - using callableTraits = functionTraits; - using ResultType = typename callableTraits::ResultType; - static const unsigned arity = callableTraits::arity - 1; // ignore object pointer arg // TODO const -> constexpr with MSVC2015 - - template - struct argument - { - using type = typename callableTraits::template argument::type; // ignore object pointer arg - }; -}; - -// lvalue ref callables -template -struct functionTraits : public functionTraits -{ -}; - -// const lvalue ref callables -template -struct functionTraits : public functionTraits -{ -}; - -// rvalue ref callables -template -struct functionTraits : public functionTraits -{ -}; - -template -using functionResult_t = typename functionTraits::ResultType; - -//////////////////// -// functionTakesArgument -//////////////////// -namespace Internal { - -template -struct functionTakesArgumentArityDispatch; - -template -struct functionTakesArgumentArityDispatch - : public std::false_type -{ -}; - -template -struct functionTakesArgumentArityDispatch - : public std::is_same::template argument::type> -{ -}; - -} // Internal - -template -struct functionTakesArgument : public Internal::functionTakesArgumentArityDispatch< - std::integral_constant::arity > index)>, - Function, index, T> -{ -}; - -} // Utils diff --git a/src/libs/utils/fuzzymatcher.h b/src/libs/utils/fuzzymatcher.h index dc665ec8648..88c5269870d 100644 --- a/src/libs/utils/fuzzymatcher.h +++ b/src/libs/utils/fuzzymatcher.h @@ -7,7 +7,7 @@ #include "utils_global.h" -#include +#include QT_BEGIN_NAMESPACE class QRegularExpression; @@ -26,8 +26,8 @@ public: class HighlightingPositions { public: - QVector starts; - QVector lengths; + QList starts; + QList lengths; }; static QRegularExpression createRegExp( diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp index 22d55e23bf0..7fe0507311a 100644 --- a/src/libs/utils/highlightingitemdelegate.cpp +++ b/src/libs/utils/highlightingitemdelegate.cpp @@ -134,12 +134,12 @@ void HighlightingItemDelegate::drawText(QPainter *painter, if (index.model()->hasChildren(index)) text += " (" + QString::number(index.model()->rowCount(index)) + ')'; - QVector searchTermStarts = - index.model()->data(index, int(HighlightingItemRole::StartColumn)).value>(); - QVector searchTermLengths = - index.model()->data(index, int(HighlightingItemRole::Length)).value>(); + QList searchTermStarts + = index.model()->data(index, int(HighlightingItemRole::StartColumn)).value>(); + QList searchTermLengths + = index.model()->data(index, int(HighlightingItemRole::Length)).value>(); - QVector formats; + QList formats; const QString extraText = index.model()->data(index, int(HighlightingItemRole::DisplayExtra)).toString(); @@ -230,7 +230,7 @@ QSizeF doTextLayout(QTextLayout *textLayout, int lineWidth) void HighlightingItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text, - const QVector &format) const + const QList &format) const { QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; diff --git a/src/libs/utils/highlightingitemdelegate.h b/src/libs/utils/highlightingitemdelegate.h index cbda7042c26..ed47f18cca2 100644 --- a/src/libs/utils/highlightingitemdelegate.h +++ b/src/libs/utils/highlightingitemdelegate.h @@ -36,7 +36,7 @@ private: const QRect &rect, const QModelIndex &index) const; using QItemDelegate::drawDisplay; void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, - const QString &text, const QVector &format) const; + const QString &text, const QList &format) const; QString m_tabString; }; diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp index f8c93b9fbef..8eff3fdad82 100644 --- a/src/libs/utils/historycompleter.cpp +++ b/src/libs/utils/historycompleter.cpp @@ -30,8 +30,8 @@ public: void addEntry(const QString &str); QStringList list; - QString historyKey; - QString historyKeyIsLastItemEmpty; + Key historyKey; + Key historyKeyIsLastItemEmpty; int maxLines = 6; bool isLastItemEmpty = isLastItemEmptyDefault; }; @@ -174,17 +174,16 @@ void HistoryCompleterPrivate::addEntry(const QString &str) isLastItemEmptyDefault); } -HistoryCompleter::HistoryCompleter(const QString &historyKey, QObject *parent) +HistoryCompleter::HistoryCompleter(const Key &historyKey, QObject *parent) : QCompleter(parent), d(new HistoryCompleterPrivate) { QTC_ASSERT(!historyKey.isEmpty(), return); QTC_ASSERT(theSettings, return); - d->historyKey = QLatin1String("CompleterHistory/") + historyKey; + d->historyKey = "CompleterHistory/" + historyKey; d->list = theSettings->value(d->historyKey).toStringList(); - d->historyKeyIsLastItemEmpty = QLatin1String("CompleterHistory/") - + historyKey + QLatin1String(".IsLastItemEmpty"); + d->historyKeyIsLastItemEmpty = "CompleterHistory/" + historyKey + ".IsLastItemEmpty"; d->isLastItemEmpty = theSettings->value(d->historyKeyIsLastItemEmpty, isLastItemEmptyDefault) .toBool(); @@ -208,10 +207,10 @@ QString HistoryCompleter::historyItem() const return d->list.at(0); } -bool HistoryCompleter::historyExistsFor(const QString &historyKey) +bool HistoryCompleter::historyExistsFor(const Key &historyKey) { QTC_ASSERT(theSettings, return false); - const QString fullKey = QLatin1String("CompleterHistory/") + historyKey; + const Key fullKey = "CompleterHistory/" + historyKey; return theSettings->value(fullKey).isValid(); } diff --git a/src/libs/utils/historycompleter.h b/src/libs/utils/historycompleter.h index 81335413edc..4afb3f420ca 100644 --- a/src/libs/utils/historycompleter.h +++ b/src/libs/utils/historycompleter.h @@ -5,6 +5,8 @@ #include "utils_global.h" +#include "storekey.h" + #include namespace Utils { @@ -15,15 +17,15 @@ namespace Internal { class HistoryCompleterPrivate; } class QTCREATOR_UTILS_EXPORT HistoryCompleter : public QCompleter { - Q_OBJECT - public: static void setSettings(QtcSettings *settings); - HistoryCompleter(const QString &historyKey, QObject *parent = nullptr); + HistoryCompleter(const Key &historyKey, QObject *parent = nullptr); bool removeHistoryItem(int index); QString historyItem() const; bool hasHistory() const { return historySize() > 0; } - static bool historyExistsFor(const QString &historyKey); + static bool historyExistsFor(const Key &historyKey); + void clearHistory(); + void addEntry(const QString &str); private: ~HistoryCompleter() override; @@ -31,10 +33,6 @@ private: int maximalHistorySize() const; void setMaximalHistorySize(int numberOfEntries); -public Q_SLOTS: - void clearHistory(); - void addEntry(const QString &str); - private: Internal::HistoryCompleterPrivate *d; }; diff --git a/src/libs/utils/hostosinfo.cpp b/src/libs/utils/hostosinfo.cpp index bf038ea82d2..d8361266f08 100644 --- a/src/libs/utils/hostosinfo.cpp +++ b/src/libs/utils/hostosinfo.cpp @@ -3,8 +3,11 @@ #include "hostosinfo.h" +#include "filepath.h" #include "utilstr.h" +#include + #if !defined(QT_NO_OPENGL) && defined(QT_GUI_LIB) #include #endif @@ -21,7 +24,7 @@ #include #endif -using namespace Utils; +namespace Utils { Qt::CaseSensitivity HostOsInfo::m_overrideFileNameCaseSensitivity = Qt::CaseSensitive; bool HostOsInfo::m_useOverrideFileNameCaseSensitivity = false; @@ -116,3 +119,11 @@ std::optional HostOsInfo::totalMemoryInstalledInBytes() #endif return {}; } + +const FilePath &HostOsInfo::root() +{ + static const FilePath rootDir = FilePath::fromUserInput(QDir::rootPath()); + return rootDir; +} + +} // namespace Utils diff --git a/src/libs/utils/hostosinfo.h b/src/libs/utils/hostosinfo.h index 090bf9e6d4a..44880f7885c 100644 --- a/src/libs/utils/hostosinfo.h +++ b/src/libs/utils/hostosinfo.h @@ -21,6 +21,8 @@ QT_END_NAMESPACE namespace Utils { +class FilePath; + class QTCREATOR_UTILS_EXPORT HostOsInfo { public: @@ -86,6 +88,8 @@ public: static std::optional totalMemoryInstalledInBytes(); + static const FilePath &root(); + private: static Qt::CaseSensitivity m_overrideFileNameCaseSensitivity; static bool m_useOverrideFileNameCaseSensitivity; diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp index 4ce143b56c7..5bd862c8f14 100644 --- a/src/libs/utils/icon.cpp +++ b/src/libs/utils/icon.cpp @@ -6,6 +6,7 @@ #include "qtcassert.h" #include "theme/theme.h" #include "stylehelper.h" +#include "utilsicons.h" #include #include @@ -36,7 +37,7 @@ static QPixmap maskToColorAndAlpha(const QPixmap &mask, const QColor &color) using MaskAndColor = QPair; using MasksAndColors = QList; -static MasksAndColors masksAndColors(const QVector &icon, int dpr) +static MasksAndColors masksAndColors(const QList &icon, int dpr) { MasksAndColors result; for (const IconMaskAndColor &i: icon) { @@ -134,15 +135,15 @@ static QPixmap masksToIcon(const MasksAndColors &masks, const QPixmap &combinedM Icon::Icon() = default; -Icon::Icon(QVector args, Icon::IconStyleOptions style) - : m_iconSourceList(std::move(args)) +Icon::Icon(const QList &args, Icon::IconStyleOptions style) + : m_iconSourceList(args) , m_style(style) { } Icon::Icon(const FilePath &imageFileName) + : m_iconSourceList({{imageFileName, Theme::Color(-1)}}) { - m_iconSourceList.append({imageFileName, Theme::Color(-1)}); } QIcon Icon::icon() const @@ -234,4 +235,52 @@ QIcon Icon::combinedIcon(const QList &icons) return combinedIcon(qIcons); } +QIcon Icon::fromTheme(const QString &name) +{ + static QHash cache; + + auto found = cache.find(name); + if (found != cache.end()) + return *found; + + QIcon icon = QIcon::fromTheme(name); + if (name == "go-next") { + cache.insert(name, !icon.isNull() ? icon : QIcon(":/utils/images/arrow.png")); + } else if (name == "document-open") { + cache.insert(name, !icon.isNull() ? icon : Icons::OPENFILE.icon()); + } else if (name == "edit-copy") { + cache.insert(name, !icon.isNull() ? icon : Icons::COPY.icon()); + } else if (name == "document-new") { + cache.insert(name, !icon.isNull() ? icon : Icons::NEWFILE.icon()); + } else if (name == "document-save") { + cache.insert(name, !icon.isNull() ? icon : Icons::SAVEFILE.icon()); + } else if (name == "edit-undo") { + cache.insert(name, !icon.isNull() ? icon : Icons::UNDO.icon()); + } else if (name == "edit-redo") { + cache.insert(name, !icon.isNull() ? icon : Icons::REDO.icon()); + } else if (name == "edit-cut") { + cache.insert(name, !icon.isNull() ? icon : Icons::CUT.icon()); + } else if (name == "edit-paste") { + cache.insert(name, !icon.isNull() ? icon : Icons::PASTE.icon()); + } else if (name == "zoom-in") { + cache.insert(name, !icon.isNull() ? icon : Icons::ZOOMIN_TOOLBAR.icon()); + } else if (name == "zoom-out") { + cache.insert(name, !icon.isNull() ? icon : Icons::ZOOMOUT_TOOLBAR.icon()); + } else if (name == "zoom-original") { + cache.insert(name, !icon.isNull() ? icon : Icons::EYE_OPEN_TOOLBAR.icon()); + } else if (name == "edit-clear") { + cache.insert(name, !icon.isNull() ? icon : Icons::EDIT_CLEAR.icon()); + } else if (name == "edit-clear-locationbar-rtl") { + // KDE has custom icons for this. If these icons are not available we use the freedesktop + // standard name "edit-clear" before falling back to a bundled resource. + cache.insert(name, !icon.isNull() ? icon : fromTheme("edit-clear")); + } else if (name == "edit-clear-locationbar-ltr") { + cache.insert(name, !icon.isNull() ? icon : fromTheme("edit-clear")); + } else { + cache.insert(name, icon); + } + + return cache[name]; +} + } // namespace Utils diff --git a/src/libs/utils/icon.h b/src/libs/utils/icon.h index c2c5e7a3de5..d26668082bf 100644 --- a/src/libs/utils/icon.h +++ b/src/libs/utils/icon.h @@ -9,8 +9,8 @@ #include "theme/theme.h" #include +#include #include -#include QT_BEGIN_NAMESPACE class QColor; @@ -40,7 +40,7 @@ public: Q_DECLARE_FLAGS(IconStyleOptions, IconStyleOption) Icon(); - Icon(QVector args, IconStyleOptions style = ToolBarStyle); + Icon(const QList &args, IconStyleOptions style = ToolBarStyle); Icon(const FilePath &imageFileName); QIcon icon() const; @@ -61,8 +61,10 @@ public: static QIcon combinedIcon(const QList &icons); static QIcon combinedIcon(const QList &icons); + static QIcon fromTheme(const QString &name); + private: - QVector m_iconSourceList; + QList m_iconSourceList; IconStyleOptions m_style = None; mutable int m_lastDevicePixelRatio = -1; mutable QIcon m_lastIcon; diff --git a/src/libs/utils/iconbutton.cpp b/src/libs/utils/iconbutton.cpp new file mode 100644 index 00000000000..746cf0ecb1c --- /dev/null +++ b/src/libs/utils/iconbutton.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "iconbutton.h" + +#include "stylehelper.h" +#include "theme/theme.h" + +#include +#include +#include + +namespace Utils { + +IconButton::IconButton(QWidget *parent) + : QAbstractButton(parent) +{ + setAttribute(Qt::WA_Hover); +} + +void IconButton::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + + QPainter p(this); + QRect r(QPoint(), size()); + + if (m_containsMouse && isEnabled()) { + QColor c = creatorTheme()->color(Theme::TextColorDisabled); + c.setAlphaF(c.alphaF() * .5); + StyleHelper::drawPanelBgRect(&p, r, c); + } + + icon().paint(&p, r, Qt::AlignCenter); +} + +void IconButton::enterEvent(QEnterEvent *e) +{ + m_containsMouse = true; + e->accept(); + update(); +} + +void IconButton::leaveEvent(QEvent *e) +{ + m_containsMouse = false; + e->accept(); + update(); +} + +QSize IconButton::sizeHint() const +{ + QWindow *window = this->window()->windowHandle(); + QSize s = icon().actualSize(window, QSize(32, 16)) + QSize(8, 8); + + if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleRelaxed) + s += QSize(5, 5); + + return s; +} + +} // namespace Utils diff --git a/src/libs/utils/iconbutton.h b/src/libs/utils/iconbutton.h new file mode 100644 index 00000000000..db615eefb11 --- /dev/null +++ b/src/libs/utils/iconbutton.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT IconButton : public QAbstractButton +{ +public: + IconButton(QWidget *parent = nullptr); + + void paintEvent(QPaintEvent *e) override; + void enterEvent(QEnterEvent *e) override; + void leaveEvent(QEvent *e) override; + + QSize sizeHint() const override; + +private: + bool m_containsMouse{false}; +}; + +} // namespace Utils diff --git a/src/libs/utils/id.cpp b/src/libs/utils/id.cpp index bc40c154611..383d992da0d 100644 --- a/src/libs/utils/id.cpp +++ b/src/libs/utils/id.cpp @@ -158,6 +158,12 @@ QString Id::toString() const return QString::fromUtf8(stringFromId.value(m_id).str); } +/*! \internal */ +Key Id::toKey() const +{ + return name(); +} + /*! Creates an id from a string representation. diff --git a/src/libs/utils/id.h b/src/libs/utils/id.h index 60351dad902..d1ac2355dd7 100644 --- a/src/libs/utils/id.h +++ b/src/libs/utils/id.h @@ -5,6 +5,8 @@ #include "utils_global.h" +#include "storekey.h" + #include #include #include @@ -30,6 +32,7 @@ public: QByteArray name() const; QString toString() const; // Avoid. + Key toKey() const; // FIXME: Replace uses with .name() after Store/key transition. QVariant toSetting() const; // Good to use. QString suffixAfter(Id baseId) const; bool isValid() const { return m_id; } diff --git a/src/plugins/projectexplorer/images/continue_1_small.png b/src/libs/utils/images/continue_1_small.png similarity index 100% rename from src/plugins/projectexplorer/images/continue_1_small.png rename to src/libs/utils/images/continue_1_small.png diff --git a/src/plugins/projectexplorer/images/continue_1_small@2x.png b/src/libs/utils/images/continue_1_small@2x.png similarity index 100% rename from src/plugins/projectexplorer/images/continue_1_small@2x.png rename to src/libs/utils/images/continue_1_small@2x.png diff --git a/src/plugins/projectexplorer/images/continue_2_small.png b/src/libs/utils/images/continue_2_small.png similarity index 100% rename from src/plugins/projectexplorer/images/continue_2_small.png rename to src/libs/utils/images/continue_2_small.png diff --git a/src/plugins/projectexplorer/images/continue_2_small@2x.png b/src/libs/utils/images/continue_2_small@2x.png similarity index 100% rename from src/plugins/projectexplorer/images/continue_2_small@2x.png rename to src/libs/utils/images/continue_2_small@2x.png diff --git a/src/plugins/projectexplorer/images/debugger_overlay_small.png b/src/libs/utils/images/debugger_overlay_small.png similarity index 100% rename from src/plugins/projectexplorer/images/debugger_overlay_small.png rename to src/libs/utils/images/debugger_overlay_small.png diff --git a/src/plugins/projectexplorer/images/debugger_overlay_small@2x.png b/src/libs/utils/images/debugger_overlay_small@2x.png similarity index 100% rename from src/plugins/projectexplorer/images/debugger_overlay_small@2x.png rename to src/libs/utils/images/debugger_overlay_small@2x.png diff --git a/src/libs/utils/infobar.cpp b/src/libs/utils/infobar.cpp index 430a294adcd..37daa486def 100644 --- a/src/libs/utils/infobar.cpp +++ b/src/libs/utils/infobar.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -22,7 +21,7 @@ static const char C_SUPPRESSED_WARNINGS[] = "SuppressedWarnings"; namespace Utils { QSet InfoBar::globallySuppressed; -QSettings *InfoBar::m_settings = nullptr; +QtcSettings *InfoBar::m_settings = nullptr; class InfoBarWidget : public QWidget { @@ -184,17 +183,17 @@ void InfoBar::globallyUnsuppressInfo(Id id) writeGloballySuppressedToSettings(); } -void InfoBar::initialize(QSettings *settings) +void InfoBar::initialize(QtcSettings *settings) { m_settings = settings; if (QTC_GUARD(m_settings)) { - const QStringList list = m_settings->value(QLatin1String(C_SUPPRESSED_WARNINGS)).toStringList(); + const QStringList list = m_settings->value(C_SUPPRESSED_WARNINGS).toStringList(); globallySuppressed = transform(list, Id::fromString); } } -QSettings *InfoBar::settings() +QtcSettings *InfoBar::settings() { return m_settings; } @@ -216,7 +215,7 @@ void InfoBar::writeGloballySuppressedToSettings() if (!m_settings) return; const QStringList list = transform(globallySuppressed, &Id::toString); - QtcSettings::setValueWithDefault(m_settings, C_SUPPRESSED_WARNINGS, list); + m_settings->setValueWithDefault(C_SUPPRESSED_WARNINGS, list); } diff --git a/src/libs/utils/infobar.h b/src/libs/utils/infobar.h index 11657375c5b..c59e6164ab4 100644 --- a/src/libs/utils/infobar.h +++ b/src/libs/utils/infobar.h @@ -6,6 +6,7 @@ #include "utils_global.h" #include "id.h" +#include "qtcsettings.h" #include #include @@ -102,8 +103,8 @@ public: static void clearGloballySuppressed(); static bool anyGloballySuppressed(); - static void initialize(QSettings *settings); - static QSettings *settings(); + static void initialize(QtcSettings *settings); + static QtcSettings *settings(); signals: void changed(); @@ -116,15 +117,13 @@ private: QSet m_suppressed; static QSet globallySuppressed; - static QSettings *m_settings; + static QtcSettings *m_settings; friend class InfoBarDisplay; }; class QTCREATOR_UTILS_EXPORT InfoBarDisplay : public QObject { - Q_OBJECT - public: InfoBarDisplay(QObject *parent = nullptr); void setTarget(QBoxLayout *layout, int index); diff --git a/src/libs/utils/itemviews.cpp b/src/libs/utils/itemviews.cpp index 27f7cbb4281..c043b07ff52 100644 --- a/src/libs/utils/itemviews.cpp +++ b/src/libs/utils/itemviews.cpp @@ -3,6 +3,8 @@ #include "itemviews.h" +namespace Utils { + /*! \class Utils::TreeView \inmodule QtCreator @@ -12,6 +14,8 @@ platforms where the default is different. Use with care. Also adds sane keyboard navigation for mac. + + Note: This uses setUniformRowHeights(true) by default. */ /*! @@ -23,6 +27,8 @@ platforms where the default is different. Use with care. Also adds sane keyboard navigation for mac. + + Note: This uses setUniformRowHeights(true) by default. */ /*! @@ -46,3 +52,25 @@ Also adds sane keyboard navigation for mac. */ + +TreeView::TreeView(QWidget *parent) + : View(parent) +{ + setUniformRowHeights(true); +} + +TreeWidget::TreeWidget(QWidget *parent) + : View(parent) +{ + setUniformRowHeights(true); +} + +ListView::ListView(QWidget *parent) + : View(parent) +{} + +ListWidget::ListWidget(QWidget *parent) + : View(parent) +{} + +} // Utils diff --git a/src/libs/utils/itemviews.h b/src/libs/utils/itemviews.h index 8b5074b20cc..50e8a4af01e 100644 --- a/src/libs/utils/itemviews.h +++ b/src/libs/utils/itemviews.h @@ -63,38 +63,26 @@ public: class QTCREATOR_UTILS_EXPORT TreeView : public View { - Q_OBJECT public: - TreeView(QWidget *parent = nullptr) - : View(parent) - {} + TreeView(QWidget *parent = nullptr); }; class QTCREATOR_UTILS_EXPORT TreeWidget : public View { - Q_OBJECT public: - TreeWidget(QWidget *parent = nullptr) - : View(parent) - {} + TreeWidget(QWidget *parent = nullptr); }; class QTCREATOR_UTILS_EXPORT ListView : public View { - Q_OBJECT public: - ListView(QWidget *parent = nullptr) - : View(parent) - {} + ListView(QWidget *parent = nullptr); }; class QTCREATOR_UTILS_EXPORT ListWidget : public View { - Q_OBJECT public: - ListWidget(QWidget *parent = nullptr) - : View(parent) - {} + ListWidget(QWidget *parent = nullptr); }; } // Utils diff --git a/src/libs/utils/json.cpp b/src/libs/utils/json.cpp deleted file mode 100644 index 5afd4a685b7..00000000000 --- a/src/libs/utils/json.cpp +++ /dev/null @@ -1,722 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "json.h" - -#include "fileutils.h" -#include "qtcassert.h" - -#include -#include - -using namespace Utils; - -JsonMemoryPool::~JsonMemoryPool() -{ - for (char *obj : std::as_const(_objs)) { - reinterpret_cast(obj)->~JsonValue(); - delete[] obj; - } -} - -JsonValue::JsonValue(Kind kind) - : m_kind(kind) -{} - -JsonValue::~JsonValue() = default; - -JsonValue *JsonValue::create(const QString &s, JsonMemoryPool *pool) -{ - const QJsonDocument document = QJsonDocument::fromJson(s.toUtf8()); - if (document.isNull()) - return nullptr; - - return build(document.toVariant(), pool); -} - -void *JsonValue::operator new(size_t size, JsonMemoryPool *pool) -{ return pool->allocate(size); } - -void JsonValue::operator delete(void *) -{ } - -void JsonValue::operator delete(void *, JsonMemoryPool *) -{ } - -QString JsonValue::kindToString(JsonValue::Kind kind) -{ - if (kind == String) - return QLatin1String("string"); - if (kind == Double) - return QLatin1String("number"); - if (kind == Int) - return QLatin1String("integer"); - if (kind == Object) - return QLatin1String("object"); - if (kind == Array) - return QLatin1String("array"); - if (kind == Boolean) - return QLatin1String("boolean"); - if (kind == Null) - return QLatin1String("null"); - - return QLatin1String("unknown"); -} - -JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool) -{ - switch (variant.typeId()) { - - case QVariant::List: { - auto newValue = new (pool) JsonArrayValue; - const QList list = variant.toList(); - for (const QVariant &element : list) - newValue->addElement(build(element, pool)); - return newValue; - } - - case QVariant::Map: { - auto newValue = new (pool) JsonObjectValue; - const QVariantMap variantMap = variant.toMap(); - for (QVariantMap::const_iterator it = variantMap.begin(); it != variantMap.end(); ++it) - newValue->addMember(it.key(), build(it.value(), pool)); - return newValue; - } - - case QVariant::String: - return new (pool) JsonStringValue(variant.toString()); - - case QVariant::Int: - return new (pool) JsonIntValue(variant.toInt()); - - case QVariant::Double: - return new (pool) JsonDoubleValue(variant.toDouble()); - - case QVariant::Bool: - return new (pool) JsonBooleanValue(variant.toBool()); - - case QVariant::Invalid: - return new (pool) JsonNullValue; - - default: - break; - } - - return nullptr; -} - - -/////////////////////////////////////////////////////////////////////////////// - -QString JsonSchema::kType() { return QStringLiteral("type"); } -QString JsonSchema::kProperties() { return QStringLiteral("properties"); } -QString JsonSchema::kPatternProperties() { return QStringLiteral("patternProperties"); } -QString JsonSchema::kAdditionalProperties() { return QStringLiteral("additionalProperties"); } -QString JsonSchema::kItems() { return QStringLiteral("items"); } -QString JsonSchema::kAdditionalItems() { return QStringLiteral("additionalItems"); } -QString JsonSchema::kRequired() { return QStringLiteral("required"); } -QString JsonSchema::kDependencies() { return QStringLiteral("dependencies"); } -QString JsonSchema::kMinimum() { return QStringLiteral("minimum"); } -QString JsonSchema::kMaximum() { return QStringLiteral("maximum"); } -QString JsonSchema::kExclusiveMinimum() { return QStringLiteral("exclusiveMinimum"); } -QString JsonSchema::kExclusiveMaximum() { return QStringLiteral("exclusiveMaximum"); } -QString JsonSchema::kMinItems() { return QStringLiteral("minItems"); } -QString JsonSchema::kMaxItems() { return QStringLiteral("maxItems"); } -QString JsonSchema::kUniqueItems() { return QStringLiteral("uniqueItems"); } -QString JsonSchema::kPattern() { return QStringLiteral("pattern"); } -QString JsonSchema::kMinLength() { return QStringLiteral("minLength"); } -QString JsonSchema::kMaxLength() { return QStringLiteral("maxLength"); } -QString JsonSchema::kTitle() { return QStringLiteral("title"); } -QString JsonSchema::kDescription() { return QStringLiteral("description"); } -QString JsonSchema::kExtends() { return QStringLiteral("extends"); } -QString JsonSchema::kRef() { return QStringLiteral("$ref"); } - -JsonSchema::JsonSchema(JsonObjectValue *rootObject, const JsonSchemaManager *manager) - : m_manager(manager) -{ - enter(rootObject); -} - -bool JsonSchema::isTypeConstrained() const -{ - // Simple types - if (JsonStringValue *sv = getStringValue(kType(), currentValue())) - return isCheckableType(sv->value()); - - // Union types - if (JsonArrayValue *av = getArrayValue(kType(), currentValue())) { - QTC_ASSERT(currentIndex() != -1, return false); - QTC_ASSERT(av->elements().at(currentIndex())->kind() == JsonValue::String, return false); - JsonStringValue *sv = av->elements().at(currentIndex())->toString(); - return isCheckableType(sv->value()); - } - - return false; -} - -bool JsonSchema::acceptsType(const QString &type) const -{ - // Simple types - if (JsonStringValue *sv = getStringValue(kType(), currentValue())) - return typeMatches(sv->value(), type); - - // Union types - if (JsonArrayValue *av = getArrayValue(kType(), currentValue())) { - QTC_ASSERT(currentIndex() != -1, return false); - QTC_ASSERT(av->elements().at(currentIndex())->kind() == JsonValue::String, return false); - JsonStringValue *sv = av->elements().at(currentIndex())->toString(); - return typeMatches(sv->value(), type); - } - - return false; -} - -QStringList JsonSchema::validTypes(JsonObjectValue *v) -{ - QStringList all; - - if (JsonStringValue *sv = getStringValue(kType(), v)) - all.append(sv->value()); - - if (JsonObjectValue *ov = getObjectValue(kType(), v)) - return validTypes(ov); - - if (JsonArrayValue *av = getArrayValue(kType(), v)) { - const QList elements = av->elements(); - for (JsonValue *v : elements) { - if (JsonStringValue *sv = v->toString()) - all.append(sv->value()); - else if (JsonObjectValue *ov = v->toObject()) - all.append(validTypes(ov)); - } - } - - return all; -} - -bool JsonSchema::typeMatches(const QString &expected, const QString &actual) -{ - if (expected == QLatin1String("number") && actual == QLatin1String("integer")) - return true; - - return expected == actual; -} - -bool JsonSchema::isCheckableType(const QString &s) -{ - return s == QLatin1String("string") - || s == QLatin1String("number") - || s == QLatin1String("integer") - || s == QLatin1String("boolean") - || s == QLatin1String("object") - || s == QLatin1String("array") - || s == QLatin1String("null"); -} - -QStringList JsonSchema::validTypes() const -{ - return validTypes(currentValue()); -} - -bool JsonSchema::hasTypeSchema() const -{ - return getObjectValue(kType(), currentValue()); -} - -void JsonSchema::enterNestedTypeSchema() -{ - QTC_ASSERT(hasTypeSchema(), return); - - enter(getObjectValue(kType(), currentValue())); -} - -QStringList JsonSchema::properties(JsonObjectValue *v) const -{ - using Members = QHash; - - QStringList all; - - if (JsonObjectValue *ov = getObjectValue(kProperties(), v)) { - const Members members = ov->members(); - const Members::ConstIterator cend = members.constEnd(); - for (Members::ConstIterator it = members.constBegin(); it != cend; ++it) - if (hasPropertySchema(it.key())) - all.append(it.key()); - } - - if (JsonObjectValue *base = resolveBase(v)) - all.append(properties(base)); - - return all; -} - -QStringList JsonSchema::properties() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Object)), return QStringList()); - - return properties(currentValue()); -} - -JsonObjectValue *JsonSchema::propertySchema(const QString &property, - JsonObjectValue *v) const -{ - if (JsonObjectValue *ov = getObjectValue(kProperties(), v)) { - JsonValue *member = ov->member(property); - if (member && member->kind() == JsonValue::Object) - return member->toObject(); - } - - if (JsonObjectValue *base = resolveBase(v)) - return propertySchema(property, base); - - return nullptr; -} - -bool JsonSchema::hasPropertySchema(const QString &property) const -{ - return propertySchema(property, currentValue()); -} - -void JsonSchema::enterNestedPropertySchema(const QString &property) -{ - QTC_ASSERT(hasPropertySchema(property), return); - - JsonObjectValue *schema = propertySchema(property, currentValue()); - - enter(schema); -} - -/*! - * An array schema is allowed to have its \e items specification in the form of - * another schema - * or in the form of an array of schemas [Sec. 5.5]. This functions checks whether this is case - * in which the items are a schema. - * - * Returns whether or not the items from the array are a schema. - */ -bool JsonSchema::hasItemSchema() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); - - return getObjectValue(kItems(), currentValue()); -} - -void JsonSchema::enterNestedItemSchema() -{ - QTC_ASSERT(hasItemSchema(), return); - - enter(getObjectValue(kItems(), currentValue())); -} - -/*! - * An array schema is allowed to have its \e items specification in the form of another schema - * or in the form of an array of schemas [Sec. 5.5]. This functions checks whether this is case - * in which the items are an array of schemas. - * - * Returns whether or not the items from the array are a an array of schemas. - */ -bool JsonSchema::hasItemArraySchema() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); - - return getArrayValue(kItems(), currentValue()); -} - -int JsonSchema::itemArraySchemaSize() const -{ - QTC_ASSERT(hasItemArraySchema(), return false); - - return getArrayValue(kItems(), currentValue())->size(); -} - -/*! - * When evaluating the items of an array it might be necessary to \e enter a - * particular schema, - * since this API assumes that there's always a valid schema in context (the one the user is - * interested on). This shall only happen if the item at the supplied array index is of type - * object, which is then assumed to be a schema. - * - * The function also marks the context as being inside an array evaluation. - * - * Returns whether it was necessary to enter a schema for the supplied - * array \a index, false if index is out of bounds. - */ -bool JsonSchema::maybeEnterNestedArraySchema(int index) -{ - QTC_ASSERT(itemArraySchemaSize(), return false); - QTC_ASSERT(index >= 0 && index < itemArraySchemaSize(), return false); - - JsonValue *v = getArrayValue(kItems(), currentValue())->elements().at(index); - - return maybeEnter(v, Array, index); -} - -/*! - * The type of a schema can be specified in the form of a union type, which is basically an - * array of allowed types for the particular instance [Sec. 5.1]. This function checks whether - * the current schema is one of such. - * - * Returns whether or not the current schema specifies a union type. - */ -bool JsonSchema::hasUnionSchema() const -{ - return getArrayValue(kType(), currentValue()); -} - -int JsonSchema::unionSchemaSize() const -{ - return getArrayValue(kType(), currentValue())->size(); -} - -/*! - * When evaluating union types it might be necessary to enter a particular - * schema, since this - * API assumes that there's always a valid schema in context (the one the user is interested on). - * This shall only happen if the item at the supplied union \a index, which is then assumed to be - * a schema. - * - * The function also marks the context as being inside an union evaluation. - * - * Returns whether or not it was necessary to enter a schema for the - * supplied union index. - */ -bool JsonSchema::maybeEnterNestedUnionSchema(int index) -{ - QTC_ASSERT(unionSchemaSize(), return false); - QTC_ASSERT(index >= 0 && index < unionSchemaSize(), return false); - - JsonValue *v = getArrayValue(kType(), currentValue())->elements().at(index); - - return maybeEnter(v, Union, index); -} - -void JsonSchema::leaveNestedSchema() -{ - QTC_ASSERT(!m_schemas.isEmpty(), return); - - leave(); -} - -bool JsonSchema::required() const -{ - if (JsonBooleanValue *bv = getBooleanValue(kRequired(), currentValue())) - return bv->value(); - - return false; -} - -bool JsonSchema::hasMinimum() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); - - return getDoubleValue(kMinimum(), currentValue()); -} - -double JsonSchema::minimum() const -{ - QTC_ASSERT(hasMinimum(), return 0); - - return getDoubleValue(kMinimum(), currentValue())->value(); -} - -bool JsonSchema::hasExclusiveMinimum() -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); - - if (JsonBooleanValue *bv = getBooleanValue(kExclusiveMinimum(), currentValue())) - return bv->value(); - - return false; -} - -bool JsonSchema::hasMaximum() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); - - return getDoubleValue(kMaximum(), currentValue()); -} - -double JsonSchema::maximum() const -{ - QTC_ASSERT(hasMaximum(), return 0); - - return getDoubleValue(kMaximum(), currentValue())->value(); -} - -bool JsonSchema::hasExclusiveMaximum() -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Int)), return false); - - if (JsonBooleanValue *bv = getBooleanValue(kExclusiveMaximum(), currentValue())) - return bv->value(); - - return false; -} - -QString JsonSchema::pattern() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return QString()); - - if (JsonStringValue *sv = getStringValue(kPattern(), currentValue())) - return sv->value(); - - return QString(); -} - -int JsonSchema::minimumLength() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return -1); - - if (JsonDoubleValue *dv = getDoubleValue(kMinLength(), currentValue())) - return dv->value(); - - return -1; -} - -int JsonSchema::maximumLength() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::String)), return -1); - - if (JsonDoubleValue *dv = getDoubleValue(kMaxLength(), currentValue())) - return dv->value(); - - return -1; -} - -bool JsonSchema::hasAdditionalItems() const -{ - QTC_ASSERT(acceptsType(JsonValue::kindToString(JsonValue::Array)), return false); - - return currentValue()->member(kAdditionalItems()); -} - -bool JsonSchema::maybeSchemaName(const QString &s) -{ - if (s.isEmpty() || s == QLatin1String("any")) - return false; - - return !isCheckableType(s); -} - -JsonObjectValue *JsonSchema::rootValue() const -{ - QTC_ASSERT(!m_schemas.isEmpty(), return nullptr); - - return m_schemas.first().m_value; -} - -JsonObjectValue *JsonSchema::currentValue() const -{ - QTC_ASSERT(!m_schemas.isEmpty(), return nullptr); - - return m_schemas.last().m_value; -} - -int JsonSchema::currentIndex() const -{ - QTC_ASSERT(!m_schemas.isEmpty(), return 0); - - return m_schemas.last().m_index; -} - -void JsonSchema::evaluate(EvaluationMode eval, int index) -{ - QTC_ASSERT(!m_schemas.isEmpty(), return); - - m_schemas.last().m_eval = eval; - m_schemas.last().m_index = index; -} - -void JsonSchema::enter(JsonObjectValue *ov, EvaluationMode eval, int index) -{ - Context context; - context.m_eval = eval; - context.m_index = index; - context.m_value = resolveReference(ov); - - m_schemas.push_back(context); -} - -bool JsonSchema::maybeEnter(JsonValue *v, EvaluationMode eval, int index) -{ - evaluate(eval, index); - - if (v->kind() == JsonValue::Object) { - enter(v->toObject()); - return true; - } - - if (v->kind() == JsonValue::String) { - const QString &s = v->toString()->value(); - if (maybeSchemaName(s)) { - JsonSchema *schema = m_manager->schemaByName(s); - if (schema) { - enter(schema->rootValue()); - return true; - } - } - } - - return false; -} - -void JsonSchema::leave() -{ - QTC_ASSERT(!m_schemas.isEmpty(), return); - - m_schemas.pop_back(); -} - -JsonObjectValue *JsonSchema::resolveReference(JsonObjectValue *ov) const -{ - if (JsonStringValue *sv = getStringValue(kRef(), ov)) { - JsonSchema *referenced = m_manager->schemaByName(sv->value()); - if (referenced) - return referenced->rootValue(); - } - - return ov; -} - -JsonObjectValue *JsonSchema::resolveBase(JsonObjectValue *ov) const -{ - if (JsonValue *v = ov->member(kExtends())) { - if (v->kind() == JsonValue::String) { - JsonSchema *schema = m_manager->schemaByName(v->toString()->value()); - if (schema) - return schema->rootValue(); - } else if (v->kind() == JsonValue::Object) { - return resolveReference(v->toObject()); - } - } - - return nullptr; -} - -JsonStringValue *JsonSchema::getStringValue(const QString &name, JsonObjectValue *value) -{ - JsonValue *v = value->member(name); - if (!v) - return nullptr; - - return v->toString(); -} - -JsonObjectValue *JsonSchema::getObjectValue(const QString &name, JsonObjectValue *value) -{ - JsonValue *v = value->member(name); - if (!v) - return nullptr; - - return v->toObject(); -} - -JsonBooleanValue *JsonSchema::getBooleanValue(const QString &name, JsonObjectValue *value) -{ - JsonValue *v = value->member(name); - if (!v) - return nullptr; - - return v->toBoolean(); -} - -JsonArrayValue *JsonSchema::getArrayValue(const QString &name, JsonObjectValue *value) -{ - JsonValue *v = value->member(name); - if (!v) - return nullptr; - - return v->toArray(); -} - -JsonDoubleValue *JsonSchema::getDoubleValue(const QString &name, JsonObjectValue *value) -{ - JsonValue *v = value->member(name); - if (!v) - return nullptr; - - return v->toDouble(); -} - - -/////////////////////////////////////////////////////////////////////////////// - -JsonSchemaManager::JsonSchemaManager(const QStringList &searchPaths) - : m_searchPaths(searchPaths) -{ - for (const QString &path : searchPaths) { - QDir dir(path); - if (!dir.exists()) - continue; - dir.setNameFilters(QStringList(QLatin1String("*.json"))); - const QList entries = dir.entryInfoList(); - for (const QFileInfo &fi : entries) - m_schemas.insert(fi.baseName(), JsonSchemaData(fi.absoluteFilePath())); - } -} - -JsonSchemaManager::~JsonSchemaManager() -{ - for (const JsonSchemaData &schemaData : std::as_const(m_schemas)) - delete schemaData.m_schema; -} - -/*! - * Tries to find a JSON schema to validate \a fileName against. According - * to the specification, how the schema/instance association is done is implementation defined. - * Currently we use a quite naive approach which is simply based on file names. Specifically, - * if one opens a foo.json file we'll look for a schema named foo.json. We should probably - * investigate alternative settings later. - * - * Returns a valid schema or 0. - */ -JsonSchema *JsonSchemaManager::schemaForFile(const QString &fileName) const -{ - QString baseName(QFileInfo(fileName).baseName()); - - return schemaByName(baseName); -} - -JsonSchema *JsonSchemaManager::schemaByName(const QString &baseName) const -{ - QHash::iterator it = m_schemas.find(baseName); - if (it == m_schemas.end()) { - for (const QString &path : m_searchPaths) { - QFileInfo candidate(path + baseName + ".json"); - if (candidate.exists()) { - m_schemas.insert(baseName, candidate.absoluteFilePath()); - break; - } - } - } - - it = m_schemas.find(baseName); - if (it == m_schemas.end()) - return nullptr; - - JsonSchemaData *schemaData = &it.value(); - if (!schemaData->m_schema) { - // Schemas are built on-demand. - QFileInfo currentSchema(schemaData->m_absoluteFileName); - Q_ASSERT(currentSchema.exists()); - if (schemaData->m_lastParseAttempt.isNull() - || schemaData->m_lastParseAttempt < currentSchema.lastModified()) { - schemaData->m_schema = parseSchema(currentSchema.absoluteFilePath()); - } - } - - return schemaData->m_schema; -} - -JsonSchema *JsonSchemaManager::parseSchema(const QString &schemaFileName) const -{ - FileReader reader; - if (reader.fetch(FilePath::fromString(schemaFileName), QIODevice::Text)) { - const QString &contents = QString::fromUtf8(reader.data()); - JsonValue *json = JsonValue::create(contents, &m_pool); - if (json && json->kind() == JsonValue::Object) - return new JsonSchema(json->toObject(), this); - } - - return nullptr; -} diff --git a/src/libs/utils/json.h b/src/libs/utils/json.h deleted file mode 100644 index 96a06772083..00000000000 --- a/src/libs/utils/json.h +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QVariant; -QT_END_NAMESPACE - -namespace Utils { - -class JsonStringValue; -class JsonDoubleValue; -class JsonIntValue; -class JsonObjectValue; -class JsonArrayValue; -class JsonBooleanValue; -class JsonNullValue; - -class QTCREATOR_UTILS_EXPORT JsonMemoryPool -{ -public: - ~JsonMemoryPool(); - - inline void *allocate(size_t size) - { - auto obj = new char[size]; - _objs.append(obj); - return obj; - } - -private: - QVector _objs; -}; - -/*! - * \brief The JsonValue class - */ -class QTCREATOR_UTILS_EXPORT JsonValue -{ -public: - enum Kind { - String, - Double, - Int, - Object, - Array, - Boolean, - Null, - Unknown - }; - - virtual ~JsonValue(); - - Kind kind() const { return m_kind; } - static QString kindToString(Kind kind); - - virtual JsonStringValue *toString() { return nullptr; } - virtual JsonDoubleValue *toDouble() { return nullptr; } - virtual JsonIntValue *toInt() { return nullptr; } - virtual JsonObjectValue *toObject() { return nullptr; } - virtual JsonArrayValue *toArray() { return nullptr; } - virtual JsonBooleanValue *toBoolean() { return nullptr; } - virtual JsonNullValue *toNull() { return nullptr; } - - static JsonValue *create(const QString &s, JsonMemoryPool *pool); - void *operator new(size_t size, JsonMemoryPool *pool); - void operator delete(void *); - void operator delete(void *, JsonMemoryPool *); - -protected: - JsonValue(Kind kind); - -private: - static JsonValue *build(const QVariant &varixant, JsonMemoryPool *pool); - - Kind m_kind; -}; - - -/*! - * \brief The JsonStringValue class - */ -class QTCREATOR_UTILS_EXPORT JsonStringValue : public JsonValue -{ -public: - JsonStringValue(const QString &value) - : JsonValue(String) - , m_value(value) - {} - - JsonStringValue *toString() override { return this; } - - const QString &value() const { return m_value; } - -private: - QString m_value; -}; - - -/*! - * \brief The JsonDoubleValue class - */ -class QTCREATOR_UTILS_EXPORT JsonDoubleValue : public JsonValue -{ -public: - JsonDoubleValue(double value) - : JsonValue(Double) - , m_value(value) - {} - - JsonDoubleValue *toDouble() override { return this; } - - double value() const { return m_value; } - -private: - double m_value; -}; - -/*! - * \brief The JsonIntValue class - */ -class QTCREATOR_UTILS_EXPORT JsonIntValue : public JsonValue -{ -public: - JsonIntValue(int value) - : JsonValue(Int) - , m_value(value) - {} - - JsonIntValue *toInt() override { return this; } - - int value() const { return m_value; } - -private: - int m_value; -}; - - -/*! - * \brief The JsonObjectValue class - */ -class QTCREATOR_UTILS_EXPORT JsonObjectValue : public JsonValue -{ -public: - JsonObjectValue() - : JsonValue(Object) - {} - - JsonObjectValue *toObject() override { return this; } - - void addMember(const QString &name, JsonValue *value) { m_members.insert(name, value); } - bool hasMember(const QString &name) const { return m_members.contains(name); } - JsonValue *member(const QString &name) const { return m_members.value(name); } - QHash members() const { return m_members; } - bool isEmpty() const { return m_members.isEmpty(); } - -protected: - JsonObjectValue(Kind kind) - : JsonValue(kind) - {} - -private: - QHash m_members; -}; - - -/*! - * \brief The JsonArrayValue class - */ -class QTCREATOR_UTILS_EXPORT JsonArrayValue : public JsonValue -{ -public: - JsonArrayValue() - : JsonValue(Array) - {} - - JsonArrayValue *toArray() override { return this; } - - void addElement(JsonValue *value) { m_elements.append(value); } - QList elements() const { return m_elements; } - int size() const { return m_elements.size(); } - -private: - QList m_elements; -}; - - -/*! - * \brief The JsonBooleanValue class - */ -class QTCREATOR_UTILS_EXPORT JsonBooleanValue : public JsonValue -{ -public: - JsonBooleanValue(bool value) - : JsonValue(Boolean) - , m_value(value) - {} - - JsonBooleanValue *toBoolean() override { return this; } - - bool value() const { return m_value; } - -private: - bool m_value; -}; - -class QTCREATOR_UTILS_EXPORT JsonNullValue : public JsonValue -{ -public: - JsonNullValue() - : JsonValue(Null) - {} - - JsonNullValue *toNull() override { return this; } -}; - -class JsonSchemaManager; - -/*! - * \brief The JsonSchema class - * - * [NOTE: This is an incomplete implementation and a work in progress.] - * - * This class provides an interface for traversing and evaluating a JSON schema, as described - * in the draft http://tools.ietf.org/html/draft-zyp-json-schema-03. - * - * JSON schemas are recursive in concept. This means that a particular attribute from a schema - * might be also another schema. Therefore, the basic working principle of this API is that - * from within some schema, one can investigate its attributes and if necessary "enter" a - * corresponding nested schema. Afterwards, it's expected that one would "leave" such nested - * schema. - * - * All functions assume that the current "context" is a valid schema. Once an instance of this - * class is created the root schema is put on top of the stack. - * - */ -class QTCREATOR_UTILS_EXPORT JsonSchema -{ -public: - bool isTypeConstrained() const; - bool acceptsType(const QString &type) const; - QStringList validTypes() const; - - // Applicable on schemas of any type. - bool required() const; - - bool hasTypeSchema() const; - void enterNestedTypeSchema(); - - bool hasUnionSchema() const; - int unionSchemaSize() const; - bool maybeEnterNestedUnionSchema(int index); - - void leaveNestedSchema(); - - // Applicable on schemas of type number/integer. - bool hasMinimum() const; - bool hasMaximum() const; - bool hasExclusiveMinimum(); - bool hasExclusiveMaximum(); - double minimum() const; - double maximum() const; - - // Applicable on schemas of type string. - QString pattern() const; - int minimumLength() const; - int maximumLength() const; - - // Applicable on schemas of type object. - QStringList properties() const; - bool hasPropertySchema(const QString &property) const; - void enterNestedPropertySchema(const QString &property); - - // Applicable on schemas of type array. - bool hasAdditionalItems() const; - - bool hasItemSchema() const; - void enterNestedItemSchema(); - - bool hasItemArraySchema() const; - int itemArraySchemaSize() const; - bool maybeEnterNestedArraySchema(int index); - -private: - friend class JsonSchemaManager; - JsonSchema(JsonObjectValue *rootObject, const JsonSchemaManager *manager); - Q_DISABLE_COPY(JsonSchema) - - enum EvaluationMode { - Normal, - Array, - Union - }; - - void enter(JsonObjectValue *ov, EvaluationMode eval = Normal, int index = -1); - bool maybeEnter(JsonValue *v, EvaluationMode eval, int index); - void evaluate(EvaluationMode eval, int index); - void leave(); - - JsonObjectValue *resolveReference(JsonObjectValue *ov) const; - JsonObjectValue *resolveBase(JsonObjectValue *ov) const; - - JsonObjectValue *currentValue() const; - int currentIndex() const; - - JsonObjectValue *rootValue() const; - - static JsonStringValue *getStringValue(const QString &name, JsonObjectValue *value); - static JsonObjectValue *getObjectValue(const QString &name, JsonObjectValue *value); - static JsonBooleanValue *getBooleanValue(const QString &name, JsonObjectValue *value); - static JsonArrayValue *getArrayValue(const QString &name, JsonObjectValue *value); - static JsonDoubleValue *getDoubleValue(const QString &name, JsonObjectValue *value); - - static QStringList validTypes(JsonObjectValue *v); - static bool typeMatches(const QString &expected, const QString &actual); - static bool isCheckableType(const QString &s); - - QStringList properties(JsonObjectValue *v) const; - JsonObjectValue *propertySchema(const QString &property, JsonObjectValue *v) const; - // TODO: Similar functions for other attributes which require looking into base schemas. - - static bool maybeSchemaName(const QString &s); - - static QString kType(); - static QString kProperties(); - static QString kPatternProperties(); - static QString kAdditionalProperties(); - static QString kItems(); - static QString kAdditionalItems(); - static QString kRequired(); - static QString kDependencies(); - static QString kMinimum(); - static QString kMaximum(); - static QString kExclusiveMinimum(); - static QString kExclusiveMaximum(); - static QString kMinItems(); - static QString kMaxItems(); - static QString kUniqueItems(); - static QString kPattern(); - static QString kMinLength(); - static QString kMaxLength(); - static QString kTitle(); - static QString kDescription(); - static QString kExtends(); - static QString kRef(); - - struct Context - { - JsonObjectValue *m_value; - EvaluationMode m_eval; - int m_index; - }; - - QVector m_schemas; - const JsonSchemaManager *m_manager; -}; - - -/*! - * \brief The JsonSchemaManager class - */ -class QTCREATOR_UTILS_EXPORT JsonSchemaManager -{ -public: - JsonSchemaManager(const QStringList &searchPaths); - ~JsonSchemaManager(); - - JsonSchema *schemaForFile(const QString &fileName) const; - JsonSchema *schemaByName(const QString &baseName) const; - -private: - struct JsonSchemaData - { - JsonSchemaData(const QString &absoluteFileName, JsonSchema *schema = nullptr) - : m_absoluteFileName(absoluteFileName) - , m_schema(schema) - {} - QString m_absoluteFileName; - JsonSchema *m_schema; - QDateTime m_lastParseAttempt; - }; - - JsonSchema *parseSchema(const QString &schemaFileName) const; - - QStringList m_searchPaths; - mutable QHash m_schemas; - mutable JsonMemoryPool m_pool; -}; - -} // namespace Utils diff --git a/src/libs/utils/launcherinterface.cpp b/src/libs/utils/launcherinterface.cpp index 218286521db..d188e224fc1 100644 --- a/src/libs/utils/launcherinterface.cpp +++ b/src/libs/utils/launcherinterface.cpp @@ -82,9 +82,13 @@ void LauncherInterfacePrivate::doStart() connect(m_process, &QProcess::readyReadStandardError, this, &LauncherInterfacePrivate::handleProcessStderr); #ifdef Q_OS_UNIX +# if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + m_process->setUnixProcessParameters(QProcess::UnixProcessFlag::CreateNewSession); +# else m_process->setChildProcessModifier([] { setpgid(0, 0); }); +# endif #endif m_process->start(launcherFilePath(), QStringList(m_server->fullServerName())); diff --git a/src/libs/utils/launchersocket.h b/src/libs/utils/launchersocket.h index be1b7f7f9ba..54c212c479b 100644 --- a/src/libs/utils/launchersocket.h +++ b/src/libs/utils/launchersocket.h @@ -128,7 +128,6 @@ private: class LauncherHandle : public QObject { - Q_OBJECT public: // Called from caller's thread, moved to launcher's thread afterwards. LauncherHandle(quintptr token) : m_token(token) {} diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 4d4f01ddbe4..31c67e4d0f6 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -3,20 +3,22 @@ #include "layoutbuilder.h" +#include #include #include #include #include #include #include -#include #include #include #include +#include +#include #include #include #include -#include +#include namespace Layouting { @@ -220,8 +222,8 @@ LayoutItem::~LayoutItem() = default; struct ResultItem { ResultItem() = default; - explicit ResultItem(QLayout *l) : layout(l) {} - explicit ResultItem(QWidget *w) : widget(w) {} + explicit ResultItem(QLayout *l) : layout(l), empty(!l) {} + explicit ResultItem(QWidget *w) : widget(w), empty(!w) {} QString text; QLayout *layout = nullptr; @@ -236,7 +238,7 @@ struct Slice { Slice() = default; Slice(QLayout *l) : layout(l) {} - Slice(QWidget *w) : widget(w) {} + Slice(QWidget *w, bool isLayouting=false) : widget(w), isLayouting(isLayouting) {} QLayout *layout = nullptr; QWidget *widget = nullptr; @@ -247,6 +249,7 @@ struct Slice int currentGridColumn = 0; int currentGridRow = 0; bool isFormAlignment = false; + bool isLayouting = false; Qt::Alignment align = {}; // Can be changed to // Grid or Form @@ -395,14 +398,6 @@ void Slice::flush() for (const ResultItem &item : std::as_const(pendingItems)) addItemToFlowLayout(flowLayout, item); - } else if (auto stackLayout = qobject_cast(layout)) { - for (const ResultItem &item : std::as_const(pendingItems)) { - if (item.widget) - stackLayout->addWidget(item.widget); - else - QTC_CHECK(false); - } - } else { QTC_CHECK(false); } @@ -572,9 +567,26 @@ void LayoutItem::attachTo(QWidget *w) const QWidget *LayoutItem::emerge() { - auto w = new QWidget; - attachTo(w); - return w; + LayoutBuilder builder; + + builder.stack.append(Slice()); + addItemHelper(builder, *this); + + if (builder.stack.empty()) + return nullptr; + + QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr); + ResultItem ri = builder.stack.last().pendingItems.takeFirst(); + + QTC_ASSERT(ri.layout || ri.widget, return nullptr); + + if (ri.layout) { + auto w = new QWidget; + w->setLayout(ri.layout); + return w; + } + + return ri.widget; } static void layoutExit(LayoutBuilder &builder) @@ -583,10 +595,30 @@ static void layoutExit(LayoutBuilder &builder) QLayout *layout = builder.stack.last().layout; builder.stack.pop_back(); - if (QWidget *widget = builder.stack.last().widget) - widget->setLayout(layout); - else + if (builder.stack.last().isLayouting) { builder.stack.last().pendingItems.append(ResultItem(layout)); + } else if (QWidget *widget = builder.stack.last().widget) { + widget->setLayout(layout); + } else + builder.stack.last().pendingItems.append(ResultItem(layout)); +} + +template +static void layoutingWidgetExit(LayoutBuilder &builder) +{ + const Slice slice = builder.stack.last(); + T *w = qobject_cast(slice.widget); + for (const ResultItem &ri : slice.pendingItems) { + if (ri.widget) { + w->addWidget(ri.widget); + } else if (ri.layout) { + auto child = new QWidget; + child->setLayout(ri.layout); + w->addWidget(child); + } + } + builder.stack.pop_back(); + builder.stack.last().pendingItems.append(ResultItem(w)); } static void widgetExit(LayoutBuilder &builder) @@ -638,13 +670,6 @@ Form::Form(std::initializer_list items) onExit = layoutExit; } -Stack::Stack(std::initializer_list items) -{ - subItems = items; - onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QStackedLayout); }; - onExit = layoutExit; -} - LayoutItem br() { LayoutItem item; @@ -736,6 +761,18 @@ Group::Group(std::initializer_list items) setupWidget(this); } +Stack::Stack(std::initializer_list items) +{ + // We use a QStackedWidget instead of a QStackedLayout here because the latter will call + // "setVisible()" when a child is added, which can lead to the widget being spawned as a + // top-level widget. This can lead to the focus shifting away from the main application. + subItems = items; + onAdd = [](LayoutBuilder &builder) { + builder.stack.append(Slice(new QStackedWidget, true)); + }; + onExit = layoutingWidgetExit; +} + PushButton::PushButton(std::initializer_list items) { this->subItems = items; @@ -760,18 +797,20 @@ Splitter::Splitter(std::initializer_list items) onAdd = [](LayoutBuilder &builder) { auto splitter = new QSplitter; splitter->setOrientation(Qt::Vertical); - builder.stack.append(splitter); + builder.stack.append(Slice(splitter, true)); }; - onExit = [](LayoutBuilder &builder) { - const Slice slice = builder.stack.last(); - QSplitter *splitter = qobject_cast(slice.widget); - for (const ResultItem &ri : slice.pendingItems) { - if (ri.widget) - splitter->addWidget(ri.widget); - } - builder.stack.pop_back(); - builder.stack.last().pendingItems.append(ResultItem(splitter)); + onExit = layoutingWidgetExit; +} + +ToolBar::ToolBar(std::initializer_list items) +{ + subItems = items; + onAdd = [](LayoutBuilder &builder) { + auto toolbar = new QToolBar; + toolbar->setOrientation(Qt::Horizontal); + builder.stack.append(Slice(toolbar, true)); }; + onExit = layoutingWidgetExit; } TabWidget::TabWidget(std::initializer_list items) @@ -798,6 +837,13 @@ Tab::Tab(const QString &tabName, const LayoutItem &item) }; } +// Special If + +If::If(bool condition, const LayoutItems &items, const LayoutItems &other) +{ + subItems.append(condition ? items : other); +} + // Special Application Application::Application(std::initializer_list items) @@ -833,6 +879,17 @@ LayoutItem title(const QString &title) }; } +LayoutItem windowTitle(const QString &windowTitle) +{ + return [windowTitle](QObject *target) { + if (auto widget = qobject_cast(target)) { + widget->setWindowTitle(windowTitle); + } else { + QTC_CHECK(false); + } + }; +} + LayoutItem text(const QString &text) { return [text](QObject *target) { @@ -890,6 +947,18 @@ LayoutItem columnStretch(int column, int stretch) }; } +LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy) +{ + return [policy](QObject *target) { + if (auto form = qobject_cast(target)) { + form->setFieldGrowthPolicy(policy); + } else { + QTC_CHECK(false); + } + }; +} + + // Id based setters LayoutItem id(ID &out) diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 4a756139897..c9fac7d8383 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include #include @@ -140,6 +141,12 @@ public: Tab(const QString &tabName, const LayoutItem &item); }; +class QTCREATOR_UTILS_EXPORT If : public LayoutItem +{ +public: + If(bool condition, const LayoutItems &item, const LayoutItems &other = {}); +}; + class QTCREATOR_UTILS_EXPORT Group : public LayoutItem { public: @@ -170,6 +177,12 @@ public: Splitter(std::initializer_list items); }; +class QTCREATOR_UTILS_EXPORT ToolBar : public LayoutItem +{ +public: + ToolBar(std::initializer_list items); +}; + class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem { public: @@ -215,6 +228,8 @@ QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int); QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch); QTCREATOR_UTILS_EXPORT LayoutItem spacing(int); QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle); +QTCREATOR_UTILS_EXPORT LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy); + // "Getters" diff --git a/src/libs/utils/link.h b/src/libs/utils/link.h index 00194654c97..57fb4c896fd 100644 --- a/src/libs/utils/link.h +++ b/src/libs/utils/link.h @@ -38,14 +38,19 @@ public: bool operator==(const Link &other) const { - return targetFilePath == other.targetFilePath - && targetLine == other.targetLine - && targetColumn == other.targetColumn - && linkTextStart == other.linkTextStart - && linkTextEnd == other.linkTextEnd; + return hasSameLocation(other) + && linkTextStart == other.linkTextStart + && linkTextEnd == other.linkTextEnd; } bool operator!=(const Link &other) const { return !(*this == other); } + bool hasSameLocation(const Link &other) const + { + return targetFilePath == other.targetFilePath + && targetLine == other.targetLine + && targetColumn == other.targetColumn; + } + friend size_t qHash(const Link &l, uint seed = 0) { return qHashMulti(seed, l.targetFilePath, l.targetLine, l.targetColumn); } diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 3ab7d92e9a6..f6d3dad3623 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -88,10 +88,10 @@ public: QHash m_map; QHash m_prefixMap; - QVector m_extraResolvers; + QList m_extraResolvers; QMap m_descriptions; QString m_displayName; - QVector m_subProviders; + QList m_subProviders; bool m_accumulating = false; bool m_aborted = false; diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h index 12172a5e17e..adde4db1d3f 100644 --- a/src/libs/utils/macroexpander.h +++ b/src/libs/utils/macroexpander.h @@ -7,7 +7,6 @@ #include #include -#include #include @@ -18,7 +17,7 @@ namespace Internal { class MacroExpanderPrivate; } class FilePath; class MacroExpander; using MacroExpanderProvider = std::function; -using MacroExpanderProviders = QVector; +using MacroExpanderProviders = QList; class QTCREATOR_UTILS_EXPORT MacroExpander { diff --git a/src/libs/utils/mapreduce.h b/src/libs/utils/mapreduce.h deleted file mode 100644 index 891f0083c52..00000000000 --- a/src/libs/utils/mapreduce.h +++ /dev/null @@ -1,594 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include "algorithm.h" -#include "runextensions.h" - -#include - -#include - -namespace Utils { - -enum class MapReduceOption -{ - Ordered, - Unordered -}; - -namespace Internal { - -class QTCREATOR_UTILS_EXPORT MapReduceObject : public QObject -{ - Q_OBJECT -}; - -template -class MapReduceBase : public MapReduceObject -{ -protected: - static const int MAX_PROGRESS = 1000000; - // either const or non-const reference wrapper for items from the iterator - using ItemReferenceWrapper = std::reference_wrapper< - std::remove_reference_t::reference>>; - -public: - MapReduceBase(QFutureInterface &futureInterface, ForwardIterator begin, ForwardIterator end, - MapFunction &&map, State &state, ReduceFunction &&reduce, - MapReduceOption option, QThreadPool *pool, int size) - : m_futureInterface(futureInterface), - m_iterator(begin), - m_end(end), - m_map(std::forward(map)), - m_state(state), - m_reduce(std::forward(reduce)), - m_threadPool(pool), - m_handleProgress(size >= 0), - m_size(size), - m_option(option) - { - if (!m_threadPool) - m_threadPool = new QThreadPool(this); - if (m_handleProgress) // progress is handled by us - m_futureInterface.setProgressRange(0, MAX_PROGRESS); - connect(&m_selfWatcher, &QFutureWatcher::canceled, - this, &MapReduceBase::cancelAll); - m_selfWatcher.setFuture(QFuture(futureInterface.future())); - } - - void exec() - { - // do not enter event loop for empty containers or if already canceled - if (!m_futureInterface.isCanceled() && schedule()) - m_loop.exec(); - } - -protected: - virtual void reduce(QFutureWatcher *watcher, int index) = 0; - - bool schedule() - { - bool didSchedule = false; - while (m_iterator != m_end - && m_mapWatcher.size() < std::max(m_threadPool->maxThreadCount(), 1)) { - didSchedule = true; - auto watcher = new QFutureWatcher(); - connect(watcher, &QFutureWatcher::finished, this, [this, watcher] { - mapFinished(watcher); - }); - if (m_handleProgress) { - connect(watcher, &QFutureWatcher::progressValueChanged, - this, &MapReduceBase::updateProgress); - connect(watcher, &QFutureWatcher::progressRangeChanged, - this, &MapReduceBase::updateProgress); - } - m_mapWatcher.append(watcher); - m_watcherIndex.append(m_currentIndex); - ++m_currentIndex; - watcher->setFuture(runAsync(m_threadPool, std::cref(m_map), - ItemReferenceWrapper(*m_iterator))); - ++m_iterator; - } - return didSchedule; - } - - void mapFinished(QFutureWatcher *watcher) - { - int index = m_mapWatcher.indexOf(watcher); - int watcherIndex = m_watcherIndex.at(index); - m_mapWatcher.removeAt(index); // remove so we can schedule next one - m_watcherIndex.removeAt(index); - bool didSchedule = false; - if (!m_futureInterface.isCanceled()) { - // first schedule the next map... - didSchedule = schedule(); - ++m_successfullyFinishedMapCount; - updateProgress(); - // ...then reduce - reduce(watcher, watcherIndex); - } - delete watcher; - if (!didSchedule && m_mapWatcher.isEmpty()) - m_loop.quit(); - } - - void updateProgress() - { - if (!m_handleProgress) // cannot compute progress - return; - if (m_size == 0 || m_successfullyFinishedMapCount == m_size) { - m_futureInterface.setProgressValue(MAX_PROGRESS); - return; - } - if (!m_futureInterface.isProgressUpdateNeeded()) - return; - const double progressPerMap = MAX_PROGRESS / double(m_size); - double progress = m_successfullyFinishedMapCount * progressPerMap; - for (const QFutureWatcher *watcher : std::as_const(m_mapWatcher)) { - if (watcher->progressMinimum() != watcher->progressMaximum()) { - const double range = watcher->progressMaximum() - watcher->progressMinimum(); - progress += (watcher->progressValue() - watcher->progressMinimum()) / range * progressPerMap; - } - } - m_futureInterface.setProgressValue(int(progress)); - } - - void cancelAll() - { - for (QFutureWatcher *watcher : std::as_const(m_mapWatcher)) - watcher->cancel(); - } - - QFutureWatcher m_selfWatcher; - QFutureInterface &m_futureInterface; - ForwardIterator m_iterator; - const ForwardIterator m_end; - MapFunction m_map; - State &m_state; - ReduceFunction m_reduce; - QEventLoop m_loop; - QThreadPool *m_threadPool; // for reusing threads - QList *> m_mapWatcher; - QList m_watcherIndex; - int m_currentIndex = 0; - const bool m_handleProgress; - const int m_size; - int m_successfullyFinishedMapCount = 0; - MapReduceOption m_option; -}; - -// non-void result of map function. -template -class MapReduce : public MapReduceBase -{ - using BaseType = MapReduceBase; -public: - MapReduce(QFutureInterface &futureInterface, ForwardIterator begin, ForwardIterator end, - MapFunction &&map, State &state, ReduceFunction &&reduce, MapReduceOption option, - QThreadPool *pool, int size) - : BaseType(futureInterface, begin, end, std::forward(map), state, - std::forward(reduce), option, pool, size) - { - } - -protected: - void reduce(QFutureWatcher *watcher, int index) override - { - if (BaseType::m_option == MapReduceOption::Unordered) { - reduceOne(watcher->future().results()); - } else { - if (m_nextIndex == index) { - // handle this result and all directly following - reduceOne(watcher->future().results()); - ++m_nextIndex; - while (!m_pendingResults.isEmpty() && m_pendingResults.firstKey() == m_nextIndex) { - reduceOne(m_pendingResults.take(m_nextIndex)); - ++m_nextIndex; - } - } else { - // add result to pending results - m_pendingResults.insert(index, watcher->future().results()); - } - } - } - -private: - void reduceOne(const QList &results) - { - const int resultCount = results.size(); - for (int i = 0; i < resultCount; ++i) { - Internal::runAsyncImpl(BaseType::m_futureInterface, BaseType::m_reduce, - BaseType::m_state, results.at(i)); - } - } - - QMap> m_pendingResults; - int m_nextIndex = 0; -}; - -// specialization for void result of map function. Reducing is a no-op. -template -class MapReduce : public MapReduceBase -{ - using BaseType = MapReduceBase; -public: - MapReduce(QFutureInterface &futureInterface, ForwardIterator begin, ForwardIterator end, - MapFunction &&map, State &state, ReduceFunction &&reduce, MapReduceOption option, - QThreadPool *pool, int size) - : BaseType(futureInterface, begin, end, std::forward(map), state, - std::forward(reduce), option, pool, size) - { - } - -protected: - void reduce(QFutureWatcher *, int) override - { - } - -}; - -template -functionResult_t -callWithMaybeFutureInterfaceDispatch(std::false_type, QFutureInterface &, - Function &&function, Args&&... args) -{ - return function(std::forward(args)...); -} - -template -functionResult_t -callWithMaybeFutureInterfaceDispatch(std::true_type, QFutureInterface &futureInterface, - Function &&function, Args&&... args) -{ - return function(futureInterface, std::forward(args)...); -} - -template -functionResult_t -callWithMaybeFutureInterface(QFutureInterface &futureInterface, - Function &&function, Args&&... args) -{ - return callWithMaybeFutureInterfaceDispatch( - functionTakesArgument&>(), - futureInterface, std::forward(function), std::forward(args)...); -} - -template -void blockingIteratorMapReduce(QFutureInterface &futureInterface, ForwardIterator begin, ForwardIterator end, - InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option, QThreadPool *pool, int size) -{ - auto state = callWithMaybeFutureInterface - (futureInterface, std::forward(init)); - MapReduce::type, MapFunction, decltype(state), ReduceResult, ReduceFunction> - mr(futureInterface, begin, end, std::forward(map), state, - std::forward(reduce), option, pool, size); - mr.exec(); - callWithMaybeFutureInterface&> - (futureInterface, std::forward(cleanup), state); -} - -template -void blockingContainerMapReduce(QFutureInterface &futureInterface, Container &&container, - InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option, QThreadPool *pool) -{ - blockingIteratorMapReduce(futureInterface, std::begin(container), std::end(container), - std::forward(init), std::forward(map), - std::forward(reduce), - std::forward(cleanup), - option, pool, static_cast(container.size())); -} - -template -void blockingContainerRefMapReduce(QFutureInterface &futureInterface, - std::reference_wrapper containerWrapper, - InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option, QThreadPool *pool) -{ - blockingContainerMapReduce(futureInterface, containerWrapper.get(), - std::forward(init), std::forward(map), - std::forward(reduce), - std::forward(cleanup), - option, pool); -} - -template -static void *dummyInit() { return nullptr; } - -// copies or moves state to member, and then moves it to the result of the call operator -template -struct StateWrapper { - using StateResult = std::decay_t; // State is const& or & for lvalues - StateWrapper(State &&state) : m_state(std::forward(state)) { } - StateResult operator()() - { - return std::move(m_state); // invalidates m_state - } - - StateResult m_state; -}; - -// copies or moves reduce function to member, calls the reduce function with state and mapped value -template -struct ReduceWrapper { - using Reduce = std::decay_t; - ReduceWrapper(ReduceFunction &&reduce) : m_reduce(std::forward(reduce)) { } - void operator()(QFutureInterface &, StateResult &state, const MapResult &mapResult) - { - m_reduce(state, mapResult); - } - - Reduce m_reduce; -}; - -template -struct DummyReduce { - MapResult operator()(void *, const MapResult &result) const { return result; } -}; -template <> -struct DummyReduce { - void operator()() const { } // needed for resultType with MSVC2013 -}; - -template -static void dummyCleanup(void *) { } - -template -static void cleanupReportingState(QFutureInterface &fi, StateResult &state) -{ - fi.reportResult(state); -} - -} // Internal - -template ::type> -QFuture -mapReduce(ForwardIterator begin, ForwardIterator end, InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, - int size = -1) -{ - return runAsync(priority, - Internal::blockingIteratorMapReduce< - ForwardIterator, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t>, - begin, end, std::forward(init), std::forward(map), - std::forward(reduce), std::forward(cleanup), - option, pool, size); -} - -/*! - Calls the map function on all items in \a container in parallel through Utils::runAsync. - - The reduce function is called in the mapReduce thread with each of the reported results from - the map function, in arbitrary order, but never in parallel. - It gets passed a reference to a user defined state object, and a result from the map function. - If it takes a QFutureInterface reference as its first argument, it can report results - for the mapReduce operation through that. Otherwise, any values returned by the reduce function - are reported as results of the mapReduce operation. - - The init function is called in the mapReduce thread before the actual mapping starts, - and must return the initial state object for the reduce function. It gets the QFutureInterface - of the mapReduce operation passed as an argument. - - The cleanup function is called in the mapReduce thread after all map and reduce calls have - finished, with the QFutureInterface of the mapReduce operation and the final state object - as arguments, and can be used to clean up any resources, or report a final result of the - mapReduce. - - Container - - StateType InitFunction(QFutureInterface&) - or - StateType InitFunction() - - void MapFunction(QFutureInterface&, const ItemType&) - or - MapResultType MapFunction(const ItempType&) - - void ReduceFunction(QFutureInterface&, StateType&, const MapResultType&) - or - ReduceResultType ReduceFunction(StateType&, const MapResultType&) - - void CleanUpFunction(QFutureInterface&, StateType&) - or - void CleanUpFunction(StateType&) - - Notes: - \list - \li Container can be a move-only type or a temporary. If it is a lvalue reference, it will - be copied to the mapReduce thread. You can avoid that by using - the version that takes iterators, or by using std::ref/cref to pass a reference_wrapper. - \li ItemType can be a move-only type, if the map function takes (const) references to ItemType. - \li StateType can be a move-only type. - \li The init, map, reduce and cleanup functions can be move-only types and are moved to the - mapReduce thread if they are rvalues. - \endlist - - */ -template ::type> -QFuture -mapReduce(Container &&container, InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return runAsync(priority, - Internal::blockingContainerMapReduce< - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t>, - std::forward(container), - std::forward(init), std::forward(map), - std::forward(reduce), std::forward(cleanup), - option, pool); -} - -template ::type> -QFuture -mapReduce(std::reference_wrapper containerWrapper, InitFunction &&init, MapFunction &&map, - ReduceFunction &&reduce, CleanUpFunction &&cleanup, - MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return runAsync(priority, - Internal::blockingContainerRefMapReduce< - Container, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t, - std::decay_t>, - containerWrapper, - std::forward(init), std::forward(map), - std::forward(reduce), std::forward(cleanup), - option, pool); -} - -template , // State = T& or const T& for lvalues, so decay that away - typename MapResult = typename Internal::resultType::type> -QFuture -mapReduce(ForwardIterator begin, ForwardIterator end, MapFunction &&map, State &&initialState, - ReduceFunction &&reduce, MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, - int size = -1) -{ - return mapReduce(begin, end, - Internal::StateWrapper(std::forward(initialState)), - std::forward(map), - Internal::ReduceWrapper(std::forward(reduce)), - &Internal::cleanupReportingState, - option, pool, priority, size); -} - -template , // State = T& or const T& for lvalues, so decay that away - typename MapResult = typename Internal::resultType::type> -QFuture -mapReduce(Container &&container, MapFunction &&map, State &&initialState, ReduceFunction &&reduce, - MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return mapReduce(std::forward(container), - Internal::StateWrapper(std::forward(initialState)), - std::forward(map), - Internal::ReduceWrapper(std::forward(reduce)), - &Internal::cleanupReportingState, - option, pool, priority); -} - -template , // State = T& or const T& for lvalues, so decay that away - typename MapResult = typename Internal::resultType::type> -Q_REQUIRED_RESULT -StateResult -mappedReduced(ForwardIterator begin, ForwardIterator end, MapFunction &&map, State &&initialState, - ReduceFunction &&reduce, MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, - int size = -1) -{ - return mapReduce(begin, end, - std::forward(map), std::forward(initialState), - std::forward(reduce), - option, pool, priority, size).result(); -} - -template , // State = T& or const T& for lvalues, so decay that away - typename MapResult = typename Internal::resultType::type> -Q_REQUIRED_RESULT -StateResult -mappedReduced(Container &&container, MapFunction &&map, State &&initialState, ReduceFunction &&reduce, - MapReduceOption option = MapReduceOption::Unordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return mapReduce(std::forward(container), std::forward(map), - std::forward(initialState), std::forward(reduce), - option, pool, priority).result(); -} - -template ::type> -QFuture -map(ForwardIterator begin, ForwardIterator end, MapFunction &&map, - MapReduceOption option = MapReduceOption::Ordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, - int size = -1) -{ - return mapReduce(begin, end, - &Internal::dummyInit, - std::forward(map), - Internal::DummyReduce(), - &Internal::dummyCleanup, - option, pool, priority, size); -} - -template ::type> -QFuture -map(Container &&container, MapFunction &&map, MapReduceOption option = MapReduceOption::Ordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return mapReduce(std::forward(container), - Internal::dummyInit, - std::forward(map), - Internal::DummyReduce(), - Internal::dummyCleanup, - option, pool, priority); -} - -template class ResultContainer, typename ForwardIterator, typename MapFunction, - typename MapResult = typename Internal::resultType::type> -Q_REQUIRED_RESULT -ResultContainer -mapped(ForwardIterator begin, ForwardIterator end, MapFunction &&mapFun, - MapReduceOption option = MapReduceOption::Ordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority, int size = -1) -{ - return Utils::transform(map(begin, end, - std::forward(mapFun), - option, pool, priority, size).results(), - [](const MapResult &r) { return r; }); -} - -template class ResultContainer, typename Container, typename MapFunction, - typename MapResult = typename Internal::resultType::type> -Q_REQUIRED_RESULT -ResultContainer -mapped(Container &&container, MapFunction &&mapFun, - MapReduceOption option = MapReduceOption::Ordered, - QThreadPool *pool = nullptr, QThread::Priority priority = QThread::InheritPriority) -{ - return Utils::transform(map(container, - std::forward(mapFun), - option, pool, priority).results(), - [](const MapResult &r) { return r; }); -} - -} // Utils diff --git a/src/libs/utils/mimetypes2/mimedatabase.cpp b/src/libs/utils/mimetypes2/mimedatabase.cpp index b703421d96f..60f3fbf0db5 100644 --- a/src/libs/utils/mimetypes2/mimedatabase.cpp +++ b/src/libs/utils/mimetypes2/mimedatabase.cpp @@ -13,6 +13,8 @@ #include "algorithm.h" +#include + #include #include #include @@ -90,6 +92,7 @@ static void updateOverriddenMimeTypes(std::vectorcheckInitPhase(nameOrAlias); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(nameOrAlias)); - return d->mimeTypeForName(nameOrAlias); } @@ -598,12 +598,9 @@ MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const */ MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const { + d->checkInitPhase(fileInfo.filePath()); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileInfo.filePath())); - if (fileInfo.isDir()) return d->mimeTypeForName(QLatin1String("inode/directory")); @@ -656,12 +653,9 @@ MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const { if (mode == MatchExtension) { + d->checkInitPhase(fileName); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileName)); - const QStringList matches = d->mimeTypeForFileName(fileName); const int matchCount = matches.count(); if (matchCount == 0) { @@ -693,12 +687,9 @@ MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) */ QList MimeDatabase::mimeTypesForFileName(const QString &fileName) const { + d->checkInitPhase(fileName); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileName)); - const QStringList matches = d->mimeTypeForFileName(fileName); QList mimes; mimes.reserve(matches.count()); @@ -728,11 +719,9 @@ QString MimeDatabase::suffixForFileName(const QString &fileName) const */ MimeType MimeDatabase::mimeTypeForData(const QByteArray &data) const { + d->checkInitPhase("data"); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for data before plugins are initialized"); - int accuracy = 0; return d->findByData(data, &accuracy); } @@ -860,11 +849,9 @@ MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const */ QList MimeDatabase::allMimeTypes() const { + d->checkInitPhase("all mime types"); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for all mime types before plugins are initialized"); - return d->allMimeTypes(); } @@ -921,4 +908,27 @@ void MimeDatabasePrivate::setGlobPatternsForMimeType(const MimeType &mimeType, } } +void MimeDatabasePrivate::checkInitPhase(const QString &info) +{ + QReadLocker locker(&m_initMutex); + if (m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) { + qWarning("Accessing MimeDatabase for %s before plugins are initialized", qPrintable(info)); + return; + } + // run initialization functions and ensure providers are loaded + // the initializers will call other MIME database functions which "checkInitPhase" again, + // so make sure not to recurse + if (!m_initialized.exchange(true)) { + for (const std::function &f : m_initializers) + f(); + QMutexLocker locker(&mutex); + providers(); + } +} + +void MimeDatabasePrivate::addInitializer(const std::function &init) +{ + m_initializers.append(init); +} + } // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimedatabase_p.h b/src/libs/utils/mimetypes2/mimedatabase_p.h index 8709b2918eb..c816a5c0e5b 100644 --- a/src/libs/utils/mimetypes2/mimedatabase_p.h +++ b/src/libs/utils/mimetypes2/mimedatabase_p.h @@ -25,8 +25,11 @@ #include #include -#include +#include + +#include #include +#include QT_BEGIN_NAMESPACE class QIODevice; @@ -75,6 +78,8 @@ public: void setMagicRulesForMimeType(const MimeType &mimeType, const QMap> &rules); void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns); + void checkInitPhase(const QString &info); + void addInitializer(const std::function &init); private: using Providers = std::vector>; @@ -86,6 +91,7 @@ private: QElapsedTimer m_lastCheck; // added for Qt Creator + QList> m_initializers; QHash m_additionalData; // id -> data bool m_forceLoad = true; @@ -94,6 +100,8 @@ public: QMutex mutex; // added for Qt Creator + QReadWriteLock m_initMutex; + std::atomic_bool m_initialized = false; int m_startupPhase = 0; }; diff --git a/src/libs/utils/mimetypes2/mimemagicrule_p.h b/src/libs/utils/mimetypes2/mimemagicrule_p.h index fe2e0ff3e42..293f61ca914 100644 --- a/src/libs/utils/mimetypes2/mimemagicrule_p.h +++ b/src/libs/utils/mimetypes2/mimemagicrule_p.h @@ -14,7 +14,7 @@ // We mean it. // -#include +#include "../utils_global.h" #include #include diff --git a/src/libs/utils/mimetypes2/mimeprovider.cpp b/src/libs/utils/mimetypes2/mimeprovider.cpp index c20e367517f..682cbb7e8a5 100644 --- a/src/libs/utils/mimetypes2/mimeprovider.cpp +++ b/src/libs/utils/mimetypes2/mimeprovider.cpp @@ -505,7 +505,7 @@ bool MimeBinaryProvider::loadMimeTypePrivate(MimeTypePrivate &data) // shared-mime-info since 1.3 lowercases the xml files QString mimeFile = m_directory + QLatin1Char('/') + data.name.toLower() + QLatin1String(".xml"); - if (!QFile::exists(mimeFile)) + if (!QFileInfo::exists(mimeFile)) mimeFile = m_directory + QLatin1Char('/') + data.name + QLatin1String(".xml"); // pre-1.3 QFile qfile(mimeFile); diff --git a/src/libs/utils/mimetypes2/mimetype.h b/src/libs/utils/mimetypes2/mimetype.h index ec28140d6ae..b512aaae723 100644 --- a/src/libs/utils/mimetypes2/mimetype.h +++ b/src/libs/utils/mimetypes2/mimetype.h @@ -4,7 +4,7 @@ #pragma once -#include +#include "../utils_global.h" #include #include diff --git a/src/libs/utils/mimetypes2/mimeutils.cpp b/src/libs/utils/mimetypes2/mimeutils.cpp index 92aeed56f0c..711dc97187a 100644 --- a/src/libs/utils/mimetypes2/mimeutils.cpp +++ b/src/libs/utils/mimetypes2/mimeutils.cpp @@ -57,7 +57,7 @@ QList allMimeTypes() void setMimeStartupPhase(MimeStartupPhase phase) { auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); + QWriteLocker locker(&d->m_initMutex); if (int(phase) != d->m_startupPhase + 1) { qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d", d->m_startupPhase, @@ -69,12 +69,14 @@ void setMimeStartupPhase(MimeStartupPhase phase) void addMimeTypes(const QString &id, const QByteArray &data) { auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - - if (d->m_startupPhase >= int(MimeStartupPhase::PluginsDelayedInitializing)) { - qWarning("Adding items for ID \"%s\" to MimeDatabase after initialization time", - qPrintable(id)); + { + QReadLocker locker(&d->m_initMutex); + if (d->m_startupPhase >= int(MimeStartupPhase::PluginsDelayedInitializing)) { + qWarning("Adding items for ID \"%s\" to MimeDatabase after initialization time", + qPrintable(id)); + } } + QMutexLocker locker(&d->mutex); d->addMimeData(id, data); } @@ -130,4 +132,14 @@ void visitMimeParents(const MimeType &mimeType, } } +/*! + The \a init function will be executed once after the MIME database is first initialized. + It must be thread safe. +*/ +void addMimeInitializer(const std::function &init) +{ + auto d = MimeDatabasePrivate::instance(); + d->addInitializer(init); +} + } // namespace Utils diff --git a/src/libs/utils/mimeutils.h b/src/libs/utils/mimeutils.h index 3fa0fc4ebb9..c5f8b819456 100644 --- a/src/libs/utils/mimeutils.h +++ b/src/libs/utils/mimeutils.h @@ -43,6 +43,7 @@ enum class MimeStartupPhase { }; QTCREATOR_UTILS_EXPORT void setMimeStartupPhase(MimeStartupPhase); +QTCREATOR_UTILS_EXPORT void addMimeInitializer(const std::function &init); QTCREATOR_UTILS_EXPORT void addMimeTypes(const QString &id, const QByteArray &data); QTCREATOR_UTILS_EXPORT QMap> magicRulesForMimeType( const MimeType &mimeType); // priority -> rules diff --git a/src/libs/utils/minimizableinfobars.cpp b/src/libs/utils/minimizableinfobars.cpp index a4fcd478118..d2ff3fe96f7 100644 --- a/src/libs/utils/minimizableinfobars.cpp +++ b/src/libs/utils/minimizableinfobars.cpp @@ -33,7 +33,7 @@ void MinimizableInfoBars::setPossibleInfoBarEntries(const QListsetValueWithDefault(settingsKey(id), show, kShowInInfoBarDefault); } } // namespace Utils diff --git a/src/libs/utils/minimizableinfobars.h b/src/libs/utils/minimizableinfobars.h index a5818ac2cb6..03430f513d1 100644 --- a/src/libs/utils/minimizableinfobars.h +++ b/src/libs/utils/minimizableinfobars.h @@ -7,6 +7,7 @@ #include "id.h" #include "infobar.h" +#include "store.h" #include #include @@ -21,15 +22,13 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT MinimizableInfoBars : public QObject { - Q_OBJECT - public: using ActionCreator = std::function; public: explicit MinimizableInfoBars(InfoBar &infoBar); - void setSettingsGroup(const QString &settingsGroup); + void setSettingsGroup(const Key &settingsGroup); void setPossibleInfoBarEntries(const QList &entries); void createShowInfoBarActions(const ActionCreator &actionCreator) const; @@ -39,7 +38,7 @@ public: private: void createActions(); - QString settingsKey(const Id &id) const; + Key settingsKey(const Id &id) const; bool showInInfoBar(const Id &id) const; void setShowInInfoBar(const Id &id, bool show); @@ -47,9 +46,8 @@ private: void showInfoBar(const Id &id); -private: InfoBar &m_infoBar; - QString m_settingsGroup; + Key m_settingsGroup; QHash m_actions; QHash m_isInfoVisible; QHash m_infoEntries; diff --git a/src/libs/utils/namevaluedictionary.h b/src/libs/utils/namevaluedictionary.h index 4c8b5466816..722558f8912 100644 --- a/src/libs/utils/namevaluedictionary.h +++ b/src/libs/utils/namevaluedictionary.h @@ -30,7 +30,7 @@ inline bool operator<(const DictKey &k1, const DictKey &k2) inline bool operator>(const DictKey &k1, const DictKey &k2) { return k2 < k1; } using NameValuePair = std::pair; -using NameValuePairs = QVector; +using NameValuePairs = QList; using NameValueMap = QMap>; class QTCREATOR_UTILS_EXPORT NameValueDictionary diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h index b1f2bc22f84..62ba9d25baf 100644 --- a/src/libs/utils/namevalueitem.h +++ b/src/libs/utils/namevalueitem.h @@ -10,7 +10,6 @@ #include #include -#include namespace Utils { diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp index 892702566f4..51403f534eb 100644 --- a/src/libs/utils/namevaluesdialog.cpp +++ b/src/libs/utils/namevaluesdialog.cpp @@ -3,7 +3,7 @@ #include "namevaluesdialog.h" -#include "environment.h" +#include "algorithm.h" #include "hostosinfo.h" #include "utilstr.h" @@ -15,7 +15,6 @@ #include namespace Utils { - namespace Internal { static EnvironmentItems cleanUp(const EnvironmentItems &items) @@ -29,14 +28,26 @@ static EnvironmentItems cleanUp(const EnvironmentItems &items) const QString &itemName = item.name; QString emptyName = itemName; emptyName.remove(QLatin1Char(' ')); - if (!emptyName.isEmpty() && !uniqueSet.contains(itemName)) { + if (!emptyName.isEmpty() && Utils::insert(uniqueSet, itemName)) uniqueItems.prepend(item); - uniqueSet.insert(itemName); - } } return uniqueItems; } +class NameValueItemsWidget : public QWidget +{ +public: + explicit NameValueItemsWidget(QWidget *parent = nullptr); + + void setEnvironmentItems(const EnvironmentItems &items); + EnvironmentItems environmentItems() const; + + void setPlaceholderText(const QString &text); + +private: + QPlainTextEdit *m_editor; +}; + NameValueItemsWidget::NameValueItemsWidget(QWidget *parent) : QWidget(parent) { @@ -65,6 +76,7 @@ void NameValueItemsWidget::setPlaceholderText(const QString &text) { m_editor->setPlaceholderText(text); } + } // namespace Internal NameValuesDialog::NameValuesDialog(const QString &windowTitle, const QString &helpText, QWidget *parent) diff --git a/src/libs/utils/namevaluesdialog.h b/src/libs/utils/namevaluesdialog.h index ad1795d503e..ada71d61fcf 100644 --- a/src/libs/utils/namevaluesdialog.h +++ b/src/libs/utils/namevaluesdialog.h @@ -13,32 +13,12 @@ #include #include -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -QT_END_NAMESPACE - namespace Utils { -namespace Internal { -class NameValueItemsWidget : public QWidget -{ - Q_OBJECT -public: - explicit NameValueItemsWidget(QWidget *parent = nullptr); - - void setEnvironmentItems(const EnvironmentItems &items); - EnvironmentItems environmentItems() const; - - void setPlaceholderText(const QString &text); - -private: - QPlainTextEdit *m_editor; -}; -} // namespace Internal +namespace Internal { class NameValueItemsWidget; } class QTCREATOR_UTILS_EXPORT NameValuesDialog : public QDialog { - Q_OBJECT public: void setNameValueItems(const NameValueItems &items); NameValueItems nameValueItems() const; @@ -52,7 +32,6 @@ public: Polisher polish = {}, const QString &windowTitle = {}, const QString &helpText = {}); - protected: explicit NameValuesDialog(const QString &windowTitle, const QString &helpText, diff --git a/src/libs/utils/namevaluevalidator.h b/src/libs/utils/namevaluevalidator.h index c5c3aa23a69..2f11196eb6a 100644 --- a/src/libs/utils/namevaluevalidator.h +++ b/src/libs/utils/namevaluevalidator.h @@ -21,7 +21,6 @@ class NameValueModel; class QTCREATOR_UTILS_EXPORT NameValueValidator : public QValidator { - Q_OBJECT public: NameValueValidator(QWidget *parent, NameValueModel *model, @@ -40,4 +39,5 @@ private: QPersistentModelIndex m_index; mutable QTimer m_hideTipTimer; }; + } // namespace Utils diff --git a/src/libs/utils/navigationtreeview.cpp b/src/libs/utils/navigationtreeview.cpp index 7a6bd4be234..61ef04d8167 100644 --- a/src/libs/utils/navigationtreeview.cpp +++ b/src/libs/utils/navigationtreeview.cpp @@ -24,8 +24,7 @@ NavigationTreeView::NavigationTreeView(QWidget *parent) : TreeView(parent) { setFrameStyle(QFrame::NoFrame); - setIndentation(indentation() * 9/10); - setUniformRowHeights(true); + setIndentation(indentation() * 7/10); setTextElideMode(Qt::ElideNone); setAttribute(Qt::WA_MacShowFocusRect, false); diff --git a/src/libs/utils/navigationtreeview.h b/src/libs/utils/navigationtreeview.h index ab1b1b7b4a7..a3a0e2b07de 100644 --- a/src/libs/utils/navigationtreeview.h +++ b/src/libs/utils/navigationtreeview.h @@ -11,7 +11,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT NavigationTreeView : public TreeView { - Q_OBJECT public: explicit NavigationTreeView(QWidget *parent = nullptr); void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override; diff --git a/src/libs/utils/networkaccessmanager.h b/src/libs/utils/networkaccessmanager.h index 725757c2fe6..33a479946b2 100644 --- a/src/libs/utils/networkaccessmanager.h +++ b/src/libs/utils/networkaccessmanager.h @@ -11,7 +11,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT NetworkAccessManager : public QNetworkAccessManager { - Q_OBJECT public: NetworkAccessManager(QObject *parent = nullptr); diff --git a/src/libs/utils/optionpushbutton.cpp b/src/libs/utils/optionpushbutton.cpp index c3993fef4b0..c20b2e74706 100644 --- a/src/libs/utils/optionpushbutton.cpp +++ b/src/libs/utils/optionpushbutton.cpp @@ -45,13 +45,16 @@ OptionPushButton::OptionPushButton(const QString &text, QWidget *parent) This menu is shown if the user clicks on the menu indicator that is shown. If the user clicks anywhere else on the button, QAbstractButton::clicked() is sent instead. + \note Calling this method removes all connections to the QAbstractButton::pressed() signal. + Ownership of the menu is not transferred to the push button. */ void OptionPushButton::setOptionalMenu(QMenu *menu) { setMenu(menu); - // hack away that QPushButton opens the menu on "pressed" - disconnect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed())); + // Hack away that QPushButton opens the menu on "pressed". + // Also removes all other connections to "pressed". + disconnect(this, &QPushButton::pressed, 0, 0); } /*! diff --git a/src/libs/utils/optionpushbutton.h b/src/libs/utils/optionpushbutton.h index e0b5800fd39..143ec2477ea 100644 --- a/src/libs/utils/optionpushbutton.h +++ b/src/libs/utils/optionpushbutton.h @@ -16,8 +16,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT OptionPushButton : public QPushButton { - Q_OBJECT - public: OptionPushButton(QWidget *parent = nullptr); OptionPushButton(const QString &text, QWidget *parent = nullptr); diff --git a/src/libs/utils/overlaywidget.h b/src/libs/utils/overlaywidget.h index 82e635c7ab6..8bc5b7d1eb5 100644 --- a/src/libs/utils/overlaywidget.h +++ b/src/libs/utils/overlaywidget.h @@ -13,7 +13,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget { - Q_OBJECT public: using PaintFunction = std::function; diff --git a/src/libs/utils/passworddialog.cpp b/src/libs/utils/passworddialog.cpp new file mode 100644 index 00000000000..7958f7d6bd9 --- /dev/null +++ b/src/libs/utils/passworddialog.cpp @@ -0,0 +1,204 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "passworddialog.h" + +#include "layoutbuilder.h" +#include "stylehelper.h" +#include "utilsicons.h" +#include "utilstr.h" + +#include +#include +#include +#include +#include +#include + +namespace Utils { + +ShowPasswordButton::ShowPasswordButton(QWidget *parent) + : QAbstractButton(parent) +{ + setAttribute(Qt::WA_Hover); + setCheckable(true); + setToolTip(Tr::tr("Show/Hide Password")); +} + +void ShowPasswordButton::paintEvent(QPaintEvent *e) +{ + Q_UNUSED(e); + QIcon icon = isChecked() ? Utils::Icons::EYE_OPEN_TOOLBAR.icon() + : Utils::Icons::EYE_CLOSED_TOOLBAR.icon(); + QPainter p(this); + QRect r(QPoint(), size()); + + if (m_containsMouse && isEnabled()) + StyleHelper::drawPanelBgRect(&p, r, creatorTheme()->color(Theme::FancyToolButtonHoverColor)); + + QWindow *window = this->window()->windowHandle(); + QSize s = icon.actualSize(window, QSize(32, 16)); + + QPixmap px = icon.pixmap(s); + QRect iRect(QPoint(), s); + iRect.moveCenter(r.center()); + p.drawPixmap(iRect, px); +} + +void ShowPasswordButton::enterEvent(QEnterEvent *e) +{ + m_containsMouse = true; + e->accept(); + update(); +} + +void ShowPasswordButton::leaveEvent(QEvent *e) +{ + m_containsMouse = false; + e->accept(); + update(); +} + +QSize ShowPasswordButton::sizeHint() const +{ + QWindow *window = this->window()->windowHandle(); + QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(window, QSize(32, 16)) + QSize(8, 8); + + if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleRelaxed) + s += QSize(5, 5); + + return s; +} + +class PasswordDialogPrivate +{ +public: + QLineEdit *m_userNameLineEdit{nullptr}; + QLineEdit *m_passwordLineEdit{nullptr}; + QCheckBox *m_checkBox{nullptr}; + QDialogButtonBox *m_buttonBox{nullptr}; +}; + +PasswordDialog::PasswordDialog(const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + bool withUsername, + QWidget *parent) + : QDialog(parent) + , d(new PasswordDialogPrivate) +{ + setWindowTitle(title); + + d->m_passwordLineEdit = new QLineEdit(); + d->m_passwordLineEdit->setEchoMode(QLineEdit::Password); + + if (withUsername) + d->m_userNameLineEdit = new QLineEdit(); + + d->m_checkBox = new QCheckBox(); + d->m_checkBox->setText(doNotAskAgainLabel); + + auto *showPasswordButton = new ShowPasswordButton; + + connect(showPasswordButton, &QAbstractButton::toggled, this, [this, showPasswordButton] { + d->m_passwordLineEdit->setEchoMode(showPasswordButton->isChecked() ? QLineEdit::Normal + : QLineEdit::Password); + }); + + d->m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(d->m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(d->m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + using namespace Layouting; + + // clang-format off + Column { + prompt, + If { + withUsername, { + Form { + Tr::tr("User:"), d->m_userNameLineEdit, br, + Tr::tr("Password:"), Row { d->m_passwordLineEdit, showPasswordButton }, br, + } + }, { + Row { + d->m_passwordLineEdit, showPasswordButton, + }, + } + }, + Row { + d->m_checkBox, + d->m_buttonBox, + } + }.attachTo(this); + // clang-format on + + d->m_passwordLineEdit->setMinimumWidth(d->m_passwordLineEdit->width() / 2); + setFixedSize(sizeHint()); +} + +PasswordDialog::~PasswordDialog() = default; + +void PasswordDialog::setUser(const QString &user) +{ + QTC_ASSERT(d->m_userNameLineEdit, return); + d->m_userNameLineEdit->setText(user); +} + +QString PasswordDialog::user() const +{ + QTC_ASSERT(d->m_userNameLineEdit, return {}); + return d->m_userNameLineEdit->text(); +} + +QString PasswordDialog::password() const +{ + return d->m_passwordLineEdit->text(); +} + +std::optional> PasswordDialog::getUserAndPassword( + const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + const QString &userName, + const CheckableDecider &decider, + QWidget *parent) +{ + if (!decider.shouldAskAgain()) + return std::nullopt; + + PasswordDialog dialog(title, prompt, doNotAskAgainLabel, true, parent); + + dialog.setUser(userName); + + if (dialog.exec() == QDialog::Accepted) + return qMakePair(dialog.user(), dialog.password()); + + if (dialog.d->m_checkBox->isChecked()) + decider.doNotAskAgain(); + + return std::nullopt; +} + +std::optional PasswordDialog::getPassword(const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + const CheckableDecider &decider, + QWidget *parent) +{ + if (!decider.shouldAskAgain()) + return std::nullopt; + + PasswordDialog dialog(title, prompt, doNotAskAgainLabel, false, parent); + + if (dialog.exec() == QDialog::Accepted) + return dialog.password(); + + if (dialog.d->m_checkBox->isChecked()) + decider.doNotAskAgain(); + + return std::nullopt; +} + +} // namespace Utils diff --git a/src/libs/utils/passworddialog.h b/src/libs/utils/passworddialog.h new file mode 100644 index 00000000000..a3cdb98b0f6 --- /dev/null +++ b/src/libs/utils/passworddialog.h @@ -0,0 +1,68 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include "checkablemessagebox.h" + +#include +#include + +#include +#include + +namespace Utils { + +class PasswordDialogPrivate; + +class QTCREATOR_UTILS_EXPORT ShowPasswordButton : public QAbstractButton +{ +public: + ShowPasswordButton(QWidget *parent = nullptr); + + void paintEvent(QPaintEvent *e) override; + void enterEvent(QEnterEvent *e) override; + void leaveEvent(QEvent *e) override; + + QSize sizeHint() const override; + +private: + bool m_containsMouse{false}; +}; + +class QTCREATOR_UTILS_EXPORT PasswordDialog : public QDialog +{ +public: + PasswordDialog(const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + bool withUsername, + QWidget *parent = nullptr); + virtual ~PasswordDialog(); + + void setUser(const QString &user); + QString user() const; + + QString password() const; + + static std::optional> getUserAndPassword( + const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + const QString &userName, + const CheckableDecider &decider, + QWidget *parent = nullptr); + + static std::optional getPassword(const QString &title, + const QString &prompt, + const QString &doNotAskAgainLabel, + const CheckableDecider &decider, + QWidget *parent = nullptr); + +private: + std::unique_ptr d; +}; + +} // namespace Utils diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 1fe1e962560..048fb57ddb7 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -3,6 +3,7 @@ #include "pathchooser.h" +#include "async.h" #include "commandline.h" #include "environment.h" #include "fileutils.h" @@ -15,6 +16,7 @@ #include "utilstr.h" #include +#include #include #include #include @@ -375,9 +377,10 @@ bool PathChooser::isReadOnly() const void PathChooser::setReadOnly(bool b) { d->m_lineEdit->setReadOnly(b); + d->m_lineEdit->setFrame(!b); const auto buttons = d->m_buttons; for (QAbstractButton *button : buttons) - button->setEnabled(!b); + button->setVisible(!b); } void PathChooser::slotBrowse(bool remote) @@ -531,100 +534,73 @@ void PathChooser::setToolTip(const QString &toolTip) d->m_lineEdit->setToolTip(toolTip); } -FancyLineEdit::ValidationFunction PathChooser::defaultValidationFunction() const +static FancyLineEdit::AsyncValidationResult validatePath(FilePath filePath, + const QString &defaultValue, + PathChooser::Kind kind) { - return std::bind(&PathChooser::validatePath, this, std::placeholders::_1, std::placeholders::_2); -} - -bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const -{ - QString input = edit->text(); - - if (input.isEmpty()) { - if (!d->m_defaultValue.isEmpty()) { - input = d->m_defaultValue; - } else { - if (errorMessage) - *errorMessage = Tr::tr("The path must not be empty."); - return false; - } - } - - const FilePath filePath = d->expandedPath(FilePath::fromUserInput(input)); if (filePath.isEmpty()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" expanded to an empty string.").arg(input); - return false; + if (!defaultValue.isEmpty()) { + filePath = FilePath::fromUserInput(defaultValue); + } else { + return make_unexpected(Tr::tr("The path must not be empty.")); + } } // Check if existing - switch (d->m_acceptingKind) { + switch (kind) { case PathChooser::ExistingDirectory: if (!filePath.exists()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput())); } if (!filePath.isDir()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" is not a directory.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" is not a directory.").arg(filePath.toUserOutput())); } break; case PathChooser::File: if (!filePath.exists()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput())); } if (!filePath.isFile()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" is not a file.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" is not a file.").arg(filePath.toUserOutput())); } break; case PathChooser::SaveFile: if (!filePath.parentDir().exists()) { - if (errorMessage) - *errorMessage = Tr::tr("The directory \"%1\" does not exist.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The directory \"%1\" does not exist.").arg(filePath.toUserOutput())); } if (filePath.exists() && filePath.isDir()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" is not a file.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" is not a file.").arg(filePath.toUserOutput())); } break; case PathChooser::ExistingCommand: if (!filePath.exists()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" does not exist.").arg(filePath.toUserOutput())); } if (!filePath.isExecutableFile()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" is not an executable file.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" is not an executable file.").arg(filePath.toUserOutput())); } break; case PathChooser::Directory: if (filePath.exists() && !filePath.isDir()) { - if (errorMessage) - *errorMessage = Tr::tr("The path \"%1\" is not a directory.").arg(filePath.toUserOutput()); - return false; + return make_unexpected( + Tr::tr("The path \"%1\" is not a directory.").arg(filePath.toUserOutput())); } if (filePath.osType() == OsTypeWindows && !filePath.startsWithDriveLetter() && !filePath.startsWith("\\\\") && !filePath.startsWith("//")) { - if (errorMessage) - *errorMessage = Tr::tr("Invalid path \"%1\".").arg(filePath.toUserOutput()); - return false; + return make_unexpected(Tr::tr("Invalid path \"%1\".").arg(filePath.toUserOutput())); } break; case PathChooser::Command: if (filePath.exists() && !filePath.isExecutableFile()) { - if (errorMessage) - *errorMessage = Tr::tr("Cannot execute \"%1\".").arg(filePath.toUserOutput()); - return false; + return make_unexpected(Tr::tr("Cannot execute \"%1\".").arg(filePath.toUserOutput())); } break; @@ -632,9 +608,32 @@ bool PathChooser::validatePath(FancyLineEdit *edit, QString *errorMessage) const ; } - if (errorMessage) - *errorMessage = Tr::tr("Full path: \"%1\"").arg(filePath.toUserOutput()); - return true; + return filePath.toUserOutput(); +} + +FancyLineEdit::AsyncValidationFunction PathChooser::defaultValidationFunction() const +{ + return [this](const QString &text) -> FancyLineEdit::AsyncValidationFuture { + if (text.isEmpty()) { + return QtFuture::makeReadyFuture((Utils::expected_str( + make_unexpected(Tr::tr("The path must not be empty."))))); + } + + const FilePath expanded = d->expandedPath(FilePath::fromUserInput(text)); + + if (expanded.isEmpty()) { + return QtFuture::makeReadyFuture((Utils::expected_str( + make_unexpected(Tr::tr("The path \"%1\" expanded to an empty string.") + .arg(expanded.toUserOutput()))))); + } + + return Utils::asyncRun( + [expanded, + defVal = d->m_defaultValue, + kind = d->m_acceptingKind]() -> FancyLineEdit::AsyncValidationResult { + return validatePath(expanded, defVal, kind); + }); + }; } void PathChooser::setValidationFunction(const FancyLineEdit::ValidationFunction &fn) @@ -736,7 +735,7 @@ void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList ef->setArguments(arguments); } -void PathChooser::setHistoryCompleter(const QString &historyKey, bool restoreLastItemFromHistory) +void PathChooser::setHistoryCompleter(const Key &historyKey, bool restoreLastItemFromHistory) { d->m_lineEdit->setHistoryCompleter(historyKey, restoreLastItemFromHistory); } diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 62a370185da..b7b4a040f31 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -80,7 +80,7 @@ public: /** Returns the suggested label title when used in a form layout. */ static QString label(); - FancyLineEdit::ValidationFunction defaultValidationFunction() const; + FancyLineEdit::AsyncValidationFunction defaultValidationFunction() const; void setValidationFunction(const FancyLineEdit::ValidationFunction &fn); /** Return the home directory, which needs some fixing under Windows. */ @@ -105,7 +105,7 @@ public: static void installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments); // Enable a history completer with a history of entries. - void setHistoryCompleter(const QString &historyKey, bool restoreLastItemFromHistory = false); + void setHistoryCompleter(const Key &historyKey, bool restoreLastItemFromHistory = false); // Sets a macro expander that is used when producing path and fileName. // By default, the global expander is used. @@ -153,7 +153,6 @@ private: // Use filePath().toString() or better suitable conversions. QString path() const { return filePath().toString(); } - bool validatePath(FancyLineEdit *edit, QString *errorMessage) const; // Returns overridden title or the one from QString makeDialogTitle(const QString &title); void slotBrowse(bool remote); diff --git a/src/libs/utils/pathlisteditor.cpp b/src/libs/utils/pathlisteditor.cpp index f2349b275fd..383ca88b030 100644 --- a/src/libs/utils/pathlisteditor.cpp +++ b/src/libs/utils/pathlisteditor.cpp @@ -138,7 +138,7 @@ QStringList PathListEditor::pathList() const { const QString text = d->edit->toPlainText().trimmed(); if (text.isEmpty()) - return QStringList(); + return {}; // trim each line QStringList rc = text.split('\n', Qt::SkipEmptyParts); const QStringList::iterator end = rc.end(); diff --git a/src/libs/utils/persistentcachestore.cpp b/src/libs/utils/persistentcachestore.cpp new file mode 100644 index 00000000000..0cac5f83cde --- /dev/null +++ b/src/libs/utils/persistentcachestore.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "persistentcachestore.h" + +#include "filepath.h" +#include "fileutils.h" + +#include <QMap> +#include <QMutex> +#include <QStandardPaths> + +namespace Utils { + +class PrivateGlobal +{ +public: + QMutex mutex; + QMap<Key, Store> caches; +}; + +static expected_str<FilePath> cacheFolder() +{ + static const FilePath folder = FilePath::fromUserInput(QStandardPaths::writableLocation( + QStandardPaths::CacheLocation)) + / "CachedStores"; + static expected_str<void> created = folder.ensureWritableDir(); + static expected_str<FilePath> result = created ? folder + : expected_str<FilePath>( + make_unexpected(created.error())); + + QTC_ASSERT_EXPECTED(result, return result); + return result; +} + +static PrivateGlobal &globals() +{ + static PrivateGlobal global; + return global; +} + +static expected_str<FilePath> filePathFromKey(const Key &cacheKey) +{ + static const expected_str<FilePath> folder = cacheFolder(); + if (!folder) + return folder; + + return (*folder / FileUtils::fileSystemFriendlyName(stringFromKey(cacheKey))).withSuffix(".json"); +} + +expected_str<Store> PersistentCacheStore::byKey(const Key &cacheKey) +{ + const expected_str<FilePath> path = filePathFromKey(cacheKey); + if (!path) + return make_unexpected(path.error()); + + QMutexLocker locker(&globals().mutex); + + auto it = globals().caches.find(cacheKey); + if (it != globals().caches.end()) + return it.value(); + + const expected_str<QByteArray> contents = path->fileContents(); + if (!contents) + return make_unexpected(contents.error()); + + auto result = storeFromJson(*contents); + if (!result) + return result; + + if (result->value("__cache_key__").toString() != stringFromKey(cacheKey)) { + return make_unexpected(QString("Cache key mismatch: \"%1\" to \"%2\" in \"%3\".") + .arg(stringFromKey(cacheKey)) + .arg(result->value("__cache_key__").toString()) + .arg(path->toUserOutput())); + } + + return result; +} + +expected_str<void> PersistentCacheStore::write(const Key &cacheKey, const Store &store) +{ + const expected_str<FilePath> path = filePathFromKey(cacheKey); + if (!path) + return make_unexpected(path.error()); + + QMutexLocker locker(&globals().mutex); + globals().caches.insert(cacheKey, store); + + // TODO: The writing of the store data could be done in a separate thread in the future. + Store storeCopy = store; + storeCopy.insert("__cache_key__", stringFromKey(cacheKey)); + storeCopy.insert("__last_modified__", QDateTime::currentDateTime().toString(Qt::ISODate)); + QByteArray json = jsonFromStore(storeCopy); + const expected_str<qint64> result = path->writeFileContents(json); + if (!result) + return make_unexpected(result.error()); + return {}; +} + +expected_str<void> PersistentCacheStore::clear(const Key &cacheKey) +{ + const expected_str<FilePath> path = filePathFromKey(cacheKey); + if (!path) + return make_unexpected(path.error()); + + QMutexLocker locker(&globals().mutex); + globals().caches.remove(cacheKey); + + if (!path->removeFile()) + return make_unexpected(QString("Failed to remove cache file.")); + return {}; +} + +} // namespace Utils diff --git a/src/libs/utils/persistentcachestore.h b/src/libs/utils/persistentcachestore.h new file mode 100644 index 00000000000..0c40061d619 --- /dev/null +++ b/src/libs/utils/persistentcachestore.h @@ -0,0 +1,22 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include "expected.h" +#include "store.h" +#include "storekey.h" + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT PersistentCacheStore +{ +public: + static expected_str<Store> byKey(const Key &cacheKey); + static expected_str<void> write(const Key &cacheKey, const Store &store); + static expected_str<void> clear(const Key &cacheKey); +}; + +} // namespace Utils diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp index 2daf2934c51..54a829004b3 100644 --- a/src/libs/utils/persistentsettings.cpp +++ b/src/libs/utils/persistentsettings.cpp @@ -95,22 +95,18 @@ static QRect stringToRectangle(const QString &v) namespace Utils { -struct Context // Basic context containing element name string constants. -{ - Context() {} - const QString qtCreatorElement = QString("qtcreator"); - const QString dataElement = QString("data"); - const QString variableElement = QString("variable"); - const QString typeAttribute = QString("type"); - const QString valueElement = QString("value"); - const QString valueListElement = QString("valuelist"); - const QString valueMapElement = QString("valuemap"); - const QString keyAttribute = QString("key"); -}; +const QString qtCreatorElement("qtcreator"); +const QString dataElement("data"); +const QString variableElement("variable"); +const QString typeAttribute("type"); +const QString valueElement("value"); +const QString valueListElement("valuelist"); +const QString valueMapElement("valuemap"); +const QString keyAttribute("key"); struct ParseValueStackEntry { - explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = QString()) : type(t), key(k) {} + explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = {}) : type(t), key(k) {} explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k); QVariant value() const; @@ -123,8 +119,8 @@ struct ParseValueStackEntry QVariantMap mapValue; }; -ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k) : - type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue) +ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k) + : type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue) { QTC_ASSERT(simpleValue.isValid(), return); } @@ -160,22 +156,16 @@ void ParseValueStackEntry::addChild(const QString &key, const QVariant &v) } } -class ParseContext : public Context +class ParseContext { public: QVariantMap parse(const FilePath &file); private: - enum Element { QtCreatorElement, DataElement, VariableElement, - SimpleValueElement, ListValueElement, MapValueElement, UnknownElement }; - - Element element(const QStringView &r) const; - static inline bool isValueElement(Element e) - { return e == SimpleValueElement || e == ListValueElement || e == MapValueElement; } QVariant readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const; bool handleStartElement(QXmlStreamReader &r); - bool handleEndElement(const QStringView &name); + bool handleEndElement(const QStringView name); static QString formatWarning(const QXmlStreamReader &r, const QString &message); @@ -204,7 +194,7 @@ QVariantMap ParseContext::parse(const FilePath &file) case QXmlStreamReader::Invalid: qWarning("Error reading %s:%d: %s", qPrintable(file.fileName()), int(r.lineNumber()), qPrintable(r.errorString())); - return QVariantMap(); + return {}; default: break; } // switch token @@ -215,19 +205,14 @@ QVariantMap ParseContext::parse(const FilePath &file) bool ParseContext::handleStartElement(QXmlStreamReader &r) { const QStringView name = r.name(); - const Element e = element(name); - if (e == VariableElement) { + if (name == variableElement) { m_currentVariableName = r.readElementText(); return false; } - if (!ParseContext::isValueElement(e)) - return false; - - const QXmlStreamAttributes attributes = r.attributes(); - const QString key = attributes.hasAttribute(keyAttribute) ? - attributes.value(keyAttribute).toString() : QString(); - switch (e) { - case SimpleValueElement: { + if (name == valueElement) { + const QXmlStreamAttributes attributes = r.attributes(); + const QString key = attributes.hasAttribute(keyAttribute) ? + attributes.value(keyAttribute).toString() : QString(); // This reads away the end element, so, handle end element right here. const QVariant v = readSimpleValue(r, attributes); if (!v.isValid()) { @@ -237,22 +222,26 @@ bool ParseContext::handleStartElement(QXmlStreamReader &r) m_valueStack.push_back(ParseValueStackEntry(v, key)); return handleEndElement(name); } - case ListValueElement: + if (name == valueListElement) { + const QXmlStreamAttributes attributes = r.attributes(); + const QString key = attributes.hasAttribute(keyAttribute) ? + attributes.value(keyAttribute).toString() : QString(); m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key)); - break; - case MapValueElement: + return false; + } + if (name == valueMapElement) { + const QXmlStreamAttributes attributes = r.attributes(); + const QString key = attributes.hasAttribute(keyAttribute) ? + attributes.value(keyAttribute).toString() : QString(); m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key)); - break; - default: - break; + return false; } return false; } -bool ParseContext::handleEndElement(const QStringView &name) +bool ParseContext::handleEndElement(const QStringView name) { - const Element e = element(name); - if (ParseContext::isValueElement(e)) { + if (name == valueElement || name == valueListElement || name == valueMapElement) { QTC_ASSERT(!m_valueStack.isEmpty(), return true); const ParseValueStackEntry top = m_valueStack.pop(); if (m_valueStack.isEmpty()) { // Last element? -> Done with that variable. @@ -262,8 +251,9 @@ bool ParseContext::handleEndElement(const QStringView &name) return false; } m_valueStack.top().addChild(top.key, top.value()); + return false; } - return e == QtCreatorElement; + return name == qtCreatorElement; } QString ParseContext::formatWarning(const QXmlStreamReader &r, const QString &message) @@ -278,23 +268,6 @@ QString ParseContext::formatWarning(const QXmlStreamReader &r, const QString &me return result; } -ParseContext::Element ParseContext::element(const QStringView &r) const -{ - if (r == valueElement) - return SimpleValueElement; - if (r == valueListElement) - return ListValueElement; - if (r == valueMapElement) - return MapValueElement; - if (r == qtCreatorElement) - return QtCreatorElement; - if (r == dataElement) - return DataElement; - if (r == variableElement) - return VariableElement; - return UnknownElement; -} - QVariant ParseContext::readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttributes &attributes) const { // Simple value @@ -318,16 +291,16 @@ QVariant ParseContext::readSimpleValue(QXmlStreamReader &r, const QXmlStreamAttr PersistentSettingsReader::PersistentSettingsReader() = default; -QVariant PersistentSettingsReader::restoreValue(const QString &variable, const QVariant &defaultValue) const +QVariant PersistentSettingsReader::restoreValue(const Key &variable, const QVariant &defaultValue) const { - if (m_valueMap.contains(variable)) - return m_valueMap.value(variable); + if (m_valueMap.contains(stringFromKey(variable))) + return m_valueMap.value(stringFromKey(variable)); return defaultValue; } -QVariantMap PersistentSettingsReader::restoreValues() const +Store PersistentSettingsReader::restoreValues() const { - return m_valueMap; + return storeFromMap(m_valueMap); } bool PersistentSettingsReader::load(const FilePath &fileName) @@ -352,47 +325,50 @@ FilePath PersistentSettingsReader::filePath() \class Utils::PersistentSettingsWriter \inmodule QtCreator - \brief The PersistentSettingsWriter class serializes a QVariantMap of + \brief The PersistentSettingsWriter class serializes a Store of arbitrary, nested data structures to an XML file. \sa Utils::PersistentSettingsReader */ -static void writeVariantValue(QXmlStreamWriter &w, const Context &ctx, - const QVariant &variant, const QString &key = QString()) +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) +static QString xmlAttrFromKey(const QString &key) { return key; } +#else +static QString xmlAttrFromKey(const QString &key) { return key; } +#endif + +static void writeVariantValue(QXmlStreamWriter &w, const QVariant &variant, const QString &key = {}) { - switch (static_cast<int>(variant.type())) { - case static_cast<int>(QVariant::StringList): - case static_cast<int>(QVariant::List): { - w.writeStartElement(ctx.valueListElement); - w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::List))); + static const int storeId = qMetaTypeId<Store>(); + + const int variantType = variant.typeId(); + if (variantType == QMetaType::QStringList || variantType == QMetaType::QVariantList) { + w.writeStartElement(valueListElement); + w.writeAttribute(typeAttribute, "QVariantList"); if (!key.isEmpty()) - w.writeAttribute(ctx.keyAttribute, key); + w.writeAttribute(keyAttribute, xmlAttrFromKey(key)); const QList<QVariant> list = variant.toList(); for (const QVariant &var : list) - writeVariantValue(w, ctx, var); + writeVariantValue(w, var); w.writeEndElement(); - break; - } - case static_cast<int>(QVariant::Map): { - w.writeStartElement(ctx.valueMapElement); - w.writeAttribute(ctx.typeAttribute, QLatin1String(QVariant::typeToName(QVariant::Map))); + } else if (variantType == storeId || variantType == QMetaType::QVariantMap) { + w.writeStartElement(valueMapElement); + w.writeAttribute(typeAttribute, "QVariantMap"); if (!key.isEmpty()) - w.writeAttribute(ctx.keyAttribute, key); + w.writeAttribute(keyAttribute, xmlAttrFromKey(key)); const QVariantMap varMap = variant.toMap(); - const QVariantMap::const_iterator cend = varMap.constEnd(); - for (QVariantMap::const_iterator i = varMap.constBegin(); i != cend; ++i) - writeVariantValue(w, ctx, i.value(), i.key()); + const auto cend = varMap.constEnd(); + for (auto i = varMap.constBegin(); i != cend; ++i) + writeVariantValue(w, i.value(), i.key()); w.writeEndElement(); - } - break; - case static_cast<int>(QMetaType::QObjectStar): // ignore QObjects! - case static_cast<int>(QMetaType::VoidStar): // ignore void pointers! - break; - default: - w.writeStartElement(ctx.valueElement); - w.writeAttribute(ctx.typeAttribute, QLatin1String(variant.typeName())); + } else if (variantType == QMetaType::QObjectStar) { + // ignore QObjects + } else if (variantType == QMetaType::VoidStar) { + // ignore void pointers + } else { + w.writeStartElement(valueElement); + w.writeAttribute(typeAttribute, QLatin1String(variant.typeName())); if (!key.isEmpty()) - w.writeAttribute(ctx.keyAttribute, key); + w.writeAttribute(keyAttribute, xmlAttrFromKey(key)); switch (variant.type()) { case QVariant::Rect: w.writeCharacters(rectangleToString(variant.toRect())); @@ -402,7 +378,6 @@ static void writeVariantValue(QXmlStreamWriter &w, const Context &ctx, break; } w.writeEndElement(); - break; } } @@ -410,7 +385,7 @@ PersistentSettingsWriter::PersistentSettingsWriter(const FilePath &fileName, con m_fileName(fileName), m_docType(docType) { } -bool PersistentSettingsWriter::save(const QVariantMap &data, QString *errorString) const +bool PersistentSettingsWriter::save(const Store &data, QString *errorString) const { if (data == m_savedData) return true; @@ -418,7 +393,7 @@ bool PersistentSettingsWriter::save(const QVariantMap &data, QString *errorStrin } #ifdef QT_GUI_LIB -bool PersistentSettingsWriter::save(const QVariantMap &data, QWidget *parent) const +bool PersistentSettingsWriter::save(const Store &data, QWidget *parent) const { QString errorString; const bool success = save(data, &errorString); @@ -432,17 +407,16 @@ FilePath PersistentSettingsWriter::fileName() const { return m_fileName; } //** * @brief Set contents of file (e.g. from data read from it). */ -void PersistentSettingsWriter::setContents(const QVariantMap &data) +void PersistentSettingsWriter::setContents(const Store &data) { m_savedData = data; } -bool PersistentSettingsWriter::write(const QVariantMap &data, QString *errorString) const +bool PersistentSettingsWriter::write(const Store &data, QString *errorString) const { m_fileName.parentDir().ensureWritableDir(); FileSaver saver(m_fileName, QIODevice::Text); if (!saver.hasError()) { - const Context ctx; QXmlStreamWriter w(saver.file()); w.setAutoFormatting(true); w.setAutoFormattingIndent(1); // Historical, used to be QDom. @@ -452,12 +426,12 @@ bool PersistentSettingsWriter::write(const QVariantMap &data, QString *errorStri arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion(), QDateTime::currentDateTime().toString(Qt::ISODate))); - w.writeStartElement(ctx.qtCreatorElement); - const QVariantMap::const_iterator cend = data.constEnd(); - for (QVariantMap::const_iterator it = data.constBegin(); it != cend; ++it) { - w.writeStartElement(ctx.dataElement); - w.writeTextElement(ctx.variableElement, it.key()); - writeVariantValue(w, ctx, it.value()); + w.writeStartElement(qtCreatorElement); + const QVariantMap map = mapFromStore(data); + for (auto it = map.constBegin(), cend = map.constEnd(); it != cend; ++it) { + w.writeStartElement(dataElement); + w.writeTextElement(variableElement, it.key()); + writeVariantValue(w, it.value()); w.writeEndElement(); } w.writeEndDocument(); diff --git a/src/libs/utils/persistentsettings.h b/src/libs/utils/persistentsettings.h index 7e07f0237c6..8092e8a0118 100644 --- a/src/libs/utils/persistentsettings.h +++ b/src/libs/utils/persistentsettings.h @@ -6,6 +6,7 @@ #include "utils_global.h" #include "filepath.h" +#include "store.h" #include <QVariant> @@ -19,13 +20,13 @@ class QTCREATOR_UTILS_EXPORT PersistentSettingsReader { public: PersistentSettingsReader(); - QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const; - QVariantMap restoreValues() const; + QVariant restoreValue(const Key &variable, const QVariant &defaultValue = {}) const; + Store restoreValues() const; bool load(const FilePath &fileName); FilePath filePath(); private: - QMap<QString, QVariant> m_valueMap; + QVariantMap m_valueMap; FilePath m_filePath; }; @@ -34,21 +35,21 @@ class QTCREATOR_UTILS_EXPORT PersistentSettingsWriter public: PersistentSettingsWriter(const FilePath &fileName, const QString &docType); - bool save(const QVariantMap &data, QString *errorString) const; + bool save(const Store &data, QString *errorString) const; #ifdef QT_GUI_LIB - bool save(const QVariantMap &data, QWidget *parent) const; + bool save(const Store &data, QWidget *parent) const; #endif FilePath fileName() const; - void setContents(const QVariantMap &data); + void setContents(const Store &data); private: - bool write(const QVariantMap &data, QString *errorString) const; + bool write(const Store &data, QString *errorString) const; const FilePath m_fileName; const QString m_docType; - mutable QMap<QString, QVariant> m_savedData; + mutable Store m_savedData; }; } // namespace Utils diff --git a/src/libs/utils/port.h b/src/libs/utils/port.h index c4b46631de2..2bf561f0b94 100644 --- a/src/libs/utils/port.h +++ b/src/libs/utils/port.h @@ -5,8 +5,8 @@ #include "utils_global.h" -#include <QMetaType> #include <QList> +#include <QMetaType> #include <QString> namespace Utils { diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 8721d6c4214..534dcff78f1 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -410,6 +410,13 @@ public: } connect(m_ptyProcess->notifier(), &QIODevice::readyRead, this, [this] { + if (m_setup.m_ptyData->ptyInputFlagsChangedHandler() + && m_inputFlags != m_ptyProcess->inputFlags()) { + m_inputFlags = m_ptyProcess->inputFlags(); + m_setup.m_ptyData->ptyInputFlagsChangedHandler()( + static_cast<Pty::PtyInputFlag>(m_inputFlags.toInt())); + } + emit readyRead(m_ptyProcess->readAll(), {}); }); @@ -430,6 +437,7 @@ public: private: std::unique_ptr<IPtyProcess> m_ptyProcess; + IPtyProcess::PtyInputFlags m_inputFlags; }; class QProcessImpl final : public DefaultImpl @@ -1183,10 +1191,25 @@ const Environment &Process::controlEnvironment() const return d->m_setup.m_controlEnvironment; } +void Process::setRunData(const ProcessRunData &data) +{ + if (data.workingDirectory.needsDevice() && data.command.executable().needsDevice()) { + QTC_CHECK(data.workingDirectory.isSameDevice(data.command.executable())); + } + d->m_setup.m_commandLine = data.command; + d->m_setup.m_workingDirectory = data.workingDirectory; + d->m_setup.m_environment = data.environment; +} + +ProcessRunData Process::runData() const +{ + return {d->m_setup.m_commandLine, d->m_setup.m_workingDirectory, d->m_setup.m_environment}; +} + void Process::setCommand(const CommandLine &cmdLine) { if (d->m_setup.m_workingDirectory.needsDevice() && cmdLine.executable().needsDevice()) { - QTC_CHECK(d->m_setup.m_workingDirectory.host() == cmdLine.executable().host()); + QTC_CHECK(d->m_setup.m_workingDirectory.isSameDevice(cmdLine.executable())); } d->m_setup.m_commandLine = cmdLine; } @@ -1228,7 +1251,19 @@ void Process::start() } else { processImpl = d->createProcessInterface(); } - QTC_ASSERT(processImpl, return); + + if (!processImpl) { + // This happens if a device does not implement the createProcessInterface() function. + d->m_result = ProcessResult::StartFailed; + d->m_resultData.m_exitCode = 255; + d->m_resultData.m_exitStatus = QProcess::CrashExit; + d->m_resultData.m_errorString = Tr::tr("Failed to create process interface for \"%1\".") + .arg(d->m_setup.m_commandLine.toUserOutput()); + d->m_resultData.m_error = QProcess::FailedToStart; + d->emitGuardedSignal(&Process::done); + return; + } + d->setProcessInterface(processImpl); d->m_state = QProcess::Starting; d->m_process->m_setup = d->m_setup; @@ -1463,18 +1498,6 @@ QString Process::errorString() const return resultData().m_errorString; } -// Path utilities - -Environment Process::systemEnvironmentForBinary(const FilePath &filePath) -{ - if (filePath.needsDevice()) { - QTC_ASSERT(s_deviceHooks.systemEnvironmentForBinary, return {}); - return s_deviceHooks.systemEnvironmentForBinary(filePath); - } - - return Environment::systemEnvironment(); -} - qint64 Process::applicationMainThreadId() const { return d->m_applicationMainThreadId; diff --git a/src/libs/utils/process.h b/src/libs/utils/process.h index 83f1bb990b4..3134894b393 100644 --- a/src/libs/utils/process.h +++ b/src/libs/utils/process.h @@ -30,6 +30,7 @@ class Environment; class DeviceProcessHooks; class ProcessInterface; class ProcessResultData; +class ProcessRunData; class QTCREATOR_UTILS_EXPORT Process final : public QObject { @@ -78,6 +79,21 @@ public: // ProcessSetupData related + void setRunData(const ProcessRunData &data); + ProcessRunData runData() const; + + void setCommand(const CommandLine &cmdLine); + const CommandLine &commandLine() const; + + void setWorkingDirectory(const FilePath &dir); + FilePath workingDirectory() const; + + void setEnvironment(const Environment &env); // Main process + const Environment &environment() const; + + void setControlEnvironment(const Environment &env); // Possible helper process (ssh on host etc) + const Environment &controlEnvironment() const; + void setProcessImpl(ProcessImpl processImpl); void setPtyData(const std::optional<Pty::Data> &data); @@ -90,18 +106,6 @@ public: void setProcessMode(ProcessMode processMode); ProcessMode processMode() const; - void setEnvironment(const Environment &env); // Main process - const Environment &environment() const; - - void setControlEnvironment(const Environment &env); // Possible helper process (ssh on host etc) - const Environment &controlEnvironment() const; - - void setCommand(const CommandLine &cmdLine); - const CommandLine &commandLine() const; - - void setWorkingDirectory(const FilePath &dir); - FilePath workingDirectory() const; - void setWriteData(const QByteArray &writeData); void setUseCtrlCStub(bool enabled); // release only @@ -111,8 +115,9 @@ public: bool isRunAsRoot() const; void setAbortOnMetaChars(bool abort); - QProcess::ProcessChannelMode processChannelMode() const; void setProcessChannelMode(QProcess::ProcessChannelMode mode); + QProcess::ProcessChannelMode processChannelMode() const; + void setStandardInputFile(const QString &inputFile); void setExtraData(const QString &key, const QVariant &value); @@ -134,9 +139,6 @@ public: // These (or some of them) may be potentially moved outside of the class. // Some of them could be aggregated in another public utils class. - // TODO: Unused currently? Should it serve as a compartment for contrary of remoteEnvironment? - static Environment systemEnvironmentForBinary(const FilePath &filePath); - static bool startDetached(const CommandLine &cmd, const FilePath &workingDirectory = {}, qint64 *pid = nullptr); @@ -207,7 +209,6 @@ class DeviceProcessHooks { public: std::function<ProcessInterface *(const FilePath &)> processImplHook; - std::function<Environment(const FilePath &)> systemEnvironmentForBinary; }; class QTCREATOR_UTILS_EXPORT ProcessTaskAdapter : public Tasking::TaskAdapter<Process> @@ -217,8 +218,8 @@ public: void start() final; }; +using ProcessTask = Tasking::CustomTask<ProcessTaskAdapter>; + } // namespace Utils -TASKING_DECLARE_TASK(ProcessTask, Utils::ProcessTaskAdapter); - #endif // UTILS_PROCESS_H diff --git a/src/libs/utils/processhandle.cpp b/src/libs/utils/processhandle.cpp index 06850457d37..c27e8a70db6 100644 --- a/src/libs/utils/processhandle.cpp +++ b/src/libs/utils/processhandle.cpp @@ -47,7 +47,7 @@ bool ProcessHandle::equals(const ProcessHandle &rhs) const return m_pid == rhs.m_pid; } -#ifndef Q_OS_OSX +#ifndef Q_OS_MACOS bool ProcessHandle::activate() { return false; diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h index a0460c5af3a..796972d3093 100644 --- a/src/libs/utils/processinterface.h +++ b/src/libs/utils/processinterface.h @@ -18,12 +18,19 @@ namespace Internal { class ProcessPrivate; } namespace Pty { +enum PtyInputFlag { + None = 0x0, + InputModeHidden = 0x1, +}; + using ResizeHandler = std::function<void(const QSize &)>; +using PtyInputFlagsChangeHandler = std::function<void(PtyInputFlag)>; class QTCREATOR_UTILS_EXPORT SharedData { public: ResizeHandler m_handler; + PtyInputFlagsChangeHandler m_inputFlagsChangedHandler; }; class QTCREATOR_UTILS_EXPORT Data @@ -32,6 +39,15 @@ public: Data() : m_data(new SharedData) {} void setResizeHandler(const ResizeHandler &handler) { m_data->m_handler = handler; } + void setPtyInputFlagsChangedHandler(const PtyInputFlagsChangeHandler &handler) + { + m_data->m_inputFlagsChangedHandler = handler; + } + + PtyInputFlagsChangeHandler ptyInputFlagsChangedHandler() const + { + return m_data->m_inputFlagsChangedHandler; + } QSize size() const { return m_size; } void resize(const QSize &size); @@ -43,6 +59,14 @@ private: } // namespace Pty +class QTCREATOR_UTILS_EXPORT ProcessRunData +{ +public: + Utils::CommandLine command; + Utils::FilePath workingDirectory; + Utils::Environment environment; +}; + class QTCREATOR_UTILS_EXPORT ProcessSetupData { public: diff --git a/src/libs/utils/processutils.cpp b/src/libs/utils/processutils.cpp index 62ea514b658..1c4f988fc51 100644 --- a/src/libs/utils/processutils.cpp +++ b/src/libs/utils/processutils.cpp @@ -105,6 +105,32 @@ BOOL CALLBACK sendInterruptMessageToAllWindowsOfProcess_enumWnd(HWND hwnd, LPARA ProcessHelper::ProcessHelper(QObject *parent) : QProcess(parent), m_processStartHandler(this) +{} + +void ProcessHelper::setLowPriority() +{ + m_lowPriority = true; + enableChildProcessModifier(); +} + +void ProcessHelper::setUnixTerminalDisabled() +{ +#if defined(Q_OS_UNIX) +# if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) + m_unixTerminalDisabled = true; + enableChildProcessModifier(); +# else + setUnixProcessParameters(QProcess::UnixProcessFlag::CreateNewSession); +# endif +#endif +} + +void ProcessHelper::setUseCtrlCStub(bool enabled) +{ + m_useCtrlCStub = enabled; +} + +void ProcessHelper::enableChildProcessModifier() { #if defined(Q_OS_UNIX) setChildProcessModifier([this] { @@ -115,18 +141,15 @@ ProcessHelper::ProcessHelper(QObject *parent) perror("Failed to set nice value"); } +# if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) // Disable terminal by becoming a session leader. if (m_unixTerminalDisabled) setsid(); +# endif }); #endif } -void ProcessHelper::setUseCtrlCStub(bool enabled) -{ - m_useCtrlCStub = enabled; -} - void ProcessHelper::terminateProcess() { #ifdef Q_OS_WIN diff --git a/src/libs/utils/processutils.h b/src/libs/utils/processutils.h index 89202c0daf2..c2a864db1fb 100644 --- a/src/libs/utils/processutils.h +++ b/src/libs/utils/processutils.h @@ -40,8 +40,8 @@ public: using QProcess::setErrorString; - void setLowPriority() { m_lowPriority = true; } - void setUnixTerminalDisabled() { m_unixTerminalDisabled = true; } + void setLowPriority(); + void setUnixTerminalDisabled(); void setUseCtrlCStub(bool enabled); // release only static void terminateProcess(QProcess *process); @@ -49,6 +49,7 @@ public: static void interruptPid(qint64 pid); private: + void enableChildProcessModifier(); void terminateProcess(); bool m_lowPriority = false; diff --git a/src/libs/utils/progressindicator.h b/src/libs/utils/progressindicator.h index df2e587a849..71732570e8a 100644 --- a/src/libs/utils/progressindicator.h +++ b/src/libs/utils/progressindicator.h @@ -55,7 +55,6 @@ private: class QTCREATOR_UTILS_EXPORT ProgressIndicator : public OverlayWidget { - Q_OBJECT public: explicit ProgressIndicator(ProgressIndicatorSize size, QWidget *parent = nullptr); diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp index 0b1a0a63557..b710e68723b 100644 --- a/src/libs/utils/projectintropage.cpp +++ b/src/libs/utils/projectintropage.cpp @@ -79,6 +79,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) : d->m_nameLineEdit = new Utils::FancyLineEdit(frame); d->m_pathChooser = new Utils::PathChooser(frame); + d->m_pathChooser->setObjectName("baseFolder"); // used by Squish d->m_pathChooser->setExpectedKind(PathChooser::Directory); d->m_pathChooser->setDisabled(d->m_forceSubProject); diff --git a/src/libs/utils/qtcsettings.cpp b/src/libs/utils/qtcsettings.cpp index 65e73615e2d..2fb036422c1 100644 --- a/src/libs/utils/qtcsettings.cpp +++ b/src/libs/utils/qtcsettings.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "qtcsettings.h" +#include "store.h" namespace Utils { @@ -10,7 +11,8 @@ namespace Utils { \inheaderfile utils/qtcsettings.h \inmodule QtCreator - \brief The QtcSettings class is an extension of the QSettings class. + \brief The QtcSettings class is an extension of the QSettings class + the uses Utils::Key instead of QString for keys. Use Utils::QtcSettings::setValueWithDefault() to write values with a default. @@ -29,4 +31,39 @@ namespace Utils { \sa QSettings::setValue() */ +void QtcSettings::beginGroup(const Key &prefix) +{ + QSettings::beginGroup(stringFromKey(prefix)); +} + +QVariant QtcSettings::value(const Key &key) const +{ + return QSettings::value(stringFromKey(key)); +} + +QVariant QtcSettings::value(const Key &key, const QVariant &def) const +{ + return QSettings::value(stringFromKey(key), def); +} + +void QtcSettings::setValue(const Key &key, const QVariant &value) +{ + QSettings::setValue(stringFromKey(key), mapEntryFromStoreEntry(value)); +} + +void QtcSettings::remove(const Key &key) +{ + QSettings::remove(stringFromKey(key)); +} + +bool QtcSettings::contains(const Key &key) const +{ + return QSettings::contains(stringFromKey(key)); +} + +KeyList QtcSettings::childKeys() const +{ + return keysFromStrings(QSettings::childKeys()); +} + } // namespace Utils diff --git a/src/libs/utils/qtcsettings.h b/src/libs/utils/qtcsettings.h index 13e5ef4c3cb..8a048b73482 100644 --- a/src/libs/utils/qtcsettings.h +++ b/src/libs/utils/qtcsettings.h @@ -5,59 +5,57 @@ #include "utils_global.h" +#include "store.h" + #include <QSettings> namespace Utils { -class QTCREATOR_UTILS_EXPORT QtcSettings : public QSettings +class QTCREATOR_UTILS_EXPORT QtcSettings : private QSettings { public: using QSettings::QSettings; + using QSettings::group; + using QSettings::endGroup; + using QSettings::allKeys; + using QSettings::fileName; + using QSettings::setParent; + using QSettings::sync; + using QSettings::beginReadArray; + using QSettings::beginWriteArray; + using QSettings::endArray; + using QSettings::setArrayIndex; + using QSettings::childGroups; + using QSettings::status; + using QSettings::clear; + + void beginGroup(const Key &prefix); + + QVariant value(const Key &key) const; + QVariant value(const Key &key, const QVariant &def) const; + void setValue(const Key &key, const QVariant &value); + void remove(const Key &key); + bool contains(const Key &key) const; + + KeyList childKeys() const; template<typename T> - void setValueWithDefault(const QString &key, const T &val, const T &defaultValue); - template<typename T> - void setValueWithDefault(const QString &key, const T &val); + void setValueWithDefault(const Key &key, const T &val, const T &defaultValue) + { + if (val == defaultValue) + remove(key); + else + setValue(key, val); + } template<typename T> - static void setValueWithDefault(QSettings *settings, - const QString &key, - const T &val, - const T &defaultValue); - template<typename T> - static void setValueWithDefault(QSettings *settings, const QString &key, const T &val); + void setValueWithDefault(const Key &key, const T &val) + { + if (val == T()) + remove(key); + else + setValue(key, val); + } }; -template<typename T> -void QtcSettings::setValueWithDefault(const QString &key, const T &val, const T &defaultValue) -{ - setValueWithDefault(this, key, val, defaultValue); -} - -template<typename T> -void QtcSettings::setValueWithDefault(const QString &key, const T &val) -{ - setValueWithDefault(this, key, val); -} - -template<typename T> -void QtcSettings::setValueWithDefault(QSettings *settings, - const QString &key, - const T &val, - const T &defaultValue) -{ - if (val == defaultValue) - settings->remove(key); - else - settings->setValue(key, QVariant::fromValue(val)); -} - -template<typename T> -void QtcSettings::setValueWithDefault(QSettings *settings, const QString &key, const T &val) -{ - if (val == T()) - settings->remove(key); - else - settings->setValue(key, QVariant::fromValue(val)); -} } // namespace Utils diff --git a/src/libs/utils/removefiledialog.h b/src/libs/utils/removefiledialog.h index 7fef70eb63f..a0a4299c253 100644 --- a/src/libs/utils/removefiledialog.h +++ b/src/libs/utils/removefiledialog.h @@ -17,8 +17,6 @@ class FilePath; class QTCREATOR_UTILS_EXPORT RemoveFileDialog : public QDialog { - Q_OBJECT - public: explicit RemoveFileDialog(const FilePath &filePath, QWidget *parent = nullptr); ~RemoveFileDialog() override; diff --git a/src/libs/utils/runextensions.cpp b/src/libs/utils/runextensions.cpp deleted file mode 100644 index efcd73a643e..00000000000 --- a/src/libs/utils/runextensions.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "runextensions.h" - -namespace Utils { -namespace Internal { - -RunnableThread::RunnableThread(QRunnable *runnable, QObject *parent) - : QThread(parent), - m_runnable(runnable) -{ -} - -void RunnableThread::run() -{ - m_runnable->run(); - if (m_runnable->autoDelete()) - delete m_runnable; -} - -} // Internal -} // Utils diff --git a/src/libs/utils/runextensions.h b/src/libs/utils/runextensions.h deleted file mode 100644 index 6505b12a9cf..00000000000 --- a/src/libs/utils/runextensions.h +++ /dev/null @@ -1,479 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "utils_global.h" - -#include "functiontraits.h" - -#include <QCoreApplication> -#include <QFuture> -#include <QFutureInterface> -#include <QFutureWatcher> -#include <QRunnable> -#include <QThread> -#include <QThreadPool> - -#include <functional> - -// hasCallOperator & Co must be outside of any namespace -// because of internal compiler error with MSVC2015 Update 2 - -using testCallOperatorYes = char; -using testCallOperatorNo = struct { char foo[2]; }; - -template<typename C> -static testCallOperatorYes testCallOperator(decltype(&C::operator())); - -template<typename> -static testCallOperatorNo testCallOperator(...); - -template<typename T> -struct hasCallOperator -{ - static const bool value = (sizeof(testCallOperator<T>(nullptr)) == sizeof(testCallOperatorYes)); -}; - -namespace Utils { - -namespace Internal { - -/* - resultType<F>::type - - Returns the type of results that would be reported by a callable of type F - when called through the runAsync methods. I.e. the ResultType in - - void f(QFutureInterface<Result> &fi, ...) - ResultType f(...) - - Returns void if F is not callable, and if F is a callable that does not take - a QFutureInterface& as its first parameter and returns void. -*/ - -template <typename Function> -struct resultType; - -template <typename Function, typename Arg> -struct resultTypeWithArgument; - -template <typename Function, int index, bool> -struct resultTypeTakesArguments; - -template <typename Function, bool> -struct resultTypeIsMemberFunction; - -template <typename Function, bool> -struct resultTypeIsFunctionLike; - -template <typename Function, bool> -struct resultTypeHasCallOperator; - -template <typename Function, typename ResultType> -struct resultTypeWithArgument<Function, QFutureInterface<ResultType>&> -{ - using type = ResultType; -}; - -template <typename Function, typename Arg> -struct resultTypeWithArgument -{ - using type = functionResult_t<Function>; -}; - -template <typename Function, int index> -struct resultTypeTakesArguments<Function, index, true> - : public resultTypeWithArgument<Function, typename functionTraits<Function>::template argument<index>::type> -{ -}; - -template <typename Function, int index> -struct resultTypeTakesArguments<Function, index, false> -{ - using type = functionResult_t<Function>; -}; - -template <typename Function> -struct resultTypeIsFunctionLike<Function, true> - : public resultTypeTakesArguments<Function, 0, (functionTraits<Function>::arity > 0)> -{ -}; - -template <typename Function> -struct resultTypeIsMemberFunction<Function, true> - : public resultTypeTakesArguments<Function, 1, (functionTraits<Function>::arity > 1)> -{ -}; - -template <typename Function> -struct resultTypeIsMemberFunction<Function, false> -{ - using type = void; -}; - -template <typename Function> -struct resultTypeIsFunctionLike<Function, false> - : public resultTypeIsMemberFunction<Function, std::is_member_function_pointer<Function>::value> -{ -}; - -template <typename Function> -struct resultTypeHasCallOperator<Function, false> - : public resultTypeIsFunctionLike<Function, std::is_function<std::remove_pointer_t<std::decay_t<Function>>>::value> -{ -}; - -template <typename Callable> -struct resultTypeHasCallOperator<Callable, true> - : public resultTypeTakesArguments<Callable, 0, (functionTraits<Callable>::arity > 0)> -{ -}; - -template <typename Function> -struct resultType - : public resultTypeHasCallOperator<Function, hasCallOperator<Function>::value> -{ -}; - -template <typename Function> -struct resultType<Function&> : public resultType<Function> -{ -}; - -template <typename Function> -struct resultType<const Function&> : public resultType<Function> -{ -}; - -template <typename Function> -struct resultType<Function &&> : public resultType<Function> -{ -}; - -template <typename Function> -struct resultType<std::reference_wrapper<Function>> : public resultType<Function> -{ -}; -template <typename Function> -struct resultType<std::reference_wrapper<const Function>> : public resultType<Function> -{ -}; - -/* - Callable object that wraps a member function pointer with the object it - will be called on. -*/ - -template <typename Function> -class MemberCallable; - -template <typename Result, typename Obj, typename... Args> -class MemberCallable<Result(Obj::*)(Args...) const> -{ -public: - MemberCallable(Result(Obj::* function)(Args...) const, const Obj *obj) - : m_function(function), - m_obj(obj) - { - } - - Result operator()(Args&&... args) const - { - return ((*m_obj).*m_function)(std::forward<Args>(args)...); - } - -private: - Result(Obj::* m_function)(Args...) const; - const Obj *m_obj; -}; - -template <typename Result, typename Obj, typename... Args> -class MemberCallable<Result(Obj::*)(Args...)> -{ -public: - MemberCallable(Result(Obj::* function)(Args...), Obj *obj) - : m_function(function), - m_obj(obj) - { - } - - Result operator()(Args&&... args) const - { - return ((*m_obj).*m_function)(std::forward<Args>(args)...); - } - -private: - Result(Obj::* m_function)(Args...); - Obj *m_obj; -}; - -/* - Helper functions for runAsync that run in the started thread. -*/ - -// void function that does not take QFutureInterface -template <typename ResultType, typename Function, typename... Args> -void runAsyncReturnVoidDispatch(std::true_type, QFutureInterface<ResultType> &, Function &&function, Args&&... args) -{ - function(std::forward<Args>(args)...); -} - -// non-void function that does not take QFutureInterface -template <typename ResultType, typename Function, typename... Args> -void runAsyncReturnVoidDispatch(std::false_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args) -{ - futureInterface.reportResult(function(std::forward<Args>(args)...)); -} - -// function that takes QFutureInterface -template <typename ResultType, typename Function, typename... Args> -void runAsyncQFutureInterfaceDispatch(std::true_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args) -{ - function(futureInterface, std::forward<Args>(args)...); -} - -// function that does not take QFutureInterface -template <typename ResultType, typename Function, typename... Args> -void runAsyncQFutureInterfaceDispatch(std::false_type, QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args) -{ - runAsyncReturnVoidDispatch(std::is_void<std::invoke_result_t<Function, Args...>>(), - futureInterface, std::forward<Function>(function), std::forward<Args>(args)...); -} - -// function, function pointer, or other callable object that is no member pointer -template <typename ResultType, typename Function, typename... Args, - typename = std::enable_if_t<!std::is_member_pointer<std::decay_t<Function>>::value> - > -void runAsyncMemberDispatch(QFutureInterface<ResultType> &futureInterface, Function &&function, Args&&... args) -{ - runAsyncQFutureInterfaceDispatch(functionTakesArgument<Function, 0, QFutureInterface<ResultType>&>(), - futureInterface, std::forward<Function>(function), std::forward<Args>(args)...); -} - -// Function = member function -template <typename ResultType, typename Function, typename Obj, typename... Args, - typename = std::enable_if_t<std::is_member_pointer<std::decay_t<Function>>::value> - > -void runAsyncMemberDispatch(QFutureInterface<ResultType> &futureInterface, Function &&function, Obj &&obj, Args&&... args) -{ - // Wrap member function with object into callable - runAsyncImpl(futureInterface, - MemberCallable<std::decay_t<Function>>(std::forward<Function>(function), std::forward<Obj>(obj)), - std::forward<Args>(args)...); -} - -// cref to function/callable -template <typename ResultType, typename Function, typename... Args> -void runAsyncImpl(QFutureInterface<ResultType> &futureInterface, - std::reference_wrapper<Function> functionWrapper, Args&&... args) -{ - runAsyncMemberDispatch(futureInterface, functionWrapper.get(), std::forward<Args>(args)...); -} - -// function/callable, no cref -template <typename ResultType, typename Function, typename... Args> -void runAsyncImpl(QFutureInterface<ResultType> &futureInterface, - Function &&function, Args&&... args) -{ - runAsyncMemberDispatch(futureInterface, std::forward<Function>(function), - std::forward<Args>(args)...); -} -/* - AsyncJob is a QRunnable that wraps a function with the - arguments that are passed to it when it is run in a thread. -*/ - -template <class T> -std::decay_t<T> -decayCopy(T&& v) -{ - return std::forward<T>(v); -} - -template <typename ResultType, typename Function, typename... Args> -class AsyncJob : public QRunnable -{ -public: - AsyncJob(Function &&function, Args&&... args) - // decay copy like std::thread - : data(decayCopy(std::forward<Function>(function)), decayCopy(std::forward<Args>(args))...) - { - // we need to report it as started even though it isn't yet, because someone might - // call waitForFinished on the future, which does _not_ block if the future is not started - futureInterface.setRunnable(this); - futureInterface.reportStarted(); - } - - ~AsyncJob() override - { - // QThreadPool can delete runnables even if they were never run (e.g. QThreadPool::clear). - // Since we reported them as started, we make sure that we always report them as finished. - // reportFinished only actually sends the signal if it wasn't already finished. - futureInterface.reportFinished(); - } - - QFuture<ResultType> future() { return futureInterface.future(); } - - void run() override - { - if (priority != QThread::InheritPriority) - if (QThread *thread = QThread::currentThread()) - if (thread != qApp->thread()) - thread->setPriority(priority); - if (futureInterface.isCanceled()) { - futureInterface.reportFinished(); - return; - } - runHelper(std::make_index_sequence<std::tuple_size<Data>::value>()); - } - - void setThreadPool(QThreadPool *pool) - { - futureInterface.setThreadPool(pool); - } - - void setThreadPriority(QThread::Priority p) - { - priority = p; - } - -private: - using Data = std::tuple<std::decay_t<Function>, std::decay_t<Args>...>; - - template <std::size_t... index> - void runHelper(std::index_sequence<index...>) - { - // invalidates data, which is moved into the call - runAsyncImpl(futureInterface, std::move(std::get<index>(data))...); - if (futureInterface.isPaused()) - futureInterface.waitForResume(); - futureInterface.reportFinished(); - } - - Data data; - QFutureInterface<ResultType> futureInterface; - QThread::Priority priority = QThread::InheritPriority; -}; - -class QTCREATOR_UTILS_EXPORT RunnableThread : public QThread -{ -public: - explicit RunnableThread(QRunnable *runnable, QObject *parent = nullptr); - -protected: - void run() override; - -private: - QRunnable *m_runnable; -}; - -template<typename Function, - typename... Args, - typename ResultType = typename Internal::resultType<Function>::type> -QFuture<ResultType> runAsync_internal(QThreadPool *pool, - QThread::Priority priority, - Function &&function, - Args &&... args) -{ - auto job = new Internal::AsyncJob<ResultType,Function,Args...> - (std::forward<Function>(function), std::forward<Args>(args)...); - job->setThreadPriority(priority); - QFuture<ResultType> future = job->future(); - if (pool) { - job->setThreadPool(pool); - pool->start(job); - } else { - auto thread = new Internal::RunnableThread(job); - thread->moveToThread(qApp->thread()); // make sure thread gets deleteLater on main thread - QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater); - thread->start(priority); - } - return future; -} - -} // Internal - -/*! - The interface of \c {runAsync} is similar to the std::thread constructor and \c {std::invoke}. - - The \a function argument can be a member function, - an object with \c {operator()} (with no overloads), - a \c {std::function}, lambda, function pointer or function reference. - The \a args are passed to the function call after they are copied/moved to the thread. - - The \a function can take a \c {QFutureInterface<ResultType>&} as its first argument, followed by - other custom arguments which need to be passed to this function. - If it does not take a \c {QFutureInterface<ResultType>&} as its first argument - and its return type is not void, the function call's result is reported to the QFuture. - If \a function is a (non-static) member function, the first argument in \a args is expected - to be the object that the function is called on. - - If a thread \a pool is given, the function is run there. Otherwise a new, independent thread - is started. - - \sa std::thread - \sa std::invoke - \sa QThreadPool - \sa QThread::Priority - */ -template <typename Function, typename... Args, - typename ResultType = typename Internal::resultType<Function>::type> -QFuture<ResultType> -runAsync(QThreadPool *pool, QThread::Priority priority, Function &&function, Args&&... args) -{ - return Internal::runAsync_internal(pool, - priority, - std::forward<Function>(function), - std::forward<Args>(args)...); -} - -/*! - Runs \a function with \a args in a new thread with given thread \a priority. - \sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...) - \sa QThread::Priority - */ -template <typename Function, typename... Args, - typename ResultType = typename Internal::resultType<Function>::type> -QFuture<ResultType> -runAsync(QThread::Priority priority, Function &&function, Args&&... args) -{ - return runAsync(static_cast<QThreadPool *>(nullptr), priority, - std::forward<Function>(function), std::forward<Args>(args)...); -} - -/*! - Runs \a function with \a args in a new thread with thread priority QThread::InheritPriority. - \sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...) - \sa QThread::Priority - */ -template <typename Function, typename... Args, - typename = std::enable_if_t< - !std::is_same<std::decay_t<Function>, QThreadPool>::value - && !std::is_same<std::decay_t<Function>, QThread::Priority>::value - >, - typename ResultType = typename Internal::resultType<Function>::type> -QFuture<ResultType> -runAsync(Function &&function, Args&&... args) -{ - return runAsync(static_cast<QThreadPool *>(nullptr), - QThread::InheritPriority, std::forward<Function>(function), - std::forward<Args>(args)...); -} - -/*! - Runs \a function with \a args in a thread \a pool with thread priority QThread::InheritPriority. - \sa runAsync(QThreadPool*,QThread::Priority,Function&&,Args&&...) - \sa QThread::Priority - */ -template <typename Function, typename... Args, - typename = std::enable_if_t<!std::is_same<std::decay_t<Function>, QThread::Priority>::value>, - typename ResultType = typename Internal::resultType<Function>::type> -QFuture<ResultType> -runAsync(QThreadPool *pool, Function &&function, Args&&... args) -{ - return runAsync(pool, QThread::InheritPriority, std::forward<Function>(function), - std::forward<Args>(args)...); -} - -} // namespace Utils diff --git a/src/libs/utils/savefile.cpp b/src/libs/utils/savefile.cpp index 4cf579ac0b9..a9d0cca3a40 100644 --- a/src/libs/utils/savefile.cpp +++ b/src/libs/utils/savefile.cpp @@ -111,7 +111,7 @@ bool SaveFile::commit() if (!result) { DWORD replaceErrorCode = GetLastError(); QString errorStr; - if (!QFile::exists(finalFileName)) { + if (!QFileInfo::exists(finalFileName)) { // Replace failed because finalFileName does not exist, try rename. if (!(result = rename(finalFileName))) errorStr = errorString(); @@ -148,7 +148,7 @@ bool SaveFile::commit() // Back up current file. // If it's opened by another application, the lock follows the move. - if (QFile::exists(finalFileName)) { + if (QFileInfo::exists(finalFileName)) { // Kill old backup. Might be useful if creator crashed before removing backup. QFile::remove(backupName); QFile finalFile(finalFileName); diff --git a/src/libs/utils/searchresultitem.h b/src/libs/utils/searchresultitem.h index ea9d0332d5a..766da19ffdc 100644 --- a/src/libs/utils/searchresultitem.h +++ b/src/libs/utils/searchresultitem.h @@ -5,9 +5,9 @@ #include "utils_global.h" -#include <utils/filepath.h> -#include <utils/hostosinfo.h> -#include <utils/textutils.h> +#include "filepath.h" +#include "hostosinfo.h" +#include "textutils.h" #include <QColor> #include <QHash> @@ -96,6 +96,11 @@ private: using SearchResultItems = QList<SearchResultItem>; +inline size_t qHash(const SearchResultItem &item) +{ + return item.mainRange().begin.line << 16 | item.mainRange().begin.column; +} + } // namespace Utils Q_DECLARE_METATYPE(Utils::SearchResultItem) diff --git a/src/libs/utils/settingsaccessor.cpp b/src/libs/utils/settingsaccessor.cpp index 275290674fd..1c9f30ecba5 100644 --- a/src/libs/utils/settingsaccessor.cpp +++ b/src/libs/utils/settingsaccessor.cpp @@ -46,9 +46,9 @@ SettingsAccessor::~SettingsAccessor() = default; /*! * Restore settings from disk and report any issues in a message box centered on \a parent. */ -QVariantMap SettingsAccessor::restoreSettings(QWidget *parent) const +Store SettingsAccessor::restoreSettings(QWidget *parent) const { - QTC_ASSERT(!m_baseFilePath.isEmpty(), return QVariantMap()); + QTC_ASSERT(!m_baseFilePath.isEmpty(), return Store()); return restoreSettings(m_baseFilePath, parent); } @@ -56,7 +56,7 @@ QVariantMap SettingsAccessor::restoreSettings(QWidget *parent) const /*! * Save \a data to disk and report any issues in a message box centered on \a parent. */ -bool SettingsAccessor::saveSettings(const QVariantMap &data, QWidget *parent) const +bool SettingsAccessor::saveSettings(const Store &data, QWidget *parent) const { QTC_CHECK(!m_docType.isEmpty()); QTC_CHECK(!m_applicationDisplayName.isEmpty()); @@ -83,14 +83,14 @@ SettingsAccessor::RestoreData SettingsAccessor::readData(const FilePath &path, Q * Store the \a data in \a path on disk. Do all the necessary preprocessing of the data. */ std::optional<SettingsAccessor::Issue> SettingsAccessor::writeData(const FilePath &path, - const QVariantMap &data, + const Store &data, QWidget *parent) const { Q_UNUSED(parent) return writeFile(path, prepareToWriteSettings(data)); } -QVariantMap SettingsAccessor::restoreSettings(const FilePath &settingsPath, QWidget *parent) const +Store SettingsAccessor::restoreSettings(const FilePath &settingsPath, QWidget *parent) const { QTC_CHECK(!m_docType.isEmpty()); QTC_CHECK(!m_applicationDisplayName.isEmpty()); @@ -99,7 +99,7 @@ QVariantMap SettingsAccessor::restoreSettings(const FilePath &settingsPath, QWid const ProceedInfo pi = result.hasIssue() ? reportIssues(result.issue.value(), result.path, parent) : ProceedInfo::Continue; - return pi == ProceedInfo::DiscardAndContinue ? QVariantMap() : result.data; + return pi == ProceedInfo::DiscardAndContinue ? Store() : result.data; } /*! @@ -116,7 +116,7 @@ SettingsAccessor::RestoreData SettingsAccessor::readFile(const FilePath &path) c .arg(path.toUserOutput()), Issue::Type::ERROR)); } - const QVariantMap data = reader.restoreValues(); + const Store data = reader.restoreValues(); if (!m_readOnly && path == m_baseFilePath) { if (!m_writer) m_writer = std::make_unique<PersistentSettingsWriter>(m_baseFilePath, m_docType); @@ -132,7 +132,7 @@ SettingsAccessor::RestoreData SettingsAccessor::readFile(const FilePath &path) c * This method does not do *any* processing of the file contents. */ std::optional<SettingsAccessor::Issue> SettingsAccessor::writeFile(const FilePath &path, - const QVariantMap &data) const + const Store &data) const { if (data.isEmpty()) { return Issue(Tr::tr("Failed to Write File"), @@ -175,7 +175,7 @@ SettingsAccessor::reportIssues(const Issue &issue, const FilePath &path, QWidget /*! * This method is called right after reading data from disk and modifies \a data. */ -QVariantMap SettingsAccessor::preprocessReadSettings(const QVariantMap &data) const +Store SettingsAccessor::preprocessReadSettings(const Store &data) const { return data; } @@ -183,7 +183,7 @@ QVariantMap SettingsAccessor::preprocessReadSettings(const QVariantMap &data) co /*! * This method is called right before writing data to disk and modifies \a data. */ -QVariantMap SettingsAccessor::prepareToWriteSettings(const QVariantMap &data) const +Store SettingsAccessor::prepareToWriteSettings(const Store &data) const { return data; } @@ -212,9 +212,9 @@ int BackUpStrategy::compare(const SettingsAccessor::RestoreData &data1, return 0; } -std::optional<FilePath> BackUpStrategy::backupName(const QVariantMap &oldData, +std::optional<FilePath> BackUpStrategy::backupName(const Store &oldData, const FilePath &path, - const QVariantMap &data) const + const Store &data) const { if (oldData == data) return std::nullopt; @@ -230,7 +230,7 @@ BackingUpSettingsAccessor::readData(const FilePath &path, QWidget *parent) const { const FilePaths fileList = readFileCandidates(path); if (fileList.isEmpty()) // No settings found at all. - return RestoreData(path, QVariantMap()); + return RestoreData(path, Store()); RestoreData result = bestReadFileData(fileList, parent); if (result.path.isEmpty()) @@ -253,7 +253,7 @@ BackingUpSettingsAccessor::readData(const FilePath &path, QWidget *parent) const } std::optional<SettingsAccessor::Issue> BackingUpSettingsAccessor::writeData(const FilePath &path, - const QVariantMap &data, + const Store &data, QWidget *parent) const { if (data.isEmpty()) @@ -290,7 +290,7 @@ BackingUpSettingsAccessor::bestReadFileData(const FilePaths &candidates, QWidget return bestMatch; } -void BackingUpSettingsAccessor::backupFile(const FilePath &path, const QVariantMap &data, +void BackingUpSettingsAccessor::backupFile(const FilePath &path, const Store &data, QWidget *parent) const { RestoreData oldSettings = SettingsAccessor::readData(path, parent); @@ -331,9 +331,9 @@ int VersionedBackUpStrategy::compare(const SettingsAccessor::RestoreData &data1, return -1; } -std::optional<FilePath> VersionedBackUpStrategy::backupName(const QVariantMap &oldData, +std::optional<FilePath> VersionedBackUpStrategy::backupName(const Store &oldData, const FilePath &path, - const QVariantMap &data) const + const Store &data) const { Q_UNUSED(data) FilePath backupName = path; @@ -374,7 +374,7 @@ QString VersionUpgrader::backupExtension() const /*! * Performs a simple renaming of the listed keys in \a changes recursively on \a map. */ -QVariantMap VersionUpgrader::renameKeys(const QList<Change> &changes, QVariantMap map) const +Store VersionUpgrader::renameKeys(const QList<Change> &changes, Store map) const { for (const Change &change : changes) { const auto oldSetting = map.constFind(change.first); @@ -384,11 +384,11 @@ QVariantMap VersionUpgrader::renameKeys(const QList<Change> &changes, QVariantMa } } - QVariantMap::iterator i = map.begin(); + Store::iterator i = map.begin(); while (i != map.end()) { QVariant v = i.value(); - if (v.type() == QVariant::Map) - i.value() = renameKeys(changes, v.toMap()); + if (Utils::isStore(v)) + i.value() = variantFromStore(renameKeys(changes, storeFromVariant(v))); ++i; } @@ -433,9 +433,9 @@ SettingsAccessor::RestoreData UpgradingSettingsAccessor::readData(const FilePath return upgradeSettings(BackingUpSettingsAccessor::readData(path, parent), currentVersion()); } -QVariantMap UpgradingSettingsAccessor::prepareToWriteSettings(const QVariantMap &data) const +Store UpgradingSettingsAccessor::prepareToWriteSettings(const Store &data) const { - QVariantMap tmp = BackingUpSettingsAccessor::prepareToWriteSettings(data); + Store tmp = BackingUpSettingsAccessor::prepareToWriteSettings(data); setVersionInMap(tmp,currentVersion()); if (!m_id.isEmpty()) @@ -639,7 +639,7 @@ MergingSettingsAccessor::mergeSettings(const SettingsAccessor::RestoreData &main = [this](const SettingsMergeData &global, const SettingsMergeData &local) { return merge(global, local); }; - const QVariantMap result = mergeQVariantMaps(main.data, secondary.data, mergeFunction).toMap(); + const Store result = storeFromVariant(mergeQVariantMaps(main.data, secondary.data, mergeFunction)); // Update from the base version to Creator's version. return RestoreData(main.path, postprocessMerge(main.data, secondary.data, result)); @@ -648,14 +648,14 @@ MergingSettingsAccessor::mergeSettings(const SettingsAccessor::RestoreData &main /*! * Returns true for housekeeping related keys. */ -bool MergingSettingsAccessor::isHouseKeepingKey(const QString &key) +bool MergingSettingsAccessor::isHouseKeepingKey(const Key &key) { return key == VERSION_KEY || key == ORIGINAL_VERSION_KEY || key == SETTINGS_ID_KEY; } -QVariantMap MergingSettingsAccessor::postprocessMerge(const QVariantMap &main, - const QVariantMap &secondary, - const QVariantMap &result) const +Store MergingSettingsAccessor::postprocessMerge(const Store &main, + const Store &secondary, + const Store &result) const { Q_UNUSED(main) Q_UNUSED(secondary) @@ -666,74 +666,75 @@ QVariantMap MergingSettingsAccessor::postprocessMerge(const QVariantMap &main, // Helper functions: // -------------------------------------------------------------------- -int versionFromMap(const QVariantMap &data) +int versionFromMap(const Store &data) { return data.value(VERSION_KEY, -1).toInt(); } -int originalVersionFromMap(const QVariantMap &data) +int originalVersionFromMap(const Store &data) { return data.value(ORIGINAL_VERSION_KEY, versionFromMap(data)).toInt(); } -QByteArray settingsIdFromMap(const QVariantMap &data) +QByteArray settingsIdFromMap(const Store &data) { return data.value(SETTINGS_ID_KEY).toByteArray(); } -void setOriginalVersionInMap(QVariantMap &data, int version) +void setOriginalVersionInMap(Store &data, int version) { data.insert(ORIGINAL_VERSION_KEY, version); } -void setVersionInMap(QVariantMap &data, int version) +void setVersionInMap(Store &data, int version) { data.insert(VERSION_KEY, version); } -void setSettingsIdInMap(QVariantMap &data, const QByteArray &id) +void setSettingsIdInMap(Store &data, const QByteArray &id) { data.insert(SETTINGS_ID_KEY, id); } -static QVariant mergeQVariantMapsRecursion(const QVariantMap &mainTree, const QVariantMap &secondaryTree, - const QString &keyPrefix, - const QVariantMap &mainSubtree, const QVariantMap &secondarySubtree, +static QVariant mergeQVariantMapsRecursion(const Store &mainTree, const Store &secondaryTree, + const Key &keyPrefix, + const Store &mainSubtree, const Store &secondarySubtree, const SettingsMergeFunction &merge) { - QVariantMap result; - const QList<QString> allKeys = filteredUnique(mainSubtree.keys() + secondarySubtree.keys()); + Store result; + const QList<Key> allKeys = filteredUnique(mainSubtree.keys() + secondarySubtree.keys()); - MergingSettingsAccessor::SettingsMergeData global = {mainTree, secondaryTree, QString()}; - MergingSettingsAccessor::SettingsMergeData local = {mainSubtree, secondarySubtree, QString()}; + MergingSettingsAccessor::SettingsMergeData global = {mainTree, secondaryTree, Key()}; + MergingSettingsAccessor::SettingsMergeData local = {mainSubtree, secondarySubtree, Key()}; - for (const QString &key : allKeys) { + for (const Key &key : allKeys) { global.key = keyPrefix + key; local.key = key; - std::optional<QPair<QString, QVariant>> mergeResult = merge(global, local); + std::optional<QPair<Key, QVariant>> mergeResult = merge(global, local); if (!mergeResult) continue; - QPair<QString, QVariant> kv = mergeResult.value(); + QPair<Key, QVariant> kv = mergeResult.value(); - if (kv.second.type() == QVariant::Map) { - const QString newKeyPrefix = keyPrefix + kv.first + '/'; + if (Utils::isStore(kv.second)) { + const Key newKeyPrefix = keyPrefix + kv.first + '/'; kv.second = mergeQVariantMapsRecursion(mainTree, secondaryTree, newKeyPrefix, - kv.second.toMap(), secondarySubtree.value(kv.first) - .toMap(), merge); + storeFromVariant(kv.second), + storeFromVariant(secondarySubtree.value(kv.first)), + merge); } if (!kv.second.isNull()) result.insert(kv.first, kv.second); } - return result; + return variantFromStore(result); } -QVariant mergeQVariantMaps(const QVariantMap &mainTree, const QVariantMap &secondaryTree, +QVariant mergeQVariantMaps(const Store &mainTree, const Store &secondaryTree, const SettingsMergeFunction &merge) { - return mergeQVariantMapsRecursion(mainTree, secondaryTree, QString(), + return mergeQVariantMapsRecursion(mainTree, secondaryTree, Key(), mainTree, secondaryTree, merge); } diff --git a/src/libs/utils/settingsaccessor.h b/src/libs/utils/settingsaccessor.h index 683743ac62f..b882fbaeb78 100644 --- a/src/libs/utils/settingsaccessor.h +++ b/src/libs/utils/settingsaccessor.h @@ -6,10 +6,10 @@ #include "utils_global.h" #include "filepath.h" +#include "store.h" #include <QHash> #include <QMessageBox> -#include <QVariantMap> #include <memory> #include <optional> @@ -20,28 +20,28 @@ namespace Utils { // Helper: // ----------------------------------------------------------------------------- -QTCREATOR_UTILS_EXPORT int versionFromMap(const QVariantMap &data); -QTCREATOR_UTILS_EXPORT int originalVersionFromMap(const QVariantMap &data); -QTCREATOR_UTILS_EXPORT QByteArray settingsIdFromMap(const QVariantMap &data); +QTCREATOR_UTILS_EXPORT int versionFromMap(const Store &data); +QTCREATOR_UTILS_EXPORT int originalVersionFromMap(const Store &data); +QTCREATOR_UTILS_EXPORT QByteArray settingsIdFromMap(const Store &data); -QTCREATOR_UTILS_EXPORT void setVersionInMap(QVariantMap &data, int version); -QTCREATOR_UTILS_EXPORT void setOriginalVersionInMap(QVariantMap &data, int version); -QTCREATOR_UTILS_EXPORT void setSettingsIdInMap(QVariantMap &data, const QByteArray &id); +QTCREATOR_UTILS_EXPORT void setVersionInMap(Store &data, int version); +QTCREATOR_UTILS_EXPORT void setOriginalVersionInMap(Store &data, int version); +QTCREATOR_UTILS_EXPORT void setSettingsIdInMap(Store &data, const QByteArray &id); // -------------------------------------------------------------------- // Helpers: // -------------------------------------------------------------------- -QTCREATOR_UTILS_EXPORT int versionFromMap(const QVariantMap &data); -QTCREATOR_UTILS_EXPORT int originalVersionFromMap(const QVariantMap &data); -QTCREATOR_UTILS_EXPORT QByteArray settingsIdFromMap(const QVariantMap &data); +QTCREATOR_UTILS_EXPORT int versionFromMap(const Store &data); +QTCREATOR_UTILS_EXPORT int originalVersionFromMap(const Store &data); +QTCREATOR_UTILS_EXPORT QByteArray settingsIdFromMap(const Store &data); -QTCREATOR_UTILS_EXPORT void setVersionInMap(QVariantMap &data, int version); -QTCREATOR_UTILS_EXPORT void setOriginalVersionInMap(QVariantMap &data, int version); -QTCREATOR_UTILS_EXPORT void setSettingsIdInMap(QVariantMap &data, const QByteArray &id); +QTCREATOR_UTILS_EXPORT void setVersionInMap(Store &data, int version); +QTCREATOR_UTILS_EXPORT void setOriginalVersionInMap(Store &data, int version); +QTCREATOR_UTILS_EXPORT void setSettingsIdInMap(Store &data, const QByteArray &id); class PersistentSettingsWriter; -using SettingsMergeResult = std::optional<QPair<QString, QVariant>>; +using SettingsMergeResult = std::optional<QPair<Utils::Key, QVariant>>; // -------------------------------------------------------------------- // SettingsAccessor: @@ -76,7 +76,7 @@ public: class RestoreData { public: RestoreData() = default; - RestoreData(const FilePath &path, const QVariantMap &data) : path{path}, data{data} { } + RestoreData(const FilePath &path, const Store &data) : path{path}, data{data} { } RestoreData(const QString &title, const QString &message, const Issue::Type type) : RestoreData(Issue(title, message, type)) { } @@ -87,12 +87,12 @@ public: bool hasWarning() const { return hasIssue() && issue.value().type == Issue::Type::WARNING; } FilePath path; - QVariantMap data; + Store data; std::optional<Issue> issue; }; - QVariantMap restoreSettings(QWidget *parent) const; - bool saveSettings(const QVariantMap &data, QWidget *parent) const; + Store restoreSettings(QWidget *parent) const; + bool saveSettings(const Store &data, QWidget *parent) const; void setBaseFilePath(const FilePath &baseFilePath) { m_baseFilePath = baseFilePath; } void setReadOnly() { m_readOnly = true; } @@ -100,7 +100,7 @@ public: virtual RestoreData readData(const FilePath &path, QWidget *parent) const; virtual std::optional<Issue> writeData(const FilePath &path, - const QVariantMap &data, + const Store &data, QWidget *parent) const; void setDocType(const QString &docType) { m_docType = docType; } @@ -108,14 +108,14 @@ public: protected: // Report errors: - QVariantMap restoreSettings(const FilePath &settingsPath, QWidget *parent) const; + Store restoreSettings(const FilePath &settingsPath, QWidget *parent) const; static ProceedInfo reportIssues(const Issue &issue, const FilePath &path, QWidget *parent); - virtual QVariantMap preprocessReadSettings(const QVariantMap &data) const; - virtual QVariantMap prepareToWriteSettings(const QVariantMap &data) const; + virtual Store preprocessReadSettings(const Store &data) const; + virtual Store prepareToWriteSettings(const Store &data) const; virtual RestoreData readFile(const FilePath &path) const; - virtual std::optional<Issue> writeFile(const FilePath &path, const QVariantMap &data) const; + virtual std::optional<Issue> writeFile(const FilePath &path, const Store &data) const; QString m_docType; QString m_applicationDisplayName; @@ -141,9 +141,9 @@ public: virtual int compare(const SettingsAccessor::RestoreData &data1, const SettingsAccessor::RestoreData &data2) const; - virtual std::optional<FilePath> backupName(const QVariantMap &oldData, + virtual std::optional<FilePath> backupName(const Store &oldData, const FilePath &path, - const QVariantMap &data) const; + const Store &data) const; }; class QTCREATOR_UTILS_EXPORT BackingUpSettingsAccessor : public SettingsAccessor @@ -153,7 +153,7 @@ public: RestoreData readData(const FilePath &path, QWidget *parent) const override; std::optional<Issue> writeData(const FilePath &path, - const QVariantMap &data, + const Store &data, QWidget *parent) const override; BackUpStrategy *strategy() const { return m_strategy.get(); } @@ -162,7 +162,7 @@ public: private: FilePaths readFileCandidates(const FilePath &path) const; RestoreData bestReadFileData(const FilePaths &candidates, QWidget *parent) const; - void backupFile(const FilePath &path, const QVariantMap &data, QWidget *parent) const; + void backupFile(const FilePath &path, const Store &data, QWidget *parent) const; std::unique_ptr<BackUpStrategy> m_strategy; }; @@ -183,9 +183,9 @@ public: int compare(const SettingsAccessor::RestoreData &data1, const SettingsAccessor::RestoreData &data2) const override; - std::optional<FilePath> backupName(const QVariantMap &oldData, + std::optional<FilePath> backupName(const Store &oldData, const FilePath &path, - const QVariantMap &data) const override; + const Store &data) const override; const UpgradingSettingsAccessor *accessor() const { return m_accessor; } @@ -193,7 +193,7 @@ protected: const UpgradingSettingsAccessor *m_accessor = nullptr; }; -// Handles updating a QVariantMap from version() to version() + 1 +// Handles updating a Store from version() to version() + 1 class QTCREATOR_UTILS_EXPORT VersionUpgrader { public: @@ -203,11 +203,11 @@ public: int version() const; QString backupExtension() const; - virtual QVariantMap upgrade(const QVariantMap &data) = 0; + virtual Store upgrade(const Store &data) = 0; protected: - using Change = QPair<QLatin1String, QLatin1String>; - QVariantMap renameKeys(const QList<Change> &changes, QVariantMap map) const; + using Change = QPair<Key, Key>; + Store renameKeys(const QList<Change> &changes, Store map) const; private: const int m_version; @@ -233,7 +233,7 @@ public: RestoreData readData(const FilePath &path, QWidget *parent) const override; protected: - QVariantMap prepareToWriteSettings(const QVariantMap &data) const override; + Store prepareToWriteSettings(const Store &data) const override; void setSettingsId(const QByteArray &id) { m_id = id; } @@ -254,9 +254,9 @@ class QTCREATOR_UTILS_EXPORT MergingSettingsAccessor : public UpgradingSettingsA { public: struct SettingsMergeData { - QVariantMap main; - QVariantMap secondary; - QString key; + Store main; + Store secondary; + Key key; }; MergingSettingsAccessor(); @@ -271,10 +271,10 @@ protected: virtual SettingsMergeResult merge(const SettingsMergeData &global, const SettingsMergeData &local) const = 0; - static bool isHouseKeepingKey(const QString &key); + static bool isHouseKeepingKey(const Key &key); - virtual QVariantMap postprocessMerge(const QVariantMap &main, const QVariantMap &secondary, - const QVariantMap &result) const; + virtual Store postprocessMerge(const Store &main, const Store &secondary, + const Store &result) const; private: std::unique_ptr<SettingsAccessor> m_secondaryAccessor; @@ -282,7 +282,7 @@ private: using SettingsMergeFunction = std::function<SettingsMergeResult(const MergingSettingsAccessor::SettingsMergeData &, const MergingSettingsAccessor::SettingsMergeData &)>; -QTCREATOR_UTILS_EXPORT QVariant mergeQVariantMaps(const QVariantMap &mainTree, const QVariantMap &secondaryTree, +QTCREATOR_UTILS_EXPORT QVariant mergeQVariantMaps(const Store &mainTree, const Store &secondaryTree, const SettingsMergeFunction &merge); } // namespace Utils diff --git a/src/libs/utils/settingsutils.h b/src/libs/utils/settingsutils.h deleted file mode 100644 index 2feb62380d1..00000000000 --- a/src/libs/utils/settingsutils.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QSettings> -#include <QString> -#include <QStringList> -#include <QVariant> - -namespace Utils { - -template <class SettingsClassT> -void fromSettings(const QString &postFix, - const QString &category, - QSettings *s, - SettingsClassT *obj) -{ - QVariantMap map; - s->beginGroup(category + postFix); - const QStringList keys = s->allKeys(); - for (const QString &key : keys) - map.insert(key, s->value(key)); - s->endGroup(); - obj->fromMap(map); -} - -template <class SettingsClassT> -void toSettings(const QString &postFix, - const QString &category, - QSettings *s, - const SettingsClassT *obj) -{ - QString group = postFix; - if (!category.isEmpty()) - group.insert(0, category); - const QVariantMap map = obj->toMap(); - - s->beginGroup(group); - for (auto it = map.constBegin(), end = map.constEnd(); it != end; ++it) - s->setValue(it.key(), it.value()); - s->endGroup(); -} - -} // Utils diff --git a/src/libs/utils/statuslabel.h b/src/libs/utils/statuslabel.h index 091c186bbf0..5fdf852a9e8 100644 --- a/src/libs/utils/statuslabel.h +++ b/src/libs/utils/statuslabel.h @@ -15,11 +15,9 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT StatusLabel : public QLabel { - Q_OBJECT public: explicit StatusLabel(QWidget *parent = nullptr); -public slots: void showStatusMessage(const QString &message, int timeoutMS = 5000); void clearStatusMessage(); diff --git a/src/libs/utils/store.cpp b/src/libs/utils/store.cpp new file mode 100644 index 00000000000..37f939f5a8c --- /dev/null +++ b/src/libs/utils/store.cpp @@ -0,0 +1,193 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "store.h" + +#include "algorithm.h" +#include "qtcassert.h" +#include "qtcsettings.h" + +#include <QJsonDocument> +#include <QJsonParseError> + +namespace Utils { + +KeyList keysFromStrings(const QStringList &list) +{ + return transform(list, &keyFromString); +} + +QStringList stringsFromKeys(const KeyList &list) +{ + return transform(list, &stringFromKey); +} + +QVariant variantFromStore(const Store &store) +{ + return QVariant::fromValue(store); +} + +Store storeFromVariant(const QVariant &value) +{ + if (value.typeId() == qMetaTypeId<Store>()) + return value.value<Store>(); + + if (value.typeId() == QMetaType::QVariantMap) + return storeFromMap(value.toMap()); + + if (value.typeId() == qMetaTypeId<OldStore>()) + return storeFromMap(value.toMap()); + + if (!value.isValid()) + return {}; + + QTC_CHECK(false); + return Store(); +} + +static QVariantList storeListFromMapList(const QVariantList &mapList); +static QVariantList mapListFromStoreList(const QVariantList &storeList); + +QVariant storeEntryFromMapEntry(const QVariant &mapEntry) +{ + if (mapEntry.type() == QVariant::Map) + return QVariant::fromValue(storeFromMap(mapEntry.toMap())); + + if (mapEntry.type() == QVariant::List) + return QVariant::fromValue(storeListFromMapList(mapEntry.toList())); + + return mapEntry; +} + +QVariant mapEntryFromStoreEntry(const QVariant &storeEntry) +{ + if (storeEntry.metaType() == QMetaType::fromType<Store>()) + return QVariant::fromValue(mapFromStore(storeEntry.value<Store>())); + + if (storeEntry.type() == QVariant::List) + return QVariant::fromValue(mapListFromStoreList(storeEntry.toList())); + + return storeEntry; +} + +static QVariantList storeListFromMapList(const QVariantList &mapList) +{ + QVariantList storeList; + + for (const auto &mapEntry : mapList) + storeList.append(storeEntryFromMapEntry(mapEntry)); + + return storeList; +} + +static QVariantList mapListFromStoreList(const QVariantList &storeList) +{ + QVariantList mapList; + + for (const QVariant &storeEntry : storeList) + mapList.append(mapEntryFromStoreEntry(storeEntry)); + + return mapList; +} + +Store storeFromMap(const QVariantMap &map) +{ + Store store; + + for (auto it = map.begin(); it != map.end(); ++it) + store.insert(keyFromString(it.key()), storeEntryFromMapEntry(it.value())); + + return store; +} + +QVariantMap mapFromStore(const Store &store) +{ + QVariantMap map; + + for (auto it = store.begin(); it != store.end(); ++it) + map.insert(stringFromKey(it.key()), mapEntryFromStoreEntry(it.value())); + + return map; +} + +bool isStore(const QVariant &value) +{ + const int typeId = value.typeId(); + return typeId == QMetaType::QVariantMap || typeId == qMetaTypeId<Store>(); +} + +Key::Key(const char *key, size_t n) + : data(QByteArray::fromRawData(key, n)) +{} + +Key::Key(const Key &base, int number) + : data(base.data + QByteArray::number(number)) +{} + +Key::~Key() +{} + +const QByteArrayView Key::view() const +{ + return data; +} + +const QByteArray &Key::toByteArray() const +{ + return data; +} + +Key numberedKey(const Key &key, int number) +{ + return Key(key, number); +} + +Key keyFromString(const QString &str) +{ + return str.toUtf8(); +} + +QString stringFromKey(const Key &key) +{ + return QString::fromLatin1(key.view()); +} + +expected_str<Store> storeFromJson(const QByteArray &json) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(json, &error); + if (error.error != QJsonParseError::NoError) + return make_unexpected(error.errorString()); + + if (!doc.isObject()) + return make_unexpected(QString("Not a valid JSON object.")); + + return storeFromMap(doc.toVariant().toMap()); +} + +QByteArray jsonFromStore(const Store &store) +{ + QJsonDocument doc = QJsonDocument::fromVariant(mapFromStore(store)); + return doc.toJson(); +} + +Store storeFromSettings(const Key &groupKey, QtcSettings *s) +{ + Store store; + s->beginGroup(groupKey); + const KeyList keys = keysFromStrings(s->allKeys()); + for (const Key &key : keys) + store.insert(key, storeEntryFromMapEntry(s->value(key))); + s->endGroup(); + return store; +} + +void storeToSettings(const Key &groupKey, QtcSettings *s, const Store &store) +{ + s->beginGroup(groupKey); + for (auto it = store.constBegin(), end = store.constEnd(); it != end; ++it) + s->setValue(it.key(), mapEntryFromStoreEntry(it.value())); + s->endGroup(); +} + +} // Utils diff --git a/src/libs/utils/store.h b/src/libs/utils/store.h new file mode 100644 index 00000000000..df45cc8e9ce --- /dev/null +++ b/src/libs/utils/store.h @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "expected.h" +#include "storekey.h" + +#include <QMap> +#include <QVariant> + +namespace Utils { + +class QtcSettings; + +using KeyList = QList<Key>; + +using Store = QMap<Key, QVariant>; +using OldStore = QMap<QByteArray, QVariant>; + +QTCREATOR_UTILS_EXPORT KeyList keysFromStrings(const QStringList &list); +QTCREATOR_UTILS_EXPORT QStringList stringsFromKeys(const KeyList &list); + +QTCREATOR_UTILS_EXPORT QVariant variantFromStore(const Store &store); +QTCREATOR_UTILS_EXPORT Store storeFromVariant(const QVariant &value); + +QTCREATOR_UTILS_EXPORT Store storeFromMap(const QVariantMap &map); +QTCREATOR_UTILS_EXPORT QVariantMap mapFromStore(const Store &store); + +QTCREATOR_UTILS_EXPORT bool isStore(const QVariant &value); + +QTCREATOR_UTILS_EXPORT Key numberedKey(const Key &key, int number); + +QTCREATOR_UTILS_EXPORT expected_str<Store> storeFromJson(const QByteArray &json); +QTCREATOR_UTILS_EXPORT QByteArray jsonFromStore(const Store &store); + +// These recursively change type. +QTCREATOR_UTILS_EXPORT QVariant storeEntryFromMapEntry(const QVariant &value); +QTCREATOR_UTILS_EXPORT QVariant mapEntryFromStoreEntry(const QVariant &value); + +// Don't use in new code. +QTCREATOR_UTILS_EXPORT Store storeFromSettings(const Key &groupKey, QtcSettings *s); +QTCREATOR_UTILS_EXPORT void storeToSettings(const Key &groupKey, QtcSettings *s, const Store &store); + +} // Utils + +Q_DECLARE_METATYPE(Utils::Store) +Q_DECLARE_METATYPE(Utils::OldStore) diff --git a/src/libs/utils/storekey.h b/src/libs/utils/storekey.h new file mode 100644 index 00000000000..68b433d05a6 --- /dev/null +++ b/src/libs/utils/storekey.h @@ -0,0 +1,69 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include <QByteArrayView> +#include <QString> +#include <QHashFunctions> + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT Key +{ +public: + Key() = default; + Key(const Key &) = default; + Key(Key &&) = default; + + Key(const QByteArray &key) : data(key) {} + + template <int N> + Key(const char (&key)[N]) : data(key) {} + + // FIXME: + // The following is wanted, but not used yet due to unclear ASAN report. + // template <int N> + // Key(const char (&key)[N]) : Key(key, strlen(key)) {} + + Key(const char *key, size_t n); + + Key(const Key &base, int number); + ~Key(); + + Key &operator=(const Key &) = default; + Key &operator=(Key &&) = default; + + const QByteArrayView view() const; + const QByteArray &toByteArray() const; + QByteArrayView operator()() const { return data; } + + bool isEmpty() const { return data.isEmpty(); } + void clear() { data.clear(); } + + friend bool operator<(const Key &a, const Key &b) { return a.data < b.data; } + friend bool operator==(const Key &a, const Key &b) { return a.data == b.data; } + + friend Key operator+(const Key &a, const Key &b) + { + return Key(a.data + b.data); + } + friend Key operator+(const Key &a, char b) + { + return Key(a.data + b); + } + friend size_t qHash(const Key &key, size_t seed = 0) + { + return qHash(key.data, seed); + } + +private: + QByteArray data; +}; + +QTCREATOR_UTILS_EXPORT Key keyFromString(const QString &str); +QTCREATOR_UTILS_EXPORT QString stringFromKey(const Key &key); + +} // Utils diff --git a/src/libs/utils/stringtable.h b/src/libs/utils/stringtable.h index 1394b1c853d..eedfe513552 100644 --- a/src/libs/utils/stringtable.h +++ b/src/libs/utils/stringtable.h @@ -3,7 +3,7 @@ #pragma once -#include <utils/utils_global.h> +#include "utils_global.h" namespace Utils::StringTable { diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index a33a08e3d99..c3b6c596905 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -5,6 +5,7 @@ #include "filepath.h" #include "qtcassert.h" +#include "stylehelper.h" #include "theme/theme.h" #include "utilstr.h" @@ -13,11 +14,13 @@ #include <QClipboard> #endif +#include <QCollator> #include <QDir> #include <QFontMetrics> #include <QJsonArray> #include <QJsonValue> #include <QLocale> +#include <QPalette> #include <QRegularExpression> #include <QSet> #include <QTextDocument> @@ -323,10 +326,18 @@ QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line int caseFriendlyCompare(const QString &a, const QString &b) { - int result = a.compare(b, Qt::CaseInsensitive); + static const auto makeCollator = [](Qt::CaseSensitivity caseSensitivity) { + QCollator collator; + collator.setNumericMode(true); + collator.setCaseSensitivity(caseSensitivity); + return collator; + }; + static const QCollator insensitiveCollator = makeCollator(Qt::CaseInsensitive); + const int result = insensitiveCollator.compare(a, b); if (result != 0) return result; - return a.compare(b, Qt::CaseSensitive); + static const QCollator sensitiveCollator = makeCollator(Qt::CaseSensitive); + return sensitiveCollator.compare(a, b); } QString quoteAmpersands(const QString &text) @@ -573,16 +584,28 @@ QTCREATOR_UTILS_EXPORT int endOfNextWord(const QString &string, int position) MarkdownHighlighter::MarkdownHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) , h2Brush(Qt::NoBrush) + , m_codeBgBrush(Qt::NoBrush) { parent->setIndentWidth(30); // default value is 40 } +QBrush MarkdownHighlighter::codeBgBrush() +{ + if (m_codeBgBrush.style() == Qt::NoBrush) { + m_codeBgBrush = StyleHelper::mergedColors(QGuiApplication::palette().color(QPalette::Text), + QGuiApplication::palette().color(QPalette::Base), + 10); + } + return m_codeBgBrush; +} + void MarkdownHighlighter::highlightBlock(const QString &text) { if (text.isEmpty()) return; - QTextBlockFormat fmt = currentBlock().blockFormat(); + const QTextBlock block = currentBlock(); + QTextBlockFormat fmt = block.blockFormat(); QTextCursor cur(currentBlock()); if (fmt.hasProperty(QTextFormat::HeadingLevel)) { fmt.setTopMargin(10); @@ -610,7 +633,8 @@ void MarkdownHighlighter::highlightBlock(const QString &text) } cur.setBlockFormat(fmt); } else if (fmt.hasProperty(QTextFormat::BlockCodeLanguage) && fmt.indent() == 0) { - // set identation for code blocks + // set identation and background for code blocks + fmt.setBackground(codeBgBrush()); fmt.setIndent(1); cur.setBlockFormat(fmt); } @@ -624,6 +648,16 @@ void MarkdownHighlighter::highlightBlock(const QString &text) list->setFormat(listFmt); } } + + // background color of code + for (auto it = block.begin(); it != block.end(); ++it) { + const QTextFragment fragment = it.fragment(); + QTextCharFormat fmt = fragment.charFormat(); + if (fmt.fontFixedPitch()) { + fmt.setBackground(codeBgBrush()); + setFormat(fragment.position() - block.position(), fragment.length(), fmt); + } + } } } // namespace Utils diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 3bab6110cf3..b27a2efc6f0 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -130,7 +130,10 @@ public: void highlightBlock(const QString &text); private: + QBrush codeBgBrush(); + QBrush h2Brush; + QBrush m_codeBgBrush; }; } // namespace Utils diff --git a/src/libs/utils/styleanimator.h b/src/libs/utils/styleanimator.h index 1cb0f67cc11..6745890d148 100644 --- a/src/libs/utils/styleanimator.h +++ b/src/libs/utils/styleanimator.h @@ -64,8 +64,6 @@ public : class QTCREATOR_UTILS_EXPORT StyleAnimator : public QObject { - Q_OBJECT - public: StyleAnimator(QObject *parent = nullptr) : QObject(parent) {} diff --git a/src/libs/utils/styledbar.h b/src/libs/utils/styledbar.h index c6c41575cfb..654a8f216e6 100644 --- a/src/libs/utils/styledbar.h +++ b/src/libs/utils/styledbar.h @@ -11,7 +11,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT StyledBar : public QWidget { - Q_OBJECT public: StyledBar(QWidget *parent = nullptr); void setSingleRow(bool singleRow); @@ -26,9 +25,9 @@ protected: class QTCREATOR_UTILS_EXPORT StyledSeparator : public QWidget { - Q_OBJECT public: StyledSeparator(QWidget *parent = nullptr); + protected: void paintEvent(QPaintEvent *event) override; }; diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index cc860e715a9..b93481ef9d4 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -712,6 +712,12 @@ bool StyleHelper::isQDSTheme() return creatorTheme() ? creatorTheme()->flag(Theme::QDSTheme) : false; } +Qt::HighDpiScaleFactorRoundingPolicy StyleHelper::defaultHighDpiScaleFactorRoundingPolicy() +{ + return HostOsInfo::isMacHost() ? Qt::HighDpiScaleFactorRoundingPolicy::Unset + : Qt::HighDpiScaleFactorRoundingPolicy::Round; +} + QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> ¶meters) { QFontDatabase a; @@ -847,7 +853,7 @@ QString StyleHelper::dpiSpecificImageFile(const QString &fileName) if (qApp->devicePixelRatio() > 1.0) { const QString atDprfileName = imageFileWithResolution(fileName, qRound(qApp->devicePixelRatio())); - if (QFile::exists(atDprfileName)) + if (QFileInfo::exists(atDprfileName)) return atDprfileName; } return fileName; @@ -867,7 +873,7 @@ QList<int> StyleHelper::availableImageResolutions(const QString &fileName) QList<int> result; const int maxResolutions = qApp->devicePixelRatio(); for (int i = 1; i <= maxResolutions; ++i) - if (QFile::exists(imageFileWithResolution(fileName, i))) + if (QFileInfo::exists(imageFileWithResolution(fileName, i))) result.append(i); return result; } diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index ffc03c1a29c..ac5e53a4bd4 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -108,6 +108,9 @@ QTCREATOR_UTILS_EXPORT void setPanelWidgetSingleRow(QWidget *widget, bool value QTCREATOR_UTILS_EXPORT bool isQDSTheme(); +QTCREATOR_UTILS_EXPORT + Qt::HighDpiScaleFactorRoundingPolicy defaultHighDpiScaleFactorRoundingPolicy(); + class IconFontHelper { public: diff --git a/src/libs/utils/terminalcommand.cpp b/src/libs/utils/terminalcommand.cpp index bb1492515f3..0d6ab280ede 100644 --- a/src/libs/utils/terminalcommand.cpp +++ b/src/libs/utils/terminalcommand.cpp @@ -6,14 +6,11 @@ #include "algorithm.h" #include "environment.h" #include "hostosinfo.h" - -#include <QCoreApplication> -#include <QFileInfo> -#include <QSettings> +#include "qtcsettings.h" namespace Utils { -static QSettings *s_settings = nullptr; +static QtcSettings *s_settings = nullptr; TerminalCommand::TerminalCommand(const FilePath &command, const QString &openArgs, const QString &executeArgs, bool needsQuotes) @@ -40,12 +37,12 @@ bool TerminalCommand::operator<(const TerminalCommand &other) const return command < other.command; } -void TerminalCommand::setSettings(QSettings *settings) +void TerminalCommand::setSettings(QtcSettings *settings) { s_settings = settings; } -Q_GLOBAL_STATIC_WITH_ARGS(const QVector<TerminalCommand>, knownTerminals, ( +Q_GLOBAL_STATIC_WITH_ARGS(const QList<TerminalCommand>, knownTerminals, ( { {"x-terminal-emulator", "", "-e"}, {"xdg-terminal", "", "", true}, @@ -82,9 +79,9 @@ TerminalCommand TerminalCommand::defaultTerminalEmulator() return defaultTerm; } -QVector<TerminalCommand> TerminalCommand::availableTerminalEmulators() +QList<TerminalCommand> TerminalCommand::availableTerminalEmulators() { - QVector<TerminalCommand> result; + QList<TerminalCommand> result; if (HostOsInfo::isAnyUnixHost()) { const Environment env = Environment::systemEnvironment(); diff --git a/src/libs/utils/terminalcommand.h b/src/libs/utils/terminalcommand.h index edb9ffcadde..5ef1eba1cf4 100644 --- a/src/libs/utils/terminalcommand.h +++ b/src/libs/utils/terminalcommand.h @@ -7,16 +7,13 @@ #include "filepath.h" +#include <QList> #include <QMetaType> -#include <QVector> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE namespace Utils { class Environment; +class QtcSettings; class QTCREATOR_UTILS_EXPORT TerminalCommand { @@ -33,9 +30,9 @@ public: QString executeArgs; bool needsQuotes = false; - static void setSettings(QSettings *settings); + static void setSettings(QtcSettings *settings); static TerminalCommand defaultTerminalEmulator(); - static QVector<TerminalCommand> availableTerminalEmulators(); + static QList<TerminalCommand> availableTerminalEmulators(); static TerminalCommand terminalEmulator(); static void setTerminalEmulator(const TerminalCommand &term); }; diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp index 3bda25b109c..bb517cb9554 100644 --- a/src/libs/utils/terminalhooks.cpp +++ b/src/libs/utils/terminalhooks.cpp @@ -6,24 +6,28 @@ #include "externalterminalprocessimpl.h" #include "filepath.h" #include "process.h" +#include "utilstr.h" #include <QMutex> namespace Utils::Terminal { -FilePath defaultShellForDevice(const FilePath &deviceRoot) +expected_str<FilePath> defaultShellForDevice(const FilePath &deviceRoot) { if (deviceRoot.osType() == OsTypeWindows) return deviceRoot.withNewPath("cmd.exe").searchInPath(); - const Environment env = deviceRoot.deviceEnvironment(); - FilePath shell = FilePath::fromUserInput(env.value_or("SHELL", "/bin/sh")); + const expected_str<Environment> env = deviceRoot.deviceEnvironmentWithError(); + if (!env) + return make_unexpected(env.error()); + + FilePath shell = FilePath::fromUserInput(env->value_or("SHELL", "/bin/sh")); if (!shell.isAbsolutePath()) - shell = env.searchInPath(shell.nativePath()); + shell = env->searchInPath(shell.nativePath()); if (shell.isEmpty()) - return shell; + return make_unexpected(Tr::tr("Could not find any shell.")); return deviceRoot.withNewMappedPath(shell); } @@ -32,7 +36,6 @@ class HooksPrivate { public: HooksPrivate() - : m_getTerminalCommandsForDevicesHook([] { return QList<NameAndCommandLine>{}; }) { auto openTerminal = [](const OpenTerminalParameters ¶meters) { DeviceFileHooks::instance().openTerminal(parameters.workingDirectory.value_or( @@ -79,8 +82,6 @@ public: return m_openTerminal; } - Hooks::GetTerminalCommandsForDevicesHook m_getTerminalCommandsForDevicesHook; - private: Hooks::OpenTerminal m_openTerminal; Hooks::CreateTerminalProcessInterface m_createTerminalProcessInterface; @@ -111,11 +112,6 @@ ProcessInterface *Hooks::createTerminalProcessInterface() const return d->createTerminalProcessInterface()(); } -Hooks::GetTerminalCommandsForDevicesHook &Hooks::getTerminalCommandsForDevicesHook() -{ - return d->m_getTerminalCommandsForDevicesHook; -} - void Hooks::addCallbackSet(const QString &name, const CallbackSet &callbackSet) { d->addCallbackSet(name, callbackSet); diff --git a/src/libs/utils/terminalhooks.h b/src/libs/utils/terminalhooks.h index b37c51517c0..449c23daa93 100644 --- a/src/libs/utils/terminalhooks.h +++ b/src/libs/utils/terminalhooks.h @@ -8,6 +8,8 @@ #include "filepath.h" #include "id.h" +#include <QIcon> + #include <functional> #include <memory> @@ -41,9 +43,24 @@ enum class ExitBehavior { Close, Restart, Keep }; struct OpenTerminalParameters { + OpenTerminalParameters() = default; + OpenTerminalParameters(const CommandLine &commandLine) : shellCommand(commandLine) {} + OpenTerminalParameters(const FilePath &directory, std::optional<Environment> env) : + workingDirectory(directory), + environment(env) + {} + OpenTerminalParameters(const CommandLine &commandLine, + const FilePath &directory, + std::optional<Environment> env) : + shellCommand(commandLine), + workingDirectory(directory), + environment(env) + {} + std::optional<CommandLine> shellCommand; std::optional<FilePath> workingDirectory; std::optional<Environment> environment; + QIcon icon; ExitBehavior m_exitBehavior{ExitBehavior::Close}; std::optional<Id> identifier{std::nullopt}; }; @@ -54,7 +71,7 @@ struct NameAndCommandLine CommandLine commandLine; }; -QTCREATOR_UTILS_EXPORT FilePath defaultShellForDevice(const FilePath &deviceRoot); +QTCREATOR_UTILS_EXPORT expected_str<FilePath> defaultShellForDevice(const FilePath &deviceRoot); class QTCREATOR_UTILS_EXPORT Hooks { @@ -68,14 +85,10 @@ public: CreateTerminalProcessInterface createTerminalProcessInterface; }; - using GetTerminalCommandsForDevicesHook = Hook<QList<NameAndCommandLine>>; - public: static Hooks &instance(); ~Hooks(); - GetTerminalCommandsForDevicesHook &getTerminalCommandsForDevicesHook(); - void openTerminal(const OpenTerminalParameters ¶meters) const; ProcessInterface *createTerminalProcessInterface() const; diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp index 204cd79d7a1..9fb226ece54 100644 --- a/src/libs/utils/terminalinterface.cpp +++ b/src/libs/utils/terminalinterface.cpp @@ -45,12 +45,12 @@ static QString msgUnexpectedOutput(const QByteArray &what) static QString msgCannotChangeToWorkDir(const FilePath &dir, const QString &why) { - return Tr::tr("Cannot change to working directory \"%1\": %2").arg(dir.toString(), why); + return Tr::tr("Cannot change to working directory \"%1\": %2").arg(dir.toUserOutput(), why); } -static QString msgCannotExecute(const QString &p, const QString &why) +static QString msgCannotExecute(const FilePath &p, const QString &why) { - return Tr::tr("Cannot execute \"%1\": %2").arg(p, why); + return Tr::tr("Cannot execute \"%1\": %2").arg(p.toUserOutput(), why); } static QString msgPromptToClose() @@ -169,7 +169,7 @@ void TerminalInterface::onStubReadyRead() errnoToString(out.mid(10).toInt()))); } else if (out.startsWith("err:exec ")) { emitError(QProcess::FailedToStart, - msgCannotExecute(m_setup.m_commandLine.executable().toString(), + msgCannotExecute(m_setup.m_commandLine.executable(), errnoToString(out.mid(9).toInt()))); } else if (out.startsWith("spid ")) { d->envListFile.reset(); diff --git a/src/libs/utils/textfileformat.cpp b/src/libs/utils/textfileformat.cpp index 264c6c4351f..0fea53720f3 100644 --- a/src/libs/utils/textfileformat.cpp +++ b/src/libs/utils/textfileformat.cpp @@ -288,6 +288,19 @@ TextFileFormat::ReadResult TextFileFormat::readFileUTF8(const FilePath &filePath return TextFileFormat::ReadSuccess; } +tl::expected<QString, std::pair<TextFileFormat::ReadResult, QString>> +TextFileFormat::readFile(const FilePath &filePath, const QTextCodec *defaultCodec) +{ + QString plainText; + TextFileFormat format; + QString errorString; + const TextFileFormat::ReadResult result = + readTextFile(filePath, defaultCodec, &plainText, &format, &errorString, nullptr); + if (result != TextFileFormat::ReadSuccess) + return tl::unexpected(std::make_pair(result, errorString)); + return plainText; +} + /*! Writes out a text file to \a filePath into a string, \a plainText. diff --git a/src/libs/utils/textfileformat.h b/src/libs/utils/textfileformat.h index f500fed50e6..4cd67abec8e 100644 --- a/src/libs/utils/textfileformat.h +++ b/src/libs/utils/textfileformat.h @@ -3,9 +3,11 @@ #pragma once +#include "expected.h" #include "utils_global.h" #include <QStringList> +#include <utility> QT_BEGIN_NAMESPACE class QTextCodec; @@ -53,6 +55,8 @@ public: QByteArray *decodingErrorSample = nullptr); static ReadResult readFileUTF8(const FilePath &filePath, const QTextCodec *defaultCodec, QByteArray *plainText, QString *errorString); + static tl::expected<QString, std::pair<ReadResult, QString>> + readFile(const FilePath &filePath, const QTextCodec *defaultCodec); bool writeFile(const FilePath &filePath, QString plainText, QString *errorString) const; diff --git a/src/libs/utils/textutils.cpp b/src/libs/utils/textutils.cpp index 28a9e12e1bb..f8ada6cca68 100644 --- a/src/libs/utils/textutils.cpp +++ b/src/libs/utils/textutils.cpp @@ -64,6 +64,16 @@ Position Position::fromCursor(const QTextCursor &c) return c.isNull() ? Position{} : Position{c.blockNumber() + 1, c.positionInBlock()}; } +int Position::toPositionInDocument(const QTextDocument *document) const +{ + QTC_ASSERT(document, return -1); + const QTextBlock block = document->findBlockByNumber(line - 1); + if (block.isValid()) + return block.position() + qMin(column, block.length() - 1); + + return -1; +} + int Range::length(const QString &text) const { if (end.line < begin.line) @@ -124,11 +134,10 @@ QString textAt(QTextCursor tc, int pos, int length) if (pos < 0) pos = 0; tc.movePosition(QTextCursor::End); - if (pos + length > tc.position()) - length = tc.position() - pos; + const int end = std::min(pos + length, tc.position()); tc.setPosition(pos); - tc.setPosition(pos + length, QTextCursor::KeepAnchor); + tc.setPosition(end, QTextCursor::KeepAnchor); // selectedText() returns U+2029 (PARAGRAPH SEPARATOR) instead of newline return tc.selectedText().replace(QChar::ParagraphSeparator, QLatin1Char('\n')); @@ -139,10 +148,10 @@ QTextCursor selectAt(QTextCursor textCursor, int line, int column, uint length) if (line < 1) line = 1; - if (column < 1) - column = 1; + if (column < 0) + column = 0; - const int anchorPosition = positionInText(textCursor.document(), line, column); + const int anchorPosition = positionInText(textCursor.document(), line, column + 1); textCursor.setPosition(anchorPosition); textCursor.setPosition(anchorPosition + int(length), QTextCursor::KeepAnchor); @@ -252,26 +261,6 @@ bool utf8AdvanceCodePoint(const char *¤t) return true; } -void applyReplacements(QTextDocument *doc, const Replacements &replacements) -{ - if (replacements.empty()) - return; - - int fullOffsetShift = 0; - QTextCursor editCursor(doc); - editCursor.beginEditBlock(); - for (const Text::Replacement &replacement : replacements) { - editCursor.setPosition(replacement.offset + fullOffsetShift); - editCursor.movePosition(QTextCursor::NextCharacter, - QTextCursor::KeepAnchor, - replacement.length); - editCursor.removeSelectedText(); - editCursor.insertText(replacement.text); - fullOffsetShift += replacement.text.length() - replacement.length; - } - editCursor.endEditBlock(); -} - QDebug &operator<<(QDebug &stream, const Position &pos) { stream << "line: " << pos.line << ", column: " << pos.column; diff --git a/src/libs/utils/textutils.h b/src/libs/utils/textutils.h index 80e4150c1d2..d21ca20d4d9 100644 --- a/src/libs/utils/textutils.h +++ b/src/libs/utils/textutils.h @@ -33,6 +33,8 @@ public: static Position fromFileName(QStringView fileName, int &postfixPos); static Position fromPositionInDocument(const QTextDocument *document, int pos); static Position fromCursor(const QTextCursor &cursor); + + int toPositionInDocument(const QTextDocument *document) const; }; class QTCREATOR_UTILS_EXPORT Range @@ -49,25 +51,6 @@ public: bool operator!=(const Range &other) const { return !(operator==(other)); } }; -struct Replacement -{ - Replacement() = default; - Replacement(int offset, int length, const QString &text) - : offset(offset) - , length(length) - , text(text) - {} - - int offset = -1; - int length = -1; - QString text; - - bool isValid() const { return offset >= 0 && length >= 0; } -}; -using Replacements = std::vector<Replacement>; - -QTCREATOR_UTILS_EXPORT void applyReplacements(QTextDocument *doc, const Replacements &replacements); - // line is 1-based, column is 0-based QTCREATOR_UTILS_EXPORT bool convertPosition(const QTextDocument *document, int pos, @@ -78,6 +61,7 @@ QTCREATOR_UTILS_EXPORT int positionInText(const QTextDocument *textDocument, int QTCREATOR_UTILS_EXPORT QString textAt(QTextCursor tc, int pos, int length); +// line is 1-based, column is 0-based QTCREATOR_UTILS_EXPORT QTextCursor selectAt(QTextCursor textCursor, int line, int column, uint length); QTCREATOR_UTILS_EXPORT QTextCursor flippedCursor(const QTextCursor &cursor); diff --git a/src/libs/utils/theme/theme_p.h b/src/libs/utils/theme/theme_p.h index 6aa5fb06a6c..14483f6d58c 100644 --- a/src/libs/utils/theme/theme_p.h +++ b/src/libs/utils/theme/theme_p.h @@ -23,9 +23,9 @@ public: QStringList preferredStyles; QString defaultTextEditorColorScheme; QString enforceAccentColorOnMacOS; - QVector<QPair<QColor, QString> > colors; - QVector<QString> imageFiles; - QVector<bool> flags; + QList<QPair<QColor, QString> > colors; + QList<QString> imageFiles; + QList<bool> flags; QMap<QString, QColor> palette; }; diff --git a/src/libs/utils/tooltip/tips.h b/src/libs/utils/tooltip/tips.h index 1eef42e8dcf..447c5e63439 100644 --- a/src/libs/utils/tooltip/tips.h +++ b/src/libs/utils/tooltip/tips.h @@ -3,8 +3,6 @@ #pragma once -#include "../utils_global.h" - #include <QLabel> #include <QPixmap> #include <QVariant> @@ -41,7 +39,6 @@ using TextItem = std::pair<QString, Qt::TextFormat>; class TextTip : public TipLabel { - Q_OBJECT public: TextTip(QWidget *parent); @@ -61,7 +58,6 @@ private: class ColorTip : public TipLabel { - Q_OBJECT public: ColorTip(QWidget *parent); diff --git a/src/libs/utils/transientscroll.h b/src/libs/utils/transientscroll.h index c63c89d2e1b..b10f77541d6 100644 --- a/src/libs/utils/transientscroll.h +++ b/src/libs/utils/transientscroll.h @@ -18,7 +18,6 @@ class ScrollBarPrivate; class QTCREATOR_UTILS_EXPORT TransientScrollAreaSupport : public QObject { - Q_OBJECT public: static void support(QAbstractScrollArea *scrollArea); static void supportWidget(QWidget *widget); @@ -35,7 +34,6 @@ private: class QTCREATOR_UTILS_EXPORT ScrollBar : public QScrollBar { - Q_OBJECT friend class ScrollAreaPrivate; diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp index e5e5aa59b99..fe96b9c83e4 100644 --- a/src/libs/utils/treemodel.cpp +++ b/src/libs/utils/treemodel.cpp @@ -4,6 +4,7 @@ #include "treemodel.h" #include "qtcassert.h" +#include "stringutils.h" #include <QStack> #include <QSize> @@ -717,7 +718,7 @@ void TreeItem::sortChildren(const std::function<bool(const TreeItem *, const Tre { if (m_model) { if (const int n = childCount()) { - QVector<TreeItem *> tmp = m_children; + QList<TreeItem *> tmp = m_children; std::sort(tmp.begin(), tmp.end(), cmp); if (tmp == m_children) { // Nothing changed. @@ -1199,4 +1200,12 @@ Qt::ItemFlags StaticTreeItem::flags(int column) const return Qt::ItemIsEnabled; } +bool SortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + if (m_lessThan) + return m_lessThan(source_left, source_right); + return caseFriendlyCompare(sourceModel()->data(source_left).toString(), + sourceModel()->data(source_right).toString()) < 0; +} + } // namespace Utils diff --git a/src/libs/utils/treemodel.h b/src/libs/utils/treemodel.h index 2cff2f72414..a8cc073fcff 100644 --- a/src/libs/utils/treemodel.h +++ b/src/libs/utils/treemodel.h @@ -7,7 +7,7 @@ #include "indexedcontainerproxyconstiterator.h" -#include <QAbstractItemModel> +#include <QSortFilterProxyModel> #include <functional> @@ -49,7 +49,7 @@ public: TreeItem *lastChild() const; int level() const; - using const_iterator = QVector<TreeItem *>::const_iterator; + using const_iterator = QList<TreeItem *>::const_iterator; using value_type = TreeItem *; int childCount() const { return m_children.size(); } int indexInParent() const; @@ -81,7 +81,7 @@ private: TreeItem *m_parent = nullptr; // Not owned. BaseTreeModel *m_model = nullptr; // Not owned. - QVector<TreeItem *> m_children; // Owned. + QList<TreeItem *> m_children; // Owned. friend class BaseTreeModel; }; @@ -344,6 +344,21 @@ public: } }; +// By default, does natural sorting by display name. Call setLessThan() to customize. +class QTCREATOR_UTILS_EXPORT SortModel : public QSortFilterProxyModel +{ +public: + using QSortFilterProxyModel::QSortFilterProxyModel; + using LessThan = std::function<bool(const QModelIndex &, const QModelIndex &)>; + void setLessThan(const LessThan &lessThan) { m_lessThan = lessThan; } + +protected: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + +private: + LessThan m_lessThan; +}; + } // namespace Utils Q_DECLARE_METATYPE(Utils::TreeItem *) diff --git a/src/libs/utils/treeviewcombobox.h b/src/libs/utils/treeviewcombobox.h index b8afc20bc88..32169f3db36 100644 --- a/src/libs/utils/treeviewcombobox.h +++ b/src/libs/utils/treeviewcombobox.h @@ -12,7 +12,6 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT TreeViewComboBoxView : public QTreeView { - Q_OBJECT public: TreeViewComboBoxView(QWidget *parent = nullptr); void adjustWidth(int width); @@ -20,7 +19,6 @@ public: class QTCREATOR_UTILS_EXPORT TreeViewComboBox : public QComboBox { - Q_OBJECT public: TreeViewComboBox(QWidget *parent = nullptr); diff --git a/src/libs/utils/archive.cpp b/src/libs/utils/unarchiver.cpp similarity index 64% rename from src/libs/utils/archive.cpp rename to src/libs/utils/unarchiver.cpp index 5e62835a20f..9435bc77a51 100644 --- a/src/libs/utils/archive.cpp +++ b/src/libs/utils/unarchiver.cpp @@ -1,11 +1,10 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "archive.h" +#include "unarchiver.h" #include "algorithm.h" #include "mimeutils.h" -#include "process.h" #include "qtcassert.h" #include "utilstr.h" @@ -38,9 +37,9 @@ static FilePaths additionalInstallDirs(const QString ®istryKey, const QString #endif } -static const QVector<Tool> &sTools() +static const QList<Tool> &sTools() { - static QVector<Tool> tools; + static QList<Tool> tools; if (tools.isEmpty()) { if (HostOsInfo::isWindowsHost()) { tools << Tool{{"powershell", "-command Expand-Archive -Force '%{src}' '%{dest}'", CommandLine::Raw}, @@ -77,7 +76,7 @@ static const QVector<Tool> &sTools() return tools; } -static QVector<Tool> toolsForMimeType(const MimeType &mimeType) +static QList<Tool> toolsForMimeType(const MimeType &mimeType) { return Utils::filtered(sTools(), [mimeType](const Tool &tool) { return Utils::anyOf(tool.supportedMimeTypes, @@ -85,7 +84,7 @@ static QVector<Tool> toolsForMimeType(const MimeType &mimeType) }); } -static QVector<Tool> toolsForFilePath(const FilePath &fp) +static QList<Tool> toolsForFilePath(const FilePath &fp) { return toolsForMimeType(mimeTypeForFile(fp)); } @@ -99,66 +98,52 @@ static std::optional<Tool> resolveTool(const Tool &tool) return executable.isEmpty() ? std::nullopt : std::make_optional(resolvedTool); } -static std::optional<Tool> unzipTool(const FilePath &src, const FilePath &dest) +expected_str<Unarchiver::SourceAndCommand> Unarchiver::sourceAndCommand(const FilePath &sourceFile) { - const QVector<Tool> tools = toolsForFilePath(src); + const QList<Tool> tools = toolsForFilePath(sourceFile); + if (tools.isEmpty()) + return make_unexpected(Tr::tr("File format not supported.")); + for (const Tool &tool : tools) { const std::optional<Tool> resolvedTool = resolveTool(tool); - if (resolvedTool) { - Tool result = *resolvedTool; - const QString srcStr = src.path(); - const QString destStr = dest.path(); - const QString args = result.command.arguments().replace("%{src}", srcStr).replace("%{dest}", destStr); - result.command.setArguments(args); - return result; - } + if (resolvedTool) + return SourceAndCommand(sourceFile, resolvedTool->command); } - return {}; + + const QStringList execs = transform<QStringList>(tools, [](const Tool &tool) { + return tool.command.executable().toUserOutput(); + }); + return make_unexpected(Tr::tr("Could not find any unarchiving executable in PATH (%1).") + .arg(execs.join(", "))); } -bool Archive::supportsFile(const FilePath &filePath, QString *reason) +static CommandLine unarchiveCommand(const CommandLine &commandTemplate, const FilePath &sourceFile, + const FilePath &destDir) { - const QVector<Tool> tools = toolsForFilePath(filePath); - if (tools.isEmpty()) { - if (reason) - *reason = Tr::tr("File format not supported."); - return false; - } - if (!anyOf(tools, [tools](const Tool &t) { return resolveTool(t); })) { - if (reason) { - const QStringList execs = transform<QStringList>(tools, [](const Tool &tool) { - return tool.command.executable().toUserOutput(); - }); - *reason = Tr::tr("Could not find any unarchiving executable in PATH (%1).") - .arg(execs.join(", ")); - } - return false; - } - return true; + CommandLine command = commandTemplate; + command.setArguments(command.arguments().replace("%{src}", sourceFile.path()) + .replace("%{dest}", destDir.path())); + return command; } -Archive::Archive(const FilePath &src, const FilePath &dest) +void Unarchiver::start() { - const std::optional<Tool> tool = unzipTool(src, dest); - if (!tool) + QTC_ASSERT(!m_process, emit done(false); return); + + if (!m_sourceAndCommand) { + emit outputReceived(Tr::tr("No source file set.")); + emit done(false); return; - m_commandLine = tool->command; - m_workingDirectory = dest.absoluteFilePath(); -} + } + if (m_destDir.isEmpty()) { + emit outputReceived(Tr::tr("No destination directory set.")); + emit done(false); + return; + } -Archive::~Archive() = default; - -bool Archive::isValid() const -{ - return !m_commandLine.isEmpty(); -} - -void Archive::unarchive() -{ - QTC_ASSERT(isValid(), return); - QTC_ASSERT(!m_process, return); - - m_workingDirectory.ensureWritableDir(); + const CommandLine command = unarchiveCommand(m_sourceAndCommand->m_commandTemplate, + m_sourceAndCommand->m_sourceFile, m_destDir); + m_destDir.ensureWritableDir(); m_process.reset(new Process); m_process->setProcessChannelMode(QProcess::MergedChannels); @@ -166,18 +151,28 @@ void Archive::unarchive() emit outputReceived(m_process->readAllStandardOutput()); }); QObject::connect(m_process.get(), &Process::done, this, [this] { - const bool successfulFinish = m_process->result() == ProcessResult::FinishedWithSuccess; - if (!successfulFinish) + const bool success = m_process->result() == ProcessResult::FinishedWithSuccess; + if (!success) emit outputReceived(Tr::tr("Command failed.")); - emit finished(successfulFinish); + emit done(success); }); emit outputReceived(Tr::tr("Running %1\nin \"%2\".\n\n", "Running <cmd> in <workingdirectory>") - .arg(m_commandLine.toUserOutput(), m_workingDirectory.toUserOutput())); + .arg(command.toUserOutput(), m_destDir.toUserOutput())); - m_process->setCommand(m_commandLine); - m_process->setWorkingDirectory(m_workingDirectory); + m_process->setCommand(command); + m_process->setWorkingDirectory(m_destDir); m_process->start(); } +UnarchiverTaskAdapter::UnarchiverTaskAdapter() +{ + connect(task(), &Unarchiver::done, this, &Tasking::TaskInterface::done); +} + +void UnarchiverTaskAdapter::start() +{ + task()->start(); +} + } // namespace Utils diff --git a/src/libs/utils/unarchiver.h b/src/libs/utils/unarchiver.h new file mode 100644 index 00000000000..3e2b2f89386 --- /dev/null +++ b/src/libs/utils/unarchiver.h @@ -0,0 +1,57 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "utils_global.h" + +#include "commandline.h" +#include "process.h" + +#include <solutions/tasking/tasktree.h> + +#include <QObject> + +namespace Utils { + +class QTCREATOR_UTILS_EXPORT Unarchiver : public QObject +{ + Q_OBJECT +public: + class SourceAndCommand + { + private: + friend class Unarchiver; + SourceAndCommand(const FilePath &sourceFile, const CommandLine &commandTemplate) + : m_sourceFile(sourceFile), m_commandTemplate(commandTemplate) {} + FilePath m_sourceFile; + CommandLine m_commandTemplate; + }; + + static expected_str<SourceAndCommand> sourceAndCommand(const FilePath &sourceFile); + + void setSourceAndCommand(const SourceAndCommand &data) { m_sourceAndCommand = data; } + void setDestDir(const FilePath &destDir) { m_destDir = destDir; } + + void start(); + +signals: + void outputReceived(const QString &output); + void done(bool success); + +private: + std::optional<SourceAndCommand> m_sourceAndCommand; + FilePath m_destDir; + std::unique_ptr<Process> m_process; +}; + +class QTCREATOR_UTILS_EXPORT UnarchiverTaskAdapter : public Tasking::TaskAdapter<Unarchiver> +{ +public: + UnarchiverTaskAdapter(); + void start() final; +}; + +using UnarchiverTask = Tasking::CustomTask<UnarchiverTaskAdapter>; + +} // namespace Utils diff --git a/src/libs/utils/uncommentselection.cpp b/src/libs/utils/uncommentselection.cpp index 5bba136cdf1..d2feb494e77 100644 --- a/src/libs/utils/uncommentselection.cpp +++ b/src/libs/utils/uncommentselection.cpp @@ -40,23 +40,11 @@ bool CommentDefinition::hasMultiLineStyle() const return !multiLineStart.isEmpty() && !multiLineEnd.isEmpty(); } -static bool isComment(const QString &text, int index, - const QString &commentType) +static bool isComment(const QString &text, int index, const QString &commentType) { - const int length = commentType.length(); - - Q_ASSERT(text.length() - index >= length); - - int i = 0; - while (i < length) { - if (text.at(index + i) != commentType.at(i)) - return false; - ++i; - } - return true; + return QStringView(text).mid(index).startsWith(commentType); } - QTextCursor unCommentSelection(const QTextCursor &cursorIn, const CommentDefinition &definition, bool preferSingleLine) @@ -176,12 +164,34 @@ QTextCursor unCommentSelection(const QTextCursor &cursorIn, } const int singleLineLength = definition.singleLine.length(); + int minTab = INT_MAX; + if (definition.isAfterWhitespace && !doSingleLineStyleUncomment) { + for (QTextBlock block = startBlock; block != endBlock && minTab != 0; block = block.next()) { + QTextCursor c(block); + if (doc->characterAt(block.position()).isSpace()) { + c.movePosition(QTextCursor::NextWord); + if (c.block() != block) // ignore empty lines + continue; + } + const int pos = c.positionInBlock(); + if (pos < minTab) + minTab = pos; + } + } for (QTextBlock block = startBlock; block != endBlock; block = block.next()) { if (doSingleLineStyleUncomment) { QString text = block.text(); int i = 0; while (i <= text.size() - singleLineLength) { - if (isComment(text, i, definition.singleLine)) { + if (definition.isAfterWhitespace + && isComment(text, i, definition.singleLine + ' ')) { + cursor.setPosition(block.position() + i); + cursor.movePosition(QTextCursor::NextCharacter, + QTextCursor::KeepAnchor, + singleLineLength + 1); + cursor.removeSelectedText(); + break; + } else if (isComment(text, i, definition.singleLine)) { cursor.setPosition(block.position() + i); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, @@ -197,11 +207,13 @@ QTextCursor unCommentSelection(const QTextCursor &cursorIn, const QString text = block.text(); for (QChar c : text) { if (!c.isSpace()) { - if (definition.isAfterWhiteSpaces) - cursor.setPosition(block.position() + text.indexOf(c)); - else + if (definition.isAfterWhitespace) { + cursor.setPosition(block.position() + minTab); + cursor.insertText(definition.singleLine + ' '); + } else { cursor.setPosition(block.position()); - cursor.insertText(definition.singleLine); + cursor.insertText(definition.singleLine); + } break; } } diff --git a/src/libs/utils/uncommentselection.h b/src/libs/utils/uncommentselection.h index a309b028aa4..b6836ec5101 100644 --- a/src/libs/utils/uncommentselection.h +++ b/src/libs/utils/uncommentselection.h @@ -31,7 +31,7 @@ public: bool hasMultiLineStyle() const; public: - bool isAfterWhiteSpaces = false; + bool isAfterWhitespace = false; QString singleLine; QString multiLineStart; QString multiLineEnd; diff --git a/src/libs/utils/unixutils.cpp b/src/libs/utils/unixutils.cpp index 5d626c74f24..f0a7bbf0a13 100644 --- a/src/libs/utils/unixutils.cpp +++ b/src/libs/utils/unixutils.cpp @@ -8,42 +8,39 @@ #include "utilstr.h" #include <QFileInfo> -#include <QSettings> -using namespace Utils; +namespace Utils::UnixUtils { -QString UnixUtils::defaultFileBrowser() +QString defaultFileBrowser() { return QLatin1String("xdg-open %d"); } -QString UnixUtils::fileBrowser(const QSettings *settings) +QString fileBrowser(const QtcSettings *settings) { const QString dflt = defaultFileBrowser(); if (!settings) return dflt; - return settings->value(QLatin1String("General/FileBrowser"), dflt).toString(); + return settings->value("General/FileBrowser", dflt).toString(); } -void UnixUtils::setFileBrowser(QSettings *settings, const QString &term) +void setFileBrowser(QtcSettings *settings, const QString &term) { - QtcSettings::setValueWithDefault(settings, "General/FileBrowser", term, defaultFileBrowser()); + settings->setValueWithDefault("General/FileBrowser", term, defaultFileBrowser()); } - -QString UnixUtils::fileBrowserHelpText() +QString fileBrowserHelpText() { - QString help = Tr::tr("<table border=1 cellspacing=0 cellpadding=3>" - "<tr><th>Variable</th><th>Expands to</th></tr>" - "<tr><td>%d</td><td>directory of current file</td></tr>" - "<tr><td>%f</td><td>file name (with full path)</td></tr>" - "<tr><td>%n</td><td>file name (without path)</td></tr>" - "<tr><td>%%</td><td>%</td></tr>" - "</table>"); - return help; + return Tr::tr("<table border=1 cellspacing=0 cellpadding=3>" + "<tr><th>Variable</th><th>Expands to</th></tr>" + "<tr><td>%d</td><td>directory of current file</td></tr>" + "<tr><td>%f</td><td>file name (with full path)</td></tr>" + "<tr><td>%n</td><td>file name (without path)</td></tr>" + "<tr><td>%%</td><td>%</td></tr>" + "</table>"); } -QString UnixUtils::substituteFileBrowserParameters(const QString &pre, const QString &file) +QString substituteFileBrowserParameters(const QString &pre, const QString &file) { QString cmd; for (int i = 0; i < pre.size(); ++i) { @@ -72,3 +69,5 @@ QString UnixUtils::substituteFileBrowserParameters(const QString &pre, const QSt return cmd; } + +} // Utils::UnixUtils diff --git a/src/libs/utils/unixutils.h b/src/libs/utils/unixutils.h index f82517e9005..a2396d5cc2d 100644 --- a/src/libs/utils/unixutils.h +++ b/src/libs/utils/unixutils.h @@ -6,21 +6,18 @@ #include "utils_global.h" QT_BEGIN_NAMESPACE -class QSettings; class QString; QT_END_NAMESPACE -namespace Utils { +namespace Utils { class QtcSettings; } -class QTCREATOR_UTILS_EXPORT UnixUtils -{ -public: - static QString defaultFileBrowser(); - static QString fileBrowser(const QSettings *settings); - static void setFileBrowser(QSettings *settings, const QString &term); - static QString fileBrowserHelpText(); - static QString substituteFileBrowserParameters(const QString &command, - const QString &file); -}; +namespace Utils::UnixUtils { -} +QTCREATOR_UTILS_EXPORT QString defaultFileBrowser(); +QTCREATOR_UTILS_EXPORT QString fileBrowser(const QtcSettings *settings); +QTCREATOR_UTILS_EXPORT void setFileBrowser(QtcSettings *settings, const QString &term); +QTCREATOR_UTILS_EXPORT QString fileBrowserHelpText(); +QTCREATOR_UTILS_EXPORT QString substituteFileBrowserParameters(const QString &command, + const QString &file); + +} // Utils::UnixUtils diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index d117b7057cb..51d6857d122 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -1,480 +1,477 @@ -import qbs 1.0 import qbs.FileInfo -Project { +QtcLibrary { name: "Utils" - - QtcLibrary { - cpp.includePaths: base.concat("mimetypes2", ".") - cpp.defines: base.concat([ - "UTILS_LIBRARY" - ]) - cpp.dynamicLibraries: { - var libs = []; - if (qbs.targetOS.contains("windows")) { - libs.push("user32", "iphlpapi", "ws2_32", "shell32", "ole32"); - if (qbs.toolchainType === "mingw") - libs.push("uuid"); - else if (qbs.toolchainType === "msvc") - libs.push("dbghelp"); - } else if (qbs.targetOS.contains("unix")) { - if (!qbs.targetOS.contains("macos")) - libs.push("X11"); - if (!qbs.targetOS.contains("openbsd")) - libs.push("pthread"); - } - return libs; + cpp.includePaths: base.concat("mimetypes2", ".") + cpp.defines: base.concat(["UTILS_LIBRARY"]) + cpp.dynamicLibraries: { + var libs = []; + if (qbs.targetOS.contains("windows")) { + libs.push("user32", "iphlpapi", "ws2_32", "shell32", "ole32"); + if (qbs.toolchain.contains("mingw")) + libs.push("uuid"); + else if (qbs.toolchain.contains("msvc")) + libs.push("dbghelp"); + } else if (qbs.targetOS.contains("unix")) { + if (!qbs.targetOS.contains("macos")) + libs.push("X11"); + if (!qbs.targetOS.contains("openbsd")) + libs.push("pthread"); } + return libs; + } - cpp.enableExceptions: true + cpp.enableExceptions: true - Properties { - condition: qbs.targetOS.contains("macos") - cpp.frameworks: ["Foundation", "AppKit"] - } + Properties { + condition: qbs.targetOS.contains("macos") + cpp.frameworks: ["Foundation", "AppKit"] + } - Depends { name: "Qt"; submodules: ["concurrent", "core-private", "network", "qml", "widgets", "xml"] } - Depends { name: "Qt.macextras"; condition: Qt.core.versionMajor < 6 && qbs.targetOS.contains("macos") } - Depends { name: "Tasking" } - Depends { name: "app_version_header" } - Depends { name: "ptyqt" } + Depends { name: "Qt"; submodules: ["concurrent", "core-private", "network", "qml", "widgets", "xml"] } + Depends { name: "Qt.macextras"; condition: Qt.core.versionMajor < 6 && qbs.targetOS.contains("macos") } + Depends { name: "Spinner" } + Depends { name: "Tasking" } + Depends { name: "ptyqt" } + files: [ + "algorithm.h", + "ansiescapecodehandler.cpp", + "ansiescapecodehandler.h", + "appinfo.cpp", + "appinfo.h", + "appmainwindow.cpp", + "appmainwindow.h", + "aspects.cpp", + "aspects.h", + "async.cpp", + "async.h", + "basetreeview.cpp", + "basetreeview.h", + "benchmarker.cpp", + "benchmarker.h", + "buildablehelperlibrary.cpp", + "buildablehelperlibrary.h", + "camelcasecursor.cpp", + "camelcasecursor.h", + "categorysortfiltermodel.cpp", + "categorysortfiltermodel.h", + "changeset.cpp", + "changeset.h", + "checkablemessagebox.cpp", + "checkablemessagebox.h", + "clangutils.cpp", + "clangutils.h", + "classnamevalidatinglineedit.cpp", + "classnamevalidatinglineedit.h", + "codegeneration.cpp", + "codegeneration.h", + "commandline.cpp", + "commandline.h", + "completinglineedit.cpp", + "completinglineedit.h", + "completingtextedit.cpp", + "completingtextedit.h", + "cpplanguage_details.h", + "crumblepath.cpp", + "crumblepath.h", + "delegates.cpp", + "delegates.h", + "detailsbutton.cpp", + "detailsbutton.h", + "detailswidget.cpp", + "detailswidget.h", + "devicefileaccess.cpp", + "devicefileaccess.h", + "deviceshell.cpp", + "deviceshell.h", + "differ.cpp", + "differ.h", + "displayname.cpp", + "displayname.h", + "dropsupport.cpp", + "dropsupport.h", + "elfreader.cpp", + "elfreader.h", + "elidinglabel.cpp", + "elidinglabel.h", + "environment.cpp", + "environment.h", + "environmentdialog.cpp", + "environmentdialog.h", + "environmentmodel.cpp", + "environmentmodel.h", + "execmenu.cpp", + "execmenu.h", + "externalterminalprocessimpl.cpp", + "externalterminalprocessimpl.h", + "fadingindicator.cpp", + "fadingindicator.h", + "faketooltip.cpp", + "faketooltip.h", + "fancylineedit.cpp", + "fancylineedit.h", + "fancymainwindow.cpp", + "fancymainwindow.h", + "filecrumblabel.cpp", + "filecrumblabel.h", + "fileinprojectfinder.cpp", + "fileinprojectfinder.h", + "filenamevalidatinglineedit.cpp", + "filenamevalidatinglineedit.h", + "filepath.cpp", + "filepath.h", + "filesearch.cpp", + "filesearch.h", + "filestreamer.cpp", + "filestreamer.h", + "filestreamermanager.cpp", + "filestreamermanager.h", + "filesystemmodel.cpp", + "filesystemmodel.h", + "filesystemwatcher.cpp", + "filesystemwatcher.h", + "fileutils.cpp", + "fileutils.h", + "filewizardpage.cpp", + "filewizardpage.h", + "flowlayout.cpp", + "flowlayout.h", + "futuresynchronizer.cpp", + "futuresynchronizer.h", + "fuzzymatcher.cpp", + "fuzzymatcher.h", + "globalfilechangeblocker.cpp", + "globalfilechangeblocker.h", + "guard.cpp", + "guard.h", + "highlightingitemdelegate.cpp", + "highlightingitemdelegate.h", + "historycompleter.cpp", + "historycompleter.h", + "hostosinfo.h", + "hostosinfo.cpp", + "htmldocextractor.cpp", + "htmldocextractor.h", + "icon.cpp", + "icon.h", + "iconbutton.cpp", + "iconbutton.h", + "id.cpp", + "id.h", + "indexedcontainerproxyconstiterator.h", + "infobar.cpp", + "infobar.h", + "infolabel.cpp", + "infolabel.h", + "itemviews.cpp", + "itemviews.h", + "jsontreeitem.cpp", + "jsontreeitem.h", + "launcherinterface.cpp", + "launcherinterface.h", + "launcherpackets.cpp", + "launcherpackets.h", + "launchersocket.cpp", + "launchersocket.h", + "layoutbuilder.cpp", + "layoutbuilder.h", + "link.cpp", + "link.h", + "listmodel.h", + "listutils.h", + "macroexpander.cpp", + "macroexpander.h", + "mathutils.cpp", + "mathutils.h", + "mimeutils.h", + "minimizableinfobars.cpp", + "minimizableinfobars.h", + "multitextcursor.cpp", + "multitextcursor.h", + "namevaluedictionary.cpp", + "namevaluedictionary.h", + "namevalueitem.cpp", + "namevalueitem.h", + "namevaluemodel.cpp", + "namevaluemodel.h", + "namevaluesdialog.cpp", + "namevaluesdialog.h", + "namevaluevalidator.cpp", + "namevaluevalidator.h", + "navigationtreeview.cpp", + "navigationtreeview.h", + "networkaccessmanager.cpp", + "networkaccessmanager.h", + "optionpushbutton.h", + "optionpushbutton.cpp", + "osspecificaspects.h", + "outputformat.h", + "outputformatter.cpp", + "outputformatter.h", + "overlaywidget.cpp", + "overlaywidget.h", + "overridecursor.cpp", + "overridecursor.h", + "parameteraction.cpp", + "parameteraction.h", + "passworddialog.cpp", + "passworddialog.h", + "pathchooser.cpp", + "pathchooser.h", + "pathlisteditor.cpp", + "pathlisteditor.h", + "persistentcachestore.cpp", + "persistentcachestore.h", + "persistentsettings.cpp", + "persistentsettings.h", + "pointeralgorithm.h", + "port.cpp", + "port.h", + "portlist.cpp", + "portlist.h", + "predicates.h", + "process.cpp", + "process.h", + "processenums.h", + "processhandle.cpp", + "processhandle.h", + "processinfo.cpp", + "processinfo.h", + "processinterface.cpp", + "processinterface.h", + "processreaper.cpp", + "processreaper.h", + "processutils.cpp", + "processutils.h", + "progressindicator.cpp", + "progressindicator.h", + "projectintropage.cpp", + "projectintropage.h", + "proxyaction.cpp", + "proxyaction.h", + "qrcparser.cpp", + "qrcparser.h", + "qtcassert.cpp", + "qtcassert.h", + "qtcolorbutton.cpp", + "qtcolorbutton.h", + "qtcsettings.cpp", + "qtcsettings.h", + "reloadpromptutils.cpp", + "reloadpromptutils.h", + "removefiledialog.cpp", + "removefiledialog.h", + "savefile.cpp", + "savefile.h", + "scopedswap.h", + "scopedtimer.cpp", + "scopedtimer.h", + "searchresultitem.cpp", + "searchresultitem.h", + "set_algorithm.h", + "settingsaccessor.cpp", + "settingsaccessor.h", + "settingsselector.cpp", + "settingsselector.h", + "singleton.cpp", + "singleton.h", + "sizedarray.h", + "smallstring.h", + "smallstringiterator.h", + "smallstringio.h", + "smallstringliteral.h", + "smallstringlayout.h", + "smallstringmemory.h", + "smallstringvector.h", + "sortfiltermodel.h", + "span.h", + "../3rdparty/span/span.hpp", + "statuslabel.cpp", + "statuslabel.h", + "store.cpp", + "store.h", + "storekey.h", + "stringtable.cpp", + "stringtable.h", + "stringutils.cpp", + "stringutils.h", + "styleanimator.cpp", + "styleanimator.h", + "styledbar.cpp", + "styledbar.h", + "stylehelper.cpp", + "stylehelper.h", + "templateengine.cpp", + "templateengine.h", + "temporarydirectory.cpp", + "temporarydirectory.h", + "temporaryfile.cpp", + "temporaryfile.h", + "terminalcommand.cpp", + "terminalcommand.h", + "terminalhooks.cpp", + "terminalhooks.h", + "terminalinterface.cpp", + "terminalinterface.h", + "textfieldcheckbox.cpp", + "textfieldcheckbox.h", + "textfieldcombobox.cpp", + "textfieldcombobox.h", + "textfileformat.cpp", + "textfileformat.h", + "textutils.cpp", + "textutils.h", + "threadutils.cpp", + "threadutils.h", + "transientscroll.cpp", + "transientscroll.h", + "treemodel.cpp", + "treemodel.h", + "treeviewcombobox.cpp", + "treeviewcombobox.h", + "headerviewstretcher.cpp", + "headerviewstretcher.h", + "unarchiver.cpp", + "unarchiver.h", + "uncommentselection.cpp", + "uncommentselection.h", + "uniqueobjectptr.h", + "unixutils.cpp", + "unixutils.h", + "url.cpp", + "url.h", + "utils.qrc", + "utils_global.h", + "utilsicons.h", + "utilsicons.cpp", + "utilstr.h", + "variablechooser.cpp", + "variablechooser.h", + "winutils.cpp", + "winutils.h", + "wizard.cpp", + "wizard.h", + "wizardpage.cpp", + "wizardpage.h", + "images/*.png", + ] + + Group { + name: "FSEngine" + prefix: "fsengine/" + cpp.defines: outer.concat("QTC_UTILS_WITH_FSENGINE") files: [ - "QtConcurrentTools", - "algorithm.h", - "ansiescapecodehandler.cpp", - "ansiescapecodehandler.h", - "appmainwindow.cpp", - "appmainwindow.h", - "archive.cpp", - "archive.h", - "aspects.cpp", - "aspects.h", - "async.cpp", - "async.h", - "basetreeview.cpp", - "basetreeview.h", - "benchmarker.cpp", - "benchmarker.h", - "buildablehelperlibrary.cpp", - "buildablehelperlibrary.h", - "camelcasecursor.cpp", - "camelcasecursor.h", - "categorysortfiltermodel.cpp", - "categorysortfiltermodel.h", - "changeset.cpp", - "changeset.h", - "checkablemessagebox.cpp", - "checkablemessagebox.h", - "clangutils.cpp", - "clangutils.h", - "classnamevalidatinglineedit.cpp", - "classnamevalidatinglineedit.h", - "codegeneration.cpp", - "codegeneration.h", - "commandline.cpp", - "commandline.h", - "completinglineedit.cpp", - "completinglineedit.h", - "completingtextedit.cpp", - "completingtextedit.h", - "cpplanguage_details.h", - "crumblepath.cpp", - "crumblepath.h", - "delegates.cpp", - "delegates.h", - "detailsbutton.cpp", - "detailsbutton.h", - "detailswidget.cpp", - "detailswidget.h", - "devicefileaccess.cpp", - "devicefileaccess.h", - "deviceshell.cpp", - "deviceshell.h", - "differ.cpp", - "differ.h", - "displayname.cpp", - "displayname.h", - "dropsupport.cpp", - "dropsupport.h", - "elfreader.cpp", - "elfreader.h", - "elidinglabel.cpp", - "elidinglabel.h", - "environment.cpp", - "environment.h", - "environmentdialog.cpp", - "environmentdialog.h", - "environmentmodel.cpp", - "environmentmodel.h", - "execmenu.cpp", - "execmenu.h", - "externalterminalprocessimpl.cpp", - "externalterminalprocessimpl.h", - "fadingindicator.cpp", - "fadingindicator.h", - "faketooltip.cpp", - "faketooltip.h", - "fancylineedit.cpp", - "fancylineedit.h", - "fancymainwindow.cpp", - "fancymainwindow.h", - "filecrumblabel.cpp", - "filecrumblabel.h", - "fileinprojectfinder.cpp", - "fileinprojectfinder.h", - "filenamevalidatinglineedit.cpp", - "filenamevalidatinglineedit.h", - "filepath.cpp", - "filepath.h", - "filesearch.cpp", - "filesearch.h", - "filestreamer.cpp", - "filestreamer.h", - "filestreamermanager.cpp", - "filestreamermanager.h", - "filesystemmodel.cpp", - "filesystemmodel.h", - "filesystemwatcher.cpp", - "filesystemwatcher.h", - "fileutils.cpp", - "fileutils.h", - "filewizardpage.cpp", - "filewizardpage.h", - "fixedsizeclicklabel.cpp", - "fixedsizeclicklabel.h", - "flowlayout.cpp", - "flowlayout.h", - "functiontraits.h", - "futuresynchronizer.cpp", - "futuresynchronizer.h", - "fuzzymatcher.cpp", - "fuzzymatcher.h", - "globalfilechangeblocker.cpp", - "globalfilechangeblocker.h", - "guard.cpp", - "guard.h", - "highlightingitemdelegate.cpp", - "highlightingitemdelegate.h", - "historycompleter.cpp", - "historycompleter.h", - "hostosinfo.h", - "hostosinfo.cpp", - "htmldocextractor.cpp", - "htmldocextractor.h", - "icon.cpp", - "icon.h", - "id.cpp", - "id.h", - "indexedcontainerproxyconstiterator.h", - "infobar.cpp", - "infobar.h", - "infolabel.cpp", - "infolabel.h", - "itemviews.cpp", - "itemviews.h", - "json.cpp", - "json.h", - "jsontreeitem.cpp", - "jsontreeitem.h", - "launcherinterface.cpp", - "launcherinterface.h", - "launcherpackets.cpp", - "launcherpackets.h", - "launchersocket.cpp", - "launchersocket.h", - "layoutbuilder.cpp", - "layoutbuilder.h", - "link.cpp", - "link.h", - "listmodel.h", - "listutils.h", - "macroexpander.cpp", - "macroexpander.h", - "mapreduce.h", - "mathutils.cpp", - "mathutils.h", - "mimeutils.h", - "minimizableinfobars.cpp", - "minimizableinfobars.h", - "multitextcursor.cpp", - "multitextcursor.h", - "namevaluedictionary.cpp", - "namevaluedictionary.h", - "namevalueitem.cpp", - "namevalueitem.h", - "namevaluemodel.cpp", - "namevaluemodel.h", - "namevaluesdialog.cpp", - "namevaluesdialog.h", - "namevaluevalidator.cpp", - "namevaluevalidator.h", - "navigationtreeview.cpp", - "navigationtreeview.h", - "networkaccessmanager.cpp", - "networkaccessmanager.h", - "optionpushbutton.h", - "optionpushbutton.cpp", - "osspecificaspects.h", - "outputformat.h", - "outputformatter.cpp", - "outputformatter.h", - "overlaywidget.cpp", - "overlaywidget.h", - "overridecursor.cpp", - "overridecursor.h", - "parameteraction.cpp", - "parameteraction.h", - "pathchooser.cpp", - "pathchooser.h", - "pathlisteditor.cpp", - "pathlisteditor.h", - "persistentsettings.cpp", - "persistentsettings.h", - "predicates.h", - "pointeralgorithm.h", - "port.cpp", - "port.h", - "portlist.cpp", - "portlist.h", - "process.cpp", - "process.h", - "processenums.h", - "processhandle.cpp", - "processhandle.h", - "processinfo.cpp", - "processinfo.h", - "processinterface.cpp", - "processinterface.h", - "processreaper.cpp", - "processreaper.h", - "processutils.cpp", - "processutils.h", - "progressindicator.cpp", - "progressindicator.h", - "projectintropage.cpp", - "projectintropage.h", - "proxyaction.cpp", - "proxyaction.h", - "qrcparser.cpp", - "qrcparser.h", - "qtcassert.cpp", - "qtcassert.h", - "qtcolorbutton.cpp", - "qtcolorbutton.h", - "qtcsettings.cpp", - "qtcsettings.h", - "reloadpromptutils.cpp", - "reloadpromptutils.h", - "removefiledialog.cpp", - "removefiledialog.h", - "runextensions.cpp", - "runextensions.h", - "savefile.cpp", - "savefile.h", - "scopedswap.h", - "scopedtimer.cpp", - "scopedtimer.h", - "searchresultitem.cpp", - "searchresultitem.h", - "set_algorithm.h", - "settingsaccessor.cpp", - "settingsaccessor.h", - "settingsselector.cpp", - "settingsselector.h", - "settingsutils.h", - "singleton.cpp", - "singleton.h", - "sizedarray.h", - "smallstring.h", - "smallstringiterator.h", - "smallstringio.h", - "smallstringliteral.h", - "smallstringlayout.h", - "smallstringmemory.h", - "smallstringvector.h", - "sortfiltermodel.h", - "span.h", - "../3rdparty/span/span.hpp", - "statuslabel.cpp", - "statuslabel.h", - "stringtable.cpp", - "stringtable.h", - "stringutils.cpp", - "stringutils.h", - "styleanimator.cpp", - "styleanimator.h", - "styledbar.cpp", - "styledbar.h", - "stylehelper.cpp", - "stylehelper.h", - "templateengine.cpp", - "templateengine.h", - "temporarydirectory.cpp", - "temporarydirectory.h", - "temporaryfile.cpp", - "temporaryfile.h", - "terminalcommand.cpp", - "terminalcommand.h", - "terminalhooks.cpp", - "terminalhooks.h", - "terminalinterface.cpp", - "terminalinterface.h", - "textfieldcheckbox.cpp", - "textfieldcheckbox.h", - "textfieldcombobox.cpp", - "textfieldcombobox.h", - "textfileformat.cpp", - "textfileformat.h", - "textutils.cpp", - "textutils.h", - "threadutils.cpp", - "threadutils.h", - "treemodel.cpp", - "treemodel.h", - "treeviewcombobox.cpp", - "treeviewcombobox.h", - "headerviewstretcher.cpp", - "headerviewstretcher.h", - "uncommentselection.cpp", - "uncommentselection.h", - "uniqueobjectptr.h", - "unixutils.cpp", - "unixutils.h", - "url.cpp", - "url.h", - "utils.qrc", - "utils_global.h", - "utilsicons.h", - "utilsicons.cpp", - "utilstr.h", - "variablechooser.cpp", - "variablechooser.h", - "winutils.cpp", - "winutils.h", - "wizard.cpp", - "wizard.h", - "wizardpage.cpp", - "wizardpage.h", - "images/*.png", + "diriterator.h", + "fileiconprovider.cpp", + "fileiconprovider.h", + "fileiteratordevicesappender.h", + "fixedlistfsengine.h", + "fsengine.cpp", + "fsengine.h", + "fsenginehandler.cpp", + "fsenginehandler.h", + "fsengine_impl.cpp", + "fsengine_impl.h", + "rootinjectfsengine.h", ] + } - Group { - name: "FSEngine" - prefix: "fsengine/" - cpp.defines: outer.concat("QTC_UTILS_WITH_FSENGINE") - files: [ - "diriterator.h", - "fileiconprovider.cpp", - "fileiconprovider.h", - "fileiteratordevicesappender.h", - "fixedlistfsengine.h", - "fsengine.cpp", - "fsengine.h", - "fsenginehandler.cpp", - "fsenginehandler.h", - "fsengine_impl.cpp", - "fsengine_impl.h", - "rootinjectfsengine.h", - ] - } + Group { + name: "Theme" + prefix: "theme/" + files: [ + "theme.cpp", + "theme.h", + "theme_p.h", + ] + } - Group { - name: "Theme" - prefix: "theme/" - files: [ - "theme.cpp", - "theme.h", - "theme_p.h", - ] - } + Group { + name: "Tooltip" + prefix: "tooltip/" + files: [ + "effects.h", + "tips.cpp", + "tips.h", + "tooltip.cpp", + "tooltip.h", + ] + } - Group { - name: "Tooltip" - prefix: "tooltip/" - files: [ - "effects.h", - "tips.cpp", - "tips.h", - "tooltip.cpp", - "tooltip.h", - ] - } + Group { + name: "FileUtils_macos" + condition: qbs.targetOS.contains("macos") + files: [ + "fileutils_mac.h", "fileutils_mac.mm", + ] + } + Group { + name: "Theme_macos" + condition: qbs.targetOS.contains("macos") + prefix: "theme/" + files: [ + "theme_mac.h", "theme_mac.mm", + ] + } + + Group { + name: "ProcessHandle_macos" + condition: qbs.targetOS.contains("macos") + files: [ + "processhandle_mac.mm", + ] + } + + Group { + name: "MimeTypes" + prefix: "mimetypes2/" + files: [ + "mimedatabase.cpp", + "mimedatabase.h", + "mimedatabase_p.h", + "mimeglobpattern.cpp", + "mimeglobpattern_p.h", + "mimemagicrule.cpp", + "mimemagicrule_p.h", + "mimemagicrulematcher.cpp", + "mimemagicrulematcher_p.h", + "mimeprovider.cpp", + "mimeprovider_p.h", + "mimetype.cpp", + "mimetype.h", + "mimetype_p.h", + "mimetypeparser.cpp", + "mimetypeparser_p.h", + "mimeutils.cpp" + ] + } + + Group { + name: "TouchBar support" + prefix: "touchbar/" + files: "touchbar.h" Group { - name: "FileUtils_macos" + name: "TouchBar implementation" condition: qbs.targetOS.contains("macos") files: [ - "fileutils_mac.h", "fileutils_mac.mm", + "touchbar_appdelegate_mac_p.h", + "touchbar_mac_p.h", + "touchbar_mac.mm", + "touchbar_appdelegate_mac.mm", ] } - Group { - name: "Theme_macos" - condition: qbs.targetOS.contains("macos") - prefix: "theme/" - files: [ - "theme_mac.h", "theme_mac.mm", - ] - } - - Group { - name: "ProcessHandle_macos" - condition: qbs.targetOS.contains("macos") - files: [ - "processhandle_mac.mm", - ] - } - - Group { - name: "MimeTypes" - prefix: "mimetypes2/" - files: [ - "mimedatabase.cpp", - "mimedatabase.h", - "mimedatabase_p.h", - "mimeglobpattern.cpp", - "mimeglobpattern_p.h", - "mimemagicrule.cpp", - "mimemagicrule_p.h", - "mimemagicrulematcher.cpp", - "mimemagicrulematcher_p.h", - "mimeprovider.cpp", - "mimeprovider_p.h", - "mimetype.cpp", - "mimetype.h", - "mimetype_p.h", - "mimetypeparser.cpp", - "mimetypeparser_p.h", - "mimeutils.cpp" - ] - } - - Group { - name: "TouchBar support" - prefix: "touchbar/" - files: "touchbar.h" - Group { - name: "TouchBar implementation" - condition: qbs.targetOS.contains("macos") - files: [ - "touchbar_appdelegate_mac_p.h", - "touchbar_mac_p.h", - "touchbar_mac.mm", - "touchbar_appdelegate_mac.mm", - ] - } - Group { - name: "TouchBar stub" - condition: !qbs.targetOS.contains("macos") - files: "touchbar.cpp" - } - } - - Export { - Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] } - Depends { name: "Tasking" } - cpp.includePaths: base.concat("mimetypes2") + name: "TouchBar stub" + condition: !qbs.targetOS.contains("macos") + files: "touchbar.cpp" } } + + Export { + Depends { name: "Qt"; submodules: ["concurrent", "widgets" ] } + Depends { name: "Tasking" } + cpp.includePaths: "mimetypes2" + } } diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index c0f2d2559a1..91070dc708b 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -48,6 +48,12 @@ <file>images/clean_pane_small@2x.png</file> <file>images/compile_error_taskbar.png</file> <file>images/compile_error_taskbar@2x.png</file> + <file>images/continue_1_small.png</file> + <file>images/continue_1_small@2x.png</file> + <file>images/continue_2_small.png</file> + <file>images/continue_2_small@2x.png</file> + <file>images/debugger_overlay_small.png</file> + <file>images/debugger_overlay_small@2x.png</file> <file>images/editcopy.png</file> <file>images/editcopy@2x.png</file> <file>images/editcut.png</file> diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index 01b1f0c1dbc..3e379ef5b96 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -63,6 +63,8 @@ const Icon BOOKMARK_TOOLBAR({ {":/utils/images/bookmark.png", Theme::IconsBaseColor}}); const Icon BOOKMARK_TEXTEDITOR({ {":/utils/images/bookmark.png", Theme::Bookmarks_TextMarkColor}}, Icon::Tint); +const Icon SNAPSHOT({ + {":/utils/images/snapshot.png", Theme::PanelTextColorMid}}, Icon::Tint); const Icon SNAPSHOT_TOOLBAR({ {":/utils/images/snapshot.png", Theme::IconsBaseColor}}); const Icon NEWSEARCH_TOOLBAR({ @@ -225,6 +227,12 @@ const Icon INTERRUPT_SMALL({ {":/utils/images/interrupt_small.png", Theme::IconsInterruptColor}}, Icon::MenuTintedStyle); const Icon INTERRUPT_SMALL_TOOLBAR({ {":/utils/images/interrupt_small.png", Theme::IconsInterruptToolBarColor}}); +const Icon CONTINUE_SMALL({ + {":/utils/images/continue_1_small.png", Theme::IconsInterruptColor}, + {":/utils/images/continue_2_small.png", Theme::IconsRunColor}}, Icon::MenuTintedStyle); +const Icon CONTINUE_SMALL_TOOLBAR({ + {":/utils/images/continue_1_small.png", Theme::IconsInterruptToolBarColor}, + {":/utils/images/continue_2_small.png", Theme::IconsRunToolBarColor}}); const Icon BOUNDING_RECT({ {":/utils/images/boundingrect.png", Theme::IconsBaseColor}}); const Icon EYE_OPEN({ @@ -288,6 +296,7 @@ const Icon MACOS_TOUCHBAR_BOOKMARK( ":/utils/images/macos_touchbar_bookmark.png"); const Icon MACOS_TOUCHBAR_CLEAR( ":/utils/images/macos_touchbar_clear.png"); + } // namespace Icons QIcon CodeModelIcon::iconForType(CodeModelIcon::Type type) diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index 5a75267a363..bef0ef517f2 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -36,6 +36,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon BROKEN; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TEXTEDITOR; +QTCREATOR_UTILS_EXPORT extern const Icon SNAPSHOT; QTCREATOR_UTILS_EXPORT extern const Icon SNAPSHOT_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon NEWSEARCH_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon SETTINGS; @@ -120,6 +121,9 @@ QTCREATOR_UTILS_EXPORT extern const Icon STOP_SMALL; QTCREATOR_UTILS_EXPORT extern const Icon STOP_SMALL_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon INTERRUPT_SMALL; QTCREATOR_UTILS_EXPORT extern const Icon INTERRUPT_SMALL_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon CONTINUE_SMALL; +QTCREATOR_UTILS_EXPORT extern const Icon CONTINUE_SMALL_TOOLBAR; + QTCREATOR_UTILS_EXPORT extern const Icon BOUNDING_RECT; QTCREATOR_UTILS_EXPORT extern const Icon EYE_OPEN; QTCREATOR_UTILS_EXPORT extern const Icon EYE_OPEN_TOOLBAR; @@ -148,6 +152,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon CODEMODEL_FIXIT; QTCREATOR_UTILS_EXPORT extern const Icon MACOS_TOUCHBAR_BOOKMARK; QTCREATOR_UTILS_EXPORT extern const Icon MACOS_TOUCHBAR_CLEAR; + } // namespace Icons namespace CodeModelIcon { diff --git a/src/libs/utils/variablechooser.cpp b/src/libs/utils/variablechooser.cpp index 07348b64894..22513d4bb45 100644 --- a/src/libs/utils/variablechooser.cpp +++ b/src/libs/utils/variablechooser.cpp @@ -81,7 +81,7 @@ public: void createIconButton() { - m_iconButton = new IconButton; + m_iconButton = new FancyIconButton; m_iconButton->setIcon(Icons::REPLACE.icon()); m_iconButton->setToolTip(Tr::tr("Insert Variable")); m_iconButton->hide(); @@ -108,7 +108,7 @@ public: QPointer<QLineEdit> m_lineEdit; QPointer<QTextEdit> m_textEdit; QPointer<QPlainTextEdit> m_plainTextEdit; - QPointer<IconButton> m_iconButton; + QPointer<FancyIconButton> m_iconButton; FancyLineEdit *m_variableFilter; VariableTreeView *m_variableTree; diff --git a/src/libs/utils/variablechooser.h b/src/libs/utils/variablechooser.h index 257dcea5e2c..2ad7aebfbf5 100644 --- a/src/libs/utils/variablechooser.h +++ b/src/libs/utils/variablechooser.h @@ -19,8 +19,6 @@ namespace Internal { class VariableChooserPrivate; } class QTCREATOR_UTILS_EXPORT VariableChooser : public QWidget { - Q_OBJECT - public: explicit VariableChooser(QWidget *parent = nullptr); ~VariableChooser() override; diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp index ce37d80705e..bbf22730a4f 100644 --- a/src/libs/utils/wizard.cpp +++ b/src/libs/utils/wizard.cpp @@ -5,6 +5,7 @@ #include "algorithm.h" #include "hostosinfo.h" +#include "icon.h" #include "qtcassert.h" #include "theme/theme.h" #include "utilstr.h" @@ -110,7 +111,7 @@ LinearProgressWidget::LinearProgressWidget(WizardProgress *progress, QWidget *pa m_dotsItemWidget(nullptr), m_disableUpdatesCount(0) { - m_indicatorPixmap = QIcon::fromTheme(QLatin1String("go-next"), QIcon(QLatin1String(":/utils/images/arrow.png"))).pixmap(16); + m_indicatorPixmap = Icon::fromTheme("go-next").pixmap(16); m_wizardProgress = progress; m_mainLayout = new QVBoxLayout(this); m_itemWidgetLayout = new QVBoxLayout(); @@ -630,7 +631,7 @@ QList<WizardProgressItem *> WizardProgressPrivate::singlePathBetween(WizardProgr if (!item) item = m_startItem; if (!item) - return QList<WizardProgressItem *>(); + return {}; // Optimization. It is workaround for case A->B, B->C, A->C where "from" is A and "to" is C. // When we had X->A in addition and "from" was X and "to" was C, this would not work @@ -666,7 +667,7 @@ QList<WizardProgressItem *> WizardProgressPrivate::singlePathBetween(WizardProgr while (itItem != itEnd) { path.prepend(itItem.key()); if (itItem.value().count() != 1) - return QList<WizardProgressItem *>(); + return {}; it = itItem.value().constBegin().key(); if (it == item) return path; diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 68985fbf000..5d7316dee61 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(serialterminal) add_subdirectory(helloworld) add_subdirectory(imageviewer) add_subdirectory(marketplace) +add_subdirectory(screenrecorder) add_subdirectory(updateinfo) add_subdirectory(welcome) @@ -21,7 +22,6 @@ add_subdirectory(silversearcher) # Level 3: (only depends on Level 2 and below) add_subdirectory(axivion) -add_subdirectory(bookmarks) add_subdirectory(cppeditor) add_subdirectory(haskell) add_subdirectory(help) @@ -116,3 +116,4 @@ add_subdirectory(terminal) if (WITH_QMLDESIGNER) add_subdirectory(effectmakernew) endif() +add_subdirectory(compilerexplorer) diff --git a/src/plugins/android/Android.json.in b/src/plugins/android/Android.json.in index 52aff652e8b..7a020c5061f 100644 --- a/src/plugins/android/Android.json.in +++ b/src/plugins/android/Android.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"Android\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"KDE Necessitas\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Android", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "KDE Necessitas", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Support for deployment to and execution on Android Devices.\", - \"Url\" : \"http://necessitas.kde.org\", - $$dependencyList, + "Category" : "Device Support", + "Description" : "Support for deployment to and execution on Android Devices.", + "Url" : "http://necessitas.kde.org", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/vnd.google.android.android_manifest\'>\", - \" <comment>Android manifest file</comment>\", - \" <sub-class-of type=\'application/xml\'/>\", - \" <glob pattern=\'AndroidManifest.xml\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/vnd.google.android.android_manifest'>", + " <comment>Android manifest file</comment>", + " <sub-class-of type='application/xml'/>", + " <glob pattern='AndroidManifest.xml'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/android/CMakeLists.txt b/src/plugins/android/CMakeLists.txt index 9246c94e829..6048a5ecd76 100644 --- a/src/plugins/android/CMakeLists.txt +++ b/src/plugins/android/CMakeLists.txt @@ -3,7 +3,6 @@ add_qtc_plugin(Android PLUGIN_DEPENDS Core Debugger ProjectExplorer QtSupport LanguageClient SOURCES android.qrc - android_global.h androidtr.h androidavdmanager.cpp androidavdmanager.h androidbuildapkstep.cpp androidbuildapkstep.h @@ -14,7 +13,6 @@ add_qtc_plugin(Android androiddeployqtstep.cpp androiddeployqtstep.h androiddevice.cpp androiddevice.h androiddeviceinfo.cpp androiddeviceinfo.h - androiderrormessage.cpp androiderrormessage.h androidextralibrarylistmodel.cpp androidextralibrarylistmodel.h androidglobal.h androidmanager.cpp androidmanager.h diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index c6d1611636f..81fd94bca76 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -1,129 +1,122 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "Android" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] } - Depends { name: "Core" } - Depends { name: "Debugger" } - Depends { name: "LanguageClient" } - Depends { name: "LanguageServerProtocol" } - Depends { name: "ProParser" } - Depends { name: "ProjectExplorer" } - Depends { name: "QmlDebug" } - Depends { name: "QtSupport" } - Depends { name: "TextEditor" } - Depends { name: "Utils" } - Depends { name: "app_version_header" } + Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] } + Depends { name: "Core" } + Depends { name: "Debugger" } + Depends { name: "LanguageClient" } + Depends { name: "LanguageServerProtocol" } + Depends { name: "ProParser" } + Depends { name: "ProjectExplorer" } + Depends { name: "QmlDebug" } + Depends { name: "QtSupport" } + Depends { name: "TextEditor" } + Depends { name: "Utils" } + files: [ + "androidtr.h", + "android.qrc", + "androidavdmanager.cpp", + "androidavdmanager.h", + "androidconfigurations.cpp", + "androidconfigurations.h", + "androidconstants.h", + "androidcreatekeystorecertificate.cpp", + "androidcreatekeystorecertificate.h", + "androidbuildapkstep.cpp", + "androidbuildapkstep.h", + "androiddeployqtstep.cpp", + "androiddeployqtstep.h", + "androiddebugsupport.cpp", + "androiddebugsupport.h", + "androiddevice.cpp", + "androiddevice.h", + "androiddeviceinfo.cpp", + "androiddeviceinfo.h", + "androidextralibrarylistmodel.cpp", + "androidextralibrarylistmodel.h", + "androidglobal.h", + "androidmanager.cpp", + "androidmanager.h", + "androidmanifestdocument.cpp", + "androidmanifestdocument.h", + "androidmanifesteditor.cpp", + "androidmanifesteditor.h", + "androidmanifesteditoriconwidget.cpp", + "androidmanifesteditoriconwidget.h", + "androidmanifesteditoriconcontainerwidget.cpp", + "androidmanifesteditoriconcontainerwidget.h", + "androidmanifesteditorfactory.cpp", + "androidmanifesteditorfactory.h", + "androidmanifesteditorwidget.cpp", + "androidmanifesteditorwidget.h", + "androidpackageinstallationstep.cpp", + "androidpackageinstallationstep.h", + "androidplugin.cpp", + "androidplugin.h", + "androidpotentialkit.cpp", + "androidpotentialkit.h", + "androidqmlpreviewworker.h", + "androidqmlpreviewworker.cpp", + "androidqmltoolingsupport.cpp", + "androidqmltoolingsupport.h", + "androidqtversion.cpp", + "androidqtversion.h", + "androidrunconfiguration.cpp", + "androidrunconfiguration.h", + "androidruncontrol.cpp", + "androidruncontrol.h", + "androidrunner.cpp", + "androidrunner.h", + "androidrunnerworker.cpp", + "androidrunnerworker.h", + "androidsdkdownloader.cpp", + "androidsdkdownloader.h", + "androidsdkmanager.cpp", + "androidsdkmanager.h", + "androidsdkmanagerwidget.cpp", + "androidsdkmanagerwidget.h", + "androidsdkmodel.cpp", + "androidsdkmodel.h", + "androidsdkpackage.cpp", + "androidsdkpackage.h", + "androidsettingswidget.cpp", + "androidsettingswidget.h", + "androidsignaloperation.cpp", + "androidsignaloperation.h", + "androidtoolchain.cpp", + "androidtoolchain.h", + "avddialog.cpp", + "avddialog.h", + "avdmanageroutputparser.cpp", + "avdmanageroutputparser.h", + "certificatesmodel.cpp", + "certificatesmodel.h", + "createandroidmanifestwizard.h", + "createandroidmanifestwizard.cpp", + "javaeditor.cpp", + "javaeditor.h", + "javaindenter.cpp", + "javaindenter.h", + "javalanguageserver.cpp", + "javalanguageserver.h", + "javaparser.cpp", + "javaparser.h", + "splashscreencontainerwidget.cpp", + "splashscreencontainerwidget.h", + "splashscreenwidget.cpp", + "splashscreenwidget.h", + "sdkmanageroutputparser.cpp", + "sdkmanageroutputparser.h" + ] + + QtcTestFiles { files: [ - "android_global.h", "androidtr.h", - "android.qrc", - "androidavdmanager.cpp", - "androidavdmanager.h", - "androidconfigurations.cpp", - "androidconfigurations.h", - "androidconstants.h", - "androidcreatekeystorecertificate.cpp", - "androidcreatekeystorecertificate.h", - "androidbuildapkstep.cpp", - "androidbuildapkstep.h", - "androiddeployqtstep.cpp", - "androiddeployqtstep.h", - "androiddebugsupport.cpp", - "androiddebugsupport.h", - "androiddevice.cpp", - "androiddevice.h", - "androiddeviceinfo.cpp", - "androiddeviceinfo.h", - "androiderrormessage.h", - "androiderrormessage.cpp", - "androidextralibrarylistmodel.cpp", - "androidextralibrarylistmodel.h", - "androidglobal.h", - "androidmanager.cpp", - "androidmanager.h", - "androidmanifestdocument.cpp", - "androidmanifestdocument.h", - "androidmanifesteditor.cpp", - "androidmanifesteditor.h", - "androidmanifesteditoriconwidget.cpp", - "androidmanifesteditoriconwidget.h", - "androidmanifesteditoriconcontainerwidget.cpp", - "androidmanifesteditoriconcontainerwidget.h", - "androidmanifesteditorfactory.cpp", - "androidmanifesteditorfactory.h", - "androidmanifesteditorwidget.cpp", - "androidmanifesteditorwidget.h", - "androidpackageinstallationstep.cpp", - "androidpackageinstallationstep.h", - "androidplugin.cpp", - "androidplugin.h", - "androidpotentialkit.cpp", - "androidpotentialkit.h", - "androidqmlpreviewworker.h", - "androidqmlpreviewworker.cpp", - "androidqmltoolingsupport.cpp", - "androidqmltoolingsupport.h", - "androidqtversion.cpp", - "androidqtversion.h", - "androidrunconfiguration.cpp", - "androidrunconfiguration.h", - "androidruncontrol.cpp", - "androidruncontrol.h", - "androidrunner.cpp", - "androidrunner.h", - "androidrunnerworker.cpp", - "androidrunnerworker.h", - "androidsdkdownloader.cpp", - "androidsdkdownloader.h", - "androidsdkmanager.cpp", - "androidsdkmanager.h", - "androidsdkmanagerwidget.cpp", - "androidsdkmanagerwidget.h", - "androidsdkmodel.cpp", - "androidsdkmodel.h", - "androidsdkpackage.cpp", - "androidsdkpackage.h", - "androidsettingswidget.cpp", - "androidsettingswidget.h", - "androidsignaloperation.cpp", - "androidsignaloperation.h", - "androidtoolchain.cpp", - "androidtoolchain.h", - "avddialog.cpp", - "avddialog.h", - "avdmanageroutputparser.cpp", - "avdmanageroutputparser.h", - "certificatesmodel.cpp", - "certificatesmodel.h", - "createandroidmanifestwizard.h", - "createandroidmanifestwizard.cpp", - "javaeditor.cpp", - "javaeditor.h", - "javaindenter.cpp", - "javaindenter.h", - "javalanguageserver.cpp", - "javalanguageserver.h", - "javaparser.cpp", - "javaparser.h", - "splashscreencontainerwidget.cpp", - "splashscreencontainerwidget.h", - "splashscreenwidget.cpp", - "splashscreenwidget.h", - "sdkmanageroutputparser.cpp", - "sdkmanageroutputparser.h" + "android_tst.qrc", + "androidsdkmanager_test.cpp", + "androidsdkmanager_test.h", + "sdkmanageroutputparser_test.cpp", + "sdkmanageroutputparser_test.h", ] - - QtcTestFiles { - files: [ - "android_tst.qrc", - "androidsdkmanager_test.cpp", - "androidsdkmanager_test.h", - "sdkmanageroutputparser_test.cpp", - "sdkmanageroutputparser_test.h", - ] - } } } diff --git a/src/plugins/android/android_global.h b/src/plugins/android/android_global.h deleted file mode 100644 index 377e46f773f..00000000000 --- a/src/plugins/android/android_global.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <qglobal.h> - -#if defined(ANDROID_LIBRARY) -# define ANDROID_EXPORT Q_DECL_EXPORT -#elif defined(ANDROID_STATIC_LIBRARY) -# define ANDROID_EXPORT -#else -# define ANDROID_EXPORT Q_DECL_IMPORT -#endif diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index e5568ef541f..dfb78ed29cd 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -38,8 +38,7 @@ bool AndroidAvdManager::avdManagerCommand(const AndroidConfig &config, const QSt { CommandLine cmd(config.avdManagerToolPath(), args); Process proc; - Environment env = AndroidConfigurations::toolsEnvironment(config); - proc.setEnvironment(env); + proc.setEnvironment(config.toolsEnvironment()); qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput(); proc.setCommand(cmd); proc.runBlocking(); @@ -87,7 +86,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA qCDebug(avdManagerLog).noquote() << "Running AVD Manager command:" << avdManager.toUserOutput(); Process proc; proc.setProcessMode(ProcessMode::Writer); - proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config)); + proc.setEnvironment(config.toolsEnvironment()); proc.setCommand(avdManager); proc.start(); if (!proc.waitForStarted()) { @@ -142,18 +141,6 @@ QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const return Utils::asyncRun(&createAvdCommand, m_config, info); } -bool AndroidAvdManager::removeAvd(const QString &name) const -{ - const CommandLine command(m_config.avdManagerToolPath(), {"delete", "avd", "-n", name}); - qCDebug(avdManagerLog).noquote() << "Running command (removeAvd):" << command.toUserOutput(); - Process proc; - proc.setTimeoutS(5); - proc.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); - proc.setCommand(command); - proc.runBlocking(); - return proc.result() == ProcessResult::FinishedWithSuccess; -} - static void avdConfigEditManufacturerTag(const FilePath &avdPath, bool recoverMode = false) { if (!avdPath.exists()) diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 545dedbfe21..807bf2258ed 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -17,7 +17,6 @@ public: ~AndroidAvdManager(); QFuture<CreateAvdInfo> createAvd(CreateAvdInfo info) const; - bool removeAvd(const QString &name) const; QFuture<AndroidDeviceInfoList> avdList() const; QString startAvd(const QString &name) const; @@ -28,6 +27,7 @@ public: static bool avdManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output); + const AndroidConfig &config() const { return m_config; } private: bool waitForBooted(const QString &serialNumber, const std::optional<QFuture<void>> &future = {}) const; diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index b27730b45b0..3e840715499 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -29,7 +29,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/fancylineedit.h> @@ -72,7 +72,6 @@ static Q_LOGGING_CATEGORY(buildapkstepLog, "qtc.android.build.androidbuildapkste const char KeystoreLocationKey[] = "KeystoreLocation"; const char BuildTargetSdkKey[] = "BuildTargetSdk"; const char BuildToolsVersionKey[] = "BuildToolsVersion"; -const char VerboseOutputKey[] = "VerboseOutput"; class PasswordInputDialog : public QDialog { @@ -115,11 +114,6 @@ private: FilePath appProjectFilePath() const; QString openSslIncludeFileContent(const FilePath &projectPath); - QWidget *createApplicationGroup(); - QWidget *createSignPackageGroup(); - QWidget *createAdvancedGroup(); - QWidget *createAdditionalLibrariesGroup(); - private: AndroidBuildApkStep *m_step = nullptr; QCheckBox *m_signPackageCheckBox = nullptr; @@ -132,35 +126,85 @@ private: AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) : m_step(step) { + QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + + + // Application Signature Group + + auto keystoreLocationChooser = new PathChooser; + keystoreLocationChooser->setExpectedKind(PathChooser::File); + keystoreLocationChooser->lineEdit()->setReadOnly(true); + keystoreLocationChooser->setFilePath(m_step->keystorePath()); + keystoreLocationChooser->setInitialBrowsePathBackup(FileUtils::homePath()); + keystoreLocationChooser->setPromptDialogFilter(Tr::tr("Keystore files (*.keystore *.jks)")); + keystoreLocationChooser->setPromptDialogTitle(Tr::tr("Select Keystore File")); + connect(keystoreLocationChooser, &PathChooser::textChanged, this, [this, keystoreLocationChooser] { + const FilePath file = keystoreLocationChooser->rawFilePath(); + m_step->setKeystorePath(file); + m_signPackageCheckBox->setChecked(!file.isEmpty()); + if (!file.isEmpty()) + setCertificates(); + }); + + auto keystoreCreateButton = new QPushButton(Tr::tr("Create...")); + connect(keystoreCreateButton, &QAbstractButton::clicked, this, [this, keystoreLocationChooser] { + AndroidCreateKeystoreCertificate d; + if (d.exec() != QDialog::Accepted) + return; + keystoreLocationChooser->setFilePath(d.keystoreFilePath()); + m_step->setKeystorePath(d.keystoreFilePath()); + m_step->setKeystorePassword(d.keystorePassword()); + m_step->setCertificateAlias(d.certificateAlias()); + m_step->setCertificatePassword(d.certificatePassword()); + setCertificates(); + }); + + m_signPackageCheckBox = new QCheckBox(Tr::tr("Sign package")); + m_signPackageCheckBox->setChecked(m_step->signPackage()); + + m_signingDebugWarningLabel = new InfoLabel(Tr::tr("Signing a debug package"), + InfoLabel::Warning); + m_signingDebugWarningLabel->hide(); + m_signingDebugWarningLabel->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred); + + m_certificatesAliasComboBox = new QComboBox; + m_certificatesAliasComboBox->setEnabled(false); + m_certificatesAliasComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + using namespace Layouting; - Column { - createSignPackageGroup(), - createApplicationGroup(), - createAdvancedGroup(), - createAdditionalLibrariesGroup(), - noMargin - }.attachTo(this); + Group signPackageGroup { + title(Tr::tr("Application Signature")), + Form { + Tr::tr("Keystore:"), keystoreLocationChooser, keystoreCreateButton, br, + m_signPackageCheckBox, br, + Tr::tr("Certificate alias:"), m_certificatesAliasComboBox, + m_signingDebugWarningLabel, st, br, + } + }; - connect(m_step->buildConfiguration(), &BuildConfiguration::buildTypeChanged, - this, &AndroidBuildApkWidget::updateSigningWarning); + connect(m_signPackageCheckBox, &QAbstractButton::toggled, + this, &AndroidBuildApkWidget::signPackageCheckBoxToggled); - connect(m_signPackageCheckBox, &QAbstractButton::clicked, - m_addDebuggerCheckBox, &QWidget::setEnabled); + auto updateAlias = [this](int idx) { + QString alias = m_certificatesAliasComboBox->itemText(idx); + if (!alias.isEmpty()) + m_step->setCertificateAlias(alias); + }; - signPackageCheckBoxToggled(m_step->signPackage()); - updateSigningWarning(); -} + connect(m_certificatesAliasComboBox, &QComboBox::activated, this, updateAlias); + connect(m_certificatesAliasComboBox, &QComboBox::currentIndexChanged, this, updateAlias); + + // Application group -QWidget *AndroidBuildApkWidget::createApplicationGroup() -{ QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(m_step->target()->kit()); const int minApiSupported = AndroidManager::defaultMinimumSDK(qt); QStringList targets = AndroidConfig::apiLevelNamesFor(AndroidConfigurations::sdkManager()-> filteredSdkPlatforms(minApiSupported)); targets.removeDuplicates(); - auto group = new QGroupBox(Tr::tr("Application"), this); - auto targetSDKComboBox = new QComboBox(); targetSDKComboBox->addItems(targets); targetSDKComboBox->setCurrentIndex(targets.indexOf(m_step->buildTargetSdk())); @@ -190,9 +234,6 @@ QWidget *AndroidBuildApkWidget::createApplicationGroup() : buildToolsVersions.indexOf(m_step->buildToolsVersion()); buildToolsSdkComboBox->setCurrentIndex(initIdx); - auto formLayout = new QFormLayout(group); - formLayout->addRow(Tr::tr("Android build-tools version:"), buildToolsSdkComboBox); - formLayout->addRow(Tr::tr("Android build platform SDK:"), targetSDKComboBox); auto createAndroidTemplatesButton = new QPushButton(Tr::tr("Create Templates")); createAndroidTemplatesButton->setToolTip( @@ -202,96 +243,19 @@ QWidget *AndroidBuildApkWidget::createApplicationGroup() wizard.exec(); }); - formLayout->addRow(Tr::tr("Android customization:"), createAndroidTemplatesButton); - - return group; -} - -QWidget *AndroidBuildApkWidget::createSignPackageGroup() -{ - QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - - auto group = new QGroupBox(Tr::tr("Application Signature"), this); - - auto keystoreLocationLabel = new QLabel(Tr::tr("Keystore:"), group); - keystoreLocationLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); - - auto keystoreLocationChooser = new PathChooser(group); - keystoreLocationChooser->setExpectedKind(PathChooser::File); - keystoreLocationChooser->lineEdit()->setReadOnly(true); - keystoreLocationChooser->setFilePath(m_step->keystorePath()); - keystoreLocationChooser->setInitialBrowsePathBackup(FileUtils::homePath()); - keystoreLocationChooser->setPromptDialogFilter(Tr::tr("Keystore files (*.keystore *.jks)")); - keystoreLocationChooser->setPromptDialogTitle(Tr::tr("Select Keystore File")); - connect(keystoreLocationChooser, &PathChooser::textChanged, this, [this, keystoreLocationChooser] { - const FilePath file = keystoreLocationChooser->rawFilePath(); - m_step->setKeystorePath(file); - m_signPackageCheckBox->setChecked(!file.isEmpty()); - if (!file.isEmpty()) - setCertificates(); - }); - - auto keystoreCreateButton = new QPushButton(Tr::tr("Create..."), group); - connect(keystoreCreateButton, &QAbstractButton::clicked, this, [this, keystoreLocationChooser] { - AndroidCreateKeystoreCertificate d; - if (d.exec() != QDialog::Accepted) - return; - keystoreLocationChooser->setFilePath(d.keystoreFilePath()); - m_step->setKeystorePath(d.keystoreFilePath()); - m_step->setKeystorePassword(d.keystorePassword()); - m_step->setCertificateAlias(d.certificateAlias()); - m_step->setCertificatePassword(d.certificatePassword()); - setCertificates(); - }); - - m_signPackageCheckBox = new QCheckBox(Tr::tr("Sign package"), group); - m_signPackageCheckBox->setChecked(m_step->signPackage()); - - m_signingDebugWarningLabel = new Utils::InfoLabel(Tr::tr("Signing a debug package"), - Utils::InfoLabel::Warning, group); - m_signingDebugWarningLabel->hide(); - - auto certificateAliasLabel = new QLabel(Tr::tr("Certificate alias:"), group); - certificateAliasLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter); - - m_certificatesAliasComboBox = new QComboBox(group); - m_certificatesAliasComboBox->setEnabled(false); - m_certificatesAliasComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); - - using namespace Layouting; - Column { - Row { keystoreLocationLabel, keystoreLocationChooser, keystoreCreateButton }, - m_signPackageCheckBox, - Row { m_signingDebugWarningLabel, certificateAliasLabel, m_certificatesAliasComboBox } - }.attachTo(group); - - connect(m_signPackageCheckBox, &QAbstractButton::toggled, - this, &AndroidBuildApkWidget::signPackageCheckBoxToggled); - - auto updateAlias = [this](int idx) { - QString alias = m_certificatesAliasComboBox->itemText(idx); - if (!alias.isEmpty()) - m_step->setCertificateAlias(alias); + Group applicationGroup { + title(Tr::tr("Application")), + Form { + Tr::tr("Android build-tools version:"), buildToolsSdkComboBox, br, + Tr::tr("Android build platform SDK:"), targetSDKComboBox, br, + Tr::tr("Android customization:"), createAndroidTemplatesButton, + } }; - connect(m_certificatesAliasComboBox, &QComboBox::activated, this, updateAlias); - connect(m_certificatesAliasComboBox, &QComboBox::currentIndexChanged, this, updateAlias); - return group; -} + // Advanced Actions group -QWidget *AndroidBuildApkWidget::createAdvancedGroup() -{ - auto group = new QGroupBox(Tr::tr("Advanced Actions"), this); - - auto openPackageLocationCheckBox = new QCheckBox(Tr::tr("Open package location after build"), group); - openPackageLocationCheckBox->setChecked(m_step->openPackageLocation()); - connect(openPackageLocationCheckBox, &QAbstractButton::toggled, - this, [this](bool checked) { m_step->setOpenPackageLocation(checked); }); - - m_addDebuggerCheckBox = new QCheckBox(Tr::tr("Add debug server"), group); + m_addDebuggerCheckBox = new QCheckBox(Tr::tr("Add debug server")); m_addDebuggerCheckBox->setEnabled(false); m_addDebuggerCheckBox->setToolTip(Tr::tr("Packages debug server with " "the APK to enable debugging. For the signed APK this option is unchecked by default.")); @@ -299,36 +263,26 @@ QWidget *AndroidBuildApkWidget::createAdvancedGroup() connect(m_addDebuggerCheckBox, &QAbstractButton::toggled, m_step, &AndroidBuildApkStep::setAddDebugger); - auto verboseOutputCheckBox = new QCheckBox(Tr::tr("Verbose output"), group); - verboseOutputCheckBox->setChecked(m_step->verboseOutput()); + Group advancedGroup { + title(Tr::tr("Advanced Actions")), + Column { + m_step->buildAAB, + m_step->openPackageLocation, + m_step->verboseOutput, + m_addDebuggerCheckBox + } + }; - auto vbox = new QVBoxLayout(group); - QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(m_step->kit()); - if (version && version->qtVersion() >= QVersionNumber(5, 14)) { - auto buildAAB = new QCheckBox(Tr::tr("Build Android App Bundle (*.aab)"), group); - buildAAB->setChecked(m_step->buildAAB()); - connect(buildAAB, &QAbstractButton::toggled, m_step, &AndroidBuildApkStep::setBuildAAB); - vbox->addWidget(buildAAB); - } - vbox->addWidget(openPackageLocationCheckBox); - vbox->addWidget(verboseOutputCheckBox); - vbox->addWidget(m_addDebuggerCheckBox); - connect(verboseOutputCheckBox, &QAbstractButton::toggled, - this, [this](bool checked) { m_step->setVerboseOutput(checked); }); + // Additional Libraries group - return group; -} - -QWidget *AndroidBuildApkWidget::createAdditionalLibrariesGroup() -{ - auto group = new QGroupBox(Tr::tr("Additional Libraries")); - group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + auto additionalLibrariesGroup = new QGroupBox(Tr::tr("Additional Libraries")); + additionalLibrariesGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); auto libsModel = new AndroidExtraLibraryListModel(m_step->buildSystem(), this); connect(libsModel, &AndroidExtraLibraryListModel::enabledChanged, this, - [this, group](const bool enabled) { - group->setEnabled(enabled); + [this, additionalLibrariesGroup](const bool enabled) { + additionalLibrariesGroup->setEnabled(enabled); m_openSslCheckBox->setChecked(isOpenSslLibsIncluded()); }); @@ -364,11 +318,10 @@ QWidget *AndroidBuildApkWidget::createAdditionalLibrariesGroup() connect(m_openSslCheckBox, &QAbstractButton::clicked, this, &AndroidBuildApkWidget::onOpenSslCheckBoxChanged); - using namespace Layouting; Grid { m_openSslCheckBox, br, libsView, Column { addLibButton, removeLibButton, st } - }.attachTo(group); + }.attachTo(additionalLibrariesGroup); QItemSelectionModel *libSelection = libsView->selectionModel(); connect(libSelection, &QItemSelectionModel::selectionChanged, this, [libSelection, removeLibButton] { @@ -378,9 +331,26 @@ QWidget *AndroidBuildApkWidget::createAdditionalLibrariesGroup() Target *target = m_step->target(); const QString buildKey = target->activeBuildKey(); const ProjectNode *node = target->project()->findNodeForBuildKey(buildKey); - group->setEnabled(node && !node->parseInProgress()); + additionalLibrariesGroup->setEnabled(node && !node->parseInProgress()); - return group; + // main layout + + Column { + signPackageGroup, + applicationGroup, + advancedGroup, + additionalLibrariesGroup, + noMargin + }.attachTo(this); + + connect(m_step->buildConfiguration(), &BuildConfiguration::buildTypeChanged, + this, &AndroidBuildApkWidget::updateSigningWarning); + + connect(m_signPackageCheckBox, &QAbstractButton::clicked, + m_addDebuggerCheckBox, &QWidget::setEnabled); + + signPackageCheckBoxToggled(m_step->signPackage()); + updateSigningWarning(); } void AndroidBuildApkWidget::signPackageCheckBoxToggled(bool checked) @@ -450,8 +420,7 @@ QString AndroidBuildApkWidget::openSslIncludeFileContent(const FilePath &project return "android: include(" + openSslPath + "/openssl.pri)"; if (projectPath.endsWith("CMakeLists.txt")) return "if (ANDROID)\n include(" + openSslPath + "/CMakeLists.txt)\nendif()"; - - return QString(); + return {}; } void AndroidBuildApkWidget::setCertificates() @@ -480,6 +449,18 @@ AndroidBuildApkStep::AndroidBuildApkStep(BuildStepList *parent, Utils::Id id) setImmutable(true); setDisplayName(Tr::tr("Build Android APK")); + QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit()); + + // FIXME: This is not saved due to missing setSettingsKey(). Intentional? + buildAAB.setLabelText(Tr::tr("Build Android App Bundle (*.aab)")); + buildAAB.setVisible(version && version->qtVersion() >= QVersionNumber(5, 14)); + + // FIXME: This is not saved due to missing setSettingsKey(). Intentional? + openPackageLocation.setLabelText(Tr::tr("Open package location after build")); + + verboseOutput.setSettingsKey("VerboseOutput"); + verboseOutput.setLabelText(Tr::tr("Verbose output")); + connect(this, &BuildStep::addOutput, this, [this](const QString &string, OutputFormat format) { if (format == OutputFormat::Stderr) stdError(string); @@ -532,7 +513,7 @@ bool AndroidBuildApkStep::init() return false; } - m_openPackageLocationForRun = m_openPackageLocation; + m_openPackageLocationForRun = openPackageLocation(); const FilePath outputDir = AndroidManager::androidBuildDirectory(target()); m_packagePath = AndroidManager::packagePath(target()); @@ -562,12 +543,12 @@ bool AndroidBuildApkStep::init() "--android-platform", m_buildTargetSdk, "--jdk", AndroidConfigurations::currentConfig().openJDKLocation().path()}; - if (m_verbose) + if (verboseOutput()) arguments << "--verbose"; arguments << "--gradle"; - if (m_buildAAB) + if (buildAAB()) arguments << "--aab" << "--jarsigner"; if (buildType() == BuildConfiguration::Release) { @@ -632,13 +613,6 @@ QWidget *AndroidBuildApkStep::createConfigWidget() return new AndroidBuildApkWidget(this); } -void AndroidBuildApkStep::finish(ProcessResult result) -{ - if (m_openPackageLocationForRun && isSuccess(result)) - QTimer::singleShot(0, this, &AndroidBuildApkStep::showInGraphicalShell); - AbstractProcessStep::finish(result); -} - bool AndroidBuildApkStep::verifyKeystorePassword() { if (!m_keystorePath.exists()) { @@ -703,42 +677,31 @@ static bool copyFileIfNewer(const FilePath &sourceFilePath, return true; } -void AndroidBuildApkStep::doRun() +Tasking::GroupItem AndroidBuildApkStep::runRecipe() { - if (m_skipBuilding) { - reportWarningOrError(Tr::tr("Android deploy settings file not found, not building an APK."), - Task::Error); - emit finished(true); - return; - } - - if (AndroidManager::skipInstallationAndPackageSteps(target())) { - reportWarningOrError(Tr::tr("Product type is not an application, not building an APK."), - Task::Warning); - emit finished(true); - return; - } - - auto setup = [this] { - const auto androidAbis = AndroidManager::applicationAbis(target()); - const QString buildKey = target()->activeBuildKey(); + using namespace Tasking; + const auto setupHelper = [this] { QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit()); if (!version) { reportWarningOrError(Tr::tr("The Qt version for kit %1 is invalid.") - .arg(kit()->displayName()), Task::Error); + .arg(kit()->displayName()), Task::Error); return false; } + const auto androidAbis = AndroidManager::applicationAbis(target()); + const QString buildKey = target()->activeBuildKey(); const FilePath buildDir = buildDirectory(); const FilePath androidBuildDir = AndroidManager::androidBuildDirectory(target()); for (const auto &abi : androidAbis) { FilePath androidLibsDir = androidBuildDir / "libs" / abi; if (!androidLibsDir.exists()) { if (!androidLibsDir.ensureWritableDir()) { - reportWarningOrError(Tr::tr("The Android build folder %1 was not found and could " - "not be created.").arg(androidLibsDir.toUserOutput()), - Task::Error); + reportWarningOrError( + Tr::tr("The Android build folder \"%1\" was not found and could " + "not be created.") + .arg(androidLibsDir.toUserOutput()), + Task::Error); return false; } else if (version->qtVersion() >= QVersionNumber(6, 0, 0) && version->qtVersion() <= QVersionNumber(6, 1, 1)) { @@ -752,25 +715,24 @@ void AndroidBuildApkStep::doRun() continue; if (!from.copyFile(to)) { - reportWarningOrError(Tr::tr("Cannot copy the target's lib file %1 to the " - "Android build folder %2.") - .arg(fileName, androidLibsDir.toUserOutput()), + reportWarningOrError(Tr::tr("Cannot copy the target's lib file \"%1\" to " + "the Android build folder \"%2\".") + .arg(fileName, androidLibsDir.toUserOutput()), Task::Error); return false; } } } - } - bool inputExists = m_inputFile.exists(); + const bool inputExists = m_inputFile.exists(); if (inputExists && !AndroidManager::isQtCreatorGenerated(m_inputFile)) return true; // use the generated file if it was not generated by qtcreator BuildSystem *bs = buildSystem(); const FilePaths targets = Utils::transform( - bs->extraData(buildKey, Android::Constants::AndroidTargets).toStringList(), - &FilePath::fromUserInput); + bs->extraData(buildKey, Android::Constants::AndroidTargets).toStringList(), + &FilePath::fromUserInput); if (targets.isEmpty()) return inputExists; // qmake does this job for us @@ -783,9 +745,9 @@ void AndroidBuildApkStep::doRun() for (const FilePath &target : targets) { if (!copyFileIfNewer(target, androidLibsDir.pathAppended(target.fileName()))) { reportWarningOrError( - Tr::tr("Cannot copy file \"%1\" to Android build libs folder \"%2\".") - .arg(target.toUserOutput()).arg(androidLibsDir.toUserOutput()), - Task::Error); + Tr::tr("Cannot copy file \"%1\" to Android build libs folder \"%2\".") + .arg(target.toUserOutput()).arg(androidLibsDir.toUserOutput()), + Task::Error); return false; } } @@ -821,41 +783,61 @@ void AndroidBuildApkStep::doRun() } deploySettings["application-binary"] = applicationBinary; - QString extraLibs = bs->extraData(buildKey, Android::Constants::AndroidExtraLibs).toString(); + const QString extraLibs = bs->extraData(buildKey, Android::Constants::AndroidExtraLibs).toString(); if (!extraLibs.isEmpty()) deploySettings["android-extra-libs"] = extraLibs; - QString androidSrcs = bs->extraData(buildKey, Android::Constants::AndroidPackageSourceDir).toString(); + const QString androidSrcs = bs->extraData(buildKey, Android::Constants::AndroidPackageSourceDir).toString(); if (!androidSrcs.isEmpty()) deploySettings["android-package-source-directory"] = androidSrcs; - QString qmlImportPath = bs->extraData(buildKey, "QML_IMPORT_PATH").toString(); + const QString qmlImportPath = bs->extraData(buildKey, "QML_IMPORT_PATH").toString(); if (!qmlImportPath.isEmpty()) deploySettings["qml-import-paths"] = qmlImportPath; QString qmlRootPath = bs->extraData(buildKey, "QML_ROOT_PATH").toString(); if (qmlRootPath.isEmpty()) qmlRootPath = target()->project()->rootProjectDirectory().toString(); - deploySettings["qml-root-path"] = qmlRootPath; + deploySettings["qml-root-path"] = qmlRootPath; QFile f{m_inputFile.toString()}; if (!f.open(QIODevice::WriteOnly)) { reportWarningOrError(Tr::tr("Cannot open androiddeployqt input file \"%1\" for writing.") - .arg(m_inputFile.toUserOutput()), Task::Error); + .arg(m_inputFile.toUserOutput()), Task::Error); return false; } f.write(QJsonDocument{deploySettings}.toJson()); return true; }; - if (!setup()) { - reportWarningOrError(Tr::tr("Cannot set up \"%1\", not building an APK.").arg(displayName()), - Task::Error); - emit finished(false); - return; - } + const auto onSetup = [this, setupHelper] { + if (m_skipBuilding) { + reportWarningOrError(Tr::tr("Android deploy settings file not found, " + "not building an APK."), Task::Error); + return SetupResult::StopWithDone; + } + if (AndroidManager::skipInstallationAndPackageSteps(target())) { + reportWarningOrError(Tr::tr("Product type is not an application, not building an APK."), + Task::Warning); + return SetupResult::StopWithDone; + } + if (setupHelper()) + return SetupResult::Continue; + reportWarningOrError(Tr::tr("Cannot set up \"%1\", not building an APK.") + .arg(displayName()), Task::Error); + return SetupResult::StopWithError; + }; + const auto onDone = [this] { + if (m_openPackageLocationForRun) + QTimer::singleShot(0, this, &AndroidBuildApkStep::showInGraphicalShell); + }; - AbstractProcessStep::doRun(); + const Group root { + onGroupSetup(onSetup), + onGroupDone(onDone), + defaultProcessTask() + }; + return root; } void AndroidBuildApkStep::reportWarningOrError(const QString &message, Task::TaskType type) @@ -881,7 +863,7 @@ void AndroidBuildApkStep::updateBuildToolsVersionInJsonFile() } } -bool AndroidBuildApkStep::fromMap(const QVariantMap &map) +void AndroidBuildApkStep::fromMap(const Store &map) { m_keystorePath = FilePath::fromSettings(map.value(KeystoreLocationKey)); m_signPackage = false; // don't restore this @@ -891,18 +873,15 @@ bool AndroidBuildApkStep::fromMap(const QVariantMap &map) m_buildTargetSdk = AndroidConfig::apiLevelNameFor(AndroidConfigurations:: sdkManager()->latestAndroidSdkPlatform()); } - m_verbose = map.value(VerboseOutputKey).toBool(); - return ProjectExplorer::BuildStep::fromMap(map); + ProjectExplorer::BuildStep::fromMap(map); } -QVariantMap AndroidBuildApkStep::toMap() const +void AndroidBuildApkStep::toMap(Store &map) const { - QVariantMap map = ProjectExplorer::AbstractProcessStep::toMap(); + ProjectExplorer::AbstractProcessStep::toMap(map); map.insert(KeystoreLocationKey, m_keystorePath.toSettings()); map.insert(BuildTargetSdkKey, m_buildTargetSdk); map.insert(BuildToolsVersionKey, m_buildToolsVersion.toString()); - map.insert(VerboseOutputKey, m_verbose); - return map; } Utils::FilePath AndroidBuildApkStep::keystorePath() const @@ -1000,31 +979,6 @@ void AndroidBuildApkStep::setSignPackage(bool b) m_signPackage = b; } -bool AndroidBuildApkStep::buildAAB() const -{ - return m_buildAAB; -} - -void AndroidBuildApkStep::setBuildAAB(bool aab) -{ - m_buildAAB = aab; -} - -bool AndroidBuildApkStep::openPackageLocation() const -{ - return m_openPackageLocation; -} - -void AndroidBuildApkStep::setOpenPackageLocation(bool open) -{ - m_openPackageLocation = open; -} - -void AndroidBuildApkStep::setVerboseOutput(bool verbose) -{ - m_verbose = verbose; -} - bool AndroidBuildApkStep::addDebugger() const { return m_addDebugger; @@ -1035,11 +989,6 @@ void AndroidBuildApkStep::setAddDebugger(bool debug) m_addDebugger = debug; } -bool AndroidBuildApkStep::verboseOutput() const -{ - return m_verbose; -} - QAbstractItemModel *AndroidBuildApkStep::keystoreCertificates() { // check keystore passwords diff --git a/src/plugins/android/androidbuildapkstep.h b/src/plugins/android/androidbuildapkstep.h index 89ddd208a54..0280290dfd2 100644 --- a/src/plugins/android/androidbuildapkstep.h +++ b/src/plugins/android/androidbuildapkstep.h @@ -23,8 +23,8 @@ class AndroidBuildApkStep : public ProjectExplorer::AbstractProcessStep public: AndroidBuildApkStep(ProjectExplorer::BuildStepList *bc, Utils::Id id); - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; // signing Utils::FilePath keystorePath() const; @@ -37,14 +37,9 @@ public: bool signPackage() const; void setSignPackage(bool b); - bool buildAAB() const; - void setBuildAAB(bool aab); - - bool openPackageLocation() const; - void setOpenPackageLocation(bool open); - - bool verboseOutput() const; - void setVerboseOutput(bool verbose); + Utils::BoolAspect buildAAB{this}; + Utils::BoolAspect openPackageLocation{this}; + Utils::BoolAspect verboseOutput{this}; bool addDebugger() const; void setAddDebugger(bool debug); @@ -63,20 +58,16 @@ private: bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; QWidget *createConfigWidget() override; - void finish(Utils::ProcessResult result) override; bool verifyKeystorePassword(); bool verifyCertificatePassword(); - void doRun() override; + Tasking::GroupItem runRecipe() final; void stdError(const QString &output); void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type); void updateBuildToolsVersionInJsonFile(); - bool m_buildAAB = false; bool m_signPackage = false; - bool m_verbose = false; - bool m_openPackageLocation = false; bool m_openPackageLocationForRun = false; bool m_addDebugger = true; QString m_buildTargetSdk; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 82410682c60..65dae8e7fb5 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -15,7 +15,7 @@ #include <coreplugin/messagemanager.h> #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> @@ -24,10 +24,10 @@ #include <debugger/debuggeritemmanager.h> #include <debugger/debuggeritem.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <utils/algorithm.h> @@ -36,6 +36,7 @@ #include <utils/persistentsettings.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> #include <utils/stringutils.h> #include <QApplication> @@ -89,52 +90,45 @@ const char LinuxOsKey[] = "linux"; const char WindowsOsKey[] = "windows"; const char macOsKey[] = "mac"; +const char SettingsGroup[] = "AndroidConfigurations"; +const char SDKLocationKey[] = "SDKLocation"; +const char CustomNdkLocationsKey[] = "CustomNdkLocations"; +const char DefaultNdkLocationKey[] = "DefaultNdkLocation"; +const char SdkFullyConfiguredKey[] = "AllEssentialsInstalled"; +const char SDKManagerToolArgsKey[] = "SDKManagerToolArgs"; +const char OpenJDKLocationKey[] = "OpenJDKLocation"; +const char OpenSslPriLocationKey[] = "OpenSSLPriLocation"; +const char AutomaticKitCreationKey[] = "AutomatiKitCreation"; +const char EmulatorArgsKey[] = "EmulatorArgs"; -namespace { - const QLatin1String SettingsGroup("AndroidConfigurations"); - const QLatin1String SDKLocationKey("SDKLocation"); - const QLatin1String CustomNdkLocationsKey("CustomNdkLocations"); - const QLatin1String DefaultNdkLocationKey("DefaultNdkLocation"); - const QLatin1String SdkFullyConfiguredKey("AllEssentialsInstalled"); - const QLatin1String SDKManagerToolArgsKey("SDKManagerToolArgs"); - const QLatin1String OpenJDKLocationKey("OpenJDKLocation"); - const QLatin1String OpenSslPriLocationKey("OpenSSLPriLocation"); - const QLatin1String AutomaticKitCreationKey("AutomatiKitCreation"); - const QLatin1String EmulatorArgsKey("EmulatorArgs"); +const QLatin1String ArmToolchainPrefix("arm-linux-androideabi"); +const QLatin1String X86ToolchainPrefix("x86"); +const QLatin1String AArch64ToolchainPrefix("aarch64-linux-android"); +const QLatin1String X86_64ToolchainPrefix("x86_64"); - const QLatin1String ArmToolchainPrefix("arm-linux-androideabi"); - const QLatin1String X86ToolchainPrefix("x86"); - const QLatin1String AArch64ToolchainPrefix("aarch64-linux-android"); - const QLatin1String X86_64ToolchainPrefix("x86_64"); +const QLatin1String ArmToolsPrefix ("arm-linux-androideabi"); +const QLatin1String X86ToolsPrefix("i686-linux-android"); +const QLatin1String AArch64ToolsPrefix("aarch64-linux-android"); +const QLatin1String X86_64ToolsPrefix("x86_64-linux-android"); - const QLatin1String ArmToolsPrefix("arm-linux-androideabi"); - const QLatin1String X86ToolsPrefix("i686-linux-android"); - const QLatin1String AArch64ToolsPrefix("aarch64-linux-android"); - const QLatin1String X86_64ToolsPrefix("x86_64-linux-android"); +const QLatin1String Unknown("unknown"); +const QLatin1String keytoolName("keytool"); +const Key changeTimeStamp("ChangeTimeStamp"); - const QLatin1String ArmToolsDisplayName("arm"); - const QLatin1String X86ToolsDisplayName("i686"); - const QLatin1String AArch64ToolsDisplayName("aarch64"); - const QLatin1String X86_64ToolsDisplayName("x86_64"); +const char sdkToolsVersionKey[] = "Pkg.Revision"; +const char ndkRevisionKey[] = "Pkg.Revision"; - const QLatin1String Unknown("unknown"); - const QLatin1String keytoolName("keytool"); - const QLatin1String changeTimeStamp("ChangeTimeStamp"); - - const QLatin1String sdkToolsVersionKey("Pkg.Revision"); - const QLatin1String ndkRevisionKey("Pkg.Revision"); - - static QString sdkSettingsFileName() - { - return Core::ICore::installerResourcePath("android.xml").toString(); - } - - static QString ndkPackageMarker() - { - return QLatin1String(Constants::ndkPackageName) + ";"; - } +static QString sdkSettingsFileName() +{ + return Core::ICore::installerResourcePath("android.xml").toString(); } +static QString ndkPackageMarker() +{ + return QLatin1String(Constants::ndkPackageName) + ";"; +} + + ////////////////////////////////// // AndroidConfig ////////////////////////////////// @@ -176,18 +170,18 @@ QLatin1String AndroidConfig::displayName(const Abi &abi) switch (abi.architecture()) { case Abi::ArmArchitecture: if (abi.wordWidth() == 64) - return AArch64ToolsDisplayName; - return ArmToolsDisplayName; + return QLatin1String(Constants::AArch64ToolsDisplayName); + return QLatin1String(Constants::ArmToolsDisplayName); case Abi::X86Architecture: if (abi.wordWidth() == 64) - return X86_64ToolsDisplayName; - return X86ToolsDisplayName; + return QLatin1String(Constants::X86_64ToolsDisplayName); + return QLatin1String(Constants::X86ToolsDisplayName); default: return Unknown; } } -void AndroidConfig::load(const QSettings &settings) +void AndroidConfig::load(const QtcSettings &settings) { // user settings QVariant emulatorArgs = settings.value(EmulatorArgsKey, QString("-netdelay none -netspeed full")); @@ -227,7 +221,7 @@ void AndroidConfig::load(const QSettings &settings) parseDependenciesJson(); } -void AndroidConfig::save(QSettings &settings) const +void AndroidConfig::save(QtcSettings &settings) const { QFileInfo fileInfo(sdkSettingsFileName()); if (fileInfo.exists()) @@ -452,7 +446,7 @@ FilePath AndroidConfig::emulatorToolPath() const if (emulatorFile.exists()) return emulatorFile; - return FilePath(); + return {}; } FilePath AndroidConfig::sdkManagerToolPath() const @@ -468,7 +462,7 @@ FilePath AndroidConfig::sdkManagerToolPath() const if (sdkmanagerTmpPath.exists()) return sdkmanagerTmpPath; - return FilePath(); + return {}; } FilePath AndroidConfig::avdManagerToolPath() const @@ -478,7 +472,7 @@ FilePath AndroidConfig::avdManagerToolPath() const if (sdkmanagerPath.exists()) return sdkmanagerPath; - return FilePath(); + return {}; } void AndroidConfig::setTemporarySdkToolsPath(const Utils::FilePath &path) @@ -498,7 +492,7 @@ FilePath AndroidConfig::sdkToolsVersionPath() const if (tmpSdkPath.exists()) return tmpSdkPath; - return FilePath(); + return {}; } FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType hostOs) @@ -523,7 +517,7 @@ FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType case OsTypeMac: hostPatterns << QLatin1String("darwin*"); break; - default: /* unknown host */ return FilePath(); + default: /* unknown host */ return {}; } QDirIterator iter(toolchainPath.toString(), hostPatterns, QDir::Dirs); @@ -675,7 +669,7 @@ QString AndroidConfig::getDeviceProperty(const QString &device, const QString &p adbProc.setCommand(cmd); adbProc.runBlocking(); if (adbProc.result() != ProcessResult::FinishedWithSuccess) - return QString(); + return {}; return adbProc.allOutput(); } @@ -692,18 +686,18 @@ QString AndroidConfig::getAvdName(const QString &serialnumber) { int index = serialnumber.indexOf(QLatin1String("-")); if (index == -1) - return QString(); + return {}; bool ok; int port = serialnumber.mid(index + 1).toInt(&ok); if (!ok) - return QString(); + return {}; const QByteArray avdName = "avd name\n"; QTcpSocket tcpSocket; tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port); if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection - return QString{}; + return {}; tcpSocket.write(avdName + "exit\n"); tcpSocket.waitForDisconnected(500); @@ -1131,9 +1125,7 @@ static bool matchToolChain(const ToolChain *atc, const ToolChain *btc) if (atc->typeId() != Constants::ANDROID_TOOLCHAIN_TYPEID || btc->typeId() != Constants::ANDROID_TOOLCHAIN_TYPEID) return false; - auto aatc = static_cast<const AndroidToolChain *>(atc); - auto abtc = static_cast<const AndroidToolChain *>(btc); - return aatc->targetAbi() == abtc->targetAbi(); + return atc->targetAbi() == btc->targetAbi(); } void AndroidConfigurations::registerNewToolChains() @@ -1352,9 +1344,8 @@ void AndroidConfigurations::updateAutomaticKitList() // register new kits const Toolchains toolchains = ToolChainManager::toolchains([](const ToolChain *tc) { - return tc->isAutoDetected() - && tc->isValid() - && tc->typeId() == Constants::ANDROID_TOOLCHAIN_TYPEID; + return tc->isAutoDetected() && tc->typeId() == Constants::ANDROID_TOOLCHAIN_TYPEID + && tc->isValid(); }); QList<Kit *> unhandledKits = existingKits; for (ToolChain *tc : toolchains) { @@ -1424,10 +1415,10 @@ void AndroidConfigurations::updateAutomaticKitList() KitManager::deregisterKit(k); } -Environment AndroidConfigurations::toolsEnvironment(const AndroidConfig &config) +Environment AndroidConfig::toolsEnvironment() const { Environment env = Environment::systemEnvironment(); - FilePath jdkLocation = config.openJDKLocation(); + FilePath jdkLocation = openJDKLocation(); if (!jdkLocation.isEmpty()) { env.set(Constants::JAVA_HOME_ENV_VAR, jdkLocation.toUserOutput()); env.prependOrSetPath(jdkLocation.pathAppended("bin")); @@ -1452,7 +1443,7 @@ AndroidConfigurations *AndroidConfigurations::instance() void AndroidConfigurations::save() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(SettingsGroup); m_config.save(*settings); settings->endGroup(); @@ -1551,7 +1542,7 @@ FilePath AndroidConfig::getJdkPath() void AndroidConfigurations::load() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(SettingsGroup); m_config.load(*settings); settings->endGroup(); diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index b7d86b80820..c92deec331c 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -4,7 +4,6 @@ #pragma once -#include "android_global.h" #include "androiddeviceinfo.h" #include "androidsdkmanager.h" #include "androidsdkpackage.h" @@ -12,18 +11,14 @@ #include <projectexplorer/toolchain.h> #include <qtsupport/qtversionmanager.h> +#include <utils/filepath.h> + #include <QStringList> #include <QVector> #include <QHash> #include <QMap> #include <QVersionNumber> -#include <utils/filepath.h> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace ProjectExplorer { class Abi; } namespace Android { @@ -56,11 +51,11 @@ public: bool containsVersion(const QVersionNumber &qtVersion) const; }; -class ANDROID_EXPORT AndroidConfig +class AndroidConfig { public: - void load(const QSettings &settings); - void save(QSettings &settings) const; + void load(const Utils::QtcSettings &settings); + void save(Utils::QtcSettings &settings) const; static QStringList apiLevelNamesFor(const SdkPlatformList &platforms); static QString apiLevelNameFor(const SdkPlatform *platform); @@ -150,6 +145,8 @@ public: static QStringList getAbis(const QString &device); static int getSDKVersion(const QString &device); + Utils::Environment toolsEnvironment() const; + private: static QString getDeviceProperty(const QString &device, const QString &property); @@ -181,7 +178,7 @@ private: mutable QHash<QString, QString> m_serialNumberToDeviceName; }; -class ANDROID_EXPORT AndroidConfigurations : public QObject +class AndroidConfigurations : public QObject { Q_OBJECT @@ -197,7 +194,6 @@ public: static void removeOldToolChains(); static void updateAutomaticKitList(); static bool force32bitEmulator(); - static Utils::Environment toolsEnvironment(const AndroidConfig &config); signals: void aboutToUpdate(); diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index 70c8c3a23f7..1bc1712e24f 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -76,4 +76,11 @@ const Utils::Id AndroidAvdPath = "AndroidAvdPath"; const char cmdlineToolsName[] = "cmdline-tools"; const char ndkPackageName[] = "ndk"; +// For AndroidQtVersion +const char ArmToolsDisplayName[] = "arm"; +const char ArmV7ToolsDisplayName[] = "armv7"; +const char X86ToolsDisplayName[] = "i686"; +const char AArch64ToolsDisplayName[] = "aarch64"; +const char X86_64ToolsDisplayName[] = "x86_64"; + } // Android::Constants diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp index 354b8a77fda..7b836171502 100644 --- a/src/plugins/android/androiddebugsupport.cpp +++ b/src/plugins/android/androiddebugsupport.cpp @@ -8,7 +8,7 @@ #include "androidmanager.h" #include "androidqtversion.h" -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerrunconfigurationaspect.h> #include <debugger/debuggerruncontrol.h> @@ -17,7 +17,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index db877b4e972..36ea8d42dc8 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -30,7 +30,7 @@ #include <projectexplorer/toolchain.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/async.h> @@ -57,7 +57,8 @@ namespace Android::Internal { static Q_LOGGING_CATEGORY(deployStepLog, "qtc.android.build.androiddeployqtstep", QtWarningMsg) -const QLatin1String UninstallPreviousPackageKey("UninstallPreviousPackage"); +const char UninstallPreviousPackageKey[] = "UninstallPreviousPackage"; + const QLatin1String InstallFailedInconsistentCertificatesString("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES"); const QLatin1String InstallFailedUpdateIncompatible("INSTALL_FAILED_UPDATE_INCOMPATIBLE"); const QLatin1String InstallFailedPermissionModelDowngrade("INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE"); @@ -89,13 +90,12 @@ private: void runCommand(const CommandLine &command); bool init() override; - void doRun() override; - void doCancel() override; + Tasking::GroupItem runRecipe() final; void gatherFilesToPull(); - DeployErrorCode runDeploy(); + DeployErrorCode runDeploy(QPromise<void> &promise); void slotAskForUninstall(DeployErrorCode errorCode); - void runImpl(QPromise<bool> &promise); + void runImpl(QPromise<void> &promise); QWidget *createConfigWidget() override; @@ -132,8 +132,6 @@ private: FilePath m_workingDirectory; Environment m_environment; AndroidDeviceInfo m_deviceInfo; - - // The synchronizer has cancelOnWait set to true by default. FutureSynchronizer m_synchronizer; }; @@ -341,7 +339,7 @@ bool AndroidDeployQtStep::init() return true; } -AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy() +AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy(QPromise<void> &promise) { CommandLine cmd(m_command); if (m_useAndroiddeployqt && m_apkPath.isEmpty()) { @@ -404,7 +402,7 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::runDeploy() if (process.state() == QProcess::NotRunning) break; - if (isCanceled()) { + if (promise.isCanceled()) { process.kill(); process.waitForFinished(); } @@ -477,16 +475,16 @@ void AndroidDeployQtStep::slotAskForUninstall(DeployErrorCode errorCode) m_askForUninstall = button == QMessageBox::Yes; } -void AndroidDeployQtStep::runImpl(QPromise<bool> &promise) +// TODO: This implementation is not thread safe. +void AndroidDeployQtStep::runImpl(QPromise<void> &promise) { if (!m_avdName.isEmpty()) { - const QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, - QFuture<void>(promise.future())); + const QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, promise.future()); qCDebug(deployStepLog) << "Deploying to AVD:" << m_avdName << serialNumber; if (serialNumber.isEmpty()) { reportWarningOrError(Tr::tr("The deployment AVD \"%1\" cannot be started.") .arg(m_avdName), Task::Error); - promise.addResult(false); + promise.future().cancel(); return; } m_serialNumber = serialNumber; @@ -494,12 +492,12 @@ void AndroidDeployQtStep::runImpl(QPromise<bool> &promise) AndroidManager::setDeviceSerialNumber(target(), serialNumber); } - DeployErrorCode returnValue = runDeploy(); + DeployErrorCode returnValue = runDeploy(promise); if (returnValue > DeployErrorCode::NoError && returnValue < DeployErrorCode::Failure) { emit askForUninstall(returnValue); if (m_askForUninstall) { m_uninstallPreviousPackageRun = true; - returnValue = runDeploy(); + returnValue = runDeploy(promise); } } @@ -529,7 +527,8 @@ void AndroidDeployQtStep::runImpl(QPromise<bool> &promise) reportWarningOrError(error, Task::Error); } } - promise.addResult(returnValue == NoError); + if (returnValue != NoError) + promise.future().cancel(); } void AndroidDeployQtStep::gatherFilesToPull() @@ -561,22 +560,13 @@ void AndroidDeployQtStep::gatherFilesToPull() << "to:" << itr.value(); } -void AndroidDeployQtStep::doRun() +Tasking::GroupItem AndroidDeployQtStep::runRecipe() { - auto * const watcher = new QFutureWatcher<bool>(this); - connect(watcher, &QFutureWatcher<bool>::finished, this, [this, watcher] { - const bool success = !watcher->isCanceled() && watcher->result(); - emit finished(success); - watcher->deleteLater(); - }); - auto future = Utils::asyncRun(&AndroidDeployQtStep::runImpl, this); - watcher->setFuture(future); - m_synchronizer.addFuture(future); -} - -void AndroidDeployQtStep::doCancel() -{ - m_synchronizer.cancelAllFutures(); + const auto onSetup = [this](Async<void> &async) { + async.setConcurrentCallData(&AndroidDeployQtStep::runImpl, this); + async.setFutureSynchronizer(&m_synchronizer); + }; + return AsyncTask<void>(onSetup); } void AndroidDeployQtStep::runCommand(const CommandLine &command) diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index ce2f4c11ee7..23cfa76592f 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -15,7 +15,7 @@ #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/devicesupport/idevicewidget.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfiguration.h> #include <projectexplorer/projectmanager.h> @@ -151,7 +151,7 @@ AndroidDevice::AndroidDevice() { setupId(IDevice::AutoDetected, Constants::ANDROID_DEVICE_ID); setType(Constants::ANDROID_DEVICE_TYPE); - setDefaultDisplayName(Tr::tr("Run on Android")); + settings()->displayName.setDefaultValue(Tr::tr("Run on Android")); setDisplayType(Tr::tr("Android")); setMachineType(IDevice::Hardware); setOsType(OsType::OsTypeOtherUnix); @@ -214,7 +214,7 @@ void AndroidDevice::addActionsIfNotFound() } } -void AndroidDevice::fromMap(const QVariantMap &map) +void AndroidDevice::fromMap(const Store &map) { IDevice::fromMap(map); initAvdSettings(); @@ -474,14 +474,27 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent) return; qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name); - m_removeAvdFutureWatcher.setFuture(Utils::asyncRun([this, name, device] { - QPair<IDevice::ConstPtr, bool> pair; - pair.first = device; - pair.second = false; - if (m_avdManager.removeAvd(name)) - pair.second = true; - return pair; - })); + m_removeAvdProcess.reset(new Process); + const AndroidConfig &config = m_avdManager.config(); + const CommandLine command(config.avdManagerToolPath(), {"delete", "avd", "-n", name}); + qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput(); + m_removeAvdProcess->setTimeoutS(5); + m_removeAvdProcess->setEnvironment(config.toolsEnvironment()); + m_removeAvdProcess->setCommand(command); + connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] { + const QString name = device->displayName(); + if (m_removeAvdProcess->result() == ProcessResult::FinishedWithSuccess) { + qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.", + qPrintable(name)); + // Remove the device from QtC after it's been removed using avdmanager. + DeviceManager::instance()->removeDevice(device->id()); + } else { + AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the " + "Android AVD \"%1\" using avdmanager tool.").arg(name)); + } + m_removeAvdProcess.release()->deleteLater(); + }); + m_removeAvdProcess->start(); } void AndroidDeviceManager::setupWifiForDevice(const IDevice::Ptr &device, QWidget *parent) @@ -544,20 +557,6 @@ void AndroidDeviceManager::setupWifiForDevice(const IDevice::Ptr &device, QWidge }); } -void AndroidDeviceManager::handleAvdRemoved() -{ - const QPair<IDevice::ConstPtr, bool> result = m_removeAvdFutureWatcher.result(); - const QString name = result.first->displayName(); - if (result.second) { - qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.", qPrintable(name)); - // Remove the device from QtC after it's been removed using avdmanager. - DeviceManager::instance()->removeDevice(result.first->id()); - } else { - AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the " - "Android AVD \"%1\" using avdmanager tool.").arg(name)); - } -} - QString AndroidDeviceManager::emulatorName(const QString &serialNumber) const { QStringList args = AndroidDeviceInfo::adbSelector(serialNumber); @@ -641,7 +640,7 @@ void AndroidDeviceManager::setupDevicesWatcher() const CommandLine command = CommandLine(m_androidConfig.adbToolPath(), {"track-devices"}); m_adbDeviceWatcherProcess->setCommand(command); m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir()); - m_adbDeviceWatcherProcess->setEnvironment(AndroidConfigurations::toolsEnvironment(m_androidConfig)); + m_adbDeviceWatcherProcess->setEnvironment(m_androidConfig.toolsEnvironment()); m_adbDeviceWatcherProcess->start(); // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, @@ -711,7 +710,7 @@ void AndroidDeviceManager::HandleAvdsListChange() AndroidDevice *newDev = new AndroidDevice(); newDev->setupId(IDevice::AutoDetected, deviceId); - newDev->setDisplayName(displayName); + newDev->settings()->displayName.setValue(displayName); newDev->setMachineType(item.type); newDev->setDeviceState(item.state); @@ -792,7 +791,7 @@ void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber) } else { AndroidDevice *newDev = new AndroidDevice(); newDev->setupId(IDevice::AutoDetected, id); - newDev->setDisplayName(displayName); + newDev->settings()->displayName.setValue(displayName); newDev->setMachineType(IDevice::Hardware); newDev->setDeviceState(state); @@ -819,8 +818,6 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent) m_androidConfig(AndroidConfigurations::currentConfig()), m_avdManager(m_androidConfig) { - connect(&m_removeAvdFutureWatcher, &QFutureWatcherBase::finished, - this, &AndroidDeviceManager::handleAvdRemoved); QTC_ASSERT(!s_instance, return); s_instance = this; } @@ -828,7 +825,6 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent) AndroidDeviceManager::~AndroidDeviceManager() { m_avdsFutureWatcher.waitForFinished(); - m_removeAvdFutureWatcher.waitForFinished(); QTC_ASSERT(s_instance == this, return); s_instance = nullptr; } diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index cf8046cfa95..2ac753e50ab 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -55,7 +55,7 @@ public: QString openGLStatus() const; protected: - void fromMap(const QVariantMap &map) final; + void fromMap(const Utils::Store &map) final; private: void addActionsIfNotFound(); @@ -101,12 +101,11 @@ private: ~AndroidDeviceManager(); void HandleDevicesListChange(const QString &serialNumber); void HandleAvdsListChange(); - void handleAvdRemoved(); QString emulatorName(const QString &serialNumber) const; QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher; - QFutureWatcher<QPair<ProjectExplorer::IDevice::ConstPtr, bool>> m_removeAvdFutureWatcher; + std::unique_ptr<Utils::Process> m_removeAvdProcess; QFileSystemWatcher m_avdFileSystemWatcher; std::unique_ptr<Utils::Process> m_adbDeviceWatcherProcess; AndroidConfig &m_androidConfig; diff --git a/src/plugins/android/androiddeviceinfo.cpp b/src/plugins/android/androiddeviceinfo.cpp index 4d34d7a57c9..3c00547d062 100644 --- a/src/plugins/android/androiddeviceinfo.cpp +++ b/src/plugins/android/androiddeviceinfo.cpp @@ -12,8 +12,8 @@ namespace Android { QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber) { if (serialNumber.startsWith(QLatin1String("????"))) - return QStringList("-d"); - return QStringList({"-s", serialNumber}); + return {"-d"}; + return {"-s", serialNumber}; } bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const diff --git a/src/plugins/android/androiderrormessage.cpp b/src/plugins/android/androiderrormessage.cpp deleted file mode 100644 index c67abf727f0..00000000000 --- a/src/plugins/android/androiderrormessage.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "androiderrormessage.h" -#include "androidtr.h" - -#include <QObject> - -namespace Android { -namespace Internal { - -QString AndroidErrorMessage::getMessage(ErrorCode errorCode, const QVariantList ¶meters) -{ - Q_UNUSED(parameters) - switch (errorCode) { - case SDKInstallationError: - return Tr::tr("Android: SDK installation error 0x%1").arg(errorCode, 0, 16); - - case NDKInstallationError: - return Tr::tr("Android: NDK installation error 0x%1").arg(errorCode, 0, 16); - - case JavaInstallationError: - return Tr::tr("Android: Java installation error 0x%1").arg(errorCode, 0, 16); - - case AntInstallationError: - return Tr::tr("Android: ant installation error 0x%1").arg(errorCode, 0, 16); - - case AdbInstallationError: - return Tr::tr("Android: adb installation error 0x%1").arg(errorCode, 0, 16); - - case DeviceConnectionError: - return Tr::tr("Android: Device connection error 0x%1").arg(errorCode, 0, 16); - - case DevicePermissionError: - return Tr::tr("Android: Device permission error 0x%1").arg(errorCode, 0, 16); - - case DeviceAuthorizationError: - return Tr::tr("Android: Device authorization error 0x%1").arg(errorCode, 0, 16); - - case DeviceAPILevelError: - return Tr::tr("Android: Device API level not supported: error 0x%1").arg(errorCode, 0, 16); - - default: - return Tr::tr("Android: Unknown error 0x%1").arg(errorCode, 0, 16); - } -} - -} // namespace Internal -} // namespace Android diff --git a/src/plugins/android/androiderrormessage.h b/src/plugins/android/androiderrormessage.h deleted file mode 100644 index c7bb7bc0daa..00000000000 --- a/src/plugins/android/androiderrormessage.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QVariantList> -#include <QCoreApplication> - -namespace Android { -namespace Internal { - -class AndroidErrorMessage -{ -public: - enum ErrorCode { - UnknownError = 0x3000, - SDKInstallationError, - NDKInstallationError, - JavaInstallationError, - AntInstallationError, - AdbInstallationError, - DeviceConnectionError, - DevicePermissionError, - DeviceAuthorizationError, - DeviceAPILevelError - }; - static QString getMessage(ErrorCode errorCode, const QVariantList ¶meters = QVariantList()); -}; - -} // namespace Internal -} // namespace Android diff --git a/src/plugins/android/androidextralibrarylistmodel.cpp b/src/plugins/android/androidextralibrarylistmodel.cpp index 9ee0c94010f..77a29020eb4 100644 --- a/src/plugins/android/androidextralibrarylistmodel.cpp +++ b/src/plugins/android/androidextralibrarylistmodel.cpp @@ -43,7 +43,7 @@ QModelIndex AndroidExtraLibraryListModel::index(int row, int column, const QMode QModelIndex AndroidExtraLibraryListModel::parent(const QModelIndex &) const { - return QModelIndex(); + return {}; } int AndroidExtraLibraryListModel::rowCount(const QModelIndex &) const diff --git a/src/plugins/android/androidextralibrarylistmodel.h b/src/plugins/android/androidextralibrarylistmodel.h index 0d42d4d9602..6f236cb8eac 100644 --- a/src/plugins/android/androidextralibrarylistmodel.h +++ b/src/plugins/android/androidextralibrarylistmodel.h @@ -4,8 +4,6 @@ #pragma once -#include "android_global.h" - #include <QAbstractItemModel> #include <QStringList> @@ -13,7 +11,7 @@ namespace ProjectExplorer { class BuildSystem; } namespace Android { -class ANDROID_EXPORT AndroidExtraLibraryListModel : public QAbstractItemModel +class AndroidExtraLibraryListModel : public QAbstractItemModel { Q_OBJECT diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 9458df2a3b9..aa8e3fe43aa 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -1,55 +1,36 @@ // Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "androidmanager.h" + #include "androidavdmanager.h" #include "androidbuildapkstep.h" -#include "androidconfigurations.h" #include "androidconstants.h" -#include "androiddeployqtstep.h" #include "androiddevice.h" -#include "androidglobal.h" -#include "androidmanager.h" #include "androidqtversion.h" -#include "androidrunconfiguration.h" -#include "androidsdkmanager.h" -#include "androidtoolchain.h" #include "androidtr.h" -#include <coreplugin/documentmanager.h> -#include <coreplugin/messagemanager.h> -#include <coreplugin/icore.h> - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectnodes.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/projectmanager.h> -#include <projectexplorer/target.h> -#include <projectexplorer/buildsystem.h> - -#include <qtsupport/qtkitinformation.h> -#include <qtsupport/qtsupportconstants.h> -#include <qtsupport/baseqtversion.h> - #include <cmakeprojectmanager/cmakeprojectconstants.h> +#include <coreplugin/icontext.h> +#include <coreplugin/messagemanager.h> + +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> + +#include <qtsupport/qtkitaspect.h> + #include <utils/algorithm.h> -#include <utils/fileutils.h> #include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/stringutils.h> -#include <QApplication> #include <QDomDocument> -#include <QFileSystemWatcher> #include <QJsonDocument> #include <QJsonObject> -#include <QList> #include <QLoggingCategory> #include <QMessageBox> -#include <QProcess> -#include <QRegularExpression> #include <QVersionNumber> using namespace ProjectExplorer; @@ -57,7 +38,7 @@ using namespace Utils; using namespace Android::Internal; -namespace Android { +namespace Android::AndroidManager { const char AndroidManifestName[] = "AndroidManifest.xml"; const char AndroidDeviceSn[] = "AndroidDeviceSerialNumber"; @@ -87,43 +68,57 @@ static const ProjectNode *currentProjectNode(const Target *target) return target->project()->findNodeForBuildKey(target->activeBuildKey()); } -QString AndroidManager::packageName(const Target *target) +QString packageName(const Target *target) { QDomDocument doc; if (!openManifest(target, doc)) - return QString(); + return {}; QDomElement manifestElem = doc.documentElement(); return manifestElem.attribute(QLatin1String("package")); } -QString AndroidManager::packageName(const FilePath &manifestFile) +QString packageName(const FilePath &manifestFile) { QDomDocument doc; if (!openXmlFile(doc, manifestFile)) - return QString(); + return {}; QDomElement manifestElem = doc.documentElement(); return manifestElem.attribute(QLatin1String("package")); } -QString AndroidManager::activityName(const Target *target) +QString activityName(const Target *target) { QDomDocument doc; if (!openManifest(target, doc)) - return QString(); + return {}; QDomElement activityElem = doc.documentElement().firstChildElement( QLatin1String("application")).firstChildElement(QLatin1String("activity")); return activityElem.attribute(QLatin1String("android:name")); } +static FilePath manifestSourcePath(const Target *target) +{ + if (const ProjectNode *node = currentProjectNode(target)) { + const QString packageSource + = node->data(Android::Constants::AndroidPackageSourceDir).toString(); + if (!packageSource.isEmpty()) { + const FilePath manifest = FilePath::fromUserInput(packageSource + "/AndroidManifest.xml"); + if (manifest.exists()) + return manifest; + } + } + return manifestPath(target); +} + /*! Returns the minimum Android API level set for the APK. Minimum API level of the kit is returned if the manifest file of the APK cannot be found or parsed. */ -int AndroidManager::minimumSDK(const Target *target) +int minimumSDK(const Target *target) { QDomDocument doc; - if (!openXmlFile(doc, AndroidManager::manifestSourcePath(target))) + if (!openXmlFile(doc, manifestSourcePath(target))) return minimumSDK(target->kit()); const int minSdkVersion = parseMinSdk(doc.documentElement()); if (minSdkVersion == 0) @@ -135,7 +130,7 @@ int AndroidManager::minimumSDK(const Target *target) Returns the minimum Android API level required by the kit to compile. -1 is returned if the kit does not support Android. */ -int AndroidManager::minimumSDK(const Kit *kit) +int minimumSDK(const Kit *kit) { int minSdkVersion = -1; QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit); @@ -152,7 +147,7 @@ int AndroidManager::minimumSDK(const Kit *kit) return minSdkVersion; } -QString AndroidManager::buildTargetSDK(const Target *target) +QString buildTargetSDK(const Target *target) { if (auto bc = target->activeBuildConfiguration()) { if (auto androidBuildApkStep = bc->buildSteps()->firstOfType<AndroidBuildApkStep>()) @@ -164,13 +159,13 @@ QString AndroidManager::buildTargetSDK(const Target *target) return fallback; } -QStringList AndroidManager::applicationAbis(const Target *target) +QStringList applicationAbis(const Target *target) { auto qt = dynamic_cast<AndroidQtVersion *>(QtSupport::QtKitAspect::qtVersion(target->kit())); return qt ? qt->androidAbis() : QStringList(); } -QString AndroidManager::archTriplet(const QString &abi) +QString archTriplet(const QString &abi) { if (abi == ProjectExplorer::Constants::ANDROID_ABI_X86) { return {"i686-linux-android"}; @@ -182,7 +177,7 @@ QString AndroidManager::archTriplet(const QString &abi) return {"arm-linux-androideabi"}; } -QJsonObject AndroidManager::deploymentSettings(const Target *target) +QJsonObject deploymentSettings(const Target *target) { QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit()); if (!qt) @@ -216,7 +211,7 @@ QJsonObject AndroidManager::deploymentSettings(const Target *target) return settings; } -bool AndroidManager::isQtCreatorGenerated(const FilePath &deploymentFile) +bool isQtCreatorGenerated(const FilePath &deploymentFile) { QFile f{deploymentFile.toString()}; if (!f.open(QIODevice::ReadOnly)) @@ -224,17 +219,17 @@ bool AndroidManager::isQtCreatorGenerated(const FilePath &deploymentFile) return QJsonDocument::fromJson(f.readAll()).object()["_description"].toString() == qtcSignature; } -FilePath AndroidManager::androidBuildDirectory(const Target *target) +FilePath androidBuildDirectory(const Target *target) { return buildDirectory(target) / Constants::ANDROID_BUILD_DIRECTORY; } -FilePath AndroidManager::androidAppProcessDir(const Target *target) +FilePath androidAppProcessDir(const Target *target) { return buildDirectory(target) / Constants::ANDROID_APP_PROCESS_DIRECTORY; } -bool AndroidManager::isQt5CmakeProject(const ProjectExplorer::Target *target) +bool isQt5CmakeProject(const ProjectExplorer::Target *target) { const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(target->kit()); const bool isQt5 = qt && qt->qtVersion() < QVersionNumber(6, 0, 0); @@ -243,7 +238,7 @@ bool AndroidManager::isQt5CmakeProject(const ProjectExplorer::Target *target) return isQt5 && isCmakeProject; } -FilePath AndroidManager::buildDirectory(const Target *target) +FilePath buildDirectory(const Target *target) { if (const BuildSystem *bs = target->buildSystem()) { const QString buildKey = target->activeBuildKey(); @@ -264,21 +259,32 @@ FilePath AndroidManager::buildDirectory(const Target *target) if (isQt5CmakeProject(target)) { // Return the main build dir and not the android libs dir const QString libsDir = QString(Constants::ANDROID_BUILD_DIRECTORY) + "/libs"; - Utils::FilePath parentDuildDir = buildDir.parentDir(); + FilePath parentDuildDir = buildDir.parentDir(); if (parentDuildDir.endsWith(libsDir) || libsDir.endsWith(libsDir + "/")) return parentDuildDir.parentDir().parentDir(); + } else { + // Qt6 + CMake: Very cautios hack to work around QTCREATORBUG-26479 for simple projects + const QString jsonFileName = + AndroidQtVersion::androidDeploymentSettingsFileName(target); + const FilePath jsonFile = buildDir / jsonFileName; + if (!jsonFile.exists()) { + const FilePath projectBuildDir = bs->buildConfiguration()->buildDirectory(); + if (buildDir != projectBuildDir) { + const FilePath projectJsonFile = projectBuildDir / jsonFileName; + if (projectJsonFile.exists()) + buildDir = projectBuildDir; + } + } } return buildDir; } return {}; } -enum PackageFormat { - Apk, - Aab -}; +enum PackageFormat { Apk, Aab }; -QString packageSubPath(PackageFormat format, BuildConfiguration::BuildType buildType, bool sig) +static QString packageSubPath(PackageFormat format, BuildConfiguration::BuildType buildType, + bool sig) { const bool deb = (buildType == BuildConfiguration::Debug); @@ -294,7 +300,7 @@ QString packageSubPath(PackageFormat format, BuildConfiguration::BuildType build : "bundle/release/android-build-release.aab"); } -FilePath AndroidManager::packagePath(const Target *target) +FilePath packagePath(const Target *target) { QTC_ASSERT(target, return {}); @@ -311,25 +317,7 @@ FilePath AndroidManager::packagePath(const Target *target) return androidBuildDirectory(target) / "build/outputs" / subPath; } -bool AndroidManager::matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis) -{ - for (const auto &abi : appAbis) { - if (deviceAbis.contains(abi)) - return true; - } - return false; -} - -QString AndroidManager::devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis) -{ - for (const auto &abi : appAbis) { - if (deviceAbis.contains(abi)) - return abi; - } - return {}; -} - -Abi AndroidManager::androidAbi2Abi(const QString &androidAbi) +Abi androidAbi2Abi(const QString &androidAbi) { if (androidAbi == ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A) { return Abi{Abi::Architecture::ArmArchitecture, @@ -364,7 +352,7 @@ Abi AndroidManager::androidAbi2Abi(const QString &androidAbi) } } -bool AndroidManager::skipInstallationAndPackageSteps(const Target *target) +bool skipInstallationAndPackageSteps(const Target *target) { // For projects using Qt 5.15 and Qt 6, the deployment settings file // is generated by CMake/qmake and not Qt Creator, so if such file doesn't exist @@ -387,21 +375,7 @@ bool AndroidManager::skipInstallationAndPackageSteps(const Target *target) return n == nullptr; // If no Application target found, then skip steps } -FilePath AndroidManager::manifestSourcePath(const Target *target) -{ - if (const ProjectNode *node = currentProjectNode(target)) { - const QString packageSource - = node->data(Android::Constants::AndroidPackageSourceDir).toString(); - if (!packageSource.isEmpty()) { - const FilePath manifest = FilePath::fromUserInput(packageSource + "/AndroidManifest.xml"); - if (manifest.exists()) - return manifest; - } - } - return manifestPath(target); -} - -FilePath AndroidManager::manifestPath(const Target *target) +FilePath manifestPath(const Target *target) { QVariant manifest = target->namedSettings(AndroidManifestName); if (manifest.isValid()) @@ -409,17 +383,17 @@ FilePath AndroidManager::manifestPath(const Target *target) return androidBuildDirectory(target).pathAppended(AndroidManifestName); } -void AndroidManager::setManifestPath(Target *target, const FilePath &path) +void setManifestPath(Target *target, const FilePath &path) { target->setNamedSettings(AndroidManifestName, QVariant::fromValue(path)); } -QString AndroidManager::deviceSerialNumber(const Target *target) +QString deviceSerialNumber(const Target *target) { return target->namedSettings(AndroidDeviceSn).toString(); } -void AndroidManager::setDeviceSerialNumber(Target *target, const QString &deviceSerialNumber) +void setDeviceSerialNumber(Target *target, const QString &deviceSerialNumber) { qCDebug(androidManagerLog) << "Target device serial changed:" << target->displayName() << deviceSerialNumber; @@ -436,7 +410,7 @@ static QString preferredAbi(const QStringList &appAbis, const Target *target) return {}; } -QString AndroidManager::apkDevicePreferredAbi(const Target *target) +QString apkDevicePreferredAbi(const Target *target) { const FilePath libsPath = androidBuildDirectory(target).pathAppended("libs"); if (!libsPath.exists()) { @@ -456,24 +430,24 @@ QString AndroidManager::apkDevicePreferredAbi(const Target *target) return preferredAbi(apkAbis, target); } -void AndroidManager::setDeviceAbis(Target *target, const QStringList &deviceAbis) +void setDeviceAbis(Target *target, const QStringList &deviceAbis) { target->setNamedSettings(AndroidDeviceAbis, deviceAbis); } -int AndroidManager::deviceApiLevel(const Target *target) +int deviceApiLevel(const Target *target) { return target->namedSettings(ApiLevelKey).toInt(); } -void AndroidManager::setDeviceApiLevel(Target *target, int level) +void setDeviceApiLevel(Target *target, int level) { qCDebug(androidManagerLog) << "Target device API level changed:" << target->displayName() << level; target->setNamedSettings(ApiLevelKey, level); } -int AndroidManager::defaultMinimumSDK(const QtSupport::QtVersion *qtVersion) +int defaultMinimumSDK(const QtSupport::QtVersion *qtVersion) { if (qtVersion && qtVersion->qtVersion() >= QVersionNumber(6, 0)) return 23; @@ -483,73 +457,75 @@ int AndroidManager::defaultMinimumSDK(const QtSupport::QtVersion *qtVersion) return 16; } -QString AndroidManager::androidNameForApiLevel(int x) +QString androidNameForApiLevel(int x) { switch (x) { case 2: return QLatin1String("Android 1.1"); case 3: - return QLatin1String("Android 1.5 (Cupcake)"); + return QLatin1String("Android 1.5 (\"Cupcake\")"); case 4: - return QLatin1String("Android 1.6 (Donut)"); + return QLatin1String("Android 1.6 (\"Donut\")"); case 5: - return QLatin1String("Android 2.0 (Eclair)"); + return QLatin1String("Android 2.0 (\"Eclair\")"); case 6: - return QLatin1String("Android 2.0.1 (Eclair)"); + return QLatin1String("Android 2.0.1 (\"Eclair\")"); case 7: - return QLatin1String("Android 2.1 (Eclair)"); + return QLatin1String("Android 2.1 (\"Eclair\")"); case 8: - return QLatin1String("Android 2.2 (Froyo)"); + return QLatin1String("Android 2.2 (\"Froyo\")"); case 9: - return QLatin1String("Android 2.3 (Gingerbread)"); + return QLatin1String("Android 2.3 (\"Gingerbread\")"); case 10: - return QLatin1String("Android 2.3.3 (Gingerbread)"); + return QLatin1String("Android 2.3.3 (\"Gingerbread\")"); case 11: - return QLatin1String("Android 3.0 (Honeycomb)"); + return QLatin1String("Android 3.0 (\"Honeycomb\")"); case 12: - return QLatin1String("Android 3.1 (Honeycomb)"); + return QLatin1String("Android 3.1 (\"Honeycomb\")"); case 13: - return QLatin1String("Android 3.2 (Honeycomb)"); + return QLatin1String("Android 3.2 (\"Honeycomb\")"); case 14: - return QLatin1String("Android 4.0 (IceCreamSandwich)"); + return QLatin1String("Android 4.0 (\"IceCreamSandwich\")"); case 15: - return QLatin1String("Android 4.0.3 (IceCreamSandwich)"); + return QLatin1String("Android 4.0.3 (\"IceCreamSandwich\")"); case 16: - return QLatin1String("Android 4.1 (Jelly Bean)"); + return QLatin1String("Android 4.1 (\"Jelly Bean\")"); case 17: - return QLatin1String("Android 4.2 (Jelly Bean)"); + return QLatin1String("Android 4.2 (\"Jelly Bean\")"); case 18: - return QLatin1String("Android 4.3 (Jelly Bean)"); + return QLatin1String("Android 4.3 (\"Jelly Bean\")"); case 19: - return QLatin1String("Android 4.4 (KitKat)"); + return QLatin1String("Android 4.4 (\"KitKat\")"); case 20: - return QLatin1String("Android 4.4W (KitKat Wear)"); + return QLatin1String("Android 4.4W (\"KitKat Wear\")"); case 21: - return QLatin1String("Android 5.0 (Lollipop)"); + return QLatin1String("Android 5.0 (\"Lollipop\")"); case 22: - return QLatin1String("Android 5.1 (Lollipop)"); + return QLatin1String("Android 5.1 (\"Lollipop\")"); case 23: - return QLatin1String("Android 6.0 (Marshmallow)"); + return QLatin1String("Android 6.0 (\"Marshmallow\")"); case 24: - return QLatin1String("Android 7.0 (Nougat)"); + return QLatin1String("Android 7.0 (\"Nougat\")"); case 25: - return QLatin1String("Android 7.1.1 (Nougat)"); + return QLatin1String("Android 7.1.1 (\"Nougat\")"); case 26: - return QLatin1String("Android 8.0 (Oreo)"); + return QLatin1String("Android 8.0 (\"Oreo\")"); case 27: - return QLatin1String("Android 8.1 (Oreo)"); + return QLatin1String("Android 8.1 (\"Oreo\")"); case 28: - return QLatin1String("Android 9.0 (Pie)"); + return QLatin1String("Android 9.0 (\"Pie\")"); case 29: - return QLatin1String("Android 10.0 (Q)"); + return QLatin1String("Android 10.0 (\"Q\")"); case 30: - return QLatin1String("Android 11.0 (R)"); + return QLatin1String("Android 11.0 (\"R\")"); case 31: - return QLatin1String("Android 12.0 (S)"); + return QLatin1String("Android 12.0 (\"S\")"); case 32: - return QLatin1String("Android 12L (Sv2, API 32)"); + return QLatin1String("Android 12L (\"Sv2\")"); case 33: - return QLatin1String("Android 13.0 (Tiramisu)"); + return QLatin1String("Android 13.0 (\"Tiramisu\")"); + case 34: + return QLatin1String("Android API 34"); default: return Tr::tr("Unknown Android version. API Level: %1").arg(x); } @@ -592,7 +568,7 @@ static int parseMinSdk(const QDomElement &manifestElem) return 0; } -void AndroidManager::installQASIPackage(Target *target, const FilePath &packagePath) +void installQASIPackage(Target *target, const FilePath &packagePath) { const QStringList appAbis = AndroidManager::applicationAbis(target); if (appAbis.isEmpty()) @@ -612,13 +588,17 @@ void AndroidManager::installQASIPackage(Target *target, const FilePath &packageP QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); arguments << "install" << "-r " << packagePath.path(); QString error; - if (!runAdbCommandDetached(arguments, &error, true)) + Process *process = startAdbProcess(arguments, &error); + if (process) { + // TODO: Potential leak when the process is still running on Creator shutdown. + QObject::connect(process, &Process::done, process, &QObject::deleteLater); + } else { Core::MessageManager::writeDisrupting( Tr::tr("Android package installation failed.\n%1").arg(error)); + } } -bool AndroidManager::checkKeystorePassword(const FilePath &keystorePath, - const QString &keystorePasswd) +bool checkKeystorePassword(const FilePath &keystorePath, const QString &keystorePasswd) { if (keystorePasswd.isEmpty()) return false; @@ -632,10 +612,8 @@ bool AndroidManager::checkKeystorePassword(const FilePath &keystorePath, return proc.result() == ProcessResult::FinishedWithSuccess; } -bool AndroidManager::checkCertificatePassword(const FilePath &keystorePath, - const QString &keystorePasswd, - const QString &alias, - const QString &certificatePasswd) +bool checkCertificatePassword(const FilePath &keystorePath, const QString &keystorePasswd, + const QString &alias, const QString &certificatePasswd) { // assumes that the keystore password is correct QStringList arguments = {"-certreq", "-keystore", keystorePath.toUserOutput(), @@ -652,8 +630,8 @@ bool AndroidManager::checkCertificatePassword(const FilePath &keystorePath, return proc.result() == ProcessResult::FinishedWithSuccess; } -bool AndroidManager::checkCertificateExists(const FilePath &keystorePath, - const QString &keystorePasswd, const QString &alias) +bool checkCertificateExists(const FilePath &keystorePath, const QString &keystorePasswd, + const QString &alias) { // assumes that the keystore password is correct QStringList arguments = { "-list", "-keystore", keystorePath.toUserOutput(), @@ -666,32 +644,27 @@ bool AndroidManager::checkCertificateExists(const FilePath &keystorePath, return proc.result() == ProcessResult::FinishedWithSuccess; } -QProcess *AndroidManager::runAdbCommandDetached(const QStringList &args, QString *err, - bool deleteOnFinish) +Process *startAdbProcess(const QStringList &args, QString *err) { - std::unique_ptr<QProcess> p(new QProcess); + std::unique_ptr<Process> process(new Process); const FilePath adb = AndroidConfigurations::currentConfig().adbToolPath(); - qCDebug(androidManagerLog).noquote() << "Running command (async):" - << CommandLine(adb, args).toUserOutput(); - p->start(adb.toString(), args); - if (p->waitForStarted(500) && p->state() == QProcess::Running) { - if (deleteOnFinish) { - connect(p.get(), &QProcess::finished, p.get(), &QObject::deleteLater); - } - return p.release(); - } + const CommandLine command{adb, args}; + qCDebug(androidManagerLog).noquote() << "Running command (async):" << command.toUserOutput(); + process->setCommand(command); + process->start(); + if (process->waitForStarted(500) && process->state() == QProcess::Running) + return process.release(); - QString errorStr = QString::fromUtf8(p->readAllStandardError()); + const QString errorStr = process->readAllStandardError(); qCDebug(androidManagerLog).noquote() << "Running command (async) failed:" - << CommandLine(adb, args).toUserOutput() - << "Output:" << errorStr; + << command.toUserOutput() << "Output:" << errorStr; if (err) *err = errorStr; return nullptr; } -SdkToolResult AndroidManager::runCommand(const CommandLine &command, - const QByteArray &writeData, int timeoutS) +static SdkToolResult runCommand(const CommandLine &command, const QByteArray &writeData, + int timeoutS) { Android::SdkToolResult cmdResult; Process cmdProc; @@ -711,10 +684,10 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command, return cmdResult; } -SdkToolResult AndroidManager::runAdbCommand(const QStringList &args, - const QByteArray &writeData, int timeoutS) +SdkToolResult runAdbCommand(const QStringList &args, const QByteArray &writeData, int timeoutS) { return runCommand({AndroidConfigurations::currentConfig().adbToolPath(), args}, writeData, timeoutS); } -} // namespace Android + +} // namespace Android::AndroidManager diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h index ca8f9d0769f..7a141b58ce5 100644 --- a/src/plugins/android/androidmanager.h +++ b/src/plugins/android/androidmanager.h @@ -3,27 +3,17 @@ #pragma once -#include "android_global.h" - -#include <QPair> -#include <QObject> -#include <QVersionNumber> - #include <qtsupport/baseqtversion.h> #include <projectexplorer/abi.h> -QT_BEGIN_NAMESPACE -class QProcess; -QT_END_NAMESPACE - namespace ProjectExplorer { class Kit; class Target; } namespace Utils { -class CommandLine; class FilePath; +class Process; } namespace Android { @@ -36,78 +26,66 @@ public: const QString &stdErr() const { return m_stdErr; } const QString &exitMessage() const { return m_exitMessage; } -private: bool m_success = false; QString m_stdOut; QString m_stdErr; QString m_exitMessage; - friend class AndroidManager; }; -class ANDROID_EXPORT AndroidManager : public QObject +namespace AndroidManager { - Q_OBJECT -public: - static QString packageName(const ProjectExplorer::Target *target); - static QString packageName(const Utils::FilePath &manifestFile); - static QString activityName(const ProjectExplorer::Target *target); +QString packageName(const ProjectExplorer::Target *target); +QString packageName(const Utils::FilePath &manifestFile); +QString activityName(const ProjectExplorer::Target *target); - static QString deviceSerialNumber(const ProjectExplorer::Target *target); - static void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber); +QString deviceSerialNumber(const ProjectExplorer::Target *target); +void setDeviceSerialNumber(ProjectExplorer::Target *target, const QString &deviceSerialNumber); - static QString apkDevicePreferredAbi(const ProjectExplorer::Target *target); - static void setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis); +QString apkDevicePreferredAbi(const ProjectExplorer::Target *target); +void setDeviceAbis(ProjectExplorer::Target *target, const QStringList &deviceAbis); - static int deviceApiLevel(const ProjectExplorer::Target *target); - static void setDeviceApiLevel(ProjectExplorer::Target *target, int level); +int deviceApiLevel(const ProjectExplorer::Target *target); +void setDeviceApiLevel(ProjectExplorer::Target *target, int level); - static QString buildTargetSDK(const ProjectExplorer::Target *target); +QString buildTargetSDK(const ProjectExplorer::Target *target); - static int minimumSDK(const ProjectExplorer::Target *target); - static int minimumSDK(const ProjectExplorer::Kit *kit); - static int defaultMinimumSDK(const QtSupport::QtVersion *qtVersion); +int minimumSDK(const ProjectExplorer::Target *target); +int minimumSDK(const ProjectExplorer::Kit *kit); +int defaultMinimumSDK(const QtSupport::QtVersion *qtVersion); - static QStringList applicationAbis(const ProjectExplorer::Target *target); - static QString archTriplet(const QString &abi); +QStringList applicationAbis(const ProjectExplorer::Target *target); +QString archTriplet(const QString &abi); - static bool isQt5CmakeProject(const ProjectExplorer::Target *target); +bool isQt5CmakeProject(const ProjectExplorer::Target *target); - static Utils::FilePath androidBuildDirectory(const ProjectExplorer::Target *target); - static Utils::FilePath androidAppProcessDir(const ProjectExplorer::Target *target); - static Utils::FilePath buildDirectory(const ProjectExplorer::Target *target); - static Utils::FilePath manifestPath(const ProjectExplorer::Target *target); - static void setManifestPath(ProjectExplorer::Target *target, const Utils::FilePath &path); - static Utils::FilePath manifestSourcePath(const ProjectExplorer::Target *target); - static Utils::FilePath packagePath(const ProjectExplorer::Target *target); - static bool matchedAbis(const QStringList &deviceAbis, const QStringList &appAbis); - static QString devicePreferredAbi(const QStringList &deviceAbis, const QStringList &appAbis); - static ProjectExplorer::Abi androidAbi2Abi(const QString &androidAbi); - static bool skipInstallationAndPackageSteps(const ProjectExplorer::Target *target); +Utils::FilePath androidBuildDirectory(const ProjectExplorer::Target *target); +Utils::FilePath androidAppProcessDir(const ProjectExplorer::Target *target); +Utils::FilePath buildDirectory(const ProjectExplorer::Target *target); +Utils::FilePath manifestPath(const ProjectExplorer::Target *target); +void setManifestPath(ProjectExplorer::Target *target, const Utils::FilePath &path); +Utils::FilePath packagePath(const ProjectExplorer::Target *target); +ProjectExplorer::Abi androidAbi2Abi(const QString &androidAbi); +bool skipInstallationAndPackageSteps(const ProjectExplorer::Target *target); - static QString androidNameForApiLevel(int x); +QString androidNameForApiLevel(int x); - static void installQASIPackage(ProjectExplorer::Target *target, const Utils::FilePath &packagePath); +void installQASIPackage(ProjectExplorer::Target *target, const Utils::FilePath &packagePath); - static bool checkKeystorePassword(const Utils::FilePath &keystorePath, - const QString &keystorePasswd); - static bool checkCertificatePassword(const Utils::FilePath &keystorePath, - const QString &keystorePasswd, - const QString &alias, const QString &certificatePasswd); - static bool checkCertificateExists(const Utils::FilePath &keystorePath, - const QString &keystorePasswd, const QString &alias); +bool checkKeystorePassword(const Utils::FilePath &keystorePath, + const QString &keystorePasswd); +bool checkCertificatePassword(const Utils::FilePath &keystorePath, + const QString &keystorePasswd, + const QString &alias, const QString &certificatePasswd); +bool checkCertificateExists(const Utils::FilePath &keystorePath, + const QString &keystorePasswd, const QString &alias); - static QProcess *runAdbCommandDetached(const QStringList &args, QString *err = nullptr, - bool deleteOnFinish = false); - static SdkToolResult runAdbCommand(const QStringList &args, const QByteArray &writeData = {}, - int timeoutS = 30); +Utils::Process *startAdbProcess(const QStringList &args, QString *err = nullptr); +SdkToolResult runAdbCommand(const QStringList &args, const QByteArray &writeData = {}, + int timeoutS = 30); - static QJsonObject deploymentSettings(const ProjectExplorer::Target *target); - static bool isQtCreatorGenerated(const Utils::FilePath &deploymentFile); - -private: - static SdkToolResult runCommand(const Utils::CommandLine &command, - const QByteArray &writeData = {}, int timeoutS = 30); -}; +QJsonObject deploymentSettings(const ProjectExplorer::Target *target); +bool isQtCreatorGenerated(const Utils::FilePath &deploymentFile); +} // namespace AndroidManager } // namespace Android diff --git a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp index 10788598935..b6ccc7aeef0 100644 --- a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp @@ -10,10 +10,11 @@ #include <QFrame> #include <QHBoxLayout> +using namespace Utils; + namespace Android { namespace Internal { -namespace { const char extraExtraExtraHighDpiIconPath[] = "/res/drawable-xxxhdpi/"; const char extraExtraHighDpiIconPath[] = "/res/drawable-xxhdpi/"; const char extraHighDpiIconPath[] = "/res/drawable-xhdpi/"; @@ -27,7 +28,6 @@ const QSize highDpiIconSize{72, 72}; const QSize extraHighDpiIconSize{96, 96}; const QSize extraExtraHighDpiIconSize{144, 144}; const QSize extraExtraExtraHighDpiIconSize{192, 192}; -} AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidget( QWidget *parent, @@ -40,7 +40,7 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg lowDpiIconSize, Tr::tr("Master icon"), Tr::tr("Select master icon.")); - masterIconButton->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + masterIconButton->setIcon(Icon::fromTheme("document-open")); iconLayout->addWidget(masterIconButton); iconLayout->addStretch(1); diff --git a/src/plugins/android/androidmanifesteditoriconwidget.cpp b/src/plugins/android/androidmanifesteditoriconwidget.cpp index e0b23ff6ae8..2c2dfca47e1 100644 --- a/src/plugins/android/androidmanifesteditoriconwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconwidget.cpp @@ -143,9 +143,13 @@ void AndroidManifestEditorIconWidget::setIconFromPath(const FilePath &iconPath) void AndroidManifestEditorIconWidget::selectIcon() { - FilePath file = FileUtils::getOpenFilePath(this, m_iconSelectionText, - FileUtils::homePath(), - Tr::tr("Images (*.png *.jpg *.jpeg *.webp *.svg)")); // TODO: See SplashContainterWidget + FilePath file = FileUtils::getOpenFilePath( + this, + m_iconSelectionText, + FileUtils::homePath(), + //: %1 expands to wildcard list for file dialog, do not change order + Tr::tr("Images %1") + .arg("(*.png *.jpg *.jpeg *.webp *.svg)")); // TODO: See SplashContainterWidget if (file.isEmpty()) return; setIconFromPath(file); diff --git a/src/plugins/android/androidmanifesteditorwidget.cpp b/src/plugins/android/androidmanifesteditorwidget.cpp index 9c6a616abe3..e6ad6ee0251 100644 --- a/src/plugins/android/androidmanifesteditorwidget.cpp +++ b/src/plugins/android/androidmanifesteditorwidget.cpp @@ -14,16 +14,16 @@ #include <coreplugin/icore.h> #include <coreplugin/editormanager/ieditor.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> -#include <projectexplorer/projectnodes.h> +#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> #include <projectexplorer/projectwindow.h> #include <projectexplorer/target.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/kitinformation.h> #include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditor.h> @@ -41,6 +41,7 @@ #include <QFileDialog> #include <QFileInfo> #include <QFormLayout> +#include <QGroupBox> #include <QHBoxLayout> #include <QImage> #include <QLabel> @@ -84,6 +85,32 @@ static Target *androidTarget(const FilePath &fileName) return nullptr; } +class PermissionsModel: public QAbstractListModel +{ +public: + PermissionsModel(QObject *parent = nullptr); + void setPermissions(const QStringList &permissions); + const QStringList &permissions(); + QModelIndex addPermission(const QString &permission); + void removePermission(int index); + QVariant data(const QModelIndex &index, int role) const override; + +protected: + int rowCount(const QModelIndex &parent) const override; + +private: + QStringList m_permissions; +}; + +class AndroidManifestTextEditorWidget : public TextEditor::TextEditorWidget +{ +public: + explicit AndroidManifestTextEditorWidget(AndroidManifestEditorWidget *parent); + +private: + Core::IContext *m_context; +}; + AndroidManifestEditorWidget::AndroidManifestEditorWidget() { m_textEditorWidget = new AndroidManifestTextEditorWidget(this); @@ -1360,7 +1387,7 @@ void PermissionsModel::removePermission(int index) QVariant PermissionsModel::data(const QModelIndex &index, int role) const { if (role != Qt::DisplayRole || !index.isValid()) - return QVariant(); + return {}; return m_permissions[index.row()]; } diff --git a/src/plugins/android/androidmanifesteditorwidget.h b/src/plugins/android/androidmanifesteditorwidget.h index 1cce7157e44..cfb59f38401 100644 --- a/src/plugins/android/androidmanifesteditorwidget.h +++ b/src/plugins/android/androidmanifesteditorwidget.h @@ -5,23 +5,19 @@ #include <texteditor/texteditor.h> -#include <QAbstractListModel> -#include <QGroupBox> -#include <QTabWidget> #include <QStackedWidget> #include <QTimer> QT_BEGIN_NAMESPACE class QCheckBox; class QDomDocument; -class QDomElement; class QComboBox; +class QGroupBox; class QPushButton; class QLabel; class QLineEdit; class QListView; -class QSpinBox; -class QToolButton; +class QTabWidget; class QXmlStreamReader; class QXmlStreamWriter; QT_END_NAMESPACE @@ -32,39 +28,13 @@ namespace Android::Internal { class AndroidManifestEditor; class AndroidManifestEditorIconContainerWidget; -class AndroidManifestEditorWidget; +class PermissionsModel; class SplashScreenContainerWidget; -class PermissionsModel: public QAbstractListModel -{ - Q_OBJECT -public: - PermissionsModel(QObject *parent = nullptr); - void setPermissions(const QStringList &permissions); - const QStringList &permissions(); - QModelIndex addPermission(const QString &permission); - void removePermission(int index); - QVariant data(const QModelIndex &index, int role) const override; - -protected: - int rowCount(const QModelIndex &parent) const override; - -private: - QStringList m_permissions; -}; - -class AndroidManifestTextEditorWidget : public TextEditor::TextEditorWidget -{ -public: - explicit AndroidManifestTextEditorWidget(AndroidManifestEditorWidget *parent); - -private: - Core::IContext *m_context; -}; - class AndroidManifestEditorWidget : public QStackedWidget { Q_OBJECT + public: enum EditorPage { General = 0, diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index e29bac3476b..7aea1208510 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -9,17 +9,17 @@ #include <projectexplorer/abstractprocessstep.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildsteplist.h> +#include <projectexplorer/buildsystem.h> #include <projectexplorer/gnumakeparser.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/buildsystem.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/hostosinfo.h> #include <utils/process.h> @@ -46,7 +46,7 @@ public: private: bool init() final; void setupOutputFormatter(OutputFormatter *formatter) final; - void doRun() final; + Tasking::GroupItem runRecipe() final; void reportWarningOrError(const QString &message, ProjectExplorer::Task::TaskType type); @@ -60,7 +60,7 @@ AndroidPackageInstallationStep::AndroidPackageInstallationStep(BuildStepList *bs setWidgetExpandedByDefault(false); setImmutable(true); setSummaryUpdater([this] { - return Tr::tr("<b>Make install:</b> Copy App Files to %1") + return Tr::tr("<b>Make install:</b> Copy App Files to \"%1\"") .arg(QDir::toNativeSeparators(nativeAndroidBuildPath())); }); setUseEnglishOutput(); @@ -120,55 +120,58 @@ void AndroidPackageInstallationStep::setupOutputFormatter(OutputFormatter *forma AbstractProcessStep::setupOutputFormatter(formatter); } -void AndroidPackageInstallationStep::doRun() +Tasking::GroupItem AndroidPackageInstallationStep::runRecipe() { - if (AndroidManager::skipInstallationAndPackageSteps(target())) { - reportWarningOrError(Tr::tr("Product type is not an application, not running the " - "Make install step."), - Task::Warning); - emit finished(true); - return; - } + using namespace Tasking; - QString error; - for (const QString &dir : std::as_const(m_androidDirsToClean)) { - FilePath androidDir = FilePath::fromString(dir); - if (!dir.isEmpty() && androidDir.exists()) { - emit addOutput(Tr::tr("Removing directory %1").arg(dir), OutputFormat::NormalMessage); - if (!androidDir.removeRecursively(&error)) { - reportWarningOrError(Tr::tr("Failed to clean \"%1\" from the previous build, with " - "error:\n%2").arg(androidDir.toUserOutput()).arg(error), - Task::TaskType::Error); - emit finished(false); - return; + const auto onSetup = [this] { + if (AndroidManager::skipInstallationAndPackageSteps(target())) { + reportWarningOrError(Tr::tr("Product type is not an application, not running the " + "Make install step."), Task::Warning); + return SetupResult::StopWithDone; + } + + for (const QString &dir : std::as_const(m_androidDirsToClean)) { + const FilePath androidDir = FilePath::fromString(dir); + if (!dir.isEmpty() && androidDir.exists()) { + emit addOutput(Tr::tr("Removing directory %1").arg(dir), OutputFormat::NormalMessage); + QString error; + if (!androidDir.removeRecursively(&error)) { + reportWarningOrError(Tr::tr("Failed to clean \"%1\" from the previous build, " + "with error:\n%2").arg(androidDir.toUserOutput()).arg(error), + Task::TaskType::Error); + return SetupResult::StopWithError; + } } } - } - AbstractProcessStep::doRun(); - // NOTE: This is a workaround for QTCREATORBUG-24155 - // Needed for Qt 5.15.0 and Qt 5.14.x versions - if (buildType() == BuildConfiguration::BuildType::Debug) { - QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit()); - if (version && version->qtVersion() >= QVersionNumber(5, 14) - && version->qtVersion() <= QVersionNumber(5, 15, 0)) { - const QString assetsDebugDir = nativeAndroidBuildPath().append( - "/assets/--Added-by-androiddeployqt--/"); - QDir dir; - if (!dir.exists(assetsDebugDir)) - dir.mkpath(assetsDebugDir); + // NOTE: This is a workaround for QTCREATORBUG-24155 + // Needed for Qt 5.15.0 and Qt 5.14.x versions + if (buildType() == BuildConfiguration::BuildType::Debug) { + QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(kit()); + if (version && version->qtVersion() >= QVersionNumber(5, 14) + && version->qtVersion() <= QVersionNumber(5, 15, 0)) { + const QString assetsDebugDir = nativeAndroidBuildPath().append( + "/assets/--Added-by-androiddeployqt--/"); + QDir dir; + if (!dir.exists(assetsDebugDir)) + dir.mkpath(assetsDebugDir); - QFile file(assetsDebugDir + "debugger.command"); - if (file.open(QIODevice::WriteOnly)) { - qCDebug(packageInstallationStepLog, "Successful added %s to the package.", - qPrintable(file.fileName())); - } else { - qCDebug(packageInstallationStepLog, - "Cannot add %s to the package. The QML debugger might not work properly.", - qPrintable(file.fileName())); + QFile file(assetsDebugDir + "debugger.command"); + if (file.open(QIODevice::WriteOnly)) { + qCDebug(packageInstallationStepLog, "Successful added %s to the package.", + qPrintable(file.fileName())); + } else { + qCDebug(packageInstallationStepLog, + "Cannot add %s to the package. The QML debugger might not work properly.", + qPrintable(file.fileName())); + } } } - } + return SetupResult::Continue; + }; + + return Group { onGroupSetup(onSetup), defaultProcessTask() }; } void AndroidPackageInstallationStep::reportWarningOrError(const QString &message, diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp index 916600c1df7..9eeee4dad29 100644 --- a/src/plugins/android/androidplugin.cpp +++ b/src/plugins/android/androidplugin.cpp @@ -41,14 +41,17 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> #include <qtsupport/qtversionmanager.h> +#include <nanotrace/nanotrace.h> + #include <QTimer> using namespace ProjectExplorer; @@ -141,6 +144,7 @@ void AndroidPlugin::kitsRestored() void AndroidPlugin::askUserAboutAndroidSetup() { + NANOTRACE_SCOPE("Android", "AndroidPlugin::askUserAboutAndroidSetup"); if (!Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting)) return; diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp index 4d95f485d0e..08089d5f7f3 100644 --- a/src/plugins/android/androidpotentialkit.cpp +++ b/src/plugins/android/androidpotentialkit.cpp @@ -6,14 +6,12 @@ #include "androidpotentialkit.h" #include "androidtr.h" -#include <app/app_version.h> - #include <coreplugin/coreicons.h> #include <coreplugin/icore.h> -#include <projectexplorer/kitmanager.h> #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/kitmanager.h> #include <qtsupport/qtversionmanager.h> #include <qtsupport/baseqtversion.h> @@ -22,10 +20,10 @@ #include <utils/utilsicons.h> #include <QGridLayout> +#include <QGuiApplication> #include <QLabel> #include <QPushButton> - namespace Android::Internal { class AndroidPotentialKitWidget : public Utils::DetailsWidget @@ -83,7 +81,7 @@ AndroidPotentialKitWidget::AndroidPotentialKitWidget(QWidget *parent) auto label = new QLabel; label->setText(Tr::tr("%1 needs additional settings to enable Android support." " You can configure those settings in the Options dialog.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + .arg(QGuiApplication::applicationDisplayName())); label->setWordWrap(true); layout->addWidget(label, 0, 0, 1, 2); diff --git a/src/plugins/android/androidqmlpreviewworker.cpp b/src/plugins/android/androidqmlpreviewworker.cpp index af238e00d0d..fc51e2a22dd 100644 --- a/src/plugins/android/androidqmlpreviewworker.cpp +++ b/src/plugins/android/androidqmlpreviewworker.cpp @@ -25,7 +25,7 @@ #include <qmlprojectmanager/qmlprojectmanagerconstants.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/async.h> #include <utils/process.h> diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp index 3dc6561311a..5ba8775b67e 100644 --- a/src/plugins/android/androidqtversion.cpp +++ b/src/plugins/android/androidqtversion.cpp @@ -12,7 +12,7 @@ #include <utils/fileutils.h> #include <utils/hostosinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionmanager.h> @@ -112,16 +112,41 @@ QString AndroidQtVersion::description() const const QStringList &AndroidQtVersion::androidAbis() const { - ensureMkSpecParsed(); + if (m_androidAbis.isEmpty()) { + bool sanityCheckNotUsed; + const BuiltWith bw = builtWith(&sanityCheckNotUsed); + if (!bw.androidAbi.isEmpty()) { + m_androidAbis << bw.androidAbi; + m_minNdk = bw.apiVersion; + } else { + ensureMkSpecParsed(); + } + } + return m_androidAbis; } int AndroidQtVersion::minimumNDK() const { - ensureMkSpecParsed(); + if (m_minNdk == -1) + ensureMkSpecParsed(); return m_minNdk; } +QString AndroidQtVersion::androidDeploymentSettingsFileName(const Target *target) +{ + const BuildSystem *bs = target->buildSystem(); + if (!bs) + return {}; + const QString buildKey = target->activeBuildKey(); + const QString displayName = bs->buildTarget(buildKey).displayName; + const QString fileName = AndroidManager::isQt5CmakeProject(target) + ? QLatin1String("android_deployment_settings.json") + : QString::fromLatin1("android-%1-deployment-settings.json") + .arg(displayName); + return fileName; +} + Utils::FilePath AndroidQtVersion::androidDeploymentSettings(const Target *target) { // Try to fetch the file name from node data as provided by qmake and Qbs @@ -134,15 +159,8 @@ Utils::FilePath AndroidQtVersion::androidDeploymentSettings(const Target *target } // If unavailable, construct the name by ourselves (CMake) - const BuildSystem *bs = target->buildSystem(); - if (!bs) - return {}; - const QString displayName = bs->buildTarget(buildKey).displayName; - return AndroidManager::buildDirectory(target).pathAppended( - AndroidManager::isQt5CmakeProject(target) - ? QLatin1String("android_deployment_settings.json") - : QString::fromLatin1("android-%1-deployment-settings.json") - .arg(displayName)); + const QString fileName = androidDeploymentSettingsFileName(target); + return AndroidManager::buildDirectory(target) / fileName; } AndroidQtVersion::BuiltWith AndroidQtVersion::builtWith(bool *ok) const @@ -170,6 +188,25 @@ static int versionFromPlatformString(const QString &string, bool *ok = nullptr) return match.hasMatch() ? match.captured(1).toInt(ok) : -1; } +static QString abiFromCompilerTarget(const QString &string) +{ + const QStringList components = string.split("-"); + if (components.isEmpty()) + return {}; + + QString qtAbi; + const QString compilerAbi = components.first(); + if (compilerAbi == Constants::AArch64ToolsDisplayName) + qtAbi = ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A; + else if (compilerAbi == Constants::ArmV7ToolsDisplayName) + qtAbi = ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A; + else if (compilerAbi == Constants::X86_64ToolsDisplayName) + qtAbi = ProjectExplorer::Constants::ANDROID_ABI_X86_64; + else if (compilerAbi == Constants::X86ToolsDisplayName) + qtAbi = ProjectExplorer::Constants::ANDROID_ABI_X86; + return qtAbi; +} + AndroidQtVersion::BuiltWith AndroidQtVersion::parseBuiltWith(const QByteArray &modulesCoreJsonData, bool *ok) { @@ -189,6 +226,10 @@ AndroidQtVersion::BuiltWith AndroidQtVersion::parseBuiltWith(const QByteArray &m result.ndkVersion = QVersionNumber::fromString(version.toString()); } } + if (const QJsonValue compilerTarget = builtWith["compiler_target"]; + !compilerTarget.isUndefined()) { + result.androidAbi = abiFromCompilerTarget(compilerTarget.toString()); + } } if (ok) diff --git a/src/plugins/android/androidqtversion.h b/src/plugins/android/androidqtversion.h index 1549c870dae..95a69a6c2ea 100644 --- a/src/plugins/android/androidqtversion.h +++ b/src/plugins/android/androidqtversion.h @@ -32,11 +32,13 @@ public: const QStringList &androidAbis() const; int minimumNDK() const; + static QString androidDeploymentSettingsFileName(const ProjectExplorer::Target *target); static Utils::FilePath androidDeploymentSettings(const ProjectExplorer::Target *target); struct BuiltWith { int apiVersion = -1; QVersionNumber ndkVersion; + QString androidAbi; }; static BuiltWith parseBuiltWith(const QByteArray &modulesCoreJsonData, bool *ok = nullptr); BuiltWith builtWith(bool *ok = nullptr) const; diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index 45f991ca747..3f241fc50a4 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -7,15 +7,13 @@ #include "androidtoolchain.h" #include "androidtr.h" -#include <app/app_version.h> - #include <projectexplorer/buildsystem.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/detailswidget.h> #include <utils/process.h> @@ -30,16 +28,17 @@ namespace Android { class BaseStringListAspect final : public Utils::StringAspect { public: - explicit BaseStringListAspect() = default; - ~BaseStringListAspect() final = default; + explicit BaseStringListAspect(AspectContainer *container) + : StringAspect(container) + {} - void fromMap(const QVariantMap &map) final + void fromMap(const Store &map) final { // Pre Qt Creator 5.0 hack: Reads QStringList as QString setValue(map.value(settingsKey()).toStringList().join('\n')); } - void toMap(QVariantMap &map) const final + void toMap(Store &map) const final { // Pre Qt Creator 5.0 hack: Writes QString as QStringList map.insert(settingsKey(), value().split('\n')); @@ -52,38 +51,34 @@ public: AndroidRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {}); + environment.addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {}); - auto extraAppArgsAspect = addAspect<ArgumentsAspect>(macroExpander()); + extraAppArgs.setMacroExpander(macroExpander()); - connect(extraAppArgsAspect, &BaseAspect::changed, this, [target, extraAppArgsAspect] { + connect(&extraAppArgs, &BaseAspect::changed, this, [this, target] { if (target->buildConfigurations().first()->buildType() == BuildConfiguration::BuildType::Release) { const QString buildKey = target->activeBuildKey(); target->buildSystem()->setExtraData(buildKey, Android::Constants::AndroidApplicationArgs, - extraAppArgsAspect->arguments()); + extraAppArgs()); } }); - auto amStartArgsAspect = addAspect<StringAspect>(); - amStartArgsAspect->setId(Constants::ANDROID_AM_START_ARGS); - amStartArgsAspect->setSettingsKey("Android.AmStartArgsKey"); - amStartArgsAspect->setLabelText(Tr::tr("Activity manager start arguments:")); - amStartArgsAspect->setDisplayStyle(StringAspect::LineEditDisplay); - amStartArgsAspect->setHistoryCompleter("Android.AmStartArgs.History"); + amStartArgs.setId(Constants::ANDROID_AM_START_ARGS); + amStartArgs.setSettingsKey("Android.AmStartArgsKey"); + amStartArgs.setLabelText(Tr::tr("Activity manager start arguments:")); + amStartArgs.setDisplayStyle(StringAspect::LineEditDisplay); + amStartArgs.setHistoryCompleter("Android.AmStartArgs.History"); - auto preStartShellCmdAspect = addAspect<BaseStringListAspect>(); - preStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay); - preStartShellCmdAspect->setId(Constants::ANDROID_PRESTARTSHELLCMDLIST); - preStartShellCmdAspect->setSettingsKey("Android.PreStartShellCmdListKey"); - preStartShellCmdAspect->setLabelText(Tr::tr("Pre-launch on-device shell commands:")); + preStartShellCmd.setDisplayStyle(StringAspect::TextEditDisplay); + preStartShellCmd.setId(Constants::ANDROID_PRESTARTSHELLCMDLIST); + preStartShellCmd.setSettingsKey("Android.PreStartShellCmdListKey"); + preStartShellCmd.setLabelText(Tr::tr("Pre-launch on-device shell commands:")); - auto postStartShellCmdAspect = addAspect<BaseStringListAspect>(); - postStartShellCmdAspect->setDisplayStyle(StringAspect::TextEditDisplay); - postStartShellCmdAspect->setId(Constants::ANDROID_POSTFINISHSHELLCMDLIST); - postStartShellCmdAspect->setSettingsKey("Android.PostStartShellCmdListKey"); - postStartShellCmdAspect->setLabelText(Tr::tr("Post-quit on-device shell commands:")); + postStartShellCmd.setDisplayStyle(StringAspect::TextEditDisplay); + postStartShellCmd.setId(Constants::ANDROID_POSTFINISHSHELLCMDLIST); + postStartShellCmd.setSettingsKey("Android.PostStartShellCmdListKey"); + postStartShellCmd.setLabelText(Tr::tr("Post-quit on-device shell commands:")); setUpdater([this] { const BuildTargetInfo bti = buildTargetInfo(); @@ -93,6 +88,12 @@ public: connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); } + + EnvironmentAspect environment{this}; + ArgumentsAspect extraAppArgs{this}; + StringAspect amStartArgs{this}; + BaseStringListAspect preStartShellCmd{this}; + BaseStringListAspect postStartShellCmd{this}; }; AndroidRunConfigurationFactory::AndroidRunConfigurationFactory() diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h index 9ad677aeef3..8652c4ddfc6 100644 --- a/src/plugins/android/androidrunconfiguration.h +++ b/src/plugins/android/androidrunconfiguration.h @@ -3,8 +3,6 @@ #pragma once -#include "android_global.h" - #include <projectexplorer/runconfiguration.h> namespace Android { diff --git a/src/plugins/android/androidruncontrol.cpp b/src/plugins/android/androidruncontrol.cpp index 16a20b627ad..4dc0aea4296 100644 --- a/src/plugins/android/androidruncontrol.cpp +++ b/src/plugins/android/androidruncontrol.cpp @@ -22,9 +22,6 @@ public: explicit AndroidRunSupport(ProjectExplorer::RunControl *runControl, const QString &intentName = QString()); ~AndroidRunSupport() override; - - void start() override; - void stop() override; }; AndroidRunSupport::AndroidRunSupport(RunControl *runControl, const QString &intentName) @@ -38,16 +35,6 @@ AndroidRunSupport::~AndroidRunSupport() stop(); } -void AndroidRunSupport::start() -{ - AndroidRunner::start(); -} - -void AndroidRunSupport::stop() -{ - AndroidRunner::stop(); -} - AndroidRunWorkerFactory::AndroidRunWorkerFactory() { setProduct<AndroidRunSupport>(); diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index c3212f8e68b..b62e5563413 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -18,7 +18,7 @@ #include <projectexplorer/projectexplorersettings.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/url.h> #include <QHostAddress> diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index c50129aac25..b97cc7fac11 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -7,7 +7,7 @@ #include "androidrunnerworker.h" #include "androidtr.h" -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerrunconfigurationaspect.h> #include <projectexplorer/buildconfiguration.h> @@ -18,7 +18,7 @@ #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/async.h> #include <utils/fileutils.h> @@ -53,6 +53,8 @@ namespace Internal { static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done"); static const QRegularExpression userIdPattern("u(\\d+)_a"); +static const int s_jdbTimeout = 5000; + static int APP_START_TIMEOUT = 45000; static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, int msecs = APP_START_TIMEOUT) @@ -139,18 +141,6 @@ static void findProcessPIDAndUser(QPromise<PidUserPair> &promise, promise.addResult(PidUserPair(processPID, processUser)); } -static void deleter(QProcess *p) -{ - qCDebug(androidRunWorkerLog) << "Killing process:" << p->objectName(); - p->terminate(); - if (!p->waitForFinished(1000)) { - p->kill(); - p->waitForFinished(); - } - // Might get deleted from its own signal handler. - p->deleteLater(); -} - static QString gdbServerArch(const QString &androidAbi) { if (androidAbi == ProjectExplorer::Constants::ANDROID_ABI_ARM64_V8A) @@ -212,14 +202,8 @@ static FilePath debugServer(bool useLldb, const Target *target) return {}; } - AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packageName) : m_packageName(packageName) - , m_adbLogcatProcess(nullptr, deleter) - , m_psIsAlive(nullptr, deleter) - , m_debugServerProcess(nullptr, deleter) - , m_jdbProcess(nullptr, deleter) - { auto runControl = runner->runControl(); m_useLldb = Debugger::DebuggerKitAspect::engineType(runControl->kit()) @@ -263,34 +247,28 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release) m_extraAppParams = runControl->commandLine().arguments(); - if (const QVariantMap sd = runControl->settingsData(Constants::ANDROID_AM_START_ARGS); + if (const Store sd = runControl->settingsData(Constants::ANDROID_AM_START_ARGS); !sd.values().isEmpty()) { QTC_CHECK(sd.first().type() == QVariant::String); const QString startArgs = sd.first().toString(); m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix); } - if (const QVariantMap sd = runControl->settingsData(Constants::ANDROID_PRESTARTSHELLCMDLIST); + if (const Store sd = runControl->settingsData(Constants::ANDROID_PRESTARTSHELLCMDLIST); !sd.values().isEmpty()) { QTC_CHECK(sd.first().type() == QVariant::String); const QStringList commands = sd.first().toString().split('\n', Qt::SkipEmptyParts); for (const QString &shellCmd : commands) m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); } - const auto preStartCmdList = runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST); - for (const QString &shellCmd : preStartCmdList.toStringList()) - m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); - if (const QVariantMap sd = runControl->settingsData(Constants::ANDROID_POSTFINISHSHELLCMDLIST); + if (const Store sd = runControl->settingsData(Constants::ANDROID_POSTFINISHSHELLCMDLIST); !sd.values().isEmpty()) { QTC_CHECK(sd.first().type() == QVariant::String); const QStringList commands = sd.first().toString().split('\n', Qt::SkipEmptyParts); for (const QString &shellCmd : commands) m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); } - const auto postFinishCmdList = runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST); - for (const QString &shellCmd : postFinishCmdList.toStringList()) - m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd)); m_debugServerPath = debugServer(m_useLldb, target); qCDebug(androidRunWorkerLog).noquote() << "Device Serial:" << m_deviceSerialNumber @@ -412,13 +390,13 @@ void AndroidRunnerWorker::forceStop() void AndroidRunnerWorker::logcatReadStandardError() { if (m_processPID != -1) - logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true); + logcatProcess(m_adbLogcatProcess->readAllRawStandardError(), m_stderrBuffer, true); } void AndroidRunnerWorker::logcatReadStandardOutput() { if (m_processPID != -1) - logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false); + logcatProcess(m_adbLogcatProcess->readAllRawStandardOutput(), m_stdoutBuffer, false); } void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError) @@ -510,16 +488,16 @@ void Android::Internal::AndroidRunnerWorker::asyncStartLogcat() { // Its assumed that the device or avd returned by selector() is online. // Start the logcat process before app starts. - QTC_ASSERT(!m_adbLogcatProcess, /**/); + QTC_CHECK(!m_adbLogcatProcess); // Ideally AndroidManager::runAdbCommandDetached() should be used, but here // we need to connect the readyRead signals from logcat otherwise we might // lost some output between the process start and connecting those signals. - m_adbLogcatProcess.reset(new QProcess()); + m_adbLogcatProcess.reset(new Process); - connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardOutput, + connect(m_adbLogcatProcess.get(), &Process::readyReadStandardOutput, this, &AndroidRunnerWorker::logcatReadStandardOutput); - connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardError, + connect(m_adbLogcatProcess.get(), &Process::readyReadStandardError, this, &AndroidRunnerWorker::logcatReadStandardError); // Get target current time to fetch only recent logs @@ -535,7 +513,8 @@ void Android::Internal::AndroidRunnerWorker::asyncStartLogcat() const FilePath adb = AndroidConfigurations::currentConfig().adbToolPath(); qCDebug(androidRunWorkerLog).noquote() << "Running logcat command (async):" << CommandLine(adb, logcatArgs).toUserOutput(); - m_adbLogcatProcess->start(adb.toString(), logcatArgs); + m_adbLogcatProcess->setCommand({adb, logcatArgs}); + m_adbLogcatProcess->start(); if (m_adbLogcatProcess->waitForStarted(500) && m_adbLogcatProcess->state() == QProcess::Running) m_adbLogcatProcess->setObjectName("AdbLogcatProcess"); } @@ -680,7 +659,7 @@ bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, << "platform" // << "--server" // Can lead to zombie servers << "--listen" << QString("*:%1").arg(m_localDebugServerPort.toString()); - m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(lldbServerArgs, &lldbServerErr)); + m_debugServerProcess.reset(AndroidManager::startAdbProcess(lldbServerArgs, &lldbServerErr)); if (!m_debugServerProcess) { qCDebug(androidRunWorkerLog) << "Debugger process failed to start" << lldbServerErr; @@ -700,7 +679,7 @@ bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir, gdbServerErr += adbArgs; gdbServerErr << debugServerFile << "--multi" << "+" + gdbServerSocket; - m_debugServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerErr, &gdbProcessErr)); + m_debugServerProcess.reset(AndroidManager::startAdbProcess(gdbServerErr, &gdbProcessErr)); if (!m_debugServerProcess) { qCDebug(androidRunWorkerLog) << "Debugger process failed to start" << gdbServerErr; @@ -761,7 +740,7 @@ void AndroidRunnerWorker::handleJdbWaiting() } m_afterFinishAdbCommands.push_back(removeForward.join(' ')); - FilePath jdbPath = AndroidConfigurations::currentConfig().openJDKLocation() + const FilePath jdbPath = AndroidConfigurations::currentConfig().openJDKLocation() .pathAppended("bin/jdb").withExecutableSuffix(); QStringList jdbArgs("-connect"); @@ -769,14 +748,16 @@ void AndroidRunnerWorker::handleJdbWaiting() .arg(m_localJdbServerPort.toString()); qCDebug(androidRunWorkerLog).noquote() << "Starting JDB:" << CommandLine(jdbPath, jdbArgs).toUserOutput(); - std::unique_ptr<QProcess, Deleter> jdbProcess(new QProcess, &deleter); - jdbProcess->setProcessChannelMode(QProcess::MergedChannels); - jdbProcess->start(jdbPath.toString(), jdbArgs); - if (!jdbProcess->waitForStarted()) { + m_jdbProcess.reset(new Process); + m_jdbProcess->setProcessChannelMode(QProcess::MergedChannels); + m_jdbProcess->setCommand({jdbPath, jdbArgs}); + m_jdbProcess->setReaperTimeout(s_jdbTimeout); + m_jdbProcess->start(); + if (!m_jdbProcess->waitForStarted()) { emit remoteProcessFinished(Tr::tr("Failed to start JDB.")); + m_jdbProcess.reset(); return; } - m_jdbProcess = std::move(jdbProcess); m_jdbProcess->setObjectName("JdbProcess"); } @@ -786,7 +767,7 @@ void AndroidRunnerWorker::handleJdbSettled() auto waitForCommand = [this] { for (int i = 0; i < 120 && m_jdbProcess->state() == QProcess::Running; ++i) { m_jdbProcess->waitForReadyRead(500); - QByteArray lines = m_jdbProcess->readAll(); + const QByteArray lines = m_jdbProcess->readAllRawStandardOutput(); const auto linesList = lines.split('\n'); for (const auto &line : linesList) { auto msg = line.trimmed(); @@ -798,22 +779,14 @@ void AndroidRunnerWorker::handleJdbSettled() }; const QStringList commands{"threads", "cont", "exit"}; - const int jdbTimeout = 5000; for (const QString &command : commands) { - if (waitForCommand()) { - m_jdbProcess->write(QString("%1\n").arg(command).toLatin1()); - m_jdbProcess->waitForBytesWritten(jdbTimeout); - } + if (waitForCommand()) + m_jdbProcess->write(QString("%1\n").arg(command)); } - if (!m_jdbProcess->waitForFinished(jdbTimeout)) { - m_jdbProcess->terminate(); - if (!m_jdbProcess->waitForFinished(jdbTimeout)) { - qCDebug(androidRunWorkerLog) << "Killing JDB process"; - m_jdbProcess->kill(); - m_jdbProcess->waitForFinished(); - } + if (!m_jdbProcess->waitForFinished(s_jdbTimeout)) { + m_jdbProcess.reset(); } else if (m_jdbProcess->exitStatus() == QProcess::NormalExit && m_jdbProcess->exitCode() == 0) { qCDebug(androidRunWorkerLog) << "JDB settled"; return; @@ -873,12 +846,11 @@ void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser) logcatReadStandardOutput(); QTC_ASSERT(!m_psIsAlive, /**/); QStringList isAliveArgs = selector() << "shell" << pidPollingScript.arg(m_processPID); - m_psIsAlive.reset(AndroidManager::runAdbCommandDetached(isAliveArgs)); + m_psIsAlive.reset(AndroidManager::startAdbProcess(isAliveArgs)); QTC_ASSERT(m_psIsAlive, return); m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); - connect(m_psIsAlive.get(), &QProcess::finished, - this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, PidUserPair(-1, -1))); + connect(m_psIsAlive.get(), &Process::done, this, [this] { onProcessIdChanged({-1, -1}); }); } } diff --git a/src/plugins/android/androidrunnerworker.h b/src/plugins/android/androidrunnerworker.h index 7e41d2e9c73..052fe2812cb 100644 --- a/src/plugins/android/androidrunnerworker.h +++ b/src/plugins/android/androidrunnerworker.h @@ -12,11 +12,10 @@ #include <QFuture> #include <utility> -QT_BEGIN_NAMESPACE -class QProcess; -QT_END_NAMESPACE - -namespace Utils { class FilePath; } +namespace Utils { +class FilePath; +class Process; +} namespace ProjectExplorer { class RunWorker; } namespace Android { @@ -77,7 +76,6 @@ private: Settled }; void onProcessIdChanged(PidUserPair pidUser); - using Deleter = void (*)(QProcess *); // Create the processes and timer in the worker thread, for correct thread affinity bool m_isPreNougat = false; @@ -88,8 +86,8 @@ private: QStringList m_amStartExtraArgs; qint64 m_processPID = -1; qint64 m_processUser = -1; - std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess; - std::unique_ptr<QProcess, Deleter> m_psIsAlive; + std::unique_ptr<Utils::Process> m_adbLogcatProcess; + std::unique_ptr<Utils::Process> m_psIsAlive; QByteArray m_stdoutBuffer; QByteArray m_stderrBuffer; QFuture<PidUserPair> m_pidFinder; @@ -100,8 +98,8 @@ private: QUrl m_qmlServer; JDBState m_jdbState = JDBState::Idle; Utils::Port m_localJdbServerPort; - std::unique_ptr<QProcess, Deleter> m_debugServerProcess; // gdbserver or lldb-server - std::unique_ptr<QProcess, Deleter> m_jdbProcess; + std::unique_ptr<Utils::Process> m_debugServerProcess; // gdbserver or lldb-server + std::unique_ptr<Utils::Process> m_jdbProcess; QString m_deviceSerialNumber; int m_apiLevel = -1; QString m_extraAppParams; diff --git a/src/plugins/android/androidsdkdownloader.cpp b/src/plugins/android/androidsdkdownloader.cpp index f975ecbd589..20d89436a7c 100644 --- a/src/plugins/android/androidsdkdownloader.cpp +++ b/src/plugins/android/androidsdkdownloader.cpp @@ -5,43 +5,84 @@ #include "androidsdkdownloader.h" #include "androidtr.h" -#include <utils/archive.h> +#include <solutions/tasking/networkquery.h> + #include <utils/filepath.h> +#include <utils/networkaccessmanager.h> +#include <utils/unarchiver.h> #include <coreplugin/icore.h> #include <QCryptographicHash> #include <QLoggingCategory> +#include <QProgressDialog> #include <QStandardPaths> using namespace Utils; -namespace { -Q_LOGGING_CATEGORY(sdkDownloaderLog, "qtc.android.sdkDownloader", QtWarningMsg) -} +namespace { Q_LOGGING_CATEGORY(sdkDownloaderLog, "qtc.android.sdkDownloader", QtWarningMsg) } -namespace Android { -namespace Internal { +namespace Android::Internal { /** * @class SdkDownloader * @brief Download Android SDK tools package from within Qt Creator. */ AndroidSdkDownloader::AndroidSdkDownloader() : m_androidConfig(AndroidConfigurations::currentConfig()) -{ - connect(&m_manager, &QNetworkAccessManager::finished, this, &AndroidSdkDownloader::downloadFinished); -} +{} AndroidSdkDownloader::~AndroidSdkDownloader() = default; -#if QT_CONFIG(ssl) -void AndroidSdkDownloader::sslErrors(const QList<QSslError> &sslErrors) +static bool isHttpRedirect(QNetworkReply *reply) { - for (const QSslError &error : sslErrors) - qCDebug(sdkDownloaderLog, "SSL error: %s\n", qPrintable(error.errorString())); - cancelWithError(Tr::tr("Encountered SSL errors, download is aborted.")); + const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 + || statusCode == 307 || statusCode == 308; +} + +static FilePath sdkFromUrl(const QUrl &url) +{ + const QString path = url.path(); + QString basename = QFileInfo(path).fileName(); + + if (basename.isEmpty()) + basename = "sdk-tools.zip"; + + if (QFileInfo::exists(basename)) { + int i = 0; + basename += '.'; + while (QFileInfo::exists(basename + QString::number(i))) + ++i; + basename += QString::number(i); + } + + return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) + / basename; +} + +// TODO: Make it a separate async task in a chain? +static std::optional<QString> saveToDisk(const FilePath &filename, QIODevice *data) +{ + QFile file(filename.toString()); + if (!file.open(QIODevice::WriteOnly)) { + return Tr::tr("Could not open \"%1\" for writing: %2.") + .arg(filename.toUserOutput(), file.errorString()); + } + file.write(data->readAll()); + return {}; +} + +// TODO: Make it a separate async task in a chain? +static bool verifyFileIntegrity(const FilePath fileName, const QByteArray &sha256) +{ + QFile file(fileName.toString()); + if (file.open(QFile::ReadOnly)) { + QCryptographicHash hash(QCryptographicHash::Sha256); + if (hash.addData(&file)) + return hash.result() == sha256; + } + return false; } -#endif void AndroidSdkDownloader::downloadAndExtractSdk() { @@ -50,57 +91,111 @@ void AndroidSdkDownloader::downloadAndExtractSdk() return; } - QNetworkRequest request(m_androidConfig.sdkToolsUrl()); - m_reply = m_manager.get(request); - -#if QT_CONFIG(ssl) - connect(m_reply, &QNetworkReply::sslErrors, this, &AndroidSdkDownloader::sslErrors); -#endif - - m_progressDialog = new QProgressDialog(Tr::tr("Downloading SDK Tools package..."), Tr::tr("Cancel"), - 0, 100, Core::ICore::dialogParent()); + m_progressDialog.reset(new QProgressDialog(Tr::tr("Downloading SDK Tools package..."), + Tr::tr("Cancel"), 0, 100, Core::ICore::dialogParent())); m_progressDialog->setWindowModality(Qt::ApplicationModal); m_progressDialog->setWindowTitle(dialogTitle()); m_progressDialog->setFixedSize(m_progressDialog->sizeHint()); - - connect(m_reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 max) { - m_progressDialog->setRange(0, max); - m_progressDialog->setValue(received); + m_progressDialog->setAutoClose(false); + connect(m_progressDialog.get(), &QProgressDialog::canceled, this, [this] { + m_progressDialog.release()->deleteLater(); + m_taskTree.reset(); }); - connect(m_progressDialog, &QProgressDialog::canceled, this, &AndroidSdkDownloader::cancel); + using namespace Tasking; - connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this] { - if (!Archive::supportsFile(m_sdkFilename)) - return; - const FilePath extractDir = m_sdkFilename.parentDir(); - m_archive.reset(new Archive(m_sdkFilename, extractDir)); - if (m_archive->isValid()) { - connect(m_archive.get(), &Archive::finished, this, [this, extractDir](bool success) { - if (success) { - // Save the extraction path temporarily which can be used by sdkmanager - // to install essential packages at firt time setup. - m_androidConfig.setTemporarySdkToolsPath( - extractDir.pathAppended(Constants::cmdlineToolsName)); - emit sdkExtracted(); - } - m_archive.release()->deleteLater(); + TreeStorage<std::optional<FilePath>> storage; + + const auto onQuerySetup = [this](NetworkQuery &query) { + query.setRequest(QNetworkRequest(m_androidConfig.sdkToolsUrl())); + query.setNetworkAccessManager(NetworkAccessManager::instance()); + NetworkQuery *queryPtr = &query; + connect(queryPtr, &NetworkQuery::started, this, [this, queryPtr] { + QNetworkReply *reply = queryPtr->reply(); + if (!reply) + return; + connect(reply, &QNetworkReply::downloadProgress, + this, [this](qint64 received, qint64 max) { + m_progressDialog->setRange(0, max); + m_progressDialog->setValue(received); }); - m_archive->unarchive(); +#if QT_CONFIG(ssl) + connect(reply, &QNetworkReply::sslErrors, + this, [this, reply](const QList<QSslError> &sslErrors) { + for (const QSslError &error : sslErrors) + qCDebug(sdkDownloaderLog, "SSL error: %s\n", qPrintable(error.errorString())); + logError(Tr::tr("Encountered SSL errors, download is aborted.")); + reply->abort(); + }); +#endif + }); + }; + const auto onQueryDone = [this, storage](const NetworkQuery &query) { + QNetworkReply *reply = query.reply(); + QTC_ASSERT(reply, return); + const QUrl url = reply->url(); + if (isHttpRedirect(reply)) { + logError(Tr::tr("Download from %1 was redirected.").arg(url.toString())); + return; } - }); -} + const FilePath sdkFileName = sdkFromUrl(url); + const std::optional<QString> saveResult = saveToDisk(sdkFileName, reply); + if (saveResult) { + logError(*saveResult); + return; + } + *storage = sdkFileName; + }; + const auto onQueryError = [this](const NetworkQuery &query) { + QNetworkReply *reply = query.reply(); + QTC_ASSERT(reply, return); + const QUrl url = reply->url(); + logError(Tr::tr("Downloading Android SDK Tools from URL %1 has failed: %2.") + .arg(url.toString(), reply->errorString())); + }; -bool AndroidSdkDownloader::verifyFileIntegrity() -{ - QFile f(m_sdkFilename.toString()); - if (f.open(QFile::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Sha256); - if (hash.addData(&f)) { - return hash.result() == m_androidConfig.getSdkToolsSha256(); + const auto onUnarchiveSetup = [this, storage](Unarchiver &unarchiver) { + m_progressDialog->setRange(0, 0); + m_progressDialog->setLabelText(Tr::tr("Unarchiving SDK Tools package...")); + if (!*storage) + return SetupResult::StopWithError; + const FilePath sdkFileName = **storage; + if (!verifyFileIntegrity(sdkFileName, m_androidConfig.getSdkToolsSha256())) { + logError(Tr::tr("Verifying the integrity of the downloaded file has failed.")); + return SetupResult::StopWithError; } - } - return false; + const auto sourceAndCommand = Unarchiver::sourceAndCommand(sdkFileName); + if (!sourceAndCommand) { + logError(sourceAndCommand.error()); + return SetupResult::StopWithError; + } + unarchiver.setSourceAndCommand(*sourceAndCommand); + unarchiver.setDestDir(sdkFileName.parentDir()); + return SetupResult::Continue; + }; + const auto onUnarchiverDone = [this, storage](const Unarchiver &) { + m_androidConfig.setTemporarySdkToolsPath( + (*storage)->parentDir().pathAppended(Constants::cmdlineToolsName)); + QMetaObject::invokeMethod(this, [this] { emit sdkExtracted(); }, Qt::QueuedConnection); + }; + const auto onUnarchiverError = [this](const Unarchiver &) { + logError(Tr::tr("Unarchiving error.")); + }; + + const Group root { + Tasking::Storage(storage), + NetworkQueryTask(onQuerySetup, onQueryDone, onQueryError), + UnarchiverTask(onUnarchiveSetup, onUnarchiverDone, onUnarchiverError) + }; + + m_taskTree.reset(new TaskTree(root)); + const auto onDone = [this] { + m_taskTree.release()->deleteLater(); + m_progressDialog.reset(); + }; + connect(m_taskTree.get(), &TaskTree::done, this, onDone); + connect(m_taskTree.get(), &TaskTree::errorOccurred, this, onDone); + m_taskTree->start(); } QString AndroidSdkDownloader::dialogTitle() @@ -108,91 +203,11 @@ QString AndroidSdkDownloader::dialogTitle() return Tr::tr("Download SDK Tools"); } -void AndroidSdkDownloader::cancel() -{ - if (m_reply) { - m_reply->abort(); - m_reply->deleteLater(); - } - if (m_progressDialog) - m_progressDialog->cancel(); -} - -void AndroidSdkDownloader::cancelWithError(const QString &error) -{ - cancel(); - logError(error); -} - void AndroidSdkDownloader::logError(const QString &error) { qCDebug(sdkDownloaderLog, "%s", error.toUtf8().data()); - emit sdkDownloaderError(error); + QMetaObject::invokeMethod(this, [this, error] { emit sdkDownloaderError(error); }, + Qt::QueuedConnection); } -FilePath AndroidSdkDownloader::getSaveFilename(const QUrl &url) -{ - QString path = url.path(); - QString basename = QFileInfo(path).fileName(); - - if (basename.isEmpty()) - basename = "sdk-tools.zip"; - - if (QFile::exists(basename)) { - int i = 0; - basename += '.'; - while (QFile::exists(basename + QString::number(i))) - ++i; - basename += QString::number(i); - } - - return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) - / basename; -} - -bool AndroidSdkDownloader::saveToDisk(const FilePath &filename, QIODevice *data) -{ - QFile file(filename.toString()); - if (!file.open(QIODevice::WriteOnly)) { - logError(QString(Tr::tr("Could not open %1 for writing: %2.")) - .arg(filename.toUserOutput(), file.errorString())); - return false; - } - - file.write(data->readAll()); - file.close(); - - return true; -} - -bool AndroidSdkDownloader::isHttpRedirect(QNetworkReply *reply) -{ - int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 - || statusCode == 307 || statusCode == 308; -} - -void AndroidSdkDownloader::downloadFinished(QNetworkReply *reply) -{ - QUrl url = reply->url(); - if (reply->error()) { - cancelWithError(QString(Tr::tr("Downloading Android SDK Tools from URL %1 has failed: %2.")) - .arg(url.toString(), reply->errorString())); - } else { - if (isHttpRedirect(reply)) { - cancelWithError(QString(Tr::tr("Download from %1 was redirected.")).arg(url.toString())); - } else { - m_sdkFilename = getSaveFilename(url); - if (saveToDisk(m_sdkFilename, reply) && verifyFileIntegrity()) - emit sdkPackageWriteFinished(); - else - cancelWithError( - Tr::tr("Writing and verifying the integrity of the downloaded file has failed.")); - } - } - - reply->deleteLater(); -} - -} // Internal -} // Android +} // namespace Android::Internal diff --git a/src/plugins/android/androidsdkdownloader.h b/src/plugins/android/androidsdkdownloader.h index ac1eef76070..7859f2b6a8e 100644 --- a/src/plugins/android/androidsdkdownloader.h +++ b/src/plugins/android/androidsdkdownloader.h @@ -1,22 +1,19 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#ifndef ANDROIDSDKDOWNLOADER_H -#define ANDROIDSDKDOWNLOADER_H +#pragma once #include "androidconfigurations.h" -#include <QNetworkReply> #include <QObject> -#include <QProgressDialog> -namespace Utils { -class Archive; -class FilePath; -} +QT_BEGIN_NAMESPACE +class QProgressDialog; +QT_END_NAMESPACE -namespace Android { -namespace Internal { +namespace Tasking { class TaskTree; } + +namespace Android::Internal { class AndroidSdkDownloader : public QObject { @@ -25,39 +22,20 @@ class AndroidSdkDownloader : public QObject public: AndroidSdkDownloader(); ~AndroidSdkDownloader(); + void downloadAndExtractSdk(); static QString dialogTitle(); - void cancel(); - signals: - void sdkPackageWriteFinished(); void sdkExtracted(); void sdkDownloaderError(const QString &error); private: - static Utils::FilePath getSaveFilename(const QUrl &url); - bool saveToDisk(const Utils::FilePath &filename, QIODevice *data); - static bool isHttpRedirect(QNetworkReply *m_reply); - - bool verifyFileIntegrity(); - void cancelWithError(const QString &error); void logError(const QString &error); - void downloadFinished(QNetworkReply *m_reply); -#if QT_CONFIG(ssl) - void sslErrors(const QList<QSslError> &errors); -#endif - - QNetworkAccessManager m_manager; - QNetworkReply *m_reply = nullptr; - Utils::FilePath m_sdkFilename; - QProgressDialog *m_progressDialog = nullptr; AndroidConfig &m_androidConfig; - std::unique_ptr<Utils::Archive> m_archive; + std::unique_ptr<QProgressDialog> m_progressDialog; + std::unique_ptr<Tasking::TaskTree> m_taskTree; }; -} // Internal -} // Android - -#endif // ANDROIDSDKDOWNLOADER_H +} // namespace Android::Internal diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index 1839f7c21c4..b0da90123e8 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -17,7 +17,6 @@ #include <QLoggingCategory> #include <QReadWriteLock> #include <QRegularExpression> -#include <QSettings> #include <QTextCodec> namespace { @@ -94,7 +93,7 @@ static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &ar << CommandLine(config.sdkManagerToolPath(), newArgs) .toUserOutput(); Process proc; - proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config)); + proc.setEnvironment(config.toolsEnvironment()); proc.setTimeoutS(timeout); proc.setTimeOutMessageBoxEnabled(true); proc.setCommand({config.sdkManagerToolPath(), newArgs}); @@ -122,7 +121,7 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar .toUserOutput(); int offset = promise.future().progressValue(); Process proc; - proc.setEnvironment(AndroidConfigurations::toolsEnvironment(config)); + proc.setEnvironment(config.toolsEnvironment()); bool assertionFound = false; proc.setTimeoutS(timeout); proc.setStdOutCallback([offset, progressQuota, &proc, &assertionFound, &promise](const QString &out) { @@ -439,7 +438,7 @@ void AndroidSdkManagerPrivate::updateInstalled(SdkCmdPromise &promise) if (result.stdError.isEmpty() && !result.success) result.stdError = Tr::tr("Failed."); - result.stdOutput = Tr::tr("Done\n\n"); + result.stdOutput = Tr::tr("Done") + "\n\n"; promise.addResult(result); promise.setProgressValue(100); } @@ -469,8 +468,8 @@ void AndroidSdkManagerPrivate::update(SdkCmdPromise &fi, const QStringList &inst currentProgress += progressQuota; fi.setProgressValue(currentProgress); if (result.stdError.isEmpty() && !result.success) - result.stdError = Tr::tr("AndroidSdkManager", "Failed"); - result.stdOutput = Tr::tr("AndroidSdkManager", "Done\n\n"); + result.stdError = Tr::tr("Failed"); + result.stdOutput = Tr::tr("Done") + "\n\n"; fi.addResult(result); return fi.isCanceled(); }; @@ -523,7 +522,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdPromise &fi) Process licenseCommand; licenseCommand.setProcessMode(ProcessMode::Writer); - licenseCommand.setEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); + licenseCommand.setEnvironment(m_config.toolsEnvironment()); bool reviewingLicenses = false; licenseCommand.setCommand(CommandLine(m_config.sdkManagerToolPath(), {"--licenses", sdkRootArg(m_config)})); licenseCommand.setUseCtrlCStub(true); @@ -570,7 +569,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdPromise &fi) m_licenseTextCache.clear(); result.success = licenseCommand.exitStatus() == QProcess::NormalExit; if (!result.success) - result.stdError = Tr::tr("License command failed.\n\n"); + result.stdError = Tr::tr("License command failed.") + "\n\n"; fi.addResult(result); fi.setProgressValue(100); } diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index 8627e0c64ed..cd3bbb8532b 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -7,8 +7,6 @@ #include "androidsdkmodel.h" #include "androidtr.h" -#include <app/app_version.h> - #include <utils/async.h> #include <utils/layoutbuilder.h> #include <utils/outputformatter.h> @@ -18,6 +16,7 @@ #include <QAbstractButton> #include <QDialogButtonBox> #include <QGridLayout> +#include <QGuiApplication> #include <QLabel> #include <QLineEdit> #include <QLoggingCategory> @@ -282,8 +281,8 @@ void AndroidSdkManagerWidget::installEssentials() Tr::tr("Android SDK Changes"), Tr::tr("%1 cannot find the following essential packages: \"%2\".\n" "Install them manually after the current operation is done.\n") - .arg(Core::Constants::IDE_DISPLAY_NAME) - .arg(m_sdkModel->missingEssentials().join("\", \""))); + .arg(QGuiApplication::applicationDisplayName(), + m_sdkModel->missingEssentials().join("\", \""))); } onApplyButton(Tr::tr("Android SDK installation is missing necessary packages. " "Do you want to install the missing packages?")); @@ -291,9 +290,10 @@ void AndroidSdkManagerWidget::installEssentials() void AndroidSdkManagerWidget::beginLicenseCheck() { - m_formatter->appendMessage(Tr::tr("Checking pending licenses...\n"), NormalMessageFormat); + m_formatter->appendMessage(Tr::tr("Checking pending licenses...") + "\n", NormalMessageFormat); m_formatter->appendMessage(Tr::tr("The installation of Android SDK packages may fail if the " - "respective licenses are not accepted.\n"), + "respective licenses are not accepted.") + + "\n", LogMessageFormat); addPackageFuture(m_sdkManager->checkPendingLicenses()); } @@ -303,7 +303,7 @@ void AndroidSdkManagerWidget::onApplyButton(const QString &extraMessage) QTC_ASSERT(m_currentView == PackageListing, return); if (m_sdkManager->isBusy()) { - m_formatter->appendMessage(Tr::tr("\nSDK Manager is busy."), StdErrFormat); + m_formatter->appendMessage("\n" + Tr::tr("SDK Manager is busy."), StdErrFormat); return; } @@ -357,7 +357,7 @@ void AndroidSdkManagerWidget::onApplyButton(const QString &extraMessage) void AndroidSdkManagerWidget::onUpdatePackages() { if (m_sdkManager->isBusy()) { - m_formatter->appendMessage(Tr::tr("\nSDK Manager is busy."), StdErrFormat); + m_formatter->appendMessage("\n" + Tr::tr("SDK Manager is busy."), StdErrFormat); return; } switchView(Operations); diff --git a/src/plugins/android/androidsdkmanagerwidget.h b/src/plugins/android/androidsdkmanagerwidget.h index 9346bfcd944..718d5390d69 100644 --- a/src/plugins/android/androidsdkmanagerwidget.h +++ b/src/plugins/android/androidsdkmanagerwidget.h @@ -43,8 +43,6 @@ class AndroidSdkModel; class OptionsDialog : public QDialog { - Q_OBJECT - public: OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args, QWidget *parent = nullptr); diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp index 4bf70f6b857..f470f7b9083 100644 --- a/src/plugins/android/androidsdkmodel.cpp +++ b/src/plugins/android/androidsdkmodel.cpp @@ -84,14 +84,14 @@ QModelIndex AndroidSdkModel::index(int row, int column, const QModelIndex &paren return createIndex(row, column); // Top level items (Tools & platform) } - return QModelIndex(); + return {}; } QModelIndex AndroidSdkModel::parent(const QModelIndex &index) const { void *ip = index.internalPointer(); if (!ip) - return QModelIndex(); + return {}; auto package = static_cast<const AndroidSdkPackage *>(ip); if (package->type() == AndroidSdkPackage::SystemImagePackage) { @@ -107,7 +107,7 @@ QModelIndex AndroidSdkModel::parent(const QModelIndex &index) const return createIndex(0, 0); // Tools } - return QModelIndex(); + return {}; } int AndroidSdkModel::rowCount(const QModelIndex &parent) const @@ -137,7 +137,7 @@ int AndroidSdkModel::columnCount(const QModelIndex &parent) const QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) - return QVariant(); + return {}; if (!index.parent().isValid()) { // Top level tools @@ -160,7 +160,7 @@ QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const return platform->apiLevel(); } } - return QVariant(); + return {}; } auto p = static_cast<const AndroidSdkPackage *>(index.internalPointer()); @@ -213,7 +213,7 @@ QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const if (role == PackageStateRole) return p->state(); - return QVariant(); + return {}; } QHash<int, QByteArray> AndroidSdkModel::roleNames() const diff --git a/src/plugins/android/androidsdkmodel.h b/src/plugins/android/androidsdkmodel.h index 49bdef0ded1..36dbe27b312 100644 --- a/src/plugins/android/androidsdkmodel.h +++ b/src/plugins/android/androidsdkmodel.h @@ -15,7 +15,6 @@ class AndroidSdkManager; class AndroidSdkModel : public QAbstractItemModel { - Q_OBJECT public: enum PackageColumn { packageNameColumn = 0, @@ -53,7 +52,6 @@ private: void clearContainers(); void refreshData(); -private: const AndroidConfig &m_config; AndroidSdkManager *m_sdkManager; QList<const SdkPlatform *> m_sdkPlatforms; diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index dfb14c49cfc..ce8fbcf8614 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -30,6 +30,7 @@ #include <QListWidget> #include <QLoggingCategory> #include <QMessageBox> +#include <QProgressDialog> #include <QPushButton> #include <QStandardPaths> #include <QTimer> @@ -280,7 +281,7 @@ AndroidSettingsWidget::AndroidSettingsWidget() { JavaPathExistsAndWritableRow, Tr::tr("JDK path exists and is writable.") }, { SdkPathExistsAndWritableRow, Tr::tr("Android SDK path exists and is writable.") }, { SdkToolsInstalledRow, Tr::tr("Android SDK Command-line Tools installed.") }, - { SdkManagerSuccessfulRow, Tr::tr("Android SDK Command-line Tools run.") }, + { SdkManagerSuccessfulRow, Tr::tr("Android SDK Command-line Tools runs.") }, { PlatformToolsInstalledRow, Tr::tr("Android SDK Platform-Tools installed.") }, { AllEssentialsInstalledRow, Tr::tr( "All essential packages installed for all installed Qt versions.") }, diff --git a/src/plugins/android/androidsignaloperation.h b/src/plugins/android/androidsignaloperation.h index 1c3bb48454c..2a6e3ddb6cf 100644 --- a/src/plugins/android/androidsignaloperation.h +++ b/src/plugins/android/androidsignaloperation.h @@ -13,7 +13,6 @@ namespace Internal { class AndroidSignalOperation : public ProjectExplorer::DeviceProcessSignalOperation { - Q_OBJECT public: ~AndroidSignalOperation() override; void killProcess(qint64 pid) override; diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index 7af3bb417b6..5d96d8c4bdc 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -9,6 +9,7 @@ #include <projectexplorer/kitmanager.h> #include <projectexplorer/toolchainmanager.h> #include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> #include <utils/environment.h> @@ -47,7 +48,7 @@ static ToolChain *findToolChain(FilePath &compilerPath, Id lang, const QString & } AndroidToolChain::AndroidToolChain() - : ClangToolChain(Constants::ANDROID_TOOLCHAIN_TYPEID) + : GccToolChain(Constants::ANDROID_TOOLCHAIN_TYPEID, Clang) { setTypeDisplayName(Tr::tr("Android Clang")); } @@ -80,7 +81,7 @@ bool AndroidToolChain::isValid() const const bool isChildofSdk = compilerCommand().isChildOf( AndroidConfigurations::currentConfig().sdkLocation()); - return ClangToolChain::isValid() && typeId() == Constants::ANDROID_TOOLCHAIN_TYPEID + return GccToolChain::isValid() && typeId() == Constants::ANDROID_TOOLCHAIN_TYPEID && targetAbi().isValid() && (isChildofNdk || isChildofSdk) && !originalTargetTriple().isEmpty(); } @@ -93,7 +94,8 @@ void AndroidToolChain::addToEnvironment(Environment &env) const if (javaHome.exists()) { env.set(Constants::JAVA_HOME_ENV_VAR, javaHome.toUserOutput()); const FilePath javaBin = javaHome.pathAppended("bin"); - const FilePath currentJavaFilePath = env.searchInPath("java"); + const FilePath currentJavaFilePath + = env.searchInPath("java", {}, {}, FilePath::WithExeSuffix); if (!currentJavaFilePath.isChildOf(javaBin)) env.prependOrSetPath(javaBin); } @@ -101,11 +103,13 @@ void AndroidToolChain::addToEnvironment(Environment &env) const env.set(QLatin1String("ANDROID_SDK_ROOT"), config.sdkLocation().toUserOutput()); } -bool AndroidToolChain::fromMap(const QVariantMap &data) +void AndroidToolChain::fromMap(const Store &data) { - if (!ClangToolChain::fromMap(data)) - return false; - return isValid(); + GccToolChain::fromMap(data); + if (hasError()) + return; + if (!isValid()) + reportError(); } QStringList AndroidToolChain::suggestedMkspecList() const diff --git a/src/plugins/android/androidtoolchain.h b/src/plugins/android/androidtoolchain.h index af4572c3cbd..bd33dc75704 100644 --- a/src/plugins/android/androidtoolchain.h +++ b/src/plugins/android/androidtoolchain.h @@ -12,7 +12,7 @@ namespace Internal { using ToolChainList = QList<ProjectExplorer::ToolChain *>; -class AndroidToolChain : public ProjectExplorer::ClangToolChain +class AndroidToolChain : public ProjectExplorer::GccToolChain { public: ~AndroidToolChain() override; @@ -22,7 +22,7 @@ public: QStringList suggestedMkspecList() const override; Utils::FilePath makeCommand(const Utils::Environment &environment) const override; - bool fromMap(const QVariantMap &data) override; + void fromMap(const Utils::Store &data) override; void setNdkLocation(const Utils::FilePath &ndkLocation); Utils::FilePath ndkLocation() const; diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 8e7f0b0fd0e..801a7248a0a 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -166,7 +166,7 @@ ProjectExplorer::IDevice::Ptr AvdDialog::device() const using namespace ProjectExplorer; dev->setupId(IDevice::AutoDetected, deviceId); dev->setMachineType(IDevice::Emulator); - dev->setDisplayName(m_createdAvdInfo.name); + dev->settings()->displayName.setValue(m_createdAvdInfo.name); dev->setDeviceState(IDevice::DeviceConnected); dev->setExtraData(Constants::AndroidAvdName, m_createdAvdInfo.name); dev->setExtraData(Constants::AndroidCpuAbi, {m_createdAvdInfo.abi}); diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index b7888b1db9d..4cd03870443 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -24,10 +24,11 @@ class AndroidConfig; class SdkPlatform; namespace Internal { + class AndroidSdkManager; + class AvdDialog : public QDialog { - Q_OBJECT public: explicit AvdDialog(const AndroidConfig &config, QWidget *parent = nullptr); int exec() override; diff --git a/src/plugins/android/certificatesmodel.cpp b/src/plugins/android/certificatesmodel.cpp index 47aa3701cc8..4c80fe3972a 100644 --- a/src/plugins/android/certificatesmodel.cpp +++ b/src/plugins/android/certificatesmodel.cpp @@ -40,7 +40,7 @@ int CertificatesModel::rowCount(const QModelIndex &parent) const QVariant CertificatesModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) - return QVariant(); + return {}; if (role == Qt::DisplayRole) return m_certs[index.row()].first; return m_certs[index.row()].second; diff --git a/src/plugins/android/createandroidmanifestwizard.cpp b/src/plugins/android/createandroidmanifestwizard.cpp index 1eb979f7f59..7c334476a21 100644 --- a/src/plugins/android/createandroidmanifestwizard.cpp +++ b/src/plugins/android/createandroidmanifestwizard.cpp @@ -16,7 +16,7 @@ #include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/infolabel.h> #include <utils/pathchooser.h> diff --git a/src/plugins/android/createandroidmanifestwizard.h b/src/plugins/android/createandroidmanifestwizard.h index e1552311317..4d5a309583f 100644 --- a/src/plugins/android/createandroidmanifestwizard.h +++ b/src/plugins/android/createandroidmanifestwizard.h @@ -3,8 +3,6 @@ #pragma once -#include "android_global.h" - #include <utils/fileutils.h> #include <utils/wizard.h> diff --git a/src/plugins/android/javalanguageserver.cpp b/src/plugins/android/javalanguageserver.cpp index 977b3fc61a9..d6ea811f00b 100644 --- a/src/plugins/android/javalanguageserver.cpp +++ b/src/plugins/android/javalanguageserver.cpp @@ -10,11 +10,11 @@ #include <languageclient/client.h> #include <languageclient/languageclientinterface.h> #include <languageclient/languageclientutils.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/environment.h> #include <utils/pathchooser.h> #include <utils/temporarydirectory.h> @@ -136,14 +136,14 @@ bool JLSSettings::isValid() const return StdIOSettings::isValid() && !m_languageServer.isEmpty(); } -QVariantMap JLSSettings::toMap() const +Store JLSSettings::toMap() const { - QVariantMap map = StdIOSettings::toMap(); + Store map = StdIOSettings::toMap(); map.insert(languageServerKey, m_languageServer.toSettings()); return map; } -void JLSSettings::fromMap(const QVariantMap &map) +void JLSSettings::fromMap(const Store &map) { StdIOSettings::fromMap(map); m_languageServer = FilePath::fromSettings(map[languageServerKey]); diff --git a/src/plugins/android/javalanguageserver.h b/src/plugins/android/javalanguageserver.h index 50e3fc72222..a8d00ffcee5 100644 --- a/src/plugins/android/javalanguageserver.h +++ b/src/plugins/android/javalanguageserver.h @@ -16,8 +16,8 @@ public: bool applyFromSettingsWidget(QWidget *widget) final; QWidget *createSettingsWidget(QWidget *parent) const final; bool isValid() const final; - QVariantMap toMap() const final; - void fromMap(const QVariantMap &map) final; + Utils::Store toMap() const final; + void fromMap(const Utils::Store &map) final; LanguageClient::BaseSettings *copy() const final; LanguageClient::Client *createClient(LanguageClient::BaseClientInterface *interface) const final; LanguageClient::BaseClientInterface *createInterface( diff --git a/src/plugins/android/splashscreencontainerwidget.cpp b/src/plugins/android/splashscreencontainerwidget.cpp index a30af15f24c..e0e203121df 100644 --- a/src/plugins/android/splashscreencontainerwidget.cpp +++ b/src/plugins/android/splashscreencontainerwidget.cpp @@ -185,17 +185,17 @@ SplashScreenContainerWidget::SplashScreenContainerWidget( m_masterImage = new QToolButton(this); m_masterImage->setToolTip(Tr::tr("Select master image to use.")); - m_masterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + m_masterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Master image:"), m_masterImage); m_portraitMasterImage = new QToolButton(this); m_portraitMasterImage->setToolTip(Tr::tr("Select portrait master image to use.")); - m_portraitMasterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + m_portraitMasterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Portrait master image:"), m_portraitMasterImage); m_landscapeMasterImage = new QToolButton(this); m_landscapeMasterImage->setToolTip(Tr::tr("Select landscape master image to use.")); - m_landscapeMasterImage->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); + m_landscapeMasterImage->setIcon(Icon::fromTheme("document-open")); formLayout->addRow(Tr::tr("Landscape master image:"), m_landscapeMasterImage); auto clearAllButton = new QToolButton(this); diff --git a/src/plugins/autotest/AutoTest.json.in b/src/plugins/autotest/AutoTest.json.in index 9dc49b1caa7..230a9ee0f69 100644 --- a/src/plugins/autotest/AutoTest.json.in +++ b/src/plugins/autotest/AutoTest.json.in @@ -1,18 +1,18 @@ { -\"Name\" : \"AutoTest\", -\"Version\" : \"$$QTCREATOR_VERSION\", -\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", -\"Vendor\" : \"The Qt Company Ltd\", -\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", -\"License\" : [ \"Commercial Usage\", -\"\", -\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", -\"\", -\"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.\" +"Name" : "AutoTest", +"Version" : "${IDE_VERSION}", +"CompatVersion" : "${IDE_VERSION_COMPAT}", +"Vendor" : "The Qt Company Ltd", +"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", +"License" : [ "Commercial Usage", +"", +"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", +"", +"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." ], -\"Description\" : \"Auto Test plugin.\", -\"Url\" : \"http://www.qt.io\", -$$dependencyList +"Description" : "Auto Test plugin.", +"Url" : "http://www.qt.io", +${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/autotest/CMakeLists.txt b/src/plugins/autotest/CMakeLists.txt index bbad11ff0dd..fd5e51ca004 100644 --- a/src/plugins/autotest/CMakeLists.txt +++ b/src/plugins/autotest/CMakeLists.txt @@ -17,17 +17,16 @@ add_qtc_plugin(AutoTest boost/boosttestoutputreader.cpp boost/boosttestoutputreader.h boost/boosttestparser.cpp boost/boosttestparser.h boost/boosttestresult.cpp boost/boosttestresult.h - boost/boosttestsettings.cpp boost/boosttestsettings.h boost/boosttesttreeitem.cpp boost/boosttesttreeitem.h catch/catchcodeparser.cpp catch/catchcodeparser.h catch/catchconfiguration.cpp catch/catchconfiguration.h - catch/catchframework.h catch/catchframework.cpp catch/catchoutputreader.h - catch/catchoutputreader.cpp catch/catchresult.h catch/catchresult.cpp catch/catchtestparser.h - catch/catchtestparser.cpp catch/catchtreeitem.h catch/catchtreeitem.cpp - catch/catchtestsettings.cpp catch/catchtestsettings.h + catch/catchoutputreader.cpp catch/catchoutputreader.h + catch/catchresult.h catch/catchresult.cpp + catch/catchtestframework.cpp catch/catchtestframework.h + catch/catchtestparser.cpp catch/catchtestparser.h + catch/catchtreeitem.h catch/catchtreeitem.cpp ctest/ctestconfiguration.cpp ctest/ctestconfiguration.h ctest/ctestoutputreader.cpp ctest/ctestoutputreader.h - ctest/ctestsettings.cpp ctest/ctestsettings.h ctest/ctesttool.cpp ctest/ctesttool.h ctest/ctesttreeitem.cpp ctest/ctesttreeitem.h gtest/gtest_utils.cpp gtest/gtest_utils.h @@ -37,7 +36,6 @@ add_qtc_plugin(AutoTest gtest/gtestoutputreader.cpp gtest/gtestoutputreader.h gtest/gtestparser.cpp gtest/gtestparser.h gtest/gtestresult.cpp gtest/gtestresult.h - gtest/gtestsettings.cpp gtest/gtestsettings.h gtest/gtesttreeitem.cpp gtest/gtesttreeitem.h gtest/gtestvisitors.cpp gtest/gtestvisitors.h itemdatacache.h @@ -51,7 +49,6 @@ add_qtc_plugin(AutoTest qtest/qttestoutputreader.cpp qtest/qttestoutputreader.h qtest/qttestparser.cpp qtest/qttestparser.h qtest/qttestresult.cpp qtest/qttestresult.h - qtest/qttestsettings.cpp qtest/qttestsettings.h qtest/qttesttreeitem.cpp qtest/qttesttreeitem.h qtest/qttestvisitors.cpp qtest/qttestvisitors.h quick/quicktest_utils.cpp quick/quicktest_utils.h diff --git a/src/plugins/autotest/autotest.qbs b/src/plugins/autotest/autotest.qbs index 90f1728c4ae..9e9625006b6 100644 --- a/src/plugins/autotest/autotest.qbs +++ b/src/plugins/autotest/autotest.qbs @@ -1,5 +1,3 @@ -import qbs - QtcPlugin { name: "AutoTest" @@ -18,16 +16,8 @@ QtcPlugin { "QmakeProjectManager" ] - Depends { - name: "QtSupport" - condition: qtc.testsEnabled - } - - Depends { - name: "Qt.testlib" - condition: qtc.testsEnabled - } - + Depends { name: "QtSupport"; condition: qtc.withPluginTests } + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } Depends { name: "Qt.widgets" } files: [ diff --git a/src/plugins/autotest/autotestconstants.h b/src/plugins/autotest/autotestconstants.h index 007e81b00ee..0610216bbda 100644 --- a/src/plugins/autotest/autotestconstants.h +++ b/src/plugins/autotest/autotestconstants.h @@ -8,6 +8,7 @@ namespace Autotest { namespace Constants { +const char ACTION_DISABLE_TMP[] = "AutoTest.DisableTemp"; const char ACTION_SCAN_ID[] = "AutoTest.ScanAction"; const char ACTION_RUN_ALL_ID[] = "AutoTest.RunAll"; const char ACTION_RUN_ALL_NODEPLOY_ID[] = "AutoTest.RunAllNoDeploy"; diff --git a/src/plugins/autotest/autotestplugin.cpp b/src/plugins/autotest/autotestplugin.cpp index 91c058cec77..0f93fb7dc75 100644 --- a/src/plugins/autotest/autotestplugin.cpp +++ b/src/plugins/autotest/autotestplugin.cpp @@ -13,13 +13,12 @@ #include "testprojectsettings.h" #include "testresultspane.h" #include "testrunner.h" -#include "testsettings.h" #include "testsettingspage.h" #include "testtreeitem.h" #include "testtreemodel.h" #include "boost/boosttestframework.h" -#include "catch/catchframework.h" +#include "catch/catchtestframework.h" #include "ctest/ctesttool.h" #include "gtest/gtestframework.h" #include "qtest/qttestframework.h" @@ -30,8 +29,8 @@ #include <coreplugin/actionmanager/command.h> #include <coreplugin/coreconstants.h> #include <coreplugin/icontext.h> -#include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> +#include <coreplugin/progressmanager/progressmanager.h> #include <cplusplus/CppDocument.h> #include <cplusplus/LookupContext.h> @@ -48,13 +47,14 @@ #include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projectpanelfactory.h> -#include <projectexplorer/runcontrol.h> +#include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> #include <utils/algorithm.h> +#include <utils/processinterface.h> #include <utils/textutils.h> #include <utils/utilsicons.h> @@ -94,14 +94,13 @@ public: void onRunFailedTriggered(); void onRunFileTriggered(); void onRunUnderCursorTriggered(TestRunMode mode); + void onDisableTemporarily(bool disable); - TestSettings m_settings; TestSettingsPage m_testSettingPage; TestCodeParser m_testCodeParser; TestTreeModel m_testTreeModel{&m_testCodeParser}; TestRunner m_testRunner; - TestFrameworkManager m_frameworkManager; #ifdef WITH_TESTS LoadProjectScenario m_loadProjectScenario{&m_testTreeModel}; #endif @@ -130,15 +129,15 @@ AutotestPluginPrivate::AutotestPluginPrivate() { dd = this; // Needed as the code below access it via the static plugin interface initializeMenuEntries(); - m_frameworkManager.registerTestFramework(new QtTestFramework); - m_frameworkManager.registerTestFramework(new QuickTestFramework); - m_frameworkManager.registerTestFramework(new GTestFramework); - m_frameworkManager.registerTestFramework(new BoostTestFramework); - m_frameworkManager.registerTestFramework(new CatchFramework); + TestFrameworkManager::registerTestFramework(&theQtTestFramework()); + TestFrameworkManager::registerTestFramework(&theQuickTestFramework()); + TestFrameworkManager::registerTestFramework(&theGTestFramework()); + TestFrameworkManager::registerTestFramework(&theBoostTestFramework()); + TestFrameworkManager::registerTestFramework(&theCatchFramework()); - m_frameworkManager.registerTestTool(new CTestTool); + TestFrameworkManager::registerTestTool(&theCTestTool()); + TestFrameworkManager::synchronizeSettings(); - m_frameworkManager.synchronizeSettings(ICore::settings()); m_resultsPane = TestResultsPane::instance(); auto panelFactory = new ProjectExplorer::ProjectPanelFactory(); @@ -150,7 +149,7 @@ AutotestPluginPrivate::AutotestPluginPrivate() }); ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); - TestFrameworkManager::activateFrameworksAndToolsFromSettings(&m_settings); + TestFrameworkManager::activateFrameworksAndToolsFromSettings(); m_testTreeModel.synchronizeTestFrameworks(); m_testTreeModel.synchronizeTestTools(); @@ -257,12 +256,25 @@ void AutotestPluginPrivate::initializeMenuEntries() action->setEnabled(false); menu->addAction(command); + action = new QAction(Tr::tr("Disable Temporarily"), this); + action->setToolTip(Tr::tr("Disable scanning and other actions until explicitly rescanning, " + "re-enabling, or restarting Qt Creator.")); + action->setCheckable(true); + command = ActionManager::registerAction(action, Constants::ACTION_DISABLE_TMP); + connect(action, &QAction::triggered, this, &AutotestPluginPrivate::onDisableTemporarily); + menu->addAction(command); + action = new QAction(Tr::tr("Re&scan Tests"), this); command = ActionManager::registerAction(action, Constants::ACTION_SCAN_ID); command->setDefaultKeySequence( QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+T, Ctrl+Meta+S") : Tr::tr("Alt+Shift+T,Alt+S"))); - connect(action, &QAction::triggered, this, [] { dd->m_testCodeParser.updateTestTree(); }); + connect(action, &QAction::triggered, this, [] { + if (dd->m_testCodeParser.state() == TestCodeParser::DisabledTemporarily) + dd->onDisableTemporarily(false); // Rescan Test should explicitly re-enable + else + dd->m_testCodeParser.updateTestTree(); + }); menu->addAction(command); ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS); @@ -295,48 +307,52 @@ void AutotestPlugin::extensionsInitialized() if (!contextMenu) // if QC is started without CppEditor plugin return; - QAction *action = new QAction(Tr::tr("&Run Test Under Cursor"), this); + ActionContainer * const runTestMenu = ActionManager::createMenu("Autotest.TestUnderCursor"); + runTestMenu->menu()->setTitle(Tr::tr("Run Test Under Cursor")); + contextMenu->addSeparator(); + contextMenu->addMenu(runTestMenu); + contextMenu->addSeparator(); + + QAction *action = new QAction(Tr::tr("&Run Test"), this); action->setEnabled(false); action->setIcon(Utils::Icons::RUN_SMALL.icon()); Command *command = ActionManager::registerAction(action, Constants::ACTION_RUN_UCURSOR); connect(action, &QAction::triggered, std::bind(&AutotestPluginPrivate::onRunUnderCursorTriggered, dd, TestRunMode::Run)); - contextMenu->addSeparator(); - contextMenu->addAction(command); + runTestMenu->addAction(command); - action = new QAction(Tr::tr("Run Test Under Cursor Without Deployment"), this); + action = new QAction(Tr::tr("Run Test Without Deployment"), this); action->setEnabled(false); action->setIcon(Utils::Icons::RUN_SMALL.icon()); command = ActionManager::registerAction(action, Constants::ACTION_RUN_UCURSOR_NODEPLOY); connect(action, &QAction::triggered, std::bind(&AutotestPluginPrivate::onRunUnderCursorTriggered, dd, TestRunMode::RunWithoutDeploy)); - contextMenu->addAction(command); + runTestMenu->addAction(command); - action = new QAction(Tr::tr("&Debug Test Under Cursor"), this); + action = new QAction(Tr::tr("&Debug Test"), this); action->setEnabled(false); action->setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL.icon()); command = ActionManager::registerAction(action, Constants::ACTION_RUN_DBG_UCURSOR); connect(action, &QAction::triggered, std::bind(&AutotestPluginPrivate::onRunUnderCursorTriggered, dd, TestRunMode::Debug)); - contextMenu->addAction(command); + runTestMenu->addAction(command); - action = new QAction(Tr::tr("Debug Test Under Cursor Without Deployment"), this); + action = new QAction(Tr::tr("Debug Test Without Deployment"), this); action->setEnabled(false); action->setIcon(ProjectExplorer::Icons::DEBUG_START_SMALL.icon()); command = ActionManager::registerAction(action, Constants::ACTION_RUN_DBG_UCURSOR_NODEPLOY); connect(action, &QAction::triggered, std::bind(&AutotestPluginPrivate::onRunUnderCursorTriggered, dd, TestRunMode::DebugWithoutDeploy)); - contextMenu->addAction(command); - contextMenu->addSeparator(); + runTestMenu->addAction(command); } ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown() { - dd->m_testCodeParser.aboutToShutdown(); + dd->m_testCodeParser.aboutToShutdown(true); dd->m_testTreeModel.disconnect(); return SynchronousShutdown; } @@ -394,7 +410,7 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode) const int line = currentEditor->currentLine(); const FilePath filePath = currentEditor->textDocument()->filePath(); - const CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot(); const CPlusPlus::Document::Ptr doc = snapshot.document(filePath); if (doc.isNull()) // not part of C++ snapshot return; @@ -470,6 +486,23 @@ void AutotestPluginPrivate::onRunUnderCursorTriggered(TestRunMode mode) m_testRunner.runTests(mode, testsToRun); } +void AutotestPluginPrivate::onDisableTemporarily(bool disable) +{ + if (disable) { + // cancel running parse + m_testCodeParser.aboutToShutdown(false); + // clear model + m_testTreeModel.removeAllTestItems(); + m_testTreeModel.removeAllTestToolItems(); + AutotestPlugin::updateMenuItemsEnabledState(); + } else { + // re-enable + m_testCodeParser.setState(TestCodeParser::Idle); + // trigger scan + m_testCodeParser.updateTestTree(); + } +} + TestFrameworks AutotestPlugin::activeTestFrameworks() { ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); @@ -492,11 +525,12 @@ void AutotestPlugin::updateMenuItemsEnabledState() { const ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); const ProjectExplorer::Target *target = project ? project->activeTarget() : nullptr; - const bool canScan = !dd->m_testRunner.isTestRunning() - && dd->m_testCodeParser.state() == TestCodeParser::Idle; + const bool disabled = dd->m_testCodeParser.state() == TestCodeParser::DisabledTemporarily; + const bool canScan = disabled || (!dd->m_testRunner.isTestRunning() + && dd->m_testCodeParser.state() == TestCodeParser::Idle); const bool hasTests = dd->m_testTreeModel.hasTests(); // avoid expensive call to PE::canRunStartupProject() - limit to minimum necessary checks - const bool canRun = hasTests && canScan + const bool canRun = !disabled && hasTests && canScan && project && !project->needsConfiguration() && target && target->activeRunConfiguration() && !ProjectExplorer::BuildManager::isBuilding(); diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp index 50d0b496c21..0ce52721e3f 100644 --- a/src/plugins/autotest/autotestunittests.cpp +++ b/src/plugins/autotest/autotestunittests.cpp @@ -7,7 +7,7 @@ #include "testframeworkmanager.h" #include "testtreemodel.h" -#include "qtest/qttestsettings.h" +#include "qtest/qttestframework.h" #include <cppeditor/cppmodelmanager.h> #include <cppeditor/cpptoolstestcase.h> @@ -15,11 +15,13 @@ #include <extensionsystem/pluginmanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/toolchain.h> +#include <qtsupport/qtkitaspect.h> + #include <utils/environment.h> #include <QFileInfo> @@ -27,8 +29,6 @@ #include <QSignalSpy> #include <QTest> -#include <qtsupport/qtkitinformation.h> - using namespace Core; using namespace ExtensionSystem; using namespace ProjectExplorer; @@ -77,10 +77,7 @@ void AutoTestUnitTests::initTestCase() } // Enable quick check for derived tests - static const Id id = Id("AutoTest.Framework.QtTest"); - static_cast<Autotest::Internal::QtTestSettings *>( - TestFrameworkManager::frameworkForId(id)->testSettings()) - ->quickCheckForDerivedTests.setValue(true); + theQtTestFramework().quickCheckForDerivedTests.setValue(true); } void AutoTestUnitTests::cleanupTestCase() diff --git a/src/plugins/autotest/boost/boosttestconfiguration.cpp b/src/plugins/autotest/boost/boosttestconfiguration.cpp index 768161a5c0c..af3f1e9eb18 100644 --- a/src/plugins/autotest/boost/boosttestconfiguration.cpp +++ b/src/plugins/autotest/boost/boosttestconfiguration.cpp @@ -3,10 +3,9 @@ #include "boosttestconfiguration.h" +#include "boosttestframework.h" #include "boosttestoutputreader.h" -#include "boosttestsettings.h" -#include "../itestframework.h" #include "../testsettings.h" #include <utils/algorithm.h> @@ -18,10 +17,10 @@ namespace Internal { TestOutputReader *BoostTestConfiguration::createOutputReader(Process *app) const { - auto settings = static_cast<BoostTestSettings *>(framework()->testSettings()); + BoostTestFramework &settings = theBoostTestFramework(); return new BoostTestOutputReader(app, buildDirectory(), projectFile(), - LogLevel(settings->logLevel.value()), - ReportLevel(settings->reportLevel.value())); + LogLevel(settings.logLevel()), + ReportLevel(settings.reportLevel())); } enum class InterferingType { Options, EnvironmentVariables }; @@ -47,7 +46,7 @@ static QStringList interfering(InterferingType type) return QString("BOOST_TEST_" + item).toUpper(); }); } - return QStringList(); + return {}; } static QStringList filterInterfering(const QStringList &provided, QStringList *omitted) @@ -86,19 +85,19 @@ static QStringList filterInterfering(const QStringList &provided, QStringList *o QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted) const { - auto boostSettings = static_cast<BoostTestSettings *>(framework()->testSettings()); + BoostTestFramework &boostSettings = theBoostTestFramework(); QStringList arguments; - arguments << "-l" << BoostTestSettings::logLevelToOption(LogLevel(boostSettings->logLevel.value())); - arguments << "-r" << BoostTestSettings::reportLevelToOption(ReportLevel(boostSettings->reportLevel.value())); + arguments << "-l" << BoostTestFramework::logLevelToOption(LogLevel(boostSettings.logLevel())); + arguments << "-r" << BoostTestFramework::reportLevelToOption(ReportLevel(boostSettings.reportLevel())); - if (boostSettings->randomize.value()) - arguments << QString("--random=").append(QString::number(boostSettings->seed.value())); + if (boostSettings.randomize()) + arguments << QString("--random=").append(QString::number(boostSettings.seed())); - if (boostSettings->systemErrors.value()) + if (boostSettings.systemErrors()) arguments << "-s"; - if (boostSettings->fpExceptions.value()) + if (boostSettings.fpExceptions()) arguments << "--detect_fp_exceptions"; - if (!boostSettings->memLeaks.value()) + if (!boostSettings.memLeaks()) arguments << "--detect_memory_leaks=0"; // TODO improve the test case gathering and arguments building to avoid too long command lines @@ -110,7 +109,7 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted) arguments << "-t" << "\"" + test + "\""; } - if (TestSettings::instance()->processArgs()) { + if (testSettings().processArgs()) { arguments << filterInterfering(runnable().command.arguments().split( ' ', Qt::SkipEmptyParts), omitted); } diff --git a/src/plugins/autotest/boost/boosttestconstants.h b/src/plugins/autotest/boost/boosttestconstants.h index c56ca447a9d..8b8e668f04f 100644 --- a/src/plugins/autotest/boost/boosttestconstants.h +++ b/src/plugins/autotest/boost/boosttestconstants.h @@ -9,7 +9,7 @@ namespace Autotest { namespace BoostTest { namespace Constants { -const char FRAMEWORK_NAME[] = "Boost"; +const char FRAMEWORK_ID[] = "AutoTest.Framework.Boost"; const char FRAMEWORK_SETTINGS_CATEGORY[] = QT_TRANSLATE_NOOP("QtC::Autotest", "Boost Test"); const unsigned FRAMEWORK_PRIORITY = 11; const char BOOST_MASTER_SUITE[] = "Master Test Suite"; diff --git a/src/plugins/autotest/boost/boosttestframework.cpp b/src/plugins/autotest/boost/boosttestframework.cpp index b431bbed76b..38f800357b4 100644 --- a/src/plugins/autotest/boost/boosttestframework.cpp +++ b/src/plugins/autotest/boost/boosttestframework.cpp @@ -6,10 +6,126 @@ #include "boosttestconstants.h" #include "boosttesttreeitem.h" #include "boosttestparser.h" +#include "../autotestconstants.h" #include "../autotesttr.h" -namespace Autotest { -namespace Internal { +#include <coreplugin/dialogs/ioptionspage.h> + +#include <utils/layoutbuilder.h> + +using namespace Layouting; +using namespace Utils; + +namespace Autotest::Internal { + +BoostTestFramework &theBoostTestFramework() +{ + static BoostTestFramework framework; + return framework; +} + +BoostTestFramework::BoostTestFramework() +{ + setActive(true); + setSettingsGroups("Autotest", "BoostTest"); + setId(BoostTest::Constants::FRAMEWORK_ID); + setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setPriority(BoostTest::Constants::FRAMEWORK_PRIORITY); + + setLayouter([this] { + return Row { Form { + logLevel, br, + reportLevel, br, + randomize, Row { seed }, br, + systemErrors, br, + fpExceptions, br, + memLeaks, + }, st}; + }); + + logLevel.setSettingsKey("LogLevel"); + logLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + logLevel.addOption("All"); + logLevel.addOption("Success"); + logLevel.addOption("Test Suite"); + logLevel.addOption("Unit Scope"); + logLevel.addOption("Message"); + logLevel.addOption("Warning"); + logLevel.addOption("Error"); + logLevel.addOption("C++ Exception"); + logLevel.addOption("System Error"); + logLevel.addOption("Fatal Error"); + logLevel.addOption("Nothing"); + logLevel.setDefaultValue(int(LogLevel::Warning)); + logLevel.setLabelText(Tr::tr("Log format:")); + + reportLevel.setSettingsKey("ReportLevel"); + reportLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + reportLevel.addOption("Confirm"); + reportLevel.addOption("Short"); + reportLevel.addOption("Detailed"); + reportLevel.addOption("No"); + reportLevel.setDefaultValue(int(ReportLevel::Confirm)); + reportLevel.setLabelText(Tr::tr("Report level:")); + + seed.setSettingsKey("Seed"); + seed.setEnabled(false); + seed.setRange(0, INT_MAX); // UINT_MAX would be correct, but inner QSpinBox is limited to int + seed.setLabelText(Tr::tr("Seed:")); + seed.setToolTip(Tr::tr("A seed of 0 means no randomization. A value of 1 uses the current " + "time, any other value is used as random seed generator.")); + seed.setEnabler(&randomize); + + randomize.setSettingsKey("Randomize"); + randomize.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + randomize.setLabelText(Tr::tr("Randomize")); + randomize.setToolTip(Tr::tr("Randomize execution order.")); + + systemErrors.setSettingsKey("SystemErrors"); + systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + systemErrors.setLabelText(Tr::tr("Catch system errors")); + systemErrors.setToolTip(Tr::tr("Catch or ignore system errors.")); + + fpExceptions.setSettingsKey("FPExceptions"); + fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + fpExceptions.setLabelText(Tr::tr("Floating point exceptions")); + fpExceptions.setToolTip(Tr::tr("Enable floating point exception traps.")); + + memLeaks.setSettingsKey("MemoryLeaks"); + memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + memLeaks.setDefaultValue(true); + memLeaks.setLabelText(Tr::tr("Detect memory leaks")); + memLeaks.setToolTip(Tr::tr("Enable memory leak detection.")); +} + +QString BoostTestFramework::logLevelToOption(const LogLevel logLevel) +{ + switch (logLevel) { + case LogLevel::All: return QString("all"); + case LogLevel::Success: return QString("success"); + case LogLevel::TestSuite: return QString("test_suite"); + case LogLevel::UnitScope: return QString("unit_scope"); + case LogLevel::Message: return QString("message"); + case LogLevel::Error: return QString("error"); + case LogLevel::CppException: return QString("cpp_exception"); + case LogLevel::SystemError: return QString("system_error"); + case LogLevel::FatalError: return QString("fatal_error"); + case LogLevel::Nothing: return QString("nothing"); + case LogLevel::Warning: return QString("warning"); + } + return {}; +} + +QString BoostTestFramework::reportLevelToOption(const ReportLevel reportLevel) +{ + switch (reportLevel) { + case ReportLevel::Confirm: return QString("confirm"); + case ReportLevel::Short: return QString("short"); + case ReportLevel::Detailed: return QString("detailed"); + case ReportLevel::No: return QString("no"); + } + return {}; +} ITestParser *BoostTestFramework::createTestParser() { @@ -21,20 +137,21 @@ ITestTreeItem *BoostTestFramework::createRootNode() return new BoostTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } -const char *BoostTestFramework::name() const -{ - return BoostTest::Constants::FRAMEWORK_NAME; -} +// BoostSettingsPage -QString BoostTestFramework::displayName() const +class BoostSettingsPage final : public Core::IOptionsPage { - return Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY); -} +public: + BoostSettingsPage() + { + setId(Id(Constants::SETTINGSPAGE_PREFIX).withSuffix(QString("%1.Boost") + .arg(BoostTest::Constants::FRAMEWORK_PRIORITY))); + setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); + setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setSettingsProvider([] { return &theBoostTestFramework() ; }); + } +}; -unsigned BoostTestFramework::priority() const -{ - return BoostTest::Constants::FRAMEWORK_PRIORITY; -} +const BoostSettingsPage settingsPage; -} // namespace Internal -} // namespace Autotest +} // Autotest::Internal diff --git a/src/plugins/autotest/boost/boosttestframework.h b/src/plugins/autotest/boost/boosttestframework.h index da015fbd6f3..582ba5e6924 100644 --- a/src/plugins/autotest/boost/boosttestframework.h +++ b/src/plugins/autotest/boost/boosttestframework.h @@ -5,24 +5,54 @@ #include "../itestframework.h" -#include "boosttestsettings.h" - namespace Autotest::Internal { +enum class LogLevel +{ + All, + Success, + TestSuite, + UnitScope, + Message, + Warning, + Error, + CppException, + SystemError, + FatalError, + Nothing +}; + +enum class ReportLevel +{ + Confirm, + Short, + Detailed, + No +}; + class BoostTestFramework : public ITestFramework { public: - BoostTestFramework() : ITestFramework(true) {} + BoostTestFramework(); -private: - const char *name() const override; - QString displayName() const override; - unsigned priority() const override; - ITestSettings *testSettings() override { return &m_settings; } ITestParser *createTestParser() override; ITestTreeItem *createRootNode() override; - BoostTestSettings m_settings{settingsId()}; + static QString logLevelToOption(const LogLevel logLevel); + static QString reportLevelToOption(const ReportLevel reportLevel); + + Utils::SelectionAspect logLevel{this}; + Utils::SelectionAspect reportLevel{this}; + Utils::IntegerAspect seed{this}; + Utils::BoolAspect randomize{this}; + Utils::BoolAspect systemErrors{this}; + Utils::BoolAspect fpExceptions{this}; + Utils::BoolAspect memLeaks{this}; }; +BoostTestFramework &theBoostTestFramework(); + } // Autotest::Internal + +Q_DECLARE_METATYPE(Autotest::Internal::LogLevel) +Q_DECLARE_METATYPE(Autotest::Internal::ReportLevel) diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp index b903f6c10b1..7b58d74f528 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.cpp +++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp @@ -3,7 +3,7 @@ #include "boosttestoutputreader.h" -#include "boosttestsettings.h" +#include "boosttestframework.h" #include "boosttestresult.h" #include "../autotesttr.h" @@ -327,7 +327,8 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) BoostTestResult result(id(), {}, m_projectFile); const int failed = match.captured(1).toInt(); const int fatals = m_summary.value(ResultType::MessageFatal); - QString txt = Tr::tr("%1 failures detected in %2.").arg(failed).arg(match.captured(3)); + QString txt + = Tr::tr("%n failure(s) detected in %1.", nullptr, failed).arg(match.captured(3)); const int passed = qMax(0, m_testCaseCount - failed); if (m_testCaseCount != -1) txt.append(' ').append(Tr::tr("%1 tests passed.").arg(passed)); diff --git a/src/plugins/autotest/boost/boosttestparser.cpp b/src/plugins/autotest/boost/boosttestparser.cpp index 8c701942ada..47b190ecffe 100644 --- a/src/plugins/autotest/boost/boosttestparser.cpp +++ b/src/plugins/autotest/boost/boosttestparser.cpp @@ -104,9 +104,8 @@ bool BoostTestParser::processDocument(QPromise<TestParseResultPtr> &promise, if (doc.isNull() || !includesBoostTest(doc, m_cppSnapshot) || !hasBoostTestMacros(doc)) return false; - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); const QList<CppEditor::ProjectPart::ConstPtr> projectParts - = modelManager->projectPart(fileName); + = CppEditor::CppModelManager::projectPart(fileName); if (projectParts.isEmpty()) // happens if shutting down while parsing return false; const CppEditor::ProjectPart::ConstPtr projectPart = projectParts.first(); diff --git a/src/plugins/autotest/boost/boosttestresult.cpp b/src/plugins/autotest/boost/boosttestresult.cpp index 80cca80cc4b..72d89dfcfe5 100644 --- a/src/plugins/autotest/boost/boosttestresult.cpp +++ b/src/plugins/autotest/boost/boosttestresult.cpp @@ -73,8 +73,8 @@ static ResultHooks::FindTestItemHook findTestItemHook(const FilePath &projectFil const QString &testSuiteName) { return [=](const TestResult &result) -> ITestTreeItem * { - const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix(BoostTest::Constants::FRAMEWORK_NAME); - ITestFramework *framework = TestFrameworkManager::frameworkForId(id); + ITestFramework *framework = + TestFrameworkManager::frameworkForId(BoostTest::Constants::FRAMEWORK_ID); QTC_ASSERT(framework, return nullptr); const TestTreeItem *rootNode = framework->rootNode(); if (!rootNode) diff --git a/src/plugins/autotest/boost/boosttestsettings.cpp b/src/plugins/autotest/boost/boosttestsettings.cpp deleted file mode 100644 index 4ff10a90ce8..00000000000 --- a/src/plugins/autotest/boost/boosttestsettings.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "boosttestsettings.h" - -#include "boosttestconstants.h" - -#include "../autotestconstants.h" -#include "../autotesttr.h" - -#include <utils/layoutbuilder.h> - -using namespace Layouting; -using namespace Utils; - -namespace Autotest::Internal { - -BoostTestSettings::BoostTestSettings(Id settingsId) -{ - setId(settingsId); - setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); - setDisplayName(Tr::tr(BoostTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); - setSettingsGroups("Autotest", "BoostTest"); - - setLayouter([this] { - return Row { Form { - logLevel, br, - reportLevel, br, - randomize, Row { seed }, br, - systemErrors, br, - fpExceptions, br, - memLeaks, - }, st}; - }); - - logLevel.setSettingsKey("LogLevel"); - logLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - logLevel.addOption("All"); - logLevel.addOption("Success"); - logLevel.addOption("Test Suite"); - logLevel.addOption("Unit Scope"); - logLevel.addOption("Message"); - logLevel.addOption("Warning"); - logLevel.addOption("Error"); - logLevel.addOption("C++ Exception"); - logLevel.addOption("System Error"); - logLevel.addOption("Fatal Error"); - logLevel.addOption("Nothing"); - logLevel.setDefaultValue(int(LogLevel::Warning)); - logLevel.setLabelText(Tr::tr("Log format:")); - - reportLevel.setSettingsKey("ReportLevel"); - reportLevel.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - reportLevel.addOption("Confirm"); - reportLevel.addOption("Short"); - reportLevel.addOption("Detailed"); - reportLevel.addOption("No"); - reportLevel.setDefaultValue(int(ReportLevel::Confirm)); - reportLevel.setLabelText(Tr::tr("Report level:")); - - seed.setSettingsKey("Seed"); - seed.setEnabled(false); - seed.setRange(0, INT_MAX); // UINT_MAX would be correct, but inner QSpinBox is limited to int - seed.setLabelText(Tr::tr("Seed:")); - seed.setToolTip(Tr::tr("A seed of 0 means no randomization. A value of 1 uses the current " - "time, any other value is used as random seed generator.")); - seed.setEnabler(&randomize); - - randomize.setSettingsKey("Randomize"); - randomize.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - randomize.setLabelText(Tr::tr("Randomize")); - randomize.setToolTip(Tr::tr("Randomize execution order.")); - - systemErrors.setSettingsKey("SystemErrors"); - systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - systemErrors.setLabelText(Tr::tr("Catch system errors")); - systemErrors.setToolTip(Tr::tr("Catch or ignore system errors.")); - - fpExceptions.setSettingsKey("FPExceptions"); - fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - fpExceptions.setLabelText(Tr::tr("Floating point exceptions")); - fpExceptions.setToolTip(Tr::tr("Enable floating point exception traps.")); - - memLeaks.setSettingsKey("MemoryLeaks"); - memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - memLeaks.setDefaultValue(true); - memLeaks.setLabelText(Tr::tr("Detect memory leaks")); - memLeaks.setToolTip(Tr::tr("Enable memory leak detection.")); -} - -QString BoostTestSettings::logLevelToOption(const LogLevel logLevel) -{ - switch (logLevel) { - case LogLevel::All: return QString("all"); - case LogLevel::Success: return QString("success"); - case LogLevel::TestSuite: return QString("test_suite"); - case LogLevel::UnitScope: return QString("unit_scope"); - case LogLevel::Message: return QString("message"); - case LogLevel::Error: return QString("error"); - case LogLevel::CppException: return QString("cpp_exception"); - case LogLevel::SystemError: return QString("system_error"); - case LogLevel::FatalError: return QString("fatal_error"); - case LogLevel::Nothing: return QString("nothing"); - case LogLevel::Warning: return QString("warning"); - } - return {}; -} - -QString BoostTestSettings::reportLevelToOption(const ReportLevel reportLevel) -{ - switch (reportLevel) { - case ReportLevel::Confirm: return QString("confirm"); - case ReportLevel::Short: return QString("short"); - case ReportLevel::Detailed: return QString("detailed"); - case ReportLevel::No: return QString("no"); - } - return {}; -} - -} // Autotest::Internal diff --git a/src/plugins/autotest/boost/boosttestsettings.h b/src/plugins/autotest/boost/boosttestsettings.h deleted file mode 100644 index e95f98218c2..00000000000 --- a/src/plugins/autotest/boost/boosttestsettings.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Autotest::Internal { - -enum class LogLevel -{ - All, - Success, - TestSuite, - UnitScope, - Message, - Warning, - Error, - CppException, - SystemError, - FatalError, - Nothing -}; - -enum class ReportLevel -{ - Confirm, - Short, - Detailed, - No -}; - -class BoostTestSettings : public Core::PagedSettings -{ -public: - explicit BoostTestSettings(Utils::Id settingsId); - - static QString logLevelToOption(const LogLevel logLevel); - static QString reportLevelToOption(const ReportLevel reportLevel); - - Utils::SelectionAspect logLevel{this}; - Utils::SelectionAspect reportLevel{this}; - Utils::IntegerAspect seed{this}; - Utils::BoolAspect randomize{this}; - Utils::BoolAspect systemErrors{this}; - Utils::BoolAspect fpExceptions{this}; - Utils::BoolAspect memLeaks{this}; -}; - -} // Autotest::Internal - -Q_DECLARE_METATYPE(Autotest::Internal::LogLevel) -Q_DECLARE_METATYPE(Autotest::Internal::ReportLevel) diff --git a/src/plugins/autotest/boost/boosttesttreeitem.cpp b/src/plugins/autotest/boost/boosttesttreeitem.cpp index 957ff73021a..d1fcdcdedd9 100644 --- a/src/plugins/autotest/boost/boosttesttreeitem.cpp +++ b/src/plugins/autotest/boost/boosttesttreeitem.cpp @@ -178,10 +178,9 @@ QList<ITestConfiguration *> BoostTestTreeItem::getAllTestConfigurations() const ++funcChildren; }); if (funcChildren) { - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); testsPerProjectfile[item->proFile()].testCases += funcChildren; - testsPerProjectfile[item->proFile()].internalTargets.unite(cppMM->internalTargets(item->filePath())); + testsPerProjectfile[item->proFile()].internalTargets.unite( + CppEditor::CppModelManager::internalTargets(item->filePath())); } }); @@ -219,8 +218,6 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations( if (!item->enabled()) // ignore child tests known to be disabled when using run selected return; if (predicate(item)) { - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); QString tcName = item->name(); if (item->state().testFlag(BoostTestTreeItem::Templated)) tcName.append("<*"); @@ -229,7 +226,8 @@ QList<ITestConfiguration *> BoostTestTreeItem::getTestConfigurations( tcName = handleSpecialFunctionNames(tcName); testCasesForProjectFile[item->proFile()].testCases.append( item->prependWithParentsSuitePaths(tcName)); - testCasesForProjectFile[item->proFile()].internalTargets.unite(cppMM->internalTargets(item->filePath())); + testCasesForProjectFile[item->proFile()].internalTargets.unite( + CppEditor::CppModelManager::internalTargets(item->filePath())); } }); @@ -265,8 +263,6 @@ ITestConfiguration *BoostTestTreeItem::testConfiguration() const { ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); QTC_ASSERT(project, return nullptr); - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return nullptr); const Type itemType = type(); if (itemType == TestSuite || itemType == TestCase) { @@ -300,7 +296,7 @@ ITestConfiguration *BoostTestTreeItem::testConfiguration() const config->setProjectFile(proFile()); config->setProject(project); config->setTestCases(testCases); - config->setInternalTargets(cppMM->internalTargets(filePath())); + config->setInternalTargets(CppEditor::CppModelManager::internalTargets(filePath())); return config; } return nullptr; diff --git a/src/plugins/autotest/catch/catchconfiguration.cpp b/src/plugins/autotest/catch/catchconfiguration.cpp index a5036398688..e1773064f77 100644 --- a/src/plugins/autotest/catch/catchconfiguration.cpp +++ b/src/plugins/autotest/catch/catchconfiguration.cpp @@ -3,10 +3,9 @@ #include "catchconfiguration.h" +#include "catchtestframework.h" #include "catchoutputreader.h" -#include "catchtestsettings.h" -#include "../itestframework.h" #include "../testsettings.h" using namespace Utils; @@ -79,37 +78,35 @@ QStringList CatchConfiguration::argumentsForTestRunner(QStringList *omitted) con arguments << "\"" + testCases().join("\", \"") + "\""; arguments << "--reporter" << "xml"; - if (TestSettings::instance()->processArgs()) { + if (testSettings().processArgs()) { arguments << filterInterfering(runnable().command.arguments().split( ' ', Qt::SkipEmptyParts), omitted); } - auto settings = static_cast<CatchTestSettings *>(framework()->testSettings()); - if (!settings) - return arguments; + CatchFramework &settings = theCatchFramework(); - if (settings->abortAfterChecked.value()) - arguments << "-x" << QString::number(settings->abortAfter.value()); - if (settings->samplesChecked.value()) - arguments << "--benchmark-samples" << QString::number(settings->benchmarkSamples.value()); - if (settings->resamplesChecked.value()) - arguments << "--benchmark-resamples" << QString::number(settings->benchmarkResamples.value()); - if (settings->warmupChecked.value()) - arguments << "--benchmark-warmup-time" << QString::number(settings->benchmarkWarmupTime.value()); - if (settings->confidenceIntervalChecked.value()) - arguments << "--benchmark-confidence-interval" << QString::number(settings->confidenceInterval.value()); - if (settings->noAnalysis.value()) + if (settings.abortAfterChecked()) + arguments << "-x" << QString::number(settings.abortAfter()); + if (settings.samplesChecked()) + arguments << "--benchmark-samples" << QString::number(settings.benchmarkSamples()); + if (settings.resamplesChecked()) + arguments << "--benchmark-resamples" << QString::number(settings.benchmarkResamples()); + if (settings.warmupChecked()) + arguments << "--benchmark-warmup-time" << QString::number(settings.benchmarkWarmupTime()); + if (settings.confidenceIntervalChecked()) + arguments << "--benchmark-confidence-interval" << QString::number(settings.confidenceInterval()); + if (settings.noAnalysis()) arguments << "--benchmark-no-analysis"; - if (settings->showSuccess.value()) + if (settings.showSuccess()) arguments << "-s"; - if (settings->noThrow.value()) + if (settings.noThrow()) arguments << "-e"; - if (settings->visibleWhitespace.value()) + if (settings.visibleWhitespace()) arguments << "-i"; - if (settings->warnOnEmpty.value()) + if (settings.warnOnEmpty()) arguments << "-w" << "NoAssertions"; - if (isDebugRunMode() && settings->breakOnFailure.value()) + if (isDebugRunMode() && settings.breakOnFailure()) arguments << "-b"; return arguments; } diff --git a/src/plugins/autotest/catch/catchframework.cpp b/src/plugins/autotest/catch/catchframework.cpp deleted file mode 100644 index d2afeddaad2..00000000000 --- a/src/plugins/autotest/catch/catchframework.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019 Jochen Seemann -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "catchframework.h" - -#include "catchtestparser.h" -#include "catchtreeitem.h" -#include "../autotesttr.h" - -namespace Autotest { -namespace Internal { - -const char *CatchFramework::name() const -{ - return "Catch"; -} - -QString CatchFramework::displayName() const -{ - return Tr::tr("Catch Test"); -} - -unsigned CatchFramework::priority() const -{ - return 12; -} - -ITestParser *CatchFramework::createTestParser() -{ - return new CatchTestParser(this); -} - -ITestTreeItem *CatchFramework::createRootNode() -{ - return new CatchTreeItem(this, displayName(), {}, ITestTreeItem::Root); -} - -} // namespace Internal -} // namespace Autotest diff --git a/src/plugins/autotest/catch/catchframework.h b/src/plugins/autotest/catch/catchframework.h deleted file mode 100644 index 6bd20b44f6a..00000000000 --- a/src/plugins/autotest/catch/catchframework.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019 Jochen Seemann -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../itestframework.h" - -#include "catchtestsettings.h" - -namespace Autotest::Internal { - -class CatchFramework : public ITestFramework -{ -public: - CatchFramework() : ITestFramework(true) {} - - const char *name() const override; - QString displayName() const override; - unsigned priority() const override; - -protected: - ITestParser *createTestParser() override; - ITestTreeItem *createRootNode() override; - -private: - ITestSettings * testSettings() override { return &m_settings; } - CatchTestSettings m_settings{settingsId()}; -}; - -} // Autotest::Internal diff --git a/src/plugins/autotest/catch/catchtestsettings.cpp b/src/plugins/autotest/catch/catchtestframework.cpp similarity index 80% rename from src/plugins/autotest/catch/catchtestsettings.cpp rename to src/plugins/autotest/catch/catchtestframework.cpp index 5ee4a6acf97..8c312f65d15 100644 --- a/src/plugins/autotest/catch/catchtestsettings.cpp +++ b/src/plugins/autotest/catch/catchtestframework.cpp @@ -1,11 +1,16 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "catchtestsettings.h" +#include "catchtestframework.h" + +#include "catchtestparser.h" +#include "catchtreeitem.h" #include "../autotestconstants.h" #include "../autotesttr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/layoutbuilder.h> using namespace Layouting; @@ -13,12 +18,19 @@ using namespace Utils; namespace Autotest::Internal { -CatchTestSettings::CatchTestSettings(Id settingsId) +CatchFramework &theCatchFramework() { - setId(settingsId); - setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); - setDisplayName(Tr::tr("Catch Test")); + static CatchFramework framework; + return framework; +} + +CatchFramework::CatchFramework() +{ + setActive(true); setSettingsGroups("Autotest", "Catch2"); + setPriority(12); + setId("AutoTest.Framework.Catch"); + setDisplayName(Tr::tr("Catch Test")); setLayouter([this] { return Row { Form { @@ -107,4 +119,30 @@ CatchTestSettings::CatchTestSettings(Id settingsId) warnOnEmpty.setToolTip(Tr::tr("Warns if a test section does not check any assertion.")); } +ITestParser *CatchFramework::createTestParser() +{ + return new CatchTestParser(this); +} + +ITestTreeItem *CatchFramework::createRootNode() +{ + return new CatchTreeItem(this, displayName(), {}, ITestTreeItem::Root); +} + +// CatchTestSettingsPage + +class CatchTestSettingsPage final : public Core::IOptionsPage +{ +public: + CatchTestSettingsPage() + { + setId(Id(Constants::SETTINGSPAGE_PREFIX).withSuffix("12.Catch")); + setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); + setDisplayName(Tr::tr("Catch Test")); + setSettingsProvider([] { return &theCatchFramework(); }); + } +}; + +const CatchTestSettingsPage settingsPage; + } // Autotest::Internal diff --git a/src/plugins/autotest/catch/catchtestsettings.h b/src/plugins/autotest/catch/catchtestframework.h similarity index 79% rename from src/plugins/autotest/catch/catchtestsettings.h rename to src/plugins/autotest/catch/catchtestframework.h index 71c1caf5839..5b5b3f27bda 100644 --- a/src/plugins/autotest/catch/catchtestsettings.h +++ b/src/plugins/autotest/catch/catchtestframework.h @@ -3,14 +3,14 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include "../itestframework.h" namespace Autotest::Internal { -class CatchTestSettings : public Core::PagedSettings +class CatchFramework : public ITestFramework { public: - explicit CatchTestSettings(Utils::Id settingsId); + CatchFramework(); Utils::IntegerAspect abortAfter{this}; Utils::IntegerAspect benchmarkSamples{this}; @@ -28,6 +28,12 @@ public: Utils::BoolAspect noThrow{this}; Utils::BoolAspect visibleWhitespace{this}; Utils::BoolAspect warnOnEmpty{this}; + +protected: + ITestParser *createTestParser() override; + ITestTreeItem *createRootNode() override; }; +CatchFramework &theCatchFramework(); + } // Autotest::Internal diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp index a37f8b4ca73..96f536a9055 100644 --- a/src/plugins/autotest/catch/catchtestparser.cpp +++ b/src/plugins/autotest/catch/catchtestparser.cpp @@ -4,11 +4,12 @@ #include "catchtestparser.h" #include "catchcodeparser.h" -#include "catchframework.h" +#include "catchtestframework.h" #include "catchtreeitem.h" #include <cppeditor/cppmodelmanager.h> #include <cppeditor/projectpart.h> + #include <utils/qtcassert.h> #include <QPromise> @@ -99,7 +100,6 @@ bool CatchTestParser::processDocument(QPromise<TestParseResultPtr> &promise, if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot)) return false; - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); const QString &filePath = doc->filePath().toString(); const QByteArray &fileContent = getFileContent(fileName); @@ -115,7 +115,8 @@ bool CatchTestParser::processDocument(QPromise<TestParseResultPtr> &promise, } - const QList<CppEditor::ProjectPart::ConstPtr> projectParts = modelManager->projectPart(fileName); + const QList<CppEditor::ProjectPart::ConstPtr> projectParts + = CppEditor::CppModelManager::projectPart(fileName); if (projectParts.isEmpty()) // happens if shutting down while parsing return false; FilePath proFile; diff --git a/src/plugins/autotest/catch/catchtreeitem.cpp b/src/plugins/autotest/catch/catchtreeitem.cpp index 034c7ab1d04..9f1d7c8e17c 100644 --- a/src/plugins/autotest/catch/catchtreeitem.cpp +++ b/src/plugins/autotest/catch/catchtreeitem.cpp @@ -145,8 +145,6 @@ ITestConfiguration *CatchTreeItem::testConfiguration() const { ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); QTC_ASSERT(project, return nullptr); - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return nullptr); if (type() != TestCase) return nullptr; @@ -157,7 +155,7 @@ ITestConfiguration *CatchTreeItem::testConfiguration() const config->setProjectFile(proFile()); config->setProject(project); config->setTestCases(QStringList(testCasesString())); - config->setInternalTargets(cppMM->internalTargets(filePath())); + config->setInternalTargets(CppEditor::CppModelManager::internalTargets(filePath())); return config; } @@ -180,8 +178,6 @@ static void collectTestInfo(const TestTreeItem *item, bool ignoreCheckState) { QTC_ASSERT(item, return); - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); const int childCount = item->childCount(); if (item->type() == TestTreeItem::GroupNode) { item->forFirstLevelChildItems([&testCasesForProfile, ignoreCheckState](TestTreeItem *it) { @@ -198,15 +194,16 @@ static void collectTestInfo(const TestTreeItem *item, CatchTreeItem *current = static_cast<CatchTreeItem *>(it); testCasesForProfile[projectFile].names.append(current->testCasesString()); }); - testCasesForProfile[projectFile].internalTargets.unite(cppMM->internalTargets(item->filePath())); + testCasesForProfile[projectFile].internalTargets.unite( + CppEditor::CppModelManager::internalTargets(item->filePath())); } else if (item->checked() == Qt::PartiallyChecked) { - item->forFirstLevelChildItems([&testCasesForProfile, cppMM](TestTreeItem *child) { + item->forFirstLevelChildItems([&testCasesForProfile](TestTreeItem *child) { QTC_ASSERT(child->type() == TestTreeItem::TestCase, return); if (child->checked() == Qt::Checked) { CatchTreeItem *current = static_cast<CatchTreeItem *>(child); testCasesForProfile[child->proFile()].names.append(current->testCasesString()); testCasesForProfile[child->proFile()].internalTargets.unite( - cppMM->internalTargets(child->filePath())); + CppEditor::CppModelManager::internalTargets(child->filePath())); } }); @@ -222,13 +219,11 @@ static void collectFailedTestInfo(const CatchTreeItem *item, item->forAllChildItems([&testCasesForProfile](TestTreeItem *it) { QTC_ASSERT(it, return); QTC_ASSERT(it->parentItem(), return); - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); if (it->type() == TestTreeItem::TestCase && it->data(0, FailedRole).toBool()) { CatchTreeItem *current = static_cast<CatchTreeItem *>(it); testCasesForProfile[it->proFile()].names.append(current->testCasesString()); testCasesForProfile[it->proFile()].internalTargets.unite( - cppMM->internalTargets(it->filePath())); + CppEditor::CppModelManager::internalTargets(it->filePath())); } }); } @@ -270,8 +265,6 @@ QList<ITestConfiguration *> CatchTreeItem::getFailedTestConfigurations() const QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const FilePath &fileName) const { QList<ITestConfiguration *> result; - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return result); ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); if (!project || type() != Root) @@ -296,7 +289,8 @@ QList<ITestConfiguration *> CatchTreeItem::getTestConfigurationsForFile(const Fi testConfig->setTestCases(testCases); testConfig->setProjectFile(item->proFile()); testConfig->setProject(ProjectExplorer::ProjectManager::startupProject()); - testConfig->setInternalTargets(cppMM->internalTargets(item->filePath())); + testConfig->setInternalTargets( + CppEditor::CppModelManager::internalTargets(item->filePath())); result << testConfig; } diff --git a/src/plugins/autotest/ctest/ctestsettings.cpp b/src/plugins/autotest/ctest/ctestsettings.cpp deleted file mode 100644 index 70016f79209..00000000000 --- a/src/plugins/autotest/ctest/ctestsettings.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "ctestsettings.h" - -#include "../autotestconstants.h" -#include "../autotesttr.h" - -#include <utils/layoutbuilder.h> - -using namespace Layouting; -using namespace Utils; - -namespace Autotest::Internal { - -CTestSettings::CTestSettings(Id settingsId) -{ - setSettingsGroups("Autotest", "CTest"); - setId(settingsId); - setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); - setDisplayName(Tr::tr("CTest")); - - setLayouter([this] { - return Row { Form { - outputOnFail, br, - scheduleRandom, br, - stopOnFailure, br, - outputMode, br, - Group { - title(Tr::tr("Repeat tests")), - repeat.groupChecker(), - Row { repetitionMode, repetitionCount}, - }, br, - Group { - title(Tr::tr("Run in parallel")), - parallel.groupChecker(), - Column { - Row { jobs }, br, - Row { testLoad, threshold} - } - } - }, st }; - }); - - outputOnFail.setSettingsKey("OutputOnFail"); - outputOnFail.setLabelText(Tr::tr("Output on failure")); - outputOnFail.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - outputOnFail.setDefaultValue(true); - - outputMode.setSettingsKey("OutputMode"); - outputMode.setLabelText(Tr::tr("Output mode")); - outputMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - outputMode.addOption({Tr::tr("Default"), {}, 0}); - outputMode.addOption({Tr::tr("Verbose"), {}, 1}); - outputMode.addOption({Tr::tr("Very Verbose"), {}, 2}); - - repetitionMode.setSettingsKey("RepetitionMode"); - repetitionMode.setLabelText(Tr::tr("Repetition mode")); - repetitionMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - repetitionMode.addOption({Tr::tr("Until Fail"), {}, 0}); - repetitionMode.addOption({Tr::tr("Until Pass"), {}, 1}); - repetitionMode.addOption({Tr::tr("After Timeout"), {}, 2}); - - repetitionCount.setSettingsKey("RepetitionCount"); - repetitionCount.setDefaultValue(1); - repetitionCount.setLabelText(Tr::tr("Count")); - repetitionCount.setToolTip(Tr::tr("Number of re-runs for the test.")); - repetitionCount.setRange(1, 10000); - - repeat.setSettingsKey("Repeat"); - - scheduleRandom.setSettingsKey("ScheduleRandom"); - scheduleRandom.setLabelText(Tr::tr("Schedule random")); - scheduleRandom.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - - stopOnFailure.setSettingsKey("StopOnFail"); - stopOnFailure.setLabelText(Tr::tr("Stop on failure")); - stopOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - - parallel.setSettingsKey("Parallel"); - parallel.setToolTip(Tr::tr("Run tests in parallel mode using given number of jobs.")); - - jobs.setSettingsKey("Jobs"); - jobs.setLabelText(Tr::tr("Jobs")); - jobs.setDefaultValue(1); - jobs.setRange(1, 128); - - testLoad.setSettingsKey("TestLoad"); - testLoad.setLabelText(Tr::tr("Test load")); - testLoad.setToolTip(Tr::tr("Try not to start tests when they may cause CPU load to pass a " - "threshold.")); - - threshold.setSettingsKey("Threshold"); - threshold.setLabelText(Tr::tr("Threshold")); - threshold.setDefaultValue(1); - threshold.setRange(1, 128); - threshold.setEnabler(&testLoad); -} - -QStringList CTestSettings::activeSettingsAsOptions() const -{ - QStringList options; - if (outputOnFail.value()) - options << "--output-on-failure"; - switch (outputMode.value()) { - case 1: options << "-V"; break; - case 2: options << "-VV"; break; - default: break; - } - if (repeat.value()) { - QString repeatOption; - switch (repetitionMode.value()) { - case 0: repeatOption = "until-fail"; break; - case 1: repeatOption = "until-pass"; break; - case 2: repeatOption = "after-timeout"; break; - default: break; - } - if (!repeatOption.isEmpty()) { - repeatOption.append(':'); - repeatOption.append(QString::number(repetitionCount.value())); - options << "--repeat" << repeatOption; - } - } - if (scheduleRandom.value()) - options << "--schedule-random"; - if (stopOnFailure.value()) - options << "--stop-on-failure"; - if (parallel.value()) { - options << "-j" << QString::number(jobs.value()); - if (testLoad.value()) - options << "--test-load" << QString::number(threshold.value()); - } - return options; -} - -} // Autotest::Internal diff --git a/src/plugins/autotest/ctest/ctestsettings.h b/src/plugins/autotest/ctest/ctestsettings.h deleted file mode 100644 index 77de6b0aac8..00000000000 --- a/src/plugins/autotest/ctest/ctestsettings.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Autotest::Internal { - -class CTestSettings : public Core::PagedSettings -{ -public: - explicit CTestSettings(Utils::Id settingsId); - - QStringList activeSettingsAsOptions() const; - - Utils::IntegerAspect repetitionCount{this}; - Utils::SelectionAspect repetitionMode{this}; - Utils::SelectionAspect outputMode{this}; - Utils::BoolAspect outputOnFail{this}; - Utils::BoolAspect stopOnFailure{this}; - Utils::BoolAspect scheduleRandom{this}; - Utils::BoolAspect repeat{this}; - // FIXME.. this makes the outputreader fail to get all results correctly for visual display - Utils::BoolAspect parallel{this}; - Utils::IntegerAspect jobs{this}; - Utils::BoolAspect testLoad{this}; - Utils::IntegerAspect threshold{this}; -}; - -} // Autotest::Internal - diff --git a/src/plugins/autotest/ctest/ctesttool.cpp b/src/plugins/autotest/ctest/ctesttool.cpp index fd2fe675282..2dee957194f 100644 --- a/src/plugins/autotest/ctest/ctesttool.cpp +++ b/src/plugins/autotest/ctest/ctesttool.cpp @@ -1,20 +1,155 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "ctesttool.h" + #include "ctesttreeitem.h" +#include "../autotestconstants.h" #include "../autotesttr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <cmakeprojectmanager/cmakeprojectconstants.h> #include <projectexplorer/buildsystem.h> -namespace Autotest { -namespace Internal { +#include <utils/layoutbuilder.h> -Utils::Id CTestTool::buildSystemId() const +using namespace Layouting; +using namespace Utils; + +namespace Autotest::Internal { + +CTestTool &theCTestTool() { - return Utils::Id(CMakeProjectManager::Constants::CMAKE_PROJECT_ID); + static CTestTool tool; + return tool; +} + +CTestTool::CTestTool() +{ + setActive(false); + setSettingsGroups("Autotest", "CTest"); + setAutoApply(false); + setId("AutoTest.Framework.CTest"); + setDisplayName(Tr::tr("CTest")); + + setLayouter([this] { + return Row { Form { + outputOnFail, br, + scheduleRandom, br, + stopOnFailure, br, + outputMode, br, + Group { + title(Tr::tr("Repeat tests")), + repeat.groupChecker(), + Row { repetitionMode, repetitionCount}, + }, br, + Group { + title(Tr::tr("Run in parallel")), + parallel.groupChecker(), + Column { + Row { jobs }, br, + Row { testLoad, threshold} + } + } + }, st }; + }); + + outputOnFail.setSettingsKey("OutputOnFail"); + outputOnFail.setLabelText(Tr::tr("Output on failure")); + outputOnFail.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + outputOnFail.setDefaultValue(true); + + outputMode.setSettingsKey("OutputMode"); + outputMode.setLabelText(Tr::tr("Output mode")); + outputMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + outputMode.addOption({Tr::tr("Default"), {}, 0}); + outputMode.addOption({Tr::tr("Verbose"), {}, 1}); + outputMode.addOption({Tr::tr("Very Verbose"), {}, 2}); + + repetitionMode.setSettingsKey("RepetitionMode"); + repetitionMode.setLabelText(Tr::tr("Repetition mode")); + repetitionMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + repetitionMode.addOption({Tr::tr("Until Fail"), {}, 0}); + repetitionMode.addOption({Tr::tr("Until Pass"), {}, 1}); + repetitionMode.addOption({Tr::tr("After Timeout"), {}, 2}); + + repetitionCount.setSettingsKey("RepetitionCount"); + repetitionCount.setDefaultValue(1); + repetitionCount.setLabelText(Tr::tr("Count")); + repetitionCount.setToolTip(Tr::tr("Number of re-runs for the test.")); + repetitionCount.setRange(1, 10000); + + repeat.setSettingsKey("Repeat"); + + scheduleRandom.setSettingsKey("ScheduleRandom"); + scheduleRandom.setLabelText(Tr::tr("Schedule random")); + scheduleRandom.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + + stopOnFailure.setSettingsKey("StopOnFail"); + stopOnFailure.setLabelText(Tr::tr("Stop on failure")); + stopOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + + parallel.setSettingsKey("Parallel"); + parallel.setToolTip(Tr::tr("Run tests in parallel mode using given number of jobs.")); + + jobs.setSettingsKey("Jobs"); + jobs.setLabelText(Tr::tr("Jobs")); + jobs.setDefaultValue(1); + jobs.setRange(1, 128); + + testLoad.setSettingsKey("TestLoad"); + testLoad.setLabelText(Tr::tr("Test load")); + testLoad.setToolTip(Tr::tr("Try not to start tests when they may cause CPU load to pass a " + "threshold.")); + + threshold.setSettingsKey("Threshold"); + threshold.setLabelText(Tr::tr("Threshold")); + threshold.setDefaultValue(1); + threshold.setRange(1, 128); + threshold.setEnabler(&testLoad); +} + +QStringList CTestTool::activeSettingsAsOptions() const +{ + QStringList options; + if (outputOnFail()) + options << "--output-on-failure"; + switch (outputMode()) { + case 1: options << "-V"; break; + case 2: options << "-VV"; break; + default: break; + } + if (repeat()) { + QString repeatOption; + switch (repetitionMode()) { + case 0: repeatOption = "until-fail"; break; + case 1: repeatOption = "until-pass"; break; + case 2: repeatOption = "after-timeout"; break; + default: break; + } + if (!repeatOption.isEmpty()) { + repeatOption.append(':'); + repeatOption.append(QString::number(repetitionCount())); + options << "--repeat" << repeatOption; + } + } + if (scheduleRandom()) + options << "--schedule-random"; + if (stopOnFailure()) + options << "--stop-on-failure"; + if (parallel()) { + options << "-j" << QString::number(jobs()); + if (testLoad()) + options << "--test-load" << QString::number(threshold()); + } + return options; +} + +Id CTestTool::buildSystemId() const +{ + return Id(CMakeProjectManager::Constants::CMAKE_PROJECT_ID); } ITestTreeItem *CTestTool::createItemFromTestCaseInfo(const ProjectExplorer::TestCaseInfo &tci) @@ -24,20 +159,25 @@ ITestTreeItem *CTestTool::createItemFromTestCaseInfo(const ProjectExplorer::Test return item; } -const char *CTestTool::name() const -{ - return "CTest"; -} - -QString CTestTool::displayName() const -{ - return Tr::tr("CTest"); -} - ITestTreeItem *CTestTool::createRootNode() { return new CTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } -} // namespace Internal -} // namespace Autotest +// CTestToolSettingsPage + +class CTestToolSettingsPage final : public Core::IOptionsPage +{ +public: + CTestToolSettingsPage() + { + setId(Id(Constants::SETTINGSPAGE_PREFIX).withSuffix("255.CTest")); + setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); + setDisplayName(Tr::tr("CTest")); + setSettingsProvider([] { return &theCTestTool(); }); + } +}; + +const CTestToolSettingsPage settingsPage; + +} // Autotest::Internal diff --git a/src/plugins/autotest/ctest/ctesttool.h b/src/plugins/autotest/ctest/ctesttool.h index e7a7f74218a..3d65712c0fc 100644 --- a/src/plugins/autotest/ctest/ctesttool.h +++ b/src/plugins/autotest/ctest/ctesttool.h @@ -1,31 +1,40 @@ -// Copyright (C) 2020 The Qt Company Ltd. +// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "../itestframework.h" -#include "ctestsettings.h" namespace Autotest::Internal { class CTestTool final : public Autotest::ITestTool { public: - CTestTool() : Autotest::ITestTool(false) {} + CTestTool(); Utils::Id buildSystemId() const final; ITestTreeItem *createItemFromTestCaseInfo(const ProjectExplorer::TestCaseInfo &tci) final; -protected: - const char *name() const final; - QString displayName() const final; ITestTreeItem *createRootNode() final; -private: - ITestSettings *testSettings() override { return &m_settings; } + QStringList activeSettingsAsOptions() const; - CTestSettings m_settings{settingsId()}; + Utils::IntegerAspect repetitionCount{this}; + Utils::SelectionAspect repetitionMode{this}; + Utils::SelectionAspect outputMode{this}; + Utils::BoolAspect outputOnFail{this}; + Utils::BoolAspect stopOnFailure{this}; + Utils::BoolAspect scheduleRandom{this}; + Utils::BoolAspect repeat{this}; + // FIXME.. this makes the outputreader fail to get all results correctly for visual display + Utils::BoolAspect parallel{this}; + Utils::IntegerAspect jobs{this}; + Utils::BoolAspect testLoad{this}; + Utils::IntegerAspect threshold{this}; }; +CTestTool &theCTestTool(); + } // Autotest::Internal + diff --git a/src/plugins/autotest/ctest/ctesttreeitem.cpp b/src/plugins/autotest/ctest/ctesttreeitem.cpp index ed5401cc13c..ad744bb5c87 100644 --- a/src/plugins/autotest/ctest/ctesttreeitem.cpp +++ b/src/plugins/autotest/ctest/ctesttreeitem.cpp @@ -4,10 +4,9 @@ #include "ctesttreeitem.h" #include "ctestconfiguration.h" -#include "ctestsettings.h" +#include "ctesttool.h" #include "../autotestplugin.h" -#include "../itestframework.h" #include "../testsettings.h" #include <projectexplorer/buildconfiguration.h> @@ -88,9 +87,8 @@ QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringLi return {}; const ProjectExplorer::BuildSystem *buildSystem = target->buildSystem(); - QStringList options{"--timeout", QString::number(TestSettings::instance()->timeout() / 1000)}; - auto ctestSettings = static_cast<CTestSettings *>(testBase()->testSettings()); - options << ctestSettings->activeSettingsAsOptions(); + QStringList options{"--timeout", QString::number(testSettings().timeout() / 1000)}; + options << theCTestTool().activeSettingsAsOptions(); CommandLine command = buildSystem->commandLineForTests(selected, options); if (command.executable().isEmpty()) return {}; @@ -108,6 +106,7 @@ QList<ITestConfiguration *> CTestTreeItem::testConfigurationsFor(const QStringLi env.set("QT_FORCE_STDERR_LOGGING", "1"); env.set("QT_LOGGING_TO_CONSOLE", "1"); } + env.setFallback("CLICOLOR_FORCE", "1"); config->setEnvironment(env); const ProjectExplorer::BuildConfiguration *buildConfig = target->activeBuildConfiguration(); if (QTC_GUARD(buildConfig)) diff --git a/src/plugins/autotest/gtest/gtestconfiguration.cpp b/src/plugins/autotest/gtest/gtestconfiguration.cpp index 8680da1fcaf..248bba0d058 100644 --- a/src/plugins/autotest/gtest/gtestconfiguration.cpp +++ b/src/plugins/autotest/gtest/gtestconfiguration.cpp @@ -3,10 +3,9 @@ #include "gtestconfiguration.h" +#include "gtestframework.h" #include "gtestoutputreader.h" -#include "gtestsettings.h" -#include "../itestframework.h" #include "../testsettings.h" #include <utils/algorithm.h> @@ -54,7 +53,7 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted) QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) const { QStringList arguments; - if (TestSettings::instance()->processArgs()) { + if (testSettings().processArgs()) { arguments << filterInterfering(runnable().command.arguments().split( ' ', Qt::SkipEmptyParts), omitted); } @@ -67,21 +66,19 @@ QStringList GTestConfiguration::argumentsForTestRunner(QStringList *omitted) con arguments << "--gtest_filter=\"" + testSets.join(':') + '"'; } - auto gSettings = static_cast<GTestSettings *>(framework()->testSettings()); - if (!gSettings) - return arguments; + GTestFramework &gSettings = theGTestFramework(); - if (gSettings->runDisabled.value()) + if (gSettings.runDisabled()) arguments << "--gtest_also_run_disabled_tests"; - if (gSettings->repeat.value()) - arguments << QString("--gtest_repeat=%1").arg(gSettings->iterations.value()); - if (gSettings->shuffle.value()) - arguments << "--gtest_shuffle" << QString("--gtest_random_seed=%1").arg(gSettings->seed.value()); - if (gSettings->throwOnFailure.value()) + if (gSettings.repeat()) + arguments << QString("--gtest_repeat=%1").arg(gSettings.iterations()); + if (gSettings.shuffle()) + arguments << "--gtest_shuffle" << QString("--gtest_random_seed=%1").arg(gSettings.seed()); + if (gSettings.throwOnFailure()) arguments << "--gtest_throw_on_failure"; if (isDebugRunMode()) { - if (gSettings->breakOnFailure.value()) + if (gSettings.breakOnFailure()) arguments << "--gtest_break_on_failure"; arguments << "--gtest_catch_exceptions=0"; } diff --git a/src/plugins/autotest/gtest/gtestconstants.h b/src/plugins/autotest/gtest/gtestconstants.h index 7d422e8df8b..378efd6d1e8 100644 --- a/src/plugins/autotest/gtest/gtestconstants.h +++ b/src/plugins/autotest/gtest/gtestconstants.h @@ -9,7 +9,7 @@ namespace Autotest { namespace GTest { namespace Constants { -const char FRAMEWORK_NAME[] = "GTest"; +const char FRAMEWORK_ID[] = "AutoTest.Framework.GTest"; const char FRAMEWORK_SETTINGS_CATEGORY[] = QT_TRANSLATE_NOOP("QtC::Autotest", "Google Test"); const unsigned FRAMEWORK_PRIORITY = 10; const char DEFAULT_FILTER[] = "*.*"; diff --git a/src/plugins/autotest/gtest/gtestframework.cpp b/src/plugins/autotest/gtest/gtestframework.cpp index af0a6eaa46c..8e54e1d7255 100644 --- a/src/plugins/autotest/gtest/gtestframework.cpp +++ b/src/plugins/autotest/gtest/gtestframework.cpp @@ -3,21 +3,135 @@ #include "gtestframework.h" -#include "../autotesttr.h" +#include "gtest_utils.h" #include "gtesttreeitem.h" #include "gtestparser.h" +#include "gtestconstants.h" +#include "../autotestconstants.h" +#include "../autotesttr.h" +#include "../testtreemodel.h" #include <QRegularExpression> -namespace Autotest { -namespace Internal { +#include <coreplugin/dialogs/ioptionspage.h> -static GTestSettings *g_settings; +#include <utils/layoutbuilder.h> + +using namespace Layouting; +using namespace Utils; + +namespace Autotest::Internal { + +GTestFramework &theGTestFramework() +{ + static GTestFramework framework; + return framework; +} GTestFramework::GTestFramework() - : ITestFramework(true) { - g_settings = &m_settings; + setActive(true); + setSettingsGroups("Autotest", "GTest"); + setId(GTest::Constants::FRAMEWORK_ID); + setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setPriority(GTest::Constants::FRAMEWORK_PRIORITY); + + setLayouter([this] { + return Row { Form { + runDisabled, br, + throwOnFailure, br, + breakOnFailure, br, + repeat, iterations, br, + shuffle, seed, br, + groupMode, br, + gtestFilter, br + }, st }; + }); + + iterations.setSettingsKey("Iterations"); + iterations.setDefaultValue(1); + iterations.setEnabled(false); + iterations.setLabelText(Tr::tr("Iterations:")); + iterations.setEnabler(&repeat); + + seed.setSettingsKey("Seed"); + seed.setSpecialValueText({}); + seed.setRange(0, 99999); + seed.setEnabled(false); + seed.setLabelText(Tr::tr("Seed:")); + seed.setToolTip(Tr::tr("A seed of 0 generates a seed based on the current timestamp.")); + seed.setEnabler(&shuffle); + + runDisabled.setSettingsKey("RunDisabled"); + runDisabled.setLabelText(Tr::tr("Run disabled tests")); + runDisabled.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + runDisabled.setToolTip(Tr::tr("Executes disabled tests when performing a test run.")); + + shuffle.setSettingsKey("Shuffle"); + shuffle.setLabelText(Tr::tr("Shuffle tests")); + shuffle.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + shuffle.setToolTip(Tr::tr("Shuffles tests automatically on every iteration by the given seed.")); + + repeat.setSettingsKey("Repeat"); + repeat.setLabelText(Tr::tr("Repeat tests")); + repeat.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to " + "avoid canceling the tests).")); + + throwOnFailure.setSettingsKey("ThrowOnFailure"); + throwOnFailure.setLabelText(Tr::tr("Throw on failure")); + throwOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + throwOnFailure.setToolTip(Tr::tr("Turns assertion failures into C++ exceptions.")); + + breakOnFailure.setSettingsKey("BreakOnFailure"); + breakOnFailure.setDefaultValue(true); + breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging")); + breakOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints.")); + + groupMode.setSettingsKey("GroupMode"); + groupMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + groupMode.setFromSettingsTransformation([this](const QVariant &savedValue) -> QVariant { + // avoid problems if user messes around with the settings file + bool ok = false; + const int tmp = savedValue.toInt(&ok); + return ok ? groupMode.indexForItemValue(tmp) : GTest::Constants::Directory; + }); + groupMode.setToSettingsTransformation([this](const QVariant &value) { + return groupMode.itemValueForIndex(value.toInt()); + }); + groupMode.addOption({Tr::tr("Directory"), {}, GTest::Constants::Directory}); + groupMode.addOption({Tr::tr("GTest Filter"), {}, GTest::Constants::GTestFilter}); + groupMode.setDefaultValue(GTest::Constants::Directory); + groupMode.setLabelText(Tr::tr("Group mode:")); + groupMode.setToolTip(Tr::tr("Select on what grouping the tests should be based.")); + + gtestFilter.setSettingsKey("GTestFilter"); + gtestFilter.setDisplayStyle(StringAspect::LineEditDisplay); + gtestFilter.setDefaultValue(GTest::Constants::DEFAULT_FILTER); + gtestFilter.setFromSettingsTransformation([](const QVariant &savedValue) -> QVariant { + // avoid problems if user messes around with the settings file + const QString tmp = savedValue.toString(); + if (GTestUtils::isValidGTestFilter(tmp)) + return tmp; + return GTest::Constants::DEFAULT_FILTER; + }); + gtestFilter.setEnabled(false); + gtestFilter.setLabelText(Tr::tr("Active filter:")); + gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\nSee Google Test " + "documentation for further information on GTest filters.")); + + gtestFilter.setValidationFunction([](FancyLineEdit *edit, QString * /*error*/) { + return edit && GTestUtils::isValidGTestFilter(edit->text()); + }); + + connect(&groupMode, &SelectionAspect::volatileValueChanged, >estFilter, [this] { + gtestFilter.setEnabled(groupMode.itemValueForIndex(groupMode.volatileValue()) + == GTest::Constants::GTestFilter); + }); + connect(this, &AspectContainer::applied, this, [] { + TestTreeModel::instance()->rebuild({GTest::Constants::FRAMEWORK_ID}); + }); } ITestParser *GTestFramework::createTestParser() @@ -30,24 +144,9 @@ ITestTreeItem *GTestFramework::createRootNode() return new GTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } -const char *GTestFramework::name() const -{ - return GTest::Constants::FRAMEWORK_NAME; -} - -QString GTestFramework:: displayName() const -{ - return Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY); -} - -unsigned GTestFramework::priority() const -{ - return GTest::Constants::FRAMEWORK_PRIORITY; -} - QString GTestFramework::currentGTestFilter() { - return g_settings->gtestFilter.value(); + return theGTestFramework().gtestFilter(); } QString GTestFramework::groupingToolTip() const @@ -56,9 +155,9 @@ QString GTestFramework::groupingToolTip() const "GTest filter.\nSee also Google Test settings."); } -GTest::Constants::GroupMode GTestFramework::groupMode() +GTest::Constants::GroupMode GTestFramework::staticGroupMode() { - return GTest::Constants::GroupMode(g_settings->groupMode.itemValue().toInt()); + return GTest::Constants::GroupMode(theGTestFramework().groupMode.itemValue().toInt()); } QStringList GTestFramework::testNameForSymbolName(const QString &symbolName) const @@ -71,5 +170,21 @@ QStringList GTestFramework::testNameForSymbolName(const QString &symbolName) con return { match.captured(2), match.captured(4) }; } -} // namespace Internal -} // namespace Autotest +// GTestSettingPage + +class GTestSettingsPage final : public Core::IOptionsPage +{ +public: + GTestSettingsPage() + { + setId(Id(Constants::SETTINGSPAGE_PREFIX).withSuffix(QString("%1.GTest") + .arg(GTest::Constants::FRAMEWORK_PRIORITY))); + setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); + setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setSettingsProvider([] { return &theGTestFramework(); }); + } +}; + +const GTestSettingsPage settingsPage; + +} // Autotest::Internal diff --git a/src/plugins/autotest/gtest/gtestframework.h b/src/plugins/autotest/gtest/gtestframework.h index 03c8a4fc0d9..ff9e2766b58 100644 --- a/src/plugins/autotest/gtest/gtestframework.h +++ b/src/plugins/autotest/gtest/gtestframework.h @@ -4,8 +4,8 @@ #pragma once #include "../itestframework.h" + #include "gtestconstants.h" -#include "gtestsettings.h" namespace Autotest::Internal { @@ -14,20 +14,26 @@ class GTestFramework : public ITestFramework public: GTestFramework(); - static GTest::Constants::GroupMode groupMode(); + Utils::IntegerAspect iterations{this}; + Utils::IntegerAspect seed{this}; + Utils::BoolAspect runDisabled{this}; + Utils::BoolAspect shuffle{this}; + Utils::BoolAspect repeat{this}; + Utils::BoolAspect throwOnFailure{this}; + Utils::BoolAspect breakOnFailure{this}; + Utils::SelectionAspect groupMode{this}; + Utils::StringAspect gtestFilter{this}; + + static GTest::Constants::GroupMode staticGroupMode(); static QString currentGTestFilter(); QStringList testNameForSymbolName(const QString &symbolName) const override; -private: - const char *name() const override; - QString displayName() const override; - unsigned priority() const override; + QString groupingToolTip() const override; - ITestSettings *testSettings() override { return &m_settings; } ITestParser *createTestParser() override; ITestTreeItem *createRootNode() override; - - GTestSettings m_settings{settingsId()}; }; +GTestFramework &theGTestFramework(); + } // Autotest::Internal diff --git a/src/plugins/autotest/gtest/gtestparser.cpp b/src/plugins/autotest/gtest/gtestparser.cpp index bb2285bd43a..8fc292a9224 100644 --- a/src/plugins/autotest/gtest/gtestparser.cpp +++ b/src/plugins/autotest/gtest/gtestparser.cpp @@ -86,7 +86,6 @@ bool GTestParser::processDocument(QPromise<TestParseResultPtr> &promise, } const FilePath filePath = doc->filePath(); - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName); document->check(); CPlusPlus::AST *ast = document->translationUnit()->ast(); @@ -95,7 +94,8 @@ bool GTestParser::processDocument(QPromise<TestParseResultPtr> &promise, const QMap<GTestCaseSpec, GTestCodeLocationList> result = visitor.gtestFunctions(); FilePath proFile; - const QList<CppEditor::ProjectPart::ConstPtr> &ppList = modelManager->projectPart(filePath); + const QList<CppEditor::ProjectPart::ConstPtr> &ppList = + CppEditor::CppModelManager::projectPart(filePath); if (!ppList.isEmpty()) proFile = FilePath::fromString(ppList.first()->projectFile); else diff --git a/src/plugins/autotest/gtest/gtestresult.cpp b/src/plugins/autotest/gtest/gtestresult.cpp index f34b8f069d7..070aa837d38 100644 --- a/src/plugins/autotest/gtest/gtestresult.cpp +++ b/src/plugins/autotest/gtest/gtestresult.cpp @@ -84,8 +84,7 @@ static ResultHooks::FindTestItemHook findTestItemHook(const FilePath &projectFil const QString &testCaseName) { return [=](const TestResult &result) -> ITestTreeItem * { - const Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix(GTest::Constants::FRAMEWORK_NAME); - ITestFramework *framework = TestFrameworkManager::frameworkForId(id); + ITestFramework *framework = TestFrameworkManager::frameworkForId(GTest::Constants::FRAMEWORK_ID); QTC_ASSERT(framework, return nullptr); const TestTreeItem *rootNode = framework->rootNode(); if (!rootNode) diff --git a/src/plugins/autotest/gtest/gtestsettings.cpp b/src/plugins/autotest/gtest/gtestsettings.cpp deleted file mode 100644 index 7aa370366f3..00000000000 --- a/src/plugins/autotest/gtest/gtestsettings.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "gtestsettings.h" - -#include "gtest_utils.h" -#include "gtestconstants.h" -#include "../autotestconstants.h" -#include "../autotesttr.h" -#include "../testtreemodel.h" - -#include <utils/layoutbuilder.h> - -using namespace Layouting; -using namespace Utils; - -namespace Autotest::Internal { - -GTestSettings::GTestSettings(Id settingsId) -{ - setSettingsGroups("Autotest", "GTest"); - setId(settingsId); - setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); - setDisplayName(Tr::tr(GTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); - - setLayouter([this] { - return Row { Form { - runDisabled, br, - throwOnFailure, br, - breakOnFailure, br, - repeat, iterations, br, - shuffle, seed, br, - groupMode, br, - gtestFilter, br - }, st }; - }); - - iterations.setSettingsKey("Iterations"); - iterations.setDefaultValue(1); - iterations.setEnabled(false); - iterations.setLabelText(Tr::tr("Iterations:")); - iterations.setEnabler(&repeat); - - seed.setSettingsKey("Seed"); - seed.setSpecialValueText({}); - seed.setRange(0, 99999); - seed.setEnabled(false); - seed.setLabelText(Tr::tr("Seed:")); - seed.setToolTip(Tr::tr("A seed of 0 generates a seed based on the current timestamp.")); - seed.setEnabler(&shuffle); - - runDisabled.setSettingsKey("RunDisabled"); - runDisabled.setLabelText(Tr::tr("Run disabled tests")); - runDisabled.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - runDisabled.setToolTip(Tr::tr("Executes disabled tests when performing a test run.")); - - shuffle.setSettingsKey("Shuffle"); - shuffle.setLabelText(Tr::tr("Shuffle tests")); - shuffle.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - shuffle.setToolTip(Tr::tr("Shuffles tests automatically on every iteration by the given seed.")); - - repeat.setSettingsKey("Repeat"); - repeat.setLabelText(Tr::tr("Repeat tests")); - repeat.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to " - "avoid canceling the tests).")); - - throwOnFailure.setSettingsKey("ThrowOnFailure"); - throwOnFailure.setLabelText(Tr::tr("Throw on failure")); - throwOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - throwOnFailure.setToolTip(Tr::tr("Turns assertion failures into C++ exceptions.")); - - breakOnFailure.setSettingsKey("BreakOnFailure"); - breakOnFailure.setDefaultValue(true); - breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging")); - breakOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints.")); - - groupMode.setSettingsKey("GroupMode"); - groupMode.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - groupMode.setFromSettingsTransformation([this](const QVariant &savedValue) -> QVariant { - // avoid problems if user messes around with the settings file - bool ok = false; - const int tmp = savedValue.toInt(&ok); - return ok ? groupMode.indexForItemValue(tmp) : GTest::Constants::Directory; - }); - groupMode.setToSettingsTransformation([this](const QVariant &value) { - return groupMode.itemValueForIndex(value.toInt()); - }); - groupMode.addOption({Tr::tr("Directory"), {}, GTest::Constants::Directory}); - groupMode.addOption({Tr::tr("GTest Filter"), {}, GTest::Constants::GTestFilter}); - groupMode.setDefaultValue(GTest::Constants::Directory); - groupMode.setLabelText(Tr::tr("Group mode:")); - groupMode.setToolTip(Tr::tr("Select on what grouping the tests should be based.")); - - gtestFilter.setSettingsKey("GTestFilter"); - gtestFilter.setDisplayStyle(StringAspect::LineEditDisplay); - gtestFilter.setDefaultValue(GTest::Constants::DEFAULT_FILTER); - gtestFilter.setFromSettingsTransformation([](const QVariant &savedValue) -> QVariant { - // avoid problems if user messes around with the settings file - const QString tmp = savedValue.toString(); - if (GTestUtils::isValidGTestFilter(tmp)) - return tmp; - return GTest::Constants::DEFAULT_FILTER; - }); - gtestFilter.setEnabled(false); - gtestFilter.setLabelText(Tr::tr("Active filter:")); - gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\nSee Google Test " - "documentation for further information on GTest filters.")); - - gtestFilter.setValidationFunction([](FancyLineEdit *edit, QString * /*error*/) { - return edit && GTestUtils::isValidGTestFilter(edit->text()); - }); - - QObject::connect(&groupMode, &SelectionAspect::volatileValueChanged, - >estFilter, [this](int val) { - gtestFilter.setEnabled(groupMode.itemValueForIndex(val) == GTest::Constants::GTestFilter); - }); - - QObject::connect(this, &AspectContainer::applied, this, [] { - Id id = Id(Constants::FRAMEWORK_PREFIX).withSuffix(GTest::Constants::FRAMEWORK_NAME); - TestTreeModel::instance()->rebuild({id}); - }); -} - -} // Autotest::Internal diff --git a/src/plugins/autotest/gtest/gtestsettings.h b/src/plugins/autotest/gtest/gtestsettings.h deleted file mode 100644 index ea0ab565e6c..00000000000 --- a/src/plugins/autotest/gtest/gtestsettings.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Autotest::Internal { - -class GTestSettings : public Core::PagedSettings -{ -public: - explicit GTestSettings(Utils::Id settingsId); - - Utils::IntegerAspect iterations{this}; - Utils::IntegerAspect seed{this}; - Utils::BoolAspect runDisabled{this}; - Utils::BoolAspect shuffle{this}; - Utils::BoolAspect repeat{this}; - Utils::BoolAspect throwOnFailure{this}; - Utils::BoolAspect breakOnFailure{this}; - Utils::SelectionAspect groupMode{this}; - Utils::StringAspect gtestFilter{this}; -}; - -} // Autotest::Internal diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp index f91b68f0e38..8ece0e618ab 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.cpp +++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp @@ -113,7 +113,7 @@ QVariant GTestTreeItem::data(int column, int role) const } case Qt::DecorationRole: if (type() == GroupNode - && GTestFramework::groupMode() == GTest::Constants::GTestFilter) { + && GTestFramework::staticGroupMode() == GTest::Constants::GTestFilter) { static const QIcon filterIcon = Icon({{":/utils/images/filtericon.png", Theme::PanelTextColorMid}}).icon(); return filterIcon; @@ -121,7 +121,7 @@ QVariant GTestTreeItem::data(int column, int role) const break; case Qt::ToolTipRole: if (type() == GroupNode - && GTestFramework::groupMode() == GTest::Constants::GTestFilter) { + && GTestFramework::staticGroupMode() == GTest::Constants::GTestFilter) { const auto tpl = QString("<p>%1</p><p>%2</p>").arg(filePath().toString()); return tpl.arg(Tr::tr("Change GTest filter in use inside the settings.")); } @@ -361,7 +361,7 @@ TestTreeItem *GTestTreeItem::find(const TestParseResult *result) switch (type()) { case Root: if (result->framework->grouping()) { - if (GTestFramework::groupMode() == GTest::Constants::Directory) { + if (GTestFramework::staticGroupMode() == GTest::Constants::Directory) { const FilePath base = parseResult->fileName.absolutePath(); for (int row = 0; row < childCount(); ++row) { GTestTreeItem *group = static_cast<GTestTreeItem *>(childAt(row)); @@ -445,7 +445,7 @@ bool GTestTreeItem::modify(const TestParseResult *result) TestTreeItem *GTestTreeItem::createParentGroupNode() const { - if (GTestFramework::groupMode() == GTest::Constants::Directory) { + if (GTestFramework::staticGroupMode() == GTest::Constants::Directory) { const FilePath &absPath = filePath().absolutePath(); return new GTestTreeItem(framework(), absPath.baseName(), absPath, TestTreeItem::GroupNode); } else { // GTestFilter @@ -504,14 +504,14 @@ QString GTestTreeItem::nameSuffix() const QSet<QString> internalTargets(const TestTreeItem &item) { QSet<QString> result; - const auto cppMM = CppEditor::CppModelManager::instance(); - const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject()); + const auto projectInfo = CppEditor::CppModelManager::projectInfo( + ProjectExplorer::ProjectManager::startupProject()); if (!projectInfo) return {}; const FilePath filePath = item.filePath(); const QVector<CppEditor::ProjectPart::ConstPtr> projectParts = projectInfo->projectParts(); if (projectParts.isEmpty()) - return cppMM->dependingInternalTargets(item.filePath()); + return CppEditor::CppModelManager::dependingInternalTargets(item.filePath()); for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectParts) { if (FilePath::fromString(projectPart->projectFile) == item.proFile() && Utils::anyOf(projectPart->files, [&filePath](const CppEditor::ProjectFile &pf) { @@ -519,7 +519,7 @@ QSet<QString> internalTargets(const TestTreeItem &item) })) { result.insert(projectPart->buildSystemTarget); if (projectPart->buildTargetType != ProjectExplorer::BuildTargetType::Executable) - result.unite(cppMM->dependingInternalTargets(filePath)); + result.unite(CppEditor::CppModelManager::dependingInternalTargets(filePath)); } } return result; @@ -531,7 +531,7 @@ bool GTestTreeItem::isGroupNodeFor(const TestTreeItem *other) const if (type() != TestTreeItem::GroupNode) return false; - if (GTestFramework::groupMode() == GTest::Constants::Directory) { + if (GTestFramework::staticGroupMode() == GTest::Constants::Directory) { return other->filePath().absolutePath() == filePath(); } else { // GTestFilter QString fullName; @@ -566,7 +566,7 @@ TestTreeItem *GTestTreeItem::applyFilters() if (type() != TestSuite) return nullptr; - if (GTestFramework::groupMode() != GTest::Constants::GTestFilter) + if (GTestFramework::staticGroupMode() != GTest::Constants::GTestFilter) return nullptr; const QString gtestFilter = GTestFramework::currentGTestFilter(); diff --git a/src/plugins/autotest/gtest/gtestvisitors.cpp b/src/plugins/autotest/gtest/gtestvisitors.cpp index ea7f89863b3..9e9315a518d 100644 --- a/src/plugins/autotest/gtest/gtestvisitors.cpp +++ b/src/plugins/autotest/gtest/gtestvisitors.cpp @@ -91,7 +91,7 @@ bool GTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast) int line = 0; int column = 0; unsigned token = id->firstToken(); - m_document->translationUnit()->getTokenStartPosition(token, &line, &column); + m_document->translationUnit()->getTokenPosition(token, &line, &column); GTestCodeLocationAndType locationAndType; locationAndType.m_name = testCaseName; diff --git a/src/plugins/autotest/itestframework.cpp b/src/plugins/autotest/itestframework.cpp index 0f1919b2af8..4dd176b169c 100644 --- a/src/plugins/autotest/itestframework.cpp +++ b/src/plugins/autotest/itestframework.cpp @@ -12,22 +12,9 @@ using namespace Utils; namespace Autotest { -ITestBase::ITestBase(bool activeByDefault, const ITestBase::TestBaseType type) - : m_active(activeByDefault) - , m_type(type) +ITestBase::ITestBase() {} -Id ITestBase::settingsId() const -{ - return Id(Constants::SETTINGSPAGE_PREFIX) - .withSuffix(QString("%1.%2").arg(priority()).arg(QLatin1String(name()))); -} - -Id ITestBase::id() const -{ - return Id(Constants::FRAMEWORK_PREFIX).withSuffix(name()); -} - void ITestBase::resetRootNode() { if (!m_rootNode) @@ -38,10 +25,11 @@ void ITestBase::resetRootNode() m_rootNode = nullptr; } - -ITestFramework::ITestFramework(bool activeByDefault) - : ITestBase(activeByDefault, ITestBase::Framework) -{} +ITestFramework::ITestFramework() +{ + setType(ITestBase::Framework); + setAutoApply(false); +} ITestFramework::~ITestFramework() { @@ -68,9 +56,11 @@ QStringList ITestFramework::testNameForSymbolName(const QString &) const return {}; } -ITestTool::ITestTool(bool activeByDefault) - : ITestBase(activeByDefault, ITestBase::Tool) -{} +ITestTool::ITestTool() +{ + setType(ITestBase::Tool); + setPriority(255); +} ITestTreeItem *ITestTool::rootNode() { diff --git a/src/plugins/autotest/itestframework.h b/src/plugins/autotest/itestframework.h index d7a4d7927da..ac7b85eda42 100644 --- a/src/plugins/autotest/itestframework.h +++ b/src/plugins/autotest/itestframework.h @@ -3,6 +3,7 @@ #pragma once +#include <utils/aspects.h> #include <utils/id.h> namespace ProjectExplorer { struct TestCaseInfo; } @@ -12,12 +13,11 @@ namespace Autotest { class ITestFramework; class ITestParser; -using ITestSettings = Utils::AspectContainer; class ITestTool; class ITestTreeItem; class TestTreeItem; -class ITestBase +class ITestBase : public Utils::AspectContainer { public: enum TestBaseType @@ -27,18 +27,13 @@ public: Tool = 0x2 }; - explicit ITestBase(bool activeByDefault, const TestBaseType type); + ITestBase(); virtual ~ITestBase() = default; - virtual const char *name() const = 0; - virtual QString displayName() const = 0; - virtual unsigned priority() const = 0; // should this be modifyable? + QString displayName() const { return m_displayName; } TestBaseType type() const { return m_type; } - - virtual ITestSettings *testSettings() { return nullptr; } - - Utils::Id settingsId() const; - Utils::Id id() const; + Utils::Id id() const { return m_id; } + int priority() const { return m_priority; } bool active() const { return m_active; } void setActive(bool active) { m_active = active; } @@ -49,12 +44,20 @@ public: virtual ITestTool *asTestTool() { return nullptr; } protected: + void setPriority(int priority) { m_priority = priority; } + void setDisplayName(const QString &displayName) { m_displayName = displayName; } + void setType(const TestBaseType type) { m_type = type; } + void setId(const Utils::Id id) { m_id = id; } + virtual ITestTreeItem *createRootNode() = 0; private: ITestTreeItem *m_rootNode = nullptr; bool m_active = false; TestBaseType m_type = None; + int m_priority = 0; + QString m_displayName; + Utils::Id m_id; friend class ITestFramework; friend class ITestTool; @@ -63,7 +66,7 @@ private: class ITestFramework : public ITestBase { public: - explicit ITestFramework(bool activeByDefault); + ITestFramework(); ~ITestFramework() override; TestTreeItem *rootNode(); @@ -91,7 +94,7 @@ using TestFrameworks = QList<ITestFramework *>; class ITestTool : public ITestBase { public: - explicit ITestTool(bool activeByDefault); + ITestTool(); ITestTreeItem *rootNode(); @@ -100,9 +103,6 @@ public: virtual ITestTreeItem *createItemFromTestCaseInfo(const ProjectExplorer::TestCaseInfo &tci) = 0; ITestTool *asTestTool() final { return this; } - -private: - unsigned priority() const final { return 255; } }; using TestTools = QList<ITestTool *>; diff --git a/src/plugins/autotest/itestparser.cpp b/src/plugins/autotest/itestparser.cpp index 79fde2a0bb1..9438fe4caf1 100644 --- a/src/plugins/autotest/itestparser.cpp +++ b/src/plugins/autotest/itestparser.cpp @@ -5,6 +5,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <cppeditor/cppmodelmanager.h> +#include <projectexplorer/projectmanager.h> #include <utils/textfileformat.h> #include <utils/algorithm.h> @@ -28,14 +29,14 @@ void CppParser::init(const QSet<FilePath> &filesToParse, bool fullParse) { Q_UNUSED(filesToParse) Q_UNUSED(fullParse) - m_cppSnapshot = CppEditor::CppModelManager::instance()->snapshot(); - m_workingCopy = CppEditor::CppModelManager::instance()->workingCopy(); + m_cppSnapshot = CppEditor::CppModelManager::snapshot(); + m_workingCopy = CppEditor::CppModelManager::workingCopy(); } bool CppParser::selectedForBuilding(const FilePath &fileName) { QList<CppEditor::ProjectPart::ConstPtr> projParts = - CppEditor::CppModelManager::instance()->projectPart(fileName); + CppEditor::CppModelManager::projectPart(fileName); return !projParts.isEmpty() && projParts.at(0)->selectedForBuilding; } @@ -62,8 +63,8 @@ bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, const QString &cacheString, const std::function<bool(const FilePath &)> &checker) { - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); - const QList<CppEditor::ProjectPart::ConstPtr> projectParts = modelManager->projectPart(filePath); + const QList<CppEditor::ProjectPart::ConstPtr> projectParts + = CppEditor::CppModelManager::projectPart(filePath); if (projectParts.isEmpty()) return false; const QStringList precompiledHeaders = projectParts.first()->precompiledHeaders; @@ -105,6 +106,31 @@ bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, }); } +std::optional<QSet<FilePath>> CppParser::filesContainingMacro(const QByteArray ¯oName) +{ + // safety net to avoid adding some option + static const bool noPrefilter = qtcEnvironmentVariableIsSet("QTC_AUTOTEST_DISABLE_PREFILTER"); + if (noPrefilter) + return std::nullopt; + + QSet<FilePath> result; + CppEditor::ProjectInfo::ConstPtr infos = CppEditor::CppModelManager::projectInfo( + ProjectExplorer::ProjectManager::startupProject()); + if (!infos) + return std::nullopt; + + const auto projectParts = infos->projectParts(); + for (const auto &pp : projectParts) { + if (!pp->selectedForBuilding) + continue; + + const ProjectExplorer::Macros macros = pp->projectMacros; + if (Utils::anyOf(pp->projectMacros, Utils::equal(&ProjectExplorer::Macro::key, macroName))) + result.unite(Utils::transform<QSet>(pp->files, &CppEditor::ProjectFile::path)); + } + return std::make_optional(result); +} + void CppParser::release() { m_cppSnapshot = CPlusPlus::Snapshot(); diff --git a/src/plugins/autotest/itestparser.h b/src/plugins/autotest/itestparser.h index 93233bd7a97..6e3305dd6ea 100644 --- a/src/plugins/autotest/itestparser.h +++ b/src/plugins/autotest/itestparser.h @@ -9,6 +9,8 @@ #include <cppeditor/cppworkingcopy.h> #include <qmljs/qmljsdocument.h> +#include <optional> + QT_BEGIN_NAMESPACE template <class T> class QPromise; @@ -76,6 +78,9 @@ public: static bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot, const Utils::FilePath &filePath, const QRegularExpression &headerFileRegex); + // returns all files of the startup project whose ProjectPart has the given \a macroName + // set as a project define + static std::optional<QSet<Utils::FilePath>> filesContainingMacro(const QByteArray ¯oName); protected: CPlusPlus::Snapshot m_cppSnapshot; diff --git a/src/plugins/autotest/loadprojectscenario.cpp b/src/plugins/autotest/loadprojectscenario.cpp index fa0530cbc94..9dc136f4e49 100644 --- a/src/plugins/autotest/loadprojectscenario.cpp +++ b/src/plugins/autotest/loadprojectscenario.cpp @@ -6,7 +6,7 @@ #include <cppeditor/cpptoolstestcase.h> #include <cppeditor/projectinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> diff --git a/src/plugins/autotest/projectsettingswidget.cpp b/src/plugins/autotest/projectsettingswidget.cpp index da988928dc4..53870990b5d 100644 --- a/src/plugins/autotest/projectsettingswidget.cpp +++ b/src/plugins/autotest/projectsettingswidget.cpp @@ -10,11 +10,10 @@ #include "testtreemodel.h" #include <utils/algorithm.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> -#include <QBoxLayout> #include <QComboBox> -#include <QLabel> #include <QTreeWidget> namespace Autotest { @@ -25,46 +24,45 @@ enum ItemDataRole { BaseTypeRole }; -static QSpacerItem *createSpacer(QSizePolicy::Policy horizontal, QSizePolicy::Policy vertical) -{ - return new QSpacerItem(20, 10, horizontal, vertical); -} - ProjectTestSettingsWidget::ProjectTestSettingsWidget(ProjectExplorer::Project *project, QWidget *parent) : ProjectExplorer::ProjectSettingsWidget(parent) , m_projectSettings(AutotestPlugin::projectSettings(project)) { setGlobalSettingsId(Constants::AUTOTEST_SETTINGS_ID); - auto verticalLayout = new QVBoxLayout(this); - verticalLayout->setContentsMargins(0, 0, 0, 0); - auto generalWidget = new QWidget; - auto groupBoxLayout = new QVBoxLayout; - groupBoxLayout->setContentsMargins(0, 0, 0, 0); + QWidget *generalWidget; m_activeFrameworks = new QTreeWidget; m_activeFrameworks->setHeaderHidden(true); m_activeFrameworks->setRootIsDecorated(false); - groupBoxLayout->addWidget(new QLabel(Tr::tr("Active frameworks:"))); - groupBoxLayout->addWidget(m_activeFrameworks); - auto horizontalLayout = new QHBoxLayout; - horizontalLayout->addWidget(new QLabel(Tr::tr("Automatically run tests after build"))); m_runAfterBuild = new QComboBox; m_runAfterBuild->addItem(Tr::tr("None")); m_runAfterBuild->addItem(Tr::tr("All")); m_runAfterBuild->addItem(Tr::tr("Selected")); m_runAfterBuild->setCurrentIndex(int(m_projectSettings->runAfterBuild())); - horizontalLayout->addWidget(m_runAfterBuild); - horizontalLayout->addItem(createSpacer(QSizePolicy::Expanding, QSizePolicy::Minimum)); - groupBoxLayout->addLayout(horizontalLayout); - generalWidget->setLayout(groupBoxLayout); - horizontalLayout = new QHBoxLayout; - verticalLayout->addItem(createSpacer(QSizePolicy::Minimum, QSizePolicy::Fixed)); - horizontalLayout->addWidget(generalWidget); - horizontalLayout->addItem(createSpacer(QSizePolicy::Expanding, QSizePolicy::Minimum)); - verticalLayout->addLayout(horizontalLayout); - verticalLayout->addItem(createSpacer(QSizePolicy::Minimum, QSizePolicy::Expanding)); + using namespace Layouting; + Column { + Widget { + bindTo(&generalWidget), + Column { + Row { + Group { + title(Tr::tr("Active frameworks:")), + Column { m_activeFrameworks }, + }, + st, + }, + Row { + Tr::tr("Automatically run tests after build"), + m_runAfterBuild, + st, + }, + noMargin(), + }, + }, + noMargin(), + }.attachTo(this); generalWidget->setDisabled(m_projectSettings->useGlobalSettings()); diff --git a/src/plugins/autotest/qtest/qttest_utils.cpp b/src/plugins/autotest/qtest/qttest_utils.cpp index 9bedaa64c37..3717a563862 100644 --- a/src/plugins/autotest/qtest/qttest_utils.cpp +++ b/src/plugins/autotest/qtest/qttest_utils.cpp @@ -99,13 +99,13 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted, if (knownAllowedOptionsWithParameter.contains(currentOpt)) { allowed.append(currentOpt); ++it; - QTC_ASSERT(it != end, return QStringList()); + QTC_ASSERT(it != end, return {}); allowed.append(*it); } else if (knownInterferingOptionWithParameter.contains(currentOpt)) { if (omitted) { omitted->append(currentOpt); ++it; - QTC_ASSERT(it != end, return QStringList()); + QTC_ASSERT(it != end, return {}); omitted->append(*it); } } else if (knownInterferingSingleOptions.contains(currentOpt)) { @@ -115,7 +115,7 @@ QStringList filterInterfering(const QStringList &provided, QStringList *omitted, if (knownAllowedQuickOptionsWithParameter.contains(currentOpt)) { allowed.append(currentOpt); ++it; - QTC_ASSERT(it != end, return QStringList()); + QTC_ASSERT(it != end, return {}); allowed.append(*it); } else if (knownAllowedSingleQuickOptions.contains(currentOpt)) { allowed.append(currentOpt); @@ -137,7 +137,7 @@ Environment prepareBasicEnvironment(const Environment &env) result.set("QT_FORCE_STDERR_LOGGING", "1"); result.set("QT_LOGGING_TO_CONSOLE", "1"); } - const int timeout = TestSettings::instance()->timeout(); + const int timeout = testSettings().timeout(); if (timeout > 5 * 60 * 1000) // Qt5.5 introduced hard limit, Qt5.6.1 added env var to raise this result.set("QTEST_FUNCTION_TIMEOUT", QString::number(timeout)); return result; diff --git a/src/plugins/autotest/qtest/qttestconfiguration.cpp b/src/plugins/autotest/qtest/qttestconfiguration.cpp index 5dcf59a48e1..f69a76fe6ac 100644 --- a/src/plugins/autotest/qtest/qttestconfiguration.cpp +++ b/src/plugins/autotest/qtest/qttestconfiguration.cpp @@ -3,11 +3,10 @@ #include "qttestconfiguration.h" +#include "qttestframework.h" #include "qttestoutputreader.h" -#include "qttestsettings.h" #include "qttest_utils.h" -#include "../itestframework.h" #include "../testsettings.h" #include <utils/algorithm.h> @@ -29,8 +28,7 @@ static QStringList quoteIfNeeded(const QStringList &testCases, bool debugMode) TestOutputReader *QtTestConfiguration::createOutputReader(Process *app) const { - auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings()); - const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value() + const QtTestOutputReader::OutputMode mode = theQtTestFramework().useXMLOutput() ? QtTestOutputReader::XML : QtTestOutputReader::PlainText; return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QtTest); @@ -39,34 +37,32 @@ TestOutputReader *QtTestConfiguration::createOutputReader(Process *app) const QStringList QtTestConfiguration::argumentsForTestRunner(QStringList *omitted) const { QStringList arguments; - if (TestSettings::instance()->processArgs()) { + if (testSettings().processArgs()) { arguments.append(QTestUtils::filterInterfering( runnable().command.arguments().split(' ', Qt::SkipEmptyParts), omitted, false)); } - auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings()); - if (!qtSettings) - return arguments; - if (qtSettings->useXMLOutput.value()) + QtTestFramework &qtSettings = theQtTestFramework(); + if (qtSettings.useXMLOutput()) arguments << "-xml"; if (!testCases().isEmpty()) arguments << quoteIfNeeded(testCases(), isDebugRunMode()); - const QString &metricsOption = QtTestSettings::metricsTypeToOption(MetricsType(qtSettings->metrics.value())); + const QString metricsOption = QtTestFramework::metricsTypeToOption(MetricsType(qtSettings.metrics())); if (!metricsOption.isEmpty()) arguments << metricsOption; - if (qtSettings->verboseBench.value()) + if (qtSettings.verboseBench()) arguments << "-vb"; - if (qtSettings->logSignalsSlots.value()) + if (qtSettings.logSignalsSlots()) arguments << "-vs"; - if (isDebugRunMode() && qtSettings->noCrashHandler.value()) + if (isDebugRunMode() && qtSettings.noCrashHandler()) arguments << "-nocrashhandler"; - if (qtSettings->limitWarnings.value() && qtSettings->maxWarnings.value() != 2000) - arguments << "-maxwarnings" << QString::number(qtSettings->maxWarnings.value()); + if (qtSettings.limitWarnings() && qtSettings.maxWarnings() != 2000) + arguments << "-maxwarnings" << QString::number(qtSettings.maxWarnings()); return arguments; } diff --git a/src/plugins/autotest/qtest/qttestconstants.h b/src/plugins/autotest/qtest/qttestconstants.h index 10042a1c961..d3e7a59334b 100644 --- a/src/plugins/autotest/qtest/qttestconstants.h +++ b/src/plugins/autotest/qtest/qttestconstants.h @@ -9,7 +9,7 @@ namespace Autotest { namespace QtTest { namespace Constants { -const char FRAMEWORK_NAME[] = "QtTest"; +const char FRAMEWORK_ID[] = "AutoTest.Framework.QtTest"; const char FRAMEWORK_SETTINGS_CATEGORY[] = QT_TRANSLATE_NOOP("QtC::Autotest", "Qt Test"); const unsigned FRAMEWORK_PRIORITY = 1; diff --git a/src/plugins/autotest/qtest/qttestframework.cpp b/src/plugins/autotest/qtest/qttestframework.cpp index d13470c8569..a406506a675 100644 --- a/src/plugins/autotest/qtest/qttestframework.cpp +++ b/src/plugins/autotest/qtest/qttestframework.cpp @@ -3,13 +3,121 @@ #include "qttestframework.h" +#include "../autotestconstants.h" +#include "../autotesttr.h" #include "qttestconstants.h" #include "qttestparser.h" #include "qttesttreeitem.h" -#include "../autotesttr.h" -namespace Autotest { -namespace Internal { +#include <coreplugin/dialogs/ioptionspage.h> + +#include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> + +using namespace Layouting; +using namespace Utils; + +namespace Autotest::Internal { + +QtTestFramework &theQtTestFramework() +{ + static QtTestFramework framework; + return framework; +} + +QtTestFramework::QtTestFramework() +{ + setActive(true); + setId(QtTest::Constants::FRAMEWORK_ID); + setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setPriority(QtTest::Constants::FRAMEWORK_PRIORITY); + setSettingsGroups("Autotest", "QtTest"); + + setLayouter([this] { + return Row { Form { + noCrashHandler, br, + useXMLOutput, br, + verboseBench, br, + logSignalsSlots, br, + limitWarnings, maxWarnings, br, + Group { + title(Tr::tr("Benchmark Metrics")), + Column { metrics } + }, br, + quickCheckForDerivedTests, br + }, st }; + }); + + metrics.setSettingsKey("Metrics"); + metrics.setDefaultValue(Walltime); + metrics.addOption(Tr::tr("Walltime"), Tr::tr("Uses walltime metrics for executing benchmarks (default).")); + metrics.addOption(Tr::tr("Tick counter"), Tr::tr("Uses tick counter when executing benchmarks.")); + metrics.addOption(Tr::tr("Event counter"), Tr::tr("Uses event counter when executing benchmarks.")); + metrics.addOption({ + Tr::tr("Callgrind"), + Tr::tr("Uses Valgrind Callgrind when executing benchmarks (it must be installed)."), + HostOsInfo::isAnyUnixHost() // valgrind available on UNIX + }); + metrics.addOption({ + Tr::tr("Perf"), + Tr::tr("Uses Perf when executing benchmarks (it must be installed)."), + HostOsInfo::isLinuxHost() // according to docs perf Linux only + }); + + noCrashHandler.setSettingsKey("NoCrashhandlerOnDebug"); + noCrashHandler.setDefaultValue(true); + noCrashHandler.setLabelText(Tr::tr("Disable crash handler while debugging")); + noCrashHandler.setToolTip(Tr::tr("Enables interrupting tests on assertions.")); + + useXMLOutput.setSettingsKey("UseXMLOutput"); + useXMLOutput.setDefaultValue(true); + useXMLOutput.setLabelText(Tr::tr("Use XML output")); + useXMLOutput.setToolTip(Tr::tr("XML output is recommended, because it avoids parsing issues, " + "while plain text is more human readable.\n\nWarning: " + "Plain text misses some information, such as duration.")); + + verboseBench.setSettingsKey("VerboseBench"); + verboseBench.setLabelText(Tr::tr("Verbose benchmarks")); + + logSignalsSlots.setSettingsKey("LogSignalsSlots"); + logSignalsSlots.setLabelText(Tr::tr("Log signals and slots")); + logSignalsSlots.setToolTip(Tr::tr("Log every signal emission and resulting slot invocations.")); + + limitWarnings.setSettingsKey("LimitWarnings"); + limitWarnings.setLabelText(Tr::tr("Limit warnings")); + limitWarnings.setToolTip(Tr::tr("Set the maximum number of warnings. 0 means that the number " + "is not limited.")); + + maxWarnings.setSettingsKey("MaxWarnings"); + maxWarnings.setRange(0, 10000); + maxWarnings.setDefaultValue(2000); + maxWarnings.setSpecialValueText(Tr::tr("Unlimited")); + maxWarnings.setEnabler(&limitWarnings); + + quickCheckForDerivedTests.setSettingsKey("QuickCheckForDerivedTests"); + quickCheckForDerivedTests.setDefaultValue(false); + quickCheckForDerivedTests.setLabelText(Tr::tr("Check for derived Qt Quick tests")); + quickCheckForDerivedTests.setToolTip( + Tr::tr("Search for Qt Quick tests that are derived from TestCase.\nWarning: Enabling this " + "feature significantly increases scan time.")); +} + +QString QtTestFramework::metricsTypeToOption(const MetricsType type) +{ + switch (type) { + case MetricsType::Walltime: + return {}; + case MetricsType::TickCounter: + return QString("-tickcounter"); + case MetricsType::EventCounter: + return QString("-eventcounter"); + case MetricsType::CallGrind: + return QString("-callgrind"); + case MetricsType::Perf: + return QString("-perf"); + } + return {}; +} ITestParser *QtTestFramework::createTestParser() { @@ -21,21 +129,6 @@ ITestTreeItem *QtTestFramework::createRootNode() return new QtTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } -const char *QtTestFramework::name() const -{ - return QtTest::Constants::FRAMEWORK_NAME; -} - -QString QtTestFramework::displayName() const -{ - return Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY); -} - -unsigned QtTestFramework::priority() const -{ - return QtTest::Constants::FRAMEWORK_PRIORITY; -} - QStringList QtTestFramework::testNameForSymbolName(const QString &symbolName) const { int index = symbolName.lastIndexOf("::"); @@ -44,5 +137,21 @@ QStringList QtTestFramework::testNameForSymbolName(const QString &symbolName) co return { symbolName.left(index), symbolName.mid(index + 2) }; } -} // namespace Internal -} // namespace Autotest +// QtTestSettingsPage + +class QtTestSettingPage final : public Core::IOptionsPage +{ +public: + QtTestSettingPage() + { + setId(Id(Constants::SETTINGSPAGE_PREFIX).withSuffix(QString("%1.QtTest") + .arg(QtTest::Constants::FRAMEWORK_PRIORITY))); + setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); + setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); + setSettingsProvider([] { return &theQtTestFramework(); }); + } +}; + +const QtTestSettingPage settingsPage; + +} // Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttestframework.h b/src/plugins/autotest/qtest/qttestframework.h index c90bd3c9d8c..47bf454484e 100644 --- a/src/plugins/autotest/qtest/qttestframework.h +++ b/src/plugins/autotest/qtest/qttestframework.h @@ -5,25 +5,39 @@ #include "../itestframework.h" -#include "qttestsettings.h" - namespace Autotest::Internal { +enum MetricsType +{ + Walltime, + TickCounter, + EventCounter, + CallGrind, + Perf +}; + class QtTestFramework : public ITestFramework { public: - QtTestFramework() : ITestFramework(true) {} + QtTestFramework(); + + static QString metricsTypeToOption(const MetricsType type); + + Utils::SelectionAspect metrics{this}; + Utils::BoolAspect noCrashHandler{this}; + Utils::BoolAspect useXMLOutput{this}; + Utils::BoolAspect verboseBench{this}; + Utils::BoolAspect logSignalsSlots{this}; + Utils::BoolAspect limitWarnings{this}; + Utils::IntegerAspect maxWarnings{this}; + Utils::BoolAspect quickCheckForDerivedTests{this}; QStringList testNameForSymbolName(const QString &symbolName) const override; -private: - const char *name() const override; - QString displayName() const override; - unsigned priority() const override; + ITestParser *createTestParser() override; ITestTreeItem *createRootNode() override; - ITestSettings *testSettings() override { return &m_settings; } - - QtTestSettings m_settings{settingsId()}; }; +QtTestFramework &theQtTestFramework(); + } // Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp index 57c85b08a9e..a74944b7b64 100644 --- a/src/plugins/autotest/qtest/qttestparser.cpp +++ b/src/plugins/autotest/qtest/qttestparser.cpp @@ -74,7 +74,7 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc, const CPlusPlus: static bool qtTestLibDefined(const FilePath &fileName) { const QList<CppEditor::ProjectPart::ConstPtr> parts = - CppEditor::CppModelManager::instance()->projectPart(fileName); + CppEditor::CppModelManager::projectPart(fileName); if (parts.size() > 0) { return Utils::anyOf(parts.at(0)->projectMacros, [](const ProjectExplorer::Macro ¯o) { return macro.key == "QT_TESTLIB_LIB"; @@ -83,11 +83,10 @@ static bool qtTestLibDefined(const FilePath &fileName) return false; } -TestCases QtTestParser::testCases(const CppEditor::CppModelManager *modelManager, - const FilePath &filePath) const +TestCases QtTestParser::testCases(const FilePath &filePath) const { const QByteArray &fileContent = getFileContent(filePath); - CPlusPlus::Document::Ptr document = modelManager->document(filePath); + CPlusPlus::Document::Ptr document = CppEditor::CppModelManager::document(filePath); if (document.isNull()) return {}; @@ -296,6 +295,9 @@ static bool isQObject(const CPlusPlus::Document::Ptr &declaringDoc) bool QtTestParser::processDocument(QPromise<TestParseResultPtr> &promise, const FilePath &fileName) { + if (!m_prefilteredFiles.contains(fileName)) + return false; + CPlusPlus::Document::Ptr doc = document(fileName); if (doc.isNull()) return false; @@ -305,8 +307,7 @@ bool QtTestParser::processDocument(QPromise<TestParseResultPtr> &promise, return false; } - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); - TestCases testCaseList(testCases(modelManager, fileName)); + TestCases testCaseList(testCases(fileName)); bool reported = false; // we might be in a reparse without the original entry point with the QTest::qExec() if (testCaseList.isEmpty() && !oldTestCases.empty()) @@ -319,7 +320,7 @@ bool QtTestParser::processDocument(QPromise<TestParseResultPtr> &promise, continue; QList<CppEditor::ProjectPart::ConstPtr> projectParts - = modelManager->projectPart(fileName); + = CppEditor::CppModelManager::projectPart(fileName); if (projectParts.isEmpty()) // happens if shutting down while parsing return false; @@ -420,6 +421,12 @@ void QtTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse) m_testCases = QTestUtils::testCaseNamesForFiles(framework(), filesToParse); m_alternativeFiles = QTestUtils::alternativeFiles(framework(), filesToParse); } + + if (std::optional<QSet<Utils::FilePath>> prefiltered = filesContainingMacro("QT_TESTLIB_LIB")) + m_prefilteredFiles = prefiltered->intersect(filesToParse); + else + m_prefilteredFiles = filesToParse; + CppParser::init(filesToParse, fullParse); } @@ -427,6 +434,7 @@ void QtTestParser::release() { m_testCases.clear(); m_alternativeFiles.clear(); + m_prefilteredFiles.clear(); CppParser::release(); } diff --git a/src/plugins/autotest/qtest/qttestparser.h b/src/plugins/autotest/qtest/qttestparser.h index abd8b2b0aaa..9f8c44593f0 100644 --- a/src/plugins/autotest/qtest/qttestparser.h +++ b/src/plugins/autotest/qtest/qttestparser.h @@ -10,8 +10,6 @@ #include <optional> -namespace CppEditor { class CppModelManager; } - namespace Autotest { namespace Internal { @@ -40,8 +38,7 @@ public: const Utils::FilePath &fileName) override; private: - TestCases testCases(const CppEditor::CppModelManager *modelManager, - const Utils::FilePath &fileName) const; + TestCases testCases(const Utils::FilePath &fileName) const; QHash<QString, QtTestCodeLocationList> checkForDataTags(const Utils::FilePath &fileName) const; struct TestCaseData { Utils::FilePath fileName; @@ -60,6 +57,7 @@ private: const QString &projectFile) const; QHash<Utils::FilePath, TestCases> m_testCases; QMultiHash<Utils::FilePath, Utils::FilePath> m_alternativeFiles; + QSet<Utils::FilePath> m_prefilteredFiles; }; } // namespace Internal diff --git a/src/plugins/autotest/qtest/qttestresult.cpp b/src/plugins/autotest/qtest/qttestresult.cpp index 1f15b3e82dc..581a334efc6 100644 --- a/src/plugins/autotest/qtest/qttestresult.cpp +++ b/src/plugins/autotest/qtest/qttestresult.cpp @@ -130,8 +130,8 @@ static ResultHooks::FindTestItemHook findTestItemHook(const FilePath &projectFil { return [=](const TestResult &result) -> ITestTreeItem * { const Id id = type == TestType::QtTest - ? Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME) - : Id(Constants::FRAMEWORK_PREFIX).withSuffix(QuickTest::Constants::FRAMEWORK_NAME); + ? Id(QtTest::Constants::FRAMEWORK_ID) + : Id(QuickTest::Constants::FRAMEWORK_ID); ITestFramework *framework = TestFrameworkManager::frameworkForId(id); QTC_ASSERT(framework, return nullptr); const TestTreeItem *rootNode = framework->rootNode(); @@ -151,8 +151,8 @@ struct QtTestData TestType m_type; QString m_function; QString m_dataTag; - bool isTestFunction() const { return !m_function.isEmpty() && m_dataTag.isEmpty(); }; - bool isDataTag() const { return !m_function.isEmpty() && !m_dataTag.isEmpty(); }; + bool isTestFunction() const { return !m_function.isEmpty() && m_dataTag.isEmpty(); } + bool isDataTag() const { return !m_function.isEmpty() && !m_dataTag.isEmpty(); } }; static ResultHooks::DirectParentHook directParentHook(const QString &functionName, diff --git a/src/plugins/autotest/qtest/qttestsettings.cpp b/src/plugins/autotest/qtest/qttestsettings.cpp deleted file mode 100644 index a8953a04492..00000000000 --- a/src/plugins/autotest/qtest/qttestsettings.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qttestsettings.h" - -#include "../autotestconstants.h" -#include "../autotesttr.h" -#include "qttestconstants.h" - -#include <utils/hostosinfo.h> -#include <utils/layoutbuilder.h> - -using namespace Layouting; -using namespace Utils; - -namespace Autotest::Internal { - -QtTestSettings::QtTestSettings(Id settingsId) -{ - setSettingsGroups("Autotest", "QtTest"); - setId(settingsId); - setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); - setDisplayName(Tr::tr(QtTest::Constants::FRAMEWORK_SETTINGS_CATEGORY)); - - setLayouter([this] { - return Row { Form { - noCrashHandler, br, - useXMLOutput, br, - verboseBench, br, - logSignalsSlots, br, - limitWarnings, maxWarnings, br, - Group { - title(Tr::tr("Benchmark Metrics")), - Column { metrics } - }, br, - quickCheckForDerivedTests, br - }, st }; - }); - - metrics.setSettingsKey("Metrics"); - metrics.setDefaultValue(Walltime); - metrics.addOption(Tr::tr("Walltime"), Tr::tr("Uses walltime metrics for executing benchmarks (default).")); - metrics.addOption(Tr::tr("Tick counter"), Tr::tr("Uses tick counter when executing benchmarks.")); - metrics.addOption(Tr::tr("Event counter"), Tr::tr("Uses event counter when executing benchmarks.")); - metrics.addOption({ - Tr::tr("Callgrind"), - Tr::tr("Uses Valgrind Callgrind when executing benchmarks (it must be installed)."), - HostOsInfo::isAnyUnixHost() // valgrind available on UNIX - }); - metrics.addOption({ - Tr::tr("Perf"), - Tr::tr("Uses Perf when executing benchmarks (it must be installed)."), - HostOsInfo::isLinuxHost() // according to docs perf Linux only - }); - - noCrashHandler.setSettingsKey("NoCrashhandlerOnDebug"); - noCrashHandler.setDefaultValue(true); - noCrashHandler.setLabelText(Tr::tr("Disable crash handler while debugging")); - noCrashHandler.setToolTip(Tr::tr("Enables interrupting tests on assertions.")); - - useXMLOutput.setSettingsKey("UseXMLOutput"); - useXMLOutput.setDefaultValue(true); - useXMLOutput.setLabelText(Tr::tr("Use XML output")); - useXMLOutput.setToolTip(Tr::tr("XML output is recommended, because it avoids parsing issues, " - "while plain text is more human readable.\n\nWarning: " - "Plain text misses some information, such as duration.")); - - verboseBench.setSettingsKey("VerboseBench"); - verboseBench.setLabelText(Tr::tr("Verbose benchmarks")); - - logSignalsSlots.setSettingsKey("LogSignalsSlots"); - logSignalsSlots.setLabelText(Tr::tr("Log signals and slots")); - logSignalsSlots.setToolTip(Tr::tr("Log every signal emission and resulting slot invocations.")); - - limitWarnings.setSettingsKey("LimitWarnings"); - limitWarnings.setLabelText(Tr::tr("Limit warnings")); - limitWarnings.setToolTip(Tr::tr("Set the maximum number of warnings. 0 means that the number " - "is not limited.")); - - maxWarnings.setSettingsKey("MaxWarnings"); - maxWarnings.setRange(0, 10000); - maxWarnings.setDefaultValue(2000); - maxWarnings.setSpecialValueText(Tr::tr("Unlimited")); - maxWarnings.setEnabler(&limitWarnings); - - quickCheckForDerivedTests.setSettingsKey("QuickCheckForDerivedTests"); - quickCheckForDerivedTests.setDefaultValue(false); - quickCheckForDerivedTests.setLabelText(Tr::tr("Check for derived Qt Quick tests")); - quickCheckForDerivedTests.setToolTip( - Tr::tr("Search for Qt Quick tests that are derived from TestCase.\nWarning: Enabling this " - "feature significantly increases scan time.")); -} - -QString QtTestSettings::metricsTypeToOption(const MetricsType type) -{ - switch (type) { - case MetricsType::Walltime: - return {}; - case MetricsType::TickCounter: - return QString("-tickcounter"); - case MetricsType::EventCounter: - return QString("-eventcounter"); - case MetricsType::CallGrind: - return QString("-callgrind"); - case MetricsType::Perf: - return QString("-perf"); - } - return {}; -} - -} // Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttestsettings.h b/src/plugins/autotest/qtest/qttestsettings.h deleted file mode 100644 index 31394ee6027..00000000000 --- a/src/plugins/autotest/qtest/qttestsettings.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Autotest::Internal { - -enum MetricsType -{ - Walltime, - TickCounter, - EventCounter, - CallGrind, - Perf -}; - -class QtTestSettings : public Core::PagedSettings -{ -public: - explicit QtTestSettings(Utils::Id settingsId); - - static QString metricsTypeToOption(const MetricsType type); - - Utils::SelectionAspect metrics{this}; - Utils::BoolAspect noCrashHandler{this}; - Utils::BoolAspect useXMLOutput{this}; - Utils::BoolAspect verboseBench{this}; - Utils::BoolAspect logSignalsSlots{this}; - Utils::BoolAspect limitWarnings{this}; - Utils::IntegerAspect maxWarnings{this}; - Utils::BoolAspect quickCheckForDerivedTests{this}; -}; - -} // Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttesttreeitem.cpp b/src/plugins/autotest/qtest/qttesttreeitem.cpp index 81f95c17752..68b50be9079 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.cpp +++ b/src/plugins/autotest/qtest/qttesttreeitem.cpp @@ -8,7 +8,9 @@ #include "../autotesttr.h" #include "../itestframework.h" +#include <cplusplus/Symbol.h> #include <cppeditor/cppmodelmanager.h> +#include <cppeditor/symbolfinder.h> #include <projectexplorer/projectmanager.h> @@ -16,8 +18,7 @@ using namespace Utils; -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { QtTestTreeItem::QtTestTreeItem(ITestFramework *testFramework, const QString &name, const FilePath &filePath, TestTreeItem::Type type) @@ -46,12 +47,17 @@ QVariant QtTestTreeItem::data(int column, int role) const case Qt::ToolTipRole: { QString toolTip = TestTreeItem::data(column, role).toString(); if (m_multiTest && type() == TestCase) { - toolTip.append(Tr::tr("<p>Multiple testcases inside a single executable are not officially " - "supported. Depending on the implementation they might get executed " - "or not, but never will be explicitly selectable.</p>")); + toolTip.append( + "<p>" + + Tr::tr("Multiple testcases inside a single executable are not officially " + "supported. Depending on the implementation they might get executed " + "or not, but never will be explicitly selectable.") + + "</p>"); + } else if (type() == TestFunction) { + // avoid confusion (displaying header file, but ending up inside source) + toolTip = parentItem()->name() + "::" + name(); } return toolTip; - break; } case Qt::CheckStateRole: switch (type()) { @@ -69,6 +75,13 @@ QVariant QtTestTreeItem::data(int column, int role) const default: return m_multiTest; } + case LinkRole: + if (type() == GroupNode || type() == Root) + return QVariant(); + if (type() == TestDataFunction || type() == TestDataTag) + return TestTreeItem::data(column, role); + // other functions would end up inside declaration - so, find its definition + return linkForTreeItem(); } return TestTreeItem::data(column, role); } @@ -120,8 +133,6 @@ ITestConfiguration *QtTestTreeItem::testConfiguration() const { ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); QTC_ASSERT(project, return nullptr); - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return nullptr); QtTestConfiguration *config = nullptr; switch (type()) { @@ -155,15 +166,13 @@ ITestConfiguration *QtTestTreeItem::testConfiguration() const return nullptr; } if (config) - config->setInternalTargets(cppMM->internalTargets(filePath())); + config->setInternalTargets(CppEditor::CppModelManager::internalTargets(filePath())); return config; } static void fillTestConfigurationsFromCheckState(const TestTreeItem *item, QList<ITestConfiguration *> &testConfigurations) { - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); QTC_ASSERT(item, return); if (item->type() == TestTreeItem::GroupNode) { for (int row = 0, count = item->childCount(); row < count; ++row) @@ -198,15 +207,14 @@ static void fillTestConfigurationsFromCheckState(const TestTreeItem *item, testConfig->setTestCases(testCases); testConfig->setProjectFile(item->proFile()); testConfig->setProject(ProjectExplorer::ProjectManager::startupProject()); - testConfig->setInternalTargets(cppMM->internalTargets(item->filePath())); + testConfig->setInternalTargets( + CppEditor::CppModelManager::internalTargets(item->filePath())); testConfigurations << testConfig; } } static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration *> &testConfigs) { - const auto cppMM = CppEditor::CppModelManager::instance(); - QTC_ASSERT(cppMM, return); QTC_ASSERT(item, return); if (item->type() == TestTreeItem::GroupNode) { for (int row = 0, count = item->childCount(); row < count; ++row) @@ -232,7 +240,8 @@ static void collectFailedTestInfo(TestTreeItem *item, QList<ITestConfiguration * testConfig->setTestCases(testCases); testConfig->setProjectFile(item->proFile()); testConfig->setProject(ProjectExplorer::ProjectManager::startupProject()); - testConfig->setInternalTargets(cppMM->internalTargets(item->filePath())); + testConfig->setInternalTargets( + CppEditor::CppModelManager::internalTargets(item->filePath())); testConfigs << testConfig; } @@ -407,6 +416,26 @@ bool QtTestTreeItem::isGroupable() const return type() == TestCase; } +QVariant QtTestTreeItem::linkForTreeItem() const +{ + QVariant itemLink; + using namespace CPlusPlus; + const Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const Document::Ptr doc = snapshot.document(filePath()); + Symbol *symbol = doc->lastVisibleSymbolAt(line(), this->column() + 1); + if (auto decl = symbol->asDeclaration()) { + static CppEditor::SymbolFinder symbolFinder; + if (Symbol *definition = symbolFinder.findMatchingDefinition(decl, snapshot, true); + definition && definition->fileId()) { + itemLink.setValue(Link(FilePath::fromUtf8(definition->fileName()), + definition->line(), definition->column() - 1)); + } + } + if (!itemLink.isValid()) // fallback in case we failed to find the definition + itemLink.setValue(Link(filePath(), line(), this->column())); + return itemLink; +} + TestTreeItem *QtTestTreeItem::findChildByFileNameAndType(const FilePath &file, const QString &name, Type type) const { @@ -442,5 +471,4 @@ QString QtTestTreeItem::nameSuffix() const return suffix.isEmpty() ? suffix : QString{" [" + suffix + "]"}; } -} // namespace Internal -} // namespace Autotest +} // namespace Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttesttreeitem.h b/src/plugins/autotest/qtest/qttesttreeitem.h index ebcc8b89dfe..51ad5b97657 100644 --- a/src/plugins/autotest/qtest/qttesttreeitem.h +++ b/src/plugins/autotest/qtest/qttesttreeitem.h @@ -5,8 +5,7 @@ #include "../testtreeitem.h" -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { class QtTestTreeItem : public TestTreeItem { @@ -36,6 +35,7 @@ public: TestTreeItem *createParentGroupNode() const override; bool isGroupable() const override; private: + QVariant linkForTreeItem() const; TestTreeItem *findChildByFileNameAndType(const Utils::FilePath &file, const QString &name, Type type) const; TestTreeItem *findChildByNameAndInheritanceAndMultiTest(const QString &name, bool inherited, @@ -53,5 +53,4 @@ public: typedef QVector<QtTestCodeLocationAndType> QtTestCodeLocationList; -} // namespace Internal -} // namespace Autotest +} // namespace Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttestvisitors.cpp b/src/plugins/autotest/qtest/qttestvisitors.cpp index 6f615c99321..f19b855d559 100644 --- a/src/plugins/autotest/qtest/qttestvisitors.cpp +++ b/src/plugins/autotest/qtest/qttestvisitors.cpp @@ -14,8 +14,7 @@ using namespace CPlusPlus; using namespace Utils; -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { static QStringList specialFunctions({"initTestCase", "cleanupTestCase", "init", "cleanup"}); @@ -49,19 +48,16 @@ bool TestVisitor::visit(Class *symbol) const QString name = o.prettyName(func->name()); QtTestCodeLocationAndType locationAndType; - Function *functionDefinition = m_symbolFinder.findMatchingDefinition( - func, m_snapshot, true); - if (functionDefinition && functionDefinition->fileId()) { - locationAndType.m_filePath = FilePath::fromString( - QString::fromUtf8(functionDefinition->fileName())); - locationAndType.m_line = functionDefinition->line(); - locationAndType.m_column = functionDefinition->column() - 1; - } else { // if we cannot find the definition use declaration as fallback - locationAndType.m_filePath = FilePath::fromString( - QString::fromUtf8(member->fileName())); - locationAndType.m_line = member->line(); - locationAndType.m_column = member->column() - 1; + if (name.endsWith("_data")) { + // costly.. but we need at least the correct entry for finding data tags + Function *functionDefinition = m_symbolFinder.findMatchingDefinition( + func, m_snapshot, true); + if (functionDefinition && functionDefinition->fileId()) + member = functionDefinition; } + locationAndType.m_filePath = FilePath::fromUtf8(member->fileName()); + locationAndType.m_line = member->line(); + locationAndType.m_column = member->column() - 1; if (specialFunctions.contains(name)) locationAndType.m_type = TestTreeItem::TestSpecialFunction; else if (name.endsWith("_data")) @@ -224,7 +220,7 @@ bool TestDataFunctionVisitor::visit(CallAST *ast) return true; int line = 0; int column = 0; - m_currentDoc->translationUnit()->getTokenStartPosition( + m_currentDoc->translationUnit()->getTokenPosition( firstToken, &line, &column); QtTestCodeLocationAndType locationAndType; locationAndType.m_name = name; @@ -287,5 +283,4 @@ bool TestDataFunctionVisitor::newRowCallFound(CallAST *ast, unsigned *firstToken return found; } -} // namespace Internal -} // namespace Autotest +} // namespace Autotest::Internal diff --git a/src/plugins/autotest/qtest/qttestvisitors.h b/src/plugins/autotest/qtest/qttestvisitors.h index dc18914b544..cf1f14642ec 100644 --- a/src/plugins/autotest/qtest/qttestvisitors.h +++ b/src/plugins/autotest/qtest/qttestvisitors.h @@ -16,8 +16,7 @@ #include <QMap> #include <QSet> -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { class TestVisitor : public CPlusPlus::SymbolVisitor { @@ -84,5 +83,4 @@ private: bool m_insideUsingQTest = false; }; -} // namespace Internal -} // namespace Autotest +} // namespace Autotest::Internal diff --git a/src/plugins/autotest/quick/quicktestconfiguration.cpp b/src/plugins/autotest/quick/quicktestconfiguration.cpp index e37b208ac44..ebf2c2406ea 100644 --- a/src/plugins/autotest/quick/quicktestconfiguration.cpp +++ b/src/plugins/autotest/quick/quicktestconfiguration.cpp @@ -3,9 +3,8 @@ #include "quicktestconfiguration.h" -#include "../itestframework.h" #include "../qtest/qttestoutputreader.h" -#include "../qtest/qttestsettings.h" +#include "../qtest/qttestframework.h" #include "../qtest/qttest_utils.h" #include "../testsettings.h" @@ -22,8 +21,7 @@ QuickTestConfiguration::QuickTestConfiguration(ITestFramework *framework) TestOutputReader *QuickTestConfiguration::createOutputReader(Process *app) const { - auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings()); - const QtTestOutputReader::OutputMode mode = qtSettings && qtSettings->useXMLOutput.value() + const QtTestOutputReader::OutputMode mode = theQtTestFramework().useXMLOutput() ? QtTestOutputReader::XML : QtTestOutputReader::PlainText; return new QtTestOutputReader(app, buildDirectory(), projectFile(), mode, TestType::QuickTest); @@ -32,31 +30,29 @@ TestOutputReader *QuickTestConfiguration::createOutputReader(Process *app) const QStringList QuickTestConfiguration::argumentsForTestRunner(QStringList *omitted) const { QStringList arguments; - if (TestSettings::instance()->processArgs()) { + if (testSettings().processArgs()) { arguments.append(QTestUtils::filterInterfering (runnable().command.arguments().split(' ', Qt::SkipEmptyParts), omitted, true)); } - auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings()); - if (!qtSettings) - return arguments; - if (qtSettings->useXMLOutput.value()) + QtTestFramework &qtSettings = theQtTestFramework(); + if (qtSettings.useXMLOutput()) arguments << "-xml"; if (!testCases().isEmpty()) arguments << testCases(); - const QString &metricsOption = QtTestSettings::metricsTypeToOption(MetricsType(qtSettings->metrics.value())); + const QString &metricsOption = QtTestFramework::metricsTypeToOption(MetricsType(qtSettings.metrics())); if (!metricsOption.isEmpty()) arguments << metricsOption; if (isDebugRunMode()) { - if (qtSettings->noCrashHandler.value()) + if (qtSettings.noCrashHandler()) arguments << "-nocrashhandler"; } - if (qtSettings->limitWarnings.value() && qtSettings->maxWarnings.value() != 2000) - arguments << "-maxwarnings" << QString::number(qtSettings->maxWarnings.value()); + if (qtSettings.limitWarnings() && qtSettings.maxWarnings() != 2000) + arguments << "-maxwarnings" << QString::number(qtSettings.maxWarnings()); return arguments; } diff --git a/src/plugins/autotest/quick/quicktestframework.cpp b/src/plugins/autotest/quick/quicktestframework.cpp index a8289ad48e5..875520a5e70 100644 --- a/src/plugins/autotest/quick/quicktestframework.cpp +++ b/src/plugins/autotest/quick/quicktestframework.cpp @@ -5,14 +5,24 @@ #include "quicktestparser.h" #include "quicktesttreeitem.h" -#include "../autotestconstants.h" #include "../autotesttr.h" -#include "../testframeworkmanager.h" -#include "../qtest/qttestconstants.h" namespace Autotest { namespace Internal { +QuickTestFramework &theQuickTestFramework() +{ + static QuickTestFramework framework; + return framework; +} + +QuickTestFramework::QuickTestFramework() +{ + setId(QuickTest::Constants::FRAMEWORK_ID); + setDisplayName(Tr::tr("Quick Test")); + setPriority(5); +} + ITestParser *QuickTestFramework::createTestParser() { return new QuickTestParser(this); @@ -23,28 +33,5 @@ ITestTreeItem *QuickTestFramework::createRootNode() return new QuickTestTreeItem(this, displayName(), {}, ITestTreeItem::Root); } -const char *QuickTestFramework::name() const -{ - return QuickTest::Constants::FRAMEWORK_NAME; -} - -QString QuickTestFramework::displayName() const -{ - return Tr::tr("Quick Test"); -} - -unsigned QuickTestFramework::priority() const -{ - return 5; -} - -ITestSettings *QuickTestFramework::testSettings() -{ - static const Utils::Id id - = Utils::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME); - ITestFramework *qtTestFramework = TestFrameworkManager::frameworkForId(id); - return qtTestFramework->testSettings(); -} - } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/quick/quicktestframework.h b/src/plugins/autotest/quick/quicktestframework.h index 99c7eb0552a..d07732b9108 100644 --- a/src/plugins/autotest/quick/quicktestframework.h +++ b/src/plugins/autotest/quick/quicktestframework.h @@ -5,30 +5,30 @@ #include "../itestframework.h" +#include "../qtest/qttestframework.h" + namespace Autotest { namespace QuickTest { namespace Constants { -const char FRAMEWORK_NAME[] = "QtQuickTest"; +const char FRAMEWORK_ID[] = "AutoTest.Framework.QtQuickTest"; } // namespace Constants } // namespace QuickTest namespace Internal { -class QuickTestFramework : public ITestFramework +class QuickTestFramework : public QtTestFramework { public: - QuickTestFramework() : ITestFramework(true) {} - const char *name() const override; - QString displayName() const override; - unsigned priority() const override; - ITestSettings *testSettings() override; + QuickTestFramework(); protected: ITestParser *createTestParser() override; ITestTreeItem *createRootNode() override; }; +QuickTestFramework &theQuickTestFramework(); + } // namespace Internal } // namespace Autotest diff --git a/src/plugins/autotest/quick/quicktestparser.cpp b/src/plugins/autotest/quick/quicktestparser.cpp index 4fa8d4fb81d..017f9f4c6dd 100644 --- a/src/plugins/autotest/quick/quicktestparser.cpp +++ b/src/plugins/autotest/quick/quicktestparser.cpp @@ -8,7 +8,7 @@ #include "quicktest_utils.h" #include "../testcodeparser.h" #include "../testtreemodel.h" -#include "../qtest/qttestsettings.h" +#include "../qtest/qttestframework.h" #include <cppeditor/cppmodelmanager.h> #include <cppeditor/projectpart.h> @@ -28,8 +28,7 @@ using namespace QmlJS; using namespace Utils; -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { TestTreeItem *QuickTestParseResult::createTestTreeItem() const { @@ -82,9 +81,10 @@ static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc, return false; } -static QString quickTestSrcDir(const CppEditor::CppModelManager *cppMM, const FilePath &fileName) +static QString quickTestSrcDir(const FilePath &fileName) { - const QList<CppEditor::ProjectPart::ConstPtr> parts = cppMM->projectPart(fileName); + const QList<CppEditor::ProjectPart::ConstPtr> parts = + CppEditor::CppModelManager::projectPart(fileName); if (parts.size() > 0) { const ProjectExplorer::Macros ¯os = parts.at(0)->projectMacros; auto found = std::find_if(macros.cbegin(), macros.cend(), @@ -127,7 +127,7 @@ QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) cons return {}; document->check(); CPlusPlus::AST *ast = document->translationUnit()->ast(); - QuickTestAstVisitor astVisitor(document, m_cppSnapshot); + QuickTestAstVisitor astVisitor(document); astVisitor.accept(ast); if (!astVisitor.testBaseName().isEmpty()) return astVisitor.testBaseName(); @@ -252,17 +252,20 @@ bool QuickTestParser::handleQtQuickTest(QPromise<TestParseResultPtr> &promise, CPlusPlus::Document::Ptr document, ITestFramework *framework) { - const CppEditor::CppModelManager *modelManager = CppEditor::CppModelManager::instance(); if (quickTestName(document).isEmpty()) return false; - QList<CppEditor::ProjectPart::ConstPtr> ppList = modelManager->projectPart(document->filePath()); + QList<CppEditor::ProjectPart::ConstPtr> ppList = + CppEditor::CppModelManager::projectPart(document->filePath()); if (ppList.isEmpty()) // happens if shutting down while parsing return false; const FilePath cppFileName = document->filePath(); const FilePath proFile = FilePath::fromString(ppList.at(0)->projectFile); - m_mainCppFiles.insert(cppFileName, proFile); - const FilePath srcDir = FilePath::fromString(quickTestSrcDir(modelManager, cppFileName)); + { + QWriteLocker lock(&m_parseLock); + m_mainCppFiles.insert(cppFileName, proFile); + } + const FilePath srcDir = FilePath::fromString(quickTestSrcDir(cppFileName)); if (srcDir.isEmpty()) return false; @@ -340,13 +343,13 @@ QuickTestParser::QuickTestParser(ITestFramework *framework) void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse) { m_qmlSnapshot = QmlJSTools::Internal::ModelManager::instance()->snapshot(); + QWriteLocker lock(&m_parseLock); // should not be necessary if (!fullParse) { // in a full parse we get the correct entry points by the respective main m_proFilesForQmlFiles = QuickTestUtils::proFilesForQmlFiles(framework(), filesToParse); // get rid of cached main cpp files that are going to get processed anyhow for (const FilePath &file : filesToParse) { - if (m_mainCppFiles.contains(file)) { - m_mainCppFiles.remove(file); + if (m_mainCppFiles.remove(file) == 1) { if (m_mainCppFiles.isEmpty()) break; } @@ -355,9 +358,14 @@ void QuickTestParser::init(const QSet<FilePath> &filesToParse, bool fullParse) // get rid of all cached main cpp files m_mainCppFiles.clear(); } + lock.unlock(); - auto qtSettings = static_cast<QtTestSettings *>(framework()->testSettings()); - m_checkForDerivedTests = qtSettings->quickCheckForDerivedTests.value(); + m_checkForDerivedTests = theQtTestFramework().quickCheckForDerivedTests(); + + if (std::optional<QSet<Utils::FilePath>> prefiltered = filesContainingMacro("QT_QMLTEST_LIB")) + m_prefilteredFiles = prefiltered->intersect(filesToParse); + else + m_prefilteredFiles = filesToParse; CppParser::init(filesToParse, fullParse); } @@ -366,6 +374,7 @@ void QuickTestParser::release() { m_qmlSnapshot = Snapshot(); m_proFilesForQmlFiles.clear(); + m_prefilteredFiles.clear(); CppParser::release(); } @@ -384,6 +393,9 @@ bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise, m_checkForDerivedTests); } + if (!m_prefilteredFiles.contains(fileName)) + return false; + CPlusPlus::Document::Ptr cppdoc = document(fileName); if (cppdoc.isNull() || !includesQtQuickTest(cppdoc, m_cppSnapshot)) return false; @@ -391,10 +403,10 @@ bool QuickTestParser::processDocument(QPromise<TestParseResultPtr> &promise, return handleQtQuickTest(promise, cppdoc, framework()); } -FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) const +FilePath QuickTestParser::projectFileForMainCppFile(const FilePath &fileName) { - return m_mainCppFiles.contains(fileName) ? m_mainCppFiles.value(fileName) : FilePath(); + QReadLocker lock(&m_parseLock); + return m_mainCppFiles.value(fileName); } -} // namespace Internal -} // namespace Autotest +} // namespace Autotest::Internal diff --git a/src/plugins/autotest/quick/quicktestparser.h b/src/plugins/autotest/quick/quicktestparser.h index dfd71333ead..c22e1d480e1 100644 --- a/src/plugins/autotest/quick/quicktestparser.h +++ b/src/plugins/autotest/quick/quicktestparser.h @@ -8,6 +8,7 @@ #include <qmljs/qmljsdocument.h> #include <QFileSystemWatcher> +#include <QReadWriteLock> namespace Autotest { namespace Internal { @@ -28,7 +29,7 @@ public: void release() override; bool processDocument(QPromise<TestParseResultPtr> &promise, const Utils::FilePath &fileName) override; - Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName) const; + Utils::FilePath projectFileForMainCppFile(const Utils::FilePath &fileName); QStringList supportedExtensions() const override { return {"qml"}; }; private: @@ -44,6 +45,8 @@ private: QFileSystemWatcher m_directoryWatcher; QMap<QString, QMap<QString, QDateTime> > m_watchedFiles; QMap<Utils::FilePath, Utils::FilePath> m_mainCppFiles; + QSet<Utils::FilePath> m_prefilteredFiles; + QReadWriteLock m_parseLock; // guard for m_mainCppFiles bool m_checkForDerivedTests = false; }; diff --git a/src/plugins/autotest/quick/quicktesttreeitem.cpp b/src/plugins/autotest/quick/quicktesttreeitem.cpp index 88f43e15419..5252f8ea27b 100644 --- a/src/plugins/autotest/quick/quicktesttreeitem.cpp +++ b/src/plugins/autotest/quick/quicktesttreeitem.cpp @@ -370,8 +370,8 @@ bool QuickTestTreeItem::isGroupable() const QSet<QString> internalTargets(const FilePath &proFile) { QSet<QString> result; - const auto cppMM = CppEditor::CppModelManager::instance(); - const auto projectInfo = cppMM->projectInfo(ProjectExplorer::ProjectManager::startupProject()); + const auto projectInfo = + CppEditor::CppModelManager::projectInfo(ProjectExplorer::ProjectManager::startupProject()); if (!projectInfo) return {}; for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectInfo->projectParts()) { diff --git a/src/plugins/autotest/quick/quicktestvisitors.cpp b/src/plugins/autotest/quick/quicktestvisitors.cpp index 158f37db839..9403aa1351e 100644 --- a/src/plugins/autotest/quick/quicktestvisitors.cpp +++ b/src/plugins/autotest/quick/quicktestvisitors.cpp @@ -166,11 +166,9 @@ void TestQmlVisitor::throwRecursionDepthError() /************************************** QuickTestAstVisitor *************************************/ -QuickTestAstVisitor::QuickTestAstVisitor(CPlusPlus::Document::Ptr doc, - const CPlusPlus::Snapshot &snapshot) +QuickTestAstVisitor::QuickTestAstVisitor(CPlusPlus::Document::Ptr doc) : ASTVisitor(doc->translationUnit()) , m_currentDoc(doc) - , m_snapshot(snapshot) { } diff --git a/src/plugins/autotest/quick/quicktestvisitors.h b/src/plugins/autotest/quick/quicktestvisitors.h index 58dae69e921..11104cc329a 100644 --- a/src/plugins/autotest/quick/quicktestvisitors.h +++ b/src/plugins/autotest/quick/quicktestvisitors.h @@ -56,7 +56,7 @@ private: class QuickTestAstVisitor : public CPlusPlus::ASTVisitor { public: - QuickTestAstVisitor(CPlusPlus::Document::Ptr doc, const CPlusPlus::Snapshot &snapshot); + QuickTestAstVisitor(CPlusPlus::Document::Ptr doc); bool visit(CPlusPlus::CallAST *ast) override; @@ -64,7 +64,6 @@ public: private: QString m_testBaseName; CPlusPlus::Document::Ptr m_currentDoc; - const CPlusPlus::Snapshot &m_snapshot; }; } // namespace Internal diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index dc76520dd3a..9b68815bea6 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -5,6 +5,7 @@ #include "autotestconstants.h" #include "autotesttr.h" +#include "testsettings.h" #include "testtreemodel.h" #include <coreplugin/editormanager/editormanager.h> @@ -63,6 +64,12 @@ void TestCodeParser::setState(State state) if (m_parserState == Shutdown) return; qCDebug(LOG) << "setState(" << state << "), currentState:" << m_parserState; + if (m_parserState == DisabledTemporarily && state == Idle) { + m_parserState = Idle; + qCDebug(LOG) << "Just re-enabling parser."; + return; + } + // avoid triggering parse before code model parsing has finished, but mark as dirty if (isProjectParsing() || m_codeModelParsing) { m_dirty = true; @@ -201,12 +208,15 @@ void TestCodeParser::onProjectPartsUpdated(Project *project) emitUpdateTestTree(); } -void TestCodeParser::aboutToShutdown() +void TestCodeParser::aboutToShutdown(bool isFinal) { - qCDebug(LOG) << "Disabling (immediately) - shutting down"; - m_parserState = Shutdown; + qCDebug(LOG) << "Disabling (immediately) -" + << (isFinal ? "shutting down" : "disabled temporarily"); + m_parserState = isFinal ? Shutdown : DisabledTemporarily; m_taskTree.reset(); m_futureSynchronizer.waitForFinished(); + if (!isFinal) + onFinished(false); } bool TestCodeParser::postponed(const QSet<FilePath> &filePaths) @@ -257,6 +267,7 @@ bool TestCodeParser::postponed(const QSet<FilePath> &filePaths) } return true; case Shutdown: + case DisabledTemporarily: break; } QTC_ASSERT(false, return false); // should not happen at all @@ -276,7 +287,7 @@ static void parseFileForTests(QPromise<TestParseResultPtr> &promise, void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths, const QList<ITestParser *> &parsers) { - if (m_parserState == Shutdown || m_testCodeParsers.isEmpty()) + if (m_parserState == Shutdown || m_parserState == DisabledTemporarily || m_testCodeParsers.isEmpty()) return; if (postponed(filePaths)) @@ -336,7 +347,7 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths, qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "StartParsing"; m_parsingTimer.restart(); QSet<QString> extensions; - const auto cppSnapshot = CppEditor::CppModelManager::instance()->snapshot(); + const auto cppSnapshot = CppEditor::CppModelManager::snapshot(); for (ITestParser *parser : codeParsers) { parser->init(files, isFullParse); @@ -360,7 +371,11 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths, using namespace Tasking; - QList<GroupItem> tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))}; + int limit = testSettings().scanThreadLimit(); + if (limit == 0) + limit = std::max(QThread::idealThreadCount() / 4, 1); + qCDebug(LOG) << "Using" << limit << "threads for scan."; + QList<GroupItem> tasks{parallelLimit(limit)}; for (const FilePath &file : filteredFiles) { const auto setup = [this, codeParsers, file](Async<TestParseResultPtr> &async) { async.setConcurrentCallData(parseFileForTests, codeParsers, file); @@ -414,7 +429,8 @@ void TestCodeParser::onAllTasksFinished(Id type) m_codeModelParsing = false; // avoid illegal parser state if respective widgets became hidden while parsing - setState(Idle); + if (m_parserState != DisabledTemporarily) + setState(Idle); } void TestCodeParser::onFinished(bool success) @@ -440,12 +456,23 @@ void TestCodeParser::onFinished(bool success) emit parsingFinished(); qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "ParsingFin"; qCDebug(LOG) << "Parsing took:" << m_parsingTimer.elapsed() << "ms"; + if (LOG().isInfoEnabled()) { + qCInfo(LOG).noquote().nospace() + << "Current test tree:" << TestTreeModel::instance()->report(true); + } else { + qCDebug(LOG).noquote().nospace() + << "Current test tree:" << TestTreeModel::instance()->report(false); + } } m_dirty = false; break; case Shutdown: qCDebug(LOG) << "Shutdown complete - not emitting parsingFinished (onFinished)"; break; + case DisabledTemporarily: + qCDebug(LOG) << "Disabling complete - emitting parsingFinished"; + emit parsingFinished(); // ensure hidden progress indicator + break; default: qWarning("I should not be here... State: %d", m_parserState); break; @@ -477,6 +504,12 @@ void TestCodeParser::onPartialParsingFinished() m_updateParsers.clear(); emit parsingFinished(); qCDebug(LOG) << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") << "ParsingFin"; + if (LOG().isDebugEnabled()) { + QMetaObject::invokeMethod(this, [] { // sweep() needs to be processed before logging + qCDebug(LOG).noquote().nospace() + << "Current test tree:" << TestTreeModel::instance()->report(false); + }, Qt::QueuedConnection); + } } else { qCDebug(LOG) << "not emitting parsingFinished" << "(on PartialParsingFinished, singleshot scheduled)"; diff --git a/src/plugins/autotest/testcodeparser.h b/src/plugins/autotest/testcodeparser.h index 2dd81d93d60..49e123ba496 100644 --- a/src/plugins/autotest/testcodeparser.h +++ b/src/plugins/autotest/testcodeparser.h @@ -31,7 +31,8 @@ public: Idle, PartialParse, FullParse, - Shutdown + Shutdown, + DisabledTemporarily }; TestCodeParser(); @@ -63,7 +64,7 @@ public: void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); void onStartupProjectChanged(ProjectExplorer::Project *project); void onProjectPartsUpdated(ProjectExplorer::Project *project); - void aboutToShutdown(); + void aboutToShutdown(bool isFinal); private: bool postponed(const QSet<Utils::FilePath> &fileList); diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 5f225db1972..3312704a56e 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -10,7 +10,7 @@ #include <projectexplorer/buildsystem.h> #include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/deploymentdata.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/runconfiguration.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> @@ -214,7 +214,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode) continue; } - const Runnable runnable = runConfig->runnable(); + const ProcessRunData runnable = runConfig->runnable(); // not the best approach - but depending on the build system and whether the executables // are going to get installed or not we have to soften the condition... const FilePath currentExecutable = ensureExeEnding(runnable.command.executable()); @@ -246,7 +246,7 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode) // we failed to find a valid runconfiguration - but we've got the executable already if (auto rc = target->activeRunConfiguration()) { if (isLocal(target)) { // FIXME for now only Desktop support - const Runnable runnable = rc->runnable(); + const ProcessRunData runnable = rc->runnable(); m_runnable.environment = runnable.environment; m_deducedConfiguration = true; m_deducedFrom = rc->displayName(); diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index 6292ef8204c..a7f4eabf5bf 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -6,13 +6,18 @@ #include "autotestconstants.h" #include <projectexplorer/project.h> -#include <projectexplorer/runcontrol.h> +#include <projectexplorer/runconfiguration.h> + #include <utils/environment.h> +#include <utils/processinterface.h> #include <QPointer> #include <QStringList> -namespace Utils { class Process; } +namespace Utils { +class Process; +class ProcessRunData; +} namespace Autotest { namespace Internal { @@ -49,10 +54,10 @@ public: void setTestCaseCount(int count) { m_testCaseCount = count; } int testCaseCount() const { return m_testCaseCount; } ProjectExplorer::Project *project() const { return m_project.data(); } - ProjectExplorer::Runnable runnable() const { return m_runnable; } + Utils::ProcessRunData runnable() const { return m_runnable; } protected: - ProjectExplorer::Runnable m_runnable; + Utils::ProcessRunData m_runnable; private: ITestBase *m_testBase = nullptr; diff --git a/src/plugins/autotest/testframeworkmanager.cpp b/src/plugins/autotest/testframeworkmanager.cpp index 3c6aff581db..d44dc3d1e7c 100644 --- a/src/plugins/autotest/testframeworkmanager.cpp +++ b/src/plugins/autotest/testframeworkmanager.cpp @@ -9,103 +9,91 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> -#include <QSettings> - using namespace Utils; -namespace Autotest { +namespace Autotest::TestFrameworkManager { -static TestFrameworkManager *s_instance = nullptr; - -TestFrameworkManager::TestFrameworkManager() +TestFrameworks &testFrameworks() { - s_instance = this; + static TestFrameworks theFrameworks; + return theFrameworks; } -TestFrameworkManager::~TestFrameworkManager() +TestTools &testTools() { - qDeleteAll(m_registeredFrameworks); - qDeleteAll(m_registeredTestTools); - s_instance = nullptr; + static TestTools theTools; + return theTools; } -bool TestFrameworkManager::registerTestFramework(ITestFramework *framework) +void registerTestFramework(ITestFramework *framework) { - QTC_ASSERT(framework, return false); - QTC_ASSERT(!m_registeredFrameworks.contains(framework), return false); + QTC_ASSERT(framework, return); + QTC_ASSERT(!testFrameworks().contains(framework), return); // TODO check for unique priority before registering - m_registeredFrameworks.append(framework); - Utils::sort(m_registeredFrameworks, &ITestFramework::priority); - return true; + testFrameworks().append(framework); + Utils::sort(testFrameworks(), &ITestFramework::priority); } -bool TestFrameworkManager::registerTestTool(ITestTool *testTool) +void registerTestTool(ITestTool *testTool) { - QTC_ASSERT(testTool, return false); - QTC_ASSERT(!m_registeredTestTools.contains(testTool), return false); - m_registeredTestTools.append(testTool); - return true; + QTC_ASSERT(testTool, return); + QTC_ASSERT(!testTools().contains(testTool), return); + testTools().append(testTool); } -void TestFrameworkManager::activateFrameworksAndToolsFromSettings( - const Internal::TestSettings *settings) +void activateFrameworksAndToolsFromSettings() { - for (ITestFramework *framework : std::as_const(s_instance->m_registeredFrameworks)) { - framework->setActive(settings->frameworks.value(framework->id(), false)); - framework->setGrouping(settings->frameworksGrouping.value(framework->id(), false)); + const Internal::TestSettings &settings = Internal::testSettings(); + for (ITestFramework *framework : std::as_const(testFrameworks())) { + framework->setActive(settings.frameworks.value(framework->id(), false)); + framework->setGrouping(settings.frameworksGrouping.value(framework->id(), false)); } - for (ITestTool *testTool : std::as_const(s_instance->m_registeredTestTools)) - testTool->setActive(settings->tools.value(testTool->id(), false)); + for (ITestTool *testTool : std::as_const(testTools())) + testTool->setActive(settings.tools.value(testTool->id(), false)); } -const TestFrameworks TestFrameworkManager::registeredFrameworks() +const TestFrameworks registeredFrameworks() { - return s_instance->m_registeredFrameworks; + return testFrameworks(); } -const TestTools TestFrameworkManager::registeredTestTools() +const TestTools registeredTestTools() { - return s_instance->m_registeredTestTools; + return testTools(); } -ITestFramework *TestFrameworkManager::frameworkForId(Id frameworkId) +ITestFramework *frameworkForId(Id frameworkId) { - return Utils::findOrDefault(s_instance->m_registeredFrameworks, - [frameworkId](ITestFramework *framework) { + return Utils::findOrDefault(testFrameworks(), [frameworkId](ITestFramework *framework) { return framework->id() == frameworkId; }); } -ITestTool *TestFrameworkManager::testToolForId(Id testToolId) +ITestTool *testToolForId(Id testToolId) { - return Utils::findOrDefault(s_instance->m_registeredTestTools, - [testToolId](ITestTool *testTool) { + return Utils::findOrDefault(testTools(), [testToolId](ITestTool *testTool) { return testTool->id() == testToolId; }); } -ITestTool *TestFrameworkManager::testToolForBuildSystemId(Id buildSystemId) +ITestTool *testToolForBuildSystemId(Id buildSystemId) { if (!buildSystemId.isValid()) return nullptr; - return Utils::findOrDefault(s_instance->m_registeredTestTools, - [&buildSystemId](ITestTool *testTool) { + return Utils::findOrDefault(testTools(), [&buildSystemId](ITestTool *testTool) { return testTool->buildSystemId() == buildSystemId; }); } -void TestFrameworkManager::synchronizeSettings(QSettings *s) +void synchronizeSettings() { - Internal::TestSettings::instance()->fromSettings(s); - for (ITestFramework *framework : std::as_const(m_registeredFrameworks)) { - if (ITestSettings *fSettings = framework->testSettings()) - fSettings->readSettings(s); - } - for (ITestTool *testTool : std::as_const(m_registeredTestTools)) { - if (ITestSettings *tSettings = testTool->testSettings()) - tSettings->readSettings(s); - } + Internal::testSettings().fromSettings(); + for (ITestFramework *framework : std::as_const(testFrameworks())) + framework->readSettings(); + + for (ITestTool *testTool : std::as_const(testTools())) + testTool->readSettings(); } -} // namespace Autotest +} // Autotest::TestframeworkManager diff --git a/src/plugins/autotest/testframeworkmanager.h b/src/plugins/autotest/testframeworkmanager.h index 73dfd96ff96..9ced25d8fb9 100644 --- a/src/plugins/autotest/testframeworkmanager.h +++ b/src/plugins/autotest/testframeworkmanager.h @@ -5,36 +5,18 @@ #include "itestframework.h" -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Autotest::TestFrameworkManager { -namespace Autotest { -namespace Internal { -class TestSettings; -} +void registerTestFramework(ITestFramework *framework); +void registerTestTool(ITestTool *testTool); +void synchronizeSettings(); -class TestFrameworkManager final -{ +ITestFramework *frameworkForId(Utils::Id frameworkId); +ITestTool *testToolForId(Utils::Id testToolId); +ITestTool *testToolForBuildSystemId(Utils::Id buildSystemId); +void activateFrameworksAndToolsFromSettings(); +const TestFrameworks registeredFrameworks(); +const TestTools registeredTestTools(); -public: - TestFrameworkManager(); - ~TestFrameworkManager(); - bool registerTestFramework(ITestFramework *framework); - bool registerTestTool(ITestTool *testTool); - void synchronizeSettings(QSettings *s); - - static ITestFramework *frameworkForId(Utils::Id frameworkId); - static ITestTool *testToolForId(Utils::Id testToolId); - static ITestTool *testToolForBuildSystemId(Utils::Id buildSystemId); - static void activateFrameworksAndToolsFromSettings(const Internal::TestSettings *settings); - static const TestFrameworks registeredFrameworks(); - static const TestTools registeredTestTools(); - -private: - TestFrameworks m_registeredFrameworks; - TestTools m_registeredTestTools; -}; - -} // namespace Autotest +} // Autotest::TestFrameworkManager diff --git a/src/plugins/autotest/testnavigationwidget.cpp b/src/plugins/autotest/testnavigationwidget.cpp index 431f5a3af89..1908af8203b 100644 --- a/src/plugins/autotest/testnavigationwidget.cpp +++ b/src/plugins/autotest/testnavigationwidget.cpp @@ -6,6 +6,7 @@ #include "autotestconstants.h" #include "autotesticons.h" #include "autotesttr.h" +#include "itemdatacache.h" #include "testcodeparser.h" #include "testframeworkmanager.h" #include "testrunner.h" @@ -25,6 +26,7 @@ #include <utils/algorithm.h> #include <utils/link.h> +#include <utils/navigationtreeview.h> #include <utils/progressindicator.h> #include <utils/stylehelper.h> #include <utils/utilsicons.h> @@ -35,13 +37,45 @@ #include <QToolButton> #include <QVBoxLayout> +using namespace Core; using namespace Utils; -namespace Autotest { -namespace Internal { +namespace Autotest::Internal { -TestNavigationWidget::TestNavigationWidget(QWidget *parent) : - QWidget(parent) +class TestNavigationWidget : public QWidget +{ +public: + TestNavigationWidget(); + + void contextMenuEvent(QContextMenuEvent *event) override; + QList<QToolButton *> createToolButtons(); + + void updateExpandedStateCache(); + +private: + void onItemActivated(const QModelIndex &index); + void onSortClicked(); + void onFilterMenuTriggered(QAction *action); + void onParsingStarted(); + void onParsingFinished(); + void initializeFilterMenu(); + void onRunThisTestTriggered(TestRunMode runMode); + void reapplyCachedExpandedState(); + + TestTreeModel *m_model; + TestTreeSortFilterModel *m_sortFilterModel; + TestTreeView *m_view; + QToolButton *m_sort; + QToolButton *m_filterButton; + QMenu *m_filterMenu; + bool m_sortAlphabetically; + Utils::ProgressIndicator *m_progressIndicator; + QTimer *m_progressTimer; + QFrame *m_missingFrameworksWidget; + ItemDataCache<bool> m_expandedStateCache; +}; + +TestNavigationWidget::TestNavigationWidget() { setWindowTitle(Tr::tr("Tests")); m_model = TestTreeModel::instance(); @@ -68,7 +102,7 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) : layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(m_missingFrameworksWidget); - layout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_view)); + layout->addWidget(ItemViewFind::createSearchableWrapper(m_view)); setLayout(layout); connect(m_view, &TestTreeView::activated, this, &TestNavigationWidget::onItemActivated); @@ -150,14 +184,14 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) } } - QAction *runAll = Core::ActionManager::command(Constants::ACTION_RUN_ALL_ID)->action(); - QAction *runSelected = Core::ActionManager::command(Constants::ACTION_RUN_SELECTED_ID)->action(); - QAction *runAllNoDeploy = Core::ActionManager::command(Constants::ACTION_RUN_ALL_NODEPLOY_ID)->action(); - QAction *runSelectedNoDeploy = Core::ActionManager::command(Constants::ACTION_RUN_SELECTED_NODEPLOY_ID)->action(); + QAction *runAll = ActionManager::command(Constants::ACTION_RUN_ALL_ID)->action(); + QAction *runSelected = ActionManager::command(Constants::ACTION_RUN_SELECTED_ID)->action(); + QAction *runAllNoDeploy = ActionManager::command(Constants::ACTION_RUN_ALL_NODEPLOY_ID)->action(); + QAction *runSelectedNoDeploy = ActionManager::command(Constants::ACTION_RUN_SELECTED_NODEPLOY_ID)->action(); QAction *selectAll = new QAction(Tr::tr("Select All"), &menu); QAction *deselectAll = new QAction(Tr::tr("Deselect All"), &menu); - // TODO remove? - QAction *rescan = Core::ActionManager::command(Constants::ACTION_SCAN_ID)->action(); + QAction *rescan = ActionManager::command(Constants::ACTION_SCAN_ID)->action(); + QAction *disable = ActionManager::command(Constants::ACTION_DISABLE_TMP)->action(); connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll); connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll); @@ -182,6 +216,8 @@ void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event) menu.addAction(deselectAll); menu.addSeparator(); menu.addAction(rescan); + menu.addSeparator(); + menu.addAction(disable); menu.exec(mapToGlobal(event->pos())); } @@ -247,7 +283,7 @@ void TestNavigationWidget::onItemActivated(const QModelIndex &index) { const Link link = index.data(LinkRole).value<Link>(); if (link.hasValidTarget()) - Core::EditorManager::openEditorAt(link); + EditorManager::openEditorAt(link); } void TestNavigationWidget::onSortClicked() @@ -326,6 +362,8 @@ void TestNavigationWidget::reapplyCachedExpandedState() } } +// TestNavigationWidgetFactory + TestNavigationWidgetFactory::TestNavigationWidgetFactory() { setDisplayName(Tr::tr("Tests")); @@ -333,11 +371,10 @@ TestNavigationWidgetFactory::TestNavigationWidgetFactory() setPriority(666); } -Core::NavigationView TestNavigationWidgetFactory::createWidget() +NavigationView TestNavigationWidgetFactory::createWidget() { TestNavigationWidget *treeViewWidget = new TestNavigationWidget; return {treeViewWidget, treeViewWidget->createToolButtons()}; } -} // namespace Internal -} // namespace Autotest +} // Autotest::Internal diff --git a/src/plugins/autotest/testnavigationwidget.h b/src/plugins/autotest/testnavigationwidget.h index 53c2bb293d6..3b598a670fc 100644 --- a/src/plugins/autotest/testnavigationwidget.h +++ b/src/plugins/autotest/testnavigationwidget.h @@ -3,78 +3,17 @@ #pragma once -#include "itemdatacache.h" - #include <coreplugin/inavigationwidgetfactory.h> -#include <utils/navigationtreeview.h> - -QT_BEGIN_NAMESPACE -class QAction; -class QMenu; -class QTimer; -class QToolButton; -QT_END_NAMESPACE - -namespace Utils { -class ProgressIndicator; -} - -namespace Autotest { - -class TestTreeModel; - -namespace Internal { - -class TestRunner; -class TestTreeSortFilterModel; -class TestTreeView; - -class TestNavigationWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TestNavigationWidget(QWidget *parent = nullptr); - void contextMenuEvent(QContextMenuEvent *event) override; - QList<QToolButton *> createToolButtons(); - - void updateExpandedStateCache(); - -private: - void onItemActivated(const QModelIndex &index); - void onSortClicked(); - void onFilterMenuTriggered(QAction *action); - void onParsingStarted(); - void onParsingFinished(); - void initializeFilterMenu(); - void onRunThisTestTriggered(TestRunMode runMode); - void reapplyCachedExpandedState(); - - TestTreeModel *m_model; - TestTreeSortFilterModel *m_sortFilterModel; - TestTreeView *m_view; - QToolButton *m_sort; - QToolButton *m_filterButton; - QMenu *m_filterMenu; - bool m_sortAlphabetically; - Utils::ProgressIndicator *m_progressIndicator; - QTimer *m_progressTimer; - QFrame *m_missingFrameworksWidget; - ItemDataCache<bool> m_expandedStateCache; -}; +namespace Autotest::Internal { class TestNavigationWidgetFactory : public Core::INavigationWidgetFactory { - Q_OBJECT - public: TestNavigationWidgetFactory(); private: Core::NavigationView createWidget() override; - }; -} // namespace Internal -} // namespace Autotest +} // Autotest::Internal diff --git a/src/plugins/autotest/testprojectsettings.cpp b/src/plugins/autotest/testprojectsettings.cpp index dd0c05079cc..0b75dd9804c 100644 --- a/src/plugins/autotest/testprojectsettings.cpp +++ b/src/plugins/autotest/testprojectsettings.cpp @@ -23,7 +23,7 @@ static const char SK_ACTIVE_FRAMEWORKS[] = "AutoTest.ActiveFrameworks"; static const char SK_RUN_AFTER_BUILD[] = "AutoTest.RunAfterBuild"; static const char SK_CHECK_STATES[] = "AutoTest.CheckStates"; -static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.frameworkmanager", QtWarningMsg) +static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.projectsettings", QtWarningMsg) TestProjectSettings::TestProjectSettings(ProjectExplorer::Project *project) : m_project(project) @@ -78,15 +78,15 @@ void TestProjectSettings::load() m_activeTestFrameworks.clear(); m_activeTestTools.clear(); if (activeFrameworks.isValid()) { - const QMap<QString, QVariant> frameworksMap = activeFrameworks.toMap(); + const Store frameworksMap = storeFromVariant(activeFrameworks); for (ITestFramework *framework : registeredFrameworks) { const Id id = framework->id(); - bool active = frameworksMap.value(id.toString(), framework->active()).toBool(); + bool active = frameworksMap.value(id.toKey(), framework->active()).toBool(); m_activeTestFrameworks.insert(framework, active); } for (ITestTool *testTool : registeredTestTools) { const Id id = testTool->id(); - bool active = frameworksMap.value(id.toString(), testTool->active()).toBool(); + bool active = frameworksMap.value(id.toKey(), testTool->active()).toBool(); m_activeTestTools.insert(testTool, active); } } else { diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp index c14b6c17032..14fe1f646f9 100644 --- a/src/plugins/autotest/testresultdelegate.cpp +++ b/src/plugins/autotest/testresultdelegate.cpp @@ -161,10 +161,10 @@ void TestResultDelegate::clearCache() void TestResultDelegate::limitTextOutput(QString &output) const { - int maxLineCount = Internal::TestSettings::instance()->resultDescriptionMaxSize(); + int maxLineCount = testSettings().resultDescriptionMaxSize(); bool limited = false; - if (Internal::TestSettings::instance()->limitResultDescription() && maxLineCount > 0) { + if (testSettings().limitResultDescription() && maxLineCount > 0) { int index = -1; int lastChar = output.size() - 1; @@ -182,7 +182,7 @@ void TestResultDelegate::limitTextOutput(QString &output) const } } - if (TestSettings::instance()->limitResultOutput() && output.length() > outputLimit) { + if (testSettings().limitResultOutput() && output.length() > outputLimit) { output = output.left(outputLimit); limited = true; } diff --git a/src/plugins/autotest/testresultmodel.cpp b/src/plugins/autotest/testresultmodel.cpp index 41c53ac100d..bca407b0375 100644 --- a/src/plugins/autotest/testresultmodel.cpp +++ b/src/plugins/autotest/testresultmodel.cpp @@ -11,6 +11,7 @@ #include "testtreemodel.h" #include <projectexplorer/projectexplorericons.h> +#include <utils/algorithm.h> #include <utils/qtcassert.h> #include <QFontMetrics> @@ -215,10 +216,7 @@ bool TestResultItem::updateDescendantTypes(ResultType t) if (t == ResultType::TestStart || t == ResultType::TestEnd) // these are special return false; - if (m_descendantsTypes.contains(t)) - return false; - m_descendantsTypes.insert(t); - return true; + return Utils::insert(m_descendantsTypes, t); } bool TestResultItem::descendantTypesContainsAnyOf(const QSet<ResultType> &types) const @@ -286,7 +284,7 @@ void TestResultModel::addTestResult(const TestResult &testResult, bool autoExpan TestResultItem *newItem = new TestResultItem(testResult); TestResultItem *root = nullptr; - if (TestSettings::instance()->displayApplication()) { + if (testSettings().displayApplication()) { const QString application = testResult.id(); if (!application.isEmpty()) { root = rootItem()->findFirstLevelChild([&application](TestResultItem *child) { @@ -479,8 +477,7 @@ void TestResultFilterModel::enableAllResultTypes(bool enabled) void TestResultFilterModel::toggleTestResultType(ResultType type) { - if (m_enabled.contains(type)) { - m_enabled.remove(type); + if (m_enabled.remove(type)) { if (type == ResultType::MessageInternal) m_enabled.remove(ResultType::TestEnd); if (type == ResultType::MessageDebug) diff --git a/src/plugins/autotest/testresultspane.cpp b/src/plugins/autotest/testresultspane.cpp index 457b273a4a1..d22ad892e45 100644 --- a/src/plugins/autotest/testresultspane.cpp +++ b/src/plugins/autotest/testresultspane.cpp @@ -56,6 +56,7 @@ namespace Internal { ResultsTreeView::ResultsTreeView(QWidget *parent) : TreeView(parent) { + setUniformRowHeights(false); setAttribute(Qt::WA_MacShowFocusRect, false); setFrameStyle(NoFrame); } @@ -73,6 +74,9 @@ TestResultsPane::TestResultsPane(QObject *parent) : IOutputPane(parent), m_context(new IContext(this)) { + setId("TestResults"); + setDisplayName(Tr::tr("Test Results")); + setPriorityInStatusBar(-30); m_outputWidget = new QStackedWidget; QWidget *visualOutputWidget = new QWidget; m_outputWidget->addWidget(visualOutputWidget); @@ -273,16 +277,6 @@ QList<QWidget *> TestResultsPane::toolBarWidgets() const return result; } -QString TestResultsPane::displayName() const -{ - return Tr::tr("Test Results"); -} - -int TestResultsPane::priorityInStatusBar() const -{ - return -666; -} - void TestResultsPane::clearContents() { m_filterModel->clearTestResults(); @@ -291,7 +285,7 @@ void TestResultsPane::clearContents() setIconBadgeNumber(0); navigateStateChanged(); m_summaryWidget->setVisible(false); - m_autoScroll = TestSettings::instance()->autoScroll(); + m_autoScroll = testSettings().autoScroll(); connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection); m_textOutput->clear(); @@ -437,7 +431,7 @@ void TestResultsPane::onRunSelectedTriggered() void TestResultsPane::initializeFilterMenu() { - const bool omitIntern = TestSettings::instance()->omitInternalMsg(); + const bool omitIntern = testSettings().omitInternalMsg(); // FilterModel has all messages enabled by default if (omitIntern) m_filterModel->toggleTestResultType(ResultType::MessageInternal); @@ -553,8 +547,7 @@ void TestResultsPane::onTestRunFinished() m_model->removeCurrentTestMessage(); disconnect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged, this, &TestResultsPane::onScrollBarRangeChanged); - if (TestSettings::instance()->popupOnFinish() - && (!TestSettings::instance()->popupOnFail() || hasFailedTests(m_model))) { + if (testSettings().popupOnFinish() && (!testSettings().popupOnFail() || hasFailedTests(m_model))) { popup(IOutputPane::NoModeSwitch); } createMarks(); diff --git a/src/plugins/autotest/testresultspane.h b/src/plugins/autotest/testresultspane.h index 530b17c644c..d762890c97c 100644 --- a/src/plugins/autotest/testresultspane.h +++ b/src/plugins/autotest/testresultspane.h @@ -60,8 +60,6 @@ public: // IOutputPane interface QWidget *outputWidget(QWidget *parent) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void setFocus() override; bool hasFocus() const override; diff --git a/src/plugins/autotest/testrunconfiguration.h b/src/plugins/autotest/testrunconfiguration.h index 1e89949ce13..fb81fb4d890 100644 --- a/src/plugins/autotest/testrunconfiguration.h +++ b/src/plugins/autotest/testrunconfiguration.h @@ -23,7 +23,8 @@ class TestRunConfiguration : public ProjectExplorer::RunConfiguration { public: TestRunConfiguration(ProjectExplorer::Target *parent, TestConfiguration *config) - : ProjectExplorer::RunConfiguration(parent, "AutoTest.TestRunConfig") + : ProjectExplorer::RunConfiguration(parent, "AutoTest.TestRunConfig"), + debuggerAspect(parent) { setDefaultDisplayName(QCoreApplication::translate("QtC::Autotest", "AutoTest Debug")); @@ -31,15 +32,15 @@ public: if (auto debuggable = dynamic_cast<DebuggableTestConfiguration *>(config)) enableQuick = debuggable->mixedDebugging(); - auto debugAspect = addAspect<Debugger::DebuggerRunConfigurationAspect>(parent); - debugAspect->setUseQmlDebugger(enableQuick); + registerAspect(&debuggerAspect); + debuggerAspect.setUseQmlDebugger(enableQuick); ProjectExplorer::ProjectExplorerPlugin::updateRunActions(); m_testConfig = config; } - ProjectExplorer::Runnable runnable() const override + Utils::ProcessRunData runnable() const override { - ProjectExplorer::Runnable r; + Utils::ProcessRunData r; QTC_ASSERT(m_testConfig, return r); r.command.setExecutable(m_testConfig->executableFilePath()); r.command.addArgs(m_testConfig->argumentsForTestRunner().join(' '), Utils::CommandLine::Raw); @@ -50,6 +51,7 @@ public: private: TestConfiguration *m_testConfig = nullptr; + Debugger::DebuggerRunConfigurationAspect debuggerAspect; }; } // namespace Internal diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index d355317b5eb..ecfd2dc54dd 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -16,7 +16,7 @@ #include <coreplugin/icore.h> #include <coreplugin/progressmanager/taskprogress.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerruncontrol.h> #include <projectexplorer/buildconfiguration.h> @@ -258,7 +258,7 @@ static RunConfiguration *getRunConfiguration(const QString &buildTargetKey) int TestRunner::precheckTestConfigurations() { - const bool omitWarnings = TestSettings::instance()->omitRunConfigWarn(); + const bool omitWarnings = testSettings().omitRunConfigWarn(); int testCaseCount = 0; for (ITestConfiguration *itc : std::as_const(m_selectedTests)) { if (itc->testBase()->type() == ITestBase::Tool) { @@ -402,7 +402,7 @@ void TestRunner::runTestsHelper() } process.setEnvironment(environment); - m_cancelTimer.setInterval(TestSettings::instance()->timeout()); + m_cancelTimer.setInterval(testSettings().timeout()); m_cancelTimer.start(); qCInfo(runnerLog) << "Command:" << process.commandLine().executable(); @@ -446,7 +446,7 @@ void TestRunner::runTestsHelper() }; const Group group { finishAllAndDone, - Storage(storage), + Tasking::Storage(storage), onGroupSetup(onSetup), ProcessTask(onProcessSetup, onProcessDone, onProcessDone) }; @@ -468,7 +468,7 @@ void TestRunner::runTestsHelper() cancelCurrent(UserCanceled); }); - if (TestSettings::instance()->popupOnStart()) + if (testSettings().popupOnStart()) AutotestPlugin::popupResultsPane(); m_taskTree->start(); @@ -539,7 +539,7 @@ void TestRunner::debugTests() runControl->copyDataFromRunConfiguration(config->runConfiguration()); QStringList omitted; - Runnable inferior = config->runnable(); + ProcessRunData inferior = config->runnable(); inferior.command.setExecutable(commandFilePath); const QStringList args = config->argumentsForTestRunner(&omitted); @@ -589,9 +589,8 @@ void TestRunner::debugTests() runControl, &RunControl::initiateStop); connect(runControl, &RunControl::stopped, this, &TestRunner::onFinished); - m_finishDebugConnect = connect(runControl, &RunControl::finished, this, &TestRunner::onFinished); ProjectExplorerPlugin::startRunControl(runControl); - if (useOutputProcessor && TestSettings::instance()->popupOnStart()) + if (useOutputProcessor && testSettings().popupOnStart()) AutotestPlugin::popupResultsPane(); } @@ -672,10 +671,10 @@ static RunAfterBuildMode runAfterBuild() return RunAfterBuildMode::None; if (!project->namedSettings(Constants::SK_USE_GLOBAL).isValid()) - return TestSettings::instance()->runAfterBuildMode(); + return testSettings().runAfterBuildMode(); TestProjectSettings *projectSettings = AutotestPlugin::projectSettings(project); - return projectSettings->useGlobalSettings() ? TestSettings::instance()->runAfterBuildMode() + return projectSettings->useGlobalSettings() ? testSettings().runAfterBuildMode() : projectSettings->runAfterBuild(); } @@ -705,7 +704,6 @@ void TestRunner::onFinished() if (m_taskTree) m_taskTree.release()->deleteLater(); disconnect(m_stopDebugConnect); - disconnect(m_finishDebugConnect); disconnect(m_targetConnect); qDeleteAll(m_selectedTests); m_selectedTests.clear(); diff --git a/src/plugins/autotest/testrunner.h b/src/plugins/autotest/testrunner.h index 25c65f89851..8d0aac6d811 100644 --- a/src/plugins/autotest/testrunner.h +++ b/src/plugins/autotest/testrunner.h @@ -79,7 +79,6 @@ private: QMetaObject::Connection m_buildConnect; // temporarily used when debugging QMetaObject::Connection m_stopDebugConnect; - QMetaObject::Connection m_finishDebugConnect; // temporarily used for handling of switching the current target QMetaObject::Connection m_targetConnect; QTimer m_cancelTimer; diff --git a/src/plugins/autotest/testsettings.cpp b/src/plugins/autotest/testsettings.cpp index b4507a9d9ab..1d5230d711d 100644 --- a/src/plugins/autotest/testsettings.cpp +++ b/src/plugins/autotest/testsettings.cpp @@ -7,7 +7,9 @@ #include "autotesttr.h" #include "testframeworkmanager.h" -#include <QSettings> +#include <utils/qtcsettings.h> + +using namespace Utils; namespace Autotest::Internal { @@ -15,19 +17,22 @@ static const char groupSuffix[] = ".group"; constexpr int defaultTimeout = 60000; -static TestSettings *s_instance; - -TestSettings *TestSettings::instance() +TestSettings &testSettings() { - return s_instance; + static TestSettings theSettings; + return theSettings; } TestSettings::TestSettings() { - s_instance = this; - setSettingsGroup(Constants::SETTINGSGROUP); + scanThreadLimit.setSettingsKey("ScanThreadLimit"); + scanThreadLimit.setDefaultValue(0); + scanThreadLimit.setRange(0, QThread::idealThreadCount()); + scanThreadLimit.setSpecialValueText("Automatic"); + scanThreadLimit.setToolTip(Tr::tr("Number of worker threads used when scanning for tests.")); + timeout.setSettingsKey("Timeout"); timeout.setDefaultValue(defaultTimeout); timeout.setRange(5000, 36'000'000); // 36 Mio ms = 36'000 s = 10 h @@ -102,28 +107,30 @@ TestSettings::TestSettings() runAfterBuild.addOption(Tr::tr("Selected")); } -void TestSettings::toSettings(QSettings *s) const +void TestSettings::toSettings() const { - AspectContainer::writeSettings(s); + AspectContainer::writeSettings(); + QtcSettings *s = BaseAspect::qtcSettings(); s->beginGroup(Constants::SETTINGSGROUP); // store frameworks and their current active and grouping state for (auto it = frameworks.cbegin(); it != frameworks.cend(); ++it) { const Utils::Id &id = it.key(); - s->setValue(id.toString(), it.value()); - s->setValue(id.toString() + groupSuffix, frameworksGrouping.value(id)); + s->setValue(id.toKey(), it.value()); + s->setValue(id.toKey() + groupSuffix, frameworksGrouping.value(id)); } // ..and the testtools as well for (auto it = tools.cbegin(); it != tools.cend(); ++it) - s->setValue(it.key().toString(), it.value()); + s->setValue(it.key().toKey(), it.value()); s->endGroup(); } -void TestSettings::fromSettings(QSettings *s) +void TestSettings::fromSettings() { - AspectContainer::readSettings(s); + AspectContainer::readSettings(); + QtcSettings *s = BaseAspect::qtcSettings(); s->beginGroup(Constants::SETTINGSGROUP); // try to get settings for registered frameworks @@ -132,8 +139,8 @@ void TestSettings::fromSettings(QSettings *s) frameworksGrouping.clear(); for (const ITestFramework *framework : registered) { // get their active state - const Utils::Id id = framework->id(); - const QString key = id.toString(); + const Id id = framework->id(); + const Key key = id.toKey(); frameworks.insert(id, s->value(key, framework->active()).toBool()); // and whether grouping is enabled frameworksGrouping.insert(id, s->value(key + groupSuffix, framework->grouping()).toBool()); @@ -143,14 +150,14 @@ void TestSettings::fromSettings(QSettings *s) tools.clear(); for (const ITestTool *testTool : registeredTools) { const Utils::Id id = testTool->id(); - tools.insert(id, s->value(id.toString(), testTool->active()).toBool()); + tools.insert(id, s->value(id.toKey(), testTool->active()).toBool()); } s->endGroup(); } RunAfterBuildMode TestSettings::runAfterBuildMode() const { - return static_cast<RunAfterBuildMode>(runAfterBuild.value()); + return static_cast<RunAfterBuildMode>(runAfterBuild()); } } // namespace Autotest::Internal diff --git a/src/plugins/autotest/testsettings.h b/src/plugins/autotest/testsettings.h index 1e33269bd4f..d73f3d3454b 100644 --- a/src/plugins/autotest/testsettings.h +++ b/src/plugins/autotest/testsettings.h @@ -27,11 +27,10 @@ class TestSettings : public Utils::AspectContainer, public NonAspectSettings public: TestSettings(); - static TestSettings *instance(); - - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + void toSettings() const; + void fromSettings(); + Utils::IntegerAspect scanThreadLimit{this}; Utils::IntegerAspect timeout{this}; Utils::BoolAspect omitInternalMsg{this}; Utils::BoolAspect omitRunConfigWarn{this}; @@ -49,4 +48,6 @@ public: RunAfterBuildMode runAfterBuildMode() const; }; +TestSettings &testSettings(); + } // Autotest::Internal diff --git a/src/plugins/autotest/testsettingspage.cpp b/src/plugins/autotest/testsettingspage.cpp index 6c9850bc2ac..985206238fd 100644 --- a/src/plugins/autotest/testsettingspage.cpp +++ b/src/plugins/autotest/testsettingspage.cpp @@ -10,7 +10,6 @@ #include "testsettings.h" #include "testtreemodel.h" -#include <coreplugin/icore.h> #include <utils/algorithm.h> #include <utils/id.h> #include <utils/infolabel.h> @@ -51,6 +50,8 @@ TestSettingsWidget::TestSettingsWidget() { auto timeoutLabel = new QLabel(Tr::tr("Timeout:")); timeoutLabel->setToolTip(Tr::tr("Timeout used when executing each test case.")); + auto scanThreadLabel = new QLabel(Tr::tr("Scan threads:")); + scanThreadLabel->setToolTip("Number of worker threads used when scanning for tests."); m_frameworkTreeWidget = new QTreeWidget; m_frameworkTreeWidget->setRootIsDecorated(false); @@ -79,10 +80,11 @@ TestSettingsWidget::TestSettingsWidget() onClicked([] { AutotestPlugin::clearChoiceCache(); }, this) }; - TestSettings &s = *TestSettings::instance(); + TestSettings &s = Internal::testSettings(); Group generalGroup { title(Tr::tr("General")), Column { + Row { scanThreadLabel, s.scanThreadLimit, st }, s.omitInternalMsg, s.omitRunConfigWarn, s.limitResultOutput, @@ -121,7 +123,7 @@ TestSettingsWidget::TestSettingsWidget() populateFrameworksListWidget(s.frameworks, s.tools); setOnApply([this] { - TestSettings &s = *TestSettings::instance(); + TestSettings &s = Internal::testSettings(); NonAspectSettings tmp; testSettings(tmp); @@ -134,7 +136,7 @@ TestSettingsWidget::TestSettingsWidget() testSettings(s); testToolsSettings(s); - s.toSettings(Core::ICore::settings()); + s.toSettings(); for (ITestFramework *framework : TestFrameworkManager::registeredFrameworks()) { framework->setActive(s.frameworks.value(framework->id(), false)); diff --git a/src/plugins/autotest/testtreeitem.cpp b/src/plugins/autotest/testtreeitem.cpp index 69481b60cad..adfacf7731f 100644 --- a/src/plugins/autotest/testtreeitem.cpp +++ b/src/plugins/autotest/testtreeitem.cpp @@ -331,7 +331,7 @@ ITestConfiguration *TestTreeItem::asConfiguration(TestRunMode mode) const QList<ITestConfiguration *> TestTreeItem::getTestConfigurationsForFile(const FilePath &) const { - return QList<ITestConfiguration *>(); + return {}; } bool TestTreeItem::isGroupNodeFor(const TestTreeItem *other) const diff --git a/src/plugins/autotest/testtreemodel.cpp b/src/plugins/autotest/testtreemodel.cpp index 7d0dd3954d0..85b8156ff39 100644 --- a/src/plugins/autotest/testtreemodel.cpp +++ b/src/plugins/autotest/testtreemodel.cpp @@ -500,6 +500,40 @@ void TestTreeModel::sweep() #endif } +QString TestTreeModel::report(bool full) const +{ + QString result; + int items = 0; + QString tree; + for (TestTreeItem *rootNode : frameworkRootNodes()) { + int itemsPerRoot = 0; + result.append("\n"); + result += rootNode->name(); + result.append(" > "); + + if (full) { + TestTreeSortFilterModel sortFilterModel(const_cast<TestTreeModel *>(this)); + sortFilterModel.setDynamicSortFilter(true); + sortFilterModel.sort(0); + tree = "\n" + sortFilterModel.report(); + rootNode->forAllChildren([&itemsPerRoot](TreeItem *) { + ++itemsPerRoot; + }); + + } else { + rootNode->forAllChildren([&itemsPerRoot](TreeItem *) { + ++itemsPerRoot; + }); + } + result.append(QString::number(itemsPerRoot)); + items += itemsPerRoot; + } + result.append("\nItems: " + QString::number(items)); + if (full) + return tree + '\n' + result; + return result; +} + /** * @note after calling this function emit testTreeModelChanged() if it returns true */ @@ -577,7 +611,8 @@ void TestTreeModel::insertItemInParent(TestTreeItem *item, TestTreeItem *root, b delete item; } else { // restore former check state if available - std::optional<Qt::CheckState> cached = m_checkStateCache->get(item); + std::optional<Qt::CheckState> cached = m_checkStateCache ? m_checkStateCache->get(item) + : std::optional<Qt::CheckState>{}; if (cached.has_value()) item->setData(0, cached.value(), Qt::CheckStateRole); else @@ -891,6 +926,26 @@ TestTreeSortFilterModel::FilterMode TestTreeSortFilterModel::toFilterMode(int f) } } +static QString dumpIndex(const QModelIndex &idx, int level = 0) +{ + QString result; + result.append(QString(level, ' ')); + result.append(idx.data().toString() + '\n'); + for (int row = 0, end = idx.model()->rowCount(idx); row < end; ++row) + result.append(dumpIndex(idx.model()->index(row, 0, idx), level + 1)); + return result; +} + +QString TestTreeSortFilterModel::report() const +{ + QString result; + for (int row = 0, end = rowCount(); row < end; ++row) { + auto idx = index(row, 0); + result.append(dumpIndex(idx)); + } + return result; +} + bool TestTreeSortFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { // root items keep the intended order diff --git a/src/plugins/autotest/testtreemodel.h b/src/plugins/autotest/testtreemodel.h index c48c957d999..291c6d4017d 100644 --- a/src/plugins/autotest/testtreemodel.h +++ b/src/plugins/autotest/testtreemodel.h @@ -68,6 +68,7 @@ public: void markAllFrameworkItemsForRemoval(); void markForRemoval(const QSet<Utils::FilePath> &filePaths); void sweep(); + QString report(bool full) const; signals: void testTreeModelChanged(); @@ -117,6 +118,7 @@ public: void toggleFilter(FilterMode filterMode); static FilterMode toFilterMode(int f); + QString report() const; protected: bool lessThan(const QModelIndex &left, const QModelIndex &right) const final; bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const final; diff --git a/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in b/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in index 5e3a7d5850b..399a70dd3a6 100644 --- a/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in +++ b/src/plugins/autotoolsprojectmanager/AutotoolsProjectManager.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"AutotoolsProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"Openismus GmbH\", - \"Copyright\" : \"(C) 2016 Openismus GmbH, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "AutotoolsProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "Openismus GmbH", + "Copyright" : "(C) 2016 Openismus GmbH, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Autotools project integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Build Systems", + "Description" : "Autotools project integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/autotoolsprojectmanager/CMakeLists.txt b/src/plugins/autotoolsprojectmanager/CMakeLists.txt index 0536178881e..261403ce8c5 100644 --- a/src/plugins/autotoolsprojectmanager/CMakeLists.txt +++ b/src/plugins/autotoolsprojectmanager/CMakeLists.txt @@ -8,7 +8,7 @@ add_qtc_plugin(AutotoolsProjectManager autotoolsbuildsystem.cpp autotoolsbuildsystem.h autotoolsprojectconstants.h autotoolsprojectmanagertr.h - autotoolsprojectplugin.cpp autotoolsprojectplugin.h + autotoolsprojectplugin.cpp configurestep.cpp configurestep.h makefileparser.cpp makefileparser.h makefileparserthread.cpp makefileparserthread.h diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp index 090389716de..ec54184478b 100644 --- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp +++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp @@ -14,6 +14,7 @@ #include <projectexplorer/target.h> #include <utils/aspects.h> +#include <utils/process.h> #include <QDateTime> @@ -39,7 +40,7 @@ public: AutogenStep(BuildStepList *bsl, Id id); private: - void doRun() final; + Tasking::GroupItem runRecipe() final; bool m_runAutogen = false; StringAspect m_arguments{this}; @@ -69,29 +70,33 @@ AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, i }); } -void AutogenStep::doRun() +Tasking::GroupItem AutogenStep::runRecipe() { - // Check whether we need to run autogen.sh - const FilePath projectDir = project()->projectDirectory(); - const FilePath configure = projectDir / "configure"; - const FilePath configureAc = projectDir / "configure.ac"; - const FilePath makefileAm = projectDir / "Makefile.am"; + using namespace Tasking; - if (!configure.exists() - || configure.lastModified() < configureAc.lastModified() - || configure.lastModified() < makefileAm.lastModified()) { - m_runAutogen = true; - } + const auto onSetup = [this] { + // Check whether we need to run autogen.sh + const FilePath projectDir = project()->projectDirectory(); + const FilePath configure = projectDir / "configure"; + const FilePath configureAc = projectDir / "configure.ac"; + const FilePath makefileAm = projectDir / "Makefile.am"; - if (!m_runAutogen) { - emit addOutput(Tr::tr("Configuration unchanged, skipping autogen step."), - OutputFormat::NormalMessage); - emit finished(true); - return; - } + if (!configure.exists() + || configure.lastModified() < configureAc.lastModified() + || configure.lastModified() < makefileAm.lastModified()) { + m_runAutogen = true; + } - m_runAutogen = false; - AbstractProcessStep::doRun(); + if (!m_runAutogen) { + emit addOutput(Tr::tr("Configuration unchanged, skipping autogen step."), + OutputFormat::NormalMessage); + return SetupResult::StopWithDone; + } + return SetupResult::Continue; + }; + const auto onDone = [this] { m_runAutogen = false; }; + + return Group { onGroupSetup(onSetup), onGroupDone(onDone), defaultProcessTask() }; } // AutogenStepFactory diff --git a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp index a238f41b688..9dea7d6f7fb 100644 --- a/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp +++ b/src/plugins/autotoolsprojectmanager/autoreconfstep.cpp @@ -11,9 +11,9 @@ #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/target.h> #include <utils/aspects.h> +#include <utils/process.h> using namespace ProjectExplorer; using namespace Utils; @@ -34,58 +34,60 @@ namespace AutotoolsProjectManager::Internal { class AutoreconfStep final : public AbstractProcessStep { public: - AutoreconfStep(BuildStepList *bsl, Id id); + AutoreconfStep(BuildStepList *bsl, Id id) + : AbstractProcessStep(bsl, id) + { + arguments.setSettingsKey("AutotoolsProjectManager.AutoreconfStep.AdditionalArguments"); + arguments.setLabelText(Tr::tr("Arguments:")); + arguments.setValue("--force --install"); + arguments.setDisplayStyle(StringAspect::LineEditDisplay); + arguments.setHistoryCompleter("AutotoolsPM.History.AutoreconfStepArgs"); - void doRun() override; + connect(&arguments, &BaseAspect::changed, this, [this] { m_runAutoreconf = true; }); -private: - bool m_runAutoreconf = false; -}; + setCommandLineProvider([this] { + return CommandLine("autoreconf", arguments(), CommandLine::Raw); + }); -AutoreconfStep::AutoreconfStep(BuildStepList *bsl, Id id) - : AbstractProcessStep(bsl, id) -{ - auto arguments = addAspect<StringAspect>(); - arguments->setSettingsKey("AutotoolsProjectManager.AutoreconfStep.AdditionalArguments"); - arguments->setLabelText(Tr::tr("Arguments:")); - arguments->setValue("--force --install"); - arguments->setDisplayStyle(StringAspect::LineEditDisplay); - arguments->setHistoryCompleter("AutotoolsPM.History.AutoreconfStepArgs"); + setWorkingDirectoryProvider([this] { + return project()->projectDirectory(); + }); - connect(arguments, &BaseAspect::changed, this, [this] { - m_runAutoreconf = true; - }); - - setCommandLineProvider([arguments] { - return CommandLine("autoreconf", arguments->value(), CommandLine::Raw); - }); - - setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); - - setSummaryUpdater([this] { - ProcessParameters param; - setupProcessParameters(¶m); - return param.summary(displayName()); - }); -} - -void AutoreconfStep::doRun() -{ - // Check whether we need to run autoreconf - const FilePath configure = project()->projectDirectory() / "configure"; - if (!configure.exists()) - m_runAutoreconf = true; - - if (!m_runAutoreconf) { - emit addOutput(Tr::tr("Configuration unchanged, skipping autoreconf step."), - OutputFormat::NormalMessage); - emit finished(true); - return; + setSummaryUpdater([this] { + ProcessParameters param; + setupProcessParameters(¶m); + return param.summary(displayName()); + }); } - m_runAutoreconf = false; - AbstractProcessStep::doRun(); -} +private: + Tasking::GroupItem runRecipe() final + { + using namespace Tasking; + + const auto onSetup = [this] { + // Check whether we need to run autoreconf + const FilePath configure = project()->projectDirectory() / "configure"; + if (!configure.exists()) + m_runAutoreconf = true; + + if (!m_runAutoreconf) { + emit addOutput(::AutotoolsProjectManager::Tr::tr( + "Configuration unchanged, skipping autoreconf step."), + OutputFormat::NormalMessage); + return SetupResult::StopWithDone; + } + return SetupResult::Continue; + }; + const auto onDone = [this] { m_runAutoreconf = false; }; + + return Group { onGroupSetup(onSetup), onGroupDone(onDone), defaultProcessTask() }; + } + + bool m_runAutoreconf = false; + StringAspect arguments{this}; +}; + // AutoreconfStepFactory diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.qbs b/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.qbs index ffe9690a35c..d9da0c1af9d 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.qbs +++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectmanager.qbs @@ -23,7 +23,6 @@ QtcPlugin { "autotoolsprojectconstants.h", "autotoolsprojectmanagertr.h", "autotoolsprojectplugin.cpp", - "autotoolsprojectplugin.h", "configurestep.cpp", "configurestep.h", "makefileparser.cpp", diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.cpp b/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.cpp index 4a338024731..7dd53126506 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.cpp @@ -1,8 +1,6 @@ // Copyright (C) 2016 Openismus GmbH. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "autotoolsprojectplugin.h" - #include "autogenstep.h" #include "autoreconfstep.h" #include "autotoolsbuildconfiguration.h" @@ -18,6 +16,10 @@ #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> +#include <extensionsystem/iplugin.h> + +using namespace ProjectExplorer; + namespace AutotoolsProjectManager::Internal { /** @@ -28,7 +30,7 @@ namespace AutotoolsProjectManager::Internal { * It is responsible to parse the Makefile.am files and do trigger project * updates if a Makefile.am file or a configure.ac file has been changed. */ -class AutotoolsProject : public ProjectExplorer::Project +class AutotoolsProject : public Project { public: explicit AutotoolsProject(const Utils::FilePath &fileName) @@ -40,33 +42,61 @@ public: setHasMakeInstallEquivalent(true); - setBuildSystemCreator([](ProjectExplorer::Target *t) { return new AutotoolsBuildSystem(t); }); + setBuildSystemCreator([](Target *t) { return new AutotoolsBuildSystem(t); }); } }; +/** + * @brief Implementation of the ExtensionsSystem::IPlugin interface. + * + * The plugin creates the following components: + * + * - AutotoolsManager: Will manage the new autotools project and + * tell QtCreator for which MIME types the autotools project should + * be instantiated. + * + * - MakeStepFactory: This factory is used to create make steps. + * + * - AutogenStepFactory: This factory is used to create autogen steps. + * + * - AutoreconfStepFactory: This factory is used to create autoreconf + * steps. + * + * - ConfigureStepFactory: This factory is used to create configure steps. + * + * - MakefileEditorFactory: Provides a specialized editor with automatic + * syntax highlighting for Makefile.am files. + * + * - AutotoolsTargetFactory: Our current target is desktop. + * + * - AutotoolsBuildConfigurationFactory: Creates build configurations that + * contain the steps (make, autogen, autoreconf or configure) that will + * be executed in the build process) + */ class AutotoolsProjectPluginPrivate { public: - AutotoolsBuildConfigurationFactory buildConfigurationFactory; - MakeStepFactory makeStepFaactory; + AutotoolsBuildConfigurationFactory buildConfigFactory; + MakeStepFactory makeStepFactory; AutogenStepFactory autogenStepFactory; ConfigureStepFactory configureStepFactory; AutoreconfStepFactory autoreconfStepFactory; }; -AutotoolsProjectPlugin::~AutotoolsProjectPlugin() +class AutotoolsProjectPlugin final : public ExtensionSystem::IPlugin { - delete d; -} + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AutotoolsProjectManager.json") -void AutotoolsProjectPlugin::extensionsInitialized() -{ } + void initialize() final + { + d = std::make_unique<AutotoolsProjectPluginPrivate>(); + } -void AutotoolsProjectPlugin::initialize() -{ - d = new AutotoolsProjectPluginPrivate; - ProjectExplorer::ProjectManager::registerProjectType<AutotoolsProject>(Constants::MAKEFILE_MIMETYPE); -} + std::unique_ptr<AutotoolsProjectPluginPrivate> d; +}; } // AutotoolsProjectManager::Internal + +#include "autotoolsprojectplugin.moc" diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h b/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h deleted file mode 100644 index a705da891d5..00000000000 --- a/src/plugins/autotoolsprojectmanager/autotoolsprojectplugin.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2016 Openismus GmbH. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace AutotoolsProjectManager::Internal { - -/** - * @brief Implementation of the ExtensionsSystem::IPlugin interface. - * - * The plugin creates the following components: - * - * - AutotoolsManager: Will manage the new autotools project and - * tell QtCreator for which MIME types the autotools project should - * be instantiated. - * - * - MakeStepFactory: This factory is used to create make steps. - * - * - AutogenStepFactory: This factory is used to create autogen steps. - * - * - AutoreconfStepFactory: This factory is used to create autoreconf - * steps. - * - * - ConfigureStepFactory: This factory is used to create configure steps. - * - * - MakefileEditorFactory: Provides a specialized editor with automatic - * syntax highlighting for Makefile.am files. - * - * - AutotoolsTargetFactory: Our current target is desktop. - * - * - AutotoolsBuildConfigurationFactory: Creates build configurations that - * contain the steps (make, autogen, autoreconf or configure) that will - * be executed in the build process) - */ - -class AutotoolsProjectPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AutotoolsProjectManager.json") - - ~AutotoolsProjectPlugin() final; - - void extensionsInitialized() final; - void initialize() final; - - class AutotoolsProjectPluginPrivate *d; -}; - -} // AutotoolsProjectManager::Internal diff --git a/src/plugins/autotoolsprojectmanager/configurestep.cpp b/src/plugins/autotoolsprojectmanager/configurestep.cpp index 6975a1cd91a..14c7a3eefa6 100644 --- a/src/plugins/autotoolsprojectmanager/configurestep.cpp +++ b/src/plugins/autotoolsprojectmanager/configurestep.cpp @@ -13,6 +13,7 @@ #include <projectexplorer/target.h> #include <utils/aspects.h> +#include <utils/process.h> #include <QDateTime> @@ -37,71 +38,69 @@ namespace AutotoolsProjectManager::Internal { class ConfigureStep final : public AbstractProcessStep { public: - ConfigureStep(BuildStepList *bsl, Id id); + ConfigureStep(BuildStepList *bsl, Id id) + : AbstractProcessStep(bsl, id) + { + arguments.setDisplayStyle(StringAspect::LineEditDisplay); + arguments.setSettingsKey("AutotoolsProjectManager.ConfigureStep.AdditionalArguments"); + arguments.setLabelText(Tr::tr("Arguments:")); + arguments.setHistoryCompleter("AutotoolsPM.History.ConfigureArgs"); - void setAdditionalArguments(const QString &list); + connect(&arguments, &BaseAspect::changed, this, [this] { + m_runConfigure = true; + }); + + setCommandLineProvider([this] { + return getCommandLine(arguments()); + }); + + setSummaryUpdater([this] { + ProcessParameters param; + setupProcessParameters(¶m); + + return param.summaryInWorkdir(displayName()); + }); + } private: - void doRun() final; + Tasking::GroupItem runRecipe() final; - CommandLine getCommandLine(const QString &arguments); + CommandLine getCommandLine(const QString &arguments) + { + return {project()->projectDirectory() / "configure", arguments, CommandLine::Raw}; + } bool m_runConfigure = false; + StringAspect arguments{this}; }; -ConfigureStep::ConfigureStep(BuildStepList *bsl, Id id) - : AbstractProcessStep(bsl, id) +Tasking::GroupItem ConfigureStep::runRecipe() { - auto arguments = addAspect<StringAspect>(); - arguments->setDisplayStyle(StringAspect::LineEditDisplay); - arguments->setSettingsKey("AutotoolsProjectManager.ConfigureStep.AdditionalArguments"); - arguments->setLabelText(Tr::tr("Arguments:")); - arguments->setHistoryCompleter("AutotoolsPM.History.ConfigureArgs"); + using namespace Tasking; - connect(arguments, &BaseAspect::changed, this, [this] { - m_runConfigure = true; - }); + const auto onSetup = [this] { + // Check whether we need to run configure + const FilePath configure = project()->projectDirectory() / "configure"; + const FilePath configStatus = buildDirectory() / "config.status"; - setCommandLineProvider([this, arguments] { - return getCommandLine(arguments->value()); - }); + if (!configStatus.exists() || configStatus.lastModified() < configure.lastModified()) + m_runConfigure = true; - setSummaryUpdater([this] { - ProcessParameters param; - setupProcessParameters(¶m); + if (!m_runConfigure) { + emit addOutput(Tr::tr("Configuration unchanged, skipping configure step."), OutputFormat::NormalMessage); + return SetupResult::StopWithDone; + } - return param.summaryInWorkdir(displayName()); - }); -} + ProcessParameters *param = processParameters(); + if (!param->effectiveCommand().exists()) { + param->setCommandLine(getCommandLine(param->command().arguments())); + setSummaryText(param->summaryInWorkdir(displayName())); + } + return SetupResult::Continue; + }; + const auto onDone = [this] { m_runConfigure = false; }; -CommandLine ConfigureStep::getCommandLine(const QString &arguments) -{ - return {project()->projectDirectory() / "configure", arguments, CommandLine::Raw}; -} - -void ConfigureStep::doRun() -{ - // Check whether we need to run configure - const FilePath configure = project()->projectDirectory() / "configure"; - const FilePath configStatus = buildDirectory() / "config.status"; - - if (!configStatus.exists() || configStatus.lastModified() < configure.lastModified()) - m_runConfigure = true; - - if (!m_runConfigure) { - emit addOutput(Tr::tr("Configuration unchanged, skipping configure step."), OutputFormat::NormalMessage); - emit finished(true); - return; - } - - ProcessParameters *param = processParameters(); - if (!param->effectiveCommand().exists()) { - param->setCommandLine(getCommandLine(param->command().arguments())); - setSummaryText(param->summaryInWorkdir(displayName())); - } - - m_runConfigure = false; - AbstractProcessStep::doRun(); + return Group { onGroupSetup(onSetup), onGroupDone(onDone), defaultProcessTask() }; } // ConfigureStepFactory diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.cpp b/src/plugins/autotoolsprojectmanager/makefileparser.cpp index e1cac6b1866..8a2f684f877 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.cpp +++ b/src/plugins/autotoolsprojectmanager/makefileparser.cpp @@ -300,7 +300,7 @@ QStringList MakefileParser::directorySources(const QString &directory, { if (isCanceled()) { m_success = false; - return QStringList(); + return {}; } emit status(Tr::tr("Parsing directory %1").arg(directory)); @@ -343,7 +343,7 @@ QStringList MakefileParser::targetValues(bool *hasVariables) const int index = m_line.indexOf(QLatin1Char('=')); if (index < 0) { m_success = false; - return QStringList(); + return {}; } m_line.remove(0, index + 1); // remove the 'target = ' prefix @@ -423,7 +423,7 @@ QStringList MakefileParser::parseTermsAfterAssign(const QString &line) { int assignPos = line.indexOf(QLatin1Char('=')) + 1; if (assignPos <= 0 || assignPos >= line.size()) - return QStringList(); + return {}; const QStringList parts = ProcessArgs::splitArgs(line.mid(assignPos), HostOsInfo::hostOs()); QStringList result; diff --git a/src/plugins/autotoolsprojectmanager/makefileparser.h b/src/plugins/autotoolsprojectmanager/makefileparser.h index 2d4786e0dab..17d39b73533 100644 --- a/src/plugins/autotoolsprojectmanager/makefileparser.h +++ b/src/plugins/autotoolsprojectmanager/makefileparser.h @@ -12,7 +12,9 @@ #include <atomic> -QT_FORWARD_DECLARE_CLASS(QDir) +QT_BEGIN_NAMESPACE +class QDir; +QT_END_NAMESPACE namespace AutotoolsProjectManager::Internal { diff --git a/src/plugins/axivion/Axivion.json.in b/src/plugins/axivion/Axivion.json.in index fac0520565f..50958b9e9da 100644 --- a/src/plugins/axivion/Axivion.json.in +++ b/src/plugins/axivion/Axivion.json.in @@ -1,21 +1,21 @@ { - \"Name\" : \"Axivion\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Revision\" : \"$$QTC_PLUGIN_REVISION\", - \"Experimental\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Axivion", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Revision" : "${QTC_PLUGIN_REVISION}", + "Experimental" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"Integration of the axivion dashboard.\", - \"Url\" : \"https://www.qt.io\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "Integration of the axivion dashboard.", + "Url" : "https://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/axivion/CMakeLists.txt b/src/plugins/axivion/CMakeLists.txt index b346cc276dc..c71df674750 100644 --- a/src/plugins/axivion/CMakeLists.txt +++ b/src/plugins/axivion/CMakeLists.txt @@ -10,6 +10,8 @@ add_qtc_plugin(Axivion axivionquery.h axivionquery.cpp axivionresultparser.h axivionresultparser.cpp axivionsettings.cpp axivionsettings.h - axivionsettingspage.cpp axivionsettingspage.h axiviontr.h + dashboard/dto.cpp dashboard/dto.h + dashboard/concat.cpp dashboard/concat.h + dashboard/dashboardclient.cpp dashboard/dashboardclient.h ) diff --git a/src/plugins/axivion/axivion.qbs b/src/plugins/axivion/axivion.qbs index bfe8efe6cf5..bdd20a89349 100644 --- a/src/plugins/axivion/axivion.qbs +++ b/src/plugins/axivion/axivion.qbs @@ -25,8 +25,22 @@ QtcPlugin { "axivionresultparser.cpp", "axivionsettings.cpp", "axivionsettings.h", - "axivionsettingspage.cpp", - "axivionsettingspage.h", "axiviontr.h", + "dashboard/dashboardclient.cpp", + "dashboard/dashboardclient.h", ] + + cpp.includePaths: base.concat(["."]) // needed for the generated stuff below + + Group { + name: "Generated DTOs" + prefix: "dashboard/" + + files: [ + "concat.cpp", + "concat.h", + "dto.cpp", + "dto.h", + ] + } } diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index f36df8d0f17..6e7836af8e5 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -4,8 +4,8 @@ #include "axivionoutputpane.h" #include "axivionplugin.h" -#include "axivionresultparser.h" #include "axiviontr.h" +#include "dashboard/dto.h" #include <utils/qtcassert.h> #include <utils/utilsicons.h> @@ -18,6 +18,9 @@ #include <QTextBrowser> #include <QToolButton> +#include <map> +#include <memory> + namespace Axivion::Internal { class DashboardWidget : public QScrollArea @@ -58,7 +61,7 @@ DashboardWidget::DashboardWidget(QWidget *parent) setWidgetResizable(true); } -static QPixmap trendIcon(int added, int removed) +static QPixmap trendIcon(qint64 added, qint64 removed) { static const QPixmap unchanged = Utils::Icons::NEXT.pixmap(); static const QPixmap increased = Utils::Icon( @@ -70,10 +73,20 @@ static QPixmap trendIcon(int added, int removed) return added < removed ? decreased : increased; } +static qint64 extract_value(const std::map<QString, Dto::Any> &map, const QString &key) +{ + const auto search = map.find(key); + if (search == map.end()) + return 0; + const Dto::Any &value = search->second; + if (!value.isDouble()) + return 0; + return static_cast<qint64>(value.getDouble()); +} + void DashboardWidget::updateUi() { - const ProjectInfo &info = AxivionPlugin::projectInfo(); - m_project->setText(info.name); + m_project->setText({}); m_loc->setText({}); m_timestamp->setText({}); QLayoutItem *child; @@ -81,65 +94,87 @@ void DashboardWidget::updateUi() delete child->widget(); delete child; } - - if (info.versions.isEmpty()) + std::shared_ptr<const DashboardClient::ProjectInfo> projectInfo = AxivionPlugin::projectInfo(); + if (!projectInfo) + return; + const Dto::ProjectInfoDto &info = projectInfo->data; + m_project->setText(info.name); + if (info.versions.empty()) return; - const ResultVersion &last = info.versions.last(); - m_loc->setText(QString::number(last.linesOfCode)); - const QDateTime timeStamp = QDateTime::fromString(last.timeStamp, Qt::ISODate); - m_timestamp->setText(timeStamp.isValid() ? timeStamp.toString("yyyy-MM-dd HH::mm::ss") + const Dto::AnalysisVersionDto &last = info.versions.back(); + if (last.linesOfCode.has_value()) + m_loc->setText(QString::number(last.linesOfCode.value())); + const QDateTime timeStamp = QDateTime::fromString(last.date, Qt::ISODate); + m_timestamp->setText(timeStamp.isValid() ? timeStamp.toString("yyyy-MM-dd HH:mm:ss t") : Tr::tr("unknown")); - const QList<IssueKind> &issueKinds = info.issueKinds; + const std::vector<Dto::IssueKindInfoDto> &issueKinds = info.issueKinds; auto toolTip = [issueKinds](const QString &prefix){ - for (const IssueKind &kind : issueKinds) { + for (const Dto::IssueKindInfoDto &kind : issueKinds) { if (kind.prefix == prefix) - return kind.nicePlural; + return kind.nicePluralName; } return prefix; }; - auto addValuesWidgets = [this, &toolTip](const IssueCount &issueCount, int row){ - const QString currentToolTip = toolTip(issueCount.issueKind); - QLabel *label = new QLabel(issueCount.issueKind, this); + auto addValuesWidgets = [this, &toolTip](const QString &issueKind, qint64 total, qint64 added, qint64 removed, int row) { + const QString currentToolTip = toolTip(issueKind); + QLabel *label = new QLabel(issueKind, this); label->setToolTip(currentToolTip); m_gridLayout->addWidget(label, row, 0); - label = new QLabel(QString::number(issueCount.total), this); + label = new QLabel(QString::number(total), this); label->setToolTip(currentToolTip); label->setAlignment(Qt::AlignRight); m_gridLayout->addWidget(label, row, 1); label = new QLabel(this); - label->setPixmap(trendIcon(issueCount.added, issueCount.removed)); + label->setPixmap(trendIcon(added, removed)); label->setToolTip(currentToolTip); m_gridLayout->addWidget(label, row, 2); - label = new QLabel('+' + QString::number(issueCount.added)); + label = new QLabel('+' + QString::number(added)); label->setAlignment(Qt::AlignRight); label->setToolTip(currentToolTip); m_gridLayout->addWidget(label, row, 3); label = new QLabel("/"); label->setToolTip(currentToolTip); m_gridLayout->addWidget(label, row, 4); - label = new QLabel('-' + QString::number(issueCount.removed)); + label = new QLabel('-' + QString::number(removed)); label->setAlignment(Qt::AlignRight); label->setToolTip(currentToolTip); m_gridLayout->addWidget(label, row, 5); }; - int allTotal = 0, allAdded = 0, allRemoved = 0, row = 0; - for (auto issueCount : std::as_const(last.issueCounts)) { - allTotal += issueCount.total; - allAdded += issueCount.added; - allRemoved += issueCount.removed; - addValuesWidgets(issueCount, row); - ++row; + qint64 allTotal = 0; + qint64 allAdded = 0; + qint64 allRemoved = 0; + qint64 row = 0; + // This code is overly complex because of a heedlessness in the + // Axivion Dashboard API definition. Other Axivion IDE plugins do + // not use the issue counts, thus the QtCreator Axivion Plugin + // is going to stop using them, too. + if (last.issueCounts.isMap()) { + for (const Dto::Any::MapEntry &issueCount : last.issueCounts.getMap()) { + if (issueCount.second.isMap()) { + const Dto::Any::Map &counts = issueCount.second.getMap(); + qint64 total = extract_value(counts, QStringLiteral(u"Total")); + allTotal += total; + qint64 added = extract_value(counts, QStringLiteral(u"Added")); + allAdded += added; + qint64 removed = extract_value(counts, QStringLiteral(u"Removed")); + allRemoved += removed; + addValuesWidgets(issueCount.first, total, added, removed, row); + ++row; + } + } } - - const IssueCount total{{}, Tr::tr("Total:"), allTotal, allAdded, allRemoved}; - addValuesWidgets(total, row); + addValuesWidgets(Tr::tr("Total:"), allTotal, allAdded, allRemoved, row); } AxivionOutputPane::AxivionOutputPane(QObject *parent) : Core::IOutputPane(parent) { + setId("Axivion"); + setDisplayName(Tr::tr("Axivion")); + setPriorityInStatusBar(-50); + m_outputWidget = new QStackedWidget; DashboardWidget *dashboardWidget = new DashboardWidget(m_outputWidget); m_outputWidget->addWidget(dashboardWidget); @@ -176,16 +211,6 @@ QList<QWidget *> AxivionOutputPane::toolBarWidgets() const return buttons; } -QString AxivionOutputPane::displayName() const -{ - return Tr::tr("Axivion"); -} - -int AxivionOutputPane::priorityInStatusBar() const -{ - return -1; -} - void AxivionOutputPane::clearContents() { } diff --git a/src/plugins/axivion/axivionoutputpane.h b/src/plugins/axivion/axivionoutputpane.h index d11accc1401..140d50b7235 100644 --- a/src/plugins/axivion/axivionoutputpane.h +++ b/src/plugins/axivion/axivionoutputpane.h @@ -21,8 +21,6 @@ public: // IOutputPane interface QWidget *outputWidget(QWidget *parent) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void setFocus() override; bool hasFocus() const override; diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index 7648448132a..a22885afb01 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -8,8 +8,9 @@ #include "axivionquery.h" #include "axivionresultparser.h" #include "axivionsettings.h" -#include "axivionsettingspage.h" #include "axiviontr.h" +#include "dashboard/dashboardclient.h" +#include "dashboard/dto.h" #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/editormanager.h> @@ -27,13 +28,22 @@ #include <texteditor/texteditor.h> #include <texteditor/textmark.h> +#include <utils/algorithm.h> +#include <utils/expected.h> +#include <utils/networkaccessmanager.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> #include <QAction> +#include <QFutureWatcher> #include <QMessageBox> +#include <QNetworkAccessManager> +#include <QNetworkReply> #include <QTimer> +#include <exception> +#include <memory> + constexpr char AxivionTextMarkId[] = "AxivionTextMark"; namespace Axivion::Internal { @@ -41,10 +51,11 @@ namespace Axivion::Internal { class AxivionPluginPrivate : public QObject { public: - AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project); + AxivionPluginPrivate(); + void handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors); void onStartupProjectChanged(); void fetchProjectInfo(const QString &projectName); - void handleProjectInfo(const ProjectInfo &info); + void handleProjectInfo(DashboardClient::RawProjectInfo rawInfo); void handleOpenedDocs(ProjectExplorer::Project *project); void onDocumentOpened(Core::IDocument *doc); void onDocumentClosed(Core::IDocument * doc); @@ -52,15 +63,12 @@ public: void handleIssuesForFile(const IssuesList &issues); void fetchRuleInfo(const QString &id); - AxivionSettings m_axivionSettings; - AxivionSettingsPage m_axivionSettingsPage{&m_axivionSettings}; + Utils::NetworkAccessManager m_networkAccessManager; AxivionOutputPane m_axivionOutputPane; - QHash<ProjectExplorer::Project *, AxivionProjectSettings *> m_axivionProjectSettings; - ProjectInfo m_currentProjectInfo; + std::shared_ptr<const DashboardClient::ProjectInfo> m_currentProjectInfo; bool m_runningQuery = false; }; -static AxivionPlugin *s_instance = nullptr; static AxivionPluginPrivate *dd = nullptr; class AxivionTextMark : public TextEditor::TextMark @@ -91,40 +99,21 @@ AxivionTextMark::AxivionTextMark(const Utils::FilePath &filePath, const ShortIss }); } -AxivionPlugin::AxivionPlugin() -{ - s_instance = this; -} - AxivionPlugin::~AxivionPlugin() { - if (dd && !dd->m_axivionProjectSettings.isEmpty()) { - qDeleteAll(dd->m_axivionProjectSettings); - dd->m_axivionProjectSettings.clear(); - } + AxivionProjectSettings::destroyProjectSettings(); delete dd; dd = nullptr; } -AxivionPlugin *AxivionPlugin::instance() +void AxivionPlugin::initialize() { - return s_instance; -} - -bool AxivionPlugin::initialize(const QStringList &arguments, QString *errorMessage) -{ - Q_UNUSED(arguments) - Q_UNUSED(errorMessage) - dd = new AxivionPluginPrivate; - dd->m_axivionSettings.fromSettings(Core::ICore::settings()); auto panelFactory = new ProjectExplorer::ProjectPanelFactory; panelFactory->setPriority(250); panelFactory->setDisplayName(Tr::tr("Axivion")); - panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project){ - return new AxivionProjectSettingsWidget(project); - }); + panelFactory->setCreateWidgetFunction(&AxivionProjectSettings::createSettingsWidget); ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); connect(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::startupProjectChanged, @@ -133,39 +122,6 @@ bool AxivionPlugin::initialize(const QStringList &arguments, QString *errorMessa dd, &AxivionPluginPrivate::onDocumentOpened); connect(Core::EditorManager::instance(), &Core::EditorManager::documentClosed, dd, &AxivionPluginPrivate::onDocumentClosed); - return true; -} - -AxivionSettings *AxivionPlugin::settings() -{ - QTC_ASSERT(dd, return nullptr); - return &dd->m_axivionSettings; -} - -AxivionProjectSettings *AxivionPlugin::projectSettings(ProjectExplorer::Project *project) -{ - QTC_ASSERT(project, return nullptr); - QTC_ASSERT(dd, return nullptr); - - return dd->projectSettings(project); -} - -bool AxivionPlugin::handleCertificateIssue() -{ - QTC_ASSERT(dd, return false); - - const QString serverHost = QUrl(dd->m_axivionSettings.server.dashboard).host(); - if (QMessageBox::question(Core::ICore::dialogParent(), Tr::tr("Certificate Error"), - Tr::tr("Server certificate for %1 cannot be authenticated.\n" - "Do you want to disable SSL verification for this server?\n" - "Note: This can expose you to man-in-the-middle attack.") - .arg(serverHost)) - != QMessageBox::Yes) { - return false; - } - dd->m_axivionSettings.server.validateCert = false; - emit s_instance->settingsChanged(); - return true; } void AxivionPlugin::fetchProjectInfo(const QString &projectName) @@ -174,18 +130,57 @@ void AxivionPlugin::fetchProjectInfo(const QString &projectName) dd->fetchProjectInfo(projectName); } -ProjectInfo AxivionPlugin::projectInfo() +std::shared_ptr<const DashboardClient::ProjectInfo> AxivionPlugin::projectInfo() { QTC_ASSERT(dd, return {}); return dd->m_currentProjectInfo; } -AxivionProjectSettings *AxivionPluginPrivate::projectSettings(ProjectExplorer::Project *project) +// FIXME: extend to give some details? +// FIXME: move when curl is no more in use? +bool AxivionPlugin::handleCertificateIssue() { - auto &settings = m_axivionProjectSettings[project]; - if (!settings) - settings = new AxivionProjectSettings(project); - return settings; + QTC_ASSERT(dd, return false); + const QString serverHost = QUrl(settings().server.dashboard).host(); + if (QMessageBox::question(Core::ICore::dialogParent(), Tr::tr("Certificate Error"), + Tr::tr("Server certificate for %1 cannot be authenticated.\n" + "Do you want to disable SSL verification for this server?\n" + "Note: This can expose you to man-in-the-middle attack.") + .arg(serverHost)) + != QMessageBox::Yes) { + return false; + } + settings().server.validateCert = false; + settings().apply(); + + return true; +} + +AxivionPluginPrivate::AxivionPluginPrivate() +{ +#if QT_CONFIG(ssl) + connect(&m_networkAccessManager, &QNetworkAccessManager::sslErrors, + this, &AxivionPluginPrivate::handleSslErrors); +#endif // ssl +} + +void AxivionPluginPrivate::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors) +{ +#if QT_CONFIG(ssl) + const QList<QSslError::SslError> accepted{ + QSslError::CertificateNotYetValid, QSslError::CertificateExpired, + QSslError::InvalidCaCertificate, QSslError::CertificateUntrusted, + QSslError::HostNameMismatch + }; + if (Utils::allOf(errors, + [&accepted](const QSslError &e) { return accepted.contains(e.error()); })) { + if (!settings().server.validateCert || AxivionPlugin::handleCertificateIssue()) + reply->ignoreSslErrors(errors); + } +#else // ssl + Q_UNUSED(reply) + Q_UNUSED(errors) +#endif // ssl } void AxivionPluginPrivate::onStartupProjectChanged() @@ -193,42 +188,44 @@ void AxivionPluginPrivate::onStartupProjectChanged() ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); if (!project) { clearAllMarks(); - m_currentProjectInfo = ProjectInfo(); + m_currentProjectInfo = {}; m_axivionOutputPane.updateDashboard(); return; } - const AxivionProjectSettings *projSettings = projectSettings(project); + const AxivionProjectSettings *projSettings = AxivionProjectSettings::projectSettings(project); fetchProjectInfo(projSettings->dashboardProjectName()); } void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) { if (m_runningQuery) { // re-schedule - QTimer::singleShot(3000, [this, projectName]{ fetchProjectInfo(projectName); }); + QTimer::singleShot(3000, this, [this, projectName] { fetchProjectInfo(projectName); }); return; } clearAllMarks(); if (projectName.isEmpty()) { - m_currentProjectInfo = ProjectInfo(); + m_currentProjectInfo = {}; m_axivionOutputPane.updateDashboard(); return; } m_runningQuery = true; - - AxivionQuery query(AxivionQuery::ProjectInfo, {projectName}); - AxivionQueryRunner *runner = new AxivionQueryRunner(query, this); - connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){ - handleProjectInfo(ResultParser::parseProjectInfo(result)); - }); - connect(runner, &AxivionQueryRunner::finished, [runner]{ runner->deleteLater(); }); - runner->start(); + DashboardClient client { this->m_networkAccessManager }; + QFuture<DashboardClient::RawProjectInfo> response = client.fetchProjectInfo(projectName); + auto responseWatcher = std::make_shared<QFutureWatcher<DashboardClient::RawProjectInfo>>(); + connect(responseWatcher.get(), + &QFutureWatcher<DashboardClient::RawProjectInfo>::finished, + this, + [this, responseWatcher]() { + handleProjectInfo(responseWatcher->result()); + }); + responseWatcher->setFuture(response); } void AxivionPluginPrivate::fetchRuleInfo(const QString &id) { if (m_runningQuery) { - QTimer::singleShot(3000, [this, id]{ fetchRuleInfo(id); }); + QTimer::singleShot(3000, this, [this, id] { fetchRuleInfo(id); }); return; } @@ -265,20 +262,15 @@ void AxivionPluginPrivate::clearAllMarks() onDocumentClosed(doc); } -void AxivionPluginPrivate::handleProjectInfo(const ProjectInfo &info) +void AxivionPluginPrivate::handleProjectInfo(DashboardClient::RawProjectInfo rawInfo) { m_runningQuery = false; - if (!info.error.isEmpty()) { - Core::MessageManager::writeFlashing("Axivion: " + info.error); + if (!rawInfo) { + Core::MessageManager::writeFlashing(QStringLiteral(u"Axivion: ") + rawInfo.error()); return; } - - m_currentProjectInfo = info; + m_currentProjectInfo = std::make_shared<const DashboardClient::ProjectInfo>(std::move(rawInfo.value())); m_axivionOutputPane.updateDashboard(); - - if (m_currentProjectInfo.name.isEmpty()) - return; - // handle already opened documents if (auto buildSystem = ProjectExplorer::ProjectManager::startupBuildSystem(); !buildSystem || !buildSystem->isParsing()) { @@ -292,7 +284,7 @@ void AxivionPluginPrivate::handleProjectInfo(const ProjectInfo &info) void AxivionPluginPrivate::onDocumentOpened(Core::IDocument *doc) { - if (m_currentProjectInfo.name.isEmpty()) // we do not have a project info (yet) + if (!m_currentProjectInfo) // we do not have a project info (yet) return; ProjectExplorer::Project *project = ProjectExplorer::ProjectManager::startupProject(); @@ -301,7 +293,7 @@ void AxivionPluginPrivate::onDocumentOpened(Core::IDocument *doc) Utils::FilePath relative = doc->filePath().relativeChildPath(project->projectDirectory()); // for now only style violations - AxivionQuery query(AxivionQuery::IssuesForFileList, {m_currentProjectInfo.name, "SV", + AxivionQuery query(AxivionQuery::IssuesForFileList, {m_currentProjectInfo->data.name, "SV", relative.path() } ); AxivionQueryRunner *runner = new AxivionQueryRunner(query, this); connect(runner, &AxivionQueryRunner::resultRetrieved, this, [this](const QByteArray &result){ diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h index 992971b6bd6..380b8630f61 100644 --- a/src/plugins/axivion/axivionplugin.h +++ b/src/plugins/axivion/axivionplugin.h @@ -3,13 +3,16 @@ #pragma once +#include "dashboard/dashboardclient.h" + #include <extensionsystem/iplugin.h> +#include <memory> + namespace ProjectExplorer { class Project; } namespace Axivion::Internal { -class AxivionSettings; class AxivionProjectSettings; class ProjectInfo; @@ -19,21 +22,15 @@ class AxivionPlugin final : public ExtensionSystem::IPlugin Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Axivion.json") public: - AxivionPlugin(); + AxivionPlugin() {} ~AxivionPlugin() final; - static AxivionPlugin *instance(); - static AxivionSettings *settings(); - static AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project); - - static bool handleCertificateIssue(); static void fetchProjectInfo(const QString &projectName); - static ProjectInfo projectInfo(); -signals: - void settingsChanged(); + static std::shared_ptr<const DashboardClient::ProjectInfo> projectInfo(); + static bool handleCertificateIssue(); private: - bool initialize(const QStringList &arguments, QString *errorMessage) final; + void initialize() final; void extensionsInitialized() final {} }; diff --git a/src/plugins/axivion/axivionprojectsettings.cpp b/src/plugins/axivion/axivionprojectsettings.cpp index 32689ae950d..d92a601435d 100644 --- a/src/plugins/axivion/axivionprojectsettings.cpp +++ b/src/plugins/axivion/axivionprojectsettings.cpp @@ -10,6 +10,8 @@ #include "axiviontr.h" #include <projectexplorer/project.h> +#include <projectexplorer/projectsettingswidget.h> + #include <utils/infolabel.h> #include <utils/qtcassert.h> @@ -17,10 +19,43 @@ #include <QTreeWidget> #include <QVBoxLayout> +using namespace ProjectExplorer; +using namespace Utils; + namespace Axivion::Internal { const char PSK_PROJECTNAME[] = "Axivion.ProjectName"; +// AxivionProjectSettingsHandler + +class AxivionProjectSettingsHandler : public QObject +{ +public: + AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project) + { + auto &settings = m_axivionProjectSettings[project]; + if (!settings) + settings = new AxivionProjectSettings(project); + return settings; + } + + void destroy() + { + qDeleteAll(m_axivionProjectSettings); + m_axivionProjectSettings.clear(); + } + + QHash<ProjectExplorer::Project *, AxivionProjectSettings *> m_axivionProjectSettings; +}; + +static AxivionProjectSettingsHandler &projectSettingsHandler() +{ + static AxivionProjectSettingsHandler theProjectSettingsHandler; + return theProjectSettingsHandler; +} + +// AxivionProjectSettings + AxivionProjectSettings::AxivionProjectSettings(ProjectExplorer::Project *project) : m_project{project} { @@ -31,6 +66,16 @@ AxivionProjectSettings::AxivionProjectSettings(ProjectExplorer::Project *project this, &AxivionProjectSettings::save); } +AxivionProjectSettings *AxivionProjectSettings::projectSettings(ProjectExplorer::Project *project) +{ + return projectSettingsHandler().projectSettings(project); +} + +void AxivionProjectSettings::destroyProjectSettings() +{ + projectSettingsHandler().destroy(); +} + void AxivionProjectSettings::load() { m_dashboardProjectName = m_project->namedSettings(PSK_PROJECTNAME).toString(); @@ -41,11 +86,33 @@ void AxivionProjectSettings::save() m_project->setNamedSettings(PSK_PROJECTNAME, m_dashboardProjectName); } -AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Project *project, - QWidget *parent) - : ProjectExplorer::ProjectSettingsWidget{parent} - , m_projectSettings(AxivionPlugin::projectSettings(project)) - , m_globalSettings(AxivionPlugin::settings()) +// AxivionProjectSettingsWidget + +class AxivionProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget +{ +public: + explicit AxivionProjectSettingsWidget(ProjectExplorer::Project *project); + +private: + void fetchProjects(); + void onDashboardInfoReceived(const DashboardInfo &info); + void onSettingsChanged(); + void linkProject(); + void unlinkProject(); + void updateUi(); + void updateEnabledStates(); + + AxivionProjectSettings *m_projectSettings = nullptr; + QLabel *m_linkedProject = nullptr; + QTreeWidget *m_dashboardProjects = nullptr; + QPushButton *m_fetchProjects = nullptr; + QPushButton *m_link = nullptr; + QPushButton *m_unlink = nullptr; + Utils::InfoLabel *m_infoLabel = nullptr; +}; + +AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Project *project) + : m_projectSettings(projectSettingsHandler().projectSettings(project)) { setUseGlobalSettingsCheckBoxVisible(false); setUseGlobalSettingsLabelVisible(true); @@ -87,7 +154,7 @@ AxivionProjectSettingsWidget::AxivionProjectSettingsWidget(ProjectExplorer::Proj this, &AxivionProjectSettingsWidget::linkProject); connect(m_unlink, &QPushButton::clicked, this, &AxivionProjectSettingsWidget::unlinkProject); - connect(AxivionPlugin::instance(), &AxivionPlugin::settingsChanged, + connect(&settings(), &AspectContainer::changed, this, &AxivionProjectSettingsWidget::onSettingsChanged); updateUi(); @@ -163,9 +230,9 @@ void AxivionProjectSettingsWidget::updateUi() void AxivionProjectSettingsWidget::updateEnabledStates() { - const bool hasDashboardSettings = m_globalSettings->curl().isExecutableFile() - && !m_globalSettings->server.dashboard.isEmpty() - && !m_globalSettings->server.token.isEmpty(); + const bool hasDashboardSettings = settings().curl().isExecutableFile() + && !settings().server.dashboard.isEmpty() + && !settings().server.token.isEmpty(); const bool linked = !m_projectSettings->dashboardProjectName().isEmpty(); const bool linkable = m_dashboardProjects->topLevelItemCount() && !m_dashboardProjects->selectedItems().isEmpty(); @@ -181,4 +248,9 @@ void AxivionProjectSettingsWidget::updateEnabledStates() } } +ProjectSettingsWidget *AxivionProjectSettings::createSettingsWidget(ProjectExplorer::Project *project) +{ + return new AxivionProjectSettingsWidget(project); +} + } // Axivion::Internal diff --git a/src/plugins/axivion/axivionprojectsettings.h b/src/plugins/axivion/axivionprojectsettings.h index d5bc1fd2e62..d11b001d9f3 100644 --- a/src/plugins/axivion/axivionprojectsettings.h +++ b/src/plugins/axivion/axivionprojectsettings.h @@ -3,26 +3,15 @@ #pragma once -#include "axivionsettings.h" - -#include <projectexplorer/projectsettingswidget.h> - #include <QObject> -QT_BEGIN_NAMESPACE -class QLabel; -class QPushButton; -class QTreeWidget; -QT_END_NAMESPACE - -namespace ProjectExplorer { class Project; } - -namespace Utils { class InfoLabel; } +namespace ProjectExplorer { +class Project; +class ProjectSettingsWidget; +} namespace Axivion::Internal { -class DashboardInfo; - class AxivionProjectSettings : public QObject { public: @@ -31,6 +20,10 @@ public: void setDashboardProjectName(const QString &name) { m_dashboardProjectName = name; } QString dashboardProjectName() const { return m_dashboardProjectName; } + static AxivionProjectSettings *projectSettings(ProjectExplorer::Project *project); + static void destroyProjectSettings(); + static ProjectExplorer::ProjectSettingsWidget *createSettingsWidget(ProjectExplorer::Project *project); + private: void load(); void save(); @@ -39,29 +32,4 @@ private: QString m_dashboardProjectName; }; -class AxivionProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget -{ -public: - explicit AxivionProjectSettingsWidget(ProjectExplorer::Project *project, - QWidget *parent = nullptr); - -private: - void fetchProjects(); - void onDashboardInfoReceived(const DashboardInfo &info); - void onSettingsChanged(); - void linkProject(); - void unlinkProject(); - void updateUi(); - void updateEnabledStates(); - - AxivionProjectSettings *m_projectSettings = nullptr; - AxivionSettings *m_globalSettings; - QLabel *m_linkedProject = nullptr; - QTreeWidget *m_dashboardProjects = nullptr; - QPushButton *m_fetchProjects = nullptr; - QPushButton *m_link = nullptr; - QPushButton *m_unlink = nullptr; - Utils::InfoLabel *m_infoLabel = nullptr; -}; - } // Axivion::Internal diff --git a/src/plugins/axivion/axivionquery.cpp b/src/plugins/axivion/axivionquery.cpp index fda0eccf128..13f18229087 100644 --- a/src/plugins/axivion/axivionquery.cpp +++ b/src/plugins/axivion/axivionquery.cpp @@ -53,8 +53,7 @@ QString AxivionQuery::toString() const AxivionQueryRunner::AxivionQueryRunner(const AxivionQuery &query, QObject *parent) : QObject(parent) { - const AxivionSettings *settings = AxivionPlugin::settings(); - const AxivionServer server = settings->server; + const AxivionServer server = settings().server; QStringList args = server.curlArguments(); args << "-i"; @@ -65,7 +64,7 @@ AxivionQueryRunner::AxivionQueryRunner(const AxivionQuery &query, QObject *paren url += query.toString(); args << url; - m_process.setCommand({settings->curl(), args}); + m_process.setCommand({settings().curl(), args}); connect(&m_process, &Process::done, this, [this]{ if (m_process.result() != ProcessResult::FinishedWithSuccess) { const int exitCode = m_process.exitCode(); diff --git a/src/plugins/axivion/axivionresultparser.cpp b/src/plugins/axivion/axivionresultparser.cpp index 0d8bb27b7db..cd4ec0b11c6 100644 --- a/src/plugins/axivion/axivionresultparser.cpp +++ b/src/plugins/axivion/axivionresultparser.cpp @@ -10,6 +10,7 @@ #include <QJsonObject> #include <QRegularExpression> +#include <stdexcept> #include <utility> namespace Axivion::Internal { @@ -79,124 +80,6 @@ static std::pair<BaseResult, QJsonDocument> prehandleHeaderAndBody(const QByteAr return {result, doc}; } -static User::UserType userTypeForString(const QString &type) -{ - if (type == "DASHBOARD_USER") - return User::Dashboard; - if (type == "VIRTUAL_USER") - return User::Virtual; - return User::Unknown; -} - -static User userFromJson(const QJsonObject &object) -{ - User result; - if (object.isEmpty()) { - result.error = "Not a user object."; - return result; - } - result.name = object.value("name").toString(); - result.displayName = object.value("displayName").toString(); - result.type = userTypeForString(object.value("type").toString()); - return result; -} - -static QList<User> usersFromJson(const QJsonArray &array) -{ - QList<User> result; - for (const QJsonValue &value : array) { - User user = userFromJson(value.toObject()); - if (!user.error.isEmpty()) // add this error to result.error? - continue; - result.append(user); - } - return result; -} - -static IssueCount issueCountFromJson(const QJsonObject &object) -{ - IssueCount result; - if (object.isEmpty()) { - result.error = "Not an issue count object."; - return result; - } - result.added = object.value("Added").toInt(); - result.removed = object.value("Removed").toInt(); - result.total = object.value("Total").toInt(); - return result; -} - -static QList<IssueCount> issueCountsFromJson(const QJsonObject &object) -{ - QList<IssueCount> result; - - const QStringList keys = object.keys(); - for (const QString &k : keys) { - IssueCount issue = issueCountFromJson(object.value(k).toObject()); - if (!issue.error.isEmpty()) // add this error to result.error? - continue; - issue.issueKind = k; - result.append(issue); - } - return result; -} - -static ResultVersion versionFromJson(const QJsonObject &object) -{ - ResultVersion result; - if (object.isEmpty()) { - result.error = "Not a version object."; - return result; - } - const QJsonValue issuesValue = object.value("issueCounts"); - if (!issuesValue.isObject()) { - result.error = "Not an object (issueCounts)."; - return result; - } - result.issueCounts = issueCountsFromJson(issuesValue.toObject()); - result.timeStamp = object.value("date").toString(); - result.name = object.value("name").toString(); - result.linesOfCode = object.value("linesOfCode").toInt(); - return result; -} - -static QList<ResultVersion> versionsFromJson(const QJsonArray &array) -{ - QList<ResultVersion> result; - for (const QJsonValue &value : array) { - ResultVersion version = versionFromJson(value.toObject()); - if (!version.error.isEmpty()) // add this error to result.error? - continue; - result.append(version); - } - return result; -} - -static IssueKind issueKindFromJson(const QJsonObject &object) -{ - IssueKind result; - if (object.isEmpty()) { - result.error = "Not an issue kind object."; - return result; - } - result.prefix = object.value("prefix").toString(); - result.niceSingular = object.value("niceSingularName").toString(); - result.nicePlural = object.value("nicePluralName").toString(); - return result; -} - -static QList<IssueKind> issueKindsFromJson(const QJsonArray &array) -{ - QList<IssueKind> result; - for (const QJsonValue &value : array) { - IssueKind kind = issueKindFromJson(value.toObject()); - if (!kind.error.isEmpty()) // add this error to result.error? - continue; - result.append(kind); - } - return result; -} - namespace ResultParser { DashboardInfo parseDashboardInfo(const QByteArray &input) @@ -236,43 +119,6 @@ DashboardInfo parseDashboardInfo(const QByteArray &input) return result; } -ProjectInfo parseProjectInfo(const QByteArray &input) -{ - ProjectInfo result; - - auto [header, body] = splitHeaderAndBody(input); - auto [error, doc] = prehandleHeaderAndBody(header, body); - if (!error.error.isEmpty()) { - result.error = error.error; - return result; - } - - const QJsonObject object = doc.object(); - result.name = object.value("name").toString(); - - const QJsonValue usersValue = object.value("users"); - if (!usersValue.isArray()) { - result.error = "Malformed json response (users)."; - return result; - } - result.users = usersFromJson(usersValue.toArray()); - - const QJsonValue versionsValue = object.value("versions"); - if (!versionsValue.isArray()) { - result.error = "Malformed json response (versions)."; - return result; - } - result.versions = versionsFromJson(versionsValue.toArray()); - - const QJsonValue issueKindsValue = object.value("issueKinds"); - if (!issueKindsValue.isArray()) { - result.error = "Malformed json response (issueKinds)."; - return result; - } - result.issueKinds = issueKindsFromJson(issueKindsValue.toArray()); - return result; -} - static QRegularExpression issueCsvLineRegex(const QByteArray &firstCsvLine) { QString pattern = "^"; diff --git a/src/plugins/axivion/axivionresultparser.h b/src/plugins/axivion/axivionresultparser.h index 84c5a6b8ca8..d8d4a39a72c 100644 --- a/src/plugins/axivion/axivionresultparser.h +++ b/src/plugins/axivion/axivionresultparser.h @@ -3,6 +3,10 @@ #pragma once +#include "dashboard/dto.h" + +#include <utils/expected.h> + #include <QList> namespace Axivion::Internal { @@ -27,49 +31,6 @@ public: QList<Project> projects; }; -class User : public BaseResult -{ -public: - QString name; - QString displayName; - enum UserType { Dashboard, Virtual, Unknown } type; -}; - -class IssueKind : public BaseResult -{ -public: - QString prefix; - QString niceSingular; - QString nicePlural; -}; - -class IssueCount : public BaseResult -{ -public: - QString issueKind; - int total = 0; - int added = 0; - int removed = 0; -}; - -class ResultVersion : public BaseResult -{ -public: - QString name; - QString timeStamp; - QList<IssueCount> issueCounts; - int linesOfCode = 0; -}; - -class ProjectInfo : public BaseResult -{ -public: - QString name; - QList<User> users; - QList<ResultVersion> versions; - QList<IssueKind> issueKinds; -}; - class ShortIssue : public BaseResult { public: @@ -92,7 +53,7 @@ public: namespace ResultParser { DashboardInfo parseDashboardInfo(const QByteArray &input); -ProjectInfo parseProjectInfo(const QByteArray &input); +Utils::expected_str<Dto::ProjectInfoDto> parseProjectInfo(const QByteArray &input); IssuesList parseIssuesList(const QByteArray &input); QString parseRuleInfo(const QByteArray &input); diff --git a/src/plugins/axivion/axivionsettings.cpp b/src/plugins/axivion/axivionsettings.cpp index 4a183631633..b18dcfa34aa 100644 --- a/src/plugins/axivion/axivionsettings.cpp +++ b/src/plugins/axivion/axivionsettings.cpp @@ -5,13 +5,26 @@ #include "axiviontr.h" -#include <utils/filepath.h> -#include <utils/hostosinfo.h> +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/icore.h> +#include <utils/hostosinfo.h> +#include <utils/id.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> + +#include <QDialog> +#include <QDialogButtonBox> #include <QJsonDocument> #include <QJsonObject> -#include <QSettings> +#include <QLabel> +#include <QPushButton> +#include <QRegularExpression> #include <QStandardPaths> +#include <QUuid> +#include <QVBoxLayout> + +using namespace Utils; namespace Axivion::Internal { @@ -72,18 +85,9 @@ QStringList AxivionServer::curlArguments() const return args; } -AxivionSettings::AxivionSettings() +static Utils::FilePath tokensFilePath() { - setSettingsGroup("Axivion"); - - curl.setSettingsKey("Curl"); - curl.setLabelText(Tr::tr("curl:")); - curl.setExpectedKind(Utils::PathChooser::ExistingCommand); -} - -static Utils::FilePath tokensFilePath(const QSettings *s) -{ - return Utils::FilePath::fromString(s->fileName()).parentDir() + return Utils::FilePath::fromString(Core::ICore::settings()->fileName()).parentDir() .pathAppended("qtcreator/axivion.json"); } @@ -109,23 +113,231 @@ static AxivionServer readTokenFile(const Utils::FilePath &filePath) return AxivionServer::fromJson(doc.object()); } -void AxivionSettings::toSettings(QSettings *s) const +// AxivionSetting + +AxivionSettings &settings() { - writeTokenFile(tokensFilePath(s), server); - Utils::AspectContainer::writeSettings(s); + static AxivionSettings theSettings; + return theSettings; } -void AxivionSettings::fromSettings(QSettings *s) +AxivionSettings::AxivionSettings() { - Utils::AspectContainer::readSettings(s); - server = readTokenFile(tokensFilePath(s)); + setSettingsGroup("Axivion"); + + curl.setSettingsKey("Curl"); + curl.setLabelText(Tr::tr("curl:")); + curl.setExpectedKind(Utils::PathChooser::ExistingCommand); + + AspectContainer::readSettings(); + + server = readTokenFile(tokensFilePath()); if (curl().isEmpty() || !curl().exists()) { const QString curlPath = QStandardPaths::findExecutable( - Utils::HostOsInfo::withExecutableSuffix("curl")); + HostOsInfo::withExecutableSuffix("curl")); if (!curlPath.isEmpty()) - curl.setFilePath(Utils::FilePath::fromString(curlPath)); + curl.setValue(FilePath::fromString(curlPath)); } } +void AxivionSettings::toSettings() const +{ + writeTokenFile(tokensFilePath(), server); + Utils::AspectContainer::writeSettings(); +} + +// AxivionSettingsPage + +// may allow some invalid, but does some minimal check for legality +static bool hostValid(const QString &host) +{ + static const QRegularExpression ip(R"(^(\d+).(\d+).(\d+).(\d+)$)"); + static const QRegularExpression dn(R"(^([a-zA-Z0-9][a-zA-Z0-9-]+\.)+[a-zA-Z0-9][a-zA-Z0-9-]+$)"); + const QRegularExpressionMatch match = ip.match(host); + if (match.hasMatch()) { + for (int i = 1; i < 5; ++i) { + int val = match.captured(i).toInt(); + if (val < 0 || val > 255) + return false; + } + return true; + } + return (host == "localhost") || dn.match(host).hasMatch(); +} + +static bool isUrlValid(const QString &in) +{ + const QUrl url(in); + return hostValid(url.host()) && (url.scheme() == "https" || url.scheme() == "http"); +} + +class DashboardSettingsWidget : public QWidget +{ +public: + enum Mode { Display, Edit }; + explicit DashboardSettingsWidget(Mode m, QWidget *parent, QPushButton *ok = nullptr); + + AxivionServer dashboardServer() const; + void setDashboardServer(const AxivionServer &server); + + bool isValid() const; + +private: + Mode m_mode = Display; + Id m_id; + StringAspect m_dashboardUrl; + StringAspect m_description; + StringAspect m_token; + BoolAspect m_valid; +}; + +DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPushButton *ok) + : QWidget(parent) + , m_mode(mode) +{ + auto labelStyle = mode == Display ? StringAspect::LabelDisplay : StringAspect::LineEditDisplay; + m_dashboardUrl.setLabelText(Tr::tr("Dashboard URL:")); + m_dashboardUrl.setDisplayStyle(labelStyle); + m_dashboardUrl.setValidationFunction([](FancyLineEdit *edit, QString *){ + return isUrlValid(edit->text()); + }); + m_description.setLabelText(Tr::tr("Description:")); + m_description.setDisplayStyle(labelStyle); + m_description.setPlaceHolderText(Tr::tr("Non-empty description")); + + m_token.setLabelText(Tr::tr("Access token:")); + m_token.setDisplayStyle(labelStyle); + m_token.setPlaceHolderText(Tr::tr("IDE Access Token")); + m_token.setVisible(mode == Edit); + + using namespace Layouting; + + Form { + m_dashboardUrl, br, + m_description, br, + m_token, br, + mode == Edit ? normalMargin : noMargin + }.attachTo(this); + + if (mode == Edit) { + QTC_ASSERT(ok, return); + auto checkValidity = [this, ok] { + m_valid.setValue(isValid()); + ok->setEnabled(m_valid()); + }; + connect(&m_dashboardUrl, &BaseAspect::changed, this, checkValidity); + connect(&m_description, &BaseAspect::changed, this, checkValidity); + connect(&m_token, &BaseAspect::changed, this, checkValidity); + } +} + +AxivionServer DashboardSettingsWidget::dashboardServer() const +{ + AxivionServer result; + if (m_id.isValid()) + result.id = m_id; + else + result.id = m_mode == Edit ? Utils::Id::fromName(QUuid::createUuid().toByteArray()) : m_id; + result.dashboard = m_dashboardUrl(); + result.description = m_description(); + result.token = m_token(); + return result; +} + +void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server) +{ + m_id = server.id; + m_dashboardUrl.setValue(server.dashboard); + m_description.setValue(server.description); + m_token.setValue(server.token); +} + +bool DashboardSettingsWidget::isValid() const +{ + return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); +} + +class AxivionSettingsWidget : public Core::IOptionsPageWidget +{ +public: + AxivionSettingsWidget(); + + void apply() override; + +private: + void showEditServerDialog(); + + DashboardSettingsWidget *m_dashboardDisplay = nullptr; + QPushButton *m_edit = nullptr; +}; + +AxivionSettingsWidget::AxivionSettingsWidget() +{ + using namespace Layouting; + + m_dashboardDisplay = new DashboardSettingsWidget(DashboardSettingsWidget::Display, this); + m_dashboardDisplay->setDashboardServer(settings().server); + m_edit = new QPushButton(Tr::tr("Edit..."), this); + Row { + Form { + m_dashboardDisplay, br, + settings().curl, br, + }, Column { m_edit, st } + }.attachTo(this); + + connect(m_edit, &QPushButton::clicked, this, &AxivionSettingsWidget::showEditServerDialog); +} + +void AxivionSettingsWidget::apply() +{ + settings().server = m_dashboardDisplay->dashboardServer(); + settings().toSettings(); +} + +void AxivionSettingsWidget::showEditServerDialog() +{ + const AxivionServer old = m_dashboardDisplay->dashboardServer(); + QDialog d; + d.setWindowTitle(Tr::tr("Edit Dashboard Configuration")); + QVBoxLayout *layout = new QVBoxLayout; + auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, this); + auto ok = buttons->button(QDialogButtonBox::Ok); + auto dashboardWidget = new DashboardSettingsWidget(DashboardSettingsWidget::Edit, this, ok); + dashboardWidget->setDashboardServer(old); + layout->addWidget(dashboardWidget); + ok->setEnabled(m_dashboardDisplay->isValid()); + connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, &d, &QDialog::reject); + connect(ok, &QPushButton::clicked, &d, &QDialog::accept); + layout->addWidget(buttons); + d.setLayout(layout); + d.resize(500, 200); + + if (d.exec() != QDialog::Accepted) + return; + if (dashboardWidget->isValid()) { + const AxivionServer server = dashboardWidget->dashboardServer(); + if (server != old) + m_dashboardDisplay->setDashboardServer(server); + } +} + +// AxivionSettingsPage + +class AxivionSettingsPage : public Core::IOptionsPage +{ +public: + AxivionSettingsPage() + { + setId("Axivion.Settings.General"); + setDisplayName(Tr::tr("General")); + setCategory("XY.Axivion"); + setDisplayCategory(Tr::tr("Axivion")); + setCategoryIconPath(":/axivion/images/axivion.png"); + setWidgetCreator([] { return new AxivionSettingsWidget; }); + } +}; + +const AxivionSettingsPage settingsPage; + } // Axivion::Internal diff --git a/src/plugins/axivion/axivionsettings.h b/src/plugins/axivion/axivionsettings.h index 0df1746387f..ef497b7182f 100644 --- a/src/plugins/axivion/axivionsettings.h +++ b/src/plugins/axivion/axivionsettings.h @@ -10,7 +10,6 @@ QT_BEGIN_NAMESPACE class QJsonObject; -class QSettings; QT_END_NAMESPACE namespace Axivion::Internal { @@ -41,11 +40,13 @@ class AxivionSettings : public Utils::AspectContainer { public: AxivionSettings(); - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + + void toSettings() const; AxivionServer server; // shall we have more than one? Utils::FilePathAspect curl{this}; }; +AxivionSettings &settings(); + } // Axivion::Internal diff --git a/src/plugins/axivion/axivionsettingspage.cpp b/src/plugins/axivion/axivionsettingspage.cpp deleted file mode 100644 index e837d71598d..00000000000 --- a/src/plugins/axivion/axivionsettingspage.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "axivionsettingspage.h" - -#include "axivionplugin.h" -#include "axivionsettings.h" -#include "axiviontr.h" - -#include <coreplugin/icore.h> -#include <utils/aspects.h> -#include <utils/id.h> -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> - -#include <QDialog> -#include <QDialogButtonBox> -#include <QLabel> -#include <QPushButton> -#include <QRegularExpression> -#include <QUuid> -#include <QVBoxLayout> - -using namespace Utils; - -namespace Axivion::Internal { - -// may allow some invalid, but does some minimal check for legality -static bool hostValid(const QString &host) -{ - static const QRegularExpression ip(R"(^(\d+).(\d+).(\d+).(\d+)$)"); - static const QRegularExpression dn(R"(^([a-zA-Z0-9][a-zA-Z0-9-]+\.)+[a-zA-Z0-9][a-zA-Z0-9-]+$)"); - const QRegularExpressionMatch match = ip.match(host); - if (match.hasMatch()) { - for (int i = 1; i < 5; ++i) { - int val = match.captured(i).toInt(); - if (val < 0 || val > 255) - return false; - } - return true; - } - return (host == "localhost") || dn.match(host).hasMatch(); -} - -static bool isUrlValid(const QString &in) -{ - const QUrl url(in); - return hostValid(url.host()) && (url.scheme() == "https" || url.scheme() == "http"); -} - -class DashboardSettingsWidget : public QWidget -{ -public: - enum Mode { Display, Edit }; - explicit DashboardSettingsWidget(Mode m, QWidget *parent, QPushButton *ok = nullptr); - - AxivionServer dashboardServer() const; - void setDashboardServer(const AxivionServer &server); - - bool isValid() const; - -private: - Mode m_mode = Display; - Id m_id; - StringAspect m_dashboardUrl; - StringAspect m_description; - StringAspect m_token; - BoolAspect m_valid; -}; - -DashboardSettingsWidget::DashboardSettingsWidget(Mode mode, QWidget *parent, QPushButton *ok) - : QWidget(parent) - , m_mode(mode) -{ - auto labelStyle = mode == Display ? StringAspect::LabelDisplay : StringAspect::LineEditDisplay; - m_dashboardUrl.setLabelText(Tr::tr("Dashboard URL:")); - m_dashboardUrl.setDisplayStyle(labelStyle); - m_dashboardUrl.setValidationFunction([](FancyLineEdit *edit, QString *){ - return isUrlValid(edit->text()); - }); - m_description.setLabelText(Tr::tr("Description:")); - m_description.setDisplayStyle(labelStyle); - m_description.setPlaceHolderText(Tr::tr("Non-empty description")); - - m_token.setLabelText(Tr::tr("Access token:")); - m_token.setDisplayStyle(labelStyle); - m_token.setPlaceHolderText(Tr::tr("IDE Access Token")); - m_token.setVisible(mode == Edit); - - using namespace Layouting; - - Form { - m_dashboardUrl, br, - m_description, br, - m_token, br, - mode == Edit ? normalMargin : noMargin - }.attachTo(this); - - if (mode == Edit) { - QTC_ASSERT(ok, return); - auto checkValidity = [this, ok] { - m_valid.setValue(isValid()); - ok->setEnabled(m_valid()); - }; - connect(&m_dashboardUrl, &BaseAspect::changed, this, checkValidity); - connect(&m_description, &BaseAspect::changed, this, checkValidity); - connect(&m_token, &BaseAspect::changed, this, checkValidity); - } -} - -AxivionServer DashboardSettingsWidget::dashboardServer() const -{ - AxivionServer result; - if (m_id.isValid()) - result.id = m_id; - else - result.id = m_mode == Edit ? Utils::Id::fromName(QUuid::createUuid().toByteArray()) : m_id; - result.dashboard = m_dashboardUrl(); - result.description = m_description(); - result.token = m_token(); - return result; -} - -void DashboardSettingsWidget::setDashboardServer(const AxivionServer &server) -{ - m_id = server.id; - m_dashboardUrl.setValue(server.dashboard); - m_description.setValue(server.description); - m_token.setValue(server.token); -} - -bool DashboardSettingsWidget::isValid() const -{ - return !m_token().isEmpty() && !m_description().isEmpty() && isUrlValid(m_dashboardUrl()); -} - -class AxivionSettingsWidget : public Core::IOptionsPageWidget -{ -public: - explicit AxivionSettingsWidget(AxivionSettings *settings); - - void apply() override; -private: - void showEditServerDialog(); - - AxivionSettings *m_settings; - - DashboardSettingsWidget *m_dashboardDisplay = nullptr; - QPushButton *m_edit = nullptr; -}; - -AxivionSettingsWidget::AxivionSettingsWidget(AxivionSettings *settings) - : m_settings(settings) -{ - using namespace Layouting; - - m_dashboardDisplay = new DashboardSettingsWidget(DashboardSettingsWidget::Display, this); - m_dashboardDisplay->setDashboardServer(m_settings->server); - m_edit = new QPushButton(Tr::tr("Edit..."), this); - Row { - Form { - m_dashboardDisplay, br, - m_settings->curl, br, - }, Column { m_edit, st } - }.attachTo(this); - - connect(m_edit, &QPushButton::clicked, this, &AxivionSettingsWidget::showEditServerDialog); -} - -void AxivionSettingsWidget::apply() -{ - m_settings->server = m_dashboardDisplay->dashboardServer(); - m_settings->toSettings(Core::ICore::settings()); - emit AxivionPlugin::instance()->settingsChanged(); -} - -void AxivionSettingsWidget::showEditServerDialog() -{ - const AxivionServer old = m_dashboardDisplay->dashboardServer(); - QDialog d; - d.setWindowTitle(Tr::tr("Edit Dashboard Configuration")); - QVBoxLayout *layout = new QVBoxLayout; - auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, this); - auto ok = buttons->button(QDialogButtonBox::Ok); - auto dashboardWidget = new DashboardSettingsWidget(DashboardSettingsWidget::Edit, this, ok); - dashboardWidget->setDashboardServer(old); - layout->addWidget(dashboardWidget); - ok->setEnabled(m_dashboardDisplay->isValid()); - connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, &d, &QDialog::reject); - connect(ok, &QPushButton::clicked, &d, &QDialog::accept); - layout->addWidget(buttons); - d.setLayout(layout); - d.resize(500, 200); - - if (d.exec() != QDialog::Accepted) - return; - if (dashboardWidget->isValid()) { - const AxivionServer server = dashboardWidget->dashboardServer(); - if (server != old) - m_dashboardDisplay->setDashboardServer(server); - } -} - -AxivionSettingsPage::AxivionSettingsPage(AxivionSettings *settings) - : m_settings(settings) -{ - setId("Axivion.Settings.General"); - setDisplayName(Tr::tr("General")); - setCategory("XY.Axivion"); - setDisplayCategory(Tr::tr("Axivion")); - setCategoryIconPath(":/axivion/images/axivion.png"); - setWidgetCreator([this] { return new AxivionSettingsWidget(m_settings); }); -} - -} // Axivion::Internal diff --git a/src/plugins/axivion/axivionsettingspage.h b/src/plugins/axivion/axivionsettingspage.h deleted file mode 100644 index 744818300d9..00000000000 --- a/src/plugins/axivion/axivionsettingspage.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Axivion::Internal { - -class AxivionSettings; - -class AxivionSettingsPage : public Core::IOptionsPage -{ -public: - explicit AxivionSettingsPage(AxivionSettings *settings); - -private: - AxivionSettings *m_settings; -}; - -} // Axivion::Internal diff --git a/src/plugins/axivion/dashboard/concat.cpp b/src/plugins/axivion/dashboard/concat.cpp new file mode 100644 index 00000000000..7d0ad6f7285 --- /dev/null +++ b/src/plugins/axivion/dashboard/concat.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + * + * Purpose: Helper functions to concatenate strings/bytes + * + * !!!!!! GENERATED, DO NOT EDIT !!!!!! + * + * This file was generated with the script at + * <AxivionSuiteRepo>/projects/libs/dashboard_cpp_api/generator/generate_dashboard_cpp_api.py + */ + +#include "dashboard/concat.h" + +#include <utility> + +namespace Axivion::Internal::Dto +{ + +template<typename Input, typename Output> +static Output concat(const std::initializer_list<const Input> &args) +{ + size_t size = 0; + for (const Input &arg : args) + size += arg.size(); + Output output; + output.reserve(size); + for (const Input &arg : args) + output += arg; + return output; +} + +std::string concat(const std::initializer_list<const std::string_view> &args) +{ + return concat<std::string_view, std::string>(args); +} + +QString concat(const std::initializer_list<const QStringView> &args) +{ + return concat<QStringView, QString>(args); +} + +QByteArray concat_bytes(const std::initializer_list<const QByteArrayView> &args) +{ + return concat<QByteArrayView, QByteArray>(args); +} + +} // namespace Axivion::Internal::Dto diff --git a/src/plugins/axivion/dashboard/concat.h b/src/plugins/axivion/dashboard/concat.h new file mode 100644 index 00000000000..33429e3bf42 --- /dev/null +++ b/src/plugins/axivion/dashboard/concat.h @@ -0,0 +1,33 @@ +#pragma once + +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + * + * Purpose: Helper functions to concatenate strings/bytes + * + * !!!!!! GENERATED, DO NOT EDIT !!!!!! + * + * This file was generated with the script at + * <AxivionSuiteRepo>/projects/libs/dashboard_cpp_api/generator/generate_dashboard_cpp_api.py + */ + +#include <QByteArray> +#include <QByteArrayView> +#include <QString> +#include <QStringView> + +#include <string> +#include <string_view> + +namespace Axivion::Internal::Dto +{ +std::string concat(const std::initializer_list<const std::string_view> &args); + +QString concat(const std::initializer_list<const QStringView> &args); + +QByteArray concat_bytes(const std::initializer_list<const QByteArrayView> &args); + +} // namespace Axivion::Internal::Dto diff --git a/src/plugins/axivion/dashboard/dashboardclient.cpp b/src/plugins/axivion/dashboard/dashboardclient.cpp new file mode 100644 index 00000000000..7e1a2761ec3 --- /dev/null +++ b/src/plugins/axivion/dashboard/dashboardclient.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + */ + +#include "dashboardclient.h" + +#include "axivionsettings.h" + +#include <QByteArray> +#include <QCoreApplication> +#include <QLatin1String> +#include <QNetworkReply> +#include <QNetworkRequest> + +#include <memory> + +namespace Axivion::Internal +{ + +DashboardClient::DashboardClient(Utils::NetworkAccessManager &networkAccessManager) + : m_networkAccessManager(networkAccessManager) +{ +} + +static void deleteLater(QObject *obj) +{ + obj->deleteLater(); +} + +using RawBody = Utils::expected_str<DataWithOrigin<QByteArray>>; + +class RawBodyReader final +{ +public: + RawBodyReader(std::shared_ptr<QNetworkReply> reply) + : m_reply(std::move(reply)) + { } + + ~RawBodyReader() { } + + RawBody operator()() + { + QNetworkReply::NetworkError error = m_reply->error(); + if (error != QNetworkReply::NetworkError::NoError) + return tl::make_unexpected(QString::number(error) + + QLatin1String(": ") + + m_reply->errorString()); + return DataWithOrigin(m_reply->url(), m_reply->readAll()); + } + +private: + std::shared_ptr<QNetworkReply> m_reply; +}; + +template<typename T> +static Utils::expected_str<DataWithOrigin<T>> RawBodyParser(RawBody rawBody) +{ + if (!rawBody) + return tl::make_unexpected(std::move(rawBody.error())); + try { + T data = T::deserialize(rawBody.value().data); + return DataWithOrigin(std::move(rawBody.value().origin), + std::move(data)); + } catch (const Dto::invalid_dto_exception &e) { + return tl::make_unexpected(QString::fromUtf8(e.what())); + } +} + +QFuture<DashboardClient::RawProjectInfo> DashboardClient::fetchProjectInfo(const QString &projectName) +{ + const AxivionServer &server = settings().server; + QString dashboard = server.dashboard; + if (!dashboard.endsWith(QLatin1Char('/'))) + dashboard += QLatin1Char('/'); + QUrl url = QUrl(dashboard) + .resolved(QUrl(QStringLiteral(u"api/projects/"))) + .resolved(QUrl(projectName)); + QNetworkRequest request{ url }; + request.setRawHeader(QByteArrayLiteral(u8"Accept"), + QByteArrayLiteral(u8"Application/Json")); + request.setRawHeader(QByteArrayLiteral(u8"Authorization"), + QByteArrayLiteral(u8"AxToken ") + server.token.toUtf8()); + QByteArray ua = QByteArrayLiteral(u8"Axivion") + + QCoreApplication::applicationName().toUtf8() + + QByteArrayLiteral(u8"Plugin/") + + QCoreApplication::applicationVersion().toUtf8(); + request.setRawHeader(QByteArrayLiteral(u8"X-Axivion-User-Agent"), ua); + std::shared_ptr<QNetworkReply> reply{ this->m_networkAccessManager.get(request), deleteLater }; + return QtFuture::connect(reply.get(), &QNetworkReply::finished) + .onCanceled(reply.get(), [reply] { reply->abort(); }) + .then(RawBodyReader(reply)) + .then(QtFuture::Launch::Async, &RawBodyParser<Dto::ProjectInfoDto>); +} + +} diff --git a/src/plugins/axivion/dashboard/dashboardclient.h b/src/plugins/axivion/dashboard/dashboardclient.h new file mode 100644 index 00000000000..eda623b878b --- /dev/null +++ b/src/plugins/axivion/dashboard/dashboardclient.h @@ -0,0 +1,45 @@ +#pragma once + +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + */ + +#include "dashboard/dto.h" + +#include <utils/expected.h> +#include <utils/networkaccessmanager.h> + +#include <QFuture> +#include <QNetworkReply> + +namespace Axivion::Internal +{ + +template<typename T> +class DataWithOrigin +{ +public: + QUrl origin; + T data; + + DataWithOrigin(QUrl origin, T data) : origin(std::move(origin)), data(std::move(data)) { } +}; + +class DashboardClient +{ +public: + using ProjectInfo = DataWithOrigin<Dto::ProjectInfoDto>; + using RawProjectInfo = Utils::expected_str<ProjectInfo>; + + DashboardClient(Utils::NetworkAccessManager &networkAccessManager); + + QFuture<RawProjectInfo> fetchProjectInfo(const QString &projectName); + +private: + Utils::NetworkAccessManager &m_networkAccessManager; +}; + +} // namespace Axivion::Internal diff --git a/src/plugins/axivion/dashboard/dto.cpp b/src/plugins/axivion/dashboard/dto.cpp new file mode 100644 index 00000000000..1720087f2c8 --- /dev/null +++ b/src/plugins/axivion/dashboard/dto.cpp @@ -0,0 +1,4277 @@ +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + * + * Purpose: Dashboard C++ API Implementation + * + * !!!!!! GENERATED, DO NOT EDIT !!!!!! + * + * This file was generated with the script at + * <AxivionSuiteRepo>/projects/libs/dashboard_cpp_api/generator/generate_dashboard_cpp_api.py + */ + +#undef QT_RESTRICTED_CAST_FROM_ASCII +#define QT_NO_CAST_FROM_ASCII 1 +#define QT_NO_CAST_TO_ASCII 1 +#define QT_NO_CAST_FROM_BYTEARRAY 1 + +#include "dashboard/dto.h" + +#include "dashboard/concat.h" + +#include <QJsonValue> +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonDocument> + +#include <cmath> +#include <cstdint> +#include <initializer_list> +#include <limits> +#include <string> +#include <typeinfo> +#include <utility> + +namespace Axivion::Internal::Dto { + + template<typename T> + static std::string to_std_string(const T &e) + { + return std::to_string(e); + } + + template<> + std::string to_std_string(const QString &qstr) + { + return qstr.toStdString(); + } + + template<> + std::string to_std_string(const QAnyStringView &qasv) + { + return to_std_string(qasv.toString()); + } + + // exceptions + + invalid_dto_exception::invalid_dto_exception(const std::string_view type_name, const std::exception &ex) : + std::runtime_error(concat({ type_name, u8": ", ex.what() })) + {} + + invalid_dto_exception::invalid_dto_exception(const std::string_view type_name, const std::string_view message) : + std::runtime_error(concat({ type_name, u8": ", message })) + {} + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + [[noreturn]] static void throw_invalid_dto_exception(const std::exception &ex) + { + throw invalid_dto_exception(typeid(T).name(), ex); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + [[noreturn]] static void throw_invalid_dto_exception(std::string_view message) + { + throw invalid_dto_exception(typeid(T).name(), message); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + [[noreturn]] static void throw_json_type_conversion(QJsonValue::Type type) { + throw_invalid_dto_exception<T>(concat({ + u8"Error parsing JSON: Cannot convert type ", + to_std_string(type) + })); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T, typename V> + [[noreturn]] static void throw_json_value_conversion(const V &raw_value) { + throw_invalid_dto_exception<T>(concat({ + u8"Error parsing JSON: Cannot convert raw value ", + to_std_string(raw_value) + })); + } + + // basic json (de)serialization + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + static QJsonObject toJsonObject(const QJsonValue &json) + { + if (json.isObject()) + { + return json.toObject(); + } + throw_json_type_conversion<std::map<QString, T>>(json.type()); + } + + template<typename T> + class de_serializer final + { + public: + // Require usage of template specializations. + // This static members have to be implemented: + // + // // throws Axivion::Internal::Dto::invalid_dto_exception + // static T deserialize(const QJsonValue &json); + // + // static QJsonValue serialize(const T &value); + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + static T deserialize_json(const QJsonValue &json) + { + return de_serializer<T>::deserialize(json); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + static T deserialize_bytes(const QByteArray &json) + { + QJsonValue qjv; + { + QJsonParseError error; + const QJsonDocument qjd = QJsonDocument::fromJson(json, &error); + if (error.error != QJsonParseError::ParseError::NoError) + { + throw_invalid_dto_exception<T>(concat({ + u8"Error parsing JSON - ", + to_std_string(error.error), + u8" at ", + to_std_string(error.offset), + u8": ", + to_std_string(error.errorString()) + })); + } + if (!qjd.isObject()) + { + throw_invalid_dto_exception<T>(u8"Error parsing JSON: parsed data is no JSON object"); + } + qjv = QJsonValue(qjd.object()); + } + return deserialize_json<T>(qjv); + } + + template<typename T> + static QJsonValue serialize_json(const T &value) + { + return de_serializer<T>::serialize(value); + } + + template<typename T> + static QByteArray serialize_bytes(const T &value) + { + QJsonDocument qjd; + { + QJsonValue qjv = serialize_json(value); + if (qjv.isObject()) + { + qjd = QJsonDocument(qjv.toObject()); + } + else if (qjv.isArray()) + { + qjd = QJsonDocument(qjv.toArray()); + } + else + { + throw std::domain_error(concat({ + u8"Error serializing JSON - value is not an object or array:", + to_std_string(qjv.type()) + })); + } + } + return qjd.toJson(QJsonDocument::JsonFormat::Indented); + } + + template<> + class de_serializer<std::nullptr_t> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::nullptr_t deserialize(const QJsonValue &json) + { + if (json.isNull()) + { + return nullptr; + } + throw_json_type_conversion<std::nullptr_t>(json.type()); + } + + static QJsonValue serialize(const std::nullptr_t&) + { + return {}; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<> + class de_serializer<QString> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static QString deserialize(const QJsonValue &json) + { + if (json.isString()) + { + return json.toString(); + } + throw_json_type_conversion<QString>(json.type()); + } + + static QJsonValue serialize(const QString &value) + { + return { value }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<> + class de_serializer<bool> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static bool deserialize(const QJsonValue &json) + { + if (json.isBool()) + { + return json.toBool(); + } + throw_json_type_conversion<bool>(json.type()); + } + + static QJsonValue serialize(const bool &value) + { + return { value }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<> + class de_serializer<qint32> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static qint32 deserialize(const QJsonValue &json) + { + if (!json.isDouble()) + { + throw_json_type_conversion<qint32>(json.type()); + } + const double rawValue = json.toDouble(); + const qint32 value = static_cast<qint32>(rawValue); + if (static_cast<double>(value) != rawValue) + { + throw_json_value_conversion<qint32>(rawValue); + } + return value; + } + + static QJsonValue serialize(const qint32 &value) + { + return { static_cast<qint64>(value) }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<> + class de_serializer<qint64> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static qint64 deserialize(const QJsonValue &json) + { + if (!json.isDouble()) + { + throw_json_type_conversion<qint64>(json.type()); + } + const double rawValue = json.toDouble(); + const qint64 value = static_cast<qint64>(rawValue); + if (static_cast<double>(value) != rawValue) + { + throw_json_value_conversion<qint64>(rawValue); + } + return value; + } + + static QJsonValue serialize(const qint64 &value) + { + return { value }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + static const QLatin1String deSerializerDoublePositiveInfinity{"Infinity"}; + static const QLatin1String deSerializerDoubleNegativeInfinity{"-Infinity"}; + static const QLatin1String deSerializerDoubleNAN{"NaN"}; + + template<> + class de_serializer<double> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static double deserialize(const QJsonValue &json) + { + if (json.isDouble()) + { + return json.toDouble(); + } + if (json.isString()) + { + const QString rawValue = json.toString(); + if (rawValue == deSerializerDoublePositiveInfinity) + { + return std::numeric_limits<double>::infinity(); + } + if (rawValue == deSerializerDoubleNegativeInfinity) + { + return -std::numeric_limits<double>::infinity(); + } + if (rawValue == deSerializerDoubleNAN) + { + return std::numeric_limits<double>::quiet_NaN(); + } + throw_json_value_conversion<double>(rawValue); + } + throw_json_type_conversion<double>(json.type()); + } + + static QJsonValue serialize(const double &value) + { + if (value == std::numeric_limits<double>::infinity()) + { + return { deSerializerDoublePositiveInfinity }; + } + if (value == -std::numeric_limits<double>::infinity()) + { + return { deSerializerDoubleNegativeInfinity }; + } + if (std::isnan(value)) + { + return { deSerializerDoubleNAN }; + } + return { value }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<typename T> + class de_serializer<std::optional<T>> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::optional<T> deserialize(const QJsonValue &json) + { + if (json.isNull()) + { + return std::nullopt; + } + return deserialize_json<T>(json); + } + + static QJsonValue serialize(const std::optional<T> &value) + { + if (value.has_value()) { + return serialize_json(*value); + } + return serialize_json(nullptr); + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<typename T> + class de_serializer<std::vector<T>> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::vector<T> deserialize(const QJsonValue &json) + { + if (!json.isArray()) + { + throw_json_type_conversion<std::vector<T>>(json.type()); + } + const QJsonArray ja = json.toArray(); + std::vector<T> value; + value.reserve(ja.size()); + for (const auto item : ja) + { + value.push_back(deserialize_json<T>(item)); + } + return value; + } + + static QJsonValue serialize(const std::vector<T> &value) + { + QJsonArray ja; + for (const T &e : value) + { + ja.push_back(serialize_json(e)); + } + return { ja }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<typename T> + class de_serializer<std::unordered_set<T>> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::unordered_set<T> deserialize(const QJsonValue &json) + { + if (!json.isArray()) + { + throw_json_type_conversion<std::unordered_set<T>>(json.type()); + } + const QJsonArray ja = json.toArray(); + std::unordered_set<T> value; + value.reserve(ja.size()); + for (const auto item : ja) + { + value.insert(deserialize_json<T>(item)); + } + return value; + } + + static QJsonValue serialize(const std::unordered_set<T> &value) + { + QJsonArray ja; + for (const T &e : value) + { + ja.push_back(serialize_json(e)); + } + return { ja }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + template<typename T> + class de_serializer<std::map<QString, T>> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::map<QString, T> deserialize(const QJsonValue &json) + { + const QJsonObject jo = toJsonObject<std::map<QString, T>>(json); + std::map<QString, T> value; + // value.reserve(jo.size()); + for (auto it = jo.constBegin(), end = jo.constEnd(); it != end; ++it) + { + value[it.key()] = deserialize_json<T>(it.value()); + } + return value; + } + + static QJsonValue serialize(const std::map<QString, T> &value) + { + QJsonObject jo; + for (const auto &[key, val] : value) + { + jo.insert(key, serialize_json(val)); + } + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // field (de)serialization + + template<typename T> + class field_de_serializer final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static T deserialize(const QJsonObject &jo, const QString &key) + { + const auto it = jo.constFind(key); + if (it == jo.constEnd()) + { + throw_invalid_dto_exception<T>(concat({ + u8"Error parsing JSON: key not found ", + to_std_string(key) + })); + } + return deserialize_json<T>(it.value()); + } + + static void serialize(QJsonObject &json, const QString &key, const T &value) + { + json.insert(key, serialize_json(value)); + } + + field_de_serializer() = delete; + ~field_de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + template<typename T> + static T deserialize_field(const QJsonObject &jo, const QString &key) + { + return field_de_serializer<T>::deserialize(jo, key); + } + + template<typename T> + static void serialize_field(QJsonObject &jo, const QString &key, const T &value) + { + field_de_serializer<T>::serialize(jo, key, value); + } + + template<typename T> + class field_de_serializer<std::optional<T>> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static std::optional<T> deserialize(const QJsonObject &jo, const QString &key) + { + const auto it = jo.constFind(key); + if (it == jo.constEnd()) + { + return std::nullopt; + } + const auto value = it.value(); + if (value.isNull()) + { + return std::nullopt; + } + return deserialize_json<T>(value); + } + + static void serialize(QJsonObject &json, const QString &key, const std::optional<T> &value) + { + if (value.has_value()) + { + serialize_field(json, key, *value); + } + } + + field_de_serializer() = delete; + ~field_de_serializer() = delete; + }; + + // any + + template<> + class de_serializer<Any> final + { + public: + static Any deserialize(const QJsonValue &json) + { + if (json.isNull()) + { + return Any(); + } + if (json.isString()) + { + return Any(deserialize_json<QString>(json)); + } + if (json.isDouble()) + { + return Any(deserialize_json<double>(json)); + } + if (json.isObject()) + { + return Any(deserialize_json<Any::Map>(json)); + } + if (json.isArray()) + { + return Any(deserialize_json<Any::Vector>(json)); + } + if (json.isBool()) + { + return Any(deserialize_json<bool>(json)); + } + throw std::domain_error(concat({ + u8"Unknown json value type: ", + to_std_string(json.type()) + })); + } + + static QJsonValue serialize(const Any &value) { + if (value.isNull()) + { + return serialize_json(nullptr); + } + if (value.isString()) + { + return serialize_json(value.getString()); + } + if (value.isDouble()) + { + return serialize_json(value.getDouble()); + } + if (value.isMap()) + { + return serialize_json(value.getMap()); + } + if (value.isList()) + { + return serialize_json(value.getList()); + } + if (value.isBool()) + { + return serialize_json(value.getBool()); + } + throw std::domain_error(u8"Unknown Axivion::Internal::Dto::any variant"); + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + Any Any::deserialize(const QByteArray &json) + { + return deserialize_bytes<Any>(json); + } + + Any::Any() {} + + Any::Any(QString value) : data(std::move(value)) {} + + Any::Any(double value) : data(std::move(value)) {} + + Any::Any(Map value) : data(std::move(value)) {} + + Any::Any(Vector value) : data(std::move(value)) {} + + Any::Any(bool value) : data(std::move(value)) {} + + bool Any::isNull() const + { + return this->data.index() == 0; + } + + bool Any::isString() const + { + return this->data.index() == 1; + } + + QString &Any::getString() + { + return std::get<1>(this->data); + } + + const QString &Any::getString() const { + return std::get<1>(this->data); + } + + bool Any::isDouble() const + { + return this->data.index() == 2; + } + + double &Any::getDouble() + { + return std::get<2>(this->data); + } + + const double &Any::getDouble() const + { + return std::get<2>(this->data); + } + + bool Any::isMap() const + { + return this->data.index() == 3; + } + + Any::Map &Any::getMap() + { + return std::get<3>(this->data); + } + + const Any::Map &Any::getMap() const + { + return std::get<3>(this->data); + } + + bool Any::isList() const + { + return this->data.index() == 4; + } + + Any::Vector &Any::getList() + { + return std::get<4>(this->data); + } + + const Any::Vector &Any::getList() const + { + return std::get<4>(this->data); + } + + bool Any::isBool() const + { + return this->data.index() == 5; + } + + bool &Any::getBool() + { + return std::get<5>(this->data); + } + + const bool &Any::getBool() const + { + return std::get<5>(this->data); + } + + QByteArray Any::serialize() const + { + return serialize_bytes(*this); + } + + // version + + constexpr std::array<qint32, 4> ApiVersion::number{7,6,3,12797}; + const QLatin1String ApiVersion::string{"7.6.3.12797"}; + const QLatin1String ApiVersion::name{"7.6.3"}; + const QLatin1String ApiVersion::timestamp{"2023-08-30 15:49:00 +00:00"}; + + // AnalyzedFileDto + + static const QLatin1String analyzedFileKeyPath{"path"}; + static const QLatin1String analyzedFileKeyIsSystemHeader{"isSystemHeader"}; + static const QLatin1String analyzedFileKeyLanguageName{"languageName"}; + + template<> + class de_serializer<AnalyzedFileDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static AnalyzedFileDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<AnalyzedFileDto>(json); + return { + deserialize_field<QString>(jo, analyzedFileKeyPath), + deserialize_field<std::optional<bool>>(jo, analyzedFileKeyIsSystemHeader), + deserialize_field<std::optional<QString>>(jo, analyzedFileKeyLanguageName) + }; + } + + static QJsonValue serialize(const AnalyzedFileDto &value) { + QJsonObject jo; + serialize_field(jo, analyzedFileKeyPath, value.path); + serialize_field(jo, analyzedFileKeyIsSystemHeader, value.isSystemHeader); + serialize_field(jo, analyzedFileKeyLanguageName, value.languageName); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + AnalyzedFileDto AnalyzedFileDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<AnalyzedFileDto>(json); + } + + AnalyzedFileDto::AnalyzedFileDto( + QString path, + std::optional<bool> isSystemHeader, + std::optional<QString> languageName + ) : + path(std::move(path)), + isSystemHeader(std::move(isSystemHeader)), + languageName(std::move(languageName)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray AnalyzedFileDto::serialize() const + { + return serialize_bytes(*this); + } + + // ApiTokenType + + const QLatin1String ApiTokenTypeMeta::sourcefetch{"SourceFetch"}; + const QLatin1String ApiTokenTypeMeta::general{"General"}; + const QLatin1String ApiTokenTypeMeta::ideplugin{"IdePlugin"}; + const QLatin1String ApiTokenTypeMeta::login{"LogIn"}; + const QLatin1String ApiTokenTypeMeta::continuousintegration{"ContinuousIntegration"}; + + // throws std::range_error + ApiTokenType ApiTokenTypeMeta::strToEnum(QAnyStringView str) + { + if (str == ApiTokenTypeMeta::sourcefetch) + { + return ApiTokenType::sourcefetch; + } + if (str == ApiTokenTypeMeta::general) + { + return ApiTokenType::general; + } + if (str == ApiTokenTypeMeta::ideplugin) + { + return ApiTokenType::ideplugin; + } + if (str == ApiTokenTypeMeta::login) + { + return ApiTokenType::login; + } + if (str == ApiTokenTypeMeta::continuousintegration) + { + return ApiTokenType::continuousintegration; + } + throw std::range_error(concat({ u8"Unknown ApiTokenType str: ", to_std_string(str) })); + } + + QLatin1String ApiTokenTypeMeta::enumToStr(ApiTokenType e) + { + switch (e) + { + case ApiTokenType::sourcefetch: + return ApiTokenTypeMeta::sourcefetch; + case ApiTokenType::general: + return ApiTokenTypeMeta::general; + case ApiTokenType::ideplugin: + return ApiTokenTypeMeta::ideplugin; + case ApiTokenType::login: + return ApiTokenTypeMeta::login; + case ApiTokenType::continuousintegration: + return ApiTokenTypeMeta::continuousintegration;; + default: + throw std::domain_error(concat({ + u8"Unknown ApiTokenType enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // ChangePasswordFormDto + + static const QLatin1String changePasswordFormKeyCurrentPassword{"currentPassword"}; + static const QLatin1String changePasswordFormKeyNewPassword{"newPassword"}; + + template<> + class de_serializer<ChangePasswordFormDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ChangePasswordFormDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ChangePasswordFormDto>(json); + return { + deserialize_field<QString>(jo, changePasswordFormKeyCurrentPassword), + deserialize_field<QString>(jo, changePasswordFormKeyNewPassword) + }; + } + + static QJsonValue serialize(const ChangePasswordFormDto &value) { + QJsonObject jo; + serialize_field(jo, changePasswordFormKeyCurrentPassword, value.currentPassword); + serialize_field(jo, changePasswordFormKeyNewPassword, value.newPassword); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ChangePasswordFormDto ChangePasswordFormDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ChangePasswordFormDto>(json); + } + + ChangePasswordFormDto::ChangePasswordFormDto( + QString currentPassword, + QString newPassword + ) : + currentPassword(std::move(currentPassword)), + newPassword(std::move(newPassword)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ChangePasswordFormDto::serialize() const + { + return serialize_bytes(*this); + } + + // ColumnTypeOptionDto + + static const QLatin1String columnTypeOptionKeyKey{"key"}; + static const QLatin1String columnTypeOptionKeyDisplayName{"displayName"}; + static const QLatin1String columnTypeOptionKeyDisplayColor{"displayColor"}; + + template<> + class de_serializer<ColumnTypeOptionDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ColumnTypeOptionDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ColumnTypeOptionDto>(json); + return { + deserialize_field<QString>(jo, columnTypeOptionKeyKey), + deserialize_field<std::optional<QString>>(jo, columnTypeOptionKeyDisplayName), + deserialize_field<QString>(jo, columnTypeOptionKeyDisplayColor) + }; + } + + static QJsonValue serialize(const ColumnTypeOptionDto &value) { + QJsonObject jo; + serialize_field(jo, columnTypeOptionKeyKey, value.key); + serialize_field(jo, columnTypeOptionKeyDisplayName, value.displayName); + serialize_field(jo, columnTypeOptionKeyDisplayColor, value.displayColor); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ColumnTypeOptionDto ColumnTypeOptionDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ColumnTypeOptionDto>(json); + } + + ColumnTypeOptionDto::ColumnTypeOptionDto( + QString key, + std::optional<QString> displayName, + QString displayColor + ) : + key(std::move(key)), + displayName(std::move(displayName)), + displayColor(std::move(displayColor)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ColumnTypeOptionDto::serialize() const + { + return serialize_bytes(*this); + } + + // CommentRequestDto + + static const QLatin1String commentRequestKeyText{"text"}; + + template<> + class de_serializer<CommentRequestDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static CommentRequestDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<CommentRequestDto>(json); + return { + deserialize_field<QString>(jo, commentRequestKeyText) + }; + } + + static QJsonValue serialize(const CommentRequestDto &value) { + QJsonObject jo; + serialize_field(jo, commentRequestKeyText, value.text); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + CommentRequestDto CommentRequestDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<CommentRequestDto>(json); + } + + CommentRequestDto::CommentRequestDto( + QString text + ) : + text(std::move(text)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray CommentRequestDto::serialize() const + { + return serialize_bytes(*this); + } + + // CsrfTokenDto + + static const QLatin1String csrfTokenKeyCsrfToken{"csrfToken"}; + + template<> + class de_serializer<CsrfTokenDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static CsrfTokenDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<CsrfTokenDto>(json); + return { + deserialize_field<QString>(jo, csrfTokenKeyCsrfToken) + }; + } + + static QJsonValue serialize(const CsrfTokenDto &value) { + QJsonObject jo; + serialize_field(jo, csrfTokenKeyCsrfToken, value.csrfToken); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + CsrfTokenDto CsrfTokenDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<CsrfTokenDto>(json); + } + + CsrfTokenDto::CsrfTokenDto( + QString csrfToken + ) : + csrfToken(std::move(csrfToken)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray CsrfTokenDto::serialize() const + { + return serialize_bytes(*this); + } + + // EntityDto + + static const QLatin1String entityKeyId{"id"}; + static const QLatin1String entityKeyName{"name"}; + static const QLatin1String entityKeyType{"type"}; + static const QLatin1String entityKeyPath{"path"}; + static const QLatin1String entityKeyLine{"line"}; + + template<> + class de_serializer<EntityDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static EntityDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<EntityDto>(json); + return { + deserialize_field<QString>(jo, entityKeyId), + deserialize_field<QString>(jo, entityKeyName), + deserialize_field<QString>(jo, entityKeyType), + deserialize_field<std::optional<QString>>(jo, entityKeyPath), + deserialize_field<std::optional<qint32>>(jo, entityKeyLine) + }; + } + + static QJsonValue serialize(const EntityDto &value) { + QJsonObject jo; + serialize_field(jo, entityKeyId, value.id); + serialize_field(jo, entityKeyName, value.name); + serialize_field(jo, entityKeyType, value.type); + serialize_field(jo, entityKeyPath, value.path); + serialize_field(jo, entityKeyLine, value.line); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + EntityDto EntityDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<EntityDto>(json); + } + + EntityDto::EntityDto( + QString id, + QString name, + QString type, + std::optional<QString> path, + std::optional<qint32> line + ) : + id(std::move(id)), + name(std::move(name)), + type(std::move(type)), + path(std::move(path)), + line(std::move(line)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray EntityDto::serialize() const + { + return serialize_bytes(*this); + } + + // ErrorDto + + static const QLatin1String errorKeyDashboardVersionNumber{"dashboardVersionNumber"}; + static const QLatin1String errorKeyType{"type"}; + static const QLatin1String errorKeyMessage{"message"}; + static const QLatin1String errorKeyLocalizedMessage{"localizedMessage"}; + static const QLatin1String errorKeyDetails{"details"}; + static const QLatin1String errorKeyLocalizedDetails{"localizedDetails"}; + static const QLatin1String errorKeySupportAddress{"supportAddress"}; + static const QLatin1String errorKeyDisplayServerBugHint{"displayServerBugHint"}; + static const QLatin1String errorKeyData{"data"}; + + template<> + class de_serializer<ErrorDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ErrorDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ErrorDto>(json); + return { + deserialize_field<std::optional<QString>>(jo, errorKeyDashboardVersionNumber), + deserialize_field<QString>(jo, errorKeyType), + deserialize_field<QString>(jo, errorKeyMessage), + deserialize_field<QString>(jo, errorKeyLocalizedMessage), + deserialize_field<std::optional<QString>>(jo, errorKeyDetails), + deserialize_field<std::optional<QString>>(jo, errorKeyLocalizedDetails), + deserialize_field<std::optional<QString>>(jo, errorKeySupportAddress), + deserialize_field<std::optional<bool>>(jo, errorKeyDisplayServerBugHint), + deserialize_field<std::optional<std::map<QString, Any>>>(jo, errorKeyData) + }; + } + + static QJsonValue serialize(const ErrorDto &value) { + QJsonObject jo; + serialize_field(jo, errorKeyDashboardVersionNumber, value.dashboardVersionNumber); + serialize_field(jo, errorKeyType, value.type); + serialize_field(jo, errorKeyMessage, value.message); + serialize_field(jo, errorKeyLocalizedMessage, value.localizedMessage); + serialize_field(jo, errorKeyDetails, value.details); + serialize_field(jo, errorKeyLocalizedDetails, value.localizedDetails); + serialize_field(jo, errorKeySupportAddress, value.supportAddress); + serialize_field(jo, errorKeyDisplayServerBugHint, value.displayServerBugHint); + serialize_field(jo, errorKeyData, value.data); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ErrorDto ErrorDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ErrorDto>(json); + } + + ErrorDto::ErrorDto( + std::optional<QString> dashboardVersionNumber, + QString type, + QString message, + QString localizedMessage, + std::optional<QString> details, + std::optional<QString> localizedDetails, + std::optional<QString> supportAddress, + std::optional<bool> displayServerBugHint, + std::optional<std::map<QString, Any>> data + ) : + dashboardVersionNumber(std::move(dashboardVersionNumber)), + type(std::move(type)), + message(std::move(message)), + localizedMessage(std::move(localizedMessage)), + details(std::move(details)), + localizedDetails(std::move(localizedDetails)), + supportAddress(std::move(supportAddress)), + displayServerBugHint(std::move(displayServerBugHint)), + data(std::move(data)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ErrorDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueCommentDto + + static const QLatin1String issueCommentKeyUsername{"username"}; + static const QLatin1String issueCommentKeyUserDisplayName{"userDisplayName"}; + static const QLatin1String issueCommentKeyDate{"date"}; + static const QLatin1String issueCommentKeyDisplayDate{"displayDate"}; + static const QLatin1String issueCommentKeyText{"text"}; + static const QLatin1String issueCommentKeyHtml{"html"}; + static const QLatin1String issueCommentKeyCommentDeletionId{"commentDeletionId"}; + + template<> + class de_serializer<IssueCommentDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueCommentDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueCommentDto>(json); + return { + deserialize_field<QString>(jo, issueCommentKeyUsername), + deserialize_field<QString>(jo, issueCommentKeyUserDisplayName), + deserialize_field<QString>(jo, issueCommentKeyDate), + deserialize_field<QString>(jo, issueCommentKeyDisplayDate), + deserialize_field<QString>(jo, issueCommentKeyText), + deserialize_field<std::optional<QString>>(jo, issueCommentKeyHtml), + deserialize_field<std::optional<QString>>(jo, issueCommentKeyCommentDeletionId) + }; + } + + static QJsonValue serialize(const IssueCommentDto &value) { + QJsonObject jo; + serialize_field(jo, issueCommentKeyUsername, value.username); + serialize_field(jo, issueCommentKeyUserDisplayName, value.userDisplayName); + serialize_field(jo, issueCommentKeyDate, value.date); + serialize_field(jo, issueCommentKeyDisplayDate, value.displayDate); + serialize_field(jo, issueCommentKeyText, value.text); + serialize_field(jo, issueCommentKeyHtml, value.html); + serialize_field(jo, issueCommentKeyCommentDeletionId, value.commentDeletionId); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueCommentDto IssueCommentDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueCommentDto>(json); + } + + IssueCommentDto::IssueCommentDto( + QString username, + QString userDisplayName, + QString date, + QString displayDate, + QString text, + std::optional<QString> html, + std::optional<QString> commentDeletionId + ) : + username(std::move(username)), + userDisplayName(std::move(userDisplayName)), + date(std::move(date)), + displayDate(std::move(displayDate)), + text(std::move(text)), + html(std::move(html)), + commentDeletionId(std::move(commentDeletionId)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueCommentDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueKind + + const QLatin1String IssueKindMeta::av{"AV"}; + const QLatin1String IssueKindMeta::cl{"CL"}; + const QLatin1String IssueKindMeta::cy{"CY"}; + const QLatin1String IssueKindMeta::de{"DE"}; + const QLatin1String IssueKindMeta::mv{"MV"}; + const QLatin1String IssueKindMeta::sv{"SV"}; + + // throws std::range_error + IssueKind IssueKindMeta::strToEnum(QAnyStringView str) + { + if (str == IssueKindMeta::av) + { + return IssueKind::av; + } + if (str == IssueKindMeta::cl) + { + return IssueKind::cl; + } + if (str == IssueKindMeta::cy) + { + return IssueKind::cy; + } + if (str == IssueKindMeta::de) + { + return IssueKind::de; + } + if (str == IssueKindMeta::mv) + { + return IssueKind::mv; + } + if (str == IssueKindMeta::sv) + { + return IssueKind::sv; + } + throw std::range_error(concat({ u8"Unknown IssueKind str: ", to_std_string(str) })); + } + + QLatin1String IssueKindMeta::enumToStr(IssueKind e) + { + switch (e) + { + case IssueKind::av: + return IssueKindMeta::av; + case IssueKind::cl: + return IssueKindMeta::cl; + case IssueKind::cy: + return IssueKindMeta::cy; + case IssueKind::de: + return IssueKindMeta::de; + case IssueKind::mv: + return IssueKindMeta::mv; + case IssueKind::sv: + return IssueKindMeta::sv;; + default: + throw std::domain_error(concat({ + u8"Unknown IssueKind enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // IssueKindForNamedFilterCreation + + const QLatin1String IssueKindForNamedFilterCreationMeta::av{"AV"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::cl{"CL"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::cy{"CY"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::de{"DE"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::mv{"MV"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::sv{"SV"}; + const QLatin1String IssueKindForNamedFilterCreationMeta::universal{"UNIVERSAL"}; + + // throws std::range_error + IssueKindForNamedFilterCreation IssueKindForNamedFilterCreationMeta::strToEnum(QAnyStringView str) + { + if (str == IssueKindForNamedFilterCreationMeta::av) + { + return IssueKindForNamedFilterCreation::av; + } + if (str == IssueKindForNamedFilterCreationMeta::cl) + { + return IssueKindForNamedFilterCreation::cl; + } + if (str == IssueKindForNamedFilterCreationMeta::cy) + { + return IssueKindForNamedFilterCreation::cy; + } + if (str == IssueKindForNamedFilterCreationMeta::de) + { + return IssueKindForNamedFilterCreation::de; + } + if (str == IssueKindForNamedFilterCreationMeta::mv) + { + return IssueKindForNamedFilterCreation::mv; + } + if (str == IssueKindForNamedFilterCreationMeta::sv) + { + return IssueKindForNamedFilterCreation::sv; + } + if (str == IssueKindForNamedFilterCreationMeta::universal) + { + return IssueKindForNamedFilterCreation::universal; + } + throw std::range_error(concat({ u8"Unknown IssueKindForNamedFilterCreation str: ", to_std_string(str) })); + } + + QLatin1String IssueKindForNamedFilterCreationMeta::enumToStr(IssueKindForNamedFilterCreation e) + { + switch (e) + { + case IssueKindForNamedFilterCreation::av: + return IssueKindForNamedFilterCreationMeta::av; + case IssueKindForNamedFilterCreation::cl: + return IssueKindForNamedFilterCreationMeta::cl; + case IssueKindForNamedFilterCreation::cy: + return IssueKindForNamedFilterCreationMeta::cy; + case IssueKindForNamedFilterCreation::de: + return IssueKindForNamedFilterCreationMeta::de; + case IssueKindForNamedFilterCreation::mv: + return IssueKindForNamedFilterCreationMeta::mv; + case IssueKindForNamedFilterCreation::sv: + return IssueKindForNamedFilterCreationMeta::sv; + case IssueKindForNamedFilterCreation::universal: + return IssueKindForNamedFilterCreationMeta::universal;; + default: + throw std::domain_error(concat({ + u8"Unknown IssueKindForNamedFilterCreation enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // IssueSourceLocationDto + + static const QLatin1String issueSourceLocationKeyFileName{"fileName"}; + static const QLatin1String issueSourceLocationKeyRole{"role"}; + static const QLatin1String issueSourceLocationKeySourceCodeUrl{"sourceCodeUrl"}; + static const QLatin1String issueSourceLocationKeyStartLine{"startLine"}; + static const QLatin1String issueSourceLocationKeyStartColumn{"startColumn"}; + static const QLatin1String issueSourceLocationKeyEndLine{"endLine"}; + static const QLatin1String issueSourceLocationKeyEndColumn{"endColumn"}; + + template<> + class de_serializer<IssueSourceLocationDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueSourceLocationDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueSourceLocationDto>(json); + return { + deserialize_field<QString>(jo, issueSourceLocationKeyFileName), + deserialize_field<std::optional<QString>>(jo, issueSourceLocationKeyRole), + deserialize_field<QString>(jo, issueSourceLocationKeySourceCodeUrl), + deserialize_field<qint32>(jo, issueSourceLocationKeyStartLine), + deserialize_field<qint32>(jo, issueSourceLocationKeyStartColumn), + deserialize_field<qint32>(jo, issueSourceLocationKeyEndLine), + deserialize_field<qint32>(jo, issueSourceLocationKeyEndColumn) + }; + } + + static QJsonValue serialize(const IssueSourceLocationDto &value) { + QJsonObject jo; + serialize_field(jo, issueSourceLocationKeyFileName, value.fileName); + serialize_field(jo, issueSourceLocationKeyRole, value.role); + serialize_field(jo, issueSourceLocationKeySourceCodeUrl, value.sourceCodeUrl); + serialize_field(jo, issueSourceLocationKeyStartLine, value.startLine); + serialize_field(jo, issueSourceLocationKeyStartColumn, value.startColumn); + serialize_field(jo, issueSourceLocationKeyEndLine, value.endLine); + serialize_field(jo, issueSourceLocationKeyEndColumn, value.endColumn); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueSourceLocationDto IssueSourceLocationDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueSourceLocationDto>(json); + } + + IssueSourceLocationDto::IssueSourceLocationDto( + QString fileName, + std::optional<QString> role, + QString sourceCodeUrl, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn + ) : + fileName(std::move(fileName)), + role(std::move(role)), + sourceCodeUrl(std::move(sourceCodeUrl)), + startLine(std::move(startLine)), + startColumn(std::move(startColumn)), + endLine(std::move(endLine)), + endColumn(std::move(endColumn)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueSourceLocationDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueTagDto + + static const QLatin1String issueTagKeyTag{"tag"}; + static const QLatin1String issueTagKeyColor{"color"}; + + template<> + class de_serializer<IssueTagDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueTagDto>(json); + return { + deserialize_field<QString>(jo, issueTagKeyTag), + deserialize_field<QString>(jo, issueTagKeyColor) + }; + } + + static QJsonValue serialize(const IssueTagDto &value) { + QJsonObject jo; + serialize_field(jo, issueTagKeyTag, value.tag); + serialize_field(jo, issueTagKeyColor, value.color); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueTagDto IssueTagDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueTagDto>(json); + } + + IssueTagDto::IssueTagDto( + QString tag, + QString color + ) : + tag(std::move(tag)), + color(std::move(color)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueTagDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueTagTypeDto + + static const QLatin1String issueTagTypeKeyId{"id"}; + static const QLatin1String issueTagTypeKeyText{"text"}; + static const QLatin1String issueTagTypeKeyTag{"tag"}; + static const QLatin1String issueTagTypeKeyColor{"color"}; + static const QLatin1String issueTagTypeKeyDescription{"description"}; + static const QLatin1String issueTagTypeKeySelected{"selected"}; + + template<> + class de_serializer<IssueTagTypeDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagTypeDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueTagTypeDto>(json); + return { + deserialize_field<QString>(jo, issueTagTypeKeyId), + deserialize_field<std::optional<QString>>(jo, issueTagTypeKeyText), + deserialize_field<std::optional<QString>>(jo, issueTagTypeKeyTag), + deserialize_field<QString>(jo, issueTagTypeKeyColor), + deserialize_field<std::optional<QString>>(jo, issueTagTypeKeyDescription), + deserialize_field<std::optional<bool>>(jo, issueTagTypeKeySelected) + }; + } + + static QJsonValue serialize(const IssueTagTypeDto &value) { + QJsonObject jo; + serialize_field(jo, issueTagTypeKeyId, value.id); + serialize_field(jo, issueTagTypeKeyText, value.text); + serialize_field(jo, issueTagTypeKeyTag, value.tag); + serialize_field(jo, issueTagTypeKeyColor, value.color); + serialize_field(jo, issueTagTypeKeyDescription, value.description); + serialize_field(jo, issueTagTypeKeySelected, value.selected); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueTagTypeDto IssueTagTypeDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueTagTypeDto>(json); + } + + IssueTagTypeDto::IssueTagTypeDto( + QString id, + std::optional<QString> text, + std::optional<QString> tag, + QString color, + std::optional<QString> description, + std::optional<bool> selected + ) : + id(std::move(id)), + text(std::move(text)), + tag(std::move(tag)), + color(std::move(color)), + description(std::move(description)), + selected(std::move(selected)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueTagTypeDto::serialize() const + { + return serialize_bytes(*this); + } + + // MessageSeverity + + const QLatin1String MessageSeverityMeta::debug{"DEBUG"}; + const QLatin1String MessageSeverityMeta::info{"INFO"}; + const QLatin1String MessageSeverityMeta::warning{"WARNING"}; + const QLatin1String MessageSeverityMeta::error{"ERROR"}; + const QLatin1String MessageSeverityMeta::fatal{"FATAL"}; + + // throws std::range_error + MessageSeverity MessageSeverityMeta::strToEnum(QAnyStringView str) + { + if (str == MessageSeverityMeta::debug) + { + return MessageSeverity::debug; + } + if (str == MessageSeverityMeta::info) + { + return MessageSeverity::info; + } + if (str == MessageSeverityMeta::warning) + { + return MessageSeverity::warning; + } + if (str == MessageSeverityMeta::error) + { + return MessageSeverity::error; + } + if (str == MessageSeverityMeta::fatal) + { + return MessageSeverity::fatal; + } + throw std::range_error(concat({ u8"Unknown MessageSeverity str: ", to_std_string(str) })); + } + + QLatin1String MessageSeverityMeta::enumToStr(MessageSeverity e) + { + switch (e) + { + case MessageSeverity::debug: + return MessageSeverityMeta::debug; + case MessageSeverity::info: + return MessageSeverityMeta::info; + case MessageSeverity::warning: + return MessageSeverityMeta::warning; + case MessageSeverity::error: + return MessageSeverityMeta::error; + case MessageSeverity::fatal: + return MessageSeverityMeta::fatal;; + default: + throw std::domain_error(concat({ + u8"Unknown MessageSeverity enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // MetricDto + + static const QLatin1String metricKeyName{"name"}; + static const QLatin1String metricKeyDisplayName{"displayName"}; + static const QLatin1String metricKeyMinValue{"minValue"}; + static const QLatin1String metricKeyMaxValue{"maxValue"}; + + template<> + class de_serializer<MetricDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static MetricDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<MetricDto>(json); + return { + deserialize_field<QString>(jo, metricKeyName), + deserialize_field<QString>(jo, metricKeyDisplayName), + deserialize_field<Any>(jo, metricKeyMinValue), + deserialize_field<Any>(jo, metricKeyMaxValue) + }; + } + + static QJsonValue serialize(const MetricDto &value) { + QJsonObject jo; + serialize_field(jo, metricKeyName, value.name); + serialize_field(jo, metricKeyDisplayName, value.displayName); + serialize_field(jo, metricKeyMinValue, value.minValue); + serialize_field(jo, metricKeyMaxValue, value.maxValue); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + MetricDto MetricDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<MetricDto>(json); + } + + MetricDto::MetricDto( + QString name, + QString displayName, + Any minValue, + Any maxValue + ) : + name(std::move(name)), + displayName(std::move(displayName)), + minValue(std::move(minValue)), + maxValue(std::move(maxValue)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray MetricDto::serialize() const + { + return serialize_bytes(*this); + } + + // MetricValueTableRowDto + + static const QLatin1String metricValueTableRowKeyMetric{"metric"}; + static const QLatin1String metricValueTableRowKeyPath{"path"}; + static const QLatin1String metricValueTableRowKeyLine{"line"}; + static const QLatin1String metricValueTableRowKeyValue{"value"}; + static const QLatin1String metricValueTableRowKeyEntity{"entity"}; + static const QLatin1String metricValueTableRowKeyEntityType{"entityType"}; + static const QLatin1String metricValueTableRowKeyEntityId{"entityId"}; + + template<> + class de_serializer<MetricValueTableRowDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueTableRowDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<MetricValueTableRowDto>(json); + return { + deserialize_field<QString>(jo, metricValueTableRowKeyMetric), + deserialize_field<std::optional<QString>>(jo, metricValueTableRowKeyPath), + deserialize_field<std::optional<qint32>>(jo, metricValueTableRowKeyLine), + deserialize_field<std::optional<double>>(jo, metricValueTableRowKeyValue), + deserialize_field<QString>(jo, metricValueTableRowKeyEntity), + deserialize_field<QString>(jo, metricValueTableRowKeyEntityType), + deserialize_field<QString>(jo, metricValueTableRowKeyEntityId) + }; + } + + static QJsonValue serialize(const MetricValueTableRowDto &value) { + QJsonObject jo; + serialize_field(jo, metricValueTableRowKeyMetric, value.metric); + serialize_field(jo, metricValueTableRowKeyPath, value.path); + serialize_field(jo, metricValueTableRowKeyLine, value.line); + serialize_field(jo, metricValueTableRowKeyValue, value.value); + serialize_field(jo, metricValueTableRowKeyEntity, value.entity); + serialize_field(jo, metricValueTableRowKeyEntityType, value.entityType); + serialize_field(jo, metricValueTableRowKeyEntityId, value.entityId); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + MetricValueTableRowDto MetricValueTableRowDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<MetricValueTableRowDto>(json); + } + + MetricValueTableRowDto::MetricValueTableRowDto( + QString metric, + std::optional<QString> path, + std::optional<qint32> line, + std::optional<double> value, + QString entity, + QString entityType, + QString entityId + ) : + metric(std::move(metric)), + path(std::move(path)), + line(std::move(line)), + value(std::move(value)), + entity(std::move(entity)), + entityType(std::move(entityType)), + entityId(std::move(entityId)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray MetricValueTableRowDto::serialize() const + { + return serialize_bytes(*this); + } + + // NamedFilterType + + const QLatin1String NamedFilterTypeMeta::predefined{"PREDEFINED"}; + const QLatin1String NamedFilterTypeMeta::global{"GLOBAL"}; + const QLatin1String NamedFilterTypeMeta::custom{"CUSTOM"}; + + // throws std::range_error + NamedFilterType NamedFilterTypeMeta::strToEnum(QAnyStringView str) + { + if (str == NamedFilterTypeMeta::predefined) + { + return NamedFilterType::predefined; + } + if (str == NamedFilterTypeMeta::global) + { + return NamedFilterType::global; + } + if (str == NamedFilterTypeMeta::custom) + { + return NamedFilterType::custom; + } + throw std::range_error(concat({ u8"Unknown NamedFilterType str: ", to_std_string(str) })); + } + + QLatin1String NamedFilterTypeMeta::enumToStr(NamedFilterType e) + { + switch (e) + { + case NamedFilterType::predefined: + return NamedFilterTypeMeta::predefined; + case NamedFilterType::global: + return NamedFilterTypeMeta::global; + case NamedFilterType::custom: + return NamedFilterTypeMeta::custom;; + default: + throw std::domain_error(concat({ + u8"Unknown NamedFilterType enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // NamedFilterVisibilityDto + + static const QLatin1String namedFilterVisibilityKeyGroups{"groups"}; + + template<> + class de_serializer<NamedFilterVisibilityDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterVisibilityDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<NamedFilterVisibilityDto>(json); + return { + deserialize_field<std::optional<std::vector<QString>>>(jo, namedFilterVisibilityKeyGroups) + }; + } + + static QJsonValue serialize(const NamedFilterVisibilityDto &value) { + QJsonObject jo; + serialize_field(jo, namedFilterVisibilityKeyGroups, value.groups); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + NamedFilterVisibilityDto NamedFilterVisibilityDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<NamedFilterVisibilityDto>(json); + } + + NamedFilterVisibilityDto::NamedFilterVisibilityDto( + std::optional<std::vector<QString>> groups + ) : + groups(std::move(groups)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray NamedFilterVisibilityDto::serialize() const + { + return serialize_bytes(*this); + } + + // ProjectReferenceDto + + static const QLatin1String projectReferenceKeyName{"name"}; + static const QLatin1String projectReferenceKeyUrl{"url"}; + + template<> + class de_serializer<ProjectReferenceDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ProjectReferenceDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ProjectReferenceDto>(json); + return { + deserialize_field<QString>(jo, projectReferenceKeyName), + deserialize_field<QString>(jo, projectReferenceKeyUrl) + }; + } + + static QJsonValue serialize(const ProjectReferenceDto &value) { + QJsonObject jo; + serialize_field(jo, projectReferenceKeyName, value.name); + serialize_field(jo, projectReferenceKeyUrl, value.url); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ProjectReferenceDto ProjectReferenceDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ProjectReferenceDto>(json); + } + + ProjectReferenceDto::ProjectReferenceDto( + QString name, + QString url + ) : + name(std::move(name)), + url(std::move(url)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ProjectReferenceDto::serialize() const + { + return serialize_bytes(*this); + } + + // RuleDto + + static const QLatin1String ruleKeyName{"name"}; + static const QLatin1String ruleKeyOriginal_name{"original_name"}; + static const QLatin1String ruleKeyDisabled{"disabled"}; + + template<> + class de_serializer<RuleDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static RuleDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<RuleDto>(json); + return { + deserialize_field<QString>(jo, ruleKeyName), + deserialize_field<QString>(jo, ruleKeyOriginal_name), + deserialize_field<std::optional<bool>>(jo, ruleKeyDisabled) + }; + } + + static QJsonValue serialize(const RuleDto &value) { + QJsonObject jo; + serialize_field(jo, ruleKeyName, value.name); + serialize_field(jo, ruleKeyOriginal_name, value.original_name); + serialize_field(jo, ruleKeyDisabled, value.disabled); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + RuleDto RuleDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<RuleDto>(json); + } + + RuleDto::RuleDto( + QString name, + QString original_name, + std::optional<bool> disabled + ) : + name(std::move(name)), + original_name(std::move(original_name)), + disabled(std::move(disabled)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray RuleDto::serialize() const + { + return serialize_bytes(*this); + } + + // SortDirection + + const QLatin1String SortDirectionMeta::asc{"ASC"}; + const QLatin1String SortDirectionMeta::desc{"DESC"}; + + // throws std::range_error + SortDirection SortDirectionMeta::strToEnum(QAnyStringView str) + { + if (str == SortDirectionMeta::asc) + { + return SortDirection::asc; + } + if (str == SortDirectionMeta::desc) + { + return SortDirection::desc; + } + throw std::range_error(concat({ u8"Unknown SortDirection str: ", to_std_string(str) })); + } + + QLatin1String SortDirectionMeta::enumToStr(SortDirection e) + { + switch (e) + { + case SortDirection::asc: + return SortDirectionMeta::asc; + case SortDirection::desc: + return SortDirectionMeta::desc;; + default: + throw std::domain_error(concat({ + u8"Unknown SortDirection enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // TableCellAlignment + + const QLatin1String TableCellAlignmentMeta::left{"left"}; + const QLatin1String TableCellAlignmentMeta::right{"right"}; + const QLatin1String TableCellAlignmentMeta::center{"center"}; + + // throws std::range_error + TableCellAlignment TableCellAlignmentMeta::strToEnum(QAnyStringView str) + { + if (str == TableCellAlignmentMeta::left) + { + return TableCellAlignment::left; + } + if (str == TableCellAlignmentMeta::right) + { + return TableCellAlignment::right; + } + if (str == TableCellAlignmentMeta::center) + { + return TableCellAlignment::center; + } + throw std::range_error(concat({ u8"Unknown TableCellAlignment str: ", to_std_string(str) })); + } + + QLatin1String TableCellAlignmentMeta::enumToStr(TableCellAlignment e) + { + switch (e) + { + case TableCellAlignment::left: + return TableCellAlignmentMeta::left; + case TableCellAlignment::right: + return TableCellAlignmentMeta::right; + case TableCellAlignment::center: + return TableCellAlignmentMeta::center;; + default: + throw std::domain_error(concat({ + u8"Unknown TableCellAlignment enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // ToolsVersionDto + + static const QLatin1String toolsVersionKeyName{"name"}; + static const QLatin1String toolsVersionKeyNumber{"number"}; + static const QLatin1String toolsVersionKeyBuildDate{"buildDate"}; + + template<> + class de_serializer<ToolsVersionDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ToolsVersionDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ToolsVersionDto>(json); + return { + deserialize_field<QString>(jo, toolsVersionKeyName), + deserialize_field<QString>(jo, toolsVersionKeyNumber), + deserialize_field<QString>(jo, toolsVersionKeyBuildDate) + }; + } + + static QJsonValue serialize(const ToolsVersionDto &value) { + QJsonObject jo; + serialize_field(jo, toolsVersionKeyName, value.name); + serialize_field(jo, toolsVersionKeyNumber, value.number); + serialize_field(jo, toolsVersionKeyBuildDate, value.buildDate); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ToolsVersionDto ToolsVersionDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ToolsVersionDto>(json); + } + + ToolsVersionDto::ToolsVersionDto( + QString name, + QString number, + QString buildDate + ) : + name(std::move(name)), + number(std::move(number)), + buildDate(std::move(buildDate)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ToolsVersionDto::serialize() const + { + return serialize_bytes(*this); + } + + // UserRefType + + const QLatin1String UserRefTypeMeta::virtual_user{"VIRTUAL_USER"}; + const QLatin1String UserRefTypeMeta::dashboard_user{"DASHBOARD_USER"}; + const QLatin1String UserRefTypeMeta::unmapped_user{"UNMAPPED_USER"}; + + // throws std::range_error + UserRefType UserRefTypeMeta::strToEnum(QAnyStringView str) + { + if (str == UserRefTypeMeta::virtual_user) + { + return UserRefType::virtual_user; + } + if (str == UserRefTypeMeta::dashboard_user) + { + return UserRefType::dashboard_user; + } + if (str == UserRefTypeMeta::unmapped_user) + { + return UserRefType::unmapped_user; + } + throw std::range_error(concat({ u8"Unknown UserRefType str: ", to_std_string(str) })); + } + + QLatin1String UserRefTypeMeta::enumToStr(UserRefType e) + { + switch (e) + { + case UserRefType::virtual_user: + return UserRefTypeMeta::virtual_user; + case UserRefType::dashboard_user: + return UserRefTypeMeta::dashboard_user; + case UserRefType::unmapped_user: + return UserRefTypeMeta::unmapped_user;; + default: + throw std::domain_error(concat({ + u8"Unknown UserRefType enum: ", + to_std_string(static_cast<int>(e)) + })); + } + } + + // VersionKindCountDto + + static const QLatin1String versionKindCountKeyTotal{"Total"}; + static const QLatin1String versionKindCountKeyAdded{"Added"}; + static const QLatin1String versionKindCountKeyRemoved{"Removed"}; + + template<> + class de_serializer<VersionKindCountDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static VersionKindCountDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<VersionKindCountDto>(json); + return { + deserialize_field<qint32>(jo, versionKindCountKeyTotal), + deserialize_field<qint32>(jo, versionKindCountKeyAdded), + deserialize_field<qint32>(jo, versionKindCountKeyRemoved) + }; + } + + static QJsonValue serialize(const VersionKindCountDto &value) { + QJsonObject jo; + serialize_field(jo, versionKindCountKeyTotal, value.Total); + serialize_field(jo, versionKindCountKeyAdded, value.Added); + serialize_field(jo, versionKindCountKeyRemoved, value.Removed); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + VersionKindCountDto VersionKindCountDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<VersionKindCountDto>(json); + } + + VersionKindCountDto::VersionKindCountDto( + qint32 Total, + qint32 Added, + qint32 Removed + ) : + Total(std::move(Total)), + Added(std::move(Added)), + Removed(std::move(Removed)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray VersionKindCountDto::serialize() const + { + return serialize_bytes(*this); + } + + // AnalysisVersionDto + + static const QLatin1String analysisVersionKeyDate{"date"}; + static const QLatin1String analysisVersionKeyLabel{"label"}; + static const QLatin1String analysisVersionKeyIndex{"index"}; + static const QLatin1String analysisVersionKeyName{"name"}; + static const QLatin1String analysisVersionKeyMillis{"millis"}; + static const QLatin1String analysisVersionKeyIssueCounts{"issueCounts"}; + static const QLatin1String analysisVersionKeyToolsVersion{"toolsVersion"}; + static const QLatin1String analysisVersionKeyLinesOfCode{"linesOfCode"}; + static const QLatin1String analysisVersionKeyCloneRatio{"cloneRatio"}; + + template<> + class de_serializer<AnalysisVersionDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static AnalysisVersionDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<AnalysisVersionDto>(json); + return { + deserialize_field<QString>(jo, analysisVersionKeyDate), + deserialize_field<std::optional<QString>>(jo, analysisVersionKeyLabel), + deserialize_field<qint32>(jo, analysisVersionKeyIndex), + deserialize_field<QString>(jo, analysisVersionKeyName), + deserialize_field<qint64>(jo, analysisVersionKeyMillis), + deserialize_field<Any>(jo, analysisVersionKeyIssueCounts), + deserialize_field<std::optional<ToolsVersionDto>>(jo, analysisVersionKeyToolsVersion), + deserialize_field<std::optional<qint64>>(jo, analysisVersionKeyLinesOfCode), + deserialize_field<std::optional<double>>(jo, analysisVersionKeyCloneRatio) + }; + } + + static QJsonValue serialize(const AnalysisVersionDto &value) { + QJsonObject jo; + serialize_field(jo, analysisVersionKeyDate, value.date); + serialize_field(jo, analysisVersionKeyLabel, value.label); + serialize_field(jo, analysisVersionKeyIndex, value.index); + serialize_field(jo, analysisVersionKeyName, value.name); + serialize_field(jo, analysisVersionKeyMillis, value.millis); + serialize_field(jo, analysisVersionKeyIssueCounts, value.issueCounts); + serialize_field(jo, analysisVersionKeyToolsVersion, value.toolsVersion); + serialize_field(jo, analysisVersionKeyLinesOfCode, value.linesOfCode); + serialize_field(jo, analysisVersionKeyCloneRatio, value.cloneRatio); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + AnalysisVersionDto AnalysisVersionDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<AnalysisVersionDto>(json); + } + + AnalysisVersionDto::AnalysisVersionDto( + QString date, + std::optional<QString> label, + qint32 index, + QString name, + qint64 millis, + Any issueCounts, + std::optional<ToolsVersionDto> toolsVersion, + std::optional<qint64> linesOfCode, + std::optional<double> cloneRatio + ) : + date(std::move(date)), + label(std::move(label)), + index(std::move(index)), + name(std::move(name)), + millis(std::move(millis)), + issueCounts(std::move(issueCounts)), + toolsVersion(std::move(toolsVersion)), + linesOfCode(std::move(linesOfCode)), + cloneRatio(std::move(cloneRatio)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray AnalysisVersionDto::serialize() const + { + return serialize_bytes(*this); + } + + // ApiTokenCreationRequestDto + + static const QLatin1String apiTokenCreationRequestKeyPassword{"password"}; + static const QLatin1String apiTokenCreationRequestKeyType{"type"}; + static const QLatin1String apiTokenCreationRequestKeyDescription{"description"}; + static const QLatin1String apiTokenCreationRequestKeyMaxAgeMillis{"maxAgeMillis"}; + + template<> + class de_serializer<ApiTokenCreationRequestDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ApiTokenCreationRequestDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ApiTokenCreationRequestDto>(json); + return { + deserialize_field<QString>(jo, apiTokenCreationRequestKeyPassword), + deserialize_field<QString>(jo, apiTokenCreationRequestKeyType), + deserialize_field<QString>(jo, apiTokenCreationRequestKeyDescription), + deserialize_field<qint64>(jo, apiTokenCreationRequestKeyMaxAgeMillis) + }; + } + + static QJsonValue serialize(const ApiTokenCreationRequestDto &value) { + QJsonObject jo; + serialize_field(jo, apiTokenCreationRequestKeyPassword, value.password); + serialize_field(jo, apiTokenCreationRequestKeyType, value.type); + serialize_field(jo, apiTokenCreationRequestKeyDescription, value.description); + serialize_field(jo, apiTokenCreationRequestKeyMaxAgeMillis, value.maxAgeMillis); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ApiTokenCreationRequestDto ApiTokenCreationRequestDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ApiTokenCreationRequestDto>(json); + } + + ApiTokenCreationRequestDto::ApiTokenCreationRequestDto( + QString password, + QString type, + QString description, + qint64 maxAgeMillis + ) : + password(std::move(password)), + type(std::move(type)), + description(std::move(description)), + maxAgeMillis(std::move(maxAgeMillis)) + { } + + ApiTokenCreationRequestDto::ApiTokenCreationRequestDto( + QString password, + ApiTokenType type, + QString description, + qint64 maxAgeMillis + ) : ApiTokenCreationRequestDto( + std::move(password), + ApiTokenTypeMeta::enumToStr(type), + std::move(description), + std::move(maxAgeMillis)) + { } + + // throws std::range_error + ApiTokenType ApiTokenCreationRequestDto::getTypeEnum() const + { + return ApiTokenTypeMeta::strToEnum(type); + } + + void ApiTokenCreationRequestDto::setTypeEnum(ApiTokenType newValue) + { + type = ApiTokenTypeMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ApiTokenCreationRequestDto::serialize() const + { + return serialize_bytes(*this); + } + + // ApiTokenInfoDto + + static const QLatin1String apiTokenInfoKeyId{"id"}; + static const QLatin1String apiTokenInfoKeyUrl{"url"}; + static const QLatin1String apiTokenInfoKeyIsValid{"isValid"}; + static const QLatin1String apiTokenInfoKeyType{"type"}; + static const QLatin1String apiTokenInfoKeyDescription{"description"}; + static const QLatin1String apiTokenInfoKeyToken{"token"}; + static const QLatin1String apiTokenInfoKeyCreationDate{"creationDate"}; + static const QLatin1String apiTokenInfoKeyDisplayCreationDate{"displayCreationDate"}; + static const QLatin1String apiTokenInfoKeyExpirationDate{"expirationDate"}; + static const QLatin1String apiTokenInfoKeyDisplayExpirationDate{"displayExpirationDate"}; + static const QLatin1String apiTokenInfoKeyLastUseDate{"lastUseDate"}; + static const QLatin1String apiTokenInfoKeyDisplayLastUseDate{"displayLastUseDate"}; + static const QLatin1String apiTokenInfoKeyUsedByCurrentRequest{"usedByCurrentRequest"}; + + template<> + class de_serializer<ApiTokenInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ApiTokenInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ApiTokenInfoDto>(json); + return { + deserialize_field<QString>(jo, apiTokenInfoKeyId), + deserialize_field<QString>(jo, apiTokenInfoKeyUrl), + deserialize_field<bool>(jo, apiTokenInfoKeyIsValid), + deserialize_field<QString>(jo, apiTokenInfoKeyType), + deserialize_field<QString>(jo, apiTokenInfoKeyDescription), + deserialize_field<std::optional<QString>>(jo, apiTokenInfoKeyToken), + deserialize_field<QString>(jo, apiTokenInfoKeyCreationDate), + deserialize_field<QString>(jo, apiTokenInfoKeyDisplayCreationDate), + deserialize_field<QString>(jo, apiTokenInfoKeyExpirationDate), + deserialize_field<QString>(jo, apiTokenInfoKeyDisplayExpirationDate), + deserialize_field<std::optional<QString>>(jo, apiTokenInfoKeyLastUseDate), + deserialize_field<QString>(jo, apiTokenInfoKeyDisplayLastUseDate), + deserialize_field<bool>(jo, apiTokenInfoKeyUsedByCurrentRequest) + }; + } + + static QJsonValue serialize(const ApiTokenInfoDto &value) { + QJsonObject jo; + serialize_field(jo, apiTokenInfoKeyId, value.id); + serialize_field(jo, apiTokenInfoKeyUrl, value.url); + serialize_field(jo, apiTokenInfoKeyIsValid, value.isValid); + serialize_field(jo, apiTokenInfoKeyType, value.type); + serialize_field(jo, apiTokenInfoKeyDescription, value.description); + serialize_field(jo, apiTokenInfoKeyToken, value.token); + serialize_field(jo, apiTokenInfoKeyCreationDate, value.creationDate); + serialize_field(jo, apiTokenInfoKeyDisplayCreationDate, value.displayCreationDate); + serialize_field(jo, apiTokenInfoKeyExpirationDate, value.expirationDate); + serialize_field(jo, apiTokenInfoKeyDisplayExpirationDate, value.displayExpirationDate); + serialize_field(jo, apiTokenInfoKeyLastUseDate, value.lastUseDate); + serialize_field(jo, apiTokenInfoKeyDisplayLastUseDate, value.displayLastUseDate); + serialize_field(jo, apiTokenInfoKeyUsedByCurrentRequest, value.usedByCurrentRequest); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ApiTokenInfoDto ApiTokenInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ApiTokenInfoDto>(json); + } + + ApiTokenInfoDto::ApiTokenInfoDto( + QString id, + QString url, + bool isValid, + QString type, + QString description, + std::optional<QString> token, + QString creationDate, + QString displayCreationDate, + QString expirationDate, + QString displayExpirationDate, + std::optional<QString> lastUseDate, + QString displayLastUseDate, + bool usedByCurrentRequest + ) : + id(std::move(id)), + url(std::move(url)), + isValid(std::move(isValid)), + type(std::move(type)), + description(std::move(description)), + token(std::move(token)), + creationDate(std::move(creationDate)), + displayCreationDate(std::move(displayCreationDate)), + expirationDate(std::move(expirationDate)), + displayExpirationDate(std::move(displayExpirationDate)), + lastUseDate(std::move(lastUseDate)), + displayLastUseDate(std::move(displayLastUseDate)), + usedByCurrentRequest(std::move(usedByCurrentRequest)) + { } + + ApiTokenInfoDto::ApiTokenInfoDto( + QString id, + QString url, + bool isValid, + ApiTokenType type, + QString description, + std::optional<QString> token, + QString creationDate, + QString displayCreationDate, + QString expirationDate, + QString displayExpirationDate, + std::optional<QString> lastUseDate, + QString displayLastUseDate, + bool usedByCurrentRequest + ) : ApiTokenInfoDto( + std::move(id), + std::move(url), + std::move(isValid), + ApiTokenTypeMeta::enumToStr(type), + std::move(description), + std::move(token), + std::move(creationDate), + std::move(displayCreationDate), + std::move(expirationDate), + std::move(displayExpirationDate), + std::move(lastUseDate), + std::move(displayLastUseDate), + std::move(usedByCurrentRequest)) + { } + + // throws std::range_error + ApiTokenType ApiTokenInfoDto::getTypeEnum() const + { + return ApiTokenTypeMeta::strToEnum(type); + } + + void ApiTokenInfoDto::setTypeEnum(ApiTokenType newValue) + { + type = ApiTokenTypeMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ApiTokenInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // ColumnInfoDto + + static const QLatin1String columnInfoKeyKey{"key"}; + static const QLatin1String columnInfoKeyHeader{"header"}; + static const QLatin1String columnInfoKeyCanSort{"canSort"}; + static const QLatin1String columnInfoKeyCanFilter{"canFilter"}; + static const QLatin1String columnInfoKeyAlignment{"alignment"}; + static const QLatin1String columnInfoKeyType{"type"}; + static const QLatin1String columnInfoKeyTypeOptions{"typeOptions"}; + static const QLatin1String columnInfoKeyWidth{"width"}; + static const QLatin1String columnInfoKeyShowByDefault{"showByDefault"}; + static const QLatin1String columnInfoKeyLinkKey{"linkKey"}; + + template<> + class de_serializer<ColumnInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ColumnInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ColumnInfoDto>(json); + return { + deserialize_field<QString>(jo, columnInfoKeyKey), + deserialize_field<std::optional<QString>>(jo, columnInfoKeyHeader), + deserialize_field<bool>(jo, columnInfoKeyCanSort), + deserialize_field<bool>(jo, columnInfoKeyCanFilter), + deserialize_field<QString>(jo, columnInfoKeyAlignment), + deserialize_field<QString>(jo, columnInfoKeyType), + deserialize_field<std::optional<std::vector<ColumnTypeOptionDto>>>(jo, columnInfoKeyTypeOptions), + deserialize_field<qint32>(jo, columnInfoKeyWidth), + deserialize_field<bool>(jo, columnInfoKeyShowByDefault), + deserialize_field<std::optional<QString>>(jo, columnInfoKeyLinkKey) + }; + } + + static QJsonValue serialize(const ColumnInfoDto &value) { + QJsonObject jo; + serialize_field(jo, columnInfoKeyKey, value.key); + serialize_field(jo, columnInfoKeyHeader, value.header); + serialize_field(jo, columnInfoKeyCanSort, value.canSort); + serialize_field(jo, columnInfoKeyCanFilter, value.canFilter); + serialize_field(jo, columnInfoKeyAlignment, value.alignment); + serialize_field(jo, columnInfoKeyType, value.type); + serialize_field(jo, columnInfoKeyTypeOptions, value.typeOptions); + serialize_field(jo, columnInfoKeyWidth, value.width); + serialize_field(jo, columnInfoKeyShowByDefault, value.showByDefault); + serialize_field(jo, columnInfoKeyLinkKey, value.linkKey); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ColumnInfoDto ColumnInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ColumnInfoDto>(json); + } + + ColumnInfoDto::ColumnInfoDto( + QString key, + std::optional<QString> header, + bool canSort, + bool canFilter, + QString alignment, + QString type, + std::optional<std::vector<ColumnTypeOptionDto>> typeOptions, + qint32 width, + bool showByDefault, + std::optional<QString> linkKey + ) : + key(std::move(key)), + header(std::move(header)), + canSort(std::move(canSort)), + canFilter(std::move(canFilter)), + alignment(std::move(alignment)), + type(std::move(type)), + typeOptions(std::move(typeOptions)), + width(std::move(width)), + showByDefault(std::move(showByDefault)), + linkKey(std::move(linkKey)) + { } + + ColumnInfoDto::ColumnInfoDto( + QString key, + std::optional<QString> header, + bool canSort, + bool canFilter, + TableCellAlignment alignment, + QString type, + std::optional<std::vector<ColumnTypeOptionDto>> typeOptions, + qint32 width, + bool showByDefault, + std::optional<QString> linkKey + ) : ColumnInfoDto( + std::move(key), + std::move(header), + std::move(canSort), + std::move(canFilter), + TableCellAlignmentMeta::enumToStr(alignment), + std::move(type), + std::move(typeOptions), + std::move(width), + std::move(showByDefault), + std::move(linkKey)) + { } + + // throws std::range_error + TableCellAlignment ColumnInfoDto::getAlignmentEnum() const + { + return TableCellAlignmentMeta::strToEnum(alignment); + } + + void ColumnInfoDto::setAlignmentEnum(TableCellAlignment newValue) + { + alignment = TableCellAlignmentMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ColumnInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // DashboardInfoDto + + static const QLatin1String dashboardInfoKeyMainUrl{"mainUrl"}; + static const QLatin1String dashboardInfoKeyDashboardVersion{"dashboardVersion"}; + static const QLatin1String dashboardInfoKeyDashboardVersionNumber{"dashboardVersionNumber"}; + static const QLatin1String dashboardInfoKeyDashboardBuildDate{"dashboardBuildDate"}; + static const QLatin1String dashboardInfoKeyUsername{"username"}; + static const QLatin1String dashboardInfoKeyCsrfTokenHeader{"csrfTokenHeader"}; + static const QLatin1String dashboardInfoKeyCsrfToken{"csrfToken"}; + static const QLatin1String dashboardInfoKeyCheckCredentialsUrl{"checkCredentialsUrl"}; + static const QLatin1String dashboardInfoKeyNamedFiltersUrl{"namedFiltersUrl"}; + static const QLatin1String dashboardInfoKeyProjects{"projects"}; + static const QLatin1String dashboardInfoKeyUserApiTokenUrl{"userApiTokenUrl"}; + static const QLatin1String dashboardInfoKeyUserNamedFiltersUrl{"userNamedFiltersUrl"}; + static const QLatin1String dashboardInfoKeySupportAddress{"supportAddress"}; + static const QLatin1String dashboardInfoKeyIssueFilterHelp{"issueFilterHelp"}; + static const QLatin1String dashboardInfoKeyCsrfTokenUrl{"csrfTokenUrl"}; + + template<> + class de_serializer<DashboardInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static DashboardInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<DashboardInfoDto>(json); + return { + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyMainUrl), + deserialize_field<QString>(jo, dashboardInfoKeyDashboardVersion), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyDashboardVersionNumber), + deserialize_field<QString>(jo, dashboardInfoKeyDashboardBuildDate), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyUsername), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyCsrfTokenHeader), + deserialize_field<QString>(jo, dashboardInfoKeyCsrfToken), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyCheckCredentialsUrl), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyNamedFiltersUrl), + deserialize_field<std::optional<std::vector<ProjectReferenceDto>>>(jo, dashboardInfoKeyProjects), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyUserApiTokenUrl), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyUserNamedFiltersUrl), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeySupportAddress), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyIssueFilterHelp), + deserialize_field<std::optional<QString>>(jo, dashboardInfoKeyCsrfTokenUrl) + }; + } + + static QJsonValue serialize(const DashboardInfoDto &value) { + QJsonObject jo; + serialize_field(jo, dashboardInfoKeyMainUrl, value.mainUrl); + serialize_field(jo, dashboardInfoKeyDashboardVersion, value.dashboardVersion); + serialize_field(jo, dashboardInfoKeyDashboardVersionNumber, value.dashboardVersionNumber); + serialize_field(jo, dashboardInfoKeyDashboardBuildDate, value.dashboardBuildDate); + serialize_field(jo, dashboardInfoKeyUsername, value.username); + serialize_field(jo, dashboardInfoKeyCsrfTokenHeader, value.csrfTokenHeader); + serialize_field(jo, dashboardInfoKeyCsrfToken, value.csrfToken); + serialize_field(jo, dashboardInfoKeyCheckCredentialsUrl, value.checkCredentialsUrl); + serialize_field(jo, dashboardInfoKeyNamedFiltersUrl, value.namedFiltersUrl); + serialize_field(jo, dashboardInfoKeyProjects, value.projects); + serialize_field(jo, dashboardInfoKeyUserApiTokenUrl, value.userApiTokenUrl); + serialize_field(jo, dashboardInfoKeyUserNamedFiltersUrl, value.userNamedFiltersUrl); + serialize_field(jo, dashboardInfoKeySupportAddress, value.supportAddress); + serialize_field(jo, dashboardInfoKeyIssueFilterHelp, value.issueFilterHelp); + serialize_field(jo, dashboardInfoKeyCsrfTokenUrl, value.csrfTokenUrl); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + DashboardInfoDto DashboardInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<DashboardInfoDto>(json); + } + + DashboardInfoDto::DashboardInfoDto( + std::optional<QString> mainUrl, + QString dashboardVersion, + std::optional<QString> dashboardVersionNumber, + QString dashboardBuildDate, + std::optional<QString> username, + std::optional<QString> csrfTokenHeader, + QString csrfToken, + std::optional<QString> checkCredentialsUrl, + std::optional<QString> namedFiltersUrl, + std::optional<std::vector<ProjectReferenceDto>> projects, + std::optional<QString> userApiTokenUrl, + std::optional<QString> userNamedFiltersUrl, + std::optional<QString> supportAddress, + std::optional<QString> issueFilterHelp, + std::optional<QString> csrfTokenUrl + ) : + mainUrl(std::move(mainUrl)), + dashboardVersion(std::move(dashboardVersion)), + dashboardVersionNumber(std::move(dashboardVersionNumber)), + dashboardBuildDate(std::move(dashboardBuildDate)), + username(std::move(username)), + csrfTokenHeader(std::move(csrfTokenHeader)), + csrfToken(std::move(csrfToken)), + checkCredentialsUrl(std::move(checkCredentialsUrl)), + namedFiltersUrl(std::move(namedFiltersUrl)), + projects(std::move(projects)), + userApiTokenUrl(std::move(userApiTokenUrl)), + userNamedFiltersUrl(std::move(userNamedFiltersUrl)), + supportAddress(std::move(supportAddress)), + issueFilterHelp(std::move(issueFilterHelp)), + csrfTokenUrl(std::move(csrfTokenUrl)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray DashboardInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueCommentListDto + + static const QLatin1String issueCommentListKeyComments{"comments"}; + + template<> + class de_serializer<IssueCommentListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueCommentListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueCommentListDto>(json); + return { + deserialize_field<std::vector<IssueCommentDto>>(jo, issueCommentListKeyComments) + }; + } + + static QJsonValue serialize(const IssueCommentListDto &value) { + QJsonObject jo; + serialize_field(jo, issueCommentListKeyComments, value.comments); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueCommentListDto IssueCommentListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueCommentListDto>(json); + } + + IssueCommentListDto::IssueCommentListDto( + std::vector<IssueCommentDto> comments + ) : + comments(std::move(comments)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueCommentListDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueKindInfoDto + + static const QLatin1String issueKindInfoKeyPrefix{"prefix"}; + static const QLatin1String issueKindInfoKeyNiceSingularName{"niceSingularName"}; + static const QLatin1String issueKindInfoKeyNicePluralName{"nicePluralName"}; + + template<> + class de_serializer<IssueKindInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueKindInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueKindInfoDto>(json); + return { + deserialize_field<QString>(jo, issueKindInfoKeyPrefix), + deserialize_field<QString>(jo, issueKindInfoKeyNiceSingularName), + deserialize_field<QString>(jo, issueKindInfoKeyNicePluralName) + }; + } + + static QJsonValue serialize(const IssueKindInfoDto &value) { + QJsonObject jo; + serialize_field(jo, issueKindInfoKeyPrefix, value.prefix); + serialize_field(jo, issueKindInfoKeyNiceSingularName, value.niceSingularName); + serialize_field(jo, issueKindInfoKeyNicePluralName, value.nicePluralName); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueKindInfoDto IssueKindInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueKindInfoDto>(json); + } + + IssueKindInfoDto::IssueKindInfoDto( + QString prefix, + QString niceSingularName, + QString nicePluralName + ) : + prefix(std::move(prefix)), + niceSingularName(std::move(niceSingularName)), + nicePluralName(std::move(nicePluralName)) + { } + + IssueKindInfoDto::IssueKindInfoDto( + IssueKind prefix, + QString niceSingularName, + QString nicePluralName + ) : IssueKindInfoDto( + IssueKindMeta::enumToStr(prefix), + std::move(niceSingularName), + std::move(nicePluralName)) + { } + + // throws std::range_error + IssueKind IssueKindInfoDto::getPrefixEnum() const + { + return IssueKindMeta::strToEnum(prefix); + } + + void IssueKindInfoDto::setPrefixEnum(IssueKind newValue) + { + prefix = IssueKindMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueKindInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueTagTypeListDto + + static const QLatin1String issueTagTypeListKeyTags{"tags"}; + + template<> + class de_serializer<IssueTagTypeListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagTypeListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueTagTypeListDto>(json); + return { + deserialize_field<std::vector<IssueTagTypeDto>>(jo, issueTagTypeListKeyTags) + }; + } + + static QJsonValue serialize(const IssueTagTypeListDto &value) { + QJsonObject jo; + serialize_field(jo, issueTagTypeListKeyTags, value.tags); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueTagTypeListDto IssueTagTypeListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueTagTypeListDto>(json); + } + + IssueTagTypeListDto::IssueTagTypeListDto( + std::vector<IssueTagTypeDto> tags + ) : + tags(std::move(tags)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueTagTypeListDto::serialize() const + { + return serialize_bytes(*this); + } + + // LineMarkerDto + + static const QLatin1String lineMarkerKeyKind{"kind"}; + static const QLatin1String lineMarkerKeyId{"id"}; + static const QLatin1String lineMarkerKeyStartLine{"startLine"}; + static const QLatin1String lineMarkerKeyStartColumn{"startColumn"}; + static const QLatin1String lineMarkerKeyEndLine{"endLine"}; + static const QLatin1String lineMarkerKeyEndColumn{"endColumn"}; + static const QLatin1String lineMarkerKeyDescription{"description"}; + static const QLatin1String lineMarkerKeyIssueUrl{"issueUrl"}; + static const QLatin1String lineMarkerKeyIsNew{"isNew"}; + + template<> + class de_serializer<LineMarkerDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static LineMarkerDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<LineMarkerDto>(json); + return { + deserialize_field<QString>(jo, lineMarkerKeyKind), + deserialize_field<std::optional<qint64>>(jo, lineMarkerKeyId), + deserialize_field<qint32>(jo, lineMarkerKeyStartLine), + deserialize_field<qint32>(jo, lineMarkerKeyStartColumn), + deserialize_field<qint32>(jo, lineMarkerKeyEndLine), + deserialize_field<qint32>(jo, lineMarkerKeyEndColumn), + deserialize_field<QString>(jo, lineMarkerKeyDescription), + deserialize_field<std::optional<QString>>(jo, lineMarkerKeyIssueUrl), + deserialize_field<std::optional<bool>>(jo, lineMarkerKeyIsNew) + }; + } + + static QJsonValue serialize(const LineMarkerDto &value) { + QJsonObject jo; + serialize_field(jo, lineMarkerKeyKind, value.kind); + serialize_field(jo, lineMarkerKeyId, value.id); + serialize_field(jo, lineMarkerKeyStartLine, value.startLine); + serialize_field(jo, lineMarkerKeyStartColumn, value.startColumn); + serialize_field(jo, lineMarkerKeyEndLine, value.endLine); + serialize_field(jo, lineMarkerKeyEndColumn, value.endColumn); + serialize_field(jo, lineMarkerKeyDescription, value.description); + serialize_field(jo, lineMarkerKeyIssueUrl, value.issueUrl); + serialize_field(jo, lineMarkerKeyIsNew, value.isNew); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + LineMarkerDto LineMarkerDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<LineMarkerDto>(json); + } + + LineMarkerDto::LineMarkerDto( + QString kind, + std::optional<qint64> id, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn, + QString description, + std::optional<QString> issueUrl, + std::optional<bool> isNew + ) : + kind(std::move(kind)), + id(std::move(id)), + startLine(std::move(startLine)), + startColumn(std::move(startColumn)), + endLine(std::move(endLine)), + endColumn(std::move(endColumn)), + description(std::move(description)), + issueUrl(std::move(issueUrl)), + isNew(std::move(isNew)) + { } + + LineMarkerDto::LineMarkerDto( + IssueKind kind, + std::optional<qint64> id, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn, + QString description, + std::optional<QString> issueUrl, + std::optional<bool> isNew + ) : LineMarkerDto( + IssueKindMeta::enumToStr(kind), + std::move(id), + std::move(startLine), + std::move(startColumn), + std::move(endLine), + std::move(endColumn), + std::move(description), + std::move(issueUrl), + std::move(isNew)) + { } + + // throws std::range_error + IssueKind LineMarkerDto::getKindEnum() const + { + return IssueKindMeta::strToEnum(kind); + } + + void LineMarkerDto::setKindEnum(IssueKind newValue) + { + kind = IssueKindMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray LineMarkerDto::serialize() const + { + return serialize_bytes(*this); + } + + // RepositoryUpdateMessageDto + + static const QLatin1String repositoryUpdateMessageKeySeverity{"severity"}; + static const QLatin1String repositoryUpdateMessageKeyMessage{"message"}; + + template<> + class de_serializer<RepositoryUpdateMessageDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static RepositoryUpdateMessageDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<RepositoryUpdateMessageDto>(json); + return { + deserialize_field<std::optional<QString>>(jo, repositoryUpdateMessageKeySeverity), + deserialize_field<std::optional<QString>>(jo, repositoryUpdateMessageKeyMessage) + }; + } + + static QJsonValue serialize(const RepositoryUpdateMessageDto &value) { + QJsonObject jo; + serialize_field(jo, repositoryUpdateMessageKeySeverity, value.severity); + serialize_field(jo, repositoryUpdateMessageKeyMessage, value.message); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + RepositoryUpdateMessageDto RepositoryUpdateMessageDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<RepositoryUpdateMessageDto>(json); + } + + RepositoryUpdateMessageDto::RepositoryUpdateMessageDto( + std::optional<QString> severity, + std::optional<QString> message + ) : + severity(std::move(severity)), + message(std::move(message)) + { } + + RepositoryUpdateMessageDto::RepositoryUpdateMessageDto( + std::optional<MessageSeverity> severity, + std::optional<QString> message + ) : RepositoryUpdateMessageDto( + optionalTransform<QString, MessageSeverity>(severity, MessageSeverityMeta::enumToStr), + std::move(message)) + { } + + // throws std::range_error + std::optional<MessageSeverity> RepositoryUpdateMessageDto::getSeverityEnum() const + { + return optionalTransform<MessageSeverity, QString>(severity, MessageSeverityMeta::strToEnum); + } + + void RepositoryUpdateMessageDto::setSeverityEnum(std::optional<MessageSeverity> newValue) + { + severity = optionalTransform<QString, MessageSeverity>(newValue, MessageSeverityMeta::enumToStr); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray RepositoryUpdateMessageDto::serialize() const + { + return serialize_bytes(*this); + } + + // RuleListDto + + static const QLatin1String ruleListKeyRules{"rules"}; + + template<> + class de_serializer<RuleListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static RuleListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<RuleListDto>(json); + return { + deserialize_field<std::vector<RuleDto>>(jo, ruleListKeyRules) + }; + } + + static QJsonValue serialize(const RuleListDto &value) { + QJsonObject jo; + serialize_field(jo, ruleListKeyRules, value.rules); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + RuleListDto RuleListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<RuleListDto>(json); + } + + RuleListDto::RuleListDto( + std::vector<RuleDto> rules + ) : + rules(std::move(rules)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray RuleListDto::serialize() const + { + return serialize_bytes(*this); + } + + // SortInfoDto + + static const QLatin1String sortInfoKeyKey{"key"}; + static const QLatin1String sortInfoKeyDirection{"direction"}; + + template<> + class de_serializer<SortInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static SortInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<SortInfoDto>(json); + return { + deserialize_field<QString>(jo, sortInfoKeyKey), + deserialize_field<QString>(jo, sortInfoKeyDirection) + }; + } + + static QJsonValue serialize(const SortInfoDto &value) { + QJsonObject jo; + serialize_field(jo, sortInfoKeyKey, value.key); + serialize_field(jo, sortInfoKeyDirection, value.direction); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + SortInfoDto SortInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<SortInfoDto>(json); + } + + SortInfoDto::SortInfoDto( + QString key, + QString direction + ) : + key(std::move(key)), + direction(std::move(direction)) + { } + + SortInfoDto::SortInfoDto( + QString key, + SortDirection direction + ) : SortInfoDto( + std::move(key), + SortDirectionMeta::enumToStr(direction)) + { } + + // throws std::range_error + SortDirection SortInfoDto::getDirectionEnum() const + { + return SortDirectionMeta::strToEnum(direction); + } + + void SortInfoDto::setDirectionEnum(SortDirection newValue) + { + direction = SortDirectionMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray SortInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // UserRefDto + + static const QLatin1String userRefKeyName{"name"}; + static const QLatin1String userRefKeyDisplayName{"displayName"}; + static const QLatin1String userRefKeyType{"type"}; + static const QLatin1String userRefKeyIsPublic{"isPublic"}; + + template<> + class de_serializer<UserRefDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static UserRefDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<UserRefDto>(json); + return { + deserialize_field<QString>(jo, userRefKeyName), + deserialize_field<QString>(jo, userRefKeyDisplayName), + deserialize_field<std::optional<QString>>(jo, userRefKeyType), + deserialize_field<std::optional<bool>>(jo, userRefKeyIsPublic) + }; + } + + static QJsonValue serialize(const UserRefDto &value) { + QJsonObject jo; + serialize_field(jo, userRefKeyName, value.name); + serialize_field(jo, userRefKeyDisplayName, value.displayName); + serialize_field(jo, userRefKeyType, value.type); + serialize_field(jo, userRefKeyIsPublic, value.isPublic); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + UserRefDto UserRefDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<UserRefDto>(json); + } + + UserRefDto::UserRefDto( + QString name, + QString displayName, + std::optional<QString> type, + std::optional<bool> isPublic + ) : + name(std::move(name)), + displayName(std::move(displayName)), + type(std::move(type)), + isPublic(std::move(isPublic)) + { } + + UserRefDto::UserRefDto( + QString name, + QString displayName, + std::optional<UserRefType> type, + std::optional<bool> isPublic + ) : UserRefDto( + std::move(name), + std::move(displayName), + optionalTransform<QString, UserRefType>(type, UserRefTypeMeta::enumToStr), + std::move(isPublic)) + { } + + // throws std::range_error + std::optional<UserRefType> UserRefDto::getTypeEnum() const + { + return optionalTransform<UserRefType, QString>(type, UserRefTypeMeta::strToEnum); + } + + void UserRefDto::setTypeEnum(std::optional<UserRefType> newValue) + { + type = optionalTransform<QString, UserRefType>(newValue, UserRefTypeMeta::enumToStr); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray UserRefDto::serialize() const + { + return serialize_bytes(*this); + } + + // AnalyzedFileListDto + + static const QLatin1String analyzedFileListKeyVersion{"version"}; + static const QLatin1String analyzedFileListKeyRows{"rows"}; + + template<> + class de_serializer<AnalyzedFileListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static AnalyzedFileListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<AnalyzedFileListDto>(json); + return { + deserialize_field<AnalysisVersionDto>(jo, analyzedFileListKeyVersion), + deserialize_field<std::vector<AnalyzedFileDto>>(jo, analyzedFileListKeyRows) + }; + } + + static QJsonValue serialize(const AnalyzedFileListDto &value) { + QJsonObject jo; + serialize_field(jo, analyzedFileListKeyVersion, value.version); + serialize_field(jo, analyzedFileListKeyRows, value.rows); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + AnalyzedFileListDto AnalyzedFileListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<AnalyzedFileListDto>(json); + } + + AnalyzedFileListDto::AnalyzedFileListDto( + AnalysisVersionDto version, + std::vector<AnalyzedFileDto> rows + ) : + version(std::move(version)), + rows(std::move(rows)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray AnalyzedFileListDto::serialize() const + { + return serialize_bytes(*this); + } + + // EntityListDto + + static const QLatin1String entityListKeyVersion{"version"}; + static const QLatin1String entityListKeyEntities{"entities"}; + + template<> + class de_serializer<EntityListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static EntityListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<EntityListDto>(json); + return { + deserialize_field<std::optional<AnalysisVersionDto>>(jo, entityListKeyVersion), + deserialize_field<std::vector<EntityDto>>(jo, entityListKeyEntities) + }; + } + + static QJsonValue serialize(const EntityListDto &value) { + QJsonObject jo; + serialize_field(jo, entityListKeyVersion, value.version); + serialize_field(jo, entityListKeyEntities, value.entities); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + EntityListDto EntityListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<EntityListDto>(json); + } + + EntityListDto::EntityListDto( + std::optional<AnalysisVersionDto> version, + std::vector<EntityDto> entities + ) : + version(std::move(version)), + entities(std::move(entities)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray EntityListDto::serialize() const + { + return serialize_bytes(*this); + } + + // FileViewDto + + static const QLatin1String fileViewKeyFileName{"fileName"}; + static const QLatin1String fileViewKeyVersion{"version"}; + static const QLatin1String fileViewKeySourceCodeUrl{"sourceCodeUrl"}; + static const QLatin1String fileViewKeyLineMarkers{"lineMarkers"}; + + template<> + class de_serializer<FileViewDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static FileViewDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<FileViewDto>(json); + return { + deserialize_field<QString>(jo, fileViewKeyFileName), + deserialize_field<std::optional<QString>>(jo, fileViewKeyVersion), + deserialize_field<std::optional<QString>>(jo, fileViewKeySourceCodeUrl), + deserialize_field<std::vector<LineMarkerDto>>(jo, fileViewKeyLineMarkers) + }; + } + + static QJsonValue serialize(const FileViewDto &value) { + QJsonObject jo; + serialize_field(jo, fileViewKeyFileName, value.fileName); + serialize_field(jo, fileViewKeyVersion, value.version); + serialize_field(jo, fileViewKeySourceCodeUrl, value.sourceCodeUrl); + serialize_field(jo, fileViewKeyLineMarkers, value.lineMarkers); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + FileViewDto FileViewDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<FileViewDto>(json); + } + + FileViewDto::FileViewDto( + QString fileName, + std::optional<QString> version, + std::optional<QString> sourceCodeUrl, + std::vector<LineMarkerDto> lineMarkers + ) : + fileName(std::move(fileName)), + version(std::move(version)), + sourceCodeUrl(std::move(sourceCodeUrl)), + lineMarkers(std::move(lineMarkers)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray FileViewDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueDto + + static const QLatin1String issueKeyKind{"kind"}; + static const QLatin1String issueKeyId{"id"}; + static const QLatin1String issueKeyParentProject{"parentProject"}; + static const QLatin1String issueKeySourceLocations{"sourceLocations"}; + static const QLatin1String issueKeyIssueKind{"issueKind"}; + static const QLatin1String issueKeyIsHidden{"isHidden"}; + static const QLatin1String issueKeyIssueViewUrl{"issueViewUrl"}; + + template<> + class de_serializer<IssueDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueDto>(json); + return { + deserialize_field<QString>(jo, issueKeyKind), + deserialize_field<qint64>(jo, issueKeyId), + deserialize_field<ProjectReferenceDto>(jo, issueKeyParentProject), + deserialize_field<std::vector<IssueSourceLocationDto>>(jo, issueKeySourceLocations), + deserialize_field<IssueKindInfoDto>(jo, issueKeyIssueKind), + deserialize_field<bool>(jo, issueKeyIsHidden), + deserialize_field<std::optional<QString>>(jo, issueKeyIssueViewUrl) + }; + } + + static QJsonValue serialize(const IssueDto &value) { + QJsonObject jo; + serialize_field(jo, issueKeyKind, value.kind); + serialize_field(jo, issueKeyId, value.id); + serialize_field(jo, issueKeyParentProject, value.parentProject); + serialize_field(jo, issueKeySourceLocations, value.sourceLocations); + serialize_field(jo, issueKeyIssueKind, value.issueKind); + serialize_field(jo, issueKeyIsHidden, value.isHidden); + serialize_field(jo, issueKeyIssueViewUrl, value.issueViewUrl); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueDto IssueDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueDto>(json); + } + + IssueDto::IssueDto( + QString kind, + qint64 id, + ProjectReferenceDto parentProject, + std::vector<IssueSourceLocationDto> sourceLocations, + IssueKindInfoDto issueKind, + bool isHidden, + std::optional<QString> issueViewUrl + ) : + kind(std::move(kind)), + id(std::move(id)), + parentProject(std::move(parentProject)), + sourceLocations(std::move(sourceLocations)), + issueKind(std::move(issueKind)), + isHidden(std::move(isHidden)), + issueViewUrl(std::move(issueViewUrl)) + { } + + IssueDto::IssueDto( + IssueKind kind, + qint64 id, + ProjectReferenceDto parentProject, + std::vector<IssueSourceLocationDto> sourceLocations, + IssueKindInfoDto issueKind, + bool isHidden, + std::optional<QString> issueViewUrl + ) : IssueDto( + IssueKindMeta::enumToStr(kind), + std::move(id), + std::move(parentProject), + std::move(sourceLocations), + std::move(issueKind), + std::move(isHidden), + std::move(issueViewUrl)) + { } + + // throws std::range_error + IssueKind IssueDto::getKindEnum() const + { + return IssueKindMeta::strToEnum(kind); + } + + void IssueDto::setKindEnum(IssueKind newValue) + { + kind = IssueKindMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueDto::serialize() const + { + return serialize_bytes(*this); + } + + // IssueTableDto + + static const QLatin1String issueTableKeyStartVersion{"startVersion"}; + static const QLatin1String issueTableKeyEndVersion{"endVersion"}; + static const QLatin1String issueTableKeyTableViewUrl{"tableViewUrl"}; + static const QLatin1String issueTableKeyColumns{"columns"}; + static const QLatin1String issueTableKeyRows{"rows"}; + static const QLatin1String issueTableKeyTotalRowCount{"totalRowCount"}; + static const QLatin1String issueTableKeyTotalAddedCount{"totalAddedCount"}; + static const QLatin1String issueTableKeyTotalRemovedCount{"totalRemovedCount"}; + + template<> + class de_serializer<IssueTableDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTableDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<IssueTableDto>(json); + return { + deserialize_field<std::optional<AnalysisVersionDto>>(jo, issueTableKeyStartVersion), + deserialize_field<AnalysisVersionDto>(jo, issueTableKeyEndVersion), + deserialize_field<std::optional<QString>>(jo, issueTableKeyTableViewUrl), + deserialize_field<std::optional<std::vector<ColumnInfoDto>>>(jo, issueTableKeyColumns), + deserialize_field<std::vector<std::map<QString, Any>>>(jo, issueTableKeyRows), + deserialize_field<std::optional<qint32>>(jo, issueTableKeyTotalRowCount), + deserialize_field<std::optional<qint32>>(jo, issueTableKeyTotalAddedCount), + deserialize_field<std::optional<qint32>>(jo, issueTableKeyTotalRemovedCount) + }; + } + + static QJsonValue serialize(const IssueTableDto &value) { + QJsonObject jo; + serialize_field(jo, issueTableKeyStartVersion, value.startVersion); + serialize_field(jo, issueTableKeyEndVersion, value.endVersion); + serialize_field(jo, issueTableKeyTableViewUrl, value.tableViewUrl); + serialize_field(jo, issueTableKeyColumns, value.columns); + serialize_field(jo, issueTableKeyRows, value.rows); + serialize_field(jo, issueTableKeyTotalRowCount, value.totalRowCount); + serialize_field(jo, issueTableKeyTotalAddedCount, value.totalAddedCount); + serialize_field(jo, issueTableKeyTotalRemovedCount, value.totalRemovedCount); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + IssueTableDto IssueTableDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<IssueTableDto>(json); + } + + IssueTableDto::IssueTableDto( + std::optional<AnalysisVersionDto> startVersion, + AnalysisVersionDto endVersion, + std::optional<QString> tableViewUrl, + std::optional<std::vector<ColumnInfoDto>> columns, + std::vector<std::map<QString, Any>> rows, + std::optional<qint32> totalRowCount, + std::optional<qint32> totalAddedCount, + std::optional<qint32> totalRemovedCount + ) : + startVersion(std::move(startVersion)), + endVersion(std::move(endVersion)), + tableViewUrl(std::move(tableViewUrl)), + columns(std::move(columns)), + rows(std::move(rows)), + totalRowCount(std::move(totalRowCount)), + totalAddedCount(std::move(totalAddedCount)), + totalRemovedCount(std::move(totalRemovedCount)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray IssueTableDto::serialize() const + { + return serialize_bytes(*this); + } + + // MetricListDto + + static const QLatin1String metricListKeyVersion{"version"}; + static const QLatin1String metricListKeyMetrics{"metrics"}; + + template<> + class de_serializer<MetricListDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static MetricListDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<MetricListDto>(json); + return { + deserialize_field<std::optional<AnalysisVersionDto>>(jo, metricListKeyVersion), + deserialize_field<std::vector<MetricDto>>(jo, metricListKeyMetrics) + }; + } + + static QJsonValue serialize(const MetricListDto &value) { + QJsonObject jo; + serialize_field(jo, metricListKeyVersion, value.version); + serialize_field(jo, metricListKeyMetrics, value.metrics); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + MetricListDto MetricListDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<MetricListDto>(json); + } + + MetricListDto::MetricListDto( + std::optional<AnalysisVersionDto> version, + std::vector<MetricDto> metrics + ) : + version(std::move(version)), + metrics(std::move(metrics)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray MetricListDto::serialize() const + { + return serialize_bytes(*this); + } + + // MetricValueRangeDto + + static const QLatin1String metricValueRangeKeyStartVersion{"startVersion"}; + static const QLatin1String metricValueRangeKeyEndVersion{"endVersion"}; + static const QLatin1String metricValueRangeKeyEntity{"entity"}; + static const QLatin1String metricValueRangeKeyMetric{"metric"}; + static const QLatin1String metricValueRangeKeyValues{"values"}; + + template<> + class de_serializer<MetricValueRangeDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueRangeDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<MetricValueRangeDto>(json); + return { + deserialize_field<AnalysisVersionDto>(jo, metricValueRangeKeyStartVersion), + deserialize_field<AnalysisVersionDto>(jo, metricValueRangeKeyEndVersion), + deserialize_field<QString>(jo, metricValueRangeKeyEntity), + deserialize_field<QString>(jo, metricValueRangeKeyMetric), + deserialize_field<std::vector<std::optional<double>>>(jo, metricValueRangeKeyValues) + }; + } + + static QJsonValue serialize(const MetricValueRangeDto &value) { + QJsonObject jo; + serialize_field(jo, metricValueRangeKeyStartVersion, value.startVersion); + serialize_field(jo, metricValueRangeKeyEndVersion, value.endVersion); + serialize_field(jo, metricValueRangeKeyEntity, value.entity); + serialize_field(jo, metricValueRangeKeyMetric, value.metric); + serialize_field(jo, metricValueRangeKeyValues, value.values); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + MetricValueRangeDto MetricValueRangeDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<MetricValueRangeDto>(json); + } + + MetricValueRangeDto::MetricValueRangeDto( + AnalysisVersionDto startVersion, + AnalysisVersionDto endVersion, + QString entity, + QString metric, + std::vector<std::optional<double>> values + ) : + startVersion(std::move(startVersion)), + endVersion(std::move(endVersion)), + entity(std::move(entity)), + metric(std::move(metric)), + values(std::move(values)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray MetricValueRangeDto::serialize() const + { + return serialize_bytes(*this); + } + + // MetricValueTableDto + + static const QLatin1String metricValueTableKeyColumns{"columns"}; + static const QLatin1String metricValueTableKeyRows{"rows"}; + + template<> + class de_serializer<MetricValueTableDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueTableDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<MetricValueTableDto>(json); + return { + deserialize_field<std::vector<ColumnInfoDto>>(jo, metricValueTableKeyColumns), + deserialize_field<std::vector<MetricValueTableRowDto>>(jo, metricValueTableKeyRows) + }; + } + + static QJsonValue serialize(const MetricValueTableDto &value) { + QJsonObject jo; + serialize_field(jo, metricValueTableKeyColumns, value.columns); + serialize_field(jo, metricValueTableKeyRows, value.rows); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + MetricValueTableDto MetricValueTableDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<MetricValueTableDto>(json); + } + + MetricValueTableDto::MetricValueTableDto( + std::vector<ColumnInfoDto> columns, + std::vector<MetricValueTableRowDto> rows + ) : + columns(std::move(columns)), + rows(std::move(rows)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray MetricValueTableDto::serialize() const + { + return serialize_bytes(*this); + } + + // NamedFilterCreateDto + + static const QLatin1String namedFilterCreateKeyDisplayName{"displayName"}; + static const QLatin1String namedFilterCreateKeyKind{"kind"}; + static const QLatin1String namedFilterCreateKeyFilters{"filters"}; + static const QLatin1String namedFilterCreateKeySorters{"sorters"}; + static const QLatin1String namedFilterCreateKeyVisibility{"visibility"}; + + template<> + class de_serializer<NamedFilterCreateDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterCreateDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<NamedFilterCreateDto>(json); + return { + deserialize_field<QString>(jo, namedFilterCreateKeyDisplayName), + deserialize_field<QString>(jo, namedFilterCreateKeyKind), + deserialize_field<std::map<QString, QString>>(jo, namedFilterCreateKeyFilters), + deserialize_field<std::vector<SortInfoDto>>(jo, namedFilterCreateKeySorters), + deserialize_field<std::optional<NamedFilterVisibilityDto>>(jo, namedFilterCreateKeyVisibility) + }; + } + + static QJsonValue serialize(const NamedFilterCreateDto &value) { + QJsonObject jo; + serialize_field(jo, namedFilterCreateKeyDisplayName, value.displayName); + serialize_field(jo, namedFilterCreateKeyKind, value.kind); + serialize_field(jo, namedFilterCreateKeyFilters, value.filters); + serialize_field(jo, namedFilterCreateKeySorters, value.sorters); + serialize_field(jo, namedFilterCreateKeyVisibility, value.visibility); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + NamedFilterCreateDto NamedFilterCreateDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<NamedFilterCreateDto>(json); + } + + NamedFilterCreateDto::NamedFilterCreateDto( + QString displayName, + QString kind, + std::map<QString, QString> filters, + std::vector<SortInfoDto> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ) : + displayName(std::move(displayName)), + kind(std::move(kind)), + filters(std::move(filters)), + sorters(std::move(sorters)), + visibility(std::move(visibility)) + { } + + NamedFilterCreateDto::NamedFilterCreateDto( + QString displayName, + IssueKindForNamedFilterCreation kind, + std::map<QString, QString> filters, + std::vector<SortInfoDto> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ) : NamedFilterCreateDto( + std::move(displayName), + IssueKindForNamedFilterCreationMeta::enumToStr(kind), + std::move(filters), + std::move(sorters), + std::move(visibility)) + { } + + // throws std::range_error + IssueKindForNamedFilterCreation NamedFilterCreateDto::getKindEnum() const + { + return IssueKindForNamedFilterCreationMeta::strToEnum(kind); + } + + void NamedFilterCreateDto::setKindEnum(IssueKindForNamedFilterCreation newValue) + { + kind = IssueKindForNamedFilterCreationMeta::enumToStr(newValue); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray NamedFilterCreateDto::serialize() const + { + return serialize_bytes(*this); + } + + // NamedFilterInfoDto + + static const QLatin1String namedFilterInfoKeyKey{"key"}; + static const QLatin1String namedFilterInfoKeyDisplayName{"displayName"}; + static const QLatin1String namedFilterInfoKeyUrl{"url"}; + static const QLatin1String namedFilterInfoKeyIsPredefined{"isPredefined"}; + static const QLatin1String namedFilterInfoKeyType{"type"}; + static const QLatin1String namedFilterInfoKeyCanWrite{"canWrite"}; + static const QLatin1String namedFilterInfoKeyFilters{"filters"}; + static const QLatin1String namedFilterInfoKeySorters{"sorters"}; + static const QLatin1String namedFilterInfoKeySupportsAllIssueKinds{"supportsAllIssueKinds"}; + static const QLatin1String namedFilterInfoKeyIssueKindRestrictions{"issueKindRestrictions"}; + static const QLatin1String namedFilterInfoKeyVisibility{"visibility"}; + + template<> + class de_serializer<NamedFilterInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<NamedFilterInfoDto>(json); + return { + deserialize_field<QString>(jo, namedFilterInfoKeyKey), + deserialize_field<QString>(jo, namedFilterInfoKeyDisplayName), + deserialize_field<std::optional<QString>>(jo, namedFilterInfoKeyUrl), + deserialize_field<bool>(jo, namedFilterInfoKeyIsPredefined), + deserialize_field<std::optional<QString>>(jo, namedFilterInfoKeyType), + deserialize_field<bool>(jo, namedFilterInfoKeyCanWrite), + deserialize_field<std::map<QString, QString>>(jo, namedFilterInfoKeyFilters), + deserialize_field<std::optional<std::vector<SortInfoDto>>>(jo, namedFilterInfoKeySorters), + deserialize_field<bool>(jo, namedFilterInfoKeySupportsAllIssueKinds), + deserialize_field<std::optional<std::unordered_set<QString>>>(jo, namedFilterInfoKeyIssueKindRestrictions), + deserialize_field<std::optional<NamedFilterVisibilityDto>>(jo, namedFilterInfoKeyVisibility) + }; + } + + static QJsonValue serialize(const NamedFilterInfoDto &value) { + QJsonObject jo; + serialize_field(jo, namedFilterInfoKeyKey, value.key); + serialize_field(jo, namedFilterInfoKeyDisplayName, value.displayName); + serialize_field(jo, namedFilterInfoKeyUrl, value.url); + serialize_field(jo, namedFilterInfoKeyIsPredefined, value.isPredefined); + serialize_field(jo, namedFilterInfoKeyType, value.type); + serialize_field(jo, namedFilterInfoKeyCanWrite, value.canWrite); + serialize_field(jo, namedFilterInfoKeyFilters, value.filters); + serialize_field(jo, namedFilterInfoKeySorters, value.sorters); + serialize_field(jo, namedFilterInfoKeySupportsAllIssueKinds, value.supportsAllIssueKinds); + serialize_field(jo, namedFilterInfoKeyIssueKindRestrictions, value.issueKindRestrictions); + serialize_field(jo, namedFilterInfoKeyVisibility, value.visibility); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + NamedFilterInfoDto NamedFilterInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<NamedFilterInfoDto>(json); + } + + NamedFilterInfoDto::NamedFilterInfoDto( + QString key, + QString displayName, + std::optional<QString> url, + bool isPredefined, + std::optional<QString> type, + bool canWrite, + std::map<QString, QString> filters, + std::optional<std::vector<SortInfoDto>> sorters, + bool supportsAllIssueKinds, + std::optional<std::unordered_set<QString>> issueKindRestrictions, + std::optional<NamedFilterVisibilityDto> visibility + ) : + key(std::move(key)), + displayName(std::move(displayName)), + url(std::move(url)), + isPredefined(std::move(isPredefined)), + type(std::move(type)), + canWrite(std::move(canWrite)), + filters(std::move(filters)), + sorters(std::move(sorters)), + supportsAllIssueKinds(std::move(supportsAllIssueKinds)), + issueKindRestrictions(std::move(issueKindRestrictions)), + visibility(std::move(visibility)) + { } + + NamedFilterInfoDto::NamedFilterInfoDto( + QString key, + QString displayName, + std::optional<QString> url, + bool isPredefined, + std::optional<NamedFilterType> type, + bool canWrite, + std::map<QString, QString> filters, + std::optional<std::vector<SortInfoDto>> sorters, + bool supportsAllIssueKinds, + std::optional<std::unordered_set<QString>> issueKindRestrictions, + std::optional<NamedFilterVisibilityDto> visibility + ) : NamedFilterInfoDto( + std::move(key), + std::move(displayName), + std::move(url), + std::move(isPredefined), + optionalTransform<QString, NamedFilterType>(type, NamedFilterTypeMeta::enumToStr), + std::move(canWrite), + std::move(filters), + std::move(sorters), + std::move(supportsAllIssueKinds), + std::move(issueKindRestrictions), + std::move(visibility)) + { } + + // throws std::range_error + std::optional<NamedFilterType> NamedFilterInfoDto::getTypeEnum() const + { + return optionalTransform<NamedFilterType, QString>(type, NamedFilterTypeMeta::strToEnum); + } + + void NamedFilterInfoDto::setTypeEnum(std::optional<NamedFilterType> newValue) + { + type = optionalTransform<QString, NamedFilterType>(newValue, NamedFilterTypeMeta::enumToStr); + } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray NamedFilterInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // NamedFilterUpdateDto + + static const QLatin1String namedFilterUpdateKeyName{"name"}; + static const QLatin1String namedFilterUpdateKeyFilters{"filters"}; + static const QLatin1String namedFilterUpdateKeySorters{"sorters"}; + static const QLatin1String namedFilterUpdateKeyVisibility{"visibility"}; + + template<> + class de_serializer<NamedFilterUpdateDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterUpdateDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<NamedFilterUpdateDto>(json); + return { + deserialize_field<std::optional<QString>>(jo, namedFilterUpdateKeyName), + deserialize_field<std::optional<std::map<QString, QString>>>(jo, namedFilterUpdateKeyFilters), + deserialize_field<std::optional<std::vector<SortInfoDto>>>(jo, namedFilterUpdateKeySorters), + deserialize_field<std::optional<NamedFilterVisibilityDto>>(jo, namedFilterUpdateKeyVisibility) + }; + } + + static QJsonValue serialize(const NamedFilterUpdateDto &value) { + QJsonObject jo; + serialize_field(jo, namedFilterUpdateKeyName, value.name); + serialize_field(jo, namedFilterUpdateKeyFilters, value.filters); + serialize_field(jo, namedFilterUpdateKeySorters, value.sorters); + serialize_field(jo, namedFilterUpdateKeyVisibility, value.visibility); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + NamedFilterUpdateDto NamedFilterUpdateDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<NamedFilterUpdateDto>(json); + } + + NamedFilterUpdateDto::NamedFilterUpdateDto( + std::optional<QString> name, + std::optional<std::map<QString, QString>> filters, + std::optional<std::vector<SortInfoDto>> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ) : + name(std::move(name)), + filters(std::move(filters)), + sorters(std::move(sorters)), + visibility(std::move(visibility)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray NamedFilterUpdateDto::serialize() const + { + return serialize_bytes(*this); + } + + // ProjectInfoDto + + static const QLatin1String projectInfoKeyName{"name"}; + static const QLatin1String projectInfoKeyIssueFilterHelp{"issueFilterHelp"}; + static const QLatin1String projectInfoKeyTableMetaUri{"tableMetaUri"}; + static const QLatin1String projectInfoKeyUsers{"users"}; + static const QLatin1String projectInfoKeyVersions{"versions"}; + static const QLatin1String projectInfoKeyIssueKinds{"issueKinds"}; + static const QLatin1String projectInfoKeyHasHiddenIssues{"hasHiddenIssues"}; + + template<> + class de_serializer<ProjectInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static ProjectInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<ProjectInfoDto>(json); + return { + deserialize_field<QString>(jo, projectInfoKeyName), + deserialize_field<std::optional<QString>>(jo, projectInfoKeyIssueFilterHelp), + deserialize_field<std::optional<QString>>(jo, projectInfoKeyTableMetaUri), + deserialize_field<std::vector<UserRefDto>>(jo, projectInfoKeyUsers), + deserialize_field<std::vector<AnalysisVersionDto>>(jo, projectInfoKeyVersions), + deserialize_field<std::vector<IssueKindInfoDto>>(jo, projectInfoKeyIssueKinds), + deserialize_field<bool>(jo, projectInfoKeyHasHiddenIssues) + }; + } + + static QJsonValue serialize(const ProjectInfoDto &value) { + QJsonObject jo; + serialize_field(jo, projectInfoKeyName, value.name); + serialize_field(jo, projectInfoKeyIssueFilterHelp, value.issueFilterHelp); + serialize_field(jo, projectInfoKeyTableMetaUri, value.tableMetaUri); + serialize_field(jo, projectInfoKeyUsers, value.users); + serialize_field(jo, projectInfoKeyVersions, value.versions); + serialize_field(jo, projectInfoKeyIssueKinds, value.issueKinds); + serialize_field(jo, projectInfoKeyHasHiddenIssues, value.hasHiddenIssues); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + ProjectInfoDto ProjectInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<ProjectInfoDto>(json); + } + + ProjectInfoDto::ProjectInfoDto( + QString name, + std::optional<QString> issueFilterHelp, + std::optional<QString> tableMetaUri, + std::vector<UserRefDto> users, + std::vector<AnalysisVersionDto> versions, + std::vector<IssueKindInfoDto> issueKinds, + bool hasHiddenIssues + ) : + name(std::move(name)), + issueFilterHelp(std::move(issueFilterHelp)), + tableMetaUri(std::move(tableMetaUri)), + users(std::move(users)), + versions(std::move(versions)), + issueKinds(std::move(issueKinds)), + hasHiddenIssues(std::move(hasHiddenIssues)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray ProjectInfoDto::serialize() const + { + return serialize_bytes(*this); + } + + // RepositoryUpdateResponseDto + + static const QLatin1String repositoryUpdateResponseKeyMessages{"messages"}; + static const QLatin1String repositoryUpdateResponseKeyHasErrors{"hasErrors"}; + static const QLatin1String repositoryUpdateResponseKeyHasWarnings{"hasWarnings"}; + + template<> + class de_serializer<RepositoryUpdateResponseDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static RepositoryUpdateResponseDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<RepositoryUpdateResponseDto>(json); + return { + deserialize_field<std::vector<RepositoryUpdateMessageDto>>(jo, repositoryUpdateResponseKeyMessages), + deserialize_field<bool>(jo, repositoryUpdateResponseKeyHasErrors), + deserialize_field<bool>(jo, repositoryUpdateResponseKeyHasWarnings) + }; + } + + static QJsonValue serialize(const RepositoryUpdateResponseDto &value) { + QJsonObject jo; + serialize_field(jo, repositoryUpdateResponseKeyMessages, value.messages); + serialize_field(jo, repositoryUpdateResponseKeyHasErrors, value.hasErrors); + serialize_field(jo, repositoryUpdateResponseKeyHasWarnings, value.hasWarnings); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + RepositoryUpdateResponseDto RepositoryUpdateResponseDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<RepositoryUpdateResponseDto>(json); + } + + RepositoryUpdateResponseDto::RepositoryUpdateResponseDto( + std::vector<RepositoryUpdateMessageDto> messages, + bool hasErrors, + bool hasWarnings + ) : + messages(std::move(messages)), + hasErrors(std::move(hasErrors)), + hasWarnings(std::move(hasWarnings)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray RepositoryUpdateResponseDto::serialize() const + { + return serialize_bytes(*this); + } + + // TableInfoDto + + static const QLatin1String tableInfoKeyTableDataUri{"tableDataUri"}; + static const QLatin1String tableInfoKeyIssueBaseViewUri{"issueBaseViewUri"}; + static const QLatin1String tableInfoKeyColumns{"columns"}; + static const QLatin1String tableInfoKeyFilters{"filters"}; + static const QLatin1String tableInfoKeyUserDefaultFilter{"userDefaultFilter"}; + static const QLatin1String tableInfoKeyAxivionDefaultFilter{"axivionDefaultFilter"}; + + template<> + class de_serializer<TableInfoDto> final + { + public: + // throws Axivion::Internal::Dto::invalid_dto_exception + static TableInfoDto deserialize(const QJsonValue &json) { + const QJsonObject jo = toJsonObject<TableInfoDto>(json); + return { + deserialize_field<QString>(jo, tableInfoKeyTableDataUri), + deserialize_field<std::optional<QString>>(jo, tableInfoKeyIssueBaseViewUri), + deserialize_field<std::vector<ColumnInfoDto>>(jo, tableInfoKeyColumns), + deserialize_field<std::vector<NamedFilterInfoDto>>(jo, tableInfoKeyFilters), + deserialize_field<std::optional<QString>>(jo, tableInfoKeyUserDefaultFilter), + deserialize_field<QString>(jo, tableInfoKeyAxivionDefaultFilter) + }; + } + + static QJsonValue serialize(const TableInfoDto &value) { + QJsonObject jo; + serialize_field(jo, tableInfoKeyTableDataUri, value.tableDataUri); + serialize_field(jo, tableInfoKeyIssueBaseViewUri, value.issueBaseViewUri); + serialize_field(jo, tableInfoKeyColumns, value.columns); + serialize_field(jo, tableInfoKeyFilters, value.filters); + serialize_field(jo, tableInfoKeyUserDefaultFilter, value.userDefaultFilter); + serialize_field(jo, tableInfoKeyAxivionDefaultFilter, value.axivionDefaultFilter); + return { jo }; + } + + de_serializer() = delete; + ~de_serializer() = delete; + }; + + // throws Axivion::Internal::Dto::invalid_dto_exception + TableInfoDto TableInfoDto::deserialize(const QByteArray &json) + { + return deserialize_bytes<TableInfoDto>(json); + } + + TableInfoDto::TableInfoDto( + QString tableDataUri, + std::optional<QString> issueBaseViewUri, + std::vector<ColumnInfoDto> columns, + std::vector<NamedFilterInfoDto> filters, + std::optional<QString> userDefaultFilter, + QString axivionDefaultFilter + ) : + tableDataUri(std::move(tableDataUri)), + issueBaseViewUri(std::move(issueBaseViewUri)), + columns(std::move(columns)), + filters(std::move(filters)), + userDefaultFilter(std::move(userDefaultFilter)), + axivionDefaultFilter(std::move(axivionDefaultFilter)) + { } + + // throws Axivion::Internal::Dto::invalid_dto_exception + QByteArray TableInfoDto::serialize() const + { + return serialize_bytes(*this); + } +} diff --git a/src/plugins/axivion/dashboard/dto.h b/src/plugins/axivion/dashboard/dto.h new file mode 100644 index 00000000000..97ca5d602dc --- /dev/null +++ b/src/plugins/axivion/dashboard/dto.h @@ -0,0 +1,3029 @@ +#pragma once + +/* + * Copyright (C) 2022-current by Axivion GmbH + * https://www.axivion.com/ + * + * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + * + * Purpose: Dashboard C++ API Header + * + * !!!!!! GENERATED, DO NOT EDIT !!!!!! + * + * This file was generated with the script at + * <AxivionSuiteRepo>/projects/libs/dashboard_cpp_api/generator/generate_dashboard_cpp_api.py + */ + +#include <QAnyStringView> +#include <QByteArray> +#include <QHashFunctions> +#include <QLatin1String> +#include <QString> +#include <QStringView> +#include <QtGlobal> + +#include <array> +#include <cstddef> +#include <exception> +#include <functional> +#include <optional> +#include <stdexcept> +#include <string_view> +#include <map> +#include <unordered_set> +#include <variant> +#include <vector> + +namespace Axivion::Internal::Dto +{ + class invalid_dto_exception : public std::runtime_error + { + public: + invalid_dto_exception(const std::string_view type_name, const std::exception &ex); + + invalid_dto_exception(const std::string_view type_name, const std::string_view message); + }; + + template<typename O, typename I> + std::optional<O> optionalTransform(const std::optional<I> &input, const std::function<O(const I&)> &transformer) + { + if (input.has_value()) + { + return transformer(*input); + } + return std::nullopt; + } + + class Serializable + { + public: + virtual QByteArray serialize() const = 0; + + virtual ~Serializable() = default; + }; + + class Any : public Serializable + { + public: + using Map = std::map<QString, Any>; + using MapEntry = std::pair<const QString, Any>; + using Vector = std::vector<Any>; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static Any deserialize(const QByteArray &json); + + Any(); + + Any(QString value); + + Any(double value); + + Any(Map value); + + Any(Vector value); + + Any(bool value); + + bool isNull() const; + + bool isString() const; + + // Throws std::bad_variant_access + QString &getString(); + + // Throws std::bad_variant_access + const QString &getString() const; + + bool isDouble() const; + + // Throws std::bad_variant_access + double &getDouble(); + + // Throws std::bad_variant_access + const double &getDouble() const; + + bool isMap() const; + + // Throws std::bad_variant_access + Map &getMap(); + + // Throws std::bad_variant_access + const Map &getMap() const; + + bool isList() const; + + // Throws std::bad_variant_access + Vector &getList(); + + // Throws std::bad_variant_access + const Vector &getList() const; + + bool isBool() const; + + // Throws std::bad_variant_access + bool &getBool(); + + // Throws std::bad_variant_access + const bool &getBool() const; + + virtual QByteArray serialize() const override; + + private: + std::variant< + std::nullptr_t, // .index() == 0 + QString, // .index() == 1 + double, // .index() == 2 + Map, // .index() == 3 + Vector, // .index() == 4 + bool // .index() == 5 + > data; + }; + + class ApiVersion + { + public: + static const std::array<qint32, 4> number; + static const QLatin1String string; + static const QLatin1String name; + static const QLatin1String timestamp; + }; + + /** + * Describes an analyzed file in a version. + */ + class AnalyzedFileDto : public Serializable + { + public: + + /** + * <p>The absolute path of the file. + */ + QString path; + + /** + * <p>Indicates whether this file is a system header file. + */ + std::optional<bool> isSystemHeader; + + /** + * <p>The name of the language used to analyze this file. + */ + std::optional<QString> languageName; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static AnalyzedFileDto deserialize(const QByteArray &json); + + AnalyzedFileDto( + QString path, + std::optional<bool> isSystemHeader, + std::optional<QString> languageName + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Available types of ApiToken + * + * <p>* `General` - Powerful token kind granting the same permissions as + * a regular password based login. + * * `IdePlugin` - Limits user permissions to those typically needed by IDE + * plugins. + * * `SourceFetch` - Used internally for local build. Cannot be created via this API. + * * `LogIn` - Used internally by browsers for the "Keep me logged in" functionality. Cannot be created via this API. + * * `ContinuousIntegration` - Limits user permissions to those typically needed for CI purposes. + * + * <p>For the types `IdePlugin` and `LogIn` the Dashboard will automatically + * delete the tokens when the owner changes his password. The Dashboard + * will try to detect this for external password changes as well + * but cannot guarantee this will always work. + * + * @since 7.1.0 + */ + enum class ApiTokenType + { + sourcefetch, + general, + ideplugin, + login, + continuousintegration + }; + + class ApiTokenTypeMeta final + { + public: + static const QLatin1String sourcefetch; + static const QLatin1String general; + static const QLatin1String ideplugin; + static const QLatin1String login; + static const QLatin1String continuousintegration; + + // Throws std::range_error + static ApiTokenType strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(ApiTokenType e); + + ApiTokenTypeMeta() = delete; + ~ApiTokenTypeMeta() = delete; + }; + + /** + * Request data for changing a user password + */ + class ChangePasswordFormDto : public Serializable + { + public: + + /** + * <p>The current password + */ + QString currentPassword; + + /** + * <p>The new password + */ + QString newPassword; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ChangePasswordFormDto deserialize(const QByteArray &json); + + ChangePasswordFormDto( + QString currentPassword, + QString newPassword + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Additional TypeInfo of a ColumnType + */ + class ColumnTypeOptionDto : public Serializable + { + public: + + /** + * <p>The name of the option that shall be used for displaying and filtering as well. + */ + QString key; + + /** + * <p>Name for displaying the option in UIs. + * + * <p>Deprecated since 6.9.0. Use `key` instead. # Older aclipse versions rely on the field being non-null + * + * @deprecated + */ + std::optional<QString> displayName; + + /** + * <p>A color hex code recommended for displaying the value in GUIs. + * + * <p>Example colors are: "#FF0000" - red "#008000" - green + */ + QString displayColor; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ColumnTypeOptionDto deserialize(const QByteArray &json); + + ColumnTypeOptionDto( + QString key, + std::optional<QString> displayName, + QString displayColor + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes a Comment Request + */ + class CommentRequestDto : public Serializable + { + public: + + /** + * <p>The comment text + */ + QString text; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static CommentRequestDto deserialize(const QByteArray &json); + + CommentRequestDto( + QString text + ); + + virtual QByteArray serialize() const override; + }; + + /** + * CSRF token + * + * @since 7.7.0 + */ + class CsrfTokenDto : public Serializable + { + public: + + /** + * <p>The value expected to be sent along the ``csrfTokenHeader`` for all HTTP requests that are not GET, HEAD, OPTIONS or TRACE. + * + * <p>Note, that this does not replace authentication of subsequent requests. Also the token is combined with the authentication + * data, so will not work when authenticating as another user. Its lifetime is limited, so when creating a very long-running + * application you should consider refreshing this token from time to time. + */ + QString csrfToken; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static CsrfTokenDto deserialize(const QByteArray &json); + + CsrfTokenDto( + QString csrfToken + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A Project Entity such as a Class, a Method, a File or a Module + * or the System Entity. + */ + class EntityDto : public Serializable + { + public: + + /** + * <p>The project-wide ID used to refer to this entity. + */ + QString id; + + /** + * <p>a non-unique name of the entity + */ + QString name; + + /** + * <p>The type of the entity + */ + QString type; + + /** + * <p>The file path of an entity if it can be associated with a file + */ + std::optional<QString> path; + + /** + * <p>The line number of an entity if it can be associated with a file location + */ + std::optional<qint32> line; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static EntityDto deserialize(const QByteArray &json); + + EntityDto( + QString id, + QString name, + QString type, + std::optional<QString> path, + std::optional<qint32> line + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes an error returned from the server + * + * <p>Usually they are caused by wrong API usage but they may also happen + * in case of bugs in the server + */ + class ErrorDto : public Serializable + { + public: + + /** + * <p>a parseable version number indicating the server version + * + * @since 6.6.0 + */ + std::optional<QString> dashboardVersionNumber; + + /** + * <p>the name of the error kind + */ + QString type; + + /** + * <p>A human readable short english message describing the error. + * Ideally it does not contain any linebreaks. + */ + QString message; + + /** + * <p>use this instead of message in order to display a message translated + * according to your language preferences. Will contain exactly the same + * contents as `message` in case no translation is available. + */ + QString localizedMessage; + + /** + * <p>Optional error details. + * Consumers should expect this to be a multiline string and display it + * in a mono-space font without adding additional linebreaks. + * + * @since 7.5.0 + */ + std::optional<QString> details; + + /** + * <p>Optional translation of `details` according to your language preferences. + * Will not be available in case no translation is available. + * + * @since 7.5.0 + */ + std::optional<QString> localizedDetails; + + /** + * <p>E-mail address for support requests + * + * @since 7.5.0 + */ + std::optional<QString> supportAddress; + + /** + * <p>If this is `true`, this is an indication by the server, that this error + * is probably a bug on server-side and clients are encouraged to encourage + * users to escalate this problem, e.g. using `supportAddress`. + * + * <p>Keep in mind that this boolean may be wrong in both directions, e.g.: + * * if a timeout is configured too low, then this can be a false positive + * * if the bug lies in the exception judgment than this is a false negative + * + * <p>So it should always be clear to users that they have the ultimate choice + * on what to do with an error popup. + * + * @since 7.5.0 + */ + std::optional<bool> displayServerBugHint; + + /** + * <p>Optional field containing additional error information meant for automatic processing. + * + * <p>* This data is meant for helping software that uses the API to better understand and communicate + * certain types of error to the user. + * * Always inspect the `type` so you know what keys you can expect. + * + * <p>Error types having additional information: + * + * <p> * type = InvalidFilterException (since 6.5.0): + * + * <p> * optionally has a string datum 'column' referencing the column that has the invalid + * filter value. The file filter is referred to by the string "any path". + * * optionally has a string datum 'help' providing an ascii-encoded URL + * pointing to human-readable help that might help a user understanding + * and resolving the error. If the URL is relative, then it is meant + * relative to the Dashboard the error originated from. + * + * <p> * type = PasswordVerificationException (since 7.1.0): + * + * <p> * optionally has a boolean flag 'passwordMayBeUsedAsApiToken' to indicate that the + * provided password may be used as API token with the respective API. E.g. use + * 'Authorization: AxToken ...' header instead of HTTP basic auth. + * + * @since 6.5.0 + */ + std::optional<std::map<QString, Any>> data; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ErrorDto deserialize(const QByteArray &json); + + ErrorDto( + std::optional<QString> dashboardVersionNumber, + QString type, + QString message, + QString localizedMessage, + std::optional<QString> details, + std::optional<QString> localizedDetails, + std::optional<QString> supportAddress, + std::optional<bool> displayServerBugHint, + std::optional<std::map<QString, Any>> data + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes an issue comment + */ + class IssueCommentDto : public Serializable + { + public: + + /** + * <p>The loginname of the user that created the comment. + */ + QString username; + + /** + * <p>The recommended display name of the user that wrote the comment. + */ + QString userDisplayName; + + /** + * <p>The Date when the comment was created as a ISO8601-parseable string. + */ + QString date; + + /** + * <p>The Date when the comment was created for UI-display. + * + * <p>It is formatted as a human-readable string relative to query time, e.g. + * ``2 minutes ago``. + */ + QString displayDate; + + /** + * <p>The comment text. + */ + QString text; + + /** + * <p>The linkified comment text. + * + * @since 7.6.0 + */ + std::optional<QString> html; + + /** + * <p>The id for comment deletion. + * + * <p>When the requesting user is allowed to delete the comment, contains an id + * that can be used to mark the comment as deleted using another API. This is + * never set when the Comment is returned as the result of an Issue-List query. + */ + std::optional<QString> commentDeletionId; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueCommentDto deserialize(const QByteArray &json); + + IssueCommentDto( + QString username, + QString userDisplayName, + QString date, + QString displayDate, + QString text, + std::optional<QString> html, + std::optional<QString> commentDeletionId + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Shortname for an erosion issue kind + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV`` + */ + enum class IssueKind + { + av, + cl, + cy, + de, + mv, + sv + }; + + class IssueKindMeta final + { + public: + static const QLatin1String av; + static const QLatin1String cl; + static const QLatin1String cy; + static const QLatin1String de; + static const QLatin1String mv; + static const QLatin1String sv; + + // Throws std::range_error + static IssueKind strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(IssueKind e); + + IssueKindMeta() = delete; + ~IssueKindMeta() = delete; + }; + + /** + * IssueKind for Named-Filter creation + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV``, ``UNIVERSAL`` + */ + enum class IssueKindForNamedFilterCreation + { + av, + cl, + cy, + de, + mv, + sv, + universal + }; + + class IssueKindForNamedFilterCreationMeta final + { + public: + static const QLatin1String av; + static const QLatin1String cl; + static const QLatin1String cy; + static const QLatin1String de; + static const QLatin1String mv; + static const QLatin1String sv; + static const QLatin1String universal; + + // Throws std::range_error + static IssueKindForNamedFilterCreation strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(IssueKindForNamedFilterCreation e); + + IssueKindForNamedFilterCreationMeta() = delete; + ~IssueKindForNamedFilterCreationMeta() = delete; + }; + + /** + * <p>Identifies a source code location which may be a single line of code or an + * couple of adjacent lines (fragment) inside the same source file. + */ + class IssueSourceLocationDto : public Serializable + { + public: + + /** + * <p>Refers to the file with a normalized path relative to the current project + * root. + */ + QString fileName; + + /** + * <p>A user-readable description of this source location's role in the + * issue (e.g. 'Source' or 'Target') + */ + std::optional<QString> role; + + /** + * <p>Host-relative URL of the source code version for which these + * startLine/endLine values are valid + */ + QString sourceCodeUrl; + + /** + * <p>The first line of the fragment + */ + qint32 startLine; + + /** + * The column of the start line in which the issue starts. + * + * <p>1-relative + * + * <p>0 iff unknown + * + * @since 7.2.0 + */ + qint32 startColumn; + + /** + * The last line of the fragment (inclusive) + * + * <p>This is the same as `startLine` if the location has only one line. + */ + qint32 endLine; + + /** + * The column of the end line in which the issue ends. + * + * <p>1-relative + * + * <p>0 iff unknown + * + * @since 7.2.0 + */ + qint32 endColumn; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueSourceLocationDto deserialize(const QByteArray &json); + + IssueSourceLocationDto( + QString fileName, + std::optional<QString> role, + QString sourceCodeUrl, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn + ); + + virtual QByteArray serialize() const override; + }; + + /** + * An issue tag as returned by the Issue-List API. + */ + class IssueTagDto : public Serializable + { + public: + + /** + * <p>Use this for displaying the tag + */ + QString tag; + + /** + * <p>An RGB hex color in the form #RRGGBB directly usable by css. + * + * <p>The colors are best suited to draw a label on bright background and to + * contain white letters for labeling. + */ + QString color; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagDto deserialize(const QByteArray &json); + + IssueTagDto( + QString tag, + QString color + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes an issue tag as returned by the Issue-Tags API. + */ + class IssueTagTypeDto : public Serializable + { + public: + + /** + * <p>A canonicalized variant of the tag name that can be used for client-side equality checks and sorting. + */ + QString id; + + /** + * <p>Deprecated since 6.9.0, use ``tag`` instead. + * + * @deprecated + */ + std::optional<QString> text; + + /** + * <p>Use this for displaying the tag + * + * @since 6.9.0 + */ + std::optional<QString> tag; + + /** + * <p>An RGB hex color in the form #RRGGBB directly usable by css. + * + * <p>The colors are best suited to draw a label on bright background and to + * contain white letters for labeling. + */ + QString color; + + /** + * <p>The description of the tag. It can be assumed to be plain text with no need for further + * syntactic interpretation. + */ + std::optional<QString> description; + + /** + * <p>Whether the tag is attached to the issue or only proposed. + */ + std::optional<bool> selected; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagTypeDto deserialize(const QByteArray &json); + + IssueTagTypeDto( + QString id, + std::optional<QString> text, + std::optional<QString> tag, + QString color, + std::optional<QString> description, + std::optional<bool> selected + ); + + virtual QByteArray serialize() const override; + }; + + /** + * The log-level of a message + * + * <p>* `DEBUG` + * * `INFO` + * * `WARNING` + * * `ERROR` + * * `FATAL` + */ + enum class MessageSeverity + { + debug, + info, + warning, + error, + fatal + }; + + class MessageSeverityMeta final + { + public: + static const QLatin1String debug; + static const QLatin1String info; + static const QLatin1String warning; + static const QLatin1String error; + static const QLatin1String fatal; + + // Throws std::range_error + static MessageSeverity strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(MessageSeverity e); + + MessageSeverityMeta() = delete; + ~MessageSeverityMeta() = delete; + }; + + /** + * Describes a Metric as configured for a project in a version + */ + class MetricDto : public Serializable + { + public: + + /** + * <p>The ID of the metric + */ + QString name; + + /** + * <p>a more descriptive name of the metric + */ + QString displayName; + + /** + * <p>The configured minimum threshold for the metric. + * + * <p>Can have two possible string values ``-Infinity`` and ``Infinity`` + * otherwise it is a number. If not configured, this field will not + * be available. + */ + Any minValue; + + /** + * <p>The configured maximum threshold for the metric. + * + * <p>Can have two possible string values ``-Infinity`` and ``Infinity`` + * otherwise it is a number. If not configured, this field will not + * be available. + */ + Any maxValue; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static MetricDto deserialize(const QByteArray &json); + + MetricDto( + QString name, + QString displayName, + Any minValue, + Any maxValue + ); + + virtual QByteArray serialize() const override; + }; + + /** + * An entity table row + * + * <p>Note, that if you specify ``forceStrings=true`` when querying the table, + * the field types will all be ``string`` and the empty string will indicate + * an absent value. + */ + class MetricValueTableRowDto : public Serializable + { + public: + + /** + * <p>The Metric Id + */ + QString metric; + + /** + * <p>The source file of the entity definition + */ + std::optional<QString> path; + + /** + * <p>The source file line number of the entity definition + */ + std::optional<qint32> line; + + /** + * <p>The measured or aggregated metric value + */ + std::optional<double> value; + + /** + * <p>The non-unique entity name + */ + QString entity; + + /** + * <p>The entity type + */ + QString entityType; + + /** + * <p>The project-wide entity ID + */ + QString entityId; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueTableRowDto deserialize(const QByteArray &json); + + MetricValueTableRowDto( + QString metric, + std::optional<QString> path, + std::optional<qint32> line, + std::optional<double> value, + QString entity, + QString entityType, + QString entityId + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A named filter type + * + * <p>* `PREDEFINED` - Named filters of this type are immutable and exist out of the box and can be used by everyone + * * `GLOBAL` - Named filters of this type are usable by everyone and managed by the so called filter managers + * * `CUSTOM` - Named filters of this type are creatable by everyone but only visible to their owner + */ + enum class NamedFilterType + { + predefined, + global, + custom + }; + + class NamedFilterTypeMeta final + { + public: + static const QLatin1String predefined; + static const QLatin1String global; + static const QLatin1String custom; + + // Throws std::range_error + static NamedFilterType strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(NamedFilterType e); + + NamedFilterTypeMeta() = delete; + ~NamedFilterTypeMeta() = delete; + }; + + /** + * NamedFilter visibility configuration + * + * <p>Only applicable for global named filters. + * + * <p>You may not have access to this information depending on your permissions. + * + * @since 7.3.0 + */ + class NamedFilterVisibilityDto : public Serializable + { + public: + + /** + * <p>IDs of user-groups, that are allowed to see the named filter. + * + * <p>The named filter is visible to all users, if this property is not given. + */ + std::optional<std::vector<QString>> groups; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterVisibilityDto deserialize(const QByteArray &json); + + NamedFilterVisibilityDto( + std::optional<std::vector<QString>> groups + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A reference to a project + */ + class ProjectReferenceDto : public Serializable + { + public: + + /** + * <p>The name of the project. Use this string to refer to the project. + */ + QString name; + + /** + * <p>URI to get further information about the project. + */ + QString url; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ProjectReferenceDto deserialize(const QByteArray &json); + + ProjectReferenceDto( + QString name, + QString url + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A rule configured on a project + * + * @since 6.5.0 + */ + class RuleDto : public Serializable + { + public: + + /** + * <p>the rule name (possibly renamed via configuration) + */ + QString name; + + /** + * <p>the original (unrenamed) rule name + */ + QString original_name; + + /** + * <p>Whether or not the rule was disabled. + * + * <p>Note, that this value is only available for analysis runs + * done with at least 6.5.0 + */ + std::optional<bool> disabled; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static RuleDto deserialize(const QByteArray &json); + + RuleDto( + QString name, + QString original_name, + std::optional<bool> disabled + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A sorting direction + * + * <p>* `ASC` - `smaller` values first + * * `DESC` - `greater` values first + */ + enum class SortDirection + { + asc, + desc + }; + + class SortDirectionMeta final + { + public: + static const QLatin1String asc; + static const QLatin1String desc; + + // Throws std::range_error + static SortDirection strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(SortDirection e); + + SortDirectionMeta() = delete; + ~SortDirectionMeta() = delete; + }; + + /** + * How the column values should be aligned. + * + * <p>* `left` - Align value to the left of its cell + * * `right` - Align value to the right of its cell + * * `center` - Center value in its cell + */ + enum class TableCellAlignment + { + left, + right, + center + }; + + class TableCellAlignmentMeta final + { + public: + static const QLatin1String left; + static const QLatin1String right; + static const QLatin1String center; + + // Throws std::range_error + static TableCellAlignment strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(TableCellAlignment e); + + TableCellAlignmentMeta() = delete; + ~TableCellAlignmentMeta() = delete; + }; + + /** + * Refers to a specific version of the Axivion Suite + */ + class ToolsVersionDto : public Serializable + { + public: + + /** + * <p>Version number for display purposes + */ + QString name; + + /** + * <p>Parseable, numeric version number suitable for version comparisons + */ + QString number; + + /** + * <p>Build date in an ISO8601-parseable string of the form YYYY-MM-DD + */ + QString buildDate; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ToolsVersionDto deserialize(const QByteArray &json); + + ToolsVersionDto( + QString name, + QString number, + QString buildDate + ); + + virtual QByteArray serialize() const override; + }; + + /** + * User Type + * + * <p>* `VIRTUAL_USER` - virtual user that does not represent a specific person + * * `DASHBOARD_USER` - a `regular`, explicitly managed user + * * `UNMAPPED_USER` - a user that is not explicitly managed and e.g. only known via an analysis result + * + * @since 7.1.0 + */ + enum class UserRefType + { + virtual_user, + dashboard_user, + unmapped_user + }; + + class UserRefTypeMeta final + { + public: + static const QLatin1String virtual_user; + static const QLatin1String dashboard_user; + static const QLatin1String unmapped_user; + + // Throws std::range_error + static UserRefType strToEnum(QAnyStringView str); + + static QLatin1String enumToStr(UserRefType e); + + UserRefTypeMeta() = delete; + ~UserRefTypeMeta() = delete; + }; + + /** + * Kind-specific issue count statistics that are cheaply available. + */ + class VersionKindCountDto : public Serializable + { + public: + + /** + * <p>The number of issues of a kind in a version. + */ + qint32 Total; + + /** + * <p>The number of issues of a kind present in a version that were not present in the previous version. + */ + qint32 Added; + + /** + * <p>The number of issues of a kind that were present in the previous version and are not present in the current version any more. + */ + qint32 Removed; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static VersionKindCountDto deserialize(const QByteArray &json); + + VersionKindCountDto( + qint32 Total, + qint32 Added, + qint32 Removed + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes the version of analysis data of a certain Project. + */ + class AnalysisVersionDto : public Serializable + { + public: + + /** + * <p>The ID used to refer to an `AnalysisVersion` in this API. + * + * <p>ISO8601 compatible date-strings that are guaranteed to + * contain time zone information. + * + * <p>An example version string is `2020-11-23T09:37:04.797286Z`. + * + * <p>The precision of these dates is undefined, thus in order to be sure to refer + * to exactly the same version when specifying a version as an argument e.g. + * when querying issues, you must use this exact string. + * + * <p>If you want to interpret the version-date e.g. for passing it to a + * graph-drawing library or to order a list of `AnalysisVersion`s, + * it is easier to use the field `millis` instead of parsing these dates. + */ + QString date; + + /** + * <p>Optional label of the version. + * + * @since 7.6.0 + */ + std::optional<QString> label; + + /** + * <p>The 0-based index of all the known analysis versions of a project. + * + * <p>The version with index 0 never contains actual analysis data but always + * refers to a fictional version without any issues that happened before + * version 1. + */ + qint32 index; + + /** + * <p>The display name of the analysis version consisting of the date formatted with the + * preferred time zone of the user making the request and the label in parentheses. + */ + QString name; + + /** + * <p>Analysis version timestamp in milliseconds + * + * <p>The number of milliseconds passed since 1970-01-01T00:00:00 UTC + * + * <p>Meant for programmatic interpretation of the actual instant in time that + * represents a version. + */ + qint64 millis; + + /** + * <p>For every Issue Kind contains some Issue counts. + * + * <p>Namely the Total count, as well as the newly Added and newly Removed issues in comparison + * with the version before. + * + * <p>N.B. The Bauhaus Version used to analyze the project must be at least 6.5.0 in order for + * these values to be available. + * + * @since 6.6.0 + */ + Any issueCounts; + + /** + * Refers to a specific version of the Axivion Suite + * + * <p>Version information of the Axivion Suite used to do this analysis run. + * + * <p>Note, that this field is only available when the analysis was done with at least version + * 6.5.0. + * + * @since 6.9.15 + */ + std::optional<ToolsVersionDto> toolsVersion; + + /** + * <p>The total lines of code of the project at the current version if available + * + * @since 7.0.4 + */ + std::optional<qint64> linesOfCode; + + /** + * <p>The clone ratio of the project at the current version if available + * + * @since 7.0.4 + */ + std::optional<double> cloneRatio; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static AnalysisVersionDto deserialize(const QByteArray &json); + + AnalysisVersionDto( + QString date, + std::optional<QString> label, + qint32 index, + QString name, + qint64 millis, + Any issueCounts, + std::optional<ToolsVersionDto> toolsVersion, + std::optional<qint64> linesOfCode, + std::optional<double> cloneRatio + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains Request-Data for creating an ApiToken + * + * @since 7.1.0 + */ + class ApiTokenCreationRequestDto : public Serializable + { + public: + + /** + * <p>Dashboard password of the user that requests the token creation + */ + QString password; + + /** + * Available types of ApiToken + * + * <p>the type of the token to create + * + * @since 7.1.0 + */ + QString type; + + /** + * <p>Purpose of the Token + */ + QString description; + + /** + * <p>Used for configuring the Token expiration. + * + * <p>* positive values are maxAge in milliseconds + * * 0 means: choose a default for me (recommended) + * * negative values are not allowed + * + * <p>Note, that the server clock is decisive for when the actual token expiration will occur. + * Expired tokens will be invalidated or deleted on the server depending on their type. + */ + qint64 maxAgeMillis; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ApiTokenCreationRequestDto deserialize(const QByteArray &json); + + ApiTokenCreationRequestDto( + QString password, + QString type, + QString description, + qint64 maxAgeMillis + ); + + ApiTokenCreationRequestDto( + QString password, + ApiTokenType type, + QString description, + qint64 maxAgeMillis + ); + + // Throws std::range_error + ApiTokenType getTypeEnum() const; + + void setTypeEnum(ApiTokenType newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains Meta-Data of an ApiToken + * + * <p>When this is returned as response of a creation request it will also + * contain the token secret + * + * @since 7.1.0 + */ + class ApiTokenInfoDto : public Serializable + { + public: + + /** + * <p>The unique Token-ID. + */ + QString id; + + /** + * <p>The token URL + */ + QString url; + + /** + * <p>Whether the token was still valid at query time. + * + * <p>Invalid Tokens are effectively tombstones and cannot be used for + * authentication any more. + * Note, that this field is no indication on whether or not this object + * is transporting the secret. + */ + bool isValid; + + /** + * Available types of ApiToken + * + * <p>The type of the Token + * + * @since 7.1.0 + */ + QString type; + + /** + * <p>Description that was given on token creation. + */ + QString description; + + /** + * <p>The secret token value. + * + * <p>This is only initialized upon token creation. Use this to authenticate + * against the Dashboard as described in :ref:`authentication`. + */ + std::optional<QString> token; + + /** + * <p>ISO8601 format date string + */ + QString creationDate; + + /** + * <p>Alternative representation of the token creation date, like "2 days ago" etc + */ + QString displayCreationDate; + + /** + * <p>ISO8601 format date string + */ + QString expirationDate; + + /** + * <p>Alternative representation of the token expiration date, like "3 months from now" etc + */ + QString displayExpirationDate; + + /** + * <p>ISO8601 format date if the token has already been used + */ + std::optional<QString> lastUseDate; + + /** + * <p>Alternative representation of the token last use date, e.g. "2 days ago" or "Never" + */ + QString displayLastUseDate; + + /** + * <p>Whether this token is used by the current request. + * + * <p>Deletion of this token will invalidate the currently used credentials + */ + bool usedByCurrentRequest; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ApiTokenInfoDto deserialize(const QByteArray &json); + + ApiTokenInfoDto( + QString id, + QString url, + bool isValid, + QString type, + QString description, + std::optional<QString> token, + QString creationDate, + QString displayCreationDate, + QString expirationDate, + QString displayExpirationDate, + std::optional<QString> lastUseDate, + QString displayLastUseDate, + bool usedByCurrentRequest + ); + + ApiTokenInfoDto( + QString id, + QString url, + bool isValid, + ApiTokenType type, + QString description, + std::optional<QString> token, + QString creationDate, + QString displayCreationDate, + QString expirationDate, + QString displayExpirationDate, + std::optional<QString> lastUseDate, + QString displayLastUseDate, + bool usedByCurrentRequest + ); + + // Throws std::range_error + ApiTokenType getTypeEnum() const; + + void setTypeEnum(ApiTokenType newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Represents a table column. + */ + class ColumnInfoDto : public Serializable + { + public: + + /** + * <p>Key used to identify the column in the rows and for sorting/filtering. + * + * <p>For Issue-Table columns, see :ref:`column <issue-table-columns>`. + */ + QString key; + + /** + * <p>Nice header name for the column for UI display purposes. + */ + std::optional<QString> header; + + /** + * <p>Specifies whether this column can be sorted. + */ + bool canSort; + + /** + * <p>Specifies whether this column can be filtered. + */ + bool canFilter; + + /** + * How the column values should be aligned. + * + * <p>How the column values should be aligned. + */ + QString alignment; + + /** + * <p>The column type. + * + * <p>Possible values: + * * `string` - a unicode string or ``null`` + * * `number` - either ``null`` or ``"Infinity"`` or ``"-Infinity"`` or ``"NaN"`` (string!) or a decimal number with base 10. + * * `state` - The fields are strings. Possible values are defined via ``typeOptions``. + * * `boolean` - The fields are boolean values, either ``false`` or ``true``. Has ``typeOptions``. + * * `path` - **Since 6.4.1** similar to ``string`` or ``null``, however they are normalized (guaranteed single-slash separators) for easy parsing as path + * * `tags` - **Since 6.5.0** an array of :json:object:`IssueTag` + * * `comments` - **Since 6.9.0** array of :json:object:`IssueComment` + * * `owners` - **Since 7.0.0** array of :json:object:`UserRef` + */ + QString type; + + /** + * <p>Describes possible values for the field. + * + * <p>Currently this is only used for the types ``state`` and ``boolean``. + * In case of type ``boolean`` this always contains exactly 2 elements, + * the first describing the false-equivalent, the second describing the + * true-equivalent. In case of type ``state`` this contains the possible string + * values the field may have. + */ + std::optional<std::vector<ColumnTypeOptionDto>> typeOptions; + + /** + * <p>Suggested column width in pixels + */ + qint32 width; + + /** + * <p>whether a gui should show this column by default. + */ + bool showByDefault; + + /** + * <p>Key used to identify a column that provides the hyperlink targets + * for this column ('ErrorLink' or null) + */ + std::optional<QString> linkKey; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ColumnInfoDto deserialize(const QByteArray &json); + + ColumnInfoDto( + QString key, + std::optional<QString> header, + bool canSort, + bool canFilter, + QString alignment, + QString type, + std::optional<std::vector<ColumnTypeOptionDto>> typeOptions, + qint32 width, + bool showByDefault, + std::optional<QString> linkKey + ); + + ColumnInfoDto( + QString key, + std::optional<QString> header, + bool canSort, + bool canFilter, + TableCellAlignment alignment, + QString type, + std::optional<std::vector<ColumnTypeOptionDto>> typeOptions, + qint32 width, + bool showByDefault, + std::optional<QString> linkKey + ); + + // Throws std::range_error + TableCellAlignment getAlignmentEnum() const; + + void setAlignmentEnum(TableCellAlignment newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Main API endpoint response + */ + class DashboardInfoDto : public Serializable + { + public: + + /** + * <p>The complete dashboard base URL. + * + * <p>Use this to point your browser to the Dashboard or + * combine it with the other host-relative URLs returned by this API + * in order to get complete URLs. + * + * <p>Note, that this URL may be different from the URL you are currently + * accessing the Dashboard with for various reasons. + * + * <p>Also note, that the Dashboard cannot always know the proper value + * to return here, e.g. when running behind a proxy. So an administrator + * might need to help out the Dashboard by configuring the proper value + * in the global settings. + * + * @since 7.4.0 + */ + std::optional<QString> mainUrl; + + /** + * <p>Axivion Dashboard version serving this API. + */ + QString dashboardVersion; + + /** + * <p>Parseable Axivion Dashboard Version. + * + * @since 6.6.0 + */ + std::optional<QString> dashboardVersionNumber; + + /** + * <p>Dashboard Server Build date. + */ + QString dashboardBuildDate; + + /** + * <p>Name of the successfully authenticated user if a dashboard-user is associated with the request. + */ + std::optional<QString> username; + + /** + * <p>The HTTP-Request Header expected present for all HTTP requests that are not GET, HEAD, OPTIONS or TRACE. + * + * <p>Deprecated since 7.7.0: the header name is always ``AX-CSRF-Token``. + * + * @deprecated + */ + std::optional<QString> csrfTokenHeader; + + /** + * <p>The value expected to be sent along the ``csrfTokenHeader`` for all HTTP requests that are not GET, HEAD, OPTIONS or TRACE. + * + * <p>Note, that this does not replace authentication of subsequent requests. Also the token is combined with the authentication + * data, so will not work when authenticating as another user. Its lifetime is limited, so when creating a very long-running + * application you should consider refreshing this token from time to time. + */ + QString csrfToken; + + /** + * <p>An URI that can be used to check credentials via GET. It returns `"ok"` in case of valid credentials. + * + * @since 6.5.4 + */ + std::optional<QString> checkCredentialsUrl; + + /** + * <p>Endpoint for managing global named filters + * + * @since 7.3.0 + */ + std::optional<QString> namedFiltersUrl; + + /** + * <p>List of references to the projects visible to the authenticated user. + */ + std::optional<std::vector<ProjectReferenceDto>> projects; + + /** + * <p>Endpoint for creating and listing api tokens of the current user + * + * @since 7.1.0 + */ + std::optional<QString> userApiTokenUrl; + + /** + * <p>Endpoint for managing custom named filters of the current user + * + * @since 7.3.0 + */ + std::optional<QString> userNamedFiltersUrl; + + /** + * <p>E-mail address for support requests + * + * @since 7.4.3 + */ + std::optional<QString> supportAddress; + + /** + * <p>A host-relative URL that can be used to display filter-help meant for humans. + * + * @since 7.4.3 + */ + std::optional<QString> issueFilterHelp; + + /** + * <p>Endoint for creating new CSRF tokens. + * + * @since 7.7.0 + */ + std::optional<QString> csrfTokenUrl; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static DashboardInfoDto deserialize(const QByteArray &json); + + DashboardInfoDto( + std::optional<QString> mainUrl, + QString dashboardVersion, + std::optional<QString> dashboardVersionNumber, + QString dashboardBuildDate, + std::optional<QString> username, + std::optional<QString> csrfTokenHeader, + QString csrfToken, + std::optional<QString> checkCredentialsUrl, + std::optional<QString> namedFiltersUrl, + std::optional<std::vector<ProjectReferenceDto>> projects, + std::optional<QString> userApiTokenUrl, + std::optional<QString> userNamedFiltersUrl, + std::optional<QString> supportAddress, + std::optional<QString> issueFilterHelp, + std::optional<QString> csrfTokenUrl + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Response when listing comments of an issue + */ + class IssueCommentListDto : public Serializable + { + public: + + /** + * <p>Comments in chronological order + */ + std::vector<IssueCommentDto> comments; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueCommentListDto deserialize(const QByteArray &json); + + IssueCommentListDto( + std::vector<IssueCommentDto> comments + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Describes an Issue Kind. + */ + class IssueKindInfoDto : public Serializable + { + public: + + /** + * Shortname for an erosion issue kind + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV`` + */ + QString prefix; + + /** + * <p>A singular string for using in UI texts about the issue kind. + */ + QString niceSingularName; + + /** + * <p>A plural string for using in UI texts about the issue kind. + */ + QString nicePluralName; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueKindInfoDto deserialize(const QByteArray &json); + + IssueKindInfoDto( + QString prefix, + QString niceSingularName, + QString nicePluralName + ); + + IssueKindInfoDto( + IssueKind prefix, + QString niceSingularName, + QString nicePluralName + ); + + // Throws std::range_error + IssueKind getPrefixEnum() const; + + void setPrefixEnum(IssueKind newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * List of Issue Tag Types + */ + class IssueTagTypeListDto : public Serializable + { + public: + + /** + * <p>Result when querying tags of a given issue + */ + std::vector<IssueTagTypeDto> tags; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTagTypeListDto deserialize(const QByteArray &json); + + IssueTagTypeListDto( + std::vector<IssueTagTypeDto> tags + ); + + virtual QByteArray serialize() const override; + }; + + /** + * <p>Describes a tainted location of a source file + */ + class LineMarkerDto : public Serializable + { + public: + + /** + * The issue kind + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV`` + */ + QString kind; + + /** + * The issue id. + * + * <p>This id is not global. It is only unique for the same project and kind. + * + * <p>Note, that this may not be available e.g. when served by the PluginARServer. + */ + std::optional<qint64> id; + + /** + * The first tainted line. + * + * <p>First line in file is line 1. + */ + qint32 startLine; + + /** + * The column of the start line in which the issue starts. + * + * <p>First column in line is column 1. + * + * <p>0 iff unknown + * + * @since 7.2.0 + */ + qint32 startColumn; + + /** + * The last tainted line. + * + * <p>First line in file is line 1. + */ + qint32 endLine; + + /** + * The column of the end line in which the issue ends. + * + * <p>First column in line is column 1. + * + * <p>0 iff unknown + * + * @since 7.2.0 + */ + qint32 endColumn; + + /** + * <p>A prosaic (one-liner) description of the issue. + */ + QString description; + + /** + * <p>Host-relative API URI to access further information about the issue. + * + * <p>Note, that this may not be available e.g. when served by the PluginARServer. + */ + std::optional<QString> issueUrl; + + /** + * <p>Determines, whether the issue is new in the given version, i.e. it did not + * exist in the version before the given version and the given version is not + * the first analyzed version. + * + * @since 6.5.0 + */ + std::optional<bool> isNew; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static LineMarkerDto deserialize(const QByteArray &json); + + LineMarkerDto( + QString kind, + std::optional<qint64> id, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn, + QString description, + std::optional<QString> issueUrl, + std::optional<bool> isNew + ); + + LineMarkerDto( + IssueKind kind, + std::optional<qint64> id, + qint32 startLine, + qint32 startColumn, + qint32 endLine, + qint32 endColumn, + QString description, + std::optional<QString> issueUrl, + std::optional<bool> isNew + ); + + // Throws std::range_error + IssueKind getKindEnum() const; + + void setKindEnum(IssueKind newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * A log message with an associated log level + * + * <p>Contains messages returned from the VCS-adapters reporting on + * the VCS update result. + */ + class RepositoryUpdateMessageDto : public Serializable + { + public: + + /** + * The log-level of a message + * + * <p>The log-level of the message. + */ + std::optional<QString> severity; + + /** + * <p>the log-message. It may contain new-lines. + */ + std::optional<QString> message; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static RepositoryUpdateMessageDto deserialize(const QByteArray &json); + + RepositoryUpdateMessageDto( + std::optional<QString> severity, + std::optional<QString> message + ); + + RepositoryUpdateMessageDto( + std::optional<MessageSeverity> severity, + std::optional<QString> message + ); + + // Throws std::range_error + std::optional<MessageSeverity> getSeverityEnum() const; + + void setSeverityEnum(std::optional<MessageSeverity> newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains a list of rules + * + * @since 6.5.0 + */ + class RuleListDto : public Serializable + { + public: + + + std::vector<RuleDto> rules; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static RuleListDto deserialize(const QByteArray &json); + + RuleListDto( + std::vector<RuleDto> rules + ); + + virtual QByteArray serialize() const override; + }; + + /** + * A Column Key + Sort Direction to indicate table sort preferences. + */ + class SortInfoDto : public Serializable + { + public: + + /** + * <p>The :ref:`column key<issue-table-columns>` + */ + QString key; + + /** + * A sorting direction + * + * <p>The sort direction associated with this column. + */ + QString direction; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static SortInfoDto deserialize(const QByteArray &json); + + SortInfoDto( + QString key, + QString direction + ); + + SortInfoDto( + QString key, + SortDirection direction + ); + + // Throws std::range_error + SortDirection getDirectionEnum() const; + + void setDirectionEnum(SortDirection newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Information about a user + */ + class UserRefDto : public Serializable + { + public: + + /** + * <p>User name. Use this to refer to the same user. + */ + QString name; + + /** + * <p>Use this for display of the user in a UI. + */ + QString displayName; + + /** + * User Type + * + * <p>User Type + * + * @since 7.1.0 + */ + std::optional<QString> type; + + /** + * <p>Whether this user is a so-called `public` user. + * + * @since 7.1.0 + */ + std::optional<bool> isPublic; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static UserRefDto deserialize(const QByteArray &json); + + UserRefDto( + QString name, + QString displayName, + std::optional<QString> type, + std::optional<bool> isPublic + ); + + UserRefDto( + QString name, + QString displayName, + std::optional<UserRefType> type, + std::optional<bool> isPublic + ); + + // Throws std::range_error + std::optional<UserRefType> getTypeEnum() const; + + void setTypeEnum(std::optional<UserRefType> newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains a list of analyzed file descriptions. + */ + class AnalyzedFileListDto : public Serializable + { + public: + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The version this list was queried with. + */ + AnalysisVersionDto version; + + + std::vector<AnalyzedFileDto> rows; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static AnalyzedFileListDto deserialize(const QByteArray &json); + + AnalyzedFileListDto( + AnalysisVersionDto version, + std::vector<AnalyzedFileDto> rows + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains a list of entities and the version of their versioned aspects + */ + class EntityListDto : public Serializable + { + public: + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The version this entity list was queried with. + */ + std::optional<AnalysisVersionDto> version; + + + std::vector<EntityDto> entities; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static EntityListDto deserialize(const QByteArray &json); + + EntityListDto( + std::optional<AnalysisVersionDto> version, + std::vector<EntityDto> entities + ); + + virtual QByteArray serialize() const override; + }; + + /** + * <p>Describes information that can be used to display a file with erosion information. + * + * @since 6.2.0 + */ + class FileViewDto : public Serializable + { + public: + + /** + * <p>The complete path of the file relative to the projects file-root + */ + QString fileName; + + /** + * <p>ISO-8601 Datestring of the file's version that can be used when manually + * constructing an URL to retrieve the file's source-code. + * + * <p>The format used is yyyy-MM-ddTHH:mm:ss.SSSZZ + * + * @since 6.4.2 + */ + std::optional<QString> version; + + /** + * <p>Refers to the source code which can be gotten as text/plain or as an + * application/json object containing a token-sequence + * + * <p>Note, that this may not be available e.g. when served by the PluginARServer. + */ + std::optional<QString> sourceCodeUrl; + + /** + * <p>The erosion information associated with the file + */ + std::vector<LineMarkerDto> lineMarkers; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static FileViewDto deserialize(const QByteArray &json); + + FileViewDto( + QString fileName, + std::optional<QString> version, + std::optional<QString> sourceCodeUrl, + std::vector<LineMarkerDto> lineMarkers + ); + + virtual QByteArray serialize() const override; + }; + + /** + * <p>Describes an issue + */ + class IssueDto : public Serializable + { + public: + + /** + * The issue kind + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV`` + */ + QString kind; + + /** + * The issue id + * + * <p>This id is not global. It is only unique for the same project and kind. + */ + qint64 id; + + /** + * The Project the issue belongs to + */ + ProjectReferenceDto parentProject; + + /** + * Source locations associated with the issue + */ + std::vector<IssueSourceLocationDto> sourceLocations; + + /** + * The issue kind + */ + IssueKindInfoDto issueKind; + + /** + * Whether or not the issue is hidden + * + * @since 6.4.0 + */ + bool isHidden; + + /** + * <p>Versioned host relative URL to view issue in browser + * + * @since 7.2.1 + */ + std::optional<QString> issueViewUrl; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueDto deserialize(const QByteArray &json); + + IssueDto( + QString kind, + qint64 id, + ProjectReferenceDto parentProject, + std::vector<IssueSourceLocationDto> sourceLocations, + IssueKindInfoDto issueKind, + bool isHidden, + std::optional<QString> issueViewUrl + ); + + IssueDto( + IssueKind kind, + qint64 id, + ProjectReferenceDto parentProject, + std::vector<IssueSourceLocationDto> sourceLocations, + IssueKindInfoDto issueKind, + bool isHidden, + std::optional<QString> issueViewUrl + ); + + // Throws std::range_error + IssueKind getKindEnum() const; + + void setKindEnum(IssueKind newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Result of querying the issue-list retrieval entry point mainly + * containing a list of issues. + */ + class IssueTableDto : public Serializable + { + public: + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The version of the ``removed`` issues. + * + * <p>If the query was not an actual diff query this will be unset. + */ + std::optional<AnalysisVersionDto> startVersion; + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The version of the ``added`` issues for a diff query + * or simply the version of a normal issue list query (no startVersion) + */ + AnalysisVersionDto endVersion; + + /** + * <p>Url to view the issues in the Dashboard Browser UI + * + * @since 7.2.1 + */ + std::optional<QString> tableViewUrl; + + /** + * <p>The Issue Table Columns describing the issue fields. + * + * <p>Deprecated since 7.1.0. Use :json:object`TableInfo` instead + * + * @deprecated + */ + std::optional<std::vector<ColumnInfoDto>> columns; + + /** + * <p>The actual issue data objects. + * + * <p>The issue object contents are dynamic and depend on the queried + * issue kind. See :ref:`here<issue-table-columns>` for the individual field + * descriptions depending on the issue kind. The values need to be interpreted + * according to their columntype. + * + * <p>This only contains a subset of the complete data if paging is enabled via + * ``offset`` and ``limit``. + */ + std::vector<std::map<QString, Any>> rows; + + /** + * <p>The total number of issues. + * + * <p>Only available when ``computeTotalRowCount`` was specified as ``true``. + * Mostly useful when doing paged queries using the query parameters ``limit`` + * and ``offset``. + */ + std::optional<qint32> totalRowCount; + + /** + * <p>The total number of issues existing in the ``current`` version and not in + * the ``baseline`` version. + * + * <p>Only useful in diff queries and only calculated when + * ``computeTotalRowCount`` was specified as ``true``. + */ + std::optional<qint32> totalAddedCount; + + /** + * <p>The total number of issues existing in the ``baseline`` version and not in + * the ``current`` version. + * + * <p>Only useful in diff queries and only calculated when + * ``computeTotalRowCount`` was specified as ``true``. + */ + std::optional<qint32> totalRemovedCount; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static IssueTableDto deserialize(const QByteArray &json); + + IssueTableDto( + std::optional<AnalysisVersionDto> startVersion, + AnalysisVersionDto endVersion, + std::optional<QString> tableViewUrl, + std::optional<std::vector<ColumnInfoDto>> columns, + std::vector<std::map<QString, Any>> rows, + std::optional<qint32> totalRowCount, + std::optional<qint32> totalAddedCount, + std::optional<qint32> totalRemovedCount + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains a list of metric descriptions + */ + class MetricListDto : public Serializable + { + public: + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The version this metric list was queried with. + */ + std::optional<AnalysisVersionDto> version; + + + std::vector<MetricDto> metrics; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static MetricListDto deserialize(const QByteArray &json); + + MetricListDto( + std::optional<AnalysisVersionDto> version, + std::vector<MetricDto> metrics + ); + + virtual QByteArray serialize() const override; + }; + + /** + * The result of a metric values query + */ + class MetricValueRangeDto : public Serializable + { + public: + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The start version of the metric value range. + */ + AnalysisVersionDto startVersion; + + /** + * Describes the version of analysis data of a certain Project. + * + * <p>The end version of the metric value range. + */ + AnalysisVersionDto endVersion; + + /** + * <p>The id of the entity + */ + QString entity; + + /** + * <p>The id of the metric + */ + QString metric; + + /** + * <p>An array with the metric values. + * + * <p>The array size is ``endVersion.index - startVersion.index + 1``. + * Its values are numbers or ``null`` if no value is available. + * They correspond to the range defined by ``startVersion`` and ``endVersion``. + */ + std::vector<std::optional<double>> values; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueRangeDto deserialize(const QByteArray &json); + + MetricValueRangeDto( + AnalysisVersionDto startVersion, + AnalysisVersionDto endVersion, + QString entity, + QString metric, + std::vector<std::optional<double>> values + ); + + virtual QByteArray serialize() const override; + }; + + /** + * The result of a metric value table query + */ + class MetricValueTableDto : public Serializable + { + public: + + /** + * <p>The column descriptions of the entity columns. + * + * <p>Only contains the two fields ``key`` and ``header``. + */ + std::vector<ColumnInfoDto> columns; + + /** + * <p>The entity data. + */ + std::vector<MetricValueTableRowDto> rows; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static MetricValueTableDto deserialize(const QByteArray &json); + + MetricValueTableDto( + std::vector<ColumnInfoDto> columns, + std::vector<MetricValueTableRowDto> rows + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Named Filter Creation Request Data + * + * <p>Contains information to create a named filter from scratch + * + * @since 7.3.0 + */ + class NamedFilterCreateDto : public Serializable + { + public: + + + QString displayName; + + /** + * IssueKind for Named-Filter creation + * + * <p>one of ``AV``, ``CL``, ``CY``, ``DE``, ``MV``, ``SV``, ``UNIVERSAL`` + */ + QString kind; + + /** + * <p>The actual filters with column-id as key and filter-value as value + * + * <p>* Possible keys are described here :ref:`column keys<issue-table-columns>` + * * This does not necessarily contain an entry for every column. In fact it may even be empty. + * * A filter value can never be null. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::map<QString, QString> filters; + + /** + * <p>Defines the sort order to apply. + * + * <p>* The first entry has the highest sort priority and the last one the lowest. + * * No column key may be referenced twice. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::vector<SortInfoDto> sorters; + + /** + * NamedFilter visibility configuration + * + * <p>Only applicable for global named filters. + * + * <p>You may not have access to this information depending on your permissions. + * + * @since 7.3.0 + */ + std::optional<NamedFilterVisibilityDto> visibility; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterCreateDto deserialize(const QByteArray &json); + + NamedFilterCreateDto( + QString displayName, + QString kind, + std::map<QString, QString> filters, + std::vector<SortInfoDto> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ); + + NamedFilterCreateDto( + QString displayName, + IssueKindForNamedFilterCreation kind, + std::map<QString, QString> filters, + std::vector<SortInfoDto> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ); + + // Throws std::range_error + IssueKindForNamedFilterCreation getKindEnum() const; + + void setKindEnum(IssueKindForNamedFilterCreation newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * A Named Filter configuration. + * + * <p>It can consist of the following: + * - Filter values for the individual columns and the path-filter + * - A sort configuration indicating how to sort the table columns + * + * @since 6.9.0 + */ + class NamedFilterInfoDto : public Serializable + { + public: + + /** + * <p>Uniquely identifies this filter configuration. + */ + QString key; + + /** + * <p>The name to use when displaying this filter. + */ + QString displayName; + + /** + * <p>The URL for getting/updating/deleting this named filter. + * + * @since 7.3.0 + */ + std::optional<QString> url; + + /** + * <p>Whether this filter is a predefined filter. + */ + bool isPredefined; + + /** + * A named filter type + * + * <p>* `PREDEFINED` - Named filters of this type are immutable and exist out of the box and can be used by everyone + * * `GLOBAL` - Named filters of this type are usable by everyone and managed by the so called filter managers + * * `CUSTOM` - Named filters of this type are creatable by everyone but only visible to their owner + * + * @since 7.3.0 + */ + std::optional<QString> type; + + /** + * <p>Whether the user that requested this object can change or delete this filter configuration + */ + bool canWrite; + + /** + * <p>The actual filters with column-id as key and filter-value as value + * + * <p>* Possible keys are described here :ref:`column keys<issue-table-columns>` + * * This does not necessarily contain an entry for every column. In fact it may even be empty. + * * A filter value can never be null. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::map<QString, QString> filters; + + /** + * <p>Defines the sort order to apply. + * + * <p>* The first entry has the highest sort priority and the last one the lowest. + * * No column key may be referenced twice. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::optional<std::vector<SortInfoDto>> sorters; + + /** + * <p>True if this filter is valid for all issue kinds, false otherwise. + */ + bool supportsAllIssueKinds; + + /** + * <p>Supported Issue Kinds. + * + * <p>If ``supportsAllIssueKinds`` is false, then this set indicates, which issue kinds are supported. + */ + std::optional<std::unordered_set<QString>> issueKindRestrictions; + + /** + * NamedFilter visibility configuration + * + * <p>Only applicable for global named filters. + * + * <p>You may not have access to this information depending on your permissions. + * + * @since 7.3.0 + */ + std::optional<NamedFilterVisibilityDto> visibility; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterInfoDto deserialize(const QByteArray &json); + + NamedFilterInfoDto( + QString key, + QString displayName, + std::optional<QString> url, + bool isPredefined, + std::optional<QString> type, + bool canWrite, + std::map<QString, QString> filters, + std::optional<std::vector<SortInfoDto>> sorters, + bool supportsAllIssueKinds, + std::optional<std::unordered_set<QString>> issueKindRestrictions, + std::optional<NamedFilterVisibilityDto> visibility + ); + + NamedFilterInfoDto( + QString key, + QString displayName, + std::optional<QString> url, + bool isPredefined, + std::optional<NamedFilterType> type, + bool canWrite, + std::map<QString, QString> filters, + std::optional<std::vector<SortInfoDto>> sorters, + bool supportsAllIssueKinds, + std::optional<std::unordered_set<QString>> issueKindRestrictions, + std::optional<NamedFilterVisibilityDto> visibility + ); + + // Throws std::range_error + std::optional<NamedFilterType> getTypeEnum() const; + + void setTypeEnum(std::optional<NamedFilterType> newValue); + + virtual QByteArray serialize() const override; + }; + + /** + * Named Filter Update Request Data + * + * <p>Contains information to update an existing named filter. + * Fields that are not given, won't be touched. + * + * @since 7.3.0 + */ + class NamedFilterUpdateDto : public Serializable + { + public: + + /** + * <p>An optional new name for the named filter. + * + * <p>Changing this will also result in a changed ID and URL. + */ + std::optional<QString> name; + + /** + * <p>The actual filters with column-id as key and filter-value as value + * + * <p>* Possible keys are described here :ref:`column keys<issue-table-columns>` + * * This does not necessarily contain an entry for every column. In fact it may even be empty. + * * A filter value can never be null. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::optional<std::map<QString, QString>> filters; + + /** + * <p>Defines the sort order to apply. + * + * <p>* The first entry has the highest sort priority and the last one the lowest. + * * No column key may be referenced twice. + * * May contain references to columns that do not exist in the table column configuration. + */ + std::optional<std::vector<SortInfoDto>> sorters; + + /** + * NamedFilter visibility configuration + * + * <p>Only applicable for global named filters. + * + * <p>You may not have access to this information depending on your permissions. + * + * @since 7.3.0 + */ + std::optional<NamedFilterVisibilityDto> visibility; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static NamedFilterUpdateDto deserialize(const QByteArray &json); + + NamedFilterUpdateDto( + std::optional<QString> name, + std::optional<std::map<QString, QString>> filters, + std::optional<std::vector<SortInfoDto>> sorters, + std::optional<NamedFilterVisibilityDto> visibility + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Information about a project + */ + class ProjectInfoDto : public Serializable + { + public: + + /** + * <p>The name of the project. Use this string to refer to the project. + */ + QString name; + + /** + * <p>A host-relative URL that can be used to display filter-help meant for humans. + * + * @since 6.5.0 + */ + std::optional<QString> issueFilterHelp; + + /** + * <p>URL to query the table meta data (column definitions). Needs the issue kind as a query parameter ``kind``. + * + * @since 6.9.0 + */ + std::optional<QString> tableMetaUri; + + /** + * <p>List of users associated with the project and visible to the authenticated user. + */ + std::vector<UserRefDto> users; + + /** + * <p>List of analysis versions associated with the project. + */ + std::vector<AnalysisVersionDto> versions; + + /** + * <p>List of IssueKinds associated with the project. + */ + std::vector<IssueKindInfoDto> issueKinds; + + /** + * <p>Whether or not the project has hidden issues. When this is false then UI should try to hide all the complexity related with hidden issues. + */ + bool hasHiddenIssues; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static ProjectInfoDto deserialize(const QByteArray &json); + + ProjectInfoDto( + QString name, + std::optional<QString> issueFilterHelp, + std::optional<QString> tableMetaUri, + std::vector<UserRefDto> users, + std::vector<AnalysisVersionDto> versions, + std::vector<IssueKindInfoDto> issueKinds, + bool hasHiddenIssues + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Response to Repository Update Request + * + * <p>Contains messages returned from the VCS-adapters reporting on + * the VCS update result. + */ + class RepositoryUpdateResponseDto : public Serializable + { + public: + + /** + * <p>The messages returned from the VCS-adapters + */ + std::vector<RepositoryUpdateMessageDto> messages; + + /** + * <p>Whether at least one of the messages is at least an ERROR + */ + bool hasErrors; + + /** + * <p>Whether at least one of the messages is at least a WARNING + */ + bool hasWarnings; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static RepositoryUpdateResponseDto deserialize(const QByteArray &json); + + RepositoryUpdateResponseDto( + std::vector<RepositoryUpdateMessageDto> messages, + bool hasErrors, + bool hasWarnings + ); + + virtual QByteArray serialize() const override; + }; + + /** + * Contains meta information for issuelist querying of a specific issue kind. + * + * @since 6.9.0 + */ + class TableInfoDto : public Serializable + { + public: + + /** + * <p>Host-relative URL to query the rows of the issue table. + * + * <p>Needs get parameters to specify which data is requested exactly. + */ + QString tableDataUri; + + /** + * <p>Host-relative URL base to view an issue in the dashboard. + * + * <p>In order to construct the issue URL it is necessary to append the issue-id (e.g. SV123) + * to the URL path. Also the query-parameter is not part of the URL base and can be added. + * + * @since 7.2.1 + */ + std::optional<QString> issueBaseViewUri; + + /** + * <p>Columns with detailed information on how to create a table to display the data. + */ + std::vector<ColumnInfoDto> columns; + + /** + * <p>The ``Named Filters`` available for the chosen issue kind. + * + * <p>This will contain predefined and custom filter configurations and never be empty. + * + * <p>The list order is a recommendation for UI display. + */ + std::vector<NamedFilterInfoDto> filters; + + /** + * <p>The key of the configured default filter. + * + * <p>This will not be given if the configured default is not also visible + * to the requesting user. + */ + std::optional<QString> userDefaultFilter; + + /** + * <p>The key of the factory default filter. + * + * <p>This will always point to an existing and visible named filter. + */ + QString axivionDefaultFilter; + + // Throws Axivion::Internal::Dto::invalid_dto_exception + static TableInfoDto deserialize(const QByteArray &json); + + TableInfoDto( + QString tableDataUri, + std::optional<QString> issueBaseViewUri, + std::vector<ColumnInfoDto> columns, + std::vector<NamedFilterInfoDto> filters, + std::optional<QString> userDefaultFilter, + QString axivionDefaultFilter + ); + + virtual QByteArray serialize() const override; + }; + +} diff --git a/src/plugins/baremetal/BareMetal.json.in b/src/plugins/baremetal/BareMetal.json.in index c27befaddc3..13bd9ba4ac9 100644 --- a/src/plugins/baremetal/BareMetal.json.in +++ b/src/plugins/baremetal/BareMetal.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"BareMetal\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"Tim Sander\", - \"Copyright\" : \"(C) 2016 Tim Sander, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "BareMetal", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "Tim Sander", + "Copyright" : "(C) 2016 Tim Sander, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"This plugin adds a target for bare metal development.\", - $$dependencyList + "Category" : "Device Support", + "Description" : "This plugin adds a target for bare metal development.", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/baremetal/baremetaldebugsupport.cpp b/src/plugins/baremetal/baremetaldebugsupport.cpp index 4475be0b964..18aad498535 100644 --- a/src/plugins/baremetal/baremetaldebugsupport.cpp +++ b/src/plugins/baremetal/baremetaldebugsupport.cpp @@ -10,12 +10,12 @@ #include "debugserverprovidermanager.h" #include "idebugserverprovider.h" -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerruncontrol.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfiguration.h> diff --git a/src/plugins/baremetal/baremetaldevice.cpp b/src/plugins/baremetal/baremetaldevice.cpp index d7754ebc9a0..e0d271f2591 100644 --- a/src/plugins/baremetal/baremetaldevice.cpp +++ b/src/plugins/baremetal/baremetaldevice.cpp @@ -14,6 +14,7 @@ #include <utils/qtcassert.h> using namespace ProjectExplorer; +using namespace Utils; namespace BareMetal::Internal { @@ -62,7 +63,7 @@ void BareMetalDevice::unregisterDebugServerProvider(IDebugServerProvider *provid m_debugServerProviderId.clear(); } -void BareMetalDevice::fromMap(const QVariantMap &map) +void BareMetalDevice::fromMap(const Store &map) { IDevice::fromMap(map); QString providerId = map.value(debugServerProviderIdKeyC).toString(); @@ -78,9 +79,9 @@ void BareMetalDevice::fromMap(const QVariantMap &map) } } -QVariantMap BareMetalDevice::toMap() const +Store BareMetalDevice::toMap() const { - QVariantMap map = IDevice::toMap(); + Store map = IDevice::toMap(); map.insert(debugServerProviderIdKeyC, debugServerProviderId()); return map; } diff --git a/src/plugins/baremetal/baremetaldevice.h b/src/plugins/baremetal/baremetaldevice.h index 662f24c08bc..b8aa72e12be 100644 --- a/src/plugins/baremetal/baremetaldevice.h +++ b/src/plugins/baremetal/baremetaldevice.h @@ -31,8 +31,8 @@ public: void unregisterDebugServerProvider(IDebugServerProvider *provider); protected: - void fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Utils::Store &map) final; + Utils::Store toMap() const final; private: BareMetalDevice(); diff --git a/src/plugins/baremetal/baremetaldeviceconfigurationwizard.cpp b/src/plugins/baremetal/baremetaldeviceconfigurationwizard.cpp index 79df101ccf4..20b27d77e60 100644 --- a/src/plugins/baremetal/baremetaldeviceconfigurationwizard.cpp +++ b/src/plugins/baremetal/baremetaldeviceconfigurationwizard.cpp @@ -26,7 +26,7 @@ ProjectExplorer::IDevice::Ptr BareMetalDeviceConfigurationWizard::device() const { const auto dev = BareMetalDevice::create(); dev->setupId(ProjectExplorer::IDevice::ManuallyAdded, Utils::Id()); - dev->setDisplayName(m_setupPage->configurationName()); + dev->settings()->displayName.setDefaultValue(m_setupPage->configurationName()); dev->setType(Constants::BareMetalOsType); dev->setMachineType(ProjectExplorer::IDevice::Hardware); dev->setDebugServerProviderId(m_setupPage->debugServerProviderId()); diff --git a/src/plugins/baremetal/baremetalplugin.cpp b/src/plugins/baremetal/baremetalplugin.cpp index f97bf1b40c6..b8383c65b9d 100644 --- a/src/plugins/baremetal/baremetalplugin.cpp +++ b/src/plugins/baremetal/baremetalplugin.cpp @@ -11,7 +11,6 @@ #include "baremetaltr.h" #include "debugserverprovidermanager.h" -#include "debugserverproviderssettingspage.h" #include "iarewtoolchain.h" #include "keiltoolchain.h" @@ -53,7 +52,6 @@ public: BareMetalDeviceFactory deviceFactory; BareMetalRunConfigurationFactory runConfigurationFactory; BareMetalCustomRunConfigurationFactory customRunConfigurationFactory; - DebugServerProvidersSettingsPage debugServerProviderSettinsPage; DebugServerProviderManager debugServerProviderManager; BareMetalDeployConfigurationFactory deployConfigurationFactory; BareMetalDebugSupportFactory runWorkerFactory; diff --git a/src/plugins/baremetal/baremetalrunconfiguration.cpp b/src/plugins/baremetal/baremetalrunconfiguration.cpp index 4fe6b090246..8b805732974 100644 --- a/src/plugins/baremetal/baremetalrunconfiguration.cpp +++ b/src/plugins/baremetal/baremetalrunconfiguration.cpp @@ -25,20 +25,24 @@ public: explicit BareMetalRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - const auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setDisplayStyle(StringAspect::LabelDisplay); - exeAspect->setPlaceHolderText(Tr::tr("Unknown")); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setPlaceHolderText(Tr::tr("Unknown")); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr); + arguments.setMacroExpander(macroExpander()); - setUpdater([this, exeAspect] { + workingDir.setMacroExpander(macroExpander()); + + setUpdater([this] { const BuildTargetInfo bti = buildTargetInfo(); - exeAspect->setExecutable(bti.targetFilePath); + executable.setExecutable(bti.targetFilePath); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); } + + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; }; class BareMetalCustomRunConfiguration final : public RunConfiguration @@ -47,33 +51,37 @@ public: explicit BareMetalCustomRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - const auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setSettingsKey("BareMetal.CustomRunConfig.Executable"); - exeAspect->setPlaceHolderText(Tr::tr("Unknown")); - exeAspect->setDisplayStyle(StringAspect::PathChooserDisplay); - exeAspect->setHistoryCompleter("BareMetal.CustomRunConfig.History"); - exeAspect->setExpectedKind(PathChooser::Any); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setSettingsKey("BareMetal.CustomRunConfig.Executable"); + executable.setPlaceHolderText(Tr::tr("Unknown")); + executable.setReadOnly(false); + executable.setHistoryCompleter("BareMetal.CustomRunConfig.History"); + executable.setExpectedKind(PathChooser::Any); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr); + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); setDefaultDisplayName(RunConfigurationFactory::decoratedTargetName( - Tr::tr("Custom Executable"), target)); + Tr::tr("Custom Executable"), target)); } public: - Tasks checkForIssues() const final; + Tasks checkForIssues() const final + { + Tasks tasks; + if (executable.executable().isEmpty()) { + tasks << createConfigurationIssue(Tr::tr("The remote executable must be set in order to " + "run a custom remote run configuration.")); + } + return tasks; + } + + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; }; -Tasks BareMetalCustomRunConfiguration::checkForIssues() const -{ - Tasks tasks; - if (aspect<ExecutableAspect>()->executable().isEmpty()) { - tasks << createConfigurationIssue(Tr::tr("The remote executable must be set in order to " - "run a custom remote run configuration.")); - } - return tasks; -} // BareMetalRunConfigurationFactory diff --git a/src/plugins/baremetal/debugserverproviderchooser.cpp b/src/plugins/baremetal/debugserverproviderchooser.cpp index acf25b5a791..ac05c88e0d4 100644 --- a/src/plugins/baremetal/debugserverproviderchooser.cpp +++ b/src/plugins/baremetal/debugserverproviderchooser.cpp @@ -13,7 +13,6 @@ #include <QComboBox> #include <QHBoxLayout> #include <QPushButton> -#include <QSettings> namespace BareMetal::Internal { diff --git a/src/plugins/baremetal/debugserverprovidermanager.cpp b/src/plugins/baremetal/debugserverprovidermanager.cpp index be4ed0ab06f..e7afc2ef10d 100644 --- a/src/plugins/baremetal/debugserverprovidermanager.cpp +++ b/src/plugins/baremetal/debugserverprovidermanager.cpp @@ -24,6 +24,8 @@ #include <utils/persistentsettings.h> #include <utils/qtcassert.h> +using namespace Utils; + namespace BareMetal::Internal { const char dataKeyC[] = "DebugServerProvider."; @@ -77,27 +79,27 @@ DebugServerProviderManager *DebugServerProviderManager::instance() void DebugServerProviderManager::restoreProviders() { - Utils::PersistentSettingsReader reader; + PersistentSettingsReader reader; if (!reader.load(m_configFile)) return; - const QVariantMap data = reader.restoreValues(); + const Store data = reader.restoreValues(); const int version = data.value(fileVersionKeyC, 0).toInt(); if (version < 1) return; const int count = data.value(countKeyC, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = QString::fromLatin1(dataKeyC) + QString::number(i); + const Key key = numberedKey(dataKeyC, i); if (!data.contains(key)) break; - QVariantMap map = data.value(key).toMap(); - const QStringList keys = map.keys(); - for (const QString &key : keys) { - const int lastDot = key.lastIndexOf('.'); + Store map = storeFromVariant(data.value(key)); + const KeyList keys = map.keys(); + for (const Key &key : keys) { + const int lastDot = key.view().lastIndexOf('.'); if (lastDot != -1) - map[key.mid(lastDot + 1)] = map[key]; + map[key.toByteArray().mid(lastDot + 1)] = map[key]; } bool restored = false; for (IDebugServerProviderFactory *f : std::as_const(m_factories)) { @@ -120,17 +122,18 @@ void DebugServerProviderManager::restoreProviders() void DebugServerProviderManager::saveProviders() { - QVariantMap data; + Store data; data.insert(fileVersionKeyC, 1); int count = 0; for (const IDebugServerProvider *p : std::as_const(m_providers)) { if (p->isValid()) { - const QVariantMap tmp = p->toMap(); + Store tmp; + p->toMap(tmp); if (tmp.isEmpty()) continue; - const QString key = QString::fromLatin1(dataKeyC) + QString::number(count); - data.insert(key, tmp); + const Key key = numberedKey(dataKeyC, count); + data.insert(key, variantFromStore(tmp)); ++count; } } diff --git a/src/plugins/baremetal/debugserverprovidermanager.h b/src/plugins/baremetal/debugserverprovidermanager.h index 9e8661590e9..08a9d3b51d6 100644 --- a/src/plugins/baremetal/debugserverprovidermanager.h +++ b/src/plugins/baremetal/debugserverprovidermanager.h @@ -6,7 +6,7 @@ #include <QList> #include <QObject> -#include <utils/fileutils.h> +#include <utils/filepath.h> namespace Utils { class PersistentSettingsWriter; } diff --git a/src/plugins/baremetal/debugserverproviderssettingspage.cpp b/src/plugins/baremetal/debugserverproviderssettingspage.cpp index 58af3578a96..5c7a1cda51d 100644 --- a/src/plugins/baremetal/debugserverproviderssettingspage.cpp +++ b/src/plugins/baremetal/debugserverproviderssettingspage.cpp @@ -8,8 +8,9 @@ #include "debugserverprovidermanager.h" #include "idebugserverprovider.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> -#include <extensionsystem/pluginmanager.h> + #include <projectexplorer/projectexplorerconstants.h> #include <utils/algorithm.h> @@ -346,7 +347,9 @@ DebugServerProvidersSettingsWidget::DebugServerProvidersSettingsWidget() for (const auto f : DebugServerProviderManager::factories()) { if (id.startsWith(f->id())) { IDebugServerProvider *p = f->create(); - p->fromMap(old->toMap()); + Store map; + old->toMap(map); + p->fromMap(map); p->setDisplayName(Tr::tr("Clone of %1").arg(old->displayName())); p->resetId(); addProviderToModel(p); @@ -427,12 +430,18 @@ QModelIndex DebugServerProvidersSettingsWidget::currentIndex() const // DebugServerProvidersSettingsPage -DebugServerProvidersSettingsPage::DebugServerProvidersSettingsPage() +class DebugServerProvidersSettingsPage final : public Core::IOptionsPage { - setId(Constants::DEBUG_SERVER_PROVIDERS_SETTINGS_ID); - setDisplayName(Tr::tr("Bare Metal")); - setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); - setWidgetCreator([] { return new DebugServerProvidersSettingsWidget; }); -} +public: + DebugServerProvidersSettingsPage() + { + setId(Constants::DEBUG_SERVER_PROVIDERS_SETTINGS_ID); + setDisplayName(Tr::tr("Bare Metal")); + setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); + setWidgetCreator([] { return new DebugServerProvidersSettingsWidget; }); + } +}; + +static const DebugServerProvidersSettingsPage settingsPage; } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugserverproviderssettingspage.h b/src/plugins/baremetal/debugserverproviderssettingspage.h index 11ffd18383a..80586d727b5 100644 --- a/src/plugins/baremetal/debugserverproviderssettingspage.h +++ b/src/plugins/baremetal/debugserverproviderssettingspage.h @@ -3,25 +3,13 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> - #include <utils/treemodel.h> -QT_BEGIN_NAMESPACE -class QItemSelectionModel; -class QPushButton; -class QTreeView; -QT_END_NAMESPACE - -namespace Utils { class DetailsWidget; } - namespace BareMetal::Internal { class DebugServerProviderNode; -class DebugServerProvidersSettingsWidget; class IDebugServerProvider; class IDebugServerProviderConfigWidget; -class IDebugServerProviderFactory; // DebugServerProviderModel @@ -57,12 +45,4 @@ private: QList<IDebugServerProvider *> m_providersToRemove; }; -// DebugServerProvidersSettingsPage - -class DebugServerProvidersSettingsPage final : public Core::IOptionsPage -{ -public: - DebugServerProvidersSettingsPage(); -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp index d307eefb0ec..7ce65d689ed 100644 --- a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.cpp @@ -34,8 +34,81 @@ const char targetDisableStackC[] = "TargetDisableStack"; const char gdbShutDownAfterDisconnectC[] = "GdbShutDownAfterDisconnect"; const char gdbNotUseCacheC[] = "GdbNotUseCache"; +enum InterfaceType { SWD, JTAG }; + +// EBlinkGdbServerProviderWidget + +class EBlinkGdbServerProvider; + +class EBlinkGdbServerProviderConfigWidget final : public GdbServerProviderConfigWidget +{ +public: + explicit EBlinkGdbServerProviderConfigWidget(EBlinkGdbServerProvider *provider); + +private: + void apply() final; + void discard() final; + + InterfaceType interfaceTypeToWidget(int idx) const; + InterfaceType interfaceTypeFromWidget() const; + + void populateInterfaceTypes(); + void setFromProvider(); + + HostWidget *m_gdbHostWidget = nullptr; + PathChooser *m_executableFileChooser = nullptr; + QSpinBox *m_verboseLevelSpinBox = nullptr; + QCheckBox *m_resetOnConnectCheckBox = nullptr; + QCheckBox *m_notUseCacheCheckBox = nullptr; + QCheckBox *m_shutDownAfterDisconnectCheckBox = nullptr; + QComboBox *m_interfaceTypeComboBox = nullptr; + //QLineEdit *m_deviceScriptLineEdit = nullptr; + PathChooser *m_scriptFileChooser = nullptr; + QSpinBox *m_interfaceSpeedSpinBox = nullptr; + QPlainTextEdit *m_initCommandsTextEdit = nullptr; + QPlainTextEdit *m_resetCommandsTextEdit = nullptr; +}; + // EBlinkGdbServerProvider +class EBlinkGdbServerProvider final : public GdbServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + + QString channelString() const final; + Utils::CommandLine command() const final; + + QSet<StartupMode> supportedStartupModes() const final; + bool isValid() const final; + +private: + EBlinkGdbServerProvider(); + + static QString defaultInitCommands(); + static QString defaultResetCommands(); + + Utils::FilePath m_executableFile = "eblink"; // server execute filename + int m_verboseLevel = 0; // verbose <0..7> Specify generally verbose logging + InterfaceType m_interfaceType = SWD; // -I stlink ;swd(default) jtag + Utils::FilePath m_deviceScript = "stm32-auto.script"; // -D <script> ;Select the device script <>.script + bool m_interfaceResetOnConnect = true; // (inversed)-I stlink,dr ;Disable reset at connection (hotplug) + int m_interfaceSpeed = 4000; // -I stlink,speed=4000 + QString m_interfaceExplicidDevice; // device=<usb_bus>:<usb_addr> ; Set device explicit + QString m_targetName = {"cortex-m"}; // -T cortex-m(default) + bool m_targetDisableStack = false; // -T cortex-m,nu ;Disable stack unwind at exception + bool m_gdbShutDownAfterDisconnect = true;// -G S ; Shutdown after disconnect + bool m_gdbNotUseCache = false; // -G nc ; Don't use EBlink flash cache + + QString scriptFileWoExt() const; + + friend class EBlinkGdbServerProviderConfigWidget; + friend class EBlinkGdbServerProviderFactory; +}; + EBlinkGdbServerProvider::EBlinkGdbServerProvider() : GdbServerProvider(Constants::GDBSERVER_EBLINK_PROVIDER_ID) { @@ -142,9 +215,9 @@ bool EBlinkGdbServerProvider::isValid() const } } -QVariantMap EBlinkGdbServerProvider::toMap() const +void EBlinkGdbServerProvider::toMap(Store &data) const { - QVariantMap data = GdbServerProvider::toMap(); + GdbServerProvider::toMap(data); data.insert(executableFileKeyC, m_executableFile.toSettings()); data.insert(verboseLevelKeyC, m_verboseLevel); data.insert(interfaceTypeC, m_interfaceType); @@ -156,20 +229,15 @@ QVariantMap EBlinkGdbServerProvider::toMap() const data.insert(targetDisableStackC, m_targetDisableStack); data.insert(gdbShutDownAfterDisconnectC, m_gdbShutDownAfterDisconnect); data.insert(gdbNotUseCacheC, m_gdbNotUseCache); - - return data; } -bool EBlinkGdbServerProvider::fromMap(const QVariantMap &data) +void EBlinkGdbServerProvider::fromMap(const Store &data) { - if (!GdbServerProvider::fromMap(data)) - return false; - + GdbServerProvider::fromMap(data); m_executableFile = FilePath::fromSettings(data.value(executableFileKeyC)); m_verboseLevel = data.value(verboseLevelKeyC).toInt(); m_interfaceResetOnConnect = data.value(interfaceResetOnConnectC).toBool(); - m_interfaceType = static_cast<InterfaceType>( - data.value(interfaceTypeC).toInt()); + m_interfaceType = static_cast<InterfaceType>(data.value(interfaceTypeC).toInt()); m_deviceScript = FilePath::fromSettings(data.value(deviceScriptC)); m_interfaceResetOnConnect = data.value(interfaceResetOnConnectC).toBool(); m_interfaceSpeed = data.value(interfaceSpeedC).toInt(); @@ -178,8 +246,6 @@ bool EBlinkGdbServerProvider::fromMap(const QVariantMap &data) m_targetDisableStack = data.value(targetDisableStackC).toBool(); m_gdbShutDownAfterDisconnect = data.value(gdbShutDownAfterDisconnectC).toBool(); m_gdbNotUseCache = data.value(gdbNotUseCacheC).toBool(); - - return true; } bool EBlinkGdbServerProvider::operator==(const IDebugServerProvider &other) const @@ -212,6 +278,7 @@ EBlinkGdbServerProviderFactory::EBlinkGdbServerProviderFactory() // EBlinkGdbServerProviderConfigWidget + EBlinkGdbServerProviderConfigWidget::EBlinkGdbServerProviderConfigWidget( EBlinkGdbServerProvider *p) : GdbServerProviderConfigWidget(p) @@ -298,26 +365,21 @@ EBlinkGdbServerProviderConfigWidget::EBlinkGdbServerProviderConfigWidget( this, &GdbServerProviderConfigWidget::dirty); } -EBlinkGdbServerProvider::InterfaceType -EBlinkGdbServerProviderConfigWidget::interfaceTypeToWidget(int idx) const +InterfaceType EBlinkGdbServerProviderConfigWidget::interfaceTypeToWidget(int idx) const { m_interfaceTypeComboBox->setCurrentIndex(idx); return interfaceTypeFromWidget(); } -EBlinkGdbServerProvider::InterfaceType -EBlinkGdbServerProviderConfigWidget::interfaceTypeFromWidget() const +InterfaceType EBlinkGdbServerProviderConfigWidget::interfaceTypeFromWidget() const { - return static_cast<EBlinkGdbServerProvider::InterfaceType>( - m_interfaceTypeComboBox->currentIndex()); + return static_cast<InterfaceType>(m_interfaceTypeComboBox->currentIndex()); } void EBlinkGdbServerProviderConfigWidget::populateInterfaceTypes() { - m_interfaceTypeComboBox->insertItem(EBlinkGdbServerProvider::SWD, Tr::tr("SWD"), - EBlinkGdbServerProvider::SWD); - m_interfaceTypeComboBox->insertItem(EBlinkGdbServerProvider::JTAG, Tr::tr("JTAG"), - EBlinkGdbServerProvider::JTAG); + m_interfaceTypeComboBox->insertItem(SWD, Tr::tr("SWD"), SWD); + m_interfaceTypeComboBox->insertItem(JTAG, Tr::tr("JTAG"), JTAG); } void EBlinkGdbServerProviderConfigWidget::setFromProvider() diff --git a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.h index 9f74ba52f48..276f42b9c29 100644 --- a/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/eblinkgdbserverprovider.h @@ -5,98 +5,12 @@ #include "gdbserverprovider.h" -QT_BEGIN_NAMESPACE -class QCheckBox; -class QPlainTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - namespace BareMetal::Internal { -// EBlinkGdbServerProvider - -class EBlinkGdbServerProvider final : public GdbServerProvider +class EBlinkGdbServerProviderFactory final : public IDebugServerProviderFactory { public: - enum InterfaceType { SWD, JTAG }; - - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - - QString channelString() const final; - Utils::CommandLine command() const final; - - QSet<StartupMode> supportedStartupModes() const final; - bool isValid() const final; - -private: - EBlinkGdbServerProvider(); - - static QString defaultInitCommands(); - static QString defaultResetCommands(); - - Utils::FilePath m_executableFile = "eblink"; // server execute filename - int m_verboseLevel = 0; // verbose <0..7> Specify generally verbose logging - InterfaceType m_interfaceType = SWD; // -I stlink ;swd(default) jtag - Utils::FilePath m_deviceScript = "stm32-auto.script"; // -D <script> ;Select the device script <>.script - bool m_interfaceResetOnConnect = true; // (inversed)-I stlink,dr ;Disable reset at connection (hotplug) - int m_interfaceSpeed = 4000; // -I stlink,speed=4000 - QString m_interfaceExplicidDevice; // device=<usb_bus>:<usb_addr> ; Set device explicit - QString m_targetName = {"cortex-m"}; // -T cortex-m(default) - bool m_targetDisableStack = false; // -T cortex-m,nu ;Disable stack unwind at exception - bool m_gdbShutDownAfterDisconnect = true;// -G S ; Shutdown after disconnect - bool m_gdbNotUseCache = false; // -G nc ; Don't use EBlink flash cache - - QString scriptFileWoExt() const; - - friend class EBlinkGdbServerProviderConfigWidget; - friend class EBlinkGdbServerProviderFactory; -}; - -// EBlinkGdbServerProviderFactory - -class EBlinkGdbServerProviderFactory final - : public IDebugServerProviderFactory -{ -public: - explicit EBlinkGdbServerProviderFactory(); -}; - -// EBlinkGdbServerProviderConfigWidget - -class EBlinkGdbServerProviderConfigWidget final - : public GdbServerProviderConfigWidget -{ -public: - explicit EBlinkGdbServerProviderConfigWidget( - EBlinkGdbServerProvider *provider); - -private: - void apply() final; - void discard() final; - - EBlinkGdbServerProvider::InterfaceType interfaceTypeToWidget(int idx) const; - EBlinkGdbServerProvider::InterfaceType interfaceTypeFromWidget() const; - - void populateInterfaceTypes(); - void setFromProvider(); - - HostWidget *m_gdbHostWidget = nullptr; - Utils::PathChooser *m_executableFileChooser = nullptr; - QSpinBox *m_verboseLevelSpinBox = nullptr; - QCheckBox *m_resetOnConnectCheckBox = nullptr; - QCheckBox *m_notUseCacheCheckBox = nullptr; - QCheckBox *m_shutDownAfterDisconnectCheckBox = nullptr; - QComboBox *m_interfaceTypeComboBox = nullptr; - //QLineEdit *m_deviceScriptLineEdit = nullptr; - Utils::PathChooser *m_scriptFileChooser = nullptr; - QSpinBox *m_interfaceSpeedSpinBox = nullptr; - QPlainTextEdit *m_initCommandsTextEdit = nullptr; - QPlainTextEdit *m_resetCommandsTextEdit = nullptr; + EBlinkGdbServerProviderFactory(); }; } // BareMetal::Internal - diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp index 222d22ade98..3da6b06254f 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp @@ -120,15 +120,14 @@ bool GdbServerProvider::operator==(const IDebugServerProvider &other) const && m_useExtendedRemote == p->m_useExtendedRemote; } -QVariantMap GdbServerProvider::toMap() const +void GdbServerProvider::toMap(Store &data) const { - QVariantMap data = IDebugServerProvider::toMap(); + IDebugServerProvider::toMap(data); data.insert(startupModeKeyC, m_startupMode); data.insert(peripheralDescriptionFileKeyC, m_peripheralDescriptionFile.toSettings()); data.insert(initCommandsKeyC, m_initCommands); data.insert(resetCommandsKeyC, m_resetCommands); data.insert(useExtendedRemoteKeyC, m_useExtendedRemote); - return data; } bool GdbServerProvider::isValid() const @@ -136,8 +135,7 @@ bool GdbServerProvider::isValid() const return !channelString().isEmpty(); } -bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, - QString &errorMessage) const +bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessage) const { QTC_ASSERT(runTool, return false); const RunControl *runControl = runTool->runControl(); @@ -155,7 +153,7 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, return false; } - Runnable inferior; + ProcessRunData inferior; inferior.command.setExecutable(bin); if (const auto argAspect = runControl->aspect<ArgumentsAspect>()) inferior.command.setArguments(argAspect->arguments); @@ -181,17 +179,14 @@ RunWorker *GdbServerProvider::targetRunner(RunControl *runControl) const return new GdbServerProviderRunner(runControl, command()); } -bool GdbServerProvider::fromMap(const QVariantMap &data) +void GdbServerProvider::fromMap(const Store &data) { - if (!IDebugServerProvider::fromMap(data)) - return false; - + IDebugServerProvider::fromMap(data); m_startupMode = static_cast<StartupMode>(data.value(startupModeKeyC).toInt()); m_peripheralDescriptionFile = FilePath::fromSettings(data.value(peripheralDescriptionFileKeyC)); m_initCommands = data.value(initCommandsKeyC).toString(); m_resetCommands = data.value(resetCommandsKeyC).toString(); m_useExtendedRemote = data.value(useExtendedRemoteKeyC).toBool(); - return true; } // GdbServerProviderConfigWidget diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h index 1c23d499957..f9b0887693c 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.h @@ -33,7 +33,7 @@ public: bool operator==(const IDebugServerProvider &other) const override; - QVariantMap toMap() const override; + void toMap(Utils::Store &data) const override; virtual Utils::CommandLine command() const; @@ -55,7 +55,7 @@ protected: void setResetCommands(const QString &); void setUseExtendedRemote(bool); - bool fromMap(const QVariantMap &data) override; + void fromMap(const Utils::Store &data) override; StartupMode m_startupMode = StartupOnNetwork; Utils::FilePath m_peripheralDescriptionFile; diff --git a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.cpp index 418de9b2016..b882cf6ec22 100644 --- a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.cpp @@ -16,8 +16,46 @@ namespace BareMetal::Internal { +// GenericGdbServerProviderConfigWidget + +class GenericGdbServerProvider; + +class GenericGdbServerProviderConfigWidget final : public GdbServerProviderConfigWidget +{ +public: + explicit GenericGdbServerProviderConfigWidget(GenericGdbServerProvider *provider); + +private: + void apply() final; + void discard() final; + + void setFromProvider(); + + HostWidget *m_hostWidget = nullptr; + QCheckBox *m_useExtendedRemoteCheckBox = nullptr; + QPlainTextEdit *m_initCommandsTextEdit = nullptr; + QPlainTextEdit *m_resetCommandsTextEdit = nullptr; +}; + // GenericGdbServerProvider +class GenericGdbServerProvider final : public GdbServerProvider +{ +private: + GenericGdbServerProvider(); + QSet<StartupMode> supportedStartupModes() const final; + ProjectExplorer::RunWorker *targetRunner(ProjectExplorer::RunControl *runControl) const final { + Q_UNUSED(runControl) + // Generic Runner assumes GDB Server already running + return nullptr; + } + + friend class GenericGdbServerProviderConfigWidget; + friend class GenericGdbServerProviderFactory; +}; + +// GenericGdbServerProviderFactory + GenericGdbServerProvider::GenericGdbServerProvider() : GdbServerProvider(Constants::GDBSERVER_GENERIC_PROVIDER_ID) { diff --git a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h index 98ca17451c1..e44122c2007 100644 --- a/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/genericgdbserverprovider.h @@ -5,58 +5,12 @@ #include "gdbserverprovider.h" -QT_BEGIN_NAMESPACE -class QCheckBox; -class QPlainTextEdit; -QT_END_NAMESPACE - namespace BareMetal::Internal { -// GenericGdbServerProvider - -class GenericGdbServerProvider final : public GdbServerProvider -{ -private: - GenericGdbServerProvider(); - QSet<StartupMode> supportedStartupModes() const final; - ProjectExplorer::RunWorker *targetRunner(ProjectExplorer::RunControl *runControl) const final { - Q_UNUSED(runControl) - // Generic Runner assumes GDB Server already running - return nullptr; - } - - friend class GenericGdbServerProviderConfigWidget; - friend class GenericGdbServerProviderFactory; - friend class BareMetalDevice; -}; - -// GenericGdbServerProviderFactory - class GenericGdbServerProviderFactory final : public IDebugServerProviderFactory { public: GenericGdbServerProviderFactory(); }; -// GenericGdbServerProviderConfigWidget - -class GenericGdbServerProviderConfigWidget final - : public GdbServerProviderConfigWidget -{ -public: - explicit GenericGdbServerProviderConfigWidget( - GenericGdbServerProvider *provider); - -private: - void apply() final; - void discard() final; - - void setFromProvider(); - - HostWidget *m_hostWidget = nullptr; - QCheckBox *m_useExtendedRemoteCheckBox = nullptr; - QPlainTextEdit *m_initCommandsTextEdit = nullptr; - QPlainTextEdit *m_resetCommandsTextEdit = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp index c1ee31b7109..56495e32c6f 100644 --- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.cpp @@ -32,8 +32,84 @@ const char jlinkTargetInterfaceKeyC[] = "JLinkTargetInterface"; const char jlinkTargetInterfaceSpeedKeyC[] = "JLinkTargetInterfaceSpeed"; const char additionalArgumentsKeyC[] = "AdditionalArguments"; +// JLinkGdbServerProviderConfigWidget + +class JLinkGdbServerProvider; + +class JLinkGdbServerProviderConfigWidget final : public GdbServerProviderConfigWidget +{ +public: + explicit JLinkGdbServerProviderConfigWidget(JLinkGdbServerProvider *provider); + +private: + void apply() final; + void discard() final; + + void populateHostInterfaces(); + void populateTargetInterfaces(); + void populateTargetSpeeds(); + + void setHostInterface(const QString &newIface); + void setTargetInterface(const QString &newIface); + void setTargetSpeed(const QString &newSpeed); + + void updateAllowedControls(); + + void setFromProvider(); + + HostWidget *m_hostWidget = nullptr; + PathChooser *m_executableFileChooser = nullptr; + + QWidget *m_hostInterfaceWidget = nullptr; + QComboBox *m_hostInterfaceComboBox = nullptr; + QLabel *m_hostInterfaceAddressLabel = nullptr; + QLineEdit *m_hostInterfaceAddressLineEdit = nullptr; + + QWidget *m_targetInterfaceWidget = nullptr; + QComboBox *m_targetInterfaceComboBox = nullptr; + QLabel *m_targetInterfaceSpeedLabel = nullptr; + QComboBox *m_targetInterfaceSpeedComboBox = nullptr; + + QLineEdit *m_jlinkDeviceLineEdit = nullptr; + QPlainTextEdit *m_additionalArgumentsTextEdit = nullptr; + QPlainTextEdit *m_initCommandsTextEdit = nullptr; + QPlainTextEdit *m_resetCommandsTextEdit = nullptr; +}; + // JLinkGdbServerProvider +class JLinkGdbServerProvider final : public GdbServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + + QString channelString() const final; + CommandLine command() const final; + + QSet<StartupMode> supportedStartupModes() const final; + bool isValid() const final; + +private: + JLinkGdbServerProvider(); + + static QString defaultInitCommands(); + static QString defaultResetCommands(); + + FilePath m_executableFile; + QString m_jlinkDevice; + QString m_jlinkHost = {"USB"}; + QString m_jlinkHostAddr; + QString m_jlinkTargetIface = {"SWD"}; + QString m_jlinkTargetIfaceSpeed = {"12000"}; + QString m_additionalArguments; + + friend class JLinkGdbServerProviderConfigWidget; + friend class JLinkGdbServerProviderFactory; +}; + JLinkGdbServerProvider::JLinkGdbServerProvider() : GdbServerProvider(Constants::GDBSERVER_JLINK_PROVIDER_ID) { @@ -118,9 +194,9 @@ bool JLinkGdbServerProvider::isValid() const return true; } -QVariantMap JLinkGdbServerProvider::toMap() const +void JLinkGdbServerProvider::toMap(Store &data) const { - QVariantMap data = GdbServerProvider::toMap(); + GdbServerProvider::toMap(data); data.insert(executableFileKeyC, m_executableFile.toSettings()); data.insert(jlinkDeviceKeyC, m_jlinkDevice); data.insert(jlinkHostInterfaceKeyC, m_jlinkHost); @@ -128,14 +204,11 @@ QVariantMap JLinkGdbServerProvider::toMap() const data.insert(jlinkTargetInterfaceKeyC, m_jlinkTargetIface); data.insert(jlinkTargetInterfaceSpeedKeyC, m_jlinkTargetIfaceSpeed); data.insert(additionalArgumentsKeyC, m_additionalArguments); - return data; } -bool JLinkGdbServerProvider::fromMap(const QVariantMap &data) +void JLinkGdbServerProvider::fromMap(const Store &data) { - if (!GdbServerProvider::fromMap(data)) - return false; - + GdbServerProvider::fromMap(data); m_executableFile = FilePath::fromSettings(data.value(executableFileKeyC)); m_jlinkDevice = data.value(jlinkDeviceKeyC).toString(); m_additionalArguments = data.value(additionalArgumentsKeyC).toString(); @@ -143,7 +216,6 @@ bool JLinkGdbServerProvider::fromMap(const QVariantMap &data) m_jlinkHostAddr = data.value(jlinkHostInterfaceIPAddressKeyC).toString(); m_jlinkTargetIface = data.value(jlinkTargetInterfaceKeyC).toString(); m_jlinkTargetIfaceSpeed = data.value(jlinkTargetInterfaceSpeedKeyC).toString(); - return true; } bool JLinkGdbServerProvider::operator==(const IDebugServerProvider &other) const diff --git a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.h index aacb781b045..1b88c2ce07b 100644 --- a/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/jlinkgdbserverprovider.h @@ -5,98 +5,12 @@ #include "gdbserverprovider.h" -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - namespace BareMetal::Internal { -// JLinkGdbServerProvider - -class JLinkGdbServerProvider final : public GdbServerProvider -{ -public: - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - - QString channelString() const final; - Utils::CommandLine command() const final; - - QSet<StartupMode> supportedStartupModes() const final; - bool isValid() const final; - -private: - JLinkGdbServerProvider(); - - static QString defaultInitCommands(); - static QString defaultResetCommands(); - - Utils::FilePath m_executableFile; - QString m_jlinkDevice; - QString m_jlinkHost = {"USB"}; - QString m_jlinkHostAddr; - QString m_jlinkTargetIface = {"SWD"}; - QString m_jlinkTargetIfaceSpeed = {"12000"}; - QString m_additionalArguments; - - friend class JLinkGdbServerProviderConfigWidget; - friend class JLinkGdbServerProviderFactory; -}; - -// JLinkGdbServerProviderFactory - -class JLinkGdbServerProviderFactory final - : public IDebugServerProviderFactory +class JLinkGdbServerProviderFactory final : public IDebugServerProviderFactory { public: JLinkGdbServerProviderFactory(); }; -// JLinkGdbServerProviderConfigWidget - -class JLinkGdbServerProviderConfigWidget final - : public GdbServerProviderConfigWidget -{ -public: - explicit JLinkGdbServerProviderConfigWidget(JLinkGdbServerProvider *provider); - -private: - void apply() final; - void discard() final; - - void populateHostInterfaces(); - void populateTargetInterfaces(); - void populateTargetSpeeds(); - - void setHostInterface(const QString &newIface); - void setTargetInterface(const QString &newIface); - void setTargetSpeed(const QString &newSpeed); - - void updateAllowedControls(); - - void setFromProvider(); - - HostWidget *m_hostWidget = nullptr; - Utils::PathChooser *m_executableFileChooser = nullptr; - - QWidget *m_hostInterfaceWidget = nullptr; - QComboBox *m_hostInterfaceComboBox = nullptr; - QLabel *m_hostInterfaceAddressLabel = nullptr; - QLineEdit *m_hostInterfaceAddressLineEdit = nullptr; - - QWidget *m_targetInterfaceWidget = nullptr; - QComboBox *m_targetInterfaceComboBox = nullptr; - QLabel *m_targetInterfaceSpeedLabel = nullptr; - QComboBox *m_targetInterfaceSpeedComboBox = nullptr; - - QLineEdit *m_jlinkDeviceLineEdit = nullptr; - QPlainTextEdit *m_additionalArgumentsTextEdit = nullptr; - QPlainTextEdit *m_initCommandsTextEdit = nullptr; - QPlainTextEdit *m_resetCommandsTextEdit = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp index ca7f25e56f3..ff4ae311afd 100644 --- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.cpp @@ -27,8 +27,63 @@ const char rootScriptsDirKeyC[] = "RootScriptsDir"; const char configurationFileKeyC[] = "ConfigurationPath"; const char additionalArgumentsKeyC[] = "AdditionalArguments"; +// OpenOcdGdbServerProviderConfigWidget + +class OpenOcdGdbServerProvider; + +class OpenOcdGdbServerProviderConfigWidget final : public GdbServerProviderConfigWidget +{ +public: + explicit OpenOcdGdbServerProviderConfigWidget(OpenOcdGdbServerProvider *provider); + +private: + void apply() final; + void discard() final; + + void startupModeChanged(); + void setFromProvider(); + + HostWidget *m_hostWidget = nullptr; + Utils::PathChooser *m_executableFileChooser = nullptr; + Utils::PathChooser *m_rootScriptsDirChooser = nullptr; + Utils::PathChooser *m_configurationFileChooser = nullptr; + QLineEdit *m_additionalArgumentsLineEdit = nullptr; + QPlainTextEdit *m_initCommandsTextEdit = nullptr; + QPlainTextEdit *m_resetCommandsTextEdit = nullptr; +}; + // OpenOcdGdbServerProvider +class OpenOcdGdbServerProvider final : public GdbServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + + QString channelString() const final; + Utils::CommandLine command() const final; + + QSet<StartupMode> supportedStartupModes() const final; + bool isValid() const final; + +private: + explicit OpenOcdGdbServerProvider(); + + static QString defaultInitCommands(); + static QString defaultResetCommands(); + + Utils::FilePath m_executableFile = "openocd"; + Utils::FilePath m_rootScriptsDir; + Utils::FilePath m_configurationFile; + QString m_additionalArguments; + + friend class OpenOcdGdbServerProviderConfigWidget; + friend class OpenOcdGdbServerProviderFactory; +}; + + OpenOcdGdbServerProvider::OpenOcdGdbServerProvider() : GdbServerProvider(Constants::GDBSERVER_OPENOCD_PROVIDER_ID) { @@ -125,26 +180,22 @@ bool OpenOcdGdbServerProvider::isValid() const return true; } -QVariantMap OpenOcdGdbServerProvider::toMap() const +void OpenOcdGdbServerProvider::toMap(Store &data) const { - QVariantMap data = GdbServerProvider::toMap(); + GdbServerProvider::toMap(data); data.insert(executableFileKeyC, m_executableFile.toSettings()); data.insert(rootScriptsDirKeyC, m_rootScriptsDir.toSettings()); data.insert(configurationFileKeyC, m_configurationFile.toSettings()); data.insert(additionalArgumentsKeyC, m_additionalArguments); - return data; } -bool OpenOcdGdbServerProvider::fromMap(const QVariantMap &data) +void OpenOcdGdbServerProvider::fromMap(const Store &data) { - if (!GdbServerProvider::fromMap(data)) - return false; - + GdbServerProvider::fromMap(data); m_executableFile = FilePath::fromSettings(data.value(executableFileKeyC)); m_rootScriptsDir = FilePath::fromSettings(data.value(rootScriptsDirKeyC)); m_configurationFile = FilePath::fromSettings(data.value(configurationFileKeyC)); m_additionalArguments = data.value(additionalArgumentsKeyC).toString(); - return true; } bool OpenOcdGdbServerProvider::operator==(const IDebugServerProvider &other) const diff --git a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.h index e21972a9ab5..8c6e89b2cd0 100644 --- a/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/openocdgdbserverprovider.h @@ -5,76 +5,12 @@ #include "gdbserverprovider.h" -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - namespace BareMetal::Internal { -// OpenOcdGdbServerProvider - -class OpenOcdGdbServerProvider final : public GdbServerProvider -{ -public: - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - - QString channelString() const final; - Utils::CommandLine command() const final; - - QSet<StartupMode> supportedStartupModes() const final; - bool isValid() const final; - -private: - explicit OpenOcdGdbServerProvider(); - - static QString defaultInitCommands(); - static QString defaultResetCommands(); - - Utils::FilePath m_executableFile = "openocd"; - Utils::FilePath m_rootScriptsDir; - Utils::FilePath m_configurationFile; - QString m_additionalArguments; - - friend class OpenOcdGdbServerProviderConfigWidget; - friend class OpenOcdGdbServerProviderFactory; -}; - -// OpenOcdGdbServerProviderFactory - -class OpenOcdGdbServerProviderFactory final - : public IDebugServerProviderFactory +class OpenOcdGdbServerProviderFactory final : public IDebugServerProviderFactory { public: OpenOcdGdbServerProviderFactory(); }; -// OpenOcdGdbServerProviderConfigWidget - -class OpenOcdGdbServerProviderConfigWidget final - : public GdbServerProviderConfigWidget -{ -public: - explicit OpenOcdGdbServerProviderConfigWidget(OpenOcdGdbServerProvider *provider); - -private: - void apply() final; - void discard() final; - - void startupModeChanged(); - void setFromProvider(); - - HostWidget *m_hostWidget = nullptr; - Utils::PathChooser *m_executableFileChooser = nullptr; - Utils::PathChooser *m_rootScriptsDirChooser = nullptr; - Utils::PathChooser *m_configurationFileChooser = nullptr; - QLineEdit *m_additionalArgumentsLineEdit = nullptr; - QPlainTextEdit *m_initCommandsTextEdit = nullptr; - QPlainTextEdit *m_resetCommandsTextEdit = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp index 78d0e6fedbe..b94012c9dc7 100644 --- a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.cpp @@ -29,8 +29,72 @@ const char resetBoardKeyC[] = "ResetBoard"; const char transportLayerKeyC[] = "TransportLayer"; const char connectUnderResetKeyC[] = "ConnectUnderReset"; +class StLinkUtilGdbServerProvider; + +enum TransportLayer { ScsiOverUsb = 1, RawUsb = 2, UnspecifiedTransport }; + +// StLinkUtilGdbServerProviderConfigWidget + +class StLinkUtilGdbServerProviderConfigWidget final : public GdbServerProviderConfigWidget +{ +public: + explicit StLinkUtilGdbServerProviderConfigWidget(StLinkUtilGdbServerProvider *provider); + +private: + void apply() final; + void discard() final; + + TransportLayer transportLayerFromIndex(int idx) const; + TransportLayer transportLayer() const; + void setTransportLayer(TransportLayer); + + void populateTransportLayers(); + void setFromProvider(); + + HostWidget *m_hostWidget = nullptr; + Utils::PathChooser *m_executableFileChooser = nullptr; + QSpinBox *m_verboseLevelSpinBox = nullptr; + QCheckBox *m_extendedModeCheckBox = nullptr; + QCheckBox *m_resetOnConnectCheckBox = nullptr; + QCheckBox *m_resetBoardCheckBox = nullptr; + QComboBox *m_transportLayerComboBox = nullptr; + QPlainTextEdit *m_initCommandsTextEdit = nullptr; + QPlainTextEdit *m_resetCommandsTextEdit = nullptr; +}; + // StLinkUtilGdbServerProvider +class StLinkUtilGdbServerProvider final : public GdbServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + + QString channelString() const final; + Utils::CommandLine command() const final; + + QSet<StartupMode> supportedStartupModes() const final; + bool isValid() const final; + +private: + StLinkUtilGdbServerProvider(); + + static QString defaultInitCommands(); + static QString defaultResetCommands(); + + Utils::FilePath m_executableFile = "st-util"; + int m_verboseLevel = 0; // 0..99 + bool m_extendedMode = false; // Listening for connections after disconnect + bool m_resetBoard = true; + bool m_connectUnderReset = false; // Makes it possible to connect to the device before code execution + TransportLayer m_transport = RawUsb; + + friend class StLinkUtilGdbServerProviderConfigWidget; + friend class StLinkUtilGdbServerProviderFactory; +}; + StLinkUtilGdbServerProvider::StLinkUtilGdbServerProvider() : GdbServerProvider(Constants::GDBSERVER_STLINK_UTIL_PROVIDER_ID) { @@ -113,23 +177,20 @@ bool StLinkUtilGdbServerProvider::isValid() const return true; } -QVariantMap StLinkUtilGdbServerProvider::toMap() const +void StLinkUtilGdbServerProvider::toMap(Store &data) const { - QVariantMap data = GdbServerProvider::toMap(); + GdbServerProvider::toMap(data); data.insert(executableFileKeyC, m_executableFile.toSettings()); data.insert(verboseLevelKeyC, m_verboseLevel); data.insert(extendedModeKeyC, m_extendedMode); data.insert(resetBoardKeyC, m_resetBoard); data.insert(transportLayerKeyC, m_transport); data.insert(connectUnderResetKeyC, m_connectUnderReset); - return data; } -bool StLinkUtilGdbServerProvider::fromMap(const QVariantMap &data) +void StLinkUtilGdbServerProvider::fromMap(const Store &data) { - if (!GdbServerProvider::fromMap(data)) - return false; - + GdbServerProvider::fromMap(data); m_executableFile = FilePath::fromSettings(data.value(executableFileKeyC)); m_verboseLevel = data.value(verboseLevelKeyC).toInt(); m_extendedMode = data.value(extendedModeKeyC).toBool(); @@ -137,7 +198,6 @@ bool StLinkUtilGdbServerProvider::fromMap(const QVariantMap &data) m_transport = static_cast<TransportLayer>( data.value(transportLayerKeyC).toInt()); m_connectUnderReset = data.value(connectUnderResetKeyC).toBool(); - return true; } bool StLinkUtilGdbServerProvider::operator==(const IDebugServerProvider &other) const @@ -261,22 +321,18 @@ void StLinkUtilGdbServerProviderConfigWidget::discard() GdbServerProviderConfigWidget::discard(); } -StLinkUtilGdbServerProvider::TransportLayer -StLinkUtilGdbServerProviderConfigWidget::transportLayerFromIndex(int idx) const +TransportLayer StLinkUtilGdbServerProviderConfigWidget::transportLayerFromIndex(int idx) const { - return static_cast<StLinkUtilGdbServerProvider::TransportLayer>( - m_transportLayerComboBox->itemData(idx).toInt()); + return static_cast<TransportLayer>(m_transportLayerComboBox->itemData(idx).toInt()); } -StLinkUtilGdbServerProvider::TransportLayer -StLinkUtilGdbServerProviderConfigWidget::transportLayer() const +TransportLayer StLinkUtilGdbServerProviderConfigWidget::transportLayer() const { const int idx = m_transportLayerComboBox->currentIndex(); return transportLayerFromIndex(idx); } -void StLinkUtilGdbServerProviderConfigWidget::setTransportLayer( - StLinkUtilGdbServerProvider::TransportLayer tl) +void StLinkUtilGdbServerProviderConfigWidget::setTransportLayer(TransportLayer tl) { for (int idx = 0; idx < m_transportLayerComboBox->count(); ++idx) { if (tl == transportLayerFromIndex(idx)) { @@ -290,13 +346,13 @@ void StLinkUtilGdbServerProviderConfigWidget::populateTransportLayers() { m_transportLayerComboBox->insertItem( m_transportLayerComboBox->count(), Tr::tr("ST-LINK/V1"), - StLinkUtilGdbServerProvider::ScsiOverUsb); + ScsiOverUsb); m_transportLayerComboBox->insertItem( m_transportLayerComboBox->count(), Tr::tr("ST-LINK/V2"), - StLinkUtilGdbServerProvider::RawUsb); + RawUsb); m_transportLayerComboBox->insertItem( m_transportLayerComboBox->count(), Tr::tr("Keep unspecified"), - StLinkUtilGdbServerProvider::UnspecifiedTransport); + UnspecifiedTransport); } void StLinkUtilGdbServerProviderConfigWidget::setFromProvider() diff --git a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.h b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.h index 66a3930676f..8b96150abc1 100644 --- a/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.h +++ b/src/plugins/baremetal/debugservers/gdb/stlinkutilgdbserverprovider.h @@ -5,87 +5,12 @@ #include "gdbserverprovider.h" -QT_BEGIN_NAMESPACE -class QCheckBox; -class QPlainTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - namespace BareMetal::Internal { -// StLinkUtilGdbServerProvider - -class StLinkUtilGdbServerProvider final : public GdbServerProvider -{ -public: - enum TransportLayer { ScsiOverUsb = 1, RawUsb = 2, UnspecifiedTransport }; - - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - - QString channelString() const final; - Utils::CommandLine command() const final; - - QSet<StartupMode> supportedStartupModes() const final; - bool isValid() const final; - -private: - StLinkUtilGdbServerProvider(); - - static QString defaultInitCommands(); - static QString defaultResetCommands(); - - Utils::FilePath m_executableFile = "st-util"; - int m_verboseLevel = 0; // 0..99 - bool m_extendedMode = false; // Listening for connections after disconnect - bool m_resetBoard = true; - bool m_connectUnderReset = false; // Makes it possible to connect to the device before code execution - TransportLayer m_transport = RawUsb; - - friend class StLinkUtilGdbServerProviderConfigWidget; - friend class StLinkUtilGdbServerProviderFactory; -}; - -// StLinkUtilGdbServerProviderFactory - -class StLinkUtilGdbServerProviderFactory final - : public IDebugServerProviderFactory +class StLinkUtilGdbServerProviderFactory final : public IDebugServerProviderFactory { public: StLinkUtilGdbServerProviderFactory(); }; -// StLinkUtilGdbServerProviderConfigWidget - -class StLinkUtilGdbServerProviderConfigWidget final - : public GdbServerProviderConfigWidget -{ -public: - explicit StLinkUtilGdbServerProviderConfigWidget(StLinkUtilGdbServerProvider *provider); - -private: - void apply() final; - void discard() final; - - StLinkUtilGdbServerProvider::TransportLayer transportLayerFromIndex(int idx) const; - StLinkUtilGdbServerProvider::TransportLayer transportLayer() const; - void setTransportLayer(StLinkUtilGdbServerProvider::TransportLayer); - - void populateTransportLayers(); - void setFromProvider(); - - HostWidget *m_hostWidget = nullptr; - Utils::PathChooser *m_executableFileChooser = nullptr; - QSpinBox *m_verboseLevelSpinBox = nullptr; - QCheckBox *m_extendedModeCheckBox = nullptr; - QCheckBox *m_resetOnConnectCheckBox = nullptr; - QCheckBox *m_resetBoardCheckBox = nullptr; - QComboBox *m_transportLayerComboBox = nullptr; - QPlainTextEdit *m_initCommandsTextEdit = nullptr; - QPlainTextEdit *m_resetCommandsTextEdit = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.cpp index 7bb0bec353c..f7a28b573e7 100644 --- a/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.cpp @@ -32,6 +32,24 @@ constexpr char adapterOptionsKeyC[] = "AdapterOptions"; constexpr char adapterPortKeyC[] = "AdapterPort"; constexpr char adapterSpeedKeyC[] = "AdapterSpeed"; +class JLinkUvscAdapterOptions final +{ +public: + enum Port { JTAG, SWD }; + enum Speed { + Speed_50MHz = 50000, Speed_33MHz = 33000, Speed_25MHz = 25000, + Speed_20MHz = 20000, Speed_10MHz = 10000, Speed_5MHz = 5000, + Speed_3MHz = 3000, Speed_2MHz = 2000, Speed_1MHz = 1000, + Speed_500kHz = 500, Speed_200kHz = 200, Speed_100kHz = 100, + }; + Port port = Port::SWD; + Speed speed = Speed::Speed_1MHz; + + Store toMap() const; + bool fromMap(const Store &data); + bool operator==(const JLinkUvscAdapterOptions &other) const; +}; + static int decodeSpeedCode(JLinkUvscAdapterOptions::Speed speed) { switch (speed) { @@ -102,6 +120,49 @@ static QString buildDllRegistryName(const DeviceSelection &device, return content; } +// JLinkUvscAdapterOptions + +Store JLinkUvscAdapterOptions::toMap() const +{ + Store map; + map.insert(adapterPortKeyC, port); + map.insert(adapterSpeedKeyC, speed); + return map; +} + +bool JLinkUvscAdapterOptions::fromMap(const Store &data) +{ + port = static_cast<Port>(data.value(adapterPortKeyC, SWD).toInt()); + speed = static_cast<Speed>(data.value(adapterSpeedKeyC, Speed_1MHz).toInt()); + return true; +} + +bool JLinkUvscAdapterOptions::operator==(const JLinkUvscAdapterOptions &other) const +{ + return port == other.port && speed == other.speed; +} + +// JLinkUvscServerProvider + +class JLinkUvscServerProvider final : public UvscServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + Utils::FilePath optionsFilePath(Debugger::DebuggerRunTool *runTool, + QString &errorMessage) const final; +private: + explicit JLinkUvscServerProvider(); + + JLinkUvscAdapterOptions m_adapterOpts; + + friend class JLinkUvscServerProviderConfigWidget; + friend class JLinkUvscServerProviderFactory; + friend class JLinkUvProjectOptions; +}; + // JLinkUvProjectOptions class JLinkUvProjectOptions final : public Uv::ProjectOptions @@ -126,29 +187,50 @@ public: } }; -// JLinkUvscAdapterOptions +// JLinkUvscServerProviderFactory -QVariantMap JLinkUvscAdapterOptions::toMap() const +class JLinkUvscAdapterOptionsWidget; + +class JLinkUvscServerProviderConfigWidget final : public UvscServerProviderConfigWidget { - QVariantMap map; - map.insert(adapterPortKeyC, port); - map.insert(adapterSpeedKeyC, speed); - return map; -} +public: + explicit JLinkUvscServerProviderConfigWidget(JLinkUvscServerProvider *provider); -bool JLinkUvscAdapterOptions::fromMap(const QVariantMap &data) +private: + void apply() override; + void discard() override; + + void setAdapterOpitons(const JLinkUvscAdapterOptions &adapterOpts); + JLinkUvscAdapterOptions adapterOptions() const; + void setFromProvider(); + + JLinkUvscAdapterOptionsWidget *m_adapterOptionsWidget = nullptr; +}; + +// JLinkUvscAdapterOptionsWidget + +class JLinkUvscAdapterOptionsWidget final : public QWidget { - port = static_cast<Port>(data.value(adapterPortKeyC, SWD).toInt()); - speed = static_cast<Speed>(data.value(adapterSpeedKeyC, Speed_1MHz).toInt()); - return true; -} + Q_OBJECT -bool JLinkUvscAdapterOptions::operator==(const JLinkUvscAdapterOptions &other) const -{ - return port == other.port && speed == other.speed; -} +public: + explicit JLinkUvscAdapterOptionsWidget(QWidget *parent = nullptr); + void setAdapterOptions(const JLinkUvscAdapterOptions &adapterOpts); + JLinkUvscAdapterOptions adapterOptions() const; -// JLinkUvscServerProvider +signals: + void optionsChanged(); + +private: + JLinkUvscAdapterOptions::Port portAt(int index) const; + JLinkUvscAdapterOptions::Speed speedAt(int index) const; + + void populatePorts(); + void populateSpeeds(); + + QComboBox *m_portBox = nullptr; + QComboBox *m_speedBox = nullptr; +}; JLinkUvscServerProvider::JLinkUvscServerProvider() : UvscServerProvider(Constants::UVSC_JLINK_PROVIDER_ID) @@ -158,19 +240,16 @@ JLinkUvscServerProvider::JLinkUvscServerProvider() setSupportedDrivers({"Segger\\JL2CM3.dll"}); } -QVariantMap JLinkUvscServerProvider::toMap() const +void JLinkUvscServerProvider::toMap(Store &data) const { - QVariantMap data = UvscServerProvider::toMap(); - data.insert(adapterOptionsKeyC, m_adapterOpts.toMap()); - return data; + UvscServerProvider::toMap(data); + data.insert(adapterOptionsKeyC, variantFromStore(m_adapterOpts.toMap())); } -bool JLinkUvscServerProvider::fromMap(const QVariantMap &data) +void JLinkUvscServerProvider::fromMap(const Store &data) { - if (!UvscServerProvider::fromMap(data)) - return false; - m_adapterOpts.fromMap(data.value(adapterOptionsKeyC).toMap()); - return true; + UvscServerProvider::fromMap(data); + m_adapterOpts.fromMap(storeFromVariant(data.value(adapterOptionsKeyC))); } bool JLinkUvscServerProvider::operator==(const IDebugServerProvider &other) const @@ -343,3 +422,5 @@ void JLinkUvscAdapterOptionsWidget::populateSpeeds() } } // BareMetal::Internal + +#include "jlinkuvscserverprovider.moc" diff --git a/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.h index bf2c5f295d3..110763f4334 100644 --- a/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.h +++ b/src/plugins/baremetal/debugservers/uvsc/jlinkuvscserverprovider.h @@ -5,103 +5,12 @@ #include "uvscserverprovider.h" -QT_BEGIN_NAMESPACE -class QComboBox; -QT_END_NAMESPACE - namespace BareMetal::Internal { -// JLinkUvscAdapterOptions - -class JLinkUvscAdapterOptions final -{ -public: - enum Port { JTAG, SWD }; - enum Speed { - Speed_50MHz = 50000, Speed_33MHz = 33000, Speed_25MHz = 25000, - Speed_20MHz = 20000, Speed_10MHz = 10000, Speed_5MHz = 5000, - Speed_3MHz = 3000, Speed_2MHz = 2000, Speed_1MHz = 1000, - Speed_500kHz = 500, Speed_200kHz = 200, Speed_100kHz = 100, - }; - Port port = Port::SWD; - Speed speed = Speed::Speed_1MHz; - - QVariantMap toMap() const; - bool fromMap(const QVariantMap &data); - bool operator==(const JLinkUvscAdapterOptions &other) const; -}; - -// JLinkUvscServerProvider - -class JLinkUvscServerProvider final : public UvscServerProvider -{ -public: - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - Utils::FilePath optionsFilePath(Debugger::DebuggerRunTool *runTool, - QString &errorMessage) const final; -private: - explicit JLinkUvscServerProvider(); - - JLinkUvscAdapterOptions m_adapterOpts; - - friend class JLinkUvscServerProviderConfigWidget; - friend class JLinkUvscServerProviderFactory; - friend class JLinkUvProjectOptions; -}; - -// JLinkUvscServerProviderFactory - class JLinkUvscServerProviderFactory final : public IDebugServerProviderFactory { public: JLinkUvscServerProviderFactory(); }; -// JLinkUvscServerProviderConfigWidget - -class JLinkUvscAdapterOptionsWidget; -class JLinkUvscServerProviderConfigWidget final : public UvscServerProviderConfigWidget -{ -public: - explicit JLinkUvscServerProviderConfigWidget(JLinkUvscServerProvider *provider); - -private: - void apply() override; - void discard() override; - - void setAdapterOpitons(const JLinkUvscAdapterOptions &adapterOpts); - JLinkUvscAdapterOptions adapterOptions() const; - void setFromProvider(); - - JLinkUvscAdapterOptionsWidget *m_adapterOptionsWidget = nullptr; -}; - -// JLinkUvscAdapterOptionsWidget - -class JLinkUvscAdapterOptionsWidget final : public QWidget -{ - Q_OBJECT - -public: - explicit JLinkUvscAdapterOptionsWidget(QWidget *parent = nullptr); - void setAdapterOptions(const JLinkUvscAdapterOptions &adapterOpts); - JLinkUvscAdapterOptions adapterOptions() const; - -signals: - void optionsChanged(); - -private: - JLinkUvscAdapterOptions::Port portAt(int index) const; - JLinkUvscAdapterOptions::Speed speedAt(int index) const; - - void populatePorts(); - void populateSpeeds(); - - QComboBox *m_portBox = nullptr; - QComboBox *m_speedBox = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.cpp index d7eba5ebfd1..57a52d2e850 100644 --- a/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.cpp @@ -63,19 +63,16 @@ SimulatorUvscServerProvider::SimulatorUvscServerProvider() setDriverSelection(defaultSimulatorDriverSelection()); } -QVariantMap SimulatorUvscServerProvider::toMap() const +void SimulatorUvscServerProvider::toMap(Store &data) const { - QVariantMap data = UvscServerProvider::toMap(); + UvscServerProvider::toMap(data); data.insert(limitSpeedKeyC, m_limitSpeed); - return data; } -bool SimulatorUvscServerProvider::fromMap(const QVariantMap &data) +void SimulatorUvscServerProvider::fromMap(const Store &data) { - if (!UvscServerProvider::fromMap(data)) - return false; + UvscServerProvider::fromMap(data); m_limitSpeed = data.value(limitSpeedKeyC).toBool(); - return true; } bool SimulatorUvscServerProvider::operator==(const IDebugServerProvider &other) const diff --git a/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.h index e08a02d741a..ea30b1dda1e 100644 --- a/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.h +++ b/src/plugins/baremetal/debugservers/uvsc/simulatoruvscserverprovider.h @@ -16,8 +16,8 @@ namespace BareMetal::Internal { class SimulatorUvscServerProvider final : public UvscServerProvider { public: - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; + void toMap(Utils::Store &data) const final; + void fromMap(const Utils::Store &data) final; bool operator==(const IDebugServerProvider &other) const final; bool isSimulator() const final { return true; } diff --git a/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.cpp index 17fcd7f0e77..a91e236b9dd 100644 --- a/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.cpp @@ -32,6 +32,29 @@ constexpr char adapterOptionsKeyC[] = "AdapterOptions"; constexpr char adapterPortKeyC[] = "AdapterPort"; constexpr char adapterSpeedKeyC[] = "AdapterSpeed"; +// StLinkUvscAdapterOptions + +class StLinkUvscAdapterOptions final +{ +public: + enum Port { JTAG, SWD }; + enum Speed { + // SWD speeds. + Speed_4MHz = 0, Speed_1_8MHz, Speed_950kHz, Speed_480kHz, + Speed_240kHz, Speed_125kHz, Speed_100kHz, Speed_50kHz, + Speed_25kHz, Speed_15kHz, Speed_5kHz, + // JTAG speeds. + Speed_9MHz = 256, Speed_4_5MHz, Speed_2_25MHz, Speed_1_12MHz, + Speed_560kHz, Speed_280kHz, Speed_140kHz, + }; + Port port = Port::SWD; + Speed speed = Speed::Speed_4MHz; + + QVariantMap toMap() const; + bool fromMap(const Store &data); + bool operator==(const StLinkUvscAdapterOptions &other) const; +}; + static QString buildAdapterOptions(const StLinkUvscAdapterOptions &opts) { QString s; @@ -58,6 +81,27 @@ static QString buildDllRegistryName(const DeviceSelection &device, .arg(path.fileName(), flashStart, flashSize, device.name, path.filePath(), adaptOpts); } +// StLinkUvscServerProvider + +class StLinkUvscServerProvider final : public UvscServerProvider +{ +public: + void toMap(Store &data) const final; + void fromMap(const Store &data) final; + + bool operator==(const IDebugServerProvider &other) const final; + Utils::FilePath optionsFilePath(Debugger::DebuggerRunTool *runTool, + QString &errorMessage) const final; +private: + explicit StLinkUvscServerProvider(); + + StLinkUvscAdapterOptions m_adapterOpts; + + friend class StLinkUvscServerProviderConfigWidget; + friend class StLinkUvscServerProviderFactory; + friend class StLinkUvProjectOptions; +}; + // StLinkUvProjectOptions class StLinkUvProjectOptions final : public Uv::ProjectOptions @@ -82,8 +126,6 @@ public: } }; -// StLinkUvscAdapterOptions - QVariantMap StLinkUvscAdapterOptions::toMap() const { QVariantMap map; @@ -92,7 +134,7 @@ QVariantMap StLinkUvscAdapterOptions::toMap() const return map; } -bool StLinkUvscAdapterOptions::fromMap(const QVariantMap &data) +bool StLinkUvscAdapterOptions::fromMap(const Store &data) { port = static_cast<Port>(data.value(adapterPortKeyC, SWD).toInt()); speed = static_cast<Speed>(data.value(adapterSpeedKeyC, Speed_4MHz).toInt()); @@ -104,6 +146,50 @@ bool StLinkUvscAdapterOptions::operator==(const StLinkUvscAdapterOptions &other) return port == other.port && speed == other.speed; } +// StLinkUvscServerProviderConfigWidget + +class StLinkUvscAdapterOptionsWidget; +class StLinkUvscServerProviderConfigWidget final : public UvscServerProviderConfigWidget +{ +public: + explicit StLinkUvscServerProviderConfigWidget(StLinkUvscServerProvider *provider); + +private: + void apply() override; + void discard() override; + + void setAdapterOpitons(const StLinkUvscAdapterOptions &adapterOpts); + StLinkUvscAdapterOptions adapterOptions() const; + void setFromProvider(); + + StLinkUvscAdapterOptionsWidget *m_adapterOptionsWidget = nullptr; +}; + +// StLinkUvscAdapterOptionsWidget + +class StLinkUvscAdapterOptionsWidget final : public QWidget +{ + Q_OBJECT + +public: + explicit StLinkUvscAdapterOptionsWidget(QWidget *parent = nullptr); + void setAdapterOptions(const StLinkUvscAdapterOptions &adapterOpts); + StLinkUvscAdapterOptions adapterOptions() const; + +signals: + void optionsChanged(); + +private: + StLinkUvscAdapterOptions::Port portAt(int index) const; + StLinkUvscAdapterOptions::Speed speedAt(int index) const; + + void populatePorts(); + void populateSpeeds(); + + QComboBox *m_portBox = nullptr; + QComboBox *m_speedBox = nullptr; +}; + // StLinkUvscServerProvider StLinkUvscServerProvider::StLinkUvscServerProvider() @@ -114,19 +200,16 @@ StLinkUvscServerProvider::StLinkUvscServerProvider() setSupportedDrivers({"STLink\\ST-LINKIII-KEIL_SWO.dll"}); } -QVariantMap StLinkUvscServerProvider::toMap() const +void StLinkUvscServerProvider::toMap(Store &data) const { - QVariantMap data = UvscServerProvider::toMap(); + UvscServerProvider::toMap(data); data.insert(adapterOptionsKeyC, m_adapterOpts.toMap()); - return data; } -bool StLinkUvscServerProvider::fromMap(const QVariantMap &data) +void StLinkUvscServerProvider::fromMap(const Store &data) { - if (!UvscServerProvider::fromMap(data)) - return false; - m_adapterOpts.fromMap(data.value(adapterOptionsKeyC).toMap()); - return true; + UvscServerProvider::fromMap(data); + m_adapterOpts.fromMap(storeFromVariant(data.value(adapterOptionsKeyC))); } bool StLinkUvscServerProvider::operator==(const IDebugServerProvider &other) const @@ -310,3 +393,5 @@ void StLinkUvscAdapterOptionsWidget::populateSpeeds() } } // BareMetal::Internal + +#include "stlinkuvscserverprovider.moc" diff --git a/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.h index 26614c29bfb..c6f2a89aa3d 100644 --- a/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.h +++ b/src/plugins/baremetal/debugservers/uvsc/stlinkuvscserverprovider.h @@ -5,106 +5,12 @@ #include "uvscserverprovider.h" -QT_BEGIN_NAMESPACE -class QComboBox; -QT_END_NAMESPACE - namespace BareMetal::Internal { -// StLinkUvscAdapterOptions - -class StLinkUvscAdapterOptions final -{ -public: - enum Port { JTAG, SWD }; - enum Speed { - // SWD speeds. - Speed_4MHz = 0, Speed_1_8MHz, Speed_950kHz, Speed_480kHz, - Speed_240kHz, Speed_125kHz, Speed_100kHz, Speed_50kHz, - Speed_25kHz, Speed_15kHz, Speed_5kHz, - // JTAG speeds. - Speed_9MHz = 256, Speed_4_5MHz, Speed_2_25MHz, Speed_1_12MHz, - Speed_560kHz, Speed_280kHz, Speed_140kHz, - }; - Port port = Port::SWD; - Speed speed = Speed::Speed_4MHz; - - QVariantMap toMap() const; - bool fromMap(const QVariantMap &data); - bool operator==(const StLinkUvscAdapterOptions &other) const; -}; - -// StLinkUvscServerProvider - -class StLinkUvscServerProvider final : public UvscServerProvider -{ -public: - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - bool operator==(const IDebugServerProvider &other) const final; - Utils::FilePath optionsFilePath(Debugger::DebuggerRunTool *runTool, - QString &errorMessage) const final; -private: - explicit StLinkUvscServerProvider(); - - StLinkUvscAdapterOptions m_adapterOpts; - - friend class StLinkUvscServerProviderConfigWidget; - friend class StLinkUvscServerProviderFactory; - friend class StLinkUvProjectOptions; -}; - -// StLinkUvscServerProviderFactory - class StLinkUvscServerProviderFactory final : public IDebugServerProviderFactory { public: StLinkUvscServerProviderFactory(); }; -// StLinkUvscServerProviderConfigWidget - -class StLinkUvscAdapterOptionsWidget; -class StLinkUvscServerProviderConfigWidget final : public UvscServerProviderConfigWidget -{ -public: - explicit StLinkUvscServerProviderConfigWidget(StLinkUvscServerProvider *provider); - -private: - void apply() override; - void discard() override; - - void setAdapterOpitons(const StLinkUvscAdapterOptions &adapterOpts); - StLinkUvscAdapterOptions adapterOptions() const; - void setFromProvider(); - - StLinkUvscAdapterOptionsWidget *m_adapterOptionsWidget = nullptr; -}; - -// StLinkUvscAdapterOptionsWidget - -class StLinkUvscAdapterOptionsWidget final : public QWidget -{ - Q_OBJECT - -public: - explicit StLinkUvscAdapterOptionsWidget(QWidget *parent = nullptr); - void setAdapterOptions(const StLinkUvscAdapterOptions &adapterOpts); - StLinkUvscAdapterOptions adapterOptions() const; - -signals: - void optionsChanged(); - -private: - StLinkUvscAdapterOptions::Port portAt(int index) const; - StLinkUvscAdapterOptions::Speed speedAt(int index) const; - - void populatePorts(); - void populateSpeeds(); - - QComboBox *m_portBox = nullptr; - QComboBox *m_speedBox = nullptr; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp index faa32fea086..abe6fecdc3c 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvproject.cpp @@ -6,7 +6,7 @@ #include <cppeditor/cppmodelmanager.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerruncontrol.h> #include <projectexplorer/projectmanager.h> @@ -65,7 +65,7 @@ static void extractAllFiles(const DebuggerRunTool *runTool, QStringList &include FilePaths &headers, FilePaths &sources, FilePaths &assemblers) { const auto project = runTool->runControl()->project(); - const CppEditor::ProjectInfo::ConstPtr info = CppModelManager::instance()->projectInfo(project); + const CppEditor::ProjectInfo::ConstPtr info = CppModelManager::projectInfo(project); if (!info) return; const QVector<ProjectPart::ConstPtr> parts = info->projectParts(); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp index a266ed7bfdc..f77a0ce0cfd 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp @@ -13,7 +13,7 @@ #include <baremetal/baremetaltr.h> #include <baremetal/debugserverprovidermanager.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerruncontrol.h> #include <projectexplorer/project.h> @@ -147,13 +147,12 @@ FilePath UvscServerProvider::buildOptionsFilePath(DebuggerRunTool *runTool) cons return path; } -QVariantMap UvscServerProvider::toMap() const +void UvscServerProvider::toMap(Store &data) const { - QVariantMap data = IDebugServerProvider::toMap(); + IDebugServerProvider::toMap(data); data.insert(toolsIniKeyC, m_toolsIniFile.toSettings()); - data.insert(deviceSelectionKeyC, m_deviceSelection.toMap()); - data.insert(driverSelectionKeyC, m_driverSelection.toMap()); - return data; + data.insert(deviceSelectionKeyC, variantFromStore(m_deviceSelection.toMap())); + data.insert(driverSelectionKeyC, variantFromStore(m_driverSelection.toMap())); } bool UvscServerProvider::isValid() const @@ -193,7 +192,7 @@ bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMess const FilePath peripheralDescriptionFile = FilePath::fromString(m_deviceSelection.svd); - Runnable inferior; + ProcessRunData inferior; inferior.command.setExecutable(bin); runTool->runParameters().peripheralDescriptionFile = peripheralDescriptionFile; runTool->runParameters().uVisionProjectFilePath = projFilePath; @@ -210,24 +209,22 @@ bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMess ProjectExplorer::RunWorker *UvscServerProvider::targetRunner(RunControl *runControl) const { // Get uVision executable path. - const Runnable uv = DebuggerKitAspect::runnable(runControl->kit()); + const ProcessRunData uv = DebuggerKitAspect::runnable(runControl->kit()); CommandLine server(uv.command.executable()); server.addArg("-j0"); server.addArg(QStringLiteral("-s%1").arg(m_channel.port())); - Runnable r; + ProcessRunData r; r.command = server; return new UvscServerProviderRunner(runControl, r); } -bool UvscServerProvider::fromMap(const QVariantMap &data) +void UvscServerProvider::fromMap(const Store &data) { - if (!IDebugServerProvider::fromMap(data)) - return false; + IDebugServerProvider::fromMap(data); m_toolsIniFile = FilePath::fromSettings(data.value(toolsIniKeyC)); - m_deviceSelection.fromMap(data.value(deviceSelectionKeyC).toMap()); - m_driverSelection.fromMap(data.value(driverSelectionKeyC).toMap()); - return true; + m_deviceSelection.fromMap(storeFromVariant(data.value(deviceSelectionKeyC))); + m_driverSelection.fromMap(storeFromVariant(data.value(driverSelectionKeyC))); } FilePath UvscServerProvider::projectFilePath(DebuggerRunTool *runTool, QString &errorMessage) const @@ -351,7 +348,7 @@ void UvscServerProviderConfigWidget::setFromProvider() // UvscServerProviderRunner UvscServerProviderRunner::UvscServerProviderRunner(ProjectExplorer::RunControl *runControl, - const Runnable &runnable) + const ProcessRunData &runnable) : RunWorker(runControl) { setId("BareMetalUvscServer"); @@ -371,8 +368,7 @@ UvscServerProviderRunner::UvscServerProviderRunner(ProjectExplorer::RunControl * void UvscServerProviderRunner::start() { - const QString msg = Tr::tr("Starting %1 ...") - .arg(m_process.commandLine().displayName()); + const QString msg = Tr::tr("Starting %1...").arg(m_process.commandLine().displayName()); appendMessage(msg, NormalMessageFormat); m_process.start(); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h index 4c14ee7b7c1..2cce62f6c18 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.h @@ -48,7 +48,7 @@ public: bool operator==(const IDebugServerProvider &other) const override; - QVariantMap toMap() const override; + void toMap(Utils::Store &map) const override; bool aboutToRun(Debugger::DebuggerRunTool *runTool, QString &errorMessage) const final; ProjectExplorer::RunWorker *targetRunner(ProjectExplorer::RunControl *runControl) const final; @@ -69,7 +69,7 @@ protected: Utils::FilePath buildProjectFilePath(Debugger::DebuggerRunTool *runTool) const; Utils::FilePath buildOptionsFilePath(Debugger::DebuggerRunTool *runTool) const; - bool fromMap(const QVariantMap &data) override; + void fromMap(const Utils::Store &data) override; // uVision specific stuff. virtual Utils::FilePath projectFilePath(Debugger::DebuggerRunTool *runTool, @@ -121,7 +121,7 @@ class UvscServerProviderRunner final : public ProjectExplorer::RunWorker { public: explicit UvscServerProviderRunner(ProjectExplorer::RunControl *runControl, - const ProjectExplorer::Runnable &runnable); + const Utils::ProcessRunData &runnable); private: void start() final; diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.cpp b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.cpp index b41f835c7b5..2c87aa5db15 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.cpp @@ -52,9 +52,9 @@ constexpr char deviceAlgorithmIndexKeyC[] = "DeviceAlgorithmIndex"; // DeviceSelection -QVariantMap DeviceSelection::toMap() const +Store DeviceSelection::toMap() const { - QVariantMap map; + Store map; // Software package. map.insert(packageDescrKeyC, package.desc); map.insert(packageFileKeyC, package.file); @@ -79,30 +79,30 @@ QVariantMap DeviceSelection::toMap() const // Device MEMORY. QVariantList memoryList; for (const DeviceSelection::Memory &memory : std::as_const(memories)) { - QVariantMap m; + Store m; m.insert(deviceMemoryIdKeyC, memory.id); m.insert(deviceMemoryStartKeyC, memory.start); m.insert(deviceMemorySizeKeyC, memory.size); - memoryList.push_back(m); + memoryList.push_back(variantFromStore(m)); } map.insert(deviceMemoryKeyC, memoryList); // Device ALGORITHM. QVariantList algorithmList; for (const DeviceSelection::Algorithm &algorithm : std::as_const(algorithms)) { - QVariantMap m; + Store m; m.insert(deviceAlgorithmPathKeyC, algorithm.path); m.insert(deviceAlgorithmFlashStartKeyC, algorithm.flashStart); m.insert(deviceAlgorithmFlashSizeKeyC, algorithm.flashSize); m.insert(deviceAlgorithmRamStartKeyC, algorithm.ramStart); m.insert(deviceAlgorithmRamSizeKeyC, algorithm.ramSize); - algorithmList.push_back(m); + algorithmList.push_back(variantFromStore(m)); } map.insert(deviceAlgorithmKeyC, algorithmList); map.insert(deviceAlgorithmIndexKeyC, algorithmIndex); return map; } -void DeviceSelection::fromMap(const QVariantMap &map) +void DeviceSelection::fromMap(const Store &map) { // Software package. package.desc = map.value(packageDescrKeyC).toString(); @@ -264,6 +264,7 @@ void DeviceSelectionMemoryModel::refresh() DeviceSelectionMemoryView::DeviceSelectionMemoryView(DeviceSelection &selection, QWidget *parent) : TreeView(parent) { + setUniformRowHeights(false); setRootIsDecorated(true); setSelectionMode(QAbstractItemView::SingleSelection); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.h b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.h index cee1876f6d8..f750b3847fd 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.h +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdeviceselection.h @@ -5,6 +5,7 @@ #include <utils/basetreeview.h> #include <utils/treemodel.h> +#include <utils/store.h> QT_BEGIN_NAMESPACE class QComboBox; @@ -71,8 +72,8 @@ public: Algorithms algorithms; int algorithmIndex = 0; - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool operator==(const DeviceSelection &other) const; }; diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.cpp b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.cpp index 5b403737f0a..2abe09305cc 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.cpp @@ -22,9 +22,9 @@ constexpr char driverNameKeyC[] = "DriverName"; // DriverSelection -QVariantMap DriverSelection::toMap() const +Store DriverSelection::toMap() const { - QVariantMap map; + Store map; map.insert(driverIndexKeyC, index); map.insert(driverCpuDllIndexKeyC, cpuDllIndex); map.insert(driverDllKeyC, dll); @@ -33,7 +33,7 @@ QVariantMap DriverSelection::toMap() const return map; } -void DriverSelection::fromMap(const QVariantMap &map) +void DriverSelection::fromMap(const Store &map) { index = map.value(driverIndexKeyC).toInt(); cpuDllIndex = map.value(driverCpuDllIndexKeyC).toInt(); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.h b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.h index 65bcd52d80d..8362e58fc1a 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.h +++ b/src/plugins/baremetal/debugservers/uvsc/uvtargetdriverselection.h @@ -5,6 +5,7 @@ #include <utils/basetreeview.h> #include <utils/treemodel.h> +#include <utils/store.h> QT_BEGIN_NAMESPACE class QComboBox; @@ -23,8 +24,8 @@ public: int index = 0; int cpuDllIndex = 0; - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool operator==(const DriverSelection &other) const; }; diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index c6653fefbb4..262516b4b35 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -10,6 +10,7 @@ #include <projectexplorer/abiwidget.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmacro.h> +#include <projectexplorer/toolchainconfigwidget.h> #include <projectexplorer/toolchainmanager.h> #include <utils/algorithm.h> @@ -33,16 +34,6 @@ using namespace Utils; namespace BareMetal::Internal { -// Helpers: - -static const char compilerPlatformCodeGenFlagsKeyC[] = "PlatformCodeGenFlags"; - -static bool compilerExists(const FilePath &compilerPath) -{ - const QFileInfo fi = compilerPath.toFileInfo(); - return fi.exists() && fi.isExecutable() && fi.isFile(); -} - static QString cppLanguageOption(const FilePath &compiler) { const QString baseName = compiler.baseName(); @@ -262,15 +253,70 @@ static QString buildDisplayName(Abi::Architecture arch, Utils::Id language, return Tr::tr("IAREW %1 (%2, %3)").arg(version, langName, archName); } +// IarToolChainConfigWidget + +class IarToolChain; + +class IarToolChainConfigWidget final : public ToolChainConfigWidget +{ +public: + explicit IarToolChainConfigWidget(IarToolChain *tc); + +private: + void applyImpl() final; + void discardImpl() final { setFromToolchain(); } + bool isDirtyImpl() const final; + void makeReadOnlyImpl() final; + + void setFromToolchain(); + void handleCompilerCommandChange(); + void handlePlatformCodeGenFlagsChange(); + + PathChooser *m_compilerCommand = nullptr; + AbiWidget *m_abiWidget = nullptr; + QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; + Macros m_macros; +}; + // IarToolChain -IarToolChain::IarToolChain() : - ToolChain(Constants::IAREW_TOOLCHAIN_TYPEID) +class IarToolChain final : public ToolChain { - setTypeDisplayName(Tr::tr("IAREW")); - setTargetAbiKey("TargetAbi"); - setCompilerCommandKey("CompilerPath"); -} +public: + IarToolChain() : ToolChain(Constants::IAREW_TOOLCHAIN_TYPEID) + { + setTypeDisplayName(Tr::tr("IAREW")); + setTargetAbiKey("TargetAbi"); + setCompilerCommandKey("CompilerPath"); + + m_extraCodeModelFlags.setSettingsKey("PlatformCodeGenFlags"); + connect(&m_extraCodeModelFlags, &BaseAspect::changed, + this, &IarToolChain::toolChainUpdated); + } + + MacroInspectionRunner createMacroInspectionRunner() const final; + + LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; + WarningFlags warningFlags(const QStringList &cxxflags) const final; + + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Environment &) const final; + void addToEnvironment(Environment &env) const final; + QList<OutputLineParser *> createOutputParsers() const final { return {new IarParser()}; } + + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() final; + + bool operator==(const ToolChain &other) const final; + + QStringList extraCodeModelFlags() const final { return m_extraCodeModelFlags(); } + + FilePath makeCommand(const Environment &) const final { return {}; } + +private: + StringListAspect m_extraCodeModelFlags{this}; + + friend class IarToolChainFactory; + friend class IarToolChainConfigWidget; +}; ToolChain::MacroInspectionRunner IarToolChain::createMacroInspectionRunner() const { @@ -279,7 +325,7 @@ ToolChain::MacroInspectionRunner IarToolChain::createMacroInspectionRunner() con const FilePath compiler = compilerCommand(); const Id languageId = language(); - const QStringList extraArgs = m_extraCodeModelFlags; + const QStringList extraArgs = m_extraCodeModelFlags(); MacrosCache macrosCache = predefinedMacrosCache(); return [env, compiler, extraArgs, macrosCache, languageId] @@ -343,26 +389,6 @@ void IarToolChain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -QList<Utils::OutputLineParser *> IarToolChain::createOutputParsers() const -{ - return {new IarParser()}; -} - -QVariantMap IarToolChain::toMap() const -{ - QVariantMap data = ToolChain::toMap(); - data.insert(compilerPlatformCodeGenFlagsKeyC, m_extraCodeModelFlags); - return data; -} - -bool IarToolChain::fromMap(const QVariantMap &data) -{ - if (!ToolChain::fromMap(data)) - return false; - m_extraCodeModelFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList(); - return true; -} - std::unique_ptr<ToolChainConfigWidget> IarToolChain::createConfigurationWidget() { return std::make_unique<IarToolChainConfigWidget>(this); @@ -375,27 +401,10 @@ bool IarToolChain::operator==(const ToolChain &other) const const auto customTc = static_cast<const IarToolChain *>(&other); return compilerCommand() == customTc->compilerCommand() - && m_extraCodeModelFlags == customTc->m_extraCodeModelFlags; + && m_extraCodeModelFlags() == customTc->m_extraCodeModelFlags(); } -void IarToolChain::setExtraCodeModelFlags(const QStringList &flags) -{ - if (flags == m_extraCodeModelFlags) - return; - m_extraCodeModelFlags = flags; - toolChainUpdated(); -} -QStringList IarToolChain::extraCodeModelFlags() const -{ - return m_extraCodeModelFlags; -} - -FilePath IarToolChain::makeCommand(const Environment &env) const -{ - Q_UNUSED(env) - return {}; -} // IarToolChainFactory @@ -463,7 +472,7 @@ Toolchains IarToolChainFactory::autoDetect(const ToolchainDetector &detector) co // Build full compiler path. compilerPath += entry.subExePath; const FilePath fn = FilePath::fromUserInput(compilerPath); - if (compilerExists(fn)) { + if (fn.isExecutableFile()) { // Note: threeLevelKey is a guessed toolchain version. candidates.push_back({fn, threeLevelKey}); } @@ -574,7 +583,9 @@ void IarToolChainConfigWidget::applyImpl() const auto tc = static_cast<IarToolChain *>(toolChain()); const QString displayName = tc->displayName(); tc->setCompilerCommand(m_compilerCommand->filePath()); - tc->setExtraCodeModelFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); + + tc->m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); + tc->setTargetAbi(m_abiWidget->currentAbi()); tc->setDisplayName(displayName); @@ -610,14 +621,14 @@ void IarToolChainConfigWidget::setFromToolchain() m_compilerCommand->setFilePath(tc->compilerCommand()); m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = compilerExists(m_compilerCommand->filePath()); + const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); } void IarToolChainConfigWidget::handleCompilerCommandChange() { const FilePath compilerPath = m_compilerCommand->filePath(); - const bool haveCompiler = compilerExists(compilerPath); + const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); const QStringList extraArgs = splitString(m_platformCodeGenFlagsLineEdit->text()); diff --git a/src/plugins/baremetal/iarewtoolchain.h b/src/plugins/baremetal/iarewtoolchain.h index d342c934e49..1b67e9e051f 100644 --- a/src/plugins/baremetal/iarewtoolchain.h +++ b/src/plugins/baremetal/iarewtoolchain.h @@ -3,63 +3,10 @@ #pragma once -#include <projectexplorer/abi.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainconfigwidget.h> - -QT_BEGIN_NAMESPACE -class QLineEdit; -class QPlainTextEdit; -class QPushButton; -class QTextEdit; -QT_END_NAMESPACE - -namespace Utils { -class FilePath; -class PathChooser; -} - -namespace ProjectExplorer { class AbiWidget; } namespace BareMetal::Internal { -// IarToolChain - -class IarToolChain final : public ProjectExplorer::ToolChain -{ -public: - MacroInspectionRunner createMacroInspectionRunner() const final; - - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; - Utils::WarningFlags warningFlags(const QStringList &cxxflags) const final; - - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &) const final; - void addToEnvironment(Utils::Environment &env) const final; - QList<Utils::OutputLineParser *> createOutputParsers() const final; - - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; - - bool operator ==(const ToolChain &other) const final; - - void setExtraCodeModelFlags(const QStringList &flags); - QStringList extraCodeModelFlags() const final; - - Utils::FilePath makeCommand(const Utils::Environment &env) const final; - -private: - IarToolChain(); - - QStringList m_extraCodeModelFlags; - - friend class IarToolChainFactory; - friend class IarToolChainConfigWidget; -}; - -// IarToolChainFactory - class IarToolChainFactory final : public ProjectExplorer::ToolChainFactory { public: @@ -77,29 +24,4 @@ private: const Candidate &candidate, Utils::Id languageId) const; }; -// IarToolChainConfigWidget - -class IarToolChainConfigWidget final : public ProjectExplorer::ToolChainConfigWidget -{ - Q_OBJECT - -public: - explicit IarToolChainConfigWidget(IarToolChain *tc); - -private: - void applyImpl() final; - void discardImpl() final { setFromToolchain(); } - bool isDirtyImpl() const final; - void makeReadOnlyImpl() final; - - void setFromToolchain(); - void handleCompilerCommandChange(); - void handlePlatformCodeGenFlagsChange(); - - Utils::PathChooser *m_compilerCommand = nullptr; - ProjectExplorer::AbiWidget *m_abiWidget = nullptr; - QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; - ProjectExplorer::Macros m_macros; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/idebugserverprovider.cpp b/src/plugins/baremetal/idebugserverprovider.cpp index 80a733c1115..8725b5eb316 100644 --- a/src/plugins/baremetal/idebugserverprovider.cpp +++ b/src/plugins/baremetal/idebugserverprovider.cpp @@ -18,6 +18,7 @@ using namespace Debugger; using namespace ProjectExplorer; +using namespace Utils; namespace BareMetal::Internal { @@ -137,15 +138,13 @@ IDebugServerProviderConfigWidget *IDebugServerProvider::configurationWidget() co return m_configurationWidgetCreator(); } -QVariantMap IDebugServerProvider::toMap() const +void IDebugServerProvider::toMap(Store &data) const { - return { - {idKeyC, m_id}, - {displayNameKeyC, m_displayName}, - {engineTypeKeyC, m_engineType}, - {hostKeyC, m_channel.host()}, - {portKeyC, m_channel.port()}, - }; + data.insert(idKeyC, m_id); + data.insert(displayNameKeyC, m_displayName); + data.insert(engineTypeKeyC, m_engineType); + data.insert(hostKeyC, m_channel.host()); + data.insert(portKeyC, m_channel.port()); } void IDebugServerProvider::registerDevice(BareMetalDevice *device) @@ -168,7 +167,7 @@ void IDebugServerProvider::resetId() m_id = createId(m_id); } -bool IDebugServerProvider::fromMap(const QVariantMap &data) +void IDebugServerProvider::fromMap(const Store &data) { m_id = data.value(idKeyC).toString(); m_displayName = data.value(displayNameKeyC).toString(); @@ -176,7 +175,6 @@ bool IDebugServerProvider::fromMap(const QVariantMap &data) data.value(engineTypeKeyC, NoEngineType).toInt()); m_channel.setHost(data.value(hostKeyC).toString()); m_channel.setPort(data.value(portKeyC).toInt()); - return true; } void IDebugServerProvider::setConfigurationWidgetCreator(const std::function<IDebugServerProviderConfigWidget *()> &configurationWidgetCreator) @@ -208,16 +206,14 @@ IDebugServerProvider *IDebugServerProviderFactory::create() const return m_creator(); } -IDebugServerProvider *IDebugServerProviderFactory::restore(const QVariantMap &data) const +IDebugServerProvider *IDebugServerProviderFactory::restore(const Store &data) const { IDebugServerProvider *p = m_creator(); - if (p->fromMap(data)) - return p; - delete p; - return nullptr; + p->fromMap(data); + return p; } -bool IDebugServerProviderFactory::canRestore(const QVariantMap &data) const +bool IDebugServerProviderFactory::canRestore(const Store &data) const { const QString id = idFromMap(data); return id.startsWith(m_id + ':'); @@ -233,12 +229,12 @@ void IDebugServerProviderFactory::setCreator(const std::function<IDebugServerPro m_creator = creator; } -QString IDebugServerProviderFactory::idFromMap(const QVariantMap &data) +QString IDebugServerProviderFactory::idFromMap(const Store &data) { return data.value(idKeyC).toString(); } -void IDebugServerProviderFactory::idToMap(QVariantMap &data, const QString &id) +void IDebugServerProviderFactory::idToMap(Store &data, const QString &id) { data.insert(idKeyC, id); } diff --git a/src/plugins/baremetal/idebugserverprovider.h b/src/plugins/baremetal/idebugserverprovider.h index a936ad61421..12b5fe7d10f 100644 --- a/src/plugins/baremetal/idebugserverprovider.h +++ b/src/plugins/baremetal/idebugserverprovider.h @@ -4,12 +4,14 @@ #pragma once #include <debugger/debuggerconstants.h> -#include <projectexplorer/abi.h> -#include <utils/fileutils.h> -#include <QObject> +#include <projectexplorer/abi.h> + +#include <utils/filepath.h> +#include <utils/store.h> + #include <QSet> -#include <QVariantMap> +#include <QUrl> #include <QWidget> QT_BEGIN_NAMESPACE @@ -65,8 +67,8 @@ public: void setConfigurationWidgetCreator (const std::function<IDebugServerProviderConfigWidget *()> &configurationWidgetCreator); - virtual QVariantMap toMap() const; - virtual bool fromMap(const QVariantMap &data); + virtual void toMap(Utils::Store &data) const; + virtual void fromMap(const Utils::Store &data); virtual bool aboutToRun(Debugger::DebuggerRunTool *runTool, QString &errorMessage) const = 0; @@ -108,12 +110,12 @@ public: QString displayName() const; IDebugServerProvider *create() const; - IDebugServerProvider *restore(const QVariantMap &data) const; + IDebugServerProvider *restore(const Utils::Store &data) const; - bool canRestore(const QVariantMap &data) const; + bool canRestore(const Utils::Store &data) const; - static QString idFromMap(const QVariantMap &data); - static void idToMap(QVariantMap &data, const QString &id); + static QString idFromMap(const Utils::Store &data); + static void idToMap(Utils::Store &data, const QString &id); protected: IDebugServerProviderFactory(); diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index 3bb2423ab23..d39dc9f03b0 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -10,6 +10,7 @@ #include <projectexplorer/abiwidget.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmacro.h> +#include <projectexplorer/toolchainconfigwidget.h> #include <projectexplorer/toolchainmanager.h> #include <utils/algorithm.h> @@ -35,16 +36,6 @@ using namespace Utils; namespace BareMetal::Internal { -// Helpers: - -static const char compilerPlatformCodeGenFlagsKeyC[] = "PlatformCodeGenFlags"; - -static bool compilerExists(const FilePath &compilerPath) -{ - const QFileInfo fi = compilerPath.toFileInfo(); - return fi.exists() && fi.isExecutable() && fi.isFile(); -} - static Abi::Architecture guessArchitecture(const FilePath &compilerPath) { const QFileInfo fi = compilerPath.toFileInfo(); @@ -402,13 +393,70 @@ static void addDefaultCpuArgs(const FilePath &compiler, QStringList &extraArgs) // KeilToolchain -KeilToolChain::KeilToolChain() : - ToolChain(Constants::KEIL_TOOLCHAIN_TYPEID) +class KeilToolChain; + +class KeilToolChainConfigWidget final : public ToolChainConfigWidget { - setTypeDisplayName(Tr::tr("KEIL")); - setTargetAbiKey("TargetAbi"); - setCompilerCommandKey("CompilerPath"); -} +public: + explicit KeilToolChainConfigWidget(KeilToolChain *tc); + +private: + void applyImpl() final; + void discardImpl() final { setFromToolChain(); } + bool isDirtyImpl() const final; + void makeReadOnlyImpl() final; + + void setFromToolChain(); + void handleCompilerCommandChange(); + void handlePlatformCodeGenFlagsChange(); + + PathChooser *m_compilerCommand = nullptr; + AbiWidget *m_abiWidget = nullptr; + QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; + Macros m_macros; +}; + +// KeilToolChain + +class KeilToolChain final : public ToolChain +{ +public: + KeilToolChain() : + ToolChain(Constants::KEIL_TOOLCHAIN_TYPEID) + { + setTypeDisplayName(Tr::tr("KEIL")); + setTargetAbiKey("TargetAbi"); + setCompilerCommandKey("CompilerPath"); + + m_extraCodeModelFlags.setSettingsKey("PlatformCodeGenFlags"); + connect(&m_extraCodeModelFlags, &BaseAspect::changed, + this, &KeilToolChain::toolChainUpdated); + } + + MacroInspectionRunner createMacroInspectionRunner() const final; + + LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; + WarningFlags warningFlags(const QStringList &cxxflags) const final; + + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Environment &) const final; + void addToEnvironment(Environment &env) const final; + + QList<OutputLineParser *> createOutputParsers() const final { return {new KeilParser}; } + + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() final; + + bool operator==(const ToolChain &other) const final; + + QStringList extraCodeModelFlags() const final; + + FilePath makeCommand(const Environment &) const final { return {}; } + +private: + StringListAspect m_extraCodeModelFlags{this}; + + friend class KeilToolChainFactory; + friend class KeilToolChainConfigWidget; +}; ToolChain::MacroInspectionRunner KeilToolChain::createMacroInspectionRunner() const { @@ -419,7 +467,7 @@ ToolChain::MacroInspectionRunner KeilToolChain::createMacroInspectionRunner() co const Id lang = language(); MacrosCache macroCache = predefinedMacrosCache(); - const QStringList extraArgs = m_extraCodeModelFlags; + const QStringList extraArgs = m_extraCodeModelFlags(); return [env, compiler, extraArgs, macroCache, lang](const QStringList &flags) { Q_UNUSED(flags) @@ -467,26 +515,6 @@ void KeilToolChain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -QList<OutputLineParser *> KeilToolChain::createOutputParsers() const -{ - return {new KeilParser}; -} - -QVariantMap KeilToolChain::toMap() const -{ - QVariantMap data = ToolChain::toMap(); - data.insert(compilerPlatformCodeGenFlagsKeyC, m_extraCodeModelFlags); - return data; -} - -bool KeilToolChain::fromMap(const QVariantMap &data) -{ - if (!ToolChain::fromMap(data)) - return false; - m_extraCodeModelFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList(); - return true; -} - std::unique_ptr<ToolChainConfigWidget> KeilToolChain::createConfigurationWidget() { return std::make_unique<KeilToolChainConfigWidget>(this); @@ -500,26 +528,12 @@ bool KeilToolChain::operator ==(const ToolChain &other) const const auto customTc = static_cast<const KeilToolChain *>(&other); return compilerCommand() == customTc->compilerCommand() && targetAbi() == customTc->targetAbi() - && m_extraCodeModelFlags == customTc->m_extraCodeModelFlags; -} - -void KeilToolChain::setExtraCodeModelFlags(const QStringList &flags) -{ - if (flags == m_extraCodeModelFlags) - return; - m_extraCodeModelFlags = flags; - toolChainUpdated(); + && m_extraCodeModelFlags() == customTc->m_extraCodeModelFlags(); } QStringList KeilToolChain::extraCodeModelFlags() const { - return m_extraCodeModelFlags; -} - -FilePath KeilToolChain::makeCommand(const Environment &env) const -{ - Q_UNUSED(env) - return {}; + return m_extraCodeModelFlags(); } // KeilToolchainFactory @@ -682,7 +696,7 @@ Toolchains KeilToolChainFactory::autoDetectToolchain(const Candidate &candidate, tc->setDetection(ToolChain::AutoDetection); tc->setLanguage(language); tc->setCompilerCommand(candidate.compilerPath); - tc->setExtraCodeModelFlags(extraArgs); + tc->m_extraCodeModelFlags.setValue(extraArgs); tc->setTargetAbi(abi); tc->setDisplayName(buildDisplayName(abi.architecture(), language, candidate.compilerVersion)); @@ -727,7 +741,7 @@ void KeilToolChainConfigWidget::applyImpl() const auto tc = static_cast<KeilToolChain *>(toolChain()); const QString displayName = tc->displayName(); tc->setCompilerCommand(m_compilerCommand->filePath()); - tc->setExtraCodeModelFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); + tc->m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); tc->setTargetAbi(m_abiWidget->currentAbi()); tc->setDisplayName(displayName); @@ -763,14 +777,14 @@ void KeilToolChainConfigWidget::setFromToolChain() m_compilerCommand->setFilePath(tc->compilerCommand()); m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = compilerExists(m_compilerCommand->filePath()); + const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); } void KeilToolChainConfigWidget::handleCompilerCommandChange() { const FilePath compilerPath = m_compilerCommand->filePath(); - const bool haveCompiler = compilerExists(compilerPath); + const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); const QStringList prevExtraArgs = splitString(m_platformCodeGenFlagsLineEdit->text()); diff --git a/src/plugins/baremetal/keiltoolchain.h b/src/plugins/baremetal/keiltoolchain.h index 77803b6cea1..6f897369095 100644 --- a/src/plugins/baremetal/keiltoolchain.h +++ b/src/plugins/baremetal/keiltoolchain.h @@ -3,61 +3,10 @@ #pragma once -#include <projectexplorer/abi.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainconfigwidget.h> - -QT_BEGIN_NAMESPACE -class QLineEdit; -class QPlainTextEdit; -class QPushButton; -class QTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - -namespace ProjectExplorer { class AbiWidget; } namespace BareMetal::Internal { -// KeilToolChain - -class KeilToolChain final : public ProjectExplorer::ToolChain -{ -public: - MacroInspectionRunner createMacroInspectionRunner() const final; - - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; - Utils::WarningFlags warningFlags(const QStringList &cxxflags) const final; - - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( - const Utils::Environment &) const final; - void addToEnvironment(Utils::Environment &env) const final; - QList<Utils::OutputLineParser *> createOutputParsers() const final; - - QVariantMap toMap() const final; - bool fromMap(const QVariantMap &data) final; - - std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; - - bool operator ==(const ToolChain &other) const final; - - void setExtraCodeModelFlags(const QStringList &flags); - QStringList extraCodeModelFlags() const final; - - Utils::FilePath makeCommand(const Utils::Environment &env) const final; - -private: - KeilToolChain(); - - QStringList m_extraCodeModelFlags; - - friend class KeilToolChainFactory; - friend class KeilToolChainConfigWidget; -}; - -// KeilToolchainFactory - class KeilToolChainFactory final : public ProjectExplorer::ToolChainFactory { public: @@ -73,27 +22,4 @@ private: const Candidate &candidate, Utils::Id language) const; }; -// KeilToolchainConfigWidget - -class KeilToolChainConfigWidget final : public ProjectExplorer::ToolChainConfigWidget -{ -public: - explicit KeilToolChainConfigWidget(KeilToolChain *tc); - -private: - void applyImpl() final; - void discardImpl() final { setFromToolChain(); } - bool isDirtyImpl() const final; - void makeReadOnlyImpl() final; - - void setFromToolChain(); - void handleCompilerCommandChange(); - void handlePlatformCodeGenFlagsChange(); - - Utils::PathChooser *m_compilerCommand = nullptr; - ProjectExplorer::AbiWidget *m_abiWidget = nullptr; - QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; - ProjectExplorer::Macros m_macros; -}; - } // BareMetal::Internal diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index a35a468d202..e57dd9fafe2 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -10,6 +10,7 @@ #include <projectexplorer/abiwidget.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmacro.h> +#include <projectexplorer/toolchainconfigwidget.h> #include <projectexplorer/toolchainmanager.h> #include <utils/algorithm.h> @@ -34,14 +35,6 @@ using namespace Utils; namespace BareMetal::Internal { -// Helpers: - -static bool compilerExists(const FilePath &compilerPath) -{ - const QFileInfo fi = compilerPath.toFileInfo(); - return fi.exists() && fi.isExecutable() && fi.isFile(); -} - static QString compilerTargetFlag(const Abi &abi) { switch (abi.architecture()) { @@ -168,29 +161,73 @@ static Abi guessAbi(const Macros ¯os) guessFormat(arch), guessWordWidth(macros)}; } -static QString buildDisplayName(Abi::Architecture arch, Utils::Id language, - const QString &version) +static QString buildDisplayName(Abi::Architecture arch, Id language, const QString &version) { const QString archName = Abi::toString(arch); const QString langName = ToolChainManager::displayNameOfLanguageId(language); return Tr::tr("SDCC %1 (%2, %3)").arg(version, langName, archName); } -static Utils::FilePath compilerPathFromEnvironment(const QString &compilerName) +static FilePath compilerPathFromEnvironment(const QString &compilerName) { const Environment systemEnvironment = Environment::systemEnvironment(); return systemEnvironment.searchInPath(compilerName); } +// SdccToolChainConfigWidget + +class SdccToolChain; + +class SdccToolChainConfigWidget final : public ToolChainConfigWidget +{ +public: + explicit SdccToolChainConfigWidget(SdccToolChain *tc); + +private: + void applyImpl() final; + void discardImpl() final { setFromToolchain(); } + bool isDirtyImpl() const final; + void makeReadOnlyImpl() final; + + void setFromToolchain(); + void handleCompilerCommandChange(); + + PathChooser *m_compilerCommand = nullptr; + AbiWidget *m_abiWidget = nullptr; + Macros m_macros; +}; + // SdccToolChain -SdccToolChain::SdccToolChain() : - ToolChain(Constants::SDCC_TOOLCHAIN_TYPEID) +class SdccToolChain final : public ToolChain { - setTypeDisplayName(Tr::tr("SDCC")); - setTargetAbiKey("TargetAbi"); - setCompilerCommandKey("CompilerPath"); -} +public: + SdccToolChain() : ToolChain(Constants::SDCC_TOOLCHAIN_TYPEID) + { + setTypeDisplayName(Tr::tr("SDCC")); + setTargetAbiKey("TargetAbi"); + setCompilerCommandKey("CompilerPath"); + } + + MacroInspectionRunner createMacroInspectionRunner() const final; + + LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; + WarningFlags warningFlags(const QStringList &cxxflags) const final; + + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Environment &) const final; + void addToEnvironment(Environment &env) const final; + QList<OutputLineParser *> createOutputParsers() const final { return {new SdccParser}; } + + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() final; + + bool operator==(const ToolChain &other) const final; + + FilePath makeCommand(const Environment &) const final { return {}; } + +private: + friend class SdccToolChainFactory; + friend class SdccToolChainConfigWidget; +}; ToolChain::MacroInspectionRunner SdccToolChain::createMacroInspectionRunner() const { @@ -215,7 +252,7 @@ ToolChain::MacroInspectionRunner SdccToolChain::createMacroInspectionRunner() co }; } -Utils::LanguageExtensions SdccToolChain::languageExtensions(const QStringList &) const +LanguageExtensions SdccToolChain::languageExtensions(const QStringList &) const { return LanguageExtension::None; } @@ -246,11 +283,6 @@ void SdccToolChain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -QList<Utils::OutputLineParser *> SdccToolChain::createOutputParsers() const -{ - return {new SdccParser}; -} - std::unique_ptr<ToolChainConfigWidget> SdccToolChain::createConfigurationWidget() { return std::make_unique<SdccToolChainConfigWidget>(this); @@ -266,12 +298,6 @@ bool SdccToolChain::operator==(const ToolChain &other) const && targetAbi() == customTc->targetAbi(); } -FilePath SdccToolChain::makeCommand(const Environment &env) const -{ - Q_UNUSED(env) - return {}; -} - // SdccToolChainFactory SdccToolChainFactory::SdccToolChainFactory() @@ -301,7 +327,7 @@ Toolchains SdccToolChainFactory::autoDetect(const ToolchainDetector &detector) c compilerPath += "/bin/sdcc.exe"; const FilePath fn = FilePath::fromString( QFileInfo(compilerPath).absoluteFilePath()); - if (!compilerExists(fn)) + if (!fn.isExecutableFile()) return Candidate{}; // Build compiler version. const QString version = QString("%1.%2.%3").arg( @@ -466,14 +492,14 @@ void SdccToolChainConfigWidget::setFromToolchain() const auto tc = static_cast<SdccToolChain *>(toolChain()); m_compilerCommand->setFilePath(tc->compilerCommand()); m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = compilerExists(m_compilerCommand->filePath()); + const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); } void SdccToolChainConfigWidget::handleCompilerCommandChange() { const FilePath compilerPath = m_compilerCommand->filePath(); - const bool haveCompiler = compilerExists(compilerPath); + const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); m_macros = dumpPredefinedMacros(compilerPath, env, {}); diff --git a/src/plugins/baremetal/sdcctoolchain.h b/src/plugins/baremetal/sdcctoolchain.h index 274ae41fb2b..972eee000d5 100644 --- a/src/plugins/baremetal/sdcctoolchain.h +++ b/src/plugins/baremetal/sdcctoolchain.h @@ -3,52 +3,10 @@ #pragma once -#include <projectexplorer/abi.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainconfigwidget.h> - -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -class QPushButton; -class QTextEdit; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - -namespace ProjectExplorer { class AbiWidget; } namespace BareMetal::Internal { -// SdccToolChain - -class SdccToolChain final : public ProjectExplorer::ToolChain -{ -public: - MacroInspectionRunner createMacroInspectionRunner() const final; - - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const final; - Utils::WarningFlags warningFlags(const QStringList &cxxflags) const final; - - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( - const Utils::Environment &) const final; - void addToEnvironment(Utils::Environment &env) const final; - QList<Utils::OutputLineParser *> createOutputParsers() const final; - - std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; - - bool operator ==(const ToolChain &other) const final; - - Utils::FilePath makeCommand(const Utils::Environment &env) const final; - -private: - SdccToolChain(); - - friend class SdccToolChainFactory; - friend class SdccToolChainConfigWidget; -}; - -// SdccToolChainFactory - class SdccToolChainFactory final : public ProjectExplorer::ToolChainFactory { public: @@ -64,25 +22,4 @@ private: const Candidate &candidate, Utils::Id language) const; }; -// SdccToolChainConfigWidget - -class SdccToolChainConfigWidget final : public ProjectExplorer::ToolChainConfigWidget -{ -public: - explicit SdccToolChainConfigWidget(SdccToolChain *tc); - -private: - void applyImpl() final; - void discardImpl() final { setFromToolchain(); } - bool isDirtyImpl() const final; - void makeReadOnlyImpl() final; - - void setFromToolchain(); - void handleCompilerCommandChange(); - - Utils::PathChooser *m_compilerCommand = nullptr; - ProjectExplorer::AbiWidget *m_abiWidget = nullptr; - ProjectExplorer::Macros m_macros; -}; - } // BareMetal::Internal diff --git a/src/plugins/bazaar/Bazaar.json.in b/src/plugins/bazaar/Bazaar.json.in index 74c336b0752..82bb55661f6 100644 --- a/src/plugins/bazaar/Bazaar.json.in +++ b/src/plugins/bazaar/Bazaar.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Bazaar\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Hugues Delorme\", - \"Copyright\" : \"(C) 2016 Hugues Delorme, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Bazaar", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Hugues Delorme", + "Copyright" : "(C) 2016 Hugues Delorme, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Bazaar integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Version Control", + "Description" : "Bazaar integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/bazaar/annotationhighlighter.cpp b/src/plugins/bazaar/annotationhighlighter.cpp index 8f99f9ec06a..7b5e2ba0208 100644 --- a/src/plugins/bazaar/annotationhighlighter.cpp +++ b/src/plugins/bazaar/annotationhighlighter.cpp @@ -19,7 +19,7 @@ QString BazaarAnnotationHighlighter::changeNumber(const QString &block) const const QRegularExpressionMatch match = m_changeset.match(block); if (match.hasMatch()) return match.captured(1); - return QString(); + return {}; } } // Bazaar::Internal diff --git a/src/plugins/bazaar/bazaarcommitwidget.cpp b/src/plugins/bazaar/bazaarcommitwidget.cpp index 379fca66610..465395e0aef 100644 --- a/src/plugins/bazaar/bazaarcommitwidget.cpp +++ b/src/plugins/bazaar/bazaarcommitwidget.cpp @@ -159,7 +159,7 @@ QString BazaarCommitWidget::committer() const const QString author = m_bazaarCommitPanel->authorLineEdit->text(); const QString email = m_bazaarCommitPanel->emailLineEdit->text(); if (author.isEmpty()) - return QString(); + return {}; QString user = author; if (!email.isEmpty()) { diff --git a/src/plugins/bazaar/bazaareditor.cpp b/src/plugins/bazaar/bazaareditor.cpp index 956ed6f28a9..146b2e71932 100644 --- a/src/plugins/bazaar/bazaareditor.cpp +++ b/src/plugins/bazaar/bazaareditor.cpp @@ -56,7 +56,7 @@ QString BazaarEditorWidget::changeUnderCursor(const QTextCursor &cursorIn) const } } } - return QString(); + return {}; } VcsBase::BaseAnnotationHighlighter *BazaarEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp index 542c2ef7066..8a9268748dd 100644 --- a/src/plugins/bazaar/bazaarplugin.cpp +++ b/src/plugins/bazaar/bazaarplugin.cpp @@ -215,7 +215,6 @@ public: void createRepositoryActions(const Core::Context &context); // Variables - BazaarSettings m_setting; BazaarClient m_client; VcsSubmitEditorFactory m_submitEditorFactory { diff --git a/src/plugins/bazaar/bazaarsettings.cpp b/src/plugins/bazaar/bazaarsettings.cpp index dc6ef77dd48..6683036d5ee 100644 --- a/src/plugins/bazaar/bazaarsettings.cpp +++ b/src/plugins/bazaar/bazaarsettings.cpp @@ -6,6 +6,7 @@ #include "bazaartr.h" #include "constants.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <utils/layoutbuilder.h> @@ -16,21 +17,16 @@ using namespace Utils; namespace Bazaar::Internal { -static BazaarSettings *theSettings; - BazaarSettings &settings() { - return *theSettings; + static BazaarSettings theSettings; + return theSettings; } BazaarSettings::BazaarSettings() { - theSettings = this; - + setAutoApply(false); setSettingsGroup(Constants::BAZAAR); - setId(VcsBase::Constants::VCS_ID_BAZAAR); - setDisplayName(Tr::tr("Bazaar")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); binaryPath.setExpectedKind(PathChooser::ExistingCommand); binaryPath.setDefaultValue(Constants::BAZAARDEFAULT); @@ -90,6 +86,24 @@ BazaarSettings::BazaarSettings() st }; }); + + readSettings(); } +// BazaarSettingsPage + +class BazaarSettingsPage final : public Core::IOptionsPage +{ +public: + BazaarSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_BAZAAR); + setDisplayName(Tr::tr("Bazaar")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const BazaarSettingsPage settingsPage; + } // Bazaar::Internal diff --git a/src/plugins/bazaar/pullorpushdialog.cpp b/src/plugins/bazaar/pullorpushdialog.cpp index 4c2cd99e7c5..1c4f8a36ddf 100644 --- a/src/plugins/bazaar/pullorpushdialog.cpp +++ b/src/plugins/bazaar/pullorpushdialog.cpp @@ -32,11 +32,11 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent) m_localPathChooser->setEnabled(false); auto urlButton = new QRadioButton(Tr::tr("Specify URL:")); - urlButton->setToolTip(Tr::tr("For example: 'https://[user[:pass]@]host[:port]/[path]'.")); + urlButton->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\".")); m_urlLineEdit = new QLineEdit; m_urlLineEdit->setEnabled(false); - m_urlLineEdit->setToolTip(Tr::tr("For example: 'https://[user[:pass]@]host[:port]/[path]'.")); + m_urlLineEdit->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\".")); m_rememberCheckBox = new QCheckBox(Tr::tr("Remember specified location as default")); m_rememberCheckBox->setEnabled(false); @@ -112,7 +112,7 @@ PullOrPushDialog::~PullOrPushDialog() = default; QString PullOrPushDialog::branchLocation() const { if (m_defaultButton->isChecked()) - return QString(); + return {}; if (m_localButton->isChecked()) return m_localPathChooser->filePath().toString(); return m_urlLineEdit->text(); diff --git a/src/plugins/beautifier/Beautifier.json.in b/src/plugins/beautifier/Beautifier.json.in index db8cc8b001d..79de488b617 100644 --- a/src/plugins/beautifier/Beautifier.json.in +++ b/src/plugins/beautifier/Beautifier.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"Beautifier\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"Lorenz Haas\", - \"Copyright\" : \"(C) 2017 Lorenz Haas, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Beautifier", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "Lorenz Haas", + "Copyright" : "(C) 2017 Lorenz Haas, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"C++\", - \"Description\" : \"Format source files with the help of beautifiers like AStyle, uncrustify or clang-format.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "C++", + "Description" : "Format source files with the help of beautifiers like AStyle, uncrustify or clang-format.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/beautifier/CMakeLists.txt b/src/plugins/beautifier/CMakeLists.txt index 4e921342b7a..e2ea02a75b9 100644 --- a/src/plugins/beautifier/CMakeLists.txt +++ b/src/plugins/beautifier/CMakeLists.txt @@ -2,23 +2,16 @@ add_qtc_plugin(Beautifier DEPENDS Qt::Xml PLUGIN_DEPENDS Core ProjectExplorer TextEditor SOURCES - abstractsettings.cpp abstractsettings.h artisticstyle/artisticstyle.cpp artisticstyle/artisticstyle.h - artisticstyle/artisticstyleconstants.h - artisticstyle/artisticstylesettings.cpp artisticstyle/artisticstylesettings.h beautifier.qrc - beautifierabstracttool.h beautifierconstants.h beautifierplugin.cpp beautifierplugin.h + beautifiertool.cpp beautifiertool.h beautifiertr.h clangformat/clangformat.cpp clangformat/clangformat.h - clangformat/clangformatconstants.h - clangformat/clangformatsettings.cpp clangformat/clangformatsettings.h configurationdialog.cpp configurationdialog.h configurationeditor.cpp configurationeditor.h configurationpanel.cpp configurationpanel.h generalsettings.cpp generalsettings.h uncrustify/uncrustify.cpp uncrustify/uncrustify.h - uncrustify/uncrustifyconstants.h - uncrustify/uncrustifysettings.cpp uncrustify/uncrustifysettings.h ) diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp index 1f063829944..95c3d43ecbf 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.cpp @@ -5,11 +5,10 @@ #include "artisticstyle.h" -#include "artisticstyleconstants.h" - #include "../beautifierconstants.h" -#include "../beautifierplugin.h" +#include "../beautifiertool.h" #include "../beautifiertr.h" +#include "../configurationpanel.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -17,6 +16,7 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/icore.h> #include <coreplugin/idocument.h> #include <projectexplorer/project.h> @@ -27,119 +27,330 @@ #include <utils/fileutils.h> #include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> +#include <utils/process.h> #include <utils/stringutils.h> #include <QAction> +#include <QApplication> +#include <QDateTime> +#include <QFile> +#include <QGroupBox> #include <QMenu> +#include <QRegularExpression> #include <QVersionNumber> +#include <QXmlStreamWriter> using namespace TextEditor; +using namespace Utils; namespace Beautifier::Internal { +// Settings + +static QString asDisplayName() { return Tr::tr("Artistic Style"); } + +const char SETTINGS_NAME[] = "artisticstyle"; + +class ArtisticStyleSettings : public AbstractSettings +{ +public: + ArtisticStyleSettings() + : AbstractSettings(SETTINGS_NAME, ".astyle") + { + setVersionRegExp(QRegularExpression("([2-9]{1})\\.([0-9]{1,2})(\\.[1-9]{1})?$")); + command.setLabelText(Tr::tr("Artistic Style command:")); + command.setDefaultValue("astyle"); + command.setPromptDialogTitle(BeautifierTool::msgCommandPromptDialogTitle(asDisplayName())); + + useOtherFiles.setSettingsKey("useOtherFiles"); + useOtherFiles.setLabelText(Tr::tr("Use file *.astylerc defined in project files")); + useOtherFiles.setDefaultValue(true); + + useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); + useSpecificConfigFile.setLabelText(Tr::tr("Use specific config file:")); + + specificConfigFile.setSettingsKey("specificConfigFile"); + specificConfigFile.setExpectedKind(PathChooser::File); + specificConfigFile.setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)")); + + useHomeFile.setSettingsKey("useHomeFile"); + useHomeFile.setLabelText(Tr::tr("Use file .astylerc or astylerc in HOME"). + replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); + + useCustomStyle.setSettingsKey("useCustomStyle"); + useCustomStyle.setLabelText(Tr::tr("Use customized style:")); + + customStyle.setSettingsKey("customStyle"); + + documentationFilePath = + Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) + .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME) + .stringAppended(".xml"); + + read(); + } + + void createDocumentationFile() const override + { + Process process; + process.setTimeoutS(2); + process.setCommand({command(), {"-h"}}); + process.runBlocking(); + if (process.result() != ProcessResult::FinishedWithSuccess) + return; + + if (!documentationFilePath.exists()) + documentationFilePath.parentDir().ensureWritableDir(); + + QFile file(documentationFilePath.toFSPathString()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return; + + bool contextWritten = false; + QXmlStreamWriter stream(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument("1.0", true); + stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); + stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); + + // astyle writes its output to 'error'... + const QStringList lines = process.cleanedStdErr().split(QLatin1Char('\n')); + QStringList keys; + QStringList docu; + for (QString line : lines) { + line = line.trimmed(); + if ((line.startsWith("--") && !line.startsWith("---")) || line.startsWith("OR ")) { + const QStringList rawKeys = line.split(" OR ", Qt::SkipEmptyParts); + for (QString k : rawKeys) { + k = k.trimmed(); + k.remove('#'); + keys << k; + if (k.startsWith("--")) + keys << k.right(k.size() - 2); + } + } else { + if (line.isEmpty()) { + if (!keys.isEmpty()) { + // Write entry + stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); + stream.writeStartElement(Constants::DOCUMENTATION_XMLKEYS); + for (const QString &key : std::as_const(keys)) + stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, key); + stream.writeEndElement(); + const QString text = "<p><span class=\"option\">" + + keys.filter(QRegularExpression("^\\-")).join(", ") + "</span></p><p>" + + (docu.join(' ').toHtmlEscaped()) + "</p>"; + stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); + stream.writeEndElement(); + contextWritten = true; + } + keys.clear(); + docu.clear(); + } else if (!keys.isEmpty()) { + docu << line; + } + } + } + + stream.writeEndElement(); + stream.writeEndDocument(); + + // An empty file causes error messages and a contextless file preventing this function to run + // again in order to generate the documentation successfully. Thus delete the file. + if (!contextWritten) { + file.close(); + file.remove(); + } + } + + BoolAspect useOtherFiles{this}; + BoolAspect useSpecificConfigFile{this}; + FilePathAspect specificConfigFile{this}; + BoolAspect useHomeFile{this}; + BoolAspect useCustomStyle{this}; + StringAspect customStyle{this}; +}; + +static ArtisticStyleSettings &settings() +{ + static ArtisticStyleSettings theSettings; + return theSettings; +} + +// ArtisticStyleOptionsPage + +class ArtisticStyleSettingsPageWidget : public Core::IOptionsPageWidget +{ +public: + ArtisticStyleSettingsPageWidget() + { + QGroupBox *options = nullptr; + + auto configurations = new ConfigurationPanel(this); + configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + configurations->setSettings(&settings()); + configurations->setCurrentConfiguration(settings().customStyle()); + + using namespace Layouting; + + ArtisticStyleSettings &s = settings(); + + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Column { + s.useOtherFiles, + Row { s.useSpecificConfigFile, s.specificConfigFile }, + s.useHomeFile, + Row { s.useCustomStyle, configurations }, + } + }, + st + }.attachTo(this); + + setOnApply([&s, configurations] { + s.customStyle.setValue(configurations->currentConfiguration()); + settings().apply(); + s.save(); + }); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + } +}; + +// Style + ArtisticStyle::ArtisticStyle() { Core::ActionContainer *menu = Core::ActionManager::createMenu("ArtisticStyle.Menu"); menu->menu()->setTitle(Tr::tr("&Artistic Style")); - m_formatFile = new QAction(BeautifierPlugin::msgFormatCurrentFile(), this); + m_formatFile = new QAction(msgFormatCurrentFile(), this); menu->addAction(Core::ActionManager::registerAction(m_formatFile, "ArtisticStyle.FormatFile")); connect(m_formatFile, &QAction::triggered, this, &ArtisticStyle::formatFile); Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, + connect(&settings().supportedMimeTypes, &Utils::BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } QString ArtisticStyle::id() const { - return QLatin1String(Constants::ARTISTICSTYLE_DISPLAY_NAME); + return "Artistic Style"; } void ArtisticStyle::updateActions(Core::IEditor *editor) { - m_formatFile->setEnabled(editor && m_settings.isApplicable(editor->document())); + m_formatFile->setEnabled(editor && settings().isApplicable(editor->document())); } void ArtisticStyle::formatFile() { - const QString cfgFileName = configurationFile(); - if (cfgFileName.isEmpty()) { - BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile( - Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME))); - } else { - formatCurrentFile(command(cfgFileName)); - } + const FilePath cfgFileName = configurationFile(); + if (cfgFileName.isEmpty()) + showError(BeautifierTool::msgCannotGetConfigurationFile(asDisplayName())); + else + formatCurrentFile(textCommand(cfgFileName.toFSPathString())); } -QString ArtisticStyle::configurationFile() const +FilePath ArtisticStyle::configurationFile() const { - if (m_settings.useCustomStyle()) - return m_settings.styleFileName(m_settings.customStyle()); + if (settings().useCustomStyle()) + return settings().styleFileName(settings().customStyle()); - if (m_settings.useOtherFiles()) { + if (settings().useOtherFiles()) { if (const ProjectExplorer::Project *project = ProjectExplorer::ProjectTree::currentProject()) { - const Utils::FilePaths astyleRcfiles = project->files( + const FilePaths astyleRcfiles = project->files( [](const ProjectExplorer::Node *n) { return n->filePath().endsWith(".astylerc"); }); - for (const Utils::FilePath &file : astyleRcfiles) { - const QFileInfo fi = file.toFileInfo(); - if (fi.isReadable()) - return file.toString(); + for (const FilePath &file : astyleRcfiles) { + if (file.isReadableFile()) + return file; } } } - if (m_settings.useSpecificConfigFile()) { - const Utils::FilePath file = m_settings.specificConfigFile(); + if (settings().useSpecificConfigFile()) { + const FilePath file = settings().specificConfigFile(); if (file.exists()) - return file.toUserOutput(); - } - - if (m_settings.useHomeFile()) { - const QDir homeDirectory = QDir::home(); - QString file = homeDirectory.filePath(".astylerc"); - if (QFile::exists(file)) - return file; - file = homeDirectory.filePath("astylerc"); - if (QFile::exists(file)) return file; } - return QString(); + if (settings().useHomeFile()) { + const FilePath homeDirectory = FileUtils::homePath(); + FilePath file = homeDirectory / ".astylerc"; + if (file.exists()) + return file; + file = homeDirectory / "astylerc"; + if (file.exists()) + return file; + } + + return {}; } -Command ArtisticStyle::command() const +Command ArtisticStyle::textCommand() const { - const QString cfgFile = configurationFile(); - return cfgFile.isEmpty() ? Command() : command(cfgFile); + const FilePath cfgFile = configurationFile(); + return cfgFile.isEmpty() ? Command() : textCommand(cfgFile.toFSPathString()); } bool ArtisticStyle::isApplicable(const Core::IDocument *document) const { - return m_settings.isApplicable(document); + return settings().isApplicable(document); } -Command ArtisticStyle::command(const QString &cfgFile) const +Command ArtisticStyle::textCommand(const QString &cfgFile) const { - Command command; - command.setExecutable(m_settings.command()); - command.addOption("-q"); - command.addOption("--options=" + cfgFile); + Command cmd; + cmd.setExecutable(settings().command()); + cmd.addOption("-q"); + cmd.addOption("--options=" + cfgFile); - const QVersionNumber version = m_settings.version(); + const QVersionNumber version = settings().version(); if (version > QVersionNumber(2, 3)) { - command.setProcessing(Command::PipeProcessing); + cmd.setProcessing(Command::PipeProcessing); if (version == QVersionNumber(2, 4)) - command.setPipeAddsNewline(true); - command.setReturnsCRLF(Utils::HostOsInfo::isWindowsHost()); - command.addOption("-z2"); + cmd.setPipeAddsNewline(true); + cmd.setReturnsCRLF(Utils::HostOsInfo::isWindowsHost()); + cmd.addOption("-z2"); } else { - command.addOption("%file"); + cmd.addOption("%file"); } - return command; + return cmd; } + +// ArtisticStyleSettingsPage + +class ArtisticStyleSettingsPage final : public Core::IOptionsPage +{ +public: + ArtisticStyleSettingsPage() + { + setId("ArtisticStyle"); + setDisplayName(asDisplayName()); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([] { return new ArtisticStyleSettingsPageWidget; }); + } +}; + +const ArtisticStyleSettingsPage settingsPage; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstyle.h b/src/plugins/beautifier/artisticstyle/artisticstyle.h index 3f3f15946af..2a20b7f1f2f 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstyle.h +++ b/src/plugins/beautifier/artisticstyle/artisticstyle.h @@ -3,30 +3,30 @@ #pragma once -#include "../beautifierabstracttool.h" +#include "../beautifiertool.h" -#include "artisticstylesettings.h" +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE namespace Beautifier::Internal { -class ArtisticStyle : public BeautifierAbstractTool +class ArtisticStyle : public BeautifierTool { public: ArtisticStyle(); QString id() const override; void updateActions(Core::IEditor *editor) override; - TextEditor::Command command() const override; + TextEditor::Command textCommand() const override; bool isApplicable(const Core::IDocument *document) const override; private: void formatFile(); - QString configurationFile() const; - TextEditor::Command command(const QString &cfgFile) const; + Utils::FilePath configurationFile() const; + TextEditor::Command textCommand(const QString &cfgFile) const; QAction *m_formatFile = nullptr; - ArtisticStyleSettings m_settings; - ArtisticStyleOptionsPage m_page{&m_settings}; }; } // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstyleconstants.h b/src/plugins/beautifier/artisticstyle/artisticstyleconstants.h deleted file mode 100644 index 92b95f70baf..00000000000 --- a/src/plugins/beautifier/artisticstyle/artisticstyleconstants.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QtGlobal> - -namespace Beautifier::Constants { - -const char ARTISTICSTYLE_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Beautifier", "Artistic Style"); - -} // Beautifier::Constants diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp deleted file mode 100644 index 3117269f0fa..00000000000 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "artisticstylesettings.h" - -#include "artisticstyleconstants.h" -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include <coreplugin/icore.h> - -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> -#include <utils/process.h> -#include <utils/stringutils.h> - -#include <QApplication> -#include <QDateTime> -#include <QFile> -#include <QFileInfo> -#include <QGroupBox> -#include <QRegularExpression> -#include <QXmlStreamWriter> - -using namespace Utils; - -namespace Beautifier::Internal { - -const char SETTINGS_NAME[] = "artisticstyle"; - -ArtisticStyleSettings::ArtisticStyleSettings() - : AbstractSettings(SETTINGS_NAME, ".astyle") -{ - setVersionRegExp(QRegularExpression("([2-9]{1})\\.([0-9]{1,2})(\\.[1-9]{1})?$")); - command.setLabelText(Tr::tr("Artistic Style command:")); - command.setDefaultValue("astyle"); - command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( - Tr::tr(Constants::ARTISTICSTYLE_DISPLAY_NAME))); - - useOtherFiles.setSettingsKey("useOtherFiles"); - useOtherFiles.setLabelText(Tr::tr("Use file *.astylerc defined in project files")); - useOtherFiles.setDefaultValue(true); - - useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); - useSpecificConfigFile.setLabelText(Tr::tr("Use specific config file:")); - - specificConfigFile.setSettingsKey("specificConfigFile"); - specificConfigFile.setExpectedKind(PathChooser::File); - specificConfigFile.setPromptDialogFilter(Tr::tr("AStyle (*.astylerc)")); - - useHomeFile.setSettingsKey("useHomeFile"); - useHomeFile.setLabelText(Tr::tr("Use file .astylerc or astylerc in HOME"). - replace("HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); - - useCustomStyle.setSettingsKey("useCustomStyle"); - useCustomStyle.setLabelText(Tr::tr("Use customized style:")); - - customStyle.setSettingsKey("customStyle"); - - documentationFilePath = - Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) - .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME) - .pathAppended(SETTINGS_NAME) - .stringAppended(".xml"); - - read(); -} - -void ArtisticStyleSettings::createDocumentationFile() const -{ - Process process; - process.setTimeoutS(2); - process.setCommand({command(), {"-h"}}); - process.runBlocking(); - if (process.result() != ProcessResult::FinishedWithSuccess) - return; - - if (!documentationFilePath.exists()) - documentationFilePath.parentDir().ensureWritableDir(); - - QFile file(documentationFilePath.toFSPathString()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - return; - - bool contextWritten = false; - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument("1.0", true); - stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); - stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); - - // astyle writes its output to 'error'... - const QStringList lines = process.cleanedStdErr().split(QLatin1Char('\n')); - QStringList keys; - QStringList docu; - for (QString line : lines) { - line = line.trimmed(); - if ((line.startsWith("--") && !line.startsWith("---")) || line.startsWith("OR ")) { - const QStringList rawKeys = line.split(" OR ", Qt::SkipEmptyParts); - for (QString k : rawKeys) { - k = k.trimmed(); - k.remove('#'); - keys << k; - if (k.startsWith("--")) - keys << k.right(k.size() - 2); - } - } else { - if (line.isEmpty()) { - if (!keys.isEmpty()) { - // Write entry - stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); - stream.writeStartElement(Constants::DOCUMENTATION_XMLKEYS); - for (const QString &key : std::as_const(keys)) - stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, key); - stream.writeEndElement(); - const QString text = "<p><span class=\"option\">" - + keys.filter(QRegularExpression("^\\-")).join(", ") + "</span></p><p>" - + (docu.join(' ').toHtmlEscaped()) + "</p>"; - stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); - stream.writeEndElement(); - contextWritten = true; - } - keys.clear(); - docu.clear(); - } else if (!keys.isEmpty()) { - docu << line; - } - } - } - - stream.writeEndElement(); - stream.writeEndDocument(); - - // An empty file causes error messages and a contextless file preventing this function to run - // again in order to generate the documentation successfully. Thus delete the file. - if (!contextWritten) { - file.close(); - file.remove(); - } -} - -class ArtisticStyleOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit ArtisticStyleOptionsPageWidget(ArtisticStyleSettings *settings) - { - QGroupBox *options = nullptr; - - auto configurations = new ConfigurationPanel(this); - configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - configurations->setSettings(settings); - configurations->setCurrentConfiguration(settings->customStyle()); - - using namespace Layouting; - - ArtisticStyleSettings &s = *settings; - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - s.command, br, - s.supportedMimeTypes - } - }, - Group { - title(Tr::tr("Options")), - bindTo(&options), - Column { - s.useOtherFiles, - Row { s.useSpecificConfigFile, s.specificConfigFile }, - s.useHomeFile, - Row { s.useCustomStyle, configurations }, - } - }, - st - }.attachTo(this); - - setOnApply([&s, configurations] { - s.customStyle.setValue(configurations->currentConfiguration()); - s.save(); - }); - - s.read(); - - connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); - options->setEnabled(s.command.pathChooser()->isValid()); - } -}; - -ArtisticStyleOptionsPage::ArtisticStyleOptionsPage(ArtisticStyleSettings *settings) -{ - setId("ArtisticStyle"); - setDisplayName(Tr::tr("Artistic Style")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new ArtisticStyleOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h b/src/plugins/beautifier/artisticstyle/artisticstylesettings.h deleted file mode 100644 index 25932cf8db8..00000000000 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../abstractsettings.h" - -namespace Beautifier::Internal { - -class ArtisticStyleSettings : public AbstractSettings -{ -public: - ArtisticStyleSettings(); - - Utils::BoolAspect useOtherFiles{this}; - Utils::BoolAspect useSpecificConfigFile{this}; - Utils::FilePathAspect specificConfigFile{this}; - Utils::BoolAspect useHomeFile{this}; - Utils::BoolAspect useCustomStyle{this}; - Utils::StringAspect customStyle{this}; - - void createDocumentationFile() const override; -}; - -class ArtisticStyleOptionsPage final : public Core::IOptionsPage -{ -public: - explicit ArtisticStyleOptionsPage(ArtisticStyleSettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/beautifier.qbs b/src/plugins/beautifier/beautifier.qbs index 24fecd671f1..1d540b96cca 100644 --- a/src/plugins/beautifier/beautifier.qbs +++ b/src/plugins/beautifier/beautifier.qbs @@ -11,13 +11,12 @@ QtcPlugin { Depends { name: "ProjectExplorer" } files: [ - "abstractsettings.h", - "abstractsettings.cpp", "beautifier.qrc", - "beautifierabstracttool.h", "beautifierconstants.h", "beautifierplugin.cpp", "beautifierplugin.h", + "beautifiertool.h", + "beautifiertool.cpp", "beautifiertr.h", "configurationdialog.cpp", "configurationdialog.h", @@ -35,9 +34,6 @@ QtcPlugin { files: [ "artisticstyle.cpp", "artisticstyle.h", - "artisticstyleconstants.h", - "artisticstylesettings.cpp", - "artisticstylesettings.h" ] } @@ -47,9 +43,6 @@ QtcPlugin { files: [ "clangformat.cpp", "clangformat.h", - "clangformatconstants.h", - "clangformatsettings.cpp", - "clangformatsettings.h" ] } @@ -59,9 +52,6 @@ QtcPlugin { files: [ "uncrustify.cpp", "uncrustify.h", - "uncrustifyconstants.h", - "uncrustifysettings.cpp", - "uncrustifysettings.h" ] } } diff --git a/src/plugins/beautifier/beautifierabstracttool.h b/src/plugins/beautifier/beautifierabstracttool.h deleted file mode 100644 index 00454b60de8..00000000000 --- a/src/plugins/beautifier/beautifierabstracttool.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <texteditor/command.h> - -#include <QObject> - -namespace Core { -class IDocument; -class IEditor; -} - -namespace Beautifier::Internal { - -class BeautifierAbstractTool : public QObject -{ -public: - BeautifierAbstractTool() = default; - - virtual QString id() const = 0; - virtual void updateActions(Core::IEditor *editor) = 0; - - /** - * Returns the tool's command to format an entire file. - * - * @note The received command may be invalid. - */ - virtual TextEditor::Command command() const = 0; - - virtual bool isApplicable(const Core::IDocument *document) const = 0; -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp index b849976a05b..e7a4b7593fc 100644 --- a/src/plugins/beautifier/beautifierplugin.cpp +++ b/src/plugins/beautifier/beautifierplugin.cpp @@ -19,26 +19,21 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/messagemanager.h> + #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/projecttree.h> + #include <texteditor/formattexteditor.h> #include <texteditor/textdocument.h> -#include <texteditor/textdocumentlayout.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorconstants.h> + #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/mimeutils.h> -#include <utils/process.h> -#include <utils/qtcassert.h> -#include <utils/temporarydirectory.h> -#include <utils/textutils.h> #include <QMenu> -#include <QPlainTextEdit> -#include <QScrollBar> -#include <QTextBlock> using namespace TextEditor; @@ -68,17 +63,9 @@ public: void autoFormatOnSave(Core::IDocument *document); - GeneralSettings generalSettings; - ArtisticStyle artisticStyleBeautifier; ClangFormat clangFormatBeautifier; Uncrustify uncrustifyBeautifier; - - BeautifierAbstractTool *m_tools[3] { - &artisticStyleBeautifier, - &uncrustifyBeautifier, - &clangFormatBeautifier - }; }; static BeautifierPluginPrivate *dd = nullptr; @@ -105,8 +92,8 @@ ExtensionSystem::IPlugin::ShutdownFlag BeautifierPlugin::aboutToShutdown() BeautifierPluginPrivate::BeautifierPluginPrivate() { - for (BeautifierAbstractTool *tool : m_tools) - generalSettings.autoFormatTools.addOption(tool->id()); + for (BeautifierTool *tool : BeautifierTool::allTools()) + generalSettings().autoFormatTools.addOption(tool->id()); updateActions(); @@ -119,20 +106,20 @@ BeautifierPluginPrivate::BeautifierPluginPrivate() void BeautifierPluginPrivate::updateActions(Core::IEditor *editor) { - for (BeautifierAbstractTool *tool : m_tools) + for (BeautifierTool *tool : BeautifierTool::allTools()) tool->updateActions(editor); } void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document) { - if (!generalSettings.autoFormatOnSave.value()) + if (!generalSettings().autoFormatOnSave()) return; - if (!isAutoFormatApplicable(document, generalSettings.allowedMimeTypes())) + if (!isAutoFormatApplicable(document, generalSettings().allowedMimeTypes())) return; // Check if file is contained in the current project (if wished) - if (generalSettings.autoFormatOnlyCurrentProject.value()) { + if (generalSettings().autoFormatOnlyCurrentProject()) { const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject(); if (!pro || pro->files([document](const ProjectExplorer::Node *n) { @@ -145,13 +132,14 @@ void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document) } // Find tool to use by id and format file! - const QString id = generalSettings.autoFormatTools.stringValue(); - auto tool = std::find_if(std::begin(m_tools), std::end(m_tools), - [&id](const BeautifierAbstractTool *t){return t->id() == id;}); - if (tool != std::end(m_tools)) { + const QString id = generalSettings().autoFormatTools.stringValue(); + const QList<BeautifierTool *> &tools = BeautifierTool::allTools(); + auto tool = std::find_if(std::begin(tools), std::end(tools), + [&id](const BeautifierTool *t){return t->id() == id;}); + if (tool != std::end(tools)) { if (!(*tool)->isApplicable(document)) return; - const TextEditor::Command command = (*tool)->command(); + const TextEditor::Command command = (*tool)->textCommand(); if (!command.isValid()) return; const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForDocument(document); @@ -162,50 +150,4 @@ void BeautifierPluginPrivate::autoFormatOnSave(Core::IDocument *document) } } -void BeautifierPlugin::showError(const QString &error) -{ - Core::MessageManager::writeFlashing(Tr::tr("Error in Beautifier: %1").arg(error.trimmed())); -} - -QString BeautifierPlugin::msgCannotGetConfigurationFile(const QString &command) -{ - return Tr::tr("Cannot get configuration file for %1.").arg(command); -} - -QString BeautifierPlugin::msgFormatCurrentFile() -{ - //: Menu entry - return Tr::tr("Format &Current File"); -} - -QString BeautifierPlugin::msgFormatSelectedText() -{ - //: Menu entry - return Tr::tr("Format &Selected Text"); -} - -QString BeautifierPlugin::msgFormatAtCursor() -{ - //: Menu entry - return Tr::tr("&Format at Cursor"); -} - -QString BeautifierPlugin::msgFormatLines() -{ - //: Menu entry - return Tr::tr("Format &Line(s)"); -} - -QString BeautifierPlugin::msgDisableFormattingSelectedText() -{ - //: Menu entry - return Tr::tr("&Disable Formatting for Selected Text"); -} - -QString BeautifierPlugin::msgCommandPromptDialogTitle(const QString &command) -{ - //: File dialog title for path chooser when choosing binary - return Tr::tr("%1 Command").arg(command); -} - } // Beautifier::Internal diff --git a/src/plugins/beautifier/beautifierplugin.h b/src/plugins/beautifier/beautifierplugin.h index 8b3d89ed210..e64403d9a7c 100644 --- a/src/plugins/beautifier/beautifierplugin.h +++ b/src/plugins/beautifier/beautifierplugin.h @@ -13,17 +13,6 @@ class BeautifierPlugin : public ExtensionSystem::IPlugin Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Beautifier.json") -public: - static QString msgCannotGetConfigurationFile(const QString &command); - static QString msgFormatCurrentFile(); - static QString msgFormatSelectedText(); - static QString msgFormatAtCursor(); - static QString msgFormatLines(); - static QString msgDisableFormattingSelectedText(); - static QString msgCommandPromptDialogTitle(const QString &command); - static void showError(const QString &error); - -private: void initialize() override; void extensionsInitialized() override; ShutdownFlag aboutToShutdown() override; diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/beautifiertool.cpp similarity index 66% rename from src/plugins/beautifier/abstractsettings.cpp rename to src/plugins/beautifier/beautifiertool.cpp index 362a2481538..01f470ad7c5 100644 --- a/src/plugins/beautifier/abstractsettings.cpp +++ b/src/plugins/beautifier/beautifiertool.cpp @@ -1,14 +1,14 @@ // Copyright (C) 2016 Lorenz Haas // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "abstractsettings.h" +#include "beautifiertool.h" #include "beautifierconstants.h" -#include "beautifierplugin.h" #include "beautifiertr.h" #include <coreplugin/icore.h> #include <coreplugin/idocument.h> +#include <coreplugin/messagemanager.h> #include <utils/algorithm.h> #include <utils/fileutils.h> @@ -17,7 +17,6 @@ #include <utils/process.h> #include <QFile> -#include <QFileInfo> #include <QRegularExpression> #include <QVersionNumber> #include <QXmlStreamReader> @@ -26,6 +25,68 @@ using namespace Utils; namespace Beautifier::Internal { +static QList<BeautifierTool *> &theTools() +{ + static QList<BeautifierTool *> tools; + return tools; +} + +BeautifierTool::BeautifierTool() +{ + theTools().append(this); +} + +const QList<BeautifierTool *> &BeautifierTool::allTools() +{ + return theTools(); +} + +void BeautifierTool::showError(const QString &error) +{ + Core::MessageManager::writeFlashing(Tr::tr("Error in Beautifier: %1").arg(error.trimmed())); +} + +QString BeautifierTool::msgCannotGetConfigurationFile(const QString &command) +{ + return Tr::tr("Cannot get configuration file for %1.").arg(command); +} + +QString BeautifierTool::msgFormatCurrentFile() +{ + //: Menu entry + return Tr::tr("Format &Current File"); +} + +QString BeautifierTool::msgFormatSelectedText() +{ + //: Menu entry + return Tr::tr("Format &Selected Text"); +} + +QString BeautifierTool::msgFormatAtCursor() +{ + //: Menu entry + return Tr::tr("&Format at Cursor"); +} + +QString BeautifierTool::msgFormatLines() +{ + //: Menu entry + return Tr::tr("Format &Line(s)"); +} + +QString BeautifierTool::msgDisableFormattingSelectedText() +{ + //: Menu entry + return Tr::tr("&Disable Formatting for Selected Text"); +} + +QString BeautifierTool::msgCommandPromptDialogTitle(const QString &command) +{ + //: File dialog title for path chooser when choosing binary + return Tr::tr("%1 Command").arg(command); +} + class VersionUpdater { public: @@ -81,16 +142,15 @@ private: AbstractSettings::AbstractSettings(const QString &name, const QString &ending) : m_ending(ending) , m_styleDir(Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) - .pathAppended(name) - .toString()) - , m_versionUpdater(new VersionUpdater) + .pathAppended(name)) { setSettingsGroups(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP, name); + setAutoApply(false); command.setSettingsKey("command"); command.setExpectedKind(PathChooser::ExistingCommand); command.setCommandVersionArguments({"--version"}); - command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); + command.setPromptDialogTitle(BeautifierTool::msgCommandPromptDialogTitle("Clang Format")); command.setValidatePlaceHolder(true); supportedMimeTypes.setDisplayStyle(StringAspect::LineEditDisplay); @@ -113,16 +173,14 @@ AbstractSettings::AbstractSettings(const QString &name, const QString &ending) return types.join("; "); }); - connect(&command, &BaseAspect::changed, this, [this] { - m_versionUpdater->update(command()); - }); + connect(&command, &BaseAspect::changed, this, [this] { m_version = {}; version(); }); } AbstractSettings::~AbstractSettings() = default; QStringList AbstractSettings::completerWords() { - return QStringList(); + return {}; } QStringList AbstractSettings::styles() const @@ -144,15 +202,15 @@ bool AbstractSettings::styleExists(const QString &key) const bool AbstractSettings::styleIsReadOnly(const QString &key) { - const QFileInfo fi(m_styleDir.absoluteFilePath(key + m_ending)); - if (!fi.exists()) { + const FilePath filePath = m_styleDir.pathAppended(key + m_ending); + if (!filePath.exists()) { // newly added style which was not saved yet., thus it is not read only. //TODO In a later version when we have predefined styles in Core::ICore::resourcePath() // we need to check if it is a read only global config file... return false; } - return !fi.isWritable(); + return !filePath.isWritableFile(); } void AbstractSettings::setStyle(const QString &key, const QString &value) @@ -179,19 +237,25 @@ void AbstractSettings::replaceStyle(const QString &oldKey, const QString &newKey m_changedStyles.insert(newKey); } -QString AbstractSettings::styleFileName(const QString &key) const +FilePath AbstractSettings::styleFileName(const QString &key) const { - return m_styleDir.absoluteFilePath(key + m_ending); + return m_styleDir.pathAppended(key + m_ending); } QVersionNumber AbstractSettings::version() const { - return m_versionUpdater->version(); + if (m_version.isNull()) { + VersionUpdater updater; + updater.setVersionRegExp(m_versionRegExp); + updater.update(command()); + m_version = updater.version(); + } + return m_version; } void AbstractSettings::setVersionRegExp(const QRegularExpression &versionRegExp) { - m_versionUpdater->setVersionRegExp(versionRegExp); + m_versionRegExp = versionRegExp; } bool AbstractSettings::isApplicable(const Core::IDocument *document) const @@ -228,9 +292,7 @@ QString AbstractSettings::documentation(const QString &option) const void AbstractSettings::save() { // Save settings, except styles - QSettings *s = Core::ICore::settings(); - - AspectContainer::writeSettings(s); + AspectContainer::writeSettings(); // Save styles if (m_stylesToRemove.isEmpty() && m_styles.isEmpty()) @@ -238,10 +300,14 @@ void AbstractSettings::save() // remove old files and possible subfolder for (const QString &key : std::as_const(m_stylesToRemove)) { - const QFileInfo fi(styleFileName(key)); - QFile::remove(fi.absoluteFilePath()); - if (fi.absoluteDir() != m_styleDir) - m_styleDir.rmdir(fi.absolutePath()); + FilePath filePath = styleFileName(key); + filePath.removeFile(); + QTC_ASSERT(m_styleDir.isAbsolutePath(), break); + QTC_ASSERT(!m_styleDir.needsDevice(), break); + if (filePath.parentDir() != m_styleDir) { + // FIXME: Missing in FilePath + QDir(m_styleDir.toString()).rmdir(filePath.parentDir().toString()); + } } m_stylesToRemove.clear(); @@ -253,24 +319,24 @@ void AbstractSettings::save() continue; } - const QFileInfo fi(styleFileName(iStyles.key())); - if (!(m_styleDir.mkpath(fi.absolutePath()))) { - BeautifierPlugin::showError(Tr::tr("Cannot save styles. %1 does not exist.") - .arg(fi.absolutePath())); + const FilePath filePath = styleFileName(iStyles.key()); + if (!filePath.parentDir().ensureWritableDir()) { + BeautifierTool::showError(Tr::tr("Cannot save styles. %1 does not exist.") + .arg(filePath.toUserOutput())); continue; } - FileSaver saver(FilePath::fromUserInput(fi.absoluteFilePath())); + FileSaver saver(filePath); if (saver.hasError()) { - BeautifierPlugin::showError(Tr::tr("Cannot open file \"%1\": %2.") - .arg(saver.filePath().toUserOutput()) - .arg(saver.errorString())); + BeautifierTool::showError(Tr::tr("Cannot open file \"%1\": %2.") + .arg(filePath.toUserOutput()) + .arg(saver.errorString())); } else { saver.write(iStyles.value().toLocal8Bit()); if (!saver.finalize()) { - BeautifierPlugin::showError(Tr::tr("Cannot save file \"%1\": %2.") - .arg(saver.filePath().toUserOutput()) - .arg(saver.errorString())); + BeautifierTool::showError(Tr::tr("Cannot save file \"%1\": %2.") + .arg(filePath.toUserOutput()) + .arg(saver.errorString())); } } ++iStyles; @@ -287,8 +353,7 @@ void AbstractSettings::createDocumentationFile() const void AbstractSettings::read() { // Read settings, except styles - QSettings *s = Core::ICore::settings(); - AspectContainer::readSettings(s); + AspectContainer::readSettings(); m_styles.clear(); m_changedStyles.clear(); @@ -300,7 +365,7 @@ void AbstractSettings::readDocumentation() { const FilePath filename = documentationFilePath; if (filename.isEmpty()) { - BeautifierPlugin::showError(Tr::tr("No documentation file specified.")); + BeautifierTool::showError(Tr::tr("No documentation file specified.")); return; } @@ -309,8 +374,8 @@ void AbstractSettings::readDocumentation() createDocumentationFile(); if (!file.open(QIODevice::ReadOnly)) { - BeautifierPlugin::showError(Tr::tr("Cannot open documentation file \"%1\".") - .arg(filename.toUserOutput())); + BeautifierTool::showError(Tr::tr("Cannot open documentation file \"%1\".") + .arg(filename.toUserOutput())); return; } @@ -318,8 +383,8 @@ void AbstractSettings::readDocumentation() if (!xml.readNextStartElement()) return; if (xml.name() != QLatin1String(Constants::DOCUMENTATION_XMLROOT)) { - BeautifierPlugin::showError(Tr::tr("The file \"%1\" is not a valid documentation file.") - .arg(filename.toUserOutput())); + BeautifierTool::showError(Tr::tr("The file \"%1\" is not a valid documentation file.") + .arg(filename.toUserOutput())); return; } @@ -349,8 +414,8 @@ void AbstractSettings::readDocumentation() } if (xml.hasError()) { - BeautifierPlugin::showError(Tr::tr("Cannot read documentation file \"%1\": %2.") - .arg(filename.toUserOutput()).arg(xml.errorString())); + BeautifierTool::showError(Tr::tr("Cannot read documentation file \"%1\": %2.") + .arg(filename.toUserOutput()).arg(xml.errorString())); } } @@ -359,18 +424,17 @@ void AbstractSettings::readStyles() if (!m_styleDir.exists()) return; - const QStringList files - = m_styleDir.entryList({'*' + m_ending}, - QDir::Files | QDir::Readable | QDir::NoDotAndDotDot); - for (const QString &filename : files) { + const FileFilter filter = {{'*' + m_ending}, QDir::Files | QDir::Readable | QDir::NoDotAndDotDot}; + const FilePaths files = m_styleDir.dirEntries(filter); + for (const FilePath &filePath : files) { // do not allow empty file names - if (filename == m_ending) + if (filePath.fileName() == m_ending) continue; - QFile file(m_styleDir.absoluteFilePath(filename)); - if (file.open(QIODevice::ReadOnly)) { + if (auto contents = filePath.fileContents()) { + const QString filename = filePath.fileName(); m_styles.insert(filename.left(filename.length() - m_ending.length()), - QString::fromLocal8Bit(file.readAll())); + QString::fromLocal8Bit(*contents)); } } } diff --git a/src/plugins/beautifier/abstractsettings.h b/src/plugins/beautifier/beautifiertool.h similarity index 57% rename from src/plugins/beautifier/abstractsettings.h rename to src/plugins/beautifier/beautifiertool.h index 8e9ceea931d..b41197b49e5 100644 --- a/src/plugins/beautifier/abstractsettings.h +++ b/src/plugins/beautifier/beautifiertool.h @@ -5,28 +5,55 @@ #include <coreplugin/dialogs/ioptionspage.h> -#include <utils/aspects.h> -#include <utils/filepath.h> +#include <texteditor/command.h> + +#include <utils/aspects.h> -#include <QDir> #include <QHash> #include <QMap> +#include <QObject> +#include <QRegularExpression> #include <QSet> -#include <QString> #include <QStringList> +#include <QVersionNumber> #include <memory> -QT_BEGIN_NAMESPACE -class QRegularExpression; -class QVersionNumber; -QT_END_NAMESPACE +namespace Core { +class IDocument; +class IEditor; +} -namespace Core { class IDocument; } +namespace Beautifier::Internal { -namespace Beautifier::Internal { +class BeautifierTool : public QObject +{ +public: + BeautifierTool(); -class VersionUpdater; + static const QList<BeautifierTool *> &allTools(); + + virtual QString id() const = 0; + virtual void updateActions(Core::IEditor *editor) = 0; + + /** + * Returns the tool's command to format an entire file. + * + * @note The received command may be invalid. + */ + virtual TextEditor::Command textCommand() const = 0; + + virtual bool isApplicable(const Core::IDocument *document) const = 0; + + static QString msgCannotGetConfigurationFile(const QString &command); + static QString msgFormatCurrentFile(); + static QString msgFormatSelectedText(); + static QString msgFormatAtCursor(); + static QString msgFormatLines(); + static QString msgDisableFormattingSelectedText(); + static QString msgCommandPromptDialogTitle(const QString &command); + static void showError(const QString &error); +}; class AbstractSettings : public Utils::AspectContainer { @@ -47,7 +74,7 @@ public: void setStyle(const QString &key, const QString &value); void removeStyle(const QString &key); void replaceStyle(const QString &oldKey, const QString &newKey, const QString &value); - virtual QString styleFileName(const QString &key) const; + virtual Utils::FilePath styleFileName(const QString &key) const; Utils::FilePathAspect command{this}; Utils::StringAspect supportedMimeTypes{this}; @@ -66,18 +93,19 @@ protected: QMap<QString, QString> m_styles; QString m_ending; - QDir m_styleDir; + Utils::FilePath m_styleDir; void readDocumentation(); virtual void readStyles(); private: - std::unique_ptr<VersionUpdater> m_versionUpdater; QStringList m_stylesToRemove; QSet<QString> m_changedStyles; QHash<QString, int> m_options; QStringList m_docu; QStringList m_supportedMimeTypes; + mutable QVersionNumber m_version; + QRegularExpression m_versionRegExp; }; } // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformat.cpp b/src/plugins/beautifier/clangformat/clangformat.cpp index 92c8c7f454d..0d6c9235da0 100644 --- a/src/plugins/beautifier/clangformat/clangformat.cpp +++ b/src/plugins/beautifier/clangformat/clangformat.cpp @@ -5,11 +5,10 @@ #include "clangformat.h" -#include "clangformatconstants.h" - #include "../beautifierconstants.h" -#include "../beautifierplugin.h" +#include "../beautifiertool.h" #include "../beautifiertr.h" +#include "../configurationpanel.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -17,45 +16,322 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/icore.h> #include <coreplugin/idocument.h> + #include <texteditor/formattexteditor.h> #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> + #include <utils/algorithm.h> +#include <utils/aspects.h> #include <utils/fileutils.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> #include <QAction> +#include <QButtonGroup> +#include <QComboBox> +#include <QDateTime> +#include <QGroupBox> #include <QMenu> +#include <QRadioButton> #include <QTextBlock> #include <QTextCodec> +#include <QXmlStreamWriter> using namespace TextEditor; +using namespace Utils; namespace Beautifier::Internal { +const char SETTINGS_NAME[] = "clangformat"; + +class ClangFormatSettings : public AbstractSettings +{ +public: + ClangFormatSettings() + : AbstractSettings(SETTINGS_NAME, ".clang-format") + { + command.setDefaultValue("clang-format"); + command.setPromptDialogTitle(BeautifierTool::msgCommandPromptDialogTitle("Clang Format")); + command.setLabelText(Tr::tr("Clang Format command:")); + + usePredefinedStyle.setSettingsKey("usePredefinedStyle"); + usePredefinedStyle.setDefaultValue(true); + usePredefinedStyle.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + usePredefinedStyle.setLabelText(Tr::tr("Use predefined style:")); + + predefinedStyle.setSettingsKey("predefinedStyle"); + predefinedStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + predefinedStyle.addOption("LLVM"); + predefinedStyle.addOption("Google"); + predefinedStyle.addOption("Chromium"); + predefinedStyle.addOption("Mozilla"); + predefinedStyle.addOption("WebKit"); + predefinedStyle.addOption("File"); + predefinedStyle.setDefaultValue("LLVM"); + + fallbackStyle.setSettingsKey("fallbackStyle"); + fallbackStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + fallbackStyle.addOption("Default"); + fallbackStyle.addOption("None"); + fallbackStyle.addOption("LLVM"); + fallbackStyle.addOption("Google"); + fallbackStyle.addOption("Chromium"); + fallbackStyle.addOption("Mozilla"); + fallbackStyle.addOption("WebKit"); + fallbackStyle.setDefaultValue("Default"); + + customStyle.setSettingsKey("customStyle"); + + documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + .pathAppended(Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME).stringAppended(".xml"); + + read(); + } + + void createDocumentationFile() const override; + + QStringList completerWords() override; + + BoolAspect usePredefinedStyle{this}; + SelectionAspect predefinedStyle{this}; + SelectionAspect fallbackStyle{this}; + StringAspect customStyle{this}; + + Utils::FilePath styleFileName(const QString &key) const override; + +private: + void readStyles() override; +}; + +void ClangFormatSettings::createDocumentationFile() const +{ + QFile file(documentationFilePath.toFSPathString()); + const QFileInfo fi(file); + if (!fi.exists()) + fi.dir().mkpath(fi.absolutePath()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return; + + QXmlStreamWriter stream(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument("1.0", true); + stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); + stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); + + const QStringList lines = { + "BasedOnStyle {string: LLVM, Google, Chromium, Mozilla, WebKit}", + "AccessModifierOffset {int}", + "AlignEscapedNewlinesLeft {bool}", + "AlignTrailingComments {bool}", + "AllowAllParametersOfDeclarationOnNextLine {bool}", + "AllowShortFunctionsOnASingleLine {bool}", + "AllowShortIfStatementsOnASingleLine {bool}", + "AllowShortLoopsOnASingleLine {bool}", + "AlwaysBreakBeforeMultilineStrings {bool}", + "AlwaysBreakTemplateDeclarations {bool}", + "BinPackParameters {bool}", + "BreakBeforeBinaryOperators {bool}", + "BreakBeforeBraces {BraceBreakingStyle: BS_Attach, BS_Linux, BS_Stroustrup, BS_Allman, BS_GNU}", + "BreakBeforeTernaryOperators {bool}", + "BreakConstructorInitializersBeforeComma {bool}", + "ColumnLimit {unsigned}", + "CommentPragmas {string}", + "ConstructorInitializerAllOnOneLineOrOnePerLine {bool}", + "ConstructorInitializerIndentWidth {unsigned}", + "ContinuationIndentWidth {unsigned}", + "Cpp11BracedListStyle {bool}", + "IndentCaseLabels {bool}", + "IndentFunctionDeclarationAfterType {bool}", + "IndentWidth {unsigned}", + "Language {LanguageKind: LK_None, LK_Cpp, LK_JavaScript, LK_Proto}", + "MaxEmptyLinesToKeep {unsigned}", + "NamespaceIndentation {NamespaceIndentationKind: NI_None, NI_Inner, NI_All}", + "ObjCSpaceAfterProperty {bool}", + "ObjCSpaceBeforeProtocolList {bool}", + "PenaltyBreakBeforeFirstCallParameter {unsigned}", + "PenaltyBreakComment {unsigned}", + "PenaltyBreakFirstLessLess {unsigned}", + "PenaltyBreakString {unsigned}", + "PenaltyExcessCharacter {unsigned}", + "PenaltyReturnTypeOnItsOwnLine {unsigned}", + "PointerBindsToType {bool}", + "SpaceBeforeAssignmentOperators {bool}", + "SpaceBeforeParens {SpaceBeforeParensOptions: SBPO_Never, SBPO_ControlStatements, SBPO_Always}", + "SpaceInEmptyParentheses {bool}", + "SpacesBeforeTrailingComments {unsigned}", + "SpacesInAngles {bool}", + "SpacesInCStyleCastParentheses {bool}", + "SpacesInContainerLiterals {bool}", + "SpacesInParentheses {bool}", + "Standard {LanguageStandard: LS_Cpp03, LS_Cpp11, LS_Auto}", + "TabWidth {unsigned}", + "UseTab {UseTabStyle: UT_Never, UT_ForIndentation, UT_Always}" + }; + + for (const QString& line : lines) { + const int firstSpace = line.indexOf(' '); + const QString keyword = line.left(firstSpace); + const QString options = line.right(line.size() - firstSpace).trimmed(); + const QString text = "<p><span class=\"option\">" + keyword + + "</span> <span class=\"param\">" + options + + "</span></p><p>" + Tr::tr("No description available.") + "</p>"; + stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); + stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, keyword); + stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); + stream.writeEndElement(); + } + + stream.writeEndElement(); + stream.writeEndDocument(); +} + +QStringList ClangFormatSettings::completerWords() +{ + return { + "LLVM", + "Google", + "Chromium", + "Mozilla", + "WebKit", + "BS_Attach", + "BS_Linux", + "BS_Stroustrup", + "BS_Allman", + "NI_None", + "NI_Inner", + "NI_All", + "LS_Cpp03", + "LS_Cpp11", + "LS_Auto", + "UT_Never", + "UT_ForIndentation", + "UT_Always" + }; +} + +FilePath ClangFormatSettings::styleFileName(const QString &key) const +{ + return m_styleDir / key / m_ending; +} + +void ClangFormatSettings::readStyles() +{ + const FilePaths dirs = m_styleDir.dirEntries(QDir::AllDirs | QDir::NoDotAndDotDot); + for (const FilePath &dir : dirs) { + if (auto contents = dir.pathAppended(m_ending).fileContents()) + m_styles.insert(dir.fileName(), QString::fromLocal8Bit(*contents)); + } +} + +static ClangFormatSettings &settings() +{ + static ClangFormatSettings theSettings; + return theSettings; +} + +class ClangFormatSettingsPageWidget : public Core::IOptionsPageWidget +{ +public: + ClangFormatSettingsPageWidget() + { + ClangFormatSettings &s = settings(); + QGroupBox *options = nullptr; + + auto predefinedStyleButton = new QRadioButton; + auto customizedStyleButton = new QRadioButton(Tr::tr("Use customized style:")); + + auto styleButtonGroup = new QButtonGroup; + styleButtonGroup->addButton(predefinedStyleButton); + styleButtonGroup->addButton(customizedStyleButton); + + auto configurations = new ConfigurationPanel(this); + configurations->setSettings(&s); + configurations->setCurrentConfiguration(s.customStyle()); + + using namespace Layouting; + + auto fallbackBlob = Row { noMargin, Tr::tr("Fallback style:"), s.fallbackStyle }.emerge(); + + auto predefinedBlob = Column { noMargin, s.predefinedStyle, fallbackBlob }.emerge(); + // clang-format off + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Form { + s.usePredefinedStyle.adoptButton(predefinedStyleButton), predefinedBlob, br, + customizedStyleButton, configurations, + }, + }, + st + }.attachTo(this); + // clang-format on + + if (s.usePredefinedStyle.value()) + predefinedStyleButton->click(); + else + customizedStyleButton->click(); + + const auto updateEnabled = [&s, styleButtonGroup, predefinedBlob, fallbackBlob, + configurations, predefinedStyleButton] { + const bool predefSelected = styleButtonGroup->checkedButton() == predefinedStyleButton; + predefinedBlob->setEnabled(predefSelected); + fallbackBlob->setEnabled(predefSelected && s.predefinedStyle.volatileValue() == 5); // File + configurations->setEnabled(!predefSelected); + }; + updateEnabled(); + connect(styleButtonGroup, &QButtonGroup::buttonClicked, this, updateEnabled); + connect(&s.predefinedStyle, &SelectionAspect::volatileValueChanged, this, updateEnabled); + + setOnApply([configurations, customizedStyleButton] { + settings().usePredefinedStyle.setValue(!customizedStyleButton->isChecked()); + settings().customStyle.setValue(configurations->currentConfiguration()); + settings().apply(); + settings().save(); + }); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + } +}; + +// ClangFormat + ClangFormat::ClangFormat() { Core::ActionContainer *menu = Core::ActionManager::createMenu("ClangFormat.Menu"); menu->menu()->setTitle(Tr::tr("&ClangFormat")); - m_formatFile = new QAction(BeautifierPlugin::msgFormatCurrentFile(), this); + m_formatFile = new QAction(msgFormatCurrentFile(), this); Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, "ClangFormat.FormatFile"); menu->addAction(cmd); connect(m_formatFile, &QAction::triggered, this, &ClangFormat::formatFile); - m_formatLines = new QAction(BeautifierPlugin::msgFormatLines(), this); + m_formatLines = new QAction(msgFormatLines(), this); cmd = Core::ActionManager::registerAction(m_formatLines, "ClangFormat.FormatLines"); menu->addAction(cmd); connect(m_formatLines, &QAction::triggered, this, &ClangFormat::formatLines); - m_formatRange = new QAction(BeautifierPlugin::msgFormatAtCursor(), this); + m_formatRange = new QAction(msgFormatAtCursor(), this); cmd = Core::ActionManager::registerAction(m_formatRange, "ClangFormat.FormatAtCursor"); menu->addAction(cmd); connect(m_formatRange, &QAction::triggered, this, &ClangFormat::formatAtCursor); - m_disableFormattingSelectedText - = new QAction(BeautifierPlugin::msgDisableFormattingSelectedText(), this); + m_disableFormattingSelectedText = new QAction(msgDisableFormattingSelectedText(), this); cmd = Core::ActionManager::registerAction( m_disableFormattingSelectedText, "ClangFormat.DisableFormattingSelectedText"); menu->addAction(cmd); @@ -64,25 +340,25 @@ ClangFormat::ClangFormat() Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, + connect(&settings().supportedMimeTypes, &BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } QString ClangFormat::id() const { - return QLatin1String(Constants::CLANGFORMAT_DISPLAY_NAME); + return "ClangFormat"; } void ClangFormat::updateActions(Core::IEditor *editor) { - const bool enabled = editor && m_settings.isApplicable(editor->document()); + const bool enabled = editor && settings().isApplicable(editor->document()); m_formatFile->setEnabled(enabled); m_formatRange->setEnabled(enabled); } void ClangFormat::formatFile() { - formatCurrentFile(command()); + formatCurrentFile(textCommand()); } void ClangFormat::formatAtPosition(const int pos, const int length) @@ -93,7 +369,7 @@ void ClangFormat::formatAtPosition(const int pos, const int length) const QTextCodec *codec = widget->textDocument()->codec(); if (!codec) { - formatCurrentFile(command(pos, length)); + formatCurrentFile(textCommand(pos, length)); return; } @@ -101,7 +377,7 @@ void ClangFormat::formatAtPosition(const int pos, const int length) const QStringView buffer(text); const int encodedOffset = codec->fromUnicode(buffer.left(pos)).size(); const int encodedLength = codec->fromUnicode(buffer.mid(pos, length)).size(); - formatCurrentFile(command(encodedOffset, encodedLength)); + formatCurrentFile(textCommand(encodedOffset, encodedLength)); } void ClangFormat::formatAtCursor() @@ -144,7 +420,7 @@ void ClangFormat::formatLines() lineEnd = end.blockNumber() + 1; } - auto cmd = command(); + auto cmd = textCommand(); cmd.addOption(QString("-lines=%1:%2").arg(QString::number(lineStart)).arg(QString::number(lineEnd))); formatCurrentFile(cmd); } @@ -184,43 +460,60 @@ void ClangFormat::disableFormattingSelectedText() formatAtPosition(selectionStartBlock.position(), reformatTextLength); } -Command ClangFormat::command() const +Command ClangFormat::textCommand() const { - Command command; - command.setExecutable(m_settings.command()); - command.setProcessing(Command::PipeProcessing); + Command cmd; + cmd.setExecutable(settings().command()); + cmd.setProcessing(Command::PipeProcessing); - if (m_settings.usePredefinedStyle()) { - const QString predefinedStyle = m_settings.predefinedStyle.stringValue(); - command.addOption("-style=" + predefinedStyle); + if (settings().usePredefinedStyle()) { + const QString predefinedStyle = settings().predefinedStyle.stringValue(); + cmd.addOption("-style=" + predefinedStyle); if (predefinedStyle == "File") { - const QString fallbackStyle = m_settings.fallbackStyle.stringValue(); + const QString fallbackStyle = settings().fallbackStyle.stringValue(); if (fallbackStyle != "Default") - command.addOption("-fallback-style=" + fallbackStyle); + cmd.addOption("-fallback-style=" + fallbackStyle); } - command.addOption("-assume-filename=%file"); + cmd.addOption("-assume-filename=%file"); } else { - command.addOption("-style=file"); - const QString path = - QFileInfo(m_settings.styleFileName(m_settings.customStyle())).absolutePath(); - command.addOption("-assume-filename=" + path + QDir::separator() + "%filename"); + cmd.addOption("-style=file"); + const FilePath path = settings().styleFileName(settings().customStyle()) + .absolutePath().pathAppended("%filename"); + cmd.addOption("-assume-filename=" + path.nativePath()); } - return command; + return cmd; } bool ClangFormat::isApplicable(const Core::IDocument *document) const { - return m_settings.isApplicable(document); + return settings().isApplicable(document); } -Command ClangFormat::command(int offset, int length) const +Command ClangFormat::textCommand(int offset, int length) const { - Command c = command(); - c.addOption("-offset=" + QString::number(offset)); - c.addOption("-length=" + QString::number(length)); - return c; + Command cmd = textCommand(); + cmd.addOption("-offset=" + QString::number(offset)); + cmd.addOption("-length=" + QString::number(length)); + return cmd; } + +// ClangFormatSettingsPage + +class ClangFormatSettingsPage final : public Core::IOptionsPage +{ +public: + ClangFormatSettingsPage() + { + setId("ClangFormat"); + setDisplayName(Tr::tr("Clang Format")); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([] { return new ClangFormatSettingsPageWidget; }); + } +}; + +const ClangFormatSettingsPage settingsPage; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformat.h b/src/plugins/beautifier/clangformat/clangformat.h index 65fd23a43f1..82f6ac160b9 100644 --- a/src/plugins/beautifier/clangformat/clangformat.h +++ b/src/plugins/beautifier/clangformat/clangformat.h @@ -3,20 +3,22 @@ #pragma once -#include "../beautifierabstracttool.h" +#include "../beautifiertool.h" -#include "clangformatsettings.h" +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE namespace Beautifier::Internal { -class ClangFormat : public BeautifierAbstractTool +class ClangFormat : public BeautifierTool { public: ClangFormat(); QString id() const override; void updateActions(Core::IEditor *editor) override; - TextEditor::Command command() const override; + TextEditor::Command textCommand() const override; bool isApplicable(const Core::IDocument *document) const override; private: @@ -25,14 +27,12 @@ private: void formatAtCursor(); void formatLines(); void disableFormattingSelectedText(); - TextEditor::Command command(int offset, int length) const; + TextEditor::Command textCommand(int offset, int length) const; QAction *m_formatFile = nullptr; QAction *m_formatLines = nullptr; QAction *m_formatRange = nullptr; QAction *m_disableFormattingSelectedText = nullptr; - ClangFormatSettings m_settings; - ClangFormatOptionsPage m_page{&m_settings}; }; } // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformatconstants.h b/src/plugins/beautifier/clangformat/clangformatconstants.h deleted file mode 100644 index 177253f6c18..00000000000 --- a/src/plugins/beautifier/clangformat/clangformatconstants.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QtGlobal> - -namespace Beautifier::Constants { - -const char CLANGFORMAT_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Beautifier", "ClangFormat"); - -} // Beautifier::Constants diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.cpp b/src/plugins/beautifier/clangformat/clangformatsettings.cpp deleted file mode 100644 index 629e4932403..00000000000 --- a/src/plugins/beautifier/clangformat/clangformatsettings.cpp +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "clangformatsettings.h" - -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include <coreplugin/icore.h> - -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> - -#include <QDateTime> -#include <QXmlStreamWriter> -#include <QButtonGroup> -#include <QComboBox> -#include <QGroupBox> -#include <QRadioButton> - -using namespace Utils; - -namespace Beautifier::Internal { - -const char SETTINGS_NAME[] = "clangformat"; - -ClangFormatSettings::ClangFormatSettings() - : AbstractSettings(SETTINGS_NAME, ".clang-format") -{ - command.setDefaultValue("clang-format"); - command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); - command.setLabelText(Tr::tr("Clang Format command:")); - - usePredefinedStyle.setSettingsKey("usePredefinedStyle"); - usePredefinedStyle.setLabelText(Tr::tr("Use predefined style:")); - usePredefinedStyle.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel); - usePredefinedStyle.setDefaultValue(true); - - predefinedStyle.setSettingsKey("predefinedStyle"); - predefinedStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - predefinedStyle.addOption("LLVM"); - predefinedStyle.addOption("Google"); - predefinedStyle.addOption("Chromium"); - predefinedStyle.addOption("Mozilla"); - predefinedStyle.addOption("WebKit"); - predefinedStyle.addOption("File"); - predefinedStyle.setDefaultValue("LLVM"); - - fallbackStyle.setSettingsKey("fallbackStyle"); - fallbackStyle.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - fallbackStyle.addOption("Default"); - fallbackStyle.addOption("None"); - fallbackStyle.addOption("LLVM"); - fallbackStyle.addOption("Google"); - fallbackStyle.addOption("Chromium"); - fallbackStyle.addOption("Mozilla"); - fallbackStyle.addOption("WebKit"); - fallbackStyle.setDefaultValue("Default"); - - customStyle.setSettingsKey("customStyle"); - - documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) - .pathAppended(Constants::DOCUMENTATION_DIRNAME) - .pathAppended(SETTINGS_NAME).stringAppended(".xml"); - - read(); -} - -void ClangFormatSettings::createDocumentationFile() const -{ - QFile file(documentationFilePath.toFSPathString()); - const QFileInfo fi(file); - if (!fi.exists()) - fi.dir().mkpath(fi.absolutePath()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - return; - - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument("1.0", true); - stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); - stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); - - const QStringList lines = { - "BasedOnStyle {string: LLVM, Google, Chromium, Mozilla, WebKit}", - "AccessModifierOffset {int}", - "AlignEscapedNewlinesLeft {bool}", - "AlignTrailingComments {bool}", - "AllowAllParametersOfDeclarationOnNextLine {bool}", - "AllowShortFunctionsOnASingleLine {bool}", - "AllowShortIfStatementsOnASingleLine {bool}", - "AllowShortLoopsOnASingleLine {bool}", - "AlwaysBreakBeforeMultilineStrings {bool}", - "AlwaysBreakTemplateDeclarations {bool}", - "BinPackParameters {bool}", - "BreakBeforeBinaryOperators {bool}", - "BreakBeforeBraces {BraceBreakingStyle: BS_Attach, BS_Linux, BS_Stroustrup, BS_Allman, BS_GNU}", - "BreakBeforeTernaryOperators {bool}", - "BreakConstructorInitializersBeforeComma {bool}", - "ColumnLimit {unsigned}", - "CommentPragmas {string}", - "ConstructorInitializerAllOnOneLineOrOnePerLine {bool}", - "ConstructorInitializerIndentWidth {unsigned}", - "ContinuationIndentWidth {unsigned}", - "Cpp11BracedListStyle {bool}", - "IndentCaseLabels {bool}", - "IndentFunctionDeclarationAfterType {bool}", - "IndentWidth {unsigned}", - "Language {LanguageKind: LK_None, LK_Cpp, LK_JavaScript, LK_Proto}", - "MaxEmptyLinesToKeep {unsigned}", - "NamespaceIndentation {NamespaceIndentationKind: NI_None, NI_Inner, NI_All}", - "ObjCSpaceAfterProperty {bool}", - "ObjCSpaceBeforeProtocolList {bool}", - "PenaltyBreakBeforeFirstCallParameter {unsigned}", - "PenaltyBreakComment {unsigned}", - "PenaltyBreakFirstLessLess {unsigned}", - "PenaltyBreakString {unsigned}", - "PenaltyExcessCharacter {unsigned}", - "PenaltyReturnTypeOnItsOwnLine {unsigned}", - "PointerBindsToType {bool}", - "SpaceBeforeAssignmentOperators {bool}", - "SpaceBeforeParens {SpaceBeforeParensOptions: SBPO_Never, SBPO_ControlStatements, SBPO_Always}", - "SpaceInEmptyParentheses {bool}", - "SpacesBeforeTrailingComments {unsigned}", - "SpacesInAngles {bool}", - "SpacesInCStyleCastParentheses {bool}", - "SpacesInContainerLiterals {bool}", - "SpacesInParentheses {bool}", - "Standard {LanguageStandard: LS_Cpp03, LS_Cpp11, LS_Auto}", - "TabWidth {unsigned}", - "UseTab {UseTabStyle: UT_Never, UT_ForIndentation, UT_Always}" - }; - - for (const QString& line : lines) { - const int firstSpace = line.indexOf(' '); - const QString keyword = line.left(firstSpace); - const QString options = line.right(line.size() - firstSpace).trimmed(); - const QString text = "<p><span class=\"option\">" + keyword - + "</span> <span class=\"param\">" + options - + "</span></p><p>" + Tr::tr("No description available.") + "</p>"; - stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); - stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, keyword); - stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); - stream.writeEndElement(); - } - - stream.writeEndElement(); - stream.writeEndDocument(); -} - -QStringList ClangFormatSettings::completerWords() -{ - return { - "LLVM", - "Google", - "Chromium", - "Mozilla", - "WebKit", - "BS_Attach", - "BS_Linux", - "BS_Stroustrup", - "BS_Allman", - "NI_None", - "NI_Inner", - "NI_All", - "LS_Cpp03", - "LS_Cpp11", - "LS_Auto", - "UT_Never", - "UT_ForIndentation", - "UT_Always" - }; -} - -QString ClangFormatSettings::styleFileName(const QString &key) const -{ - return m_styleDir.absolutePath() + '/' + key + '/' + m_ending; -} - -void ClangFormatSettings::readStyles() -{ - const QStringList dirs = m_styleDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); - for (const QString &dir : dirs) { - QFile file(m_styleDir.absoluteFilePath(dir + '/' + m_ending)); - if (file.open(QIODevice::ReadOnly)) - m_styles.insert(dir, QString::fromLocal8Bit(file.readAll())); - } -} - -class ClangFormatOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit ClangFormatOptionsPageWidget(ClangFormatSettings *settings) - { - ClangFormatSettings &s = *settings; - QGroupBox *options = nullptr; - - auto predefinedStyleButton = new QRadioButton; - s.usePredefinedStyle.adoptButton(predefinedStyleButton); - - auto customizedStyleButton = new QRadioButton(Tr::tr("Use customized style:")); - - auto styleButtonGroup = new QButtonGroup; - styleButtonGroup->addButton(predefinedStyleButton); - styleButtonGroup->addButton(customizedStyleButton); - - auto configurations = new ConfigurationPanel(this); - configurations->setSettings(&s); - configurations->setCurrentConfiguration(s.customStyle()); - - using namespace Layouting; - - auto fallbackBlob = Row { noMargin, Tr::tr("Fallback style:"), s.fallbackStyle }.emerge(); - - auto predefinedBlob = Column { noMargin, s.predefinedStyle, fallbackBlob }.emerge(); - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - s.command, br, - s.supportedMimeTypes - } - }, - Group { - title(Tr::tr("Options")), - bindTo(&options), - Form { - s.usePredefinedStyle, predefinedBlob, br, - customizedStyleButton, configurations, - }, - }, - st - }.attachTo(this); - - if (s.usePredefinedStyle.value()) - predefinedStyleButton->click(); - else - customizedStyleButton->click(); - - const auto updateEnabled = [&s, styleButtonGroup, predefinedBlob, fallbackBlob, - configurations, predefinedStyleButton] { - const bool predefSelected = styleButtonGroup->checkedButton() == predefinedStyleButton; - predefinedBlob->setEnabled(predefSelected); - fallbackBlob->setEnabled(predefSelected && s.predefinedStyle.volatileValue().toInt() == 5); // File - configurations->setEnabled(!predefSelected); - }; - updateEnabled(); - connect(styleButtonGroup, &QButtonGroup::buttonClicked, this, updateEnabled); - connect(&s.predefinedStyle, &SelectionAspect::volatileValueChanged, this, updateEnabled); - - setOnApply([settings, configurations, customizedStyleButton] { - settings->usePredefinedStyle.setValue(!customizedStyleButton->isChecked()); - settings->customStyle.setValue(configurations->currentConfiguration()); - settings->save(); - }); - - s.read(); - - connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); - options->setEnabled(s.command.pathChooser()->isValid()); - } -}; - -ClangFormatOptionsPage::ClangFormatOptionsPage(ClangFormatSettings *settings) -{ - setId("ClangFormat"); - setDisplayName(Tr::tr("Clang Format")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new ClangFormatOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.h b/src/plugins/beautifier/clangformat/clangformatsettings.h deleted file mode 100644 index 64f2b4b9bbd..00000000000 --- a/src/plugins/beautifier/clangformat/clangformatsettings.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../abstractsettings.h" - -namespace Beautifier::Internal { - -class ClangFormatSettings : public AbstractSettings -{ -public: - explicit ClangFormatSettings(); - - void createDocumentationFile() const override; - - QStringList completerWords() override; - - Utils::BoolAspect usePredefinedStyle{this}; - Utils::SelectionAspect predefinedStyle{this}; - Utils::SelectionAspect fallbackStyle{this}; - Utils::StringAspect customStyle{this}; - - QString styleFileName(const QString &key) const override; - -private: - void readStyles() override; -}; - -class ClangFormatOptionsPage final : public Core::IOptionsPage -{ -public: - explicit ClangFormatOptionsPage(ClangFormatSettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/configurationdialog.cpp b/src/plugins/beautifier/configurationdialog.cpp index 09e8c595f30..9d3cf5f5cc8 100644 --- a/src/plugins/beautifier/configurationdialog.cpp +++ b/src/plugins/beautifier/configurationdialog.cpp @@ -3,7 +3,7 @@ #include "configurationdialog.h" -#include "abstractsettings.h" +#include "beautifiertool.h" #include "beautifiertr.h" #include "configurationeditor.h" diff --git a/src/plugins/beautifier/configurationeditor.cpp b/src/plugins/beautifier/configurationeditor.cpp index a90e6230a2f..1a365dc4e7c 100644 --- a/src/plugins/beautifier/configurationeditor.cpp +++ b/src/plugins/beautifier/configurationeditor.cpp @@ -3,7 +3,7 @@ #include "configurationeditor.h" -#include "abstractsettings.h" +#include "beautifiertool.h" #include <texteditor/fontsettings.h> #include <texteditor/texteditorsettings.h> diff --git a/src/plugins/beautifier/configurationpanel.cpp b/src/plugins/beautifier/configurationpanel.cpp index fa0fca71393..fca4242e373 100644 --- a/src/plugins/beautifier/configurationpanel.cpp +++ b/src/plugins/beautifier/configurationpanel.cpp @@ -3,7 +3,7 @@ #include "configurationpanel.h" -#include "abstractsettings.h" +#include "beautifiertool.h" #include "beautifiertr.h" #include "configurationdialog.h" diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp index b0da1fc2d4c..be80de39385 100644 --- a/src/plugins/beautifier/generalsettings.cpp +++ b/src/plugins/beautifier/generalsettings.cpp @@ -6,6 +6,8 @@ #include "beautifierconstants.h" #include "beautifiertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/algorithm.h> #include <utils/genericconstants.h> #include <utils/layoutbuilder.h> @@ -14,13 +16,15 @@ using namespace Utils; namespace Beautifier::Internal { +GeneralSettings &generalSettings() +{ + static GeneralSettings theSettings; + return theSettings; +} + GeneralSettings::GeneralSettings() { - setId(Constants::OPTION_GENERAL_ID); - setDisplayName(Tr::tr("General")); - setCategory(Constants::OPTION_CATEGORY); - setDisplayCategory(Tr::tr("Beautifier")); - setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png"); + setAutoApply(false); setSettingsGroups("Beautifier", "General"); autoFormatOnSave.setSettingsKey(Utils::Constants::BEAUTIFIER_AUTO_FORMAT_ON_SAVE); @@ -61,7 +65,7 @@ GeneralSettings::GeneralSettings() QList<MimeType> GeneralSettings::allowedMimeTypes() const { - const QStringList stringTypes = autoFormatMime.value().split(';'); + const QStringList stringTypes = autoFormatMime().split(';'); QList<MimeType> types; for (QString t : stringTypes) { @@ -73,4 +77,20 @@ QList<MimeType> GeneralSettings::allowedMimeTypes() const return types; } +class GeneralSettingsPage final : public Core::IOptionsPage +{ +public: + GeneralSettingsPage() + { + setId(Constants::OPTION_GENERAL_ID); + setDisplayName(Tr::tr("General")); + setCategory(Constants::OPTION_CATEGORY); + setDisplayCategory(Tr::tr("Beautifier")); + setCategoryIconPath(":/beautifier/images/settingscategory_beautifier.png"); + setSettingsProvider([] { return &generalSettings(); }); + } +}; + +const GeneralSettingsPage settingsPage; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/generalsettings.h b/src/plugins/beautifier/generalsettings.h index 0be68b98e35..23c55e1765d 100644 --- a/src/plugins/beautifier/generalsettings.h +++ b/src/plugins/beautifier/generalsettings.h @@ -3,13 +3,12 @@ #pragma once +#include <utils/aspects.h> #include <utils/mimeutils.h> -#include <coreplugin/dialogs/ioptionspage.h> - namespace Beautifier::Internal { -class GeneralSettings : public Core::PagedSettings +class GeneralSettings final : public Utils::AspectContainer { public: GeneralSettings(); @@ -22,4 +21,6 @@ public: Utils::StringAspect autoFormatMime{this}; }; +GeneralSettings &generalSettings(); + } // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustify.cpp b/src/plugins/beautifier/uncrustify/uncrustify.cpp index 7079d4665ba..29722d4df19 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustify.cpp @@ -5,11 +5,10 @@ #include "uncrustify.h" -#include "uncrustifyconstants.h" - #include "../beautifierconstants.h" -#include "../beautifierplugin.h" +#include "../beautifiertool.h" #include "../beautifiertr.h" +#include "../configurationpanel.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -17,6 +16,7 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/icore.h> #include <coreplugin/idocument.h> #include <projectexplorer/project.h> @@ -26,47 +26,241 @@ #include <texteditor/formattexteditor.h> #include <texteditor/texteditor.h> -#include <utils/filepath.h> +#include <utils/aspects.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> +#include <utils/process.h> #include <QAction> +#include <QCheckBox> +#include <QDateTime> +#include <QFile> +#include <QFileInfo> +#include <QGroupBox> +#include <QLabel> +#include <QLineEdit> #include <QMenu> +#include <QRegularExpression> #include <QVersionNumber> +#include <QXmlStreamWriter> using namespace TextEditor; using namespace Utils; namespace Beautifier::Internal { +const char SETTINGS_NAME[] = "uncrustify"; + +static QString uDisplayName() { return Tr::tr("Uncrustify"); } + +class UncrustifySettings : public AbstractSettings +{ +public: + UncrustifySettings() + : AbstractSettings(SETTINGS_NAME, ".cfg") + { + setVersionRegExp(QRegularExpression("([0-9]{1})\\.([0-9]{2})")); + + command.setDefaultValue("uncrustify"); + command.setLabelText(Tr::tr("Uncrustify command:")); + command.setPromptDialogTitle(BeautifierTool::msgCommandPromptDialogTitle(uDisplayName())); + + useOtherFiles.setSettingsKey("useOtherFiles"); + useOtherFiles.setDefaultValue(true); + useOtherFiles.setLabelText(Tr::tr("Use file uncrustify.cfg defined in project files")); + + useHomeFile.setSettingsKey("useHomeFile"); + useHomeFile.setLabelText(Tr::tr("Use file uncrustify.cfg in HOME") + .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); + + useCustomStyle.setSettingsKey("useCustomStyle"); + useCustomStyle.setLabelText(Tr::tr("Use customized style:")); + + useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); + useSpecificConfigFile.setLabelText(Tr::tr("Use file specific uncrustify.cfg")); + + customStyle.setSettingsKey("customStyle"); + + formatEntireFileFallback.setSettingsKey("formatEntireFileFallback"); + formatEntireFileFallback.setDefaultValue(true); + formatEntireFileFallback.setLabelText(Tr::tr("Format entire file if no text was selected")); + formatEntireFileFallback.setToolTip(Tr::tr("For action Format Selected Text")); + + specificConfigFile.setSettingsKey("specificConfigFile"); + specificConfigFile.setExpectedKind(Utils::PathChooser::File); + specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); + + documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + .pathAppended(Constants::DOCUMENTATION_DIRNAME) + .pathAppended(SETTINGS_NAME).stringAppended(".xml"); + + read(); + } + + void createDocumentationFile() const override + { + Process process; + process.setTimeoutS(2); + process.setCommand({command(), {"--show-config"}}); + process.runBlocking(); + if (process.result() != ProcessResult::FinishedWithSuccess) + return; + + QFile file(documentationFilePath.toFSPathString()); + const QFileInfo fi(file); + if (!fi.exists()) + fi.dir().mkpath(fi.absolutePath()); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + return; + + bool contextWritten = false; + QXmlStreamWriter stream(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument("1.0", true); + stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); + stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); + + const QStringList lines = process.allOutput().split(QLatin1Char('\n')); + const int totalLines = lines.count(); + for (int i = 0; i < totalLines; ++i) { + const QString &line = lines.at(i); + if (line.startsWith('#') || line.trimmed().isEmpty()) + continue; + + const int firstSpace = line.indexOf(' '); + const QString keyword = line.left(firstSpace); + const QString options = line.right(line.size() - firstSpace).trimmed(); + QStringList docu; + while (++i < totalLines) { + const QString &subline = lines.at(i); + if (line.startsWith('#') || subline.trimmed().isEmpty()) { + const QString text = "<p><span class=\"option\">" + keyword + + "</span> <span class=\"param\">" + options + + "</span></p><p>" + docu.join(' ').toHtmlEscaped() + "</p>"; + stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); + stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, keyword); + stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); + stream.writeEndElement(); + contextWritten = true; + break; + } else { + docu << subline; + } + } + } + + stream.writeEndElement(); + stream.writeEndDocument(); + + // An empty file causes error messages and a contextless file preventing this function to run + // again in order to generate the documentation successfully. Thus delete the file. + if (!contextWritten) { + file.close(); + file.remove(); + } + } + + BoolAspect useOtherFiles{this}; + BoolAspect useHomeFile{this}; + BoolAspect useCustomStyle{this}; + + StringAspect customStyle{this}; + BoolAspect formatEntireFileFallback{this}; + + FilePathAspect specificConfigFile{this}; + BoolAspect useSpecificConfigFile{this}; +}; + +static UncrustifySettings &settings() +{ + static UncrustifySettings theSettings; + return theSettings; +} + +class UncrustifySettingsPageWidget : public Core::IOptionsPageWidget +{ +public: + UncrustifySettingsPageWidget() + { + UncrustifySettings &s = settings(); + + auto configurations = new ConfigurationPanel(this); + configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + configurations->setSettings(&settings()); + configurations->setCurrentConfiguration(settings().customStyle()); + + QGroupBox *options = nullptr; + + using namespace Layouting; + + Column { + Group { + title(Tr::tr("Configuration")), + Form { + s.command, br, + s.supportedMimeTypes, + } + }, + Group { + title(Tr::tr("Options")), + bindTo(&options), + Column { + s.useOtherFiles, + Row { s.useSpecificConfigFile, s.specificConfigFile }, + s.useHomeFile, + Row { s.useCustomStyle, configurations }, + s.formatEntireFileFallback + }, + }, + st + }.attachTo(this); + + s.read(); + + connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); + options->setEnabled(s.command.pathChooser()->isValid()); + + setOnApply([&s, configurations] { + s.customStyle.setValue(configurations->currentConfiguration()); + settings().apply(); + s.save(); + }); + } +}; + + +// Uncrustify + Uncrustify::Uncrustify() { Core::ActionContainer *menu = Core::ActionManager::createMenu("Uncrustify.Menu"); menu->menu()->setTitle(Tr::tr("&Uncrustify")); - m_formatFile = new QAction(BeautifierPlugin::msgFormatCurrentFile(), this); + m_formatFile = new QAction(msgFormatCurrentFile(), this); Core::Command *cmd = Core::ActionManager::registerAction(m_formatFile, "Uncrustify.FormatFile"); menu->addAction(cmd); connect(m_formatFile, &QAction::triggered, this, &Uncrustify::formatFile); - m_formatRange = new QAction(BeautifierPlugin::msgFormatSelectedText(), this); + m_formatRange = new QAction(msgFormatSelectedText(), this); cmd = Core::ActionManager::registerAction(m_formatRange, "Uncrustify.FormatSelectedText"); menu->addAction(cmd); connect(m_formatRange, &QAction::triggered, this, &Uncrustify::formatSelectedText); Core::ActionManager::actionContainer(Constants::MENU_ID)->addMenu(menu); - connect(&m_settings.supportedMimeTypes, &Utils::BaseAspect::changed, + connect(&settings().supportedMimeTypes, &Utils::BaseAspect::changed, this, [this] { updateActions(Core::EditorManager::currentEditor()); }); } QString Uncrustify::id() const { - return QLatin1String(Constants::UNCRUSTIFY_DISPLAY_NAME); + return "Uncrustify"; } void Uncrustify::updateActions(Core::IEditor *editor) { - const bool enabled = editor && m_settings.isApplicable(editor->document()); + const bool enabled = editor && settings().isApplicable(editor->document()); m_formatFile->setEnabled(enabled); m_formatRange->setEnabled(enabled); } @@ -74,20 +268,17 @@ void Uncrustify::updateActions(Core::IEditor *editor) void Uncrustify::formatFile() { const FilePath cfgFileName = configurationFile(); - if (cfgFileName.isEmpty()) { - BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile( - Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); - } else { - formatCurrentFile(command(cfgFileName)); - } + if (cfgFileName.isEmpty()) + showError(msgCannotGetConfigurationFile(uDisplayName())); + else + formatCurrentFile(textCommand(cfgFileName)); } void Uncrustify::formatSelectedText() { const FilePath cfgFileName = configurationFile(); if (cfgFileName.isEmpty()) { - BeautifierPlugin::showError(BeautifierPlugin::msgCannotGetConfigurationFile( - Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); + showError(msgCannotGetConfigurationFile(uDisplayName())); return; } @@ -107,18 +298,18 @@ void Uncrustify::formatSelectedText() if (tc.positionInBlock() > 0) tc.movePosition(QTextCursor::EndOfLine); const int endPos = tc.position(); - formatCurrentFile(command(cfgFileName, true), startPos, endPos); - } else if (m_settings.formatEntireFileFallback()) { + formatCurrentFile(textCommand(cfgFileName, true), startPos, endPos); + } else if (settings().formatEntireFileFallback()) { formatFile(); } } FilePath Uncrustify::configurationFile() const { - if (m_settings.useCustomStyle()) - return FilePath::fromUserInput(m_settings.styleFileName(m_settings.customStyle())); + if (settings().useCustomStyle()) + return settings().styleFileName(settings().customStyle()); - if (m_settings.useOtherFiles()) { + if (settings().useOtherFiles()) { using namespace ProjectExplorer; if (const Project *project = ProjectTree::currentProject()) { const FilePaths files = project->files([](const Node *n) { @@ -130,13 +321,13 @@ FilePath Uncrustify::configurationFile() const } } - if (m_settings.useSpecificConfigFile()) { - const FilePath file = m_settings.specificConfigFile(); + if (settings().useSpecificConfigFile()) { + const FilePath file = settings().specificConfigFile(); if (file.exists()) return file; } - if (m_settings.useHomeFile()) { + if (settings().useHomeFile()) { const FilePath file = FileUtils::homePath() / "uncrustify.cfg"; if (file.exists()) return file; @@ -145,36 +336,53 @@ FilePath Uncrustify::configurationFile() const return {}; } -Command Uncrustify::command() const +Command Uncrustify::textCommand() const { const FilePath cfgFile = configurationFile(); - return cfgFile.isEmpty() ? Command() : command(cfgFile, false); + return cfgFile.isEmpty() ? Command() : textCommand(cfgFile, false); } bool Uncrustify::isApplicable(const Core::IDocument *document) const { - return m_settings.isApplicable(document); + return settings().isApplicable(document); } -Command Uncrustify::command(const FilePath &cfgFile, bool fragment) const +Command Uncrustify::textCommand(const FilePath &cfgFile, bool fragment) const { - Command command; - command.setExecutable(m_settings.command()); - command.setProcessing(Command::PipeProcessing); - if (m_settings.version() >= QVersionNumber(0, 62)) { - command.addOption("--assume"); - command.addOption("%file"); + Command cmd; + cmd.setExecutable(settings().command()); + cmd.setProcessing(Command::PipeProcessing); + if (settings().version() >= QVersionNumber(0, 62)) { + cmd.addOption("--assume"); + cmd.addOption("%file"); } else { - command.addOption("-l"); - command.addOption("cpp"); + cmd.addOption("-l"); + cmd.addOption("cpp"); } - command.addOption("-L"); - command.addOption("1-2"); + cmd.addOption("-L"); + cmd.addOption("1-2"); if (fragment) - command.addOption("--frag"); - command.addOption("-c"); - command.addOption(cfgFile.path()); - return command; + cmd.addOption("--frag"); + cmd.addOption("-c"); + cmd.addOption(cfgFile.path()); + return cmd; } + +// UncrustifySettingsPage + +class UncrustifySettingsPage final : public Core::IOptionsPage +{ +public: + UncrustifySettingsPage() + { + setId("Uncrustify"); + setDisplayName(uDisplayName()); + setCategory(Constants::OPTION_CATEGORY); + setWidgetCreator([] { return new UncrustifySettingsPageWidget; }); + } +}; + +const UncrustifySettingsPage settingsPage; + } // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustify.h b/src/plugins/beautifier/uncrustify/uncrustify.h index 774a400017e..090b06e0423 100644 --- a/src/plugins/beautifier/uncrustify/uncrustify.h +++ b/src/plugins/beautifier/uncrustify/uncrustify.h @@ -3,32 +3,32 @@ #pragma once -#include "../beautifierabstracttool.h" +#include "../beautifiertool.h" -#include "uncrustifysettings.h" +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE namespace Beautifier::Internal { -class Uncrustify : public BeautifierAbstractTool +class Uncrustify : public BeautifierTool { public: Uncrustify(); QString id() const override; void updateActions(Core::IEditor *editor) override; - TextEditor::Command command() const override; + TextEditor::Command textCommand() const override; bool isApplicable(const Core::IDocument *document) const override; private: void formatFile(); void formatSelectedText(); Utils::FilePath configurationFile() const; - TextEditor::Command command(const Utils::FilePath &cfgFile, bool fragment = false) const; + TextEditor::Command textCommand(const Utils::FilePath &cfgFile, bool fragment = false) const; QAction *m_formatFile = nullptr; QAction *m_formatRange = nullptr; - UncrustifySettings m_settings; - UncrustifyOptionsPage m_page{&m_settings}; }; } // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustifyconstants.h b/src/plugins/beautifier/uncrustify/uncrustifyconstants.h deleted file mode 100644 index e8c4331be72..00000000000 --- a/src/plugins/beautifier/uncrustify/uncrustifyconstants.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QtGlobal> - -namespace Beautifier::Constants { - -const char UNCRUSTIFY_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Beautifier", "Uncrustify"); - -} // Beautifier::Constants diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp deleted file mode 100644 index 1aa9e8502c6..00000000000 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "uncrustifysettings.h" - -#include "uncrustifyconstants.h" -#include "../beautifierconstants.h" -#include "../beautifierplugin.h" -#include "../beautifiertr.h" -#include "../configurationpanel.h" - -#include <coreplugin/icore.h> - -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> -#include <utils/process.h> - -#include <QCheckBox> -#include <QDateTime> -#include <QFile> -#include <QFileInfo> -#include <QGroupBox> -#include <QLabel> -#include <QLineEdit> -#include <QRegularExpression> -#include <QXmlStreamWriter> - -using namespace Utils; - -namespace Beautifier::Internal { - -const char SETTINGS_NAME[] = "uncrustify"; - -UncrustifySettings::UncrustifySettings() - : AbstractSettings(SETTINGS_NAME, ".cfg") -{ - setVersionRegExp(QRegularExpression("([0-9]{1})\\.([0-9]{2})")); - - command.setDefaultValue("uncrustify"); - command.setLabelText(Tr::tr("Uncrustify command:")); - command.setPromptDialogTitle(BeautifierPlugin::msgCommandPromptDialogTitle( - Tr::tr(Constants::UNCRUSTIFY_DISPLAY_NAME))); - - useOtherFiles.setSettingsKey("useOtherFiles"); - useOtherFiles.setDefaultValue(true); - useOtherFiles.setLabelText(Tr::tr("Use file uncrustify.cfg defined in project files")); - - useHomeFile.setSettingsKey("useHomeFile"); - useHomeFile.setLabelText(Tr::tr("Use file uncrustify.cfg in HOME") - .replace( "HOME", QDir::toNativeSeparators(QDir::home().absolutePath()))); - - useCustomStyle.setSettingsKey("useCustomStyle"); - useCustomStyle.setLabelText(Tr::tr("Use customized style:")); - - useSpecificConfigFile.setSettingsKey("useSpecificConfigFile"); - useSpecificConfigFile.setLabelText(Tr::tr("Use file specific uncrustify.cfg")); - - customStyle.setSettingsKey("customStyle"); - - formatEntireFileFallback.setSettingsKey("formatEntireFileFallback"); - formatEntireFileFallback.setDefaultValue(true); - formatEntireFileFallback.setLabelText(Tr::tr("Format entire file if no text was selected")); - formatEntireFileFallback.setToolTip(Tr::tr("For action Format Selected Text")); - - specificConfigFile.setSettingsKey("specificConfigFile"); - specificConfigFile.setExpectedKind(Utils::PathChooser::File); - specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); - - documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) - .pathAppended(Constants::DOCUMENTATION_DIRNAME) - .pathAppended(SETTINGS_NAME).stringAppended(".xml"); - - read(); -} - -void UncrustifySettings::createDocumentationFile() const -{ - Process process; - process.setTimeoutS(2); - process.setCommand({command(), {"--show-config"}}); - process.runBlocking(); - if (process.result() != ProcessResult::FinishedWithSuccess) - return; - - QFile file(documentationFilePath.toFSPathString()); - const QFileInfo fi(file); - if (!fi.exists()) - fi.dir().mkpath(fi.absolutePath()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - return; - - bool contextWritten = false; - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument("1.0", true); - stream.writeComment("Created " + QDateTime::currentDateTime().toString(Qt::ISODate)); - stream.writeStartElement(Constants::DOCUMENTATION_XMLROOT); - - const QStringList lines = process.allOutput().split(QLatin1Char('\n')); - const int totalLines = lines.count(); - for (int i = 0; i < totalLines; ++i) { - const QString &line = lines.at(i); - if (line.startsWith('#') || line.trimmed().isEmpty()) - continue; - - const int firstSpace = line.indexOf(' '); - const QString keyword = line.left(firstSpace); - const QString options = line.right(line.size() - firstSpace).trimmed(); - QStringList docu; - while (++i < totalLines) { - const QString &subline = lines.at(i); - if (line.startsWith('#') || subline.trimmed().isEmpty()) { - const QString text = "<p><span class=\"option\">" + keyword - + "</span> <span class=\"param\">" + options - + "</span></p><p>" + docu.join(' ').toHtmlEscaped() + "</p>"; - stream.writeStartElement(Constants::DOCUMENTATION_XMLENTRY); - stream.writeTextElement(Constants::DOCUMENTATION_XMLKEY, keyword); - stream.writeTextElement(Constants::DOCUMENTATION_XMLDOC, text); - stream.writeEndElement(); - contextWritten = true; - break; - } else { - docu << subline; - } - } - } - - stream.writeEndElement(); - stream.writeEndDocument(); - - // An empty file causes error messages and a contextless file preventing this function to run - // again in order to generate the documentation successfully. Thus delete the file. - if (!contextWritten) { - file.close(); - file.remove(); - } -} - -class UncrustifyOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - explicit UncrustifyOptionsPageWidget(UncrustifySettings *settings) - { - UncrustifySettings &s = *settings; - - auto configurations = new ConfigurationPanel(this); - configurations->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - configurations->setSettings(settings); - configurations->setCurrentConfiguration(settings->customStyle()); - - QGroupBox *options = nullptr; - - using namespace Layouting; - - Column { - Group { - title(Tr::tr("Configuration")), - Form { - s.command, br, - s.supportedMimeTypes, - } - }, - Group { - title(Tr::tr("Options")), - bindTo(&options), - Column { - s.useOtherFiles, - Row { s.useSpecificConfigFile, s.specificConfigFile }, - s.useHomeFile, - Row { s.useCustomStyle, configurations }, - s.formatEntireFileFallback - }, - }, - st - }.attachTo(this); - - s.read(); - - connect(s.command.pathChooser(), &PathChooser::validChanged, options, &QWidget::setEnabled); - options->setEnabled(s.command.pathChooser()->isValid()); - - setOnApply([&s, configurations] { - s.customStyle.setValue(configurations->currentConfiguration()); - s.save(); - }); - } -}; - -UncrustifyOptionsPage::UncrustifyOptionsPage(UncrustifySettings *settings) -{ - setId("Uncrustify"); - setDisplayName(Tr::tr("Uncrustify")); - setCategory(Constants::OPTION_CATEGORY); - setWidgetCreator([settings] { return new UncrustifyOptionsPageWidget(settings); }); -} - -} // Beautifier::Internal diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.h b/src/plugins/beautifier/uncrustify/uncrustifysettings.h deleted file mode 100644 index 3911cfa9e94..00000000000 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2016 Lorenz Haas -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../abstractsettings.h" - -namespace Beautifier::Internal { - -class UncrustifySettings : public AbstractSettings -{ -public: - UncrustifySettings(); - - void createDocumentationFile() const override; - - Utils::BoolAspect useOtherFiles{this}; - Utils::BoolAspect useHomeFile{this}; - Utils::BoolAspect useCustomStyle{this}; - - Utils::StringAspect customStyle{this}; - Utils::BoolAspect formatEntireFileFallback{this}; - - Utils::FilePathAspect specificConfigFile{this}; - Utils::BoolAspect useSpecificConfigFile{this}; -}; - -class UncrustifyOptionsPage final : public Core::IOptionsPage -{ -public: - explicit UncrustifyOptionsPage(UncrustifySettings *settings); -}; - -} // Beautifier::Internal diff --git a/src/plugins/bineditor/BinEditor.json.in b/src/plugins/bineditor/BinEditor.json.in index 4aa1225f8bb..5d05094b0f8 100644 --- a/src/plugins/bineditor/BinEditor.json.in +++ b/src/plugins/bineditor/BinEditor.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"BinEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "BinEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Creator\", - \"Description\" : \"Binary editor component.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Creator", + "Description" : "Binary editor component.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/bineditor/bineditorplugin.cpp b/src/plugins/bineditor/bineditorplugin.cpp index 5133d57c9dc..54b91401ad5 100644 --- a/src/plugins/bineditor/bineditorplugin.cpp +++ b/src/plugins/bineditor/bineditorplugin.cpp @@ -40,7 +40,7 @@ using namespace Core; namespace BinEditor::Internal { -class BinEditorFactory final : public IEditorFactory +class BinEditorFactory final : public QObject, public IEditorFactory { public: BinEditorFactory(); @@ -71,7 +71,8 @@ public: void highlightAll(const QString &txt, FindFlags findFlags) override { - m_widget->highlightSearchResults(txt.toLatin1(), textDocumentFlagsForFindFlags(findFlags)); + m_widget->highlightSearchResults(txt.toLatin1(), + Utils::textDocumentFlagsForFindFlags(findFlags)); } void clearHighlights() override @@ -88,10 +89,10 @@ public: return pos; } - int res = m_widget->find(pattern, pos, textDocumentFlagsForFindFlags(findFlags)); + int res = m_widget->find(pattern, pos, Utils::textDocumentFlagsForFindFlags(findFlags)); if (res < 0) { pos = (findFlags & FindBackward) ? -1 : 0; - res = m_widget->find(pattern, pos, textDocumentFlagsForFindFlags(findFlags)); + res = m_widget->find(pattern, pos, Utils::textDocumentFlagsForFindFlags(findFlags)); if (res < 0) return res; if (wrapped) @@ -119,7 +120,8 @@ public: Result result; if (found >= 0) { result = Found; - m_widget->highlightSearchResults(pattern, textDocumentFlagsForFindFlags(findFlags)); + m_widget->highlightSearchResults(pattern, + Utils::textDocumentFlagsForFindFlags(findFlags)); m_contPos = -1; } else { if (found == -2) { @@ -154,8 +156,10 @@ public: result = Found; m_incrementalStartPos = found; m_contPos = -1; - if (wasReset) - m_widget->highlightSearchResults(pattern, textDocumentFlagsForFindFlags(findFlags)); + if (wasReset) { + m_widget->highlightSearchResults(pattern, + Utils::textDocumentFlagsForFindFlags(findFlags)); + } } else if (found == -2) { result = NotYetFound; m_contPos += findFlags & FindBackward @@ -309,9 +313,8 @@ protected: bool saveImpl(QString *errorString, const Utils::FilePath &filePath, bool autoSave) override { QTC_ASSERT(!autoSave, return true); // bineditor does not support autosave - it would be a bit expensive - const FilePath &fileNameToUse = filePath.isEmpty() ? this->filePath() : filePath; - if (m_widget->save(errorString, this->filePath(), fileNameToUse)) { - setFilePath(fileNameToUse); + if (m_widget->save(errorString, this->filePath(), filePath)) { + setFilePath(filePath); return true; } return false; diff --git a/src/plugins/bineditor/bineditorwidget.cpp b/src/plugins/bineditor/bineditorwidget.cpp index bddcae36ed1..703f608ac11 100644 --- a/src/plugins/bineditor/bineditorwidget.cpp +++ b/src/plugins/bineditor/bineditorwidget.cpp @@ -18,6 +18,7 @@ #include <texteditor/texteditorconstants.h> #include <texteditor/texteditorsettings.h> +#include <utils/algorithm.h> #include <utils/fadingindicator.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> @@ -222,9 +223,8 @@ bool BinEditorWidget::requestDataAt(qint64 pos) const it = m_data.find(block); if (it != m_data.end()) return true; - if (m_requests.contains(block)) + if (!Utils::insert(m_requests, block)) return false; - m_requests.insert(block); d->fetchData((m_baseAddr / m_blockSize + block) * m_blockSize); return true; } diff --git a/src/plugins/bookmarks/Bookmarks.json.in b/src/plugins/bookmarks/Bookmarks.json.in deleted file mode 100644 index 32ccf2418b5..00000000000 --- a/src/plugins/bookmarks/Bookmarks.json.in +++ /dev/null @@ -1,19 +0,0 @@ -{ - \"Name\" : \"Bookmarks\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" - ], - \"Category\" : \"Core\", - \"Description\" : \"Bookmarks in text editors.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList -} diff --git a/src/plugins/bookmarks/CMakeLists.txt b/src/plugins/bookmarks/CMakeLists.txt deleted file mode 100644 index 62a83b5adb1..00000000000 --- a/src/plugins/bookmarks/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -add_qtc_plugin(Bookmarks - PLUGIN_DEPENDS Core ProjectExplorer TextEditor - SOURCES - bookmark.cpp bookmark.h - bookmarkfilter.cpp bookmarkfilter.h - bookmarkmanager.cpp bookmarkmanager.h - bookmarks_global.h - bookmarkstr.h - bookmarksplugin.cpp bookmarksplugin.h -) diff --git a/src/plugins/bookmarks/bookmarks.qbs b/src/plugins/bookmarks/bookmarks.qbs deleted file mode 100644 index d42fa43cdfd..00000000000 --- a/src/plugins/bookmarks/bookmarks.qbs +++ /dev/null @@ -1,25 +0,0 @@ -import qbs 1.0 - -QtcPlugin { - name: "Bookmarks" - - Depends { name: "Qt.widgets" } - Depends { name: "Utils" } - - Depends { name: "Core" } - Depends { name: "ProjectExplorer" } - Depends { name: "TextEditor" } - - files: [ - "bookmark.cpp", - "bookmark.h", - "bookmarkfilter.cpp", - "bookmarkfilter.h", - "bookmarkmanager.cpp", - "bookmarkmanager.h", - "bookmarks_global.h", "bookmarkstr.h", - "bookmarksplugin.cpp", - "bookmarksplugin.h", - ] -} - diff --git a/src/plugins/bookmarks/bookmarks_global.h b/src/plugins/bookmarks/bookmarks_global.h deleted file mode 100644 index 2b9727d1c6e..00000000000 --- a/src/plugins/bookmarks/bookmarks_global.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace Bookmarks { -namespace Constants { - -const char BOOKMARKS_TOGGLE_ACTION[] = "Bookmarks.Toggle"; -const char BOOKMARKS_EDIT_ACTION[] = "Bookmarks.Edit"; -const char BOOKMARKS_MOVEUP_ACTION[] = "Bookmarks.MoveUp"; -const char BOOKMARKS_MOVEDOWN_ACTION[] = "Bookmarks.MoveDown"; -const char BOOKMARKS_EDITNOTE_ACTION[] = "Bookmarks.EditNote"; -const char BOOKMARKS_PREV_ACTION[] = "Bookmarks.Previous"; -const char BOOKMARKS_NEXT_ACTION[] = "Bookmarks.Next"; -const char BOOKMARKS_PREVDIR_ACTION[] = "Bookmarks.PreviousDirectory"; -const char BOOKMARKS_NEXTDIR_ACTION[] = "Bookmarks.NextDirectory"; -const char BOOKMARKS_PREVDOC_ACTION[] = "Bookmarks.PreviousDocument"; -const char BOOKMARKS_NEXTDOC_ACTION[] = "Bookmarks.NextDocument"; -const char BOOKMARKS_TEXT_MARK_CATEGORY[] = "Bookmarks.TextMarkCategory"; - -const char BOOKMARKS_MENU[] = "Bookmarks.Menu"; -const char BOOKMARKS_CONTEXT[] = "Bookmarks"; - -} // namespace Constants -} // namespace Bookmarks diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp deleted file mode 100644 index 7473876ec63..00000000000 --- a/src/plugins/bookmarks/bookmarksplugin.cpp +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "bookmarksplugin.h" - -#include "bookmarkfilter.h" -#include "bookmarkmanager.h" -#include "bookmarks_global.h" -#include "bookmarkstr.h" - -#include <coreplugin/icore.h> -#include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/actionmanager/actioncontainer.h> -#include <coreplugin/actionmanager/command.h> - -#include <texteditor/texteditor.h> -#include <texteditor/textdocument.h> -#include <texteditor/texteditorconstants.h> - -#include <utils/fileutils.h> -#include <utils/utilsicons.h> - -#include <QMenu> - -using namespace Core; -using namespace TextEditor; -using namespace Utils; - -using namespace Bookmarks::Constants; - -namespace Bookmarks::Internal { - -class BookmarksPluginPrivate : public QObject -{ -public: - BookmarksPluginPrivate(); - - void updateActions(bool enableToggle, int stateMask); - void editorOpened(Core::IEditor *editor); - void editorAboutToClose(Core::IEditor *editor); - - void requestContextMenu(TextEditor::TextEditorWidget *widget, - int lineNumber, QMenu *menu); - - BookmarkManager m_bookmarkManager; - BookmarkFilter m_bookmarkFilter; - BookmarkViewFactory m_bookmarkViewFactory; - - QAction m_toggleAction{Tr::tr("Toggle Bookmark"), nullptr}; - QAction m_editAction{Tr::tr("Edit Bookmark"), nullptr}; - QAction m_prevAction{Tr::tr("Previous Bookmark"), nullptr}; - QAction m_nextAction{Tr::tr("Next Bookmark"), nullptr}; - QAction m_docPrevAction{Tr::tr("Previous Bookmark in Document"), nullptr}; - QAction m_docNextAction{Tr::tr("Next Bookmark in Document"), nullptr}; - QAction m_editBookmarkAction{Tr::tr("Edit Bookmark"), nullptr}; - QAction m_bookmarkMarginAction{Tr::tr("Toggle Bookmark"), nullptr}; - - int m_marginActionLineNumber = 0; - Utils::FilePath m_marginActionFileName; -}; - -BookmarksPlugin::~BookmarksPlugin() -{ - delete d; -} - -void BookmarksPlugin::initialize() -{ - d = new BookmarksPluginPrivate; -} - -BookmarksPluginPrivate::BookmarksPluginPrivate() - : m_bookmarkFilter(&m_bookmarkManager) - , m_bookmarkViewFactory(&m_bookmarkManager) -{ - ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); - ActionContainer *touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); - ActionContainer *mbm = ActionManager::createMenu(Id(BOOKMARKS_MENU)); - mbm->menu()->setTitle(Tr::tr("&Bookmarks")); - mtools->addMenu(mbm); - - const Context editorManagerContext(Core::Constants::C_EDITORMANAGER); - - // Toggle - Command *cmd = ActionManager::registerAction(&m_toggleAction, BOOKMARKS_TOGGLE_ACTION, - editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M"))); - cmd->setTouchBarIcon(Utils::Icons::MACOS_TOUCHBAR_BOOKMARK.icon()); - mbm->addAction(cmd); - touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR); - - cmd = ActionManager::registerAction(&m_editAction, BOOKMARKS_EDIT_ACTION, editorManagerContext); - cmd->setDefaultKeySequence( - QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M"))); - mbm->addAction(cmd); - - mbm->addSeparator(); - - // Previous - m_prevAction.setIcon(Utils::Icons::PREV_TOOLBAR.icon()); - m_prevAction.setIconVisibleInMenu(false); - cmd = ActionManager::registerAction(&m_prevAction, BOOKMARKS_PREV_ACTION, editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+,") - : Tr::tr("Ctrl+,"))); - mbm->addAction(cmd); - - // Next - m_nextAction.setIcon(Utils::Icons::NEXT_TOOLBAR.icon()); - m_nextAction.setIconVisibleInMenu(false); - cmd = ActionManager::registerAction(&m_nextAction, BOOKMARKS_NEXT_ACTION, editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+.") - : Tr::tr("Ctrl+."))); - mbm->addAction(cmd); - - mbm->addSeparator(); - - // Previous Doc - cmd = ActionManager::registerAction(&m_docPrevAction, BOOKMARKS_PREVDOC_ACTION, - editorManagerContext); - mbm->addAction(cmd); - - // Next Doc - cmd = ActionManager::registerAction(&m_docNextAction, BOOKMARKS_NEXTDOC_ACTION, - editorManagerContext); - mbm->addAction(cmd); - - connect(&m_toggleAction, &QAction::triggered, this, [this] { - IEditor *editor = EditorManager::currentEditor(); - auto widget = TextEditorWidget::fromEditor(editor); - if (widget && editor && !editor->document()->isTemporary()) - m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine()); - }); - - connect(&m_editAction, &QAction::triggered, this, [this] { - IEditor *editor = EditorManager::currentEditor(); - auto widget = TextEditorWidget::fromEditor(editor); - if (widget && editor && !editor->document()->isTemporary()) { - const FilePath filePath = editor->document()->filePath(); - const int line = editor->currentLine(); - if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line)) - m_bookmarkManager.toggleBookmark(filePath, line); - m_bookmarkManager.editByFileAndLine(filePath, line); - } - }); - - connect(&m_prevAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::prev); - connect(&m_nextAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::next); - connect(&m_docPrevAction, &QAction::triggered, - &m_bookmarkManager, &BookmarkManager::prevInDocument); - connect(&m_docNextAction, &QAction::triggered, - &m_bookmarkManager, &BookmarkManager::nextInDocument); - - connect(&m_editBookmarkAction, &QAction::triggered, this, [this] { - m_bookmarkManager.editByFileAndLine(m_marginActionFileName, m_marginActionLineNumber); - }); - - connect(&m_bookmarkManager, &BookmarkManager::updateActions, - this, &BookmarksPluginPrivate::updateActions); - updateActions(false, m_bookmarkManager.state()); - - connect(&m_bookmarkMarginAction, &QAction::triggered, this, [this] { - m_bookmarkManager.toggleBookmark(m_marginActionFileName, m_marginActionLineNumber); - }); - - // EditorManager - connect(EditorManager::instance(), &EditorManager::editorAboutToClose, - this, &BookmarksPluginPrivate::editorAboutToClose); - connect(EditorManager::instance(), &EditorManager::editorOpened, - this, &BookmarksPluginPrivate::editorOpened); -} - -void BookmarksPluginPrivate::updateActions(bool enableToggle, int state) -{ - const bool hasbm = state >= BookmarkManager::HasBookMarks; - const bool hasdocbm = state == BookmarkManager::HasBookmarksInDocument; - - m_toggleAction.setEnabled(enableToggle); - m_editAction.setEnabled(enableToggle); - m_prevAction.setEnabled(hasbm); - m_nextAction.setEnabled(hasbm); - m_docPrevAction.setEnabled(hasdocbm); - m_docNextAction.setEnabled(hasdocbm); -} - -void BookmarksPluginPrivate::editorOpened(IEditor *editor) -{ - if (auto widget = TextEditorWidget::fromEditor(editor)) { - connect(widget, &TextEditorWidget::markRequested, - this, [this, editor](TextEditorWidget *, int line, TextMarkRequestKind kind) { - if (kind == BookmarkRequest && !editor->document()->isTemporary()) - m_bookmarkManager.toggleBookmark(editor->document()->filePath(), line); - }); - - connect(widget, &TextEditorWidget::markContextMenuRequested, - this, &BookmarksPluginPrivate::requestContextMenu); - } -} - -void BookmarksPluginPrivate::editorAboutToClose(IEditor *editor) -{ - if (auto widget = TextEditorWidget::fromEditor(editor)) { - disconnect(widget, &TextEditorWidget::markContextMenuRequested, - this, &BookmarksPluginPrivate::requestContextMenu); - } -} - -void BookmarksPluginPrivate::requestContextMenu(TextEditorWidget *widget, - int lineNumber, QMenu *menu) -{ - if (widget->textDocument()->isTemporary()) - return; - - m_marginActionLineNumber = lineNumber; - m_marginActionFileName = widget->textDocument()->filePath(); - - menu->addAction(&m_bookmarkMarginAction); - if (m_bookmarkManager.hasBookmarkInPosition(m_marginActionFileName, m_marginActionLineNumber)) - menu->addAction(&m_editBookmarkAction); -} - -} // Bookmarks::Internal diff --git a/src/plugins/bookmarks/bookmarksplugin.h b/src/plugins/bookmarks/bookmarksplugin.h deleted file mode 100644 index 06764043c7b..00000000000 --- a/src/plugins/bookmarks/bookmarksplugin.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace Bookmarks::Internal { - -class BookmarksPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Bookmarks.json") - - ~BookmarksPlugin() final; - - void initialize() final; - - class BookmarksPluginPrivate *d = nullptr; -}; - -} // Bookmarks::Internal diff --git a/src/plugins/boot2qt/Boot2Qt.json.in b/src/plugins/boot2qt/Boot2Qt.json.in index 13b22578e2b..6ab0b2a7d60 100644 --- a/src/plugins/boot2qt/Boot2Qt.json.in +++ b/src/plugins/boot2qt/Boot2Qt.json.in @@ -1,21 +1,21 @@ { - \"Name\" : \"Boot2Qt\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Boot2Qt", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Support for the Boot2Qt Device access using the Qt Debug Bridge.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Support for the Boot2Qt Device access using the Qt Debug Bridge.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/boot2qt/CMakeLists.txt b/src/plugins/boot2qt/CMakeLists.txt index ba8faecd31b..156150422b6 100644 --- a/src/plugins/boot2qt/CMakeLists.txt +++ b/src/plugins/boot2qt/CMakeLists.txt @@ -6,7 +6,6 @@ add_qtc_plugin(Boot2Qt qdb.qrc qdb_global.h qdbconstants.h - qdbdeployconfigurationfactory.cpp qdbdeployconfigurationfactory.h qdbdevice.cpp qdbdevice.h qdbdevicedebugsupport.cpp qdbdevicedebugsupport.h qdbmakedefaultappstep.cpp qdbmakedefaultappstep.h diff --git a/src/plugins/boot2qt/boot2qt.qbs b/src/plugins/boot2qt/boot2qt.qbs index ba2a5cd6dc8..6be777e17c1 100644 --- a/src/plugins/boot2qt/boot2qt.qbs +++ b/src/plugins/boot2qt/boot2qt.qbs @@ -21,8 +21,6 @@ QtcPlugin { "qdbutils.h", "qdbconstants.h", "qdb_global.h", - "qdbdeployconfigurationfactory.cpp", - "qdbdeployconfigurationfactory.h", "qdbdevice.cpp", "qdbdevice.h", "qdbdevicedebugsupport.cpp", diff --git a/src/plugins/boot2qt/device-detection/devicedetector.cpp b/src/plugins/boot2qt/device-detection/devicedetector.cpp index 78dfeb259de..10faa4fb3ca 100644 --- a/src/plugins/boot2qt/device-detection/devicedetector.cpp +++ b/src/plugins/boot2qt/device-detection/devicedetector.cpp @@ -82,7 +82,7 @@ void DeviceDetector::handleDeviceEvent(QdbDeviceTracker::DeviceEventType eventTy const QString name = Tr::tr("Qt Debug Bridge device %1").arg(serial); QdbDevice::Ptr device = QdbDevice::create(); device->setupId(IDevice::AutoDetected, deviceId); - device->setDisplayName(name); + device->settings()->displayName.setValue(name); device->setType(Qdb::Constants::QdbLinuxOsType); device->setMachineType(IDevice::Hardware); diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp deleted file mode 100644 index 3abb700df1d..00000000000 --- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qdbdeployconfigurationfactory.h" - -#include "qdbconstants.h" -#include "qdbtr.h" - -#include <projectexplorer/deploymentdataview.h> -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/project.h> -#include <projectexplorer/target.h> - -#include <remotelinux/remotelinux_constants.h> - -using namespace ProjectExplorer; - -namespace Qdb::Internal { - -QdbDeployConfigurationFactory::QdbDeployConfigurationFactory() -{ - setConfigBaseId(Constants::QdbDeployConfigurationId); - addSupportedTargetDeviceType(Constants::QdbLinuxOsType); - setDefaultDisplayName(Tr::tr("Deploy to Boot2Qt target")); - setUseDeploymentDataView(); - - addInitialStep(RemoteLinux::Constants::MakeInstallStepId, [](Target *target) { - const Project * const prj = target->project(); - return prj->deploymentKnowledge() == DeploymentKnowledge::Bad - && prj->hasMakeInstallEquivalent(); - }); - addInitialStep(Qdb::Constants::QdbStopApplicationStepId); - addInitialStep(RemoteLinux::Constants::RsyncDeployStepId, [](Target *target) { - auto device = DeviceKitAspect::device(target->kit()); - auto buildDevice = BuildDeviceKitAspect::device(target->kit()); - if (buildDevice && buildDevice->rootPath().needsDevice()) - return false; - return !device - || (device && device->extraData(RemoteLinux::Constants::SupportsRSync).toBool()); - }); - addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) { - auto device = DeviceKitAspect::device(target->kit()); - auto buildDevice = BuildDeviceKitAspect::device(target->kit()); - if (buildDevice && buildDevice->rootPath().needsDevice()) - return false; - return device && !device->extraData(RemoteLinux::Constants::SupportsRSync).toBool(); - }); - // This step is for: - // a) A remote build device, as they do not support real rsync yet. - // b) If there is no target device setup yet. - addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) { - auto device = DeviceKitAspect::device(target->kit()); - auto buildDevice = BuildDeviceKitAspect::device(target->kit()); - if (buildDevice && buildDevice->rootPath().needsDevice()) - return true; - return false; - }); -} - -} // Qdb::Internal diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.h b/src/plugins/boot2qt/qdbdeployconfigurationfactory.h deleted file mode 100644 index fa006613979..00000000000 --- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/deployconfiguration.h> - -namespace Qdb::Internal { - -class QdbDeployConfigurationFactory final : public ProjectExplorer::DeployConfigurationFactory -{ -public: - QdbDeployConfigurationFactory(); -}; - -} // Qdb::Internal diff --git a/src/plugins/boot2qt/qdbdevice.cpp b/src/plugins/boot2qt/qdbdevice.cpp index 43783279d69..717a945e9ba 100644 --- a/src/plugins/boot2qt/qdbdevice.cpp +++ b/src/plugins/boot2qt/qdbdevice.cpp @@ -137,22 +137,22 @@ QString QdbDevice::serialNumber() const return m_serialNumber; } -void QdbDevice::fromMap(const QVariantMap &map) +void QdbDevice::fromMap(const Store &map) { ProjectExplorer::IDevice::fromMap(map); setSerialNumber(map.value("Qdb.SerialNumber").toString()); } -QVariantMap QdbDevice::toMap() const +Store QdbDevice::toMap() const { - QVariantMap map = ProjectExplorer::IDevice::toMap(); + Store map = ProjectExplorer::IDevice::toMap(); map.insert("Qdb.SerialNumber", serialNumber()); return map; } void QdbDevice::setupDefaultNetworkSettings(const QString &host) { - setFreePorts(Utils::PortList::fromString("10000-10100")); + setFreePorts(PortList::fromString("10000-10100")); SshParameters parameters = sshParameters(); parameters.setHost(host); @@ -226,7 +226,7 @@ public: { QdbDevice::Ptr device = QdbDevice::create(); - device->setDisplayName(settingsPage.deviceName()); + device->settings()->displayName.setValue(settingsPage.deviceName()); device->setupId(ProjectExplorer::IDevice::ManuallyAdded, Utils::Id()); device->setType(Constants::QdbLinuxOsType); device->setMachineType(ProjectExplorer::IDevice::Hardware); diff --git a/src/plugins/boot2qt/qdbdevice.h b/src/plugins/boot2qt/qdbdevice.h index 300fe826329..6c502c2ab80 100644 --- a/src/plugins/boot2qt/qdbdevice.h +++ b/src/plugins/boot2qt/qdbdevice.h @@ -26,8 +26,8 @@ public: void setupDefaultNetworkSettings(const QString &host); protected: - void fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Utils::Store &map) final; + Utils::Store toMap() const final; private: QdbDevice(); diff --git a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp index d6e198538f9..f9cd3dd6d11 100644 --- a/src/plugins/boot2qt/qdbdevicedebugsupport.cpp +++ b/src/plugins/boot2qt/qdbdevicedebugsupport.cpp @@ -85,7 +85,7 @@ public: upperPort = qmlServerPort; } if (m_usePerf) { - QVariantMap settingsData = runControl()->settingsData("Analyzer.Perf.Settings"); + Store settingsData = runControl()->settingsData("Analyzer.Perf.Settings"); QVariant perfRecordArgs = settingsData.value("Analyzer.Perf.RecordArguments"); QString args = Utils::transform(perfRecordArgs.toStringList(), [](QString arg) { return arg.replace(',', ",,"); diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp index ae5806deb95..fbff89bd398 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp +++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp @@ -28,19 +28,15 @@ public: QdbMakeDefaultAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto selection = addAspect<SelectionAspect>(); - selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault"); - selection->addOption(Tr::tr("Set this application to start by default")); - selection->addOption(Tr::tr("Reset default application")); + selection.setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault"); + selection.addOption(Tr::tr("Set this application to start by default")); + selection.addOption(Tr::tr("Reset default application")); - setInternalInitializer([this, selection] { - m_makeDefault = selection->value() == 0; - return isDeploymentPossible(); - }); + setInternalInitializer([this] { return isDeploymentPossible(); }); } private: - Group deployRecipe() final + GroupItem deployRecipe() final { const auto setupHandler = [this](Process &process) { QString remoteExe; @@ -49,7 +45,7 @@ private: remoteExe = exeAspect->executable().nativePath(); } CommandLine cmd{deviceConfiguration()->filePath(Constants::AppcontrollerFilepath)}; - if (m_makeDefault && !remoteExe.isEmpty()) + if (selection() == 0 && !remoteExe.isEmpty()) cmd.addArgs({"--make-default", remoteExe}); else cmd.addArg("--remove-default"); @@ -60,7 +56,7 @@ private: }); }; const auto doneHandler = [this](const Process &) { - if (m_makeDefault) + if (selection() == 0) addProgressMessage(Tr::tr("Application set as the default one.")); else addProgressMessage(Tr::tr("Reset the default application.")); @@ -68,10 +64,10 @@ private: const auto errorHandler = [this](const Process &process) { addErrorMessage(Tr::tr("Remote process failed: %1").arg(process.errorString())); }; - return Group { ProcessTask(setupHandler, doneHandler, errorHandler) }; + return ProcessTask(setupHandler, doneHandler, errorHandler); } - bool m_makeDefault = true; + SelectionAspect selection{this}; }; // QdbMakeDefaultAppStepFactory diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index 8c806cb158f..a66bf2846a6 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -5,7 +5,6 @@ #include "device-detection/devicedetector.h" #include "qdbconstants.h" -#include "qdbdeployconfigurationfactory.h" #include "qdbdevice.h" #include "qdbstopapplicationstep.h" #include "qdbmakedefaultappstep.h" @@ -19,9 +18,11 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/icore.h> +#include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -60,7 +61,7 @@ static void startFlashingWizard() static bool isFlashActionDisabled() { - QSettings * const settings = Core::ICore::settings(); + QtcSettings * const settings = Core::ICore::settings(); settings->beginGroup(settingsGroupKey()); bool disabled = settings->value("flashActionDisabled", false).toBool(); settings->endGroup(); @@ -107,6 +108,50 @@ public: } }; +class QdbDeployConfigurationFactory final : public DeployConfigurationFactory +{ +public: + QdbDeployConfigurationFactory() + { + setConfigBaseId(Constants::QdbDeployConfigurationId); + addSupportedTargetDeviceType(Constants::QdbLinuxOsType); + setDefaultDisplayName(Tr::tr("Deploy to Boot2Qt target")); + setUseDeploymentDataView(); + + addInitialStep(RemoteLinux::Constants::MakeInstallStepId, [](Target *target) { + const Project * const prj = target->project(); + return prj->deploymentKnowledge() == DeploymentKnowledge::Bad + && prj->hasMakeInstallEquivalent(); + }); + addInitialStep(Qdb::Constants::QdbStopApplicationStepId); + addInitialStep(RemoteLinux::Constants::GenericDeployStepId, [](Target *target) { + auto device = DeviceKitAspect::device(target->kit()); + auto buildDevice = BuildDeviceKitAspect::device(target->kit()); + if (buildDevice && buildDevice->rootPath().needsDevice()) + return false; + return !device || (device + && device->extraData(ProjectExplorer::Constants::SUPPORTS_RSYNC).toBool()); + }); + addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) { + auto device = DeviceKitAspect::device(target->kit()); + auto buildDevice = BuildDeviceKitAspect::device(target->kit()); + if (buildDevice && buildDevice->rootPath().needsDevice()) + return false; + return device && !device->extraData(ProjectExplorer::Constants::SUPPORTS_RSYNC).toBool(); + }); + // This step is for: + // a) A remote build device, as they do not support real rsync yet. + // b) If there is no target device setup yet. + addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) { + auto device = DeviceKitAspect::device(target->kit()); + auto buildDevice = BuildDeviceKitAspect::device(target->kit()); + if (buildDevice && buildDevice->rootPath().needsDevice()) + return true; + return false; + }); + } +}; + class QdbPluginPrivate : public QObject { public: @@ -120,7 +165,7 @@ public: QdbMakeDefaultAppStepFactory m_makeDefaultAppStepFactory; QdbDeployStepFactory m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId}; - QdbDeployStepFactory m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId}; + QdbDeployStepFactory m_rsyncDeployStepFactory{RemoteLinux::Constants::GenericDeployStepId}; QdbDeployStepFactory m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId}; const QList<Id> supportedRunConfigs { diff --git a/src/plugins/boot2qt/qdbrunconfiguration.cpp b/src/plugins/boot2qt/qdbrunconfiguration.cpp index d288289dfa7..55d84445f40 100644 --- a/src/plugins/boot2qt/qdbrunconfiguration.cpp +++ b/src/plugins/boot2qt/qdbrunconfiguration.cpp @@ -10,7 +10,7 @@ #include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/deploymentdata.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> @@ -25,97 +25,85 @@ using namespace Utils; namespace Qdb::Internal { -// FullCommandLineAspect - -class FullCommandLineAspect : public StringAspect -{ -public: - explicit FullCommandLineAspect(RunConfiguration *rc) - { - setLabelText(Tr::tr("Full command line:")); - - auto exeAspect = rc->aspect<ExecutableAspect>(); - auto argumentsAspect = rc->aspect<ArgumentsAspect>(); - - auto updateCommandLine = [this, exeAspect, argumentsAspect] { - CommandLine plain{exeAspect->executable(), argumentsAspect->arguments(), CommandLine::Raw}; - CommandLine cmd; - cmd.setExecutable(plain.executable().withNewPath(Constants::AppcontrollerFilepath)); - cmd.addCommandLineAsArgs(plain); - setValue(cmd.toUserOutput()); - }; - - connect(argumentsAspect, &ArgumentsAspect::changed, this, updateCommandLine); - connect(exeAspect, &ExecutableAspect::changed, this, updateCommandLine); - updateCommandLine(); - } -}; - - // QdbRunConfiguration class QdbRunConfiguration : public RunConfiguration { public: - QdbRunConfiguration(Target *target, Utils::Id id); + QdbRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + setDefaultDisplayName(Tr::tr("Run on Boot2Qt Device")); + + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setSettingsKey("QdbRunConfig.RemoteExecutable"); + executable.setLabelText(Tr::tr("Executable on device:")); + executable.setPlaceHolderText(Tr::tr("Remote path not set")); + executable.makeOverridable("QdbRunConfig.AlternateRemoteExecutable", + "QdbRunCofig.UseAlternateRemoteExecutable"); + + symbolFile.setSettingsKey("QdbRunConfig.LocalExecutable"); + symbolFile.setLabelText(Tr::tr("Executable on host:")); + + environment.setDeviceSelector(target, EnvironmentAspect::RunDevice); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + fullCommand.setLabelText(Tr::tr("Full command line:")); + + setUpdater([this, target] { + const BuildTargetInfo bti = buildTargetInfo(); + const FilePath localExecutable = bti.targetFilePath; + const DeployableFile depFile = target->deploymentData().deployableForLocalFile(localExecutable); + IDevice::ConstPtr dev = DeviceKitAspect::device(target->kit()); + QTC_ASSERT(dev, return); + executable.setExecutable(dev->filePath(depFile.remoteFilePath())); + symbolFile.setValue(localExecutable); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + connect(target, &Target::deploymentDataChanged, this, &RunConfiguration::update); + connect(target, &Target::kitChanged, this, &RunConfiguration::update); + + auto updateFullCommand = [this] { + CommandLine plain{executable(), arguments(), CommandLine::Raw}; + CommandLine cmd; + cmd.setExecutable(plain.executable().withNewPath(Constants::AppcontrollerFilepath)); + cmd.addCommandLineAsArgs(plain); + fullCommand.setValue(cmd.toUserOutput()); + }; + + connect(&arguments, &BaseAspect::changed, this, updateFullCommand); + connect(&executable, &BaseAspect::changed, this, updateFullCommand); + updateFullCommand(); + } private: - Tasks checkForIssues() const override; - QString defaultDisplayName() const; -}; - -QdbRunConfiguration::QdbRunConfiguration(Target *target, Id id) - : RunConfiguration(target, id) -{ - auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setSettingsKey("QdbRunConfig.RemoteExecutable"); - exeAspect->setLabelText(Tr::tr("Executable on device:")); - exeAspect->setPlaceHolderText(Tr::tr("Remote path not set")); - exeAspect->makeOverridable("QdbRunConfig.AlternateRemoteExecutable", - "QdbRunCofig.UseAlternateRemoteExecutable"); - - auto symbolsAspect = addAspect<SymbolFileAspect>(); - symbolsAspect->setSettingsKey("QdbRunConfig.LocalExecutable"); - symbolsAspect->setLabelText(Tr::tr("Executable on host:")); - symbolsAspect->setDisplayStyle(SymbolFileAspect::LabelDisplay); - - auto envAspect = addAspect<RemoteLinux::RemoteLinuxEnvironmentAspect>(target); - - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<FullCommandLineAspect>(this); - - setUpdater([this, target, exeAspect, symbolsAspect] { - const BuildTargetInfo bti = buildTargetInfo(); - const FilePath localExecutable = bti.targetFilePath; - const DeployableFile depFile = target->deploymentData().deployableForLocalFile(localExecutable); - IDevice::ConstPtr dev = DeviceKitAspect::device(target->kit()); - QTC_ASSERT(dev, return); - exeAspect->setExecutable(dev->filePath(depFile.remoteFilePath())); - symbolsAspect->setFilePath(localExecutable); - }); - - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - connect(target, &Target::deploymentDataChanged, this, &RunConfiguration::update); - connect(target, &Target::kitChanged, this, &RunConfiguration::update); - - setDefaultDisplayName(Tr::tr("Run on Boot2Qt Device")); -} - -Tasks QdbRunConfiguration::checkForIssues() const -{ - Tasks tasks; - if (aspect<ExecutableAspect>()->executable().toString().isEmpty()) { - tasks << BuildSystemTask(Task::Warning, Tr::tr("The remote executable must be set " - "in order to run on a Boot2Qt device.")); + Tasks checkForIssues() const override + { + Tasks tasks; + if (executable().isEmpty()) { + tasks << BuildSystemTask(Task::Warning, Tr::tr("The remote executable must be set " + "in order to run on a Boot2Qt device.")); + } + return tasks; } - return tasks; -} -QString QdbRunConfiguration::defaultDisplayName() const -{ - return RunConfigurationFactory::decoratedTargetName(buildKey(), target()); -} + QString defaultDisplayName() const + { + return RunConfigurationFactory::decoratedTargetName(buildKey(), target()); + } + + ExecutableAspect executable{this}; + SymbolFileAspect symbolFile{this}; + RemoteLinux::RemoteLinuxEnvironmentAspect environment{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + StringAspect fullCommand{this}; +}; // QdbRunConfigurationFactory diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp index 1949aac9580..4a51f843904 100644 --- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp @@ -7,7 +7,7 @@ #include "qdbtr.h" #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -34,10 +34,10 @@ public: setInternalInitializer([this] { return isDeploymentPossible(); }); } - Group deployRecipe() final; + GroupItem deployRecipe() final; }; -Group QdbStopApplicationStep::deployRecipe() +GroupItem QdbStopApplicationStep::deployRecipe() { const auto setupHandler = [this](Process &process) { const auto device = DeviceKitAspect::device(target()->kit()); @@ -71,7 +71,7 @@ Group QdbStopApplicationStep::deployRecipe() addErrorMessage(failureMessage); } }; - return Group { ProcessTask(setupHandler, doneHandler, errorHandler) }; + return ProcessTask(setupHandler, doneHandler, errorHandler); } // QdbStopApplicationStepFactory diff --git a/src/plugins/boot2qt/qdbutils.cpp b/src/plugins/boot2qt/qdbutils.cpp index fcc6e38da29..922f0838f7a 100644 --- a/src/plugins/boot2qt/qdbutils.cpp +++ b/src/plugins/boot2qt/qdbutils.cpp @@ -34,7 +34,7 @@ FilePath findTool(QdbTool tool) QString filePath = Utils::qtcEnvironmentVariable(overridingEnvironmentVariable(tool)); if (filePath.isEmpty()) { - QSettings * const settings = Core::ICore::settings(); + QtcSettings * const settings = Core::ICore::settings(); settings->beginGroup(settingsGroupKey()); filePath = settings->value(settingsKey(tool)).toString(); settings->endGroup(); @@ -72,20 +72,21 @@ void showMessage(const QString &message, bool important) Core::MessageManager::writeSilently(fullMessage); } -QString settingsGroupKey() +Key settingsGroupKey() { - return QLatin1String("Boot2Qt"); + return "Boot2Qt"; } -QString settingsKey(QdbTool tool) +Key settingsKey(QdbTool tool) { switch (tool) { case QdbTool::FlashingWizard: - return QLatin1String("flashingWizardFilePath"); + return "flashingWizardFilePath"; case QdbTool::Qdb: - return QLatin1String("qdbFilePath"); + return "qdbFilePath"; } - QTC_ASSERT(false, return QString()); + QTC_CHECK(false); + return {}; } } // Qdb::Internal diff --git a/src/plugins/boot2qt/qdbutils.h b/src/plugins/boot2qt/qdbutils.h index 1a04007218a..61b8ab71434 100644 --- a/src/plugins/boot2qt/qdbutils.h +++ b/src/plugins/boot2qt/qdbutils.h @@ -4,6 +4,7 @@ #pragma once #include <utils/filepath.h> +#include <utils/storekey.h> namespace Qdb::Internal { @@ -15,7 +16,7 @@ enum class QdbTool { Utils::FilePath findTool(QdbTool tool); QString overridingEnvironmentVariable(QdbTool tool); void showMessage(const QString &message, bool important = false); -QString settingsGroupKey(); -QString settingsKey(QdbTool tool); +Utils::Key settingsGroupKey(); +Utils::Key settingsKey(QdbTool tool); } // Qdb::Internal diff --git a/src/plugins/clangcodemodel/ClangCodeModel.json.in b/src/plugins/clangcodemodel/ClangCodeModel.json.in index ca57ed80c81..4702372b83b 100644 --- a/src/plugins/clangcodemodel/ClangCodeModel.json.in +++ b/src/plugins/clangcodemodel/ClangCodeModel.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"ClangCodeModel\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ClangCodeModel", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"C++\", - \"Description\" : \"Clang Code Model plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "C++", + "Description" : "Clang Code Model plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs index 903a81147a9..16e4d00035e 100644 --- a/src/plugins/clangcodemodel/clangcodemodel.qbs +++ b/src/plugins/clangcodemodel/clangcodemodel.qbs @@ -10,7 +10,7 @@ QtcPlugin { Depends { name: "CppEditor" } Depends { name: "LanguageClient" } Depends { name: "ProjectExplorer" } - Depends { name: "QtSupport"; condition: qtc.testsEnabled } + Depends { name: "QtSupport"; condition: qtc.withPluginTests } Depends { name: "TextEditor" } Depends { name: "Utils" } diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 9b5ec425231..ac194abfbf9 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -53,7 +53,7 @@ void ClangCodeModelPlugin::generateCompilationDB() if (!target) return; - const auto projectInfo = CppModelManager::instance()->projectInfo(target->project()); + const auto projectInfo = CppModelManager::projectInfo(target->project()); if (!projectInfo) return; FilePath baseDir = projectInfo->buildRoot(); @@ -77,9 +77,10 @@ ClangCodeModelPlugin::~ClangCodeModelPlugin() void ClangCodeModelPlugin::initialize() { - TaskHub::addCategory(Constants::TASK_CATEGORY_DIAGNOSTICS, Tr::tr("Clang Code Model")); - CppEditor::CppModelManager::instance()->activateClangCodeModel( - std::make_unique<ClangModelManagerSupport>()); + TaskHub::addCategory({Constants::TASK_CATEGORY_DIAGNOSTICS, + Tr::tr("Clang Code Model"), + Tr::tr("C++ code issues that Clangd found in the current document.")}); + CppEditor::CppModelManager::activateClangCodeModel(std::make_unique<ClangModelManagerSupport>()); createCompilationDBAction(); #ifdef WITH_TESTS @@ -89,6 +90,7 @@ void ClangCodeModelPlugin::initialize() addTest<Tests::ClangdTestFindReferences>(); addTest<Tests::ClangdTestFollowSymbol>(); addTest<Tests::ClangdTestHighlighting>(); + addTest<Tests::ClangdTestIndirectChanges>(); addTest<Tests::ClangdTestLocalReferences>(); addTest<Tests::ClangdTestTooltips>(); addTest<Tests::ClangFixItTest>(); @@ -135,8 +137,8 @@ void ClangCodeModelPlugin::createCompilationDBAction() "No active project."); return; } - const CppEditor::ProjectInfo::ConstPtr projectInfo = CppEditor::CppModelManager::instance() - ->projectInfo(project); + const CppEditor::ProjectInfo::ConstPtr projectInfo = + CppEditor::CppModelManager::projectInfo(project); if (!projectInfo || projectInfo->projectParts().isEmpty()) { MessageManager::writeDisrupting("Cannot generate compilation database: " "Project has no C/C++ project parts."); diff --git a/src/plugins/clangcodemodel/clangdast.cpp b/src/plugins/clangcodemodel/clangdast.cpp index 01f12d1177e..0c4f4bf5d3e 100644 --- a/src/plugins/clangcodemodel/clangdast.cpp +++ b/src/plugins/clangcodemodel/clangdast.cpp @@ -18,8 +18,8 @@ using namespace Utils; namespace ClangCodeModel::Internal { -static constexpr char16_t roleKey[] = u"role"; -static constexpr char16_t arcanaKey[] = u"arcana"; +static constexpr char roleKey[] = "role"; +static constexpr char arcanaKey[] = "arcana"; QString ClangdAstNode::role() const { return typedValue<QString>(roleKey); } QString ClangdAstNode::kind() const { return typedValue<QString>(kindKey); } diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 7c40086f09c..565c8306a0b 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -44,7 +44,7 @@ #include <languageserverprotocol/progresssupport.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projecttree.h> #include <projectexplorer/projectmanager.h> @@ -99,7 +99,7 @@ class SymbolDetails : public JsonObject public: using JsonObject::JsonObject; - static constexpr char16_t usrKey[] = u"usr"; + static constexpr char usrKey[] = "usr"; // the unqualified name of the symbol QString name() const { return typedValue<QString>(nameKey); } @@ -215,8 +215,12 @@ static BaseClientInterface *clientInterface(Project *project, const Utils::FileP if (!jsonDbDir.isEmpty()) cmd.addArg("--compile-commands-dir=" + clangdExePath.withNewMappedPath(jsonDbDir).path()); if (clangdLogServer().isDebugEnabled()) - cmd.addArgs({"--log=verbose", "--pretty"}); + cmd.addArgs({"--log=verbose", "--pretty", "--hidden-features=1"}); cmd.addArg("--use-dirty-headers"); + if (settings.completionRankingModel() != ClangdSettings::CompletionRankingModel::Default) { + cmd.addArg("--ranking-model=" + ClangdSettings::rankingModelToCmdLineString( + settings.completionRankingModel())); + } const auto interface = new StdIOClientInterface; interface->setCommandLine(cmd); return interface; @@ -226,15 +230,15 @@ class DiagnosticsCapabilities : public JsonObject { public: using JsonObject::JsonObject; - void enableCategorySupport() { insert(u"categorySupport", true); } - void enableCodeActionsInline() {insert(u"codeActionsInline", true);} + void enableCategorySupport() { insert("categorySupport", true); } + void enableCodeActionsInline() {insert("codeActionsInline", true);} }; class InactiveRegionsCapabilities : public JsonObject { public: using JsonObject::JsonObject; - void enableInactiveRegionsSupport() { insert(u"inactiveRegions", true); } + void enableInactiveRegionsSupport() { insert("inactiveRegions", true); } }; class ClangdTextDocumentClientCapabilities : public TextDocumentClientCapabilities @@ -243,9 +247,9 @@ public: using TextDocumentClientCapabilities::TextDocumentClientCapabilities; void setPublishDiagnostics(const DiagnosticsCapabilities &caps) - { insert(u"publishDiagnostics", caps); } + { insert("publishDiagnostics", caps); } void setInactiveRegionsCapabilities(const InactiveRegionsCapabilities &caps) - { insert(u"inactiveRegionsCapabilities", caps); } + { insert("inactiveRegionsCapabilities", caps); } }; static qint64 getRevision(const TextDocument *doc) @@ -402,12 +406,15 @@ ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir, c setCompletionAssistProvider(new ClangdCompletionAssistProvider(this)); setQuickFixAssistProvider(new ClangdQuickFixProvider(this)); symbolSupport().setLimitRenamingToProjects(true); + symbolSupport().setRenameResultsEnhancer([](const SearchResultItems &symbolOccurrencesInCode) { + return CppEditor::symbolOccurrencesInDeclarationComments(symbolOccurrencesInCode); + }); if (!project) { QJsonObject initOptions; const Utils::FilePath includeDir = CppEditor::ClangdSettings(d->settings).clangdIncludePath(); CppEditor::CompilerOptionsBuilder optionsBuilder = clangOptionsBuilder( - *CppEditor::CppModelManager::instance()->fallbackProjectPart(), + *CppEditor::CppModelManager::fallbackProjectPart(), warningsConfigForProject(nullptr), includeDir, {}); const CppEditor::UsePrecompiledHeaders usePch = CppEditor::getPchUsage(); const QJsonArray projectPartOptions = fullProjectPartOptions( @@ -584,7 +591,8 @@ void ClangdClient::findUsages(const CppEditor::CursorInEditor &cursor, if (useClangdForRenaming) { symbolSupport().renameSymbol(cursor.textDocument(), adjustedCursor, *replacement, - renameCallback, CppEditor::preferLowerCaseFileNames()); + renameCallback, + CppEditor::preferLowerCaseFileNames(project())); return; } } @@ -628,13 +636,14 @@ void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms) const int docVersion = documentVersion(uri); if (params.version().value_or(docVersion) != docVersion) return; + QList<CodeAction> allCodeActions; for (const Diagnostic &diagnostic : params.diagnostics()) { const ClangdDiagnostic clangdDiagnostic(diagnostic); auto codeActions = clangdDiagnostic.codeActions(); if (codeActions && !codeActions->isEmpty()) { for (CodeAction &action : *codeActions) action.setDiagnostics({diagnostic}); - LanguageClient::updateCodeActionRefactoringMarker(this, *codeActions, uri); + allCodeActions << *codeActions; } else { // We know that there's only one kind of diagnostic for which clangd has // a quickfix tweak, so let's not be wasteful. @@ -644,6 +653,8 @@ void ClangdClient::handleDiagnostics(const PublishDiagnosticsParams ¶ms) requestCodeActions(uri, diagnostic); } } + if (!allCodeActions.isEmpty()) + LanguageClient::updateCodeActionRefactoringMarker(this, allCodeActions, uri); } void ClangdClient::handleDocumentOpened(TextDocument *doc) @@ -751,10 +762,17 @@ bool ClangdClient::fileBelongsToProject(const Utils::FilePath &filePath) const return Client::fileBelongsToProject(filePath); } +QList<Text::Range> ClangdClient::additionalDocumentHighlights( + TextEditorWidget *editorWidget, const QTextCursor &cursor) +{ + return CppEditor::symbolOccurrencesInDeclarationComments( + qobject_cast<CppEditor::CppEditorWidget *>(editorWidget), cursor); +} + RefactoringChangesData *ClangdClient::createRefactoringChangesBackend() const { return new CppEditor::CppRefactoringChangesData( - CppEditor::CppModelManager::instance()->snapshot()); + CppEditor::CppModelManager::snapshot()); } QVersionNumber ClangdClient::versionNumber() const @@ -874,8 +892,7 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath, // TODO: Should we write the editor defines into the json file? It seems strange // that they should affect the index only while the file is open in the editor. const auto projectPart = !config.preferredProjectPartId.isEmpty() - ? CppEditor::CppModelManager::instance()->projectPartForId( - config.preferredProjectPartId) + ? CppEditor::CppModelManager::projectPartForId(config.preferredProjectPartId) : projectPartForFile(filePath); if (!projectPart) return; @@ -1000,7 +1017,11 @@ void ClangdClient::followSymbol(TextDocument *document, const QTextCursor adjustedCursor = d->adjustedCursor(cursor, document); if (followTo == FollowTo::SymbolDef && !resolveTarget) { - symbolSupport().findLinkAt(document, adjustedCursor, callback, false); + symbolSupport().findLinkAt(document, + adjustedCursor, + callback, + false, + LanguageClient::LinkTarget::SymbolDef); return; } @@ -1056,9 +1077,11 @@ void ClangdClient::switchHeaderSource(const Utils::FilePath &filePath, bool inNe sendMessage(req); } -void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cursor, - CppEditor::RenameCallback &&callback) +void ClangdClient::findLocalUsages(CppEditor::CppEditorWidget *editorWidget, + const QTextCursor &cursor, CppEditor::RenameCallback &&callback) { + QTC_ASSERT(editorWidget, return); + TextDocument * const document = editorWidget->textDocument(); QTC_ASSERT(documentOpen(document), openDocument(document)); qCDebug(clangdLog) << "local references requested" << document->filePath() @@ -1076,7 +1099,7 @@ void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cu return; } - d->findLocalRefs = new ClangdFindLocalReferences(this, document, cursor, callback); + d->findLocalRefs = new ClangdFindLocalReferences(this, editorWidget, cursor, callback); connect(d->findLocalRefs, &ClangdFindLocalReferences::done, this, [this] { d->findLocalRefs->deleteLater(); d->findLocalRefs = nullptr; @@ -1572,7 +1595,7 @@ void ClangdClient::Private::handleSemanticTokens(TextDocument *doc, std::optional<QList<CodeAction> > ClangdDiagnostic::codeActions() const { - auto actions = optionalArray<LanguageServerProtocol::CodeAction>(u"codeActions"); + auto actions = optionalArray<LanguageServerProtocol::CodeAction>("codeActions"); if (!actions) return actions; static const QStringList badCodeActions{ @@ -1589,7 +1612,7 @@ std::optional<QList<CodeAction> > ClangdDiagnostic::codeActions() const QString ClangdDiagnostic::category() const { - return typedValue<QString>(u"category"); + return typedValue<QString>("category"); } MessageId ClangdClient::Private::getAndHandleAst(const TextDocOrFile &doc, diff --git a/src/plugins/clangcodemodel/clangdclient.h b/src/plugins/clangcodemodel/clangdclient.h index 2b354eaa2cc..4ac861127cc 100644 --- a/src/plugins/clangcodemodel/clangdclient.h +++ b/src/plugins/clangcodemodel/clangdclient.h @@ -74,7 +74,7 @@ public: const Utils::LinkHandler &callback); void switchHeaderSource(const Utils::FilePath &filePath, bool inNextSplit); - void findLocalUsages(TextEditor::TextDocument *document, const QTextCursor &cursor, + void findLocalUsages(CppEditor::CppEditorWidget *editorWidget, const QTextCursor &cursor, CppEditor::RenameCallback &&callback); void gatherHelpItemForTooltip( @@ -148,6 +148,9 @@ private: bool referencesShadowFile(const TextEditor::TextDocument *doc, const Utils::FilePath &candidate) override; bool fileBelongsToProject(const Utils::FilePath &filePath) const override; + QList<Utils::Text::Range> additionalDocumentHighlights( + TextEditor::TextEditorWidget *editorWidget, const QTextCursor &cursor) override; + class Private; class VirtualFunctionAssistProcessor; diff --git a/src/plugins/clangcodemodel/clangdcompletion.cpp b/src/plugins/clangcodemodel/clangdcompletion.cpp index 50a78c657d2..9141d39228d 100644 --- a/src/plugins/clangcodemodel/clangdcompletion.cpp +++ b/src/plugins/clangcodemodel/clangdcompletion.cpp @@ -609,7 +609,7 @@ QList<AssistProposalItemInterface *> ClangdCompletionAssistProcessor::generateCo if (!doc || pos < 0 || !Utils::anyOf(items, criterion)) return itemGenerator(items); const QString content = doc->toPlainText(); - const bool requiresSignal = CppModelManager::instance()->getSignalSlotType( + const bool requiresSignal = CppModelManager::getSignalSlotType( filePath(), content.toUtf8(), pos) == SignalSlotType::NewStyleSignal; if (requiresSignal) @@ -635,7 +635,7 @@ IAssistProposal *ClangdFunctionHintProcessor::perform() ClangdCompletionCapabilities::ClangdCompletionCapabilities(const JsonObject &object) : TextDocumentClientCapabilities::CompletionCapabilities(object) { - insert(u"editsNearCursor", true); // For dot-to-arrow correction. + insert("editsNearCursor", true); // For dot-to-arrow correction. if (std::optional<CompletionItemCapbilities> completionItemCaps = completionItem()) { completionItemCaps->setSnippetSupport(false); setCompletionItem(*completionItemCaps); diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp index b44fd41a828..19e1df4dd16 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.cpp +++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp @@ -14,6 +14,7 @@ #include <cplusplus/FindUsages.h> #include <cppeditor/cppcodemodelsettings.h> +#include <cppeditor/cppeditorwidget.h> #include <cppeditor/cppfindreferences.h> #include <cppeditor/cpptoolsreuse.h> @@ -90,7 +91,8 @@ public: const ReplacementData &replacementData, const QString &newSymbolName, const SearchResultItems &checkedItems, - bool preserveCase); + bool preserveCase, + bool preferLowerCaseFileNames); void handleFindUsagesResult(const QList<Location> &locations); void finishSearch(); void reportAllSearchResultsAndFinish(); @@ -141,13 +143,14 @@ ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *d const auto renameFilesCheckBox = new QCheckBox; renameFilesCheckBox->setVisible(false); d->search->setAdditionalReplaceWidget(renameFilesCheckBox); - const auto renameHandler = - [search = d->search](const QString &newSymbolName, - const SearchResultItems &checkedItems, - bool preserveCase) { + const bool preferLowerCase = CppEditor::preferLowerCaseFileNames(client->project()); + const auto renameHandler = [search = d->search, preferLowerCase]( + const QString &newSymbolName, + const SearchResultItems &checkedItems, + bool preserveCase) { const auto replacementData = search->userData().value<ReplacementData>(); Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems, - preserveCase); + preserveCase, preferLowerCase); }; connect(d->search, &SearchResult::replaceButtonClicked, renameHandler); } @@ -244,7 +247,8 @@ void ClangdFindReferences::Private::handleRenameRequest( const ReplacementData &replacementData, const QString &newSymbolName, const SearchResultItems &checkedItems, - bool preserveCase) + bool preserveCase, + bool preferLowerCaseFileNames) { const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems, preserveCase); @@ -261,7 +265,7 @@ void ClangdFindReferences::Private::handleRenameRequest( ProjectExplorerPlugin::renameFilesForSymbol( replacementData.oldSymbolName, newSymbolName, Utils::toList(replacementData.fileRenameCandidates), - CppEditor::preferLowerCaseFileNames()); + preferLowerCaseFileNames); } void ClangdFindReferences::Private::handleFindUsagesResult(const QList<Location> &locations) @@ -452,9 +456,10 @@ void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file item.setContainingFunctionName(getContainingFunction(astPath, range).detail()); if (search->supportsReplace()) { - const bool fileInSession = ProjectManager::projectForFile(file); - item.setSelectForReplacement(fileInSession); - if (fileInSession && file.baseName().compare(replacementData->oldSymbolName, + const Node * const node = ProjectTree::nodeForFile(file); + item.setSelectForReplacement(!ProjectManager::hasProjects() + || (node && !node->isGenerated())); + if (node && file.baseName().compare(replacementData->oldSymbolName, Qt::CaseInsensitive) == 0) { replacementData->fileRenameCandidates << file; } @@ -667,10 +672,10 @@ ClangdFindReferences::CheckUnusedData::~CheckUnusedData() class ClangdFindLocalReferences::Private { public: - Private(ClangdFindLocalReferences *q, TextDocument *document, const QTextCursor &cursor, + Private(ClangdFindLocalReferences *q, CppEditorWidget *editorWidget, const QTextCursor &cursor, const RenameCallback &callback) - : q(q), document(document), cursor(cursor), callback(callback), - uri(client()->hostPathToServerUri(document->filePath())), + : q(q), editorWidget(editorWidget), document(editorWidget->textDocument()), cursor(cursor), + callback(callback), uri(client()->hostPathToServerUri(document->filePath())), revision(document->document()->revision()) {} @@ -682,6 +687,7 @@ public: void finish(); ClangdFindLocalReferences * const q; + const QPointer<CppEditorWidget> editorWidget; const QPointer<TextDocument> document; const QTextCursor cursor; RenameCallback callback; @@ -691,9 +697,9 @@ public: }; ClangdFindLocalReferences::ClangdFindLocalReferences( - ClangdClient *client, TextDocument *document, const QTextCursor &cursor, - const RenameCallback &callback) - : QObject(client), d(new Private(this, document, cursor, callback)) + ClangdClient *client, CppEditorWidget *editorWidget, const QTextCursor &cursor, + const RenameCallback &callback) + : QObject(client), d(new Private(this, editorWidget, cursor, callback)) { d->findDefinition(); } @@ -709,7 +715,11 @@ void ClangdFindLocalReferences::Private::findDefinition() if (sentinel) getDefinitionAst(l); }; - client()->symbolSupport().findLinkAt(document, cursor, linkHandler, true); + client()->symbolSupport().findLinkAt(document, + cursor, + linkHandler, + true, + LanguageClient::LinkTarget::SymbolDef); } void ClangdFindLocalReferences::Private::getDefinitionAst(const Link &link) @@ -777,7 +787,7 @@ void ClangdFindLocalReferences::Private::handleReferences(const QList<Location> return loc.toLink(mapper); }; - const Utils::Links links = Utils::transform(references, transformLocation); + Utils::Links links = Utils::transform(references, transformLocation); // The callback only uses the symbol length, so we just create a dummy. // Note that the calculation will be wrong for identifiers with @@ -785,7 +795,27 @@ void ClangdFindLocalReferences::Private::handleReferences(const QList<Location> QString symbol; if (!references.isEmpty()) { const Range r = references.first().range(); - symbol = QString(r.end().character() - r.start().character(), 'x'); + const Position pos = r.start(); + symbol = QString(r.end().character() - pos.character(), 'x'); + if (editorWidget && document) { + QTextCursor cursor(document->document()); + cursor.setPosition(Text::positionInText(document->document(), pos.line() + 1, + pos.character() + 1)); + const QList<Text::Range> occurrencesInComments + = symbolOccurrencesInDeclarationComments(editorWidget, cursor); + for (const Text::Range &range : occurrencesInComments) { + static const auto cmp = [](const Link &l, const Text::Range &r) { + if (l.targetLine < r.begin.line) + return true; + if (l.targetLine > r.begin.line) + return false; + return l.targetColumn < r.begin.column; + }; + const auto it = std::lower_bound(links.begin(), links.end(), range, cmp); + links.emplace(it, links.first().targetFilePath, range.begin.line, + range.begin.column); + } + } } callback(symbol, links, revision); callback = {}; diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h index e110b355434..c61afe17a1f 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.h +++ b/src/plugins/clangcodemodel/clangdfindreferences.h @@ -48,9 +48,9 @@ class ClangdFindLocalReferences : public QObject { Q_OBJECT public: - explicit ClangdFindLocalReferences(ClangdClient *client, TextEditor::TextDocument *document, - const QTextCursor &cursor, - const CppEditor::RenameCallback &callback); + explicit ClangdFindLocalReferences( + ClangdClient *client, CppEditor::CppEditorWidget *editorWidget, const QTextCursor &cursor, + const CppEditor::RenameCallback &callback); ~ClangdFindLocalReferences(); signals: diff --git a/src/plugins/clangcodemodel/clangdfollowsymbol.cpp b/src/plugins/clangcodemodel/clangdfollowsymbol.cpp index 730a3fdd375..8be15c7327d 100644 --- a/src/plugins/clangcodemodel/clangdfollowsymbol.cpp +++ b/src/plugins/clangcodemodel/clangdfollowsymbol.cpp @@ -154,7 +154,11 @@ ClangdFollowSymbol::ClangdFollowSymbol(ClangdClient *client, const QTextCursor & if (self->d->cursorNode) self->d->handleGotoDefinitionResult(); }; - client->symbolSupport().findLinkAt(document, cursor, std::move(gotoDefCallback), true); + client->symbolSupport().findLinkAt(document, + cursor, + std::move(gotoDefCallback), + true, + LanguageClient::LinkTarget::SymbolDef); const auto astHandler = [self = QPointer(this)](const ClangdAstNode &ast, const MessageId &) { qCDebug(clangdLog) << "received ast response for cursor"; @@ -336,7 +340,7 @@ IAssistProposal *ClangdFollowSymbol::VirtualFunctionAssistProcessor::createPropo items << createEntry({}, m_followSymbol->d->defLink); if (!final) { const auto infoItem = new VirtualFunctionProposalItem({}, false); - infoItem->setText(Tr::tr("collecting overrides ...")); + infoItem->setText(Tr::tr("collecting overrides...")); infoItem->setOrder(-1); items << infoItem; } diff --git a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp index f783a98499a..c3e9c42903f 100644 --- a/src/plugins/clangcodemodel/clangdlocatorfilters.cpp +++ b/src/plugins/clangcodemodel/clangdlocatorfilters.cpp @@ -192,7 +192,7 @@ LocatorMatcherTask currentDocumentMatcher() }; const Group root { - Storage(resultStorage), + Tasking::Storage(resultStorage), CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone), AsyncTask<void>(onFilterSetup) }; diff --git a/src/plugins/clangcodemodel/clangdmemoryusagewidget.cpp b/src/plugins/clangcodemodel/clangdmemoryusagewidget.cpp index 58c8c9e4056..fb5c88ec222 100644 --- a/src/plugins/clangcodemodel/clangdmemoryusagewidget.cpp +++ b/src/plugins/clangcodemodel/clangdmemoryusagewidget.cpp @@ -27,10 +27,10 @@ public: using JsonObject::JsonObject; // number of bytes used, including child components - qint64 total() const { return qint64(typedValue<double>(totalKey())); } + qint64 total() const { return qint64(typedValue<double>(totalKey)); } // number of bytes used, excluding child components - qint64 self() const { return qint64(typedValue<double>(selfKey())); } + qint64 self() const { return qint64(typedValue<double>(selfKey)); } // named child components using NamedComponent = std::pair<MemoryTree, QString>; @@ -39,16 +39,15 @@ public: QList<NamedComponent> components; const auto obj = operator const QJsonObject &(); for (auto it = obj.begin(); it != obj.end(); ++it) { - if (it.key() == totalKey() || it.key() == selfKey()) + if (it.key() == QLatin1String(totalKey) || it.key() == QLatin1String(selfKey)) continue; components.push_back({MemoryTree(it.value()), it.key()}); } return components; } -private: - static QString totalKey() { return QLatin1String("_total"); } - static QString selfKey() { return QLatin1String("_self"); } + static constexpr char totalKey[] = "_total"; + static constexpr char selfKey[] = "_self"; }; diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp index 378e0f9346f..33594efc4c3 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -967,8 +967,8 @@ class InactiveRegionsParams : public JsonObject public: using JsonObject::JsonObject; - DocumentUri uri() const { return TextDocumentIdentifier(value(u"textDocument")).uri(); } - QList<Range> inactiveRegions() const { return array<Range>(u"regions"); } + DocumentUri uri() const { return TextDocumentIdentifier(value("textDocument")).uri(); } + QList<Range> inactiveRegions() const { return array<Range>("regions"); } }; class InactiveRegionsNotification : public Notification<InactiveRegionsParams> diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp index d7c94d81247..83667dde05e 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp @@ -3,6 +3,7 @@ #include "clangeditordocumentprocessor.h" +#include "clangdclient.h" #include "clangmodelmanagersupport.h" #include <cppeditor/builtincursorinfo.h> @@ -15,6 +16,8 @@ #include <cppeditor/cppworkingcopy.h> #include <cppeditor/editordocumenthandle.h> +#include <languageclient/languageclientmanager.h> + #include <texteditor/fontsettings.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorconstants.h> @@ -77,5 +80,13 @@ ClangEditorDocumentProcessor *ClangEditorDocumentProcessor::get(const Utils::Fil CppEditor::CppModelManager::cppEditorDocumentProcessor(filePath)); } +void ClangEditorDocumentProcessor::forceUpdate(TextEditor::TextDocument *doc) +{ + if (const auto client = qobject_cast<ClangdClient *>( + LanguageClient::LanguageClientManager::clientForDocument(doc))) { + client->documentContentsChanged(doc, 0, 0, 0); + } +} + } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h index bd151554003..fe2bc6a0166 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h +++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h @@ -22,7 +22,6 @@ public: void setParserConfig(const CppEditor::BaseEditorDocumentParser::Configuration &config) override; CppEditor::BaseEditorDocumentParser::Configuration parserConfig(); -public: static ClangEditorDocumentProcessor *get(const Utils::FilePath &filePath); signals: @@ -30,6 +29,8 @@ signals: const CppEditor::BaseEditorDocumentParser::Configuration &config); private: + void forceUpdate(TextEditor::TextDocument *doc) override; + TextEditor::TextDocument &m_document; }; diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 98e855b4195..e117e6b36f1 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -62,11 +62,6 @@ using namespace Utils; namespace ClangCodeModel::Internal { -static CppModelManager *cppModelManager() -{ - return CppModelManager::instance(); -} - static Project *fallbackProject() { if (Project * const p = ProjectTree::currentProject()) @@ -202,10 +197,10 @@ ClangModelManagerSupport::ClangModelManagerSupport() watchForInternalChanges(); setupClangdConfigFile(); checkSystemForClangdSuitability(); - cppModelManager()->setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>()); - cppModelManager()->setLocatorFilter(std::make_unique<ClangdAllSymbolsFilter>()); - cppModelManager()->setClassesFilter(std::make_unique<ClangdClassesFilter>()); - cppModelManager()->setFunctionsFilter(std::make_unique<ClangdFunctionsFilter>()); + CppModelManager::setCurrentDocumentFilter(std::make_unique<ClangdCurrentDocumentFilter>()); + CppModelManager::setLocatorFilter(std::make_unique<ClangdAllSymbolsFilter>()); + CppModelManager::setClassesFilter(std::make_unique<ClangdClassesFilter>()); + CppModelManager::setFunctionsFilter(std::make_unique<ClangdFunctionsFilter>()); // Setup matchers LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] { return LanguageClient::languageClientMatchers( @@ -226,7 +221,7 @@ ClangModelManagerSupport::ClangModelManagerSupport() connect(editorManager, &EditorManager::currentEditorChanged, this, &ClangModelManagerSupport::onCurrentEditorChanged); - CppModelManager *modelManager = cppModelManager(); + CppModelManager *modelManager = CppModelManager::instance(); connect(modelManager, &CppModelManager::abstractEditorSupportContentsUpdated, this, &ClangModelManagerSupport::onAbstractEditorSupportContentsUpdated); connect(modelManager, &CppModelManager::abstractEditorSupportRemoved, @@ -236,10 +231,10 @@ ClangModelManagerSupport::ClangModelManagerSupport() connect(modelManager, &CppModelManager::fallbackProjectPartUpdated, this, [this] { if (sessionModeEnabled()) return; - if (ClangdClient * const fallbackClient = clientForProject(nullptr)) { + if (ClangdClient * const fallbackClient = clientForProject(nullptr)) LanguageClientManager::shutdownClient(fallbackClient); + if (ClangdSettings::instance().useClangd()) claimNonProjectSources(new ClangdClient(nullptr, {})); - } }); auto projectManager = ProjectManager::instance(); @@ -256,9 +251,6 @@ ClangModelManagerSupport::ClangModelManagerSupport() connect(&ClangdSettings::instance(), &ClangdSettings::changed, this, &ClangModelManagerSupport::onClangdSettingsChanged); - if (ClangdSettings::instance().useClangd()) - new ClangdClient(nullptr, {}); - new ClangdQuickFixFactory(); // memory managed by CppEditor::g_cppQuickFixFactories } @@ -314,7 +306,7 @@ void ClangModelManagerSupport::startLocalRenaming(const CursorInEditor &data, { if (ClangdClient * const client = clientForFile(data.filePath()); client && client->reachable()) { - client->findLocalUsages(data.textDocument(), data.cursor(), + client->findLocalUsages(data.editorWidget(), data.cursor(), std::move(renameSymbolsCallback)); return; } @@ -382,7 +374,7 @@ void ClangModelManagerSupport::checkUnused(const Link &link, SearchResult *searc } } - CppModelManager::instance()->modelManagerSupport( + CppModelManager::modelManagerSupport( CppModelManager::Backend::Builtin)->checkUnused(link, search, callback); } @@ -409,7 +401,7 @@ void ClangModelManagerSupport::onCurrentEditorChanged(IEditor *editor) { // Update task hub issues for current CppEditorDocument TaskHub::clearTasks(Constants::TASK_CATEGORY_DIAGNOSTICS); - if (!editor || !editor->document() || !cppModelManager()->isCppEditor(editor)) + if (!editor || !editor->document() || !CppModelManager::isCppEditor(editor)) return; const FilePath filePath = editor->document()->filePath(); @@ -460,12 +452,12 @@ static bool isProjectDataUpToDate(Project *project, ProjectInfoList projectInfo, return false; ProjectInfoList newProjectInfo; if (project) { - if (const ProjectInfo::ConstPtr pi = CppModelManager::instance()->projectInfo(project)) + if (const ProjectInfo::ConstPtr pi = CppModelManager::projectInfo(project)) newProjectInfo.append(pi); else return false; } else { - newProjectInfo = CppModelManager::instance()->projectInfos(); + newProjectInfo = CppModelManager::projectInfos(); } if (newProjectInfo.size() != projectInfo.size()) return false; @@ -486,8 +478,8 @@ void ClangModelManagerSupport::updateLanguageClient(Project *project) ProjectInfoList projectInfo; if (sessionModeEnabled()) { project = nullptr; - projectInfo = CppModelManager::instance()->projectInfos(); - } else if (const ProjectInfo::ConstPtr pi = CppModelManager::instance()->projectInfo(project)) { + projectInfo = CppModelManager::projectInfos(); + } else if (const ProjectInfo::ConstPtr pi = CppModelManager::projectInfo(project)) { projectInfo.append(pi); } else { return; @@ -771,19 +763,26 @@ void ClangModelManagerSupport::onEditorOpened(IEditor *editor) QTC_ASSERT(document, return); auto textDocument = qobject_cast<TextEditor::TextDocument *>(document); - if (textDocument && cppModelManager()->isCppEditor(editor)) { + if (textDocument && CppModelManager::isCppEditor(editor)) { connectToWidgetsMarkContextMenuRequested(editor->widget()); Project * project = ProjectManager::projectForFile(document->filePath()); const ClangdSettings settings(ClangdProjectSettings(project).settings()); + if (!settings.useClangd()) + return; if (!settings.sizeIsOkay(textDocument->filePath())) return; if (sessionModeEnabled()) project = nullptr; else if (!project && ProjectFile::isHeader(document->filePath())) project = fallbackProject(); - if (ClangdClient * const client = clientForProject(project)) - LanguageClientManager::openDocumentWithClient(textDocument, client); + ClangdClient *client = clientForProject(project); + if (!client) { + if (project) + return; + client = new ClangdClient(nullptr, {}); + } + LanguageClientManager::openDocumentWithClient(textDocument, client); } } diff --git a/src/plugins/clangcodemodel/clangtextmark.cpp b/src/plugins/clangcodemodel/clangtextmark.cpp index 166a55e4f31..80e2956021f 100644 --- a/src/plugins/clangcodemodel/clangtextmark.cpp +++ b/src/plugins/clangcodemodel/clangtextmark.cpp @@ -105,7 +105,8 @@ void disableDiagnosticInCurrentProjectConfig(const ClangDiagnostic &diagnostic) projectSettings.setDiagnosticConfigId(config.id()); // Notify the user about changed project specific settings - const QString text = Tr::tr("Changes applied in Projects Mode > Clang Code Model"); + const QString text + = Tr::tr("Changes applied to diagnostic configuration \"%1\".").arg(config.displayName()); FadingIndicator::showText(Core::ICore::mainWindow(), text, FadingIndicator::SmallText); @@ -264,7 +265,7 @@ ClangdTextMark::ClangdTextMark(TextEditor::TextDocument *doc, // Copy to clipboard action QList<QAction *> actions; QAction *action = new QAction(); - action->setIcon(QIcon::fromTheme("edit-copy", Icons::COPY.icon())); + action->setIcon(Icon::fromTheme("edit-copy")); action->setToolTip(Tr::tr("Copy to Clipboard", "Clang Code Model Marks")); QObject::connect(action, &QAction::triggered, [diag] { const QString text = ClangDiagnosticWidget::createText({diag}, diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index d5d7868137b..ce9f09126a6 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -16,7 +16,7 @@ #include <cppeditor/editordocumenthandle.h> #include <cppeditor/projectpart.h> #include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <texteditor/codeassist/textdocumentmanipulatorinterface.h> diff --git a/src/plugins/clangcodemodel/test/clangdtests.cpp b/src/plugins/clangcodemodel/test/clangdtests.cpp index ceaf25787e8..bfc6d1d4f8d 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.cpp +++ b/src/plugins/clangcodemodel/test/clangdtests.cpp @@ -10,6 +10,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <cplusplus/FindUsages.h> #include <cppeditor/cppcodemodelsettings.h> +#include <cppeditor/cppeditorwidget.h> #include <cppeditor/cpptoolsreuse.h> #include <cppeditor/cpptoolstestcase.h> #include <cppeditor/semantichighlighter.h> @@ -25,7 +26,7 @@ #include <utils/environment.h> #include <utils/filepath.h> #include <utils/textutils.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QElapsedTimer> #include <QEventLoop> @@ -536,6 +537,8 @@ void ClangdTestLocalReferences::test_data() QTest::newRow("overloaded operators arguments from outside") << 171 << 7 << QList<Range>{{171, 6, 1}, {172, 6, 1}, {172, 11, 1}, {173, 6, 1}, {173, 9, 1}}; + QTest::newRow("documented function parameter") << 181 << 32 + << QList<Range>{{177, 10, 6}, {179, 9, 6}, {181, 31, 6}, {183, 6, 6}, {184, 17, 6}}; } void ClangdTestLocalReferences::test() @@ -546,6 +549,11 @@ void ClangdTestLocalReferences::test() TextEditor::TextDocument * const doc = document("references.cpp"); QVERIFY(doc); + const QList<BaseTextEditor *> editors = BaseTextEditor::textEditorsForDocument(doc); + QCOMPARE(editors.size(), 1); + const auto editorWidget = qobject_cast<CppEditor::CppEditorWidget *>( + editors.first()->editorWidget()); + QVERIFY(editorWidget); QTimer timer; timer.setSingleShot(true); @@ -561,7 +569,7 @@ void ClangdTestLocalReferences::test() QTextCursor cursor(doc->document()); const int pos = Text::positionInText(doc->document(), sourceLine, sourceColumn); cursor.setPosition(pos); - client()->findLocalUsages(doc, cursor, std::move(handler)); + client()->findLocalUsages(editorWidget, cursor, std::move(handler)); timer.start(10000); loop.exec(); QVERIFY(timer.isActive()); @@ -2061,6 +2069,47 @@ void ClangdTestExternalChanges::test() QVERIFY(waitForSignalOrTimeout(newClient, &ClangdClient::textMarkCreated, timeOutInMs())); } +ClangdTestIndirectChanges::ClangdTestIndirectChanges() +{ + setProjectFileName("indirect-changes.pro"); + setSourceFileNames({"main.cpp", "directheader.h", "indirectheader.h", "unrelatedheader.h"}); +} + +void ClangdTestIndirectChanges::test() +{ + // Initially, everything is fine. + const TextDocument * const src = document("main.cpp"); + QVERIFY(src); + QVERIFY(src->marks().isEmpty()); + + // Write into an indirectly included header file. Our source file should have diagnostics now. + const TextDocument * const indirectHeader = document("indirectheader.h"); + QVERIFY(indirectHeader); + QTextCursor cursor(indirectHeader->document()); + cursor.insertText("blubb"); + while (src->marks().isEmpty()) + QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::textMarkCreated, timeOutInMs())); + + // Remove the inserted text again; the diagnostics should disappear. + cursor.document()->undo(); + QVERIFY(cursor.document()->toPlainText().isEmpty()); + while (!src->marks().isEmpty()) { + QVERIFY(waitForSignalOrTimeout(client(), &ClangdClient::highlightingResultsReady, + timeOutInMs())); + } + + // Now write into a header file that is not included anywhere. + // We expect diagnostics only for the header itself. + const TextDocument * const unrelatedHeader = document("unrelatedheader.h"); + QVERIFY(indirectHeader); + QTextCursor cursor2(unrelatedHeader->document()); + cursor2.insertText("blubb"); + while (waitForSignalOrTimeout(client(), &ClangdClient::textMarkCreated, timeOutInMs())) + ; + QVERIFY(!unrelatedHeader->marks().isEmpty()); + QVERIFY(src->marks().isEmpty()); +} + } // namespace Tests } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/test/clangdtests.h b/src/plugins/clangcodemodel/test/clangdtests.h index 92bf4efe90c..9d6e42d3a9e 100644 --- a/src/plugins/clangcodemodel/test/clangdtests.h +++ b/src/plugins/clangcodemodel/test/clangdtests.h @@ -188,6 +188,17 @@ private slots: void test(); }; +class ClangdTestIndirectChanges : public ClangdTest +{ + Q_OBJECT + +public: + ClangdTestIndirectChanges(); + +private slots: + void test(); +}; + } // namespace Tests } // namespace Internal } // namespace ClangCodeModel diff --git a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc index 1ee4bdee92c..f61e3f0633b 100644 --- a/src/plugins/clangcodemodel/test/data/clangtestdata.qrc +++ b/src/plugins/clangcodemodel/test/data/clangtestdata.qrc @@ -60,5 +60,10 @@ <file>fixits/diagnostic_comparison_fixit.cpp</file> <file>fixits/diagnostic_semicolon_fixit_expected.cpp</file> <file>fixits/diagnostic_semicolon_fixit.cpp</file> + <file>indirect-changes/directheader.h</file> + <file>indirect-changes/indirect-changes.pro</file> + <file>indirect-changes/indirectheader.h</file> + <file>indirect-changes/main.cpp</file> + <file>indirect-changes/unrelatedheader.h</file> </qresource> </RCC> diff --git a/src/plugins/clangcodemodel/test/data/indirect-changes/directheader.h b/src/plugins/clangcodemodel/test/data/indirect-changes/directheader.h new file mode 100644 index 00000000000..9ab7726e456 --- /dev/null +++ b/src/plugins/clangcodemodel/test/data/indirect-changes/directheader.h @@ -0,0 +1 @@ +#include "indirectheader.h" diff --git a/src/plugins/clangcodemodel/test/data/indirect-changes/indirect-changes.pro b/src/plugins/clangcodemodel/test/data/indirect-changes/indirect-changes.pro new file mode 100644 index 00000000000..b1648532ca6 --- /dev/null +++ b/src/plugins/clangcodemodel/test/data/indirect-changes/indirect-changes.pro @@ -0,0 +1,4 @@ +CONFIG -= qt + +SOURCES = main.cpp +HEADERS = directheader.h indirectheader.h unrelatedheader.h diff --git a/src/plugins/clangcodemodel/test/data/indirect-changes/indirectheader.h b/src/plugins/clangcodemodel/test/data/indirect-changes/indirectheader.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/plugins/clangcodemodel/test/data/indirect-changes/main.cpp b/src/plugins/clangcodemodel/test/data/indirect-changes/main.cpp new file mode 100644 index 00000000000..63ae802b071 --- /dev/null +++ b/src/plugins/clangcodemodel/test/data/indirect-changes/main.cpp @@ -0,0 +1,3 @@ +#include "directheader.h" + +int main() {} diff --git a/src/plugins/clangcodemodel/test/data/indirect-changes/unrelatedheader.h b/src/plugins/clangcodemodel/test/data/indirect-changes/unrelatedheader.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/plugins/clangcodemodel/test/data/local-references/references.cpp b/src/plugins/clangcodemodel/test/data/local-references/references.cpp index 32ff1b90753..1c8556581d9 100644 --- a/src/plugins/clangcodemodel/test/data/local-references/references.cpp +++ b/src/plugins/clangcodemodel/test/data/local-references/references.cpp @@ -172,3 +172,14 @@ int testOperator() { vec[n] = n * 100; vec(n, n) = 100; } + +/* + * @param param1 + * @param param2 + * @note param1 and param2 should be the same. + */ +void funcWithParamComments(int param1, int param2) +{ + if (param1 != param2) + param2 = param1; +} diff --git a/src/plugins/clangformat/ClangFormat.json.in b/src/plugins/clangformat/ClangFormat.json.in index cf952b481ce..c444af0fcd6 100644 --- a/src/plugins/clangformat/ClangFormat.json.in +++ b/src/plugins/clangformat/ClangFormat.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"ClangFormat\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ClangFormat", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"C++\", - \"Description\" : \"clang-format indentation plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "C++", + "Description" : "clang-format indentation plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 4f572726826..6903491fb0c 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -8,6 +8,7 @@ #include <coreplugin/icore.h> #include <cppeditor/cppcodestylepreferences.h> +#include <cppeditor/cpptoolssettings.h> #include <projectexplorer/editorconfiguration.h> #include <projectexplorer/project.h> @@ -360,12 +361,11 @@ static Utils::Text::Position utf16LineColumn(const QByteArray &utf8Buffer, int u utf8Offset - startOfLineOffset)).length(); return position; } -Utils::Text::Replacements utf16Replacements(const QTextDocument *doc, - const QByteArray &utf8Buffer, - const clang::tooling::Replacements &replacements) +Utils::ChangeSet convertReplacements(const QTextDocument *doc, + const QByteArray &utf8Buffer, + const clang::tooling::Replacements &replacements) { - Utils::Text::Replacements convertedReplacements; - convertedReplacements.reserve(replacements.size()); + Utils::ChangeSet convertedReplacements; for (const clang::tooling::Replacement &replacement : replacements) { Utils::Text::Position lineColUtf16 = utf16LineColumn( @@ -381,16 +381,37 @@ Utils::Text::Replacements utf16Replacements(const QTextDocument *doc, continue; lineColUtf16.column = std::min(lineColUtf16.column, int(lineText.length())); - const int utf16Offset = Utils::Text::positionInText(doc, - lineColUtf16.line, - lineColUtf16.column + 1); - const int utf16Length = QString::fromUtf8( - utf8Buffer.mid(static_cast<int>(replacement.getOffset()), - static_cast<int>(replacement.getLength()))) - .size(); - convertedReplacements.emplace_back(utf16Offset, - utf16Length, - QString::fromStdString(replacement.getReplacementText().str())); + int utf16Offset = Utils::Text::positionInText(doc, + lineColUtf16.line, + lineColUtf16.column + 1); + int utf16Length = QString::fromUtf8( + utf8Buffer.mid(static_cast<int>(replacement.getOffset()), + static_cast<int>(replacement.getLength()))) + .size(); + + QString replacementText = QString::fromStdString(replacement.getReplacementText().str()); + auto sameCharAt = [&](int replacementOffset) { + if (replacementText.size() <= replacementOffset || replacementOffset < 0) + return false; + const QChar docChar = doc->characterAt(utf16Offset + replacementOffset); + const QChar replacementChar = replacementText.at(replacementOffset); + return docChar == replacementChar + || (docChar == QChar::ParagraphSeparator && replacementChar == '\n'); + }; + // remove identical prefix from replacement text + while (sameCharAt(0)) { + ++utf16Offset; + --utf16Length; + replacementText = replacementText.mid(1); + } + // remove identical suffix from replacement text + while (sameCharAt(utf16Length - 1)) { + --utf16Length; + replacementText.chop(1); + } + + if (!replacementText.isEmpty() || utf16Length > 0) + convertedReplacements.replace(utf16Offset, utf16Offset + utf16Length, replacementText); } return convertedReplacements; @@ -405,19 +426,21 @@ QString selectedLines(QTextDocument *doc, const QTextBlock &startBlock, const QT - startBlock.position() - 1)); } -int indentationForBlock(const Utils::Text::Replacements &toReplace, +int indentationForBlock(const Utils::ChangeSet &toReplace, const QByteArray &buffer, const QTextBlock ¤tBlock) { const int utf8Offset = Utils::Text::utf8NthLineOffset(currentBlock.document(), buffer, currentBlock.blockNumber() + 1); - auto replacementIt = std::find_if(toReplace.begin(), - toReplace.end(), - [utf8Offset](const Utils::Text::Replacement &replacement) { - return replacement.offset == utf8Offset - 1; - }); - if (replacementIt == toReplace.end()) + auto ops = toReplace.operationList(); + + auto replacementIt + = std::find_if(ops.begin(), ops.end(), [utf8Offset](const Utils::ChangeSet::EditOp &op) { + QTC_ASSERT(op.type == Utils::ChangeSet::EditOp::Replace, return false); + return op.pos1 == utf8Offset - 1; + }); + if (replacementIt == ops.end()) return -1; int afterLineBreak = replacementIt->text.lastIndexOf('\n'); @@ -471,20 +494,20 @@ ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc) : TextEditor::Indenter(doc) {} -Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer, - const QTextBlock &startBlock, - const QTextBlock &endBlock, - int cursorPositionInEditor, - ReplacementsToKeep replacementsToKeep, - const QChar &typedChar, - bool secondTry) const +Utils::ChangeSet ClangFormatBaseIndenter::replacements(QByteArray buffer, + const QTextBlock &startBlock, + const QTextBlock &endBlock, + int cursorPositionInEditor, + ReplacementsToKeep replacementsToKeep, + const QChar &typedChar, + bool secondTry) const { - QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return Utils::Text::Replacements()); + QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return Utils::ChangeSet()); QTC_ASSERT(!m_fileName.isEmpty(), return {}); QByteArray originalBuffer = buffer; int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, startBlock.blockNumber() + 1); - QTC_ASSERT(utf8Offset >= 0, return Utils::Text::Replacements();); + QTC_ASSERT(utf8Offset >= 0, return Utils::ChangeSet();); int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size(); int rangeStart = 0; @@ -534,15 +557,23 @@ Utils::Text::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffe true); } - return utf16Replacements(m_doc, buffer, filtered); + return convertReplacements(m_doc, buffer, filtered); } -Utils::Text::Replacements ClangFormatBaseIndenter::format( - const TextEditor::RangesInLines &rangesInLines) +Utils::EditOperations ClangFormatBaseIndenter::format(const TextEditor::RangesInLines &rangesInLines, + FormattingMode mode) { + bool doFormatting = mode == FormattingMode::Forced || formatCodeInsteadOfIndent(); +#ifdef WITH_TESTS + doFormatting = doFormatting || CppEditor::CppToolsSettings::cppCodeStyle() + ->codeStyleSettings().forceFormatting; +#endif + if (!doFormatting) + return {}; + QTC_ASSERT(!m_fileName.isEmpty(), return {}); if (rangesInLines.empty()) - return Utils::Text::Replacements(); + return {}; const QByteArray buffer = m_doc->toPlainText().toUtf8(); std::vector<clang::tooling::Range> ranges; @@ -568,7 +599,7 @@ Utils::Text::Replacements ClangFormatBaseIndenter::format( auto changedCode = clang::tooling::applyAllReplacements(buffer.data(), clangReplacements); QTC_ASSERT(changedCode, { qDebug() << QString::fromStdString(llvm::toString(changedCode.takeError())); - return Utils::Text::Replacements(); + return {}; }); ranges = clang::tooling::calculateRangesAfterReplacements(clangReplacements, ranges); @@ -580,13 +611,14 @@ Utils::Text::Replacements ClangFormatBaseIndenter::format( &status); clangReplacements = clangReplacements.merge(formatReplacements); - const Utils::Text::Replacements toReplace = utf16Replacements(m_doc, buffer, clangReplacements); - Utils::Text::applyReplacements(m_doc, toReplace); + Utils::ChangeSet changeSet = convertReplacements(m_doc, buffer, clangReplacements); + const Utils::EditOperations editOperations = changeSet.operationList(); + changeSet.apply(m_doc); - return toReplace; + return editOperations; } -Utils::Text::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBlock, +Utils::ChangeSet ClangFormatBaseIndenter::indentsFor(QTextBlock startBlock, const QTextBlock &endBlock, const QChar &typedChar, int cursorPositionInEditor, @@ -595,7 +627,7 @@ Utils::Text::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBl if (typedChar != QChar::Null && cursorPositionInEditor > 0 && m_doc->characterAt(cursorPositionInEditor - 1) == typedChar && doNotIndentInContext(m_doc, cursorPositionInEditor - 1)) { - return Utils::Text::Replacements(); + return Utils::ChangeSet(); } startBlock = reverseFindLastEmptyBlock(startBlock); @@ -634,7 +666,8 @@ void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock, const QChar &typedChar, int cursorPositionInEditor) { - applyReplacements(m_doc, indentsFor(startBlock, endBlock, typedChar, cursorPositionInEditor)); + Utils::ChangeSet changeset = indentsFor(startBlock, endBlock, typedChar, cursorPositionInEditor); + changeset.apply(m_doc); } void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, @@ -678,12 +711,9 @@ int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { - Utils::Text::Replacements toReplace = indentsFor(block, - block, - QChar::Null, - cursorPositionInEditor, - false); - if (toReplace.empty()) + Utils::ChangeSet toReplace + = indentsFor(block, block, QChar::Null, cursorPositionInEditor, false); + if (toReplace.isEmpty()) return -1; const QByteArray buffer = m_doc->toPlainText().toUtf8(); @@ -698,7 +728,7 @@ TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks( TextEditor::IndentationForBlock ret; if (blocks.isEmpty()) return ret; - Utils::Text::Replacements toReplace = indentsFor(blocks.front(), + Utils::ChangeSet toReplace = indentsFor(blocks.front(), blocks.back(), QChar::Null, cursorPositionInEditor); @@ -777,10 +807,28 @@ clang::format::FormatStyle overrideStyle(const Utils::FilePath &fileName) return currentSettingsStyle; } -clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const +static std::chrono::milliseconds getCacheTimeout() { - if (getCurrentOverriddenSettings(m_fileName)) - return overrideStyle(m_fileName); + using namespace std::chrono_literals; + bool ok = false; + const int envCacheTimeout = qEnvironmentVariableIntValue("CLANG_FORMAT_CACHE_TIMEOUT", &ok); + return ok ? std::chrono::milliseconds(envCacheTimeout) : 1s; +} + +const clang::format::FormatStyle &ClangFormatBaseIndenter::styleForFile() const +{ + using namespace std::chrono_literals; + static const std::chrono::milliseconds cacheTimeout = getCacheTimeout(); + + QDateTime time = QDateTime::currentDateTime(); + if (m_cachedStyle.expirationTime > time && !(m_cachedStyle.style == clang::format::getNoStyle())) + return m_cachedStyle.style; + + if (getCurrentOverriddenSettings(m_fileName)) { + clang::format::FormatStyle style = overrideStyle(m_fileName); + m_cachedStyle.setCache(style, cacheTimeout); + return m_cachedStyle.style; + } llvm::Expected<clang::format::FormatStyle> styleFromProjectFolder = clang::format::getStyle("file", @@ -791,14 +839,17 @@ clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const if (styleFromProjectFolder && !(*styleFromProjectFolder == clang::format::getNoStyle())) { addQtcStatementMacros(*styleFromProjectFolder); - return *styleFromProjectFolder; + m_cachedStyle.setCache(*styleFromProjectFolder, cacheTimeout); + return m_cachedStyle.style; } handleAllErrors(styleFromProjectFolder.takeError(), [](const llvm::ErrorInfoBase &) { // do nothing }); - return qtcStyle(); + + m_cachedStyle.setCache(qtcStyle(), 0ms); + return m_cachedStyle.style; } } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h index b484654a1f7..70d59661710 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.h +++ b/src/plugins/clangformat/clangformatbaseindenter.h @@ -31,7 +31,8 @@ public: void autoIndent(const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings, int cursorPositionInEditor = -1) override; - Utils::Text::Replacements format(const TextEditor::RangesInLines &rangesInLines) override; + Utils::EditOperations format(const TextEditor::RangesInLines &rangesInLines, + FormattingMode mode = FormattingMode::Forced) override; void indentBlock(const QTextBlock &block, const QChar &typedChar, @@ -46,7 +47,7 @@ public: std::optional<int> margin() const override; - clang::format::FormatStyle styleForFile() const; + const clang::format::FormatStyle &styleForFile() const; protected: virtual bool formatCodeInsteadOfIndent() const { return false; } @@ -59,18 +60,30 @@ private: const QTextBlock &endBlock, const QChar &typedChar, int cursorPositionInEditor); - Utils::Text::Replacements indentsFor(QTextBlock startBlock, - const QTextBlock &endBlock, - const QChar &typedChar, - int cursorPositionInEditor, - bool trimTrailingWhitespace = true); - Utils::Text::Replacements replacements(QByteArray buffer, - const QTextBlock &startBlock, - const QTextBlock &endBlock, - int cursorPositionInEditor, - ReplacementsToKeep replacementsToKeep, - const QChar &typedChar = QChar::Null, - bool secondTry = false) const; + Utils::ChangeSet indentsFor(QTextBlock startBlock, + const QTextBlock &endBlock, + const QChar &typedChar, + int cursorPositionInEditor, + bool trimTrailingWhitespace = true); + Utils::ChangeSet replacements(QByteArray buffer, + const QTextBlock &startBlock, + const QTextBlock &endBlock, + int cursorPositionInEditor, + ReplacementsToKeep replacementsToKeep, + const QChar &typedChar = QChar::Null, + bool secondTry = false) const; + + struct CachedStyle { + clang::format::FormatStyle style = clang::format::getNoStyle(); + QDateTime expirationTime; + void setCache(clang::format::FormatStyle newStyle, std::chrono::milliseconds timeout) + { + style = newStyle; + expirationTime = QDateTime::currentDateTime().addMSecs(timeout.count()); + } + }; + + mutable CachedStyle m_cachedStyle; }; } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatchecks.cpp b/src/plugins/clangformat/clangformatchecks.cpp index 6027e600d70..41bd75caa22 100644 --- a/src/plugins/clangformat/clangformatchecks.cpp +++ b/src/plugins/clangformat/clangformatchecks.cpp @@ -1,7 +1,7 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! +// THIS FILE IS AUTOMATICALLY GENERATED by generateClangFormatChecksLayout. DO NOT EDIT! #include "clangformatchecks.h" @@ -41,21 +41,101 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_AlignArrayOfStructures->addItems({"Left","Right","None",}); m_AlignArrayOfStructures->setObjectName("AlignArrayOfStructures"); - m_AlignConsecutiveMacros = new QComboBox(this); - m_AlignConsecutiveMacros->addItems({"None","Consecutive","AcrossEmptyLines","AcrossComments","AcrossEmptyLinesAndComments",}); - m_AlignConsecutiveMacros->setObjectName("AlignConsecutiveMacros"); + m_AlignConsecutiveMacrosEnabled = new QComboBox(this); + m_AlignConsecutiveMacrosEnabled->addItems({"Default","true","false",}); + m_AlignConsecutiveMacrosEnabled->setObjectName("AlignConsecutiveMacrosEnabled"); - m_AlignConsecutiveAssignments = new QComboBox(this); - m_AlignConsecutiveAssignments->addItems({"None","Consecutive","AcrossEmptyLines","AcrossComments","AcrossEmptyLinesAndComments",}); - m_AlignConsecutiveAssignments->setObjectName("AlignConsecutiveAssignments"); + m_AlignConsecutiveMacrosAcrossEmptyLines = new QComboBox(this); + m_AlignConsecutiveMacrosAcrossEmptyLines->addItems({"Default","true","false",}); + m_AlignConsecutiveMacrosAcrossEmptyLines->setObjectName("AlignConsecutiveMacrosAcrossEmptyLines"); - m_AlignConsecutiveBitFields = new QComboBox(this); - m_AlignConsecutiveBitFields->addItems({"None","Consecutive","AcrossEmptyLines","AcrossComments","AcrossEmptyLinesAndComments",}); - m_AlignConsecutiveBitFields->setObjectName("AlignConsecutiveBitFields"); + m_AlignConsecutiveMacrosAcrossComments = new QComboBox(this); + m_AlignConsecutiveMacrosAcrossComments->addItems({"Default","true","false",}); + m_AlignConsecutiveMacrosAcrossComments->setObjectName("AlignConsecutiveMacrosAcrossComments"); - m_AlignConsecutiveDeclarations = new QComboBox(this); - m_AlignConsecutiveDeclarations->addItems({"None","Consecutive","AcrossEmptyLines","AcrossComments","AcrossEmptyLinesAndComments",}); - m_AlignConsecutiveDeclarations->setObjectName("AlignConsecutiveDeclarations"); + m_AlignConsecutiveMacrosAlignCompound = new QComboBox(this); + m_AlignConsecutiveMacrosAlignCompound->addItems({"Default","true","false",}); + m_AlignConsecutiveMacrosAlignCompound->setObjectName("AlignConsecutiveMacrosAlignCompound"); + + m_AlignConsecutiveMacrosPadOperators = new QComboBox(this); + m_AlignConsecutiveMacrosPadOperators->addItems({"Default","true","false",}); + m_AlignConsecutiveMacrosPadOperators->setObjectName("AlignConsecutiveMacrosPadOperators"); + + m_AlignConsecutiveAssignmentsEnabled = new QComboBox(this); + m_AlignConsecutiveAssignmentsEnabled->addItems({"Default","true","false",}); + m_AlignConsecutiveAssignmentsEnabled->setObjectName("AlignConsecutiveAssignmentsEnabled"); + + m_AlignConsecutiveAssignmentsAcrossEmptyLines = new QComboBox(this); + m_AlignConsecutiveAssignmentsAcrossEmptyLines->addItems({"Default","true","false",}); + m_AlignConsecutiveAssignmentsAcrossEmptyLines->setObjectName("AlignConsecutiveAssignmentsAcrossEmptyLines"); + + m_AlignConsecutiveAssignmentsAcrossComments = new QComboBox(this); + m_AlignConsecutiveAssignmentsAcrossComments->addItems({"Default","true","false",}); + m_AlignConsecutiveAssignmentsAcrossComments->setObjectName("AlignConsecutiveAssignmentsAcrossComments"); + + m_AlignConsecutiveAssignmentsAlignCompound = new QComboBox(this); + m_AlignConsecutiveAssignmentsAlignCompound->addItems({"Default","true","false",}); + m_AlignConsecutiveAssignmentsAlignCompound->setObjectName("AlignConsecutiveAssignmentsAlignCompound"); + + m_AlignConsecutiveAssignmentsPadOperators = new QComboBox(this); + m_AlignConsecutiveAssignmentsPadOperators->addItems({"Default","true","false",}); + m_AlignConsecutiveAssignmentsPadOperators->setObjectName("AlignConsecutiveAssignmentsPadOperators"); + + m_AlignConsecutiveBitFieldsEnabled = new QComboBox(this); + m_AlignConsecutiveBitFieldsEnabled->addItems({"Default","true","false",}); + m_AlignConsecutiveBitFieldsEnabled->setObjectName("AlignConsecutiveBitFieldsEnabled"); + + m_AlignConsecutiveBitFieldsAcrossEmptyLines = new QComboBox(this); + m_AlignConsecutiveBitFieldsAcrossEmptyLines->addItems({"Default","true","false",}); + m_AlignConsecutiveBitFieldsAcrossEmptyLines->setObjectName("AlignConsecutiveBitFieldsAcrossEmptyLines"); + + m_AlignConsecutiveBitFieldsAcrossComments = new QComboBox(this); + m_AlignConsecutiveBitFieldsAcrossComments->addItems({"Default","true","false",}); + m_AlignConsecutiveBitFieldsAcrossComments->setObjectName("AlignConsecutiveBitFieldsAcrossComments"); + + m_AlignConsecutiveBitFieldsAlignCompound = new QComboBox(this); + m_AlignConsecutiveBitFieldsAlignCompound->addItems({"Default","true","false",}); + m_AlignConsecutiveBitFieldsAlignCompound->setObjectName("AlignConsecutiveBitFieldsAlignCompound"); + + m_AlignConsecutiveBitFieldsPadOperators = new QComboBox(this); + m_AlignConsecutiveBitFieldsPadOperators->addItems({"Default","true","false",}); + m_AlignConsecutiveBitFieldsPadOperators->setObjectName("AlignConsecutiveBitFieldsPadOperators"); + + m_AlignConsecutiveDeclarationsEnabled = new QComboBox(this); + m_AlignConsecutiveDeclarationsEnabled->addItems({"Default","true","false",}); + m_AlignConsecutiveDeclarationsEnabled->setObjectName("AlignConsecutiveDeclarationsEnabled"); + + m_AlignConsecutiveDeclarationsAcrossEmptyLines = new QComboBox(this); + m_AlignConsecutiveDeclarationsAcrossEmptyLines->addItems({"Default","true","false",}); + m_AlignConsecutiveDeclarationsAcrossEmptyLines->setObjectName("AlignConsecutiveDeclarationsAcrossEmptyLines"); + + m_AlignConsecutiveDeclarationsAcrossComments = new QComboBox(this); + m_AlignConsecutiveDeclarationsAcrossComments->addItems({"Default","true","false",}); + m_AlignConsecutiveDeclarationsAcrossComments->setObjectName("AlignConsecutiveDeclarationsAcrossComments"); + + m_AlignConsecutiveDeclarationsAlignCompound = new QComboBox(this); + m_AlignConsecutiveDeclarationsAlignCompound->addItems({"Default","true","false",}); + m_AlignConsecutiveDeclarationsAlignCompound->setObjectName("AlignConsecutiveDeclarationsAlignCompound"); + + m_AlignConsecutiveDeclarationsPadOperators = new QComboBox(this); + m_AlignConsecutiveDeclarationsPadOperators->addItems({"Default","true","false",}); + m_AlignConsecutiveDeclarationsPadOperators->setObjectName("AlignConsecutiveDeclarationsPadOperators"); + + m_AlignConsecutiveShortCaseStatementsEnabled = new QComboBox(this); + m_AlignConsecutiveShortCaseStatementsEnabled->addItems({"Default","true","false",}); + m_AlignConsecutiveShortCaseStatementsEnabled->setObjectName("AlignConsecutiveShortCaseStatementsEnabled"); + + m_AlignConsecutiveShortCaseStatementsAcrossEmptyLines = new QComboBox(this); + m_AlignConsecutiveShortCaseStatementsAcrossEmptyLines->addItems({"Default","true","false",}); + m_AlignConsecutiveShortCaseStatementsAcrossEmptyLines->setObjectName("AlignConsecutiveShortCaseStatementsAcrossEmptyLines"); + + m_AlignConsecutiveShortCaseStatementsAcrossComments = new QComboBox(this); + m_AlignConsecutiveShortCaseStatementsAcrossComments->addItems({"Default","true","false",}); + m_AlignConsecutiveShortCaseStatementsAcrossComments->setObjectName("AlignConsecutiveShortCaseStatementsAcrossComments"); + + m_AlignConsecutiveShortCaseStatementsAlignCaseColons = new QComboBox(this); + m_AlignConsecutiveShortCaseStatementsAlignCaseColons->addItems({"Default","true","false",}); + m_AlignConsecutiveShortCaseStatementsAlignCaseColons->setObjectName("AlignConsecutiveShortCaseStatementsAlignCaseColons"); m_AlignEscapedNewlines = new QComboBox(this); m_AlignEscapedNewlines->addItems({"DontAlign","Left","Right",}); @@ -65,22 +145,19 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_AlignOperands->addItems({"DontAlign","Align","AlignAfterOperator",}); m_AlignOperands->setObjectName("AlignOperands"); - m_AlignTrailingComments = new QComboBox(this); - m_AlignTrailingComments->addItems({"Default","true","false",}); - m_AlignTrailingComments->setObjectName("AlignTrailingComments"); + m_AlignTrailingCommentsKind = new QComboBox(this); + m_AlignTrailingCommentsKind->addItems({"Leave","Always","Never",}); + m_AlignTrailingCommentsKind->setObjectName("AlignTrailingCommentsKind"); + m_AlignTrailingCommentsOverEmptyLines = new QLineEdit(this); + m_AlignTrailingCommentsOverEmptyLines->setObjectName("AlignTrailingCommentsOverEmptyLines"); + m_setAlignTrailingCommentsOverEmptyLines = new QPushButton("Set", this); + + m_setAlignTrailingCommentsOverEmptyLines->setObjectName("setAlignTrailingCommentsOverEmptyLines"); m_AllowAllArgumentsOnNextLine = new QComboBox(this); m_AllowAllArgumentsOnNextLine->addItems({"Default","true","false",}); m_AllowAllArgumentsOnNextLine->setObjectName("AllowAllArgumentsOnNextLine"); - m_AllowAllParametersOfDeclarationOnNextLine = new QComboBox(this); - m_AllowAllParametersOfDeclarationOnNextLine->addItems({"Default","true","false",}); - m_AllowAllParametersOfDeclarationOnNextLine->setObjectName("AllowAllParametersOfDeclarationOnNextLine"); - - m_AllowShortEnumsOnASingleLine = new QComboBox(this); - m_AllowShortEnumsOnASingleLine->addItems({"Default","true","false",}); - m_AllowShortEnumsOnASingleLine->setObjectName("AllowShortEnumsOnASingleLine"); - m_AllowShortBlocksOnASingleLine = new QComboBox(this); m_AllowShortBlocksOnASingleLine->addItems({"Never","Empty","Always",}); m_AllowShortBlocksOnASingleLine->setObjectName("AllowShortBlocksOnASingleLine"); @@ -89,6 +166,10 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_AllowShortCaseLabelsOnASingleLine->addItems({"Default","true","false",}); m_AllowShortCaseLabelsOnASingleLine->setObjectName("AllowShortCaseLabelsOnASingleLine"); + m_AllowShortEnumsOnASingleLine = new QComboBox(this); + m_AllowShortEnumsOnASingleLine->addItems({"Default","true","false",}); + m_AllowShortEnumsOnASingleLine->setObjectName("AllowShortEnumsOnASingleLine"); + m_AllowShortFunctionsOnASingleLine = new QComboBox(this); m_AllowShortFunctionsOnASingleLine->addItems({"None","InlineOnly","Empty","Inline","All",}); m_AllowShortFunctionsOnASingleLine->setObjectName("AllowShortFunctionsOnASingleLine"); @@ -127,14 +208,94 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_BinPackArguments->addItems({"Default","true","false",}); m_BinPackArguments->setObjectName("BinPackArguments"); - m_InsertTrailingCommas = new QComboBox(this); - m_InsertTrailingCommas->addItems({"None","Wrapped",}); - m_InsertTrailingCommas->setObjectName("InsertTrailingCommas"); - m_BinPackParameters = new QComboBox(this); m_BinPackParameters->addItems({"Default","true","false",}); m_BinPackParameters->setObjectName("BinPackParameters"); + m_BitFieldColonSpacing = new QComboBox(this); + m_BitFieldColonSpacing->addItems({"Both","None","Before","After",}); + m_BitFieldColonSpacing->setObjectName("BitFieldColonSpacing"); + + m_BraceWrappingAfterCaseLabel = new QComboBox(this); + m_BraceWrappingAfterCaseLabel->addItems({"Default","true","false",}); + m_BraceWrappingAfterCaseLabel->setObjectName("BraceWrappingAfterCaseLabel"); + + m_BraceWrappingAfterClass = new QComboBox(this); + m_BraceWrappingAfterClass->addItems({"Default","true","false",}); + m_BraceWrappingAfterClass->setObjectName("BraceWrappingAfterClass"); + + m_BraceWrappingAfterControlStatement = new QComboBox(this); + m_BraceWrappingAfterControlStatement->addItems({"Never","MultiLine","Always",}); + m_BraceWrappingAfterControlStatement->setObjectName("BraceWrappingAfterControlStatement"); + + m_BraceWrappingAfterEnum = new QComboBox(this); + m_BraceWrappingAfterEnum->addItems({"Default","true","false",}); + m_BraceWrappingAfterEnum->setObjectName("BraceWrappingAfterEnum"); + + m_BraceWrappingAfterFunction = new QComboBox(this); + m_BraceWrappingAfterFunction->addItems({"Default","true","false",}); + m_BraceWrappingAfterFunction->setObjectName("BraceWrappingAfterFunction"); + + m_BraceWrappingAfterNamespace = new QComboBox(this); + m_BraceWrappingAfterNamespace->addItems({"Default","true","false",}); + m_BraceWrappingAfterNamespace->setObjectName("BraceWrappingAfterNamespace"); + + m_BraceWrappingAfterObjCDeclaration = new QComboBox(this); + m_BraceWrappingAfterObjCDeclaration->addItems({"Default","true","false",}); + m_BraceWrappingAfterObjCDeclaration->setObjectName("BraceWrappingAfterObjCDeclaration"); + + m_BraceWrappingAfterStruct = new QComboBox(this); + m_BraceWrappingAfterStruct->addItems({"Default","true","false",}); + m_BraceWrappingAfterStruct->setObjectName("BraceWrappingAfterStruct"); + + m_BraceWrappingAfterUnion = new QComboBox(this); + m_BraceWrappingAfterUnion->addItems({"Default","true","false",}); + m_BraceWrappingAfterUnion->setObjectName("BraceWrappingAfterUnion"); + + m_BraceWrappingAfterExternBlock = new QComboBox(this); + m_BraceWrappingAfterExternBlock->addItems({"Default","true","false",}); + m_BraceWrappingAfterExternBlock->setObjectName("BraceWrappingAfterExternBlock"); + + m_BraceWrappingBeforeCatch = new QComboBox(this); + m_BraceWrappingBeforeCatch->addItems({"Default","true","false",}); + m_BraceWrappingBeforeCatch->setObjectName("BraceWrappingBeforeCatch"); + + m_BraceWrappingBeforeElse = new QComboBox(this); + m_BraceWrappingBeforeElse->addItems({"Default","true","false",}); + m_BraceWrappingBeforeElse->setObjectName("BraceWrappingBeforeElse"); + + m_BraceWrappingBeforeLambdaBody = new QComboBox(this); + m_BraceWrappingBeforeLambdaBody->addItems({"Default","true","false",}); + m_BraceWrappingBeforeLambdaBody->setObjectName("BraceWrappingBeforeLambdaBody"); + + m_BraceWrappingBeforeWhile = new QComboBox(this); + m_BraceWrappingBeforeWhile->addItems({"Default","true","false",}); + m_BraceWrappingBeforeWhile->setObjectName("BraceWrappingBeforeWhile"); + + m_BraceWrappingIndentBraces = new QComboBox(this); + m_BraceWrappingIndentBraces->addItems({"Default","true","false",}); + m_BraceWrappingIndentBraces->setObjectName("BraceWrappingIndentBraces"); + + m_BraceWrappingSplitEmptyFunction = new QComboBox(this); + m_BraceWrappingSplitEmptyFunction->addItems({"Default","true","false",}); + m_BraceWrappingSplitEmptyFunction->setObjectName("BraceWrappingSplitEmptyFunction"); + + m_BraceWrappingSplitEmptyRecord = new QComboBox(this); + m_BraceWrappingSplitEmptyRecord->addItems({"Default","true","false",}); + m_BraceWrappingSplitEmptyRecord->setObjectName("BraceWrappingSplitEmptyRecord"); + + m_BraceWrappingSplitEmptyNamespace = new QComboBox(this); + m_BraceWrappingSplitEmptyNamespace->addItems({"Default","true","false",}); + m_BraceWrappingSplitEmptyNamespace->setObjectName("BraceWrappingSplitEmptyNamespace"); + + m_BreakAfterAttributes = new QComboBox(this); + m_BreakAfterAttributes->addItems({"Always","Leave","Never",}); + m_BreakAfterAttributes->setObjectName("BreakAfterAttributes"); + + m_BreakArrays = new QComboBox(this); + m_BreakArrays->addItems({"Default","true","false",}); + m_BreakArrays->setObjectName("BreakArrays"); + m_BreakBeforeBinaryOperators = new QComboBox(this); m_BreakBeforeBinaryOperators->addItems({"None","NonAssignment","All",}); m_BreakBeforeBinaryOperators->setObjectName("BreakBeforeBinaryOperators"); @@ -143,82 +304,14 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_BreakBeforeBraces->addItems({"Attach","Linux","Mozilla","Stroustrup","Allman","Whitesmiths","GNU","WebKit","Custom",}); m_BreakBeforeBraces->setObjectName("BreakBeforeBraces"); - m_AfterCaseLabel = new QComboBox(this); - m_AfterCaseLabel->addItems({"Default","true","false",}); - m_AfterCaseLabel->setObjectName("AfterCaseLabel"); - - m_AfterClass = new QComboBox(this); - m_AfterClass->addItems({"Default","true","false",}); - m_AfterClass->setObjectName("AfterClass"); - - m_AfterControlStatement = new QComboBox(this); - m_AfterControlStatement->addItems({"Never","MultiLine","Always",}); - m_AfterControlStatement->setObjectName("AfterControlStatement"); - - m_AfterEnum = new QComboBox(this); - m_AfterEnum->addItems({"Default","true","false",}); - m_AfterEnum->setObjectName("AfterEnum"); - - m_AfterFunction = new QComboBox(this); - m_AfterFunction->addItems({"Default","true","false",}); - m_AfterFunction->setObjectName("AfterFunction"); - - m_AfterNamespace = new QComboBox(this); - m_AfterNamespace->addItems({"Default","true","false",}); - m_AfterNamespace->setObjectName("AfterNamespace"); - - m_AfterObjCDeclaration = new QComboBox(this); - m_AfterObjCDeclaration->addItems({"Default","true","false",}); - m_AfterObjCDeclaration->setObjectName("AfterObjCDeclaration"); - - m_AfterStruct = new QComboBox(this); - m_AfterStruct->addItems({"Default","true","false",}); - m_AfterStruct->setObjectName("AfterStruct"); - - m_AfterUnion = new QComboBox(this); - m_AfterUnion->addItems({"Default","true","false",}); - m_AfterUnion->setObjectName("AfterUnion"); - - m_AfterExternBlock = new QComboBox(this); - m_AfterExternBlock->addItems({"Default","true","false",}); - m_AfterExternBlock->setObjectName("AfterExternBlock"); - - m_BeforeCatch = new QComboBox(this); - m_BeforeCatch->addItems({"Default","true","false",}); - m_BeforeCatch->setObjectName("BeforeCatch"); - - m_BeforeElse = new QComboBox(this); - m_BeforeElse->addItems({"Default","true","false",}); - m_BeforeElse->setObjectName("BeforeElse"); - - m_BeforeLambdaBody = new QComboBox(this); - m_BeforeLambdaBody->addItems({"Default","true","false",}); - m_BeforeLambdaBody->setObjectName("BeforeLambdaBody"); - - m_BeforeWhile = new QComboBox(this); - m_BeforeWhile->addItems({"Default","true","false",}); - m_BeforeWhile->setObjectName("BeforeWhile"); - - m_IndentBraces = new QComboBox(this); - m_IndentBraces->addItems({"Default","true","false",}); - m_IndentBraces->setObjectName("IndentBraces"); - - m_SplitEmptyFunction = new QComboBox(this); - m_SplitEmptyFunction->addItems({"Default","true","false",}); - m_SplitEmptyFunction->setObjectName("SplitEmptyFunction"); - - m_SplitEmptyRecord = new QComboBox(this); - m_SplitEmptyRecord->addItems({"Default","true","false",}); - m_SplitEmptyRecord->setObjectName("SplitEmptyRecord"); - - m_SplitEmptyNamespace = new QComboBox(this); - m_SplitEmptyNamespace->addItems({"Default","true","false",}); - m_SplitEmptyNamespace->setObjectName("SplitEmptyNamespace"); - m_BreakBeforeConceptDeclarations = new QComboBox(this); - m_BreakBeforeConceptDeclarations->addItems({"Default","true","false",}); + m_BreakBeforeConceptDeclarations->addItems({"Never","Allowed","Always",}); m_BreakBeforeConceptDeclarations->setObjectName("BreakBeforeConceptDeclarations"); + m_BreakBeforeInlineASMColon = new QComboBox(this); + m_BreakBeforeInlineASMColon->addItems({"Never","OnlyMultiline","Always",}); + m_BreakBeforeInlineASMColon->setObjectName("BreakBeforeInlineASMColon"); + m_BreakBeforeTernaryOperators = new QComboBox(this); m_BreakBeforeTernaryOperators->addItems({"Default","true","false",}); m_BreakBeforeTernaryOperators->setObjectName("BreakBeforeTernaryOperators"); @@ -245,16 +338,6 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setCommentPragmas = new QPushButton("Set", this); m_setCommentPragmas->setObjectName("setCommentPragmas"); - m_QualifierAlignment = new QComboBox(this); - m_QualifierAlignment->addItems({"Leave","Left","Right","Custom",}); - m_QualifierAlignment->setObjectName("QualifierAlignment"); - - m_QualifierOrder = new QPlainTextEdit(this); - m_QualifierOrder->setObjectName("QualifierOrder"); - m_QualifierOrder->setFixedHeight(100); - m_setQualifierOrder = new QPushButton("Set", this); - - m_setQualifierOrder->setObjectName("setQualifierOrder"); m_BreakInheritanceList = new QComboBox(this); m_BreakInheritanceList->addItems({"BeforeColon","BeforeComma","AfterColon","AfterComma",}); m_BreakInheritanceList->setObjectName("BreakInheritanceList"); @@ -263,11 +346,6 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_CompactNamespaces->addItems({"Default","true","false",}); m_CompactNamespaces->setObjectName("CompactNamespaces"); - m_ConstructorInitializerIndentWidth = new QLineEdit(this); - m_ConstructorInitializerIndentWidth->setObjectName("ConstructorInitializerIndentWidth"); - m_setConstructorInitializerIndentWidth = new QPushButton("Set", this); - - m_setConstructorInitializerIndentWidth->setObjectName("setConstructorInitializerIndentWidth"); m_ContinuationIndentWidth = new QLineEdit(this); m_ContinuationIndentWidth->setObjectName("ContinuationIndentWidth"); m_setContinuationIndentWidth = new QPushButton("Set", this); @@ -277,14 +355,6 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_Cpp11BracedListStyle->addItems({"Default","true","false",}); m_Cpp11BracedListStyle->setObjectName("Cpp11BracedListStyle"); - m_DeriveLineEnding = new QComboBox(this); - m_DeriveLineEnding->addItems({"Default","true","false",}); - m_DeriveLineEnding->setObjectName("DeriveLineEnding"); - - m_DerivePointerAlignment = new QComboBox(this); - m_DerivePointerAlignment->addItems({"Default","true","false",}); - m_DerivePointerAlignment->setObjectName("DerivePointerAlignment"); - m_DisableFormat = new QComboBox(this); m_DisableFormat->addItems({"Default","true","false",}); m_DisableFormat->setObjectName("DisableFormat"); @@ -301,10 +371,6 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_ExperimentalAutoDetectBinPacking->addItems({"Default","true","false",}); m_ExperimentalAutoDetectBinPacking->setObjectName("ExperimentalAutoDetectBinPacking"); - m_PackConstructorInitializers = new QComboBox(this); - m_PackConstructorInitializers->addItems({"Never","BinPack","CurrentLine","NextLine",}); - m_PackConstructorInitializers->setObjectName("PackConstructorInitializers"); - m_FixNamespaceComments = new QComboBox(this); m_FixNamespaceComments->addItems({"Default","true","false",}); m_FixNamespaceComments->setObjectName("FixNamespaceComments"); @@ -321,57 +387,33 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setIfMacros = new QPushButton("Set", this); m_setIfMacros->setObjectName("setIfMacros"); - m_TypenameMacros = new QPlainTextEdit(this); - m_TypenameMacros->setObjectName("TypenameMacros"); - m_TypenameMacros->setFixedHeight(100); - m_setTypenameMacros = new QPushButton("Set", this); - - m_setTypenameMacros->setObjectName("setTypenameMacros"); - m_StatementMacros = new QPlainTextEdit(this); - m_StatementMacros->setObjectName("StatementMacros"); - m_StatementMacros->setFixedHeight(100); - m_setStatementMacros = new QPushButton("Set", this); - - m_setStatementMacros->setObjectName("setStatementMacros"); - m_NamespaceMacros = new QPlainTextEdit(this); - m_NamespaceMacros->setObjectName("NamespaceMacros"); - m_NamespaceMacros->setFixedHeight(100); - m_setNamespaceMacros = new QPushButton("Set", this); - - m_setNamespaceMacros->setObjectName("setNamespaceMacros"); - m_WhitespaceSensitiveMacros = new QPlainTextEdit(this); - m_WhitespaceSensitiveMacros->setObjectName("WhitespaceSensitiveMacros"); - m_WhitespaceSensitiveMacros->setFixedHeight(100); - m_setWhitespaceSensitiveMacros = new QPushButton("Set", this); - - m_setWhitespaceSensitiveMacros->setObjectName("setWhitespaceSensitiveMacros"); m_IndentAccessModifiers = new QComboBox(this); m_IndentAccessModifiers->addItems({"Default","true","false",}); m_IndentAccessModifiers->setObjectName("IndentAccessModifiers"); - m_IndentCaseLabels = new QComboBox(this); - m_IndentCaseLabels->addItems({"Default","true","false",}); - m_IndentCaseLabels->setObjectName("IndentCaseLabels"); - m_IndentCaseBlocks = new QComboBox(this); m_IndentCaseBlocks->addItems({"Default","true","false",}); m_IndentCaseBlocks->setObjectName("IndentCaseBlocks"); + m_IndentCaseLabels = new QComboBox(this); + m_IndentCaseLabels->addItems({"Default","true","false",}); + m_IndentCaseLabels->setObjectName("IndentCaseLabels"); + m_IndentGotoLabels = new QComboBox(this); m_IndentGotoLabels->addItems({"Default","true","false",}); m_IndentGotoLabels->setObjectName("IndentGotoLabels"); - m_IndentPPDirectives = new QComboBox(this); - m_IndentPPDirectives->addItems({"None","AfterHash","BeforeHash",}); - m_IndentPPDirectives->setObjectName("IndentPPDirectives"); - m_IndentExternBlock = new QComboBox(this); m_IndentExternBlock->addItems({"AfterExternBlock","NoIndent","Indent",}); m_IndentExternBlock->setObjectName("IndentExternBlock"); - m_IndentRequires = new QComboBox(this); - m_IndentRequires->addItems({"Default","true","false",}); - m_IndentRequires->setObjectName("IndentRequires"); + m_IndentPPDirectives = new QComboBox(this); + m_IndentPPDirectives->addItems({"None","AfterHash","BeforeHash",}); + m_IndentPPDirectives->setObjectName("IndentPPDirectives"); + + m_IndentRequiresClause = new QComboBox(this); + m_IndentRequiresClause->addItems({"Default","true","false",}); + m_IndentRequiresClause->setObjectName("IndentRequiresClause"); m_IndentWidth = new QLineEdit(this); m_IndentWidth->setObjectName("IndentWidth"); @@ -382,6 +424,18 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_IndentWrappedFunctionNames->addItems({"Default","true","false",}); m_IndentWrappedFunctionNames->setObjectName("IndentWrappedFunctionNames"); + m_InsertBraces = new QComboBox(this); + m_InsertBraces->addItems({"Default","true","false",}); + m_InsertBraces->setObjectName("InsertBraces"); + + m_InsertNewlineAtEOF = new QComboBox(this); + m_InsertNewlineAtEOF->addItems({"Default","true","false",}); + m_InsertNewlineAtEOF->setObjectName("InsertNewlineAtEOF"); + + m_InsertTrailingCommas = new QComboBox(this); + m_InsertTrailingCommas->addItems({"None","Wrapped",}); + m_InsertTrailingCommas->setObjectName("InsertTrailingCommas"); + m_JavaImportGroups = new QPlainTextEdit(this); m_JavaImportGroups->setObjectName("JavaImportGroups"); m_JavaImportGroups->setFixedHeight(100); @@ -396,18 +450,26 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_JavaScriptWrapImports->addItems({"Default","true","false",}); m_JavaScriptWrapImports->setObjectName("JavaScriptWrapImports"); + m_KeepEmptyLinesAtEOF = new QComboBox(this); + m_KeepEmptyLinesAtEOF->addItems({"Default","true","false",}); + m_KeepEmptyLinesAtEOF->setObjectName("KeepEmptyLinesAtEOF"); + m_KeepEmptyLinesAtTheStartOfBlocks = new QComboBox(this); m_KeepEmptyLinesAtTheStartOfBlocks->addItems({"Default","true","false",}); m_KeepEmptyLinesAtTheStartOfBlocks->setObjectName("KeepEmptyLinesAtTheStartOfBlocks"); - m_Language = new QComboBox(this); - m_Language->addItems({"None","Cpp","CSharp","Java","JavaScript","Json","ObjC","Proto","TableGen","TextProto",}); - m_Language->setObjectName("Language"); - m_LambdaBodyIndentation = new QComboBox(this); m_LambdaBodyIndentation->addItems({"Signature","OuterScope",}); m_LambdaBodyIndentation->setObjectName("LambdaBodyIndentation"); + m_Language = new QComboBox(this); + m_Language->addItems({"None","Cpp","CSharp","Java","JavaScript","Json","ObjC","Proto","TableGen","TextProto","Verilog",}); + m_Language->setObjectName("Language"); + + m_LineEnding = new QComboBox(this); + m_LineEnding->addItems({"LF","CRLF","DeriveLF","DeriveCRLF",}); + m_LineEnding->setObjectName("LineEnding"); + m_MacroBlockBegin = new QLineEdit(this); m_MacroBlockBegin->setObjectName("MacroBlockBegin"); m_setMacroBlockBegin = new QPushButton("Set", this); @@ -418,6 +480,12 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setMacroBlockEnd = new QPushButton("Set", this); m_setMacroBlockEnd->setObjectName("setMacroBlockEnd"); + m_Macros = new QPlainTextEdit(this); + m_Macros->setObjectName("Macros"); + m_Macros->setFixedHeight(100); + m_setMacros = new QPushButton("Set", this); + + m_setMacros->setObjectName("setMacros"); m_MaxEmptyLinesToKeep = new QLineEdit(this); m_MaxEmptyLinesToKeep->setObjectName("MaxEmptyLinesToKeep"); m_setMaxEmptyLinesToKeep = new QPushButton("Set", this); @@ -427,6 +495,12 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_NamespaceIndentation->addItems({"None","Inner","All",}); m_NamespaceIndentation->setObjectName("NamespaceIndentation"); + m_NamespaceMacros = new QPlainTextEdit(this); + m_NamespaceMacros->setObjectName("NamespaceMacros"); + m_NamespaceMacros->setFixedHeight(100); + m_setNamespaceMacros = new QPushButton("Set", this); + + m_setNamespaceMacros->setObjectName("setNamespaceMacros"); m_ObjCBinPackProtocolList = new QComboBox(this); m_ObjCBinPackProtocolList->addItems({"Auto","Always","Never",}); m_ObjCBinPackProtocolList->setObjectName("ObjCBinPackProtocolList"); @@ -436,18 +510,22 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setObjCBlockIndentWidth = new QPushButton("Set", this); m_setObjCBlockIndentWidth->setObjectName("setObjCBlockIndentWidth"); - m_ObjCSpaceAfterProperty = new QComboBox(this); - m_ObjCSpaceAfterProperty->addItems({"Default","true","false",}); - m_ObjCSpaceAfterProperty->setObjectName("ObjCSpaceAfterProperty"); - m_ObjCBreakBeforeNestedBlockParam = new QComboBox(this); m_ObjCBreakBeforeNestedBlockParam->addItems({"Default","true","false",}); m_ObjCBreakBeforeNestedBlockParam->setObjectName("ObjCBreakBeforeNestedBlockParam"); + m_ObjCSpaceAfterProperty = new QComboBox(this); + m_ObjCSpaceAfterProperty->addItems({"Default","true","false",}); + m_ObjCSpaceAfterProperty->setObjectName("ObjCSpaceAfterProperty"); + m_ObjCSpaceBeforeProtocolList = new QComboBox(this); m_ObjCSpaceBeforeProtocolList->addItems({"Default","true","false",}); m_ObjCSpaceBeforeProtocolList->setObjectName("ObjCSpaceBeforeProtocolList"); + m_PackConstructorInitializers = new QComboBox(this); + m_PackConstructorInitializers->addItems({"Never","BinPack","CurrentLine","NextLine","NextLineOnly",}); + m_PackConstructorInitializers->setObjectName("PackConstructorInitializers"); + m_PenaltyBreakAssignment = new QLineEdit(this); m_PenaltyBreakAssignment->setObjectName("PenaltyBreakAssignment"); m_setPenaltyBreakAssignment = new QPushButton("Set", this); @@ -488,16 +566,16 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setPenaltyExcessCharacter = new QPushButton("Set", this); m_setPenaltyExcessCharacter->setObjectName("setPenaltyExcessCharacter"); - m_PenaltyReturnTypeOnItsOwnLine = new QLineEdit(this); - m_PenaltyReturnTypeOnItsOwnLine->setObjectName("PenaltyReturnTypeOnItsOwnLine"); - m_setPenaltyReturnTypeOnItsOwnLine = new QPushButton("Set", this); - - m_setPenaltyReturnTypeOnItsOwnLine->setObjectName("setPenaltyReturnTypeOnItsOwnLine"); m_PenaltyIndentedWhitespace = new QLineEdit(this); m_PenaltyIndentedWhitespace->setObjectName("PenaltyIndentedWhitespace"); m_setPenaltyIndentedWhitespace = new QPushButton("Set", this); m_setPenaltyIndentedWhitespace->setObjectName("setPenaltyIndentedWhitespace"); + m_PenaltyReturnTypeOnItsOwnLine = new QLineEdit(this); + m_PenaltyReturnTypeOnItsOwnLine->setObjectName("PenaltyReturnTypeOnItsOwnLine"); + m_setPenaltyReturnTypeOnItsOwnLine = new QPushButton("Set", this); + + m_setPenaltyReturnTypeOnItsOwnLine->setObjectName("setPenaltyReturnTypeOnItsOwnLine"); m_PointerAlignment = new QComboBox(this); m_PointerAlignment->addItems({"Left","Right","Middle",}); m_PointerAlignment->setObjectName("PointerAlignment"); @@ -507,6 +585,16 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setPPIndentWidth = new QPushButton("Set", this); m_setPPIndentWidth->setObjectName("setPPIndentWidth"); + m_QualifierAlignment = new QComboBox(this); + m_QualifierAlignment->addItems({"Leave","Left","Right","Custom",}); + m_QualifierAlignment->setObjectName("QualifierAlignment"); + + m_QualifierOrder = new QPlainTextEdit(this); + m_QualifierOrder->setObjectName("QualifierOrder"); + m_QualifierOrder->setFixedHeight(100); + m_setQualifierOrder = new QPushButton("Set", this); + + m_setQualifierOrder->setObjectName("setQualifierOrder"); m_ReferenceAlignment = new QComboBox(this); m_ReferenceAlignment->addItems({"Pointer","Left","Right","Middle",}); m_ReferenceAlignment->setObjectName("ReferenceAlignment"); @@ -519,6 +607,22 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_RemoveBracesLLVM->addItems({"Default","true","false",}); m_RemoveBracesLLVM->setObjectName("RemoveBracesLLVM"); + m_RemoveParentheses = new QComboBox(this); + m_RemoveParentheses->addItems({"Leave","MultipleParentheses","ReturnStatement",}); + m_RemoveParentheses->setObjectName("RemoveParentheses"); + + m_RemoveSemicolon = new QComboBox(this); + m_RemoveSemicolon->addItems({"Default","true","false",}); + m_RemoveSemicolon->setObjectName("RemoveSemicolon"); + + m_RequiresClausePosition = new QComboBox(this); + m_RequiresClausePosition->addItems({"OwnLine","WithPreceding","WithFollowing","SingleLine",}); + m_RequiresClausePosition->setObjectName("RequiresClausePosition"); + + m_RequiresExpressionIndentation = new QComboBox(this); + m_RequiresExpressionIndentation->addItems({"OuterScope","Keyword",}); + m_RequiresExpressionIndentation->setObjectName("RequiresExpressionIndentation"); + m_SeparateDefinitionBlocks = new QComboBox(this); m_SeparateDefinitionBlocks->addItems({"Leave","Always","Never",}); m_SeparateDefinitionBlocks->setObjectName("SeparateDefinitionBlocks"); @@ -537,7 +641,7 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_SortJavaStaticImport->setObjectName("SortJavaStaticImport"); m_SortUsingDeclarations = new QComboBox(this); - m_SortUsingDeclarations->addItems({"Default","true","false",}); + m_SortUsingDeclarations->addItems({"Never","Lexicographic","LexicographicNumeric",}); m_SortUsingDeclarations->setObjectName("SortUsingDeclarations"); m_SpaceAfterCStyleCast = new QComboBox(this); @@ -576,37 +680,53 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_SpaceBeforeInheritanceColon->addItems({"Default","true","false",}); m_SpaceBeforeInheritanceColon->setObjectName("SpaceBeforeInheritanceColon"); + m_SpaceBeforeJsonColon = new QComboBox(this); + m_SpaceBeforeJsonColon->addItems({"Default","true","false",}); + m_SpaceBeforeJsonColon->setObjectName("SpaceBeforeJsonColon"); + m_SpaceBeforeParens = new QComboBox(this); m_SpaceBeforeParens->addItems({"Never","ControlStatements","ControlStatementsExceptControlMacros","NonEmptyParentheses","Always","Custom",}); m_SpaceBeforeParens->setObjectName("SpaceBeforeParens"); - m_AfterControlStatements = new QComboBox(this); - m_AfterControlStatements->addItems({"Default","true","false",}); - m_AfterControlStatements->setObjectName("AfterControlStatements"); + m_SpaceBeforeParensOptionsAfterControlStatements = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterControlStatements->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterControlStatements->setObjectName("SpaceBeforeParensOptionsAfterControlStatements"); - m_AfterForeachMacros = new QComboBox(this); - m_AfterForeachMacros->addItems({"Default","true","false",}); - m_AfterForeachMacros->setObjectName("AfterForeachMacros"); + m_SpaceBeforeParensOptionsAfterForeachMacros = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterForeachMacros->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterForeachMacros->setObjectName("SpaceBeforeParensOptionsAfterForeachMacros"); - m_AfterFunctionDeclarationName = new QComboBox(this); - m_AfterFunctionDeclarationName->addItems({"Default","true","false",}); - m_AfterFunctionDeclarationName->setObjectName("AfterFunctionDeclarationName"); + m_SpaceBeforeParensOptionsAfterFunctionDeclarationName = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterFunctionDeclarationName->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterFunctionDeclarationName->setObjectName("SpaceBeforeParensOptionsAfterFunctionDeclarationName"); - m_AfterFunctionDefinitionName = new QComboBox(this); - m_AfterFunctionDefinitionName->addItems({"Default","true","false",}); - m_AfterFunctionDefinitionName->setObjectName("AfterFunctionDefinitionName"); + m_SpaceBeforeParensOptionsAfterFunctionDefinitionName = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterFunctionDefinitionName->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterFunctionDefinitionName->setObjectName("SpaceBeforeParensOptionsAfterFunctionDefinitionName"); - m_AfterIfMacros = new QComboBox(this); - m_AfterIfMacros->addItems({"Default","true","false",}); - m_AfterIfMacros->setObjectName("AfterIfMacros"); + m_SpaceBeforeParensOptionsAfterIfMacros = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterIfMacros->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterIfMacros->setObjectName("SpaceBeforeParensOptionsAfterIfMacros"); - m_AfterOverloadedOperator = new QComboBox(this); - m_AfterOverloadedOperator->addItems({"Default","true","false",}); - m_AfterOverloadedOperator->setObjectName("AfterOverloadedOperator"); + m_SpaceBeforeParensOptionsAfterOverloadedOperator = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterOverloadedOperator->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterOverloadedOperator->setObjectName("SpaceBeforeParensOptionsAfterOverloadedOperator"); - m_BeforeNonEmptyParentheses = new QComboBox(this); - m_BeforeNonEmptyParentheses->addItems({"Default","true","false",}); - m_BeforeNonEmptyParentheses->setObjectName("BeforeNonEmptyParentheses"); + m_SpaceBeforeParensOptionsAfterRequiresInClause = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterRequiresInClause->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterRequiresInClause->setObjectName("SpaceBeforeParensOptionsAfterRequiresInClause"); + + m_SpaceBeforeParensOptionsAfterRequiresInExpression = new QComboBox(this); + m_SpaceBeforeParensOptionsAfterRequiresInExpression->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsAfterRequiresInExpression->setObjectName("SpaceBeforeParensOptionsAfterRequiresInExpression"); + + m_SpaceBeforeParensOptionsBeforeNonEmptyParentheses = new QComboBox(this); + m_SpaceBeforeParensOptionsBeforeNonEmptyParentheses->addItems({"Default","true","false",}); + m_SpaceBeforeParensOptionsBeforeNonEmptyParentheses->setObjectName("SpaceBeforeParensOptionsBeforeNonEmptyParentheses"); + + m_SpaceBeforeSquareBrackets = new QComboBox(this); + m_SpaceBeforeSquareBrackets->addItems({"Default","true","false",}); + m_SpaceBeforeSquareBrackets->setObjectName("SpaceBeforeSquareBrackets"); m_SpaceBeforeRangeBasedForLoopColon = new QComboBox(this); m_SpaceBeforeRangeBasedForLoopColon->addItems({"Default","true","false",}); @@ -616,57 +736,40 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_SpaceInEmptyBlock->addItems({"Default","true","false",}); m_SpaceInEmptyBlock->setObjectName("SpaceInEmptyBlock"); - m_SpaceInEmptyParentheses = new QComboBox(this); - m_SpaceInEmptyParentheses->addItems({"Default","true","false",}); - m_SpaceInEmptyParentheses->setObjectName("SpaceInEmptyParentheses"); - - m_SpacesBeforeTrailingComments = new QLineEdit(this); - m_SpacesBeforeTrailingComments->setObjectName("SpacesBeforeTrailingComments"); - m_setSpacesBeforeTrailingComments = new QPushButton("Set", this); - - m_setSpacesBeforeTrailingComments->setObjectName("setSpacesBeforeTrailingComments"); m_SpacesInAngles = new QComboBox(this); m_SpacesInAngles->addItems({"Never","Always","Leave",}); m_SpacesInAngles->setObjectName("SpacesInAngles"); - m_SpacesInConditionalStatement = new QComboBox(this); - m_SpacesInConditionalStatement->addItems({"Default","true","false",}); - m_SpacesInConditionalStatement->setObjectName("SpacesInConditionalStatement"); + m_SpacesInLineCommentPrefixMinimum = new QLineEdit(this); + m_SpacesInLineCommentPrefixMinimum->setObjectName("SpacesInLineCommentPrefixMinimum"); + m_setSpacesInLineCommentPrefixMinimum = new QPushButton("Set", this); - m_SpacesInContainerLiterals = new QComboBox(this); - m_SpacesInContainerLiterals->addItems({"Default","true","false",}); - m_SpacesInContainerLiterals->setObjectName("SpacesInContainerLiterals"); + m_setSpacesInLineCommentPrefixMinimum->setObjectName("setSpacesInLineCommentPrefixMinimum"); + m_SpacesInLineCommentPrefixMaximum = new QLineEdit(this); + m_SpacesInLineCommentPrefixMaximum->setObjectName("SpacesInLineCommentPrefixMaximum"); + m_setSpacesInLineCommentPrefixMaximum = new QPushButton("Set", this); - m_SpacesInCStyleCastParentheses = new QComboBox(this); - m_SpacesInCStyleCastParentheses->addItems({"Default","true","false",}); - m_SpacesInCStyleCastParentheses->setObjectName("SpacesInCStyleCastParentheses"); + m_setSpacesInLineCommentPrefixMaximum->setObjectName("setSpacesInLineCommentPrefixMaximum"); + m_SpacesInParensOptionsInConditionalStatements = new QComboBox(this); + m_SpacesInParensOptionsInConditionalStatements->addItems({"Default","true","false",}); + m_SpacesInParensOptionsInConditionalStatements->setObjectName("SpacesInParensOptionsInConditionalStatements"); - m_Minimum = new QLineEdit(this); - m_Minimum->setObjectName("Minimum"); - m_setMinimum = new QPushButton("Set", this); + m_SpacesInParensOptionsInCStyleCasts = new QComboBox(this); + m_SpacesInParensOptionsInCStyleCasts->addItems({"Default","true","false",}); + m_SpacesInParensOptionsInCStyleCasts->setObjectName("SpacesInParensOptionsInCStyleCasts"); - m_setMinimum->setObjectName("setMinimum"); - m_Maximum = new QLineEdit(this); - m_Maximum->setObjectName("Maximum"); - m_setMaximum = new QPushButton("Set", this); + m_SpacesInParensOptionsInEmptyParentheses = new QComboBox(this); + m_SpacesInParensOptionsInEmptyParentheses->addItems({"Default","true","false",}); + m_SpacesInParensOptionsInEmptyParentheses->setObjectName("SpacesInParensOptionsInEmptyParentheses"); - m_setMaximum->setObjectName("setMaximum"); - m_SpacesInParentheses = new QComboBox(this); - m_SpacesInParentheses->addItems({"Default","true","false",}); - m_SpacesInParentheses->setObjectName("SpacesInParentheses"); + m_SpacesInParensOptionsOther = new QComboBox(this); + m_SpacesInParensOptionsOther->addItems({"Default","true","false",}); + m_SpacesInParensOptionsOther->setObjectName("SpacesInParensOptionsOther"); m_SpacesInSquareBrackets = new QComboBox(this); m_SpacesInSquareBrackets->addItems({"Default","true","false",}); m_SpacesInSquareBrackets->setObjectName("SpacesInSquareBrackets"); - m_SpaceBeforeSquareBrackets = new QComboBox(this); - m_SpaceBeforeSquareBrackets->addItems({"Default","true","false",}); - m_SpaceBeforeSquareBrackets->setObjectName("SpaceBeforeSquareBrackets"); - - m_BitFieldColonSpacing = new QComboBox(this); - m_BitFieldColonSpacing->addItems({"Both","None","Before","After",}); - m_BitFieldColonSpacing->setObjectName("BitFieldColonSpacing"); - m_Standard = new QComboBox(this); m_Standard->addItems({"Cpp03","Cpp11","Cpp14","Cpp17","Cpp20","Latest","Auto",}); m_Standard->setObjectName("Standard"); @@ -677,19 +780,43 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) m_setStatementAttributeLikeMacros = new QPushButton("Set", this); m_setStatementAttributeLikeMacros->setObjectName("setStatementAttributeLikeMacros"); + m_StatementMacros = new QPlainTextEdit(this); + m_StatementMacros->setObjectName("StatementMacros"); + m_StatementMacros->setFixedHeight(100); + m_setStatementMacros = new QPushButton("Set", this); + + m_setStatementMacros->setObjectName("setStatementMacros"); m_TabWidth = new QLineEdit(this); m_TabWidth->setObjectName("TabWidth"); m_setTabWidth = new QPushButton("Set", this); m_setTabWidth->setObjectName("setTabWidth"); - m_UseCRLF = new QComboBox(this); - m_UseCRLF->addItems({"Default","true","false",}); - m_UseCRLF->setObjectName("UseCRLF"); + m_TypeNames = new QPlainTextEdit(this); + m_TypeNames->setObjectName("TypeNames"); + m_TypeNames->setFixedHeight(100); + m_setTypeNames = new QPushButton("Set", this); + m_setTypeNames->setObjectName("setTypeNames"); + m_TypenameMacros = new QPlainTextEdit(this); + m_TypenameMacros->setObjectName("TypenameMacros"); + m_TypenameMacros->setFixedHeight(100); + m_setTypenameMacros = new QPushButton("Set", this); + + m_setTypenameMacros->setObjectName("setTypenameMacros"); m_UseTab = new QComboBox(this); m_UseTab->addItems({"Never","ForIndentation","ForContinuationAndIndentation","AlignWithSpaces","Always",}); m_UseTab->setObjectName("UseTab"); + m_VerilogBreakBetweenInstancePorts = new QComboBox(this); + m_VerilogBreakBetweenInstancePorts->addItems({"Default","true","false",}); + m_VerilogBreakBetweenInstancePorts->setObjectName("VerilogBreakBetweenInstancePorts"); + + m_WhitespaceSensitiveMacros = new QPlainTextEdit(this); + m_WhitespaceSensitiveMacros->setObjectName("WhitespaceSensitiveMacros"); + m_WhitespaceSensitiveMacros->setFixedHeight(100); + m_setWhitespaceSensitiveMacros = new QPushButton("Set", this); + + m_setWhitespaceSensitiveMacros->setObjectName("setWhitespaceSensitiveMacros"); using namespace Layouting; @@ -699,18 +826,44 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) new QLabel("AccessModifierOffset"), Row {m_AccessModifierOffset, m_setAccessModifierOffset}, br, new QLabel("AlignAfterOpenBracket"), m_AlignAfterOpenBracket, br, new QLabel("AlignArrayOfStructures"), m_AlignArrayOfStructures, br, - new QLabel("AlignConsecutiveMacros"), m_AlignConsecutiveMacros, br, - new QLabel("AlignConsecutiveAssignments"), m_AlignConsecutiveAssignments, br, - new QLabel("AlignConsecutiveBitFields"), m_AlignConsecutiveBitFields, br, - new QLabel("AlignConsecutiveDeclarations"), m_AlignConsecutiveDeclarations, br, + new QLabel("AlignConsecutiveMacros"), br, + new QLabel(" Enabled"), m_AlignConsecutiveMacrosEnabled, br, + new QLabel(" AcrossEmptyLines"), m_AlignConsecutiveMacrosAcrossEmptyLines, br, + new QLabel(" AcrossComments"), m_AlignConsecutiveMacrosAcrossComments, br, + new QLabel(" AlignCompound"), m_AlignConsecutiveMacrosAlignCompound, br, + new QLabel(" PadOperators"), m_AlignConsecutiveMacrosPadOperators, br, + new QLabel("AlignConsecutiveAssignments"), br, + new QLabel(" Enabled"), m_AlignConsecutiveAssignmentsEnabled, br, + new QLabel(" AcrossEmptyLines"), m_AlignConsecutiveAssignmentsAcrossEmptyLines, br, + new QLabel(" AcrossComments"), m_AlignConsecutiveAssignmentsAcrossComments, br, + new QLabel(" AlignCompound"), m_AlignConsecutiveAssignmentsAlignCompound, br, + new QLabel(" PadOperators"), m_AlignConsecutiveAssignmentsPadOperators, br, + new QLabel("AlignConsecutiveBitFields"), br, + new QLabel(" Enabled"), m_AlignConsecutiveBitFieldsEnabled, br, + new QLabel(" AcrossEmptyLines"), m_AlignConsecutiveBitFieldsAcrossEmptyLines, br, + new QLabel(" AcrossComments"), m_AlignConsecutiveBitFieldsAcrossComments, br, + new QLabel(" AlignCompound"), m_AlignConsecutiveBitFieldsAlignCompound, br, + new QLabel(" PadOperators"), m_AlignConsecutiveBitFieldsPadOperators, br, + new QLabel("AlignConsecutiveDeclarations"), br, + new QLabel(" Enabled"), m_AlignConsecutiveDeclarationsEnabled, br, + new QLabel(" AcrossEmptyLines"), m_AlignConsecutiveDeclarationsAcrossEmptyLines, br, + new QLabel(" AcrossComments"), m_AlignConsecutiveDeclarationsAcrossComments, br, + new QLabel(" AlignCompound"), m_AlignConsecutiveDeclarationsAlignCompound, br, + new QLabel(" PadOperators"), m_AlignConsecutiveDeclarationsPadOperators, br, + new QLabel("AlignConsecutiveShortCaseStatements"), br, + new QLabel(" Enabled"), m_AlignConsecutiveShortCaseStatementsEnabled, br, + new QLabel(" AcrossEmptyLines"), m_AlignConsecutiveShortCaseStatementsAcrossEmptyLines, br, + new QLabel(" AcrossComments"), m_AlignConsecutiveShortCaseStatementsAcrossComments, br, + new QLabel(" AlignCaseColons"), m_AlignConsecutiveShortCaseStatementsAlignCaseColons, br, new QLabel("AlignEscapedNewlines"), m_AlignEscapedNewlines, br, new QLabel("AlignOperands"), m_AlignOperands, br, - new QLabel("AlignTrailingComments"), m_AlignTrailingComments, br, + new QLabel("AlignTrailingComments"), br, + new QLabel(" Kind"), m_AlignTrailingCommentsKind, br, + new QLabel(" OverEmptyLines"), Row {m_AlignTrailingCommentsOverEmptyLines, m_setAlignTrailingCommentsOverEmptyLines}, br, new QLabel("AllowAllArgumentsOnNextLine"), m_AllowAllArgumentsOnNextLine, br, - new QLabel("AllowAllParametersOfDeclarationOnNextLine"), m_AllowAllParametersOfDeclarationOnNextLine, br, - new QLabel("AllowShortEnumsOnASingleLine"), m_AllowShortEnumsOnASingleLine, br, new QLabel("AllowShortBlocksOnASingleLine"), m_AllowShortBlocksOnASingleLine, br, new QLabel("AllowShortCaseLabelsOnASingleLine"), m_AllowShortCaseLabelsOnASingleLine, br, + new QLabel("AllowShortEnumsOnASingleLine"), m_AllowShortEnumsOnASingleLine, br, new QLabel("AllowShortFunctionsOnASingleLine"), m_AllowShortFunctionsOnASingleLine, br, new QLabel("AllowShortIfStatementsOnASingleLine"), m_AllowShortIfStatementsOnASingleLine, br, new QLabel("AllowShortLambdasOnASingleLine"), m_AllowShortLambdasOnASingleLine, br, @@ -720,81 +873,83 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) new QLabel("AlwaysBreakTemplateDeclarations"), m_AlwaysBreakTemplateDeclarations, br, new QLabel("AttributeMacros"), Row {m_AttributeMacros, m_setAttributeMacros}, br, new QLabel("BinPackArguments"), m_BinPackArguments, br, - new QLabel("InsertTrailingCommas"), m_InsertTrailingCommas, br, new QLabel("BinPackParameters"), m_BinPackParameters, br, + new QLabel("BitFieldColonSpacing"), m_BitFieldColonSpacing, br, + new QLabel("BraceWrapping"), br, + new QLabel(" AfterCaseLabel"), m_BraceWrappingAfterCaseLabel, br, + new QLabel(" AfterClass"), m_BraceWrappingAfterClass, br, + new QLabel(" AfterControlStatement"), m_BraceWrappingAfterControlStatement, br, + new QLabel(" AfterEnum"), m_BraceWrappingAfterEnum, br, + new QLabel(" AfterFunction"), m_BraceWrappingAfterFunction, br, + new QLabel(" AfterNamespace"), m_BraceWrappingAfterNamespace, br, + new QLabel(" AfterObjCDeclaration"), m_BraceWrappingAfterObjCDeclaration, br, + new QLabel(" AfterStruct"), m_BraceWrappingAfterStruct, br, + new QLabel(" AfterUnion"), m_BraceWrappingAfterUnion, br, + new QLabel(" AfterExternBlock"), m_BraceWrappingAfterExternBlock, br, + new QLabel(" BeforeCatch"), m_BraceWrappingBeforeCatch, br, + new QLabel(" BeforeElse"), m_BraceWrappingBeforeElse, br, + new QLabel(" BeforeLambdaBody"), m_BraceWrappingBeforeLambdaBody, br, + new QLabel(" BeforeWhile"), m_BraceWrappingBeforeWhile, br, + new QLabel(" IndentBraces"), m_BraceWrappingIndentBraces, br, + new QLabel(" SplitEmptyFunction"), m_BraceWrappingSplitEmptyFunction, br, + new QLabel(" SplitEmptyRecord"), m_BraceWrappingSplitEmptyRecord, br, + new QLabel(" SplitEmptyNamespace"), m_BraceWrappingSplitEmptyNamespace, br, + new QLabel("BreakAfterAttributes"), m_BreakAfterAttributes, br, + new QLabel("BreakArrays"), m_BreakArrays, br, new QLabel("BreakBeforeBinaryOperators"), m_BreakBeforeBinaryOperators, br, new QLabel("BreakBeforeBraces"), m_BreakBeforeBraces, br, - new QLabel("BraceWrapping"), br, - new QLabel(" AfterCaseLabel"), m_AfterCaseLabel, br, - new QLabel(" AfterClass"), m_AfterClass, br, - new QLabel(" AfterControlStatement"), m_AfterControlStatement, br, - new QLabel(" AfterEnum"), m_AfterEnum, br, - new QLabel(" AfterFunction"), m_AfterFunction, br, - new QLabel(" AfterNamespace"), m_AfterNamespace, br, - new QLabel(" AfterObjCDeclaration"), m_AfterObjCDeclaration, br, - new QLabel(" AfterStruct"), m_AfterStruct, br, - new QLabel(" AfterUnion"), m_AfterUnion, br, - new QLabel(" AfterExternBlock"), m_AfterExternBlock, br, - new QLabel(" BeforeCatch"), m_BeforeCatch, br, - new QLabel(" BeforeElse"), m_BeforeElse, br, - new QLabel(" BeforeLambdaBody"), m_BeforeLambdaBody, br, - new QLabel(" BeforeWhile"), m_BeforeWhile, br, - new QLabel(" IndentBraces"), m_IndentBraces, br, - new QLabel(" SplitEmptyFunction"), m_SplitEmptyFunction, br, - new QLabel(" SplitEmptyRecord"), m_SplitEmptyRecord, br, - new QLabel(" SplitEmptyNamespace"), m_SplitEmptyNamespace, br, new QLabel("BreakBeforeConceptDeclarations"), m_BreakBeforeConceptDeclarations, br, + new QLabel("BreakBeforeInlineASMColon"), m_BreakBeforeInlineASMColon, br, new QLabel("BreakBeforeTernaryOperators"), m_BreakBeforeTernaryOperators, br, new QLabel("BreakConstructorInitializers"), m_BreakConstructorInitializers, br, new QLabel("BreakAfterJavaFieldAnnotations"), m_BreakAfterJavaFieldAnnotations, br, new QLabel("BreakStringLiterals"), m_BreakStringLiterals, br, new QLabel("ColumnLimit"), Row {m_ColumnLimit, m_setColumnLimit}, br, new QLabel("CommentPragmas"), Row {m_CommentPragmas, m_setCommentPragmas}, br, - new QLabel("QualifierAlignment"), m_QualifierAlignment, br, - new QLabel("QualifierOrder"), Row {m_QualifierOrder, m_setQualifierOrder}, br, new QLabel("BreakInheritanceList"), m_BreakInheritanceList, br, new QLabel("CompactNamespaces"), m_CompactNamespaces, br, - new QLabel("ConstructorInitializerIndentWidth"), Row {m_ConstructorInitializerIndentWidth, m_setConstructorInitializerIndentWidth}, br, new QLabel("ContinuationIndentWidth"), Row {m_ContinuationIndentWidth, m_setContinuationIndentWidth}, br, new QLabel("Cpp11BracedListStyle"), m_Cpp11BracedListStyle, br, - new QLabel("DeriveLineEnding"), m_DeriveLineEnding, br, - new QLabel("DerivePointerAlignment"), m_DerivePointerAlignment, br, new QLabel("DisableFormat"), m_DisableFormat, br, new QLabel("EmptyLineAfterAccessModifier"), m_EmptyLineAfterAccessModifier, br, new QLabel("EmptyLineBeforeAccessModifier"), m_EmptyLineBeforeAccessModifier, br, new QLabel("ExperimentalAutoDetectBinPacking"), m_ExperimentalAutoDetectBinPacking, br, - new QLabel("PackConstructorInitializers"), m_PackConstructorInitializers, br, new QLabel("FixNamespaceComments"), m_FixNamespaceComments, br, new QLabel("ForEachMacros"), Row {m_ForEachMacros, m_setForEachMacros}, br, new QLabel("IfMacros"), Row {m_IfMacros, m_setIfMacros}, br, - new QLabel("TypenameMacros"), Row {m_TypenameMacros, m_setTypenameMacros}, br, - new QLabel("StatementMacros"), Row {m_StatementMacros, m_setStatementMacros}, br, - new QLabel("NamespaceMacros"), Row {m_NamespaceMacros, m_setNamespaceMacros}, br, - new QLabel("WhitespaceSensitiveMacros"), Row {m_WhitespaceSensitiveMacros, m_setWhitespaceSensitiveMacros}, br, new QLabel("IndentAccessModifiers"), m_IndentAccessModifiers, br, - new QLabel("IndentCaseLabels"), m_IndentCaseLabels, br, new QLabel("IndentCaseBlocks"), m_IndentCaseBlocks, br, + new QLabel("IndentCaseLabels"), m_IndentCaseLabels, br, new QLabel("IndentGotoLabels"), m_IndentGotoLabels, br, - new QLabel("IndentPPDirectives"), m_IndentPPDirectives, br, new QLabel("IndentExternBlock"), m_IndentExternBlock, br, - new QLabel("IndentRequires"), m_IndentRequires, br, + new QLabel("IndentPPDirectives"), m_IndentPPDirectives, br, + new QLabel("IndentRequiresClause"), m_IndentRequiresClause, br, new QLabel("IndentWidth"), Row {m_IndentWidth, m_setIndentWidth}, br, new QLabel("IndentWrappedFunctionNames"), m_IndentWrappedFunctionNames, br, + new QLabel("InsertBraces"), m_InsertBraces, br, + new QLabel("InsertNewlineAtEOF"), m_InsertNewlineAtEOF, br, + new QLabel("InsertTrailingCommas"), m_InsertTrailingCommas, br, + new QLabel("IntegerLiteralSeparator"), br, new QLabel("JavaImportGroups"), Row {m_JavaImportGroups, m_setJavaImportGroups}, br, new QLabel("JavaScriptQuotes"), m_JavaScriptQuotes, br, new QLabel("JavaScriptWrapImports"), m_JavaScriptWrapImports, br, + new QLabel("KeepEmptyLinesAtEOF"), m_KeepEmptyLinesAtEOF, br, new QLabel("KeepEmptyLinesAtTheStartOfBlocks"), m_KeepEmptyLinesAtTheStartOfBlocks, br, - new QLabel("Language"), m_Language, br, new QLabel("LambdaBodyIndentation"), m_LambdaBodyIndentation, br, + new QLabel("Language"), m_Language, br, + new QLabel("LineEnding"), m_LineEnding, br, new QLabel("MacroBlockBegin"), Row {m_MacroBlockBegin, m_setMacroBlockBegin}, br, new QLabel("MacroBlockEnd"), Row {m_MacroBlockEnd, m_setMacroBlockEnd}, br, + new QLabel("Macros"), Row {m_Macros, m_setMacros}, br, new QLabel("MaxEmptyLinesToKeep"), Row {m_MaxEmptyLinesToKeep, m_setMaxEmptyLinesToKeep}, br, new QLabel("NamespaceIndentation"), m_NamespaceIndentation, br, + new QLabel("NamespaceMacros"), Row {m_NamespaceMacros, m_setNamespaceMacros}, br, new QLabel("ObjCBinPackProtocolList"), m_ObjCBinPackProtocolList, br, new QLabel("ObjCBlockIndentWidth"), Row {m_ObjCBlockIndentWidth, m_setObjCBlockIndentWidth}, br, - new QLabel("ObjCSpaceAfterProperty"), m_ObjCSpaceAfterProperty, br, new QLabel("ObjCBreakBeforeNestedBlockParam"), m_ObjCBreakBeforeNestedBlockParam, br, + new QLabel("ObjCSpaceAfterProperty"), m_ObjCSpaceAfterProperty, br, new QLabel("ObjCSpaceBeforeProtocolList"), m_ObjCSpaceBeforeProtocolList, br, + new QLabel("PackConstructorInitializers"), m_PackConstructorInitializers, br, new QLabel("PenaltyBreakAssignment"), Row {m_PenaltyBreakAssignment, m_setPenaltyBreakAssignment}, br, new QLabel("PenaltyBreakBeforeFirstCallParameter"), Row {m_PenaltyBreakBeforeFirstCallParameter, m_setPenaltyBreakBeforeFirstCallParameter}, br, new QLabel("PenaltyBreakComment"), Row {m_PenaltyBreakComment, m_setPenaltyBreakComment}, br, @@ -803,13 +958,19 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) new QLabel("PenaltyBreakString"), Row {m_PenaltyBreakString, m_setPenaltyBreakString}, br, new QLabel("PenaltyBreakTemplateDeclaration"), Row {m_PenaltyBreakTemplateDeclaration, m_setPenaltyBreakTemplateDeclaration}, br, new QLabel("PenaltyExcessCharacter"), Row {m_PenaltyExcessCharacter, m_setPenaltyExcessCharacter}, br, - new QLabel("PenaltyReturnTypeOnItsOwnLine"), Row {m_PenaltyReturnTypeOnItsOwnLine, m_setPenaltyReturnTypeOnItsOwnLine}, br, new QLabel("PenaltyIndentedWhitespace"), Row {m_PenaltyIndentedWhitespace, m_setPenaltyIndentedWhitespace}, br, + new QLabel("PenaltyReturnTypeOnItsOwnLine"), Row {m_PenaltyReturnTypeOnItsOwnLine, m_setPenaltyReturnTypeOnItsOwnLine}, br, new QLabel("PointerAlignment"), m_PointerAlignment, br, new QLabel("PPIndentWidth"), Row {m_PPIndentWidth, m_setPPIndentWidth}, br, + new QLabel("QualifierAlignment"), m_QualifierAlignment, br, + new QLabel("QualifierOrder"), Row {m_QualifierOrder, m_setQualifierOrder}, br, new QLabel("ReferenceAlignment"), m_ReferenceAlignment, br, new QLabel("ReflowComments"), m_ReflowComments, br, new QLabel("RemoveBracesLLVM"), m_RemoveBracesLLVM, br, + new QLabel("RemoveParentheses"), m_RemoveParentheses, br, + new QLabel("RemoveSemicolon"), m_RemoveSemicolon, br, + new QLabel("RequiresClausePosition"), m_RequiresClausePosition, br, + new QLabel("RequiresExpressionIndentation"), m_RequiresExpressionIndentation, br, new QLabel("SeparateDefinitionBlocks"), m_SeparateDefinitionBlocks, br, new QLabel("ShortNamespaceLines"), Row {m_ShortNamespaceLines, m_setShortNamespaceLines}, br, new QLabel("SortIncludes"), m_SortIncludes, br, @@ -824,34 +985,39 @@ ClangFormatChecks::ClangFormatChecks(QWidget *parent) new QLabel("SpaceBeforeCpp11BracedList"), m_SpaceBeforeCpp11BracedList, br, new QLabel("SpaceBeforeCtorInitializerColon"), m_SpaceBeforeCtorInitializerColon, br, new QLabel("SpaceBeforeInheritanceColon"), m_SpaceBeforeInheritanceColon, br, + new QLabel("SpaceBeforeJsonColon"), m_SpaceBeforeJsonColon, br, new QLabel("SpaceBeforeParens"), m_SpaceBeforeParens, br, new QLabel("SpaceBeforeParensOptions"), br, - new QLabel(" AfterControlStatements"), m_AfterControlStatements, br, - new QLabel(" AfterForeachMacros"), m_AfterForeachMacros, br, - new QLabel(" AfterFunctionDeclarationName"), m_AfterFunctionDeclarationName, br, - new QLabel(" AfterFunctionDefinitionName"), m_AfterFunctionDefinitionName, br, - new QLabel(" AfterIfMacros"), m_AfterIfMacros, br, - new QLabel(" AfterOverloadedOperator"), m_AfterOverloadedOperator, br, - new QLabel(" BeforeNonEmptyParentheses"), m_BeforeNonEmptyParentheses, br, + new QLabel(" AfterControlStatements"), m_SpaceBeforeParensOptionsAfterControlStatements, br, + new QLabel(" AfterForeachMacros"), m_SpaceBeforeParensOptionsAfterForeachMacros, br, + new QLabel(" AfterFunctionDeclarationName"), m_SpaceBeforeParensOptionsAfterFunctionDeclarationName, br, + new QLabel(" AfterFunctionDefinitionName"), m_SpaceBeforeParensOptionsAfterFunctionDefinitionName, br, + new QLabel(" AfterIfMacros"), m_SpaceBeforeParensOptionsAfterIfMacros, br, + new QLabel(" AfterOverloadedOperator"), m_SpaceBeforeParensOptionsAfterOverloadedOperator, br, + new QLabel(" AfterRequiresInClause"), m_SpaceBeforeParensOptionsAfterRequiresInClause, br, + new QLabel(" AfterRequiresInExpression"), m_SpaceBeforeParensOptionsAfterRequiresInExpression, br, + new QLabel(" BeforeNonEmptyParentheses"), m_SpaceBeforeParensOptionsBeforeNonEmptyParentheses, br, + new QLabel("SpaceBeforeSquareBrackets"), m_SpaceBeforeSquareBrackets, br, new QLabel("SpaceBeforeRangeBasedForLoopColon"), m_SpaceBeforeRangeBasedForLoopColon, br, new QLabel("SpaceInEmptyBlock"), m_SpaceInEmptyBlock, br, - new QLabel("SpaceInEmptyParentheses"), m_SpaceInEmptyParentheses, br, - new QLabel("SpacesBeforeTrailingComments"), Row {m_SpacesBeforeTrailingComments, m_setSpacesBeforeTrailingComments}, br, new QLabel("SpacesInAngles"), m_SpacesInAngles, br, - new QLabel("SpacesInConditionalStatement"), m_SpacesInConditionalStatement, br, - new QLabel("SpacesInContainerLiterals"), m_SpacesInContainerLiterals, br, - new QLabel("SpacesInCStyleCastParentheses"), m_SpacesInCStyleCastParentheses, br, new QLabel("SpacesInLineCommentPrefix"), br, - new QLabel(" Minimum"), Row {m_Minimum, m_setMinimum}, br, - new QLabel(" Maximum"), Row {m_Maximum, m_setMaximum}, br, - new QLabel("SpacesInParentheses"), m_SpacesInParentheses, br, + new QLabel(" Minimum"), Row {m_SpacesInLineCommentPrefixMinimum, m_setSpacesInLineCommentPrefixMinimum}, br, + new QLabel(" Maximum"), Row {m_SpacesInLineCommentPrefixMaximum, m_setSpacesInLineCommentPrefixMaximum}, br, + new QLabel("SpacesInParensOptions"), br, + new QLabel(" InConditionalStatements"), m_SpacesInParensOptionsInConditionalStatements, br, + new QLabel(" InCStyleCasts"), m_SpacesInParensOptionsInCStyleCasts, br, + new QLabel(" InEmptyParentheses"), m_SpacesInParensOptionsInEmptyParentheses, br, + new QLabel(" Other"), m_SpacesInParensOptionsOther, br, new QLabel("SpacesInSquareBrackets"), m_SpacesInSquareBrackets, br, - new QLabel("SpaceBeforeSquareBrackets"), m_SpaceBeforeSquareBrackets, br, - new QLabel("BitFieldColonSpacing"), m_BitFieldColonSpacing, br, new QLabel("Standard"), m_Standard, br, new QLabel("StatementAttributeLikeMacros"), Row {m_StatementAttributeLikeMacros, m_setStatementAttributeLikeMacros}, br, + new QLabel("StatementMacros"), Row {m_StatementMacros, m_setStatementMacros}, br, new QLabel("TabWidth"), Row {m_TabWidth, m_setTabWidth}, br, - new QLabel("UseCRLF"), m_UseCRLF, br, + new QLabel("TypeNames"), Row {m_TypeNames, m_setTypeNames}, br, + new QLabel("TypenameMacros"), Row {m_TypenameMacros, m_setTypenameMacros}, br, new QLabel("UseTab"), m_UseTab, br, + new QLabel("VerilogBreakBetweenInstancePorts"), m_VerilogBreakBetweenInstancePorts, br, + new QLabel("WhitespaceSensitiveMacros"), Row {m_WhitespaceSensitiveMacros, m_setWhitespaceSensitiveMacros}, br, }.attachTo(this); } diff --git a/src/plugins/clangformat/clangformatchecks.h b/src/plugins/clangformat/clangformatchecks.h index a0ce073365e..b0f8a8d8623 100644 --- a/src/plugins/clangformat/clangformatchecks.h +++ b/src/plugins/clangformat/clangformatchecks.h @@ -1,7 +1,7 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT! +// THIS FILE IS AUTOMATICALLY GENERATED by generateClangFormatChecksLayout. DO NOT EDIT! #pragma once @@ -32,18 +32,39 @@ private: QPushButton *m_setAccessModifierOffset = nullptr; QComboBox *m_AlignAfterOpenBracket = nullptr; QComboBox *m_AlignArrayOfStructures = nullptr; - QComboBox *m_AlignConsecutiveMacros = nullptr; - QComboBox *m_AlignConsecutiveAssignments = nullptr; - QComboBox *m_AlignConsecutiveBitFields = nullptr; - QComboBox *m_AlignConsecutiveDeclarations = nullptr; + QComboBox *m_AlignConsecutiveMacrosEnabled = nullptr; + QComboBox *m_AlignConsecutiveMacrosAcrossEmptyLines = nullptr; + QComboBox *m_AlignConsecutiveMacrosAcrossComments = nullptr; + QComboBox *m_AlignConsecutiveMacrosAlignCompound = nullptr; + QComboBox *m_AlignConsecutiveMacrosPadOperators = nullptr; + QComboBox *m_AlignConsecutiveAssignmentsEnabled = nullptr; + QComboBox *m_AlignConsecutiveAssignmentsAcrossEmptyLines = nullptr; + QComboBox *m_AlignConsecutiveAssignmentsAcrossComments = nullptr; + QComboBox *m_AlignConsecutiveAssignmentsAlignCompound = nullptr; + QComboBox *m_AlignConsecutiveAssignmentsPadOperators = nullptr; + QComboBox *m_AlignConsecutiveBitFieldsEnabled = nullptr; + QComboBox *m_AlignConsecutiveBitFieldsAcrossEmptyLines = nullptr; + QComboBox *m_AlignConsecutiveBitFieldsAcrossComments = nullptr; + QComboBox *m_AlignConsecutiveBitFieldsAlignCompound = nullptr; + QComboBox *m_AlignConsecutiveBitFieldsPadOperators = nullptr; + QComboBox *m_AlignConsecutiveDeclarationsEnabled = nullptr; + QComboBox *m_AlignConsecutiveDeclarationsAcrossEmptyLines = nullptr; + QComboBox *m_AlignConsecutiveDeclarationsAcrossComments = nullptr; + QComboBox *m_AlignConsecutiveDeclarationsAlignCompound = nullptr; + QComboBox *m_AlignConsecutiveDeclarationsPadOperators = nullptr; + QComboBox *m_AlignConsecutiveShortCaseStatementsEnabled = nullptr; + QComboBox *m_AlignConsecutiveShortCaseStatementsAcrossEmptyLines = nullptr; + QComboBox *m_AlignConsecutiveShortCaseStatementsAcrossComments = nullptr; + QComboBox *m_AlignConsecutiveShortCaseStatementsAlignCaseColons = nullptr; QComboBox *m_AlignEscapedNewlines = nullptr; QComboBox *m_AlignOperands = nullptr; - QComboBox *m_AlignTrailingComments = nullptr; + QComboBox *m_AlignTrailingCommentsKind = nullptr; + QLineEdit *m_AlignTrailingCommentsOverEmptyLines = nullptr; + QPushButton *m_setAlignTrailingCommentsOverEmptyLines = nullptr; QComboBox *m_AllowAllArgumentsOnNextLine = nullptr; - QComboBox *m_AllowAllParametersOfDeclarationOnNextLine = nullptr; - QComboBox *m_AllowShortEnumsOnASingleLine = nullptr; QComboBox *m_AllowShortBlocksOnASingleLine = nullptr; QComboBox *m_AllowShortCaseLabelsOnASingleLine = nullptr; + QComboBox *m_AllowShortEnumsOnASingleLine = nullptr; QComboBox *m_AllowShortFunctionsOnASingleLine = nullptr; QComboBox *m_AllowShortIfStatementsOnASingleLine = nullptr; QComboBox *m_AllowShortLambdasOnASingleLine = nullptr; @@ -54,29 +75,32 @@ private: QPlainTextEdit *m_AttributeMacros = nullptr; QPushButton *m_setAttributeMacros = nullptr; QComboBox *m_BinPackArguments = nullptr; - QComboBox *m_InsertTrailingCommas = nullptr; QComboBox *m_BinPackParameters = nullptr; + QComboBox *m_BitFieldColonSpacing = nullptr; + QComboBox *m_BraceWrappingAfterCaseLabel = nullptr; + QComboBox *m_BraceWrappingAfterClass = nullptr; + QComboBox *m_BraceWrappingAfterControlStatement = nullptr; + QComboBox *m_BraceWrappingAfterEnum = nullptr; + QComboBox *m_BraceWrappingAfterFunction = nullptr; + QComboBox *m_BraceWrappingAfterNamespace = nullptr; + QComboBox *m_BraceWrappingAfterObjCDeclaration = nullptr; + QComboBox *m_BraceWrappingAfterStruct = nullptr; + QComboBox *m_BraceWrappingAfterUnion = nullptr; + QComboBox *m_BraceWrappingAfterExternBlock = nullptr; + QComboBox *m_BraceWrappingBeforeCatch = nullptr; + QComboBox *m_BraceWrappingBeforeElse = nullptr; + QComboBox *m_BraceWrappingBeforeLambdaBody = nullptr; + QComboBox *m_BraceWrappingBeforeWhile = nullptr; + QComboBox *m_BraceWrappingIndentBraces = nullptr; + QComboBox *m_BraceWrappingSplitEmptyFunction = nullptr; + QComboBox *m_BraceWrappingSplitEmptyRecord = nullptr; + QComboBox *m_BraceWrappingSplitEmptyNamespace = nullptr; + QComboBox *m_BreakAfterAttributes = nullptr; + QComboBox *m_BreakArrays = nullptr; QComboBox *m_BreakBeforeBinaryOperators = nullptr; QComboBox *m_BreakBeforeBraces = nullptr; - QComboBox *m_AfterCaseLabel = nullptr; - QComboBox *m_AfterClass = nullptr; - QComboBox *m_AfterControlStatement = nullptr; - QComboBox *m_AfterEnum = nullptr; - QComboBox *m_AfterFunction = nullptr; - QComboBox *m_AfterNamespace = nullptr; - QComboBox *m_AfterObjCDeclaration = nullptr; - QComboBox *m_AfterStruct = nullptr; - QComboBox *m_AfterUnion = nullptr; - QComboBox *m_AfterExternBlock = nullptr; - QComboBox *m_BeforeCatch = nullptr; - QComboBox *m_BeforeElse = nullptr; - QComboBox *m_BeforeLambdaBody = nullptr; - QComboBox *m_BeforeWhile = nullptr; - QComboBox *m_IndentBraces = nullptr; - QComboBox *m_SplitEmptyFunction = nullptr; - QComboBox *m_SplitEmptyRecord = nullptr; - QComboBox *m_SplitEmptyNamespace = nullptr; QComboBox *m_BreakBeforeConceptDeclarations = nullptr; + QComboBox *m_BreakBeforeInlineASMColon = nullptr; QComboBox *m_BreakBeforeTernaryOperators = nullptr; QComboBox *m_BreakConstructorInitializers = nullptr; QComboBox *m_BreakAfterJavaFieldAnnotations = nullptr; @@ -85,66 +109,60 @@ private: QPushButton *m_setColumnLimit = nullptr; QLineEdit *m_CommentPragmas = nullptr; QPushButton *m_setCommentPragmas = nullptr; - QComboBox *m_QualifierAlignment = nullptr; - QPlainTextEdit *m_QualifierOrder = nullptr; - QPushButton *m_setQualifierOrder = nullptr; QComboBox *m_BreakInheritanceList = nullptr; QComboBox *m_CompactNamespaces = nullptr; - QLineEdit *m_ConstructorInitializerIndentWidth = nullptr; - QPushButton *m_setConstructorInitializerIndentWidth = nullptr; QLineEdit *m_ContinuationIndentWidth = nullptr; QPushButton *m_setContinuationIndentWidth = nullptr; QComboBox *m_Cpp11BracedListStyle = nullptr; - QComboBox *m_DeriveLineEnding = nullptr; - QComboBox *m_DerivePointerAlignment = nullptr; QComboBox *m_DisableFormat = nullptr; QComboBox *m_EmptyLineAfterAccessModifier = nullptr; QComboBox *m_EmptyLineBeforeAccessModifier = nullptr; QComboBox *m_ExperimentalAutoDetectBinPacking = nullptr; - QComboBox *m_PackConstructorInitializers = nullptr; QComboBox *m_FixNamespaceComments = nullptr; QPlainTextEdit *m_ForEachMacros = nullptr; QPushButton *m_setForEachMacros = nullptr; QPlainTextEdit *m_IfMacros = nullptr; QPushButton *m_setIfMacros = nullptr; - QPlainTextEdit *m_TypenameMacros = nullptr; - QPushButton *m_setTypenameMacros = nullptr; - QPlainTextEdit *m_StatementMacros = nullptr; - QPushButton *m_setStatementMacros = nullptr; - QPlainTextEdit *m_NamespaceMacros = nullptr; - QPushButton *m_setNamespaceMacros = nullptr; - QPlainTextEdit *m_WhitespaceSensitiveMacros = nullptr; - QPushButton *m_setWhitespaceSensitiveMacros = nullptr; QComboBox *m_IndentAccessModifiers = nullptr; - QComboBox *m_IndentCaseLabels = nullptr; QComboBox *m_IndentCaseBlocks = nullptr; + QComboBox *m_IndentCaseLabels = nullptr; QComboBox *m_IndentGotoLabels = nullptr; - QComboBox *m_IndentPPDirectives = nullptr; QComboBox *m_IndentExternBlock = nullptr; - QComboBox *m_IndentRequires = nullptr; + QComboBox *m_IndentPPDirectives = nullptr; + QComboBox *m_IndentRequiresClause = nullptr; QLineEdit *m_IndentWidth = nullptr; QPushButton *m_setIndentWidth = nullptr; QComboBox *m_IndentWrappedFunctionNames = nullptr; + QComboBox *m_InsertBraces = nullptr; + QComboBox *m_InsertNewlineAtEOF = nullptr; + QComboBox *m_InsertTrailingCommas = nullptr; QPlainTextEdit *m_JavaImportGroups = nullptr; QPushButton *m_setJavaImportGroups = nullptr; QComboBox *m_JavaScriptQuotes = nullptr; QComboBox *m_JavaScriptWrapImports = nullptr; + QComboBox *m_KeepEmptyLinesAtEOF = nullptr; QComboBox *m_KeepEmptyLinesAtTheStartOfBlocks = nullptr; - QComboBox *m_Language = nullptr; QComboBox *m_LambdaBodyIndentation = nullptr; + QComboBox *m_Language = nullptr; + QComboBox *m_LineEnding = nullptr; QLineEdit *m_MacroBlockBegin = nullptr; QPushButton *m_setMacroBlockBegin = nullptr; QLineEdit *m_MacroBlockEnd = nullptr; QPushButton *m_setMacroBlockEnd = nullptr; + QPlainTextEdit *m_Macros = nullptr; + QPushButton *m_setMacros = nullptr; QLineEdit *m_MaxEmptyLinesToKeep = nullptr; QPushButton *m_setMaxEmptyLinesToKeep = nullptr; QComboBox *m_NamespaceIndentation = nullptr; + QPlainTextEdit *m_NamespaceMacros = nullptr; + QPushButton *m_setNamespaceMacros = nullptr; QComboBox *m_ObjCBinPackProtocolList = nullptr; QLineEdit *m_ObjCBlockIndentWidth = nullptr; QPushButton *m_setObjCBlockIndentWidth = nullptr; - QComboBox *m_ObjCSpaceAfterProperty = nullptr; QComboBox *m_ObjCBreakBeforeNestedBlockParam = nullptr; + QComboBox *m_ObjCSpaceAfterProperty = nullptr; QComboBox *m_ObjCSpaceBeforeProtocolList = nullptr; + QComboBox *m_PackConstructorInitializers = nullptr; QLineEdit *m_PenaltyBreakAssignment = nullptr; QPushButton *m_setPenaltyBreakAssignment = nullptr; QLineEdit *m_PenaltyBreakBeforeFirstCallParameter = nullptr; @@ -161,16 +179,23 @@ private: QPushButton *m_setPenaltyBreakTemplateDeclaration = nullptr; QLineEdit *m_PenaltyExcessCharacter = nullptr; QPushButton *m_setPenaltyExcessCharacter = nullptr; - QLineEdit *m_PenaltyReturnTypeOnItsOwnLine = nullptr; - QPushButton *m_setPenaltyReturnTypeOnItsOwnLine = nullptr; QLineEdit *m_PenaltyIndentedWhitespace = nullptr; QPushButton *m_setPenaltyIndentedWhitespace = nullptr; + QLineEdit *m_PenaltyReturnTypeOnItsOwnLine = nullptr; + QPushButton *m_setPenaltyReturnTypeOnItsOwnLine = nullptr; QComboBox *m_PointerAlignment = nullptr; QLineEdit *m_PPIndentWidth = nullptr; QPushButton *m_setPPIndentWidth = nullptr; + QComboBox *m_QualifierAlignment = nullptr; + QPlainTextEdit *m_QualifierOrder = nullptr; + QPushButton *m_setQualifierOrder = nullptr; QComboBox *m_ReferenceAlignment = nullptr; QComboBox *m_ReflowComments = nullptr; QComboBox *m_RemoveBracesLLVM = nullptr; + QComboBox *m_RemoveParentheses = nullptr; + QComboBox *m_RemoveSemicolon = nullptr; + QComboBox *m_RequiresClausePosition = nullptr; + QComboBox *m_RequiresExpressionIndentation = nullptr; QComboBox *m_SeparateDefinitionBlocks = nullptr; QLineEdit *m_ShortNamespaceLines = nullptr; QPushButton *m_setShortNamespaceLines = nullptr; @@ -186,38 +211,45 @@ private: QComboBox *m_SpaceBeforeCpp11BracedList = nullptr; QComboBox *m_SpaceBeforeCtorInitializerColon = nullptr; QComboBox *m_SpaceBeforeInheritanceColon = nullptr; + QComboBox *m_SpaceBeforeJsonColon = nullptr; QComboBox *m_SpaceBeforeParens = nullptr; - QComboBox *m_AfterControlStatements = nullptr; - QComboBox *m_AfterForeachMacros = nullptr; - QComboBox *m_AfterFunctionDeclarationName = nullptr; - QComboBox *m_AfterFunctionDefinitionName = nullptr; - QComboBox *m_AfterIfMacros = nullptr; - QComboBox *m_AfterOverloadedOperator = nullptr; - QComboBox *m_BeforeNonEmptyParentheses = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterControlStatements = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterForeachMacros = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterFunctionDeclarationName = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterFunctionDefinitionName = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterIfMacros = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterOverloadedOperator = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterRequiresInClause = nullptr; + QComboBox *m_SpaceBeforeParensOptionsAfterRequiresInExpression = nullptr; + QComboBox *m_SpaceBeforeParensOptionsBeforeNonEmptyParentheses = nullptr; + QComboBox *m_SpaceBeforeSquareBrackets = nullptr; QComboBox *m_SpaceBeforeRangeBasedForLoopColon = nullptr; QComboBox *m_SpaceInEmptyBlock = nullptr; - QComboBox *m_SpaceInEmptyParentheses = nullptr; - QLineEdit *m_SpacesBeforeTrailingComments = nullptr; - QPushButton *m_setSpacesBeforeTrailingComments = nullptr; QComboBox *m_SpacesInAngles = nullptr; - QComboBox *m_SpacesInConditionalStatement = nullptr; - QComboBox *m_SpacesInContainerLiterals = nullptr; - QComboBox *m_SpacesInCStyleCastParentheses = nullptr; - QLineEdit *m_Minimum = nullptr; - QPushButton *m_setMinimum = nullptr; - QLineEdit *m_Maximum = nullptr; - QPushButton *m_setMaximum = nullptr; - QComboBox *m_SpacesInParentheses = nullptr; + QLineEdit *m_SpacesInLineCommentPrefixMinimum = nullptr; + QPushButton *m_setSpacesInLineCommentPrefixMinimum = nullptr; + QLineEdit *m_SpacesInLineCommentPrefixMaximum = nullptr; + QPushButton *m_setSpacesInLineCommentPrefixMaximum = nullptr; + QComboBox *m_SpacesInParensOptionsInConditionalStatements = nullptr; + QComboBox *m_SpacesInParensOptionsInCStyleCasts = nullptr; + QComboBox *m_SpacesInParensOptionsInEmptyParentheses = nullptr; + QComboBox *m_SpacesInParensOptionsOther = nullptr; QComboBox *m_SpacesInSquareBrackets = nullptr; - QComboBox *m_SpaceBeforeSquareBrackets = nullptr; - QComboBox *m_BitFieldColonSpacing = nullptr; QComboBox *m_Standard = nullptr; QPlainTextEdit *m_StatementAttributeLikeMacros = nullptr; QPushButton *m_setStatementAttributeLikeMacros = nullptr; + QPlainTextEdit *m_StatementMacros = nullptr; + QPushButton *m_setStatementMacros = nullptr; QLineEdit *m_TabWidth = nullptr; QPushButton *m_setTabWidth = nullptr; - QComboBox *m_UseCRLF = nullptr; + QPlainTextEdit *m_TypeNames = nullptr; + QPushButton *m_setTypeNames = nullptr; + QPlainTextEdit *m_TypenameMacros = nullptr; + QPushButton *m_setTypenameMacros = nullptr; QComboBox *m_UseTab = nullptr; + QComboBox *m_VerilogBreakBetweenInstancePorts = nullptr; + QPlainTextEdit *m_WhitespaceSensitiveMacros = nullptr; + QPushButton *m_setWhitespaceSensitiveMacros = nullptr; }; diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index 7c5b8ac3369..e3328bb2695 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -3,6 +3,7 @@ #include "clangformatconfigwidget.h" +// the file below was generated by scripts/generateClangFormatChecksLayout.py #include "clangformatchecks.h" #include "clangformatconstants.h" #include "clangformatfile.h" @@ -10,8 +11,6 @@ #include "clangformattr.h" #include "clangformatutils.h" -// the file was generated by scripts/generateClangFormatChecksLayout.py - #include <coreplugin/icore.h> #include <cppeditor/cppcodestylepreferences.h> @@ -32,6 +31,7 @@ #include <utils/guard.h> #include <utils/layoutbuilder.h> #include <utils/qtcassert.h> +#include <utils/utilsicons.h> #include <QComboBox> #include <QLabel> @@ -40,9 +40,11 @@ #include <QScrollArea> #include <QSharedPointer> #include <QVBoxLayout> +#include <QVersionNumber> #include <QWeakPointer> #include <QWidget> +#include <clang/Basic/Version.h> #include <clang/Format/Format.h> #include <sstream> @@ -63,6 +65,9 @@ public: clang::format::FormatStyle style; Utils::Guard ignoreChanges; QLabel *fallbackConfig; + QLabel *clangVersion; + QLabel *clangWarningText; + QLabel *clangWarningIcon; }; bool ClangFormatConfigWidget::eventFilter(QObject *object, QEvent *event) @@ -91,6 +96,28 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc d->checksWidget->setEnabled(!codeStyle->isReadOnly() && !codeStyle->isTemporarilyReadOnly() && !codeStyle->isAdditionalTabDisabled()); + + static const int expectedMajorVersion = 17; + d->clangVersion = new QLabel(Tr::tr("Current ClangFormat version: %1.").arg(LLVM_VERSION_STRING), + this); + d->clangWarningText + = new QLabel(Tr::tr("The widget was generated for ClangFormat %1. " + "If you use a different version, the widget may work incorrectly.") + .arg(expectedMajorVersion), + this); + + QPalette palette = d->clangWarningText->palette(); + palette.setColor(QPalette::WindowText, Qt::red); + d->clangWarningText->setPalette(palette); + + d->clangWarningIcon = new QLabel(this); + d->clangWarningIcon->setPixmap(Utils::Icons::WARNING.icon().pixmap(16, 16)); + + if (LLVM_VERSION_MAJOR == expectedMajorVersion) { + d->clangWarningText->hide(); + d->clangWarningIcon->hide(); + } + FilePath fileName; if (d->project) fileName = d->project->projectFilePath().pathAppended("snippet.cpp"); @@ -111,6 +138,8 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(TextEditor::ICodeStylePreferenc Column { d->fallbackConfig, + Row {d->clangWarningIcon, d->clangWarningText, st}, + d->clangVersion, Row { d->checksScrollArea, d->preview }, }.attachTo(this); @@ -196,7 +225,7 @@ Utils::FilePath ClangFormatConfigWidget::projectPath() if (d->project) return globalPath().pathAppended("clang-format/" + projectUniqueId(d->project)); - return Utils::FilePath(); + return {}; } void ClangFormatConfigWidget::createStyleFileIfNeeded(bool isGlobal) @@ -245,77 +274,6 @@ void ClangFormatConfigWidget::updatePreview() d->preview->textDocument()->autoIndent(cursor); } -static inline void ltrim(std::string &s) -{ - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); -} - -static inline void rtrim(std::string &s) -{ - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), - s.end()); -} - -static inline void trim(std::string &s) -{ - ltrim(s); - rtrim(s); -} - -static void fillPlainText(QPlainTextEdit *plainText, const std::string &text, size_t index) -{ - if (index == std::string::npos) { - plainText->setPlainText(""); - return; - } - size_t valueStart = text.find('\n', index + 1); - size_t valueEnd; - std::string value; - QTC_ASSERT(valueStart != std::string::npos, return;); - do { - valueEnd = text.find('\n', valueStart + 1); - if (valueEnd == std::string::npos) - break; - // Skip also 2 spaces - start with valueStart + 1 + 2. - std::string line = text.substr(valueStart + 3, valueEnd - valueStart - 3); - rtrim(line); - value += value.empty() ? line : '\n' + line; - valueStart = valueEnd; - } while (valueEnd < text.size() - 1 && text.at(valueEnd + 1) == ' '); - plainText->setPlainText(QString::fromStdString(value)); -} - -static void fillComboBoxOrLineEdit(QObject *object, const std::string &text, size_t index) -{ - auto *comboBox = qobject_cast<QComboBox *>(object); - auto *lineEdit = qobject_cast<QLineEdit *>(object); - if (index == std::string::npos) { - if (comboBox) - comboBox->setCurrentIndex(0); - else - lineEdit->setText(""); - return; - } - - const size_t valueStart = text.find(':', index + 1); - QTC_ASSERT(valueStart != std::string::npos, return;); - const size_t valueEnd = text.find('\n', valueStart + 1); - QTC_ASSERT(valueEnd != std::string::npos, return;); - std::string value = text.substr(valueStart + 1, valueEnd - valueStart - 1); - trim(value); - - if (comboBox) { - if (comboBox->findText(QString::fromStdString(value)) == -1) { - comboBox->setCurrentIndex(0); - return; - } - comboBox->setCurrentText(QString::fromStdString(value)); - return; - } - - lineEdit->setText(QString::fromStdString(value)); -} - std::string ClangFormatConfigWidget::readFile(const QString &path) { const std::string defaultStyle = clang::format::configurationAsText(qtcStyle()); @@ -348,11 +306,55 @@ std::string ClangFormatConfigWidget::readFile(const QString &path) return settings; } +static std::map<QString, QString> getMapFromString(const QString &text) +{ + std::map<QString, QString> objectNameMap; + + QString parentName; + for (QString line : text.split('\n')) { + if (line.isEmpty()) + continue; + + QStringList list = line.split(':'); + QString key = !list.isEmpty() ? list[0] : ""; + QString value = line.mid(key.size() + 1).trimmed(); + + if (line.contains(':') && value.isEmpty()) { + parentName = key; + continue; + } + + if (!value.isEmpty() && !line.startsWith(" ")) + parentName = ""; + + if (line.startsWith(" - ") || line.startsWith(" ")) { + line.remove(0, 2); + if (objectNameMap.find(parentName) == objectNameMap.end()) + objectNameMap[parentName] = line + "\n"; + else + objectNameMap[parentName] += line + "\n"; + continue; + } + + if (line.startsWith(" ")) { + key.remove(0, 2); + key = parentName + key; + objectNameMap.insert(std::make_pair(key, value)); + continue; + } + + objectNameMap.insert(std::make_pair(key, value)); + } + + return objectNameMap; +} + void ClangFormatConfigWidget::fillTable() { Utils::GuardLocker locker(d->ignoreChanges); - const std::string configText = readFile(d->config->filePath().path()); + const QString configText = QString::fromStdString(readFile(d->config->filePath().path())); + std::map<QString, QString> objectNameMap = getMapFromString(configText); for (QObject *child : d->checksWidget->children()) { if (!qobject_cast<QComboBox *>(child) && !qobject_cast<QLineEdit *>(child) @@ -360,14 +362,27 @@ void ClangFormatConfigWidget::fillTable() continue; } - size_t index = configText.find('\n' + child->objectName().toStdString()); - if (index == std::string::npos) - index = configText.find("\n " + child->objectName().toStdString()); + if (objectNameMap.find(child->objectName()) == objectNameMap.end()) + continue; - if (qobject_cast<QPlainTextEdit *>(child)) - fillPlainText(qobject_cast<QPlainTextEdit *>(child), configText, index); - else - fillComboBoxOrLineEdit(child, configText, index); + if (QPlainTextEdit *plainText = qobject_cast<QPlainTextEdit *>(child)) { + plainText->setPlainText(objectNameMap[child->objectName()]); + continue; + } + + if (QComboBox *comboBox = qobject_cast<QComboBox *>(child)) { + if (comboBox->findText(objectNameMap[child->objectName()]) == -1) { + comboBox->setCurrentIndex(0); + } else { + comboBox->setCurrentText(objectNameMap[child->objectName()]); + } + continue; + } + + if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(child)) { + lineEdit->setText(objectNameMap[child->objectName()]); + continue; + } } } @@ -378,6 +393,7 @@ void ClangFormatConfigWidget::saveChanges(QObject *sender) d->config->setBasedOnStyle(basedOnStyle->currentText()); } else { QList<ClangFormatFile::Field> fields; + QString parentName; for (QObject *child : d->checksWidget->children()) { if (child->objectName() == "BasedOnStyle") @@ -386,13 +402,31 @@ void ClangFormatConfigWidget::saveChanges(QObject *sender) if (!label) continue; - QWidget *valueWidget = d->checksWidget->findChild<QWidget *>(label->text().trimmed()); - if (!valueWidget) { + // reset parent name if label starts without " " + if (!label->text().startsWith(" ")) + parentName = ""; + + QList<QWidget *> valueWidgets = d->checksWidget->findChildren<QWidget *>( + parentName + label->text().trimmed()); + + if (valueWidgets.empty()) { // Currently BraceWrapping only. fields.append({label->text(), ""}); + // save parent name + parentName = label->text().trimmed(); continue; } + QWidget *valueWidget = valueWidgets.first(); + if (valueWidgets.size() > 1) { + for (QWidget *w : valueWidgets) { + if (w->objectName() == parentName + label->text().trimmed()) { + valueWidget = w; + break; + } + } + } + if (!qobject_cast<QComboBox *>(valueWidget) && !qobject_cast<QLineEdit *>(valueWidget) && !qobject_cast<QPlainTextEdit *>(valueWidget)) { continue; @@ -403,7 +437,6 @@ void ClangFormatConfigWidget::saveChanges(QObject *sender) if (plainText->toPlainText().trimmed().isEmpty()) continue; - std::stringstream content; QStringList list = plainText->toPlainText().split('\n'); for (const QString &line : list) diff --git a/src/plugins/clangformat/clangformatconstants.h b/src/plugins/clangformat/clangformatconstants.h index 5bd8b1246c3..572effee21e 100644 --- a/src/plugins/clangformat/clangformatconstants.h +++ b/src/plugins/clangformat/clangformatconstants.h @@ -13,6 +13,7 @@ static const char OVERRIDE_FILE_ID[] = "ClangFormat.OverrideFile"; static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave"; static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; static const char MODE_ID[] = "ClangFormat.Mode"; +static const char FILE_SIZE_THREDSHOLD[] = "ClangFormat.FileSizeThreshold"; static const char USE_GLOBAL_SETTINGS[] = "ClangFormat.UseGlobalSettings"; static const char OPEN_CURRENT_CONFIG_ID[] = "ClangFormat.OpenCurrentConfig"; } // namespace Constants diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp index 64aa2c7c0fa..0898109c1e8 100644 --- a/src/plugins/clangformat/clangformatglobalconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatglobalconfigwidget.cpp @@ -16,6 +16,7 @@ #include <QCheckBox> #include <QComboBox> #include <QLabel> +#include <QSpinBox> #include <QWidget> #include <sstream> @@ -31,8 +32,14 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget( , m_project(project) , m_codeStyle(codeStyle) { + const QString sizeThresholdToolTip = Tr::tr( + "Files greater than this will not be indented by ClangFormat.\n" + "The built-in code indenter will handle indentation."); + m_projectHasClangFormat = new QLabel(this); m_formattingModeLabel = new QLabel(Tr::tr("Formatting mode:")); + m_fileSizeThresholdLabel = new QLabel(Tr::tr("Ignore files greater than:")); + m_fileSizeThresholdSpinBox = new QSpinBox(this); m_indentingOrFormatting = new QComboBox(this); m_formatWhileTyping = new QCheckBox(Tr::tr("Format while typing")); m_formatOnSave = new QCheckBox(Tr::tr("Format edited code on file save")); @@ -50,7 +57,10 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget( title(Tr::tr("ClangFormat settings:")), Column { m_useGlobalSettings, - Row { m_formattingModeLabel, m_indentingOrFormatting, st }, + Form { + m_formattingModeLabel, m_indentingOrFormatting, st, br, + m_fileSizeThresholdLabel, m_fileSizeThresholdSpinBox, st, br + }, m_formatWhileTyping, m_formatOnSave, m_projectHasClangFormat, @@ -67,9 +77,9 @@ ClangFormatGlobalConfigWidget::ClangFormatGlobalConfigWidget( initIndentationOrFormattingCombobox(); initOverrideCheckBox(); initUseGlobalSettingsCheckBox(); + initFileSizeThresholdSpinBox(); if (project) { - m_formattingModeLabel->hide(); m_formatOnSave->hide(); m_formatWhileTyping->hide(); @@ -124,6 +134,11 @@ void ClangFormatGlobalConfigWidget::initUseGlobalSettingsCheckBox() const auto enableProjectSettings = [this] { const bool isDisabled = m_project && m_useGlobalSettings->isChecked(); m_indentingOrFormatting->setDisabled(isDisabled); + m_formattingModeLabel->setDisabled(isDisabled); + m_projectHasClangFormat->setDisabled( + isDisabled + || (m_indentingOrFormatting->currentIndex() + == static_cast<int>(ClangFormatSettings::Mode::Disable))); m_overrideDefault->setDisabled(isDisabled || (m_indentingOrFormatting->currentIndex() == static_cast<int>(ClangFormatSettings::Mode::Disable))); @@ -139,6 +154,25 @@ void ClangFormatGlobalConfigWidget::initUseGlobalSettingsCheckBox() }); } +void ClangFormatGlobalConfigWidget::initFileSizeThresholdSpinBox() +{ + m_fileSizeThresholdSpinBox->setMinimum(1); + m_fileSizeThresholdSpinBox->setMaximum(std::numeric_limits<int>::max()); + m_fileSizeThresholdSpinBox->setSuffix(" KB"); + m_fileSizeThresholdSpinBox->setValue(ClangFormatSettings::instance().fileSizeThreshold()); + if (m_project) { + m_fileSizeThresholdSpinBox->hide(); + m_fileSizeThresholdLabel->hide(); + } + + connect(m_indentingOrFormatting, &QComboBox::currentIndexChanged, this, [this](int index) { + m_fileSizeThresholdLabel->setEnabled( + index != static_cast<int>(ClangFormatSettings::Mode::Disable)); + m_fileSizeThresholdSpinBox->setEnabled( + index != static_cast<int>(ClangFormatSettings::Mode::Disable)); + }); +} + bool ClangFormatGlobalConfigWidget::projectClangFormatFileExists() { llvm::Expected<clang::format::FormatStyle> styleFromProjectFolder @@ -170,6 +204,7 @@ void ClangFormatGlobalConfigWidget::initOverrideCheckBox() auto setEnableOverrideCheckBox = [this, setTemporarilyReadOnly](int index) { bool isDisable = index == static_cast<int>(ClangFormatSettings::Mode::Disable); m_overrideDefault->setDisabled(isDisable); + m_projectHasClangFormat->setDisabled(isDisable); setTemporarilyReadOnly(); }; @@ -215,6 +250,7 @@ void ClangFormatGlobalConfigWidget::apply() settings.setMode( static_cast<ClangFormatSettings::Mode>(m_indentingOrFormatting->currentIndex())); settings.setOverrideDefaultFile(m_overrideDefault->isChecked()); + settings.setFileSizeThreshold(m_fileSizeThresholdSpinBox->value()); m_overrideDefaultFile = m_overrideDefault->isChecked(); } settings.write(); diff --git a/src/plugins/clangformat/clangformatglobalconfigwidget.h b/src/plugins/clangformat/clangformatglobalconfigwidget.h index 7b2d6fe7c9c..e4a7be50478 100644 --- a/src/plugins/clangformat/clangformatglobalconfigwidget.h +++ b/src/plugins/clangformat/clangformatglobalconfigwidget.h @@ -13,6 +13,7 @@ QT_BEGIN_NAMESPACE class QCheckBox; class QComboBox; class QLabel; +class QSpinBox; QT_END_NAMESPACE namespace ProjectExplorer { class Project; } @@ -37,6 +38,7 @@ private: void initIndentationOrFormattingCombobox(); void initOverrideCheckBox(); void initUseGlobalSettingsCheckBox(); + void initFileSizeThresholdSpinBox(); bool projectClangFormatFileExists(); @@ -47,6 +49,8 @@ private: QLabel *m_projectHasClangFormat; QLabel *m_formattingModeLabel; + QLabel *m_fileSizeThresholdLabel; + QSpinBox *m_fileSizeThresholdSpinBox; QComboBox *m_indentingOrFormatting; QCheckBox *m_formatWhileTyping; QCheckBox *m_formatOnSave; diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index 2e9781de322..00199827209 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -13,6 +13,7 @@ #include <extensionsystem/pluginspec.h> #include <utils/genericconstants.h> +#include <utils/qtcsettings.h> #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> @@ -23,6 +24,7 @@ using namespace clang; using namespace format; using namespace TextEditor; +using namespace Utils; namespace ClangFormat { @@ -42,7 +44,7 @@ static bool isBeautifierOnSaveActivated() if (!isBeautifierPluginActivated()) return false; - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); bool activated = false; s->beginGroup(Utils::Constants::BEAUTIFIER_SETTINGS_GROUP); s->beginGroup(Utils::Constants::BEAUTIFIER_GENERAL_GROUP); @@ -130,7 +132,8 @@ TextEditor::Indenter *ClangFormatForwardingIndenter::currentIndenter() const { ClangFormatSettings::Mode mode = getCurrentIndentationOrFormattingSettings(m_fileName); - if (mode == ClangFormatSettings::Disable) + if (mode == ClangFormatSettings::Disable + || m_fileName.fileSize() >= ClangFormatSettings::instance().fileSizeThreshold() * 1024) return m_cppIndenter.get(); return m_clangFormatIndenter.get(); @@ -172,10 +175,10 @@ void ClangFormatForwardingIndenter::autoIndent(const QTextCursor &cursor, currentIndenter()->autoIndent(cursor, tabSettings, cursorPositionInEditor); } -Utils::Text::Replacements ClangFormatForwardingIndenter::format( - const TextEditor::RangesInLines &rangesInLines) +Utils::EditOperations ClangFormatForwardingIndenter::format( + const TextEditor::RangesInLines &rangesInLines, FormattingMode mode) { - return currentIndenter()->format(rangesInLines); + return currentIndenter()->format(rangesInLines, mode); } diff --git a/src/plugins/clangformat/clangformatindenter.h b/src/plugins/clangformat/clangformatindenter.h index d0f460aec0d..be3d54e6c8d 100644 --- a/src/plugins/clangformat/clangformatindenter.h +++ b/src/plugins/clangformat/clangformatindenter.h @@ -40,7 +40,8 @@ public: void autoIndent(const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings, int cursorPositionInEditor = -1) override; - Utils::Text::Replacements format(const TextEditor::RangesInLines &rangesInLines) override; + Utils::EditOperations format(const TextEditor::RangesInLines &rangesInLines, + FormattingMode mode) override; bool formatOnSave() const override; TextEditor::IndentationForBlock indentationForBlocks(const QVector<QTextBlock> &blocks, const TextEditor::TabSettings &tabSettings, diff --git a/src/plugins/clangformat/clangformatsettings.cpp b/src/plugins/clangformat/clangformatsettings.cpp index 4f4ea8ac232..48e34dc573e 100644 --- a/src/plugins/clangformat/clangformatsettings.cpp +++ b/src/plugins/clangformat/clangformatsettings.cpp @@ -6,8 +6,11 @@ #include <coreplugin/icore.h> +using namespace Utils; + namespace ClangFormat { -static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; + +const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; ClangFormatSettings &ClangFormatSettings::instance() { @@ -17,40 +20,38 @@ ClangFormatSettings &ClangFormatSettings::instance() ClangFormatSettings::ClangFormatSettings() { - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String(Constants::SETTINGS_ID)); - m_overrideDefaultFile = settings->value(QLatin1String(Constants::OVERRIDE_FILE_ID), false) - .toBool(); - m_formatWhileTyping = settings->value(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), false) - .toBool(); - m_formatOnSave = settings->value(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), false) - .toBool(); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(Constants::SETTINGS_ID); + m_overrideDefaultFile = settings->value(Constants::OVERRIDE_FILE_ID, false).toBool(); + m_formatWhileTyping = settings->value(Constants::FORMAT_WHILE_TYPING_ID, false).toBool(); + m_formatOnSave = settings->value(Constants::FORMAT_CODE_ON_SAVE_ID, false).toBool(); + m_fileSizeThreshold = settings->value(Constants::FILE_SIZE_THREDSHOLD, + m_fileSizeThreshold).toInt(); // Convert old settings to new ones. New settings were added to QtC 8.0 - bool isOldFormattingOn - = settings->value(QLatin1String(FORMAT_CODE_INSTEAD_OF_INDENT_ID), false).toBool(); - Core::ICore::settings()->remove(QLatin1String(FORMAT_CODE_INSTEAD_OF_INDENT_ID)); + bool isOldFormattingOn = settings->value(FORMAT_CODE_INSTEAD_OF_INDENT_ID, false).toBool(); + Core::ICore::settings()->remove(FORMAT_CODE_INSTEAD_OF_INDENT_ID); if (isOldFormattingOn) { - settings->setValue(QLatin1String(Constants::MODE_ID), + settings->setValue(Constants::MODE_ID, static_cast<int>(ClangFormatSettings::Mode::Formatting)); m_mode = ClangFormatSettings::Mode::Formatting; } else m_mode = static_cast<ClangFormatSettings::Mode>( - settings->value(QLatin1String(Constants::MODE_ID), ClangFormatSettings::Mode::Indenting) - .toInt()); + settings->value(Constants::MODE_ID, ClangFormatSettings::Mode::Indenting).toInt()); settings->endGroup(); } void ClangFormatSettings::write() const { - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String(Constants::SETTINGS_ID)); - settings->setValue(QLatin1String(Constants::OVERRIDE_FILE_ID), m_overrideDefaultFile); - settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping); - settings->setValue(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), m_formatOnSave); - settings->setValue(QLatin1String(Constants::MODE_ID), static_cast<int>(m_mode)); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(Constants::SETTINGS_ID); + settings->setValue(Constants::OVERRIDE_FILE_ID, m_overrideDefaultFile); + settings->setValue(Constants::FORMAT_WHILE_TYPING_ID, m_formatWhileTyping); + settings->setValue(Constants::FORMAT_CODE_ON_SAVE_ID, m_formatOnSave); + settings->setValue(Constants::MODE_ID, static_cast<int>(m_mode)); + settings->setValue(Constants::FILE_SIZE_THREDSHOLD, m_fileSizeThreshold); settings->endGroup(); } @@ -94,4 +95,14 @@ ClangFormatSettings::Mode ClangFormatSettings::mode() const return m_mode; } +void ClangFormatSettings::setFileSizeThreshold(int fileSizeInKb) +{ + m_fileSizeThreshold = fileSizeInKb; +} + +int ClangFormatSettings::fileSizeThreshold() const +{ + return m_fileSizeThreshold; +} + } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatsettings.h b/src/plugins/clangformat/clangformatsettings.h index c2ce53f4c76..b2ecd744688 100644 --- a/src/plugins/clangformat/clangformatsettings.h +++ b/src/plugins/clangformat/clangformatsettings.h @@ -33,11 +33,15 @@ public: void setFormatOnSave(bool enable); bool formatOnSave() const; + void setFileSizeThreshold(int fileSizeInKb); + int fileSizeThreshold() const; + private: Mode m_mode; bool m_overrideDefaultFile = false; bool m_formatWhileTyping = false; bool m_formatOnSave = false; + int m_fileSizeThreshold = 200; }; } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 1aae91a186f..f3e5c8f6749 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -32,7 +32,7 @@ using namespace Utils; namespace ClangFormat { -clang::format::FormatStyle qtcStyle() +clang::format::FormatStyle calculateQtcStyle() { clang::format::FormatStyle style = getLLVMStyle(); style.Language = FormatStyle::LK_Cpp; @@ -179,13 +179,18 @@ clang::format::FormatStyle qtcStyle() #endif style.SpacesInSquareBrackets = false; addQtcStatementMacros(style); - style.Standard = FormatStyle::LS_Cpp11; style.TabWidth = 4; style.UseTab = FormatStyle::UT_Never; style.Standard = FormatStyle::LS_Auto; return style; } +clang::format::FormatStyle qtcStyle() +{ + static clang::format::FormatStyle style = calculateQtcStyle(); + return style; +} + clang::format::FormatStyle currentQtStyle(const TextEditor::ICodeStylePreferences *preferences) { clang::format::FormatStyle style = qtcStyle(); @@ -338,7 +343,7 @@ Utils::FilePath findConfig(const Utils::FilePath &fileName) parentDirectory = parentDirectory.parentDir(); } - return Utils::FilePath(); + return {}; } Utils::FilePath configForFile(const Utils::FilePath &fileName) diff --git a/src/plugins/clangtools/CMakeLists.txt b/src/plugins/clangtools/CMakeLists.txt index e8d403ce187..c74b4702682 100644 --- a/src/plugins/clangtools/CMakeLists.txt +++ b/src/plugins/clangtools/CMakeLists.txt @@ -19,7 +19,6 @@ add_qtc_plugin(ClangTools clangfixitsrefactoringchanges.cpp clangfixitsrefactoringchanges.h clangselectablefilesdialog.cpp clangselectablefilesdialog.h clangtool.cpp clangtool.h - clangtoolruncontrol.cpp clangtoolruncontrol.h clangtoolrunner.cpp clangtoolrunner.h clangtools_global.h clangtoolstr.h diff --git a/src/plugins/clangtools/ClangTools.json.in b/src/plugins/clangtools/ClangTools.json.in index 7eea61d1bce..d6d1280c404 100644 --- a/src/plugins/clangtools/ClangTools.json.in +++ b/src/plugins/clangtools/ClangTools.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"ClangTools\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial 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.\" + "Name" : "ClangTools", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"ClangTools Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "ClangTools Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp index c1702f1183f..4eeb72f94f1 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp @@ -31,7 +31,7 @@ using namespace Utils; namespace ClangTools { namespace Internal { -int FixitsRefactoringFile::position(const QString &filePath, unsigned line, unsigned column) const +int FixitsRefactoringFile::position(const FilePath &filePath, unsigned line, unsigned column) const { QTC_ASSERT(line != 0, return -1); QTC_ASSERT(column != 0, return -1); @@ -66,10 +66,8 @@ bool FixitsRefactoringFile::apply() if (!op.apply) continue; - const FilePath filePath = FilePath::fromString(op.fileName); - // Check for permissions - if (!filePath.isWritableFile()) + if (!op.filePath.isWritableFile()) return false; qCDebug(fixitsLog) << " " << i << "Applying" << op; @@ -78,19 +76,19 @@ bool FixitsRefactoringFile::apply() shiftAffectedReplacements(op, i + 1); // Apply - QTextDocument * const doc = document(op.fileName); + QTextDocument * const doc = document(op.filePath); QTextCursor cursor(doc); cursor.setPosition(op.pos); cursor.setPosition(op.pos + op.length, QTextCursor::KeepAnchor); cursor.insertText(op.text); - auto &opsForFile = operationsByFile[filePath]; + auto &opsForFile = operationsByFile[op.filePath]; opsForFile.first.push_back(&op); opsForFile.second = i; } // Format for (auto it = operationsByFile.cbegin(); it != operationsByFile.cend(); ++it) { - QTextDocument * const doc = document(it.key().toString()); + QTextDocument * const doc = document(it.key()); const std::unique_ptr<TextEditor::Indenter> indenter(factory->createIndenter(doc)); if (!indenter) continue; @@ -104,9 +102,7 @@ bool FixitsRefactoringFile::apply() QString error; for (auto it = m_documents.begin(); it != m_documents.end(); ++it) { - if (!m_textFileFormat.writeFile(FilePath::fromString(it.key()), - it.value()->toPlainText(), - &error)) { + if (!m_textFileFormat.writeFile(it.key(), it.value()->toPlainText(), &error)) { qCDebug(fixitsLog) << "ERROR: Could not write file" << it.key() << ":" << error; return false; // Error writing file } @@ -130,31 +126,31 @@ void FixitsRefactoringFile::format(TextEditor::Indenter &indenter, const int end = doc->findBlock(op.pos + op.length).blockNumber() + 1; ranges.push_back({start, end}); } - const Text::Replacements replacements = indenter.format(ranges); + const EditOperations replacements = indenter.format(ranges); - if (replacements.empty()) + if (replacements.isEmpty()) return; - shiftAffectedReplacements(operationsForFile.front()->fileName, + shiftAffectedReplacements(operationsForFile.front()->filePath, replacements, firstOperationIndex + 1); } -QTextDocument *FixitsRefactoringFile::document(const QString &filePath) const +QTextDocument *FixitsRefactoringFile::document(const FilePath &filePath) const { if (m_documents.find(filePath) == m_documents.end()) { QString fileContents; if (!filePath.isEmpty()) { QString error; QTextCodec *defaultCodec = Core::EditorManager::defaultTextCodec(); - TextFileFormat::ReadResult result = TextFileFormat::readFile(FilePath::fromString( - filePath), + TextFileFormat::ReadResult result = TextFileFormat::readFile(filePath, defaultCodec, &fileContents, &m_textFileFormat, &error); if (result != TextFileFormat::ReadSuccess) { - qCDebug(fixitsLog) << "ERROR: Could not read " << filePath << ":" << error; + qCDebug(fixitsLog) + << "ERROR: Could not read " << filePath.toUserOutput() << ":" << error; m_textFileFormat.codec = nullptr; } } @@ -168,7 +164,7 @@ void FixitsRefactoringFile::shiftAffectedReplacements(const ReplacementOperation { for (int i = startIndex; i < m_replacementOperations.size(); ++i) { ReplacementOperation ¤t = *m_replacementOperations[i]; - if (op.fileName != current.fileName) + if (op.filePath != current.filePath) continue; ReplacementOperation before = current; @@ -182,42 +178,20 @@ void FixitsRefactoringFile::shiftAffectedReplacements(const ReplacementOperation } } -bool FixitsRefactoringFile::hasIntersection(const QString &fileName, - const Text::Replacements &replacements, - int startIndex) const -{ - for (int i = startIndex; i < m_replacementOperations.size(); ++i) { - const ReplacementOperation ¤t = *m_replacementOperations[i]; - if (fileName != current.fileName) - continue; - - // Usually the number of replacements is from 1 to 3. - if (std::any_of(replacements.begin(), - replacements.end(), - [¤t](const Text::Replacement &replacement) { - return replacement.offset + replacement.length > current.pos - && replacement.offset < current.pos + current.length; - })) { - return true; - } - } - - return false; -} - -void FixitsRefactoringFile::shiftAffectedReplacements(const QString &fileName, - const Text::Replacements &replacements, +void FixitsRefactoringFile::shiftAffectedReplacements(const FilePath &filePath, + const EditOperations &replacements, int startIndex) { for (int i = startIndex; i < m_replacementOperations.size(); ++i) { ReplacementOperation ¤t = *m_replacementOperations[i]; - if (fileName != current.fileName) + if (filePath != current.filePath) continue; - for (const auto &replacement : replacements) { - if (replacement.offset > current.pos) + for (const auto &op : replacements) { + QTC_ASSERT(op.type == ChangeSet::EditOp::Replace, continue); + if (op.pos1 > current.pos) break; - current.pos += replacement.text.size() - replacement.length; + current.pos += op.text.size() - op.length1; } } } diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.h b/src/plugins/clangtools/clangfixitsrefactoringchanges.h index 305453a4bfd..30c1f819dfe 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.h +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.h @@ -21,7 +21,7 @@ public: int pos = -1; int length = -1; QString text; - QString fileName; + Utils::FilePath filePath; bool apply = false; }; using ReplacementOperations = QVector<ReplacementOperation *>; @@ -34,28 +34,25 @@ public: FixitsRefactoringFile() = default; ~FixitsRefactoringFile() { qDeleteAll(m_documents); } - int position(const QString &filePath, unsigned line, unsigned column) const; + int position(const Utils::FilePath &filePath, unsigned line, unsigned column) const; void setReplacements(const ReplacementOperations &ops) { m_replacementOperations = ops; } bool apply(); private: - QTextDocument *document(const QString &filePath) const; + QTextDocument *document(const Utils::FilePath &filePath) const; void shiftAffectedReplacements(const ReplacementOperation &op, int startIndex); void format(TextEditor::Indenter &indenter, QTextDocument *doc, const ReplacementOperations &operationsForFile, int firstOperationIndex); - void shiftAffectedReplacements(const QString &fileName, - const Utils::Text::Replacements &replacements, + void shiftAffectedReplacements(const Utils::FilePath &filePath, + const Utils::EditOperations &replacements, int startIndex); - bool hasIntersection(const QString &fileName, - const Utils::Text::Replacements &replacements, - int startIndex) const; mutable Utils::TextFileFormat m_textFileFormat; - mutable QHash<QString, QTextDocument *> m_documents; + mutable QHash<Utils::FilePath, QTextDocument *> m_documents; ReplacementOperations m_replacementOperations; // Not owned. }; diff --git a/src/plugins/clangtools/clangselectablefilesdialog.cpp b/src/plugins/clangtools/clangselectablefilesdialog.cpp index 410f4847692..414c320b8bc 100644 --- a/src/plugins/clangtools/clangselectablefilesdialog.cpp +++ b/src/plugins/clangtools/clangselectablefilesdialog.cpp @@ -7,6 +7,7 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/find/itemviewfind.h> +#include <coreplugin/find/textfindconstants.h> #include <cppeditor/projectinfo.h> #include <projectexplorer/selectablefilesmodel.h> #include <texteditor/textdocument.h> diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index d5a3e6f202f..c25108a4185 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -3,41 +3,41 @@ #include "clangtool.h" -#include "clangfixitsrefactoringchanges.h" #include "clangselectablefilesdialog.h" -#include "clangtoolruncontrol.h" +#include "clangtoolrunner.h" #include "clangtoolsconstants.h" -#include "clangtoolsdiagnostic.h" -#include "clangtoolsdiagnosticmodel.h" #include "clangtoolsdiagnosticview.h" -#include "clangtoolslogfilereader.h" #include "clangtoolsprojectsettings.h" #include "clangtoolssettings.h" #include "clangtoolstr.h" #include "clangtoolsutils.h" +#include "executableinfo.h" #include "filterdialog.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <coreplugin/messagebox.h> +#include <coreplugin/progressmanager/taskprogress.h> -#include <cppeditor/clangdiagnosticconfigsmodel.h> #include <cppeditor/cppmodelmanager.h> #include <debugger/analyzer/analyzermanager.h> #include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/buildmanager.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> +#include <solutions/tasking/tasktree.h> + #include <texteditor/textdocument.h> #include <utils/algorithm.h> @@ -47,6 +47,8 @@ #include <utils/infolabel.h> #include <utils/progressindicator.h> #include <utils/proxyaction.h> +#include <utils/stringutils.h> +#include <utils/temporarydirectory.h> #include <utils/utilsicons.h> #include <QAction> @@ -54,16 +56,52 @@ #include <QDesktopServices> #include <QHBoxLayout> #include <QLabel> +#include <QLoggingCategory> #include <QToolButton> using namespace Core; using namespace CppEditor; using namespace Debugger; using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; -namespace ClangTools { -namespace Internal { +static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg) + +namespace ClangTools::Internal { + +class ProjectBuilderTaskAdapter : public TaskAdapter<QPointer<RunControl>> +{ +public: + void start() final { + connect(BuildManager::instance(), &BuildManager::buildQueueFinished, + this, &TaskInterface::done); + RunControl *runControl = *task(); + QTC_ASSERT(runControl, emit done(false); return); + Target *target = runControl->target(); + QTC_ASSERT(target, emit done(false); return); + if (!BuildManager::isBuilding(target)) { + BuildManager::buildProjectWithDependencies(target->project(), ConfigSelection::Active, + runControl); + } + } +}; + +using ProjectBuilderTask = CustomTask<ProjectBuilderTaskAdapter>; + +static QDebug operator<<(QDebug debug, const Environment &environment) +{ + for (const QString &entry : environment.toStringList()) + debug << "\n " << entry; + return debug; +} + +static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) +{ + for (const AnalyzeUnit &unit : analyzeUnits) + debug << "\n " << unit.file; + return debug; +} static QString makeLink(const QString &text) { @@ -76,7 +114,7 @@ class InfoBarWidget : public QFrame public: InfoBarWidget() - : m_progressIndicator(new Utils::ProgressIndicator(ProgressIndicatorSize::Small)) + : m_progressIndicator(new ProgressIndicator(ProgressIndicatorSize::Small)) , m_info(new InfoLabel({}, InfoLabel::Information)) , m_error(new InfoLabel({}, InfoLabel::Warning)) , m_diagStats(new QLabel) @@ -96,8 +134,8 @@ public: setLayout(layout); QPalette pal; - pal.setColor(QPalette::Window, Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground)); - pal.setColor(QPalette::WindowText, Utils::creatorTheme()->color(Utils::Theme::InfoBarText)); + pal.setColor(QPalette::Window, Utils::creatorTheme()->color(Theme::InfoBarBackground)); + pal.setColor(QPalette::WindowText, Utils::creatorTheme()->color(Theme::InfoBarText)); setPalette(pal); setAutoFillBackground(true); @@ -154,7 +192,7 @@ public: } private: - Utils::ProgressIndicator *m_progressIndicator; + ProgressIndicator *m_progressIndicator; InfoLabel *m_info; InfoLabel *m_error; QLabel *m_diagStats; @@ -185,7 +223,7 @@ public: ApplyFixIts(const QVector<DiagnosticItem *> &diagnosticItems) { for (DiagnosticItem *diagnosticItem : diagnosticItems) { - const Utils::FilePath &filePath = diagnosticItem->diagnostic().location.filePath; + const FilePath &filePath = diagnosticItem->diagnostic().location.filePath; QTC_ASSERT(!filePath.isEmpty(), continue); // Get or create refactoring file @@ -219,16 +257,16 @@ public: if (!step.isFixIt) continue; - const Debugger::DiagnosticLocation start = step.ranges.first(); - const Debugger::DiagnosticLocation end = step.ranges.last(); - const int startPos = file.position(start.filePath.toString(), start.line, start.column); - const int endPos = file.position(start.filePath.toString(), end.line, end.column); + const DiagnosticLocation start = step.ranges.first(); + const DiagnosticLocation end = step.ranges.last(); + const int startPos = file.position(start.filePath, start.line, start.column); + const int endPos = file.position(start.filePath, end.line, end.column); auto op = new ReplacementOperation; op->pos = startPos; op->length = endPos - startPos; op->text = step.message; - op->fileName = start.filePath.toString(); + op->filePath = start.filePath; op->apply = apply; replacements += op; @@ -278,14 +316,14 @@ public: QVector<DiagnosticItem *> itemsInvalidated; fileInfo.file.setReplacements(ops); - model->removeWatchedPath(ops.first()->fileName); + model->removeWatchedPath(ops.first()->filePath); if (fileInfo.file.apply()) { itemsApplied = itemsScheduled; } else { itemsFailedToApply = itemsScheduled; itemsInvalidated = itemsSchedulable; } - model->addWatchedPath(ops.first()->fileName); + model->addWatchedPath(ops.first()->filePath); // Update DiagnosticItem state for (DiagnosticItem *diagnosticItem : std::as_const(itemsScheduled)) @@ -298,28 +336,27 @@ public: } private: - QMap<Utils::FilePath, RefactoringFileInfo> m_refactoringFileInfos; + QMap<FilePath, RefactoringFileInfo> m_refactoringFileInfos; }; -static FileInfos sortedFileInfos(const QVector<CppEditor::ProjectPart::ConstPtr> &projectParts) +static FileInfos sortedFileInfos(const QVector<ProjectPart::ConstPtr> &projectParts) { FileInfos fileInfos; - for (const CppEditor::ProjectPart::ConstPtr &projectPart : projectParts) { + for (const ProjectPart::ConstPtr &projectPart : projectParts) { QTC_ASSERT(projectPart, continue); if (!projectPart->selectedForBuilding) continue; - for (const CppEditor::ProjectFile &file : std::as_const(projectPart->files)) { - QTC_ASSERT(file.kind != CppEditor::ProjectFile::Unclassified, continue); - QTC_ASSERT(file.kind != CppEditor::ProjectFile::Unsupported, continue); - if (file.path == CppEditor::CppModelManager::configurationFileName()) + for (const ProjectFile &file : std::as_const(projectPart->files)) { + QTC_ASSERT(file.kind != ProjectFile::Unclassified, continue); + QTC_ASSERT(file.kind != ProjectFile::Unsupported, continue); + if (file.path == CppModelManager::configurationFileName()) continue; - if (file.active - && (CppEditor::ProjectFile::isSource(file.kind) - || CppEditor::ProjectFile::isHeader(file.kind))) { - ProjectFile::Kind sourceKind = CppEditor::ProjectFile::sourceKind(file.kind); + if (file.active && (ProjectFile::isSource(file.kind) + || ProjectFile::isHeader(file.kind))) { + ProjectFile::Kind sourceKind = ProjectFile::sourceKind(file.kind); fileInfos.emplace_back(file.path, sourceKind, projectPart); } } @@ -350,7 +387,7 @@ static RunSettings runSettings() return ClangToolsSettings::instance()->runSettings(); } -ClangTool::ClangTool(const QString &name, Utils::Id id, ClangToolType type) +ClangTool::ClangTool(const QString &name, Id id, ClangToolType type) : m_name(name), m_perspective{id.toString(), name}, m_type(type) { setObjectName(name); @@ -376,8 +413,7 @@ ClangTool::ClangTool(const QString &name, Utils::Id id, ClangToolType type) initDiagnosticView(); m_diagnosticView->setModel(m_diagnosticFilterModel); m_diagnosticView->setSortingEnabled(true); - m_diagnosticView->sortByColumn(Debugger::DetailedErrorView::DiagnosticColumn, - Qt::AscendingOrder); + m_diagnosticView->sortByColumn(DetailedErrorView::DiagnosticColumn, Qt::AscendingOrder); connect(m_diagnosticView, &DiagnosticView::showHelp, this, &ClangTool::help); connect(m_diagnosticView, &DiagnosticView::showFilter, @@ -454,8 +490,7 @@ ClangTool::ClangTool(const QString &name, Utils::Id id, ClangToolType type) // Filter button action = m_showFilter = new QAction(this); - action->setIcon( - Utils::Icon({{":/utils/images/filtericon.png", Utils::Theme::IconsBaseColor}}).icon()); + action->setIcon(Utils::Icon({{":/utils/images/filtericon.png", Theme::IconsBaseColor}}).icon()); action->setToolTip(Tr::tr("Filter Diagnostics")); action->setCheckable(true); connect(action, &QAction::triggered, this, &ClangTool::filter); @@ -575,7 +610,7 @@ void ClangTool::selectPerspective() m_perspective.select(); } -void ClangTool::startTool(ClangTool::FileSelection fileSelection) +void ClangTool::startTool(FileSelection fileSelection) { const RunSettings theRunSettings = runSettings(); startTool(fileSelection, theRunSettings, diagnosticConfig(theRunSettings.diagnosticConfigId())); @@ -602,13 +637,196 @@ static bool continueDespiteReleaseBuild(const QString &toolName) return CheckableMessageBox::question(ICore::dialogParent(), title, message, - QString("ClangToolsCorrectModeWarning")) + Key("ClangToolsCorrectModeWarning")) == QMessageBox::Yes; } -void ClangTool::startTool(ClangTool::FileSelection fileSelection, - const RunSettings &runSettings, - const CppEditor::ClangDiagnosticConfig &diagnosticConfig) +Group ClangTool::runRecipe(const RunSettings &runSettings, + const ClangDiagnosticConfig &diagnosticConfig, + const FileInfos &fileInfos, + bool buildBeforeAnalysis) +{ + m_filesCount = int(fileInfos.size()); + + struct ClangStorage { + ClangStorage() { m_timer.start(); } + ~ClangStorage() { + if (m_elapsedHandler) + m_elapsedHandler(m_timer.elapsed()); + } + QElapsedTimer m_timer; + std::function<void(qint64 elapsedTime)> m_elapsedHandler = {}; + }; + const TreeStorage<ClangStorage> storage; + + std::shared_ptr<TemporaryDirectory> tempDir(new TemporaryDirectory("clangtools-XXXXXX")); + tempDir->setAutoRemove(qtcEnvironmentVariable("QTC_CLANG_DONT_DELETE_OUTPUT_FILES") != "1"); + + Target *target = m_runControl->target(); + BuildConfiguration *buildConfiguration = target->activeBuildConfiguration(); + QTC_ASSERT(buildConfiguration, return {}); + const Environment environment = buildConfiguration->environment(); + + const auto onTopSetup = [this, tempDir] { + if (tempDir->isValid()) + return SetupResult::Continue; + m_infoBarWidget->setError(InfoBarWidget::Error, + makeLink(Tr::tr("Failed to start the analyzer.")), + [this] { showOutputPane(); }); + m_runControl->postMessage(Tr::tr("Failed to create temporary directory: %1.") + .arg(tempDir->errorString()), ErrorMessageFormat); + setState(State::PreparationFailed); + return SetupResult::StopWithError; + }; + + QList<GroupItem> topTasks { onGroupSetup(onTopSetup) }; + + if (buildBeforeAnalysis) { + QPointer<RunControl> runControl(m_runControl); + const auto onSetup = [runControl](QPointer<RunControl> &buildRunControl) { + buildRunControl = runControl; + }; + const auto onError = [this](const QPointer<RunControl> &) { + const QString message(Tr::tr("Failed to build the project.")); + m_infoBarWidget->setError(InfoBarWidget::Error, message, [this] { showOutputPane(); }); + m_runControl->postMessage(message, ErrorMessageFormat); + setState(State::PreparationFailed); + }; + topTasks.append(ProjectBuilderTask(onSetup, {}, onError)); + } + + const ProjectInfo::ConstPtr projectInfoBeforeBuild + = CppModelManager::projectInfo(target->project()); + + const auto onTreeSetup = [this, storage, runSettings, diagnosticConfig, fileInfos, tempDir, + environment, projectInfoBeforeBuild](TaskTree &taskTree) { + storage->m_elapsedHandler = [this](qint64 elapsedTime) { + m_runControl->postMessage(Utils::formatElapsedTime(elapsedTime), NormalMessageFormat); + }; + auto progress = new TaskProgress(&taskTree); + progress->setDisplayName(Tr::tr("Analyzing")); + + const QString failedMessage = makeLink(Tr::tr("Failed to start the analyzer.")); + + ProjectExplorerPlugin::saveModifiedFiles(); + + Project *project = m_runControl->project(); + const ProjectInfo::ConstPtr projectInfo = CppModelManager::projectInfo(project); + if (!projectInfo) { + m_infoBarWidget->setError(InfoBarWidget::Error, failedMessage, + [this] { showOutputPane(); }); + m_runControl->postMessage(Tr::tr("No code model data available for project."), + ErrorMessageFormat); + setState(State::PreparationFailed); + return SetupResult::StopWithError; + } + + // Project changed in the mean time? + if (projectInfo->configurationOrFilesChanged(*projectInfoBeforeBuild)) { + // If it's more than a release/debug build configuration change, e.g. + // a version control checkout, files might be not valid C++ anymore + // or even gone, so better stop here. + m_infoBarWidget->setError(InfoBarWidget::Error, failedMessage, + [this] { showOutputPane(); }); + m_runControl->postMessage(Tr::tr("The project configuration changed since the start of " + "the %1. Please re-run with current configuration.") + .arg(name()), ErrorMessageFormat); + setState(State::PreparationFailed); + return SetupResult::StopWithError; + } + + setState(State::AnalyzerRunning); + + const FilePath projectFile = projectInfo->projectFilePath(); + m_runControl->postMessage(Tr::tr("Running %1 on %2 with configuration \"%3\".") + .arg(name(), projectFile.toUserOutput(), diagnosticConfig.displayName()), + NormalMessageFormat); + + const ClangToolType tool = this == ClangTidyTool::instance() ? ClangToolType::Tidy + : ClangToolType::Clazy; + const FilePath executable = toolExecutable(tool); + const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable); + // Collect files + AnalyzeUnits unitsToProcess; + for (const FileInfo &fileInfo : fileInfos) + unitsToProcess.append({fileInfo, includeDir, clangVersion}); + + qCDebug(LOG) << Q_FUNC_INFO << executable << includeDir << clangVersion; + qCDebug(LOG) << "Files to process:" << unitsToProcess; + qCDebug(LOG) << "Environment:" << environment; + + QList<GroupItem> tasks{parallelLimit(qMax(1, runSettings.parallelJobs()))}; + for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) { + if (!diagnosticConfig.isEnabled(tool) && !runSettings.hasConfigFileForSourceFile(unit.file)) + continue; + + const auto setupHandler = [this, unit, tool] { + const QString filePath = unit.file.toUserOutput(); + m_runControl->postMessage(Tr::tr("Analyzing \"%1\" [%2].") + .arg(filePath, clangToolName(tool)), StdOutFormat); + return true; + }; + const AnalyzeInputData input{tool, runSettings, diagnosticConfig, tempDir->path(), + environment, unit}; + const auto outputHandler = [this, runSettings](const AnalyzeOutputData &output) { + if (!output.success) { + qCDebug(LOG).noquote() << "Clang tool task finished with an error:" + << output.errorMessage << '\n' << output.errorDetails; + ++m_filesFailed; + + const QString message = Tr::tr("Failed to analyze \"%1\": %2") + .arg(output.fileToAnalyze.toUserOutput(), output.errorMessage); + // TODO: postMessage() instead + m_runControl->postMessage(message, StdErrFormat); + m_runControl->postMessage(output.errorDetails, StdErrFormat); + } else if (!output.errorMessage.isEmpty()) { + m_runControl->postMessage(output.errorMessage, ErrorMessageFormat); + m_runControl->postMessage(output.errorDetails, StdErrFormat); + } else { + qCDebug(LOG) << "Clang tool task finished with success:" + << output.outputFilePath; + ++m_filesSucceeded; + + const Diagnostics diagnostics = output.diagnostics; + if (!diagnostics.isEmpty()) { + // do not generate marks when we always analyze open files since marks from that + // analysis should be more up to date + const bool generateMarks = !runSettings.analyzeOpenFiles(); + onNewDiagnosticsAvailable(diagnostics, generateMarks); + } + } + updateForCurrentState(); + }; + tasks.append(clangToolTask(input, setupHandler, outputHandler)); + } + taskTree.setRecipe(tasks); + return SetupResult::Continue; + }; + + const auto onTreeDone = [this, target, runSettings](const TaskTree &) { + if (m_filesFailed != 0) { + m_runControl->postMessage(Tr::tr("Error: Failed to analyze %n files.", nullptr, + m_filesFailed), ErrorMessageFormat); + if (target && target->activeBuildConfiguration() + && !target->activeBuildConfiguration()->buildDirectory().exists() + && !runSettings.buildBeforeAnalysis()) { + m_runControl->postMessage( + Tr::tr("Note: You might need to build the project to generate or update " + "source files. To build automatically, enable " + "\"Build the project before analysis\"."), NormalMessageFormat); + } + } + m_runControl->postMessage(Tr::tr("%1 finished: Processed %2 files successfully, %3 failed.") + .arg(name()).arg(m_filesSucceeded).arg(m_filesFailed), + NormalMessageFormat); + }; + + topTasks.append(Group { Tasking::Storage(storage), TaskTreeTask(onTreeSetup, onTreeDone) }); + return {topTasks}; +} + +void ClangTool::startTool(FileSelection fileSelection, const RunSettings &runSettings, + const ClangDiagnosticConfig &diagnosticConfig) { Project *project = ProjectManager::startupProject(); QTC_ASSERT(project, return); @@ -636,36 +854,26 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection, m_runControl->setDisplayName(m_name); m_runControl->setIcon(ProjectExplorer::Icons::ANALYZER_START_SMALL_TOOLBAR); m_runControl->setTarget(project->activeTarget()); + m_runControl->setSupportsReRunning(false); m_stopAction->disconnect(); connect(m_stopAction, &QAction::triggered, m_runControl, [this] { - emit m_runControl->appendMessage(Tr::tr("%1 tool stopped by user.").arg(m_name), - NormalMessageFormat); + m_runControl->postMessage(Tr::tr("%1 tool stopped by user.").arg(m_name), + NormalMessageFormat); m_runControl->initiateStop(); setState(State::StoppedByUser); }); - connect(m_runControl, &RunControl::stopped, this, &ClangTool::onRunControlStopped); + connect(m_runControl, &RunControl::stopped, this, [this] { + if (m_state != State::StoppedByUser && m_state != State::PreparationFailed) + setState(State::AnalyzerFinished); + emit finished(m_infoBarWidget->errorText()); + }); - // Run worker const bool preventBuild = std::holds_alternative<FilePath>(fileSelection) || std::get<FileSelectionType>(fileSelection) == FileSelectionType::CurrentFile; const bool buildBeforeAnalysis = !preventBuild && runSettings.buildBeforeAnalysis(); - m_runWorker = new ClangToolRunWorker(this, - m_runControl, - runSettings, - diagnosticConfig, - fileInfos, - buildBeforeAnalysis); - connect(m_runWorker, &ClangToolRunWorker::buildFailed,this, &ClangTool::onBuildFailed); - connect(m_runWorker, &ClangToolRunWorker::startFailed, this, &ClangTool::onStartFailed); - connect(m_runWorker, &ClangToolRunWorker::started, this, &ClangTool::onStarted); - connect(m_runWorker, &ClangToolRunWorker::runnerFinished, this, [this] { - m_filesCount = m_runWorker->totalFilesToAnalyze(); - m_filesSucceeded = m_runWorker->filesAnalyzed(); - m_filesFailed = m_runWorker->filesNotAnalyzed(); - updateForCurrentState(); - }); - + m_runControl->setRunRecipe(runRecipe(runSettings, diagnosticConfig, fileInfos, + buildBeforeAnalysis)); // More init and UI update m_diagnosticFilterModel->setProject(project); m_perspective.select(); @@ -673,7 +881,6 @@ void ClangTool::startTool(ClangTool::FileSelection fileSelection, m_infoBarWidget->setInfoText("Waiting for build to finish..."); setState(State::PreparationStarted); - // Start ProjectExplorerPlugin::startRunControl(m_runControl); } @@ -689,7 +896,7 @@ FileInfos ClangTool::collectFileInfos(Project *project, FileSelection fileSelect return {}; } - const auto projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project); + const auto projectInfo = CppModelManager::projectInfo(project); QTC_ASSERT(projectInfo, return FileInfos()); const FileInfos allFileInfos = sortedFileInfos(projectInfo->projectParts()); @@ -807,7 +1014,6 @@ void ClangTool::reset() m_state = State::Initial; m_runControl = nullptr; - m_runWorker = nullptr; m_filesCount = 0; m_filesSucceeded = 0; @@ -817,12 +1023,12 @@ void ClangTool::reset() static bool canAnalyzeProject(Project *project) { if (const Target *target = project->activeTarget()) { - const Utils::Id c = ProjectExplorer::Constants::C_LANGUAGE_ID; - const Utils::Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID; + const Id c = ProjectExplorer::Constants::C_LANGUAGE_ID; + const Id cxx = ProjectExplorer::Constants::CXX_LANGUAGE_ID; const bool projectSupportsLanguage = project->projectLanguages().contains(c) || project->projectLanguages().contains(cxx); return projectSupportsLanguage - && CppModelManager::instance()->projectInfo(project) + && CppModelManager::projectInfo(project) && ToolChainKitAspect::cxxToolChain(target->kit()); } return false; @@ -952,45 +1158,19 @@ void ClangTool::filterOutCurrentKind() } } -void ClangTool::onBuildFailed() -{ - m_infoBarWidget->setError(InfoBarWidget::Error, Tr::tr("Failed to build the project."), - [this] { showOutputPane(); }); - setState(State::PreparationFailed); -} - -void ClangTool::onStartFailed() -{ - m_infoBarWidget->setError(InfoBarWidget::Error, makeLink(Tr::tr("Failed to start the analyzer.")), - [this] { showOutputPane(); }); - setState(State::PreparationFailed); -} - -void ClangTool::onStarted() -{ - setState(State::AnalyzerRunning); -} - -void ClangTool::onRunControlStopped() -{ - if (m_state != State::StoppedByUser && m_state != State::PreparationFailed) - setState(State::AnalyzerFinished); - emit finished(m_infoBarWidget->errorText()); -} - void ClangTool::update() { updateForInitialState(); updateForCurrentState(); } -using DocumentPredicate = std::function<bool(Core::IDocument *)>; +using DocumentPredicate = std::function<bool(IDocument *)>; static FileInfos fileInfosMatchingDocuments(const FileInfos &fileInfos, const DocumentPredicate &predicate) { - QSet<Utils::FilePath> documentPaths; - for (const Core::DocumentModel::Entry *e : Core::DocumentModel::entries()) { + QSet<FilePath> documentPaths; + for (const DocumentModel::Entry *e : DocumentModel::entries()) { if (predicate(e->document)) documentPaths.insert(e->filePath()); } @@ -1003,20 +1183,19 @@ static FileInfos fileInfosMatchingDocuments(const FileInfos &fileInfos, static FileInfos fileInfosMatchingOpenedDocuments(const FileInfos &fileInfos) { // Note that (initially) suspended text documents are still IDocuments, not yet TextDocuments. - return fileInfosMatchingDocuments(fileInfos, [](Core::IDocument *) { return true; }); + return fileInfosMatchingDocuments(fileInfos, [](IDocument *) { return true; }); } static FileInfos fileInfosMatchingEditedDocuments(const FileInfos &fileInfos) { - return fileInfosMatchingDocuments(fileInfos, [](Core::IDocument *document) { + return fileInfosMatchingDocuments(fileInfos, [](IDocument *document) { if (auto textDocument = qobject_cast<TextEditor::TextDocument*>(document)) return textDocument->document()->revision() > 1; return false; }); } -FileInfoProviders ClangTool::fileInfoProviders(ProjectExplorer::Project *project, - const FileInfos &allFileInfos) +FileInfoProviders ClangTool::fileInfoProviders(Project *project, const FileInfos &allFileInfos) { const QSharedPointer<ClangToolsProjectSettings> s = ClangToolsProjectSettings::getSettings(project); static FileInfoSelection openedFilesSelection; @@ -1046,7 +1225,7 @@ FileInfoProviders ClangTool::fileInfoProviders(ProjectExplorer::Project *project }; } -void ClangTool::setState(ClangTool::State state) +void ClangTool::setState(State state) { m_state = state; updateForCurrentState(); @@ -1055,7 +1234,6 @@ void ClangTool::setState(ClangTool::State state) QSet<Diagnostic> ClangTool::diagnostics() const { return Utils::filtered(m_diagnosticModel->diagnostics(), [](const Diagnostic &diagnostic) { - using CppEditor::ProjectFile; return ProjectFile::isSource(ProjectFile::classify(diagnostic.location.filePath.toString())); }); } @@ -1119,7 +1297,7 @@ void ClangTool::updateForCurrentState() break; case State::AnalyzerRunning: showProgressIcon = true; - if (m_filesCount == 0) { + if (m_filesSucceeded + m_filesFailed == 0) { infoText = Tr::tr("Analyzing..."); // Not yet fully started/initialized } else { infoText = Tr::tr("Analyzing... %1 of %n file(s) processed.", nullptr, m_filesCount) @@ -1167,12 +1345,12 @@ ClangTidyTool::ClangTidyTool() : ClangTool(Tr::tr("Clang-Tidy"), "ClangTidy.Pers { m_instance = this; } + ClazyTool::ClazyTool() : ClangTool(Tr::tr("Clazy"), "Clazy.Perspective", ClangToolType::Clazy) { m_instance = this; } -} // namespace Internal -} // namespace ClangTools +} // namespace ClangTools::Internal #include "clangtool.moc" diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index 87497e87066..75db785dae3 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -9,36 +9,20 @@ #include <debugger/debuggermainwindow.h> -#include <projectexplorer/runconfiguration.h> -#include <cppeditor/projectinfo.h> - #include <variant> QT_BEGIN_NAMESPACE -class QFrame; class QToolButton; QT_END_NAMESPACE -namespace CppEditor { -class ClangDiagnosticConfig; -} -namespace Debugger { -class DetailedErrorView; -} -namespace ProjectExplorer { -class RunControl; -} -namespace Utils { -class FilePath; -class FancyLineEdit; -} // namespace Utils +namespace CppEditor { class ClangDiagnosticConfig; } +namespace ProjectExplorer { class RunControl; } +namespace Tasking { class Group; } +namespace Utils { class FilePath; } -namespace ClangTools { -namespace Internal { +namespace ClangTools::Internal { class InfoBarWidget; -class ClangToolsDiagnosticModel; -class ClangToolRunWorker; class Diagnostic; class DiagnosticFilterModel; class DiagnosticView; @@ -61,8 +45,7 @@ public: using FileSelection = std::variant<FileSelectionType, Utils::FilePath>; void startTool(FileSelection fileSelection); - void startTool(FileSelection fileSelection, - const RunSettings &runSettings, + void startTool(FileSelection fileSelection, const RunSettings &runSettings, const CppEditor::ClangDiagnosticConfig &diagnosticConfig); FileInfos collectFileInfos(ProjectExplorer::Project *project, @@ -85,6 +68,10 @@ protected: ClangTool(const QString &name, Utils::Id id, CppEditor::ClangToolType type); private: + Tasking::Group runRecipe(const RunSettings &runSettings, + const CppEditor::ClangDiagnosticConfig &diagnosticConfig, + const FileInfos &fileInfos, bool buildBeforeAnalysis); + enum class State { Initial, PreparationStarted, @@ -107,11 +94,6 @@ private: void filterOutCurrentKind(); void setFilterOptions(const OptionalFilterOptions &filterOptions); - void onBuildFailed(); - void onStartFailed(); - void onStarted(); - void onRunControlStopped(); - void initDiagnosticView(); void loadDiagnosticsFromFiles(); @@ -126,7 +108,6 @@ private: const QString m_name; ClangToolsDiagnosticModel *m_diagnosticModel = nullptr; ProjectExplorer::RunControl *m_runControl = nullptr; - ClangToolRunWorker *m_runWorker = nullptr; InfoBarWidget *m_infoBarWidget = nullptr; DiagnosticView *m_diagnosticView = nullptr;; @@ -177,5 +158,4 @@ private: static inline ClangTool *m_instance = nullptr; }; -} // namespace Internal -} // namespace ClangTools +} // namespace ClangTools::Internal diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp deleted file mode 100644 index aaf20741a7f..00000000000 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "clangtoolruncontrol.h" - -#include "clangtool.h" -#include "clangtoolrunner.h" -#include "clangtoolstr.h" -#include "executableinfo.h" - -#include <coreplugin/progressmanager/taskprogress.h> - -#include <cppeditor/cppmodelmanager.h> - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/buildmanager.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/target.h> -#include <projectexplorer/toolchain.h> - -#include <utils/algorithm.h> -#include <utils/process.h> -#include <utils/stringutils.h> - -#include <QLoggingCategory> - -using namespace CppEditor; -using namespace ProjectExplorer; -using namespace Tasking; -using namespace Utils; - -static Q_LOGGING_CATEGORY(LOG, "qtc.clangtools.runcontrol", QtWarningMsg) - -namespace ClangTools { -namespace Internal { - -class ProjectBuilder : public RunWorker -{ -public: - ProjectBuilder(RunControl *runControl) - : RunWorker(runControl) - { - setId("ProjectBuilder"); - } - - bool success() const { return m_success; } - -private: - void start() final - { - Target *target = runControl()->target(); - QTC_ASSERT(target, reportFailure(); return); - - connect(BuildManager::instance(), &BuildManager::buildQueueFinished, - this, &ProjectBuilder::onBuildFinished, Qt::QueuedConnection); - if (!BuildManager::isBuilding(target)) - BuildManager::buildProjectWithDependencies(target->project()); - } - - void onBuildFinished(bool success) - { - disconnect(BuildManager::instance(), &BuildManager::buildQueueFinished, - this, &ProjectBuilder::onBuildFinished); - m_success = success; - reportDone(); - } - -private: - bool m_success = false; -}; - -static QDebug operator<<(QDebug debug, const Utils::Environment &environment) -{ - for (const QString &entry : environment.toStringList()) - debug << "\n " << entry; - return debug; -} - -static QDebug operator<<(QDebug debug, const AnalyzeUnits &analyzeUnits) -{ - for (const AnalyzeUnit &unit : analyzeUnits) - debug << "\n " << unit.file; - return debug; -} - -ClangToolRunWorker::ClangToolRunWorker(ClangTool *tool, RunControl *runControl, - const RunSettings &runSettings, - const CppEditor::ClangDiagnosticConfig &diagnosticConfig, - const FileInfos &fileInfos, - bool buildBeforeAnalysis) - : RunWorker(runControl) - , m_tool(tool) - , m_runSettings(runSettings) - , m_diagnosticConfig(diagnosticConfig) - , m_fileInfos(fileInfos) - , m_temporaryDir("clangtools-XXXXXX") -{ - m_temporaryDir.setAutoRemove(qtcEnvironmentVariable("QTC_CLANG_DONT_DELETE_OUTPUT_FILES") - != "1"); - setId("ClangTidyClazyRunner"); - setSupportsReRunning(false); - - if (buildBeforeAnalysis) { - m_projectBuilder = new ProjectBuilder(runControl); - addStartDependency(m_projectBuilder); - } - - Target *target = runControl->target(); - m_projectInfoBeforeBuild = CppEditor::CppModelManager::instance()->projectInfo(target->project()); - - BuildConfiguration *buildConfiguration = target->activeBuildConfiguration(); - QTC_ASSERT(buildConfiguration, return); - m_environment = buildConfiguration->environment(); - - ToolChain *toolChain = ToolChainKitAspect::cxxToolChain(target->kit()); - QTC_ASSERT(toolChain, return); - m_targetTriple = toolChain->originalTargetTriple(); - m_toolChainType = toolChain->typeId(); -} - -ClangToolRunWorker::~ClangToolRunWorker() = default; - -void ClangToolRunWorker::start() -{ - ProjectExplorerPlugin::saveModifiedFiles(); - - if (m_projectBuilder && !m_projectBuilder->success()) { - emit buildFailed(); - reportFailure(Tr::tr("Failed to build the project.")); - return; - } - - const QString &toolName = m_tool->name(); - Project *project = runControl()->project(); - m_projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project); - if (!m_projectInfo) { - reportFailure(Tr::tr("No code model data available for project.")); - return; - } - m_projectFiles = Utils::toSet(project->files(Project::AllFiles)); - - // Project changed in the mean time? - if (m_projectInfo->configurationOrFilesChanged(*m_projectInfoBeforeBuild)) { - // If it's more than a release/debug build configuration change, e.g. - // a version control checkout, files might be not valid C++ anymore - // or even gone, so better stop here. - reportFailure(Tr::tr("The project configuration changed since the start of " - "the %1. Please re-run with current configuration.") - .arg(toolName)); - emit startFailed(); - return; - } - - // Create log dir - if (!m_temporaryDir.isValid()) { - reportFailure( - Tr::tr("Failed to create temporary directory: %1.").arg(m_temporaryDir.errorString())); - emit startFailed(); - return; - } - - const Utils::FilePath projectFile = m_projectInfo->projectFilePath(); - appendMessage(Tr::tr("Running %1 on %2 with configuration \"%3\".") - .arg(toolName, projectFile.toUserOutput(), m_diagnosticConfig.displayName()), - Utils::NormalMessageFormat); - - const ClangToolType tool = m_tool == ClangTidyTool::instance() ? ClangToolType::Tidy - : ClangToolType::Clazy; - const FilePath executable = toolExecutable(tool); - const auto [includeDir, clangVersion] = getClangIncludeDirAndVersion(executable); - - // Collect files - AnalyzeUnits unitsToProcess; - for (const FileInfo &fileInfo : m_fileInfos) - unitsToProcess.append({fileInfo, includeDir, clangVersion}); - - qCDebug(LOG) << Q_FUNC_INFO << executable << includeDir << clangVersion; - qCDebug(LOG) << "Files to process:" << unitsToProcess; - qCDebug(LOG) << "Environment:" << m_environment; - - m_filesAnalyzed.clear(); - m_filesNotAnalyzed.clear(); - - QList<GroupItem> tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))}; - for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) { - if (!m_diagnosticConfig.isEnabled(tool) - && !m_runSettings.hasConfigFileForSourceFile(unit.file)) { - continue; - } - const AnalyzeInputData input{tool, m_runSettings, m_diagnosticConfig, m_temporaryDir.path(), - m_environment, unit}; - const auto setupHandler = [this, unit, tool] { - const QString filePath = unit.file.toUserOutput(); - appendMessage(Tr::tr("Analyzing \"%1\" [%2].").arg(filePath, clangToolName(tool)), - Utils::StdOutFormat); - return true; - }; - const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); }; - tasks.append(clangToolTask(input, setupHandler, outputHandler)); - } - - m_taskTree.reset(new TaskTree(tasks)); - connect(m_taskTree.get(), &TaskTree::done, this, &ClangToolRunWorker::finalize); - connect(m_taskTree.get(), &TaskTree::errorOccurred, this, &ClangToolRunWorker::finalize); - auto progress = new Core::TaskProgress(m_taskTree.get()); - progress->setDisplayName(Tr::tr("Analyzing")); - reportStarted(); - m_elapsed.start(); - m_taskTree->start(); -} - -void ClangToolRunWorker::stop() -{ - m_taskTree.reset(); - m_projectFiles.clear(); - - reportStopped(); - - // Print elapsed time since start - const QString elapsedTime = Utils::formatElapsedTime(m_elapsed.elapsed()); - appendMessage(elapsedTime, NormalMessageFormat); -} - -void ClangToolRunWorker::onDone(const AnalyzeOutputData &output) -{ - emit runnerFinished(); - - if (!output.success) { - qCDebug(LOG).noquote() << "onRunnerFinishedWithFailure:" << output.errorMessage << '\n' - << output.errorDetails; - m_filesAnalyzed.remove(output.fileToAnalyze); - m_filesNotAnalyzed.insert(output.fileToAnalyze); - - const QString message = Tr::tr("Failed to analyze \"%1\": %2") - .arg(output.fileToAnalyze.toUserOutput(), output.errorMessage); - appendMessage(message, Utils::StdErrFormat); - appendMessage(output.errorDetails, Utils::StdErrFormat); - return; - } - - qCDebug(LOG) << "onRunnerFinishedWithSuccess:" << output.outputFilePath; - - const Diagnostics diagnostics = output.diagnostics; - - if (!m_filesNotAnalyzed.contains(output.fileToAnalyze)) - m_filesAnalyzed.insert(output.fileToAnalyze); - if (!diagnostics.isEmpty()) { - // do not generate marks when we always analyze open files since marks from that - // analysis should be more up to date - const bool generateMarks = !m_runSettings.analyzeOpenFiles(); - m_tool->onNewDiagnosticsAvailable(diagnostics, generateMarks); - } -} - -void ClangToolRunWorker::finalize() -{ - if (m_taskTree) - m_taskTree.release()->deleteLater(); - const QString toolName = m_tool->name(); - if (m_filesNotAnalyzed.size() != 0) { - appendMessage(Tr::tr("Error: Failed to analyze %n files.", nullptr, m_filesNotAnalyzed.size()), - ErrorMessageFormat); - Target *target = runControl()->target(); - if (target && target->activeBuildConfiguration() && !target->activeBuildConfiguration()->buildDirectory().exists() - && !m_runSettings.buildBeforeAnalysis()) { - appendMessage( - Tr::tr("Note: You might need to build the project to generate or update source " - "files. To build automatically, enable \"Build the project before analysis\"."), - NormalMessageFormat); - } - } - - appendMessage(Tr::tr("%1 finished: " - "Processed %2 files successfully, %3 failed.") - .arg(toolName) - .arg(m_filesAnalyzed.size()) - .arg(m_filesNotAnalyzed.size()), - Utils::NormalMessageFormat); - - runControl()->initiateStop(); -} - -} // namespace Internal -} // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolruncontrol.h b/src/plugins/clangtools/clangtoolruncontrol.h deleted file mode 100644 index e6204da4ff2..00000000000 --- a/src/plugins/clangtools/clangtoolruncontrol.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "clangfileinfo.h" -#include "clangtoolssettings.h" - -#include <cppeditor/clangdiagnosticconfig.h> -#include <cppeditor/projectinfo.h> -#include <projectexplorer/runcontrol.h> -#include <utils/environment.h> -#include <utils/temporarydirectory.h> - -#include <QElapsedTimer> -#include <QSet> - -namespace Tasking { class TaskTree; } - -namespace ClangTools { -namespace Internal { - -struct AnalyzeOutputData; -class ClangTool; -class ProjectBuilder; - -class ClangToolRunWorker : public ProjectExplorer::RunWorker -{ - Q_OBJECT - -public: - ClangToolRunWorker(ClangTool *tool, - ProjectExplorer::RunControl *runControl, - const RunSettings &runSettings, - const CppEditor::ClangDiagnosticConfig &diagnosticConfig, - const FileInfos &fileInfos, - bool buildBeforeAnalysis); - ~ClangToolRunWorker(); - - int filesAnalyzed() const { return m_filesAnalyzed.size(); } - int filesNotAnalyzed() const { return m_filesNotAnalyzed.size(); } - int totalFilesToAnalyze() const { return int(m_fileInfos.size()); } - -signals: - void buildFailed(); - void runnerFinished(); - void startFailed(); - -private: - void start() final; - void stop() final; - void onDone(const AnalyzeOutputData &output); - void finalize(); - -private: - ClangTool * const m_tool; - RunSettings m_runSettings; - CppEditor::ClangDiagnosticConfig m_diagnosticConfig; - FileInfos m_fileInfos; - - ProjectBuilder *m_projectBuilder = nullptr; - Utils::Environment m_environment; - Utils::TemporaryDirectory m_temporaryDir; - - CppEditor::ProjectInfo::ConstPtr m_projectInfoBeforeBuild; - CppEditor::ProjectInfo::ConstPtr m_projectInfo; - QString m_targetTriple; - Utils::Id m_toolChainType; - - std::unique_ptr<Tasking::TaskTree> m_taskTree; - QSet<Utils::FilePath> m_projectFiles; - QSet<Utils::FilePath> m_filesAnalyzed; - QSet<Utils::FilePath> m_filesNotAnalyzed; - - QElapsedTimer m_elapsed; -}; - -} // namespace Internal -} // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index ecace1c7d36..e83037aa536 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -165,6 +165,18 @@ GroupItem clangToolTask(const AnalyzeInputData &input, }; const auto onProcessDone = [=](const Process &process) { qCDebug(LOG).noquote() << "Output:\n" << process.cleanedStdOut(); + + // Here we handle only the case of process success with stderr output. + if (!outputHandler) + return; + if (process.result() != ProcessResult::FinishedWithSuccess) + return; + const QString stdErr = process.cleanedStdErr(); + if (stdErr.isEmpty()) + return; + outputHandler( + {true, input.unit.file, {}, {}, input.tool, Tr::tr("%1 produced stderr output:") + .arg(storage->name), stdErr}); }; const auto onProcessError = [=](const Process &process) { if (!outputHandler) @@ -219,7 +231,7 @@ GroupItem clangToolTask(const AnalyzeInputData &input, const Group group { finishAllAndDone, - Storage(storage), + Tasking::Storage(storage), onGroupSetup(onSetup), Group { sequential, diff --git a/src/plugins/clangtools/clangtools.qbs b/src/plugins/clangtools/clangtools.qbs index ad8543bf611..6afa5d026b1 100644 --- a/src/plugins/clangtools/clangtools.qbs +++ b/src/plugins/clangtools/clangtools.qbs @@ -8,7 +8,7 @@ QtcPlugin { Depends { name: "CppEditor" } Depends { name: "Debugger" } Depends { name: "ProjectExplorer" } - Depends { name: "QtSupport"; condition: qtc.testsEnabled } + Depends { name: "QtSupport"; condition: qtc.withPluginTests } Depends { name: "TextEditor" } Depends { name: "Utils" } @@ -30,8 +30,6 @@ QtcPlugin { "clangselectablefilesdialog.h", "clangtool.cpp", "clangtool.h", - "clangtoolruncontrol.cpp", - "clangtoolruncontrol.h", "clangtoolrunner.cpp", "clangtoolrunner.h", "clangtools_global.h", "clangtoolstr.h", diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index e0b797a1e0a..5323f998da2 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -64,7 +64,7 @@ private: ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) : ClangToolsDiagnosticModelBase(parent) - , m_filesWatcher(std::make_unique<QFileSystemWatcher>()) + , m_filesWatcher(std::make_unique<Utils::FileSystemWatcher>()) { setRootItem(new Utils::StaticTreeItem(QString())); connectFileWatcher(); @@ -104,7 +104,7 @@ void ClangToolsDiagnosticModel::addDiagnostics(const Diagnostics &diagnostics, b if (!filePathItem) { filePathItem = new FilePathItem(filePath); rootItem()->appendChild(filePathItem); - addWatchedPath(filePath.toString()); + addWatchedPath(filePath); } // Add to file path item @@ -149,14 +149,14 @@ void ClangToolsDiagnosticModel::updateItems(const DiagnosticItem *changedItem) void ClangToolsDiagnosticModel::connectFileWatcher() { connect(m_filesWatcher.get(), - &QFileSystemWatcher::fileChanged, + &Utils::FileSystemWatcher::fileChanged, this, &ClangToolsDiagnosticModel::onFileChanged); } void ClangToolsDiagnosticModel::clearAndSetupCache() { - m_filesWatcher = std::make_unique<QFileSystemWatcher>(); + m_filesWatcher = std::make_unique<Utils::FileSystemWatcher>(); connectFileWatcher(); stepsToItemsCache.clear(); } @@ -167,17 +167,17 @@ void ClangToolsDiagnosticModel::onFileChanged(const QString &path) if (item->diagnostic().location.filePath == Utils::FilePath::fromString(path)) item->setFixItStatus(FixitStatus::Invalidated); }); - removeWatchedPath(path); + m_filesWatcher->removeFile(path); } -void ClangToolsDiagnosticModel::removeWatchedPath(const QString &path) +void ClangToolsDiagnosticModel::removeWatchedPath(const Utils::FilePath &path) { - m_filesWatcher->removePath(path); + m_filesWatcher->removeFile(path); } -void ClangToolsDiagnosticModel::addWatchedPath(const QString &path) +void ClangToolsDiagnosticModel::addWatchedPath(const Utils::FilePath &path) { - m_filesWatcher->addPath(path); + m_filesWatcher->addFile(path, Utils::FileSystemWatcher::WatchAllChanges); } static QString lineColumnString(const Debugger::DiagnosticLocation &location) diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h index 55aa165d347..cf1a08aab16 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -9,6 +9,7 @@ #include "clangtoolsutils.h" #include <debugger/analyzer/detailederrorview.h> +#include <utils/filesystemwatcher.h> #include <utils/fileutils.h> #include <utils/treemodel.h> @@ -104,8 +105,8 @@ public: QSet<QString> allChecks() const; void clear(); - void removeWatchedPath(const QString &path); - void addWatchedPath(const QString &path); + void removeWatchedPath(const Utils::FilePath &path); + void addWatchedPath(const Utils::FilePath &path); signals: void fixitStatusChanged(const QModelIndex &index, FixitStatus oldStatus, FixitStatus newStatus); @@ -120,7 +121,7 @@ private: QHash<Utils::FilePath, FilePathItem *> m_filePathToItem; QSet<Diagnostic> m_diagnostics; std::map<QVector<ExplainingStep>, QVector<DiagnosticItem *>> stepsToItemsCache; - std::unique_ptr<QFileSystemWatcher> m_filesWatcher; + std::unique_ptr<Utils::FileSystemWatcher> m_filesWatcher; }; class FilterOptions { diff --git a/src/plugins/clangtools/clangtoolsplugin.cpp b/src/plugins/clangtools/clangtoolsplugin.cpp index 35086be7aaf..6850cc96f42 100644 --- a/src/plugins/clangtools/clangtoolsplugin.cpp +++ b/src/plugins/clangtools/clangtoolsplugin.cpp @@ -17,6 +17,7 @@ #include "clangtoolsunittests.h" #endif +#include <utils/icon.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/stylehelper.h> @@ -34,7 +35,7 @@ #include <texteditor/texteditor.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectpanelfactory.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> @@ -89,7 +90,9 @@ ClangToolsPlugin::~ClangToolsPlugin() void ClangToolsPlugin::initialize() { - TaskHub::addCategory(taskCategory(), Tr::tr("Clang Tools")); + TaskHub::addCategory({taskCategory(), + Tr::tr("Clang Tools"), + Tr::tr("Issues that Clang-Tidy and Clazy found when analyzing code.")}); // Import tidy/clazy diagnostic configs from CppEditor now // instead of at opening time of the settings page @@ -135,6 +138,20 @@ void ClangToolsPlugin::onCurrentEditorChanged() void ClangToolsPlugin::registerAnalyzeActions() { + const char * const menuGroupId = "ClangToolsCppGroup"; + ActionContainer * const mtoolscpp + = ActionManager::actionContainer(CppEditor::Constants::M_TOOLS_CPP); + if (mtoolscpp) { + mtoolscpp->insertGroup(CppEditor::Constants::G_GLOBAL, menuGroupId); + mtoolscpp->addSeparator(menuGroupId); + } + Core::ActionContainer * const mcontext = Core::ActionManager::actionContainer( + CppEditor::Constants::M_CONTEXT); + if (mcontext) { + mcontext->insertGroup(CppEditor::Constants::G_GLOBAL, menuGroupId); + mcontext->addSeparator(menuGroupId); + } + for (const auto &toolInfo : {std::make_tuple(ClangTidyTool::instance(), Constants::RUN_CLANGTIDY_ON_PROJECT, Constants::RUN_CLANGTIDY_ON_CURRENT_FILE), @@ -145,14 +162,10 @@ void ClangToolsPlugin::registerAnalyzeActions() ActionManager::registerAction(tool->startAction(), std::get<1>(toolInfo)); Command *cmd = ActionManager::registerAction(tool->startOnCurrentFileAction(), std::get<2>(toolInfo)); - ActionContainer *mtoolscpp = ActionManager::actionContainer(CppEditor::Constants::M_TOOLS_CPP); if (mtoolscpp) - mtoolscpp->addAction(cmd); - - Core::ActionContainer *mcontext = Core::ActionManager::actionContainer( - CppEditor::Constants::M_CONTEXT); + mtoolscpp->addAction(cmd, menuGroupId); if (mcontext) - mcontext->addAction(cmd, CppEditor::Constants::G_CONTEXT_FIRST); + mcontext->addAction(cmd, menuGroupId); } // add button to tool bar of C++ source files diff --git a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp index 4c858451fa3..ce5c7a52549 100644 --- a/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp +++ b/src/plugins/clangtools/clangtoolspreconfiguredsessiontests.cpp @@ -12,7 +12,7 @@ #include <cppeditor/compileroptionsbuilder.h> #include <cppeditor/projectinfo.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp index 581a8ca57b6..0d791caed9a 100644 --- a/src/plugins/clangtools/clangtoolsprojectsettings.cpp +++ b/src/plugins/clangtools/clangtoolsprojectsettings.cpp @@ -8,6 +8,9 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> +#include <utils/store.h> + +using namespace Utils; namespace ClangTools { namespace Internal { @@ -97,10 +100,10 @@ void ClangToolsProjectSettings::removeAllSuppressedDiagnostics() emit suppressedDiagnosticsChanged(); } -static QVariantMap convertToMapFromVersionBefore410(ProjectExplorer::Project *p) +static Store convertToMapFromVersionBefore410(ProjectExplorer::Project *p) { // These keys haven't changed. - const QStringList keys = { + const Key keys[] = { SETTINGS_KEY_SELECTED_DIRS, SETTINGS_KEY_SELECTED_FILES, SETTINGS_KEY_SUPPRESSED_DIAGS, @@ -108,11 +111,11 @@ static QVariantMap convertToMapFromVersionBefore410(ProjectExplorer::Project *p) "ClangTools.BuildBeforeAnalysis", }; - QVariantMap map; - for (const QString &key : keys) + Store map; + for (const Key &key : keys) map.insert(key, p->namedSettings(key)); - map.insert(SETTINGS_PREFIX + QString(diagnosticConfigIdKey), + map.insert(SETTINGS_PREFIX + Key(diagnosticConfigIdKey), p->namedSettings("ClangTools.DiagnosticConfig")); return map; @@ -121,7 +124,7 @@ static QVariantMap convertToMapFromVersionBefore410(ProjectExplorer::Project *p) void ClangToolsProjectSettings::load() { // Load map - QVariantMap map = m_project->namedSettings(SETTINGS_KEY_MAIN).toMap(); + Store map = storeFromVariant(m_project->namedSettings(SETTINGS_KEY_MAIN)); bool write = false; if (map.isEmpty()) { @@ -145,7 +148,7 @@ void ClangToolsProjectSettings::load() const QVariantList list = map.value(SETTINGS_KEY_SUPPRESSED_DIAGS).toList(); for (const QVariant &v : list) { - const QVariantMap diag = v.toMap(); + const Store diag = storeFromVariant(v); const QString fp = diag.value(SETTINGS_KEY_SUPPRESSED_DIAGS_FILEPATH).toString(); if (fp.isEmpty()) continue; @@ -172,7 +175,7 @@ void ClangToolsProjectSettings::load() void ClangToolsProjectSettings::store() { - QVariantMap map; + Store map; map.insert(SETTINGS_KEY_USE_GLOBAL_SETTINGS, m_useGlobalSettings); const QStringList dirs = Utils::transform<QList>(m_selectedDirs, &Utils::FilePath::toString); @@ -183,23 +186,23 @@ void ClangToolsProjectSettings::store() QVariantList list; for (const SuppressedDiagnostic &diag : std::as_const(m_suppressedDiagnostics)) { - QVariantMap diagMap; + Store diagMap; diagMap.insert(SETTINGS_KEY_SUPPRESSED_DIAGS_FILEPATH, diag.filePath.toString()); diagMap.insert(SETTINGS_KEY_SUPPRESSED_DIAGS_MESSAGE, diag.description); diagMap.insert(SETTINGS_KEY_SUPPRESSED_DIAGS_UNIQIFIER, diag.uniquifier); - list << diagMap; + list << variantFromStore(diagMap); } map.insert(SETTINGS_KEY_SUPPRESSED_DIAGS, list); m_runSettings.toMap(map, SETTINGS_PREFIX); - m_project->setNamedSettings(SETTINGS_KEY_MAIN, map); + m_project->setNamedSettings(SETTINGS_KEY_MAIN, variantFromStore(map)); } ClangToolsProjectSettings::ClangToolsProjectSettingsPtr ClangToolsProjectSettings::getSettings(ProjectExplorer::Project *project) { - const QString key = "ClangToolsProjectSettings"; + const Key key = "ClangToolsProjectSettings"; QVariant v = project->extraData(key); if (v.isNull()) { v = QVariant::fromValue( diff --git a/src/plugins/clangtools/clangtoolssettings.cpp b/src/plugins/clangtools/clangtoolssettings.cpp index 286b076f95c..6699be10bdc 100644 --- a/src/plugins/clangtools/clangtoolssettings.cpp +++ b/src/plugins/clangtools/clangtoolssettings.cpp @@ -23,8 +23,6 @@ using namespace Utils; namespace ClangTools { namespace Internal { -const char clangTidyExecutableKey[] = "ClangTidyExecutable"; -const char clazyStandaloneExecutableKey[] = "ClazyStandaloneExecutable"; const char parallelJobsKey[] = "ParallelJobs"; const char preferConfigFileKey[] = "PreferConfigFile"; @@ -43,7 +41,7 @@ RunSettings::RunSettings() { } -void RunSettings::fromMap(const QVariantMap &map, const QString &prefix) +void RunSettings::fromMap(const Store &map, const Key &prefix) { m_diagnosticConfigId = Id::fromSetting(map.value(prefix + diagnosticConfigIdKey)); m_parallelJobs = map.value(prefix + parallelJobsKey).toInt(); @@ -52,7 +50,7 @@ void RunSettings::fromMap(const QVariantMap &map, const QString &prefix) m_analyzeOpenFiles = map.value(prefix + analyzeOpenFilesKey).toBool(); } -void RunSettings::toMap(QVariantMap &map, const QString &prefix) const +void RunSettings::toMap(Store &map, const Key &prefix) const { map.insert(prefix + diagnosticConfigIdKey, m_diagnosticConfigId.toSetting()); map.insert(prefix + parallelJobsKey, m_parallelJobs); @@ -89,23 +87,29 @@ bool RunSettings::hasConfigFileForSourceFile(const Utils::FilePath &sourceFile) return false; } -ClangToolsSettings::ClangToolsSettings() -{ - readSettings(); -} - ClangToolsSettings *ClangToolsSettings::instance() { static ClangToolsSettings instance; return &instance; } -static QVariantMap convertToMapFromVersionBefore410(QSettings *s) +ClangToolsSettings::ClangToolsSettings() +{ + setSettingsGroup(Constants::SETTINGS_ID); + + clangTidyExecutable.setSettingsKey("ClangTidyExecutable"); + + clazyStandaloneExecutable.setSettingsKey("ClazyStandaloneExecutable"); + + readSettings(); +} + +static Store convertToMapFromVersionBefore410(QtcSettings *s) { const char oldParallelJobsKey[] = "simultaneousProcesses"; const char oldBuildBeforeAnalysisKey[] = "buildBeforeAnalysis"; - QVariantMap map; + Store map; map.insert(diagnosticConfigIdKey, s->value(oldDiagnosticConfigIdKey)); map.insert(parallelJobsKey, s->value(oldParallelJobsKey)); map.insert(buildBeforeAnalysisKey, s->value(oldBuildBeforeAnalysisKey)); @@ -141,25 +145,25 @@ void ClangToolsSettings::readSettings() if (!importedConfigs.isEmpty()) write = true; - QSettings *s = Core::ICore::settings(); + AspectContainer::readSettings(); + + QtcSettings *s = Core::ICore::settings(); s->beginGroup(Constants::SETTINGS_ID); - m_clangTidyExecutable = FilePath::fromSettings(s->value(clangTidyExecutableKey)); - m_clazyStandaloneExecutable = FilePath::fromSettings(s->value(clazyStandaloneExecutableKey)); m_diagnosticConfigs.append(diagnosticConfigsFromSettings(s)); - QVariantMap map; + Store map; if (!s->value(oldDiagnosticConfigIdKey).isNull()) { map = convertToMapFromVersionBefore410(s); write = true; } else { - QVariantMap defaults; + Store defaults; defaults.insert(diagnosticConfigIdKey, defaultDiagnosticId().toSetting()); defaults.insert(parallelJobsKey, m_runSettings.parallelJobs()); defaults.insert(preferConfigFileKey, m_runSettings.preferConfigFile()); defaults.insert(buildBeforeAnalysisKey, m_runSettings.buildBeforeAnalysis()); defaults.insert(analyzeOpenFilesKey, m_runSettings.analyzeOpenFiles()); map = defaults; - for (QVariantMap::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it) + for (Store::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it) map.insert(it.key(), s->value(it.key(), it.value())); } @@ -172,37 +176,37 @@ void ClangToolsSettings::readSettings() writeSettings(); } -void ClangToolsSettings::writeSettings() +void ClangToolsSettings::writeSettings() const { - QSettings *s = Core::ICore::settings(); + AspectContainer::writeSettings(); + + QtcSettings *s = Core::ICore::settings(); s->beginGroup(Constants::SETTINGS_ID); - s->setValue(clangTidyExecutableKey, m_clangTidyExecutable.toSettings()); - s->setValue(clazyStandaloneExecutableKey, m_clazyStandaloneExecutable.toSettings()); diagnosticConfigsToSettings(s, m_diagnosticConfigs); - QVariantMap map; + Store map; m_runSettings.toMap(map); - for (QVariantMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) + for (Store::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) s->setValue(it.key(), it.value()); s->endGroup(); - emit changed(); + emit const_cast<ClangToolsSettings *>(this)->changed(); // FIXME: This is the wrong place } FilePath ClangToolsSettings::executable(ClangToolType tool) const { - return tool == ClangToolType::Tidy ? m_clangTidyExecutable : m_clazyStandaloneExecutable; + return tool == ClangToolType::Tidy ? clangTidyExecutable() : clazyStandaloneExecutable(); } void ClangToolsSettings::setExecutable(ClangToolType tool, const FilePath &path) { if (tool == ClangToolType::Tidy) { - m_clangTidyExecutable = path; + clangTidyExecutable.setValue(path); m_clangTidyVersion = {}; } else { - m_clazyStandaloneExecutable = path; + clazyStandaloneExecutable.setValue(path); m_clazyVersion = {}; } } diff --git a/src/plugins/clangtools/clangtoolssettings.h b/src/plugins/clangtools/clangtoolssettings.h index 45a2cb032e4..1c8656981df 100644 --- a/src/plugins/clangtools/clangtoolssettings.h +++ b/src/plugins/clangtools/clangtoolssettings.h @@ -5,6 +5,7 @@ #include <cppeditor/clangdiagnosticconfig.h> +#include <utils/aspects.h> #include <utils/filepath.h> #include <utils/id.h> @@ -25,8 +26,8 @@ class RunSettings public: RunSettings(); - void fromMap(const QVariantMap &map, const QString &prefix = QString()); - void toMap(QVariantMap &map, const QString &prefix = QString()) const; + void fromMap(const Utils::Store &map, const Utils::Key &prefix = {}); + void toMap(Utils::Store &map, const Utils::Key &prefix = {}) const; Utils::Id diagnosticConfigId() const; void setDiagnosticConfigId(const Utils::Id &id) { m_diagnosticConfigId = id; } @@ -55,13 +56,17 @@ private: bool m_analyzeOpenFiles = true; }; -class ClangToolsSettings : public QObject +class ClangToolsSettings : public Utils::AspectContainer { - Q_OBJECT + ClangToolsSettings(); public: static ClangToolsSettings *instance(); - void writeSettings(); + void writeSettings() const override; + + // Executables + Utils::FilePathAspect clangTidyExecutable{this}; + Utils::FilePathAspect clazyStandaloneExecutable{this}; Utils::FilePath executable(CppEditor::ClangToolType tool) const; void setExecutable(CppEditor::ClangToolType tool, const Utils::FilePath &path); @@ -76,16 +81,8 @@ public: static VersionAndSuffix clangTidyVersion(); static QVersionNumber clazyVersion(); -signals: - void changed(); - private: - ClangToolsSettings(); - void readSettings(); - - // Executables - Utils::FilePath m_clangTidyExecutable; - Utils::FilePath m_clazyStandaloneExecutable; + void readSettings() override; // Diagnostic Configs CppEditor::ClangDiagnosticConfigs m_diagnosticConfigs; diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp index 34ecf11e9a8..2ea61b7acd6 100644 --- a/src/plugins/clangtools/clangtoolsunittests.cpp +++ b/src/plugins/clangtools/clangtoolsunittests.cpp @@ -14,13 +14,13 @@ #include <cppeditor/cpptoolsreuse.h> #include <cppeditor/cpptoolstestcase.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/toolchain.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/environment.h> #include <utils/fileutils.h> diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index de3ac2b9cba..029a879ce11 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -141,7 +141,7 @@ void showHintAboutBuildBeforeAnalysis() Utils::CheckableMessageBox::information(Core::ICore::dialogParent(), Tr::tr("Info About Build the Project Before Analysis"), hintAboutBuildBeforeAnalysis(), - QString("ClangToolsDisablingBuildBeforeAnalysisHint")); + Key("ClangToolsDisablingBuildBeforeAnalysisHint")); } FilePath fullPath(const FilePath &executable) @@ -280,7 +280,7 @@ ClangDiagnosticConfig diagnosticConfig(const Utils::Id &diagConfigId) static QStringList extraOptions(const QString &envVar) { if (!qtcEnvironmentVariableIsSet(envVar)) - return QStringList(); + return {}; QString arguments = qtcEnvironmentVariable(envVar); return ProcessArgs::splitArgs(arguments, HostOsInfo::hostOs()); } @@ -307,12 +307,25 @@ QStringList extraClangToolsAppendOptions() return options; } +static QVersionNumber fixupVersion(const VersionAndSuffix &versionAndSuffix) +{ + // llvm.org only does document releases for the first released version + QVersionNumber version = QVersionNumber(versionAndSuffix.first.majorVersion(), 0, 0); + + if (version == QVersionNumber(0)) + version = QVersionNumber(12); + + // Version 17.0.0 was never released due to a git issue + if (version.majorVersion() == 17) + version = QVersionNumber(17, 0, 1); + + return version; +} + QString clangTidyDocUrl(const QString &check) { VersionAndSuffix version = ClangToolsSettings::clangTidyVersion(); - version.first = QVersionNumber(version.first.majorVersion(), 0, 0); - if (version.first == QVersionNumber(0)) - version.first = QVersionNumber(12); + version.first = fixupVersion(version); static const char versionedUrlPrefix[] = "https://releases.llvm.org/%1/tools/clang/tools/extra/docs/"; static const char unversionedUrlPrefix[] = "https://clang.llvm.org/extra/"; diff --git a/src/plugins/clangtools/diagnosticmark.cpp b/src/plugins/clangtools/diagnosticmark.cpp index 3802ee325cf..90b4e19812d 100644 --- a/src/plugins/clangtools/diagnosticmark.cpp +++ b/src/plugins/clangtools/diagnosticmark.cpp @@ -39,7 +39,7 @@ DiagnosticMark::DiagnosticMark(const Diagnostic &diagnostic, TextDocument *docum // Copy to clipboard action QList<QAction *> actions; QAction *action = new QAction(); - action->setIcon(QIcon::fromTheme("edit-copy", Icons::COPY.icon())); + action->setIcon(Icon::fromTheme("edit-copy")); action->setToolTip(Tr::tr("Copy to Clipboard")); QObject::connect(action, &QAction::triggered, [diagnostic] { const QString text = createFullLocationString(diagnostic.location) diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 28ea234f9ad..5906cbc4e32 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -109,7 +109,7 @@ static VirtualFileSystemOverlay &vfso() static FileInfo getFileInfo(const FilePath &file, Project *project) { - const ProjectInfo::ConstPtr projectInfo = CppModelManager::instance()->projectInfo(project); + const ProjectInfo::ConstPtr projectInfo = CppModelManager::projectInfo(project); if (!projectInfo) return {}; diff --git a/src/plugins/clangtools/documentquickfixfactory.cpp b/src/plugins/clangtools/documentquickfixfactory.cpp index c9875b0180c..e9807a8bcba 100644 --- a/src/plugins/clangtools/documentquickfixfactory.cpp +++ b/src/plugins/clangtools/documentquickfixfactory.cpp @@ -9,6 +9,7 @@ #include <texteditor/refactoringchanges.h> #include <utils/qtcassert.h> +#include <utils/textutils.h> namespace ClangTools { namespace Internal { diff --git a/src/plugins/clangtools/settingswidget.cpp b/src/plugins/clangtools/settingswidget.cpp index 80aad561b07..113373f57e0 100644 --- a/src/plugins/clangtools/settingswidget.cpp +++ b/src/plugins/clangtools/settingswidget.cpp @@ -49,8 +49,8 @@ SettingsWidget::SettingsWidget() pathChooser->setDefaultValue(placeHolderText); pathChooser->setFilePath(path); pathChooser->setHistoryCompleter(tool == ClangToolType::Tidy - ? QString("ClangTools.ClangTidyExecutable.History") - : QString("ClangTools.ClazyStandaloneExecutable.History")); + ? Key("ClangTools.ClangTidyExecutable.History") + : Key("ClangTools.ClazyStandaloneExecutable.History")); pathChooser->setCommandVersionArguments({"--version"}); return pathChooser; }; diff --git a/src/plugins/classview/ClassView.json.in b/src/plugins/classview/ClassView.json.in index 81cca038c3c..753824248b9 100644 --- a/src/plugins/classview/ClassView.json.in +++ b/src/plugins/classview/ClassView.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"ClassView\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) 2016 Denis Mingulov, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ClassView", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) 2016 Denis Mingulov, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"C++\", - \"Description\" : \"Class View component.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "C++", + "Description" : "Class View component.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/classview/classviewnavigationwidgetfactory.cpp b/src/plugins/classview/classviewnavigationwidgetfactory.cpp index 4647ece36dd..aa1429fc4b7 100644 --- a/src/plugins/classview/classviewnavigationwidgetfactory.cpp +++ b/src/plugins/classview/classviewnavigationwidgetfactory.cpp @@ -8,8 +8,9 @@ #include <utils/qtcassert.h> #include <utils/qtcsettings.h> +#include <utils/store.h> -#include <QSettings> +using namespace Utils; namespace ClassView { namespace Internal { @@ -42,32 +43,30 @@ Core::NavigationView NavigationWidgetFactory::createWidget() /*! Returns a settings prefix for \a position. */ -static QString settingsPrefix(int position) +static Key settingsPrefix(int position) { - return QString::fromLatin1("ClassView.Treewidget.%1.FlatMode").arg(position); + return numberedKey("ClassView.Treewidget.", position) + ".FlatMode"; } //! Flat mode settings -void NavigationWidgetFactory::saveSettings(Utils::QtcSettings *settings, - int position, - QWidget *widget) +void NavigationWidgetFactory::saveSettings(QtcSettings *settings, int position, QWidget *widget) { auto pw = qobject_cast<NavigationWidget *>(widget); QTC_ASSERT(pw, return); // .beginGroup is not used - to prevent simultaneous access - QString settingsGroup = settingsPrefix(position); + Key settingsGroup = settingsPrefix(position); settings->setValue(settingsGroup, pw->flatMode()); } -void NavigationWidgetFactory::restoreSettings(QSettings *settings, int position, QWidget *widget) +void NavigationWidgetFactory::restoreSettings(QtcSettings *settings, int position, QWidget *widget) { auto pw = qobject_cast<NavigationWidget *>(widget); QTC_ASSERT(pw, return); // .beginGroup is not used - to prevent simultaneous access - QString settingsGroup = settingsPrefix(position); + Key settingsGroup = settingsPrefix(position); pw->setFlatMode(settings->value(settingsGroup, false).toBool()); } diff --git a/src/plugins/classview/classviewnavigationwidgetfactory.h b/src/plugins/classview/classviewnavigationwidgetfactory.h index a1690c23bba..0ec38a4eeeb 100644 --- a/src/plugins/classview/classviewnavigationwidgetfactory.h +++ b/src/plugins/classview/classviewnavigationwidgetfactory.h @@ -5,25 +5,16 @@ #include <coreplugin/inavigationwidgetfactory.h> -namespace ClassView { -namespace Internal { +namespace ClassView::Internal { class NavigationWidgetFactory : public Core::INavigationWidgetFactory { - Q_OBJECT - public: NavigationWidgetFactory(); - //! \implements Core::INavigationWidgetFactory::createWidget Core::NavigationView createWidget() override; - - //! \implements Core::INavigationWidgetFactory::saveSettings void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; - - //! \implements Core::INavigationWidgetFactory::restoreSettings - void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; }; -} // namespace Internal -} // namespace ClassView +} // ClassView::Internal diff --git a/src/plugins/classview/classviewparser.cpp b/src/plugins/classview/classviewparser.cpp index b766c0c4425..2906504eb7c 100644 --- a/src/plugins/classview/classviewparser.cpp +++ b/src/plugins/classview/classviewparser.cpp @@ -250,7 +250,7 @@ ParserTreeItem::ConstPtr Parser::getCachedOrParseDocumentTree(const CPlusPlus::D void Parser::updateDocuments(const QSet<FilePath> &documentPaths) { - updateDocumentsFromSnapshot(documentPaths, CppEditor::CppModelManager::instance()->snapshot()); + updateDocumentsFromSnapshot(documentPaths, CppEditor::CppModelManager::snapshot()); } void Parser::updateDocumentsFromSnapshot(const QSet<FilePath> &documentPaths, @@ -293,7 +293,7 @@ void Parser::resetData(const QHash<FilePath, QPair<QString, FilePaths>> &project d->m_projectCache.clear(); d->m_documentCache.clear(); - const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::snapshot(); for (auto it = projects.cbegin(); it != projects.cend(); ++it) { const auto projectData = it.value(); QSet<FilePath> commonFiles; @@ -313,7 +313,7 @@ void Parser::resetData(const QHash<FilePath, QPair<QString, FilePaths>> &project void Parser::addProject(const FilePath &projectPath, const QString &projectName, const FilePaths &filesInProject) { - const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::snapshot(); QSet<FilePath> commonFiles; for (const auto &fileInProject : filesInProject) { CPlusPlus::Document::Ptr doc = snapshot.document(fileInProject); diff --git a/src/plugins/clearcase/ClearCase.json.in b/src/plugins/clearcase/ClearCase.json.in index e4ddc162b08..8bcfbb66bc0 100644 --- a/src/plugins/clearcase/ClearCase.json.in +++ b/src/plugins/clearcase/ClearCase.json.in @@ -1,31 +1,31 @@ { - \"Name\" : \"ClearCase\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Platform\" : \"^(Linux|Windows)\", - \"Vendor\" : \"AudioCodes\", - \"Copyright\" : \"(C) 2016 AudioCodes Ltd., (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ClearCase", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Platform" : "^(Linux|Windows)", + "Vendor" : "AudioCodes", + "Copyright" : "(C) 2016 AudioCodes Ltd., (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"ClearCase integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Version Control", + "Description" : "ClearCase integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/vnd.audc.text.clearcase.submit\'>\", - \" <comment>ClearCase submit template</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/vnd.audc.text.clearcase.submit'>", + " <comment>ClearCase submit template</comment>", + " <sub-class-of type='text/plain'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/clearcase/clearcase.qbs b/src/plugins/clearcase/clearcase.qbs index 2b65135f8de..a69303bded4 100644 --- a/src/plugins/clearcase/clearcase.qbs +++ b/src/plugins/clearcase/clearcase.qbs @@ -3,7 +3,7 @@ import qbs 1.0 QtcPlugin { name: "ClearCase" - pluginJsonReplacements: ({"CLEARCASE_DISABLED_STR": (qbs.targetOS.contains("macos") ? "true": "false")}) + pluginjson.replacements: ({"CLEARCASE_DISABLED_STR": (qbs.targetOS.contains("macos") ? "true": "false")}) Depends { name: "Qt.widgets" } Depends { name: "Utils" } diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index 943ffa194a4..cfbc5a1f4c0 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -828,7 +828,7 @@ QStringList ClearCasePluginPrivate::ccGetActiveVobs() const // Snapshot views does not necessarily have all active VOBs loaded, so we'll have to // check if the dirs exists as well. Else the command will work, but the output will // complain about the element not being loaded. - if (QFile::exists(prefix + relativeDir)) + if (QFileInfo::exists(prefix + relativeDir)) res.append(relativeDir); } return res; diff --git a/src/plugins/clearcase/clearcasesettings.cpp b/src/plugins/clearcase/clearcasesettings.cpp index 8ad40642b8e..eeff96b2a30 100644 --- a/src/plugins/clearcase/clearcasesettings.cpp +++ b/src/plugins/clearcase/clearcasesettings.cpp @@ -4,8 +4,9 @@ #include "clearcasesettings.h" #include <utils/environment.h> +#include <utils/qtcsettings.h> -#include <QSettings> +using namespace Utils; namespace ClearCase::Internal { @@ -40,59 +41,59 @@ ClearCaseSettings::ClearCaseSettings() : timeOutS(defaultTimeOutS) { } -void ClearCaseSettings::fromSettings(QSettings *settings) +void ClearCaseSettings::fromSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String(groupC)); - ccCommand = settings->value(QLatin1String(commandKeyC), defaultCommand()).toString(); + settings->beginGroup(groupC); + ccCommand = settings->value(commandKeyC, defaultCommand()).toString(); ccBinaryPath = Utils::Environment::systemEnvironment().searchInPath(ccCommand); - timeOutS = settings->value(QLatin1String(timeOutKeyC), defaultTimeOutS).toInt(); - autoCheckOut = settings->value(QLatin1String(autoCheckOutKeyC), false).toBool(); - noComment = settings->value(QLatin1String(noCommentKeyC), false).toBool(); - keepFileUndoCheckout = settings->value(QLatin1String(keepFileUndoCheckoutKeyC), true).toBool(); - const QString sDiffType = settings->value(QLatin1String(diffTypeKeyC), + timeOutS = settings->value(timeOutKeyC, defaultTimeOutS).toInt(); + autoCheckOut = settings->value(autoCheckOutKeyC, false).toBool(); + noComment = settings->value(noCommentKeyC, false).toBool(); + keepFileUndoCheckout = settings->value(keepFileUndoCheckoutKeyC, true).toBool(); + const QString sDiffType = settings->value(diffTypeKeyC, QLatin1String("Graphical")).toString(); switch (sDiffType[0].toUpper().toLatin1()) { case 'G': diffType = GraphicalDiff; break; case 'E': diffType = ExternalDiff; break; } - diffArgs = settings->value(QLatin1String(diffArgsKeyC), QLatin1String(defaultDiffArgs)).toString(); - autoAssignActivityName = settings->value(QLatin1String(autoAssignActivityKeyC), true).toBool(); - historyCount = settings->value(QLatin1String(historyCountKeyC), int(defaultHistoryCount)).toInt(); - disableIndexer = settings->value(QLatin1String(disableIndexerKeyC), false).toBool(); - indexOnlyVOBs = settings->value(QLatin1String(indexOnlyVOBsC), QString()).toString(); + diffArgs = settings->value(diffArgsKeyC, QLatin1String(defaultDiffArgs)).toString(); + autoAssignActivityName = settings->value(autoAssignActivityKeyC, true).toBool(); + historyCount = settings->value(historyCountKeyC, int(defaultHistoryCount)).toInt(); + disableIndexer = settings->value(disableIndexerKeyC, false).toBool(); + indexOnlyVOBs = settings->value(indexOnlyVOBsC, QString()).toString(); extDiffAvailable = !Utils::Environment::systemEnvironment().searchInPath(QLatin1String("diff")).isEmpty(); - settings->beginGroup(QLatin1String(totalFilesKeyC)); - const QStringList views = settings->childKeys(); - for (const QString &view : views) + settings->beginGroup(totalFilesKeyC); + const KeyList views = settings->childKeys(); + for (const Key &view : views) totalFiles[view] = settings->value(view).toInt(); settings->endGroup(); settings->endGroup(); } -void ClearCaseSettings::toSettings(QSettings *settings) const +void ClearCaseSettings::toSettings(QtcSettings *settings) const { - using FilesConstIt = QHash<QString, int>::ConstIterator; + using FilesConstIt = QHash<Key, int>::ConstIterator; - settings->beginGroup(QLatin1String(groupC)); - settings->setValue(QLatin1String(commandKeyC), ccCommand); - settings->setValue(QLatin1String(autoCheckOutKeyC), autoCheckOut); - settings->setValue(QLatin1String(noCommentKeyC), noComment); - settings->setValue(QLatin1String(keepFileUndoCheckoutKeyC), keepFileUndoCheckout); - settings->setValue(QLatin1String(timeOutKeyC), timeOutS); + settings->beginGroup(groupC); + settings->setValue(commandKeyC, ccCommand); + settings->setValue(autoCheckOutKeyC, autoCheckOut); + settings->setValue(noCommentKeyC, noComment); + settings->setValue(keepFileUndoCheckoutKeyC, keepFileUndoCheckout); + settings->setValue(timeOutKeyC, timeOutS); QString sDiffType; switch (diffType) { case ExternalDiff: sDiffType = QLatin1String("External"); break; default: sDiffType = QLatin1String("Graphical"); break; } - settings->setValue(QLatin1String(diffArgsKeyC), diffArgs); - settings->setValue(QLatin1String(diffTypeKeyC), sDiffType); - settings->setValue(QLatin1String(autoAssignActivityKeyC), autoAssignActivityName); - settings->setValue(QLatin1String(historyCountKeyC), historyCount); - settings->setValue(QLatin1String(disableIndexerKeyC), disableIndexer); - settings->setValue(QLatin1String(indexOnlyVOBsC), indexOnlyVOBs); - settings->beginGroup(QLatin1String(totalFilesKeyC)); + settings->setValue(diffArgsKeyC, diffArgs); + settings->setValue(diffTypeKeyC, sDiffType); + settings->setValue(autoAssignActivityKeyC, autoAssignActivityName); + settings->setValue(historyCountKeyC, historyCount); + settings->setValue(disableIndexerKeyC, disableIndexer); + settings->setValue(indexOnlyVOBsC, indexOnlyVOBs); + settings->beginGroup(totalFilesKeyC); const FilesConstIt fcend = totalFiles.constEnd(); for (FilesConstIt it = totalFiles.constBegin(); it != fcend; ++it) settings->setValue(it.key(), it.value()); diff --git a/src/plugins/clearcase/clearcasesettings.h b/src/plugins/clearcase/clearcasesettings.h index e65b2009ea5..344c31d6c9f 100644 --- a/src/plugins/clearcase/clearcasesettings.h +++ b/src/plugins/clearcase/clearcasesettings.h @@ -4,13 +4,11 @@ #pragma once #include <utils/filepath.h> +#include <utils/storekey.h> #include <QHash> -#include <QString> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace ClearCase::Internal { @@ -25,8 +23,8 @@ class ClearCaseSettings public: ClearCaseSettings(); - void fromSettings(QSettings *); - void toSettings(QSettings *) const; + void fromSettings(Utils::QtcSettings *); + void toSettings(Utils::QtcSettings *) const; bool equals(const ClearCaseSettings &s) const; @@ -40,7 +38,7 @@ public: DiffType diffType = GraphicalDiff; QString diffArgs; QString indexOnlyVOBs; - QHash<QString, int> totalFiles; + QHash<Utils::Key, int> totalFiles; bool autoAssignActivityName = true; bool autoCheckOut = true; bool noComment = false; diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp index 8476d1f04ac..cc9d837fd2e 100644 --- a/src/plugins/clearcase/clearcasesync.cpp +++ b/src/plugins/clearcase/clearcasesync.cpp @@ -118,10 +118,9 @@ void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QStrin ClearCasePlugin::setStatus(absFile, FileStatus::CheckedIn, true); } -void ClearCaseSync::updateTotalFilesCount(const QString &view, ClearCaseSettings settings, - const int processed) +void ClearCaseSync::updateTotalFilesCount(const Key &view, const int processed) { - settings = ClearCasePlugin::settings(); // Might have changed while task was running + ClearCaseSettings settings = ClearCasePlugin::settings(); settings.totalFiles[view] = processed; ClearCasePlugin::setSettings(settings); } @@ -138,7 +137,7 @@ void ClearCaseSync::updateStatusForNotManagedFiles(const QStringList &files) void ClearCaseSync::syncSnapshotView(QPromise<void> &promise, QStringList &files, const ClearCaseSettings &settings) { - const QString view = ClearCasePlugin::viewData().name; + const Key view = keyFromString(ClearCasePlugin::viewData().name); int totalFileCount = files.size(); const bool hot = (totalFileCount < 10); @@ -180,7 +179,7 @@ void ClearCaseSync::syncSnapshotView(QPromise<void> &promise, QStringList &files updateStatusForNotManagedFiles(files); promise.setProgressValue(totalFileCount + 1); if (!hot) - updateTotalFilesCount(view, settings, totalProcessed); + updateTotalFilesCount(view, totalProcessed); } } diff --git a/src/plugins/clearcase/clearcasesync.h b/src/plugins/clearcase/clearcasesync.h index 4b69985b0c7..7a303e6593b 100644 --- a/src/plugins/clearcase/clearcasesync.h +++ b/src/plugins/clearcase/clearcasesync.h @@ -5,6 +5,8 @@ #include "clearcaseplugin.h" +#include <utils/storekey.h> + QT_BEGIN_NAMESPACE class QDir; template <typename T> @@ -24,8 +26,7 @@ public: void invalidateStatus(const QDir &viewRootDir, const QStringList &files); void invalidateStatusAllFiles(); void processCleartoolLsLine(const QDir &viewRootDir, const QString &buffer); - void updateTotalFilesCount(const QString &view, ClearCaseSettings settings, - const int processed); + void updateTotalFilesCount(const Utils::Key &view, const int processed); void updateStatusForNotManagedFiles(const QStringList &files); void syncDynamicView(QPromise<void> &promise, const ClearCaseSettings &settings); diff --git a/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.qt b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.qt new file mode 100644 index 00000000000..776cb07f0b8 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.qt @@ -0,0 +1,3 @@ +Files taken from the CMake repository https://github.com/vitaut-archive/rstparser.git + +49e1e6626ba28357749acfe3bf07c4a19e5bc4ef diff --git a/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.rst b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.rst new file mode 100644 index 00000000000..1d481382226 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/README.rst @@ -0,0 +1,32 @@ +RSTParser +========= + +RSTParser is an open-source C++ library for parsing +`reStructuredText <http://docutils.sourceforge.net/rst.html>`__. + +License +------- + +Copyright (c) 2013, Victor Zverovich + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser-test.cc b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser-test.cc new file mode 100644 index 00000000000..70b9a6df337 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser-test.cc @@ -0,0 +1,248 @@ +/* + reStructuredText parser tests. + + Copyright (c) 2012, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <gtest/gtest.h> + +#include <stack> + +#ifdef _WIN32 +# include <crtdbg.h> +#endif + +#include "rstparser.h" + +namespace { + +class TestHandler : public rst::ContentHandler { + private: + std::stack<std::string> tags_; + std::string content_; + + public: + const std::string &content() const { return content_; } + + void StartBlock(rst::BlockType type) { + std::string tag; + switch (type) { + case rst::REFERENCE_LINK: + // not used, HandleReferenceLink is used instead + break; + case rst::H1: + tag = "h1"; + break; + case rst::H2: + tag = "h2"; + break; + case rst::H3: + tag = "h3"; + break; + case rst::H4: + tag = "h4"; + break; + case rst::H5: + tag = "h5"; + break; + case rst::CODE: + tag = "code"; + break; + case rst::PARAGRAPH: + tag = "p"; + break; + case rst::LINE_BLOCK: + tag = "lineblock"; + break; + case rst::BLOCK_QUOTE: + tag = "blockquote"; + break; + case rst::BULLET_LIST: + tag = "ul"; + break; + case rst::LIST_ITEM: + tag = "li"; + break; + case rst::LITERAL_BLOCK: + tag = "code"; + break; + } + content_ += "<" + tag + ">"; + tags_.push(tag); + } + + void EndBlock() { + content_ += "</" + tags_.top() + ">"; + tags_.pop(); + } + + void HandleText(const char *text, std::size_t size) { + content_.append(text, size); + } + + void HandleDirective(const std::string &type, const std::string &name) { + content_ += std::string("<div class=\"") + name + "\">" + type + "</div>"; + } + + void HandleReferenceLink(const std::string &type, const std::string &text) { + content_ += std::string("<a href=\"#") + type + "\">" + text + "</a>"; + } +}; + +std::string Parse(const char *s) { + TestHandler handler; + rst::Parser parser(&handler); + parser.Parse(s); + return handler.content(); +} +} + +TEST(ParserTest, HX) { + EXPECT_EQ("<h1>test</h1>", Parse("====\ntest\n====")); + EXPECT_EQ("<h2>test</h2>", Parse("test\n====")); + EXPECT_EQ("<h3>test</h3>", Parse("test\n----")); + EXPECT_EQ("<h4>test</h4>", Parse("test\n^^^^")); + EXPECT_EQ("<h5>test</h5>", Parse("test\n\"\"\"\"")); +} + +TEST(ParserTest, Paragraph) { + EXPECT_EQ("<p>test</p>", Parse("test")); + EXPECT_EQ("<p>test</p>", Parse("\ntest")); + EXPECT_EQ("<p>.</p>", Parse(".")); + EXPECT_EQ("<p>..test</p>", Parse("..test")); +} + +TEST(ParserTest, LineBlock) { + EXPECT_EQ("<lineblock>test</lineblock>", Parse("| test")); + EXPECT_EQ("<lineblock> abc\ndef</lineblock>", Parse("| abc\n| def")); +} + +TEST(ParserTest, BlockQuote) { + EXPECT_EQ("<blockquote>test</blockquote>", Parse(" test")); +} + +TEST(ParserTest, PreserveInnerSpace) { + EXPECT_EQ("<p>a b</p>", Parse("a b")); +} + +TEST(ParserTest, ReplaceWhitespace) { + EXPECT_EQ("<p>a b</p>", Parse("a\tb")); + EXPECT_EQ("<blockquote>a b</blockquote>", Parse(" a\tb")); + EXPECT_EQ("<p>a b</p>", Parse("a\vb")); +} + +TEST(ParserTest, StripTrailingSpace) { + EXPECT_EQ("<p>test</p>", Parse("test \t")); +} + +TEST(ParserTest, MultiLineBlock) { + EXPECT_EQ("<p>line 1\nline 2</p>", Parse("line 1\nline 2")); +} + +TEST(ParserTest, UnindentBlock) { + EXPECT_EQ("<blockquote>abc</blockquote><p>def</p>", Parse(" abc\ndef")); +} + +TEST(ParserTest, BulletList) { + EXPECT_EQ("<ul><li>item</li></ul>", Parse("* item")); + EXPECT_EQ("<ul><li>abc\ndef</li></ul>", Parse("* abc\n def")); +} + +TEST(ParserTest, Literal) { + EXPECT_EQ("<p>abc:</p><code>def</code>", Parse("abc::\n\n def")); + EXPECT_EQ("<code>abc\ndef</code>", Parse("::\n\n abc\n def")); + EXPECT_EQ("<p>abc\ndef</p>", Parse("::\n\nabc\ndef")); + EXPECT_EQ("<p>::\nabc\ndef</p>", Parse("::\nabc\ndef")); +} + +TEST(ParserTest, InlineCode) { + EXPECT_EQ("<p><code>code</code></p>", Parse("``code``")); + EXPECT_EQ("<p>`code``</p>", Parse("`code``")); + EXPECT_EQ("<p>some <code>code</code></p>", Parse("some ``code``")); + EXPECT_EQ("<p><code>code</code> some</p>", Parse("``code`` some")); + EXPECT_EQ("<p>some <code>code</code> and more</p>", Parse("some ``code`` and more")); +} + +TEST(ParserTest, Comment) { + EXPECT_EQ("", Parse("..")); + EXPECT_EQ("", Parse("..\n")); + EXPECT_EQ("", Parse(".. comment")); + EXPECT_EQ("", Parse(".. comment:")); +} + +TEST(ParserTest, Directive) { + EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::")); + EXPECT_EQ("<div class=\"name\">test</div>", Parse(".. test:: name")); + EXPECT_EQ("<div class=\"\">test</div>", Parse(".. test::")); + EXPECT_EQ("<div class=\"\">test</div>", Parse("..\ttest::")); + + EXPECT_EQ("<div class=\"to-text\">|from-text| replace</div>", Parse(".. |from-text| replace:: to-text")); + + std::string rst = +R"(.. code-block:: c++ + int main() { + if (false) + return 1; + return 0; + })"; + + std::string html = +R"(<div class="c++">code-block</div><blockquote>int main() { + if (false) + return 1; + return 0; +}</blockquote>)"; + + EXPECT_EQ(html, Parse(rst.c_str())); + + rst = +R"(.. note:: This is a cool + note. Such a cool note.)"; + + html = +R"(<div class="">note</div><blockquote>This is a cool + note. Such a cool note.</blockquote>)"; + + EXPECT_EQ(html, Parse(rst.c_str())); +} + +TEST(ParserTest, ReferenceLinks) { + EXPECT_EQ("<p><a href=\"#ref\">info</a></p>", Parse(":ref:`info`")); + EXPECT_EQ("<p>some <a href=\"#ref\">info</a></p>", Parse("some :ref:`info`")); + EXPECT_EQ("<p>some <a href=\"#ref\">info</a> and more</p>", Parse("some :ref:`info` and more")); + EXPECT_EQ("<p><a href=\"#ref\">info</a>.</p>", Parse(":ref:`info`.")); +} + + +int main(int argc, char **argv) { +#ifdef _WIN32 + // Disable message boxes on assertion failures. + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.cc b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.cc new file mode 100644 index 00000000000..8cf7af6fcb3 --- /dev/null +++ b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.cc @@ -0,0 +1,420 @@ +/* + A reStructuredText parser written in C++. + + Copyright (c) 2013, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "rstparser.h" + +#include <algorithm> +#include <cctype> +#include <cstring> + +namespace { +inline bool IsSpace(char c) { + switch (c) { + case ' ': case '\t': case '\v': case '\f': + return true; + } + return false; +} + +// Returns true if s ends with string end. +bool EndsWith(const std::string &s, const char *end) { + std::size_t size = s.size(), end_size = std::strlen(end); + return size >= end_size ? std::strcmp(&s[size - end_size], end) == 0 : false; +} +} + +rst::ContentHandler::~ContentHandler() {} + +void rst::Parser::SkipSpace() { + while (IsSpace(*ptr_)) + ++ptr_; +} + +std::string rst::Parser::ParseDirectiveType() { + const char *s = ptr_; + if (!std::isalnum(*s) && *s != '|') + return std::string(); + for (;;) { + ++s; + if (std::isalnum(*s)) + continue; + switch (*s) { + case '-': case '_': case '+': case ':': case '.': case '|': + if (std::isalnum(s[1]) || (*s == '|' && IsSpace(s[1]))) { + ++s; + continue; + } + // Fall through. + } + break; + } + std::string type; + if (s != ptr_) + type.assign(ptr_, s); + ptr_ = s; + return type; +} + +void rst::Parser::EnterBlock(rst::BlockType &prev_type, rst::BlockType type) { + if (type == prev_type) + return; + if (prev_type == LIST_ITEM) + handler_->EndBlock(); + if (type == LIST_ITEM) + handler_->StartBlock(BULLET_LIST); + prev_type = type; +} + +void rst::Parser::ParseBlock( + rst::BlockType type, rst::BlockType &prev_type, int indent) { + std::string text; + + struct InlineTags { + rst::BlockType type; + std::size_t pos {}; + std::string text; + std::string type_string; + }; + std::vector<InlineTags> inline_tags; + + bool have_h1 = false; + for (bool first = true; ; first = false) { + const char *line_start = ptr_; + if (!first) { + // Check indentation. + SkipSpace(); + const int new_indent = ptr_ - line_start; + if (new_indent < indent) + break; + // Restore the indent + if (new_indent > indent) + std::advance(ptr_, indent - new_indent); + + if (*ptr_ == '\n') { + ++ptr_; + break; // Empty line ends the block. + } + if (!*ptr_) + break; // End of input. + } + // Strip indentation. + line_start = ptr_; + + // Find the end of the line. + while (*ptr_ && *ptr_ != '\n') + ++ptr_; + + // Strip whitespace at the end of the line. + const char *end = ptr_; + while (end != line_start && IsSpace(end[-1])) + --end; + + // Copy text converting all whitespace characters to spaces. + text.reserve(end - line_start + 1); + if (!first && !have_h1) + text.push_back('\n'); + enum {TAB_WIDTH = 8}; + + // Used the sections mapping from https://docs.anaconda.com/restructuredtext/index.html + struct { + BlockType type; + int count = 0; + char c = 0; + } hx[] = { {H1, 0, '=' }, {H2, 0, '='}, {H3, 0, '-'}, {H4, 0, '^'}, {H5, 0, '\"'}}; + + for (const char *s = line_start; s != end; ++s) { + char c = *s; + if (c == '\t') { + text.append(" ", + TAB_WIDTH - ((indent + s - line_start) % TAB_WIDTH)); + } else if (IsSpace(c)) { + text.push_back(' '); + } else if (c == hx[0].c) { + ++hx[0].count; + ++hx[1].count; + } else if (c == hx[2].c) { + ++hx[2].count; + } else if (c == hx[3].c) { + ++hx[3].count; + } else if (c == hx[4].c) { + ++hx[4].count; + } else if (c == '`') { + std::string code_tag_text; + if (ParseCode(s, end - s, code_tag_text)) { + InlineTags code; + code.type = rst::CODE; + code.pos = text.size(); + code.text = code_tag_text; + inline_tags.push_back(code); + const int tag_size = 4; + s = s + code_tag_text.size() + tag_size - 1; + } else { + text.push_back(*s); + } + } else if (c == ':') { + std::string link_type; + std::string link_text; + if (ParseReferenceLink(s, end - s, link_type, link_text)) { + InlineTags link; + link.type = rst::REFERENCE_LINK; + link.pos = text.size(); + link.text = link_text; + link.type_string = link_type; + inline_tags.push_back(link); + const int tag_size = 4; + s = s + link_type.size() + link_text.size() + tag_size - 1; + } else { + text.push_back(*s); + } + } else { + text.push_back(*s); + } + } + + for (int i = 0; i < 5; ++i) { + if (hx[i].count > 0 && hx[i].count == end - line_start) { + // h1 and h2 have the same underline character + // only if there was one ontop then is h1 otherwise h2 + if (i == 0 && first) + have_h1 = true; + if ((i == 0 && !have_h1) || (i == 1 && have_h1)) + continue; + type = hx[i].type; + } + } + + if (*ptr_ == '\n') + ++ptr_; + } + + // Remove a trailing newline. + if (!text.empty() && *text.rbegin() == '\n') + text.resize(text.size() - 1); + + bool literal = type == PARAGRAPH && EndsWith(text, "::"); + if (!literal || text.size() != 2) { + std::size_t size = text.size(); + if (size == 0 && inline_tags.size() == 0) + return; + + if (literal) + --size; + EnterBlock(prev_type, type); + handler_->StartBlock(type); + + if (inline_tags.size() == 0) { + handler_->HandleText(text.c_str(), size); + } else { + std::size_t start = 0; + for (const InlineTags &in : inline_tags) { + if (in.pos > start) + handler_->HandleText(text.c_str() + start, in.pos - start); + if (in.type == rst::REFERENCE_LINK) { + handler_->HandleReferenceLink(in.type_string, in.text); + } else { + handler_->StartBlock(in.type); + handler_->HandleText(in.text.c_str(), in.text.size()); + handler_->EndBlock(); + } + start = in.pos; + } + + if (start < size) + handler_->HandleText(text.c_str() + start, size - start); + } + + handler_->EndBlock(); + } + if (literal) { + // Parse a literal block. + const char *line_start = ptr_; + SkipSpace(); + int new_indent = static_cast<int>(ptr_ - line_start); + if (new_indent > indent) + ParseBlock(LITERAL_BLOCK, prev_type, new_indent); + } +} + +void rst::Parser::ParseLineBlock(rst::BlockType &prev_type, int indent) { + std::string text; + for (bool first = true; ; first = false) { + const char *line_start = ptr_; + if (!first) { + // Check indentation. + SkipSpace(); + if (*ptr_ != '|' || !IsSpace(ptr_[1]) || ptr_ - line_start != indent) + break; + ptr_ += 2; + if (!*ptr_) + break; // End of input. + } + // Strip indentation. + line_start = ptr_; + + // Find the end of the line. + while (*ptr_ && *ptr_ != '\n') + ++ptr_; + if (*ptr_ == '\n') + ++ptr_; + text.append(line_start, ptr_); + } + + EnterBlock(prev_type, rst::LINE_BLOCK); + handler_->StartBlock(rst::LINE_BLOCK); + handler_->HandleText(text.c_str(), text.size()); + handler_->EndBlock(); +} + +bool rst::Parser::ParseCode(const char *s, std::size_t size, std::string &code) +{ + // It requires at least four ticks ``text`` + if (s[0] != '`' || s[1] != '`') + return false; + + if (size < 4) + return false; + + std::size_t start_pos = 2; + std::size_t end_pos = 0; + for (std::size_t i = start_pos; i < size - 1; ++i) { + if (s[i] == '`' && s[i + 1] == '`') { + end_pos = i; + break; + } + } + + if (end_pos == 0) + return false; + + code.assign(s + start_pos, end_pos - start_pos); + + return true; +} + +bool rst::Parser::ParseReferenceLink(const char *s, std::size_t size, std::string &type, std::string &text) +{ + // :type:`text` + if (size < 4) + return false; + + auto start_type_tag = s + 1; + auto end_type_tag = std::find(start_type_tag, s + size, ':'); + if (end_type_tag == s + size) + return false; + + type.assign(start_type_tag, end_type_tag - start_type_tag); + + if (*(end_type_tag + 1) != '`') + return false; + + auto start_text_tag = end_type_tag + 2; + auto end_text_tag = std::find(start_text_tag, s + size, '`'); + if (end_text_tag == s + size) + return false; + + text.assign(start_text_tag, end_text_tag - start_text_tag); + + return true; +} + +void rst::Parser::Parse(const char *s) { + BlockType prev_type = PARAGRAPH; + ptr_ = s; + while (*ptr_) { + // Skip whitespace and empty lines. + const char *line_start = ptr_; + SkipSpace(); + if (*ptr_ == '\n') { + ++ptr_; + continue; + } + switch (*ptr_) { + case '.': + if (ptr_[1] == '.') { + char c = ptr_[2]; + if (!IsSpace(c) && c != '\n' && c) + break; + // Parse a directive or a comment. + ptr_ += 2; + SkipSpace(); + std::string type = ParseDirectiveType(); + if (!type.empty() && ptr_[0] == ':' && ptr_[1] == ':') { + ptr_ += 2; + + const char* after_directive = ptr_; + + // Get the name of the directive + std::string name; + while (*ptr_ && *ptr_ != '\n') { + c = *ptr_++; + if (!IsSpace(c)) + name.push_back(c); + } + + // Special case for ".. note::" which can start directly after the :: + if (type == "note" && name.size() > 0) { + ptr_ = after_directive; + SkipSpace(); + handler_->HandleDirective(type, ""); + + ParseBlock(BLOCK_QUOTE, prev_type, 0); + break; + } + + handler_->HandleDirective(type, name); + } + // Skip everything till the end of the line. + while (*ptr_ && *ptr_ != '\n') + ++ptr_; + if (*ptr_ == '\n') + ++ptr_; + continue; + } + break; + case '*': case '+': case '-': + if (IsSpace(ptr_[1])) { + // Parse a bullet list item. + ptr_ += 2; + ParseBlock(LIST_ITEM, prev_type, static_cast<int>(ptr_ - line_start)); + continue; + } + break; + case '|': + if (IsSpace(ptr_[1])) { + // Parse a line block. + int indent = static_cast<int>(ptr_ - line_start); + ptr_ += 2; + ParseLineBlock(prev_type, indent); + continue; + } + break; + } + ParseBlock(std::isspace(line_start[0]) ? BLOCK_QUOTE : PARAGRAPH, + prev_type, static_cast<int>(ptr_ - line_start)); + } + EnterBlock(prev_type, PARAGRAPH); +} diff --git a/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.h b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.h new file mode 100644 index 00000000000..4fabdbf46ec --- /dev/null +++ b/src/plugins/cmakeprojectmanager/3rdparty/rstparser/rstparser.h @@ -0,0 +1,112 @@ +/* + A reStructuredText parser written in C++. + + Copyright (c) 2013, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RSTPARSER_H_ +#define RSTPARSER_H_ + +#include <memory> +#include <string> +#include <vector> + +namespace rst { + +enum BlockType { + H1, + H2, + H3, + H4, + H5, + CODE, + REFERENCE_LINK, + PARAGRAPH, + LINE_BLOCK, + BLOCK_QUOTE, + BULLET_LIST, + LIST_ITEM, + LITERAL_BLOCK +}; + +// Receive notification of the logical content of a document. +class ContentHandler { + public: + virtual ~ContentHandler(); + + // Receives notification of the beginning of a text block. + virtual void StartBlock(BlockType type) = 0; + + // Receives notification of the end of a text block. + virtual void EndBlock() = 0; + + // Receives notification of text. + virtual void HandleText(const char *text, std::size_t size) = 0; + + // Receives notification of a directive. + virtual void HandleDirective(const std::string &type, const std::string &name) = 0; + + // Receives notification of a link. + virtual void HandleReferenceLink(const std::string &type, const std::string &text) = 0; +}; + +// A parser for a subset of reStructuredText. +class Parser { + private: + ContentHandler *handler_; + const char *ptr_; + + // Skips whitespace. + void SkipSpace(); + + // Parses a directive type. + std::string ParseDirectiveType(); + + // Parses a paragraph. + void ParseParagraph(); + + // Changes the current block type sending notifications if necessary. + void EnterBlock(rst::BlockType &prev_type, rst::BlockType type); + + // Parses a block of text. + void ParseBlock(rst::BlockType type, rst::BlockType &prev_type, int indent); + + // Parses a line block. + void ParseLineBlock(rst::BlockType &prev_type, int indent); + + // Parses inline ``code`` + bool ParseCode(const char* s, std::size_t size, std::string &code); + + // Parses :reference:`link` + bool ParseReferenceLink(const char* s, std::size_t size, std::string &type, std::string &text); + + public: + explicit Parser(ContentHandler *h) : handler_(h), ptr_(0) {} + + // Parses a string containing reStructuredText and returns a document node. + void Parse(const char *s); +}; +} + +#endif // RSTPARSER_H_ diff --git a/src/plugins/cmakeprojectmanager/CMakeLists.txt b/src/plugins/cmakeprojectmanager/CMakeLists.txt index 3c8b75081f7..010f948f8a9 100644 --- a/src/plugins/cmakeprojectmanager/CMakeLists.txt +++ b/src/plugins/cmakeprojectmanager/CMakeLists.txt @@ -18,7 +18,7 @@ add_qtc_plugin(CMakeProjectManager cmakeformatter.cpp cmakeformatter.h cmakeindenter.cpp cmakeindenter.h cmakeinstallstep.cpp cmakeinstallstep.h - cmakekitinformation.cpp cmakekitinformation.h + cmakekitaspect.cpp cmakekitaspect.h cmakelocatorfilter.cpp cmakelocatorfilter.h cmakeparser.cpp cmakeparser.h cmakeprocess.cpp cmakeprocess.h @@ -46,4 +46,5 @@ add_qtc_plugin(CMakeProjectManager 3rdparty/cmake/cmListFileCache.cxx 3rdparty/cmake/cmListFileLexer.cxx 3rdparty/cmake/cmListFileCache.h + 3rdparty/rstparser/rstparser.cc 3rdparty/rstparser/rstparser.h ) diff --git a/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in b/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in index 4b11e3d7c2a..fc239c6a572 100644 --- a/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in +++ b/src/plugins/cmakeprojectmanager/CMakeProjectManager.json.in @@ -1,35 +1,35 @@ { - \"Name\" : \"CMakeProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CMakeProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"CMake support.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Build Systems", + "Description" : "CMake support.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-cmake\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>CMake Project file</comment>\", - \" <glob pattern=\'*.cmake\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/x-cmake-project\'>\", - \" <sub-class-of type=\'text/x-cmake\'/>\", - \" <comment>CMake Project file</comment>\", - \" <glob pattern=\'CMakeLists.txt\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-cmake'>", + " <sub-class-of type='text/plain'/>", + " <comment>CMake Project file</comment>", + " <glob pattern='*.cmake'/>", + " </mime-type>", + " <mime-type type='text/x-cmake-project'>", + " <sub-class-of type='text/x-cmake'/>", + " <comment>CMake Project file</comment>", + " <glob pattern='CMakeLists.txt'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/cmakeprojectmanager/builddirparameters.cpp b/src/plugins/cmakeprojectmanager/builddirparameters.cpp index 8b7680340a0..2a72521d1c5 100644 --- a/src/plugins/cmakeprojectmanager/builddirparameters.cpp +++ b/src/plugins/cmakeprojectmanager/builddirparameters.cpp @@ -5,10 +5,10 @@ #include "cmakebuildconfiguration.h" #include "cmakebuildsystem.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmaketoolmanager.h" -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> @@ -31,7 +31,7 @@ BuildDirParameters::BuildDirParameters(CMakeBuildSystem *buildSystem) expander = bc->macroExpander(); - const QStringList expandedArguments = Utils::transform(buildSystem->initialCMakeArguments(), + const QStringList expandedArguments = Utils::transform(bc->initialCMakeArguments.allValues(), [this](const QString &s) { return expander->expand(s); }); @@ -41,7 +41,7 @@ BuildDirParameters::BuildDirParameters(CMakeBuildSystem *buildSystem) [this](const QString &s) { return expander->expand(s); }); - additionalCMakeArguments = Utils::transform(buildSystem->additionalCMakeArguments(), + additionalCMakeArguments = Utils::transform(bc->additionalCMakeArguments(), [this](const QString &s) { return expander->expand(s); }); @@ -66,6 +66,8 @@ BuildDirParameters::BuildDirParameters(CMakeBuildSystem *buildSystem) environment.set("ICECC", "no"); environment.set("QTC_RUN", "1"); + environment.setFallback("CMAKE_COLOR_DIAGNOSTICS", "1"); + environment.setFallback("CLICOLOR_FORCE", "1"); cmakeToolId = CMakeKitAspect::cmakeToolId(k); } diff --git a/src/plugins/cmakeprojectmanager/cmakeabstractprocessstep.cpp b/src/plugins/cmakeprojectmanager/cmakeabstractprocessstep.cpp index 29b4425d697..49438122eb1 100644 --- a/src/plugins/cmakeprojectmanager/cmakeabstractprocessstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeabstractprocessstep.cpp @@ -3,7 +3,7 @@ #include "cmakeabstractprocessstep.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeprojectmanagertr.h" #include "cmaketool.h" diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 49879bac74f..ed81a8fea95 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -6,7 +6,7 @@ #include "cmakebuildstep.h" #include "cmakebuildsystem.h" #include "cmakeconfigitem.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" @@ -34,7 +34,7 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/environmentaspectwidget.h> #include <projectexplorer/environmentwidget.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/namedwidget.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> @@ -47,7 +47,7 @@ #include <qtsupport/baseqtversion.h> #include <qtsupport/qtbuildaspects.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/categorysortfiltermodel.h> @@ -65,7 +65,6 @@ #include <QCheckBox> #include <QDialog> #include <QDialogButtonBox> -#include <QDir> #include <QGridLayout> #include <QHeaderView> #include <QLoggingCategory> @@ -83,7 +82,6 @@ namespace CMakeProjectManager { static Q_LOGGING_CATEGORY(cmakeBuildConfigurationLog, "qtc.cmake.bc", QtWarningMsg); -const char CONFIGURATION_KEY[] = "CMake.Configuration"; const char DEVELOPMENT_TEAM_FLAG[] = "Ios:DevelopmentTeam:Flag"; const char PROVISIONING_PROFILE_FLAG[] = "Ios:ProvisioningProfile:Flag"; const char CMAKE_OSX_ARCHITECTURES_FLAG[] = "CMAKE_OSX_ARCHITECTURES:DefaultFlag"; @@ -101,7 +99,7 @@ namespace Internal { class CMakeBuildSettingsWidget : public NamedWidget { public: - CMakeBuildSettingsWidget(CMakeBuildSystem *bc); + explicit CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc); void setError(const QString &message); void setWarning(const QString &message); @@ -129,7 +127,7 @@ private: void updateConfigureDetailsWidgetsSummary( const QStringList &configurationArguments = QStringList()); - CMakeBuildSystem *m_buildSystem; + CMakeBuildConfiguration *m_buildConfig; QTreeView *m_configView; ConfigModel *m_configModel; CategorySortFilterModel *m_configFilterModel; @@ -166,16 +164,13 @@ static QModelIndex mapToSource(const QAbstractItemView *view, const QModelIndex return result; } -CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : +CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) : NamedWidget(Tr::tr("CMake")), - m_buildSystem(bs), + m_buildConfig(bc), m_configModel(new ConfigModel(this)), m_configFilterModel(new CategorySortFilterModel(this)), m_configTextFilterModel(new CategorySortFilterModel(this)) { - QTC_ASSERT(bs, return); - BuildConfiguration *bc = bs->buildConfiguration(); - m_configureDetailsWidget = new DetailsWidget; updateConfigureDetailsWidgetsSummary(); @@ -189,11 +184,11 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : m_configModel->flush(); // clear out config cache...; }); - auto buildTypeAspect = bc->aspect<BuildTypeAspect>(); - connect(buildTypeAspect, &BaseAspect::changed, this, [this, buildTypeAspect] { - if (!m_buildSystem->isMultiConfig()) { + connect(&m_buildConfig->buildTypeAspect, &BaseAspect::changed, this, [this] { + if (!m_buildConfig->cmakeBuildSystem()->isMultiConfig()) { CMakeConfig config; - config << CMakeConfigItem("CMAKE_BUILD_TYPE", buildTypeAspect->value().toUtf8()); + config << CMakeConfigItem("CMAKE_BUILD_TYPE", + m_buildConfig->buildTypeAspect().toUtf8()); m_configModel->setBatchEditConfiguration(config); } @@ -217,7 +212,9 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : m_kitConfiguration = new QPushButton(Tr::tr("Kit Configuration")); m_kitConfiguration->setToolTip(Tr::tr("Edit the current kit's CMake configuration.")); m_kitConfiguration->setFixedWidth(m_kitConfiguration->sizeHint().width()); - connect(m_kitConfiguration, &QPushButton::clicked, this, [this] { kitCMakeConfiguration(); }); + connect(m_kitConfiguration, &QPushButton::clicked, + this, &CMakeBuildSettingsWidget::kitCMakeConfiguration, + Qt::QueuedConnection); m_filterEdit = new FancyLineEdit; m_filterEdit->setPlaceholderText(Tr::tr("Filter")); @@ -247,7 +244,6 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : m_configView->setModel(m_configTextFilterModel); m_configView->setMinimumHeight(300); - m_configView->setUniformRowHeights(true); m_configView->setSortingEnabled(true); m_configView->sortByColumn(0, Qt::AscendingOrder); m_configView->header()->setSectionResizeMode(QHeaderView::Stretch); @@ -299,9 +295,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : m_batchEditButton->setToolTip(Tr::tr("Set or reset multiple values in the CMake configuration.")); m_showAdvancedCheckBox = new QCheckBox(Tr::tr("Advanced")); - - auto settings = CMakeSpecificSettings::instance(); - m_showAdvancedCheckBox->setChecked(settings->showAdvancedOptionsByDefault.value()); + m_showAdvancedCheckBox->setChecked(settings().showAdvancedOptionsByDefault()); connect(m_configView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this](const QItemSelection &, const QItemSelection &) { @@ -328,15 +322,14 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : } }; - auto configureEnvironmentAspectWidget - = bc->aspect<ConfigureEnvironmentAspect>()->createConfigWidget(); + auto configureEnvironmentAspectWidget = bc->configureEnv.createConfigWidget(); configureEnvironmentAspectWidget->setContentsMargins(0, 0, 0, 0); configureEnvironmentAspectWidget->layout()->setContentsMargins(0, 0, 0, 0); Column { Form { buildDirAspect, br, - bc->aspect<BuildTypeAspect>(), br, + bc->buildTypeAspect, br, qmlDebugAspect }, m_warningMessageLabel, @@ -347,8 +340,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : Column { cmakeConfiguration, Row { - bc->aspect<InitialCMakeArgumentsAspect>(), br, - bc->aspect<AdditionalCMakeOptionsAspect>() + bc->initialCMakeArguments, br, + bc->additionalCMakeOptions }, m_reconfigureButton, } @@ -364,10 +357,12 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : }.attachTo(this); updateAdvancedCheckBox(); - setError(m_buildSystem->error()); - setWarning(m_buildSystem->warning()); - connect(m_buildSystem, &BuildSystem::parsingStarted, this, [this] { + CMakeBuildSystem *bs = m_buildConfig->cmakeBuildSystem(); + setError(bs->error()); + setWarning(bs->warning()); + + connect(bs, &BuildSystem::parsingStarted, this, [this] { updateButtonState(); m_configView->setEnabled(false); m_showProgressTimer.start(); @@ -375,28 +370,26 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : m_configModel->setMacroExpander(bc->macroExpander()); - if (m_buildSystem->isParsing()) + if (bs->isParsing()) m_showProgressTimer.start(); else { - m_configModel->setConfiguration(m_buildSystem->configurationFromCMake()); + m_configModel->setConfiguration(bs->configurationFromCMake()); m_configModel->setInitialParametersConfiguration( - m_buildSystem->initialCMakeConfiguration()); + m_buildConfig->initialCMakeArguments.cmakeConfiguration()); } - connect(m_buildSystem, &BuildSystem::parsingFinished, this, [this] { - const CMakeConfig config = m_buildSystem->configurationFromCMake(); - auto qmlDebugAspect = m_buildSystem->buildConfiguration() - ->aspect<QtSupport::QmlDebuggingAspect>(); - const TriState qmlDebugSetting = qmlDebugAspect->value(); + connect(bs, &BuildSystem::parsingFinished, this, [this, bs] { + const CMakeConfig config = bs->configurationFromCMake(); + const TriState qmlDebugSetting = m_buildConfig->qmlDebugging(); bool qmlDebugConfig = CMakeBuildConfiguration::hasQmlDebugging(config); if ((qmlDebugSetting == TriState::Enabled && !qmlDebugConfig) || (qmlDebugSetting == TriState::Disabled && qmlDebugConfig)) { - qmlDebugAspect->setValue(TriState::Default); + m_buildConfig->qmlDebugging.setValue(TriState::Default); } m_configModel->setConfiguration(config); m_configModel->setInitialParametersConfiguration( - m_buildSystem->initialCMakeConfiguration()); - m_buildSystem->filterConfigArgumentsFromAdditionalCMakeArguments(); + m_buildConfig->initialCMakeArguments.cmakeConfiguration()); + m_buildConfig->filterConfigArgumentsFromAdditionalCMakeArguments(); updateFromKit(); m_configView->setEnabled(true); updateButtonState(); @@ -405,11 +398,11 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : updateConfigurationStateSelection(); }); - connect(m_buildSystem, &CMakeBuildSystem::configurationCleared, this, [this] { + connect(bs, &CMakeBuildSystem::configurationCleared, this, [this] { updateConfigurationStateSelection(); }); - connect(m_buildSystem, &CMakeBuildSystem::errorOccurred, this, [this] { + connect(bs, &CMakeBuildSystem::errorOccurred, this, [this] { m_showProgressTimer.stop(); m_progressIndicator->hide(); updateConfigurationStateSelection(); @@ -420,10 +413,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : connect(m_configModel, &QAbstractItemModel::modelReset, this, &CMakeBuildSettingsWidget::updateButtonState); - connect(m_buildSystem->cmakeBuildConfiguration(), - &CMakeBuildConfiguration::signingFlagsChanged, - this, - &CMakeBuildSettingsWidget::updateButtonState); + connect(m_buildConfig, &CMakeBuildConfiguration::signingFlagsChanged, + this, &CMakeBuildSettingsWidget::updateButtonState); connect(m_showAdvancedCheckBox, &QCheckBox::stateChanged, this, &CMakeBuildSettingsWidget::updateAdvancedCheckBox); @@ -440,15 +431,15 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : connect(m_resetButton, &QPushButton::clicked, this, [this] { m_configModel->resetAllChanges(isInitialConfiguration()); }); - connect(m_reconfigureButton, &QPushButton::clicked, this, [this] { - if (!m_buildSystem->isParsing()) { + connect(m_reconfigureButton, &QPushButton::clicked, this, [this, bs] { + if (!bs->isParsing()) { if (isInitialConfiguration()) { reconfigureWithInitialParameters(); } else { - m_buildSystem->runCMakeWithExtraArguments(); + bs->runCMakeWithExtraArguments(); } } else { - m_buildSystem->stopCMakeRun(); + bs->stopCMakeRun(); m_reconfigureButton->setEnabled(false); } }); @@ -485,16 +476,16 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : connect(m_batchEditButton, &QAbstractButton::clicked, this, &CMakeBuildSettingsWidget::batchEditConfiguration); - connect(m_buildSystem, &CMakeBuildSystem::errorOccurred, + connect(bs, &CMakeBuildSystem::errorOccurred, this, &CMakeBuildSettingsWidget::setError); - connect(m_buildSystem, &CMakeBuildSystem::warningOccurred, + connect(bs, &CMakeBuildSystem::warningOccurred, this, &CMakeBuildSettingsWidget::setWarning); - connect(m_buildSystem, &CMakeBuildSystem::configurationChanged, + connect(bs, &CMakeBuildSystem::configurationChanged, m_configModel, &ConfigModel::setBatchEditConfiguration); updateFromKit(); - connect(m_buildSystem->target(), &Target::kitChanged, + connect(m_buildConfig->target(), &Target::kitChanged, this, &CMakeBuildSettingsWidget::updateFromKit); connect(bc, &CMakeBuildConfiguration::enabledChanged, this, [this, bc] { if (bc->isEnabled()) @@ -504,20 +495,20 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : updateInitialCMakeArguments(); }); - connect(m_buildSystem->target()->project(), &Project::aboutToSaveSettings, this, [this] { + connect(m_buildConfig->target()->project(), &Project::aboutToSaveSettings, this, [this] { updateInitialCMakeArguments(); }); - connect(bc->aspect<InitialCMakeArgumentsAspect>(), + connect(&bc->initialCMakeArguments, &Utils::BaseAspect::labelLinkActivated, this, [this](const QString &) { - const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildSystem->kit()); + const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildConfig->kit()); CMakeTool::openCMakeHelpUrl(tool, "%1/manual/cmake.1.html#options"); }); - connect(bc->aspect<AdditionalCMakeOptionsAspect>(), + connect(&bc->additionalCMakeOptions, &Utils::BaseAspect::labelLinkActivated, this, [this](const QString &) { - const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildSystem->kit()); + const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildConfig->kit()); CMakeTool::openCMakeHelpUrl(tool, "%1/manual/cmake.1.html#options"); }); @@ -545,14 +536,14 @@ void CMakeBuildSettingsWidget::batchEditConfiguration() "<type> can have one of the following values: FILEPATH, PATH, BOOL, INTERNAL, or STRING.<br/>" "To unset a variable, use -U<variable>.<br/>")); connect(label, &QLabel::linkActivated, this, [this](const QString &) { - const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildSystem->target()->kit()); + const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildConfig->target()->kit()); CMakeTool::openCMakeHelpUrl(tool, "%1/manual/cmake-variables.7.html"); }); editor->setMinimumSize(800, 200); auto chooser = new Utils::VariableChooser(dialog); chooser->addSupportedWidget(editor); - chooser->addMacroExpanderProvider([this] { return m_buildSystem->buildConfiguration()->macroExpander(); }); + chooser->addMacroExpanderProvider([this] { return m_buildConfig->macroExpander(); }); auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); @@ -563,7 +554,7 @@ void CMakeBuildSettingsWidget::batchEditConfiguration() connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject); connect(dialog, &QDialog::accepted, this, [=]{ - const auto expander = m_buildSystem->buildConfiguration()->macroExpander(); + const auto expander = m_buildConfig->macroExpander(); const QStringList lines = editor->toPlainText().split('\n', Qt::SkipEmptyParts); const QStringList expandedLines = Utils::transform(lines, @@ -581,7 +572,7 @@ void CMakeBuildSettingsWidget::batchEditConfiguration() }); editor->setPlainText( - m_buildSystem->configurationChangesArguments(isInitialConfiguration()) + m_buildConfig->cmakeBuildSystem()->configurationChangesArguments(isInitialConfiguration()) .join('\n')); dialog->show(); @@ -589,34 +580,32 @@ void CMakeBuildSettingsWidget::batchEditConfiguration() void CMakeBuildSettingsWidget::reconfigureWithInitialParameters() { - auto settings = CMakeSpecificSettings::instance(); QMessageBox::StandardButton reply = CheckableMessageBox::question( Core::ICore::dialogParent(), Tr::tr("Re-configure with Initial Parameters"), Tr::tr("Clear CMake configuration and configure with initial parameters?"), - settings->askBeforeReConfigureInitialParams.askAgainCheckableDecider(), + settings().askBeforeReConfigureInitialParams.askAgainCheckableDecider(), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - settings->writeSettings(Core::ICore::settings()); + settings().writeSettings(); - if (reply != QMessageBox::Yes) { + if (reply != QMessageBox::Yes) return; - } - m_buildSystem->clearCMakeCache(); + m_buildConfig->cmakeBuildSystem()->clearCMakeCache(); updateInitialCMakeArguments(); if (ProjectExplorerPlugin::saveModifiedFiles()) - m_buildSystem->runCMake(); + m_buildConfig->cmakeBuildSystem()->runCMake(); } void CMakeBuildSettingsWidget::updateInitialCMakeArguments() { - CMakeConfig initialList = m_buildSystem->initialCMakeConfiguration(); + CMakeConfig initialList = m_buildConfig->initialCMakeArguments.cmakeConfiguration(); - for (const CMakeConfigItem &ci : m_buildSystem->configurationChanges()) { + for (const CMakeConfigItem &ci : m_buildConfig->cmakeBuildSystem()->configurationChanges()) { if (!ci.isInitial) continue; auto it = std::find_if(initialList.begin(), @@ -633,19 +622,18 @@ void CMakeBuildSettingsWidget::updateInitialCMakeArguments() } } - auto bc = m_buildSystem->buildConfiguration(); - bc->aspect<InitialCMakeArgumentsAspect>()->setCMakeConfiguration(initialList); + m_buildConfig->initialCMakeArguments.setCMakeConfiguration(initialList); // value() will contain only the unknown arguments (the non -D/-U arguments) // As the user would expect to have e.g. "--preset" from "Initial Configuration" // to "Current Configuration" as additional parameters - m_buildSystem->setAdditionalCMakeArguments(ProcessArgs::splitArgs( - bc->aspect<InitialCMakeArgumentsAspect>()->value(), HostOsInfo::hostOs())); + m_buildConfig->setAdditionalCMakeArguments(ProcessArgs::splitArgs( + m_buildConfig->initialCMakeArguments(), HostOsInfo::hostOs())); } void CMakeBuildSettingsWidget::kitCMakeConfiguration() { - m_buildSystem->kit()->blockNotification(); + m_buildConfig->kit()->blockNotification(); auto dialog = new QDialog(this); dialog->setWindowTitle(Tr::tr("Kit CMake Configuration")); @@ -653,23 +641,19 @@ void CMakeBuildSettingsWidget::kitCMakeConfiguration() dialog->setModal(true); dialog->setSizeGripEnabled(true); connect(dialog, &QDialog::finished, this, [this] { - m_buildSystem->kit()->unblockNotification(); + m_buildConfig->kit()->unblockNotification(); }); - CMakeKitAspect kitAspect; - CMakeGeneratorKitAspect generatorAspect; - CMakeConfigurationKitAspect configurationKitAspect; - Layouting::Grid grid; - KitAspectWidget *widget = kitAspect.createConfigWidget(m_buildSystem->kit()); + KitAspect *widget = CMakeKitAspect::createKitAspect(m_buildConfig->kit()); widget->setParent(dialog); - widget->addToLayoutWithLabel(grid, dialog); - widget = generatorAspect.createConfigWidget(m_buildSystem->kit()); + widget->addToLayout(grid); + widget = CMakeGeneratorKitAspect::createKitAspect(m_buildConfig->kit()); widget->setParent(dialog); - widget->addToLayoutWithLabel(grid, dialog); - widget = configurationKitAspect.createConfigWidget(m_buildSystem->kit()); + widget->addToLayout(grid); + widget = CMakeConfigurationKitAspect::createKitAspect(m_buildConfig->kit()); widget->setParent(dialog); - widget->addToLayoutWithLabel(grid, dialog); + widget->addToLayout(grid); grid.attachTo(dialog); auto layout = qobject_cast<QGridLayout *>(dialog->layout()); @@ -693,13 +677,12 @@ void CMakeBuildSettingsWidget::updateConfigureDetailsWidgetsSummary( ProjectExplorer::ProcessParameters params; CommandLine cmd; - const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildSystem->kit()); + const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildConfig->kit()); cmd.setExecutable(tool ? tool->cmakeExecutable() : "cmake"); - const BuildConfiguration *bc = m_buildSystem->buildConfiguration(); - const FilePath buildDirectory = bc ? bc->buildDirectory() : "."; + const FilePath buildDirectory = m_buildConfig->buildDirectory(); - cmd.addArgs({"-S", m_buildSystem->projectDirectory().path()}); + cmd.addArgs({"-S", m_buildConfig->project()->projectDirectory().path()}); cmd.addArgs({"-B", buildDirectory.path()}); cmd.addArgs(configurationArguments); @@ -710,7 +693,7 @@ void CMakeBuildSettingsWidget::updateConfigureDetailsWidgetsSummary( void CMakeBuildSettingsWidget::setError(const QString &message) { - m_buildSystem->buildConfiguration()->buildDirectoryAspect()->setProblem(message); + m_buildConfig->buildDirectoryAspect()->setProblem(message); } void CMakeBuildSettingsWidget::setWarning(const QString &message) @@ -722,7 +705,7 @@ void CMakeBuildSettingsWidget::setWarning(const QString &message) void CMakeBuildSettingsWidget::updateButtonState() { - const bool isParsing = m_buildSystem->isParsing(); + const bool isParsing = m_buildConfig->cmakeBuildSystem()->isParsing(); // Update extra data in buildconfiguration const QList<ConfigModel::DataItem> changes = m_configModel->configurationForCMake(); @@ -763,12 +746,11 @@ void CMakeBuildSettingsWidget::updateButtonState() const bool isInitial = isInitialConfiguration(); m_resetButton->setEnabled(m_configModel->hasChanges(isInitial) && !isParsing); - BuildConfiguration *bc = m_buildSystem->buildConfiguration(); - bc->aspect<InitialCMakeArgumentsAspect>()->setVisible(isInitialConfiguration()); - bc->aspect<AdditionalCMakeOptionsAspect>()->setVisible(!isInitialConfiguration()); + m_buildConfig->initialCMakeArguments.setVisible(isInitialConfiguration()); + m_buildConfig->additionalCMakeOptions.setVisible(!isInitialConfiguration()); - bc->aspect<InitialCMakeArgumentsAspect>()->setEnabled(!isParsing); - bc->aspect<AdditionalCMakeOptionsAspect>()->setEnabled(!isParsing); + m_buildConfig->initialCMakeArguments.setEnabled(!isParsing); + m_buildConfig->additionalCMakeOptions.setEnabled(!isParsing); // Update label and text boldness of the reconfigure button QFont reconfigureButtonFont = m_reconfigureButton->font(); @@ -787,11 +769,11 @@ void CMakeBuildSettingsWidget::updateButtonState() } m_reconfigureButton->setFont(reconfigureButtonFont); - m_buildSystem->setConfigurationChanges(configChanges); + m_buildConfig->cmakeBuildSystem()->setConfigurationChanges(configChanges); // Update the tooltip with the changes - const QStringList configurationArguments = m_buildSystem->configurationChangesArguments( - isInitialConfiguration()); + const QStringList configurationArguments = + m_buildConfig->cmakeBuildSystem()->configurationChangesArguments(isInitialConfiguration()); m_reconfigureButton->setToolTip(configurationArguments.join('\n')); updateConfigureDetailsWidgetsSummary(configurationArguments); } @@ -811,7 +793,7 @@ void CMakeBuildSettingsWidget::updateAdvancedCheckBox() void CMakeBuildSettingsWidget::updateFromKit() { - const Kit *k = m_buildSystem->kit(); + const Kit *k = m_buildConfig->kit(); CMakeConfig config = CMakeConfigurationKitAspect::configuration(k); config.append(CMakeGeneratorKitAspect::generatorCMakeConfig(k)); @@ -826,9 +808,8 @@ void CMakeBuildSettingsWidget::updateFromKit() // Then the additional parameters const QStringList additionalKitCMake = ProcessArgs::splitArgs( CMakeConfigurationKitAspect::additionalConfiguration(k), HostOsInfo::hostOs()); - const QStringList additionalInitialCMake = ProcessArgs::splitArgs( - m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->value(), - HostOsInfo::hostOs()); + const QStringList additionalInitialCMake = + ProcessArgs::splitArgs(m_buildConfig->initialCMakeArguments(), HostOsInfo::hostOs()); QStringList mergedArgumentList; std::set_union(additionalInitialCMake.begin(), @@ -836,8 +817,7 @@ void CMakeBuildSettingsWidget::updateFromKit() additionalKitCMake.begin(), additionalKitCMake.end(), std::back_inserter(mergedArgumentList)); - m_buildSystem->buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->setValue( - ProcessArgs::joinArgs(mergedArgumentList)); + m_buildConfig->initialCMakeArguments.setValue(ProcessArgs::joinArgs(mergedArgumentList)); } void CMakeBuildSettingsWidget::updateConfigurationStateIndex(int index) @@ -856,13 +836,12 @@ void CMakeBuildSettingsWidget::updateConfigurationStateIndex(int index) CMakeConfig CMakeBuildSettingsWidget::getQmlDebugCxxFlags() { - const auto aspect = m_buildSystem->buildConfiguration()->aspect<QtSupport::QmlDebuggingAspect>(); - const TriState qmlDebuggingState = aspect->value(); + const TriState qmlDebuggingState = m_buildConfig->qmlDebugging(); if (qmlDebuggingState == TriState::Default) // don't touch anything return {}; - const bool enable = aspect->value() == TriState::Enabled; + const bool enable = m_buildConfig->qmlDebugging() == TriState::Enabled; - const CMakeConfig configList = m_buildSystem->configurationFromCMake(); + const CMakeConfig configList = m_buildConfig->cmakeBuildSystem()->configurationFromCMake(); const QByteArrayList cxxFlagsPrev{"CMAKE_CXX_FLAGS", "CMAKE_CXX_FLAGS_DEBUG", "CMAKE_CXX_FLAGS_RELWITHDEBINFO", @@ -873,7 +852,7 @@ CMakeConfig CMakeBuildSettingsWidget::getQmlDebugCxxFlags() CMakeConfig changedConfig; if (enable) { - const FilePath cmakeCache = m_buildSystem->cmakeBuildConfiguration()->buildDirectory().pathAppended("CMakeCache.txt"); + const FilePath cmakeCache = m_buildConfig->buildDirectory().pathAppended("CMakeCache.txt"); // Only modify the CMAKE_CXX_FLAGS variable if the project was previously configured // otherwise CMAKE_CXX_FLAGS_INIT will take care of setting the qmlDebug define @@ -909,10 +888,10 @@ CMakeConfig CMakeBuildSettingsWidget::getQmlDebugCxxFlags() CMakeConfig CMakeBuildSettingsWidget::getSigningFlagsChanges() { - const CMakeConfig flags = m_buildSystem->cmakeBuildConfiguration()->signingFlags(); + const CMakeConfig flags = m_buildConfig->signingFlags(); if (flags.isEmpty()) return {}; - const CMakeConfig configList = m_buildSystem->configurationFromCMake(); + const CMakeConfig configList = m_buildConfig->cmakeBuildSystem()->configurationFromCMake(); if (configList.isEmpty()) { // we don't have any configuration --> initial configuration takes care of this itself return {}; @@ -956,8 +935,7 @@ void CMakeBuildSettingsWidget::updateSelection() void CMakeBuildSettingsWidget::updateConfigurationStateSelection() { const bool hasReplyFile - = FileApiParser::scanForCMakeReplyFile( - m_buildSystem->buildConfiguration()->buildDirectory()).exists(); + = FileApiParser::scanForCMakeReplyFile(m_buildConfig->buildDirectory()).exists(); const int switchToIndex = hasReplyFile ? 1 : 0; if (m_configurationStates->currentIndex() != switchToIndex) @@ -1035,7 +1013,7 @@ bool CMakeBuildSettingsWidget::eventFilter(QObject *target, QEvent *event) connect(help, &QAction::triggered, this, [=] { const CMakeConfigItem item = ConfigModel::dataItemFromIndex(idx).toCMakeConfigItem(); - const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildSystem->target()->kit()); + const CMakeTool *tool = CMakeKitAspect::cmakeTool(m_buildConfig->target()->kit()); const QString linkUrl = "%1/variable/" + QString::fromUtf8(item.key) + ".html"; CMakeTool::openCMakeHelpUrl(tool, linkUrl); }); @@ -1088,8 +1066,7 @@ bool CMakeBuildSettingsWidget::eventFilter(QObject *target, QEvent *event) const QStringList variableList = Utils::transform(validIndexes, [this](const QModelIndex &index) { return ConfigModel::dataItemFromIndex(index).toCMakeConfigItem().toArgument( - isInitialConfiguration() ? nullptr - : m_buildSystem->buildConfiguration()->macroExpander()); + isInitialConfiguration() ? nullptr : m_buildConfig->macroExpander()); }); setClipboardAndSelection(variableList.join('\n')); @@ -1121,7 +1098,7 @@ static bool isWindowsARM64(const Kit *k) && targetAbi.wordWidth() == 64; } -static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildType) +static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString &buildType) { // Generator: CMakeTool *tool = CMakeKitAspect::cmakeTool(k); @@ -1135,7 +1112,7 @@ static CommandLine defaultInitialCMakeCommand(const Kit *k, const QString buildT cmd.addArg("-DCMAKE_BUILD_TYPE:STRING=" + buildType); // Package manager auto setup - if (Internal::CMakeSpecificSettings::instance()->packageManagerAutoSetup.value()) { + if (settings().packageManagerAutoSetup()) { cmd.addArg(QString("-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=" "%{BuildConfig:BuildDirectory:NativeFilePath}/%1/auto-setup.cmake") .arg(Constants::PACKAGE_MANAGER_DIR)); @@ -1366,13 +1343,15 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) { m_buildSystem = new CMakeBuildSystem(this); - const auto buildDirAspect = aspect<BuildDirectoryAspect>(); - buildDirAspect->setValueAcceptor( + buildDirectoryAspect()->setValueAcceptor( [](const QString &oldDir, const QString &newDir) -> std::optional<QString> { if (oldDir.isEmpty()) return newDir; - if (QDir(oldDir).exists("CMakeCache.txt") && !QDir(newDir).exists("CMakeCache.txt")) { + const FilePath oldDirCMakeCache = FilePath::fromUserInput(oldDir).pathAppended("CMakeCache.txt"); + const FilePath newDirCMakeCache = FilePath::fromUserInput(newDir).pathAppended("CMakeCache.txt"); + + if (oldDirCMakeCache.exists() && !newDirCMakeCache.exists()) { if (QMessageBox::information( Core::ICore::dialogParent(), Tr::tr("Changing Build Directory"), @@ -1389,11 +1368,20 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) return newDir; }); - auto initialCMakeArgumentsAspect = addAspect<InitialCMakeArgumentsAspect>(); - initialCMakeArgumentsAspect->setMacroExpanderProvider([this] { return macroExpander(); }); + // Will not be displayed, only persisted + sourceDirectory.setSettingsKey("CMake.Source.Directory"); - auto additionalCMakeArgumentsAspect = addAspect<AdditionalCMakeOptionsAspect>(); - additionalCMakeArgumentsAspect->setMacroExpanderProvider([this] { return macroExpander(); }); + buildTypeAspect.setSettingsKey(CMAKE_BUILD_TYPE); + buildTypeAspect.setLabelText(Tr::tr("Build type:")); + buildTypeAspect.setDisplayStyle(StringAspect::LineEditDisplay); + buildTypeAspect.setDefaultValue("Unknown"); + + initialCMakeArguments.setMacroExpanderProvider([this] { return macroExpander(); }); + + additionalCMakeOptions.setSettingsKey("CMake.Additional.Options"); + additionalCMakeOptions.setLabelText(Tr::tr("Additional CMake <a href=\"options\">options</a>:")); + additionalCMakeOptions.setDisplayStyle(StringAspect::LineEditDisplay); + additionalCMakeOptions.setMacroExpanderProvider([this] { return macroExpander(); }); macroExpander()->registerVariable(DEVELOPMENT_TEAM_FLAG, Tr::tr("The CMake flag for the development team"), @@ -1437,25 +1425,17 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) return QLatin1String(); }); - addAspect<SourceDirectoryAspect>(); - addAspect<BuildTypeAspect>(); - addAspect<QtSupport::QmlDebuggingAspect>(this); - - addAspect<ConfigureEnvironmentAspect>(target); + qmlDebugging.setBuildConfiguration(this); setInitialBuildAndCleanSteps(target); setInitializer([this, target](const BuildInfo &info) { const Kit *k = target->kit(); const QtSupport::QtVersion *qt = QtSupport::QtKitAspect::qtVersion(k); - const QVariantMap extraInfoMap = info.extraInfo.value<QVariantMap>(); + const Store extraInfoMap = storeFromVariant(info.extraInfo); const QString buildType = extraInfoMap.contains(CMAKE_BUILD_TYPE) ? extraInfoMap.value(CMAKE_BUILD_TYPE).toString() : info.typeName; - const TriState qmlDebugging = extraInfoMap.contains(Constants::QML_DEBUG_SETTING) - ? TriState::fromVariant( - extraInfoMap.value(Constants::QML_DEBUG_SETTING)) - : TriState::Default; CommandLine cmd = defaultInitialCMakeCommand(k, buildType); m_buildSystem->setIsMultiConfig(CMakeGeneratorKitAspect::isMultiConfigGenerator(k)); @@ -1547,15 +1527,17 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) } if (extraInfoMap.contains(Constants::CMAKE_HOME_DIR)) - setSourceDirectory(FilePath::fromVariant(extraInfoMap.value(Constants::CMAKE_HOME_DIR))); + sourceDirectory.setValue(FilePath::fromVariant(extraInfoMap.value(Constants::CMAKE_HOME_DIR))); - aspect<QtSupport::QmlDebuggingAspect>()->setValue(qmlDebugging); + qmlDebugging.setValue(extraInfoMap.contains(Constants::QML_DEBUG_SETTING) + ? TriState::fromVariant(extraInfoMap.value(Constants::QML_DEBUG_SETTING)) + : TriState::Default); if (qt && qt->isQmlDebuggingSupported()) cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}"); CMakeProject *cmakeProject = static_cast<CMakeProject *>(target->project()); - aspect<ConfigureEnvironmentAspect>()->setUserEnvironmentChanges( + configureEnv.setUserEnvironmentChanges( getEnvironmentItemsFromCMakeConfigurePreset(cmakeProject, k)); QStringList initialCMakeArguments = cmd.splitArguments(); @@ -1564,8 +1546,8 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) k, configureEnvironment(), info.buildDirectory); - m_buildSystem->setInitialCMakeArguments(initialCMakeArguments); - m_buildSystem->setCMakeBuildType(buildType); + setInitialCMakeArguments(initialCMakeArguments); + setCMakeBuildType(buildType); setBuildPresetToBuildSteps(target); }); @@ -1576,53 +1558,13 @@ CMakeBuildConfiguration::~CMakeBuildConfiguration() delete m_buildSystem; } -QVariantMap CMakeBuildConfiguration::toMap() const -{ - QVariantMap map(BuildConfiguration::toMap()); - return map; -} - -bool CMakeBuildConfiguration::fromMap(const QVariantMap &map) -{ - if (!BuildConfiguration::fromMap(map)) - return false; - - const CMakeConfig conf - = Utils::filtered(Utils::transform(map.value(QLatin1String(CONFIGURATION_KEY)).toStringList(), - [](const QString &v) { return CMakeConfigItem::fromString(v); }), - [](const CMakeConfigItem &c) { return !c.isNull(); }); - - // TODO: Upgrade from Qt Creator < 4.13: Remove when no longer supported! - const QString buildTypeName = [this] { - switch (buildType()) { - case Debug: - return QString("Debug"); - case Profile: - return QString("RelWithDebInfo"); - case Release: - return QString("Release"); - case Unknown: - default: - return QString(""); - } - }(); - if (m_buildSystem->initialCMakeArguments().isEmpty()) { - CommandLine cmd = defaultInitialCMakeCommand(kit(), buildTypeName); - for (const CMakeConfigItem &item : conf) - cmd.addArg(item.toArgument(macroExpander())); - m_buildSystem->setInitialCMakeArguments(cmd.splitArguments()); - } - - return true; -} - FilePath CMakeBuildConfiguration::shadowBuildDirectory(const FilePath &projectFilePath, const Kit *k, const QString &bcName, BuildConfiguration::BuildType buildType) { if (projectFilePath.isEmpty()) - return FilePath(); + return {}; const QString projectName = projectFilePath.parentDir().fileName(); const FilePath projectDir = Project::projectDirectory(projectFilePath); @@ -1693,14 +1635,9 @@ QStringList CMakeBuildSystem::configurationChangesArguments(bool initialParamete return Utils::transform(filteredInitials, &CMakeConfigItem::toArgument); } -QStringList CMakeBuildSystem::initialCMakeArguments() const -{ - return buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->allValues(); -} - CMakeConfig CMakeBuildSystem::initialCMakeConfiguration() const { - return buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->cmakeConfiguration(); + return cmakeBuildConfiguration()->initialCMakeArguments.cmakeConfiguration(); } void CMakeBuildSystem::setConfigurationFromCMake(const CMakeConfig &config) @@ -1734,47 +1671,44 @@ void CMakeBuildSystem::clearError(ForceEnabledChanged fec) } } -void CMakeBuildSystem::setInitialCMakeArguments(const QStringList &args) +void CMakeBuildConfiguration::setInitialCMakeArguments(const QStringList &args) { QStringList additionalArguments; - buildConfiguration()->aspect<InitialCMakeArgumentsAspect>()->setAllValues(args.join('\n'), additionalArguments); + initialCMakeArguments.setAllValues(args.join('\n'), additionalArguments); // Set the unknown additional arguments also for the "Current Configuration" setAdditionalCMakeArguments(additionalArguments); } -QStringList CMakeBuildSystem::additionalCMakeArguments() const +QStringList CMakeBuildConfiguration::additionalCMakeArguments() const { - return ProcessArgs::splitArgs(buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(), - HostOsInfo::hostOs()); + return ProcessArgs::splitArgs(additionalCMakeOptions(), HostOsInfo::hostOs()); } -void CMakeBuildSystem::setAdditionalCMakeArguments(const QStringList &args) +void CMakeBuildConfiguration::setAdditionalCMakeArguments(const QStringList &args) { const QStringList expandedAdditionalArguments = Utils::transform(args, [this](const QString &s) { - return buildConfiguration()->macroExpander()->expand(s); + return macroExpander()->expand(s); }); const QStringList nonEmptyAdditionalArguments = Utils::filtered(expandedAdditionalArguments, [](const QString &s) { return !s.isEmpty(); }); - buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->setValue( - ProcessArgs::joinArgs(nonEmptyAdditionalArguments)); + additionalCMakeOptions.setValue(ProcessArgs::joinArgs(nonEmptyAdditionalArguments)); } -void CMakeBuildSystem::filterConfigArgumentsFromAdditionalCMakeArguments() +void CMakeBuildConfiguration::filterConfigArgumentsFromAdditionalCMakeArguments() { // On iOS the %{Ios:DevelopmentTeam:Flag} evalues to something like // -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM:STRING=MAGICSTRING // which is already part of the CMake variables and should not be also // in the addtional CMake options - const QStringList arguments = ProcessArgs::splitArgs( - buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->value(), - HostOsInfo::hostOs()); + const QStringList arguments = ProcessArgs::splitArgs(additionalCMakeOptions(), + HostOsInfo::hostOs()); QStringList unknownOptions; const CMakeConfig config = CMakeConfig::fromArguments(arguments, unknownOptions); - buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>()->setValue(ProcessArgs::joinArgs(unknownOptions)); + additionalCMakeOptions.setValue(ProcessArgs::joinArgs(unknownOptions)); } void CMakeBuildSystem::setError(const QString &message) @@ -1814,7 +1748,7 @@ QString CMakeBuildSystem::warning() const NamedWidget *CMakeBuildConfiguration::createConfigWidget() { - return new CMakeBuildSettingsWidget(m_buildSystem); + return new CMakeBuildSettingsWidget(this); } CMakeConfig CMakeBuildConfiguration::signingFlags() const @@ -1822,7 +1756,7 @@ CMakeConfig CMakeBuildConfiguration::signingFlags() const return {}; } -void CMakeBuildConfiguration::setInitialBuildAndCleanSteps(const ProjectExplorer::Target *target) +void CMakeBuildConfiguration::setInitialBuildAndCleanSteps(const Target *target) { const CMakeConfigItem presetItem = CMakeConfigurationKitAspect::cmakePresetConfigItem( target->kit()); @@ -2004,10 +1938,10 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType) info.typeName = "Debug"; info.displayName = ::ProjectExplorer::Tr::tr("Debug"); info.buildType = BuildConfiguration::Debug; - QVariantMap extraInfo; + Store extraInfo; // enable QML debugging by default extraInfo.insert(Constants::QML_DEBUG_SETTING, TriState::Enabled.toVariant()); - info.extraInfo = extraInfo; + info.extraInfo = variantFromStore(extraInfo); break; } case BuildTypeRelease: @@ -2029,12 +1963,12 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType) info.typeName = "Profile"; info.displayName = Tr::tr("Profile"); info.buildType = BuildConfiguration::Profile; - QVariantMap extraInfo; + Store extraInfo; // override CMake build type, which defaults to info.typeName extraInfo.insert(CMAKE_BUILD_TYPE, "RelWithDebInfo"); // enable QML debugging by default extraInfo.insert(Constants::QML_DEBUG_SETTING, TriState::Enabled.toVariant()); - info.extraInfo = extraInfo; + info.extraInfo = variantFromStore(extraInfo); break; } default: @@ -2069,14 +2003,9 @@ BuildSystem *CMakeBuildConfiguration::buildSystem() const return m_buildSystem; } -void CMakeBuildConfiguration::setSourceDirectory(const FilePath &path) +CMakeBuildSystem *CMakeBuildConfiguration::cmakeBuildSystem() const { - aspect<SourceDirectoryAspect>()->setFilePath(path); -} - -FilePath CMakeBuildConfiguration::sourceDirectory() const -{ - return aspect<SourceDirectoryAspect>()->filePath(); + return m_buildSystem; } void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const @@ -2086,16 +2015,14 @@ void CMakeBuildConfiguration::addToEnvironment(Utils::Environment &env) const if (tool && tool->cmakeExecutable().needsDevice()) return; - auto settings = CMakeSpecificSettings::instance(); - if (!settings->ninjaPath().isEmpty()) { - const Utils::FilePath ninja = settings->ninjaPath(); + const FilePath ninja = settings().ninjaPath(); + if (!ninja.isEmpty()) env.appendOrSetPath(ninja.isFile() ? ninja.parentDir() : ninja); - } } Environment CMakeBuildConfiguration::configureEnvironment() const { - Environment env = aspect<ConfigureEnvironmentAspect>()->environment(); + Environment env = configureEnv.environment(); addToEnvironment(env); return env; @@ -2108,14 +2035,13 @@ QString CMakeBuildSystem::cmakeBuildType() const return item.key == "CMAKE_BUILD_TYPE" && !item.isInitial; }); if (it != config.end()) - const_cast<CMakeBuildSystem*>(this) - ->setCMakeBuildType(QString::fromUtf8(it->value)); + cmakeBuildConfiguration()->setCMakeBuildType(QString::fromUtf8(it->value)); }; if (!isMultiConfig()) setBuildTypeFromConfig(configurationChanges()); - QString cmakeBuildType = buildConfiguration()->aspect<BuildTypeAspect>()->value(); + QString cmakeBuildType = cmakeBuildConfiguration()->buildTypeAspect(); const Utils::FilePath cmakeCacheTxt = buildConfiguration()->buildDirectory().pathAppended("CMakeCache.txt"); const bool hasCMakeCache = cmakeCacheTxt.exists(); @@ -2140,15 +2066,9 @@ QString CMakeBuildSystem::cmakeBuildType() const return cmakeBuildType; } -void CMakeBuildSystem::setCMakeBuildType(const QString &cmakeBuildType, bool quiet) +void CMakeBuildConfiguration::setCMakeBuildType(const QString &cmakeBuildType, bool quiet) { - auto aspect = buildConfiguration()->aspect<BuildTypeAspect>(); - if (quiet) { - aspect->setValueQuietly(cmakeBuildType); - aspect->update(); - } else { - aspect->setValue(cmakeBuildType); - } + buildTypeAspect.setValue(cmakeBuildType, quiet ? BaseAspect::BeQuiet : BaseAspect::DoEmit); } namespace Internal { @@ -2179,21 +2099,8 @@ void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringLis QStringList arguments = values.split('\n', Qt::SkipEmptyParts); QString cmakeGenerator; for (QString &arg: arguments) { - if (arg.startsWith("-G")) { - const QString strDash(" - "); - const int idxDash = arg.indexOf(strDash); - if (idxDash > 0) { - // -GCodeBlocks - Ninja - cmakeGenerator = "-DCMAKE_GENERATOR:STRING=" + arg.mid(idxDash + strDash.length()); - - arg = arg.left(idxDash); - arg.replace("-G", "-DCMAKE_EXTRA_GENERATOR:STRING="); - - } else { - // -GNinja - arg.replace("-G", "-DCMAKE_GENERATOR:STRING="); - } - } + if (arg.startsWith("-G")) + arg.replace("-G", "-DCMAKE_GENERATOR:STRING="); if (arg.startsWith("-A")) arg.replace("-A", "-DCMAKE_GENERATOR_PLATFORM:STRING="); if (arg.startsWith("-T")) @@ -2208,7 +2115,7 @@ void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringLis // Display the unknown arguments in "Additional CMake Options" const QString additionalOptionsValue = ProcessArgs::joinArgs(additionalOptions); - BaseAspect::setValueQuietly(additionalOptionsValue); + setValue(additionalOptionsValue, BeQuiet); } void InitialCMakeArgumentsAspect::setCMakeConfiguration(const CMakeConfig &config) @@ -2218,64 +2125,33 @@ void InitialCMakeArgumentsAspect::setCMakeConfiguration(const CMakeConfig &confi ci.isInitial = true; } -void InitialCMakeArgumentsAspect::fromMap(const QVariantMap &map) +void InitialCMakeArgumentsAspect::fromMap(const Store &map) { const QString value = map.value(settingsKey(), defaultValue()).toString(); QStringList additionalArguments; setAllValues(value, additionalArguments); } -void InitialCMakeArgumentsAspect::toMap(QVariantMap &map) const +void InitialCMakeArgumentsAspect::toMap(Store &map) const { saveToMap(map, allValues().join('\n'), defaultValue(), settingsKey()); } -InitialCMakeArgumentsAspect::InitialCMakeArgumentsAspect() +InitialCMakeArgumentsAspect::InitialCMakeArgumentsAspect(AspectContainer *container) + : StringAspect(container) { setSettingsKey("CMake.Initial.Parameters"); setLabelText(Tr::tr("Additional CMake <a href=\"options\">options</a>:")); setDisplayStyle(LineEditDisplay); } -// ---------------------------------------------------------------------- -// - AdditionalCMakeOptionsAspect: -// ---------------------------------------------------------------------- - -AdditionalCMakeOptionsAspect::AdditionalCMakeOptionsAspect() -{ - setSettingsKey("CMake.Additional.Options"); - setLabelText(Tr::tr("Additional CMake <a href=\"options\">options</a>:")); - setDisplayStyle(LineEditDisplay); -} - -// ----------------------------------------------------------------------------- -// SourceDirectoryAspect: -// ----------------------------------------------------------------------------- -SourceDirectoryAspect::SourceDirectoryAspect() -{ - // Will not be displayed, only persisted - setSettingsKey("CMake.Source.Directory"); -} - -// ----------------------------------------------------------------------------- -// BuildTypeAspect: -// ----------------------------------------------------------------------------- -BuildTypeAspect::BuildTypeAspect() -{ - setSettingsKey(CMAKE_BUILD_TYPE); - setLabelText(Tr::tr("Build type:")); - setDisplayStyle(LineEditDisplay); - setDefaultValue("Unknown"); -} - // ----------------------------------------------------------------------------- // ConfigureEnvironmentAspect: // ----------------------------------------------------------------------------- -class ConfigureEnvironmentAspectWidget final : public ProjectExplorer::EnvironmentAspectWidget +class ConfigureEnvironmentAspectWidget final : public EnvironmentAspectWidget { public: - ConfigureEnvironmentAspectWidget(ConfigureEnvironmentAspect *aspect, - ProjectExplorer::Target *target) + ConfigureEnvironmentAspectWidget(ConfigureEnvironmentAspect *aspect, Target *target) : EnvironmentAspectWidget(aspect) { envWidget()->setOpenTerminalFunc([target](const Environment &env) { @@ -2285,8 +2161,11 @@ public: } }; -ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(ProjectExplorer::Target *target) +ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(AspectContainer *container, + BuildConfiguration *bc) + : EnvironmentAspect(container) { + Target *target = bc->target(); setIsLocal(true); setAllowPrintOnRun(false); setConfigWidgetCreator( @@ -2299,14 +2178,8 @@ ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(ProjectExplorer::Target * return device ? device->systemEnvironment() : Environment::systemEnvironment(); }); - const int buildEnvIndex = addSupportedBaseEnvironment(Tr::tr("Build Environment"), [target] { - Environment env; - if (BuildConfiguration *bc = target->activeBuildConfiguration()) { - env = bc->environment(); - } else { // Fallback for targets without buildconfigurations: - env = target->kit()->buildEnvironment(); - } - return env; + const int buildEnvIndex = addSupportedBaseEnvironment(Tr::tr("Build Environment"), [bc] { + return bc->environment(); }); connect(target, @@ -2344,35 +2217,32 @@ ConfigureEnvironmentAspect::ConfigureEnvironmentAspect(ProjectExplorer::Target * }); } -void ConfigureEnvironmentAspect::fromMap(const QVariantMap &map) +void ConfigureEnvironmentAspect::fromMap(const Store &map) { // Match the key values from Qt Creator 9.0.0/1 to the ones from EnvironmentAspect - const bool cleanSystemEnvironment = map.value(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY)) - .toBool(); + const bool cleanSystemEnvironment = map.value(CLEAR_SYSTEM_ENVIRONMENT_KEY).toBool(); const QStringList userEnvironmentChanges - = map.value(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY)).toStringList(); + = map.value(USER_ENVIRONMENT_CHANGES_KEY).toStringList(); - const int baseEnvironmentIndex - = map.value(QLatin1String(BASE_ENVIRONMENT_KEY), baseEnvironmentBase()).toInt(); + const int baseEnvironmentIndex = map.value(BASE_ENVIRONMENT_KEY, baseEnvironmentBase()).toInt(); - QVariantMap tmpMap; - tmpMap.insert(QLatin1String(BASE_KEY), cleanSystemEnvironment ? 0 : baseEnvironmentIndex); - tmpMap.insert(QLatin1String(CHANGES_KEY), userEnvironmentChanges); + Store tmpMap; + tmpMap.insert(BASE_KEY, cleanSystemEnvironment ? 0 : baseEnvironmentIndex); + tmpMap.insert(CHANGES_KEY, userEnvironmentChanges); ProjectExplorer::EnvironmentAspect::fromMap(tmpMap); } -void ConfigureEnvironmentAspect::toMap(QVariantMap &map) const +void ConfigureEnvironmentAspect::toMap(Store &map) const { - QVariantMap tmpMap; + Store tmpMap; ProjectExplorer::EnvironmentAspect::toMap(tmpMap); const int baseKey = tmpMap.value(BASE_KEY).toInt(); - map.insert(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY), baseKey == 0); - map.insert(QLatin1String(BASE_ENVIRONMENT_KEY), baseKey); - map.insert(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY), - tmpMap.value(CHANGES_KEY).toStringList()); + map.insert(CLEAR_SYSTEM_ENVIRONMENT_KEY, baseKey == 0); + map.insert(BASE_ENVIRONMENT_KEY, baseKey); + map.insert(USER_ENVIRONMENT_CHANGES_KEY, tmpMap.value(CHANGES_KEY).toStringList()); } } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h index ff166133988..a23ae568012 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.h @@ -10,6 +10,8 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/environmentaspect.h> +#include <qtsupport/qtbuildaspects.h> + namespace CMakeProjectManager { class CMakeProject; @@ -19,6 +21,33 @@ class CMakeBuildSystem; class CMakeBuildSettingsWidget; class CMakeProjectImporter; +class InitialCMakeArgumentsAspect final : public Utils::StringAspect +{ +public: + InitialCMakeArgumentsAspect(Utils::AspectContainer *container); + + const CMakeConfig &cmakeConfiguration() const; + const QStringList allValues() const; + void setAllValues(const QString &values, QStringList &additionalArguments); + void setCMakeConfiguration(const CMakeConfig &config); + + void fromMap(const Utils::Store &map) final; + void toMap(Utils::Store &map) const final; + +private: + CMakeConfig m_cmakeConfiguration; +}; + +class ConfigureEnvironmentAspect final: public ProjectExplorer::EnvironmentAspect +{ +public: + ConfigureEnvironmentAspect(Utils::AspectContainer *container, + ProjectExplorer::BuildConfiguration *buildConfig); + + void fromMap(const Utils::Store &map); + void toMap(Utils::Store &map) const; +}; + } // namespace Internal class CMAKE_EXPORT CMakeBuildConfiguration : public ProjectExplorer::BuildConfiguration @@ -39,22 +68,29 @@ public: void buildTarget(const QString &buildTarget); ProjectExplorer::BuildSystem *buildSystem() const final; - void setSourceDirectory(const Utils::FilePath& path); - Utils::FilePath sourceDirectory() const; - void addToEnvironment(Utils::Environment &env) const override; Utils::Environment configureEnvironment() const; + Internal::CMakeBuildSystem *cmakeBuildSystem() const; + + QStringList additionalCMakeArguments() const; + void setAdditionalCMakeArguments(const QStringList &args); + + void setInitialCMakeArguments(const QStringList &args); + void setCMakeBuildType(const QString &cmakeBuildType, bool quiet = false); + + Internal::InitialCMakeArgumentsAspect initialCMakeArguments{this}; + Utils::StringAspect additionalCMakeOptions{this}; + Utils::FilePathAspect sourceDirectory{this}; + Utils::StringAspect buildTypeAspect{this}; + QtSupport::QmlDebuggingAspect qmlDebugging{this}; + Internal::ConfigureEnvironmentAspect configureEnv{this, this}; signals: void signingFlagsChanged(); void configureEnvironmentChanged(); -protected: - bool fromMap(const QVariantMap &map) override; - private: - QVariantMap toMap() const override; BuildType buildType() const override; ProjectExplorer::NamedWidget *createConfigWidget() override; @@ -63,6 +99,7 @@ private: void setInitialBuildAndCleanSteps(const ProjectExplorer::Target *target); void setBuildPresetToBuildSteps(const ProjectExplorer::Target *target); + void filterConfigArgumentsFromAdditionalCMakeArguments(); Internal::CMakeBuildSystem *m_buildSystem = nullptr; @@ -94,60 +131,4 @@ private: friend class Internal::CMakeProjectImporter; }; -namespace Internal { - -class InitialCMakeArgumentsAspect final : public Utils::StringAspect -{ - Q_OBJECT - - CMakeConfig m_cmakeConfiguration; -public: - InitialCMakeArgumentsAspect(); - - const CMakeConfig &cmakeConfiguration() const; - const QStringList allValues() const; - void setAllValues(const QString &values, QStringList &additionalArguments); - void setCMakeConfiguration(const CMakeConfig &config); - - void fromMap(const QVariantMap &map) final; - void toMap(QVariantMap &map) const final; -}; - -class AdditionalCMakeOptionsAspect final : public Utils::StringAspect -{ - Q_OBJECT - -public: - AdditionalCMakeOptionsAspect(); -}; - -class SourceDirectoryAspect final : public Utils::StringAspect -{ - Q_OBJECT - -public: - SourceDirectoryAspect(); -}; - -class BuildTypeAspect final : public Utils::StringAspect -{ - Q_OBJECT - -public: - BuildTypeAspect(); - using Utils::StringAspect::update; -}; - -class ConfigureEnvironmentAspect final: public ProjectExplorer::EnvironmentAspect -{ - Q_OBJECT - -public: - ConfigureEnvironmentAspect(ProjectExplorer::Target *target); - - void fromMap(const QVariantMap &map); - void toMap(QVariantMap &map) const; -}; - -} // namespace Internal } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 11b3d88dcd4..370799fe260 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -5,7 +5,7 @@ #include "cmakebuildconfiguration.h" #include "cmakebuildsystem.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeparser.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" @@ -25,7 +25,7 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/environmentwidget.h> #include <projectexplorer/gnumakeparser.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -46,6 +46,7 @@ using namespace Core; using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; namespace CMakeProjectManager::Internal { @@ -61,7 +62,20 @@ const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "CMakeProjectManager.MakeStep.ClearS const char USER_ENVIRONMENT_CHANGES_KEY[] = "CMakeProjectManager.MakeStep.UserEnvironmentChanges"; const char BUILD_PRESET_KEY[] = "CMakeProjectManager.MakeStep.BuildPreset"; -// CmakeProgressParser +class ProjectParserTaskAdapter : public TaskAdapter<QPointer<Target>> +{ +public: + void start() final { + Target *target = *task(); + if (!target) { + emit done(false); + return; + } + connect(target, &Target::parsingFinished, this, &TaskInterface::done); + } +}; + +using ProjectParserTask = CustomTask<ProjectParserTaskAdapter>; class CmakeProgressParser : public Utils::OutputLineParser { @@ -199,37 +213,34 @@ static bool supportsStageForInstallation(const Kit *kit) CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : CMakeAbstractProcessStep(bsl, id) { - m_cmakeArguments = addAspect<StringAspect>(); - m_cmakeArguments->setSettingsKey(CMAKE_ARGUMENTS_KEY); - m_cmakeArguments->setLabelText(Tr::tr("CMake arguments:")); - m_cmakeArguments->setDisplayStyle(StringAspect::LineEditDisplay); + cmakeArguments.setSettingsKey(CMAKE_ARGUMENTS_KEY); + cmakeArguments.setLabelText(Tr::tr("CMake arguments:")); + cmakeArguments.setDisplayStyle(StringAspect::LineEditDisplay); - m_toolArguments = addAspect<StringAspect>(); - m_toolArguments->setSettingsKey(TOOL_ARGUMENTS_KEY); - m_toolArguments->setLabelText(Tr::tr("Tool arguments:")); - m_toolArguments->setDisplayStyle(StringAspect::LineEditDisplay); + toolArguments.setSettingsKey(TOOL_ARGUMENTS_KEY); + toolArguments.setLabelText(Tr::tr("Tool arguments:")); + toolArguments.setDisplayStyle(StringAspect::LineEditDisplay); - m_useStaging = addAspect<BoolAspect>(); - m_useStaging->setSettingsKey(USE_STAGING_KEY); - m_useStaging->setLabel(Tr::tr("Stage for installation"), BoolAspect::LabelPlacement::AtCheckBox); - m_useStaging->setDefaultValue(supportsStageForInstallation(kit())); + useStaging.setSettingsKey(USE_STAGING_KEY); + useStaging.setLabel(Tr::tr("Stage for installation"), BoolAspect::LabelPlacement::AtCheckBox); + useStaging.setDefaultValue(supportsStageForInstallation(kit())); - m_stagingDir = addAspect<FilePathAspect>(); - m_stagingDir->setSettingsKey(STAGING_DIR_KEY); - m_stagingDir->setLabelText(Tr::tr("Staging directory:")); - m_stagingDir->setDefaultValue(initialStagingDir(kit())); + stagingDir.setSettingsKey(STAGING_DIR_KEY); + stagingDir.setLabelText(Tr::tr("Staging directory:")); + stagingDir.setDefaultValue(initialStagingDir(kit())); Kit *kit = buildConfiguration()->kit(); if (CMakeBuildConfiguration::isIos(kit)) { - m_useiOSAutomaticProvisioningUpdates = addAspect<BoolAspect>(); - m_useiOSAutomaticProvisioningUpdates->setDefaultValue(true); - m_useiOSAutomaticProvisioningUpdates->setSettingsKey( - IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY); - m_useiOSAutomaticProvisioningUpdates->setLabel( - Tr::tr("Enable automatic provisioning updates:")); - m_useiOSAutomaticProvisioningUpdates->setToolTip( - Tr::tr("Tells xcodebuild to create and download a provisioning profile " - "if a valid one does not exist.")); + useiOSAutomaticProvisioningUpdates.setDefaultValue(true); + useiOSAutomaticProvisioningUpdates.setSettingsKey( + IOS_AUTOMATIC_PROVISIONG_UPDATES_ARGUMENTS_KEY); + useiOSAutomaticProvisioningUpdates.setLabel( + Tr::tr("Enable automatic provisioning updates:")); + useiOSAutomaticProvisioningUpdates.setToolTip( + Tr::tr("Tells xcodebuild to create and download a provisioning profile " + "if a valid one does not exist.")); + } else { + useiOSAutomaticProvisioningUpdates.setVisible(false); } m_buildTargetModel.setHeader({Tr::tr("Target")}); @@ -250,8 +261,10 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : env.set("NINJA_STATUS", ninjaProgressString + "%o/sec] "); env.modify(m_userEnvironmentChanges); - if (m_useStaging && m_useStaging->value()) - env.set("DESTDIR", currentStagingDir()); + env.setFallback("CLICOLOR_FORCE", "1"); + + if (useStaging()) + env.set("DESTDIR", stagingDir().path()); }); connect(target(), &Target::parsingFinished, this, [this](bool success) { @@ -263,31 +276,28 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) : this, &CMakeBuildStep::updateBuildTargetsModel); } -QVariantMap CMakeBuildStep::toMap() const +void CMakeBuildStep::toMap(Utils::Store &map) const { - QVariantMap map(CMakeAbstractProcessStep::toMap()); + CMakeAbstractProcessStep::toMap(map); map.insert(BUILD_TARGETS_KEY, m_buildTargets); - map.insert(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY), m_clearSystemEnvironment); - map.insert(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY), EnvironmentItem::toStringList(m_userEnvironmentChanges)); - map.insert(QLatin1String(BUILD_PRESET_KEY), m_buildPreset); - - return map; + map.insert(CLEAR_SYSTEM_ENVIRONMENT_KEY, m_clearSystemEnvironment); + map.insert(USER_ENVIRONMENT_CHANGES_KEY, EnvironmentItem::toStringList(m_userEnvironmentChanges)); + map.insert(BUILD_PRESET_KEY, m_buildPreset); } -bool CMakeBuildStep::fromMap(const QVariantMap &map) +void CMakeBuildStep::fromMap(const Utils::Store &map) { setBuildTargets(map.value(BUILD_TARGETS_KEY).toStringList()); - m_clearSystemEnvironment = map.value(QLatin1String(CLEAR_SYSTEM_ENVIRONMENT_KEY)) - .toBool(); + m_clearSystemEnvironment = map.value(CLEAR_SYSTEM_ENVIRONMENT_KEY).toBool(); m_userEnvironmentChanges = EnvironmentItem::fromStringList( - map.value(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY)).toStringList()); + map.value(USER_ENVIRONMENT_CHANGES_KEY).toStringList()); updateAndEmitEnvironmentChanged(); - m_buildPreset = map.value(QLatin1String(BUILD_PRESET_KEY)).toString(); + m_buildPreset = map.value(BUILD_PRESET_KEY).toString(); - return BuildStep::fromMap(map); + BuildStep::fromMap(map); } bool CMakeBuildStep::init() @@ -338,42 +348,38 @@ void CMakeBuildStep::setupOutputFormatter(Utils::OutputFormatter *formatter) CMakeAbstractProcessStep::setupOutputFormatter(formatter); } -void CMakeBuildStep::doRun() +GroupItem CMakeBuildStep::runRecipe() { - // Make sure CMake state was written to disk before trying to build: - auto bs = static_cast<CMakeBuildSystem *>(buildSystem()); - QString message; - if (bs->persistCMakeState()) { - message = Tr::tr("Persisting CMake state..."); - } else if (bs->isWaitingForParse()) { - message = Tr::tr("Running CMake in preparation to build..."); - } else { - runImpl(); - return; - } - emit addOutput(message, OutputFormat::NormalMessage); - m_runTrigger = connect(target(), &Target::parsingFinished, - this, [this](bool success) { handleProjectWasParsed(success); }); -} - -void CMakeBuildStep::runImpl() -{ - // Do the actual build: - CMakeAbstractProcessStep::doRun(); -} - -void CMakeBuildStep::handleProjectWasParsed(bool success) -{ - disconnect(m_runTrigger); - if (isCanceled()) { - emit finished(false); - } else if (success) { - runImpl(); - } else { + const auto onParserSetup = [this](QPointer<Target> &parseTarget) { + // Make sure CMake state was written to disk before trying to build: + auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem()); + QTC_ASSERT(bs, return SetupResult::StopWithError); + QString message; + if (bs->persistCMakeState()) + message = Tr::tr("Persisting CMake state..."); + else if (bs->isWaitingForParse()) + message = Tr::tr("Running CMake in preparation to build..."); + else + return SetupResult::StopWithDone; + emit addOutput(message, OutputFormat::NormalMessage); + parseTarget = target(); + return SetupResult::Continue; + }; + const auto onParserError = [this](const QPointer<Target> &) { emit addOutput(Tr::tr("Project did not parse successfully, cannot build."), OutputFormat::ErrorMessage); - emit finished(false); - } + }; + const auto onEnd = [this] { + updateDeploymentData(); + }; + Group root { + ignoreReturnValue() ? finishAllAndDone : stopOnError, + ProjectParserTask(onParserSetup, {}, onParserError), + defaultProcessTask(), + onGroupDone(onEnd), + onGroupError(onEnd) + }; + return root; } QString CMakeBuildStep::defaultBuildTarget() const @@ -443,7 +449,7 @@ CommandLine CMakeBuildStep::cmakeCommand() const } return s; })); - if (m_useStaging->value()) + if (useStaging()) cmd.addArg("install"); auto bs = qobject_cast<CMakeBuildSystem *>(buildSystem()); @@ -455,17 +461,16 @@ CommandLine CMakeBuildStep::cmakeCommand() const cmd.addArg(bs->cmakeBuildType()); } - if (!m_cmakeArguments->value().isEmpty()) - cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw); + cmd.addArgs(cmakeArguments(), CommandLine::Raw); bool toolArgumentsSpecified = false; - if (!m_toolArguments->value().isEmpty()) { + if (!toolArguments().isEmpty()) { cmd.addArg("--"); - cmd.addArgs(m_toolArguments->value(), CommandLine::Raw); + cmd.addArgs(toolArguments(), CommandLine::Raw); toolArgumentsSpecified = true; } - if (m_useiOSAutomaticProvisioningUpdates && m_useiOSAutomaticProvisioningUpdates->value()) { + if (useiOSAutomaticProvisioningUpdates()) { // Only add the double dash if it wasn't added before. if (!toolArgumentsSpecified) cmd.addArg("--"); @@ -518,12 +523,12 @@ QWidget *CMakeBuildStep::createConfigWidget() QString summaryText = param.summary(displayName()); - m_stagingDir->setEnabled(m_useStaging->value()); - if (m_useStaging->value()) { + stagingDir.setEnabled(useStaging()); + if (useStaging()) { //: Stage (for installation) at <staging_dir> for <installation_dir> summaryText.append( "; " - + Tr::tr("Stage at %2 for %3").arg(currentStagingDir(), currentInstallPrefix())); + + Tr::tr("Stage at %2 for %3").arg(stagingDir().path(), currentInstallPrefix())); } if (!m_buildPreset.isEmpty()) { @@ -584,13 +589,11 @@ QWidget *CMakeBuildStep::createConfigWidget() }; Layouting::Form builder; - builder.addRow({m_cmakeArguments}); - builder.addRow({m_toolArguments}); - builder.addRow({m_useStaging}); - builder.addRow({m_stagingDir}); - - if (m_useiOSAutomaticProvisioningUpdates) - builder.addRow({m_useiOSAutomaticProvisioningUpdates}); + builder.addRow({cmakeArguments}); + builder.addRow({toolArguments}); + builder.addRow({useStaging}); + builder.addRow({stagingDir}); + builder.addRow({useiOSAutomaticProvisioningUpdates}); builder.addRow({new QLabel(Tr::tr("Targets:")), frame}); @@ -602,13 +605,11 @@ QWidget *CMakeBuildStep::createConfigWidget() updateDetails(); - connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails); - connect(m_toolArguments, &StringAspect::changed, this, updateDetails); - connect(m_useStaging, &BoolAspect::changed, this, updateDetails); - connect(m_stagingDir, &StringAspect::changed, this, updateDetails); - - if (m_useiOSAutomaticProvisioningUpdates) - connect(m_useiOSAutomaticProvisioningUpdates, &BoolAspect::changed, this, updateDetails); + connect(&cmakeArguments, &BaseAspect::changed, this, updateDetails); + connect(&toolArguments, &BaseAspect::changed, this, updateDetails); + connect(&useStaging, &BaseAspect::changed, this, updateDetails); + connect(&stagingDir, &BaseAspect::changed, this, updateDetails); + connect(&useiOSAutomaticProvisioningUpdates, &BaseAspect::changed, this, updateDetails); connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, this, updateDetails); @@ -678,12 +679,12 @@ void CMakeBuildStep::setConfiguration(const QString &configuration) void CMakeBuildStep::setToolArguments(const QStringList &nativeToolArguments) { - m_toolArguments->setValue(nativeToolArguments.join(" ")); + toolArguments.setValue(nativeToolArguments.join(" ")); } -void CMakeBuildStep::setCMakeArguments(const QStringList &cmakeArguments) +void CMakeBuildStep::setCMakeArguments(const QStringList &cmakeArgs) { - m_cmakeArguments->setValue(cmakeArguments.join(" ")); + cmakeArguments.setValue(cmakeArgs.join(" ")); } Environment CMakeBuildStep::environment() const @@ -756,11 +757,6 @@ QString CMakeBuildStep::currentInstallPrefix() const return QString::fromUtf8(config.valueOf("CMAKE_INSTALL_PREFIX")); } -QString CMakeBuildStep::currentStagingDir() const -{ - return m_stagingDir->filePath().path(); -} - FilePath CMakeBuildStep::cmakeExecutable() const { CMakeTool *tool = CMakeKitAspect::cmakeTool(kit()); @@ -769,30 +765,33 @@ FilePath CMakeBuildStep::cmakeExecutable() const void CMakeBuildStep::updateDeploymentData() { - if (!m_useStaging->value()) + if (!useStaging()) return; QString install = currentInstallPrefix(); - QString stagingDir = currentStagingDir(); - FilePath rootDir = cmakeExecutable().withNewPath(stagingDir); + FilePath rootDir = cmakeExecutable().withNewPath(stagingDir().path()); Q_UNUSED(install); DeploymentData deploymentData; deploymentData.setLocalInstallRoot(rootDir); - const int startPos = rootDir.path().length(); + IDeviceConstPtr runDevice = DeviceKitAspect::device(buildSystem()->kit()); const auto appFileNames = transform<QSet<QString>>(buildSystem()->applicationTargets(), [](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); }); - auto handleFile = [&appFileNames, startPos, &deploymentData](const FilePath &filePath) { - const DeployableFile::Type type = appFileNames.contains(filePath.fileName()) - ? DeployableFile::TypeExecutable - : DeployableFile::TypeNormal; - const QString targetDir = filePath.parentDir().path().mid(startPos); - deploymentData.addFile(filePath, targetDir, type); - return IterationPolicy::Continue; - }; + auto handleFile = + [&appFileNames, rootDir, &deploymentData, runDevice](const FilePath &filePath) { + const DeployableFile::Type type = appFileNames.contains(filePath.fileName()) + ? DeployableFile::TypeExecutable + : DeployableFile::TypeNormal; + + FilePath targetDirPath = filePath.parentDir().relativePathFrom(rootDir); + + const FilePath targetDir = runDevice->rootPath().pathAppended(targetDirPath.path()); + deploymentData.addFile(filePath, targetDir.nativePath(), type); + return IterationPolicy::Continue; + }; rootDir.iterateDirectory(handleFile, {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories}); @@ -800,14 +799,6 @@ void CMakeBuildStep::updateDeploymentData() buildSystem()->setDeploymentData(deploymentData); } -void CMakeBuildStep::finish(ProcessResult result) -{ - updateDeploymentData(); - - emit progress(100, {}); - AbstractProcessStep::finish(result); -} - // CMakeBuildStepFactory CMakeBuildStepFactory::CMakeBuildStepFactory() diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.h b/src/plugins/cmakeprojectmanager/cmakebuildstep.h index 85ee46d953b..34690950290 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.h @@ -44,7 +44,7 @@ public: bool buildsBuildTarget(const QString &target) const; void setBuildsBuildTarget(const QString &target, bool on); - QVariantMap toMap() const override; + void toMap(Utils::Store &map) const override; QString cleanTarget() const; QString allTarget() const ; @@ -70,6 +70,12 @@ public: void setConfiguration(const QString &configuration); + Utils::StringAspect cmakeArguments{this}; + Utils::StringAspect toolArguments{this}; + Utils::BoolAspect useiOSAutomaticProvisioningUpdates{this}; + Utils::BoolAspect useStaging{this}; + Utils::FilePathAspect stagingDir{this}; + signals: void buildTargetsChanged(); void environmentChanged(); @@ -77,38 +83,26 @@ signals: private: Utils::CommandLine cmakeCommand() const; - void finish(Utils::ProcessResult result) override; - bool fromMap(const QVariantMap &map) override; + void fromMap(const Utils::Store &map) override; bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; - void doRun() override; + Tasking::GroupItem runRecipe() final; QWidget *createConfigWidget() override; Utils::FilePath cmakeExecutable() const; QString currentInstallPrefix() const; - QString currentStagingDir() const; QString defaultBuildTarget() const; bool isCleanStep() const; - void runImpl(); - void handleProjectWasParsed(bool success); - void handleBuildTargetsChanges(bool success); void recreateBuildTargetsModel(); void updateBuildTargetsModel(); void updateDeploymentData(); - QMetaObject::Connection m_runTrigger; - friend class CMakeBuildStepConfigWidget; QStringList m_buildTargets; // Convention: Empty string member signifies "Current executable" - Utils::StringAspect *m_cmakeArguments = nullptr; - Utils::StringAspect *m_toolArguments = nullptr; - Utils::BoolAspect *m_useiOSAutomaticProvisioningUpdates = nullptr; - Utils::BoolAspect *m_useStaging = nullptr; - Utils::FilePathAspect *m_stagingDir = nullptr; QString m_allTarget = "all"; QString m_installTarget = "install"; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 733bcb48dfa..b05fbbe9174 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -7,7 +7,8 @@ #include "cmakebuildconfiguration.h" #include "cmakebuildstep.h" #include "cmakebuildtarget.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" +#include "cmakeprocess.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" @@ -26,7 +27,7 @@ #include <cppeditor/cppprojectupdater.h> #include <projectexplorer/extracompiler.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -36,11 +37,11 @@ #include <texteditor/textdocument.h> #include <qmljs/qmljsmodelmanagerinterface.h> -#include <qmljstools/qmljstoolsconstants.h> -#include <qtsupport/qtcppkitinfo.h> -#include <qtsupport/qtkitinformation.h> -#include <app/app_version.h> +#include <qmljstools/qmljstoolsconstants.h> + +#include <qtsupport/qtcppkitinfo.h> +#include <qtsupport/qtsupportconstants.h> #include <utils/algorithm.h> #include <utils/checkablemessagebox.h> @@ -117,6 +118,7 @@ CMakeBuildSystem::CMakeBuildSystem(CMakeBuildConfiguration *bc) &CMakeBuildSystem::handleParsingSucceeded); connect(&m_reader, &FileApiReader::errorOccurred, this, &CMakeBuildSystem::handleParsingFailed); connect(&m_reader, &FileApiReader::dirty, this, &CMakeBuildSystem::becameDirty); + connect(&m_reader, &FileApiReader::debuggingStarted, this, &BuildSystem::debuggingStarted); wireUpConnections(); @@ -192,10 +194,23 @@ void CMakeBuildSystem::triggerParsing() // active code model updater when the next one will be triggered. m_cppCodeModelUpdater->cancel(); + const CMakeTool *tool = m_parameters.cmakeTool(); + CMakeTool::Version version = tool ? tool->version() : CMakeTool::Version(); + const bool isDebuggable = (version.major == 3 && version.minor >= 27) || version.major > 3; + qCDebug(cmakeBuildSystemLog) << "Asking reader to parse"; m_reader.parse(reparseParameters & REPARSE_FORCE_CMAKE_RUN, reparseParameters & REPARSE_FORCE_INITIAL_CONFIGURATION, - reparseParameters & REPARSE_FORCE_EXTRA_CONFIGURATION); + reparseParameters & REPARSE_FORCE_EXTRA_CONFIGURATION, + (reparseParameters & REPARSE_DEBUG) && isDebuggable, + reparseParameters & REPARSE_PROFILING); +} + +void CMakeBuildSystem::requestDebugging() +{ + qCDebug(cmakeBuildSystemLog) << "Requesting parse due to \"Rescan Project\" command"; + reparse(REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION | REPARSE_URGENT + | REPARSE_DEBUG); } bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const @@ -257,6 +272,8 @@ static QString newFilesForFunction(const std::string &cmakeFunction, bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded) { + if (notAdded) + *notAdded = filePaths; if (auto n = dynamic_cast<CMakeTargetNode *>(context)) { const QString targetName = n->buildKey(); auto target = Utils::findOrDefault(buildTargets(), @@ -264,10 +281,9 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP return target.title == targetName; }); - if (target.backtrace.isEmpty()) { - *notAdded = filePaths; + if (target.backtrace.isEmpty()) return false; - } + const FilePath targetCMakeFile = target.backtrace.last().path; const int targetDefinitionLine = target.backtrace.last().line; @@ -282,7 +298,6 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP if (!cmakeListFile.ParseString(fileContent->toStdString(), targetCMakeFile.fileName().toStdString(), errorString)) { - *notAdded = filePaths; return false; } } @@ -293,10 +308,8 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP return func.Line() == targetDefinitionLine; }); - if (function == cmakeListFile.Functions.end()) { - *notAdded = filePaths; + if (function == cmakeListFile.Functions.end()) return false; - } // Special case: when qt_add_executable and qt_add_qml_module use the same target name // then qt_add_qml_module function should be used @@ -370,16 +383,16 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP Core::EditorManager::openEditorAt({targetCMakeFile, line, column + extraChars}, Constants::CMAKE_EDITOR_ID, Core::EditorManager::DoNotMakeVisible)); - if (!editor) { - *notAdded = filePaths; + if (!editor) return false; - } editor->insert(snippet); editor->editorWidget()->autoIndent(); if (!Core::DocumentManager::saveDocument(editor->document())) return false; + if (notAdded) + notAdded->clear(); return true; } @@ -793,6 +806,13 @@ void CMakeBuildSystem::runCMakeWithExtraArguments() reparse(REPARSE_FORCE_CMAKE_RUN | REPARSE_FORCE_EXTRA_CONFIGURATION | REPARSE_URGENT); } +void CMakeBuildSystem::runCMakeWithProfiling() +{ + qCDebug(cmakeBuildSystemLog) << "Requesting parse due \"CMake Profiler\" command"; + reparse(REPARSE_FORCE_CMAKE_RUN | REPARSE_URGENT | REPARSE_FORCE_EXTRA_CONFIGURATION + | REPARSE_PROFILING); +} + void CMakeBuildSystem::stopCMakeRun() { qCDebug(cmakeBuildSystemLog) << buildConfiguration()->displayName() @@ -996,7 +1016,7 @@ void CMakeBuildSystem::updateProjectData() const FilePath includeFileBaseDir = buildConfiguration()->buildDirectory(); QStringList cxxFlags = rpp.flagsForCxx.commandLineFlags; QStringList cFlags = rpp.flagsForC.commandLineFlags; - addTargetFlagForIos(cxxFlags, cFlags, this, [this] { + addTargetFlagForIos(cFlags, cxxFlags, this, [this] { return m_configurationFromCMake.stringValueOf("CMAKE_OSX_DEPLOYMENT_TARGET"); }); if (kitInfo.cxxToolChain) @@ -1010,7 +1030,7 @@ void CMakeBuildSystem::updateProjectData() { const bool mergedHeaderPathsAndQmlImportPaths = kit()->value( - QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), false).toBool(); + QtSupport::Constants::KIT_HAS_MERGED_HEADER_PATHS_WITH_QML_IMPORT_PATHS, false).toBool(); QStringList extraHeaderPaths; QList<QByteArray> moduleMappings; for (const RawProjectPart &rpp : std::as_const(rpps)) { @@ -1128,6 +1148,7 @@ void CMakeBuildSystem::handleParsingSucceeded(bool restoredFromBackup) }); m_buildTargets += m_reader.takeBuildTargets(errorMessage); m_cmakeFiles = m_reader.takeCMakeFileInfos(errorMessage); + setupCMakeSymbolsHash(); checkAndReportError(errorMessage); } @@ -1210,7 +1231,7 @@ void CMakeBuildSystem::wireUpConnections() const CMakeConfig config = CMakeConfig::fromFile(cmakeCacheTxt, &errorMessage); if (!config.isEmpty() && errorMessage.isEmpty()) { QString cmakeBuildTypeName = config.stringValueOf("CMAKE_BUILD_TYPE"); - setCMakeBuildType(cmakeBuildTypeName, true); + cmakeBuildConfiguration()->setCMakeBuildType(cmakeBuildTypeName, true); } } reparse(options); @@ -1218,8 +1239,7 @@ void CMakeBuildSystem::wireUpConnections() connect(project(), &Project::projectFileIsDirty, this, [this] { if (buildConfiguration()->isActive() && !isParsing()) { - auto settings = CMakeSpecificSettings::instance(); - if (settings->autorunCMake.value()) { + if (settings().autorunCMake()) { qCDebug(cmakeBuildSystemLog) << "Requesting parse due to dirty project file"; reparse(CMakeBuildSystem::REPARSE_FORCE_CMAKE_RUN); } @@ -1233,6 +1253,191 @@ void CMakeBuildSystem::wireUpConnections() } } +void CMakeBuildSystem::setupCMakeSymbolsHash() +{ + m_cmakeSymbolsHash.clear(); + + m_projectKeywords.functions.clear(); + m_projectKeywords.variables.clear(); + + auto handleFunctionMacroOption = [&](const CMakeFileInfo &cmakeFile, + const cmListFileFunction &func) { + if (func.LowerCaseName() != "function" && func.LowerCaseName() != "macro" + && func.LowerCaseName() != "option") + return; + + if (func.Arguments().size() == 0) + return; + auto arg = func.Arguments()[0]; + + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = arg.Line; + link.targetColumn = arg.Column - 1; + m_cmakeSymbolsHash.insert(QString::fromUtf8(arg.Value), link); + + if (func.LowerCaseName() == "option") + m_projectKeywords.variables[QString::fromUtf8(arg.Value)] = FilePath(); + else + m_projectKeywords.functions[QString::fromUtf8(arg.Value)] = FilePath(); + }; + + m_projectImportedTargets.clear(); + auto handleImportedTargets = [&](const CMakeFileInfo &cmakeFile, + const cmListFileFunction &func) { + if (func.LowerCaseName() != "add_library") + return; + + if (func.Arguments().size() == 0) + return; + auto arg = func.Arguments()[0]; + const QString targetName = QString::fromUtf8(arg.Value); + + const bool haveImported = Utils::contains(func.Arguments(), [](const auto &arg) { + return arg.Value == "IMPORTED"; + }); + if (haveImported && !targetName.contains("${")) { + m_projectImportedTargets << targetName; + + // Allow navigation to the imported target + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = arg.Line; + link.targetColumn = arg.Column - 1; + m_cmakeSymbolsHash.insert(targetName, link); + } + }; + + // Handle project targets, unfortunately the CMake file-api doesn't deliver the + // column of the target, just the line. Make sure to find it out + QHash<FilePath, QPair<int, QString>> projectTargetsSourceAndLine; + for (const auto &target : std::as_const(buildTargets())) { + if (target.targetType == TargetType::UtilityType) + continue; + if (target.backtrace.isEmpty()) + continue; + + projectTargetsSourceAndLine.insert(target.backtrace.last().path, + {target.backtrace.last().line, target.title}); + } + auto handleProjectTargets = [&](const CMakeFileInfo &cmakeFile, const cmListFileFunction &func) { + const auto it = projectTargetsSourceAndLine.find(cmakeFile.path); + if (it == projectTargetsSourceAndLine.end() || it->first != func.Line()) + return; + + if (func.Arguments().size() == 0) + return; + auto arg = func.Arguments()[0]; + + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = arg.Line; + link.targetColumn = arg.Column - 1; + m_cmakeSymbolsHash.insert(it->second, link); + }; + + // Gather the exported variables for the Find<Package> CMake packages + m_projectFindPackageVariables.clear(); + + const std::string fphsFunctionName = "find_package_handle_standard_args"; + CMakeKeywords keywords; + if (auto tool = CMakeKitAspect::cmakeTool(target()->kit())) + keywords = tool->keywords(); + QSet<std::string> fphsFunctionArgs; + if (keywords.functionArgs.contains(QString::fromStdString(fphsFunctionName))) { + const QList<std::string> args + = Utils::transform(keywords.functionArgs.value(QString::fromStdString(fphsFunctionName)), + &QString::toStdString); + fphsFunctionArgs = Utils::toSet(args); + } + + auto handleFindPackageVariables = [&](const CMakeFileInfo &cmakeFile, const cmListFileFunction &func) { + if (func.LowerCaseName() != fphsFunctionName) + return; + + if (func.Arguments().size() == 0) + return; + auto firstArgument = func.Arguments()[0]; + const auto filteredArguments = Utils::filtered(func.Arguments(), [&](const auto &arg) { + return !fphsFunctionArgs.contains(arg.Value) && arg != firstArgument; + }); + + for (const auto &arg : filteredArguments) { + const QString value = QString::fromUtf8(arg.Value); + if (value.contains("${") || (value.startsWith('"') && value.endsWith('"')) + || (value.startsWith("'") && value.endsWith("'"))) + continue; + + m_projectFindPackageVariables << value; + + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = arg.Line; + link.targetColumn = arg.Column - 1; + m_cmakeSymbolsHash.insert(value, link); + } + }; + + // Prepare a hash with all .cmake files + m_dotCMakeFilesHash.clear(); + auto handleDotCMakeFiles = [&](const CMakeFileInfo &cmakeFile) { + if (cmakeFile.path.suffix() == "cmake") { + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = 1; + link.targetColumn = 0; + m_dotCMakeFilesHash.insert(cmakeFile.path.completeBaseName(), link); + } + }; + + // Gather all Find<Package>.cmake and <Package>Config.cmake / <Package>-config.cmake files + m_findPackagesFilesHash.clear(); + auto handleFindPackageCMakeFiles = [&](const CMakeFileInfo &cmakeFile) { + const QString fileName = cmakeFile.path.fileName(); + + const QString findPackageName = [fileName]() -> QString { + auto findIdx = fileName.indexOf("Find"); + auto endsWithCMakeIdx = fileName.lastIndexOf(".cmake"); + if (findIdx == 0 && endsWithCMakeIdx > 0) + return fileName.mid(4, endsWithCMakeIdx - 4); + return QString(); + }(); + + const QString configPackageName = [fileName]() -> QString { + auto configCMakeIdx = fileName.lastIndexOf("Config.cmake"); + if (configCMakeIdx > 0) + return fileName.left(configCMakeIdx); + auto dashConfigCMakeIdx = fileName.lastIndexOf("-config.cmake"); + if (dashConfigCMakeIdx > 0) + return fileName.left(dashConfigCMakeIdx); + return QString(); + }(); + + if (!findPackageName.isEmpty() || !configPackageName.isEmpty()) { + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = 1; + link.targetColumn = 0; + m_findPackagesFilesHash.insert(!findPackageName.isEmpty() ? findPackageName + : configPackageName, + link); + } + }; + + for (const auto &cmakeFile : std::as_const(m_cmakeFiles)) { + for (const auto &func : cmakeFile.cmakeListFile.Functions) { + handleFunctionMacroOption(cmakeFile, func); + handleImportedTargets(cmakeFile, func); + handleProjectTargets(cmakeFile, func); + handleFindPackageVariables(cmakeFile, func); + } + handleDotCMakeFiles(cmakeFile); + handleFindPackageCMakeFiles(cmakeFile); + } + + m_projectFindPackageVariables.removeDuplicates(); +} + void CMakeBuildSystem::ensureBuildDirectory(const BuildDirParameters ¶meters) { const FilePath bdir = parameters.buildDirectory; @@ -1314,7 +1519,7 @@ void CMakeBuildSystem::runCTest() const QJsonArray nodes = btGraph.value("nodes").toArray(); const QJsonArray tests = jsonObj.value("tests").toArray(); int counter = 0; - for (const QJsonValue &testVal : tests) { + for (const auto &testVal : tests) { ++counter; const QJsonObject test = testVal.toObject(); QTC_ASSERT(!test.isEmpty(), continue); @@ -1325,11 +1530,10 @@ void CMakeBuildSystem::runCTest() if (bt != -1) { QSet<int> seen; std::function<QJsonObject(int)> findAncestor = [&](int index){ - const QJsonObject node = nodes.at(index).toObject(); + QJsonObject node = nodes.at(index).toObject(); const int parent = node.value("parent").toInt(-1); - if (seen.contains(parent) || parent < 0) + if (parent < 0 || !Utils::insert(seen, parent)) return node; - seen << parent; return findAncestor(parent); }; const QJsonObject btRef = findAncestor(bt); @@ -1570,7 +1774,7 @@ void CMakeBuildSystem::updateQmlJSCodeModel(const QStringList &extraHeaderPaths, const CMakeConfig &cm = configurationFromCMake(); addImports(cm.stringValueOf("QML_IMPORT_PATH")); - addImports(kit()->value(QtSupport::KitQmlImportPath::id()).toString()); + addImports(kit()->value(QtSupport::Constants::KIT_QML_IMPORT_PATH).toString()); for (const QString &extraHeaderPath : extraHeaderPaths) projectInfo.importPaths.maybeInsert(FilePath::fromString(extraHeaderPath), @@ -1602,7 +1806,8 @@ void CMakeBuildSystem::updateQmlJSCodeModel(const QStringList &extraHeaderPaths, void CMakeBuildSystem::updateInitialCMakeExpandableVars() { const CMakeConfig &cm = configurationFromCMake(); - const CMakeConfig &initialConfig = initialCMakeConfiguration(); + const CMakeConfig &initialConfig = + cmakeBuildConfiguration()->initialCMakeArguments.cmakeConfiguration(); CMakeConfig config; @@ -1720,11 +1925,6 @@ QList<QPair<Id, QString>> CMakeBuildSystem::generators() const for (const CMakeTool::Generator &generator : generators) { result << qMakePair(Id::fromSetting(generator.name), Tr::tr("%1 (via cmake)").arg(generator.name)); - for (const QString &extraGenerator : generator.extraGenerators) { - const QString displayName = extraGenerator + " - " + generator.name; - result << qMakePair(Id::fromSetting(displayName), - Tr::tr("%1 (via cmake)").arg(displayName)); - } } return result; } @@ -1733,7 +1933,8 @@ void CMakeBuildSystem::runGenerator(Id id) { QTC_ASSERT(cmakeBuildConfiguration(), return); const auto showError = [](const QString &detail) { - Core::MessageManager::writeDisrupting(Tr::tr("cmake generator failed: %1.").arg(detail)); + Core::MessageManager::writeDisrupting( + addCMakePrefix(Tr::tr("cmake generator failed: %1.").arg(detail))); }; const CMakeTool * const cmakeTool = CMakeKitAspect::cmakeTool(buildConfiguration()->target()->kit()); @@ -1763,7 +1964,8 @@ void CMakeBuildSystem::runGenerator(Id id) QList<CMakeConfigItem> configItems = Utils::filtered(m_configurationChanges.toList(), itemFilter); const QList<CMakeConfigItem> initialConfigItems - = Utils::filtered(initialCMakeConfiguration().toList(), itemFilter); + = Utils::filtered(cmakeBuildConfiguration()->initialCMakeArguments.cmakeConfiguration().toList(), + itemFilter); for (const CMakeConfigItem &item : std::as_const(initialConfigItems)) { if (!Utils::contains(configItems, [&item](const CMakeConfigItem &existingItem) { return existingItem.key == item.key; @@ -1773,23 +1975,24 @@ void CMakeBuildSystem::runGenerator(Id id) } for (const CMakeConfigItem &item : std::as_const(configItems)) cmdLine.addArg(item.toArgument(buildConfiguration()->macroExpander())); - if (const auto optionsAspect = buildConfiguration()->aspect<AdditionalCMakeOptionsAspect>(); - optionsAspect && !optionsAspect->value().isEmpty()) { - cmdLine.addArgs(optionsAspect->value(), CommandLine::Raw); - } + + cmdLine.addArgs(cmakeBuildConfiguration()->additionalCMakeOptions(), CommandLine::Raw); + const auto proc = new Process(this); connect(proc, &Process::done, proc, &Process::deleteLater); connect(proc, &Process::readyReadStandardOutput, this, [proc] { - Core::MessageManager::writeFlashing(QString::fromLocal8Bit(proc->readAllRawStandardOutput())); + Core::MessageManager::writeFlashing( + addCMakePrefix(QString::fromLocal8Bit(proc->readAllRawStandardOutput()).split('\n'))); }); connect(proc, &Process::readyReadStandardError, this, [proc] { - Core::MessageManager::writeDisrupting(QString::fromLocal8Bit(proc->readAllRawStandardError())); + Core::MessageManager::writeDisrupting( + addCMakePrefix(QString::fromLocal8Bit(proc->readAllRawStandardError()).split('\n'))); }); proc->setWorkingDirectory(outDir); proc->setEnvironment(buildConfiguration()->environment()); proc->setCommand(cmdLine); - Core::MessageManager::writeFlashing( - Tr::tr("Running in %1: %2.").arg(outDir.toUserOutput(), cmdLine.toUserOutput())); + Core::MessageManager::writeFlashing(addCMakePrefix( + Tr::tr("Running in \"%1\": %2.").arg(outDir.toUserOutput(), cmdLine.toUserOutput()))); proc->start(); } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index 4770b4d84bc..bf0f2c581d2 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -18,7 +18,10 @@ namespace ProjectExplorer { class ExtraCompiler; class FolderNode; } -namespace Utils { class Process; } +namespace Utils { + class Process; + class Link; +} namespace CMakeProjectManager { @@ -40,6 +43,7 @@ public: ~CMakeBuildSystem() final; void triggerParsing() final; + void requestDebugging() final; bool supportsAction(ProjectExplorer::Node *context, ProjectExplorer::ProjectAction action, @@ -67,6 +71,7 @@ public: void runCMake(); void runCMakeAndScanProjectTree(); void runCMakeWithExtraArguments(); + void runCMakeWithProfiling(); void stopCMakeRun(); bool persistCMakeState(); @@ -101,7 +106,6 @@ public: CMakeProject *project() const; QString cmakeBuildType() const; - void setCMakeBuildType(const QString &cmakeBuildType, bool quiet = false); ProjectExplorer::BuildConfiguration::BuildType buildType() const; CMakeConfig configurationFromCMake() const; @@ -109,22 +113,19 @@ public: QStringList configurationChangesArguments(bool initialParameters = false) const; - QStringList initialCMakeArguments() const; - CMakeConfig initialCMakeConfiguration() const; - - QStringList additionalCMakeArguments() const; - void setAdditionalCMakeArguments(const QStringList &args); - - void filterConfigArgumentsFromAdditionalCMakeArguments(); - void setConfigurationFromCMake(const CMakeConfig &config); void setConfigurationChanges(const CMakeConfig &config); - void setInitialCMakeArguments(const QStringList &args); - QString error() const; QString warning() const; + const QHash<QString, Utils::Link> &cmakeSymbolsHash() const { return m_cmakeSymbolsHash; } + CMakeKeywords projectKeywords() const { return m_projectKeywords; } + QStringList projectImportedTargets() const { return m_projectImportedTargets; } + QStringList projectFindPackageVariables() const { return m_projectFindPackageVariables; } + const QHash<QString, Utils::Link> &dotCMakeFilesHash() const { return m_dotCMakeFilesHash; } + const QHash<QString, Utils::Link> &findPackagesFilesHash() const { return m_findPackagesFilesHash; } + signals: void configurationCleared(); void configurationChanged(const CMakeConfig &config); @@ -132,6 +133,8 @@ signals: void warningOccurred(const QString &message); private: + CMakeConfig initialCMakeConfiguration() const; + QList<QPair<Utils::Id, QString>> generators() const override; void runGenerator(Utils::Id id) override; ProjectExplorer::ExtraCompiler *findExtraCompiler( @@ -152,6 +155,8 @@ private: = (1 << 1), // Force initial configuration arguments to cmake REPARSE_FORCE_EXTRA_CONFIGURATION = (1 << 2), // Force extra configuration arguments to cmake REPARSE_URGENT = (1 << 3), // Do not delay the parser run by 1s + REPARSE_DEBUG = (1 << 4), // Start with debugging + REPARSE_PROFILING = (1 << 5), // Start profiling }; void reparse(int reparseParameters); QString reparseParametersString(int reparseFlags); @@ -197,6 +202,8 @@ private: void runCTest(); + void setupCMakeSymbolsHash(); + struct ProjectFileArgumentPosition { cmListFileArgument argumentPosition; @@ -222,6 +229,12 @@ private: QList<ProjectExplorer::ExtraCompiler *> m_extraCompilers; QList<CMakeBuildTarget> m_buildTargets; QSet<CMakeFileInfo> m_cmakeFiles; + QHash<QString, Utils::Link> m_cmakeSymbolsHash; + QHash<QString, Utils::Link> m_dotCMakeFilesHash; + QHash<QString, Utils::Link> m_findPackagesFilesHash; + CMakeKeywords m_projectKeywords; + QStringList m_projectImportedTargets; + QStringList m_projectFindPackageVariables; QHash<QString, ProjectFileArgumentPosition> m_filesToBeRenamed; diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index 2646c5c5808..b02b2e779c0 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -372,9 +372,9 @@ CMakeConfig CMakeConfig::fromFile(const Utils::FilePath &cacheFile, QString *err continue; QTC_ASSERT(pieces.size() == 3, continue); - const QByteArray key = pieces.at(0); - const QByteArray type = pieces.at(1); - const QByteArray value = pieces.at(2); + const QByteArray &key = pieces.at(0); + const QByteArray &type = pieces.at(1); + const QByteArray &value = pieces.at(2); if (key.endsWith("-ADVANCED") && value == "1") { advancedSet.insert(key.left(key.size() - 9 /* "-ADVANCED" */)); diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 4bbe75b77c0..85822697d2e 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -2,24 +2,38 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "cmakeeditor.h" +#include "cmaketoolmanager.h" #include "cmakeautocompleter.h" +#include "cmakebuildsystem.h" #include "cmakefilecompletionassist.h" #include "cmakeindenter.h" #include "cmakeprojectconstants.h" +#include "3rdparty/cmake/cmListFileCache.h" + #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreplugintr.h> +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectmanager.h> +#include <projectexplorer/projecttree.h> +#include <projectexplorer/target.h> +#include <texteditor/basehoverhandler.h> #include <texteditor/textdocument.h> #include <texteditor/texteditoractionhandler.h> +#include <utils/textutils.h> +#include <utils/tooltip/tooltip.h> -#include <QDir> #include <QTextDocument> #include <functional> using namespace Core; +using namespace ProjectExplorer; +using namespace Utils; using namespace TextEditor; namespace CMakeProjectManager::Internal { @@ -30,56 +44,53 @@ namespace CMakeProjectManager::Internal { class CMakeEditor : public TextEditor::BaseTextEditor { + CMakeKeywords m_keywords; public: + CMakeEditor(); void contextHelp(const HelpCallback &callback) const final; }; +CMakeEditor::CMakeEditor() +{ + if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool()) + m_keywords = tool->keywords(); +} + void CMakeEditor::contextHelp(const HelpCallback &callback) const { - int pos = position(); + auto helpPrefix = [this](const QString &word) { + if (m_keywords.includeStandardModules.contains(word)) + return "module/"; + if (m_keywords.functions.contains(word)) + return "command/"; + if (m_keywords.variables.contains(word)) + return "variable/"; + if (m_keywords.directoryProperties.contains(word)) + return "prop_dir/"; + if (m_keywords.targetProperties.contains(word)) + return "prop_tgt/"; + if (m_keywords.sourceProperties.contains(word)) + return "prop_sf/"; + if (m_keywords.testProperties.contains(word)) + return "prop_test/"; + if (m_keywords.properties.contains(word)) + return "prop_gbl/"; + if (m_keywords.policies.contains(word)) + return "policy/"; + if (m_keywords.environmentVariables.contains(word)) + return "envvar/"; - QChar chr; - do { - --pos; - if (pos < 0) - break; - chr = characterAt(pos); - if (chr == QLatin1Char('(')) { - BaseTextEditor::contextHelp(callback); - return; - } - } while (chr.unicode() != QChar::ParagraphSeparator); + return "unknown/"; + }; - ++pos; - chr = characterAt(pos); - while (chr.isSpace()) { - ++pos; - chr = characterAt(pos); - } - int begin = pos; - - do { - ++pos; - chr = characterAt(pos); - } while (chr.isLetterOrNumber() || chr == QLatin1Char('_')); - int end = pos; - - while (chr.isSpace()) { - ++pos; - chr = characterAt(pos); - } - - // Not a command - if (chr != QLatin1Char('(')) { + const QString word = Utils::Text::wordUnderCursor(editorWidget()->textCursor()); + const QString id = helpPrefix(word) + word; + if (id.startsWith("unknown/")) { BaseTextEditor::contextHelp(callback); return; } - const QString id = "command/" + textAt(begin, end - begin).toLower(); - callback({{id, Utils::Text::wordUnderCursor(editorWidget()->textCursor())}, - {}, - {}, - HelpItem::Unknown}); + callback({{id, word}, {}, {}, HelpItem::Unknown}); } // @@ -92,7 +103,6 @@ public: ~CMakeEditorWidget() final = default; private: - bool save(const QString &fileName = QString()); void findLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &processLinkCallback, bool resolveTarget = true, @@ -120,7 +130,7 @@ static QString unescape(const QString &s) { QString result; int i = 0; - const int size = s.size(); + const qsizetype size = s.size(); while (i < size) { const QChar c = s.at(i); if (c == '\\' && i < size - 1) { @@ -137,6 +147,53 @@ static QString unescape(const QString &s) return result; } +static bool isValidUrlChar(const QChar &c) +{ + static QSet<QChar> urlChars{'-', '.', '_', '~', ':', '/', '?', '#', '[', ']', '@', '!', + '$', '&', '\'', '(', ')', '*', '+', ',', ';', '%', '='}; + + return (c.isLetterOrNumber() || urlChars.contains(c)) && !c.isSpace(); +} + +static bool isValidIdentifierChar(const QChar &chr) +{ + return chr.isLetterOrNumber() || chr == '_' || chr == '-'; +} + +QHash<QString, Utils::Link> getLocalSymbolsHash(const QByteArray &content, const Utils::FilePath &filePath, QString &projectName) +{ + cmListFile cmakeListFile; + if (!content.isEmpty()) { + std::string errorString; + const std::string fileName = "buffer"; + if (!cmakeListFile.ParseString(content.toStdString(), fileName, errorString)) + return {}; + } + + QHash<QString, Utils::Link> hash; + for (const auto &func : cmakeListFile.Functions) { + if (func.LowerCaseName() == "project" && func.Arguments().size() > 0) { + projectName = QString::fromUtf8(func.Arguments()[0].Value); + continue; + } + + if (func.LowerCaseName() != "function" && func.LowerCaseName() != "macro" + && func.LowerCaseName() != "set" && func.LowerCaseName() != "option") + continue; + + if (func.Arguments().size() == 0) + continue; + auto arg = func.Arguments()[0]; + + Utils::Link link; + link.targetFilePath = filePath; + link.targetLine = arg.Line; + link.targetColumn = arg.Column - 1; + hash.insert(QString::fromUtf8(arg.Value), link); + } + return hash; +} + void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &processLinkCallback, bool/* resolveTarget*/, @@ -150,14 +207,50 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, const QString block = cursor.block().text(); + int beginPos = 0; + int endPos = 0; + auto addTextStartEndToLink = [&](Utils::Link &link) { + link.linkTextStart = cursor.position() - column + beginPos + 1; + link.linkTextEnd = cursor.position() - column + endPos; + return link; + }; + // check if the current position is commented out - const int hashPos = block.indexOf(QLatin1Char('#')); - if (hashPos >= 0 && hashPos < column) + const qsizetype hashPos = block.indexOf(QLatin1Char('#')); + if (hashPos >= 0 && hashPos < column) { + // Check to see if we have a https:// link + QString buffer; + beginPos = column - 1; + while (beginPos > hashPos) { + if (isValidUrlChar(block[beginPos])) { + buffer.prepend(block.at(beginPos)); + beginPos--; + } else { + break; + } + } + // find the end of the url + endPos = column; + while (endPos < block.size()) { + if (isValidUrlChar(block[endPos])) { + buffer.append(block.at(endPos)); + endPos++; + } else { + break; + } + } + if (buffer.startsWith("http")) { + link.targetFilePath = FilePath::fromPathPart(buffer); + addTextStartEndToLink(link); + return processLinkCallback(link); + } + return processLinkCallback(link); + } // find the beginning of a filename QString buffer; - int beginPos = column - 1; + beginPos = column - 1; while (beginPos >= 0) { if (isValidFileNameChar(block, beginPos)) { buffer.prepend(block.at(beginPos)); @@ -168,7 +261,7 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, } // find the end of a filename - int endPos = column; + endPos = column; while (endPos < block.size()) { if (isValidFileNameChar(block, endPos)) { buffer.append(block.at(endPos)); @@ -181,26 +274,136 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, if (buffer.isEmpty()) return processLinkCallback(link); - QDir dir(textDocument()->filePath().toFileInfo().absolutePath()); + const Utils::FilePath dir = textDocument()->filePath().absolutePath(); buffer.replace("${CMAKE_CURRENT_SOURCE_DIR}", dir.path()); buffer.replace("${CMAKE_CURRENT_LIST_DIR}", dir.path()); + + // Lambdas to find the CMake function name + auto findFunctionStart = [cursor, this]() -> int { + int pos = cursor.position(); + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr != '('); + + if (pos > 0 && chr == '(') { + // allow space between function name and ( + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr.isSpace()); + ++pos; + } + return pos; + }; + auto findFunctionEnd = [cursor, this]() -> int { + int pos = cursor.position(); + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr != ')'); + return pos; + }; + auto findWordStart = [cursor, this](int pos) -> int { + // Find start position + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && isValidIdentifierChar(chr)); + + return ++pos; + }; + const int funcStart = findFunctionStart(); + const int funcEnd = findFunctionEnd(); + + // Resolve local variables and functions + QString projectName; + auto hash = getLocalSymbolsHash(textDocument()->textAt(0, funcEnd + 1).toUtf8(), + textDocument()->filePath(), + projectName); + if (!projectName.isEmpty()) + buffer.replace("${PROJECT_NAME}", projectName); + + if (auto project = ProjectTree::currentProject()) { + buffer.replace("${CMAKE_SOURCE_DIR}", project->projectDirectory().path()); + if (auto bs = ProjectTree::currentBuildSystem(); bs->buildConfiguration()) { + buffer.replace("${CMAKE_BINARY_DIR}", bs->buildConfiguration()->buildDirectory().path()); + + // Get the path suffix from current source dir to project source dir and apply it + // for the binary dir + const QString relativePathSuffix = textDocument() + ->filePath() + .parentDir() + .relativePathFrom(project->projectDirectory()) + .path(); + buffer.replace("${CMAKE_CURRENT_BINARY_DIR}", + bs->buildConfiguration() + ->buildDirectory() + .pathAppended(relativePathSuffix) + .path()); + + // Check if the symbols is a user defined function or macro + if (const auto cbs = qobject_cast<const CMakeBuildSystem *>(bs)) { + // Strip variable coating + if (buffer.startsWith("${") && buffer.endsWith("}")) + buffer = buffer.mid(2, buffer.size() - 3); + + if (cbs->cmakeSymbolsHash().contains(buffer)) { + link = cbs->cmakeSymbolsHash().value(buffer); + addTextStartEndToLink(link); + return processLinkCallback(link); + } + + // Handle include(CMakeFileWithoutSuffix) and find_package(Package) + QString functionName; + if (funcStart > funcEnd) { + int funcStartPos = findWordStart(funcStart); + functionName = textDocument()->textAt(funcStartPos, funcStart - funcStartPos); + + struct FunctionToHash + { + QString functionName; + const QHash<QString, Utils::Link> &hash; + } functionToHashes[] = {{"include", cbs->dotCMakeFilesHash()}, + {"find_package", cbs->findPackagesFilesHash()}}; + + for (const auto &pair : functionToHashes) { + if (functionName == pair.functionName && pair.hash.contains(buffer)) { + link = pair.hash.value(buffer); + addTextStartEndToLink(link); + return processLinkCallback(link); + } + } + } + } + } + } // TODO: Resolve more variables - QString fileName = dir.filePath(unescape(buffer)); - QFileInfo fi(fileName); - if (fi.exists()) { - if (fi.isDir()) { - QDir subDir(fi.absoluteFilePath()); - QString subProject = subDir.filePath(QLatin1String("CMakeLists.txt")); - if (QFileInfo::exists(subProject)) + // Strip variable coating + if (buffer.startsWith("${") && buffer.endsWith("}")) + buffer = buffer.mid(2, buffer.size() - 3); + + if (hash.contains(buffer)) { + link = hash.value(buffer); + addTextStartEndToLink(link); + return processLinkCallback(link); + } + + Utils::FilePath fileName = dir.withNewPath(unescape(buffer)); + if (fileName.isRelativePath()) + fileName = dir.pathAppended(fileName.path()); + if (fileName.exists()) { + if (fileName.isDir()) { + Utils::FilePath subProject = fileName.pathAppended("CMakeLists.txt"); + if (subProject.exists()) fileName = subProject; else return processLinkCallback(link); } - link.targetFilePath = Utils::FilePath::fromString(fileName); - link.linkTextStart = cursor.position() - column + beginPos + 1; - link.linkTextEnd = cursor.position() - column + endPos; + link.targetFilePath = fileName; + addTextStartEndToLink(link); } + processLinkCallback(link); } @@ -212,6 +415,88 @@ static TextDocument *createCMakeDocument() return doc; } +// +// CMakeHoverHandler +// + +class CMakeHoverHandler : public TextEditor::BaseHoverHandler +{ + mutable CMakeKeywords m_keywords; + QString m_helpToolTip; + QVariant m_contextHelp; + +public: + const CMakeKeywords &keywords() const; + + void identifyMatch(TextEditor::TextEditorWidget *editorWidget, + int pos, + ReportPriority report) final; + void operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) final; +}; + +const CMakeKeywords &CMakeHoverHandler::keywords() const +{ + if (m_keywords.functions.isEmpty()) + if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool()) + m_keywords = tool->keywords(); + + return m_keywords; +} + +void CMakeHoverHandler::identifyMatch(TextEditor::TextEditorWidget *editorWidget, + int pos, + ReportPriority report) +{ + const QScopeGuard cleanup([this, report] { report(priority()); }); + + QTextCursor cursor = editorWidget->textCursor(); + cursor.setPosition(pos); + const QString word = Utils::Text::wordUnderCursor(cursor); + + FilePath helpFile; + QString helpCategory; + struct + { + const QMap<QString, Utils::FilePath> ↦ + QString helpCategory; + } keywordsListMaps[] = {{keywords().functions, "command"}, + {keywords().variables, "variable"}, + {keywords().directoryProperties, "prop_dir"}, + {keywords().sourceProperties, "prop_sf"}, + {keywords().targetProperties, "prop_tgt"}, + {keywords().testProperties, "prop_test"}, + {keywords().properties, "prop_gbl"}, + {keywords().includeStandardModules, "module"}, + {keywords().findModules, "module"}, + {keywords().policies, "policy"}, + {keywords().environmentVariables, "envvar"}}; + + for (const auto &pair : keywordsListMaps) { + if (pair.map.contains(word)) { + helpFile = pair.map.value(word); + helpCategory = pair.helpCategory; + break; + } + } + m_helpToolTip.clear(); + if (!helpFile.isEmpty()) + m_helpToolTip = CMakeToolManager::toolTipForRstHelpFile(helpFile); + + m_contextHelp = QVariant::fromValue( + HelpItem({QString("%1/%2").arg(helpCategory, word), word}, {}, {}, HelpItem::Unknown)); + + setPriority(!m_helpToolTip.isEmpty() ? Priority_Tooltip : Priority_None); +} + +void CMakeHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) +{ + if (!m_helpToolTip.isEmpty() && toolTip() != m_helpToolTip) + Utils::ToolTip::show(point, m_helpToolTip, Qt::MarkdownText, editorWidget, m_contextHelp); + else if (m_helpToolTip.isEmpty()) + Utils::ToolTip::hide(); + setToolTip(m_helpToolTip); +} + // // CMakeEditorFactory // @@ -235,11 +520,13 @@ CMakeEditorFactory::CMakeEditorFactory() setAutoCompleterCreator([] { return new CMakeAutoCompleter; }); setEditorActionHandlers(TextEditorActionHandler::UnCommentSelection - | TextEditorActionHandler::JumpToFileUnderCursor - | TextEditorActionHandler::Format); + | TextEditorActionHandler::FollowSymbolUnderCursor + | TextEditorActionHandler::Format); + + addHoverHandler(new CMakeHoverHandler); ActionContainer *contextMenu = ActionManager::createMenu(Constants::M_CONTEXT); - contextMenu->addAction(ActionManager::command(TextEditor::Constants::JUMP_TO_FILE_UNDER_CURSOR)); + contextMenu->addAction(ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR)); contextMenu->addSeparator(Context(Constants::CMAKE_EDITOR_ID)); contextMenu->addAction(ActionManager::command(TextEditor::Constants::UN_COMMENT_SELECTION)); } diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp index 01fbeaa2329..6dc754d2bed 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp @@ -3,53 +3,624 @@ #include "cmakefilecompletionassist.h" -#include "cmakekitinformation.h" +#include "cmakebuildsystem.h" +#include "cmakebuildtarget.h" +#include "cmakebuildconfiguration.h" +#include "cmakeconfigitem.h" #include "cmakeprojectconstants.h" #include "cmaketool.h" +#include "cmaketoolmanager.h" + +#include "3rdparty/cmake/cmListFileCache.h" #include <projectexplorer/project.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/projecttree.h> #include <projectexplorer/target.h> #include <texteditor/codeassist/assistinterface.h> +#include <texteditor/codeassist/genericproposal.h> +#include <texteditor/texteditorsettings.h> -#include <QFileInfo> +#include <utils/async.h> +#include <utils/fsengine/fileiconprovider.h> +#include <utils/utilsicons.h> using namespace TextEditor; using namespace ProjectExplorer; +using namespace Utils; namespace CMakeProjectManager::Internal { -class CMakeFileCompletionAssist : public KeywordsCompletionAssistProcessor +class PerformInputData; +using PerformInputDataPtr = std::shared_ptr<PerformInputData>; + +class CMakeFileCompletionAssist : public AsyncProcessor { public: CMakeFileCompletionAssist(); - IAssistProposal *performAsync() final; + IAssistProposal *perform() final; + IAssistProposal *performAsync() final { return nullptr; } + + const QIcon m_variableIcon; + const QIcon m_projectVariableIcon; + const QIcon m_functionIcon; + const QIcon m_projectFunctionIcon; + const QIcon m_propertyIcon; + const QIcon m_argsIcon; + const QIcon m_genexIcon; + const QIcon m_moduleIcon; + const QIcon m_targetsIcon; + const QIcon m_importedTargetIcon; + + TextEditor::SnippetAssistCollector m_snippetCollector; + +private: + IAssistProposal *doPerform(const PerformInputDataPtr &data); + PerformInputDataPtr generatePerformInputData() const; }; -CMakeFileCompletionAssist::CMakeFileCompletionAssist() : - KeywordsCompletionAssistProcessor(Keywords()) +CMakeFileCompletionAssist::CMakeFileCompletionAssist() + : m_variableIcon(CodeModelIcon::iconForType(CodeModelIcon::VarPublic)) + , m_projectVariableIcon(CodeModelIcon::iconForType(CodeModelIcon::VarPublicStatic)) + , m_functionIcon(CodeModelIcon::iconForType(CodeModelIcon::FuncPublic)) + , m_projectFunctionIcon(CodeModelIcon::iconForType(CodeModelIcon::FuncPublicStatic)) + , m_propertyIcon(CodeModelIcon::iconForType(CodeModelIcon::Property)) + , m_argsIcon(CodeModelIcon::iconForType(CodeModelIcon::Enum)) + , m_genexIcon(CodeModelIcon::iconForType(CodeModelIcon::Class)) + , m_moduleIcon( + ProjectExplorer::DirectoryIcon(ProjectExplorer::Constants::FILEOVERLAY_MODULES).icon()) + , m_targetsIcon(ProjectExplorer::Icons::BUILD_SMALL.icon()) + , m_importedTargetIcon(Icon({{":/projectexplorer/images/buildhammerhandle.png", + Theme::IconsCodeModelKeywordColor}, + {":/projectexplorer/images/buildhammerhead.png", + Theme::IconsCodeModelKeywordColor}}, + Icon::MenuTintedStyle) + .icon()) + , m_snippetCollector(Constants::CMAKE_SNIPPETS_GROUP_ID, + FileIconProvider::icon(FilePath::fromString("CMakeLists.txt"))) +{} + +static bool isInComment(const AssistInterface *interface) { - setSnippetGroup(Constants::CMAKE_SNIPPETS_GROUP_ID); - setDynamicCompletionFunction(&TextEditor::pathComplete); + QTextCursor tc(interface->textDocument()); + tc.setPosition(interface->position()); + tc.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor); + return tc.selectedText().contains('#'); } -IAssistProposal *CMakeFileCompletionAssist::performAsync() +static bool isValidIdentifierChar(const QChar &chr) { - Keywords kw; - const Utils::FilePath &filePath = interface()->filePath(); - if (!filePath.isEmpty() && filePath.toFileInfo().isFile()) { - Project *p = ProjectManager::projectForFile(filePath); - if (p && p->activeTarget()) { - CMakeTool *cmake = CMakeKitAspect::cmakeTool(p->activeTarget()->kit()); - if (cmake && cmake->isValid()) - kw = cmake->keywords(); + return chr.isLetterOrNumber() || chr == '_' || chr == '-'; +} + +static int findWordStart(const AssistInterface *interface, int pos) +{ + // Find start position + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && isValidIdentifierChar(chr)); + + return ++pos; +} + +static int findFunctionStart(const AssistInterface *interface) +{ + int pos = interface->position(); + + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr != '('); + + if (pos > 0 && chr == '(') { + // allow space between function name and ( + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr.isSpace()); + ++pos; + } + + return pos; +} + +static int findFunctionEnd(const AssistInterface *interface) +{ + int pos = interface->position(); + + QChar chr; + do { + chr = interface->characterAt(--pos); + } while (pos > 0 && chr != ')'); + + return pos; +} + +static int findPathStart(const AssistInterface *interface) +{ + // For pragmatic reasons, we don't support spaces in file names here. + static const auto canOccurInFilePath = [](const QChar &c) { + return c.isLetterOrNumber() || c == '.' || c == '/' || c == '_' || c == '-'; + }; + + int pos = interface->position(); + QChar chr; + // Skip to the start of a name + do { + chr = interface->characterAt(--pos); + } while (canOccurInFilePath(chr)); + + return ++pos; +} + +struct MarkDownAssitProposalItem : public AssistProposalItem +{ + Qt::TextFormat detailFormat() const override { return Qt::MarkdownText; } +}; + +template<typename T> +static QList<AssistProposalItemInterface *> generateList(const T &words, const QIcon &icon) +{ + return transform<QList>(words, [&icon](const QString &word) -> AssistProposalItemInterface * { + AssistProposalItem *item = new AssistProposalItem(); + item->setText(word); + item->setIcon(icon); + return item; + }); +} + +static QList<AssistProposalItemInterface *> generateList(const QMap<QString, FilePath> &words, + const QIcon &icon) +{ + QList<AssistProposalItemInterface *> list; + for (auto it = words.cbegin(); it != words.cend(); ++it) { + MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem(); + item->setText(it.key()); + if (!it.value().isEmpty()) + item->setDetail(CMakeToolManager::toolTipForRstHelpFile(it.value())); + item->setIcon(icon); + list << item; + } + return list; +} + +static QList<AssistProposalItemInterface *> generateList( + const CMakeConfig &cache, + const QIcon &icon, + const QList<AssistProposalItemInterface *> &existingList) +{ + QHash<QString, AssistProposalItemInterface *> hash; + for (const auto &item : existingList) + hash.insert(item->text(), item); + + auto makeDetail = [](const CMakeConfigItem &item) { + QString detail = QString("### %1 (cache)").arg(QString::fromUtf8(item.key)); + + if (!item.documentation.isEmpty()) + detail.append(QString("\n%1\n").arg(QString::fromUtf8(item.documentation))); + else + detail.append("\n"); + + const QString value = item.toString(); + if (!value.isEmpty()) + detail.append(QString("\n```\n%1\n```\n").arg(value)); + + return detail; + }; + + QList<AssistProposalItemInterface *> list; + for (auto it = cache.cbegin(); it != cache.cend(); ++it) { + if (it->isAdvanced || it->isUnset || it->type == CMakeConfig::Type::INTERNAL) + continue; + + QString text = QString::fromUtf8(it->key); + if (!hash.contains(text)) { + MarkDownAssitProposalItem *item = new MarkDownAssitProposalItem(); + item->setText(text); + item->setDetail(makeDetail(*it)); + item->setIcon(icon); + list << item; + } else { + auto item = static_cast<AssistProposalItem *>(hash.value(text)); + + QString detail = item->detail(); + detail.append("\n"); + detail.append(makeDetail(*it)); + + item->setDetail(detail); + } + } + return list; +} + +static int addFilePathItems(const AssistInterface *interface, + QList<AssistProposalItemInterface *> &items, + int symbolStartPos) +{ + if (interface->filePath().isEmpty()) + return symbolStartPos; + + const int startPos = findPathStart(interface); + + if (interface->reason() == IdleEditor + && interface->position() - startPos + < TextEditorSettings::completionSettings().m_characterThreshold) + return symbolStartPos; + + const QString word = interface->textAt(startPos, interface->position() - startPos); + FilePath baseDir = interface->filePath().absoluteFilePath().parentDir(); + const qsizetype lastSlashPos = word.lastIndexOf(QLatin1Char('/')); + + QString prefix = word; + if (lastSlashPos != -1) { + prefix = word.mid(lastSlashPos + 1); + baseDir = baseDir.pathAppended(word.left(lastSlashPos)); + } + + const FilePaths filesPaths = baseDir.dirEntries( + FileFilter({QString("%1*").arg(prefix)}, QDir::AllEntries | QDir::NoDotAndDotDot)); + for (const auto &file : filesPaths) { + AssistProposalItem *item = new AssistProposalItem; + QString fileName = file.fileName(); + if (file.isDir()) + fileName.append("/"); + item->setText(fileName); + item->setIcon(FileIconProvider::icon(file)); + + items << item; + } + + return startPos; +} + +static cmListFile parseCMakeListFromBuffer(const QByteArray &content) +{ + cmListFile cmakeListFile; + std::string errorString; + if (!content.isEmpty()) { + const std::string fileName = "buffer"; + if (!cmakeListFile.ParseString(content.toStdString(), fileName, errorString)) + return {}; + } + return cmakeListFile; +} + +static QPair<QStringList, QStringList> getLocalFunctionsAndVariables(const cmListFile &cmakeListFile) +{ + QStringList variables; + QStringList functions; + for (const auto &func : cmakeListFile.Functions) { + if (func.Arguments().size() == 0) + continue; + + if (func.LowerCaseName() == "macro" || func.LowerCaseName() == "function") + functions << QString::fromUtf8(func.Arguments()[0].Value); + if (func.LowerCaseName() == "set" || func.LowerCaseName() == "option") + variables << QString::fromUtf8(func.Arguments()[0].Value); + } + return {functions, variables}; +} + +static void updateCMakeConfigurationWithLocalData(CMakeConfig &cmakeCache, + const cmListFile &cmakeListFile, + const FilePath ¤tDir) +{ + auto isValidCMakeVariable = [](const std::string &var) { + return var == "CMAKE_PREFIX_PATH" || var == "CMAKE_MODULE_PATH"; + }; + + const FilePath projectDir = ProjectTree::currentBuildSystem()->projectDirectory(); + auto updateDirVariables = [currentDir, projectDir, cmakeCache](QByteArray &value) { + value.replace("${CMAKE_CURRENT_SOURCE_DIR}", currentDir.path().toUtf8()); + value.replace("${CMAKE_CURRENT_LIST_DIR}", currentDir.path().toUtf8()); + value.replace("${CMAKE_SOURCE_DIR}", projectDir.path().toUtf8()); + value.replace("${CMAKE_PREFIX_PATH}", cmakeCache.valueOf("CMAKE_PREFIX_PATH")); + value.replace("${CMAKE_MODULE_PATH}", cmakeCache.valueOf("CMAKE_MODULE_PATH")); + }; + + auto insertOrAppendListValue = [&cmakeCache](const QByteArray &key, const QByteArray &value) { + auto it = std::find_if(cmakeCache.begin(), cmakeCache.end(), [key](const auto &item) { + return item.key == key; + }); + if (it == cmakeCache.end()) { + cmakeCache << CMakeConfigItem(key, value); + } else { + it->value.append(";"); + it->value.append(value); + } + }; + + for (const auto &func : cmakeListFile.Functions) { + const bool isSet = func.LowerCaseName() == "set" && func.Arguments().size() > 1; + const bool isList = func.LowerCaseName() == "list" && func.Arguments().size() > 2; + if (!isSet && !isList) + continue; + + QByteArray key; + QByteArray value; + if (isSet) { + const auto firstArg = func.Arguments()[0]; + const auto secondArg = func.Arguments()[1]; + if (!isValidCMakeVariable(firstArg.Value)) + continue; + key = QByteArray::fromStdString(firstArg.Value); + value = QByteArray::fromStdString(secondArg.Value); + } + if (isList) { + const auto firstArg = func.Arguments()[0]; + const auto secondArg = func.Arguments()[1]; + const auto thirdArg = func.Arguments()[2]; + if (firstArg.Value != "APPEND" || !isValidCMakeVariable(secondArg.Value)) + continue; + key = QByteArray::fromStdString(secondArg.Value); + value = QByteArray::fromStdString(thirdArg.Value); + } + updateDirVariables(value); + insertOrAppendListValue(key, value); + } +} + +static QPair<QStringList, QStringList> getFindAndConfigCMakePackages( + const CMakeConfig &cmakeCache, const Environment &environment) +{ + auto toFilePath = [](const QByteArray &str) -> FilePath { + return FilePath::fromUserInput(QString::fromUtf8(str)); + }; + + auto findPackageName = [](const QString &fileName) -> QString { + auto findIdx = fileName.indexOf("Find"); + auto endsWithCMakeIdx = fileName.lastIndexOf(".cmake"); + if (findIdx == 0 && endsWithCMakeIdx > 0) + return fileName.mid(4, endsWithCMakeIdx - 4); + return QString(); + }; + + auto configPackageName = [](const QString &fileName) -> QString { + auto configCMakeIdx = fileName.lastIndexOf("Config.cmake"); + if (configCMakeIdx > 0) + return fileName.left(configCMakeIdx); + auto dashConfigCMakeIdx = fileName.lastIndexOf("-config.cmake"); + if (dashConfigCMakeIdx > 0) + return fileName.left(dashConfigCMakeIdx); + return QString(); + }; + + QStringList modulePackages; + QStringList configPackages; + + struct + { + const QByteArray cmakeVariable; + const QString pathPrefix; + std::function<QString(const QString &)> function; + QStringList &result; + } mapping[] = {{"CMAKE_PREFIX_PATH", "lib/cmake", configPackageName, configPackages}, + {"CMAKE_PREFIX_PATH", "share", configPackageName, configPackages}, + {"CMAKE_MODULE_PATH", QString(), findPackageName, modulePackages}, + {"CMAKE_MODULE_PATH", QString(), configPackageName, configPackages}}; + + for (const auto &m : mapping) { + FilePaths paths = Utils::transform<FilePaths>(cmakeCache.valueOf(m.cmakeVariable).split(';'), + toFilePath); + + paths << Utils::transform<FilePaths>(environment.value(QString::fromUtf8(m.cmakeVariable)) + .split(";"), + &FilePath::fromUserInput); + + for (const auto &prefix : paths) { + // Only search for directories if we have a prefix + const FilePaths dirs = !m.pathPrefix.isEmpty() + ? prefix.pathAppended(m.pathPrefix) + .dirEntries({{"*"}, QDir::Dirs | QDir::NoDotAndDotDot}) + : FilePaths{prefix}; + const QStringList cmakeFiles + = Utils::transform<QStringList>(dirs, [](const FilePath &path) { + return Utils::transform(path.dirEntries({{"*.cmake"}, QDir::Files}, + QDir::Name), + &FilePath::fileName); + }); + m.result << Utils::transform(cmakeFiles, m.function); + } + m.result = Utils::filtered(m.result, std::not_fn(&QString::isEmpty)); + } + + return {modulePackages, configPackages}; +} + +class PerformInputData +{ +public: + CMakeKeywords keywords; + QMap<QString, FilePath> projectVariables; + QMap<QString, FilePath> projectFunctions; + QStringList buildTargets; + QStringList importedTargets; + QStringList findPackageVariables; + CMakeConfig cmakeConfiguration; + Environment environment = Environment::systemEnvironment(); +}; + +PerformInputDataPtr CMakeFileCompletionAssist::generatePerformInputData() const +{ + PerformInputDataPtr data = PerformInputDataPtr(new PerformInputData); + + const FilePath &filePath = interface()->filePath(); + if (!filePath.isEmpty() && filePath.isFile()) { + if (auto tool = CMakeToolManager::defaultProjectOrDefaultCMakeTool()) + data->keywords = tool->keywords(); + } + + if (auto bs = qobject_cast<CMakeBuildSystem *>(ProjectTree::currentBuildSystem())) { + for (const auto &target : std::as_const(bs->buildTargets())) + if (target.targetType != TargetType::UtilityType) + data->buildTargets << target.title; + const CMakeKeywords &projectKeywords = bs->projectKeywords(); + data->projectVariables = projectKeywords.variables; + data->projectFunctions = projectKeywords.functions; + data->importedTargets = bs->projectImportedTargets(); + data->findPackageVariables = bs->projectFindPackageVariables(); + data->cmakeConfiguration = bs->configurationFromCMake(); + data->environment = bs->cmakeBuildConfiguration()->configureEnvironment(); + } + + return data; +} + +IAssistProposal *CMakeFileCompletionAssist::perform() +{ + IAssistProposal *result = immediateProposal(); + interface()->prepareForAsyncUse(); + m_watcher.setFuture(Utils::asyncRun([this, inputData = generatePerformInputData()] { + interface()->recreateTextDocument(); + return doPerform(inputData); + })); + return result; +} + +IAssistProposal *CMakeFileCompletionAssist::doPerform(const PerformInputDataPtr &data) +{ + if (isInComment(interface())) + return nullptr; + + const int startPos = findWordStart(interface(), interface()->position()); + const int functionStart = findFunctionStart(interface()); + const int prevFunctionEnd = findFunctionEnd(interface()); + + QString functionName; + if (functionStart > prevFunctionEnd) { + const int functionStartPos = findWordStart(interface(), functionStart); + functionName = interface()->textAt(functionStartPos, functionStart - functionStartPos); + } + + if (interface()->reason() == IdleEditor) { + const QChar chr = interface()->characterAt(interface()->position()); + const int wordSize = interface()->position() - startPos; + if (isValidIdentifierChar(chr) + || wordSize < TextEditorSettings::completionSettings().m_characterThreshold) { + return nullptr; } } - setKeywords(kw); - return KeywordsCompletionAssistProcessor::performAsync(); + cmListFile cmakeListFile = parseCMakeListFromBuffer( + interface()->textAt(0, prevFunctionEnd + 1).toUtf8()); + auto [localFunctions, localVariables] = getLocalFunctionsAndVariables(cmakeListFile); + + CMakeConfig cmakeConfiguration = data->cmakeConfiguration; + const FilePath currentDir = interface()->filePath().absolutePath(); + updateCMakeConfigurationWithLocalData(cmakeConfiguration, cmakeListFile, currentDir); + + auto [findModules, configModules] = getFindAndConfigCMakePackages(cmakeConfiguration, + data->environment); + + QList<AssistProposalItemInterface *> items; + + const QString varGenexToken = interface()->textAt(startPos - 2, 2); + const QString varEnvironmentToken = interface()->textAt(startPos - 5, 5); + if (varGenexToken == "${" || varGenexToken == "$<" || varEnvironmentToken == "$ENV{") { + if (varGenexToken == "${") { + items.append(generateList(data->keywords.variables, m_variableIcon)); + items.append(generateList(data->projectVariables, m_projectVariableIcon)); + items.append(generateList(data->findPackageVariables, m_projectVariableIcon)); + } + if (varGenexToken == "$<") + items.append(generateList(data->keywords.generatorExpressions, m_genexIcon)); + + if (varEnvironmentToken == "$ENV{") + items.append(generateList(data->keywords.environmentVariables, m_variableIcon)); + + return new GenericProposal(startPos, items); + } + + const QString ifEnvironmentToken = interface()->textAt(startPos - 4, 4); + if ((functionName == "if" || functionName == "elseif") && ifEnvironmentToken == "ENV{") + items.append(generateList(data->keywords.environmentVariables, m_variableIcon)); + + int fileStartPos = startPos; + const auto onlyFileItems = [&] { return fileStartPos != startPos; }; + + if (functionName == "if" || functionName == "elseif" || functionName == "while" + || functionName == "set" || functionName == "list" + || functionName == "cmake_print_variables") { + items.append(generateList(data->keywords.variables, m_variableIcon)); + items.append(generateList(data->projectVariables, m_projectVariableIcon)); + items.append(generateList(data->findPackageVariables, m_projectVariableIcon)); + items.append(generateList(localVariables, m_variableIcon)); + items.append(generateList(cmakeConfiguration, m_variableIcon, items)); + } + + if (functionName == "if" || functionName == "elseif" || functionName == "cmake_policy") + items.append(generateList(data->keywords.policies, m_variableIcon)); + + if (functionName.contains("path") || functionName.contains("file") + || functionName.contains("add_executable") || functionName.contains("add_library") + || functionName == "include" || functionName == "add_subdirectory" + || functionName == "install" || functionName == "target_sources" + || functionName == "set" || functionName == "list") { + fileStartPos = addFilePathItems(interface(), items, startPos); + } + + if (functionName == "set_property" || functionName == "cmake_print_properties") + items.append(generateList(data->keywords.properties, m_propertyIcon)); + + if (functionName == "set_directory_properties") + items.append(generateList(data->keywords.directoryProperties, m_propertyIcon)); + if (functionName == "set_source_files_properties") + items.append(generateList(data->keywords.sourceProperties, m_propertyIcon)); + if (functionName == "set_target_properties") + items.append(generateList(data->keywords.targetProperties, m_propertyIcon)); + if (functionName == "set_tests_properties") + items.append(generateList(data->keywords.testProperties, m_propertyIcon)); + + if (functionName == "include" && !onlyFileItems()) + items.append(generateList(data->keywords.includeStandardModules, m_moduleIcon)); + if (functionName == "find_package") { + items.append(generateList(data->keywords.findModules, m_moduleIcon)); + items.append(generateList(findModules, m_moduleIcon)); + items.append(generateList(configModules, m_moduleIcon)); + } + + if ((functionName.contains("target") || functionName == "install" + || functionName == "add_dependencies" || functionName == "set_property" + || functionName == "export" || functionName == "cmake_print_properties" + || functionName == "if" || functionName == "elseif") + && !onlyFileItems()) { + items.append(generateList(data->buildTargets, m_targetsIcon)); + items.append(generateList(data->importedTargets, m_importedTargetIcon)); + } + + if (data->keywords.functionArgs.contains(functionName) && !onlyFileItems()) { + const QStringList functionSymbols = data->keywords.functionArgs.value(functionName); + items.append(generateList(functionSymbols, m_argsIcon)); + } else if (functionName.isEmpty()) { + // On a new line we just want functions + items.append(generateList(data->keywords.functions, m_functionIcon)); + items.append(generateList(data->projectFunctions, m_projectFunctionIcon)); + items.append(generateList(localFunctions, m_functionIcon)); + + // Snippets would make more sense only for the top level suggestions + items.append(m_snippetCollector.collect()); + } else { + // Inside an unknown function we could have variables or properties + fileStartPos = addFilePathItems(interface(), items, startPos); + if (!onlyFileItems()) { + items.append(generateList(data->keywords.variables, m_variableIcon)); + items.append(generateList(data->projectVariables, m_projectVariableIcon)); + items.append(generateList(localVariables, m_variableIcon)); + items.append(generateList(cmakeConfiguration, m_variableIcon, items)); + items.append(generateList(data->findPackageVariables, m_projectVariableIcon)); + + items.append(generateList(data->keywords.properties, m_propertyIcon)); + items.append(generateList(data->buildTargets, m_targetsIcon)); + items.append(generateList(data->importedTargets, m_importedTargetIcon)); + } + } + + return new GenericProposal(startPos, items); } IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor(const AssistInterface *) const @@ -57,4 +628,15 @@ IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor(const Assis return new CMakeFileCompletionAssist; } -} // CMakeProjectManager::Internal +int CMakeFileCompletionAssistProvider::activationCharSequenceLength() const +{ + return 4; +} + +bool CMakeFileCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const +{ + return sequence.endsWith("${") || sequence.endsWith("$<") || sequence.endsWith("/") + || sequence.endsWith("(") || sequence.endsWith("ENV{"); +} + +} // namespace CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h index 1b60a2e72f6..9b692c895c4 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h @@ -3,7 +3,7 @@ #pragma once -#include <texteditor/codeassist/keywordscompletionassist.h> +#include <texteditor/codeassist/completionassistprovider.h> namespace CMakeProjectManager::Internal { @@ -11,6 +11,8 @@ class CMakeFileCompletionAssistProvider : public TextEditor::CompletionAssistPro { public: TextEditor::IAssistProcessor *createProcessor(const TextEditor::AssistInterface *) const final; + int activationCharSequenceLength() const final; + bool isActivationCharSequence(const QString &sequence) const final; }; } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp index 8a727ba7942..58a3c537973 100644 --- a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp @@ -17,6 +17,7 @@ #include <coreplugin/idocument.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/projecttree.h> @@ -37,19 +38,15 @@ using namespace Utils; namespace CMakeProjectManager::Internal { -class CMakeFormatterPrivate : public PagedSettings +class CMakeFormatterSettings : public AspectContainer { public: - CMakeFormatterPrivate() + CMakeFormatterSettings() { + setAutoApply(false); setSettingsGroups(Constants::CMAKEFORMATTER_SETTINGS_GROUP, Constants::CMAKEFORMATTER_GENERAL_GROUP); - setId(Constants::Settings::FORMATTER_ID); - setDisplayName(Tr::tr("Formatter")); - setDisplayCategory("CMake"); - setCategory(Constants::Settings::CATEGORY); - command.setSettingsKey("autoFormatCommand"); command.setDefaultValue("cmake-format"); command.setExpectedKind(PathChooser::ExistingCommand); @@ -69,8 +66,15 @@ public: setLayouter([this] { using namespace Layouting; + + auto cmakeFormatter = new QLabel( + Tr::tr("<a href=\"%1\">CMakeFormat</a> command:") + .arg("qthelp://org.qt-project.qtcreator/doc/" + "creator-project-cmake.html#formatting-cmake-files")); + cmakeFormatter->setOpenExternalLinks(true); + return Column { - Row { Tr::tr("CMakeFormat command:"), command }, + Row { cmakeFormatter, command }, Space(10), Group { title(Tr::tr("Automatic Formatting on File Save")), @@ -89,19 +93,26 @@ public: ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID); menu->menu()->setTitle(Tr::tr("CMakeFormatter")); + menu->menu()->setIcon(ProjectExplorer::Icons::CMAKE_LOGO.icon()); menu->setOnAllDisabledBehavior(ActionContainer::Show); ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu); Core::Command *cmd = ActionManager::registerAction(&formatFile, Constants::CMAKEFORMATTER_ACTION_ID); connect(&formatFile, &QAction::triggered, this, [this] { - TextEditor::formatCurrentFile(formatCommand()); + auto command = formatCommand(); + if (auto editor = EditorManager::currentEditor()) + extendCommandWithConfigs(command, editor->document()->filePath()); + + TextEditor::formatCurrentFile(command); }); ActionManager::actionContainer(Constants::CMAKEFORMATTER_MENU_ID)->addAction(cmd); auto updateActions = [this] { auto editor = EditorManager::currentEditor(); - formatFile.setEnabled(editor && isApplicable(editor->document())); + + formatFile.setEnabled(haveValidFormatCommand && editor + && isApplicable(editor->document())); }; connect(&autoFormatMime, &Utils::StringAspect::changed, @@ -109,9 +120,18 @@ public: connect(EditorManager::instance(), &EditorManager::currentEditorChanged, this, updateActions); connect(EditorManager::instance(), &EditorManager::aboutToSave, - this, &CMakeFormatterPrivate::applyIfNecessary); + this, &CMakeFormatterSettings::applyIfNecessary); readSettings(); + + const FilePath commandPath = command().searchInPath(); + haveValidFormatCommand = commandPath.exists() && commandPath.isExecutableFile(); + + formatFile.setEnabled(haveValidFormatCommand); + connect(&command, &FilePathAspect::validChanged, this, [this](bool validState) { + haveValidFormatCommand = validState; + formatFile.setEnabled(haveValidFormatCommand); + }); } bool isApplicable(const IDocument *document) const; @@ -128,7 +148,48 @@ public: return cmd; } + static FilePaths formatConfigFiles(const FilePath &dir) + { + if (dir.isEmpty()) + return FilePaths(); + + return filtered(transform({".cmake-format", + ".cmake-format.py", + ".cmake-format.json", + ".cmake-format.yaml", + "cmake-format.py", + "cmake-format.json", + "cmake-format.yaml"}, + [dir](const QString &fileName) { + return dir.pathAppended(fileName); + }), + &FilePath::exists); + } + + static FilePaths findConfigs(const FilePath &fileName) + { + FilePath parentDirectory = fileName.parentDir(); + while (parentDirectory.exists()) { + FilePaths configFiles = formatConfigFiles(parentDirectory); + if (!configFiles.isEmpty()) + return configFiles; + + parentDirectory = parentDirectory.parentDir(); + } + return FilePaths(); + } + + static void extendCommandWithConfigs(TextEditor::Command &command, const FilePath &source) + { + const FilePaths configFiles = findConfigs(source); + if (!configFiles.isEmpty()) { + command.addOption("--config-files"); + command.addOptions(Utils::transform(configFiles, &FilePath::nativePath)); + } + } + FilePathAspect command{this}; + bool haveValidFormatCommand{false}; BoolAspect autoFormatOnSave{this}; BoolAspect autoFormatOnlyCurrentProject{this}; StringAspect autoFormatMime{this}; @@ -136,15 +197,15 @@ public: QAction formatFile{Tr::tr("Format &Current File")}; }; -bool CMakeFormatterPrivate::isApplicable(const IDocument *document) const +bool CMakeFormatterSettings::isApplicable(const IDocument *document) const { if (!document) return false; - if (autoFormatMime.value().isEmpty()) + if (autoFormatMime().isEmpty()) return true; - const QStringList allowedMimeTypes = autoFormatMime.value().split(';'); + const QStringList allowedMimeTypes = autoFormatMime().split(';'); const MimeType documentMimeType = Utils::mimeTypeForName(document->mimeType()); return anyOf(allowedMimeTypes, [&documentMimeType](const QString &mime) { @@ -152,9 +213,9 @@ bool CMakeFormatterPrivate::isApplicable(const IDocument *document) const }); } -void CMakeFormatterPrivate::applyIfNecessary(IDocument *document) const +void CMakeFormatterSettings::applyIfNecessary(IDocument *document) const { - if (!autoFormatOnSave.value()) + if (!autoFormatOnSave()) return; if (!document) @@ -164,7 +225,7 @@ void CMakeFormatterPrivate::applyIfNecessary(IDocument *document) const return; // Check if file is contained in the current project (if wished) - if (autoFormatOnlyCurrentProject.value()) { + if (autoFormatOnlyCurrentProject()) { const ProjectExplorer::Project *pro = ProjectExplorer::ProjectTree::currentProject(); if (!pro || pro->files([document](const ProjectExplorer::Node *n) { return ProjectExplorer::Project::SourceFiles(n) @@ -184,24 +245,36 @@ void CMakeFormatterPrivate::applyIfNecessary(IDocument *document) const IEditor *currentEditor = EditorManager::currentEditor(); IEditor *editor = editors.contains(currentEditor) ? currentEditor : editors.first(); - if (auto widget = TextEditorWidget::fromEditor(editor)) + if (auto widget = TextEditorWidget::fromEditor(editor)) { + extendCommandWithConfigs(command, editor->document()->filePath()); TextEditor::formatEditor(widget, command); + } } -// CMakeFormatter +CMakeFormatterSettings &formatterSettings() +{ + static CMakeFormatterSettings theSettings; + return theSettings; +} + +class CMakeFormatterSettingsPage final : public Core::IOptionsPage +{ +public: + CMakeFormatterSettingsPage() + { + setId(Constants::Settings::FORMATTER_ID); + setDisplayName(Tr::tr("Formatter")); + setDisplayCategory("CMake"); + setCategory(Constants::Settings::CATEGORY); + setSettingsProvider([] { return &formatterSettings(); }); + } +}; + +const CMakeFormatterSettingsPage settingsPage; CMakeFormatter::CMakeFormatter() - : d(new CMakeFormatterPrivate) -{} - -CMakeFormatter::~CMakeFormatter() { - delete d; -} - -void CMakeFormatter::applyIfNecessary(IDocument *document) const -{ - d->applyIfNecessary(document); + formatterSettings(); } } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.h b/src/plugins/cmakeprojectmanager/cmakeformatter.h index 9727c245a50..bb3ea353537 100644 --- a/src/plugins/cmakeprojectmanager/cmakeformatter.h +++ b/src/plugins/cmakeprojectmanager/cmakeformatter.h @@ -12,12 +12,6 @@ class CMakeFormatter { public: CMakeFormatter(); - ~CMakeFormatter(); - - void applyIfNecessary(Core::IDocument *document) const; - -private: - class CMakeFormatterPrivate *d = nullptr; }; } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp index 07d3c6fd478..6043f11adfb 100644 --- a/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeinstallstep.cpp @@ -5,7 +5,7 @@ #include "cmakeabstractprocessstep.h" #include "cmakebuildsystem.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeparser.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" @@ -16,6 +16,7 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> + #include <utils/layoutbuilder.h> using namespace Core; @@ -24,37 +25,30 @@ using namespace Utils; namespace CMakeProjectManager::Internal { -const char CMAKE_ARGUMENTS_KEY[] = "CMakeProjectManager.InstallStep.CMakeArguments"; - // CMakeInstallStep class CMakeInstallStep : public CMakeAbstractProcessStep { public: - CMakeInstallStep(ProjectExplorer::BuildStepList *bsl, Id id); + CMakeInstallStep(BuildStepList *bsl, Id id) + : CMakeAbstractProcessStep(bsl, id) + { + cmakeArguments.setSettingsKey("CMakeProjectManager.InstallStep.CMakeArguments"); + cmakeArguments.setLabelText(Tr::tr("CMake arguments:")); + cmakeArguments.setDisplayStyle(StringAspect::LineEditDisplay); + + setCommandLineProvider([this] { return cmakeCommand(); }); + } private: CommandLine cmakeCommand() const; - void finish(ProcessResult result) override; - void setupOutputFormatter(OutputFormatter *formatter) override; QWidget *createConfigWidget() override; - StringAspect *m_cmakeArguments = nullptr; + StringAspect cmakeArguments{this}; }; -CMakeInstallStep::CMakeInstallStep(BuildStepList *bsl, Id id) - : CMakeAbstractProcessStep(bsl, id) -{ - m_cmakeArguments = addAspect<StringAspect>(); - m_cmakeArguments->setSettingsKey(CMAKE_ARGUMENTS_KEY); - m_cmakeArguments->setLabelText(Tr::tr("CMake arguments:")); - m_cmakeArguments->setDisplayStyle(StringAspect::LineEditDisplay); - - setCommandLineProvider([this] { return cmakeCommand(); }); -} - void CMakeInstallStep::setupOutputFormatter(OutputFormatter *formatter) { CMakeParser *cmakeParser = new CMakeParser; @@ -82,18 +76,11 @@ CommandLine CMakeInstallStep::cmakeCommand() const cmd.addArg(bs->cmakeBuildType()); } - if (!m_cmakeArguments->value().isEmpty()) - cmd.addArgs(m_cmakeArguments->value(), CommandLine::Raw); + cmd.addArgs(cmakeArguments(), CommandLine::Raw); return cmd; } -void CMakeInstallStep::finish(ProcessResult result) -{ - emit progress(100, {}); - AbstractProcessStep::finish(result); -} - QWidget *CMakeInstallStep::createConfigWidget() { auto updateDetails = [this] { @@ -107,11 +94,11 @@ QWidget *CMakeInstallStep::createConfigWidget() setDisplayName(Tr::tr("Install", "ConfigWidget display name.")); using namespace Layouting; - auto widget = Form { m_cmakeArguments, noMargin }.emerge(); + auto widget = Form { cmakeArguments, noMargin }.emerge(); updateDetails(); - connect(m_cmakeArguments, &StringAspect::changed, this, updateDetails); + connect(&cmakeArguments, &StringAspect::changed, this, updateDetails); connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp similarity index 84% rename from src/plugins/cmakeprojectmanager/cmakekitinformation.cpp rename to src/plugins/cmakeprojectmanager/cmakekitaspect.cpp index 54add7395f4..6c112f01d5c 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitaspect.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 Canonical Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeconfigitem.h" #include "cmakeprojectconstants.h" @@ -10,14 +10,12 @@ #include "cmaketool.h" #include "cmaketoolmanager.h" -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <ios/iosconstants.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorersettings.h> @@ -25,7 +23,7 @@ #include <projectexplorer/toolchain.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/commandline.h> @@ -41,6 +39,7 @@ #include <QDialog> #include <QDialogButtonBox> #include <QGridLayout> +#include <QGuiApplication> #include <QLineEdit> #include <QPlainTextEdit> #include <QPointer> @@ -64,29 +63,83 @@ static Id defaultCMakeToolId() return defaultTool ? defaultTool->id() : Id(); } -class CMakeKitAspectWidget final : public KitAspectWidget +// Factories + +class CMakeKitAspectFactory : public KitAspectFactory { public: - CMakeKitAspectWidget(Kit *kit, const KitAspect *ki) : KitAspectWidget(kit, ki), + CMakeKitAspectFactory(); + + // KitAspect interface + Tasks validate(const Kit *k) const final; + void setup(Kit *k) final; + void fix(Kit *k) final; + ItemList toUserOutput(const Kit *k) const final; + KitAspect *createKitAspect(Kit *k) const final; + + void addToMacroExpander(Kit *k, Utils::MacroExpander *expander) const final; + + QSet<Utils::Id> availableFeatures(const Kit *k) const final; +}; + +class CMakeGeneratorKitAspectFactory : public KitAspectFactory +{ +public: + CMakeGeneratorKitAspectFactory(); + + Tasks validate(const Kit *k) const final; + void setup(Kit *k) final; + void fix(Kit *k) final; + void upgrade(Kit *k) final; + ItemList toUserOutput(const Kit *k) const final; + KitAspect *createKitAspect(Kit *k) const final; + void addToBuildEnvironment(const Kit *k, Utils::Environment &env) const final; + +private: + QVariant defaultValue(const Kit *k) const; +}; + +class CMakeConfigurationKitAspectFactory : public KitAspectFactory +{ +public: + CMakeConfigurationKitAspectFactory(); + + // KitAspect interface + Tasks validate(const Kit *k) const final; + void setup(Kit *k) final; + void fix(Kit *k) final; + ItemList toUserOutput(const Kit *k) const final; + KitAspect *createKitAspect(Kit *k) const final; + +private: + QVariant defaultValue(const Kit *k) const; +}; + +// Implementations + +class CMakeKitAspectImpl final : public KitAspect +{ +public: + CMakeKitAspectImpl(Kit *kit, const KitAspectFactory *factory) : KitAspect(kit, factory), m_comboBox(createSubWidget<QComboBox>()), m_manageButton(createManageButton(Constants::Settings::TOOLS_ID)) { m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); m_comboBox->setEnabled(false); - m_comboBox->setToolTip(ki->description()); + m_comboBox->setToolTip(factory->description()); refresh(); connect(m_comboBox, &QComboBox::currentIndexChanged, - this, &CMakeKitAspectWidget::currentCMakeToolChanged); + this, &CMakeKitAspectImpl::currentCMakeToolChanged); CMakeToolManager *cmakeMgr = CMakeToolManager::instance(); - connect(cmakeMgr, &CMakeToolManager::cmakeAdded, this, &CMakeKitAspectWidget::refresh); - connect(cmakeMgr, &CMakeToolManager::cmakeRemoved, this, &CMakeKitAspectWidget::refresh); - connect(cmakeMgr, &CMakeToolManager::cmakeUpdated, this, &CMakeKitAspectWidget::refresh); + connect(cmakeMgr, &CMakeToolManager::cmakeAdded, this, &CMakeKitAspectImpl::refresh); + connect(cmakeMgr, &CMakeToolManager::cmakeRemoved, this, &CMakeKitAspectImpl::refresh); + connect(cmakeMgr, &CMakeToolManager::cmakeUpdated, this, &CMakeKitAspectImpl::refresh); } - ~CMakeKitAspectWidget() override + ~CMakeKitAspectImpl() override { delete m_comboBox; delete m_manageButton; @@ -96,7 +149,7 @@ private: // KitAspectWidget interface void makeReadOnly() override { m_comboBox->setEnabled(false); } - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -163,22 +216,27 @@ private: QWidget *m_manageButton; }; -CMakeKitAspect::CMakeKitAspect() +CMakeKitAspectFactory::CMakeKitAspectFactory() { - setObjectName(QLatin1String("CMakeKitAspect")); setId(Constants::TOOL_ID); setDisplayName(Tr::tr("CMake Tool")); setDescription(Tr::tr("The CMake Tool to use when building a project with CMake.<br>" "This setting is ignored when using other build systems.")); setPriority(20000); + auto updateKits = [this] { + if (KitManager::isLoaded()) { + for (Kit *k : KitManager::kits()) + fix(k); + } + }; + //make sure the default value is set if a selected CMake is removed - connect(CMakeToolManager::instance(), &CMakeToolManager::cmakeRemoved, - this, [this] { for (Kit *k : KitManager::kits()) fix(k); }); + connect(CMakeToolManager::instance(), &CMakeToolManager::cmakeRemoved, this, updateKits); //make sure the default value is set if a new default CMake is set connect(CMakeToolManager::instance(), &CMakeToolManager::defaultCMakeChanged, - this, [this] { for (Kit *k : KitManager::kits()) fix(k); }); + this, updateKits); } Id CMakeKitAspect::id() @@ -206,20 +264,21 @@ void CMakeKitAspect::setCMakeTool(Kit *k, const Id id) k->setValue(Constants::TOOL_ID, toSet.toSetting()); } -Tasks CMakeKitAspect::validate(const Kit *k) const +Tasks CMakeKitAspectFactory::validate(const Kit *k) const { Tasks result; CMakeTool *tool = CMakeKitAspect::cmakeTool(k); if (tool && tool->isValid()) { CMakeTool::Version version = tool->version(); if (version.major < 3 || (version.major == 3 && version.minor < 14)) { - result << BuildSystemTask(Task::Warning, msgUnsupportedVersion(version.fullVersion)); + result << BuildSystemTask(Task::Warning, + CMakeKitAspect::msgUnsupportedVersion(version.fullVersion)); } } return result; } -void CMakeKitAspect::setup(Kit *k) +void CMakeKitAspectFactory::setup(Kit *k) { CMakeTool *tool = CMakeKitAspect::cmakeTool(k); if (tool) @@ -230,32 +289,32 @@ void CMakeKitAspect::setup(Kit *k) for (CMakeTool *tool : CMakeToolManager::cmakeTools()) { const QString toolSource = tool->detectionSource(); if (!toolSource.isEmpty() && toolSource == kitSource) { - setCMakeTool(k, tool->id()); + CMakeKitAspect::setCMakeTool(k, tool->id()); return; } } - setCMakeTool(k, defaultCMakeToolId()); + CMakeKitAspect::setCMakeTool(k, defaultCMakeToolId()); } -void CMakeKitAspect::fix(Kit *k) +void CMakeKitAspectFactory::fix(Kit *k) { setup(k); } -KitAspect::ItemList CMakeKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList CMakeKitAspectFactory::toUserOutput(const Kit *k) const { - const CMakeTool *const tool = cmakeTool(k); + const CMakeTool *const tool = CMakeKitAspect::cmakeTool(k); return {{Tr::tr("CMake"), tool ? tool->displayName() : Tr::tr("Unconfigured")}}; } -KitAspectWidget *CMakeKitAspect::createConfigWidget(Kit *k) const +KitAspect *CMakeKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new CMakeKitAspectWidget(k, this); + return new CMakeKitAspectImpl(k, this); } -void CMakeKitAspect::addToMacroExpander(Kit *k, MacroExpander *expander) const +void CMakeKitAspectFactory::addToMacroExpander(Kit *k, MacroExpander *expander) const { QTC_ASSERT(k, return); expander->registerFileVariables("CMake:Executable", Tr::tr("Path to the cmake executable"), @@ -265,9 +324,9 @@ void CMakeKitAspect::addToMacroExpander(Kit *k, MacroExpander *expander) const }); } -QSet<Id> CMakeKitAspect::availableFeatures(const Kit *k) const +QSet<Id> CMakeKitAspectFactory::availableFeatures(const Kit *k) const { - if (cmakeTool(k)) + if (CMakeKitAspect::cmakeTool(k)) return { CMakeProjectManager::Constants::CMAKE_FEATURE_ID }; return {}; } @@ -290,27 +349,27 @@ const char EXTRA_GENERATOR_KEY[] = "ExtraGenerator"; const char PLATFORM_KEY[] = "Platform"; const char TOOLSET_KEY[] = "Toolset"; -class CMakeGeneratorKitAspectWidget final : public KitAspectWidget +class CMakeGeneratorKitAspectImpl final : public KitAspect { public: - CMakeGeneratorKitAspectWidget(Kit *kit, const KitAspect *ki) - : KitAspectWidget(kit, ki), + CMakeGeneratorKitAspectImpl(Kit *kit, const KitAspectFactory *factory) + : KitAspect(kit, factory), m_label(createSubWidget<ElidingLabel>()), m_changeButton(createSubWidget<QPushButton>()) { const CMakeTool *tool = CMakeKitAspect::cmakeTool(kit); - connect(this, &KitAspectWidget::labelLinkActivated, this, [=](const QString &) { + connect(this, &KitAspect::labelLinkActivated, this, [=](const QString &) { CMakeTool::openCMakeHelpUrl(tool, "%1/manual/cmake-generators.7.html"); }); - m_label->setToolTip(ki->description()); + m_label->setToolTip(factory->description()); m_changeButton->setText(Tr::tr("Change...")); refresh(); connect(m_changeButton, &QPushButton::clicked, - this, &CMakeGeneratorKitAspectWidget::changeGenerator); + this, &CMakeGeneratorKitAspectImpl::changeGenerator); } - ~CMakeGeneratorKitAspectWidget() override + ~CMakeGeneratorKitAspectImpl() override { delete m_label; delete m_changeButton; @@ -320,7 +379,7 @@ private: // KitAspectWidget interface void makeReadOnly() override { m_changeButton->setEnabled(false); } - void addToLayout(Layouting::LayoutItem &parent) override + void addToLayoutImpl(Layouting::LayoutItem &parent) override { addMutableAction(m_label); parent.addItem(m_label); @@ -335,14 +394,10 @@ private: m_changeButton->setEnabled(m_currentTool); const QString generator = CMakeGeneratorKitAspect::generator(kit()); - const QString extraGenerator = CMakeGeneratorKitAspect::extraGenerator(kit()); const QString platform = CMakeGeneratorKitAspect::platform(kit()); const QString toolset = CMakeGeneratorKitAspect::toolset(kit()); QStringList messageLabel; - if (!extraGenerator.isEmpty()) - messageLabel << extraGenerator << " - "; - messageLabel << generator; if (!platform.isEmpty()) @@ -371,7 +426,6 @@ private: cmakeLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); auto generatorCombo = new QComboBox; - auto extraGeneratorCombo = new QComboBox; auto platformEdit = new QLineEdit; auto toolsetEdit = new QLineEdit; @@ -383,10 +437,6 @@ private: layout->addWidget(new QLabel(Tr::tr("Generator:")), row, 0); layout->addWidget(generatorCombo, row, 1); - ++row; - layout->addWidget(new QLabel(Tr::tr("Extra generator:")), row, 0); - layout->addWidget(extraGeneratorCombo, row, 1); - ++row; layout->addWidget(new QLabel(Tr::tr("Platform:")), row, 0); layout->addWidget(platformEdit, row, 1); @@ -410,19 +460,13 @@ private: for (auto it = generatorList.constBegin(); it != generatorList.constEnd(); ++it) generatorCombo->addItem(it->name); - auto updateDialog = [&generatorList, generatorCombo, extraGeneratorCombo, + auto updateDialog = [&generatorList, generatorCombo, platformEdit, toolsetEdit](const QString &name) { const auto it = std::find_if(generatorList.constBegin(), generatorList.constEnd(), [name](const CMakeTool::Generator &g) { return g.name == name; }); QTC_ASSERT(it != generatorList.constEnd(), return); generatorCombo->setCurrentText(name); - extraGeneratorCombo->clear(); - extraGeneratorCombo->addItem(Tr::tr("<none>"), QString()); - for (const QString &eg : std::as_const(it->extraGenerators)) - extraGeneratorCombo->addItem(eg, eg); - extraGeneratorCombo->setEnabled(extraGeneratorCombo->count() > 1); - platformEdit->setEnabled(it->supportsPlatform); toolsetEdit->setEnabled(it->supportsToolset); }; @@ -430,7 +474,6 @@ private: updateDialog(CMakeGeneratorKitAspect::generator(kit())); generatorCombo->setCurrentText(CMakeGeneratorKitAspect::generator(kit())); - extraGeneratorCombo->setCurrentText(CMakeGeneratorKitAspect::extraGenerator(kit())); platformEdit->setText(platformEdit->isEnabled() ? CMakeGeneratorKitAspect::platform(kit()) : QString()); toolsetEdit->setText(toolsetEdit->isEnabled() ? CMakeGeneratorKitAspect::toolset(kit()) : QString()); @@ -441,7 +484,6 @@ private: return; CMakeGeneratorKitAspect::set(kit(), generatorCombo->currentText(), - extraGeneratorCombo->currentData().toString(), platformEdit->isEnabled() ? platformEdit->text() : QString(), toolsetEdit->isEnabled() ? toolsetEdit->text() : QString()); @@ -461,11 +503,9 @@ class GeneratorInfo public: GeneratorInfo() = default; GeneratorInfo(const QString &generator_, - const QString &extraGenerator_ = QString(), const QString &platform_ = QString(), const QString &toolset_ = QString()) : generator(generator_) - , extraGenerator(extraGenerator_) , platform(platform_) , toolset(toolset_) {} @@ -512,9 +552,8 @@ static void setGeneratorInfo(Kit *k, const GeneratorInfo &info) k->setValue(GENERATOR_ID, info.toVariant()); } -CMakeGeneratorKitAspect::CMakeGeneratorKitAspect() +CMakeGeneratorKitAspectFactory::CMakeGeneratorKitAspectFactory() { - setObjectName(QLatin1String("CMakeGeneratorKitAspect")); setId(GENERATOR_ID); setDisplayName(Tr::tr("CMake <a href=\"generator\">generator</a>")); setDescription(Tr::tr("CMake generator defines how a project is built when using CMake.<br>" @@ -527,11 +566,6 @@ QString CMakeGeneratorKitAspect::generator(const Kit *k) return generatorInfo(k).generator; } -QString CMakeGeneratorKitAspect::extraGenerator(const Kit *k) -{ - return generatorInfo(k).extraGenerator; -} - QString CMakeGeneratorKitAspect::platform(const Kit *k) { return generatorInfo(k).platform; @@ -549,13 +583,6 @@ void CMakeGeneratorKitAspect::setGenerator(Kit *k, const QString &generator) setGeneratorInfo(k, info); } -void CMakeGeneratorKitAspect::setExtraGenerator(Kit *k, const QString &extraGenerator) -{ - GeneratorInfo info = generatorInfo(k); - info.extraGenerator = extraGenerator; - setGeneratorInfo(k, info); -} - void CMakeGeneratorKitAspect::setPlatform(Kit *k, const QString &platform) { GeneratorInfo info = generatorInfo(k); @@ -572,11 +599,10 @@ void CMakeGeneratorKitAspect::setToolset(Kit *k, const QString &toolset) void CMakeGeneratorKitAspect::set(Kit *k, const QString &generator, - const QString &extraGenerator, const QString &platform, const QString &toolset) { - GeneratorInfo info(generator, extraGenerator, platform, toolset); + GeneratorInfo info(generator, platform, toolset); setGeneratorInfo(k, info); } @@ -587,11 +613,7 @@ QStringList CMakeGeneratorKitAspect::generatorArguments(const Kit *k) if (info.generator.isEmpty()) return result; - if (info.extraGenerator.isEmpty()) { - result.append("-G" + info.generator); - } else { - result.append("-G" + info.extraGenerator + " - " + info.generator); - } + result.append("-G" + info.generator); if (!info.platform.isEmpty()) result.append("-A" + info.platform); @@ -602,7 +624,7 @@ QStringList CMakeGeneratorKitAspect::generatorArguments(const Kit *k) return result; } -CMakeConfig CMakeGeneratorKitAspect::generatorCMakeConfig(const ProjectExplorer::Kit *k) +CMakeConfig CMakeGeneratorKitAspect::generatorCMakeConfig(const Kit *k) { CMakeConfig config; @@ -612,9 +634,6 @@ CMakeConfig CMakeGeneratorKitAspect::generatorCMakeConfig(const ProjectExplorer: config << CMakeConfigItem("CMAKE_GENERATOR", info.generator.toUtf8()); - if (!info.extraGenerator.isEmpty()) - config << CMakeConfigItem("CMAKE_EXTRA_GENERATOR", info.extraGenerator.toUtf8()); - if (!info.platform.isEmpty()) config << CMakeConfigItem("CMAKE_GENERATOR_PLATFORM", info.platform.toUtf8()); @@ -632,7 +651,7 @@ bool CMakeGeneratorKitAspect::isMultiConfigGenerator(const Kit *k) generator == "Ninja Multi-Config"; } -QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const +QVariant CMakeGeneratorKitAspectFactory::defaultValue(const Kit *k) const { QTC_ASSERT(k, return QVariant()); @@ -649,8 +668,7 @@ QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const }); if (it != known.constEnd()) { const bool hasNinja = [k, tool] { - auto settings = Internal::CMakeSpecificSettings::instance(); - if (settings->ninjaPath().isEmpty()) { + if (Internal::settings().ninjaPath().isEmpty()) { auto findNinja = [](const Environment &env) -> bool { return !env.searchInPath("ninja").isEmpty(); }; @@ -710,7 +728,7 @@ QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const return GeneratorInfo(it->name).toVariant(); } -Tasks CMakeGeneratorKitAspect::validate(const Kit *k) const +Tasks CMakeGeneratorKitAspectFactory::validate(const Kit *k) const { CMakeTool *tool = CMakeKitAspect::cmakeTool(k); if (!tool) @@ -727,7 +745,7 @@ Tasks CMakeGeneratorKitAspect::validate(const Kit *k) const const GeneratorInfo info = generatorInfo(k); QList<CMakeTool::Generator> known = tool->supportedGenerators(); auto it = std::find_if(known.constBegin(), known.constEnd(), [info](const CMakeTool::Generator &g) { - return g.matches(info.generator, info.extraGenerator); + return g.matches(info.generator); }); if (it == known.constEnd()) { addWarning(Tr::tr("CMake Tool does not support the configured generator.")); @@ -739,15 +757,15 @@ Tasks CMakeGeneratorKitAspect::validate(const Kit *k) const } if (!tool->hasFileApi()) { addWarning(Tr::tr("The selected CMake binary does not support file-api. " - "%1 will not be able to parse CMake projects.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + "%1 will not be able to parse CMake projects.") + .arg(QGuiApplication::applicationDisplayName())); } } return result; } -void CMakeGeneratorKitAspect::setup(Kit *k) +void CMakeGeneratorKitAspectFactory::setup(Kit *k) { if (!k || k->hasValue(id())) return; @@ -756,7 +774,7 @@ void CMakeGeneratorKitAspect::setup(Kit *k) setGeneratorInfo(k, info); } -void CMakeGeneratorKitAspect::fix(Kit *k) +void CMakeGeneratorKitAspectFactory::fix(Kit *k) { const CMakeTool *tool = CMakeKitAspect::cmakeTool(k); const GeneratorInfo info = generatorInfo(k); @@ -766,7 +784,7 @@ void CMakeGeneratorKitAspect::fix(Kit *k) QList<CMakeTool::Generator> known = tool->supportedGenerators(); auto it = std::find_if(known.constBegin(), known.constEnd(), [info](const CMakeTool::Generator &g) { - return g.matches(info.generator, info.extraGenerator); + return g.matches(info.generator); }); if (it == known.constEnd()) { GeneratorInfo dv; @@ -774,14 +792,13 @@ void CMakeGeneratorKitAspect::fix(Kit *k) setGeneratorInfo(k, dv); } else { const GeneratorInfo dv(isIos(k) ? QString("Xcode") : info.generator, - info.extraGenerator, it->supportsPlatform ? info.platform : QString(), it->supportsToolset ? info.toolset : QString()); setGeneratorInfo(k, dv); } } -void CMakeGeneratorKitAspect::upgrade(Kit *k) +void CMakeGeneratorKitAspectFactory::upgrade(Kit *k) { QTC_ASSERT(k, return); @@ -800,7 +817,7 @@ void CMakeGeneratorKitAspect::upgrade(Kit *k) } } -KitAspect::ItemList CMakeGeneratorKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList CMakeGeneratorKitAspectFactory::toUserOutput(const Kit *k) const { const GeneratorInfo info = generatorInfo(k); QString message; @@ -816,12 +833,12 @@ KitAspect::ItemList CMakeGeneratorKitAspect::toUserOutput(const Kit *k) const return {{Tr::tr("CMake Generator"), message}}; } -KitAspectWidget *CMakeGeneratorKitAspect::createConfigWidget(Kit *k) const +KitAspect *CMakeGeneratorKitAspectFactory::createKitAspect(Kit *k) const { - return new CMakeGeneratorKitAspectWidget(k, this); + return new CMakeGeneratorKitAspectImpl(k, this); } -void CMakeGeneratorKitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const +void CMakeGeneratorKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const { GeneratorInfo info = generatorInfo(k); if (info.generator == "NMake Makefiles JOM") { @@ -845,11 +862,11 @@ const char CMAKE_QMAKE_KEY[] = "QT_QMAKE_EXECUTABLE"; const char CMAKE_PREFIX_PATH_KEY[] = "CMAKE_PREFIX_PATH"; const char QTC_CMAKE_PRESET_KEY[] = "QTC_CMAKE_PRESET"; -class CMakeConfigurationKitAspectWidget final : public KitAspectWidget +class CMakeConfigurationKitAspectWidget final : public KitAspect { public: - CMakeConfigurationKitAspectWidget(Kit *kit, const KitAspect *ki) - : KitAspectWidget(kit, ki), + CMakeConfigurationKitAspectWidget(Kit *kit, const KitAspectFactory *factory) + : KitAspect(kit, factory), m_summaryLabel(createSubWidget<ElidingLabel>()), m_manageButton(createSubWidget<QPushButton>()) { @@ -861,7 +878,7 @@ public: private: // KitAspectWidget interface - void addToLayout(Layouting::LayoutItem &parent) override + void addToLayoutImpl(Layouting::LayoutItem &parent) override { addMutableAction(m_summaryLabel); parent.addItem(m_summaryLabel); @@ -1003,9 +1020,8 @@ private: }; -CMakeConfigurationKitAspect::CMakeConfigurationKitAspect() +CMakeConfigurationKitAspectFactory::CMakeConfigurationKitAspectFactory() { - setObjectName(QLatin1String("CMakeConfigurationKitAspect")); setId(CONFIGURATION_ID); setDisplayName(Tr::tr("CMake Configuration")); setDescription(Tr::tr("Default configuration passed to CMake when setting up a project.")); @@ -1029,14 +1045,14 @@ void CMakeConfigurationKitAspect::setConfiguration(Kit *k, const CMakeConfig &co k->setValue(CONFIGURATION_ID, tmp); } -QString CMakeConfigurationKitAspect::additionalConfiguration(const ProjectExplorer::Kit *k) +QString CMakeConfigurationKitAspect::additionalConfiguration(const Kit *k) { if (!k) return QString(); return k->value(ADDITIONAL_CONFIGURATION_ID).toString(); } -void CMakeConfigurationKitAspect::setAdditionalConfiguration(ProjectExplorer::Kit *k, const QString &config) +void CMakeConfigurationKitAspect::setAdditionalConfiguration(Kit *k, const QString &config) { if (!k) return; @@ -1068,7 +1084,7 @@ QStringList CMakeConfigurationKitAspect::toArgumentsList(const Kit *k) [](const CMakeConfigItem &i) { return i.toArgument(nullptr); }); - current = Utils::filtered(current, [](const QString &s) { return s != "-D" || s != "-U"; }); + current = Utils::filtered(current, [](const QString &s) { return s != "-D" && s != "-U"; }); return current; } @@ -1096,7 +1112,7 @@ void CMakeConfigurationKitAspect::setCMakePreset(Kit *k, const QString &presetNa setConfiguration(k, config); } -CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const ProjectExplorer::Kit *k) +CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const Kit *k) { const CMakeConfig config = configuration(k); return Utils::findOrDefault(config, [](const CMakeConfigItem &item) { @@ -1104,16 +1120,16 @@ CMakeConfigItem CMakeConfigurationKitAspect::cmakePresetConfigItem(const Project }); } -QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const +QVariant CMakeConfigurationKitAspectFactory::defaultValue(const Kit *k) const { // FIXME: Convert preload scripts - CMakeConfig config = defaultConfiguration(k); + CMakeConfig config = CMakeConfigurationKitAspect::defaultConfiguration(k); const QStringList tmp = Utils::transform(config.toList(), [](const CMakeConfigItem &i) { return i.toString(); }); return tmp; } -Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const +Tasks CMakeConfigurationKitAspectFactory::validate(const Kit *k) const { QTC_ASSERT(k, return Tasks()); @@ -1124,7 +1140,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const const QtSupport::QtVersion *const version = QtSupport::QtKitAspect::qtVersion(k); const ToolChain *const tcC = ToolChainKitAspect::cToolChain(k); const ToolChain *const tcCxx = ToolChainKitAspect::cxxToolChain(k); - const CMakeConfig config = configuration(k); + const CMakeConfig config = CMakeConfigurationKitAspect::configuration(k); const bool isQt4 = version && version->qtVersion() < QVersionNumber(5, 0, 0); FilePath qmakePath; // This is relative to the cmake used for building. @@ -1208,27 +1224,69 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const return result; } -void CMakeConfigurationKitAspect::setup(Kit *k) +void CMakeConfigurationKitAspectFactory::setup(Kit *k) { if (k && !k->hasValue(CONFIGURATION_ID)) k->setValue(CONFIGURATION_ID, defaultValue(k)); } -void CMakeConfigurationKitAspect::fix(Kit *k) +void CMakeConfigurationKitAspectFactory::fix(Kit *k) { Q_UNUSED(k) } -KitAspect::ItemList CMakeConfigurationKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList CMakeConfigurationKitAspectFactory::toUserOutput(const Kit *k) const { - return {{Tr::tr("CMake Configuration"), toStringList(k).join("<br>")}}; + return {{Tr::tr("CMake Configuration"), CMakeConfigurationKitAspect::toStringList(k).join("<br>")}}; } -KitAspectWidget *CMakeConfigurationKitAspect::createConfigWidget(Kit *k) const +KitAspect *CMakeConfigurationKitAspectFactory::createKitAspect(Kit *k) const { if (!k) return nullptr; return new CMakeConfigurationKitAspectWidget(k, this); } +// Factory instances; + +CMakeKitAspectFactory &cmakeKitAspectFactory() +{ + static CMakeKitAspectFactory theCMakeKitAspectFactory; + return theCMakeKitAspectFactory; +} + +CMakeGeneratorKitAspectFactory &cmakeGeneratorKitAspectFactory() +{ + static CMakeGeneratorKitAspectFactory theCMakeGeneratorKitAspectFactory; + return theCMakeGeneratorKitAspectFactory; +} + +static CMakeConfigurationKitAspectFactory &cmakeConfigurationKitAspectFactory() +{ + static CMakeConfigurationKitAspectFactory theCMakeConfigurationKitAspectFactory; + return theCMakeConfigurationKitAspectFactory; +} + +KitAspect *CMakeKitAspect::createKitAspect(Kit *k) +{ + return cmakeKitAspectFactory().createKitAspect(k); +} + +KitAspect *CMakeGeneratorKitAspect::createKitAspect(Kit *k) +{ + return cmakeGeneratorKitAspectFactory().createKitAspect(k); +} + +KitAspect *CMakeConfigurationKitAspect::createKitAspect(Kit *k) +{ + return cmakeConfigurationKitAspectFactory().createKitAspect(k); +} + +void CMakeKitAspect::createFactories() +{ + cmakeKitAspectFactory(); + cmakeGeneratorKitAspectFactory(); + cmakeConfigurationKitAspectFactory(); +} + } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.h b/src/plugins/cmakeprojectmanager/cmakekitaspect.h similarity index 50% rename from src/plugins/cmakeprojectmanager/cmakekitinformation.h rename to src/plugins/cmakeprojectmanager/cmakekitaspect.h index 3b85235d917..b85bb7bad31 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.h +++ b/src/plugins/cmakeprojectmanager/cmakekitaspect.h @@ -13,68 +13,44 @@ namespace CMakeProjectManager { class CMakeTool; -class CMAKE_EXPORT CMakeKitAspect : public ProjectExplorer::KitAspect +class CMAKE_EXPORT CMakeKitAspect { public: - CMakeKitAspect(); - static Utils::Id id(); static Utils::Id cmakeToolId(const ProjectExplorer::Kit *k); static CMakeTool *cmakeTool(const ProjectExplorer::Kit *k); static void setCMakeTool(ProjectExplorer::Kit *k, const Utils::Id id); - - // KitAspect interface - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; - void setup(ProjectExplorer::Kit *k) final; - void fix(ProjectExplorer::Kit *k) final; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const final; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const final; - - void addToMacroExpander(ProjectExplorer::Kit *k, Utils::MacroExpander *expander) const final; - - QSet<Utils::Id> availableFeatures(const ProjectExplorer::Kit *k) const final; - static QString msgUnsupportedVersion(const QByteArray &versionString); + + static ProjectExplorer::KitAspect *createKitAspect(ProjectExplorer::Kit *k); + +private: + friend class CMakeToolManager; + static void createFactories(); }; -class CMAKE_EXPORT CMakeGeneratorKitAspect : public ProjectExplorer::KitAspect +class CMAKE_EXPORT CMakeGeneratorKitAspect { public: - CMakeGeneratorKitAspect(); - static QString generator(const ProjectExplorer::Kit *k); - static QString extraGenerator(const ProjectExplorer::Kit *k); static QString platform(const ProjectExplorer::Kit *k); static QString toolset(const ProjectExplorer::Kit *k); static void setGenerator(ProjectExplorer::Kit *k, const QString &generator); - static void setExtraGenerator(ProjectExplorer::Kit *k, const QString &extraGenerator); static void setPlatform(ProjectExplorer::Kit *k, const QString &platform); static void setToolset(ProjectExplorer::Kit *k, const QString &toolset); static void set(ProjectExplorer::Kit *k, const QString &generator, - const QString &extraGenerator, const QString &platform, const QString &toolset); + const QString &platform, const QString &toolset); static QStringList generatorArguments(const ProjectExplorer::Kit *k); static CMakeConfig generatorCMakeConfig(const ProjectExplorer::Kit *k); static bool isMultiConfigGenerator(const ProjectExplorer::Kit *k); - // KitAspect interface - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; - void setup(ProjectExplorer::Kit *k) final; - void fix(ProjectExplorer::Kit *k) final; - void upgrade(ProjectExplorer::Kit *k) final; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const final; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const final; - void addToBuildEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const final; - -private: - QVariant defaultValue(const ProjectExplorer::Kit *k) const; + static ProjectExplorer::KitAspect *createKitAspect(ProjectExplorer::Kit *k); }; -class CMAKE_EXPORT CMakeConfigurationKitAspect : public ProjectExplorer::KitAspect +class CMAKE_EXPORT CMakeConfigurationKitAspect { public: - CMakeConfigurationKitAspect(); - static CMakeConfig configuration(const ProjectExplorer::Kit *k); static void setConfiguration(ProjectExplorer::Kit *k, const CMakeConfig &config); @@ -91,15 +67,7 @@ public: static void setCMakePreset(ProjectExplorer::Kit *k, const QString &presetName); static CMakeConfigItem cmakePresetConfigItem(const ProjectExplorer::Kit *k); - // KitAspect interface - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; - void setup(ProjectExplorer::Kit *k) final; - void fix(ProjectExplorer::Kit *k) final; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const final; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const final; - -private: - QVariant defaultValue(const ProjectExplorer::Kit *k) const; + static ProjectExplorer::KitAspect *createKitAspect(ProjectExplorer::Kit *k); }; } // CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.cpp b/src/plugins/cmakeprojectmanager/cmakeparser.cpp index ecc33463d8d..de4633ea617 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeparser.cpp @@ -3,6 +3,9 @@ #include "cmakeparser.h" +#include "cmakeprojectmanagertr.h" + +#include <utils/algorithm.h> #include <utils/qtcassert.h> #include <projectexplorer/projectexplorerconstants.h> @@ -16,6 +19,7 @@ const char COMMON_ERROR_PATTERN[] = "^CMake Error at (.*?):([0-9]*?)( \\((.*?)\\ const char NEXT_SUBERROR_PATTERN[] = "^CMake Error in (.*?):"; const char COMMON_WARNING_PATTERN[] = "^CMake Warning (\\(dev\\) )?at (.*?):([0-9]*?)( \\((.*?)\\))?:"; const char LOCATION_LINE_PATTERN[] = ":(\\d+?):(?:(\\d+?))?$"; +const char SOURCE_LINE_AND_FUNCTION_PATTERN[] = " (.*?):([0-9]*?)( \\((.*?)\\))"; CMakeParser::CMakeParser() { @@ -30,6 +34,9 @@ CMakeParser::CMakeParser() m_locationLine.setPattern(QLatin1String(LOCATION_LINE_PATTERN)); QTC_CHECK(m_locationLine.isValid()); + + m_sourceLineAndFunction.setPattern(QLatin1String(SOURCE_LINE_AND_FUNCTION_PATTERN)); + QTC_CHECK(m_sourceLineAndFunction.isValid()); } void CMakeParser::setSourceDirectory(const FilePath &sourceDir) @@ -62,7 +69,7 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm QRegularExpressionMatch match; QString trimmedLine = rightTrimmed(line); switch (m_expectTripleLineErrorData) { - case NONE: + case NONE: { if (trimmedLine.isEmpty() && !m_lastTask.isNull()) { if (m_skippedFirstEmptyLine) { flush(); @@ -71,8 +78,10 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm m_skippedFirstEmptyLine = true; return Status::InProgress; } - if (m_skippedFirstEmptyLine) - m_skippedFirstEmptyLine = false; + QScopeGuard cleanup([this] { + if (m_skippedFirstEmptyLine) + m_skippedFirstEmptyLine = false; + }); match = m_commonError.match(trimmedLine); if (match.hasMatch()) { @@ -86,6 +95,11 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm LinkSpecs linkSpecs; addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match, 1); + + m_errorOrWarningLine.file = m_lastTask.file; + m_errorOrWarningLine.line = m_lastTask.line; + m_errorOrWarningLine.function = match.captured(3); + return {Status::InProgress, linkSpecs}; } match = m_nextSubError.match(trimmedLine); @@ -109,14 +123,28 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm LinkSpecs linkSpecs; addLinkSpecForAbsoluteFilePath(linkSpecs, m_lastTask.file, m_lastTask.line, match, 1); + + m_errorOrWarningLine.file = m_lastTask.file; + m_errorOrWarningLine.line = m_lastTask.line; + m_errorOrWarningLine.function = match.captured(4); + return {Status::InProgress, linkSpecs}; } - else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { - if (!m_lastTask.summary.isEmpty()) - m_lastTask.summary.append(' '); - m_lastTask.summary.append(trimmedLine.trimmed()); - ++m_lines; + else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull() && !m_callStack) { + if (m_skippedFirstEmptyLine) + m_lastTask.details.append(QString()); + m_lastTask.details.append(trimmedLine.mid(2)); return Status::InProgress; + } else if (trimmedLine.startsWith(QLatin1String(" ")) && !m_lastTask.isNull()) { + match = m_sourceLineAndFunction.match(trimmedLine); + if (match.hasMatch()) { + CallStackLine stackLine; + stackLine.file = absoluteFilePath(resolvePath(match.captured(1))); + stackLine.line = match.captured(2).toInt(); + stackLine.function = match.captured(3); + m_callStack.value() << stackLine; + } + return {Status::InProgress}; } else if (trimmedLine.endsWith(QLatin1String("in cmake code at"))) { m_expectTripleLineErrorData = LINE_LOCATION; flush(); @@ -131,8 +159,12 @@ OutputLineParser::Result CMakeParser::handleLine(const QString &line, OutputForm } else if (trimmedLine.startsWith("-- ") || trimmedLine.startsWith(" * ")) { // Do not pass on lines starting with "-- " or "* ". Those are typical CMake output return Status::InProgress; + } else if (trimmedLine.startsWith("Call Stack (most recent call first):")) { + m_callStack = QList<CallStackLine>(); + return {Status::InProgress}; } return Status::NotHandled; + } case LINE_LOCATION: { match = m_locationLine.match(trimmedLine); @@ -169,10 +201,47 @@ void CMakeParser::flush() { if (m_lastTask.isNull()) return; + + if (m_lastTask.summary.isEmpty() && !m_lastTask.details.isEmpty()) + m_lastTask.summary = m_lastTask.details.takeFirst(); + m_lines += m_lastTask.details.count(); + + if (m_callStack) { + m_lastTask.file = m_callStack.value().last().file; + m_lastTask.line = m_callStack.value().last().line; + + LinkSpecs specs; + m_lastTask.details << QString(); + m_lastTask.details << Tr::tr("Call stack:"); + m_lines += 2; + + m_callStack->push_front(m_errorOrWarningLine); + + int offset = m_lastTask.details.join('\n').size(); + Utils::reverseForeach(m_callStack.value(), [&](const auto &line) { + const QString fileAndLine = QString("%1:%2").arg(line.file.path()).arg(line.line); + const QString completeLine = QString(" %1%2").arg(fileAndLine).arg(line.function); + + // newline and " " + offset += 3; + specs.append(LinkSpec{offset, + int(fileAndLine.length()), + createLinkTarget(line.file, line.line, -1)}); + + m_lastTask.details << completeLine; + offset += completeLine.length() - 2; + ++m_lines; + }); + + setDetailsFormat(m_lastTask, specs); + } + Task t = m_lastTask; m_lastTask.clear(); scheduleTask(t, m_lines, 1); m_lines = 0; + + m_callStack.reset(); } } // CMakeProjectManager @@ -223,12 +292,17 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data() << QString() << QString() << (Tasks() << BuildSystemTask(Task::Error, - "Cannot find source file: unknownFile.qml Tried extensions " - ".c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx", + "Cannot find source file:\n\n" + " unknownFile.qml\n\n" + "Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp\n" + ".hxx .in .txx", FilePath::fromUserInput("src/1/app/CMakeLists.txt"), 70) << BuildSystemTask(Task::Error, - "Cannot find source file: CMakeLists.txt2 Tried extensions " - ".c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx", + "Cannot find source file:\n\n" + " CMakeLists.txt2\n\n" + "Tried extensions " + ".c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp\n" + ".hxx .in .txx", FilePath::fromUserInput("src/1/app/CMakeLists.txt"), -1)) << QString(); @@ -299,7 +373,9 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data() << QString() << QString() << (Tasks() << BuildSystemTask(Task::Error, - "Parse error. Expected \"(\", got newline with text \" \".", + "Parse error. Expected \"(\", got newline with text \"\n" + "\n" + "\".", FilePath::fromUserInput("CMakeLists.txt"), 4)) << QString(); @@ -341,6 +417,48 @@ void Internal::CMakeProjectPlugin::testCMakeParser_data() << QString::fromLatin1("-- Qt5 install prefix: /usr/lib\n" " * Plugin componentsplugin, with CONDITION TARGET QmlDesigner") << OutputParserTester::STDERR << QString() << QString() << (Tasks()) << QString(); + + QTest::newRow("cmake call-stack") + << QString::fromLatin1( + "CMake Error at /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:588 " + "(add_executable):\n" + "\n" + " Cannot find source file:\n" + "\n" + " not-existing\n" + "\n" + " Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm\n" + " .ccm .cxxm .c++m .h .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90\n" + " .f95 .f03 .hip .ispc\n" + "Call Stack (most recent call first):\n" + " /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:549 " + "(_qt_internal_create_executable)\n" + " /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:741 " + "(qt6_add_executable)\n" + " /Projects/Test-Project/CMakeLists.txt:13 (qt_add_executable)\n") + << OutputParserTester::STDERR << QString() << QString() + << (Tasks() << BuildSystemTask( + Task::Error, + "\n" + "Cannot find source file:\n" + "\n" + " not-existing\n" + "\n" + "Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm\n" + ".ccm .cxxm .c++m .h .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90\n" + ".f95 .f03 .hip .ispc\n" + "\n" + "Call stack:\n" + " /Projects/Test-Project/CMakeLists.txt:13 (qt_add_executable)\n" + " /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:741 " + "(qt6_add_executable)\n" + " /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:549 " + "(_qt_internal_create_executable)\n" + " /Qt/6.5.3/mingw_64/lib/cmake/Qt6Core/Qt6CoreMacros.cmake:588 " + "(add_executable)", + FilePath::fromUserInput("/Projects/Test-Project/CMakeLists.txt"), + 13)) + << QString(); } void Internal::CMakeProjectPlugin::testCMakeParser() diff --git a/src/plugins/cmakeprojectmanager/cmakeparser.h b/src/plugins/cmakeprojectmanager/cmakeparser.h index b9aabc2b2da..08f57a75342 100644 --- a/src/plugins/cmakeprojectmanager/cmakeparser.h +++ b/src/plugins/cmakeprojectmanager/cmakeparser.h @@ -39,8 +39,18 @@ private: QRegularExpression m_nextSubError; QRegularExpression m_commonWarning; QRegularExpression m_locationLine; + QRegularExpression m_sourceLineAndFunction; bool m_skippedFirstEmptyLine = false; int m_lines = 0; + + struct CallStackLine + { + Utils::FilePath file; + int line = -1; + QString function; + }; + std::optional<QList<CallStackLine>> m_callStack; + CallStackLine m_errorOrWarningLine; }; } // CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp index f38eb2c628f..57ff1df2d2c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.cpp @@ -13,10 +13,18 @@ #include <projectexplorer/buildsystem.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/taskhub.h> +#include <projectexplorer/kitchooser.h> +#include <extensionsystem/invoker.h> +#include <extensionsystem/pluginmanager.h> + +#include <utils/algorithm.h> #include <utils/process.h> +#include <utils/processinfo.h> #include <utils/processinterface.h> #include <utils/stringutils.h> +#include <utils/stylehelper.h> +#include <utils/theme/theme.h> using namespace Core; using namespace ProjectExplorer; @@ -53,7 +61,7 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & const QString msg = ::CMakeProjectManager::Tr::tr( "The source directory %1 is not reachable by the CMake executable %2.") .arg(parameters.sourceDirectory.displayName()).arg(cmakeExecutable.displayName()); - BuildSystem::appendBuildSystemOutput(msg + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), msg}).join('\n')); emit finished(failedToStartExitCode); return; } @@ -62,7 +70,7 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & const QString msg = ::CMakeProjectManager::Tr::tr( "The build directory %1 is not reachable by the CMake executable %2.") .arg(parameters.buildDirectory.displayName()).arg(cmakeExecutable.displayName()); - BuildSystem::appendBuildSystemOutput(msg + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), msg}).join('\n')); emit finished(failedToStartExitCode); return; } @@ -73,7 +81,7 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & if (!buildDirectory.exists()) { const QString msg = ::CMakeProjectManager::Tr::tr( "The build directory \"%1\" does not exist").arg(buildDirectory.toUserOutput()); - BuildSystem::appendBuildSystemOutput(msg + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), msg}).join('\n')); emit finished(failedToStartExitCode); return; } @@ -83,14 +91,14 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & const QString msg = ::CMakeProjectManager::Tr::tr( "CMake executable \"%1\" and build directory \"%2\" must be on the same device.") .arg(cmake->cmakeExecutable().toUserOutput(), buildDirectory.toUserOutput()); - BuildSystem::appendBuildSystemOutput(msg + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), msg}).join('\n')); emit finished(failedToStartExitCode); return; } } // Copy the "package-manager" CMake code from the ${IDE:ResourcePath} to the build directory - if (Internal::CMakeSpecificSettings::instance()->packageManagerAutoSetup.value()) { + if (settings().packageManagerAutoSetup()) { const FilePath localPackageManagerDir = buildDirectory.pathAppended(Constants::PACKAGE_MANAGER_DIR); const FilePath idePackageManagerDir = FilePath::fromString( parameters.expander->expand(QStringLiteral("%{IDE:ResourcePath}/package-manager"))); @@ -111,13 +119,14 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & m_process->setWorkingDirectory(buildDirectory); m_process->setEnvironment(parameters.environment); - m_process->setStdOutLineCallback([](const QString &s) { - BuildSystem::appendBuildSystemOutput(stripTrailingNewline(s)); + m_process->setStdOutLineCallback([this](const QString &s) { + BuildSystem::appendBuildSystemOutput(addCMakePrefix(stripTrailingNewline(s))); + emit stdOutReady(s); }); m_process->setStdErrLineCallback([this](const QString &s) { m_parser.appendMessage(s, StdErrFormat); - BuildSystem::appendBuildSystemOutput(stripTrailingNewline(s)); + BuildSystem::appendBuildSystemOutput(addCMakePrefix(stripTrailingNewline(s))); }); connect(m_process.get(), &Process::done, this, [this] { @@ -130,8 +139,9 @@ void CMakeProcess::run(const BuildDirParameters ¶meters, const QStringList & TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - BuildSystem::startNewBuildSystemOutput(::CMakeProjectManager::Tr::tr("Running %1 in %2.") - .arg(commandLine.toUserOutput(), buildDirectory.toUserOutput())); + BuildSystem::startNewBuildSystemOutput( + addCMakePrefix(::CMakeProjectManager::Tr::tr("Running %1 in %2.") + .arg(commandLine.toUserOutput(), buildDirectory.toUserOutput()))); ProcessProgress *progress = new ProcessProgress(m_process.get()); progress->setDisplayName(::CMakeProjectManager::Tr::tr("Configuring \"%1\"") @@ -164,14 +174,33 @@ void CMakeProcess::handleProcessDone(const Utils::ProcessResultData &resultData) } if (!msg.isEmpty()) { - BuildSystem::appendBuildSystemOutput(msg + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), msg}).join('\n')); TaskHub::addTask(BuildSystemTask(Task::Error, msg)); } emit finished(code); const QString elapsedTime = Utils::formatElapsedTime(m_elapsed.elapsed()); - BuildSystem::appendBuildSystemOutput(elapsedTime + '\n'); + BuildSystem::appendBuildSystemOutput(addCMakePrefix({QString(), elapsedTime}).join('\n')); +} + +QString addCMakePrefix(const QString &str) +{ + auto qColorToAnsiCode = [] (const QColor &color) { + return QString::fromLatin1("\033[38;2;%1;%2;%3m") + .arg(color.red()).arg(color.green()).arg(color.blue()); + }; + static const QColor bgColor = creatorTheme()->color(Theme::BackgroundColorNormal); + static const QColor fgColor = creatorTheme()->color(Theme::TextColorNormal); + static const QColor grey = StyleHelper::mergedColors(fgColor, bgColor, 80); + static const QString prefixString = qColorToAnsiCode(grey) + Constants::OUTPUT_PREFIX + + qColorToAnsiCode(fgColor); + return QString("%1%2").arg(prefixString, str); +} + +QStringList addCMakePrefix(const QStringList &list) +{ + return Utils::transform(list, [](const QString &str) { return addCMakePrefix(str); }); } } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeprocess.h b/src/plugins/cmakeprojectmanager/cmakeprocess.h index bb365d337cc..c46a4ba84c9 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprocess.h +++ b/src/plugins/cmakeprojectmanager/cmakeprocess.h @@ -33,6 +33,7 @@ public: signals: void finished(int exitCode); + void stdOutReady(const QString &s); private: void handleProcessDone(const Utils::ProcessResultData &resultData); @@ -42,4 +43,7 @@ private: QElapsedTimer m_elapsed; }; +QString addCMakePrefix(const QString &str); +QStringList addCMakePrefix(const QStringList &list); + } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 44ebaff555e..f29ef14ac23 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -3,7 +3,7 @@ #include "cmakeproject.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeprojectconstants.h" #include "cmakeprojectimporter.h" #include "cmakeprojectmanagertr.h" @@ -12,12 +12,12 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildinfo.h> #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> using namespace ProjectExplorer; using namespace Utils; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h index 183e9501360..2e9a6ea58e0 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectconstants.h @@ -10,6 +10,8 @@ const char CMAKE_MIMETYPE[] = "text/x-cmake"; const char CMAKE_PROJECT_MIMETYPE[] = "text/x-cmake-project"; const char CMAKE_EDITOR_ID[] = "CMakeProject.CMakeEditor"; const char RUN_CMAKE[] = "CMakeProject.RunCMake"; +const char RUN_CMAKE_PROFILER[] = "CMakeProject.RunCMakeProfiler"; +const char RUN_CMAKE_DEBUGGER[] = "CMakeProject.RunCMakeDebugger"; const char CLEAR_CMAKE_CACHE[] = "CMakeProject.ClearCache"; const char RESCAN_PROJECT[] = "CMakeProject.RescanProject"; const char RUN_CMAKE_CONTEXT_MENU[] = "CMakeProject.RunCMakeContextMenu"; @@ -23,6 +25,7 @@ const char CMAKEFORMATTER_SETTINGS_GROUP[] = "CMakeFormatter"; const char CMAKEFORMATTER_GENERAL_GROUP[] = "General"; const char CMAKEFORMATTER_ACTION_ID[] = "CMakeFormatter.Action"; const char CMAKEFORMATTER_MENU_ID[] = "CMakeFormatter.Menu"; +const char CMAKE_DEBUGGING_GROUP[] = "Debugger.Group.CMake"; const char PACKAGE_MANAGER_DIR[] = ".qtc/package-manager"; @@ -68,5 +71,8 @@ const char TOOL_ID[] = "CMakeProjectManager.CMakeKitInformation"; // Data const char BUILD_FOLDER_ROLE[] = "CMakeProjectManager.data.buildFolder"; +// Output +const char OUTPUT_PREFIX[] = "[cmake] "; + } // namespace Constants } // namespace CMakeProjectManager diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index a005bd79fa7..b8748bc6f98 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -4,7 +4,7 @@ #include "cmakeprojectimporter.h" #include "cmakebuildconfiguration.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" @@ -14,12 +14,12 @@ #include <coreplugin/messagemanager.h> #include <projectexplorer/buildinfo.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchainmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/process.h> @@ -52,7 +52,6 @@ struct DirectoryData // Kit Stuff FilePath cmakeBinary; QString generator; - QString extraGenerator; QString platform; QString toolset; FilePath sysroot; @@ -105,6 +104,40 @@ CMakeProjectImporter::CMakeProjectImporter(const FilePath &path, const CMakeProj } +using CharToHexList = QList<QPair<QString, QString>>; +static const CharToHexList &charToHexList() +{ + static const CharToHexList list = { + {"<", "{3C}"}, + {">", "{3E}"}, + {":", "{3A}"}, + {"\"", "{22}"}, + {"\\", "{5C}"}, + {"/", "{2F}"}, + {"|", "{7C}"}, + {"?", "{3F}"}, + {"*", "{2A}"}, + }; + + return list; +} + +static QString presetNameToFileName(const QString &name) +{ + QString fileName = name; + for (const auto &p : charToHexList()) + fileName.replace(p.first, p.second); + return fileName; +} + +static QString fileNameToPresetName(const QString &fileName) +{ + QString name = fileName; + for (const auto &p : charToHexList()) + name.replace(p.second, p.first); + return name; +} + FilePaths CMakeProjectImporter::importCandidates() { FilePaths candidates; @@ -130,7 +163,8 @@ FilePaths CMakeProjectImporter::importCandidates() continue; } - const FilePath configPresetDir = m_presetsTempDir.filePath(configPreset.name); + const FilePath configPresetDir = m_presetsTempDir.filePath( + presetNameToFileName(configPreset.name)); configPresetDir.createDir(); candidates << configPresetDir; @@ -230,6 +264,7 @@ static CMakeConfig configurationFromPresetProbe( const QString prefixPath = cache.stringValueOf("CMAKE_PREFIX_PATH"); const QString findRootPath = cache.stringValueOf("CMAKE_FIND_ROOT_PATH"); const QString qtHostPath = cache.stringValueOf("QT_HOST_PATH"); + const QString sysRoot = cache.stringValueOf("CMAKE_SYSROOT"); if (!cmakeMakeProgram.isEmpty()) { args.emplace_back( @@ -248,6 +283,9 @@ static CMakeConfig configurationFromPresetProbe( if (!qtHostPath.isEmpty()) { args.emplace_back(QStringLiteral("-DQT_HOST_PATH=%1").arg(qtHostPath)); } + if (!sysRoot.isEmpty()) { + args.emplace_back(QStringLiteral("-DCMAKE_SYSROOT=%1").arg(sysRoot)); + } } qCDebug(cmInputLog) << "CMake probing for compilers: " << cmakeExecutable.toUserOutput() @@ -639,7 +677,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, if (importPath.isChildOf(m_presetsTempDir.path())) { auto data = std::make_unique<DirectoryData>(); - const QString presetName = importPath.fileName(); + const QString presetName = fileNameToPresetName(importPath.fileName()); PresetsDetails::ConfigurePreset configurePreset = Utils::findOrDefault(m_project->presetsData().configurePresets, [presetName](const PresetsDetails::ConfigurePreset &preset) { @@ -659,6 +697,11 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, const CMakeTool *cmakeTool = CMakeToolManager::defaultCMakeTool(); if (cmakeTool) configurePreset.cmakeExecutable = cmakeTool->cmakeExecutable().toString(); + } else { + QString cmakeExecutable = configurePreset.cmakeExecutable.value(); + CMakePresets::Macros::expand(configurePreset, env, projectDirectory(), cmakeExecutable); + + configurePreset.cmakeExecutable = FilePath::fromUserInput(cmakeExecutable).path(); } data->cmakeBinary = Utils::FilePath::fromString(configurePreset.cmakeExecutable.value()); @@ -706,9 +749,6 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, const CMakeConfig cache = configurePreset.cacheVariables ? configurePreset.cacheVariables.value() : CMakeConfig(); - - data->sysroot = cache.filePathValueOf("CMAKE_SYSROOT"); - CMakeConfig config; const bool noCompilers = cache.valueOf("CMAKE_C_COMPILER").isEmpty() && cache.valueOf("CMAKE_CXX_COMPILER").isEmpty(); @@ -739,6 +779,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, configurePreset.generator.value().toUtf8()); } + data->sysroot = config.filePathValueOf("CMAKE_SYSROOT"); + const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env); if (!qmake.isEmpty()) data->qt = findOrCreateQtVersion(qmake); @@ -835,7 +877,6 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath, data->cmakeBinary = config.filePathValueOf("CMAKE_COMMAND"); data->generator = config.stringValueOf("CMAKE_GENERATOR"); - data->extraGenerator = config.stringValueOf("CMAKE_EXTRA_GENERATOR"); data->platform = config.stringValueOf("CMAKE_GENERATOR_PLATFORM"); if (data->platform.isEmpty()) data->platform = extractVisualStudioPlatformFromConfig(config); @@ -880,7 +921,6 @@ bool CMakeProjectImporter::matchKit(void *directoryData, const Kit *k) const return false; if (CMakeGeneratorKitAspect::generator(k) != data->generator - || CMakeGeneratorKitAspect::extraGenerator(k) != data->extraGenerator || CMakeGeneratorKitAspect::platform(k) != data->platform || CMakeGeneratorKitAspect::toolset(k) != data->toolset) return false; @@ -950,7 +990,6 @@ Kit *CMakeProjectImporter::createKit(void *directoryData) const CMakeKitAspect::setCMakeTool(k, cmtd.cmakeTool->id()); CMakeGeneratorKitAspect::setGenerator(k, data->generator); - CMakeGeneratorKitAspect::setExtraGenerator(k, data->extraGenerator); CMakeGeneratorKitAspect::setPlatform(k, data->platform); CMakeGeneratorKitAspect::setToolset(k, data->toolset); @@ -1049,7 +1088,7 @@ void CMakeProjectImporter::persistTemporaryCMake(Kit *k, const QVariantList &vl) if (vl.isEmpty()) return; // No temporary CMake QTC_ASSERT(vl.count() == 1, return); - const QVariant data = vl.at(0); + const QVariant &data = vl.at(0); CMakeTool *tmpCmake = CMakeToolManager::findById(Id::fromSetting(data)); CMakeTool *actualCmake = CMakeKitAspect::cmakeTool(k); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 7e2e854ab4b..eb1c68eae36 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -4,7 +4,8 @@ #include "cmakeprojectmanager.h" #include "cmakebuildsystem.h" -#include "cmakekitinformation.h" +#include "cmakekitaspect.h" +#include "cmakeprocess.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" #include "cmakeprojectmanagertr.h" @@ -21,11 +22,16 @@ #include <cppeditor/cpptoolsreuse.h> +#include <debugger/analyzer/analyzerconstants.h> +#include <debugger/analyzer/analyzermanager.h> + #include <projectexplorer/buildmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectexplorericons.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projecttree.h> +#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <utils/checkablemessagebox.h> @@ -42,12 +48,19 @@ using namespace Utils; namespace CMakeProjectManager::Internal { CMakeManager::CMakeManager() - : m_runCMakeAction(new QAction(QIcon(), Tr::tr("Run CMake"), this)) + : m_runCMakeAction( + new QAction(ProjectExplorer::Icons::CMAKE_LOGO.icon(), Tr::tr("Run CMake"), this)) , m_clearCMakeCacheAction(new QAction(QIcon(), Tr::tr("Clear CMake Configuration"), this)) - , m_runCMakeActionContextMenu(new QAction(QIcon(), Tr::tr("Run CMake"), this)) + , m_runCMakeActionContextMenu( + new QAction(ProjectExplorer::Icons::CMAKE_LOGO.icon(), Tr::tr("Run CMake"), this)) , m_rescanProjectAction(new QAction(QIcon(), Tr::tr("Rescan Project"), this)) , m_reloadCMakePresetsAction( - new QAction(Utils::Icons::RELOAD_TOOLBAR.icon(), Tr::tr("Reload CMake Presets"), this)) + new QAction(Utils::Icons::RELOAD.icon(), Tr::tr("Reload CMake Presets"), this)) + , m_cmakeProfilerAction( + new QAction(ProjectExplorer::Icons::CMAKE_LOGO.icon(), Tr::tr("CMake Profiler"), this)) + , m_cmakeDebuggerAction(new QAction(ProjectExplorer::Icons::CMAKE_LOGO.icon(), + Tr::tr("Start CMake Debugging"), + this)) { Core::ActionContainer *mbuild = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); @@ -57,6 +70,10 @@ CMakeManager::CMakeManager() Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_SUBPROJECTCONTEXT); Core::ActionContainer *mfile = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_FILECONTEXT); + Core::ActionContainer *manalyzer = + Core::ActionManager::actionContainer(Debugger::Constants::M_DEBUG_ANALYZER); + Core::ActionContainer *mdebugger = + Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING); const Core::Context projectContext(CMakeProjectManager::Constants::CMAKE_PROJECT_ID); const Core::Context globalContext(Core::Constants::C_GLOBAL); @@ -128,7 +145,42 @@ CMakeManager::CMakeManager() mbuild->addAction(command, ProjectExplorer::Constants::G_BUILD_BUILD); connect(m_buildFileAction, &QAction::triggered, this, [this] { buildFile(); }); + // CMake Profiler + command = Core::ActionManager::registerAction(m_cmakeProfilerAction, + Constants::RUN_CMAKE_PROFILER, + globalContext); + command->setDescription(m_cmakeProfilerAction->text()); + if (manalyzer) + manalyzer->addAction(command, Debugger::Constants::G_ANALYZER_TOOLS); + connect(m_cmakeProfilerAction, &QAction::triggered, this, [this] { + runCMakeWithProfiling(ProjectManager::startupBuildSystem()); + }); + + // CMake Debugger + mdebugger->appendGroup(Constants::CMAKE_DEBUGGING_GROUP); + mdebugger->addSeparator(Core::Context(Core::Constants::C_GLOBAL), + Constants::CMAKE_DEBUGGING_GROUP, + &m_cmakeDebuggerSeparator); + + command = Core::ActionManager::registerAction(m_cmakeDebuggerAction, + Constants::RUN_CMAKE_DEBUGGER, + globalContext); + command->setDescription(m_cmakeDebuggerAction->text()); + mdebugger->addAction(command, Constants::CMAKE_DEBUGGING_GROUP); + connect(m_cmakeDebuggerAction, &QAction::triggered, this, [] { + ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE, + false); + }); + connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, this, [this] { + auto cmakeBuildSystem = qobject_cast<CMakeBuildSystem *>( + ProjectManager::startupBuildSystem()); + if (cmakeBuildSystem) { + const BuildDirParameters parameters(cmakeBuildSystem); + const auto tool = parameters.cmakeTool(); + CMakeTool::Version version = tool ? tool->version() : CMakeTool::Version(); + m_canDebugCMake = (version.major == 3 && version.minor >= 27) || version.major > 3; + } updateCmakeActions(ProjectTree::currentNode()); }); connect(BuildManager::instance(), &BuildManager::buildStateChanged, this, [this] { @@ -150,6 +202,10 @@ void CMakeManager::updateCmakeActions(Node *node) m_runCMakeActionContextMenu->setEnabled(visible); m_clearCMakeCacheAction->setVisible(visible); m_rescanProjectAction->setVisible(visible); + m_cmakeProfilerAction->setEnabled(visible); + + m_cmakeDebuggerAction->setEnabled(m_canDebugCMake && visible); + m_cmakeDebuggerSeparator->setVisible(m_canDebugCMake && visible); const bool reloadPresetsVisible = [project] { if (!project) @@ -180,6 +236,38 @@ void CMakeManager::runCMake(BuildSystem *buildSystem) cmakeBuildSystem->runCMake(); } +void CMakeManager::runCMakeWithProfiling(BuildSystem *buildSystem) +{ + auto cmakeBuildSystem = dynamic_cast<CMakeBuildSystem *>(buildSystem); + QTC_ASSERT(cmakeBuildSystem, return ); + + if (ProjectExplorerPlugin::saveModifiedFiles()) { + // cmakeBuildSystem->runCMakeWithProfiling() below will trigger Target::buildSystemUpdated + // which will ensure that the "cmake-profile.json" has been created and we can load the viewer + std::unique_ptr<QObject> context{new QObject}; + QObject *pcontext = context.get(); + QObject::connect(cmakeBuildSystem->target(), + &Target::buildSystemUpdated, + pcontext, + [context = std::move(context)]() mutable { + context.reset(); + Core::Command *ctfVisualiserLoadTrace = Core::ActionManager::command( + "Analyzer.Menu.StartAnalyzer.CtfVisualizer.LoadTrace"); + + if (ctfVisualiserLoadTrace) { + auto *action = ctfVisualiserLoadTrace->actionForContext( + Core::Constants::C_GLOBAL); + const FilePath file = TemporaryDirectory::masterDirectoryFilePath() + / "cmake-profile.json"; + action->setData(file.nativePath()); + emit ctfVisualiserLoadTrace->action()->triggered(); + } + }); + + cmakeBuildSystem->runCMakeWithProfiling(); + } +} + void CMakeManager::rescanProject(BuildSystem *buildSystem) { auto cmakeBuildSystem = dynamic_cast<CMakeBuildSystem *>(buildSystem); @@ -231,14 +319,12 @@ void CMakeManager::enableBuildFileMenus(Node *node) void CMakeManager::reloadCMakePresets() { - auto settings = CMakeSpecificSettings::instance(); - QMessageBox::StandardButton clickedButton = CheckableMessageBox::question( Core::ICore::dialogParent(), Tr::tr("Reload CMake Presets"), Tr::tr("Re-generates the kits that were created for CMake presets. All manual " "modifications to the CMake project settings will be lost."), - settings->askBeforePresetsReload.askAgainCheckableDecider(), + settings().askBeforePresetsReload.askAgainCheckableDecider(), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes, QMessageBox::Yes, @@ -246,7 +332,7 @@ void CMakeManager::reloadCMakePresets() {QMessageBox::Yes, Tr::tr("Reload")}, }); - settings->writeSettings(Core::ICore::settings()); + settings().writeSettings(); if (clickedButton == QMessageBox::Cancel) return; @@ -325,8 +411,8 @@ void CMakeManager::buildFile(Node *node) bc->buildDirectory()); targetBase = relativeBuildDir / "CMakeFiles" / (targetNode->displayName() + ".dir"); } else if (!generator.contains("Makefiles")) { - Core::MessageManager::writeFlashing( - Tr::tr("Build File is not supported for generator \"%1\"").arg(generator)); + Core::MessageManager::writeFlashing(addCMakePrefix( + Tr::tr("Build File is not supported for generator \"%1\"").arg(generator))); return; } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h index c47d724c9b6..2004538b58d 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.h @@ -25,6 +25,7 @@ private: void updateCmakeActions(ProjectExplorer::Node *node); void clearCMakeCache(ProjectExplorer::BuildSystem *buildSystem); void runCMake(ProjectExplorer::BuildSystem *buildSystem); + void runCMakeWithProfiling(ProjectExplorer::BuildSystem *buildSystem); void rescanProject(ProjectExplorer::BuildSystem *buildSystem); void buildFileContextMenu(); void buildFile(ProjectExplorer::Node *node = nullptr); @@ -39,6 +40,10 @@ private: QAction *m_buildFileContextMenu; QAction *m_reloadCMakePresetsAction; Utils::ParameterAction *m_buildFileAction; + QAction *m_cmakeProfilerAction; + QAction *m_cmakeDebuggerAction; + QAction *m_cmakeDebuggerSeparator; + bool m_canDebugCMake = false; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs index 3597a948713..94dfa408e15 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.qbs @@ -12,7 +12,6 @@ QtcPlugin { Depends { name: "ProjectExplorer" } Depends { name: "TextEditor" } Depends { name: "QtSupport" } - Depends { name: "app_version_header" } files: [ "builddirparameters.cpp", @@ -37,8 +36,8 @@ QtcPlugin { "cmakeformatter.h", "cmakeinstallstep.cpp", "cmakeinstallstep.h", - "cmakekitinformation.h", - "cmakekitinformation.cpp", + "cmakekitaspect.h", + "cmakekitaspect.cpp", "cmakelocatorfilter.cpp", "cmakelocatorfilter.h", "cmakeparser.cpp", @@ -94,13 +93,15 @@ QtcPlugin { name: "3rdparty" cpp.includePaths: base.concat("3rdparty/cmake") - prefix: "3rdparty/cmake/" + prefix: "3rdparty/" files: [ - "cmListFileCache.cxx", - "cmListFileCache.h", - "cmListFileLexer.cxx", - "cmListFileLexer.h", - "cmStandardLexer.h", + "cmake/cmListFileCache.cxx", + "cmake/cmListFileCache.h", + "cmake/cmListFileLexer.cxx", + "cmake/cmListFileLexer.h", + "cmake/cmStandardLexer.h", + "rstparser/rstparser.cc", + "rstparser/rstparser.h" ] } } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp index b26f9a70b5b..7d017d5d86a 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -174,6 +174,9 @@ QVariant CMakeTargetNode::data(Id role) const if (role == Ios::Constants::IosCmakeGenerator) return value("CMAKE_GENERATOR"); + if (role == ProjectExplorer::Constants::QT_KEYWORDS_ENABLED) // FIXME handle correctly + return value(role.toString().toUtf8()); + QTC_ASSERT(false, qDebug() << "Unknown role" << role.toString()); // Better guess than "not present". return value(role.toString().toUtf8()); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp index 39e3ec29269..811d2c0e4b3 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectplugin.cpp @@ -9,7 +9,6 @@ #include "cmakeeditor.h" #include "cmakeformatter.h" #include "cmakeinstallstep.h" -#include "cmakekitinformation.h" #include "cmakelocatorfilter.h" #include "cmakeproject.h" #include "cmakeprojectconstants.h" @@ -17,7 +16,6 @@ #include "cmakeprojectmanagertr.h" #include "cmakeprojectnodes.h" #include "cmakesettingspage.h" -#include "cmakespecificsettings.h" #include "cmaketoolmanager.h" #include <coreplugin/actionmanager/actioncontainer.h> @@ -45,7 +43,8 @@ namespace CMakeProjectManager::Internal { class CMakeProjectPluginPrivate : public QObject { public: - CMakeToolManager cmakeToolManager; // have that before the first CMakeKitAspect + // This can't be stand-alone yet as it registers in the plugin object pool + CMakeToolManager cmakeToolManager; ParameterAction buildTargetContextAction{ Tr::tr("Build"), @@ -54,7 +53,6 @@ public: }; CMakeSettingsPage settingsPage; - CMakeSpecificSettings specificSettings; CMakeManager manager; CMakeBuildStepFactory buildStepFactory; @@ -64,10 +62,6 @@ public: CMakeBuildTargetFilter cMakeBuildTargetFilter; CMakeOpenTargetFilter cMakeOpenTargetFilter; - CMakeKitAspect cmakeKitAspect; - CMakeGeneratorKitAspect cmakeGeneratorKitAspect; - CMakeConfigurationKitAspect cmakeConfigurationKitAspect; - CMakeFormatter cmakeFormatter; }; diff --git a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp index ab950ebc82a..10f03133850 100644 --- a/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp +++ b/src/plugins/cmakeprojectmanager/cmakesettingspage.cpp @@ -113,7 +113,7 @@ public: CMakeTool cmake(m_autodetected ? CMakeTool::AutoDetection : CMakeTool::ManualDetection, m_id); cmake.setFilePath(m_executable); - m_isSupported = cmake.hasFileApi(); + m_isSupported = cmake.hasFileApi(true); m_tooltip = Tr::tr("Version: %1").arg(cmake.versionDisplay()); m_tooltip += "<br>" + Tr::tr("Supports fileApi: %1").arg(m_isSupported ? Tr::tr("yes") : Tr::tr("no")); @@ -409,14 +409,14 @@ CMakeToolItemConfigWidget::CMakeToolItemConfigWidget(CMakeToolItemModel *model) m_binaryChooser = new PathChooser(this); m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand); m_binaryChooser->setMinimumWidth(400); - m_binaryChooser->setHistoryCompleter(QLatin1String("Cmake.Command.History")); + m_binaryChooser->setHistoryCompleter("Cmake.Command.History"); m_binaryChooser->setCommandVersionArguments({"--version"}); m_binaryChooser->setAllowPathFromDevice(true); m_qchFileChooser = new PathChooser(this); m_qchFileChooser->setExpectedKind(PathChooser::File); m_qchFileChooser->setMinimumWidth(400); - m_qchFileChooser->setHistoryCompleter(QLatin1String("Cmake.qchFile.History")); + m_qchFileChooser->setHistoryCompleter("Cmake.qchFile.History"); m_qchFileChooser->setPromptDialogFilter("*.qch"); m_qchFileChooser->setPromptDialogTitle(Tr::tr("CMake .qch File")); diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp index 17d4534ff8a..efc8d242d9b 100644 --- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp +++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.cpp @@ -7,6 +7,8 @@ #include "cmakeprojectmanagertr.h" #include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + #include <projectexplorer/projectexplorerconstants.h> #include <utils/layoutbuilder.h> @@ -15,23 +17,14 @@ using namespace Utils; namespace CMakeProjectManager::Internal { -static CMakeSpecificSettings *theSettings; - -CMakeSpecificSettings *CMakeSpecificSettings::instance() +CMakeSpecificSettings &settings() { + static CMakeSpecificSettings theSettings; return theSettings; } CMakeSpecificSettings::CMakeSpecificSettings() { - theSettings = this; - - setId(Constants::Settings::GENERAL_ID); - setDisplayName(::CMakeProjectManager::Tr::tr("General")); - setDisplayCategory("CMake"); - setCategory(Constants::Settings::CATEGORY); - setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY); - setLayouter([this] { using namespace Layouting; return Column { @@ -97,4 +90,20 @@ CMakeSpecificSettings::CMakeSpecificSettings() readSettings(); } +class CMakeSpecificSettingsPage final : public Core::IOptionsPage +{ +public: + CMakeSpecificSettingsPage() + { + setId(Constants::Settings::GENERAL_ID); + setDisplayName(::CMakeProjectManager::Tr::tr("General")); + setDisplayCategory("CMake"); + setCategory(Constants::Settings::CATEGORY); + setCategoryIconPath(Constants::Icons::SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const CMakeSpecificSettingsPage settingsPage; + } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h index a9c2758e190..d75ba5abd13 100644 --- a/src/plugins/cmakeprojectmanager/cmakespecificsettings.h +++ b/src/plugins/cmakeprojectmanager/cmakespecificsettings.h @@ -3,17 +3,15 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace CMakeProjectManager::Internal { -class CMakeSpecificSettings final : public Core::PagedSettings +class CMakeSpecificSettings final : public Utils::AspectContainer { public: CMakeSpecificSettings(); - static CMakeSpecificSettings *instance(); - Utils::BoolAspect autorunCMake{this}; Utils::FilePathAspect ninjaPath{this}; Utils::BoolAspect packageManagerAutoSetup{this}; @@ -23,4 +21,6 @@ public: Utils::BoolAspect showAdvancedOptionsByDefault{this}; }; +CMakeSpecificSettings &settings(); + } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index 9239ea54177..fdaf5e6bfc6 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -6,12 +6,15 @@ #include "cmakeprojectmanagertr.h" #include "cmaketoolmanager.h" +#include <coreplugin/icore.h> #include <coreplugin/helpmanager.h> #include <utils/algorithm.h> #include <utils/environment.h> +#include <utils/persistentcachestore.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <utils/temporarydirectory.h> #include <QDir> #include <QJsonDocument> @@ -19,6 +22,7 @@ #include <QLoggingCategory> #include <QRegularExpression> #include <QSet> +#include <QXmlStreamReader> #include <QUuid> #include <memory> @@ -41,9 +45,9 @@ const char CMAKE_INFORMATION_AUTODETECTED[] = "AutoDetected"; const char CMAKE_INFORMATION_DETECTIONSOURCE[] = "DetectionSource"; const char CMAKE_INFORMATION_READERTYPE[] = "ReaderType"; -bool CMakeTool::Generator::matches(const QString &n, const QString &ex) const +bool CMakeTool::Generator::matches(const QString &n) const { - return n == name && (ex.isEmpty() || extraGenerators.contains(ex)); + return n == name; } namespace Internal { @@ -82,13 +86,13 @@ class IntrospectionData { public: bool m_didAttemptToRun = false; - bool m_didRun = true; + bool m_haveCapabilitites = true; + bool m_haveKeywords = false; QList<CMakeTool::Generator> m_generators; - QMap<QString, QStringList> m_functionArgs; + CMakeKeywords m_keywords; + QMutex m_keywordsMutex; QVector<FileApi> m_fileApis; - QStringList m_variables; - QStringList m_functions; CMakeTool::Version m_version; }; @@ -105,7 +109,7 @@ CMakeTool::CMakeTool(Detection d, const Id &id) QTC_ASSERT(m_id.isValid(), m_id = Id::fromString(QUuid::createUuid().toString())); } -CMakeTool::CMakeTool(const QVariantMap &map, bool fromSdk) : +CMakeTool::CMakeTool(const Store &map, bool fromSdk) : CMakeTool(fromSdk ? CMakeTool::AutoDetection : CMakeTool::ManualDetection, Id::fromSetting(map.value(CMAKE_INFORMATION_ID))) { @@ -151,15 +155,15 @@ FilePath CMakeTool::filePath() const return m_executable; } -bool CMakeTool::isValid() const +bool CMakeTool::isValid(bool ignoreCache) const { if (!m_id.isValid() || !m_introspection) return false; if (!m_introspection->m_didAttemptToRun) - readInformation(); + readInformation(ignoreCache); - return m_introspection->m_didRun && !m_introspection->m_fileApis.isEmpty(); + return m_introspection->m_haveCapabilitites && !m_introspection->m_fileApis.isEmpty(); } void CMakeTool::runCMake(Process &cmake, const QStringList &args, int timeoutS) const @@ -175,9 +179,9 @@ void CMakeTool::runCMake(Process &cmake, const QStringList &args, int timeoutS) cmake.runBlocking(); } -QVariantMap CMakeTool::toMap() const +Store CMakeTool::toMap() const { - QVariantMap data; + Store data; data.insert(CMAKE_INFORMATION_DISPLAYNAME, m_displayName); data.insert(CMAKE_INFORMATION_ID, m_id.toSetting()); data.insert(CMAKE_INFORMATION_COMMAND, m_executable.toString()); @@ -224,7 +228,7 @@ FilePath CMakeTool::cmakeExecutable(const FilePath &path) } } - const FilePath resolvedPath = path.canonicalPath(); + FilePath resolvedPath = path.canonicalPath(); // Evil hack to make snap-packages of CMake work. See QTCREATORBUG-23376 if (path.osType() == OsTypeLinux && resolvedPath.fileName() == "snap") return path; @@ -242,41 +246,89 @@ QList<CMakeTool::Generator> CMakeTool::supportedGenerators() const return isValid() ? m_introspection->m_generators : QList<CMakeTool::Generator>(); } -TextEditor::Keywords CMakeTool::keywords() +CMakeKeywords CMakeTool::keywords() { if (!isValid()) return {}; - if (m_introspection->m_functions.isEmpty() && m_introspection->m_didRun) { + if (!m_introspection->m_haveKeywords && m_introspection->m_haveCapabilitites) { + QMutexLocker locker(&m_introspection->m_keywordsMutex); + if (m_introspection->m_haveKeywords) + return m_introspection->m_keywords; + Process proc; - runCMake(proc, {"--help-command-list"}, 5); - if (proc.result() == ProcessResult::FinishedWithSuccess) - m_introspection->m_functions = proc.cleanedStdOut().split('\n'); - runCMake(proc, {"--help-commands"}, 5); - if (proc.result() == ProcessResult::FinishedWithSuccess) - parseFunctionDetailsOutput(proc.cleanedStdOut()); + const FilePath findCMakeRoot = TemporaryDirectory::masterDirectoryFilePath() + / "find-root.cmake"; + findCMakeRoot.writeFileContents("message(${CMAKE_ROOT})"); - runCMake(proc, {"--help-property-list"}, 5); - if (proc.result() == ProcessResult::FinishedWithSuccess) - m_introspection->m_variables = parseVariableOutput(proc.cleanedStdOut()); - - runCMake(proc, {"--help-variable-list"}, 5); + FilePath cmakeRoot; + runCMake(proc, {"-P", findCMakeRoot.nativePath()}, 5); if (proc.result() == ProcessResult::FinishedWithSuccess) { - m_introspection->m_variables.append(parseVariableOutput(proc.cleanedStdOut())); - m_introspection->m_variables = Utils::filteredUnique(m_introspection->m_variables); - Utils::sort(m_introspection->m_variables); + QStringList output = filtered(proc.allOutput().split('\n'), + std::not_fn(&QString::isEmpty)); + if (output.size() > 0) + cmakeRoot = FilePath::fromString(output[0]); } + + const struct + { + const QString helpPath; + QMap<QString, FilePath> &targetMap; + } introspections[] = { + // Functions + {"Help/command", m_introspection->m_keywords.functions}, + // Properties + {"Help/prop_dir", m_introspection->m_keywords.directoryProperties}, + {"Help/prop_sf", m_introspection->m_keywords.sourceProperties}, + {"Help/prop_test", m_introspection->m_keywords.testProperties}, + {"Help/prop_tgt", m_introspection->m_keywords.targetProperties}, + {"Help/prop_gbl", m_introspection->m_keywords.properties}, + // Variables + {"Help/variable", m_introspection->m_keywords.variables}, + // Policies + {"Help/policy", m_introspection->m_keywords.policies}, + // Environment Variables + {"Help/envvar", m_introspection->m_keywords.environmentVariables}, + }; + for (auto &i : introspections) { + const FilePaths files = cmakeRoot.pathAppended(i.helpPath) + .dirEntries({{"*.rst"}, QDir::Files}, QDir::Name); + for (const auto &filePath : files) + i.targetMap[filePath.completeBaseName()] = filePath; + } + + for (const auto &map : {m_introspection->m_keywords.directoryProperties, + m_introspection->m_keywords.sourceProperties, + m_introspection->m_keywords.testProperties, + m_introspection->m_keywords.targetProperties}) { + m_introspection->m_keywords.properties.insert(map); + } + + // Modules + const FilePaths files + = cmakeRoot.pathAppended("Help/module").dirEntries({{"*.rst"}, QDir::Files}, QDir::Name); + for (const FilePath &filePath : files) { + const QString fileName = filePath.completeBaseName(); + if (fileName.startsWith("Find")) + m_introspection->m_keywords.findModules[fileName.mid(4)] = filePath; + else + m_introspection->m_keywords.includeStandardModules[fileName] = filePath; + } + + const QStringList moduleFunctions = parseSyntaxHighlightingXml(); + for (const auto &function : moduleFunctions) + m_introspection->m_keywords.functions[function] = FilePath(); + + m_introspection->m_haveKeywords = true; } - return TextEditor::Keywords(m_introspection->m_variables, - m_introspection->m_functions, - m_introspection->m_functionArgs); + return m_introspection->m_keywords; } -bool CMakeTool::hasFileApi() const +bool CMakeTool::hasFileApi(bool ignoreCache) const { - return isValid() ? !m_introspection->m_fileApis.isEmpty() : false; + return isValid(ignoreCache) ? !m_introspection->m_fileApis.isEmpty() : false; } CMakeTool::Version CMakeTool::version() const @@ -388,17 +440,18 @@ void CMakeTool::openCMakeHelpUrl(const CMakeTool *tool, const QString &linkUrl) Core::HelpManager::showHelpUrl(linkUrl.arg(documentationUrl(version, online))); } -void CMakeTool::readInformation() const +void CMakeTool::readInformation(bool ignoreCache) const { QTC_ASSERT(m_introspection, return ); - if (!m_introspection->m_didRun && m_introspection->m_didAttemptToRun) + if (!m_introspection->m_haveCapabilitites && m_introspection->m_didAttemptToRun) return; m_introspection->m_didAttemptToRun = true; - fetchFromCapabilities(); + fetchFromCapabilities(ignoreCache); } + static QStringList parseDefinition(const QString &definition) { QStringList result; @@ -432,14 +485,12 @@ static QStringList parseDefinition(const QString &definition) void CMakeTool::parseFunctionDetailsOutput(const QString &output) { - const QSet<QString> functionSet = Utils::toSet(m_introspection->m_functions); - bool expectDefinition = false; QString currentDefinition; const QStringList lines = output.split('\n'); for (int i = 0; i < lines.count(); ++i) { - const QString line = lines.at(i); + const QString &line = lines.at(i); if (line == "::") { expectDefinition = true; @@ -452,14 +503,15 @@ void CMakeTool::parseFunctionDetailsOutput(const QString &output) QStringList words = parseDefinition(currentDefinition); if (!words.isEmpty()) { const QString command = words.takeFirst(); - if (functionSet.contains(command)) { + if (m_introspection->m_keywords.functions.contains(command)) { const QStringList tmp = Utils::sorted( - words + m_introspection->m_functionArgs[command]); - m_introspection->m_functionArgs[command] = Utils::filteredUnique(tmp); + words + m_introspection->m_keywords.functionArgs[command]); + m_introspection->m_keywords.functionArgs[command] = Utils::filteredUnique( + tmp); } } - if (!words.isEmpty() && functionSet.contains(words.at(0))) - m_introspection->m_functionArgs[words.at(0)]; + if (!words.isEmpty() && m_introspection->m_keywords.functions.contains(words.at(0))) + m_introspection->m_keywords.functionArgs[words.at(0)]; currentDefinition.clear(); } else { currentDefinition.append(line.trimmed() + ' '); @@ -470,12 +522,19 @@ void CMakeTool::parseFunctionDetailsOutput(const QString &output) QStringList CMakeTool::parseVariableOutput(const QString &output) { - const QStringList variableList = output.split('\n'); + const QStringList variableList = Utils::filtered(output.split('\n'), + std::not_fn(&QString::isEmpty)); QStringList result; for (const QString &v : variableList) { if (v.startsWith("CMAKE_COMPILER_IS_GNU<LANG>")) { // This key takes a compiler name :-/ result << "CMAKE_COMPILER_IS_GNUCC" << "CMAKE_COMPILER_IS_GNUCXX"; + } else if (v.contains("<CONFIG>") && v.contains("<LANG>")) { + const QString tmp = QString(v).replace("<CONFIG>", "%1").replace("<LANG>", "%2"); + result << tmp.arg("DEBUG").arg("C") << tmp.arg("DEBUG").arg("CXX") + << tmp.arg("RELEASE").arg("C") << tmp.arg("RELEASE").arg("CXX") + << tmp.arg("MINSIZEREL").arg("C") << tmp.arg("MINSIZEREL").arg("CXX") + << tmp.arg("RELWITHDEBINFO").arg("C") << tmp.arg("RELWITHDEBINFO").arg("CXX"); } else if (v.contains("<CONFIG>")) { const QString tmp = QString(v).replace("<CONFIG>", "%1"); result << tmp.arg("DEBUG") << tmp.arg("RELEASE") << tmp.arg("MINSIZEREL") @@ -490,21 +549,114 @@ QStringList CMakeTool::parseVariableOutput(const QString &output) return result; } -void CMakeTool::fetchFromCapabilities() const +QStringList CMakeTool::parseSyntaxHighlightingXml() { + QStringList moduleFunctions; + + const FilePath cmakeXml = Core::ICore::resourcePath("generic-highlighter/syntax/cmake.xml"); + QXmlStreamReader reader(cmakeXml.fileContents().value_or(QByteArray())); + + auto readItemList = [](QXmlStreamReader &reader) -> QStringList { + QStringList arguments; + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() == u"item") + arguments.append(reader.readElementText()); + else + reader.skipCurrentElement(); + } + return arguments; + }; + + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() != u"highlighting") + continue; + while (!reader.atEnd() && reader.readNextStartElement()) { + if (reader.name() == u"list") { + const auto name = reader.attributes().value("name").toString(); + if (name.endsWith(u"_sargs") || name.endsWith(u"_nargs")) { + const auto functionName = name.left(name.length() - 6); + QStringList arguments = readItemList(reader); + + if (m_introspection->m_keywords.functionArgs.contains(functionName)) + arguments.append( + m_introspection->m_keywords.functionArgs.value(functionName)); + + m_introspection->m_keywords.functionArgs[functionName] = arguments; + + // Functions that are part of CMake modules like ExternalProject_Add + // which are not reported by cmake --help-list-commands + if (!m_introspection->m_keywords.functions.contains(functionName)) { + moduleFunctions << functionName; + } + } else if (name == u"generator-expressions") { + m_introspection->m_keywords.generatorExpressions = toSet(readItemList(reader)); + } else { + reader.skipCurrentElement(); + } + } else { + reader.skipCurrentElement(); + } + } + } + + // Some commands have the same arguments as other commands and the `cmake.xml` + // but their relationship is weirdly defined in the `cmake.xml` file. + using ListStringPair = QList<QPair<QString, QString>>; + const ListStringPair functionPairs = {{"if", "elseif"}, + {"while", "elseif"}, + {"find_path", "find_file"}, + {"find_program", "find_library"}, + {"target_link_libraries", "target_compile_definitions"}, + {"target_link_options", "target_compile_definitions"}, + {"target_link_directories", "target_compile_options"}, + {"set_target_properties", "set_directory_properties"}, + {"set_tests_properties", "set_directory_properties"}}; + for (const auto &pair : std::as_const(functionPairs)) { + if (!m_introspection->m_keywords.functionArgs.contains(pair.first)) + m_introspection->m_keywords.functionArgs[pair.first] + = m_introspection->m_keywords.functionArgs.value(pair.second); + } + + // Special case for cmake_print_variables, which will print the names and values for variables + // and needs to be as a known function + const QString cmakePrintVariables("cmake_print_variables"); + m_introspection->m_keywords.functionArgs[cmakePrintVariables] = {}; + moduleFunctions << cmakePrintVariables; + + moduleFunctions.removeDuplicates(); + return moduleFunctions; +} + +void CMakeTool::fetchFromCapabilities(bool ignoreCache) const +{ + expected_str<Utils::Store> cache = PersistentCacheStore::byKey( + keyFromString("CMake_" + cmakeExecutable().toUserOutput())); + + if (cache && !ignoreCache) { + m_introspection->m_haveCapabilitites = true; + parseFromCapabilities(cache->value("CleanedStdOut").toString()); + return; + } + Process cmake; runCMake(cmake, {"-E", "capabilities"}); if (cmake.result() == ProcessResult::FinishedWithSuccess) { - m_introspection->m_didRun = true; + m_introspection->m_haveCapabilitites = true; parseFromCapabilities(cmake.cleanedStdOut()); } else { qCCritical(cmakeToolLog) << "Fetching capabilities failed: " << cmake.allOutput() << cmake.error(); - m_introspection->m_didRun = false; + m_introspection->m_haveCapabilitites = false; } + + Store newData{{"CleanedStdOut", cmake.cleanedStdOut()}}; + const auto result + = PersistentCacheStore::write(keyFromString("CMake_" + cmakeExecutable().toUserOutput()), + newData); + QTC_ASSERT_EXPECTED(result, return); } -static int getVersion(const QVariantMap &obj, const QString value) +static int getVersion(const QVariantMap &obj, const QString &value) { bool ok; int result = obj.value(value).toInt(&ok); @@ -529,26 +681,23 @@ void CMakeTool::parseFromCapabilities(const QString &input) const gen.value("toolsetSupport").toBool())); } - { - const QVariantMap fileApis = data.value("fileApi").toMap(); - const QVariantList requests = fileApis.value("requests").toList(); - for (const QVariant &r : requests) { - const QVariantMap object = r.toMap(); - const QString kind = object.value("kind").toString(); - const QVariantList versionList = object.value("version").toList(); - std::pair<int, int> highestVersion{-1, -1}; - for (const QVariant &v : versionList) { - const QVariantMap versionObject = v.toMap(); - const std::pair<int, int> version{getVersion(versionObject, "major"), - getVersion(versionObject, "minor")}; - if (version.first > highestVersion.first - || (version.first == highestVersion.first - && version.second > highestVersion.second)) - highestVersion = version; - } - if (!kind.isNull() && highestVersion.first != -1 && highestVersion.second != -1) - m_introspection->m_fileApis.append({kind, highestVersion}); + const QVariantMap fileApis = data.value("fileApi").toMap(); + const QVariantList requests = fileApis.value("requests").toList(); + for (const QVariant &r : requests) { + const QVariantMap object = r.toMap(); + const QString kind = object.value("kind").toString(); + const QVariantList versionList = object.value("version").toList(); + std::pair<int, int> highestVersion{-1, -1}; + for (const QVariant &v : versionList) { + const QVariantMap versionObject = v.toMap(); + const std::pair<int, int> version{getVersion(versionObject, "major"), + getVersion(versionObject, "minor")}; + if (version.first > highestVersion.first + || (version.first == highestVersion.first && version.second > highestVersion.second)) + highestVersion = version; } + if (!kind.isNull() && highestVersion.first != -1 && highestVersion.second != -1) + m_introspection->m_fileApis.append({kind, highestVersion}); } const QVariantMap versionInfo = data.value("version").toMap(); diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h index 31b9e016276..0fa06e5ca14 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.h +++ b/src/plugins/cmakeprojectmanager/cmaketool.h @@ -7,8 +7,9 @@ #include <texteditor/codeassist/keywordscompletionassist.h> -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <utils/id.h> +#include <utils/store.h> #include <optional> @@ -18,6 +19,23 @@ namespace CMakeProjectManager { namespace Internal { class IntrospectionData; } +struct CMAKE_EXPORT CMakeKeywords +{ + QMap<QString, Utils::FilePath> variables; + QMap<QString, Utils::FilePath> functions; + QMap<QString, Utils::FilePath> properties; + QSet<QString> generatorExpressions; + QMap<QString, Utils::FilePath> environmentVariables; + QMap<QString, Utils::FilePath> directoryProperties; + QMap<QString, Utils::FilePath> sourceProperties; + QMap<QString, Utils::FilePath> targetProperties; + QMap<QString, Utils::FilePath> testProperties; + QMap<QString, Utils::FilePath> includeStandardModules; + QMap<QString, Utils::FilePath> findModules; + QMap<QString, Utils::FilePath> policies; + QMap<QString, QStringList> functionArgs; +}; + class CMAKE_EXPORT CMakeTool { public: @@ -45,21 +63,21 @@ public: bool supportsPlatform = true; bool supportsToolset = true; - bool matches(const QString &n, const QString &ex = QString()) const; + bool matches(const QString &n) const; }; using PathMapper = std::function<Utils::FilePath (const Utils::FilePath &)>; explicit CMakeTool(Detection d, const Utils::Id &id); - explicit CMakeTool(const QVariantMap &map, bool fromSdk); + explicit CMakeTool(const Utils::Store &map, bool fromSdk); ~CMakeTool(); static Utils::Id createId(); - bool isValid() const; + bool isValid(bool ignoreCache = false) const; Utils::Id id() const { return m_id; } - QVariantMap toMap () const; + Utils::Store toMap () const; void setAutorun(bool autoRun) { m_isAutoRun = autoRun; } @@ -72,8 +90,8 @@ public: bool isAutoRun() const; bool autoCreateBuildDirectory() const; QList<Generator> supportedGenerators() const; - TextEditor::Keywords keywords(); - bool hasFileApi() const; + CMakeKeywords keywords(); + bool hasFileApi(bool ignoreCache = false) const; Version version() const; QString versionDisplay() const; @@ -95,13 +113,14 @@ public: static void openCMakeHelpUrl(const CMakeTool *tool, const QString &linkUrl); private: - void readInformation() const; + void readInformation(bool ignoreCache = false) const; void runCMake(Utils::Process &proc, const QStringList &args, int timeoutS = 1) const; void parseFunctionDetailsOutput(const QString &output); QStringList parseVariableOutput(const QString &output); + QStringList parseSyntaxHighlightingXml(); - void fetchFromCapabilities() const; + void fetchFromCapabilities(bool ignoreCache = false) const; void parseFromCapabilities(const QString &input) const; // Note: New items here need also be handled in CMakeToolItemModel::apply() diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp index 7e4c777317d..19d7d5f2c12 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp @@ -3,19 +3,28 @@ #include "cmaketoolmanager.h" +#include "cmakekitaspect.h" #include "cmakeprojectmanagertr.h" #include "cmakespecificsettings.h" #include "cmaketoolsettingsaccessor.h" +#include "3rdparty/rstparser/rstparser.h" + #include <extensionsystem/pluginmanager.h> #include <coreplugin/helpmanager.h> #include <coreplugin/icore.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projecttree.h> +#include <projectexplorer/target.h> +#include <stack> #include <utils/environment.h> #include <utils/pointeralgorithm.h> #include <utils/qtcassert.h> +#include <nanotrace/nanotrace.h> + using namespace Core; using namespace Utils; @@ -29,6 +38,137 @@ public: Internal::CMakeToolSettingsAccessor m_accessor; }; +class HtmlHandler : public rst::ContentHandler +{ +private: + std::stack<QString> m_tags; + + QStringList m_p; + QStringList m_h3; + QStringList m_cmake_code; + + QString m_last_directive_type; + QString m_last_directive_class; + + void StartBlock(rst::BlockType type) final + { + QString tag; + switch (type) { + case rst::REFERENCE_LINK: + // not used, HandleReferenceLink is used instead + break; + case rst::H1: + tag = "h1"; + break; + case rst::H2: + tag = "h2"; + break; + case rst::H3: + tag = "h3"; + break; + case rst::H4: + tag = "h4"; + break; + case rst::H5: + tag = "h5"; + break; + case rst::CODE: + tag = "code"; + break; + case rst::PARAGRAPH: + tag = "p"; + break; + case rst::LINE_BLOCK: + tag = "pre"; + break; + case rst::BLOCK_QUOTE: + if (m_last_directive_type == "code-block" && m_last_directive_class == "cmake") + tag = "cmake-code"; + else + tag = "blockquote"; + break; + case rst::BULLET_LIST: + tag = "ul"; + break; + case rst::LIST_ITEM: + tag = "li"; + break; + case rst::LITERAL_BLOCK: + tag = "pre"; + break; + } + + if (tag == "p") + m_p.push_back(QString()); + if (tag == "h3") + m_h3.push_back(QString()); + if (tag == "cmake-code") + m_cmake_code.push_back(QString()); + + if (tag == "code" && m_tags.top() == "p") + m_p.last().append("`"); + + m_tags.push(tag); + } + + void EndBlock() final + { + // Add a new "p" collector for any `code` markup that comes afterwads + // since we are insterested only in the first paragraph. + if (m_tags.top() == "p") + m_p.push_back(QString()); + + if (m_tags.top() == "code" && !m_p.isEmpty()) { + m_tags.pop(); + + if (m_tags.size() > 0 && m_tags.top() == "p") + m_p.last().append("`"); + } else { + m_tags.pop(); + } + } + + void HandleText(const char *text, std::size_t size) final + { + if (m_last_directive_type.endsWith("replace")) + return; + + QString str = QString::fromUtf8(text, size); + + if (m_tags.top() == "h3") + m_h3.last().append(str); + if (m_tags.top() == "p") + m_p.last().append(str); + if (m_tags.top() == "cmake-code") + m_cmake_code.last().append(str); + if (m_tags.top() == "code" && !m_p.isEmpty()) + m_p.last().append(str); + } + + void HandleDirective(const std::string &type, const std::string &name) final + { + m_last_directive_type = QString::fromStdString(type); + m_last_directive_class = QString::fromStdString(name); + } + + void HandleReferenceLink(const std::string &type, const std::string &text) final + { + Q_UNUSED(type) + if (!m_p.isEmpty()) + m_p.last().append(QString::fromStdString(text)); + } + +public: + QString content() const + { + const QString title = m_h3.isEmpty() ? QString() : m_h3.first(); + const QString description = m_p.isEmpty() ? QString() : m_p.first(); + const QString cmakeCode = m_cmake_code.isEmpty() ? QString() : m_cmake_code.first(); + + return QString("### %1\n\n%2\n\n````\n%3\n````").arg(title, description, cmakeCode); + } +}; + static CMakeToolManagerPrivate *d = nullptr; CMakeToolManager *CMakeToolManager::m_instance = nullptr; @@ -49,6 +189,8 @@ CMakeToolManager::CMakeToolManager() setObjectName("CMakeToolManager"); ExtensionSystem::PluginManager::addObject(this); + + CMakeKitAspect::createFactories(); } CMakeToolManager::~CMakeToolManager() @@ -103,6 +245,18 @@ void CMakeToolManager::deregisterCMakeTool(const Id &id) } } +CMakeTool *CMakeToolManager::defaultProjectOrDefaultCMakeTool() +{ + CMakeTool *tool = nullptr; + + if (auto bs = ProjectExplorer::ProjectTree::currentBuildSystem()) + tool = CMakeKitAspect::cmakeTool(bs->target()->kit()); + if (!tool) + tool = CMakeToolManager::defaultCMakeTool(); + + return tool; +} + CMakeTool *CMakeToolManager::defaultCMakeTool() { return findById(d->m_defaultCMake); @@ -131,6 +285,7 @@ CMakeTool *CMakeToolManager::findById(const Id &id) void CMakeToolManager::restoreCMakeTools() { + NANOTRACE_SCOPE("CMakeProjectManager", "CMakeToolManager::restoreCMakeTools"); Internal::CMakeToolSettingsAccessor::CMakeTools tools = d->m_accessor.restoreCMakeTools(ICore::dialogParent()); d->m_cmakeTools = std::move(tools.cmakeTools); @@ -142,11 +297,11 @@ void CMakeToolManager::restoreCMakeTools() // Store the default CMake tool "Autorun CMake" value globally // TODO: Remove in Qt Creator 13 - auto settings = Internal::CMakeSpecificSettings::instance(); - if (settings->autorunCMake.value() == settings->autorunCMake.defaultValue()) { + Internal::CMakeSpecificSettings &s = Internal::settings(); + if (s.autorunCMake() == s.autorunCMake.defaultValue()) { CMakeTool *cmake = defaultCMakeTool(); - settings->autorunCMake.setValue(cmake ? cmake->isAutoRun() : true); - settings->writeSettings(Core::ICore::settings()); + s.autorunCMake.setValue(cmake ? cmake->isAutoRun() : true); + s.writeSettings(); } } @@ -161,6 +316,28 @@ void CMakeToolManager::updateDocumentation() Core::HelpManager::registerDocumentation(docs); } +QString CMakeToolManager::toolTipForRstHelpFile(const FilePath &helpFile) +{ + static QHash<FilePath, QString> map; + static QMutex mutex; + QMutexLocker locker(&mutex); + + if (map.contains(helpFile)) + return map.value(helpFile); + + auto content = helpFile.fileContents(1024).value_or(QByteArray()); + content.replace("\r\n", "\n"); + + HtmlHandler handler; + rst::Parser parser(&handler); + parser.Parse(content.left(content.lastIndexOf('\n'))); + + const QString tooltip = handler.content(); + + map[helpFile] = tooltip; + return tooltip; +} + QList<Id> CMakeToolManager::autoDetectCMakeForDevice(const FilePaths &searchPaths, const QString &detectionSource, QString *logMessage) @@ -249,8 +426,9 @@ void CMakeToolManager::ensureDefaultCMakeToolIsValid() } else { if (findById(d->m_defaultCMake)) return; - auto cmakeTool = Utils::findOrDefault( - cmakeTools(), [](CMakeTool *tool){ return tool->detectionSource().isEmpty(); }); + auto cmakeTool = Utils::findOrDefault(cmakeTools(), [](CMakeTool *tool) { + return tool->detectionSource().isEmpty() && !tool->cmakeExecutable().needsDevice(); + }); if (cmakeTool) d->m_defaultCMake = cmakeTool->id(); } diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.h b/src/plugins/cmakeprojectmanager/cmaketoolmanager.h index 1b5ee74c8f1..761ba22ad76 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.h +++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.h @@ -30,6 +30,8 @@ public: static bool registerCMakeTool(std::unique_ptr<CMakeTool> &&tool); static void deregisterCMakeTool(const Utils::Id &id); + static CMakeTool *defaultProjectOrDefaultCMakeTool(); + static CMakeTool *defaultCMakeTool(); static void setDefaultCMakeTool(const Utils::Id &id); static CMakeTool *findByCommand(const Utils::FilePath &command); @@ -40,6 +42,8 @@ public: static void updateDocumentation(); + static QString toolTipForRstHelpFile(const Utils::FilePath &helpFile); + public slots: QList<Utils::Id> autoDetectCMakeForDevice(const Utils::FilePaths &searchPaths, const QString &detectionSource, diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp index 05969b492e2..b877998a079 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.cpp @@ -9,12 +9,11 @@ #include <coreplugin/icore.h> -#include <app/app_version.h> - #include <utils/algorithm.h> #include <utils/environment.h> #include <QDebug> +#include <QGuiApplication> using namespace Utils; @@ -31,7 +30,7 @@ public: CMakeToolSettingsUpgraderV0() : VersionUpgrader(0, "4.6") { } // NOOP - QVariantMap upgrade(const QVariantMap &data) final { return data; } + Store upgrade(const Store &data) final { return data; } }; // -------------------------------------------------------------------- @@ -132,7 +131,7 @@ mergeTools(std::vector<std::unique_ptr<CMakeTool>> &sdkTools, CMakeToolSettingsAccessor::CMakeToolSettingsAccessor() { setDocType("QtCreatorCMakeTools"); - setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + setApplicationDisplayName(QGuiApplication::applicationDisplayName()); setBaseFilePath(Core::ICore::userResourcePath(CMAKE_TOOL_FILENAME)); addVersionUpgrader(std::make_unique<CMakeToolSettingsUpgraderV0>()); @@ -171,43 +170,43 @@ void CMakeToolSettingsAccessor::saveCMakeTools(const QList<CMakeTool *> &cmakeTo const Id &defaultId, QWidget *parent) { - QVariantMap data; - data.insert(QLatin1String(CMAKE_TOOL_DEFAULT_KEY), defaultId.toSetting()); + Store data; + data.insert(CMAKE_TOOL_DEFAULT_KEY, defaultId.toSetting()); int count = 0; + const bool autoRun = settings().autorunCMake(); for (CMakeTool *item : cmakeTools) { Utils::FilePath fi = item->cmakeExecutable(); // Gobal Autorun value will be set for all tools // TODO: Remove in Qt Creator 13 - const auto settings = CMakeSpecificSettings::instance(); - item->setAutorun(settings->autorunCMake.value()); + item->setAutorun(autoRun); if (fi.needsDevice() || fi.isExecutableFile()) { // be graceful for device related stuff - QVariantMap tmp = item->toMap(); + Store tmp = item->toMap(); if (tmp.isEmpty()) continue; - data.insert(QString::fromLatin1(CMAKE_TOOL_DATA_KEY) + QString::number(count), tmp); + data.insert(numberedKey(CMAKE_TOOL_DATA_KEY, count), variantFromStore(tmp)); ++count; } } - data.insert(QLatin1String(CMAKE_TOOL_COUNT_KEY), count); + data.insert(CMAKE_TOOL_COUNT_KEY, count); saveSettings(data, parent); } CMakeToolSettingsAccessor::CMakeTools -CMakeToolSettingsAccessor::cmakeTools(const QVariantMap &data, bool fromSdk) const +CMakeToolSettingsAccessor::cmakeTools(const Store &data, bool fromSdk) const { CMakeTools result; - int count = data.value(QLatin1String(CMAKE_TOOL_COUNT_KEY), 0).toInt(); + int count = data.value(CMAKE_TOOL_COUNT_KEY, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = QString::fromLatin1(CMAKE_TOOL_DATA_KEY) + QString::number(i); + const Key key = numberedKey(CMAKE_TOOL_DATA_KEY, i); if (!data.contains(key)) continue; - const QVariantMap dbMap = data.value(key).toMap(); + const Store dbMap = storeFromVariant(data.value(key)); auto item = std::make_unique<CMakeTool>(dbMap, fromSdk); const FilePath cmakeExecutable = item->cmakeExecutable(); if (item->isAutoDetected() && !cmakeExecutable.needsDevice() && !cmakeExecutable.isExecutableFile()) { diff --git a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.h b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.h index e750770390c..e67ab6c0326 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.h +++ b/src/plugins/cmakeprojectmanager/cmaketoolsettingsaccessor.h @@ -5,6 +5,7 @@ #include <utils/id.h> #include <utils/settingsaccessor.h> +#include <utils/store.h> namespace CMakeProjectManager { @@ -29,7 +30,7 @@ public: QWidget *parent); private: - CMakeTools cmakeTools(const QVariantMap &data, bool fromSdk) const; + CMakeTools cmakeTools(const Utils::Store &data, bool fromSdk) const; }; } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 687c653c5ce..b5b692da080 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -10,17 +10,18 @@ #include "projecttreehelper.h" #include <cppeditor/cppeditorconstants.h> - -#include <utils/algorithm.h> -#include <utils/mimeutils.h> -#include <utils/process.h> -#include <utils/qtcassert.h> -#include <utils/utilsicons.h> +#include <cppeditor/projectinfo.h> #include <projectexplorer/projecttree.h> -#include <QDir> +#include <utils/algorithm.h> +#include <utils/icon.h> +#include <utils/mimeutils.h> +#include <utils/process.h> +#include <utils/qtcassert.h> + #include <QLoggingCategory> +#include <QtConcurrent> using namespace ProjectExplorer; using namespace Utils; @@ -39,64 +40,86 @@ class CMakeFileResult public: QSet<CMakeFileInfo> cmakeFiles; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesSource; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesBuild; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesOther; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeListNodes; + std::vector<std::unique_ptr<FileNode>> cmakeNodesSource; + std::vector<std::unique_ptr<FileNode>> cmakeNodesBuild; + std::vector<std::unique_ptr<FileNode>> cmakeNodesOther; + std::vector<std::unique_ptr<FileNode>> cmakeListNodes; }; -CMakeFileResult extractCMakeFilesData(const std::vector<CMakeFileInfo> &cmakefiles, - const FilePath &sourceDirectory, - const FilePath &buildDirectory) +static CMakeFileResult extractCMakeFilesData(const QFuture<void> &cancelFuture, + const std::vector<CMakeFileInfo> &cmakefiles, + const FilePath &sourceDirectory, + const FilePath &buildDirectory) { + if (cmakefiles.empty()) + return {}; + + // Uniquify fileInfos + std::set<CMakeFileInfo> cmakeFileSet{cmakefiles.begin(), cmakefiles.end()}; + + // Load and parse cmake files. We use concurrency here to speed up the process of + // reading many small files, which can get slow especially on remote devices. + QFuture<CMakeFileInfo> mapResult + = QtConcurrent::mapped(cmakeFileSet, [cancelFuture, sourceDirectory](const auto &info) { + if (cancelFuture.isCanceled()) + return CMakeFileInfo(); + const FilePath sfn = sourceDirectory.resolvePath(info.path); + CMakeFileInfo absolute(info); + absolute.path = sfn; + + const auto mimeType = Utils::mimeTypeForFile(info.path); + if (mimeType.matchesName(Constants::CMAKE_MIMETYPE) + || mimeType.matchesName(Constants::CMAKE_PROJECT_MIMETYPE)) { + expected_str<QByteArray> fileContent = sfn.fileContents(); + std::string errorString; + if (fileContent) { + fileContent = fileContent->replace("\r\n", "\n"); + if (!absolute.cmakeListFile.ParseString(fileContent->toStdString(), + sfn.fileName().toStdString(), + errorString)) { + qCWarning(cmakeLogger) << "Failed to parse:" << sfn.path() + << QString::fromLatin1(errorString); + } + } + } + + return absolute; + }); + + mapResult.waitForFinished(); + + if (cancelFuture.isCanceled()) + return {}; + CMakeFileResult result; - for (const CMakeFileInfo &info : cmakefiles) { - const FilePath sfn = sourceDirectory.resolvePath(info.path); - const int oldCount = result.cmakeFiles.count(); - CMakeFileInfo absolute(info); - absolute.path = sfn; + for (const auto &info : mapResult.results()) { + if (cancelFuture.isCanceled()) + return {}; - const auto mimeType = Utils::mimeTypeForFile(info.path); - if (mimeType.matchesName(Constants::CMAKE_MIMETYPE) - || mimeType.matchesName(Constants::CMAKE_PROJECT_MIMETYPE)) { - expected_str<QByteArray> fileContent = sfn.fileContents(); - std::string errorString; - if (fileContent) { - fileContent = fileContent->replace("\r\n", "\n"); - if (!absolute.cmakeListFile.ParseString(fileContent->toStdString(), - sfn.fileName().toStdString(), - errorString)) - qCWarning(cmakeLogger) - << "Failed to parse:" << sfn.path() << QString::fromLatin1(errorString); - } + result.cmakeFiles.insert(info); + + if (info.isCMake && !info.isCMakeListsDotTxt) { + // Skip files that cmake considers to be part of the installation -- but include + // CMakeLists.txt files. This fixes cmake binaries running from their own + // build directory. + continue; } - result.cmakeFiles.insert(absolute); + auto node = std::make_unique<FileNode>(info.path, FileType::Project); + node->setIsGenerated(info.isGenerated + && !info.isCMakeListsDotTxt); // CMakeLists.txt are never + // generated, independent + // what cmake thinks:-) - if (oldCount < result.cmakeFiles.count()) { - if (info.isCMake && !info.isCMakeListsDotTxt) { - // Skip files that cmake considers to be part of the installation -- but include - // CMakeLists.txt files. This fixes cmake binaries running from their own - // build directory. - continue; - } - - auto node = std::make_unique<FileNode>(sfn, FileType::Project); - node->setIsGenerated(info.isGenerated - && !info.isCMakeListsDotTxt); // CMakeLists.txt are never - // generated, independent - // what cmake thinks:-) - - if (info.isCMakeListsDotTxt) { - result.cmakeListNodes.emplace_back(std::move(node)); - } else if (sfn.isChildOf(sourceDirectory)) { - result.cmakeNodesSource.emplace_back(std::move(node)); - } else if (sfn.isChildOf(buildDirectory)) { - result.cmakeNodesBuild.emplace_back(std::move(node)); - } else { - result.cmakeNodesOther.emplace_back(std::move(node)); - } + if (info.isCMakeListsDotTxt) { + result.cmakeListNodes.emplace_back(std::move(node)); + } else if (info.path.isChildOf(sourceDirectory)) { + result.cmakeNodesSource.emplace_back(std::move(node)); + } else if (info.path.isChildOf(buildDirectory)) { + result.cmakeNodesBuild.emplace_back(std::move(node)); + } else { + result.cmakeNodesOther.emplace_back(std::move(node)); } } @@ -110,31 +133,26 @@ public: QSet<CMakeFileInfo> cmakeFiles; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesSource; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesBuild; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeNodesOther; - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> cmakeListNodes; + std::vector<std::unique_ptr<FileNode>> cmakeNodesSource; + std::vector<std::unique_ptr<FileNode>> cmakeNodesBuild; + std::vector<std::unique_ptr<FileNode>> cmakeNodesOther; + std::vector<std::unique_ptr<FileNode>> cmakeListNodes; Configuration codemodel; std::vector<TargetDetails> targetDetails; }; -PreprocessedData preprocess(FileApiData &data, - const FilePath &sourceDirectory, - const FilePath &buildDirectory, - QString &errorMessage) +static PreprocessedData preprocess(const QFuture<void> &cancelFuture, FileApiData &data, + const FilePath &sourceDirectory, const FilePath &buildDirectory) { - Q_UNUSED(errorMessage) - PreprocessedData result; result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is result.codemodel = std::move(data.codemodel); - CMakeFileResult cmakeFileResult = extractCMakeFilesData(data.cmakeFiles, - sourceDirectory, - buildDirectory); + CMakeFileResult cmakeFileResult = extractCMakeFilesData(cancelFuture, data.cmakeFiles, + sourceDirectory, buildDirectory); result.cmakeFiles = std::move(cmakeFileResult.cmakeFiles); result.cmakeNodesSource = std::move(cmakeFileResult.cmakeNodesSource); @@ -147,10 +165,11 @@ PreprocessedData preprocess(FileApiData &data, return result; } -QVector<FolderNode::LocationInfo> extractBacktraceInformation(const BacktraceInfo &backtraces, - const QDir &sourceDir, - int backtraceIndex, - unsigned int locationInfoPriority) +static QVector<FolderNode::LocationInfo> extractBacktraceInformation( + const BacktraceInfo &backtraces, + const FilePath &sourceDir, + int backtraceIndex, + unsigned int locationInfoPriority) { QVector<FolderNode::LocationInfo> info; // Set up a default target path: @@ -162,8 +181,7 @@ QVector<FolderNode::LocationInfo> extractBacktraceInformation(const BacktraceInf const size_t fileIndex = static_cast<size_t>(btNode.file); QTC_ASSERT(fileIndex < backtraces.files.size(), break); - const FilePath path = FilePath::fromString( - sourceDir.absoluteFilePath(backtraces.files[fileIndex])); + const FilePath path = sourceDir.pathAppended(backtraces.files[fileIndex]).absoluteFilePath(); if (btNode.command < 0) { // No command, skip: The file itself is already covered:-) @@ -188,137 +206,145 @@ static bool isChildOf(const FilePath &path, const FilePaths &prefixes) return false; } -QList<CMakeBuildTarget> generateBuildTargets(const PreprocessedData &input, - const FilePath &sourceDirectory, - const FilePath &buildDirectory, - bool haveLibrariesRelativeToBuildDirectory) +static CMakeBuildTarget toBuildTarget(const TargetDetails &t, + const FilePath &sourceDirectory, + const FilePath &buildDirectory, + bool relativeLibs) { - QDir sourceDir(sourceDirectory.toString()); + const FilePath currentBuildDir = buildDirectory.resolvePath(t.buildDir); - const QList<CMakeBuildTarget> result = transform<QList>(input.targetDetails, - [&sourceDir, &sourceDirectory, &buildDirectory, - &haveLibrariesRelativeToBuildDirectory](const TargetDetails &t) { - const FilePath currentBuildDir = buildDirectory.resolvePath(t.buildDir); + CMakeBuildTarget ct; + ct.title = t.name; + if (!t.artifacts.isEmpty()) + ct.executable = buildDirectory.resolvePath(t.artifacts.at(0)); + TargetType type = UtilityType; + if (t.type == "EXECUTABLE") + type = ExecutableType; + else if (t.type == "STATIC_LIBRARY") + type = StaticLibraryType; + else if (t.type == "OBJECT_LIBRARY") + type = ObjectLibraryType; + else if (t.type == "MODULE_LIBRARY" || t.type == "SHARED_LIBRARY") + type = DynamicLibraryType; + else + type = UtilityType; + ct.targetType = type; + ct.workingDirectory = ct.executable.isEmpty() + ? currentBuildDir.absolutePath() + : ct.executable.parentDir(); + ct.sourceDirectory = sourceDirectory.resolvePath(t.sourceDir); - CMakeBuildTarget ct; - ct.title = t.name; - if (!t.artifacts.isEmpty()) - ct.executable = buildDirectory.resolvePath(t.artifacts.at(0)); - TargetType type = UtilityType; - if (t.type == "EXECUTABLE") - type = ExecutableType; - else if (t.type == "STATIC_LIBRARY") - type = StaticLibraryType; - else if (t.type == "OBJECT_LIBRARY") - type = ObjectLibraryType; - else if (t.type == "MODULE_LIBRARY" || t.type == "SHARED_LIBRARY") - type = DynamicLibraryType; - else - type = UtilityType; - ct.targetType = type; - ct.workingDirectory = ct.executable.isEmpty() - ? currentBuildDir.absolutePath() - : ct.executable.parentDir(); - ct.sourceDirectory = sourceDirectory.resolvePath(t.sourceDir); + ct.backtrace = extractBacktraceInformation(t.backtraceGraph, sourceDirectory, t.backtrace, 0); - ct.backtrace = extractBacktraceInformation(t.backtraceGraph, sourceDir, t.backtrace, 0); + for (const DependencyInfo &d : t.dependencies) { + ct.dependencyDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDirectory, d.backtrace, 100)); + } + for (const SourceInfo &si : t.sources) { + ct.sourceDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDirectory, si.backtrace, 200)); + } + for (const CompileInfo &ci : t.compileGroups) { + for (const IncludeInfo &ii : ci.includes) { + ct.includeDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDirectory, ii.backtrace, 300)); + } + for (const DefineInfo &di : ci.defines) { + ct.defineDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDirectory, di.backtrace, 400)); + } + } + for (const InstallDestination &id : t.installDestination) { + ct.installDefinitions.append( + extractBacktraceInformation(t.backtraceGraph, sourceDirectory, id.backtrace, 500)); + } - for (const DependencyInfo &d : t.dependencies) { - ct.dependencyDefinitions.append( - extractBacktraceInformation(t.backtraceGraph, sourceDir, d.backtrace, 100)); - } - for (const SourceInfo &si : t.sources) { - ct.sourceDefinitions.append( - extractBacktraceInformation(t.backtraceGraph, sourceDir, si.backtrace, 200)); - } - for (const CompileInfo &ci : t.compileGroups) { - for (const IncludeInfo &ii : ci.includes) { - ct.includeDefinitions.append( - extractBacktraceInformation(t.backtraceGraph, sourceDir, ii.backtrace, 300)); + if (ct.targetType == ExecutableType) { + FilePaths librarySeachPaths; + // Is this a GUI application? + ct.linksToQtGui = Utils::contains(t.link.value().fragments, + [](const FragmentInfo &f) { + return f.role == "libraries" + && (f.fragment.contains("QtGui") + || f.fragment.contains("Qt5Gui") + || f.fragment.contains("Qt6Gui")); + }); + + ct.qtcRunnable = t.folderTargetProperty == "qtc_runnable"; + + // Extract library directories for executables: + for (const FragmentInfo &f : t.link.value().fragments) { + if (f.role == "flags") // ignore all flags fragments + continue; + + // CMake sometimes mixes several shell-escaped pieces into one fragment. Disentangle that again: + const QStringList parts = ProcessArgs::splitArgs(f.fragment, HostOsInfo::hostOs()); + for (QString part : parts) { + // Library search paths that are added with target_link_directories are added as + // -LIBPATH:... (Windows/MSVC), or + // -L... (Unix/GCC) + // with role "libraryPath" + if (f.role == "libraryPath") { + if (part.startsWith("-LIBPATH:")) + part = part.mid(9); + else if (part.startsWith("-L")) + part = part.mid(2); } - for (const DefineInfo &di : ci.defines) { - ct.defineDefinitions.append( - extractBacktraceInformation(t.backtraceGraph, sourceDir, di.backtrace, 400)); - } - } - for (const InstallDestination &id : t.installDestination) { - ct.installDefinitions.append( - extractBacktraceInformation(t.backtraceGraph, sourceDir, id.backtrace, 500)); - } - if (ct.targetType == ExecutableType) { - Utils::FilePaths librarySeachPaths; - // Is this a GUI application? - ct.linksToQtGui = Utils::contains(t.link.value().fragments, - [](const FragmentInfo &f) { - return f.role == "libraries" - && (f.fragment.contains("QtGui") - || f.fragment.contains("Qt5Gui") - || f.fragment.contains("Qt6Gui")); - }); + // Some projects abuse linking to libraries to pass random flags to the linker, so ignore + // flags mixed into a fragment + if (part.startsWith("-")) + continue; - ct.qtcRunnable = t.folderTargetProperty == "qtc_runnable"; + const FilePath buildDir = relativeLibs ? buildDirectory : currentBuildDir; + FilePath tmp = buildDir.resolvePath(part); - // Extract library directories for executables: - for (const FragmentInfo &f : t.link.value().fragments) { - if (f.role == "flags") // ignore all flags fragments - continue; + if (f.role == "libraries") + tmp = tmp.parentDir(); - // CMake sometimes mixes several shell-escaped pieces into one fragment. Disentangle that again: - const QStringList parts = ProcessArgs::splitArgs(f.fragment, HostOsInfo::hostOs()); - for (QString part : parts) { - // Library search paths that are added with target_link_directories are added as - // -LIBPATH:... (Windows/MSVC), or - // -L... (Unix/GCC) - // with role "libraryPath" - if (f.role == "libraryPath") { - if (part.startsWith("-LIBPATH:")) - part = part.mid(9); - else if (part.startsWith("-L")) - part = part.mid(2); - } - - // Some projects abuse linking to libraries to pass random flags to the linker, so ignore - // flags mixed into a fragment - if (part.startsWith("-")) - continue; - - const FilePath buildDir = haveLibrariesRelativeToBuildDirectory ? buildDirectory : currentBuildDir; - FilePath tmp = buildDir.resolvePath(part); - - if (f.role == "libraries") - tmp = tmp.parentDir(); - - if (!tmp.isEmpty() && tmp.isDir()) { - // f.role is libraryPath or frameworkPath - // On *nix, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and - // "/usr/local/lib" since these are usually in the standard search - // paths. There probably are more, but the naming schemes are arbitrary - // so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR"). - if (buildDir.osType() == OsTypeWindows - || !isChildOf(tmp, - {"/lib", - "/lib64", - "/usr/lib", - "/usr/lib64", - "/usr/local/lib"})) { - librarySeachPaths.append(tmp); - // Libraries often have their import libs in ../lib and the - // actual dll files in ../bin on windows. Qt is one example of that. - if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) { - const FilePath path = tmp.parentDir().pathAppended("bin"); - if (path.isDir()) - librarySeachPaths.append(path); - } - } + if (!tmp.isEmpty() && tmp.isDir()) { + // f.role is libraryPath or frameworkPath + // On *nix, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and + // "/usr/local/lib" since these are usually in the standard search + // paths. There probably are more, but the naming schemes are arbitrary + // so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR"). + if (buildDir.osType() == OsTypeWindows + || !isChildOf(tmp, + {"/lib", + "/lib64", + "/usr/lib", + "/usr/lib64", + "/usr/local/lib"})) { + librarySeachPaths.append(tmp); + // Libraries often have their import libs in ../lib and the + // actual dll files in ../bin on windows. Qt is one example of that. + if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) { + const FilePath path = tmp.parentDir().pathAppended("bin"); + if (path.isDir()) + librarySeachPaths.append(path); } } } - ct.libraryDirectories = filteredUnique(librarySeachPaths); } + } + ct.libraryDirectories = filteredUnique(librarySeachPaths); + } + return ct; +} - return ct; - }); +static QList<CMakeBuildTarget> generateBuildTargets(const QFuture<void> &cancelFuture, + const PreprocessedData &input, + const FilePath &sourceDirectory, + const FilePath &buildDirectory, + bool relativeLibs) +{ + QList<CMakeBuildTarget> result; + result.reserve(input.targetDetails.size()); + for (const TargetDetails &t : input.targetDetails) { + if (cancelFuture.isCanceled()) + return {}; + result.append(toBuildTarget(t, sourceDirectory, buildDirectory, relativeLibs)); + } return result; } @@ -331,19 +357,28 @@ static QStringList splitFragments(const QStringList &fragments) return result; } -bool isPchFile(const FilePath &buildDirectory, const FilePath &path) +static bool isPchFile(const FilePath &buildDirectory, const FilePath &path) { - return path.isChildOf(buildDirectory) && path.fileName().startsWith("cmake_pch"); + return path.fileName().startsWith("cmake_pch") && path.isChildOf(buildDirectory); } -RawProjectParts generateRawProjectParts(const PreprocessedData &input, - const FilePath &sourceDirectory, - const FilePath &buildDirectory) +static bool isUnityFile(const FilePath &buildDirectory, const FilePath &path) +{ + return path.fileName().startsWith("unity_") && path.isChildOf(buildDirectory) + && path.parentDir().fileName() == "Unity"; +} + +static RawProjectParts generateRawProjectParts(const QFuture<void> &cancelFuture, + const PreprocessedData &input, + const FilePath &sourceDirectory, + const FilePath &buildDirectory) { RawProjectParts rpps; for (const TargetDetails &t : input.targetDetails) { - QDir sourceDir(sourceDirectory.toString()); + if (cancelFuture.isCanceled()) + return {}; + bool needPostfix = t.compileGroups.size() > 1; int count = 1; for (const CompileInfo &ci : t.compileGroups) { @@ -353,7 +388,7 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, // CMake users worked around Creator's inability of listing header files by creating // custom targets with all the header files. This target breaks the code model, so // keep quiet about it:-) - if (ci.defines.empty() && ci.includes.empty() && allOf(ci.sources, [t](const int sid) { + if (ci.defines.empty() && ci.includes.empty() && allOf(ci.sources, [&t](const int sid) { const SourceInfo &source = t.sources[static_cast<size_t>(sid)]; return Node::fileTypeForFileName(FilePath::fromString(source.path)) == FileType::Header; @@ -377,7 +412,8 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, RawProjectPart rpp; rpp.setProjectFileLocation(t.sourceDir.pathAppended("CMakeLists.txt").toString()); rpp.setBuildSystemTarget(t.name); - const QString postfix = needPostfix ? "_cg" + QString::number(count) : QString(); + const QString postfix = needPostfix ? QString("_%1_%2").arg(ci.language).arg(count) + : QString(); rpp.setDisplayName(t.id + postfix); rpp.setMacros(transform<QVector>(ci.defines, &DefineInfo::define)); rpp.setHeaderPaths(transform<QVector>(ci.includes, &IncludeInfo::path)); @@ -385,42 +421,78 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, QStringList fragments = splitFragments(ci.fragments); // Get all sources from the compiler group, except generated sources - QStringList sources; + FilePaths sources; + for (auto idx: ci.sources) { SourceInfo si = t.sources.at(idx); if (si.isGenerated) continue; - sources.push_back(sourceDir.absoluteFilePath(si.path)); + sources.append(sourceDirectory.resolvePath(si.path)); } + // Skip groups with only generated source files e.g. <build-dir>/.rcc/qrc_<target>.cpp + if (allOf(ci.sources, [&t](const auto &idx) { return t.sources.at(idx).isGenerated; })) + continue; + // If we are not in a pch compiler group, add all the headers that are not generated - const bool hasPchSource = anyOf(sources, [buildDirectory](const QString &path) { - return isPchFile(buildDirectory, FilePath::fromString(path)); + const bool hasPchSource = anyOf(sources, [buildDirectory](const FilePath &path) { + return isPchFile(buildDirectory, path); }); - QString headerMimeType; - if (ci.language == "C") - headerMimeType = CppEditor::Constants::C_HEADER_MIMETYPE; - else if (ci.language == "CXX") - headerMimeType = CppEditor::Constants::CPP_HEADER_MIMETYPE; + const bool hasUnitySources = allOf(sources, [buildDirectory](const FilePath &path) { + return isUnityFile(buildDirectory, path); + }); + + const QString headerMimeType = [&]() -> QString { + if (ci.language == "C") { + return CppEditor::Constants::C_HEADER_MIMETYPE; + } else if (ci.language == "CXX") { + return CppEditor::Constants::CPP_HEADER_MIMETYPE; + } + return {}; + }(); + + auto haveFileKindForLanguage = [&](const auto &kind) { + if (kind == CppEditor::ProjectFile::AmbiguousHeader) + return true; + + if (ci.language == "C") + return CppEditor::ProjectFile::isC(kind); + else if (ci.language == "CXX") + return CppEditor::ProjectFile::isCxx(kind); + + return false; + }; + if (!hasPchSource) { for (const SourceInfo &si : t.sources) { if (si.isGenerated) continue; - const auto mimeTypes = Utils::mimeTypesForFileName(si.path); - for (const auto &mime : mimeTypes) - if (mime.inherits(headerMimeType)) - sources.push_back(sourceDir.absoluteFilePath(si.path)); + + const auto kind = CppEditor::ProjectFile::classify(si.path); + const bool headerType = CppEditor::ProjectFile::isHeader(kind) + && haveFileKindForLanguage(kind); + const bool sourceUnityType = hasUnitySources + ? CppEditor::ProjectFile::isSource(kind) + && haveFileKindForLanguage(kind) + : false; + if (headerType || sourceUnityType) + sources.append(sourceDirectory.resolvePath(si.path)); } } + FilePath::removeDuplicates(sources); - // Set project files except pch files - rpp.setFiles(Utils::filtered(sources, [buildDirectory](const QString &path) { - return !isPchFile(buildDirectory, FilePath::fromString(path)); - }), {}, [headerMimeType](const QString &path) { - // Similar to ProjectFile::classify but classify headers with language - // of compile group instead of ambiguous header - if (path.endsWith(".h")) + // Set project files except pch / unity files + const FilePaths filtered = Utils::filtered(sources, + [buildDirectory](const FilePath &filePath) { + return !isPchFile(buildDirectory, filePath) + && !isUnityFile(buildDirectory, filePath); + }); + + rpp.setFiles(Utils::transform(filtered, &FilePath::toFSPathString), + {}, + [headerMimeType](const QString &path) { + if (CppEditor::ProjectFile::isAmbiguousHeader(path)) return headerMimeType; return Utils::mimeTypeForFile(path).name(); }); @@ -430,10 +502,7 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, return si.path.endsWith(ending); }).path); if (!precompiled_header.isEmpty()) { - if (precompiled_header.toFileInfo().isRelative()) { - const FilePath parentDir = FilePath::fromString(sourceDir.absolutePath()); - precompiled_header = parentDir.pathAppended(precompiled_header.toString()); - } + precompiled_header = sourceDirectory.resolvePath(precompiled_header); // Remove the CMake PCH usage command line options in order to avoid the case // when the build system would produce a .pch/.gch file that would be treated @@ -445,30 +514,29 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, fragments.erase(foundPos, std::next(foundPos, args.size())); }; - remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.toString() + ".gch"}); - remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.toString() + ".pch"}); - remove({"-Xclang", "-include", "-Xclang", precompiled_header.toString()}); - remove({"-include", precompiled_header.toString()}); - remove({"/FI", precompiled_header.toString()}); + remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.path() + ".gch"}); + remove({"-Xclang", "-include-pch", "-Xclang", precompiled_header.path() + ".pch"}); + remove({"-Xclang", "-include", "-Xclang", precompiled_header.path()}); + remove({"-include", precompiled_header.path()}); + remove({"/FI", precompiled_header.path()}); // Make a copy of the CMake PCH header and use it instead FilePath qtc_precompiled_header = precompiled_header.parentDir().pathAppended(qtcPchFile); FileUtils::copyIfDifferent(precompiled_header, qtc_precompiled_header); - rpp.setPreCompiledHeaders({qtc_precompiled_header.toString()}); + rpp.setPreCompiledHeaders({qtc_precompiled_header.path()}); } - RawProjectPartFlags cProjectFlags; - cProjectFlags.commandLineFlags = fragments; - rpp.setFlagsForC(cProjectFlags); - - RawProjectPartFlags cxxProjectFlags; - cxxProjectFlags.commandLineFlags = cProjectFlags.commandLineFlags; - rpp.setFlagsForCxx(cxxProjectFlags); + RawProjectPartFlags projectFlags; + projectFlags.commandLineFlags = fragments; + if (ci.language == "C") + rpp.setFlagsForC(projectFlags); + else if (ci.language == "CXX") + rpp.setFlagsForCxx(projectFlags); const bool isExecutable = t.type == "EXECUTABLE"; - rpp.setBuildTargetType(isExecutable ? ProjectExplorer::BuildTargetType::Executable - : ProjectExplorer::BuildTargetType::Library); + rpp.setBuildTargetType(isExecutable ? BuildTargetType::Executable + : BuildTargetType::Library); rpps.append(rpp); ++count; } @@ -477,27 +545,35 @@ RawProjectParts generateRawProjectParts(const PreprocessedData &input, return rpps; } -FilePath directorySourceDir(const Configuration &c, const FilePath &sourceDir, int directoryIndex) +static FilePath directorySourceDir(const Configuration &c, + const FilePath &sourceDir, + int directoryIndex) { const size_t di = static_cast<size_t>(directoryIndex); - QTC_ASSERT(di < c.directories.size(), return FilePath()); + QTC_ASSERT(di < c.directories.size(), return {}); return sourceDir.resolvePath(c.directories[di].sourcePath); } -FilePath directoryBuildDir(const Configuration &c, const FilePath &buildDir, int directoryIndex) +static FilePath directoryBuildDir(const Configuration &c, + const FilePath &buildDir, + int directoryIndex) { const size_t di = static_cast<size_t>(directoryIndex); - QTC_ASSERT(di < c.directories.size(), return FilePath()); + QTC_ASSERT(di < c.directories.size(), return {}); return buildDir.resolvePath(c.directories[di].buildPath); } -void addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes, - const Configuration &config, - const FilePath &sourceDir) +static void addProjects(const QFuture<void> &cancelFuture, + const QHash<FilePath, ProjectNode *> &cmakeListsNodes, + const Configuration &config, + const FilePath &sourceDir) { for (const FileApiDetails::Project &p : config.projects) { + if (cancelFuture.isCanceled()) + return; + if (p.parent == -1) continue; // Top-level project has already been covered FilePath dir = directorySourceDir(config, sourceDir, p.directories[0]); @@ -505,9 +581,9 @@ void addProjects(const QHash<Utils::FilePath, ProjectNode *> &cmakeListsNodes, } } -FolderNode *createSourceGroupNode(const QString &sourceGroupName, - const FilePath &sourceDirectory, - FolderNode *targetRoot) +static FolderNode *createSourceGroupNode(const QString &sourceGroupName, + const FilePath &sourceDirectory, + FolderNode *targetRoot) { FolderNode *currentNode = targetRoot; @@ -518,10 +594,9 @@ FolderNode *createSourceGroupNode(const QString &sourceGroupName, FolderNode *existingNode = currentNode->findChildFolderNode( [&p](const FolderNode *fn) { return fn->displayName() == p; }); if (!existingNode) { - auto node = createCMakeVFolder(sourceDirectory, Node::DefaultFolderPriority + 5, p); + auto node = createCMakeVFolder(sourceDirectory, Node::DefaultFolderPriority + 5, p, true); node->setListInProject(false); - node->setIcon( - [] { return QIcon::fromTheme("edit-copy", ::Utils::Icons::COPY.icon()); }); + node->setIcon([] { return Icon::fromTheme("edit-copy"); }); existingNode = node.get(); @@ -534,17 +609,16 @@ FolderNode *createSourceGroupNode(const QString &sourceGroupName, return currentNode; } -void addCompileGroups(ProjectNode *targetRoot, - const Utils::FilePath &topSourceDirectory, - const Utils::FilePath &sourceDirectory, - const Utils::FilePath &buildDirectory, - const TargetDetails &td) +static void addCompileGroups(ProjectNode *targetRoot, + const FilePath &topSourceDirectory, + const FilePath &sourceDirectory, + const FilePath &buildDirectory, + const TargetDetails &td) { - const bool showSourceFolders = CMakeSpecificSettings::instance()->showSourceSubFolders.value(); + const bool showSourceFolders = settings().showSourceSubFolders(); const bool inSourceBuild = (sourceDirectory == buildDirectory); - std::vector<std::unique_ptr<FileNode>> toList; - QSet<Utils::FilePath> alreadyListed; + QSet<FilePath> alreadyListed; // Files already added by other configurations: targetRoot->forEachGenericNode( @@ -567,9 +641,9 @@ void addCompileGroups(ProjectNode *targetRoot, auto node = std::make_unique<FileNode>(sourcePath, Node::fileTypeForFileName(sourcePath)); node->setIsGenerated(si.isGenerated); - // CMake pch files are generated at configured time, but not marked as generated + // CMake pch / unity files are generated at configured time, but not marked as generated // so that a "clean" step won't remove them and at a subsequent build they won't exist. - if (isPchFile(buildDirectory, sourcePath)) + if (isPchFile(buildDirectory, sourcePath) || isUnityFile(buildDirectory, sourcePath)) node->setIsGenerated(true); // Where does the file node need to go? @@ -582,29 +656,18 @@ void addCompileGroups(ProjectNode *targetRoot, } } - // Calculate base directory for source groups: for (size_t i = 0; i < sourceGroupFileNodes.size(); ++i) { std::vector<std::unique_ptr<FileNode>> ¤t = sourceGroupFileNodes[i]; - FilePath baseDirectory; - // All the sourceGroupFileNodes are below sourceDirectory, so this is safe: - for (const std::unique_ptr<FileNode> &fn : current) { - if (baseDirectory.isEmpty()) { - baseDirectory = fn->filePath().parentDir(); - } else { - baseDirectory = Utils::FileUtils::commonPath(baseDirectory, fn->filePath()); - } - } - - FolderNode *insertNode = createSourceGroupNode(td.sourceGroups[i], - baseDirectory, - targetRoot); - + FolderNode *insertNode = td.sourceGroups[i] == "TREE" + ? targetRoot + : createSourceGroupNode(td.sourceGroups[i], + sourceDirectory, + targetRoot); if (showSourceFolders) { - insertNode->addNestedNodes(std::move(current), baseDirectory); + insertNode->addNestedNodes(std::move(current), sourceDirectory); } else { - for (auto &fileNodes : current) { + for (auto &fileNodes : current) insertNode->addNode(std::move(fileNodes)); - } } } @@ -614,7 +677,7 @@ void addCompileGroups(ProjectNode *targetRoot, Tr::tr("<Build Directory>"), std::move(buildFileNodes)); addCMakeVFolder(targetRoot, - Utils::FilePath(), + FilePath(), 10, Tr::tr("<Other Locations>"), std::move(otherFileNodes)); @@ -642,11 +705,12 @@ static void addGeneratedFilesNode(ProjectNode *targetRoot, const FilePath &topLe addCMakeVFolder(targetRoot, buildDir, 10, Tr::tr("<Generated Files>"), std::move(nodes)); } -void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cmakeListsNodes, - const Configuration &config, - const std::vector<TargetDetails> &targetDetails, - const FilePath &sourceDir, - const FilePath &buildDir) +static void addTargets(const QFuture<void> &cancelFuture, + const QHash<FilePath, ProjectNode *> &cmakeListsNodes, + const Configuration &config, + const std::vector<TargetDetails> &targetDetails, + const FilePath &sourceDir, + const FilePath &buildDir) { QHash<QString, const TargetDetails *> targetDetailsHash; for (const TargetDetails &t : targetDetails) @@ -661,6 +725,9 @@ void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cm }; for (const FileApiDetails::Target &t : config.targets) { + if (cancelFuture.isCanceled()) + return; + const TargetDetails &td = getTargetDetails(t.id); const FilePath dir = directorySourceDir(config, sourceDir, t.directory); @@ -676,8 +743,10 @@ void addTargets(const QHash<Utils::FilePath, ProjectExplorer::ProjectNode *> &cm } } -std::unique_ptr<CMakeProjectNode> generateRootProjectNode( - PreprocessedData &data, const FilePath &sourceDirectory, const FilePath &buildDirectory) +static std::unique_ptr<CMakeProjectNode> generateRootProjectNode(const QFuture<void> &cancelFuture, + PreprocessedData &data, + const FilePath &sourceDirectory, + const FilePath &buildDirectory) { std::unique_ptr<CMakeProjectNode> result = std::make_unique<CMakeProjectNode>(sourceDirectory); @@ -692,25 +761,34 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode( std::move(data.cmakeListNodes)); data.cmakeListNodes.clear(); // Remove all the nullptr in the vector... - addProjects(cmakeListsNodes, data.codemodel, sourceDirectory); + addProjects(cancelFuture, cmakeListsNodes, data.codemodel, sourceDirectory); + if (cancelFuture.isCanceled()) + return {}; - addTargets(cmakeListsNodes, + addTargets(cancelFuture, + cmakeListsNodes, data.codemodel, data.targetDetails, sourceDirectory, buildDirectory); + if (cancelFuture.isCanceled()) + return {}; if (!data.cmakeNodesSource.empty() || !data.cmakeNodesBuild.empty() - || !data.cmakeNodesOther.empty()) + || !data.cmakeNodesOther.empty()) { addCMakeInputs(result.get(), sourceDirectory, buildDirectory, std::move(data.cmakeNodesSource), std::move(data.cmakeNodesBuild), std::move(data.cmakeNodesOther)); - + } + if (cancelFuture.isCanceled()) + return {}; addCMakePresets(result.get(), sourceDirectory); + if (cancelFuture.isCanceled()) + return {}; data.cmakeNodesSource.clear(); // Remove all the nullptr in the vector... data.cmakeNodesBuild.clear(); // Remove all the nullptr in the vector... @@ -719,7 +797,9 @@ std::unique_ptr<CMakeProjectNode> generateRootProjectNode( return result; } -void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList<CMakeBuildTarget> &targets) +static void setupLocationInfoForTargets(const QFuture<void> &cancelFuture, + CMakeProjectNode *rootNode, + const QList<CMakeBuildTarget> &targets) { const QSet<QString> titles = Utils::transform<QSet>(targets, &CMakeBuildTarget::title); QHash<QString, FolderNode *> buildKeyToNode; @@ -730,6 +810,9 @@ void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList<CMakeBu buildKeyToNode.insert(buildKey, folderNode); }); for (const CMakeBuildTarget &t : targets) { + if (cancelFuture.isCanceled()) + return; + FolderNode *folderNode = buildKeyToNode.value(t.title); if (folderNode) { QSet<std::pair<FilePath, int>> locations; @@ -764,39 +847,47 @@ void setupLocationInfoForTargets(CMakeProjectNode *rootNode, const QList<CMakeBu } } -using namespace FileApiDetails; - // -------------------------------------------------------------------- // extractData: // -------------------------------------------------------------------- -FileApiQtcData extractData(FileApiData &input, - const FilePath &sourceDirectory, - const FilePath &buildDirectory) +FileApiQtcData extractData(const QFuture<void> &cancelFuture, FileApiData &input, + const FilePath &sourceDir, const FilePath &buildDir) { FileApiQtcData result; // Preprocess our input: - PreprocessedData data = preprocess(input, sourceDirectory, buildDirectory, result.errorMessage); - result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is - if (!result.errorMessage.isEmpty()) { + PreprocessedData data = preprocess(cancelFuture, input, sourceDir, buildDir); + if (cancelFuture.isCanceled()) + return {}; + + result.cache = std::move(data.cache); // Make sure this is available, even when nothing else is + if (!result.errorMessage.isEmpty()) return {}; - } // Ninja generator from CMake version 3.20.5 has libraries relative to build directory const bool haveLibrariesRelativeToBuildDirectory = input.replyFile.generator.startsWith("Ninja") && input.replyFile.cmakeVersion >= QVersionNumber(3, 20, 5); - result.buildTargets = generateBuildTargets(data, sourceDirectory, buildDirectory, haveLibrariesRelativeToBuildDirectory); + result.buildTargets = generateBuildTargets(cancelFuture, data, sourceDir, buildDir, + haveLibrariesRelativeToBuildDirectory); + if (cancelFuture.isCanceled()) + return {}; result.cmakeFiles = std::move(data.cmakeFiles); - result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory); + result.projectParts = generateRawProjectParts(cancelFuture, data, sourceDir, buildDir); + if (cancelFuture.isCanceled()) + return {}; - auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory); + auto rootProjectNode = generateRootProjectNode(cancelFuture, data, sourceDir, buildDir); + if (cancelFuture.isCanceled()) + return {}; ProjectTree::applyTreeManager(rootProjectNode.get(), ProjectTree::AsyncPhase); // QRC nodes result.rootProjectNode = std::move(rootProjectNode); - setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets); + setupLocationInfoForTargets(cancelFuture, result.rootProjectNode.get(), result.buildTargets); + if (cancelFuture.isCanceled()) + return {}; result.ctestPath = input.replyFile.ctestExecutable; result.isMultiConfig = input.replyFile.isMultiConfig; diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.h b/src/plugins/cmakeprojectmanager/fileapidataextractor.h index e4ed3aa778c..dbe562421e6 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.h +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.h @@ -28,6 +28,8 @@ public: bool operator==(const CMakeFileInfo& other) const { return path == other.path; } friend size_t qHash(const CMakeFileInfo &info, uint seed = 0) { return qHash(info.path, seed); } + bool operator<(const CMakeFileInfo &other) const { return path < other.path; } + Utils::FilePath path; bool isCMake = false; bool isCMakeListsDotTxt = false; @@ -50,8 +52,7 @@ public: bool usesAllCapsTargets = false; }; -FileApiQtcData extractData(FileApiData &data, - const Utils::FilePath &sourceDirectory, - const Utils::FilePath &buildDirectory); +FileApiQtcData extractData(const QFuture<void> &cancelFuture, FileApiData &input, + const Utils::FilePath &sourceDir, const Utils::FilePath &buildDir); } // CMakeProjectManager::Internal diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.cpp b/src/plugins/cmakeprojectmanager/fileapiparser.cpp index 5b299bb188f..5038f2e85e3 100644 --- a/src/plugins/cmakeprojectmanager/fileapiparser.cpp +++ b/src/plugins/cmakeprojectmanager/fileapiparser.cpp @@ -3,15 +3,16 @@ #include "fileapiparser.h" +#include "cmakeprocess.h" #include "cmakeprojectmanagertr.h" -#include <app/app_version.h> #include <coreplugin/messagemanager.h> #include <projectexplorer/rawprojectpart.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> +#include <QGuiApplication> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> @@ -43,9 +44,9 @@ FilePath FileApiParser::cmakeReplyDirectory(const FilePath &buildDirectory) static void reportFileApiSetupFailure() { Core::MessageManager::writeFlashing( - Tr::tr("Failed to set up CMake file API support. %1 cannot " - "extract project information.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + addCMakePrefix(Tr::tr("Failed to set up CMake file API support. %1 cannot " + "extract project information.") + .arg(QGuiApplication::applicationDisplayName()))); } static std::pair<int, int> cmakeVersion(const QJsonObject &obj) @@ -90,7 +91,7 @@ std::vector<int> indexList(const QJsonValue &v) std::vector<int> result; result.reserve(static_cast<size_t>(indexList.count())); - for (const QJsonValue &v : indexList) { + for (const auto &v : indexList) { result.push_back(v.toInt(-1)); } return result; @@ -138,7 +139,7 @@ static ReplyFileContents readReplyFile(const FilePath &filePath, QString &errorM bool hadInvalidObject = false; { const QJsonArray objects = rootObject.value("objects").toArray(); - for (const QJsonValue &v : objects) { + for (const auto &v : objects) { const QJsonObject object = v.toObject(); { ReplyObject r; @@ -177,7 +178,7 @@ static CMakeConfig readCacheFile(const FilePath &cacheFile, QString &errorMessag } const QJsonArray entries = root.value("entries").toArray(); - for (const QJsonValue &v : entries) { + for (const auto &v : entries) { CMakeConfigItem item; const QJsonObject entry = v.toObject(); @@ -189,7 +190,7 @@ static CMakeConfig readCacheFile(const FilePath &cacheFile, QString &errorMessag { const QJsonArray properties = entry.value("properties").toArray(); - for (const QJsonValue &v : properties) { + for (const auto &v : properties) { const QJsonObject prop = v.toObject(); auto nv = nameValue(prop); if (nv.first == "ADVANCED") { @@ -222,7 +223,7 @@ static std::vector<CMakeFileInfo> readCMakeFilesFile(const FilePath &cmakeFilesF } const QJsonArray inputs = root.value("inputs").toArray(); - for (const QJsonValue &v : inputs) { + for (const auto &v : inputs) { CMakeFileInfo info; const QJsonObject input = v.toObject(); info.path = cmakeFilesFile.withNewPath(input.value("path").toString()); @@ -252,7 +253,7 @@ std::vector<Directory> extractDirectories(const QJsonArray &directories, QString } std::vector<Directory> result; - for (const QJsonValue &v : directories) { + for (const auto &v : directories) { const QJsonObject obj = v.toObject(); if (obj.isEmpty()) { errorMessage = Tr::tr( @@ -282,7 +283,7 @@ static std::vector<Project> extractProjects(const QJsonArray &projects, QString } std::vector<Project> result; - for (const QJsonValue &v : projects) { + for (const auto &v : projects) { const QJsonObject obj = v.toObject(); if (obj.isEmpty()) { qCDebug(cmakeFileApi) << "Empty project skipped!"; @@ -313,7 +314,7 @@ static std::vector<Project> extractProjects(const QJsonArray &projects, QString static std::vector<Target> extractTargets(const QJsonArray &targets, QString &errorMessage) { std::vector<Target> result; - for (const QJsonValue &v : targets) { + for (const auto &v : targets) { const QJsonObject obj = v.toObject(); if (obj.isEmpty()) { errorMessage = Tr::tr( @@ -435,7 +436,7 @@ static std::vector<Configuration> extractConfigurations(const QJsonArray &config } std::vector<FileApiDetails::Configuration> result; - for (const QJsonValue &v : configs) { + for (const auto &v : configs) { const QJsonObject obj = v.toObject(); if (obj.isEmpty()) { errorMessage = Tr::tr( @@ -873,11 +874,11 @@ FileApiData FileApiParser::parseData(QPromise<std::shared_ptr<FileApiQtcData>> & return result; } - auto it = std::find_if(codeModels.cbegin(), codeModels.cend(), + auto it = std::find_if(codeModels.begin(), codeModels.end(), [cmakeBuildType](const Configuration& cfg) { return QString::compare(cfg.name, cmakeBuildType, Qt::CaseInsensitive) == 0; }); - if (it == codeModels.cend()) { + if (it == codeModels.end()) { QStringList buildTypes; for (const Configuration &cfg: codeModels) buildTypes << cfg.name; diff --git a/src/plugins/cmakeprojectmanager/fileapiparser.h b/src/plugins/cmakeprojectmanager/fileapiparser.h index f6b54968660..900f630cf7e 100644 --- a/src/plugins/cmakeprojectmanager/fileapiparser.h +++ b/src/plugins/cmakeprojectmanager/fileapiparser.h @@ -13,7 +13,6 @@ #include <utils/filesystemwatcher.h> #include <utils/fileutils.h> -#include <QDir> #include <QString> #include <QVector> #include <QVersionNumber> diff --git a/src/plugins/cmakeprojectmanager/fileapireader.cpp b/src/plugins/cmakeprojectmanager/fileapireader.cpp index 24e2fc94fcc..ee1edb26c02 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.cpp +++ b/src/plugins/cmakeprojectmanager/fileapireader.cpp @@ -11,11 +11,15 @@ #include <coreplugin/messagemanager.h> +#include <extensionsystem/pluginmanager.h> + #include <projectexplorer/projectexplorer.h> #include <utils/algorithm.h> #include <utils/async.h> +#include <utils/futuresynchronizer.h> #include <utils/qtcassert.h> +#include <utils/temporarydirectory.h> #include <QLoggingCategory> @@ -82,19 +86,39 @@ void FileApiReader::resetData() void FileApiReader::parse(bool forceCMakeRun, bool forceInitialConfiguration, - bool forceExtraConfiguration) + bool forceExtraConfiguration, + bool debugging, + bool profiling) { qCDebug(cmakeFileApiMode) << "Parse called with arguments: ForceCMakeRun:" << forceCMakeRun << " - forceConfiguration:" << forceInitialConfiguration << " - forceExtraConfiguration:" << forceExtraConfiguration; startState(); - const QStringList args = (forceInitialConfiguration ? m_parameters.initialCMakeArguments + QStringList args = (forceInitialConfiguration ? m_parameters.initialCMakeArguments : QStringList()) + (forceExtraConfiguration ? (m_parameters.configurationChangesArguments + m_parameters.additionalCMakeArguments) : QStringList()); + if (debugging) { + if (TemporaryDirectory::masterDirectoryFilePath().osType() == Utils::OsType::OsTypeWindows) { + args << "--debugger" + << "--debugger-pipe \\\\.\\pipe\\cmake-dap"; + } else { + FilePath file = TemporaryDirectory::masterDirectoryFilePath() / "cmake-dap.sock"; + file.removeFile(); + args << "--debugger" + << "--debugger-pipe=" + file.path(); + } + } + + if (profiling) { + const FilePath file = TemporaryDirectory::masterDirectoryFilePath() / "cmake-profile.json"; + args << "--profiling-format=google-trace" + << "--profiling-output=" + file.path(); + } + qCDebug(cmakeFileApiMode) << "Parameters request these CMake arguments:" << args; const FilePath replyFile = FileApiParser::scanForCMakeReplyFile(m_parameters.buildDirectory); @@ -107,8 +131,7 @@ void FileApiReader::parse(bool forceCMakeRun, // * A query file is newer than the reply file const bool hasArguments = !args.isEmpty(); const bool replyFileMissing = !replyFile.exists(); - const auto settings = CMakeSpecificSettings::instance(); - const bool cmakeFilesChanged = m_parameters.cmakeTool() && settings->autorunCMake.value() + const bool cmakeFilesChanged = m_parameters.cmakeTool() && settings().autorunCMake() && anyOf(m_cmakeFiles, [&replyFile](const CMakeFileInfo &info) { return !info.isGenerated && info.path.lastModified() > replyFile.lastModified(); @@ -148,7 +171,7 @@ void FileApiReader::stop() if (m_future) { m_future->cancel(); - m_future->waitForFinished(); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(*m_future); } m_future = {}; m_isParsing = false; @@ -229,7 +252,8 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac const FilePath sourceDirectory = m_parameters.sourceDirectory; const FilePath buildDirectory = m_parameters.buildDirectory; - const QString cmakeBuildType = m_parameters.cmakeBuildType == "Build" ? "" : m_parameters.cmakeBuildType; + const QString cmakeBuildType = m_parameters.cmakeBuildType == "Build" + ? "" : m_parameters.cmakeBuildType; m_lastReplyTimestamp = replyFilePath.lastModified(); @@ -241,10 +265,12 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac replyFilePath, cmakeBuildType, result->errorMessage); - if (result->errorMessage.isEmpty()) - *result = extractData(data, sourceDirectory, buildDirectory); - else + if (result->errorMessage.isEmpty()) { + *result = extractData(QFuture<void>(promise.future()), data, + sourceDirectory, buildDirectory); + } else { qWarning() << result->errorMessage; + } promise.addResult(result); }); @@ -259,8 +285,8 @@ void FileApiReader::endState(const FilePath &replyFilePath, bool restoredFromBac m_projectParts = std::move(value->projectParts); m_rootProjectNode = std::move(value->rootProjectNode); m_ctestPath = std::move(value->ctestPath); - m_isMultiConfig = std::move(value->isMultiConfig); - m_usesAllCapsTargets = std::move(value->usesAllCapsTargets); + m_isMultiConfig = value->isMultiConfig; + m_usesAllCapsTargets = value->usesAllCapsTargets; if (value->errorMessage.isEmpty()) { emit this->dataAvailable(restoredFromBackup); @@ -283,8 +309,9 @@ void FileApiReader::makeBackupConfiguration(bool store) replyPrev.removeRecursively(); QTC_CHECK(!replyPrev.exists()); if (!reply.renameFile(replyPrev)) - Core::MessageManager::writeFlashing(Tr::tr("Failed to rename \"%1\" to \"%2\".") - .arg(reply.toString(), replyPrev.toString())); + Core::MessageManager::writeFlashing( + addCMakePrefix(Tr::tr("Failed to rename \"%1\" to \"%2\".") + .arg(reply.toString(), replyPrev.toString()))); } FilePath cmakeCacheTxt = m_parameters.buildDirectory.pathAppended("CMakeCache.txt"); @@ -295,8 +322,8 @@ void FileApiReader::makeBackupConfiguration(bool store) if (cmakeCacheTxt.exists()) if (!FileUtils::copyIfDifferent(cmakeCacheTxt, cmakeCacheTxtPrev)) Core::MessageManager::writeFlashing( - Tr::tr("Failed to copy \"%1\" to \"%2\".") - .arg(cmakeCacheTxt.toString(), cmakeCacheTxtPrev.toString())); + addCMakePrefix(Tr::tr("Failed to copy \"%1\" to \"%2\".") + .arg(cmakeCacheTxt.toString(), cmakeCacheTxtPrev.toString()))); } void FileApiReader::writeConfigurationIntoBuildDirectory(const QStringList &configurationArguments) @@ -340,6 +367,10 @@ void FileApiReader::startCMakeState(const QStringList &configurationArguments) m_cmakeProcess = std::make_unique<CMakeProcess>(); connect(m_cmakeProcess.get(), &CMakeProcess::finished, this, &FileApiReader::cmakeFinishedState); + connect(m_cmakeProcess.get(), &CMakeProcess::stdOutReady, this, [this](const QString &data) { + if (data.endsWith("Waiting for debugger client to connect...\n")) + emit debuggingStarted(); + }); qCDebug(cmakeFileApiMode) << ">>>>>> Running cmake with arguments:" << configurationArguments; // Reset watcher: diff --git a/src/plugins/cmakeprojectmanager/fileapireader.h b/src/plugins/cmakeprojectmanager/fileapireader.h index 115d22ea71a..1bf1e80332d 100644 --- a/src/plugins/cmakeprojectmanager/fileapireader.h +++ b/src/plugins/cmakeprojectmanager/fileapireader.h @@ -38,7 +38,11 @@ public: void setParameters(const BuildDirParameters &p); void resetData(); - void parse(bool forceCMakeRun, bool forceInitialConfiguration, bool forceExtraConfiguration); + void parse(bool forceCMakeRun, + bool forceInitialConfiguration, + bool forceExtraConfiguration, + bool debugging, + bool profiling); void stop(); void stopCMakeRun(); @@ -64,6 +68,7 @@ signals: void dataAvailable(bool restoredFromBackup) const; void dirty() const; void errorOccurred(const QString &message) const; + void debuggingStarted() const; private: void startState(); diff --git a/src/plugins/cmakeprojectmanager/presetsparser.cpp b/src/plugins/cmakeprojectmanager/presetsparser.cpp index f3f92f76fd5..3befe4d99d8 100644 --- a/src/plugins/cmakeprojectmanager/presetsparser.cpp +++ b/src/plugins/cmakeprojectmanager/presetsparser.cpp @@ -44,7 +44,7 @@ std::optional<QStringList> parseInclude(const QJsonValue &jsonValue) if (jsonValue.isArray()) { includes = QStringList(); const QJsonArray includeArray = jsonValue.toArray(); - for (const QJsonValue &includeValue : includeArray) + for (const auto &includeValue : includeArray) includes.value() << includeValue.toString(); } } @@ -103,7 +103,7 @@ std::optional<PresetsDetails::Condition> parseCondition(const QJsonValue &jsonVa if (object.value("list").isArray()) { condition->list = QStringList(); const QJsonArray listArray = object.value("list").toArray(); - for (const QJsonValue &listValue : listArray) + for (const auto &listValue : listArray) condition->list.value() << listValue.toString(); } } @@ -127,7 +127,7 @@ std::optional<PresetsDetails::Condition> parseCondition(const QJsonValue &jsonVa if (object.value("conditions").isArray()) { condition->conditions = std::vector<PresetsDetails::Condition::ConditionPtr>(); const QJsonArray conditionsArray = object.value("conditions").toArray(); - for (const QJsonValue &conditionsValue : conditionsArray) { + for (const auto &conditionsValue : conditionsArray) { condition->conditions.value().emplace_back( std::make_shared<PresetsDetails::Condition>( parseCondition(conditionsValue).value())); @@ -160,7 +160,7 @@ bool parseConfigurePresets(const QJsonValue &jsonValue, return false; const QJsonArray configurePresetsArray = jsonValue.toArray(); - for (const QJsonValue &presetJson : configurePresetsArray) { + for (const auto &presetJson : configurePresetsArray) { if (!presetJson.isObject()) continue; @@ -176,7 +176,7 @@ bool parseConfigurePresets(const QJsonValue &jsonValue, preset.inherits = QStringList(); if (inherits.isArray()) { const QJsonArray inheritsArray = inherits.toArray(); - for (const QJsonValue &inheritsValue : inheritsArray) + for (const auto &inheritsValue : inheritsArray) preset.inherits.value() << inheritsValue.toString(); } else { QString inheritsValue = inherits.toString(); @@ -350,7 +350,7 @@ bool parseBuildPresets(const QJsonValue &jsonValue, return false; const QJsonArray buildPresetsArray = jsonValue.toArray(); - for (const QJsonValue &presetJson : buildPresetsArray) { + for (const auto &presetJson : buildPresetsArray) { if (!presetJson.isObject()) continue; @@ -366,7 +366,7 @@ bool parseBuildPresets(const QJsonValue &jsonValue, preset.inherits = QStringList(); if (inherits.isArray()) { const QJsonArray inheritsArray = inherits.toArray(); - for (const QJsonValue &inheritsValue : inheritsArray) + for (const auto &inheritsValue : inheritsArray) preset.inherits.value() << inheritsValue.toString(); } else { QString inheritsValue = inherits.toString(); @@ -404,7 +404,7 @@ bool parseBuildPresets(const QJsonValue &jsonValue, preset.targets = QStringList(); if (targets.isArray()) { const QJsonArray targetsArray = targets.toArray(); - for (const QJsonValue &targetsValue : targetsArray) + for (const auto &targetsValue : targetsArray) preset.targets.value() << targetsValue.toString(); } else { QString targetsValue = targets.toString(); @@ -424,7 +424,7 @@ bool parseBuildPresets(const QJsonValue &jsonValue, if (nativeToolOptions.isArray()) { preset.nativeToolOptions = QStringList(); const QJsonArray toolOptionsArray = nativeToolOptions.toArray(); - for (const QJsonValue &toolOptionsValue : toolOptionsArray) + for (const auto &toolOptionsValue : toolOptionsArray) preset.nativeToolOptions.value() << toolOptionsValue.toString(); } } @@ -444,7 +444,8 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage { const Utils::expected_str<QByteArray> jsonContents = jsonFile.fileContents(); if (!jsonContents) { - errorMessage = Tr::tr("Failed to read file \"%1\".").arg(jsonFile.fileName()); + errorMessage + = ::CMakeProjectManager::Tr::tr("Failed to read file \"%1\".").arg(jsonFile.fileName()); return false; } @@ -460,7 +461,8 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage } if (!jsonDoc.isObject()) { - errorMessage = Tr::tr("Invalid file \"%1\".").arg(jsonFile.fileName()); + errorMessage + = ::CMakeProjectManager::Tr::tr("Invalid file \"%1\".").arg(jsonFile.fileName()); return false; } @@ -469,7 +471,8 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage m_presetsData.fileDir = jsonFile.parentDir(); if (!parseVersion(root.value("version"), m_presetsData.version)) { - errorMessage = Tr::tr("Invalid \"version\" in file \"%1\".").arg(jsonFile.fileName()); + errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"version\" in file \"%1\".") + .arg(jsonFile.fileName()); return false; } @@ -484,8 +487,9 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage if (!parseConfigurePresets(root.value("configurePresets"), m_presetsData.configurePresets, jsonFile.parentDir())) { - errorMessage - = Tr::tr("Invalid \"configurePresets\" section in %1 file").arg(jsonFile.fileName()); + errorMessage = ::CMakeProjectManager::Tr::tr( + "Invalid \"configurePresets\" section in %1 file") + .arg(jsonFile.fileName()); return false; } @@ -493,8 +497,8 @@ bool PresetsParser::parse(const Utils::FilePath &jsonFile, QString &errorMessage if (!parseBuildPresets(root.value("buildPresets"), m_presetsData.buildPresets, jsonFile.parentDir())) { - errorMessage - = Tr::tr("Invalid \"buildPresets\" section in %1 file").arg(jsonFile.fileName()); + errorMessage = ::CMakeProjectManager::Tr::tr("Invalid \"buildPresets\" section in %1 file") + .arg(jsonFile.fileName()); return false; } diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp index 7b9efb07b94..d8dcbde1322 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.cpp +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.cpp @@ -19,13 +19,13 @@ namespace CMakeProjectManager::Internal { std::unique_ptr<FolderNode> createCMakeVFolder(const Utils::FilePath &basePath, int priority, - const QString &displayName) + const QString &displayName, + bool sourcesOrHeaders) { auto newFolder = std::make_unique<VirtualFolderNode>(basePath); newFolder->setPriority(priority); newFolder->setDisplayName(displayName); - newFolder->setIsSourcesOrHeaders(displayName == "Source Files" - || displayName == "Header Files"); + newFolder->setIsSourcesOrHeaders(sourcesOrHeaders); return newFolder; } @@ -33,13 +33,14 @@ void addCMakeVFolder(FolderNode *base, const Utils::FilePath &basePath, int priority, const QString &displayName, - std::vector<std::unique_ptr<FileNode>> &&files) + std::vector<std::unique_ptr<FileNode>> &&files, + bool sourcesOrHeaders) { if (files.size() == 0) return; FolderNode *folder = base; if (!displayName.isEmpty()) { - auto newFolder = createCMakeVFolder(basePath, priority, displayName); + auto newFolder = createCMakeVFolder(basePath, priority, displayName, sourcesOrHeaders); folder = newFolder.get(); base->addNode(std::move(newFolder)); } diff --git a/src/plugins/cmakeprojectmanager/projecttreehelper.h b/src/plugins/cmakeprojectmanager/projecttreehelper.h index 34a8723759f..aa655492d4a 100644 --- a/src/plugins/cmakeprojectmanager/projecttreehelper.h +++ b/src/plugins/cmakeprojectmanager/projecttreehelper.h @@ -11,13 +11,15 @@ namespace CMakeProjectManager::Internal { std::unique_ptr<ProjectExplorer::FolderNode> createCMakeVFolder(const Utils::FilePath &basePath, int priority, - const QString &displayName); + const QString &displayName, + bool sourcesOrHeaders); void addCMakeVFolder(ProjectExplorer::FolderNode *base, const Utils::FilePath &basePath, int priority, const QString &displayName, - std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&files); + std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&files, + bool sourcesOrHeaders = false); std::vector<std::unique_ptr<ProjectExplorer::FileNode>> &&removeKnownNodes( const QSet<Utils::FilePath> &knownFiles, diff --git a/src/plugins/coco/CMakeLists.txt b/src/plugins/coco/CMakeLists.txt index 43f4fd7c4a3..20e5132fffb 100644 --- a/src/plugins/coco/CMakeLists.txt +++ b/src/plugins/coco/CMakeLists.txt @@ -1,5 +1,4 @@ add_qtc_plugin(Coco - PUBLIC_DEPENDS app_version PLUGIN_DEPENDS Core LanguageClient SOURCES cocolanguageclient.cpp cocolanguageclient.h diff --git a/src/plugins/coco/Coco.json.in b/src/plugins/coco/Coco.json.in index d4a16e34a65..01beb8c4984 100644 --- a/src/plugins/coco/Coco.json.in +++ b/src/plugins/coco/Coco.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Coco\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Coco", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Squish Coco support. Squish Coco is a code coverage tool for Tcl, QML, C# and C/C++.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Squish Coco support. Squish Coco is a code coverage tool for Tcl, QML, C# and C/C++.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/coco/coco.qbs b/src/plugins/coco/coco.qbs index 3a771f27175..70a072c3196 100644 --- a/src/plugins/coco/coco.qbs +++ b/src/plugins/coco/coco.qbs @@ -7,8 +7,6 @@ QtcPlugin { Depends { name: "LanguageClient" } Depends { name: "TextEditor" } - Depends { name: "app_version_header" } - Depends { name: "Qt"; submodules: ["widgets"] } files: [ diff --git a/src/plugins/coco/cocolanguageclient.cpp b/src/plugins/coco/cocolanguageclient.cpp index 56f3d20ee2c..b0841791f89 100644 --- a/src/plugins/coco/cocolanguageclient.cpp +++ b/src/plugins/coco/cocolanguageclient.cpp @@ -3,7 +3,6 @@ #include "cocolanguageclient.h" -#include <app/app_version.h> #include <coreplugin/editormanager/editormanager.h> #include <languageclient/diagnosticmanager.h> #include <languageclient/languageclienthoverhandler.h> @@ -17,6 +16,7 @@ #include <texteditor/textmark.h> #include <utils/utilsicons.h> +#include <QGuiApplication> #include <QTextEdit> using namespace LanguageClient; @@ -52,7 +52,7 @@ CocoLanguageClient::CocoLanguageClient(const FilePath &coco, const FilePath &csm ClientInfo info; info.setName("CocoQtCreator"); - info.setVersion(Core::Constants::IDE_VERSION_DISPLAY); + info.setVersion(QGuiApplication::applicationDisplayName()); setClientInfo(info); initClientCapabilities(); @@ -239,7 +239,7 @@ public: void enableCodecoverageSupport() { JsonObject coverageSupport(QJsonObject{{"codeCoverageSupport", true}}); - insert(u"publishDiagnostics", coverageSupport); + insert("publishDiagnostics", coverageSupport); } }; diff --git a/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in b/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in index 00da891d743..4842c3d73cc 100644 --- a/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in +++ b/src/plugins/compilationdatabaseprojectmanager/CompilationDatabaseProjectManager.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"CompilationDatabaseProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CompilationDatabaseProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Compilation Database project support.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-compilation-database-project\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Compilation Database file</comment>\", - \" <glob pattern=\'compile_commands.json\' weight=\'100\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Category" : "Build Systems", + "Description" : "Compilation Database project support.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-compilation-database-project'>", + " <sub-class-of type='text/plain'/>", + " <comment>Compilation Database file</comment>", + " <glob pattern='compile_commands.json' weight='100'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index ad614e7e23a..2ca95d6ecb7 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -16,7 +16,7 @@ #include <projectexplorer/deploymentdata.h> #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/headerpath.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/namedwidget.h> #include <projectexplorer/projectexplorerconstants.h> @@ -391,7 +391,7 @@ void CompilationDatabaseBuildSystem::buildTreeAndProjectParts() root->addNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project)); - if (QFile::exists(dbContents.extraFileName)) + if (QFileInfo::exists(dbContents.extraFileName)) root->addNode(std::make_unique<FileNode>(Utils::FilePath::fromString(dbContents.extraFileName), FileType::Project)); diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp index f8b25747716..96b7e423d42 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabasetests.cpp @@ -9,7 +9,7 @@ #include <cppeditor/cpptoolstestcase.h> #include <cppeditor/projectinfo.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp index be0ab87e547..fd6d070f49d 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.cpp @@ -54,7 +54,7 @@ static CppEditor::ProjectFile::Kind fileKindFromString(QString flag) return ProjectFile::Unclassified; } -QStringList filterFromFileName(const QStringList &flags, QString fileName) +QStringList filterFromFileName(const QStringList &flags, const QString &fileName) { QStringList result; result.reserve(flags.size()); diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h index ec024443d8f..df0d4c9c9fe 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -31,7 +31,7 @@ public: using MimeBinaryCache = QHash<QString, bool>; -QStringList filterFromFileName(const QStringList &flags, QString baseName); +QStringList filterFromFileName(const QStringList &flags, const QString &fileName); void filteredFlags(const Utils::FilePath &filePath, const Utils::FilePath &workingDir, diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp index ccbead4c4ca..5f860956026 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -7,22 +7,108 @@ #include "compilationdatabaseprojectmanagertr.h" #include <coreplugin/progressmanager/progressmanager.h> + #include <projectexplorer/treescanner.h> + #include <utils/async.h> #include <utils/mimeutils.h> #include <QCryptographicHash> -#include <QDir> -#include <QFileInfo> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> +#include <vector> + using namespace ProjectExplorer; using namespace Utils; -namespace CompilationDatabaseProjectManager { -namespace Internal { +namespace CompilationDatabaseProjectManager::Internal { + +static QStringList jsonObjectFlags(const QJsonObject &object, QSet<QString> &flagsCache) +{ + QStringList flags; + const QJsonArray arguments = object["arguments"].toArray(); + if (arguments.isEmpty()) { + flags = splitCommandLine(object["command"].toString(), flagsCache); + } else { + flags.reserve(arguments.size()); + for (const QJsonValue &arg : arguments) { + auto flagIt = flagsCache.insert(arg.toString()); + flags.append(*flagIt); + } + } + return flags; +} + +static FilePath jsonObjectFilePath(const QJsonObject &object) +{ + const FilePath workingDir = FilePath::fromUserInput(object["directory"].toString()); + return workingDir.resolvePath(object["file"].toString()); +} + +static std::vector<DbEntry> readJsonObjects(const QByteArray &projectFileContents) +{ + std::vector<DbEntry> result; + + int objectStart = projectFileContents.indexOf('{'); + int objectEnd = projectFileContents.indexOf('}', objectStart + 1); + + QSet<QString> flagsCache; + while (objectStart >= 0 && objectEnd >= 0) { + const QJsonDocument document = QJsonDocument::fromJson( + projectFileContents.mid(objectStart, objectEnd - objectStart + 1)); + if (document.isNull()) { + // The end was found incorrectly, search for the next one. + objectEnd = projectFileContents.indexOf('}', objectEnd + 1); + continue; + } + + const QJsonObject object = document.object(); + const FilePath filePath = jsonObjectFilePath(object); + const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache), + filePath.fileName()); + result.push_back({flags, filePath, FilePath::fromUserInput(object["directory"].toString())}); + + objectStart = projectFileContents.indexOf('{', objectEnd + 1); + objectEnd = projectFileContents.indexOf('}', objectStart + 1); + } + return result; +} + +static QStringList readExtraFiles(const QString &filePath) +{ + QStringList result; + + QFile file(filePath); + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + while (!stream.atEnd()) { + const QString line = stream.readLine().trimmed(); + if (line.isEmpty() || line.startsWith('#')) + continue; + + result.push_back(line); + } + } + return result; +} + +static DbContents parseProject(const QByteArray &projectFileContents, + const FilePath &projectFilePath) +{ + DbContents dbContents; + dbContents.entries = readJsonObjects(projectFileContents); + dbContents.extraFileName = projectFilePath.toString() + + Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX; + dbContents.extras = readExtraFiles(dbContents.extraFileName); + std::sort(dbContents.entries.begin(), dbContents.entries.end(), + [](const DbEntry &lhs, const DbEntry &rhs) { + return std::lexicographical_compare(lhs.flags.begin(), lhs.flags.end(), + rhs.flags.begin(), rhs.flags.end()); + }); + return dbContents; +} CompilationDbParser::CompilationDbParser(const QString &projectName, const FilePath &projectPath, @@ -82,7 +168,7 @@ void CompilationDbParser::start() return isIgnored; }); - m_treeScanner->setTypeFactory([](const Utils::MimeType &mimeType, const Utils::FilePath &fn) { + m_treeScanner->setTypeFactory([](const MimeType &mimeType, const FilePath &fn) { return TreeScanner::genericFileType(mimeType, fn); }); m_treeScanner->asyncScanForFiles(m_rootPath); @@ -95,7 +181,7 @@ void CompilationDbParser::start() } // Thread 2: Parse the project file. - const QFuture<DbContents> future = Utils::asyncRun(&CompilationDbParser::parseProject, this); + const auto future = Utils::asyncRun(parseProject, m_projectFileContents, m_projectFilePath); Core::ProgressManager::addTask(future, Tr::tr("Parse \"%1\" project").arg(m_projectName), "CompilationDatabase.Parse"); @@ -138,95 +224,4 @@ void CompilationDbParser::finish(ParseResult result) deleteLater(); } -static QStringList jsonObjectFlags(const QJsonObject &object, QSet<QString> &flagsCache) -{ - QStringList flags; - const QJsonArray arguments = object["arguments"].toArray(); - if (arguments.isEmpty()) { - flags = splitCommandLine(object["command"].toString(), flagsCache); - } else { - flags.reserve(arguments.size()); - for (const QJsonValue &arg : arguments) { - auto flagIt = flagsCache.insert(arg.toString()); - flags.append(*flagIt); - } - } - - return flags; -} - -static FilePath jsonObjectFilePath(const QJsonObject &object) -{ - const FilePath workingDir = FilePath::fromUserInput(object["directory"].toString()); - return workingDir.resolvePath(object["file"].toString()); -} - -std::vector<DbEntry> CompilationDbParser::readJsonObjects() const -{ - std::vector<DbEntry> result; - - int objectStart = m_projectFileContents.indexOf('{'); - int objectEnd = m_projectFileContents.indexOf('}', objectStart + 1); - - QSet<QString> flagsCache; - while (objectStart >= 0 && objectEnd >= 0) { - const QJsonDocument document = QJsonDocument::fromJson( - m_projectFileContents.mid(objectStart, objectEnd - objectStart + 1)); - if (document.isNull()) { - // The end was found incorrectly, search for the next one. - objectEnd = m_projectFileContents.indexOf('}', objectEnd + 1); - continue; - } - - const QJsonObject object = document.object(); - const Utils::FilePath filePath = jsonObjectFilePath(object); - const QStringList flags = filterFromFileName(jsonObjectFlags(object, flagsCache), - filePath.fileName()); - result.push_back({flags, filePath, FilePath::fromUserInput(object["directory"].toString())}); - - objectStart = m_projectFileContents.indexOf('{', objectEnd + 1); - objectEnd = m_projectFileContents.indexOf('}', objectStart + 1); - } - - return result; -} - -QStringList readExtraFiles(const QString &filePath) -{ - QStringList result; - - QFile file(filePath); - if (file.open(QFile::ReadOnly)) { - QTextStream stream(&file); - - while (!stream.atEnd()) { - QString line = stream.readLine(); - line = line.trimmed(); - - if (line.isEmpty() || line.startsWith('#')) - continue; - - result.push_back(line); - } - } - - return result; -} - -DbContents CompilationDbParser::parseProject() -{ - DbContents dbContents; - dbContents.entries = readJsonObjects(); - dbContents.extraFileName = m_projectFilePath.toString() + - Constants::COMPILATIONDATABASEPROJECT_FILES_SUFFIX; - dbContents.extras = readExtraFiles(dbContents.extraFileName); - std::sort(dbContents.entries.begin(), dbContents.entries.end(), - [](const DbEntry &lhs, const DbEntry &rhs) { - return std::lexicographical_compare(lhs.flags.begin(), lhs.flags.end(), - rhs.flags.begin(), rhs.flags.end()); - }); - return dbContents; -} - -} // namespace Internal -} // namespace CompilationDatabaseProjectManager +} // namespace CompilationDatabaseProjectManager::Internal diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h index d0d8eb80798..a06ba82581e 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h @@ -10,19 +10,15 @@ #include <utils/fileutils.h> #include <QFutureWatcher> -#include <QList> #include <QObject> #include <QStringList> -#include <vector> - namespace ProjectExplorer { class FileNode; class TreeScanner; } -namespace CompilationDatabaseProjectManager { -namespace Internal { +namespace CompilationDatabaseProjectManager::Internal { enum class ParseResult { Success, Failure, Cached }; @@ -56,8 +52,6 @@ signals: private: void parserJobFinished(); void finish(ParseResult result); - DbContents parseProject(); - std::vector<DbEntry> readJsonObjects() const; const QString m_projectName; const Utils::FilePath m_projectFilePath; @@ -73,5 +67,4 @@ private: ProjectExplorer::BuildSystem::ParseGuard m_guard; }; -} // namespace Internal -} // namespace CompilationDatabaseProjectManager +} // namespace CompilationDatabaseProjectManager::Internal diff --git a/src/plugins/compilerexplorer/CMakeLists.txt b/src/plugins/compilerexplorer/CMakeLists.txt new file mode 100644 index 00000000000..55368044706 --- /dev/null +++ b/src/plugins/compilerexplorer/CMakeLists.txt @@ -0,0 +1,31 @@ +add_qtc_plugin(CompilerExplorer + PLUGIN_DEPENDS Core ProjectExplorer TextEditor + DEPENDS Qt::Network TerminalLib Spinner + SOURCES + api/config.h + api/compile.cpp + api/compile.h + api/compiler.cpp + api/compiler.h + api/compilerexplorerapi.h + api/language.cpp + api/language.h + api/library.cpp + api/library.h + api/request.h + + compilerexplorer.qrc + + compilerexploreraspects.cpp + compilerexploreraspects.h + compilerexplorerplugin.cpp + compilerexplorertr.h + compilerexplorereditor.cpp + compilerexplorereditor.h + compilerexplorersettings.cpp + compilerexplorersettings.h + compilerexploreroptions.cpp + compilerexploreroptions.h + + logos/logos.qrc +) diff --git a/src/plugins/compilerexplorer/CompilerExplorer.json.in b/src/plugins/compilerexplorer/CompilerExplorer.json.in new file mode 100644 index 00000000000..db289e41a8a --- /dev/null +++ b/src/plugins/compilerexplorer/CompilerExplorer.json.in @@ -0,0 +1,29 @@ +{ + "Name" : "CompilerExplorer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." + ], + "Description" : "Integrates https://godbolt.org into Qt Creator.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/compiler-explorer'>", + " <sub-class-of type='application/json'/>", + " <comment>Compiler Explorer file</comment>", + " <glob pattern='*.qtce'/>", + " </mime-type>", + "</mime-info>" + ] +} diff --git a/src/plugins/compilerexplorer/api/compile.cpp b/src/plugins/compilerexplorer/api/compile.cpp new file mode 100644 index 00000000000..8a159dcf369 --- /dev/null +++ b/src/plugins/compilerexplorer/api/compile.cpp @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compile.h" + +#include "request.h" + +namespace CompilerExplorer::Api { +QFuture<CompileResult> compile(const Config &config, const CompileParameters ¶meters) +{ + const QUrl url = config.url({"api/compiler", parameters.compilerId, "compile"}); + + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + req.setRawHeader("Accept", "application/json"); + + return jsonRequest<CompileResult>( + config.networkManager, + req, + [](const QJsonDocument &response) { + CompileResult result; + + QJsonObject obj = response.object(); + return CompileResult::fromJson(obj); + }, + QNetworkAccessManager::PostOperation, + parameters.toByteArray()); +} +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/compile.h b/src/plugins/compilerexplorer/api/compile.h new file mode 100644 index 00000000000..618af39a246 --- /dev/null +++ b/src/plugins/compilerexplorer/api/compile.h @@ -0,0 +1,342 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "compiler.h" +#include "config.h" + +#include "../compilerexploreraspects.h" + +#include <QList> +#include <QString> + +#include <QFuture> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QNetworkAccessManager> + +namespace CompilerExplorer::Api { + +struct ExecuteParameters +{ + ExecuteParameters &args(QStringList args) + { + obj["args"] = QJsonArray::fromStringList(args); + return *this; + } + ExecuteParameters &stdIn(const QString &stdIn) + { + obj["stdin"] = stdIn; + return *this; + } + QJsonObject obj; +}; + +struct CompileParameters +{ + CompileParameters() = delete; + CompileParameters(const QString &cId) + : compilerId(cId) + { + obj["compiler"] = cId; + } + CompileParameters(const Compiler &compiler) + : compilerId(compiler.id) + { + obj["compiler"] = compiler.id; + } + + QByteArray toByteArray() const { return QJsonDocument(obj).toJson(QJsonDocument::Compact); } + + struct Options + { + Options &userArguments(const QString &userArguments) + { + obj["userArguments"] = userArguments; + return *this; + } + + struct CompilerOptions + { + bool skipAsm{false}; + bool executorRequest{false}; + }; + + Options &compilerOptions(CompilerOptions compilerOptions) + { + QJsonObject co; + co["skipAsm"] = compilerOptions.skipAsm; + co["executorRequest"] = compilerOptions.executorRequest; + + obj["compilerOptions"] = co; + return *this; + } + + Options &executeParameters(ExecuteParameters executeParameters) + { + obj["executeParameters"] = executeParameters.obj; + return *this; + } + + struct Filters + { + bool binary{false}; + bool binaryObject{false}; + bool commentOnly{true}; + bool demangle{true}; + bool directives{true}; + bool execute{false}; + bool intel{true}; + bool labels{true}; + bool libraryCode{false}; + bool trim{false}; + bool debugCalls{false}; + }; + + Options &filters(Filters filters) + { + QJsonObject filter; + filter["binary"] = filters.binary; + filter["binaryObject"] = filters.binaryObject; + filter["commentOnly"] = filters.commentOnly; + filter["demangle"] = filters.demangle; + filter["directives"] = filters.directives; + filter["execute"] = filters.execute; + filter["intel"] = filters.intel; + filter["labels"] = filters.labels; + filter["libraryCode"] = filters.libraryCode; + filter["trim"] = filters.trim; + filter["debugCalls"] = filters.debugCalls; + + obj["filters"] = filter; + return *this; + } + + struct Libraries + { + QJsonArray array; + Libraries &add(const QString id, const QString version) + { + QJsonObject obj; + obj["id"] = id; + obj["version"] = version; + array.append(obj); + return *this; + } + }; + + Options &libraries(const Libraries &libraries) + { + obj["libraries"] = libraries.array; + return *this; + } + + Options &libraries(const QMap<QString, QString> &libraries) + { + Libraries result; + for (const auto &key : libraries.keys()) { + result.add(key, libraries[key]); + } + obj["libraries"] = result.array; + return *this; + } + + QJsonObject obj; + }; + + CompileParameters &source(const QString &source) + { + obj["source"] = source; + return *this; + } + CompileParameters &language(const QString &languageId) + { + obj["lang"] = languageId; + return *this; + } + CompileParameters &options(Options options) + { + obj["options"] = options.obj; + return *this; + } + + QJsonObject obj; + QString compilerId; +}; + +struct CompilerResult +{ + struct Line + { + QString text; + struct Tag + { + int column; + QString file; + int line; + int severity; + QString text; + static Tag fromJson(const QJsonObject &obj) + { + Tag tag; + tag.column = obj["column"].toInt(); + tag.file = obj["file"].toString(); + tag.line = obj["line"].toInt(); + tag.severity = obj["severity"].toInt(); + tag.text = obj["text"].toString(); + return tag; + } + }; + std::optional<Tag> tag; + + static Line fromJson(const QJsonObject &obj) + { + Line line; + line.text = obj["text"].toString(); + if (obj.contains("tag")) { + line.tag = Tag::fromJson(obj["tag"].toObject()); + } + return line; + } + }; + + int code; + bool timedOut; + bool truncated; + QList<Line> stdErr; + QList<Line> stdOut; + + static CompilerResult fromJson(const QJsonObject &object) + { + CompilerResult result; + + result.timedOut = object["timedOut"].toBool(); + result.truncated = object["truncated"].toBool(); + result.code = object["code"].toInt(); + for (const auto &line : object["stderr"].toArray()) + result.stdErr.append(Line::fromJson(line.toObject())); + for (const auto &line : object["stdout"].toArray()) + result.stdOut.append(Line::fromJson(line.toObject())); + + return result; + } +}; + +struct CompileResult : CompilerResult +{ + struct Asm + { + // A part of the asm that is a (hyper)link to a label (the name references labelDefinitions) + struct Label + { + QString name; + struct Range + { + int startCol; + int endCol; + } range; + + static Label fromJson(const QJsonObject &object) + { + Label label; + label.name = object["name"].toString(); + label.range.startCol = object["range"]["startCol"].toInt(); + label.range.endCol = object["range"]["endCol"].toInt(); + return label; + } + }; + QList<Label> labels; + // The part of the source that generated this asm + struct + { + int column; + QString file; + int line; + } source; + + QString text; + QStringList opcodes; + + static Asm fromJson(const QJsonObject &object) + { + Asm asm_; + asm_.text = object["text"].toString(); + auto opcodes = object["opcodes"].toArray(); + for (const auto &opcode : opcodes) + asm_.opcodes.append(opcode.toString()); + asm_.source.column = object["source"]["column"].toInt(); + asm_.source.file = object["source"]["file"].toString(); + asm_.source.line = object["source"]["line"].toInt(); + for (const auto &label : object["labels"].toArray()) { + asm_.labels.append(Label::fromJson(label.toObject())); + } + return asm_; + } + }; + + struct ExecResult + { + int code; + bool didExecute; + bool timedOut; + bool truncated; + QStringList stdOutLines; + QStringList stdErrLines; + + CompilerResult buildResult; + + static ExecResult fromJson(const QJsonObject &object) + { + ExecResult result; + result.code = object["code"].toInt(); + result.didExecute = object["didExecute"].toBool(); + result.timedOut = object["timedOut"].toBool(); + result.truncated = object["truncated"].toBool(); + for (const auto &line : object["stdout"].toArray()) + result.stdOutLines.append(line.toObject()["text"].toString()); + for (const auto &line : object["stderr"].toArray()) + result.stdErrLines.append(line.toObject()["text"].toString()); + result.buildResult = CompilerResult::fromJson(object["buildResult"].toObject()); + return result; + } + }; + + QMap<QString, int> labelDefinitions; + QList<Asm> assemblyLines; + + std::optional<ExecResult> execResult; + + static CompileResult fromJson(const QJsonObject &object) + { + CompilerResult compilerResult = CompilerResult::fromJson(object); + + CompileResult result; + result.code = compilerResult.code; + result.timedOut = compilerResult.timedOut; + result.truncated = compilerResult.truncated; + result.stdOut = compilerResult.stdOut; + result.stdErr = compilerResult.stdErr; + + if (object.contains("labelDefinitions")) { + QJsonObject labelDefinitions = object["labelDefinitions"].toObject(); + for (const QString &key : labelDefinitions.keys()) + result.labelDefinitions[key] = labelDefinitions[key].toInt(); + } + + if (object.contains("asm")) { + for (const auto &asmLine : object["asm"].toArray()) + result.assemblyLines.append(Asm::fromJson(asmLine.toObject())); + } + + if (object.contains("execResult")) + result.execResult = ExecResult::fromJson(object["execResult"].toObject()); + + return result; + } +}; + +QFuture<CompileResult> compile(const Config &config, const CompileParameters ¶meters); + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/compiler.cpp b/src/plugins/compilerexplorer/api/compiler.cpp new file mode 100644 index 00000000000..9bc7a57033d --- /dev/null +++ b/src/plugins/compilerexplorer/api/compiler.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compiler.h" + +#include "request.h" + +#include <QFutureWatcher> +#include <QUrlQuery> + +namespace CompilerExplorer::Api { + +QFuture<Compilers> compilers(const Config &config, + const QString &languageId, + const QSet<QString> &extraFields) +{ + QUrl url = config.url(QStringList{"api/compilers"} + << (languageId.isEmpty() ? QString() : languageId)); + + QString fieldParam; + + if (!extraFields.isEmpty()) { + QSet<QString> fields = {"id", "name", "lang", "compilerType", "semver"}; + fields.unite(extraFields); + for (const auto &field : fields) { + if (!fieldParam.isEmpty()) + fieldParam += ","; + fieldParam += field; + } + } + + if (!fieldParam.isEmpty()) + url.setQuery(QUrlQuery{{"fields", fieldParam}}); + + QNetworkRequest req(url); + req.setRawHeader("Accept", "application/json"); + + auto fromJson = [extraFields](const QJsonDocument &doc) { + QJsonArray compilers = doc.array(); + Compilers result; + + for (const auto &compiler : compilers) { + QJsonObject obj = compiler.toObject(); + Compiler c; + c.id = obj["id"].toString(); + c.name = obj["name"].toString(); + c.languageId = obj["lang"].toString(); + c.compilerType = obj["compilerType"].toString(); + c.version = obj["semver"].toString(); + c.instructionSet = obj["instructionSet"].toString(); + + for (const auto &field : extraFields) { + c.extraFields[field] = obj[field].toString(); + } + + result.append(c); + } + + return result; + }; + + return jsonRequest<Compilers>(config.networkManager, req, fromJson); +} + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/compiler.h b/src/plugins/compilerexplorer/api/compiler.h new file mode 100644 index 00000000000..070817206e2 --- /dev/null +++ b/src/plugins/compilerexplorer/api/compiler.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "config.h" + +#include <QFuture> +#include <QList> +#include <QMap> +#include <QNetworkAccessManager> +#include <QSet> +#include <QString> + +namespace CompilerExplorer::Api { + +struct Compiler +{ + QString id; + QString name; + QString languageId; + QString compilerType; + QString version; + QString instructionSet; + QMap<QString, QString> extraFields; +}; + +using Compilers = QList<Compiler>; + +QFuture<Compilers> compilers(const Config &config, + const QString &languageId = {}, + const QSet<QString> &extraFields = {}); + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/compilerexplorerapi.h b/src/plugins/compilerexplorer/api/compilerexplorerapi.h new file mode 100644 index 00000000000..abba7313074 --- /dev/null +++ b/src/plugins/compilerexplorer/api/compilerexplorerapi.h @@ -0,0 +1,9 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "compile.h" +#include "compiler.h" +#include "language.h" +#include "library.h" diff --git a/src/plugins/compilerexplorer/api/config.h b/src/plugins/compilerexplorer/api/config.h new file mode 100644 index 00000000000..c574c8e0e95 --- /dev/null +++ b/src/plugins/compilerexplorer/api/config.h @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <QNetworkAccessManager> +#include <QUrl> + +namespace CompilerExplorer::Api { + +struct Config +{ + Config(QNetworkAccessManager *networkManager) + : networkManager(networkManager) + {} + Config(QNetworkAccessManager *networkManager, const QUrl &baseUrl) + : networkManager(networkManager) + , baseUrl(baseUrl){}; + + Config(QNetworkAccessManager *networkManager, const QString &baseUrl) + : networkManager(networkManager) + , baseUrl(QUrl::fromUserInput(baseUrl)){}; + + QNetworkAccessManager *networkManager; + QUrl baseUrl{"https://godbolt.org/"}; + + QUrl url(const QStringList &paths) const { return baseUrl.resolved(paths.join("/")); } +}; + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/language.cpp b/src/plugins/compilerexplorer/api/language.cpp new file mode 100644 index 00000000000..0b95c7d2be6 --- /dev/null +++ b/src/plugins/compilerexplorer/api/language.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "language.h" + +#include "request.h" + +#include <QUrlQuery> + +namespace CompilerExplorer::Api { + +QFuture<Languages> languages(const Config &config) +{ + QUrl url = config.url({"api/languages"}); + url.setQuery(QUrlQuery{{"fields", "id,name,extensions,logoUrl"}}); + QNetworkRequest req(url); + req.setRawHeader("Accept", "application/json"); + + return jsonRequest<Languages>(config.networkManager, req, [](const QJsonDocument &doc) { + QJsonArray languages = doc.array(); + Languages result; + for (const auto &language : languages) { + QJsonObject obj = language.toObject(); + Language l; + l.id = obj["id"].toString(); + l.name = obj["name"].toString(); + l.logoUrl = obj["logoUrl"].toString(); + QJsonArray extensions = obj["extensions"].toArray(); + for (const auto &extension : extensions) { + l.extensions.append(extension.toString()); + } + + result.append(l); + } + std::sort(result.begin(), result.end(), [](const Language &a, const Language &b) { + return a.name < b.name; + }); + + return result; + }); +} + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/language.h b/src/plugins/compilerexplorer/api/language.h new file mode 100644 index 00000000000..85780b7466a --- /dev/null +++ b/src/plugins/compilerexplorer/api/language.h @@ -0,0 +1,27 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "config.h" + +#include <QFuture> +#include <QNetworkAccessManager> +#include <QString> +#include <QStringList> + +namespace CompilerExplorer::Api { + +struct Language +{ + QString id; + QString name; + QString logoUrl; + QStringList extensions; + QString monaco; +}; + +using Languages = QList<Language>; +QFuture<Languages> languages(const Config &config); + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/library.cpp b/src/plugins/compilerexplorer/api/library.cpp new file mode 100644 index 00000000000..8015beced73 --- /dev/null +++ b/src/plugins/compilerexplorer/api/library.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "library.h" + +#include "request.h" + +#include <utils/qtcassert.h> + +namespace CompilerExplorer::Api { + +QFuture<Libraries> libraries(const Config &config, const QString &languageId) +{ + QTC_ASSERT(!languageId.isEmpty(), + return QtFuture::makeExceptionalFuture<Libraries>( + std::make_exception_ptr(std::runtime_error("Language ID is empty.")))); + + const QUrl url = config.url({"api/libraries", languageId}); + + QNetworkRequest req(url); + req.setRawHeader("Accept", "application/json"); + + return jsonRequest<Libraries>(config.networkManager, req, [](const QJsonDocument &doc) { + QJsonArray libraries = doc.array(); + Libraries result; + + for (const auto &library : libraries) { + QJsonObject obj = library.toObject(); + Library l; + l.id = obj["id"].toString(); + l.name = obj["name"].toString(); + l.url = QUrl::fromUserInput(obj["url"].toString()); + + QJsonArray versions = obj["versions"].toArray(); + for (const auto &version : versions) { + l.versions.append({ + version.toObject()["version"].toString(), + version.toObject()["id"].toString(), + }); + } + + result.append(l); + } + + return result; + }); +} +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/library.h b/src/plugins/compilerexplorer/api/library.h new file mode 100644 index 00000000000..07e8a117341 --- /dev/null +++ b/src/plugins/compilerexplorer/api/library.h @@ -0,0 +1,32 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "config.h" + +#include <QFuture> +#include <QList> +#include <QNetworkAccessManager> +#include <QString> +#include <QUrl> + +namespace CompilerExplorer::Api { +struct Library +{ + QString id; + QString name; + QUrl url; + + struct Version + { + QString version; + QString id; + }; + QList<Version> versions; +}; + +using Libraries = QList<Library>; + +QFuture<Libraries> libraries(const Config &config, const QString &languageId); +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/api/request.h b/src/plugins/compilerexplorer/api/request.h new file mode 100644 index 00000000000..dc55ab80b6e --- /dev/null +++ b/src/plugins/compilerexplorer/api/request.h @@ -0,0 +1,141 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <QCoreApplication> +#include <QFuture> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QLoggingCategory> +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QPromise> +#include <QString> + +static Q_LOGGING_CATEGORY(apiLog, "qtc.compilerexplorer.api", QtWarningMsg); + +namespace CompilerExplorer::Api { + +static inline QString toString(QNetworkAccessManager::Operation op) +{ + switch (op) { + case QNetworkAccessManager::GetOperation: + return QStringLiteral("GET"); + case QNetworkAccessManager::PostOperation: + return QStringLiteral("POST"); + case QNetworkAccessManager::PutOperation: + return QStringLiteral("PUT"); + case QNetworkAccessManager::DeleteOperation: + return QStringLiteral("DELETE"); + case QNetworkAccessManager::HeadOperation: + return QStringLiteral("HEAD"); + case QNetworkAccessManager::CustomOperation: + return QStringLiteral("CUSTOM"); + case QNetworkAccessManager::UnknownOperation: + break; + } + + return "<unknown>"; +} + +static int debugRequestId = 0; + +template<typename Result> +QFuture<Result> request( + QNetworkAccessManager *networkManager, + const QNetworkRequest &req, + std::function<void(const QByteArray &, QSharedPointer<QPromise<Result>>)> callback, + QNetworkAccessManager::Operation op = QNetworkAccessManager::GetOperation, + const QByteArray &outData = {}) +{ + QSharedPointer<QPromise<Result>> p(new QPromise<Result>); + p->start(); + + // For logging purposes only + debugRequestId += 1; + + const auto reqId = [r = debugRequestId] { return QString("[%1]").arg(r); }; + + if (outData.isEmpty()) + qCDebug(apiLog).noquote() << reqId() << "Requesting" << toString(op) + << req.url().toString(); + else + qCDebug(apiLog).noquote() << reqId() << "Requesting" << toString(op) << req.url().toString() + << "with payload:" << QString::fromUtf8(outData); + + QNetworkReply *reply = nullptr; + + switch (op) { + case QNetworkAccessManager::GetOperation: + reply = networkManager->get(req); + break; + case QNetworkAccessManager::PostOperation: + reply = networkManager->post(req, outData); + break; + case QNetworkAccessManager::PutOperation: + reply = networkManager->put(req, outData); + break; + case QNetworkAccessManager::DeleteOperation: + reply = networkManager->deleteResource(req); + break; + default: + return QtFuture::makeExceptionalFuture<Result>( + std::make_exception_ptr(std::runtime_error("Unsupported operation"))); + } + + QObject::connect(reply, &QNetworkReply::finished, [p, reply, callback, reqId] { + if (reply->error() != QNetworkReply::NoError) { + qCWarning(apiLog).noquote() + << reqId() << "Request failed:" << reply->error() << reply->errorString(); + + QString errorMessage; + if (reply->error() == QNetworkReply::ContentNotFoundError) { + errorMessage = QCoreApplication::translate("QtC::CompilerExplorer", "Not found"); + } else + errorMessage = reply->errorString(); + + p->setException(std::make_exception_ptr(std::runtime_error(errorMessage.toUtf8()))); + reply->deleteLater(); + p->finish(); + return; + } + QByteArray data = reply->readAll(); + qCDebug(apiLog).noquote() << reqId() << "Request finished:" << data; + + callback(data, p); + + reply->deleteLater(); + p->finish(); + }); + + return p->future(); +} + +template<typename Result> +QFuture<Result> jsonRequest(QNetworkAccessManager *networkManager, + const QNetworkRequest &req, + std::function<Result(QJsonDocument)> callback, + QNetworkAccessManager::Operation op + = QNetworkAccessManager::GetOperation, + const QByteArray &outData = {}) +{ + return request<Result>( + networkManager, + req, + [callback](const QByteArray &reply, auto promise) { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(reply, &error); + if (error.error != QJsonParseError::NoError) { + promise->setException( + std::make_exception_ptr(std::runtime_error(error.errorString().toUtf8()))); + return; + } + promise->addResult(callback(doc)); + }, + op, + outData); +} + +} // namespace CompilerExplorer::Api diff --git a/src/plugins/compilerexplorer/compilerexplorer.qbs b/src/plugins/compilerexplorer/compilerexplorer.qbs new file mode 100644 index 00000000000..47eb03f9e46 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorer.qbs @@ -0,0 +1,42 @@ +import qbs 1.0 + +QtcPlugin { + name: "CompilerExplorer" + + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } + Depends { name: "Spinner" } + Depends { name: "TerminalLib" } + Depends { name: "TextEditor" } + Depends { name: "Qt"; submodules: ["widgets", "network"] } + + files: [ + "api/compile.cpp", + "api/compile.h", + "api/compiler.cpp", + "api/compiler.h", + "api/compilerexplorerapi.h", + "api/config.h", + "api/language.cpp", + "api/language.h", + "api/library.cpp", + "api/library.h", + "api/request.h", + + "compilerexplorer.qrc", + + "compilerexploreraspects.cpp", + "compilerexploreraspects.h", + "compilerexplorerconstants.h", + "compilerexplorereditor.cpp", + "compilerexplorereditor.h", + "compilerexploreroptions.cpp", + "compilerexploreroptions.h", + "compilerexplorerplugin.cpp", + "compilerexplorersettings.cpp", + "compilerexplorersettings.h", + "compilerexplorertr.h", + + "logos/logos.qrc" + ] +} diff --git a/src/plugins/compilerexplorer/compilerexplorer.qrc b/src/plugins/compilerexplorer/compilerexplorer.qrc new file mode 100644 index 00000000000..ea46de321c6 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorer.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/compilerexplorer/wizard/"> + <file>wizard/cpp/wizard.json</file> + <file>wizard/cpp/file.qtce</file> + <file>wizard/python/wizard.json</file> + <file>wizard/python/file.qtce</file> + </qresource> +</RCC> diff --git a/src/plugins/compilerexplorer/compilerexploreraspects.cpp b/src/plugins/compilerexplorer/compilerexploreraspects.cpp new file mode 100644 index 00000000000..cf02893d770 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexploreraspects.cpp @@ -0,0 +1,237 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compilerexploreraspects.h" +#include "compilerexplorertr.h" + +#include "api/library.h" + +#include <utils/algorithm.h> +#include <utils/layoutbuilder.h> + +#include <QComboBox> +#include <QCompleter> +#include <QFutureWatcher> +#include <QPushButton> +#include <QStackedWidget> + +using namespace Utils; + +namespace CompilerExplorer { + +LibrarySelectionAspect::LibrarySelectionAspect(AspectContainer *container) + : TypedAspect<QMap<QString, QString>>(container) +{} + +void LibrarySelectionAspect::bufferToGui() +{ + if (!m_model) + return; + + for (int i = 0; i < m_model->rowCount(); i++) { + QModelIndex idx = m_model->index(i, 0); + if (m_buffer.contains(qvariant_cast<Api::Library>(idx.data(LibraryData)).id)) + m_model->setData(idx, + m_buffer[qvariant_cast<Api::Library>(idx.data(LibraryData)).id], + SelectedVersion); + else + m_model->setData(idx, QVariant(), SelectedVersion); + } + + handleGuiChanged(); +} + +bool LibrarySelectionAspect::guiToBuffer() +{ + if (!m_model) + return false; + + auto oldBuffer = m_buffer; + + m_buffer.clear(); + + for (int i = 0; i < m_model->rowCount(); i++) { + if (m_model->item(i)->data(SelectedVersion).isValid()) { + m_buffer.insert(qvariant_cast<Api::Library>(m_model->item(i)->data(LibraryData)).id, + m_model->item(i)->data(SelectedVersion).toString()); + } + } + return oldBuffer != m_buffer; +} + +QVariantMap toVariantMap(const QMap<QString, QString> &map) +{ + QVariantMap variant; + for (const auto &key : map.keys()) + variant.insert(key, map[key]); + + return variant; +} + +QVariant LibrarySelectionAspect::variantValue() const +{ + return toVariantMap(m_internal); +} + +QVariant LibrarySelectionAspect::volatileVariantValue() const +{ + return toVariantMap(m_buffer); +} + +void LibrarySelectionAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce) +{ + QMap<QString, QString> map; + Store store = storeFromVariant(value); + for (const auto &key : store.keys()) + map[stringFromKey(key)] = store[key].toString(); + + setValue(map, howToAnnounce); +} + +void LibrarySelectionAspect::addToLayout(Layouting::LayoutItem &parent) +{ + using namespace Layouting; + + QTC_ASSERT(m_fillCallback, return); + + auto cb = [this](const QList<QStandardItem *> &items) { + for (QStandardItem *item : items) + m_model->appendRow(item); + + bufferToGui(); + }; + + if (!m_model) { + m_model = new QStandardItemModel(this); + + connect(this, &LibrarySelectionAspect::refillRequested, this, [this, cb] { + m_model->clear(); + m_fillCallback(cb); + }); + + m_fillCallback(cb); + } + + QComboBox *nameCombo = new QComboBox(); + nameCombo->setInsertPolicy(QComboBox::InsertPolicy::NoInsert); + nameCombo->setEditable(true); + nameCombo->completer()->setCompletionMode(QCompleter::PopupCompletion); + nameCombo->completer()->setFilterMode(Qt::MatchContains); + + nameCombo->setModel(m_model); + + QComboBox *versionCombo = new QComboBox(); + versionCombo->addItem("--"); + + auto refreshVersionCombo = [nameCombo, versionCombo] { + versionCombo->clear(); + versionCombo->addItem("--"); + QString selected = nameCombo->currentData(SelectedVersion).toString(); + Api::Library lib = qvariant_cast<Api::Library>(nameCombo->currentData(LibraryData)); + for (const auto &version : lib.versions) { + versionCombo->addItem(version.version, version.id); + if (version.id == selected) + versionCombo->setCurrentIndex(versionCombo->count() - 1); + } + }; + + refreshVersionCombo(); + + connect(nameCombo, &QComboBox::currentIndexChanged, this, refreshVersionCombo); + + connect(versionCombo, &QComboBox::activated, this, [this, nameCombo, versionCombo] { + if (undoStack()) { + QVariant old = m_model->data(m_model->index(nameCombo->currentIndex(), 0), + SelectedVersion); + undoStack()->push(new SelectLibraryVersionCommand(this, + nameCombo->currentIndex(), + versionCombo->currentData(), + old)); + + handleGuiChanged(); + return; + } + + m_model->setData(m_model->index(nameCombo->currentIndex(), 0), + versionCombo->currentData(), + SelectedVersion); + handleGuiChanged(); + }); + + QPushButton *clearBtn = new QPushButton("Clear All"); + connect(clearBtn, &QPushButton::clicked, clearBtn, [this, refreshVersionCombo] { + if (undoStack()) { + undoStack()->beginMacro(Tr::tr("Reset used libraries")); + for (int i = 0; i < m_model->rowCount(); i++) { + QModelIndex idx = m_model->index(i, 0); + if (idx.data(SelectedVersion).isValid()) + undoStack()->push(new SelectLibraryVersionCommand(this, + i, + QVariant(), + idx.data(SelectedVersion))); + } + undoStack()->endMacro(); + + handleGuiChanged(); + refreshVersionCombo(); + return; + } + + for (int i = 0; i < m_model->rowCount(); i++) + m_model->setData(m_model->index(i, 0), QVariant(), SelectedVersion); + handleGuiChanged(); + refreshVersionCombo(); + }); + + ElidingLabel *displayLabel = new ElidingLabel(); + + auto updateLabel = [displayLabel, this] { + QStringList libs; + for (int i = 0; i < m_model->rowCount(); i++) { + QModelIndex idx = m_model->index(i, 0); + if (idx.data(LibraryData).isValid() && idx.data(SelectedVersion).isValid()) { + auto libData = idx.data(LibraryData).value<Api::Library>(); + auto id = idx.data(SelectedVersion).toString(); + + auto versionIt = std::find_if(libData.versions.begin(), + libData.versions.end(), + [id](const Api::Library::Version &v) { + return v.id == id; + }); + const QString versionName = versionIt == libData.versions.end() + ? id + : versionIt->version; + + libs.append(QString("%1 %2").arg(libData.name).arg(versionName)); + } + } + if (libs.empty()) + displayLabel->setText(Tr::tr("No libraries selected")); + else + displayLabel->setText(libs.join(", ")); + }; + + connect(m_model, &QStandardItemModel::itemChanged, displayLabel, updateLabel); + + updateLabel(); + + QPushButton *editBtn = new QPushButton(Tr::tr("Edit")); + + // clang-format off + QStackedWidget *stack = static_cast<QStackedWidget*>( + Stack { + noMargin, + Row { noMargin, displayLabel, editBtn }, + Row { noMargin, nameCombo, versionCombo, clearBtn } + }.emerge() + ); + // clang-format on + connect(editBtn, &QPushButton::clicked, stack, [stack] { stack->setCurrentIndex(1); }); + connect(this, &LibrarySelectionAspect::returnToDisplay, stack, [stack] { + stack->setCurrentIndex(0); + }); + + addLabeledItem(parent, stack); +} + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexploreraspects.h b/src/plugins/compilerexplorer/compilerexploreraspects.h new file mode 100644 index 00000000000..10a0d9cb818 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexploreraspects.h @@ -0,0 +1,95 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <utils/aspects.h> + +#include <QComboBox> +#include <QItemSelectionModel> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QStandardItemModel> + +namespace CompilerExplorer { + +// QMap<Library.Id, Library.Version.Id> +class LibrarySelectionAspect : public Utils::TypedAspect<QMap<QString, QString>> +{ + Q_OBJECT +public: + enum Roles { + LibraryData = Qt::UserRole + 1, + SelectedVersion, + }; + + class SelectLibraryVersionCommand : public QUndoCommand + { + public: + SelectLibraryVersionCommand(LibrarySelectionAspect *aspect, + int libraryIndex, + const QVariant &versionId, + const QVariant &oldVersionId = QVariant()) + : m_aspect(aspect) + , m_libraryIndex(libraryIndex) + , m_versionId(versionId) + , m_oldVersionId(oldVersionId) + {} + + void undo() override + { + m_aspect->m_model->setData(m_aspect->m_model->index(m_libraryIndex, 0), + m_oldVersionId, + LibrarySelectionAspect::SelectedVersion); + m_aspect->handleGuiChanged(); + emit m_aspect->returnToDisplay(); + } + + void redo() override + { + m_aspect->m_model->setData(m_aspect->m_model->index(m_libraryIndex, 0), + m_versionId, + LibrarySelectionAspect::SelectedVersion); + if (!m_firstTime) { + emit m_aspect->returnToDisplay(); + m_aspect->handleGuiChanged(); + } + m_firstTime = false; + } + + private: + LibrarySelectionAspect *m_aspect; + int m_libraryIndex; + QVariant m_versionId; + QVariant m_oldVersionId; + bool m_firstTime{true}; + }; + + LibrarySelectionAspect(Utils::AspectContainer *container = nullptr); + + void addToLayout(Layouting::LayoutItem &parent) override; + + using ResultCallback = std::function<void(QList<QStandardItem *>)>; + using FillCallback = std::function<void(ResultCallback)>; + void setFillCallback(FillCallback callback) { m_fillCallback = callback; } + void refill() { emit refillRequested(); } + + void bufferToGui() override; + bool guiToBuffer() override; + + QVariant variantValue() const override; + QVariant volatileVariantValue() const override; + + void setVariantValue(const QVariant &value, Announcement howToAnnounce = DoEmit) override; + +signals: + void refillRequested(); + void returnToDisplay(); + +private: + FillCallback m_fillCallback; + QStandardItemModel *m_model{nullptr}; +}; + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexplorerconstants.h b/src/plugins/compilerexplorer/compilerexplorerconstants.h new file mode 100644 index 00000000000..1e8a244626b --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorerconstants.h @@ -0,0 +1,8 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +namespace CompilerExplorer::Constants { +const char CE_EDITOR_ID[] = "CompilerExplorer.Editor"; +} diff --git a/src/plugins/compilerexplorer/compilerexplorereditor.cpp b/src/plugins/compilerexplorer/compilerexplorereditor.cpp new file mode 100644 index 00000000000..f71010744ef --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorereditor.cpp @@ -0,0 +1,894 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compilerexplorereditor.h" +#include "compilerexplorerconstants.h" +#include "compilerexploreroptions.h" +#include "compilerexplorersettings.h" +#include "compilerexplorertr.h" + +#include <aggregation/aggregate.h> + +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/icontext.h> +#include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> + +#include <texteditor/textdocument.h> +#include <texteditor/texteditor.h> +#include <texteditor/texteditoractionhandler.h> +#include <texteditor/textmark.h> + +#include <projectexplorer/projectexplorerconstants.h> + +#include <utils/algorithm.h> +#include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> +#include <utils/mimetypes2/mimetype.h> +#include <utils/mimeutils.h> +#include <utils/store.h> +#include <utils/styledbar.h> +#include <utils/utilsicons.h> + +#include <QCompleter> +#include <QDesktopServices> +#include <QDockWidget> +#include <QNetworkAccessManager> +#include <QPushButton> +#include <QSplitter> +#include <QStackedLayout> +#include <QStandardItemModel> +#include <QTemporaryFile> +#include <QTimer> +#include <QToolBar> +#include <QToolButton> +#include <QUndoStack> + +#include <chrono> +#include <iostream> + +using namespace std::chrono_literals; +using namespace Aggregation; +using namespace TextEditor; +using namespace Utils; + +namespace CompilerExplorer { + +CodeEditorWidget::CodeEditorWidget(const std::shared_ptr<SourceSettings> &settings, + QUndoStack *undoStack) + : m_settings(settings) + , m_undoStack(undoStack){}; + +void CodeEditorWidget::updateHighlighter() +{ + const QString ext = m_settings->languageExtension(); + if (ext.isEmpty()) + return; + + Utils::MimeType mimeType = Utils::mimeTypeForFile("foo" + ext); + configureGenericHighlighter(mimeType); +} + +class SourceTextDocument : public TextDocument +{ +public: + class OpaqueUndoCommand : public QUndoCommand + { + public: + OpaqueUndoCommand(SourceTextDocument *doc) + : m_doc(doc) + {} + void undo() override { m_doc->undo(); } + void redo() override { m_doc->redo(); } + SourceTextDocument *m_doc; + }; + + SourceTextDocument(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack) + { + setPlainText(settings->source()); + + connect(this, &TextDocument::contentsChanged, this, [settings, this] { + settings->source.setVolatileValue(plainText()); + }); + + connect(&settings->source, &Utils::StringAspect::changed, this, [settings, this] { + if (settings->source.volatileValue() != plainText()) + setPlainText(settings->source.volatileValue()); + }); + + connect(this->document(), &QTextDocument::undoCommandAdded, this, [this, undoStack] { + undoStack->push(new OpaqueUndoCommand(this)); + }); + } + + void undo() { document()->undo(); } + void redo() { document()->redo(); } +}; + +JsonSettingsDocument::JsonSettingsDocument(QUndoStack *undoStack) + : m_undoStack(undoStack) +{ + setId(Constants::CE_EDITOR_ID); + setMimeType("application/compiler-explorer"); + connect(&m_ceSettings, &CompilerExplorerSettings::changed, this, [this] { + emit changed(); + emit contentsChanged(); + }); + m_ceSettings.setAutoApply(false); + m_ceSettings.setUndoStack(undoStack); +} + +Core::IDocument::OpenResult JsonSettingsDocument::open(QString *errorString, + const FilePath &filePath, + const FilePath &realFilePath) +{ + if (!filePath.isReadableFile()) + return OpenResult::ReadError; + + auto contents = realFilePath.fileContents(); + if (!contents) { + if (errorString) + *errorString = contents.error(); + return OpenResult::ReadError; + } + + auto result = storeFromJson(*contents); + if (!result) { + if (errorString) + *errorString = result.error(); + return OpenResult::ReadError; + } + + setFilePath(filePath); + + m_ceSettings.fromMap(*result); + emit settingsChanged(); + return OpenResult::Success; +} + +bool JsonSettingsDocument::saveImpl(QString *errorString, const FilePath &newFilePath, bool autoSave) +{ + Store store; + + if (autoSave) { + if (m_windowStateCallback) + m_ceSettings.windowState.setVolatileValue(m_windowStateCallback()); + + m_ceSettings.volatileToMap(store); + } else { + if (m_windowStateCallback) + m_ceSettings.windowState.setValue(m_windowStateCallback()); + + m_ceSettings.apply(); + m_ceSettings.toMap(store); + } + + Utils::FilePath path = newFilePath.isEmpty() ? filePath() : newFilePath; + + if (!newFilePath.isEmpty() && !autoSave) { + setPreferredDisplayName({}); + setFilePath(newFilePath); + } + + auto result = path.writeFileContents(jsonFromStore(store)); + if (!result && errorString) { + *errorString = result.error(); + return false; + } + + emit changed(); + return true; +} + +bool JsonSettingsDocument::isModified() const +{ + return m_ceSettings.isDirty(); +} + +bool JsonSettingsDocument::setContents(const QByteArray &contents) +{ + auto result = storeFromJson(contents); + QTC_ASSERT_EXPECTED(result, return false); + + m_ceSettings.fromMap(*result); + + emit settingsChanged(); + emit changed(); + emit contentsChanged(); + return true; +} + +SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings, + QUndoStack *undoStack) + : m_sourceSettings(settings) +{ + auto toolBar = new StyledBar; + + m_codeEditor = new CodeEditorWidget(m_sourceSettings, undoStack); + + connect(m_codeEditor, &CodeEditorWidget::gotFocus, this, &SourceEditorWidget::gotFocus); + + TextDocumentPtr document = TextDocumentPtr(new SourceTextDocument(m_sourceSettings, undoStack)); + + connect(document.get(), + &SourceTextDocument::changed, + this, + &SourceEditorWidget::sourceCodeChanged); + + m_codeEditor->setTextDocument(document); + m_codeEditor->updateHighlighter(); + + auto addCompilerButton = new QToolButton; + addCompilerButton->setText(Tr::tr("Add Compiler")); + connect(addCompilerButton, + &QToolButton::clicked, + &settings->compilers, + &AspectList::createAndAddItem); + + auto removeSourceButton = new QToolButton; + removeSourceButton->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon()); + removeSourceButton->setToolTip(Tr::tr("Remove Source")); + connect(removeSourceButton, &QToolButton::clicked, this, &SourceEditorWidget::remove); + + // clang-format off + using namespace Layouting; + + Row { + settings->languageId, + addCompilerButton, + removeSourceButton, + customMargin({6, 0, 0, 0}), spacing(0), + }.attachTo(toolBar); + + Column { + toolBar, + m_codeEditor, + noMargin(), spacing(0), + }.attachTo(this); + // clang-format on + + setWindowTitle("Source code"); + setObjectName("source_code"); + + setFocusProxy(m_codeEditor); +} + +QString SourceEditorWidget::sourceCode() +{ + if (m_codeEditor && m_codeEditor->textDocument()) + return QString::fromUtf8(m_codeEditor->textDocument()->contents()); + return {}; +} + +CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSettings, + const std::shared_ptr<CompilerSettings> &compilerSettings, + QUndoStack *undoStack) + : m_sourceSettings(sourceSettings) + , m_compilerSettings(compilerSettings) +{ + using namespace Layouting; + Store map; + + m_delayTimer = new QTimer(this); + m_delayTimer->setSingleShot(true); + m_delayTimer->setInterval(500ms); + connect(m_delayTimer, &QTimer::timeout, this, &CompilerWidget::doCompile); + + connect(m_compilerSettings.get(), + &CompilerSettings::changed, + m_delayTimer, + qOverload<>(&QTimer::start)); + + auto toolBar = new StyledBar; + + m_asmEditor = new AsmEditorWidget(undoStack); + m_asmDocument = QSharedPointer<TextDocument>(new TextDocument); + m_asmEditor->setTextDocument(m_asmDocument); + QTC_ASSERT_EXPECTED(m_asmEditor->configureGenericHighlighter("Intel x86 (NASM)"), + m_asmEditor->configureGenericHighlighter( + Utils::mimeTypeForName("text/x-asm"))); + m_asmEditor->setReadOnly(true); + + connect(m_asmEditor, &AsmEditorWidget::gotFocus, this, &CompilerWidget::gotFocus); + + auto advButton = new QToolButton; + QSplitter *splitter{nullptr}; + + auto advDlg = new QAction; + advDlg->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon()); + advDlg->setIconText(Tr::tr("Advanced Options")); + connect(advDlg, &QAction::triggered, this, [advButton, this] { + CompilerExplorerOptions *dlg = new CompilerExplorerOptions(*m_compilerSettings, advButton); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setWindowFlag(Qt::WindowType::Popup); + dlg->show(); + QRect rect = dlg->geometry(); + rect.moveTopRight(advButton->mapToGlobal(QPoint(advButton->width(), advButton->height()))); + dlg->setGeometry(rect); + }); + + connect(advButton, &QToolButton::clicked, advDlg, &QAction::trigger); + advButton->setIcon(advDlg->icon()); + + auto removeCompilerBtn = new QToolButton; + removeCompilerBtn->setIcon(Utils::Icons::CLOSE_TOOLBAR.icon()); + removeCompilerBtn->setToolTip(Tr::tr("Remove Compiler")); + connect(removeCompilerBtn, &QToolButton::clicked, this, &CompilerWidget::remove); + + compile(m_sourceSettings->source()); + + connect(&m_sourceSettings->source, &Utils::StringAspect::volatileValueChanged, this, [this] { + compile(m_sourceSettings->source.volatileValue()); + }); + + // clang-format off + + Row { + m_compilerSettings->compiler, + advButton, + removeCompilerBtn, + customMargin({6, 0, 0, 0}), spacing(0), + }.attachTo(toolBar); + + Column { + toolBar, + Splitter { + bindTo(&splitter), + m_asmEditor, + createTerminal() + }, + noMargin(), spacing(0), + }.attachTo(this); + // clang-format on + + m_spinner = new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Large, this); +} + +Core::SearchableTerminal *CompilerWidget::createTerminal() +{ + m_resultTerminal = new Core::SearchableTerminal(); + m_resultTerminal->setAllowBlinkingCursor(false); + std::array<QColor, 20> colors{Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi0), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi1), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi2), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi3), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi4), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi5), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi6), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi7), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi8), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi9), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi10), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi11), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi12), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi13), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi14), + Utils::creatorTheme()->color(Utils::Theme::TerminalAnsi15), + + Utils::creatorTheme()->color(Utils::Theme::TerminalForeground), + Utils::creatorTheme()->color(Utils::Theme::TerminalBackground), + Utils::creatorTheme()->color(Utils::Theme::TerminalSelection), + Utils::creatorTheme()->color(Utils::Theme::TerminalFindMatch)}; + + m_resultTerminal->setColors(colors); + + return m_resultTerminal; +} + +void CompilerWidget::compile(const QString &source) +{ + m_source = source; + m_delayTimer->start(); +} + +void CompilerWidget::doCompile() +{ + using namespace Api; + + QString compilerId = m_compilerSettings->compiler(); + if (compilerId.isEmpty()) + compilerId = "clang_trunk"; + + m_spinner->setVisible(true); + m_asmEditor->setEnabled(false); + + CompileParameters params + = CompileParameters(compilerId) + .source(m_source) + .language(m_sourceSettings->languageId.volatileValue()) + .options(CompileParameters::Options() + .userArguments(m_compilerSettings->compilerOptions.volatileValue()) + .compilerOptions({false, false}) + .filters({false, + m_compilerSettings->compileToBinaryObject.volatileValue(), + true, + m_compilerSettings->demangleIdentifiers.volatileValue(), + true, + m_compilerSettings->executeCode.volatileValue(), + m_compilerSettings->intelAsmSyntax.volatileValue(), + true, + false, + false, + false}) + .libraries(m_compilerSettings->libraries.volatileValue())); + + auto f = Api::compile(m_sourceSettings->apiConfigFunction()(), params); + + m_compileWatcher.reset(new QFutureWatcher<CompileResult>); + + connect(m_compileWatcher.get(), &QFutureWatcher<CompileResult>::finished, this, [this] { + m_spinner->setVisible(false); + m_asmEditor->setEnabled(true); + + try { + Api::CompileResult r = m_compileWatcher->result(); + + m_resultTerminal->restart(); + m_resultTerminal->writeToTerminal("\x1b[?25l", false); + + for (const auto &err : r.stdErr) + m_resultTerminal->writeToTerminal((err.text + "\r\n").toUtf8(), false); + for (const auto &out : r.stdOut) + m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false); + + m_resultTerminal->writeToTerminal( + QString("ASM generation compiler returned: %1\r\n\r\n").arg(r.code).toUtf8(), true); + + if (r.execResult) { + for (const auto &err : r.execResult->buildResult.stdErr) + m_resultTerminal->writeToTerminal((err.text + "\r\n").toUtf8(), false); + for (const auto &out : r.execResult->buildResult.stdOut) + m_resultTerminal->writeToTerminal((out.text + "\r\n").toUtf8(), false); + + m_resultTerminal + ->writeToTerminal(QString("Execution build compiler returned: %1\r\n\r\n") + .arg(r.execResult->buildResult.code) + .toUtf8(), + true); + + if (r.execResult->didExecute) { + m_resultTerminal->writeToTerminal(QString("Program returned: %1\r\n") + .arg(r.execResult->code) + .toUtf8(), + true); + + for (const auto &err : r.execResult->stdErrLines) + m_resultTerminal + ->writeToTerminal((" \033[0;31m" + err + "\033[0m\r\n\r\n").toUtf8(), + false); + for (const auto &out : r.execResult->stdOutLines) + m_resultTerminal->writeToTerminal((out + "\r\n").toUtf8(), false); + } + } + qDeleteAll(m_marks); + m_marks.clear(); + + QString asmText; + for (auto l : r.assemblyLines) + asmText += l.text + "\n"; + + m_asmDocument->setPlainText(asmText); + + int i = 0; + for (auto l : r.assemblyLines) { + i++; + if (l.opcodes.empty()) + continue; + + auto mark = new TextMark(m_asmDocument.get(), + i, + TextMarkCategory{Tr::tr("Bytes"), "Bytes"}); + m_asmDocument->addMark(mark); + mark->setLineAnnotation(l.opcodes.join(' ')); + m_marks.append(mark); + } + } catch (const std::exception &e) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed to compile: \"%1\".").arg(QString::fromUtf8(e.what()))); + } + }); + + m_compileWatcher->setFuture(f); +} + +EditorWidget::EditorWidget(const QSharedPointer<JsonSettingsDocument> &document, + QUndoStack *undoStack, + TextEditorActionHandler &actionHandler, + QWidget *parent) + : Utils::FancyMainWindow(parent) + , m_document(document) + , m_undoStack(undoStack) + , m_actionHandler(actionHandler) +{ + setContextMenuPolicy(Qt::NoContextMenu); + setAutoHideTitleBars(false); + setDockNestingEnabled(true); + setDocumentMode(true); + setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::South); + + document->setWindowStateCallback([this] { return storeFromMap(windowStateCallback()); }); + + document->settings()->m_sources.setItemAddedCallback<SourceSettings>( + [this](const std::shared_ptr<SourceSettings> &source) { addSourceEditor(source); }); + + document->settings()->m_sources.setItemRemovedCallback<SourceSettings>( + [this](const std::shared_ptr<SourceSettings> &source) { removeSourceEditor(source); }); + + connect(document.get(), + &JsonSettingsDocument::settingsChanged, + this, + &EditorWidget::recreateEditors); + + connect(this, &EditorWidget::gotFocus, this, [&actionHandler] { + actionHandler.updateCurrentEditor(); + }); + + setupHelpWidget(); +} + +EditorWidget::~EditorWidget() +{ + m_compilerWidgets.clear(); + m_sourceWidgets.clear(); +} + +void EditorWidget::focusInEvent(QFocusEvent *event) +{ + emit gotFocus(); + FancyMainWindow::focusInEvent(event); +} + +void EditorWidget::addCompiler(const std::shared_ptr<SourceSettings> &sourceSettings, + const std::shared_ptr<CompilerSettings> &compilerSettings, + int idx, + QDockWidget *parentDockWidget) +{ + auto compiler = new CompilerWidget(sourceSettings, compilerSettings, m_undoStack); + compiler->setWindowTitle("Compiler #" + QString::number(idx)); + compiler->setObjectName("compiler_" + QString::number(idx)); + QDockWidget *dockWidget = addDockForWidget(compiler, parentDockWidget); + dockWidget->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + addDockWidget(Qt::RightDockWidgetArea, dockWidget); + m_compilerWidgets.append(dockWidget); + + connect(compiler, + &CompilerWidget::remove, + this, + [sourceSettings = sourceSettings.get(), compilerSettings = compilerSettings.get()] { + sourceSettings->compilers.removeItem(compilerSettings->shared_from_this()); + }); + + connect(compiler, &CompilerWidget::gotFocus, this, [this]() { + m_actionHandler.updateCurrentEditor(); + }); +} + +QVariantMap EditorWidget::windowStateCallback() +{ + auto settings = saveSettings(); + QVariantMap result; + + for (const Key &key : settings.keys()) { + // QTBUG-116339 + if (stringFromKey(key) != "State") { + result.insert(stringFromKey(key), settings.value(key)); + } else { + QVariantMap m; + m["type"] = "Base64"; + m["value"] = settings.value(key).toByteArray().toBase64(); + result.insert(stringFromKey(key), m); + } + } + + return result; +} + +void EditorWidget::addSourceEditor(const std::shared_ptr<SourceSettings> &sourceSettings) +{ + auto sourceEditor = new SourceEditorWidget(sourceSettings, m_undoStack); + sourceEditor->setWindowTitle("Source Code #" + QString::number(m_sourceWidgets.size() + 1)); + sourceEditor->setObjectName("source_code_editor_" + QString::number(m_sourceWidgets.size() + 1)); + + QDockWidget *dockWidget = addDockForWidget(sourceEditor); + connect(sourceEditor, &SourceEditorWidget::remove, this, [this, sourceSettings]() { + m_undoStack->beginMacro("Remove source"); + sourceSettings->compilers.clear(); + m_document->settings()->m_sources.removeItem(sourceSettings->shared_from_this()); + m_undoStack->endMacro(); + setupHelpWidget(); + }); + + connect(sourceEditor, &SourceEditorWidget::gotFocus, this, [this]() { + m_actionHandler.updateCurrentEditor(); + }); + + dockWidget->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable); + addDockWidget(Qt::LeftDockWidgetArea, dockWidget); + + sourceSettings->compilers.forEachItem<CompilerSettings>( + [this, sourceSettings, dockWidget](const std::shared_ptr<CompilerSettings> &compilerSettings, + int idx) { + addCompiler(sourceSettings, compilerSettings, idx + 1, dockWidget); + }); + + sourceSettings->compilers.setItemAddedCallback<CompilerSettings>( + [this, sourceSettings, dockWidget]( + const std::shared_ptr<CompilerSettings> &compilerSettings) { + addCompiler(sourceSettings->shared_from_this(), + compilerSettings, + sourceSettings->compilers.size(), + dockWidget); + }); + + sourceSettings->compilers.setItemRemovedCallback<CompilerSettings>( + [this, sourceSettings](const std::shared_ptr<CompilerSettings> &compilerSettings) { + auto it = std::find_if(m_compilerWidgets.begin(), + m_compilerWidgets.end(), + [compilerSettings](const QDockWidget *c) { + return static_cast<CompilerWidget *>(c->widget()) + ->m_compilerSettings + == compilerSettings; + }); + QTC_ASSERT(it != m_compilerWidgets.end(), return); + if (!m_sourceWidgets.isEmpty()) + m_sourceWidgets.first()->widget()->setFocus(Qt::OtherFocusReason); + delete *it; + m_compilerWidgets.erase(it); + }); + + m_sourceWidgets.append(dockWidget); + + sourceEditor->setFocus(Qt::OtherFocusReason); + + setupHelpWidget(); +} + +void EditorWidget::removeSourceEditor(const std::shared_ptr<SourceSettings> &sourceSettings) +{ + auto it + = std::find_if(m_sourceWidgets.begin(), + m_sourceWidgets.end(), + [sourceSettings](const QDockWidget *c) { + return static_cast<SourceEditorWidget *>(c->widget())->sourceSettings() + == sourceSettings.get(); + }); + QTC_ASSERT(it != m_sourceWidgets.end(), return); + delete *it; + m_sourceWidgets.erase(it); + + setupHelpWidget(); +} + +void EditorWidget::recreateEditors() +{ + qDeleteAll(m_sourceWidgets); + qDeleteAll(m_compilerWidgets); + + m_sourceWidgets.clear(); + m_compilerWidgets.clear(); + + m_document->settings()->m_sources.forEachItem<SourceSettings>( + [this](const auto &sourceSettings) { addSourceEditor(sourceSettings); }); + + Store windowState = m_document->settings()->windowState.value(); + + if (!windowState.isEmpty()) { + QHash<Key, QVariant> hashMap; + for (const auto &key : windowState.keys()) { + if (key.view() != "State") + hashMap.insert(key, windowState.value(key)); + else { + QVariant v = windowState.value(key); + if (v.userType() == QMetaType::QByteArray) { + hashMap.insert(key, v); + } else if (v.userType() == QMetaType::QVariantMap) { + QVariantMap m = v.toMap(); + if (m.value("type") == "Base64") { + hashMap.insert(key, QByteArray::fromBase64(m.value("value").toByteArray())); + } + } + } + } + + restoreSettings(hashMap); + } +} + +void EditorWidget::setupHelpWidget() +{ + if (m_document->settings()->m_sources.size() == 0) { + setCentralWidget(createHelpWidget()); + centralWidget()->setFocus(Qt::OtherFocusReason); + } else { + delete takeCentralWidget(); + } +} + +HelperWidget::HelperWidget() +{ + using namespace Layouting; + + setFocusPolicy(Qt::ClickFocus); + setAttribute(Qt::WA_TransparentForMouseEvents, false); + + auto addSourceButton = new QPushButton(Tr::tr("Add Source Code")); + + connect(addSourceButton, &QPushButton::clicked, this, &HelperWidget::addSource); + + // clang-format off + Column { + st, + Row { + st, + Column { + Tr::tr("No source code added yet. Add some using the button below."), + Row { st, addSourceButton, st } + }, + st, + }, + st, + }.attachTo(this); + // clang-format on +} + +void HelperWidget::mousePressEvent(QMouseEvent *event) +{ + setFocus(Qt::MouseFocusReason); + event->accept(); +} + +QWidget *EditorWidget::createHelpWidget() const +{ + auto w = new HelperWidget; + connect(w, + &HelperWidget::addSource, + &m_document->settings()->m_sources, + &AspectList::createAndAddItem); + return w; +} + +TextEditor::TextEditorWidget *EditorWidget::focusedEditorWidget() const +{ + for (const QDockWidget *sourceWidget : m_sourceWidgets) { + TextEditorWidget *textEditor + = qobject_cast<SourceEditorWidget *>(sourceWidget->widget())->textEditor(); + if (textEditor->hasFocus()) + return textEditor; + } + + for (const QDockWidget *compilerWidget : m_compilerWidgets) { + TextEditorWidget *textEditor + = qobject_cast<CompilerWidget *>(compilerWidget->widget())->textEditor(); + if (textEditor->hasFocus()) + return textEditor; + } + + return nullptr; +} + +Editor::Editor(TextEditorActionHandler &actionHandler) + : m_document(new JsonSettingsDocument(&m_undoStack)) +{ + setContext(Core::Context(Constants::CE_EDITOR_ID)); + setWidget(new EditorWidget(m_document, &m_undoStack, actionHandler)); + + connect(&m_undoStack, &QUndoStack::canUndoChanged, this, [&actionHandler] { + actionHandler.updateActions(); + }); + connect(&m_undoStack, &QUndoStack::canRedoChanged, this, [&actionHandler] { + actionHandler.updateActions(); + }); +} + +Editor::~Editor() +{ + delete widget(); +} + +static bool childHasFocus(QWidget *parent) +{ + if (parent->hasFocus()) + return true; + + for (QWidget *child : parent->findChildren<QWidget *>()) + if (childHasFocus(child)) + return true; + + return false; +} + +QWidget *Editor::toolBar() +{ + if (!m_toolBar) { + m_toolBar = std::make_unique<QToolBar>(); + + QAction *newSource = new QAction(m_toolBar.get()); + newSource->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); + newSource->setToolTip(Tr::tr("Add Source")); + m_toolBar->addAction(newSource); + + m_toolBar->addSeparator(); + + QString link = QString(R"(<a href="%1">%1</a>)") + .arg(m_document->settings()->compilerExplorerUrl.value()); + + auto poweredByLabel = new QLabel(Tr::tr("powered by %1").arg(link)); + + poweredByLabel->setTextInteractionFlags(Qt::TextInteractionFlag::TextBrowserInteraction); + poweredByLabel->setContentsMargins(6, 0, 0, 0); + + connect(poweredByLabel, &QLabel::linkActivated, this, [](const QString &link) { + QDesktopServices::openUrl(link); + }); + + m_toolBar->addWidget(poweredByLabel); + + connect(newSource, + &QAction::triggered, + &m_document->settings()->m_sources, + &AspectList::createAndAddItem); + } + + return m_toolBar.get(); +} + +EditorFactory::EditorFactory() + : m_actionHandler(Constants::CE_EDITOR_ID, + Constants::CE_EDITOR_ID, + TextEditor::TextEditorActionHandler::None, + [](Core::IEditor *editor) -> TextEditorWidget * { + return static_cast<EditorWidget *>(editor->widget())->focusedEditorWidget(); + }) +{ + setId(Constants::CE_EDITOR_ID); + setDisplayName(Tr::tr("Compiler Explorer Editor")); + setMimeTypes({"application/compiler-explorer"}); + + auto undoStackFromEditor = [](Core::IEditor *editor) -> QUndoStack * { + if (!editor) + return nullptr; + return &static_cast<Editor *>(editor)->m_undoStack; + }; + + m_actionHandler.setCanUndoCallback([undoStackFromEditor](Core::IEditor *editor) { + if (auto undoStack = undoStackFromEditor(editor)) + return undoStack->canUndo(); + return false; + }); + + m_actionHandler.setCanRedoCallback([undoStackFromEditor](Core::IEditor *editor) { + if (auto undoStack = undoStackFromEditor(editor)) + return undoStack->canRedo(); + return false; + }); + + m_actionHandler.setUnhandledCallback( + [undoStackFromEditor](Utils::Id cmdId, Core::IEditor *editor) { + if (cmdId != Core::Constants::UNDO && cmdId != Core::Constants::REDO) + return false; + + if (!childHasFocus(editor->widget())) + return false; + + QUndoStack *undoStack = undoStackFromEditor(editor); + + if (!undoStack) + return false; + + if (cmdId == Core::Constants::UNDO) + undoStack->undo(); + else + undoStack->redo(); + + return true; + }); + + setEditorCreator([this]() { return new Editor(m_actionHandler); }); +} + +AsmEditorWidget::AsmEditorWidget(QUndoStack *stack) + : m_undoStack(stack) +{} + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexplorereditor.h b/src/plugins/compilerexplorer/compilerexplorereditor.h new file mode 100644 index 00000000000..510e5e9eb3c --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorereditor.h @@ -0,0 +1,263 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "api/compile.h" +#include "compilerexplorersettings.h" + +#include <coreplugin/terminal/searchableterminal.h> + +#include <solutions/spinner/spinner.h> + +#include <texteditor/texteditor.h> +#include <texteditor/texteditoractionhandler.h> + +#include <utils/fancymainwindow.h> + +#include <QFutureWatcher> +#include <QMainWindow> +#include <QSplitter> +#include <QUndoStack> + +#include <memory> + +namespace CppEditor { +class CppEditorWidget; +} + +namespace CompilerExplorer { + +class JsonSettingsDocument; +class SourceEditorWidget; + +class CodeEditorWidget : public TextEditor::TextEditorWidget +{ + Q_OBJECT +public: + CodeEditorWidget(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack); + + void updateHighlighter(); + + void undo() override { m_undoStack->undo(); } + void redo() override { m_undoStack->redo(); } + + void focusInEvent(QFocusEvent *event) override + { + TextEditorWidget::focusInEvent(event); + emit gotFocus(); + } + +signals: + void gotFocus(); + +private: + std::shared_ptr<SourceSettings> m_settings; + QUndoStack *m_undoStack; +}; + +class AsmEditorWidget : public TextEditor::TextEditorWidget +{ + Q_OBJECT + +public: + AsmEditorWidget(QUndoStack *undoStack); + + void focusInEvent(QFocusEvent *event) override + { + TextEditorWidget::focusInEvent(event); + emit gotFocus(); + } + + void undo() override { m_undoStack->undo(); } + void redo() override { m_undoStack->redo(); } + +signals: + void gotFocus(); + +private: + QUndoStack *m_undoStack; +}; + +class JsonSettingsDocument : public Core::IDocument +{ + Q_OBJECT +public: + JsonSettingsDocument(QUndoStack *undoStack); + + OpenResult open(QString *errorString, + const Utils::FilePath &filePath, + const Utils::FilePath &realFilePath) override; + + bool saveImpl(QString *errorString, + const Utils::FilePath &filePath = Utils::FilePath(), + bool autoSave = false) override; + + bool setContents(const QByteArray &contents) override; + + bool shouldAutoSave() const override { return !filePath().isEmpty(); } + bool isModified() const override; + bool isSaveAsAllowed() const override { return true; } + + CompilerExplorerSettings *settings() { return &m_ceSettings; } + + void setWindowStateCallback(std::function<Utils::Store()> callback) + { + m_windowStateCallback = callback; + } + +signals: + void settingsChanged(); + +private: + mutable CompilerExplorerSettings m_ceSettings; + std::function<Utils::Store()> m_windowStateCallback; + QUndoStack *m_undoStack; +}; + +class SourceEditorWidget : public QWidget +{ + Q_OBJECT +public: + SourceEditorWidget(const std::shared_ptr<SourceSettings> &settings, QUndoStack *undoStack); + + QString sourceCode(); + SourceSettings *sourceSettings() { return m_sourceSettings.get(); } + + void focusInEvent(QFocusEvent *) override { emit gotFocus(); } + + TextEditor::TextEditorWidget *textEditor() { return m_codeEditor; } + +signals: + void sourceCodeChanged(); + void remove(); + void gotFocus(); + +private: + CodeEditorWidget *m_codeEditor{nullptr}; + std::shared_ptr<SourceSettings> m_sourceSettings; +}; + +class CompilerWidget : public QWidget +{ + Q_OBJECT +public: + CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSettings, + const std::shared_ptr<CompilerSettings> &compilerSettings, + QUndoStack *undoStack); + + Core::SearchableTerminal *createTerminal(); + + void compile(const QString &source); + + std::shared_ptr<SourceSettings> m_sourceSettings; + std::shared_ptr<CompilerSettings> m_compilerSettings; + + void focusInEvent(QFocusEvent *) override { emit gotFocus(); } + + TextEditor::TextEditorWidget *textEditor() { return m_asmEditor; } + +private: + void doCompile(); + +signals: + void remove(); + void gotFocus(); + +private: + AsmEditorWidget *m_asmEditor{nullptr}; + Core::SearchableTerminal *m_resultTerminal{nullptr}; + + SpinnerSolution::Spinner *m_spinner{nullptr}; + QSharedPointer<TextEditor::TextDocument> m_asmDocument; + + std::unique_ptr<QFutureWatcher<Api::CompileResult>> m_compileWatcher; + + QString m_source; + QTimer *m_delayTimer{nullptr}; + QList<TextEditor::TextMark *> m_marks; +}; + +class HelperWidget : public QWidget +{ + Q_OBJECT +public: + HelperWidget(); + +protected: + void mousePressEvent(QMouseEvent *event) override; + +signals: + void addSource(); +}; + +class EditorWidget : public Utils::FancyMainWindow +{ + Q_OBJECT +public: + EditorWidget(const QSharedPointer<JsonSettingsDocument> &document, + QUndoStack *undoStack, + TextEditor::TextEditorActionHandler &actionHandler, + QWidget *parent = nullptr); + ~EditorWidget() override; + + TextEditor::TextEditorWidget *focusedEditorWidget() const; + +signals: + void sourceCodeChanged(); + void gotFocus(); + +protected: + void focusInEvent(QFocusEvent *event) override; + + void setupHelpWidget(); + QWidget *createHelpWidget() const; + + void addCompiler(const std::shared_ptr<SourceSettings> &sourceSettings, + const std::shared_ptr<CompilerSettings> &compilerSettings, + int idx, + QDockWidget *parentDockWidget); + + void addSourceEditor(const std::shared_ptr<SourceSettings> &sourceSettings); + void removeSourceEditor(const std::shared_ptr<SourceSettings> &sourceSettings); + + void recreateEditors(); + + QVariantMap windowStateCallback(); + +private: + QSharedPointer<JsonSettingsDocument> m_document; + QUndoStack *m_undoStack; + TextEditor::TextEditorActionHandler &m_actionHandler; + + QList<QDockWidget *> m_compilerWidgets; + QList<QDockWidget *> m_sourceWidgets; +}; + +class Editor : public Core::IEditor +{ +public: + Editor(TextEditor::TextEditorActionHandler &actionHandler); + ~Editor(); + + Core::IDocument *document() const override { return m_document.data(); } + QWidget *toolBar() override; + + QSharedPointer<JsonSettingsDocument> m_document; + QUndoStack m_undoStack; + std::unique_ptr<QToolBar> m_toolBar; +}; + +class EditorFactory : public Core::IEditorFactory +{ +public: + EditorFactory(); + +private: + TextEditor::TextEditorActionHandler m_actionHandler; + + QAction m_undoAction; + QAction m_redoAction; +}; + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexploreroptions.cpp b/src/plugins/compilerexplorer/compilerexploreroptions.cpp new file mode 100644 index 00000000000..64ed6513f45 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexploreroptions.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compilerexploreroptions.h" + +#include "compilerexplorersettings.h" + +#include <utils/layoutbuilder.h> + +#include <QFutureWatcher> +#include <QScrollArea> +#include <QStandardItemModel> + +namespace CompilerExplorer { + +CompilerExplorerOptions::CompilerExplorerOptions(CompilerSettings &compilerSettings, QWidget *parent) + : QDialog(parent, Qt::Popup) +{ + using namespace Layouting; + + // clang-format off + Form { + compilerSettings.compiler, br, + compilerSettings.compilerOptions, br, + compilerSettings.libraries, br, + compilerSettings.compileToBinaryObject, br, + compilerSettings.executeCode, br, + compilerSettings.intelAsmSyntax, br, + compilerSettings.demangleIdentifiers, br, + }.attachTo(this); + // clang-format on +} + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexploreroptions.h b/src/plugins/compilerexplorer/compilerexploreroptions.h new file mode 100644 index 00000000000..5802ce3c38b --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexploreroptions.h @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "compilerexplorersettings.h" + +#include <QDialog> + +namespace CompilerExplorer { + +class CompilerExplorerOptions : public QDialog +{ +public: + CompilerExplorerOptions(CompilerSettings &settings, QWidget *parent = nullptr); +}; + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexplorerplugin.cpp b/src/plugins/compilerexplorer/compilerexplorerplugin.cpp new file mode 100644 index 00000000000..0a5dbd764b7 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorerplugin.cpp @@ -0,0 +1,66 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compilerexplorerconstants.h" +#include "compilerexplorereditor.h" +#include "compilerexplorersettings.h" +#include "compilerexplorertr.h" + +#include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> + +#include <cppeditor/cppeditorconstants.h> + +#include <extensionsystem/iplugin.h> + +#include <projectexplorer/jsonwizard/jsonwizardfactory.h> + +#include <utils/fsengine/fileiconprovider.h> + +#include <QMenu> + +using namespace Core; + +namespace CompilerExplorer::Internal { + +class CompilerExplorerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "CompilerExplorer.json") + +public: + void initialize() override + { + static CompilerExplorer::EditorFactory ceEditorFactory; + + auto action = new QAction(Tr::tr("Open Compiler Explorer"), this); + connect(action, &QAction::triggered, this, [] { + QString name("Compiler Explorer $"); + Core::EditorManager::openEditorWithContents(Constants::CE_EDITOR_ID, + &name, + settings().defaultDocument().toUtf8()); + }); + + Utils::FileIconProvider::registerIconForMimeType(QIcon(":/compilerexplorer/logos/ce.ico"), + "application/compiler-explorer"); + + ProjectExplorer::JsonWizardFactory::addWizardPath(":/compilerexplorer/wizard/"); + + ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); + ActionContainer *mCompilerExplorer = ActionManager::createMenu("Tools.CompilerExplorer"); + QMenu *menu = mCompilerExplorer->menu(); + menu->setTitle(Tr::tr("Compiler Explorer")); + mtools->addMenu(mCompilerExplorer); + + Command *cmd = ActionManager::registerAction(action, + "CompilerExplorer.CompilerExplorerAction"); + mCompilerExplorer->addAction(cmd); + } +}; + +} // namespace CompilerExplorer::Internal + +#include "compilerexplorerplugin.moc" diff --git a/src/plugins/compilerexplorer/compilerexplorersettings.cpp b/src/plugins/compilerexplorer/compilerexplorersettings.cpp new file mode 100644 index 00000000000..a93508b5e4e --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorersettings.cpp @@ -0,0 +1,345 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "compilerexplorersettings.h" +#include "compilerexplorertr.h" + +#include "api/compiler.h" +#include "api/language.h" +#include "api/library.h" + +#include <coreplugin/messagemanager.h> + +#include <QComboBox> +#include <QFutureWatcher> +#include <QNetworkAccessManager> + +namespace CompilerExplorer { + +PluginSettings &settings() +{ + static PluginSettings instance; + return instance; +} + +PluginSettings::PluginSettings() +{ + defaultDocument.setSettingsKey("DefaultDocument"); + defaultDocument.setDefaultValue(R"( +{ + "Sources": [{ + "LanguageId": "c++", + "Source": "int main() {\n return 0;\n}", + "Compilers": [{ + "Id": "clang_trunk", + "Options": "-O3" + }] + }] +} + )"); +} + +static Api::Languages &cachedLanguages() +{ + static Api::Languages instance; + return instance; +} + +static QMap<QString, Api::Libraries> &cachedLibraries() +{ + static QMap<QString, Api::Libraries> instance; + return instance; +} + +static Api::Libraries &cachedLibraries(const QString &languageId) +{ + return cachedLibraries()[languageId]; +} + +static QMap<QString, QMap<QString, QString>> &cachedCompilers() +{ + static QMap<QString, QMap<QString, QString>> instance; + return instance; +} + +SourceSettings::SourceSettings(const ApiConfigFunction &apiConfigFunction) + : m_apiConfigFunction(apiConfigFunction) +{ + setAutoApply(false); + + source.setSettingsKey("Source"); + + languageId.setSettingsKey("LanguageId"); + languageId.setDefaultValue("c++"); + languageId.setLabelText(Tr::tr("Language:")); + languageId.setFillCallback([this](auto cb) { fillLanguageIdModel(cb); }); + + compilers.setSettingsKey("Compilers"); + compilers.setCreateItemFunction([this, apiConfigFunction] { + auto result = std::make_shared<CompilerSettings>(apiConfigFunction); + connect(this, &SourceSettings::languagesChanged, result.get(), &CompilerSettings::refresh); + connect(&languageId, + &Utils::StringSelectionAspect::changed, + result.get(), + [this, result = result.get()] { result->setLanguageId(languageId()); }); + + connect(result.get(), &Utils::AspectContainer::changed, this, &SourceSettings::changed); + + result->setLanguageId(languageId()); + return result; + }); + + for (const auto &aspect : this->aspects()) { + connect(aspect, + &Utils::BaseAspect::volatileValueChanged, + this, + &Utils::AspectContainer::changed); + } +} + +void SourceSettings::refresh() +{ + languageId.setValue(languageId.defaultValue()); + cachedLanguages().clear(); + languageId.refill(); + + compilers.forEachItem<CompilerSettings>(&CompilerSettings::refresh); +} + +QString SourceSettings::languageExtension() const +{ + auto it = std::find_if(std::begin(cachedLanguages()), + std::end(cachedLanguages()), + [this](const Api::Language &lang) { return lang.id == languageId(); }); + + if (it != cachedLanguages().end()) + return it->extensions.first(); + + return ".cpp"; +} + +CompilerSettings::CompilerSettings(const ApiConfigFunction &apiConfigFunction) + : m_apiConfigFunction(apiConfigFunction) +{ + setAutoApply(false); + compiler.setSettingsKey("Id"); + compiler.setLabelText(Tr::tr("Compiler:")); + compiler.setFillCallback([this](auto cb) { fillCompilerModel(cb); }); + + compilerOptions.setSettingsKey("Options"); + compilerOptions.setLabelText(Tr::tr("Compiler options:")); + compilerOptions.setToolTip(Tr::tr("Arguments passed to the compiler.")); + compilerOptions.setDisplayStyle(Utils::StringAspect::DisplayStyle::LineEditDisplay); + + libraries.setSettingsKey("Libraries"); + libraries.setLabelText(Tr::tr("Libraries:")); + libraries.setFillCallback([this](auto cb) { fillLibraries(cb); }); + + executeCode.setSettingsKey("ExecuteCode"); + executeCode.setLabelText(Tr::tr("Execute the code")); + + compileToBinaryObject.setSettingsKey("CompileToBinaryObject"); + compileToBinaryObject.setLabelText(Tr::tr("Compile to binary object")); + + intelAsmSyntax.setSettingsKey("IntelAsmSyntax"); + intelAsmSyntax.setLabelText(Tr::tr("Intel asm syntax")); + intelAsmSyntax.setDefaultValue(true); + + demangleIdentifiers.setSettingsKey("DemangleIdentifiers"); + demangleIdentifiers.setLabelText(Tr::tr("Demangle identifiers")); + demangleIdentifiers.setDefaultValue(true); + + for (const auto &aspect : this->aspects()) { + connect(aspect, + &Utils::BaseAspect::volatileValueChanged, + this, + &Utils::AspectContainer::changed); + } +} + +void CompilerSettings::refresh() +{ + cachedCompilers().clear(); + cachedLibraries().clear(); + + compiler.refill(); + libraries.refill(); +} + +void CompilerSettings::setLanguageId(const QString &languageId) +{ + m_languageId = languageId; + + compiler.refill(); + libraries.refill(); + + // TODO: Set real defaults ... + if (m_languageId == "c++") + compilerOptions.setValue("-O3"); + else + compilerOptions.setValue(""); +} + +void CompilerSettings::fillLibraries(const LibrarySelectionAspect::ResultCallback &cb) +{ + const QString lang = m_languageId; + auto fillFromCache = [cb, lang] { + QList<QStandardItem *> items; + for (const Api::Library &lib : cachedLibraries(lang)) { + QStandardItem *newItem = new QStandardItem(lib.name); + newItem->setData(QVariant::fromValue(lib), LibrarySelectionAspect::LibraryData); + items.append(newItem); + } + cb(items); + }; + + if (!cachedLibraries(lang).isEmpty()) { + fillFromCache(); + return; + } + + auto future = Api::libraries(m_apiConfigFunction(), lang); + + auto watcher = new QFutureWatcher<Api::Libraries>(this); + QObject::connect(watcher, + &QFutureWatcher<Api::Libraries>::finished, + this, + [watcher, fillFromCache, lang]() { + try { + cachedLibraries(lang) = watcher->result(); + fillFromCache(); + } catch (const std::exception &e) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed to fetch libraries: \"%1\".") + .arg(QString::fromUtf8(e.what()))); + } + }); + watcher->setFuture(future); +} + +void SourceSettings::fillLanguageIdModel(const Utils::StringSelectionAspect::ResultCallback &cb) +{ + auto fillFromCache = [cb, this] { + QList<QStandardItem *> items; + for (const Api::Language &language : cachedLanguages()) { + auto *newItem = new QStandardItem(language.name); + newItem->setData(language.id); + + if (QFileInfo::exists(":/compilerexplorer/logos/" + language.logoUrl)) { + QIcon icon(":/compilerexplorer/logos/" + language.logoUrl); + newItem->setIcon(icon); + } + + items.append(newItem); + } + cb(items); + emit languagesChanged(); + }; + + if (!cachedLanguages().isEmpty()) { + fillFromCache(); + return; + } + + auto future = Api::languages(m_apiConfigFunction()); + + auto watcher = new QFutureWatcher<Api::Languages>(this); + QObject::connect(watcher, + &QFutureWatcher<Api::Languages>::finished, + this, + [watcher, fillFromCache]() { + try { + cachedLanguages() = watcher->result(); + fillFromCache(); + } catch (const std::exception &e) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed to fetch languages: \"%1\".") + .arg(QString::fromUtf8(e.what()))); + } + }); + watcher->setFuture(future); +} + +void CompilerSettings::fillCompilerModel(const Utils::StringSelectionAspect::ResultCallback &cb) +{ + auto fillFromCache = [cb](auto it) { + QList<QStandardItem *> items; + for (const QString &key : it->keys()) { + QStandardItem *newItem = new QStandardItem(key); + newItem->setData(it->value(key)); + items.append(newItem); + } + cb(items); + }; + + auto it = cachedCompilers().find(m_languageId); + if (it != cachedCompilers().end()) { + fillFromCache(it); + return; + } + + auto future = Api::compilers(m_apiConfigFunction(), m_languageId); + + auto watcher = new QFutureWatcher<Api::Compilers>(this); + QObject::connect(watcher, + &QFutureWatcher<Api::Compilers>::finished, + this, + [watcher, this, fillFromCache]() { + try { + auto result = watcher->result(); + auto itCache = cachedCompilers().insert(m_languageId, {}); + + for (const Api::Compiler &compiler : result) + itCache->insert(compiler.name, compiler.id); + + fillFromCache(itCache); + } catch (const std::exception &e) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed to fetch compilers: \"%1\".") + .arg(QString::fromUtf8(e.what()))); + } + }); + watcher->setFuture(future); +} + +CompilerExplorerSettings::CompilerExplorerSettings() +{ + setAutoApply(false); + setSettingsKey("CompilerExplorer"); + static QNetworkAccessManager networkManager; + m_networkAccessManager = &networkManager; + + compilerExplorerUrl.setSettingsKey("CompilerExplorerUrl"); + compilerExplorerUrl.setLabelText(Tr::tr("Compiler Explorer URL:")); + compilerExplorerUrl.setToolTip(Tr::tr("URL of the Compiler Explorer instance to use.")); + compilerExplorerUrl.setDefaultValue("https://godbolt.org/"); + compilerExplorerUrl.setDisplayStyle(Utils::StringAspect::DisplayStyle::LineEditDisplay); + compilerExplorerUrl.setHistoryCompleter("CompilerExplorer.Url.History"); + + windowState.setSettingsKey("WindowState"); + + m_sources.setSettingsKey("Sources"); + m_sources.setCreateItemFunction([this] { + auto newSourceSettings = std::make_shared<SourceSettings>([this] { return apiConfig(); }); + connect(newSourceSettings.get(), + &Utils::AspectContainer::changed, + this, + &CompilerExplorerSettings::changed); + return newSourceSettings; + }); + + connect(&compilerExplorerUrl, &Utils::StringAspect::volatileValueChanged, this, [this] { + m_sources.forEachItem<SourceSettings>(&SourceSettings::refresh); + }); + + for (const auto &aspect : this->aspects()) { + connect(aspect, + &Utils::BaseAspect::volatileValueChanged, + this, + &CompilerExplorerSettings::changed); + } +} + +CompilerExplorerSettings::~CompilerExplorerSettings() = default; + +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/compilerexplorersettings.h b/src/plugins/compilerexplorer/compilerexplorersettings.h new file mode 100644 index 00000000000..64f67122fe6 --- /dev/null +++ b/src/plugins/compilerexplorer/compilerexplorersettings.h @@ -0,0 +1,108 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "api/config.h" +#include "compilerexploreraspects.h" + +#include <utils/aspects.h> + +#include <QNetworkAccessManager> + +namespace CompilerExplorer { +class SourceSettings; +class CompilerSettings; + +using ApiConfigFunction = std::function<Api::Config()>; + +class PluginSettings : public Utils::AspectContainer +{ +public: + PluginSettings(); + Utils::StringAspect defaultDocument{this}; +}; + +PluginSettings &settings(); + +class CompilerExplorerSettings : public Utils::AspectContainer +{ +public: + CompilerExplorerSettings(); + ~CompilerExplorerSettings(); + + Utils::StringAspect compilerExplorerUrl{this}; + Utils::TypedAspect<Utils::Store> windowState{this}; + + Utils::AspectList m_sources{this}; + + Api::Config apiConfig() const + { + return Api::Config(m_networkAccessManager, compilerExplorerUrl()); + } + + QNetworkAccessManager *networkAccessManager() const { return m_networkAccessManager; } + +private: + QNetworkAccessManager *m_networkAccessManager{nullptr}; +}; + +class SourceSettings : public Utils::AspectContainer, + public std::enable_shared_from_this<SourceSettings> +{ + Q_OBJECT +public: + SourceSettings(const ApiConfigFunction &apiConfigFunction); + + void refresh(); + + ApiConfigFunction apiConfigFunction() const { return m_apiConfigFunction; } + +public: + Utils::StringSelectionAspect languageId{this}; + Utils::StringAspect source{this}; + Utils::AspectList compilers{this}; + +public: + QString languageExtension() const; + +signals: + void languagesChanged(); + +private: + void fillLanguageIdModel(const Utils::StringSelectionAspect::ResultCallback &cb); + +private: + CompilerExplorerSettings *m_parent; + ApiConfigFunction m_apiConfigFunction; +}; + +class CompilerSettings : public Utils::AspectContainer, + public std::enable_shared_from_this<CompilerSettings> +{ +public: + CompilerSettings(const ApiConfigFunction &apiConfigFunction); + + Utils::StringSelectionAspect compiler{this}; + + Utils::StringAspect compilerOptions{this}; + LibrarySelectionAspect libraries{this}; + + // "Filters" + Utils::BoolAspect executeCode{this}; + Utils::BoolAspect compileToBinaryObject{this}; + Utils::BoolAspect intelAsmSyntax{this}; + Utils::BoolAspect demangleIdentifiers{this}; + + void refresh(); + void setLanguageId(const QString &languageId); + +private: + void fillCompilerModel(const Utils::StringSelectionAspect::ResultCallback &cb); + void fillLibraries(const LibrarySelectionAspect::ResultCallback &cb); + + QString m_languageId; + ApiConfigFunction m_apiConfigFunction; +}; + +} // namespace CompilerExplorer diff --git a/src/plugins/bookmarks/bookmarkstr.h b/src/plugins/compilerexplorer/compilerexplorertr.h similarity index 50% rename from src/plugins/bookmarks/bookmarkstr.h rename to src/plugins/compilerexplorer/compilerexplorertr.h index c48879d3470..b5aef408395 100644 --- a/src/plugins/bookmarks/bookmarkstr.h +++ b/src/plugins/compilerexplorer/compilerexplorertr.h @@ -1,15 +1,15 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include <QCoreApplication> -namespace Bookmarks { +namespace CompilerExplorer { struct Tr { - Q_DECLARE_TR_FUNCTIONS(QtC::Bookmarks) + Q_DECLARE_TR_FUNCTIONS(QtC::CompilerExplorer) }; -} // namespace Bookmarks +} // namespace CompilerExplorer diff --git a/src/plugins/compilerexplorer/logos/ce.ico b/src/plugins/compilerexplorer/logos/ce.ico new file mode 100644 index 00000000000..e0d26b2f5ff Binary files /dev/null and b/src/plugins/compilerexplorer/logos/ce.ico differ diff --git a/src/plugins/compilerexplorer/logos/logos.qrc b/src/plugins/compilerexplorer/logos/logos.qrc new file mode 100644 index 00000000000..a7aa39684f2 --- /dev/null +++ b/src/plugins/compilerexplorer/logos/logos.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/compilerexplorer/logos/"> + <file>ce.ico</file> + </qresource> +</RCC> diff --git a/src/plugins/compilerexplorer/test.rest b/src/plugins/compilerexplorer/test.rest new file mode 100644 index 00000000000..a6babdf5c21 --- /dev/null +++ b/src/plugins/compilerexplorer/test.rest @@ -0,0 +1,50 @@ + +POST https://godbolt.org/api/compiler/clang_trunk/compile HTTP/1.1 +content-type: application/json +Accept: application/json + +{ + "source": "int test() {return 20;};\nint main()\n{ return test();}", + "compiler": "clang_trunk", + "options": { + "userArguments": "", + "compilerOptions": { + "producePp": null, + "produceGccDump": {}, + "produceOptInfo": false, + "produceCfg": false, + "produceIr": null, + "produceLLVMOptPipeline": null, + "produceDevice": false, + "overrides": [] + }, + "filters": { + "binaryObject": false, + "binary": false, + "execute": true, + "intel": false, + "demangle": true, + "labels": true, + "libraryCode": false, + "directives": true, + "commentOnly": true, + "trim": false, + "debugCalls": false + }, + "tools": [], + "libraries": [], + "executeParameters": { + "args": "", + "stdin": "" + } + }, + "lang": "c++", + "files": [], + "bypassCache": 0, + "allowStoreCodeDebug": true +} + +### + +GET https://godbolt.org/api/compilers/c++ +Accept: application/json diff --git a/src/plugins/compilerexplorer/wizard/cpp/file.qtce b/src/plugins/compilerexplorer/wizard/cpp/file.qtce new file mode 100644 index 00000000000..903d5187165 --- /dev/null +++ b/src/plugins/compilerexplorer/wizard/cpp/file.qtce @@ -0,0 +1,10 @@ +{ + "Sources": [{ + "LanguageId": "c++", + "Source": "int main() {\n return 0;\n}", + "Compilers": [{ + "Id": "clang_trunk", + "Options": "-O3" + }] + }] +} diff --git a/src/plugins/compilerexplorer/wizard/cpp/wizard.json b/src/plugins/compilerexplorer/wizard/cpp/wizard.json new file mode 100644 index 00000000000..6851a0b3139 --- /dev/null +++ b/src/plugins/compilerexplorer/wizard/cpp/wizard.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "supportedProjectTypes": [], + "id": "QTCE.CppSource", + "category": "U.CompilerExplorer", + "trDescription": "Creates an example CompilerExplorer setup for C++.", + "trDisplayName": "Compiler Explorer C++ Source", + "trDisplayCategory": "Compiler Explorer", + "icon": ":/compilerexplorer/logos/ce.ico", + "iconKind": "Plain", + "options": { + "key": "DefaultSuffix", + "value": "%{JS: Util.preferredSuffix('application/compiler-explorer')}" + }, + "pages": [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": [ + { + "typeId": "File", + "data": { + "source": "file.qtce", + "target": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}", + "openInEditor": true + } + } + ] +} diff --git a/src/plugins/compilerexplorer/wizard/python/file.qtce b/src/plugins/compilerexplorer/wizard/python/file.qtce new file mode 100644 index 00000000000..5cd735227c9 --- /dev/null +++ b/src/plugins/compilerexplorer/wizard/python/file.qtce @@ -0,0 +1,9 @@ +{ + "Sources": [{ + "LanguageId": "python", + "Source": "def main():\\n print(\\"Hello World\\")\\n\\nif __name__ == \\"__main__\\":\\n main()", + "Compilers": [{ + "Id": "python311" + }] + }] +} diff --git a/src/plugins/compilerexplorer/wizard/python/wizard.json b/src/plugins/compilerexplorer/wizard/python/wizard.json new file mode 100644 index 00000000000..35a0106ab34 --- /dev/null +++ b/src/plugins/compilerexplorer/wizard/python/wizard.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "supportedProjectTypes": [], + "id": "QTCE.PySource", + "category": "U.CompilerExplorer", + "trDescription": "Creates an example CompilerExplorer setup for Python.", + "trDisplayName": "Compiler Explorer Python Source", + "trDisplayCategory": "Compiler Explorer", + "icon": ":/compilerexplorer/logos/ce.ico", + "iconKind": "Plain", + "options": { + "key": "DefaultSuffix", + "value": "%{JS: Util.preferredSuffix('application/compiler-explorer')}" + }, + "pages": [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": [ + { + "typeId": "File", + "data": { + "source": "file.qtce", + "target": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}", + "openInEditor": true + } + } + ] +} diff --git a/src/plugins/conan/Conan.json.in b/src/plugins/conan/Conan.json.in index 4ee078c1c90..b81813c0a6f 100644 --- a/src/plugins/conan/Conan.json.in +++ b/src/plugins/conan/Conan.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Conan\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Jochen Seemann\", - \"Copyright\" : \"(C) 2018 Jochen Seemann, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Conan", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Jochen Seemann", + "Copyright" : "(C) 2018 Jochen Seemann, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Experimental\" : true, - \"Description\" : \"Conan integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Experimental" : true, + "Description" : "Conan integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/conan/conaninstallstep.cpp b/src/plugins/conan/conaninstallstep.cpp index 2a634190365..0a1182eced2 100644 --- a/src/plugins/conan/conaninstallstep.cpp +++ b/src/plugins/conan/conaninstallstep.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildsteplist.h> #include <projectexplorer/gnumakeparser.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/project.h> @@ -62,6 +62,10 @@ public: private: bool init() final; void setupOutputFormatter(OutputFormatter *formatter) final; + + FilePathAspect conanFile{this}; + StringAspect additionalArguments{this}; + BoolAspect buildMissing{this}; }; ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id) @@ -70,36 +74,32 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id) setUseEnglishOutput(); setDisplayName(Tr::tr("Conan install")); - auto conanFile = addAspect<FilePathAspect>(); - conanFile->setSettingsKey("ConanPackageManager.InstallStep.ConanFile"); - conanFile->setFilePath(conanFilePath(project(), - project()->projectDirectory() / "conanfile.txt")); - conanFile->setLabelText(Tr::tr("Conan file:")); - conanFile->setToolTip(Tr::tr("Enter location of conanfile.txt or conanfile.py.")); - conanFile->setExpectedKind(PathChooser::File); + conanFile.setSettingsKey("ConanPackageManager.InstallStep.ConanFile"); + conanFile.setValue(conanFilePath(project(), project()->projectDirectory() / "conanfile.txt")); + conanFile.setLabelText(Tr::tr("Conan file:")); + conanFile.setToolTip(Tr::tr("Enter location of conanfile.txt or conanfile.py.")); + conanFile.setExpectedKind(PathChooser::File); - auto additionalArguments = addAspect<StringAspect>(); - additionalArguments->setSettingsKey("ConanPackageManager.InstallStep.AdditionalArguments"); - additionalArguments->setLabelText(Tr::tr("Additional arguments:")); - additionalArguments->setDisplayStyle(StringAspect::LineEditDisplay); + additionalArguments.setSettingsKey("ConanPackageManager.InstallStep.AdditionalArguments"); + additionalArguments.setLabelText(Tr::tr("Additional arguments:")); + additionalArguments.setDisplayStyle(StringAspect::LineEditDisplay); - auto buildMissing = addAspect<BoolAspect>(); - buildMissing->setSettingsKey("ConanPackageManager.InstallStep.BuildMissing"); - buildMissing->setLabel("Build missing:", BoolAspect::LabelPlacement::InExtraLabel); - buildMissing->setDefaultValue(true); - buildMissing->setValue(true); + buildMissing.setSettingsKey("ConanPackageManager.InstallStep.BuildMissing"); + buildMissing.setLabel("Build missing:", BoolAspect::LabelPlacement::InExtraLabel); + buildMissing.setDefaultValue(true); + buildMissing.setValue(true); - setCommandLineProvider([=] { + setCommandLineProvider([this] { BuildConfiguration::BuildType bt = buildConfiguration()->buildType(); const QString buildType = bt == BuildConfiguration::Release ? QString("Release") : QString("Debug"); CommandLine cmd(settings().conanFilePath()); cmd.addArgs({"install", "-s", "build_type=" + buildType}); - if (buildMissing->value()) + if (buildMissing()) cmd.addArg("--build=missing"); - cmd.addArg(conanFile->value()); - cmd.addArgs(additionalArguments->value(), CommandLine::Raw); + cmd.addArg(conanFile().path()); + cmd.addArgs(additionalArguments(), CommandLine::Raw); return cmd; }); diff --git a/src/plugins/conan/conanplugin.cpp b/src/plugins/conan/conanplugin.cpp index 13655b93b63..0a0d3e20244 100644 --- a/src/plugins/conan/conanplugin.cpp +++ b/src/plugins/conan/conanplugin.cpp @@ -2,23 +2,28 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "conaninstallstep.h" -#include "conansettings.h" #include <extensionsystem/iplugin.h> namespace Conan::Internal { +class ConanPluginPrivate +{ +public: + ConanInstallStepFactory conanInstallStepFactory; +}; + class ConanPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Conan.json") -public: - ConanPlugin() + void initialize() { - addManaged<ConanSettings>(); - addManaged<ConanInstallStepFactory>(); + d = std::make_unique<ConanPluginPrivate>(); } + + std::unique_ptr<ConanPluginPrivate> d; }; } // Conan::Internal diff --git a/src/plugins/conan/conansettings.cpp b/src/plugins/conan/conansettings.cpp index 50373c764b6..c6379e50826 100644 --- a/src/plugins/conan/conansettings.cpp +++ b/src/plugins/conan/conansettings.cpp @@ -3,22 +3,20 @@ #include "conansettings.h" -#include <coreplugin/icore.h> - #include <utils/hostosinfo.h> using namespace Utils; namespace Conan::Internal { -static ConanSettings *theSettings; - -ConanSettings &settings() { return *theSettings; } +ConanSettings &settings() +{ + static ConanSettings theSettings; + return theSettings; +} ConanSettings::ConanSettings() { - theSettings = this; - setSettingsGroup("ConanSettings"); setAutoApply(false); @@ -26,7 +24,7 @@ ConanSettings::ConanSettings() conanFilePath.setExpectedKind(PathChooser::ExistingCommand); conanFilePath.setDefaultValue(HostOsInfo::withExecutableSuffix("conan")); - readSettings(Core::ICore::settings()); + readSettings(); } } // Conan::Internal diff --git a/src/plugins/copilot/CMakeLists.txt b/src/plugins/copilot/CMakeLists.txt index 01129674fce..f50890d0fd2 100644 --- a/src/plugins/copilot/CMakeLists.txt +++ b/src/plugins/copilot/CMakeLists.txt @@ -6,7 +6,6 @@ add_qtc_plugin(Copilot copilotclient.cpp copilotclient.h copilotconstants.h copilothoverhandler.cpp copilothoverhandler.h - copilotoptionspage.cpp copilotoptionspage.h copilotplugin.cpp copilotplugin.h copilotprojectpanel.cpp copilotprojectpanel.h copilotsettings.cpp copilotsettings.h diff --git a/src/plugins/copilot/Copilot.json.in b/src/plugins/copilot/Copilot.json.in index ae5853c212d..81f326770d4 100644 --- a/src/plugins/copilot/Copilot.json.in +++ b/src/plugins/copilot/Copilot.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Copilot\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Copilot", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Copilot support\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Copilot support", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp index 4052ce600a7..0b5cfd2d93d 100644 --- a/src/plugins/copilot/authwidget.cpp +++ b/src/plugins/copilot/authwidget.cpp @@ -4,6 +4,7 @@ #include "authwidget.h" #include "copilotclient.h" +#include "copilotsettings.h" #include "copilottr.h" #include <utils/layoutbuilder.h> @@ -16,6 +17,7 @@ using namespace LanguageClient; using namespace Copilot::Internal; +using namespace Utils; namespace Copilot { @@ -26,7 +28,7 @@ AuthWidget::AuthWidget(QWidget *parent) m_button = new QPushButton(Tr::tr("Sign In")); m_button->setEnabled(false); - m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Small); + m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Small); m_progressIndicator->setVisible(false); m_statusLabel = new QLabel(); m_statusLabel->setVisible(false); @@ -42,12 +44,25 @@ AuthWidget::AuthWidget(QWidget *parent) }.attachTo(this); // clang-format on - connect(m_button, &QPushButton::clicked, this, [this]() { + auto update = [this] { + updateClient(FilePath::fromUserInput(settings().nodeJsPath.volatileValue()), + FilePath::fromUserInput(settings().distPath.volatileValue())); + }; + + connect(m_button, &QPushButton::clicked, this, [this, update]() { if (m_status == Status::SignedIn) signOut(); else if (m_status == Status::SignedOut) signIn(); + else + update(); }); + + connect(&settings(), &CopilotSettings::applied, this, update); + connect(&settings().nodeJsPath, &FilePathAspect::volatileValueChanged, this, update); + connect(&settings().distPath, &FilePathAspect::volatileValueChanged, this, update); + + update(); } AuthWidget::~AuthWidget() @@ -56,49 +71,53 @@ AuthWidget::~AuthWidget() LanguageClientManager::shutdownClient(m_client); } -void AuthWidget::setState(const QString &buttonText, bool working) +void AuthWidget::setState(const QString &buttonText, const QString &errorText, bool working) { m_button->setText(buttonText); m_button->setVisible(true); m_progressIndicator->setVisible(working); + m_statusLabel->setText(errorText); m_statusLabel->setVisible(!m_statusLabel->text().isEmpty()); m_button->setEnabled(!working); } void AuthWidget::checkStatus() { + if (!isEnabled()) + return; + QTC_ASSERT(m_client && m_client->reachable(), return); - setState("Checking status ...", true); + setState("Checking status ...", {}, true); m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) { if (response.error()) { - setState("failed: " + response.error()->message(), false); + setState("Failed to authenticate", response.error()->message(), false); return; } const CheckStatusResponse result = *response.result(); if (result.user().isEmpty()) { - setState("Sign in", false); + setState("Sign in", {}, false); m_status = Status::SignedOut; return; } - setState("Sign out " + result.user(), false); + setState("Sign out " + result.user(), {}, false); m_status = Status::SignedIn; }); } -void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent) +void AuthWidget::updateClient(const FilePath &nodeJs, const FilePath &agent) { LanguageClientManager::shutdownClient(m_client); m_client = nullptr; - setState(Tr::tr("Sign In"), false); + setState(Tr::tr("Sign In"), {}, false); m_button->setEnabled(false); if (!nodeJs.isExecutableFile() || !agent.exists()) return; - setState(Tr::tr("Sign In"), true); + setState(Tr::tr("Sign In"), {}, true); m_client = new CopilotClient(nodeJs, agent); connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus); @@ -115,7 +134,7 @@ void AuthWidget::signIn() qCritical() << "Not implemented"; QTC_ASSERT(m_client && m_client->reachable(), return); - setState("Signing in ...", true); + setState("Signing in ...", {}, true); m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) { QTC_ASSERT(!response.error(), return); @@ -132,19 +151,18 @@ void AuthWidget::signIn() m_client ->requestSignInConfirm(response.result()->userCode(), [this](const SignInConfirmRequest::Response &response) { - m_statusLabel->setText(""); - if (response.error()) { QMessageBox::critical(this, Tr::tr("Login Failed"), Tr::tr( - "The login request failed: ") - + response.error()->message()); - setState("Sign in", false); + "The login request failed: %1") + .arg(response.error() + ->message())); + setState("Sign in", response.error()->message(), false); return; } - setState("Sign Out " + response.result()->user(), false); + setState("Sign Out " + response.result()->user(), {}, false); }); }); } @@ -153,7 +171,7 @@ void AuthWidget::signOut() { QTC_ASSERT(m_client && m_client->reachable(), return); - setState("Signing out ...", true); + setState("Signing out ...", {}, true); m_client->requestSignOut([this](const SignOutRequest::Response &response) { QTC_ASSERT(!response.error(), return); diff --git a/src/plugins/copilot/authwidget.h b/src/plugins/copilot/authwidget.h index acb18810fe4..6da873bd001 100644 --- a/src/plugins/copilot/authwidget.h +++ b/src/plugins/copilot/authwidget.h @@ -30,7 +30,7 @@ public: void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent); private: - void setState(const QString &buttonText, bool working); + void setState(const QString &buttonText, const QString &errorText, bool working); void checkStatus(); diff --git a/src/plugins/copilot/copilot.qbs b/src/plugins/copilot/copilot.qbs index 714c45543d4..6d2b2100472 100644 --- a/src/plugins/copilot/copilot.qbs +++ b/src/plugins/copilot/copilot.qbs @@ -18,8 +18,6 @@ QtcPlugin { "copilotconstants.h", "copilothoverhandler.cpp", "copilothoverhandler.h", - "copilotoptionspage.cpp", - "copilotoptionspage.h", "copilotplugin.cpp", "copilotplugin.h", "copilotprojectpanel.cpp", diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp index 061d5d4a459..6b09882dee9 100644 --- a/src/plugins/copilot/copilotclient.cpp +++ b/src/plugins/copilot/copilotclient.cpp @@ -2,26 +2,31 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "copilotclient.h" -#include "copilotconstants.h" #include "copilotsettings.h" #include "copilotsuggestion.h" +#include "copilottr.h" #include <languageclient/languageclientinterface.h> #include <languageclient/languageclientmanager.h> #include <languageclient/languageclientsettings.h> +#include <languageserverprotocol/lsptypes.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/icore.h> #include <projectexplorer/projectmanager.h> -#include <utils/filepath.h> - #include <texteditor/textdocumentlayout.h> #include <texteditor/texteditor.h> -#include <languageserverprotocol/lsptypes.h> +#include <utils/checkablemessagebox.h> +#include <utils/filepath.h> +#include <utils/passworddialog.h> +#include <QGuiApplication> +#include <QInputDialog> +#include <QLoggingCategory> #include <QTimer> #include <QToolButton> @@ -31,6 +36,8 @@ using namespace Utils; using namespace ProjectExplorer; using namespace Core; +Q_LOGGING_CATEGORY(copilotClientLog, "qtc.copilot.client", QtWarningMsg) + namespace Copilot::Internal { static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath, @@ -52,6 +59,23 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath) langFilter.filePattern = {"*"}; setSupportedLanguage(langFilter); + + registerCustomMethod("LogMessage", [this](const LanguageServerProtocol::JsonRpcMessage &message) { + QString msg = message.toJsonObject().value("params").toObject().value("message").toString(); + qCDebug(copilotClientLog) << message.toJsonObject() + .value("params") + .toObject() + .value("message") + .toString(); + + if (msg.contains("Socket Connect returned status code,407")) { + qCWarning(copilotClientLog) << "Proxy authentication required"; + QMetaObject::invokeMethod(this, + &CopilotClient::proxyAuthenticationFailed, + Qt::QueuedConnection); + } + }); + start(); auto openDoc = [this](IDocument *document) { @@ -68,6 +92,8 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath) closeDocument(textDocument); }); + connect(this, &LanguageClient::Client::initialized, this, &CopilotClient::requestSetEditorInfo); + for (IDocument *doc : DocumentModel::openedDocuments()) openDoc(doc); } @@ -92,7 +118,7 @@ void CopilotClient::openDocument(TextDocument *document) this, [this, document](int position, int charsRemoved, int charsAdded) { Q_UNUSED(charsRemoved) - if (!CopilotSettings::instance().autoComplete()) + if (!settings().autoComplete()) return; auto project = ProjectManager::projectForFile(document->filePath()); @@ -103,7 +129,7 @@ void CopilotClient::openDocument(TextDocument *document) if (!textEditor || textEditor->document() != document) return; TextEditorWidget *widget = textEditor->editorWidget(); - if (widget->multiTextCursor().hasMultipleCursors()) + if (widget->isReadOnly() || widget->multiTextCursor().hasMultipleCursors()) return; const int cursorPosition = widget->textCursor().position(); if (cursorPosition < position || cursorPosition > position + charsAdded) @@ -218,6 +244,33 @@ void CopilotClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor) m_runningRequests.erase(it); } +static QString currentProxyPassword; + +void CopilotClient::requestSetEditorInfo() +{ + if (settings().saveProxyPassword()) + currentProxyPassword = settings().proxyPassword(); + + const EditorInfo editorInfo{QCoreApplication::applicationVersion(), + QGuiApplication::applicationDisplayName()}; + const EditorPluginInfo editorPluginInfo{QCoreApplication::applicationVersion(), + "Qt Creator Copilot plugin"}; + + SetEditorInfoParams params(editorInfo, editorPluginInfo); + + if (settings().useProxy()) { + params.setNetworkProxy( + Copilot::NetworkProxy{settings().proxyHost(), + static_cast<int>(settings().proxyPort()), + settings().proxyUser(), + currentProxyPassword, + settings().proxyRejectUnauthorized()}); + } + + SetEditorInfoRequest request(params); + sendMessage(request); +} + void CopilotClient::requestCheckStatus( bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback) { @@ -263,10 +316,42 @@ bool CopilotClient::canOpenProject(Project *project) bool CopilotClient::isEnabled(Project *project) { if (!project) - return CopilotSettings::instance().enableCopilot(); + return settings().enableCopilot(); CopilotProjectSettings settings(project); return settings.isEnabled(); } +void CopilotClient::proxyAuthenticationFailed() +{ + static bool doNotAskAgain = false; + + if (m_isAskingForPassword || !settings().enableCopilot()) + return; + + m_isAskingForPassword = true; + + auto answer = Utils::PasswordDialog::getUserAndPassword( + Tr::tr("Copilot"), + Tr::tr("Proxy username and password required:"), + Tr::tr("Do not ask again. This will disable Copilot for now."), + settings().proxyUser(), + &doNotAskAgain, + Core::ICore::dialogParent()); + + if (answer) { + settings().proxyUser.setValue(answer->first); + currentProxyPassword = answer->second; + } else { + settings().enableCopilot.setValue(false); + } + + if (settings().saveProxyPassword()) + settings().proxyPassword.setValue(currentProxyPassword); + + settings().apply(); + + m_isAskingForPassword = false; +} + } // namespace Copilot::Internal diff --git a/src/plugins/copilot/copilotclient.h b/src/plugins/copilot/copilotclient.h index 47208236b52..aa03b70960f 100644 --- a/src/plugins/copilot/copilotclient.h +++ b/src/plugins/copilot/copilotclient.h @@ -6,6 +6,7 @@ #include "copilothoverhandler.h" #include "requests/checkstatus.h" #include "requests/getcompletions.h" +#include "requests/seteditorinfo.h" #include "requests/signinconfirm.h" #include "requests/signininitiate.h" #include "requests/signout.h" @@ -50,7 +51,11 @@ public: bool isEnabled(ProjectExplorer::Project *project); + void proxyAuthenticationFailed(); + private: + void requestSetEditorInfo(); + QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests; struct ScheduleData { @@ -59,6 +64,7 @@ private: }; QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests; CopilotHoverHandler m_hoverHandler; + bool m_isAskingForPassword{false}; }; } // namespace Copilot::Internal diff --git a/src/plugins/copilot/copilotoptionspage.cpp b/src/plugins/copilot/copilotoptionspage.cpp deleted file mode 100644 index 1d9a516d662..00000000000 --- a/src/plugins/copilot/copilotoptionspage.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#include "copilotoptionspage.h" - -#include "authwidget.h" -#include "copilotconstants.h" -#include "copilotsettings.h" -#include "copilottr.h" - -#include <coreplugin/icore.h> - -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> - -#include <QToolTip> - -using namespace Utils; -using namespace LanguageClient; - -namespace Copilot { - -class CopilotOptionsPageWidget : public Core::IOptionsPageWidget -{ -public: - CopilotOptionsPageWidget() - { - using namespace Layouting; - - auto warningLabel = new QLabel; - warningLabel->setWordWrap(true); - warningLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse - | Qt::LinksAccessibleByKeyboard - | Qt::TextSelectableByMouse); - warningLabel->setText(Tr::tr( - "Enabling %1 is subject to your agreement and abidance with your applicable " - "%1 terms. It is your responsibility to know and accept the requirements and " - "parameters of using tools like %1. This may include, but is not limited to, " - "ensuring you have the rights to allow %1 access to your code, as well as " - "understanding any implications of your use of %1 and suggestions produced " - "(like copyright, accuracy, etc.)." ).arg("Copilot")); - - auto authWidget = new AuthWidget(); - - auto helpLabel = new QLabel(); - helpLabel->setTextFormat(Qt::MarkdownText); - helpLabel->setWordWrap(true); - helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse - | Qt::LinksAccessibleByKeyboard - | Qt::TextSelectableByMouse); - helpLabel->setOpenExternalLinks(true); - connect(helpLabel, &QLabel::linkHovered, [](const QString &link) { - QToolTip::showText(QCursor::pos(), link); - }); - - // clang-format off - helpLabel->setText(Tr::tr( - "The Copilot plugin requires node.js and the Copilot neovim plugin. " - "If you install the neovim plugin as described in %1, " - "the plugin will find the agent.js file automatically.\n\n" - "Otherwise you need to specify the path to the %2 " - "file from the Copilot neovim plugin.", - "Markdown text for the copilot instruction label") - .arg("[README.md](https://github.com/github/copilot.vim)") - .arg("[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)")); - - Column { - QString("<b>" + Tr::tr("Note:") + "</b>"), br, - warningLabel, br, - CopilotSettings::instance().enableCopilot, br, - authWidget, br, - CopilotSettings::instance().nodeJsPath, br, - CopilotSettings::instance().distPath, br, - CopilotSettings::instance().autoComplete, br, - helpLabel, br, - st - }.attachTo(this); - // clang-format on - - auto updateAuthWidget = [authWidget]() { - authWidget->updateClient( - FilePath::fromUserInput( - CopilotSettings::instance().nodeJsPath.volatileValue().toString()), - FilePath::fromUserInput( - CopilotSettings::instance().distPath.volatileValue().toString())); - }; - - connect(CopilotSettings::instance().nodeJsPath.pathChooser(), - &PathChooser::textChanged, - authWidget, - updateAuthWidget); - connect(CopilotSettings::instance().distPath.pathChooser(), - &PathChooser::textChanged, - authWidget, - updateAuthWidget); - updateAuthWidget(); - - setOnApply([] { - CopilotSettings::instance().apply(); - CopilotSettings::instance().writeSettings(Core::ICore::settings()); - }); - } -}; - -CopilotOptionsPage::CopilotOptionsPage() -{ - setId(Constants::COPILOT_GENERAL_OPTIONS_ID); - setDisplayName("Copilot"); - setCategory(Constants::COPILOT_GENERAL_OPTIONS_CATEGORY); - setDisplayCategory(Constants::COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY); - setCategoryIconPath(":/copilot/images/settingscategory_copilot.png"); - setWidgetCreator([] { return new CopilotOptionsPageWidget; }); -} - -CopilotOptionsPage &CopilotOptionsPage::instance() -{ - static CopilotOptionsPage settingsPage; - return settingsPage; -} - -} // namespace Copilot diff --git a/src/plugins/copilot/copilotoptionspage.h b/src/plugins/copilot/copilotoptionspage.h deleted file mode 100644 index 103e975b634..00000000000 --- a/src/plugins/copilot/copilotoptionspage.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Copilot { - -class CopilotOptionsPage : public Core::IOptionsPage -{ -public: - CopilotOptionsPage(); - - static CopilotOptionsPage &instance(); -}; - -} // Copilot diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp index bb5c80fa60f..47364c60d7d 100644 --- a/src/plugins/copilot/copilotplugin.cpp +++ b/src/plugins/copilot/copilotplugin.cpp @@ -6,7 +6,6 @@ #include "copilotclient.h" #include "copilotconstants.h" #include "copiloticons.h" -#include "copilotoptionspage.h" #include "copilotprojectpanel.h" #include "copilotsettings.h" #include "copilotsuggestion.h" @@ -14,7 +13,6 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/icore.h> #include <coreplugin/statusbarmanager.h> #include <languageclient/languageclientmanager.h> @@ -35,6 +33,7 @@ namespace Copilot { namespace Internal { enum Direction { Previous, Next }; + void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction) { QTextBlock block = editor->textCursor().block(); @@ -58,15 +57,6 @@ void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction) void CopilotPlugin::initialize() { - CopilotSettings::instance().readSettings(ICore::settings()); - - restartClient(); - - connect(&CopilotSettings::instance(), - &CopilotSettings::applied, - this, - &CopilotPlugin::restartClient); - QAction *requestAction = new QAction(this); requestAction->setText(Tr::tr("Request Copilot Suggestion")); requestAction->setToolTip( @@ -82,7 +72,7 @@ void CopilotPlugin::initialize() ActionManager::registerAction(requestAction, Constants::COPILOT_REQUEST_SUGGESTION); QAction *nextSuggestionAction = new QAction(this); - nextSuggestionAction->setText(Tr::tr("Show next Copilot Suggestion")); + nextSuggestionAction->setText(Tr::tr("Show Next Copilot Suggestion")); nextSuggestionAction->setToolTip(Tr::tr( "Cycles through the received Copilot Suggestions showing the next available Suggestion.")); @@ -94,7 +84,7 @@ void CopilotPlugin::initialize() ActionManager::registerAction(nextSuggestionAction, Constants::COPILOT_NEXT_SUGGESTION); QAction *previousSuggestionAction = new QAction(this); - previousSuggestionAction->setText(Tr::tr("Show previos Copilot Suggestion")); + previousSuggestionAction->setText(Tr::tr("Show Previous Copilot Suggestion")); previousSuggestionAction->setToolTip(Tr::tr("Cycles through the received Copilot Suggestions " "showing the previous available Suggestion.")); @@ -109,8 +99,8 @@ void CopilotPlugin::initialize() disableAction->setText(Tr::tr("Disable Copilot")); disableAction->setToolTip(Tr::tr("Disable Copilot.")); connect(disableAction, &QAction::triggered, this, [] { - CopilotSettings::instance().enableCopilot.setValue(true); - CopilotSettings::instance().apply(); + settings().enableCopilot.setValue(true); + settings().apply(); }); ActionManager::registerAction(disableAction, Constants::COPILOT_DISABLE); @@ -118,34 +108,31 @@ void CopilotPlugin::initialize() enableAction->setText(Tr::tr("Enable Copilot")); enableAction->setToolTip(Tr::tr("Enable Copilot.")); connect(enableAction, &QAction::triggered, this, [] { - CopilotSettings::instance().enableCopilot.setValue(false); - CopilotSettings::instance().apply(); + settings().enableCopilot.setValue(false); + settings().apply(); }); ActionManager::registerAction(enableAction, Constants::COPILOT_ENABLE); QAction *toggleAction = new QAction(this); toggleAction->setText(Tr::tr("Toggle Copilot")); toggleAction->setCheckable(true); - toggleAction->setChecked(CopilotSettings::instance().enableCopilot.value()); + toggleAction->setChecked(settings().enableCopilot()); toggleAction->setIcon(COPILOT_ICON.icon()); connect(toggleAction, &QAction::toggled, this, [](bool checked) { - CopilotSettings::instance().enableCopilot.setValue(checked); - CopilotSettings::instance().apply(); + settings().enableCopilot.setValue(checked); + settings().apply(); }); ActionManager::registerAction(toggleAction, Constants::COPILOT_TOGGLE); auto updateActions = [toggleAction, requestAction] { - const bool enabled = CopilotSettings::instance().enableCopilot.value(); + const bool enabled = settings().enableCopilot(); toggleAction->setToolTip(enabled ? Tr::tr("Disable Copilot.") : Tr::tr("Enable Copilot.")); toggleAction->setChecked(enabled); requestAction->setEnabled(enabled); }; - connect(&CopilotSettings::instance().enableCopilot, - &BoolAspect::valueChanged, - this, - updateActions); + connect(&settings().enableCopilot, &BaseAspect::changed, this, updateActions); updateActions(); @@ -160,19 +147,22 @@ void CopilotPlugin::initialize() ProjectPanelFactory::registerFactory(panelFactory); } -void CopilotPlugin::extensionsInitialized() +bool CopilotPlugin::delayedInitialize() { - (void)CopilotOptionsPage::instance(); + restartClient(); + + connect(&settings(), &AspectContainer::applied, this, &CopilotPlugin::restartClient); + + return true; } void CopilotPlugin::restartClient() { LanguageClient::LanguageClientManager::shutdownClient(m_client); - if (!CopilotSettings::instance().nodeJsPath().isExecutableFile()) + if (!settings().nodeJsPath().isExecutableFile()) return; - m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(), - CopilotSettings::instance().distPath()); + m_client = new CopilotClient(settings().nodeJsPath(), settings().distPath()); } ExtensionSystem::IPlugin::ShutdownFlag CopilotPlugin::aboutToShutdown() diff --git a/src/plugins/copilot/copilotplugin.h b/src/plugins/copilot/copilotplugin.h index 8c1a176a732..9f709b2a101 100644 --- a/src/plugins/copilot/copilotplugin.h +++ b/src/plugins/copilot/copilotplugin.h @@ -19,7 +19,7 @@ class CopilotPlugin : public ExtensionSystem::IPlugin public: void initialize() override; - void extensionsInitialized() override; + bool delayedInitialize() override; void restartClient(); ShutdownFlag aboutToShutdown() override; diff --git a/src/plugins/copilot/copilotprojectpanel.cpp b/src/plugins/copilot/copilotprojectpanel.cpp index f1eb5d119e8..034e43bc2dc 100644 --- a/src/plugins/copilot/copilotprojectpanel.cpp +++ b/src/plugins/copilot/copilotprojectpanel.cpp @@ -32,7 +32,8 @@ ProjectSettingsWidget *createCopilotProjectPanel(Project *project) using namespace ProjectExplorer; auto widget = new CopilotProjectSettingsWidget; - auto settings = new CopilotProjectSettings(project, widget); + auto settings = new CopilotProjectSettings(project); + settings->setParent(widget); QObject::connect(widget, &ProjectSettingsWidget::useGlobalSettingsChanged, diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp index b354bcd54bb..c75f7124235 100644 --- a/src/plugins/copilot/copilotsettings.cpp +++ b/src/plugins/copilot/copilotsettings.cpp @@ -2,13 +2,21 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "copilotsettings.h" + +#include "authwidget.h" #include "copilotconstants.h" #include "copilottr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <projectexplorer/project.h> #include <utils/algorithm.h> #include <utils/environment.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> + +#include <QToolTip> using namespace Utils; @@ -23,7 +31,7 @@ static void initEnableAspect(BoolAspect &enableCopilot) enableCopilot.setDefaultValue(false); } -CopilotSettings &CopilotSettings::instance() +CopilotSettings &settings() { static CopilotSettings settings; return settings; @@ -35,54 +43,197 @@ CopilotSettings::CopilotSettings() const FilePath nodeFromPath = FilePath("node").searchInPath(); - const FilePaths searchDirs + // clang-format off - = {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"), - FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"), - FilePath::fromUserInput( - "~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"), - FilePath::fromUserInput( - "~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"), - FilePath::fromUserInput( - "~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js")}; + // From: https://github.com/github/copilot.vim/blob/release/README.md#getting-started + const FilePaths searchDirs = { + // Vim, Linux/macOS: + FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"), + FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"), + + // Neovim, Linux/macOS: + FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/dist/agent.js"), + FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"), + + // Vim, Windows (PowerShell command): + FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/dist/agent.js"), + FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"), + + // Neovim, Windows (PowerShell command): + FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/dist/agent.js"), + FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js") + }; + // clang-format on const FilePath distFromVim = findOrDefault(searchDirs, &FilePath::exists); nodeJsPath.setExpectedKind(PathChooser::ExistingCommand); - nodeJsPath.setDefaultFilePath(nodeFromPath); + nodeJsPath.setDefaultValue(nodeFromPath.toUserOutput()); nodeJsPath.setSettingsKey("Copilot.NodeJsPath"); nodeJsPath.setLabelText(Tr::tr("Node.js path:")); nodeJsPath.setHistoryCompleter("Copilot.NodePath.History"); nodeJsPath.setDisplayName(Tr::tr("Node.js Path")); - nodeJsPath.setEnabler(&enableCopilot); - nodeJsPath.setToolTip( - Tr::tr("Select path to node.js executable. See https://nodejs.org/en/download/" - "for installation instructions.")); + //: %1 is the URL to nodejs + nodeJsPath.setToolTip(Tr::tr("Select path to node.js executable. See %1 " + "for installation instructions.") + .arg("https://nodejs.org/en/download/")); distPath.setExpectedKind(PathChooser::File); - distPath.setDefaultFilePath(distFromVim); + distPath.setDefaultValue(distFromVim.toUserOutput()); distPath.setSettingsKey("Copilot.DistPath"); distPath.setLabelText(Tr::tr("Path to agent.js:")); distPath.setHistoryCompleter("Copilot.DistPath.History"); distPath.setDisplayName(Tr::tr("Agent.js path")); - distPath.setEnabler(&enableCopilot); - distPath.setToolTip(Tr::tr( - "Select path to agent.js in Copilot Neovim plugin. See " - "https://github.com/github/copilot.vim#getting-started for installation instructions.")); + //: %1 is the URL to copilot.vim getting started + distPath.setToolTip(Tr::tr("Select path to agent.js in Copilot Neovim plugin. See " + "%1 for installation instructions.") + .arg("https://github.com/github/copilot.vim#getting-started")); - autoComplete.setDisplayName(Tr::tr("Auto Complete")); + autoComplete.setDisplayName(Tr::tr("Auto Request")); autoComplete.setSettingsKey("Copilot.Autocomplete"); - autoComplete.setLabelText(Tr::tr("Request completions automatically")); + autoComplete.setLabelText(Tr::tr("Auto request")); autoComplete.setDefaultValue(true); - autoComplete.setEnabler(&enableCopilot); autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor " "position after changes to the document.")); + useProxy.setDisplayName(Tr::tr("Use Proxy")); + useProxy.setSettingsKey("Copilot.UseProxy"); + useProxy.setLabelText(Tr::tr("Use proxy")); + useProxy.setDefaultValue(false); + useProxy.setToolTip(Tr::tr("Use a proxy to connect to the Copilot servers.")); + + proxyHost.setDisplayName(Tr::tr("Proxy Host")); + proxyHost.setDisplayStyle(StringAspect::LineEditDisplay); + proxyHost.setSettingsKey("Copilot.ProxyHost"); + proxyHost.setLabelText(Tr::tr("Proxy host:")); + proxyHost.setDefaultValue(""); + proxyHost.setToolTip(Tr::tr("The host name of the proxy server.")); + proxyHost.setHistoryCompleter("Copilot.ProxyHost.History"); + + proxyPort.setDisplayName(Tr::tr("Proxy Port")); + proxyPort.setSettingsKey("Copilot.ProxyPort"); + proxyPort.setLabelText(Tr::tr("Proxy port:")); + proxyPort.setDefaultValue(3128); + proxyPort.setToolTip(Tr::tr("The port of the proxy server.")); + proxyPort.setRange(1, 65535); + + proxyUser.setDisplayName(Tr::tr("Proxy User")); + proxyUser.setDisplayStyle(StringAspect::LineEditDisplay); + proxyUser.setSettingsKey("Copilot.ProxyUser"); + proxyUser.setLabelText(Tr::tr("Proxy user:")); + proxyUser.setDefaultValue(""); + proxyUser.setToolTip(Tr::tr("The user name to access the proxy server.")); + proxyUser.setHistoryCompleter("Copilot.ProxyUser.History"); + + saveProxyPassword.setDisplayName(Tr::tr("Save Proxy Password")); + saveProxyPassword.setSettingsKey("Copilot.SaveProxyPassword"); + saveProxyPassword.setLabelText(Tr::tr("Save proxy password")); + saveProxyPassword.setDefaultValue(false); + saveProxyPassword.setToolTip( + Tr::tr("Save the password to access the proxy server. The password is stored insecurely.")); + + proxyPassword.setDisplayName(Tr::tr("Proxy Password")); + proxyPassword.setDisplayStyle(StringAspect::PasswordLineEditDisplay); + proxyPassword.setSettingsKey("Copilot.ProxyPassword"); + proxyPassword.setLabelText(Tr::tr("Proxy password:")); + proxyPassword.setDefaultValue(""); + proxyPassword.setToolTip(Tr::tr("The password for the proxy server.")); + + proxyRejectUnauthorized.setDisplayName(Tr::tr("Reject Unauthorized")); + proxyRejectUnauthorized.setSettingsKey("Copilot.ProxyRejectUnauthorized"); + proxyRejectUnauthorized.setLabelText(Tr::tr("Reject unauthorized")); + proxyRejectUnauthorized.setDefaultValue(true); + proxyRejectUnauthorized.setToolTip(Tr::tr("Reject unauthorized certificates from the proxy " + "server. Turning this off is a security risk.")); + initEnableAspect(enableCopilot); + + readSettings(); + + // TODO: As a workaround we set the enabler after reading the settings, as that does not signal + // a change. + nodeJsPath.setEnabler(&enableCopilot); + distPath.setEnabler(&enableCopilot); + autoComplete.setEnabler(&enableCopilot); + useProxy.setEnabler(&enableCopilot); + + proxyHost.setEnabler(&useProxy); + proxyPort.setEnabler(&useProxy); + proxyUser.setEnabler(&useProxy); + saveProxyPassword.setEnabler(&useProxy); + proxyRejectUnauthorized.setEnabler(&useProxy); + + proxyPassword.setEnabler(&saveProxyPassword); + + setLayouter([this] { + using namespace Layouting; + + auto warningLabel = new QLabel; + warningLabel->setWordWrap(true); + warningLabel->setTextInteractionFlags( + Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse); + warningLabel->setText( + Tr::tr("Enabling %1 is subject to your agreement and abidance with your applicable " + "%1 terms. It is your responsibility to know and accept the requirements and " + "parameters of using tools like %1. This may include, but is not limited to, " + "ensuring you have the rights to allow %1 access to your code, as well as " + "understanding any implications of your use of %1 and suggestions produced " + "(like copyright, accuracy, etc.).") + .arg("Copilot")); + + auto authWidget = new AuthWidget(); + + auto helpLabel = new QLabel(); + helpLabel->setTextFormat(Qt::MarkdownText); + helpLabel->setWordWrap(true); + helpLabel->setTextInteractionFlags( + Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse); + helpLabel->setOpenExternalLinks(true); + connect(helpLabel, &QLabel::linkHovered, [](const QString &link) { + QToolTip::showText(QCursor::pos(), link); + }); + + // clang-format off + helpLabel->setText(Tr::tr( + "The Copilot plugin requires node.js and the Copilot neovim plugin. " + "If you install the neovim plugin as described in %1, " + "the plugin will find the agent.js file automatically.\n\n" + "Otherwise you need to specify the path to the %2 " + "file from the Copilot neovim plugin.", + "Markdown text for the copilot instruction label") + .arg("[README.md](https://github.com/github/copilot.vim)") + .arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)")); + + return Column { + Group { + title(Tr::tr("Note")), + Column { + warningLabel, br, + helpLabel, br, + } + }, + Form { + authWidget, br, + enableCopilot, br, + nodeJsPath, br, + distPath, br, + autoComplete, br, + hr, br, + useProxy, br, + proxyHost, br, + proxyPort, br, + proxyRejectUnauthorized, br, + proxyUser, br, + saveProxyPassword, br, + proxyPassword, br, + }, + st + }; + // clang-format on + }); } -CopilotProjectSettings::CopilotProjectSettings(ProjectExplorer::Project *project, QObject *parent) - : AspectContainer(parent) +CopilotProjectSettings::CopilotProjectSettings(ProjectExplorer::Project *project) { setAutoApply(true); @@ -91,11 +242,11 @@ CopilotProjectSettings::CopilotProjectSettings(ProjectExplorer::Project *project initEnableAspect(enableCopilot); - QVariantMap map = project->namedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID).toMap(); + Store map = storeFromVariant(project->namedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID)); fromMap(map); - connect(&enableCopilot, &BoolAspect::valueChanged, this, [this, project] { save(project); }); - connect(&useGlobalSettings, &BoolAspect::valueChanged, this, [this, project] { save(project); }); + connect(&enableCopilot, &BaseAspect::changed, this, [this, project] { save(project); }); + connect(&useGlobalSettings, &BaseAspect::changed, this, [this, project] { save(project); }); } void CopilotProjectSettings::setUseGlobalSettings(bool useGlobal) @@ -106,18 +257,34 @@ void CopilotProjectSettings::setUseGlobalSettings(bool useGlobal) bool CopilotProjectSettings::isEnabled() const { if (useGlobalSettings()) - return CopilotSettings::instance().enableCopilot(); + return settings().enableCopilot(); return enableCopilot(); } void CopilotProjectSettings::save(ProjectExplorer::Project *project) { - QVariantMap map; + Store map; toMap(map); - project->setNamedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID, map); + project->setNamedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID, variantFromStore(map)); // This triggers a restart of the Copilot language server. - CopilotSettings::instance().apply(); + settings().apply(); } +class CopilotSettingsPage : public Core::IOptionsPage +{ +public: + CopilotSettingsPage() + { + setId(Constants::COPILOT_GENERAL_OPTIONS_ID); + setDisplayName("Copilot"); + setCategory(Constants::COPILOT_GENERAL_OPTIONS_CATEGORY); + setDisplayCategory(Constants::COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY); + setCategoryIconPath(":/copilot/images/settingscategory_copilot.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const CopilotSettingsPage settingsPage; + } // namespace Copilot diff --git a/src/plugins/copilot/copilotsettings.h b/src/plugins/copilot/copilotsettings.h index cec44c43fed..b92106c3f73 100644 --- a/src/plugins/copilot/copilotsettings.h +++ b/src/plugins/copilot/copilotsettings.h @@ -5,9 +5,7 @@ #include <utils/aspects.h> -namespace ProjectExplorer { -class Project; -} +namespace ProjectExplorer { class Project; } namespace Copilot { @@ -16,18 +14,27 @@ class CopilotSettings : public Utils::AspectContainer public: CopilotSettings(); - static CopilotSettings &instance(); - Utils::FilePathAspect nodeJsPath{this}; Utils::FilePathAspect distPath{this}; Utils::BoolAspect autoComplete{this}; Utils::BoolAspect enableCopilot{this}; + + Utils::BoolAspect useProxy{this}; + Utils::StringAspect proxyHost{this}; + Utils::IntegerAspect proxyPort{this}; + Utils::StringAspect proxyUser{this}; + + Utils::BoolAspect saveProxyPassword{this}; + Utils::StringAspect proxyPassword{this}; + Utils::BoolAspect proxyRejectUnauthorized{this}; }; +CopilotSettings &settings(); + class CopilotProjectSettings : public Utils::AspectContainer { public: - CopilotProjectSettings(ProjectExplorer::Project *project, QObject *parent = nullptr); + explicit CopilotProjectSettings(ProjectExplorer::Project *project); void save(ProjectExplorer::Project *project); void setUseGlobalSettings(bool useGlobalSettings); diff --git a/src/plugins/copilot/requests/checkstatus.h b/src/plugins/copilot/requests/checkstatus.h index 20e77eb1457..8e9badfc4e0 100644 --- a/src/plugins/copilot/requests/checkstatus.h +++ b/src/plugins/copilot/requests/checkstatus.h @@ -10,8 +10,8 @@ namespace Copilot { class CheckStatusParams : public LanguageServerProtocol::JsonObject { - static constexpr char16_t optionsKey[] = u"options"; - static constexpr char16_t localChecksOnlyKey[] = u"options"; + static constexpr char optionsKey[] = "options"; + static constexpr char localChecksOnlyKey[] = "options"; public: using JsonObject::JsonObject; @@ -30,8 +30,8 @@ public: class CheckStatusResponse : public LanguageServerProtocol::JsonObject { - static constexpr char16_t userKey[] = u"user"; - static constexpr char16_t statusKey[] = u"status"; + static constexpr char userKey[] = "user"; + static constexpr char statusKey[] = "status"; public: using JsonObject::JsonObject; diff --git a/src/plugins/copilot/requests/getcompletions.h b/src/plugins/copilot/requests/getcompletions.h index 2280cfc73df..7b652ee4d71 100644 --- a/src/plugins/copilot/requests/getcompletions.h +++ b/src/plugins/copilot/requests/getcompletions.h @@ -11,8 +11,8 @@ namespace Copilot { class Completion : public LanguageServerProtocol::JsonObject { - static constexpr char16_t displayTextKey[] = u"displayText"; - static constexpr char16_t uuidKey[] = u"uuid"; + static constexpr char displayTextKey[] = "displayText"; + static constexpr char uuidKey[] = "uuid"; public: using JsonObject::JsonObject; @@ -41,7 +41,7 @@ public: class GetCompletionParams : public LanguageServerProtocol::JsonObject { public: - static constexpr char16_t docKey[] = u"doc"; + static constexpr char docKey[] = "doc"; GetCompletionParams(const LanguageServerProtocol::TextDocumentIdentifier &document, int version, @@ -95,7 +95,7 @@ public: class GetCompletionResponse : public LanguageServerProtocol::JsonObject { - static constexpr char16_t completionKey[] = u"completions"; + static constexpr char completionKey[] = "completions"; public: using JsonObject::JsonObject; diff --git a/src/plugins/copilot/requests/seteditorinfo.h b/src/plugins/copilot/requests/seteditorinfo.h new file mode 100644 index 00000000000..321a2e43755 --- /dev/null +++ b/src/plugins/copilot/requests/seteditorinfo.h @@ -0,0 +1,126 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "checkstatus.h" + +#include <languageserverprotocol/jsonrpcmessages.h> +#include <languageserverprotocol/lsptypes.h> + +namespace Copilot { + +class EditorPluginInfo : public LanguageServerProtocol::JsonObject +{ + static constexpr char version[] = "version"; + static constexpr char name[] = "name"; + +public: + using JsonObject::JsonObject; + + EditorPluginInfo(const QString &version, const QString &name) + { + setEditorVersion(version); + setEditorName(name); + } + + void setEditorVersion(const QString &v) { insert(version, v); } + void setEditorName(const QString &n) { insert(name, n); } +}; + +class EditorInfo : public LanguageServerProtocol::JsonObject +{ + static constexpr char version[] = "version"; + static constexpr char name[] = "name"; + +public: + using JsonObject::JsonObject; + + EditorInfo(const QString &version, const QString &name) + { + setEditorVersion(version); + setEditorName(name); + } + + void setEditorVersion(const QString &v) { insert(version, v); } + void setEditorName(const QString &n) { insert(name, n); } +}; + +class NetworkProxy : public LanguageServerProtocol::JsonObject +{ + static constexpr char host[] = "host"; + static constexpr char port[] = "port"; + static constexpr char user[] = "username"; + static constexpr char password[] = "password"; + static constexpr char rejectUnauthorized[] = "rejectUnauthorized"; + +public: + using JsonObject::JsonObject; + + NetworkProxy(const QString &host, + int port, + const QString &user, + const QString &password, + bool rejectUnauthorized) + { + setHost(host); + setPort(port); + setUser(user); + setPassword(password); + setRejectUnauthorized(rejectUnauthorized); + } + + void insertIfNotEmpty(const std::string_view key, const QString &value) + { + if (!value.isEmpty()) + insert(key, value); + } + + void setHost(const QString &h) { insert(host, h); } + void setPort(int p) { insert(port, p); } + void setUser(const QString &u) { insertIfNotEmpty(user, u); } + void setPassword(const QString &p) { insertIfNotEmpty(password, p); } + void setRejectUnauthorized(bool r) { insert(rejectUnauthorized, r); } +}; + +class SetEditorInfoParams : public LanguageServerProtocol::JsonObject +{ + static constexpr char editorInfo[] = "editorInfo"; + static constexpr char editorPluginInfo[] = "editorPluginInfo"; + static constexpr char networkProxy[] = "networkProxy"; + +public: + using JsonObject::JsonObject; + + SetEditorInfoParams(const EditorInfo &editorInfo, const EditorPluginInfo &editorPluginInfo) + { + setEditorInfo(editorInfo); + setEditorPluginInfo(editorPluginInfo); + } + + SetEditorInfoParams(const EditorInfo &editorInfo, + const EditorPluginInfo &editorPluginInfo, + const NetworkProxy &networkProxy) + { + setEditorInfo(editorInfo); + setEditorPluginInfo(editorPluginInfo); + setNetworkProxy(networkProxy); + } + + void setEditorInfo(const EditorInfo &info) { insert(editorInfo, info); } + void setEditorPluginInfo(const EditorPluginInfo &info) { insert(editorPluginInfo, info); } + void setNetworkProxy(const NetworkProxy &proxy) { insert(networkProxy, proxy); } +}; + +class SetEditorInfoRequest + : public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SetEditorInfoParams> +{ +public: + explicit SetEditorInfoRequest(const SetEditorInfoParams ¶ms) + : Request(methodName, params) + {} + using Request::Request; + constexpr static const char methodName[] = "setEditorInfo"; +}; + +} // namespace Copilot diff --git a/src/plugins/copilot/requests/signinconfirm.h b/src/plugins/copilot/requests/signinconfirm.h index 64f4ce7d53d..39ab1ce58cb 100644 --- a/src/plugins/copilot/requests/signinconfirm.h +++ b/src/plugins/copilot/requests/signinconfirm.h @@ -12,7 +12,7 @@ namespace Copilot { class SignInConfirmParams : public LanguageServerProtocol::JsonObject { - static constexpr char16_t userCodeKey[] = u"userCode"; + static constexpr char userCodeKey[] = "userCode"; public: using JsonObject::JsonObject; diff --git a/src/plugins/copilot/requests/signininitiate.h b/src/plugins/copilot/requests/signininitiate.h index 005205e6e01..94084d85bc3 100644 --- a/src/plugins/copilot/requests/signininitiate.h +++ b/src/plugins/copilot/requests/signininitiate.h @@ -17,8 +17,8 @@ using SignInInitiateParams = LanguageServerProtocol::JsonObject; class SignInInitiateResponse : public LanguageServerProtocol::JsonObject { - static constexpr char16_t verificationUriKey[] = u"verificationUri"; - static constexpr char16_t userCodeKey[] = u"userCode"; + static constexpr char verificationUriKey[] = "verificationUri"; + static constexpr char userCodeKey[] = "userCode"; public: using JsonObject::JsonObject; diff --git a/src/plugins/copilot/tests/proxy/Dockerfile b/src/plugins/copilot/tests/proxy/Dockerfile new file mode 100644 index 00000000000..ee053080402 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/Dockerfile @@ -0,0 +1,20 @@ +ARG PWDMODE=with + + +FROM ubuntu:20.04 AS base +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Etc/UTC +RUN apt-get update && apt-get install -y squid apache2-utils && rm -rf /var/lib/apt/lists/* +COPY run.sh / +RUN chmod +x /run.sh + + +FROM base as image-with-pwd +RUN echo 1234 | htpasswd -i -c /etc/squid/pswds user +COPY userauth.conf /etc/squid/conf.d/ + +FROM base as image-without-pwd +COPY noauth.conf /etc/squid/conf.d/ + +FROM image-${PWDMODE}-pwd AS final +CMD [ "/run.sh" ] diff --git a/src/plugins/copilot/tests/proxy/buildandrun.sh b/src/plugins/copilot/tests/proxy/buildandrun.sh new file mode 100755 index 00000000000..1bffd699bc5 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/buildandrun.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker build --build-arg PWDMODE=with -t copilot-proxy-test . && \ +docker run --rm -it -p 3128:3128 copilot-proxy-test diff --git a/src/plugins/copilot/tests/proxy/noauth.conf b/src/plugins/copilot/tests/proxy/noauth.conf new file mode 100644 index 00000000000..04c7e12eecf --- /dev/null +++ b/src/plugins/copilot/tests/proxy/noauth.conf @@ -0,0 +1 @@ +http_access allow all diff --git a/src/plugins/copilot/tests/proxy/run.sh b/src/plugins/copilot/tests/proxy/run.sh new file mode 100755 index 00000000000..2a0d77d481c --- /dev/null +++ b/src/plugins/copilot/tests/proxy/run.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +touch /var/log/squid/access.log +chmod 640 /var/log/squid/access.log +chown proxy:proxy /var/log/squid/access.log +tail -f /var/log/squid/access.log & +exec squid --foreground diff --git a/src/plugins/copilot/tests/proxy/userauth.conf b/src/plugins/copilot/tests/proxy/userauth.conf new file mode 100644 index 00000000000..1a344e04657 --- /dev/null +++ b/src/plugins/copilot/tests/proxy/userauth.conf @@ -0,0 +1,4 @@ +auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/pswds +auth_param basic realm proxy +acl authenticated proxy_auth REQUIRED +http_access allow authenticated diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index b262d0d7683..6079989c718 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -1,13 +1,7 @@ -if (NOT IS_ABSOLUTE ${IDE_LOGO_PATH}) - set(IDE_LOGO_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${IDE_LOGO_PATH}") -endif() -configure_file(core_logo.qrc.cmakein core_logo_cmake.qrc) - add_qtc_plugin(Core - DEPENDS Qt::PrintSupport Qt::Qml Qt::Sql Qt::Gui Qt::GuiPrivate - PUBLIC_DEPENDS Aggregation ExtensionSystem Utils app_version + DEPENDS Qt::PrintSupport Qt::Qml Qt::Sql Qt::Gui Qt::GuiPrivate TerminalLib + PUBLIC_DEPENDS Aggregation ExtensionSystem Utils SOURCES - ${CMAKE_CURRENT_BINARY_DIR}/core_logo_cmake.qrc actionmanager/actioncontainer.cpp actionmanager/actioncontainer.h actionmanager/actioncontainer_p.h @@ -92,8 +86,6 @@ add_qtc_plugin(Core editormanager/ieditorfactory.cpp editormanager/ieditorfactory.h editormanager/ieditorfactory_p.h - editormanager/iexternaleditor.cpp - editormanager/iexternaleditor.h editormanager/openeditorsview.cpp editormanager/openeditorsview.h editormanager/openeditorswindow.cpp @@ -217,12 +209,8 @@ add_qtc_plugin(Core locator/spotlightlocatorfilter.h locator/urllocatorfilter.cpp locator/urllocatorfilter.h - loggingmanager.cpp - loggingmanager.h loggingviewer.cpp loggingviewer.h - mainwindow.cpp - mainwindow.h manhattanstyle.cpp manhattanstyle.h messagebox.cpp @@ -274,7 +262,6 @@ add_qtc_plugin(Core rightpane.h session.cpp session.h - session_p.h sessiondialog.cpp sessiondialog.h sessionmodel.cpp @@ -291,6 +278,8 @@ add_qtc_plugin(Core statusbarmanager.h systemsettings.cpp systemsettings.h + terminal/searchableterminal.cpp + terminal/searchableterminal.h textdocument.cpp textdocument.h themechooser.cpp @@ -343,16 +332,6 @@ extend_qtc_plugin(Core DEFINES ENABLE_CRASHPAD ) -if ((NOT WIN32) AND (NOT APPLE)) - # install logo - foreach(size 16 24 32 48 64 128 256 512) - install( - FILES ${IDE_LOGO_PATH}/images/logo/${size}/QtProject-qtcreator.png - DESTINATION share/icons/hicolor/${size}x${size}/apps - ) - endforeach() -endif() - set(FONTS_BASE "${QtCreator_SOURCE_DIR}/src/share/3rdparty/studiofonts/") qt_add_resources(Core CoreWelcomeScreenFonts_rcc diff --git a/src/plugins/coreplugin/Core.json.in b/src/plugins/coreplugin/Core.json.in index 606085c91d8..8f121d61a3f 100644 --- a/src/plugins/coreplugin/Core.json.in +++ b/src/plugins/coreplugin/Core.json.in @@ -1,44 +1,44 @@ { - \"Name\" : \"Core\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Required\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Core", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Required" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Core\", - \"Description\" : \"The core plugin for the Qt IDE.\", - \"Url\" : \"http://www.qt.io\", - \"Arguments\" : [ + "Category" : "Core", + "Description" : "The core plugin for the Qt IDE.", + "Url" : "http://www.qt.io", + "Arguments" : [ { - \"Name\" : \"-color\", - \"Parameter\" : \"color\", - \"Description\" : \"Override selected UI color\" + "Name" : "-color", + "Parameter" : "color", + "Description" : "Override selected UI color" }, { - \"Name\" : \"-theme\", - \"Parameter\" : \"default|dark\", - \"Description\" : \"Choose a built-in theme or pass a .creatortheme file\" + "Name" : "-theme", + "Parameter" : "default|dark", + "Description" : "Choose a built-in theme or pass a .creatortheme file" }, { - \"Name\" : \"-presentationMode\", - \"Description\" : \"Enable presentation mode with pop-ups for key combos\" + "Name" : "-presentationMode", + "Description" : "Enable presentation mode with pop-ups for key combos" }, { - \"Name\" : \"-lastsession\", - \"Description\" : \"Restore the last session\" + "Name" : "-lastsession", + "Description" : "Restore the last session" }, { - \"Name\" : \"<session>\", - \"Description\" : \"Restore a saved session\" + "Name" : "<session>", + "Description" : "Restore a saved session" } ], - $$dependencyList + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/coreplugin/actionmanager/actioncontainer.cpp b/src/plugins/coreplugin/actionmanager/actioncontainer.cpp index c49b2e623fb..914a750e09a 100644 --- a/src/plugins/coreplugin/actionmanager/actioncontainer.cpp +++ b/src/plugins/coreplugin/actionmanager/actioncontainer.cpp @@ -4,8 +4,8 @@ #include "actioncontainer_p.h" #include "actionmanager.h" -#include <coreplugin/coreconstants.h> -#include <coreplugin/icontext.h> +#include "../coreconstants.h" +#include "../icontext.h" #include <utils/qtcassert.h> @@ -172,8 +172,11 @@ namespace Internal { \internal */ -ActionContainerPrivate::ActionContainerPrivate(Id id) - : m_onAllDisabledBehavior(Disable), m_id(id), m_updateRequested(false) +ActionContainerPrivate::ActionContainerPrivate(Id id, ActionManagerPrivate *actionManagerPrivate) + : m_onAllDisabledBehavior(Disable) + , m_id(id) + , m_actionManagerPrivate(actionManagerPrivate) + , m_updateRequested(false) { appendGroup(Constants::G_DEFAULT_ONE); appendGroup(Constants::G_DEFAULT_TWO); @@ -382,16 +385,7 @@ bool ActionContainerPrivate::canAddAction(Command *action) void ActionContainerPrivate::scheduleUpdate() { - if (m_updateRequested) - return; - m_updateRequested = true; - QMetaObject::invokeMethod(this, &ActionContainerPrivate::update, Qt::QueuedConnection); -} - -void ActionContainerPrivate::update() -{ - updateInternal(); - m_updateRequested = false; + m_actionManagerPrivate->scheduleContainerUpdate(this); } // ---------- MenuActionContainer ------------ @@ -401,9 +395,9 @@ void ActionContainerPrivate::update() \internal */ -MenuActionContainer::MenuActionContainer(Id id) - : ActionContainerPrivate(id), - m_menu(new QMenu) +MenuActionContainer::MenuActionContainer(Id id, ActionManagerPrivate *actionManagerPrivate) + : ActionContainerPrivate(id, actionManagerPrivate) + , m_menu(new QMenu) { m_menu->setObjectName(id.toString()); m_menu->menuAction()->setMenuRole(QAction::NoRole); @@ -450,7 +444,7 @@ void MenuActionContainer::removeMenu(ActionContainer *container) m_menu->removeAction(menu->menuAction()); } -bool MenuActionContainer::updateInternal() +bool MenuActionContainer::update() { if (onAllDisabledBehavior() == Show) return true; @@ -470,7 +464,7 @@ bool MenuActionContainer::updateInternal() qWarning("%s", warning.constData()); continue; } - if (container->updateInternal()) { + if (container->update()) { hasitems = true; break; } @@ -520,8 +514,9 @@ bool MenuActionContainer::canBeAddedToContainer(ActionContainerPrivate *containe \internal */ -MenuBarActionContainer::MenuBarActionContainer(Id id) - : ActionContainerPrivate(id), m_menuBar(nullptr) +MenuBarActionContainer::MenuBarActionContainer(Id id, ActionManagerPrivate *actionManagerPrivate) + : ActionContainerPrivate(id, actionManagerPrivate) + , m_menuBar(nullptr) { setOnAllDisabledBehavior(Show); } @@ -566,7 +561,7 @@ void MenuBarActionContainer::removeMenu(ActionContainer *container) m_menuBar->removeAction(menu->menuAction()); } -bool MenuBarActionContainer::updateInternal() +bool MenuBarActionContainer::update() { if (onAllDisabledBehavior() == Show) return true; @@ -597,9 +592,12 @@ bool MenuBarActionContainer::canBeAddedToContainer(ActionContainerPrivate *) con const char ID_PREFIX[] = "io.qt.qtcreator."; -TouchBarActionContainer::TouchBarActionContainer(Id id, const QIcon &icon, const QString &text) - : ActionContainerPrivate(id), - m_touchBar(std::make_unique<TouchBar>(id.withPrefix(ID_PREFIX).name(), icon, text)) +TouchBarActionContainer::TouchBarActionContainer(Id id, + ActionManagerPrivate *actionManagerPrivate, + const QIcon &icon, + const QString &text) + : ActionContainerPrivate(id, actionManagerPrivate) + , m_touchBar(std::make_unique<TouchBar>(id.withPrefix(ID_PREFIX).name(), icon, text)) { } @@ -653,7 +651,7 @@ bool TouchBarActionContainer::canBeAddedToContainer(ActionContainerPrivate *cont return qobject_cast<TouchBarActionContainer *>(container); } -bool TouchBarActionContainer::updateInternal() +bool TouchBarActionContainer::update() { return false; } diff --git a/src/plugins/coreplugin/actionmanager/actioncontainer_p.h b/src/plugins/coreplugin/actionmanager/actioncontainer_p.h index aa6eb7aa34a..cb94852aa0c 100644 --- a/src/plugins/coreplugin/actionmanager/actioncontainer_p.h +++ b/src/plugins/coreplugin/actionmanager/actioncontainer_p.h @@ -5,8 +5,8 @@ #include "actionmanager_p.h" -#include <coreplugin/actionmanager/actioncontainer.h> -#include <coreplugin/actionmanager/command.h> +#include "actioncontainer.h" +#include "command.h" #include <utils/touchbar/touchbar.h> @@ -25,7 +25,7 @@ class ActionContainerPrivate : public ActionContainer Q_OBJECT public: - ActionContainerPrivate(Utils::Id id); + ActionContainerPrivate(Utils::Id id, ActionManagerPrivate *actionManagerPrivate); ~ActionContainerPrivate() override = default; void setOnAllDisabledBehavior(OnAllDisabledBehavior behavior) final; @@ -55,7 +55,7 @@ public: virtual void removeAction(Command *command) = 0; virtual void removeMenu(ActionContainer *container) = 0; - virtual bool updateInternal() = 0; + virtual bool update() = 0; protected: static bool canAddAction(Command *action); @@ -67,7 +67,6 @@ protected: private: void scheduleUpdate(); - void update(); void itemDestroyed(QObject *sender); QList<Group>::const_iterator findGroup(Utils::Id groupId) const; @@ -75,6 +74,7 @@ private: OnAllDisabledBehavior m_onAllDisabledBehavior; Utils::Id m_id; + ActionManagerPrivate *m_actionManagerPrivate = nullptr; bool m_updateRequested; }; @@ -83,7 +83,7 @@ class MenuActionContainer : public ActionContainerPrivate Q_OBJECT public: - explicit MenuActionContainer(Utils::Id id); + explicit MenuActionContainer(Utils::Id id, ActionManagerPrivate *actionManagerPrivate); ~MenuActionContainer() override; QMenu *menu() const override; @@ -98,7 +98,7 @@ public: protected: bool canBeAddedToContainer(ActionContainerPrivate *container) const override; - bool updateInternal() override; + bool update() override; private: QPointer<QMenu> m_menu; @@ -109,7 +109,7 @@ class MenuBarActionContainer : public ActionContainerPrivate Q_OBJECT public: - explicit MenuBarActionContainer(Utils::Id id); + explicit MenuBarActionContainer(Utils::Id id, ActionManagerPrivate *actionManagerPrivate); void setMenuBar(QMenuBar *menuBar); QMenuBar *menuBar() const override; @@ -124,7 +124,7 @@ public: protected: bool canBeAddedToContainer(ActionContainerPrivate *container) const override; - bool updateInternal() override; + bool update() override; private: QMenuBar *m_menuBar; @@ -135,7 +135,10 @@ class TouchBarActionContainer : public ActionContainerPrivate Q_OBJECT public: - TouchBarActionContainer(Utils::Id id, const QIcon &icon, const QString &text); + TouchBarActionContainer(Utils::Id id, + ActionManagerPrivate *actionManagerPrivate, + const QIcon &icon, + const QString &text); ~TouchBarActionContainer() override; Utils::TouchBar *touchBar() const override; @@ -150,7 +153,7 @@ public: void removeMenu(ActionContainer *container) override; bool canBeAddedToContainer(ActionContainerPrivate *container) const override; - bool updateInternal() override; + bool update() override; private: std::unique_ptr<Utils::TouchBar> m_touchBar; diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.cpp b/src/plugins/coreplugin/actionmanager/actionmanager.cpp index 54e58dc217a..d58fe42cec0 100644 --- a/src/plugins/coreplugin/actionmanager/actionmanager.cpp +++ b/src/plugins/coreplugin/actionmanager/actionmanager.cpp @@ -5,20 +5,20 @@ #include "actionmanager_p.h" #include "actioncontainer_p.h" #include "command_p.h" - -#include <coreplugin/icore.h> +#include "../icore.h" #include <utils/algorithm.h> #include <utils/fadingindicator.h> #include <utils/qtcassert.h> +#include <nanotrace/nanotrace.h> + #include <QAction> #include <QApplication> #include <QDebug> #include <QMainWindow> #include <QMenu> #include <QMenuBar> -#include <QSettings> namespace { enum { warnAboutFindFailures = 0 }; @@ -188,7 +188,7 @@ ActionContainer *ActionManager::createMenu(Id id) if (it != d->m_idContainerMap.constEnd()) return it.value(); - auto mc = new MenuActionContainer(id); + auto mc = new MenuActionContainer(id, d); d->m_idContainerMap.insert(id, mc); connect(mc, &QObject::destroyed, d, &ActionManagerPrivate::containerDestroyed); @@ -213,7 +213,7 @@ ActionContainer *ActionManager::createMenuBar(Id id) auto mb = new QMenuBar; // No parent (System menu bar on macOS) mb->setObjectName(id.toString()); - auto mbc = new MenuBarActionContainer(id); + auto mbc = new MenuBarActionContainer(id, d); mbc->setMenuBar(mb); d->m_idContainerMap.insert(id, mbc); @@ -241,7 +241,7 @@ ActionContainer *ActionManager::createTouchBar(Id id, const QIcon &icon, const Q ActionContainer * const c = d->m_idContainerMap.value(id); if (c) return c; - auto ac = new TouchBarActionContainer(id, icon, text); + auto ac = new TouchBarActionContainer(id, d, icon, text); d->m_idContainerMap.insert(id, ac); connect(ac, &QObject::destroyed, d, &ActionManagerPrivate::containerDestroyed); return ac; @@ -449,6 +449,7 @@ void ActionManagerPrivate::containerDestroyed(QObject *sender) { auto container = static_cast<ActionContainerPrivate *>(sender); m_idContainerMap.remove(m_idContainerMap.key(container)); + m_scheduledContainerUpdates.remove(container); } Command *ActionManagerPrivate::overridableAction(Id id) @@ -473,10 +474,10 @@ Command *ActionManagerPrivate::overridableAction(Id id) void ActionManagerPrivate::readUserSettings(Id id, Command *cmd) { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(kKeyboardSettingsKeyV2); - if (settings->contains(id.toString())) { - const QVariant v = settings->value(id.toString()); + if (settings->contains(id.toKey())) { + const QVariant v = settings->value(id.toKey()); if (QMetaType::Type(v.type()) == QMetaType::QStringList) { cmd->setKeySequences(Utils::transform<QList>(v.toStringList(), [](const QString &s) { return QKeySequence::fromString(s); @@ -488,10 +489,27 @@ void ActionManagerPrivate::readUserSettings(Id id, Command *cmd) settings->endGroup(); } +void ActionManagerPrivate::scheduleContainerUpdate(ActionContainerPrivate *actionContainer) +{ + const bool needsSchedule = m_scheduledContainerUpdates.isEmpty(); + m_scheduledContainerUpdates.insert(actionContainer); + if (needsSchedule) + QMetaObject::invokeMethod(this, + &ActionManagerPrivate::updateContainer, + Qt::QueuedConnection); +} + +void ActionManagerPrivate::updateContainer() +{ + for (ActionContainerPrivate *c : std::as_const(m_scheduledContainerUpdates)) + c->update(); + m_scheduledContainerUpdates.clear(); +} + void ActionManagerPrivate::saveSettings(Command *cmd) { - const QString id = cmd->id().toString(); - const QString settingsKey = QLatin1String(kKeyboardSettingsKeyV2) + '/' + id; + const Key id = cmd->id().toKey(); + const Key settingsKey = Key(kKeyboardSettingsKeyV2) + '/' + id; const QList<QKeySequence> keys = cmd->keySequences(); const QList<QKeySequence> defaultKeys = cmd->defaultKeySequences(); if (keys != defaultKeys) { diff --git a/src/plugins/coreplugin/actionmanager/actionmanager.h b/src/plugins/coreplugin/actionmanager/actionmanager.h index c85c31a8d86..a0b522610f8 100644 --- a/src/plugins/coreplugin/actionmanager/actionmanager.h +++ b/src/plugins/coreplugin/actionmanager/actionmanager.h @@ -21,9 +21,11 @@ namespace Core { class ActionContainer; class Command; class Context; +class ICore; namespace Internal { class CorePlugin; +class ICorePrivate; class MainWindow; } // Internal @@ -66,7 +68,8 @@ private: static void setContext(const Context &context); friend class Core::Internal::CorePlugin; // initialization - friend class Core::Internal::MainWindow; // saving settings and setting context + friend class Core::ICore; // saving settings and setting context + friend class Core::Internal::ICorePrivate; // saving settings and setting context }; } // namespace Core diff --git a/src/plugins/coreplugin/actionmanager/actionmanager_p.h b/src/plugins/coreplugin/actionmanager/actionmanager_p.h index 47ad23f9106..c10f109a6ea 100644 --- a/src/plugins/coreplugin/actionmanager/actionmanager_p.h +++ b/src/plugins/coreplugin/actionmanager/actionmanager_p.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/icontext.h> +#include "../icontext.h" #include <QMap> #include <QHash> @@ -43,11 +43,14 @@ public: static void readUserSettings(Utils::Id id, Command *cmd); + void scheduleContainerUpdate(ActionContainerPrivate *actionContainer); + void updateContainer(); void containerDestroyed(QObject *sender); IdCmdMap m_idCmdMap; IdContainerMap m_idContainerMap; + QSet<ActionContainerPrivate *> m_scheduledContainerUpdates; Context m_context; diff --git a/src/plugins/coreplugin/actionmanager/command.cpp b/src/plugins/coreplugin/actionmanager/command.cpp index 23b398791f2..fdb1bdc4016 100644 --- a/src/plugins/coreplugin/actionmanager/command.cpp +++ b/src/plugins/coreplugin/actionmanager/command.cpp @@ -4,8 +4,8 @@ #include "command.h" #include "command_p.h" -#include <coreplugin/coreconstants.h> -#include <coreplugin/icontext.h> +#include "../coreconstants.h" +#include "../icontext.h" #include <utils/hostosinfo.h> #include <utils/stringutils.h> diff --git a/src/plugins/coreplugin/actionmanager/command.h b/src/plugins/coreplugin/actionmanager/command.h index 73dd92a5eb8..206acafaefc 100644 --- a/src/plugins/coreplugin/actionmanager/command.h +++ b/src/plugins/coreplugin/actionmanager/command.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/hostosinfo.h> #include <utils/id.h> diff --git a/src/plugins/coreplugin/actionmanager/command_p.h b/src/plugins/coreplugin/actionmanager/command_p.h index 04cbc44f13e..d90b807c553 100644 --- a/src/plugins/coreplugin/actionmanager/command_p.h +++ b/src/plugins/coreplugin/actionmanager/command_p.h @@ -5,7 +5,7 @@ #include "command.h" -#include <coreplugin/icontext.h> +#include "../icontext.h" #include <utils/id.h> #include <utils/proxyaction.h> diff --git a/src/plugins/coreplugin/actionmanager/commandbutton.h b/src/plugins/coreplugin/actionmanager/commandbutton.h index c5b6d71852c..db46225b08f 100644 --- a/src/plugins/coreplugin/actionmanager/commandbutton.h +++ b/src/plugins/coreplugin/actionmanager/commandbutton.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.cpp b/src/plugins/coreplugin/actionmanager/commandmappings.cpp index 15765698cbd..276004bdb78 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.cpp +++ b/src/plugins/coreplugin/actionmanager/commandmappings.cpp @@ -3,8 +3,8 @@ #include "commandmappings.h" -#include <coreplugin/coreplugintr.h> -#include <coreplugin/dialogs/shortcutsettings.h> +#include "../coreplugintr.h" +#include "../dialogs/shortcutsettings.h" #include <utils/fancylineedit.h> #include <utils/headerviewstretcher.h> diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.h b/src/plugins/coreplugin/actionmanager/commandmappings.h index a6725265500..d72adf00f32 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.h +++ b/src/plugins/coreplugin/actionmanager/commandmappings.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <QWidget> diff --git a/src/plugins/coreplugin/actionmanager/commandsfile.cpp b/src/plugins/coreplugin/actionmanager/commandsfile.cpp index f0a1db98608..18866662ba2 100644 --- a/src/plugins/coreplugin/actionmanager/commandsfile.cpp +++ b/src/plugins/coreplugin/actionmanager/commandsfile.cpp @@ -3,10 +3,9 @@ #include "commandsfile.h" #include "command.h" -#include <coreplugin/dialogs/shortcutsettings.h> -#include <coreplugin/icore.h> +#include "../dialogs/shortcutsettings.h" +#include "../icore.h" -#include <app/app_version.h> #include <utils/qtcassert.h> #include <utils/fileutils.h> diff --git a/src/plugins/coreplugin/actionsfilter.h b/src/plugins/coreplugin/actionsfilter.h index 692dea95d26..25e788abaa5 100644 --- a/src/plugins/coreplugin/actionsfilter.h +++ b/src/plugins/coreplugin/actionsfilter.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/locator/ilocatorfilter.h> +#include "locator/ilocatorfilter.h" #include <QAction> #include <QPointer> diff --git a/src/plugins/coreplugin/basefilewizardfactory.cpp b/src/plugins/coreplugin/basefilewizardfactory.cpp index 3f7d4c771fb..0e4652b7e53 100644 --- a/src/plugins/coreplugin/basefilewizardfactory.cpp +++ b/src/plugins/coreplugin/basefilewizardfactory.cpp @@ -165,12 +165,16 @@ bool BaseFileWizardFactory::postGenerateOpenEditors(const GeneratedFiles &l, QSt { for (const GeneratedFile &file : std::as_const(l)) { if (file.attributes() & GeneratedFile::OpenEditorAttribute) { - if (!EditorManager::openEditor(file.filePath(), file.editorId())) { - if (errorMessage) - *errorMessage = Tr::tr("Failed to open an editor for \"%1\"."). - arg(file.filePath().toUserOutput()); + IEditor * const editor = EditorManager::openEditor(file.filePath(), file.editorId()); + if (!editor) { + if (errorMessage) { + *errorMessage = Tr::tr("Failed to open an editor for \"%1\".") + .arg(file.filePath().toUserOutput()); + } return false; } + editor->document()->formatContents(); + editor->document()->save(nullptr); } } return true; diff --git a/src/plugins/coreplugin/basefilewizardfactory.h b/src/plugins/coreplugin/basefilewizardfactory.h index e6b1ed172a6..0d9033d6d59 100644 --- a/src/plugins/coreplugin/basefilewizardfactory.h +++ b/src/plugins/coreplugin/basefilewizardfactory.h @@ -5,8 +5,7 @@ #include "core_global.h" #include "generatedfile.h" - -#include <coreplugin/iwizardfactory.h> +#include "iwizardfactory.h" #include <utils/filepath.h> diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h index 2606628d266..a259717ba89 100644 --- a/src/plugins/coreplugin/coreconstants.h +++ b/src/plugins/coreplugin/coreconstants.h @@ -86,6 +86,7 @@ const char OPTIONS[] = "QtCreator.Options"; const char LOGGER[] = "QtCreator.Logger"; const char TOGGLE_LEFT_SIDEBAR[] = "QtCreator.ToggleLeftSidebar"; const char TOGGLE_RIGHT_SIDEBAR[] = "QtCreator.ToggleRightSidebar"; +const char TOGGLE_MENUBAR[] = "QtCreator.ToggleMenubar"; const char CYCLE_MODE_SELECTOR_STYLE[] = "QtCreator.CycleModeSelectorStyle"; const char TOGGLE_FULLSCREEN[] = "QtCreator.ToggleFullScreen"; diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp index 52438f2d4af..cbbb71246ea 100644 --- a/src/plugins/coreplugin/corejsextensions.cpp +++ b/src/plugins/coreplugin/corejsextensions.cpp @@ -3,8 +3,7 @@ #include "corejsextensions.h" -#include <app/app_version.h> - +#include <utils/appinfo.h> #include <utils/fileutils.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> @@ -27,7 +26,7 @@ QString UtilsJsExtension::qtVersion() const QString UtilsJsExtension::qtCreatorVersion() const { - return QLatin1String(Constants::IDE_VERSION_DISPLAY); + return appInfo().displayVersion; } QString UtilsJsExtension::toNativeSeparators(const QString &in) const diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 2d825dd3ae5..bfd0669c338 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -9,24 +9,23 @@ #include "icore.h" #include "idocument.h" #include "iwizardfactory.h" -#include "mainwindow.h" +#include "loggingviewer.h" #include "modemanager.h" #include "session.h" +#include "settingsdatabase.h" #include "themechooser.h" -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/documentmanager.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/find/findplugin.h> -#include <coreplugin/find/searchresultwindow.h> -#include <coreplugin/locator/locator.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/fileutils.h> +#include "actionmanager/actionmanager.h" +#include "coreconstants.h" +#include "documentmanager.h" +#include "fileutils.h" +#include "find/findplugin.h" +#include "locator/locator.h" -#include <app/app_version.h> #include <extensionsystem/pluginerroroverview.h> #include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginspec.h> + #include <utils/algorithm.h> #include <utils/checkablemessagebox.h> #include <utils/commandline.h> @@ -35,6 +34,7 @@ #include <utils/mimeutils.h> #include <utils/pathchooser.h> #include <utils/savefile.h> +#include <utils/store.h> #include <utils/stringutils.h> #include <utils/textutils.h> #include <utils/theme/theme.h> @@ -43,11 +43,11 @@ #include <QDateTime> #include <QDebug> #include <QDir> +#include <QGuiApplication> #include <QJsonObject> #include <QLabel> #include <QMenu> #include <QMessageBox> -#include <QSettings> #include <QUuid> #include <cstdlib> @@ -76,6 +76,10 @@ CorePlugin::CorePlugin() qRegisterMetaType<Utils::CommandLine>(); qRegisterMetaType<Utils::FilePath>(); qRegisterMetaType<Utils::Environment>(); + qRegisterMetaType<Utils::Store>(); + qRegisterMetaType<Utils::Key>(); + qRegisterMetaType<Utils::KeyList>(); + qRegisterMetaType<Utils::OldStore>(); m_instance = this; setupSystemEnvironment(); } @@ -91,7 +95,8 @@ CorePlugin::~CorePlugin() DesignMode::destroyModeIfRequired(); - delete m_mainWindow; + delete m_core; + SettingsDatabase::destroy(); setCreatorTheme(nullptr); } @@ -152,12 +157,12 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) CheckableMessageBox::initialize(ICore::settings()); new ActionManager(this); ActionManager::setPresentationModeEnabled(args.presentationMode); - m_mainWindow = new MainWindow; + m_core = new ICore; if (args.overrideColor.isValid()) - m_mainWindow->setOverrideColor(args.overrideColor); + ICore::setOverrideColor(args.overrideColor); m_locator = new Locator; std::srand(unsigned(QDateTime::currentDateTime().toSecsSinceEpoch())); - m_mainWindow->init(); + ICore::init(); m_editMode = new EditMode; ModeManager::activateMode(m_editMode->id()); m_folderNavigationWidgetFactory = new FolderNavigationWidgetFactory; @@ -191,18 +196,25 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) expander->registerVariable("Config:LastFileDialogDirectory", Tr::tr("The directory last visited in a file dialog."), [] { return DocumentManager::fileDialogLastVisitedDirectory().toString(); }); expander->registerVariable("HostOs:isWindows", - Tr::tr("Is %1 running on Windows?").arg(Constants::IDE_DISPLAY_NAME), - [] { return QVariant(Utils::HostOsInfo::isWindowsHost()).toString(); }); + Tr::tr("Is %1 running on Windows?") + .arg(QGuiApplication::applicationDisplayName()), + [] { + return QVariant(Utils::HostOsInfo::isWindowsHost()).toString(); + }); expander->registerVariable("HostOs:isOSX", - Tr::tr("Is %1 running on OS X?").arg(Constants::IDE_DISPLAY_NAME), + Tr::tr("Is %1 running on OS X?") + .arg(QGuiApplication::applicationDisplayName()), [] { return QVariant(Utils::HostOsInfo::isMacHost()).toString(); }); expander->registerVariable("HostOs:isLinux", - Tr::tr("Is %1 running on Linux?").arg(Constants::IDE_DISPLAY_NAME), + Tr::tr("Is %1 running on Linux?") + .arg(QGuiApplication::applicationDisplayName()), [] { return QVariant(Utils::HostOsInfo::isLinuxHost()).toString(); }); expander->registerVariable("HostOs:isUnix", Tr::tr("Is %1 running on any unix-based platform?") - .arg(Constants::IDE_DISPLAY_NAME), - [] { return QVariant(Utils::HostOsInfo::isAnyUnixHost()).toString(); }); + .arg(QGuiApplication::applicationDisplayName()), + [] { + return QVariant(Utils::HostOsInfo::isAnyUnixHost()).toString(); + }); expander->registerVariable("HostOs:PathListSeparator", Tr::tr("The path list separator for the platform."), [] { return QString(Utils::HostOsInfo::pathListSeparator()); }); @@ -211,7 +223,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) [] { return QString(Utils::HostOsInfo::withExecutableSuffix("")); }); expander->registerVariable("IDE:ResourcePath", Tr::tr("The directory where %1 finds its pre-installed resources.") - .arg(Constants::IDE_DISPLAY_NAME), + .arg(QGuiApplication::applicationDisplayName()), [] { return ICore::resourcePath().toString(); }); expander->registerPrefix("CurrentDate:", Tr::tr("The current date (QDate formatstring)."), [](const QString &fmt) { return QDate::currentDate().toString(fmt); }); @@ -278,9 +290,9 @@ void CorePlugin::extensionsInitialized() DesignMode::createModeIfRequired(); Find::extensionsInitialized(); m_locator->extensionsInitialized(); - m_mainWindow->extensionsInitialized(); + ICore::extensionsInitialized(); if (ExtensionSystem::PluginManager::hasError()) { - auto errorOverview = new ExtensionSystem::PluginErrorOverview(m_mainWindow); + auto errorOverview = new ExtensionSystem::PluginErrorOverview(ICore::mainWindow()); errorOverview->setAttribute(Qt::WA_DeleteOnClose); errorOverview->setModal(true); errorOverview->show(); @@ -307,11 +319,11 @@ QObject *CorePlugin::remoteCommand(const QStringList & /* options */, return nullptr; } const FilePaths filePaths = Utils::transform(args, FilePath::fromUserInput); - IDocument *res = MainWindow::openFiles( + IDocument *res = ICore::openFiles( filePaths, ICore::OpenFilesFlags(ICore::SwitchMode | ICore::CanContainLineAndColumnNumbers | ICore::SwitchSplitIfAlreadyVisible), FilePath::fromString(workingDirectory)); - m_mainWindow->raiseWindow(); + ICore::raiseMainWindow(); return res; } @@ -389,16 +401,16 @@ void CorePlugin::checkSettings() msgBox.exec(); }, Qt::QueuedConnection); }; - const QSettings * const userSettings = ICore::settings(); + const QtcSettings * const userSettings = ICore::settings(); QString errorDetails; switch (userSettings->status()) { case QSettings::NoError: { const QFileInfo fi(userSettings->fileName()); if (fi.exists() && !fi.isWritable()) { const QString errorMsg = Tr::tr("The settings file \"%1\" is not writable.\n" - "You will not be able to store any %2 settings.") - .arg(QDir::toNativeSeparators(userSettings->fileName()), - QLatin1String(Core::Constants::IDE_DISPLAY_NAME)); + "You will not be able to store any %2 settings.") + .arg(QDir::toNativeSeparators(userSettings->fileName()), + QGuiApplication::applicationDisplayName()); showMsgBox(errorMsg, QMessageBox::Warning); } return; @@ -410,10 +422,12 @@ void CorePlugin::checkSettings() errorDetails = Tr::tr("The file is invalid."); break; } - const QString errorMsg = Tr::tr("Error reading settings file \"%1\": %2\n" - "You will likely experience further problems using this instance of %3.") - .arg(QDir::toNativeSeparators(userSettings->fileName()), errorDetails, - QLatin1String(Core::Constants::IDE_DISPLAY_NAME)); + const QString errorMsg + = Tr::tr("Error reading settings file \"%1\": %2\n" + "You will likely experience further problems using this instance of %3.") + .arg(QDir::toNativeSeparators(userSettings->fileName()), + errorDetails, + QGuiApplication::applicationDisplayName()); showMsgBox(errorMsg, QMessageBox::Critical); } @@ -429,10 +443,11 @@ void CorePlugin::warnAboutCrashReporing() "To enable this feature go to %2."); if (Utils::HostOsInfo::isMacHost()) { - warnStr = warnStr.arg(QLatin1String(Core::Constants::IDE_DISPLAY_NAME), - Core::Constants::IDE_DISPLAY_NAME + Tr::tr(" > Preferences > Environment > System")); + warnStr = warnStr.arg(QGuiApplication::applicationDisplayName(), + QGuiApplication::applicationDisplayName() + + Tr::tr(" > Preferences > Environment > System")); } else { - warnStr = warnStr.arg(QLatin1String(Core::Constants::IDE_DISPLAY_NAME), + warnStr = warnStr.arg(QGuiApplication::applicationDisplayName(), Tr::tr("Edit > Preferences > Environment > System")); } @@ -459,21 +474,25 @@ void CorePlugin::warnAboutCrashReporing() QString CorePlugin::msgCrashpadInformation() { return Tr::tr("%1 uses Google Crashpad for collecting crashes and sending them to our backend " - "for processing. Crashpad may capture arbitrary contents from crashed process’ " - "memory, including user sensitive information, URLs, and whatever other content " - "users have trusted %1 with. The collected crash reports are however only used " - "for the sole purpose of fixing bugs.").arg(Core::Constants::IDE_DISPLAY_NAME) - + "<br><br>" + Tr::tr("More information:") - + "<br><a href='https://chromium.googlesource.com/crashpad/crashpad/+/master/doc/" - "overview_design.md'>" + Tr::tr("Crashpad Overview") + "</a>" - "<br><a href='https://sentry.io/security/'>" + Tr::tr("%1 security policy").arg("Sentry.io") - + "</a>"; + "for processing. Crashpad may capture arbitrary contents from crashed process’ " + "memory, including user sensitive information, URLs, and whatever other content " + "users have trusted %1 with. The collected crash reports are however only used " + "for the sole purpose of fixing bugs.") + .arg(QGuiApplication::applicationDisplayName()) + + "<br><br>" + Tr::tr("More information:") + + "<br><a href='https://chromium.googlesource.com/crashpad/crashpad/+/master/doc/" + "overview_design.md'>" + + Tr::tr("Crashpad Overview") + + "</a>" + "<br><a href='https://sentry.io/security/'>" + + Tr::tr("%1 security policy").arg("Sentry.io") + "</a>"; } ExtensionSystem::IPlugin::ShutdownFlag CorePlugin::aboutToShutdown() { + LoggingViewer::hideLoggingView(); Find::aboutToShutdown(); m_locator->aboutToShutdown(); - m_mainWindow->aboutToShutdown(); + ICore::aboutToShutdown(); return SynchronousShutdown; } diff --git a/src/plugins/coreplugin/coreplugin.h b/src/plugins/coreplugin/coreplugin.h index fa73f951c13..f06caa83d7d 100644 --- a/src/plugins/coreplugin/coreplugin.h +++ b/src/plugins/coreplugin/coreplugin.h @@ -23,6 +23,7 @@ namespace Core { class FolderNavigationWidgetFactory; class SessionManager; +class ICore; namespace Internal { @@ -74,7 +75,7 @@ private: void checkSettings(); void warnAboutCrashReporing(); - MainWindow *m_mainWindow = nullptr; + ICore *m_core = nullptr; EditMode *m_editMode = nullptr; Locator *m_locator = nullptr; std::unique_ptr<SessionManager> m_sessionManager; diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index 096076877f3..9bd3923a6fb 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -1,378 +1,375 @@ -import qbs 1.0 import qbs.FileInfo import qbs.Utilities -Project { +QtcPlugin { name: "Core" + Depends { + name: "Qt" + submodules: ["widgets", "xml", "network", "qml", "sql", "printsupport"] + } - QtcPlugin { - Depends { - name: "Qt" - submodules: ["widgets", "xml", "network", "qml", "sql", "printsupport"] - } + Depends { + name: "Qt.gui-private" + condition: qbs.targetOS.contains("windows") + } - Depends { - name: "Qt.gui-private" - condition: qbs.targetOS.contains("windows") - } + Depends { name: "Utils" } + Depends { name: "Aggregation" } + Depends { name: "TerminalLib" } - Depends { name: "Utils" } + cpp.dynamicLibraries: { + if (qbs.targetOS.contains("windows")) + return ["ole32", "user32"] + } + + cpp.frameworks: qbs.targetOS.contains("macos") ? ["AppKit"] : undefined + + Group { + name: "General" + files: [ + "actionsfilter.cpp", + "actionsfilter.h", + "basefilewizard.cpp", + "basefilewizard.h", + "basefilewizardfactory.cpp", + "basefilewizardfactory.h", + "core.qrc", + "core_global.h", + "coreconstants.h", + "coreicons.cpp", + "coreicons.h", + "corejsextensions.cpp", + "corejsextensions.h", + "coreplugin.cpp", + "coreplugin.h", + "coreplugintr.h", + "designmode.cpp", + "designmode.h", + "diffservice.cpp", + "diffservice.h", + "documentmanager.cpp", + "documentmanager.h", + "editmode.cpp", + "editmode.h", + "editortoolbar.cpp", + "editortoolbar.h", + "externaltool.cpp", + "externaltool.h", + "externaltoolmanager.cpp", + "externaltoolmanager.h", + "fancyactionbar.cpp", + "fancyactionbar.h", + "fancyactionbar.qrc", + "fancytabwidget.cpp", + "fancytabwidget.h", + "featureprovider.cpp", + "featureprovider.h", + "fileutils.cpp", + "fileutils.h", + "findplaceholder.cpp", + "findplaceholder.h", + "foldernavigationwidget.cpp", + "foldernavigationwidget.h", + "generalsettings.cpp", + "generalsettings.h", + "generatedfile.cpp", + "generatedfile.h", + "helpitem.cpp", + "helpitem.h", + "helpmanager.cpp", + "helpmanager.h", + "helpmanager_implementation.h", + "icontext.cpp", + "icontext.h", + "icore.cpp", + "icore.h", + "idocument.cpp", + "idocument.h", + "idocumentfactory.cpp", + "idocumentfactory.h", + "ifilewizardextension.h", + "imode.cpp", + "imode.h", + "inavigationwidgetfactory.cpp", + "inavigationwidgetfactory.h", + "ioutputpane.cpp", + "ioutputpane.h", + "iversioncontrol.cpp", + "iversioncontrol.h", + "iwelcomepage.cpp", + "iwelcomepage.h", + "iwizardfactory.cpp", + "iwizardfactory.h", + "jsexpander.cpp", + "jsexpander.h", + "loggingviewer.cpp", + "loggingviewer.h", + "manhattanstyle.cpp", + "manhattanstyle.h", + "messagebox.cpp", + "messagebox.h", + "messagemanager.cpp", + "messagemanager.h", + "messageoutputwindow.cpp", + "messageoutputwindow.h", + "mimetypemagicdialog.cpp", + "mimetypemagicdialog.h", + "mimetypesettings.cpp", + "mimetypesettings.h", + "minisplitter.cpp", + "minisplitter.h", + "modemanager.cpp", + "modemanager.h", + "navigationsubwidget.cpp", + "navigationsubwidget.h", + "navigationwidget.cpp", + "navigationwidget.h", + "opendocumentstreeview.cpp", + "opendocumentstreeview.h", + "outputpane.cpp", + "outputpane.h", + "outputpanemanager.cpp", + "outputpanemanager.h", + "outputwindow.cpp", + "outputwindow.h", + "patchtool.cpp", + "patchtool.h", + "plugindialog.cpp", + "plugindialog.h", + "plugininstallwizard.cpp", + "plugininstallwizard.h", + "rightpane.cpp", + "rightpane.h", + "session.cpp", + "session.h", + "sessiondialog.cpp", + "sessiondialog.h", + "sessionmodel.cpp", + "sessionmodel.h", + "sessionview.cpp", + "sessionview.h", + "settingsdatabase.cpp", + "settingsdatabase.h", + "sidebar.cpp", + "sidebar.h", + "sidebarwidget.cpp", + "sidebarwidget.h", + "statusbarmanager.cpp", + "statusbarmanager.h", + "systemsettings.cpp", + "systemsettings.h", + "textdocument.cpp", + "textdocument.h", + "themechooser.cpp", + "themechooser.h", + "vcsmanager.cpp", + "vcsmanager.h", + "versiondialog.cpp", + "versiondialog.h", + "welcomepagehelper.cpp", + "welcomepagehelper.h", + "windowsupport.cpp", + "windowsupport.h", + ] + } + + Group { + name: "studiofonts" + prefix: "../../share/3rdparty/studiofonts/" + files: "studiofonts.qrc" + } + + Group { + name: "Action Manager" + prefix: "actionmanager/" + files: [ + "actioncontainer.cpp", "actioncontainer.h", "actioncontainer_p.h", + "actionmanager.cpp", "actionmanager.h", "actionmanager_p.h", + "command.cpp", "command.h", "command_p.h", + "commandbutton.cpp", "commandbutton.h", + "commandmappings.cpp", "commandmappings.h", + "commandsfile.cpp", "commandsfile.h", + ] + } + + Group { + name: "Dialogs" + prefix: "dialogs/" + files: [ + "addtovcsdialog.cpp", "addtovcsdialog.h", + "codecselector.cpp", "codecselector.h", + "externaltoolconfig.cpp", "externaltoolconfig.h", + "filepropertiesdialog.cpp", "filepropertiesdialog.h", + "ioptionspage.cpp", "ioptionspage.h", + "newdialog.cpp", "newdialog.h", + "newdialogwidget.cpp", "newdialogwidget.h", + "openwithdialog.cpp", "openwithdialog.h", + "promptoverwritedialog.cpp", "promptoverwritedialog.h", + "readonlyfilesdialog.cpp", "readonlyfilesdialog.h", + "restartdialog.cpp", "restartdialog.h", + "saveitemsdialog.cpp", "saveitemsdialog.h", + "settingsdialog.cpp", "settingsdialog.h", + "shortcutsettings.cpp", "shortcutsettings.h", + ] + } + + Group { + name: "Editor Manager" + prefix: "editormanager/" + files: [ + "documentmodel.cpp", "documentmodel.h", "documentmodel_p.h", + "editorarea.cpp", "editorarea.h", + "editormanager.cpp", "editormanager.h", "editormanager_p.h", + "editorview.cpp", "editorview.h", + "editorwindow.cpp", "editorwindow.h", + "ieditor.cpp", "ieditor.h", + "ieditorfactory.cpp", "ieditorfactory.h", "ieditorfactory_p.h", + "openeditorsview.cpp", "openeditorsview.h", + "openeditorswindow.cpp", "openeditorswindow.h", + "systemeditor.cpp", "systemeditor.h", + ] + } + + Group { + name: "Progress Manager" + prefix: "progressmanager/" + files: [ + "futureprogress.cpp", "futureprogress.h", + "processprogress.cpp", "processprogress.h", + "progressbar.cpp", "progressbar.h", + "progressmanager.cpp", "progressmanager.h", "progressmanager_p.h", + "progressview.cpp", "progressview.h", + "taskprogress.cpp", "taskprogress.h", + ] + } + + Group { + name: "ProgressManager_win" + condition: qbs.targetOS.contains("windows") + files: [ + "progressmanager/progressmanager_win.cpp", + ] + } + + Group { + name: "ProgressManager_mac" + condition: qbs.targetOS.contains("macos") + files: [ + "progressmanager/progressmanager_mac.mm", + ] + } + + Group { + name: "ProgressManager_x11" + condition: qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos") + files: [ + "progressmanager/progressmanager_x11.cpp", + ] + } + + QtcTestFiles { + files: [ + "testdatadir.cpp", + "testdatadir.h", + "locator/locatorfiltertest.cpp", + "locator/locatorfiltertest.h", + "locator/locator_test.cpp" + ] + + cpp.defines: outer.concat(['SRCDIR="' + path + '"']) + } + + Group { + name: "Find" + prefix: "find/" + files: [ + "basetextfind.cpp", + "basetextfind.h", + "currentdocumentfind.cpp", + "currentdocumentfind.h", + "find.qrc", + "findplugin.cpp", + "findplugin.h", + "findtoolbar.cpp", + "findtoolbar.h", + "findtoolwindow.cpp", + "findtoolwindow.h", + "highlightscrollbarcontroller.cpp", + "highlightscrollbarcontroller.h", + "ifindfilter.cpp", + "ifindfilter.h", + "ifindsupport.cpp", + "ifindsupport.h", + "itemviewfind.cpp", + "itemviewfind.h", + "optionspopup.cpp", + "optionspopup.h", + "searchresulttreeitemdelegate.cpp", + "searchresulttreeitemdelegate.h", + "searchresulttreeitemroles.h", + "searchresulttreeitems.cpp", + "searchresulttreeitems.h", + "searchresulttreemodel.cpp", + "searchresulttreemodel.h", + "searchresulttreeview.cpp", + "searchresulttreeview.h", + "searchresultwidget.cpp", + "searchresultwidget.h", + "searchresultwindow.cpp", + "searchresultwindow.h", + "textfindconstants.h", + ] + } + + Group { + name: "Locator" + prefix: "locator/" + files: [ + "commandlocator.cpp", + "commandlocator.h", + "directoryfilter.cpp", + "directoryfilter.h", + "executefilter.cpp", + "executefilter.h", + "externaltoolsfilter.cpp", + "externaltoolsfilter.h", + "filesystemfilter.cpp", + "filesystemfilter.h", + "ilocatorfilter.cpp", + "ilocatorfilter.h", + "javascriptfilter.cpp", + "javascriptfilter.h", + "locatorconstants.h", + "locatorfiltersfilter.cpp", + "locatorfiltersfilter.h", + "locatormanager.cpp", + "locatormanager.h", + "locator.cpp", + "locator.h", + "locatorsettingspage.cpp", + "locatorsettingspage.h", + "locatorwidget.cpp", + "locatorwidget.h", + "opendocumentsfilter.cpp", + "opendocumentsfilter.h", + "spotlightlocatorfilter.h", + "spotlightlocatorfilter.cpp", + "urllocatorfilter.cpp", + "urllocatorfilter.h" + ] + } + + Group { + name: "Terminal" + prefix: "terminal/" + files: [ + "searchableterminal.cpp", + "searchableterminal.h", + ] + } + + Export { Depends { name: "Aggregation" } - - Depends { name: "app_version_header" } - - cpp.dynamicLibraries: { - if (qbs.targetOS.contains("windows")) - return ["ole32", "user32"] - } - - cpp.frameworks: qbs.targetOS.contains("macos") ? ["AppKit"] : undefined - - Group { - name: "General" - files: [ - "actionsfilter.cpp", - "actionsfilter.h", - "basefilewizard.cpp", - "basefilewizard.h", - "basefilewizardfactory.cpp", - "basefilewizardfactory.h", - "core.qrc", - "core_global.h", - "core_logo.qrc", - "coreconstants.h", - "coreicons.cpp", - "coreicons.h", - "corejsextensions.cpp", - "corejsextensions.h", - "coreplugin.cpp", - "coreplugin.h", - "coreplugintr.h", - "designmode.cpp", - "designmode.h", - "diffservice.cpp", - "diffservice.h", - "documentmanager.cpp", - "documentmanager.h", - "editmode.cpp", - "editmode.h", - "editortoolbar.cpp", - "editortoolbar.h", - "externaltool.cpp", - "externaltool.h", - "externaltoolmanager.cpp", - "externaltoolmanager.h", - "fancyactionbar.cpp", - "fancyactionbar.h", - "fancyactionbar.qrc", - "fancytabwidget.cpp", - "fancytabwidget.h", - "featureprovider.cpp", - "featureprovider.h", - "fileutils.cpp", - "fileutils.h", - "findplaceholder.cpp", - "findplaceholder.h", - "foldernavigationwidget.cpp", - "foldernavigationwidget.h", - "generalsettings.cpp", - "generalsettings.h", - "generatedfile.cpp", - "generatedfile.h", - "helpitem.cpp", - "helpitem.h", - "helpmanager.cpp", - "helpmanager.h", - "helpmanager_implementation.h", - "icontext.cpp", - "icontext.h", - "icore.cpp", - "icore.h", - "idocument.cpp", - "idocument.h", - "idocumentfactory.cpp", - "idocumentfactory.h", - "ifilewizardextension.h", - "imode.cpp", - "imode.h", - "inavigationwidgetfactory.cpp", - "inavigationwidgetfactory.h", - "ioutputpane.cpp", - "ioutputpane.h", - "iversioncontrol.cpp", - "iversioncontrol.h", - "iwelcomepage.cpp", - "iwelcomepage.h", - "iwizardfactory.cpp", - "iwizardfactory.h", - "jsexpander.cpp", - "jsexpander.h", - "loggingmanager.cpp", - "loggingmanager.h", - "loggingviewer.cpp", - "loggingviewer.h", - "mainwindow.cpp", - "mainwindow.h", - "manhattanstyle.cpp", - "manhattanstyle.h", - "messagebox.cpp", - "messagebox.h", - "messagemanager.cpp", - "messagemanager.h", - "messageoutputwindow.cpp", - "messageoutputwindow.h", - "mimetypemagicdialog.cpp", - "mimetypemagicdialog.h", - "mimetypesettings.cpp", - "mimetypesettings.h", - "minisplitter.cpp", - "minisplitter.h", - "modemanager.cpp", - "modemanager.h", - "navigationsubwidget.cpp", - "navigationsubwidget.h", - "navigationwidget.cpp", - "navigationwidget.h", - "opendocumentstreeview.cpp", - "opendocumentstreeview.h", - "outputpane.cpp", - "outputpane.h", - "outputpanemanager.cpp", - "outputpanemanager.h", - "outputwindow.cpp", - "outputwindow.h", - "patchtool.cpp", - "patchtool.h", - "plugindialog.cpp", - "plugindialog.h", - "plugininstallwizard.cpp", - "plugininstallwizard.h", - "rightpane.cpp", - "rightpane.h", - "session.cpp", - "session.h", - "session_p.h", - "sessiondialog.cpp", - "sessiondialog.h", - "sessionmodel.cpp", - "sessionmodel.h", - "sessionview.cpp", - "sessionview.h", - "settingsdatabase.cpp", - "settingsdatabase.h", - "sidebar.cpp", - "sidebar.h", - "sidebarwidget.cpp", - "sidebarwidget.h", - "statusbarmanager.cpp", - "statusbarmanager.h", - "systemsettings.cpp", - "systemsettings.h", - "textdocument.cpp", - "textdocument.h", - "themechooser.cpp", - "themechooser.h", - "vcsmanager.cpp", - "vcsmanager.h", - "versiondialog.cpp", - "versiondialog.h", - "welcomepagehelper.cpp", - "welcomepagehelper.h", - "windowsupport.cpp", - "windowsupport.h", - ] - } - - Group { - name: "studiofonts" - prefix: "../../share/3rdparty/studiofonts/" - files: "studiofonts.qrc" - } - - Group { - name: "Action Manager" - prefix: "actionmanager/" - files: [ - "actioncontainer.cpp", "actioncontainer.h", "actioncontainer_p.h", - "actionmanager.cpp", "actionmanager.h", "actionmanager_p.h", - "command.cpp", "command.h", "command_p.h", - "commandbutton.cpp", "commandbutton.h", - "commandmappings.cpp", "commandmappings.h", - "commandsfile.cpp", "commandsfile.h", - ] - } - - Group { - name: "Dialogs" - prefix: "dialogs/" - files: [ - "addtovcsdialog.cpp", "addtovcsdialog.h", - "codecselector.cpp", "codecselector.h", - "externaltoolconfig.cpp", "externaltoolconfig.h", - "filepropertiesdialog.cpp", "filepropertiesdialog.h", - "ioptionspage.cpp", "ioptionspage.h", - "newdialog.cpp", "newdialog.h", - "newdialogwidget.cpp", "newdialogwidget.h", - "openwithdialog.cpp", "openwithdialog.h", - "promptoverwritedialog.cpp", "promptoverwritedialog.h", - "readonlyfilesdialog.cpp", "readonlyfilesdialog.h", - "restartdialog.cpp", "restartdialog.h", - "saveitemsdialog.cpp", "saveitemsdialog.h", - "settingsdialog.cpp", "settingsdialog.h", - "shortcutsettings.cpp", "shortcutsettings.h", - ] - } - - Group { - name: "Editor Manager" - prefix: "editormanager/" - files: [ - "documentmodel.cpp", "documentmodel.h", "documentmodel_p.h", - "editorarea.cpp", "editorarea.h", - "editormanager.cpp", "editormanager.h", "editormanager_p.h", - "editorview.cpp", "editorview.h", - "editorwindow.cpp", "editorwindow.h", - "ieditor.cpp", "ieditor.h", - "ieditorfactory.cpp", "ieditorfactory.h", "ieditorfactory_p.h", - "iexternaleditor.cpp", "iexternaleditor.h", - "openeditorsview.cpp", "openeditorsview.h", - "openeditorswindow.cpp", "openeditorswindow.h", - "systemeditor.cpp", "systemeditor.h", - ] - } - - Group { - name: "Progress Manager" - prefix: "progressmanager/" - files: [ - "futureprogress.cpp", "futureprogress.h", - "processprogress.cpp", "processprogress.h", - "progressbar.cpp", "progressbar.h", - "progressmanager.cpp", "progressmanager.h", "progressmanager_p.h", - "progressview.cpp", "progressview.h", - "taskprogress.cpp", "taskprogress.h", - ] - } - - Group { - name: "ProgressManager_win" - condition: qbs.targetOS.contains("windows") - files: [ - "progressmanager/progressmanager_win.cpp", - ] - } - - Group { - name: "ProgressManager_mac" - condition: qbs.targetOS.contains("macos") - files: [ - "progressmanager/progressmanager_mac.mm", - ] - } - - Group { - name: "ProgressManager_x11" - condition: qbs.targetOS.contains("unix") && !qbs.targetOS.contains("macos") - files: [ - "progressmanager/progressmanager_x11.cpp", - ] - } - - QtcTestFiles { - files: [ - "testdatadir.cpp", - "testdatadir.h", - "locator/locatorfiltertest.cpp", - "locator/locatorfiltertest.h", - "locator/locator_test.cpp" - ] - - cpp.defines: outer.concat(['SRCDIR="' + path + '"']) - } - - Group { - name: "Find" - prefix: "find/" - files: [ - "basetextfind.cpp", - "basetextfind.h", - "currentdocumentfind.cpp", - "currentdocumentfind.h", - "find.qrc", - "findplugin.cpp", - "findplugin.h", - "findtoolbar.cpp", - "findtoolbar.h", - "findtoolwindow.cpp", - "findtoolwindow.h", - "highlightscrollbarcontroller.cpp", - "highlightscrollbarcontroller.h", - "ifindfilter.cpp", - "ifindfilter.h", - "ifindsupport.cpp", - "ifindsupport.h", - "itemviewfind.cpp", - "itemviewfind.h", - "optionspopup.cpp", - "optionspopup.h", - "searchresulttreeitemdelegate.cpp", - "searchresulttreeitemdelegate.h", - "searchresulttreeitemroles.h", - "searchresulttreeitems.cpp", - "searchresulttreeitems.h", - "searchresulttreemodel.cpp", - "searchresulttreemodel.h", - "searchresulttreeview.cpp", - "searchresulttreeview.h", - "searchresultwidget.cpp", - "searchresultwidget.h", - "searchresultwindow.cpp", - "searchresultwindow.h", - "textfindconstants.h", - ] - } - - Group { - name: "Locator" - prefix: "locator/" - files: [ - "commandlocator.cpp", - "commandlocator.h", - "directoryfilter.cpp", - "directoryfilter.h", - "executefilter.cpp", - "executefilter.h", - "externaltoolsfilter.cpp", - "externaltoolsfilter.h", - "filesystemfilter.cpp", - "filesystemfilter.h", - "ilocatorfilter.cpp", - "ilocatorfilter.h", - "javascriptfilter.cpp", - "javascriptfilter.h", - "locatorconstants.h", - "locatorfiltersfilter.cpp", - "locatorfiltersfilter.h", - "locatormanager.cpp", - "locatormanager.h", - "locator.cpp", - "locator.h", - "locatorsettingspage.cpp", - "locatorsettingspage.h", - "locatorwidget.cpp", - "locatorwidget.h", - "opendocumentsfilter.cpp", - "opendocumentsfilter.h", - "spotlightlocatorfilter.h", - "spotlightlocatorfilter.cpp", - "urllocatorfilter.cpp", - "urllocatorfilter.h" - ] - } - - Export { - Depends { name: "Aggregation" } - Depends { name: "Utils" } - } + Depends { name: "Utils" } } } diff --git a/src/plugins/coreplugin/designmode.h b/src/plugins/coreplugin/designmode.h index 844bcaf259f..da9a6f70ab6 100644 --- a/src/plugins/coreplugin/designmode.h +++ b/src/plugins/coreplugin/designmode.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/imode.h> +#include "imode.h" namespace Core { class IEditor; diff --git a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp index 59237aad79a..6883e930a5c 100644 --- a/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp +++ b/src/plugins/coreplugin/dialogs/externaltoolconfig.cpp @@ -192,7 +192,7 @@ bool ExternalToolModel::dropMimeData(const QMimeData *data, QStringList ExternalToolModel::mimeTypes() const { - return QStringList("application/qtcreator-externaltool-config"); + return {"application/qtcreator-externaltool-config"}; } QModelIndex ExternalToolModel::index(int row, int column, const QModelIndex &parent) const diff --git a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp index c37125d447a..de3362a408f 100644 --- a/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp +++ b/src/plugins/coreplugin/dialogs/filepropertiesdialog.cpp @@ -192,7 +192,7 @@ void FilePropertiesDialog::refresh() const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_filePath); m_mimeType->setText(mimeType.name()); - const EditorTypeList factories = IEditorFactory::preferredEditorTypes(m_filePath); + const EditorFactories factories = IEditorFactory::preferredEditorTypes(m_filePath); m_defaultEditor->setText(!factories.isEmpty() ? factories.at(0)->displayName() : Tr::tr("Undefined")); diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp index 56c7a46f48a..175e8289600 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp +++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp @@ -6,17 +6,13 @@ #include "ioptionspage.h" -#include <coreplugin/icore.h> - #include <utils/algorithm.h> -#include <utils/aspects.h> #include <utils/layoutbuilder.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> #include <QCheckBox> #include <QGroupBox> -#include <QIcon> #include <QLabel> #include <QPushButton> #include <QRegularExpression> @@ -76,12 +72,12 @@ namespace Core { */ /*! - Returns the category icon of the options page. This icon is displayed in the list on the left - side of the \uicontrol Options dialog. + Returns the path to the category icon of the options page. This icon will be read from this + path and displayed in the list on the left side of the \uicontrol Options dialog. */ -QIcon IOptionsPage::categoryIcon() const +FilePath IOptionsPage::categoryIconPath() const { - return m_categoryIcon.icon(); + return m_categoryIconPath; } /*! @@ -134,6 +130,15 @@ QWidget *IOptionsPage::widget() if (!m_widget) { if (m_widgetCreator) { m_widget = m_widgetCreator(); + QTC_CHECK(m_widget); + } else if (m_settingsProvider) { + m_widget = new IOptionsPageWidget; + AspectContainer *container = m_settingsProvider(); + if (auto layouter = container->layouter()) { + layouter().attachTo(m_widget); + } else { + QTC_CHECK(false); + } } else { QTC_CHECK(false); } @@ -155,10 +160,18 @@ void IOptionsPage::apply() if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget)) widget->apply(); - if (m_settings) { - if (m_settings->isDirty()) { - m_settings->apply(); - m_settings->writeSettings(ICore::settings()); + if (m_settingsProvider) { + AspectContainer *container = m_settingsProvider(); + QTC_ASSERT(container, return); + // Sanity check: Aspects in option pages should not autoapply. + if (!container->aspects().isEmpty()) { + BaseAspect *aspect = container->aspects().first(); + QTC_ASSERT(aspect, return); + QTC_ASSERT(!aspect->isAutoApply(), container->setAutoApply(false)); + } + if (container->isDirty()) { + container->apply(); + container->writeSettings(); } } } @@ -177,8 +190,10 @@ void IOptionsPage::finish() if (auto widget = qobject_cast<IOptionsPageWidget *>(m_widget)) widget->finish(); - if (m_settings) - m_settings->finish(); + if (m_settingsProvider) { + AspectContainer *container = m_settingsProvider(); + container->finish(); + } delete m_widget; } @@ -189,21 +204,12 @@ void IOptionsPage::finish() */ void IOptionsPage::setCategoryIconPath(const FilePath &categoryIconPath) { - m_categoryIcon = Icon({{categoryIconPath, Theme::PanelTextColorDark}}, Icon::Tint); + m_categoryIconPath = categoryIconPath; } -void IOptionsPage::setSettings(AspectContainer *settings) +void IOptionsPage::setSettingsProvider(const std::function<AspectContainer *()> &provider) { - m_settings = settings; -} - -void IOptionsPage::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter) -{ - m_widgetCreator = [layouter] { - auto widget = new IOptionsPageWidget; - layouter().attachTo(widget); - return widget; - }; + m_settingsProvider = provider; } /*! @@ -236,7 +242,11 @@ void IOptionsPage::setLayouter(const std::function<Layouting::LayoutItem ()> &la Sets \a categoryIcon as the category icon of the options page. */ -static QList<IOptionsPage *> g_optionsPages; +static QList<IOptionsPage *> &optionsPages() +{ + static QList<IOptionsPage *> thePages; + return thePages; +} /*! Constructs an options page and registers it @@ -245,7 +255,7 @@ static QList<IOptionsPage *> g_optionsPages; IOptionsPage::IOptionsPage(bool registerGlobally) { if (registerGlobally) - g_optionsPages.append(this); + optionsPages().append(this); } /*! @@ -253,7 +263,7 @@ IOptionsPage::IOptionsPage(bool registerGlobally) */ IOptionsPage::~IOptionsPage() { - g_optionsPages.removeOne(this); + optionsPages().removeOne(this); } /*! @@ -261,7 +271,7 @@ IOptionsPage::~IOptionsPage() */ const QList<IOptionsPage *> IOptionsPage::allOptionsPages() { - return g_optionsPages; + return optionsPages(); } /*! @@ -299,22 +309,4 @@ const QList<IOptionsPageProvider *> IOptionsPageProvider::allOptionsPagesProvide return g_optionsPagesProviders; } -QIcon IOptionsPageProvider::categoryIcon() const -{ - return m_categoryIcon.icon(); -} - -// PagedSettings - -PagedSettings::PagedSettings() -{ - setSettings(this); - setAutoApply(false); -} - -void PagedSettings::readSettings() -{ - return AspectContainer::readSettings(Core::ICore::settings()); -} - } // Core diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h index 21984d2a11a..e230cab904c 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.h +++ b/src/plugins/coreplugin/dialogs/ioptionspage.h @@ -3,23 +3,17 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/aspects.h> -#include <utils/icon.h> #include <utils/id.h> -#include <QObject> #include <QPointer> #include <QStringList> #include <QWidget> #include <functional> -namespace Layouting { class LayoutItem; }; - -namespace Utils { class AspectContainer; }; - namespace Core { class CORE_EXPORT IOptionsPageWidget : public QWidget @@ -54,7 +48,7 @@ public: QString displayName() const { return m_displayName; } Utils::Id category() const { return m_category; } QString displayCategory() const { return m_displayCategory; } - QIcon categoryIcon() const; + Utils::FilePath categoryIconPath() const; using WidgetCreator = std::function<IOptionsPageWidget *()>; void setWidgetCreator(const WidgetCreator &widgetCreator); @@ -72,26 +66,22 @@ protected: void setDisplayName(const QString &displayName) { m_displayName = displayName; } void setCategory(Utils::Id category) { m_category = category; } void setDisplayCategory(const QString &displayCategory) { m_displayCategory = displayCategory; } - void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; } void setCategoryIconPath(const Utils::FilePath &categoryIconPath); - void setSettings(Utils::AspectContainer *settings); - void setLayouter(const std::function<Layouting::LayoutItem()> &layouter); - - // Used in FontSettingsPage. FIXME? - QPointer<QWidget> m_widget; // Used in conjunction with m_widgetCreator + void setSettingsProvider(const std::function<Utils::AspectContainer *()> &provider); private: Utils::Id m_id; Utils::Id m_category; QString m_displayName; QString m_displayCategory; - Utils::Icon m_categoryIcon; + Utils::FilePath m_categoryIconPath; WidgetCreator m_widgetCreator; + QPointer<QWidget> m_widget; // Used in conjunction with m_widgetCreator mutable bool m_keywordsInitialized = false; mutable QStringList m_keywords; - Utils::AspectContainer *m_settings = nullptr; + std::function<Utils::AspectContainer *()> m_settingsProvider; }; /* @@ -104,7 +94,7 @@ private: class CORE_EXPORT IOptionsPageProvider { - Q_DISABLE_COPY_MOVE(IOptionsPageProvider); + Q_DISABLE_COPY_MOVE(IOptionsPageProvider) public: IOptionsPageProvider(); @@ -114,7 +104,7 @@ public: Utils::Id category() const { return m_category; } QString displayCategory() const { return m_displayCategory; } - QIcon categoryIcon() const; + Utils::FilePath categoryIconPath() const { return m_categoryIconPath; } virtual QList<IOptionsPage *> pages() const = 0; virtual bool matches(const QRegularExpression ®exp) const = 0; @@ -122,20 +112,11 @@ public: protected: void setCategory(Utils::Id category) { m_category = category; } void setDisplayCategory(const QString &displayCategory) { m_displayCategory = displayCategory; } - void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; } + void setCategoryIconPath(const Utils::FilePath &iconPath) { m_categoryIconPath = iconPath; } Utils::Id m_category; QString m_displayCategory; - Utils::Icon m_categoryIcon; -}; - -class CORE_EXPORT PagedSettings : public Utils::AspectContainer, public IOptionsPage -{ -public: - PagedSettings(); - - using AspectContainer::readSettings; // FIXME: Remove. - void readSettings(); // Intentionally hides AspectContainer::readSettings() + Utils::FilePath m_categoryIconPath; }; } // namespace Core diff --git a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp index 545ca9c4169..ca616c8b65c 100644 --- a/src/plugins/coreplugin/dialogs/newdialogwidget.cpp +++ b/src/plugins/coreplugin/dialogs/newdialogwidget.cpp @@ -327,8 +327,8 @@ void NewDialogWidget::showDialog() { QModelIndex idx; - QString lastPlatform = ICore::settings()->value(QLatin1String(LAST_PLATFORM_KEY)).toString(); - QString lastCategory = ICore::settings()->value(QLatin1String(LAST_CATEGORY_KEY)).toString(); + QString lastPlatform = ICore::settings()->value(LAST_PLATFORM_KEY).toString(); + QString lastCategory = ICore::settings()->value(LAST_CATEGORY_KEY).toString(); if (!lastPlatform.isEmpty()) { int index = m_comboBox->findData(lastPlatform); diff --git a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h index 89f22372408..09e717cd270 100644 --- a/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h +++ b/src/plugins/coreplugin/dialogs/readonlyfilesdialog.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/filepath.h> diff --git a/src/plugins/coreplugin/dialogs/restartdialog.h b/src/plugins/coreplugin/dialogs/restartdialog.h index 65646f3389a..c7f8fd43c51 100644 --- a/src/plugins/coreplugin/dialogs/restartdialog.h +++ b/src/plugins/coreplugin/dialogs/restartdialog.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <QCoreApplication> #include <QMessageBox> diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp index 3b9034ddd71..2d693ad7420 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -12,19 +12,16 @@ #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> -#include <extensionsystem/pluginmanager.h> - #include <QCheckBox> -#include <QDebug> #include <QDialogButtonBox> -#include <QDir> -#include <QFileInfo> #include <QLabel> #include <QPushButton> #include <QTreeWidget> Q_DECLARE_METATYPE(Core::IDocument*) +using namespace Utils; + namespace Core::Internal { SaveItemsDialog::SaveItemsDialog(QWidget *parent, const QList<IDocument *> &items) @@ -46,7 +43,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, const QList<IDocument *> &item m_treeWidget->setColumnCount(2); // QDialogButtonBox's behavior for "destructive" is wrong, the "do not save" should be left-aligned - const QDialogButtonBox::ButtonRole discardButtonRole = Utils::HostOsInfo::isMacHost() + const QDialogButtonBox::ButtonRole discardButtonRole = HostOsInfo::isMacHost() ? QDialogButtonBox::ResetRole : QDialogButtonBox::DestructiveRole; if (DiffService::instance()) { @@ -71,26 +68,24 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, const QList<IDocument *> &item for (IDocument *document : items) { QString visibleName; - QString directory; - Utils::FilePath filePath = document->filePath(); + FilePath directory; + FilePath filePath = document->filePath(); if (filePath.isEmpty()) { visibleName = document->fallbackSaveAsFileName(); } else { - directory = filePath.absolutePath().toUserOutput(); + directory = filePath.absolutePath(); visibleName = filePath.fileName(); } QTreeWidgetItem *item = new QTreeWidgetItem(m_treeWidget, - QStringList() - << visibleName - << QDir::toNativeSeparators(directory)); + QStringList{visibleName, directory.toUserOutput()}); if (!filePath.isEmpty()) - item->setIcon(0, Utils::FileIconProvider::icon(filePath)); + item->setIcon(0, FileIconProvider::icon(filePath)); item->setData(0, Qt::UserRole, QVariant::fromValue(document)); } m_treeWidget->resizeColumnToContents(0); m_treeWidget->selectAll(); - if (Utils::HostOsInfo::isMacHost()) + if (HostOsInfo::isMacHost()) m_treeWidget->setAlternatingRowColors(true); adjustButtonWidths(); updateButtons(); @@ -151,7 +146,7 @@ void SaveItemsDialog::adjustButtonWidths() if (hint > maxTextWidth) maxTextWidth = hint; } - if (Utils::HostOsInfo::isMacHost()) { + if (HostOsInfo::isMacHost()) { QPushButton *cancelButton = m_buttonBox->button(QDialogButtonBox::Cancel); int cancelButtonWidth = cancelButton->sizeHint().width(); if (cancelButtonWidth > maxTextWidth) diff --git a/src/plugins/coreplugin/dialogs/settingsdialog.cpp b/src/plugins/coreplugin/dialogs/settingsdialog.cpp index f429994edd9..d503c71671f 100644 --- a/src/plugins/coreplugin/dialogs/settingsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/settingsdialog.cpp @@ -10,10 +10,12 @@ #include <utils/algorithm.h> #include <utils/hostosinfo.h> +#include <utils/icon.h> #include <utils/fancylineedit.h> #include <utils/qtcassert.h> #include <QApplication> +#include <QCheckBox> #include <QDialog> #include <QDialogButtonBox> #include <QEventLoop> @@ -28,19 +30,21 @@ #include <QScrollArea> #include <QScrollBar> #include <QSet> -#include <QSettings> #include <QSortFilterProxyModel> #include <QSpacerItem> #include <QStackedLayout> #include <QStyle> #include <QStyledItemDelegate> +#include <extensionsystem/pluginmanager.h> + const int kInitialWidth = 750; const int kInitialHeight = 450; const int kMaxMinimumWidth = 250; const int kMaxMinimumHeight = 250; static const char pageKeyC[] = "General/LastPreferencePage"; +static const char sortKeyC[] = "General/SortCategories"; const int categoryIconSize = 24; using namespace Utils; @@ -164,8 +168,10 @@ void CategoryModel::setPages(const QList<IOptionsPage*> &pages, } if (category->displayName.isEmpty()) category->displayName = page->displayCategory(); - if (category->icon.isNull()) - category->icon = page->categoryIcon(); + if (category->icon.isNull() && !page->categoryIconPath().isEmpty()) { + Icon icon({{page->categoryIconPath(), Theme::PanelTextColorDark}}, Icon::Tint); + category->icon = icon.icon(); + } category->pages.append(page); } @@ -181,8 +187,10 @@ void CategoryModel::setPages(const QList<IOptionsPage*> &pages, } if (category->displayName.isEmpty()) category->displayName = provider->displayCategory(); - if (category->icon.isNull()) - category->icon = provider->categoryIcon(); + if (category->icon.isNull()) { + Icon icon({{provider->categoryIconPath(), Theme::PanelTextColorDark}}, Icon::Tint); + category->icon = icon.icon(); + } category->providers.append(provider); } @@ -343,9 +351,12 @@ private: void showEvent(QShowEvent *event) final { if (!widget()) { - QWidget *inner = m_page->widget(); - setWidget(inner); - inner->setAutoFillBackground(false); + if (QWidget *inner = m_page->widget()) { + setWidget(inner); + inner->setAutoFillBackground(false); + } else { + QTC_CHECK(false); + } } QScrollArea::showEvent(event); @@ -441,9 +452,9 @@ private: Id m_currentPage; QStackedLayout *m_stackedLayout; Utils::FancyLineEdit *m_filterLineEdit; + QCheckBox *m_sortCheckBox; QListView *m_categoryList; QLabel *m_headerLabel; - std::vector<QEventLoop *> m_eventLoops; bool m_running = false; bool m_applied = false; bool m_finished = false; @@ -451,13 +462,14 @@ private: static QPointer<SettingsDialog> m_instance = nullptr; -SettingsDialog::SettingsDialog(QWidget *parent) : - QDialog(parent), - m_pages(sortedOptionsPages()), - m_stackedLayout(new QStackedLayout), - m_filterLineEdit(new Utils::FancyLineEdit), - m_categoryList(new CategoryListView), - m_headerLabel(new QLabel) +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) + , m_pages(sortedOptionsPages()) + , m_stackedLayout(new QStackedLayout) + , m_filterLineEdit(new Utils::FancyLineEdit) + , m_sortCheckBox(new QCheckBox(Tr::tr("Sort categories"))) + , m_categoryList(new CategoryListView) + , m_headerLabel(new QLabel) { m_filterLineEdit->setFiltering(true); @@ -473,6 +485,12 @@ SettingsDialog::SettingsDialog(QWidget *parent) : m_categoryList->setSelectionMode(QAbstractItemView::SingleSelection); m_categoryList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + connect(m_sortCheckBox, &QAbstractButton::toggled, this, [this](bool checked) { + m_proxyModel.sort(checked ? 0 : -1); + }); + QtcSettings *settings = ICore::settings(); + m_sortCheckBox->setChecked(settings->value(sortKeyC, false).toBool()); + connect(m_categoryList->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &SettingsDialog::currentChanged); @@ -496,8 +514,8 @@ void SettingsDialog::showPage(const Id pageId) // handle the case of "show last page" Id initialPageId = pageId; if (!initialPageId.isValid()) { - QSettings *settings = ICore::settings(); - initialPageId = Id::fromSetting(settings->value(QLatin1String(pageKeyC))); + QtcSettings *settings = ICore::settings(); + initialPageId = Id::fromSetting(settings->value(pageKeyC)); } int initialCategoryIndex = -1; @@ -581,8 +599,9 @@ void SettingsDialog::createGui() mainGridLayout->addWidget(m_filterLineEdit, 0, 0, 1, 1); mainGridLayout->addLayout(headerHLayout, 0, 1, 1, 1); mainGridLayout->addWidget(m_categoryList, 1, 0, 1, 1); - mainGridLayout->addLayout(m_stackedLayout, 1, 1, 1, 1); - mainGridLayout->addWidget(buttonBox, 2, 0, 1, 2); + mainGridLayout->addWidget(m_sortCheckBox, 2, 0, 1, 1); + mainGridLayout->addLayout(m_stackedLayout, 1, 1, 2, 1); + mainGridLayout->addWidget(buttonBox, 3, 0, 1, 2); mainGridLayout->setColumnStretch(1, 4); setLayout(mainGridLayout); @@ -731,16 +750,12 @@ void SettingsDialog::apply() void SettingsDialog::done(int val) { - QSettings *settings = ICore::settings(); - settings->setValue(QLatin1String(pageKeyC), m_currentPage.toSetting()); + QtcSettings *settings = ICore::settings(); + settings->setValue(pageKeyC, m_currentPage.toSetting()); + settings->setValue(sortKeyC, m_sortCheckBox->isChecked()); ICore::saveSettings(ICore::SettingsDialogDone); // save all settings - // exit event loops in reverse order of addition - for (QEventLoop *eventLoop : m_eventLoops) - eventLoop->exit(); - m_eventLoops.clear(); - QDialog::done(val); } @@ -749,39 +764,44 @@ bool SettingsDialog::execDialog() if (!m_running) { m_running = true; m_finished = false; - static const QLatin1String kPreferenceDialogSize("Core/PreferenceDialogSize"); - if (ICore::settings()->contains(kPreferenceDialogSize)) - resize(ICore::settings()->value(kPreferenceDialogSize).toSize()); - else - resize(kInitialWidth, kInitialHeight); - exec(); - m_running = false; - m_instance = nullptr; - ICore::settings()->setValueWithDefault(kPreferenceDialogSize, - size(), - QSize(kInitialWidth, kInitialHeight)); - // make sure that the current "single" instance is deleted - // we can't delete right away, since we still access the m_applied member - deleteLater(); - } else { - // exec dialog is called while the instance is already running - // this can happen when a event triggers a code path that wants to - // show the settings dialog again - // e.g. when starting the debugger (with non-built debugging helpers), - // and manually opening the settings dialog, after the debugger hit - // a break point it will complain about missing helper, and offer the - // option to open the settings dialog. - // Keep the UI running by creating another event loop. - QEventLoop eventLoop; - m_eventLoops.emplace(m_eventLoops.begin(), &eventLoop); - eventLoop.exec(); - QTC_ASSERT(m_eventLoops.empty(), return m_applied;); + static const char kPreferenceDialogSize[] = "Core/PreferenceDialogSize"; + const QSize initialSize(kInitialWidth, kInitialHeight); + resize(ICore::settings()->value(kPreferenceDialogSize, initialSize).toSize()); + + // We call open here as exec is no longer the preferred method of displaying + // modal dialogs. The issue that triggered the change here was QTBUG-117814 + // (on macOS: Caps Lock indicator does not update) + open(); + connect(this, &QDialog::finished, this, [this, initialSize] { + m_running = false; + m_instance = nullptr; + ICore::settings()->setValueWithDefault(kPreferenceDialogSize, size(), initialSize); + // make sure that the current "single" instance is deleted + // we can't delete right away, since we still access the m_applied member + deleteLater(); + }); } + + // This function needs to be blocking, so we need to wait for the dialog to finish. + // We cannot use QDialog::exec due to the issue described above at "open()". + // Since execDialog can be called multiple times, we need to run potentially multiple + // loops here, to have every invocation of execDialog() wait for the dialog to finish. + while (m_running) + QApplication::processEvents(QEventLoop::WaitForMoreEvents); + return m_applied; } bool executeSettingsDialog(QWidget *parent, Id initialPage) { + if (!ExtensionSystem::PluginManager::isInitializationDone()) { + QObject::connect(ExtensionSystem::PluginManager::instance(), + &ExtensionSystem::PluginManager::initializationDone, + parent, + [parent, initialPage]() { executeSettingsDialog(parent, initialPage); }); + return false; + } + // Make sure all wizards are there when the user might access the keyboard shortcuts: (void) IWizardFactory::allWizardFactories(); diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.h b/src/plugins/coreplugin/dialogs/shortcutsettings.h index 49297b3d501..36e175ff673 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.h +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.h @@ -3,8 +3,8 @@ #pragma once -#include <coreplugin/actionmanager/commandmappings.h> -#include <coreplugin/dialogs/ioptionspage.h> +#include "../actionmanager/commandmappings.h" +#include "ioptionspage.h" #include <QGridLayout> #include <QKeySequence> diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index b7b7ccfbe06..9c1934103c0 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -3,25 +3,24 @@ #include "documentmanager.h" +#include "actionmanager/actioncontainer.h" +#include "actionmanager/actionmanager.h" +#include "actionmanager/command.h" #include "coreconstants.h" #include "coreplugintr.h" +#include "diffservice.h" +#include "dialogs/filepropertiesdialog.h" +#include "dialogs/readonlyfilesdialog.h" +#include "dialogs/saveitemsdialog.h" +#include "editormanager/editormanager.h" +#include "editormanager/editormanager_p.h" +#include "editormanager/editorview.h" +#include "editormanager/ieditor.h" +#include "editormanager/ieditorfactory.h" #include "icore.h" #include "idocument.h" #include "idocumentfactory.h" - -#include <coreplugin/actionmanager/actioncontainer.h> -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/actionmanager/command.h> -#include <coreplugin/diffservice.h> -#include <coreplugin/dialogs/filepropertiesdialog.h> -#include <coreplugin/dialogs/readonlyfilesdialog.h> -#include <coreplugin/dialogs/saveitemsdialog.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/editormanager/editormanager_p.h> -#include <coreplugin/editormanager/editorview.h> -#include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/editormanager/ieditorfactory.h> -#include <coreplugin/editormanager/iexternaleditor.h> +#include "systemsettings.h" #include <extensionsystem/pluginmanager.h> @@ -47,7 +46,6 @@ #include <QMainWindow> #include <QMenu> #include <QMessageBox> -#include <QSettings> #include <QStringList> #include <QTimer> @@ -721,7 +719,7 @@ bool DocumentManager::saveDocument(IDocument *document, bool addWatcher = removeDocument(document); // So that our own IDocument gets no notification at all QString errorString; - if (!document->save(&errorString, filePath, false)) { + if (!document->save(&errorString, savePath, false)) { if (isReadOnly) { QFile ofi(savePath.toString()); // Check whether the existing file is writable @@ -1336,7 +1334,8 @@ void DocumentManager::addToRecentFiles(const FilePath &filePath, Id editorId) Utils::erase(d->m_recentFiles, [fileKey](const RecentFile &file) { return fileKey == filePathKey(file.first, DocumentManager::KeepLinks); }); - while (d->m_recentFiles.count() >= EditorManagerPrivate::maxRecentFiles()) + const int maxRecentFiles = systemSettings().maxRecentFiles(); + while (d->m_recentFiles.count() >= maxRecentFiles) d->m_recentFiles.removeLast(); d->m_recentFiles.prepend(RecentFile(filePath, editorId)); } @@ -1399,22 +1398,22 @@ void restoreRecentFiles(const QVariantList &recentFiles, const QStringList &rece void readSettings() { - QSettings *s = ICore::settings(); + QtcSettings *s = ICore::settings(); d->m_recentFiles.clear(); - s->beginGroup(QLatin1String(settingsGroupC)); - const QVariantList recentFiles = s->value(QLatin1String(filesKeyC)).toList(); - const QStringList recentEditorIds = s->value(QLatin1String(editorsKeyC)).toStringList(); + s->beginGroup(settingsGroupC); + const QVariantList recentFiles = s->value(filesKeyC).toList(); + const QStringList recentEditorIds = s->value(editorsKeyC).toStringList(); s->endGroup(); restoreRecentFiles(recentFiles, recentEditorIds); - s->beginGroup(QLatin1String(directoryGroupC)); + s->beginGroup(directoryGroupC); d->m_projectsDirectory = FilePath::fromSettings( - s->value(QLatin1String(projectDirectoryKeyC), PathChooser::homePath().toSettings())); + s->value(projectDirectoryKeyC, PathChooser::homePath().toSettings())); d->m_useProjectsDirectory - = s->value(QLatin1String(useProjectDirectoryKeyC), kUseProjectsDirectoryDefault).toBool(); + = s->value(useProjectDirectoryKeyC, kUseProjectsDirectoryDefault).toBool(); s->endGroup(); } diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index ea40dd43b49..67b65659a4b 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "core_global.h" #include <utils/filepath.h> #include <utils/id.h> @@ -20,7 +20,7 @@ class IDocument; namespace Internal { class DocumentManagerPrivate; -class MainWindow; +class ICorePrivate; } class CORE_EXPORT DocumentManager : public QObject @@ -152,8 +152,8 @@ private: void updateSaveAll(); static void registerSaveAllAction(); - friend class Core::Internal::MainWindow; - friend class Core::Internal::DocumentManagerPrivate; + friend class Internal::DocumentManagerPrivate; + friend class Internal::ICorePrivate; }; class CORE_EXPORT FileChangeBlocker diff --git a/src/plugins/coreplugin/editmode.h b/src/plugins/coreplugin/editmode.h index 06bfdc3bdb9..1e7d36d5e8e 100644 --- a/src/plugins/coreplugin/editmode.h +++ b/src/plugins/coreplugin/editmode.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/imode.h> +#include "imode.h" QT_BEGIN_NAMESPACE class QSplitter; diff --git a/src/plugins/coreplugin/editormanager/documentmodel.cpp b/src/plugins/coreplugin/editormanager/documentmodel.cpp index 876b22b1281..e8183401bf6 100644 --- a/src/plugins/coreplugin/editormanager/documentmodel.cpp +++ b/src/plugins/coreplugin/editormanager/documentmodel.cpp @@ -470,10 +470,8 @@ void DocumentModelPrivate::removeAllSuspendedEntries(PinnedFileRemovalPolicy pin QSet<QString> displayNames; for (DocumentModel::Entry *entry : std::as_const(d->m_entries)) { const QString displayName = entry->plainDisplayName(); - if (displayNames.contains(displayName)) - continue; - displayNames.insert(displayName); - d->disambiguateDisplayNames(entry); + if (Utils::insert(displayNames, displayName)) + d->disambiguateDisplayNames(entry); } } @@ -592,7 +590,7 @@ QList<IEditor *> DocumentModel::editorsForFilePath(const Utils::FilePath &filePa IDocument *document = documentForFilePath(filePath); if (document) return editorsForDocument(document); - return QList<IEditor *>(); + return {}; } DocumentModel::Entry *DocumentModel::entryAtRow(int row) diff --git a/src/plugins/coreplugin/editormanager/editorarea.cpp b/src/plugins/coreplugin/editormanager/editorarea.cpp index e807fb55dd4..2b47988c3b3 100644 --- a/src/plugins/coreplugin/editormanager/editorarea.cpp +++ b/src/plugins/coreplugin/editormanager/editorarea.cpp @@ -6,10 +6,11 @@ #include "editormanager.h" #include "ieditor.h" -#include <coreplugin/coreconstants.h> -#include <coreplugin/icontext.h> -#include <coreplugin/idocument.h> -#include <coreplugin/icore.h> +#include "../coreconstants.h" +#include "../icontext.h" +#include "../icore.h" +#include "../idocument.h" + #include <utils/qtcassert.h> #include <QApplication> diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 261146797b4..ad31e04ea98 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -9,6 +9,8 @@ #include "editorview.h" #include "editorwindow.h" #include "ieditor.h" +#include "ieditorfactory.h" +#include "ieditorfactory_p.h" #include "openeditorsview.h" #include "openeditorswindow.h" #include "../actionmanager/actioncontainer.h" @@ -20,9 +22,6 @@ #include "../dialogs/readonlyfilesdialog.h" #include "../diffservice.h" #include "../documentmanager.h" -#include "../editormanager/ieditorfactory.h" -#include "../editormanager/ieditorfactory_p.h" -#include "../editormanager/iexternaleditor.h" #include "../fileutils.h" #include "../findplaceholder.h" #include "../icore.h" @@ -33,10 +32,9 @@ #include "../outputpanemanager.h" #include "../rightpane.h" #include "../settingsdatabase.h" +#include "../systemsettings.h" #include "../vcsmanager.h" -#include <app/app_version.h> - #include <extensionsystem/pluginmanager.h> #include <utils/algorithm.h> @@ -76,12 +74,14 @@ #include <QTextCodec> #include <QTimer> #include <QVBoxLayout> -#include <algorithm> #if defined(WITH_TESTS) #include <QTest> #endif +#include <algorithm> +#include <memory> + enum { debugEditorManager=0 }; static const char kCurrentDocumentPrefix[] = "CurrentDocument"; @@ -90,15 +90,6 @@ static const char kCurrentDocumentYPos[] = "CurrentDocument:YPos"; static const char kMakeWritableWarning[] = "Core.EditorManager.MakeWritable"; static const char documentStatesKey[] = "EditorManager/DocumentStates"; -static const char reloadBehaviorKey[] = "EditorManager/ReloadBehavior"; -static const char autoSaveEnabledKey[] = "EditorManager/AutoSaveEnabled"; -static const char autoSaveIntervalKey[] = "EditorManager/AutoSaveInterval"; -static const char autoSaveAfterRefactoringKey[] = "EditorManager/AutoSaveAfterRefactoring"; -static const char autoSuspendEnabledKey[] = "EditorManager/AutoSuspendEnabled"; -static const char autoSuspendMinDocumentCountKey[] = "EditorManager/AutoSuspendMinDocuments"; -static const char warnBeforeOpeningBigTextFilesKey[] = "EditorManager/WarnBeforeOpeningBigTextFiles"; -static const char bigTextFileSizeLimitKey[] = "EditorManager/BigTextFileSizeLimitInMB"; -static const char maxRecentFilesKey[] = "EditorManager/MaxRecentFiles"; static const char fileSystemCaseSensitivityKey[] = "Core/FileSystemCaseSensitivity"; static const char preferredEditorFactoriesKey[] = "EditorManager/PreferredEditorFactories"; @@ -440,7 +431,7 @@ void EditorManagerPrivate::init() ActionContainer *mfile = ActionManager::actionContainer(Constants::M_FILE); // Revert to saved - m_revertToSavedAction->setIcon(QIcon::fromTheme("document-revert")); + m_revertToSavedAction->setIcon(Icon::fromTheme("document-revert")); Command *cmd = ActionManager::registerAction(m_revertToSavedAction, Constants::REVERTTOSAVED, editManagerContext); cmd->setAttribute(Command::CA_UpdateText); @@ -732,7 +723,7 @@ EditorArea *EditorManagerPrivate::mainEditorArea() bool EditorManagerPrivate::skipOpeningBigTextFile(const FilePath &filePath) { - if (!d->m_settings.warnBeforeOpeningBigFilesEnabled) + if (!systemSettings().warnBeforeOpeningBigFiles()) return false; if (!filePath.exists()) @@ -745,7 +736,7 @@ bool EditorManagerPrivate::skipOpeningBigTextFile(const FilePath &filePath) const qint64 fileSize = filePath.fileSize(); const double fileSizeInMB = fileSize / 1000.0 / 1000.0; - if (fileSizeInMB > d->m_settings.bigFileSizeLimitInMB + if (fileSizeInMB > systemSettings().bigFileSizeLimitInMB() && fileSize < EditorManager::maxTextFileSize()) { const QString title = ::Core::Tr::tr("Continue Opening Huge Text File?"); const QString text = ::Core::Tr::tr( @@ -761,7 +752,7 @@ bool EditorManagerPrivate::skipOpeningBigTextFile(const FilePath &filePath) QMessageBox::StandardButton clickedButton = CheckableMessageBox::question(ICore::dialogParent(), title, text, decider); - setWarnBeforeOpeningBigFilesEnabled(askAgain); + systemSettings().warnBeforeOpeningBigFiles.setValue(askAgain); return clickedButton != QMessageBox::Yes; } @@ -806,12 +797,10 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file realFp = filePath; } - EditorTypeList factories = EditorType::preferredEditorTypes(filePath); - if (!(flags & EditorManager::AllowExternalEditor)) { - factories = Utils::filtered(factories, [](EditorType *type) { - return type->asEditorFactory() != nullptr; - }); - } + EditorFactories factories = IEditorFactory::preferredEditorTypes(filePath); + if (!(flags & EditorManager::AllowExternalEditor)) + factories = Utils::filtered(factories, &IEditorFactory::isInternalEditor); + if (factories.isEmpty()) { Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath); QMessageBox msgbox(QMessageBox::Critical, ::Core::Tr::tr("File Error"), @@ -822,9 +811,9 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file return nullptr; } if (editorId.isValid()) { - EditorType *factory = EditorType::editorTypeForId(editorId); + IEditorFactory *factory = IEditorFactory::editorFactoryForId(editorId); if (factory) { - QTC_CHECK(factory->asEditorFactory() || (flags & EditorManager::AllowExternalEditor)); + QTC_CHECK(factory->isInternalEditor() || (flags & EditorManager::AllowExternalEditor)); factories.removeOne(factory); factories.push_front(factory); } @@ -833,12 +822,12 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file IEditor *editor = nullptr; auto overrideCursor = Utils::OverrideCursor(QCursor(Qt::WaitCursor)); - EditorType *factory = factories.takeFirst(); + IEditorFactory *factory = factories.takeFirst(); while (factory) { QString errorString; - if (factory->asEditorFactory()) { - editor = createEditor(factory->asEditorFactory(), filePath); + if (factory->isInternalEditor()) { + editor = createEditor(factory, filePath); if (!editor) { factory = factories.isEmpty() ? nullptr : factories.takeFirst(); continue; @@ -866,10 +855,10 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file // can happen e.g. when trying to open an completely empty .qrc file QTC_CHECK(openResult == IDocument::OpenResult::CannotHandle); } else { - QTC_ASSERT(factory->asExternalEditor(), + QTC_ASSERT(factory->isExternalEditor(), factory = factories.isEmpty() ? nullptr : factories.takeFirst(); continue); - if (factory->asExternalEditor()->startEditor(filePath, &errorString)) + if (factory->startEditor(filePath, &errorString)) break; } @@ -882,12 +871,15 @@ IEditor *EditorManagerPrivate::openEditor(EditorView *view, const FilePath &file QMessageBox::Open | QMessageBox::Cancel, ICore::dialogParent()); - EditorType *selectedFactory = nullptr; + IEditorFactory *selectedFactory = nullptr; if (!factories.isEmpty()) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + msgbox.setOptions(QMessageBox::Option::DontUseNativeDialog); +#endif auto button = qobject_cast<QPushButton *>(msgbox.button(QMessageBox::Open)); QTC_ASSERT(button, return nullptr); auto menu = new QMenu(button); - for (EditorType *factory : std::as_const(factories)) { + for (IEditorFactory *factory : std::as_const(factories)) { QAction *action = menu->addAction(factory->displayName()); connect(action, &QAction::triggered, &msgbox, [&selectedFactory, factory, &msgbox] { selectedFactory = factory; @@ -1157,7 +1149,7 @@ Id EditorManagerPrivate::getOpenWithEditorId(const Utils::FilePath &fileName, bo QList<Id> allEditorIds; QStringList allEditorDisplayNames; // Built-in - const EditorTypeList editors = EditorType::preferredEditorTypes(fileName); + const EditorFactories editors = IEditorFactory::preferredEditorTypes(fileName); const int size = editors.size(); allEditorDisplayNames.reserve(size); for (int i = 0; i < size; i++) { @@ -1175,39 +1167,36 @@ Id EditorManagerPrivate::getOpenWithEditorId(const Utils::FilePath &fileName, bo return Id(); const Id selectedId = allEditorIds.at(dialog.editor()); if (isExternalEditor) { - EditorType *type = EditorType::editorTypeForId(selectedId); - *isExternalEditor = type && type->asExternalEditor() != nullptr; + IEditorFactory *type = IEditorFactory::editorFactoryForId(selectedId); + *isExternalEditor = type && type->isExternalEditor(); } return selectedId; } -static QMap<QString, QVariant> toMap(const QHash<Utils::MimeType, EditorType *> &hash) +static QMap<QString, QVariant> toMap(const QHash<QString, IEditorFactory *> &hash) { QMap<QString, QVariant> map; auto it = hash.begin(); const auto end = hash.end(); while (it != end) { - map.insert(it.key().name(), it.value()->id().toSetting()); + map.insert(it.key(), it.value()->id().toSetting()); ++it; } return map; } -static QHash<Utils::MimeType, EditorType *> fromMap(const QMap<QString, QVariant> &map) +static QHash<QString, IEditorFactory *> fromMap(const QMap<QString, QVariant> &map) { - const EditorTypeList factories = EditorType::allEditorTypes(); - QHash<Utils::MimeType, EditorType *> hash; + const EditorFactories factories = IEditorFactory::allEditorFactories(); + QHash<QString, IEditorFactory *> hash; auto it = map.begin(); const auto end = map.end(); while (it != end) { - const Utils::MimeType mimeType = Utils::mimeTypeForName(it.key()); - if (mimeType.isValid()) { - const Id factoryId = Id::fromSetting(it.value()); - EditorType *factory = Utils::findOrDefault(factories, - Utils::equal(&EditorType::id, factoryId)); - if (factory) - hash.insert(mimeType, factory); - } + const Id factoryId = Id::fromSetting(it.value()); + IEditorFactory *factory = Utils::findOrDefault(factories, + Utils::equal(&IEditorFactory::id, factoryId)); + if (factory) + hash.insert(it.key(), factory); ++it; } return hash; @@ -1215,53 +1204,16 @@ static QHash<Utils::MimeType, EditorType *> fromMap(const QMap<QString, QVariant void EditorManagerPrivate::saveSettings() { - ICore::settingsDatabase()->setValue(documentStatesKey, d->m_editorStates); + SettingsDatabase::setValue(documentStatesKey, d->m_editorStates); - const Settings def; QtcSettings *qsettings = ICore::settings(); - qsettings->setValueWithDefault(reloadBehaviorKey, - int(d->m_settings.reloadSetting), - int(def.reloadSetting)); - qsettings->setValueWithDefault(autoSaveEnabledKey, - d->m_settings.autoSaveEnabled, - def.autoSaveEnabled); - qsettings->setValueWithDefault(autoSaveIntervalKey, - d->m_settings.autoSaveInterval, - def.autoSaveInterval); - qsettings->setValueWithDefault(autoSaveAfterRefactoringKey, - d->m_settings.autoSaveAfterRefactoring, - def.autoSaveAfterRefactoring); - qsettings->setValueWithDefault(autoSuspendEnabledKey, - d->m_settings.autoSuspendEnabled, - def.autoSuspendEnabled); - qsettings->setValueWithDefault(autoSuspendMinDocumentCountKey, - d->m_settings.autoSuspendMinDocumentCount, - def.autoSuspendMinDocumentCount); - qsettings->setValueWithDefault(warnBeforeOpeningBigTextFilesKey, - d->m_settings.warnBeforeOpeningBigFilesEnabled, - def.warnBeforeOpeningBigFilesEnabled); - qsettings->setValueWithDefault(bigTextFileSizeLimitKey, - d->m_settings.bigFileSizeLimitInMB, - def.bigFileSizeLimitInMB); - qsettings->setValueWithDefault(maxRecentFilesKey, - d->m_settings.maxRecentFiles, - def.maxRecentFiles); qsettings->setValueWithDefault(preferredEditorFactoriesKey, toMap(userPreferredEditorTypes())); } void EditorManagerPrivate::readSettings() { - Settings def; - QSettings *qs = ICore::settings(); - d->m_settings.warnBeforeOpeningBigFilesEnabled - = qs->value(warnBeforeOpeningBigTextFilesKey, def.warnBeforeOpeningBigFilesEnabled).toBool(); - d->m_settings.bigFileSizeLimitInMB - = qs->value(bigTextFileSizeLimitKey, def.bigFileSizeLimitInMB).toInt(); - - const int maxRecentFiles = qs->value(maxRecentFilesKey, def.maxRecentFiles).toInt(); - if (maxRecentFiles > 0) - d->m_settings.maxRecentFiles = maxRecentFiles; + QtcSettings *qs = ICore::settings(); const Qt::CaseSensitivity defaultSensitivity = OsSpecificAspects::fileNameCaseSensitivity( HostOsInfo::hostOs()); @@ -1271,33 +1223,19 @@ void EditorManagerPrivate::readSettings() else HostOsInfo::setOverrideFileNameCaseSensitivity(sensitivity); - const QHash<Utils::MimeType, EditorType *> preferredEditorFactories = fromMap( + const QHash<QString, IEditorFactory *> preferredEditorFactories = fromMap( qs->value(preferredEditorFactoriesKey).toMap()); setUserPreferredEditorTypes(preferredEditorFactories); - SettingsDatabase *settings = ICore::settingsDatabase(); - if (settings->contains(documentStatesKey)) { - d->m_editorStates = settings->value(documentStatesKey) - .value<QMap<QString, QVariant> >(); + if (SettingsDatabase::contains(documentStatesKey)) { + d->m_editorStates = SettingsDatabase::value(documentStatesKey) + .value<QMap<QString, QVariant>>(); } - d->m_settings.reloadSetting = IDocument::ReloadSetting( - qs->value(reloadBehaviorKey, def.reloadSetting).toInt()); - - d->m_settings.autoSaveEnabled = qs->value(autoSaveEnabledKey, def.autoSaveEnabled).toBool(); - d->m_settings.autoSaveInterval = qs->value(autoSaveIntervalKey, def.autoSaveInterval).toInt(); - d->m_settings.autoSaveAfterRefactoring = qs->value(autoSaveAfterRefactoringKey, - def.autoSaveAfterRefactoring).toBool(); - - d->m_settings.autoSuspendEnabled = qs->value(autoSuspendEnabledKey, def.autoSuspendEnabled) - .toBool(); - d->m_settings.autoSuspendMinDocumentCount - = qs->value(autoSuspendMinDocumentCountKey, def.autoSuspendMinDocumentCount).toInt(); - updateAutoSave(); } -Qt::CaseSensitivity EditorManagerPrivate::readFileSystemSensitivity(QSettings *settings) +Qt::CaseSensitivity EditorManagerPrivate::readFileSystemSensitivity(QtcSettings *settings) { const Qt::CaseSensitivity defaultSensitivity = OsSpecificAspects::fileNameCaseSensitivity( HostOsInfo::hostOs()); @@ -1325,94 +1263,12 @@ void EditorManagerPrivate::writeFileSystemSensitivity(Utils::QtcSettings *settin HostOsInfo::hostOs()))); } -void EditorManagerPrivate::setAutoSaveEnabled(bool enabled) -{ - d->m_settings.autoSaveEnabled = enabled; - updateAutoSave(); -} - -bool EditorManagerPrivate::autoSaveEnabled() -{ - return d->m_settings.autoSaveEnabled; -} - -void EditorManagerPrivate::setAutoSaveInterval(int interval) -{ - d->m_settings.autoSaveInterval = interval; - updateAutoSave(); -} - -int EditorManagerPrivate::autoSaveInterval() -{ - return d->m_settings.autoSaveInterval; -} - -void EditorManagerPrivate::setAutoSaveAfterRefactoring(bool enabled) -{ - d->m_settings.autoSaveAfterRefactoring = enabled; -} - -bool EditorManagerPrivate::autoSaveAfterRefactoring() -{ - return d->m_settings.autoSaveAfterRefactoring; -} - -void EditorManagerPrivate::setAutoSuspendEnabled(bool enabled) -{ - d->m_settings.autoSuspendEnabled = enabled; -} - -bool EditorManagerPrivate::autoSuspendEnabled() -{ - return d->m_settings.autoSuspendEnabled; -} - -void EditorManagerPrivate::setAutoSuspendMinDocumentCount(int count) -{ - d->m_settings.autoSuspendMinDocumentCount = count; -} - -int EditorManagerPrivate::autoSuspendMinDocumentCount() -{ - return d->m_settings.autoSuspendMinDocumentCount; -} - -bool EditorManagerPrivate::warnBeforeOpeningBigFilesEnabled() -{ - return d->m_settings.warnBeforeOpeningBigFilesEnabled; -} - -void EditorManagerPrivate::setWarnBeforeOpeningBigFilesEnabled(bool enabled) -{ - d->m_settings.warnBeforeOpeningBigFilesEnabled = enabled; -} - -int EditorManagerPrivate::bigFileSizeLimit() -{ - return d->m_settings.bigFileSizeLimitInMB; -} - -void EditorManagerPrivate::setMaxRecentFiles(int count) -{ - d->m_settings.maxRecentFiles = count; -} - -int EditorManagerPrivate::maxRecentFiles() -{ - return d->m_settings.maxRecentFiles; -} - -void EditorManagerPrivate::setBigFileSizeLimit(int limitInMB) -{ - d->m_settings.bigFileSizeLimitInMB = limitInMB; -} - -EditorFactoryList EditorManagerPrivate::findFactories(Id editorId, const FilePath &filePath) +EditorFactories EditorManagerPrivate::findFactories(Id editorId, const FilePath &filePath) { if (debugEditorManager) qDebug() << Q_FUNC_INFO << editorId.name() << filePath; - EditorFactoryList factories; + EditorFactories factories; if (!editorId.isValid()) { factories = IEditorFactory::preferredEditorFactories(filePath); } else { @@ -1442,7 +1298,7 @@ IEditor *EditorManagerPrivate::createEditor(IEditorFactory *factory, const FileP connect(document, &IDocument::changed, d, [document] { d->handleDocumentStateChange(document); }); - emit m_instance->editorCreated(editor, filePath.toString()); + emit m_instance->editorCreated(editor, filePath); } return editor; @@ -1531,7 +1387,7 @@ IEditor *EditorManagerPrivate::duplicateEditor(IEditor *editor) return nullptr; IEditor *duplicate = editor->duplicate(); - emit m_instance->editorCreated(duplicate, duplicate->document()->filePath().toString()); + emit m_instance->editorCreated(duplicate, duplicate->document()->filePath()); addEditor(duplicate); return duplicate; } @@ -2005,8 +1861,8 @@ void EditorManagerPrivate::addDocumentToRecentFiles(IDocument *document) void EditorManagerPrivate::updateAutoSave() { - if (d->m_settings.autoSaveEnabled) - d->m_autoSaveTimer->start(d->m_settings.autoSaveInterval * (60 * 1000)); + if (systemSettings().autoSaveModifiedFiles()) + d->m_autoSaveTimer->start(systemSettings().autoSaveInterval() * (60 * 1000)); else d->m_autoSaveTimer->stop(); } @@ -2057,7 +1913,7 @@ void EditorManagerPrivate::setupSaveActions(IDocument *document, QAction *saveAc QAction *saveAsAction, QAction *revertToSavedAction) { const bool hasFile = document && !document->filePath().isEmpty(); - saveAction->setEnabled(hasFile && document->isModified()); + saveAction->setEnabled(document && (document->isModified() || !hasFile)); saveAsAction->setEnabled(document && document->isSaveAsAllowed()); revertToSavedAction->setEnabled(hasFile); @@ -2159,7 +2015,7 @@ void EditorManagerPrivate::updateWindowTitleForDocument(IDocument *document, QWi if (!windowTitle.isEmpty()) windowTitle.append(dashSep); - windowTitle.append(Core::Constants::IDE_DISPLAY_NAME); + windowTitle.append(QGuiApplication::applicationDisplayName()); window->window()->setWindowTitle(windowTitle); window->window()->setWindowFilePath(filePath.path()); @@ -2469,9 +2325,7 @@ bool EditorManagerPrivate::saveDocument(IDocument *document) document->checkPermissions(); - const QString fileName = document->filePath().toString(); - - if (fileName.isEmpty()) + if (document->filePath().isEmpty()) return saveDocumentAs(document); bool success = false; @@ -2595,20 +2449,21 @@ void EditorManagerPrivate::revertToSaved(IDocument *document) void EditorManagerPrivate::autoSuspendDocuments() { - if (!d->m_settings.autoSuspendEnabled) + if (!systemSettings().autoSuspendEnabled()) return; auto visibleDocuments = Utils::transform<QSet>(EditorManager::visibleEditors(), &IEditor::document); int keptEditorCount = 0; QList<IDocument *> documentsToSuspend; + const int minDocumentCount = systemSettings().autoSuspendMinDocumentCount(); for (const EditLocation &editLocation : std::as_const(d->m_globalHistory)) { IDocument *document = editLocation.document; if (!document || !document->isSuspendAllowed() || document->isModified() || document->isTemporary() || document->filePath().isEmpty() || visibleDocuments.contains(document)) continue; - if (keptEditorCount >= d->m_settings.autoSuspendMinDocumentCount) + if (keptEditorCount >= minDocumentCount) documentsToSuspend.append(document); else ++keptEditorCount; @@ -2950,11 +2805,11 @@ void EditorManager::populateOpenWithMenu(QMenu *menu, const FilePath &filePath) { menu->clear(); - const EditorTypeList factories = IEditorFactory::preferredEditorTypes(filePath); + const EditorFactories factories = IEditorFactory::preferredEditorTypes(filePath); const bool anyMatches = !factories.empty(); if (anyMatches) { // Add all suitable editors - for (EditorType *editorType : factories) { + for (IEditorFactory *editorType : factories) { const Id editorId = editorType->id(); // Add action to open with this very editor factory QString const actionTitle = editorType->displayName(); @@ -2964,8 +2819,8 @@ void EditorManager::populateOpenWithMenu(QMenu *menu, const FilePath &filePath) // crashes happen, because the editor instance is deleted by openEditorWith // while the menu is still being processed. connect(action, &QAction::triggered, d, [filePath, editorId] { - EditorType *type = EditorType::editorTypeForId(editorId); - if (type && type->asExternalEditor()) + IEditorFactory *type = IEditorFactory::editorFactoryForId(editorId); + if (type && type->isExternalEditor()) EditorManager::openExternalEditor(filePath, editorId); else EditorManagerPrivate::openEditorWith(filePath, editorId); @@ -2975,12 +2830,33 @@ void EditorManager::populateOpenWithMenu(QMenu *menu, const FilePath &filePath) menu->setEnabled(anyMatches); } +void EditorManager::runWithTemporaryEditor(const Utils::FilePath &filePath, + const std::function<void (IEditor *)> &callback) +{ + const MimeType mt = mimeTypeForFile(filePath, MimeMatchMode::MatchDefaultAndRemote); + const QList<IEditorFactory *> factories = IEditorFactory::defaultEditorFactories(mt); + for (IEditorFactory * const factory : factories) { + QTC_ASSERT(factory, continue); + if (!factory->isInternalEditor()) + continue; + std::unique_ptr<IEditor> editor(factory->createEditor()); + if (!editor) + continue; + editor->document()->setTemporary(true); + if (editor->document()->open(nullptr, filePath, filePath) != IDocument::OpenResult::Success) + continue; + callback(editor.get()); + break; + } +} + /*! Returns reload behavior settings. */ IDocument::ReloadSetting EditorManager::reloadSetting() { - return d->m_settings.reloadSetting; + // FIXME: Used TypedSelectionAspect once we have that. + return IDocument::ReloadSetting(systemSettings().reloadSetting.value()); } /*! @@ -2990,7 +2866,7 @@ IDocument::ReloadSetting EditorManager::reloadSetting() */ void EditorManager::setReloadSetting(IDocument::ReloadSetting behavior) { - d->m_settings.reloadSetting = behavior; + systemSettings().reloadSetting.setValue(behavior); } /*! @@ -3207,7 +3083,7 @@ bool EditorManager::isAutoSaveFile(const QString &filePath) bool EditorManager::autoSaveAfterRefactoring() { - return EditorManagerPrivate::autoSaveAfterRefactoring(); + return systemSettings().autoSaveAfterRefactoring(); } /*! @@ -3221,8 +3097,11 @@ bool EditorManager::autoSaveAfterRefactoring() */ bool EditorManager::openExternalEditor(const FilePath &filePath, Id editorId) { - IExternalEditor *ee = Utils::findOrDefault(IExternalEditor::allExternalEditors(), - Utils::equal(&IExternalEditor::id, editorId)); + IEditorFactory *ee = Utils::findOrDefault(IEditorFactory::allEditorFactories(), + [editorId](IEditorFactory *factory) { + return factory->isExternalEditor() && factory->id() == editorId; + }); + if (!ee) return false; QString errorMessage; @@ -3346,7 +3225,7 @@ IEditor *EditorManager::openEditorWithContents(Id editorId, } const FilePath filePath = FilePath::fromString(title); - EditorFactoryList factories = EditorManagerPrivate::findFactories(editorId, filePath); + EditorFactories factories = EditorManagerPrivate::findFactories(editorId, filePath); if (factories.isEmpty()) return nullptr; @@ -3466,7 +3345,7 @@ void EditorManager::setLastEditLocation(const IEditor* editor) location.document = document; location.filePath = document->filePath(); location.id = document->id(); - location.state = QVariant(state); + location.state = state; d->m_globalLastEditLocation = location; } @@ -3694,7 +3573,7 @@ void EditorManager::hideEditorStatusBar(const QString &id) */ QTextCodec *EditorManager::defaultTextCodec() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); const QByteArray codecName = settings->value(Constants::SETTINGS_DEFAULTTEXTENCODING).toByteArray(); if (QTextCodec *candidate = QTextCodec::codecForName(codecName)) @@ -3715,7 +3594,7 @@ QTextCodec *EditorManager::defaultTextCodec() */ TextFileFormat::LineTerminationMode EditorManager::defaultLineEnding() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); const int defaultLineTerminator = settings->value(Constants::SETTINGS_DEFAULT_LINE_TERMINATOR, TextFileFormat::LineTerminationMode::NativeLineTerminator).toInt(); diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 09218f42c3e..aea9e2eebf1 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -29,7 +29,7 @@ namespace Core { class IDocument; class LocatorFilterEntry; -namespace Internal { class MainWindow; } +namespace Internal { class ICorePrivate; } class CORE_EXPORT EditorManagerPlaceHolder final : public QWidget { @@ -143,6 +143,9 @@ public: static void addNativeDirAndOpenWithActions(QMenu *contextMenu, DocumentModel::Entry *entry); static void populateOpenWithMenu(QMenu *menu, const Utils::FilePath &filePath); + static void runWithTemporaryEditor(const Utils::FilePath &filePath, + const std::function<void(IEditor *)> &callback); + public: // for tests static IDocument::ReloadSetting reloadSetting(); static void setReloadSetting(IDocument::ReloadSetting behavior); @@ -151,7 +154,7 @@ signals: void currentEditorChanged(Core::IEditor *editor); void currentDocumentStateChanged(); void documentStateChanged(Core::IDocument *document); - void editorCreated(Core::IEditor *editor, const QString &fileName); + void editorCreated(Core::IEditor *editor, const Utils::FilePath &filePath); void editorOpened(Core::IEditor *editor); void documentOpened(Core::IDocument *document); void editorAboutToClose(Core::IEditor *editor); @@ -185,7 +188,7 @@ private: explicit EditorManager(QObject *parent); ~EditorManager() override; - friend class Core::Internal::MainWindow; + friend class Internal::ICorePrivate; }; } // namespace Core diff --git a/src/plugins/coreplugin/editormanager/editormanager_p.h b/src/plugins/coreplugin/editormanager/editormanager_p.h index 6ad61d3df47..25c7007b5be 100644 --- a/src/plugins/coreplugin/editormanager/editormanager_p.h +++ b/src/plugins/coreplugin/editormanager/editormanager_p.h @@ -10,7 +10,7 @@ #include "ieditor.h" #include "ieditorfactory.h" -#include <coreplugin/idocument.h> +#include "../idocument.h" #include <QList> #include <QObject> @@ -96,25 +96,9 @@ public: static void saveSettings(); static void readSettings(); - static Qt::CaseSensitivity readFileSystemSensitivity(QSettings *settings); + static Qt::CaseSensitivity readFileSystemSensitivity(Utils::QtcSettings *settings); static void writeFileSystemSensitivity(Utils::QtcSettings *settings, Qt::CaseSensitivity sensitivity); - static void setAutoSaveEnabled(bool enabled); - static bool autoSaveEnabled(); - static void setAutoSaveInterval(int interval); - static int autoSaveInterval(); - static void setAutoSaveAfterRefactoring(bool enabled); - static bool autoSaveAfterRefactoring(); - static void setAutoSuspendEnabled(bool enabled); - static bool autoSuspendEnabled(); - static void setAutoSuspendMinDocumentCount(int count); - static int autoSuspendMinDocumentCount(); - static void setWarnBeforeOpeningBigFilesEnabled(bool enabled); - static bool warnBeforeOpeningBigFilesEnabled(); - static void setBigFileSizeLimit(int limitInMB); - static int bigFileSizeLimit(); - static void setMaxRecentFiles(int count); - static int maxRecentFiles(); static EditorWindow *createEditorWindow(); static void splitNewWindow(Internal::EditorView *view); @@ -132,6 +116,8 @@ public: static void setPlaceholderText(const QString &text); static QString placeholderText(); + static void updateAutoSave(); + public slots: static bool saveDocument(Core::IDocument *document); static bool saveDocumentAs(Core::IDocument *document); @@ -181,7 +167,7 @@ private: static OpenEditorsWindow *windowPopup(); static void showPopupOrSelectDocument(); - static EditorFactoryList findFactories(Utils::Id editorId, const Utils::FilePath &filePath); + static EditorFactories findFactories(Utils::Id editorId, const Utils::FilePath &filePath); static IEditor *createEditor(IEditorFactory *factory, const Utils::FilePath &filePath); static void addEditor(IEditor *editor); static void removeEditor(IEditor *editor, bool removeSusependedEntry); @@ -191,7 +177,6 @@ private: static EditorArea *findEditorArea(const EditorView *view, int *areaIndex = nullptr); static IEditor *pickUnusedEditor(Internal::EditorView **foundView = nullptr); static void addDocumentToRecentFiles(IDocument *document); - static void updateAutoSave(); static void updateMakeWritableWarning(); static void setupSaveActions(IDocument *document, QAction *saveAction, QAction *saveAsAction, QAction *revertToSavedAction); @@ -263,23 +248,6 @@ private: EditorManager::WindowTitleHandler m_sessionTitleHandler; EditorManager::WindowTitleHandler m_titleVcsTopicHandler; - struct Settings - { - IDocument::ReloadSetting reloadSetting = IDocument::AlwaysAsk; - - bool autoSaveEnabled = true; - int autoSaveInterval = 5; - - bool autoSuspendEnabled = true; - int autoSuspendMinDocumentCount = 10; - - bool autoSaveAfterRefactoring = true; - bool warnBeforeOpeningBigFilesEnabled = true; - int bigFileSizeLimitInMB = 5; - int maxRecentFiles = 8; - }; - - Settings m_settings; QString m_placeholderText; QList<std::function<bool(IEditor *)>> m_closeEditorListeners; }; diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index defe03271de..9edef863c9a 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -7,14 +7,13 @@ #include "editormanager_p.h" #include "documentmodel.h" #include "documentmodel_p.h" +#include "../actionmanager/actionmanager.h" +#include "../editormanager/ieditor.h" +#include "../editortoolbar.h" +#include "../findplaceholder.h" +#include "../icore.h" +#include "../minisplitter.h" -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/editortoolbar.h> -#include <coreplugin/findplaceholder.h> -#include <coreplugin/icore.h> -#include <coreplugin/locator/locatorconstants.h> -#include <coreplugin/minisplitter.h> #include <utils/algorithm.h> #include <utils/infobar.h> #include <utils/qtcassert.h> @@ -241,12 +240,14 @@ void EditorView::updateEditorHistory(IEditor *editor, QList<EditLocation> &histo location.document = document; location.filePath = document->filePath(); location.id = document->id(); - location.state = QVariant(state); + location.state = state; for (int i = 0; i < history.size(); ++i) { const EditLocation &item = history.at(i); - if (item.document == document - || (!item.document && !DocumentModel::indexOfFilePath(item.filePath))) { + // remove items that refer to the same document/file, + // or that are no longer in the "open documents" + if (item.document == document || (!item.document && item.filePath == document->filePath()) + || (!item.document && !DocumentModel::indexOfFilePath(item.filePath))) { history.removeAt(i--); } } @@ -472,7 +473,7 @@ void EditorView::addCurrentPositionToNavigationHistory(const QByteArray &saveSta location.document = document; location.filePath = document->filePath(); location.id = document->id(); - location.state = QVariant(state); + location.state = state; m_currentNavigationHistoryPosition = qMin(m_currentNavigationHistoryPosition, m_navigationHistory.size()); // paranoia m_navigationHistory.insert(m_currentNavigationHistoryPosition, location); ++m_currentNavigationHistoryPosition; @@ -527,7 +528,7 @@ void EditorView::updateCurrentPositionInNavigationHistory() location->document = document; location->filePath = document->filePath(); location->id = document->id(); - location->state = QVariant(editor->saveState()); + location->state = editor->saveState(); } static bool fileNameWasRemoved(const FilePath &filePath) @@ -558,7 +559,7 @@ void EditorView::goBackInNavigationHistory() continue; } } - editor->restoreState(location.state.toByteArray()); + editor->restoreState(location.state); break; } updateNavigatorActions(); @@ -589,7 +590,7 @@ void EditorView::goForwardInNavigationHistory() continue; } } - editor->restoreState(location.state.toByteArray()); + editor->restoreState(location.state); break; } if (m_currentNavigationHistoryPosition >= m_navigationHistory.size()) @@ -615,7 +616,7 @@ void EditorView::goToEditLocation(const EditLocation &location) } if (editor) { - editor->restoreState(location.state.toByteArray()); + editor->restoreState(location.state); } } @@ -876,6 +877,20 @@ void SplitterOrView::unsplit() emit splitStateChanged(); } +static QByteArrayList saveHistory(const QList<EditLocation> &history) +{ + const QList<EditLocation> nonTempHistory = Utils::filtered(history, [](const EditLocation &loc) { + const bool isTemp = loc.filePath.isEmpty() || (loc.document && loc.document->isTemporary()); + return !isTemp; + }); + return Utils::transform(nonTempHistory, [](const EditLocation &loc) { return loc.save(); }); +} + +static QList<EditLocation> loadHistory(const QByteArrayList &data) +{ + return Utils::transform(data, + [](const QByteArray &locData) { return EditLocation::load(locData); }); +} QByteArray SplitterOrView::saveState() const { @@ -902,16 +917,17 @@ QByteArray SplitterOrView::saveState() const if (!e) { stream << QByteArray("empty"); - } else if (e == EditorManager::currentEditor()) { - stream << QByteArray("currenteditor") - << e->document()->filePath().toString() - << e->document()->id().toString() - << e->saveState(); } else { - stream << QByteArray("editor") - << e->document()->filePath().toString() - << e->document()->id().toString() - << e->saveState(); + if (e == EditorManager::currentEditor()) { + stream << QByteArray("currenteditor") << e->document()->filePath().toString() + << e->document()->id().toString() << e->saveState(); + } else { + stream << QByteArray("editor") << e->document()->filePath().toString() + << e->document()->id().toString() << e->saveState(); + } + + // save edit history + stream << saveHistory(view()->editorHistory()); } } return bytes; @@ -934,8 +950,13 @@ void SplitterOrView::restoreState(const QByteArray &state) QString fileName; QString id; QByteArray editorState; + QByteArrayList historyData; stream >> fileName >> id >> editorState; - if (!QFile::exists(fileName)) + if (!stream.atEnd()) + stream >> historyData; + view()->m_editorHistory = loadHistory(historyData); + + if (!QFileInfo::exists(fileName)) return; IEditor *e = EditorManagerPrivate::openEditor(view(), FilePath::fromString(fileName), Id::fromString(id), EditorManager::IgnoreNavigationHistory @@ -956,3 +977,23 @@ void SplitterOrView::restoreState(const QByteArray &state) } } } + +QByteArray EditLocation::save() const +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << filePath.toFSPathString() << id << state; + return data; +} + +EditLocation EditLocation::load(const QByteArray &data) +{ + EditLocation loc; + QDataStream stream(data); + QString fp; + stream >> fp; + loc.filePath = FilePath::fromString(fp); + stream >> loc.id; + stream >> loc.state; + return loc; +} diff --git a/src/plugins/coreplugin/editormanager/editorview.h b/src/plugins/coreplugin/editormanager/editorview.h index 03424a93f2d..4bf172d26b5 100644 --- a/src/plugins/coreplugin/editormanager/editorview.h +++ b/src/plugins/coreplugin/editormanager/editorview.h @@ -39,11 +39,16 @@ class EditorToolBar; namespace Internal { -struct EditLocation { +class EditLocation +{ +public: + QByteArray save() const; + static EditLocation load(const QByteArray &data); + QPointer<IDocument> document; Utils::FilePath filePath; Utils::Id id; - QVariant state; + QByteArray state; }; class SplitterOrView; diff --git a/src/plugins/coreplugin/editormanager/editorwindow.cpp b/src/plugins/coreplugin/editormanager/editorwindow.cpp index 0044804e236..3e23a7285ed 100644 --- a/src/plugins/coreplugin/editormanager/editorwindow.cpp +++ b/src/plugins/coreplugin/editormanager/editorwindow.cpp @@ -5,13 +5,13 @@ #include "editorarea.h" #include "editormanager_p.h" +#include "../coreconstants.h" +#include "../icontext.h" +#include "../icore.h" +#include "../locator/locatormanager.h" +#include "../minisplitter.h" #include <aggregation/aggregate.h> -#include <coreplugin/coreconstants.h> -#include <coreplugin/icontext.h> -#include <coreplugin/icore.h> -#include <coreplugin/locator/locatormanager.h> -#include <coreplugin/minisplitter.h> #include <utils/qtcassert.h> #include <QStatusBar> diff --git a/src/plugins/coreplugin/editormanager/ieditor.h b/src/plugins/coreplugin/editormanager/ieditor.h index 01415844231..2d8bd4b6db4 100644 --- a/src/plugins/coreplugin/editormanager/ieditor.h +++ b/src/plugins/coreplugin/editormanager/ieditor.h @@ -3,8 +3,8 @@ #pragma once -#include <coreplugin/core_global.h> -#include <coreplugin/icontext.h> +#include "../core_global.h" +#include "../icontext.h" #include <QMetaType> diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp index 878f7a5b5d8..6b33314044e 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory.cpp +++ b/src/plugins/coreplugin/editormanager/ieditorfactory.cpp @@ -9,12 +9,35 @@ #include <utils/mimeutils.h> #include <utils/qtcassert.h> -#include <QFileInfo> - using namespace Utils; namespace Core { +/* Find the one best matching the mimetype passed in. + * Recurse over the parent classes of the mimetype to find them. */ +template<class EditorTypeLike> +static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType, + const QList<EditorTypeLike *> &allFactories, + QList<EditorTypeLike *> *list) +{ + QSet<EditorTypeLike *> matches; + Utils::visitMimeParents(mimeType, [&](const Utils::MimeType &mt) -> bool { + // check for matching factories + for (EditorTypeLike *factory : allFactories) { + if (!matches.contains(factory)) { + const QStringList mimeTypes = factory->mimeTypes(); + for (const QString &mimeName : mimeTypes) { + if (mt.matchesName(mimeName)) { + list->append(factory); + matches.insert(factory); + } + } + } + } + return true; // continue + }); +} + /*! \class Core::IEditorFactory \inheaderfile coreplugin/editormanager/ieditorfactory.h @@ -28,29 +51,26 @@ namespace Core { IEditorFactory is then asked to create an editor. Implementations should set the properties of the IEditorFactory subclass in - their constructor with EditorType::setId(), EditorType::setDisplayName(), - EditorType::setMimeTypes(), and setEditorCreator() + their constructor with IEditorFactory::setId(), IEditorFactory::setDisplayName(), + IEditorFactory::setMimeTypes(), and setEditorCreator() IEditorFactory instances automatically register themselves in \QC in their constructor. - \sa Core::EditorType + There are two varieties of editors: Internal and external. Internal editors + open within the main editing area of Qt Creator. An IEditorFactory defines + an internal editor by using the \c setEditorCreator function. External + editors are external applications and are defined by using the + \c setEditorStarter function. They are accessible by the user using + the \uicontrol{Open With} dialog + \sa Core::IEditor \sa Core::IDocument \sa Core::EditorManager */ /*! - \class Core::EditorType - \inheaderfile coreplugin/editormanager/ieditorfactory.h - \inmodule QtCreator - - \brief The EditorType class is the base class for Core::IEditorFactory and - Core::IExternalEditor. -*/ - -/*! - \fn void Core::EditorType::addMimeType(const QString &mimeType) + \fn void Core::IEditorFactory::addMimeType(const QString &mimeType) Adds \a mimeType to the list of MIME types supported by this editor type. @@ -59,7 +79,7 @@ namespace Core { */ /*! - \fn QString Core::EditorType::displayName() const + \fn QString Core::IEditorFactory::displayName() const Returns a user-visible description of the editor type. @@ -67,7 +87,7 @@ namespace Core { */ /*! - \fn Utils::Id Core::EditorType::id() const + \fn Utils::Id Core::IEditorFactory::id() const Returns the ID of the editors' document type. @@ -75,7 +95,7 @@ namespace Core { */ /*! - \fn QString Core::EditorType::mimeTypes() const + \fn QString Core::IEditorFactory::mimeTypes() const Returns the list of supported MIME types of this editor type. @@ -84,7 +104,7 @@ namespace Core { */ /*! - \fn void Core::EditorType::setDisplayName(const QString &displayName) + \fn void Core::IEditorFactory::setDisplayName(const QString &displayName) Sets the \a displayName of the editor type. This is for example shown in the \uicontrol {Open With} menu and the MIME type preferences. @@ -93,7 +113,7 @@ namespace Core { */ /*! - \fn void Core::EditorType::setId(Utils::Id id) + \fn void Core::IEditorFactory::setId(Utils::Id id) Sets the \a id of the editors' document type. This must be the same as the IDocument::id() of the documents returned by created editors. @@ -102,7 +122,7 @@ namespace Core { */ /*! - \fn void Core::EditorType::setMimeTypes(const QStringList &mimeTypes) + \fn void Core::IEditorFactory::setMimeTypes(const QStringList &mimeTypes) Sets the MIME types supported by the editor type to \a mimeTypes. @@ -110,88 +130,8 @@ namespace Core { \sa mimeTypes() */ -static QList<EditorType *> g_editorTypes; -static QHash<Utils::MimeType, EditorType *> g_userPreferredEditorTypes; static QList<IEditorFactory *> g_editorFactories; - -/*! - \internal -*/ -EditorType::EditorType() -{ - g_editorTypes.append(this); -} - -/*! - \internal -*/ -EditorType::~EditorType() -{ - g_editorTypes.removeOne(this); -} - -/*! - Returns all registered internal and external editors. -*/ -const EditorTypeList EditorType::allEditorTypes() -{ - return g_editorTypes; -} - -EditorType *EditorType::editorTypeForId(const Utils::Id &id) -{ - return Utils::findOrDefault(allEditorTypes(), Utils::equal(&EditorType::id, id)); -} - -/*! - Returns all available internal and external editors for the \a mimeType in the - default order: Editor types ordered by MIME type hierarchy, internal editors - first. -*/ -const EditorTypeList EditorType::defaultEditorTypes(const MimeType &mimeType) -{ - EditorTypeList result; - const EditorTypeList allTypes = EditorType::allEditorTypes(); - const EditorTypeList allEditorFactories = Utils::filtered(allTypes, [](EditorType *e) { - return e->asEditorFactory() != nullptr; - }); - const EditorTypeList allExternalEditors = Utils::filtered(allTypes, [](EditorType *e) { - return e->asExternalEditor() != nullptr; - }); - Internal::mimeTypeFactoryLookup(mimeType, allEditorFactories, &result); - Internal::mimeTypeFactoryLookup(mimeType, allExternalEditors, &result); - return result; -} - -const EditorTypeList EditorType::preferredEditorTypes(const FilePath &filePath) -{ - // default factories by mime type - const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath, - MimeMatchMode::MatchDefaultAndRemote); - EditorTypeList factories = defaultEditorTypes(mimeType); - // user preferred factory to front - EditorType *userPreferred = Internal::userPreferredEditorTypes().value(mimeType); - if (userPreferred) { - factories.removeAll(userPreferred); - factories.prepend(userPreferred); - } - // make binary editor first internal editor for text files > 48 MB - if (filePath.fileSize() > EditorManager::maxTextFileSize() && mimeType.inherits("text/plain")) { - const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream"); - const EditorTypeList binaryEditors = defaultEditorTypes(binary); - if (!binaryEditors.isEmpty()) { - EditorType *binaryEditor = binaryEditors.first(); - factories.removeAll(binaryEditor); - int insertionIndex = 0; - while (factories.size() > insertionIndex - && factories.at(insertionIndex)->asExternalEditor() != nullptr) { - ++insertionIndex; - } - factories.insert(insertionIndex, binaryEditor); - } - } - return factories; -} +static QHash<QString, IEditorFactory *> g_userPreferredEditorTypes; /*! Creates an IEditorFactory. @@ -212,53 +152,124 @@ IEditorFactory::~IEditorFactory() } /*! - \internal + Returns all registered internal and external editors. */ -const EditorFactoryList IEditorFactory::allEditorFactories() +const EditorFactories IEditorFactory::allEditorFactories() { return g_editorFactories; } +IEditorFactory *IEditorFactory::editorFactoryForId(const Utils::Id &id) +{ + return Utils::findOrDefault(allEditorFactories(), Utils::equal(&IEditorFactory::id, id)); +} + +/*! + Returns all available internal and external editors for the \a mimeType in the + default order: Editor types ordered by MIME type hierarchy, internal editors + first. +*/ +const EditorFactories IEditorFactory::defaultEditorFactories(const MimeType &mimeType) +{ + EditorFactories result; + const EditorFactories allTypes = IEditorFactory::allEditorFactories(); + const EditorFactories allEditorFactories + = Utils::filtered(allTypes, &IEditorFactory::isInternalEditor); + const EditorFactories allExternalEditors + = Utils::filtered(allTypes, &IEditorFactory::isExternalEditor); + mimeTypeFactoryLookup(mimeType, allEditorFactories, &result); + mimeTypeFactoryLookup(mimeType, allExternalEditors, &result); + return result; +} + +// FIXME: Consolidate with preferredEditorFactories() +const EditorFactories IEditorFactory::preferredEditorTypes(const FilePath &filePath) +{ + // default factories by mime type + const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath, + MimeMatchMode::MatchDefaultAndRemote); + EditorFactories factories = defaultEditorFactories(mimeType); + // user preferred factory to front + IEditorFactory *userPreferred = Internal::userPreferredEditorTypes().value(mimeType.name()); + if (userPreferred) { + factories.removeAll(userPreferred); + factories.prepend(userPreferred); + } + // make binary editor first internal editor for text files > 48 MB + if (filePath.fileSize() > EditorManager::maxTextFileSize() && mimeType.inherits("text/plain")) { + const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream"); + const EditorFactories binaryEditors = defaultEditorFactories(binary); + if (!binaryEditors.isEmpty()) { + IEditorFactory *binaryEditor = binaryEditors.first(); + factories.removeAll(binaryEditor); + int insertionIndex = 0; + while (factories.size() > insertionIndex + && !factories.at(insertionIndex)->isInternalEditor()) { + ++insertionIndex; + } + factories.insert(insertionIndex, binaryEditor); + } + } + return factories; +} + /*! Returns the available editor factories for \a filePath in order of preference. That is the default order for the document's MIME type but with a user overridden default editor first, and the binary editor as the very first item if a text document is too large to be opened as a text file. */ -const EditorFactoryList IEditorFactory::preferredEditorFactories(const FilePath &filePath) +const EditorFactories IEditorFactory::preferredEditorFactories(const FilePath &filePath) { const auto defaultEditorFactories = [](const MimeType &mimeType) { - const EditorTypeList types = defaultEditorTypes(mimeType); - const EditorTypeList ieditorTypes = Utils::filtered(types, [](EditorType *type) { - return type->asEditorFactory() != nullptr; - }); - return Utils::qobject_container_cast<IEditorFactory *>(ieditorTypes); + QList<IEditorFactory *> editorFactories; + for (IEditorFactory *type : IEditorFactory::defaultEditorFactories(mimeType)) { + if (type->isInternalEditor()) + editorFactories.append(type); + } + return editorFactories; }; // default factories by mime type const Utils::MimeType mimeType = Utils::mimeTypeForFile(filePath); - EditorFactoryList factories = defaultEditorFactories(mimeType); + EditorFactories factories = defaultEditorFactories(mimeType); const auto factories_moveToFront = [&factories](IEditorFactory *f) { factories.removeAll(f); factories.prepend(f); }; // user preferred factory to front - EditorType *userPreferred = Internal::userPreferredEditorTypes().value(mimeType); - if (userPreferred && userPreferred->asEditorFactory()) - factories_moveToFront(userPreferred->asEditorFactory()); + IEditorFactory *userPreferred = Internal::userPreferredEditorTypes().value(mimeType.name()); + if (userPreferred && userPreferred->isInternalEditor()) + factories_moveToFront(userPreferred); // open text files > 48 MB in binary editor if (filePath.fileSize() > EditorManager::maxTextFileSize() && mimeType.inherits("text/plain")) { const Utils::MimeType binary = Utils::mimeTypeForName("application/octet-stream"); - const EditorFactoryList binaryEditors = defaultEditorFactories(binary); + const EditorFactories binaryEditors = defaultEditorFactories(binary); if (!binaryEditors.isEmpty()) factories_moveToFront(binaryEditors.first()); } return factories; } +/** + Returns true if this factory creates internal editors. +*/ +bool IEditorFactory::isInternalEditor() const +{ + return bool(m_creator); +} + +/** + Returns true if this factory creates external editors. +*/ +bool IEditorFactory::isExternalEditor() const +{ + return bool(m_starter); +} + /*! - Creates an editor. + Creates an internal editor. Uses the function set with setEditorCreator() to create the editor. @@ -270,21 +281,54 @@ IEditor *IEditorFactory::createEditor() const return m_creator(); } +/*! + Starts an external editor. + + Uses the function set with setEditorStarter() to start the editor. + + \sa setEditorStarter() +*/ +bool IEditorFactory::startEditor(const FilePath &filePath, QString *errorMessage) +{ + QTC_ASSERT(m_starter, return false); + return m_starter(filePath, errorMessage); +} + /*! Sets the function that is used to create an editor instance in createEditor() to \a creator. + This is mutually exclusive with the use of setEditorStarter. + \sa createEditor() */ void IEditorFactory::setEditorCreator(const std::function<IEditor *()> &creator) { + QTC_CHECK(!m_starter); + // The check below triggers within the TextEditorFactory sub-hierarchy + // as the base TextEditorFactory already sets as simple creator. + //QTC_CHECK(!m_creator); m_creator = creator; } +/*! + Opens the editor with \a fileName. Returns \c true on success or \c false + on failure along with the error in \a errorMessage. + + This is mutually exclusive with the use of setEditorCreator. +*/ + +void IEditorFactory::setEditorStarter(const std::function<bool(const FilePath &, QString *)> &starter) +{ + QTC_CHECK(!m_starter); + QTC_CHECK(!m_creator); + m_starter = starter; +} + /*! \internal */ -QHash<Utils::MimeType, Core::EditorType *> Core::Internal::userPreferredEditorTypes() +QHash<QString, IEditorFactory *> Internal::userPreferredEditorTypes() { return g_userPreferredEditorTypes; } @@ -292,9 +336,10 @@ QHash<Utils::MimeType, Core::EditorType *> Core::Internal::userPreferredEditorTy /*! \internal */ -void Internal::setUserPreferredEditorTypes(const QHash<Utils::MimeType, EditorType *> &factories) +void Internal::setUserPreferredEditorTypes(const QHash<QString, IEditorFactory *> &factories) { g_userPreferredEditorTypes = factories; } + } // Core diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory.h b/src/plugins/coreplugin/editormanager/ieditorfactory.h index a2f44d831d7..ddc27eebc33 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory.h +++ b/src/plugins/coreplugin/editormanager/ieditorfactory.h @@ -3,11 +3,10 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> -#include <QObject> #include <QStringList> #include <functional> @@ -19,65 +18,48 @@ class MimeType; namespace Core { -class IExternalEditor; class IEditor; class IEditorFactory; -class EditorType; -using EditorFactoryList = QList<IEditorFactory *>; -using EditorTypeList = QList<EditorType *>; +using EditorFactories = QList<IEditorFactory *>; -class CORE_EXPORT EditorType : public QObject +class CORE_EXPORT IEditorFactory { - Q_OBJECT public: - ~EditorType() override; + virtual ~IEditorFactory(); - static const EditorTypeList allEditorTypes(); - static EditorType *editorTypeForId(const Utils::Id &id); - static const EditorTypeList defaultEditorTypes(const Utils::MimeType &mimeType); - static const EditorTypeList preferredEditorTypes(const Utils::FilePath &filePath); + static const EditorFactories allEditorFactories(); + static IEditorFactory *editorFactoryForId(const Utils::Id &id); + static const EditorFactories defaultEditorFactories(const Utils::MimeType &mimeType); + static const EditorFactories preferredEditorTypes(const Utils::FilePath &filePath); + static const EditorFactories preferredEditorFactories(const Utils::FilePath &filePath); Utils::Id id() const { return m_id; } QString displayName() const { return m_displayName; } QStringList mimeTypes() const { return m_mimeTypes; } - virtual IEditorFactory *asEditorFactory() { return nullptr; }; - virtual IExternalEditor *asExternalEditor() { return nullptr; }; + bool isInternalEditor() const; + bool isExternalEditor() const; + + IEditor *createEditor() const; + bool startEditor(const Utils::FilePath &filePath, QString *errorMessage); protected: - EditorType(); + IEditorFactory(); + void setId(Utils::Id id) { m_id = id; } void setDisplayName(const QString &displayName) { m_displayName = displayName; } void setMimeTypes(const QStringList &mimeTypes) { m_mimeTypes = mimeTypes; } void addMimeType(const QString &mimeType) { m_mimeTypes.append(mimeType); } + void setEditorCreator(const std::function<IEditor *()> &creator); + void setEditorStarter(const std::function<bool(const Utils::FilePath &, QString *)> &starter); private: Utils::Id m_id; QString m_displayName; QStringList m_mimeTypes; -}; - -class CORE_EXPORT IEditorFactory : public EditorType -{ - Q_OBJECT - -public: - IEditorFactory(); - ~IEditorFactory() override; - - static const EditorFactoryList allEditorFactories(); - static const EditorFactoryList preferredEditorFactories(const Utils::FilePath &filePath); - - IEditor *createEditor() const; - - IEditorFactory *asEditorFactory() override { return this; } - -protected: - void setEditorCreator(const std::function<IEditor *()> &creator); - -private: std::function<IEditor *()> m_creator; + std::function<bool(const Utils::FilePath &, QString *)> m_starter; }; } // namespace Core diff --git a/src/plugins/coreplugin/editormanager/ieditorfactory_p.h b/src/plugins/coreplugin/editormanager/ieditorfactory_p.h index c25fa88711c..38d30524f68 100644 --- a/src/plugins/coreplugin/editormanager/ieditorfactory_p.h +++ b/src/plugins/coreplugin/editormanager/ieditorfactory_p.h @@ -3,44 +3,16 @@ #pragma once -#include <utils/mimeutils.h> - #include <QHash> -#include <QSet> namespace Core { -class EditorType; +class IEditorFactory; namespace Internal { -QHash<Utils::MimeType, EditorType *> userPreferredEditorTypes(); -void setUserPreferredEditorTypes(const QHash<Utils::MimeType, EditorType *> &factories); - -/* Find the one best matching the mimetype passed in. - * Recurse over the parent classes of the mimetype to find them. */ -template<class EditorTypeLike> -static void mimeTypeFactoryLookup(const Utils::MimeType &mimeType, - const QList<EditorTypeLike *> &allFactories, - QList<EditorTypeLike *> *list) -{ - QSet<EditorTypeLike *> matches; - Utils::visitMimeParents(mimeType, [&](const Utils::MimeType &mt) -> bool { - // check for matching factories - for (EditorTypeLike *factory : allFactories) { - if (!matches.contains(factory)) { - const QStringList mimeTypes = factory->mimeTypes(); - for (const QString &mimeName : mimeTypes) { - if (mt.matchesName(mimeName)) { - list->append(factory); - matches.insert(factory); - } - } - } - } - return true; // continue - }); -} +QHash<QString, IEditorFactory *> userPreferredEditorTypes(); +void setUserPreferredEditorTypes(const QHash<QString, IEditorFactory *> &factories); } // Internal } // Core diff --git a/src/plugins/coreplugin/editormanager/iexternaleditor.cpp b/src/plugins/coreplugin/editormanager/iexternaleditor.cpp deleted file mode 100644 index 8176bf4d3ee..00000000000 --- a/src/plugins/coreplugin/editormanager/iexternaleditor.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "iexternaleditor.h" - -#include "ieditorfactory_p.h" - -namespace Core { - -/*! - \class Core::IExternalEditor - \inheaderfile coreplugin/editormanager/iexternaleditor.h - \inmodule QtCreator - \ingroup mainclasses - - \brief The IExternalEditor class enables registering an external - editor in the \uicontrol{Open With} dialog. -*/ - -/*! - \fn bool Core::IExternalEditor::startEditor(const Utils::FilePath &fileName, QString *errorMessage) - - Opens the editor with \a fileName. Returns \c true on success or \c false - on failure along with the error in \a errorMessage. -*/ - -static QList<IExternalEditor *> g_externalEditors; - -/*! - \internal -*/ -IExternalEditor::IExternalEditor() -{ - g_externalEditors.append(this); -} - -/*! - \internal -*/ -IExternalEditor::~IExternalEditor() -{ - g_externalEditors.removeOne(this); -} - -/*! - Returns all available external editors. -*/ -const ExternalEditorList IExternalEditor::allExternalEditors() -{ - return g_externalEditors; -} - -/*! - Returns all external editors available for this \a mimeType in the default - order (editors ordered by MIME type hierarchy). -*/ -const ExternalEditorList IExternalEditor::externalEditors(const Utils::MimeType &mimeType) -{ - ExternalEditorList rc; - const ExternalEditorList allEditors = IExternalEditor::allExternalEditors(); - Internal::mimeTypeFactoryLookup(mimeType, allEditors, &rc); - return rc; -} - -} // Core diff --git a/src/plugins/coreplugin/editormanager/iexternaleditor.h b/src/plugins/coreplugin/editormanager/iexternaleditor.h deleted file mode 100644 index 28c331e5c40..00000000000 --- a/src/plugins/coreplugin/editormanager/iexternaleditor.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "ieditorfactory.h" - -#include <coreplugin/core_global.h> - -#include <utils/id.h> - -#include <QObject> - -namespace Utils { -class FilePath; -class MimeType; -} - -namespace Core { - -class IExternalEditor; - -using ExternalEditorList = QList<IExternalEditor *>; - -class CORE_EXPORT IExternalEditor : public EditorType -{ - Q_OBJECT - -public: - explicit IExternalEditor(); - ~IExternalEditor() override; - - static const ExternalEditorList allExternalEditors(); - static const ExternalEditorList externalEditors(const Utils::MimeType &mimeType); - - IExternalEditor *asExternalEditor() override { return this; } - - virtual bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) = 0; -}; - -} // namespace Core diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.cpp b/src/plugins/coreplugin/editormanager/openeditorsview.cpp index 3cbc9b4aec8..ebf2d751ec9 100644 --- a/src/plugins/coreplugin/editormanager/openeditorsview.cpp +++ b/src/plugins/coreplugin/editormanager/openeditorsview.cpp @@ -8,18 +8,64 @@ #include "ieditor.h" #include "../actionmanager/command.h" #include "../coreplugintr.h" +#include "../opendocumentstreeview.h" #include <utils/fsengine/fileiconprovider.h> #include <utils/qtcassert.h> +#include <QAbstractProxyModel> #include <QApplication> #include <QMenu> namespace Core::Internal { -//// +class ProxyModel : public QAbstractProxyModel +{ +public: + explicit ProxyModel(QObject *parent = nullptr); + + QModelIndex mapFromSource(const QModelIndex & sourceIndex) const override; + QModelIndex mapToSource(const QModelIndex & proxyIndex) const override; + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + void setSourceModel(QAbstractItemModel *sourceModel) override; + + QVariant data(const QModelIndex &index, int role) const override; + + // QAbstractProxyModel::sibling is broken in Qt 5 + QModelIndex sibling(int row, int column, const QModelIndex &idx) const override; + // QAbstractProxyModel::supportedDragActions delegation is missing in Qt 5 + Qt::DropActions supportedDragActions() const override; + +private: + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); +}; + // OpenEditorsWidget -//// + +class OpenEditorsWidget : public OpenDocumentsTreeView +{ +public: + OpenEditorsWidget(); + ~OpenEditorsWidget() override; + +private: + void handleActivated(const QModelIndex &); + void updateCurrentItem(IEditor*); + void contextMenuRequested(QPoint pos); + void activateEditor(const QModelIndex &index); + void closeDocument(const QModelIndex &index); + + ProxyModel *m_model; +}; OpenEditorsWidget::OpenEditorsWidget() { diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.h b/src/plugins/coreplugin/editormanager/openeditorsview.h index 0634e45f7d2..013ff538d69 100644 --- a/src/plugins/coreplugin/editormanager/openeditorsview.h +++ b/src/plugins/coreplugin/editormanager/openeditorsview.h @@ -3,61 +3,9 @@ #pragma once -#include <coreplugin/inavigationwidgetfactory.h> -#include <coreplugin/opendocumentstreeview.h> +#include "../inavigationwidgetfactory.h" -#include <QAbstractProxyModel> -#include <QCoreApplication> - -namespace Core { -class IEditor; - -namespace Internal { - -class ProxyModel : public QAbstractProxyModel -{ -public: - explicit ProxyModel(QObject *parent = nullptr); - QModelIndex mapFromSource(const QModelIndex & sourceIndex) const override; - QModelIndex mapToSource(const QModelIndex & proxyIndex) const override; - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &child) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - - void setSourceModel(QAbstractItemModel *sourceModel) override; - - QVariant data(const QModelIndex &index, int role) const override; - - // QAbstractProxyModel::sibling is broken in Qt 5 - QModelIndex sibling(int row, int column, const QModelIndex &idx) const override; - // QAbstractProxyModel::supportedDragActions delegation is missing in Qt 5 - Qt::DropActions supportedDragActions() const override; - -private: - void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void sourceRowsRemoved(const QModelIndex &parent, int start, int end); - void sourceRowsInserted(const QModelIndex &parent, int start, int end); - void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); - void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end); -}; - -class OpenEditorsWidget : public OpenDocumentsTreeView -{ -public: - OpenEditorsWidget(); - ~OpenEditorsWidget() override; - -private: - void handleActivated(const QModelIndex &); - void updateCurrentItem(IEditor*); - void contextMenuRequested(QPoint pos); - void activateEditor(const QModelIndex &index); - void closeDocument(const QModelIndex &index); - - ProxyModel *m_model; -}; +namespace Core::Internal { class OpenEditorsViewFactory : public INavigationWidgetFactory { @@ -67,5 +15,4 @@ public: NavigationView createWidget() override; }; -} // namespace Internal -} // namespace Core +} // Core::Internal diff --git a/src/plugins/coreplugin/editormanager/openeditorswindow.cpp b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp index b3e119f3ad9..d59f49bf803 100644 --- a/src/plugins/coreplugin/editormanager/openeditorswindow.cpp +++ b/src/plugins/coreplugin/editormanager/openeditorswindow.cpp @@ -3,15 +3,19 @@ #include "openeditorswindow.h" +#include "documentmodel.h" #include "editormanager.h" #include "editormanager_p.h" #include "editorview.h" #include "../coreplugintr.h" #include "../idocument.h" +#include <utils/algorithm.h> +#include <utils/basetreeview.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> +#include <utils/treemodel.h> #include <utils/utilsicons.h> #include <QFocusEvent> @@ -19,49 +23,132 @@ #include <QVBoxLayout> #include <QScrollBar> -Q_DECLARE_METATYPE(Core::Internal::EditorView*) -Q_DECLARE_METATYPE(Core::IDocument*) +using namespace Utils; namespace Core::Internal { -enum class Role +class OpenEditorsItem : public TreeItem { - Entry = Qt::UserRole, - View = Qt::UserRole + 1 +public: + QVariant data(int column, int role) const override + { + if (column != 0) + return {}; + + if (!entry) + return {}; + + if (role == Qt::DecorationRole) { + return !entry->filePath().isEmpty() && entry->document->isFileReadOnly() + ? DocumentModel::lockedIcon() : FileIconProvider::icon(entry->filePath()); + } + + if (role == Qt::DisplayRole) { + QString title = entry->displayName(); + if (entry->document->isModified()) + title += Tr::tr("*"); + return title; + } + + if (role == Qt::ToolTipRole) + return entry->filePath().toUserOutput(); + + return {}; + } + + DocumentModel::Entry *entry = nullptr; + EditorView *view = nullptr; }; -OpenEditorsWindow::OpenEditorsWindow(QWidget *parent) : - QFrame(parent, Qt::Popup), - m_editorList(new OpenEditorsTreeWidget(this)) +static void selectEditor(OpenEditorsItem *item) { + if (!item) + return; + if (!EditorManagerPrivate::activateEditorForEntry(item->view, item->entry)) + delete item; +} + +class OpenEditorsView : public QTreeView +{ +public: + OpenEditorsView() + { + m_model.setHeader({{}}); + setModel(&m_model); + + header()->hide(); + setIndentation(0); + setSelectionMode(QAbstractItemView::SingleSelection); + setTextElideMode(Qt::ElideMiddle); + if (Utils::HostOsInfo::isMacHost()) + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + setUniformRowHeights(true); + } + + QSize sizeHint() const override + { + return QSize(sizeHintForColumn(0) + verticalScrollBar()->width() + frameWidth() * 2, + viewportSizeHint().height() + frameWidth() * 2); + } + + OpenEditorsItem *currentItem() const + { + QModelIndexList indexes = selectedIndexes(); + return indexes.size() == 1 ? m_model.itemForIndexAtLevel<1>(indexes.front()) : nullptr; + } + + int currentRow() const + { + QModelIndexList indexes = selectedIndexes(); + return indexes.size() == 1 ? indexes.front().row() : -1; + } + + void mouseReleaseEvent(QMouseEvent *ev) override + { + QPoint pos = ev->pos(); + QModelIndex idx = indexAt(pos); + if (OpenEditorsItem *item = m_model.itemForIndexAtLevel<1>(idx)) + selectEditor(item); + setFocus(); + } + + void addHistoryItems(const QList<EditLocation> &history, EditorView *view, + QSet<const DocumentModel::Entry *> &entriesDone); + + void addRemainingItems(EditorView *view, + QSet<const DocumentModel::Entry *> &entriesDone); + + void addItem(DocumentModel::Entry *entry, QSet<const DocumentModel::Entry *> &entriesDone, + EditorView *view); + + void selectUpDown(bool up); + + TreeModel<TreeItem, OpenEditorsItem> m_model; +}; + +OpenEditorsWindow::OpenEditorsWindow(QWidget *parent) + : QFrame(parent, Qt::Popup) +{ + m_editorView = new OpenEditorsView; + setMinimumSize(300, 200); - m_editorList->setColumnCount(1); - m_editorList->header()->hide(); - m_editorList->setIndentation(0); - m_editorList->setSelectionMode(QAbstractItemView::SingleSelection); - m_editorList->setTextElideMode(Qt::ElideMiddle); - if (Utils::HostOsInfo::isMacHost()) - m_editorList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - m_editorList->installEventFilter(this); + m_editorView->installEventFilter(this); // We disable the frame on this list view and use a QFrame around it instead. // This improves the look with QGTKStyle. if (!Utils::HostOsInfo::isMacHost()) - setFrameStyle(m_editorList->frameStyle()); - m_editorList->setFrameStyle(QFrame::NoFrame); + setFrameStyle(m_editorView->frameStyle()); + m_editorView->setFrameStyle(QFrame::NoFrame); auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - layout->addWidget(m_editorList); - - connect(m_editorList, &QTreeWidget::itemClicked, - this, &OpenEditorsWindow::editorClicked); + layout->addWidget(m_editorView); } void OpenEditorsWindow::selectAndHide() { setVisible(false); - selectEditor(m_editorList->currentItem()); + selectEditor(m_editorView->currentItem()); } void OpenEditorsWindow::setVisible(bool visible) @@ -73,7 +160,7 @@ void OpenEditorsWindow::setVisible(bool visible) bool OpenEditorsWindow::eventFilter(QObject *obj, QEvent *e) { - if (obj == m_editorList) { + if (obj == m_editorView) { if (e->type() == QEvent::KeyPress) { auto ke = static_cast<QKeyEvent*>(e); if (ke->key() == Qt::Key_Escape) { @@ -82,7 +169,7 @@ bool OpenEditorsWindow::eventFilter(QObject *obj, QEvent *e) } if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) { - selectEditor(m_editorList->currentItem()); + selectEditor(m_editorView->currentItem()); return true; } } else if (e->type() == QEvent::KeyRelease) { @@ -100,18 +187,18 @@ bool OpenEditorsWindow::eventFilter(QObject *obj, QEvent *e) void OpenEditorsWindow::focusInEvent(QFocusEvent *) { - m_editorList->setFocus(); + m_editorView->setFocus(); } -void OpenEditorsWindow::selectUpDown(bool up) +void OpenEditorsView::selectUpDown(bool up) { - int itemCount = m_editorList->topLevelItemCount(); + int itemCount = m_model.rootItem()->childCount(); if (itemCount < 2) return; - int index = m_editorList->indexOfTopLevelItem(m_editorList->currentItem()); + int index = currentRow(); if (index < 0) return; - QTreeWidgetItem *editor = nullptr; + TreeItem *editor = nullptr; int count = 0; while (!editor && count < itemCount) { if (up) { @@ -123,72 +210,42 @@ void OpenEditorsWindow::selectUpDown(bool up) if (index >= itemCount) index = 0; } - editor = m_editorList->topLevelItem(index); + editor = m_model.rootItem()->childAt(index); count++; } if (editor) { - m_editorList->setCurrentItem(editor); - ensureCurrentVisible(); + setCurrentIndex(m_model.index(index, 0)); + scrollTo(currentIndex(), QAbstractItemView::PositionAtCenter); } } void OpenEditorsWindow::selectPreviousEditor() { - selectUpDown(false); -} - -QSize OpenEditorsTreeWidget::sizeHint() const -{ - return QSize(sizeHintForColumn(0) + verticalScrollBar()->width() + frameWidth() * 2, - viewportSizeHint().height() + frameWidth() * 2); -} - -QSize OpenEditorsWindow::sizeHint() const -{ - return m_editorList->sizeHint() + QSize(frameWidth() * 2, frameWidth() * 2); + m_editorView->selectUpDown(false); } void OpenEditorsWindow::selectNextEditor() { - selectUpDown(true); + m_editorView->selectUpDown(true); +} + +QSize OpenEditorsWindow::sizeHint() const +{ + return m_editorView->sizeHint() + QSize(frameWidth() * 2, frameWidth() * 2); } void OpenEditorsWindow::setEditors(const QList<EditLocation> &globalHistory, EditorView *view) { - m_editorList->clear(); + m_editorView->m_model.clear(); QSet<const DocumentModel::Entry *> entriesDone; - addHistoryItems(view->editorHistory(), view, entriesDone); + m_editorView->addHistoryItems(view->editorHistory(), view, entriesDone); // add missing editors from the global history - addHistoryItems(globalHistory, view, entriesDone); + m_editorView->addHistoryItems(globalHistory, view, entriesDone); // add purely suspended editors which are not initialised yet - addRemainingItems(view, entriesDone); -} - - -void OpenEditorsWindow::selectEditor(QTreeWidgetItem *item) -{ - if (!item) - return; - auto entry = item->data(0, int(Role::Entry)).value<DocumentModel::Entry *>(); - QTC_ASSERT(entry, return); - auto view = item->data(0, int(Role::View)).value<EditorView *>(); - if (!EditorManagerPrivate::activateEditorForEntry(view, entry)) - delete item; -} - -void OpenEditorsWindow::editorClicked(QTreeWidgetItem *item) -{ - selectEditor(item); - setFocus(); -} - - -void OpenEditorsWindow::ensureCurrentVisible() -{ - m_editorList->scrollTo(m_editorList->currentIndex(), QAbstractItemView::PositionAtCenter); + m_editorView->addRemainingItems(view, entriesDone); } static DocumentModel::Entry *entryForEditLocation(const EditLocation &item) @@ -198,7 +255,7 @@ static DocumentModel::Entry *entryForEditLocation(const EditLocation &item) return DocumentModel::entryForFilePath(item.filePath); } -void OpenEditorsWindow::addHistoryItems(const QList<EditLocation> &history, EditorView *view, +void OpenEditorsView::addHistoryItems(const QList<EditLocation> &history, EditorView *view, QSet<const DocumentModel::Entry *> &entriesDone) { for (const EditLocation &hi : history) { @@ -207,38 +264,28 @@ void OpenEditorsWindow::addHistoryItems(const QList<EditLocation> &history, Edit } } -void OpenEditorsWindow::addRemainingItems(EditorView *view, - QSet<const DocumentModel::Entry *> &entriesDone) +void OpenEditorsView::addRemainingItems(EditorView *view, + QSet<const DocumentModel::Entry *> &entriesDone) { const QList<DocumentModel::Entry *> entries = DocumentModel::entries(); for (DocumentModel::Entry *entry : entries) addItem(entry, entriesDone, view); } -void OpenEditorsWindow::addItem(DocumentModel::Entry *entry, +void OpenEditorsView::addItem(DocumentModel::Entry *entry, QSet<const DocumentModel::Entry *> &entriesDone, EditorView *view) { - if (entriesDone.contains(entry)) + if (!Utils::insert(entriesDone, entry)) return; - entriesDone.insert(entry); - QString title = entry->displayName(); - QTC_ASSERT(!title.isEmpty(), return); - auto item = new QTreeWidgetItem(); - if (entry->document->isModified()) - title += Tr::tr("*"); - item->setIcon(0, !entry->filePath().isEmpty() && entry->document->isFileReadOnly() - ? DocumentModel::lockedIcon() : Utils::FileIconProvider::icon(entry->filePath())); - item->setText(0, title); - item->setToolTip(0, entry->filePath().toString()); - item->setData(0, int(Role::Entry), QVariant::fromValue(entry)); - item->setData(0, int(Role::View), QVariant::fromValue(view)); - item->setTextAlignment(0, Qt::AlignLeft); - m_editorList->addTopLevelItem(item); + auto item = new OpenEditorsItem; + item->entry = entry; + item->view = view; + m_model.rootItem()->appendChild(item); - if (m_editorList->topLevelItemCount() == 1) - m_editorList->setCurrentItem(item); + if (m_model.rootItem()->childCount() == 1) + setCurrentIndex(m_model.index(0, 0)); } } // Core::Internal diff --git a/src/plugins/coreplugin/editormanager/openeditorswindow.h b/src/plugins/coreplugin/editormanager/openeditorswindow.h index eb644e09581..e7a53809e27 100644 --- a/src/plugins/coreplugin/editormanager/openeditorswindow.h +++ b/src/plugins/coreplugin/editormanager/openeditorswindow.h @@ -3,70 +3,34 @@ #pragma once -#include "documentmodel.h" #include "editorview.h" #include <QFrame> -#include <QIcon> #include <QList> -#include <QTreeWidget> -QT_BEGIN_NAMESPACE -class QTreeWidgetItem; -QT_END_NAMESPACE +namespace Core::Internal { -namespace Core { +class OpenEditorsView; -class IEditor; - -namespace Internal { - -class OpenEditorsTreeWidget : public QTreeWidget { -public: - explicit OpenEditorsTreeWidget(QWidget *parent = nullptr) : QTreeWidget(parent) {} - ~OpenEditorsTreeWidget() override = default; - QSize sizeHint() const override; -}; - - -class OpenEditorsWindow : public QFrame +class OpenEditorsWindow final : public QFrame { - Q_OBJECT - public: - enum Mode {ListMode, HistoryMode }; - explicit OpenEditorsWindow(QWidget *parent = nullptr); void setEditors(const QList<EditLocation> &globalHistory, EditorView *view); - bool eventFilter(QObject *src, QEvent *e) override; - void focusInEvent(QFocusEvent*) override; - void setVisible(bool visible) override; void selectNextEditor(); void selectPreviousEditor(); - QSize sizeHint() const override; - -public slots: void selectAndHide(); + QSize sizeHint() const final; + void setVisible(bool visible) final; + private: - void editorClicked(QTreeWidgetItem *item); - static void selectEditor(QTreeWidgetItem *item); + bool eventFilter(QObject *src, QEvent *e) final; + void focusInEvent(QFocusEvent *) final; - void addHistoryItems(const QList<EditLocation> &history, EditorView *view, - QSet<const DocumentModel::Entry *> &entriesDone); - void addRemainingItems(EditorView *view, - QSet<const DocumentModel::Entry *> &entriesDone); - void addItem(DocumentModel::Entry *entry, QSet<const DocumentModel::Entry *> &entriesDone, - EditorView *view); - void ensureCurrentVisible(); - void selectUpDown(bool up); - - bool isSameFile(IEditor *editorA, IEditor *editorB) const; - - OpenEditorsTreeWidget *m_editorList; + OpenEditorsView *m_editorView; }; -} // namespace Internal -} // namespace Core +} // Core::Internal diff --git a/src/plugins/coreplugin/editormanager/systemeditor.cpp b/src/plugins/coreplugin/editormanager/systemeditor.cpp index d54446aa480..2b0f91cbd2c 100644 --- a/src/plugins/coreplugin/editormanager/systemeditor.cpp +++ b/src/plugins/coreplugin/editormanager/systemeditor.cpp @@ -7,7 +7,6 @@ #include <utils/filepath.h> -#include <QStringList> #include <QUrl> #include <QDesktopServices> @@ -20,20 +19,19 @@ SystemEditor::SystemEditor() setId("CorePlugin.OpenWithSystemEditor"); setDisplayName(Tr::tr("System Editor")); setMimeTypes({"application/octet-stream"}); -} -bool SystemEditor::startEditor(const FilePath &filePath, QString *errorMessage) -{ - Q_UNUSED(errorMessage) - QUrl url; - url.setPath(filePath.toString()); - url.setScheme(QLatin1String("file")); - if (!QDesktopServices::openUrl(url)) { - if (errorMessage) - *errorMessage = Tr::tr("Could not open URL %1.").arg(url.toString()); - return false; - } - return true; + setEditorStarter([](const FilePath &filePath, QString *errorMessage) { + Q_UNUSED(errorMessage) + QUrl url; + url.setPath(filePath.toString()); + url.setScheme(QLatin1String("file")); + if (!QDesktopServices::openUrl(url)) { + if (errorMessage) + *errorMessage = Tr::tr("Could not open URL %1.").arg(url.toString()); + return false; + } + return true; + }); } } // Core::Internal diff --git a/src/plugins/coreplugin/editormanager/systemeditor.h b/src/plugins/coreplugin/editormanager/systemeditor.h index 8d0e71c5961..45cbd7d2406 100644 --- a/src/plugins/coreplugin/editormanager/systemeditor.h +++ b/src/plugins/coreplugin/editormanager/systemeditor.h @@ -3,20 +3,14 @@ #pragma once -#include "iexternaleditor.h" +#include "ieditorfactory.h" -namespace Core { -namespace Internal { +namespace Core::Internal { -class SystemEditor : public IExternalEditor +class SystemEditor final : public IEditorFactory { - Q_OBJECT - public: - explicit SystemEditor(); - - bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) override; + SystemEditor(); }; -} // namespace Internal -} // namespace Core +} // namespace Core::Internal diff --git a/src/plugins/coreplugin/externaltool.cpp b/src/plugins/coreplugin/externaltool.cpp index 6cafa6ff887..c65cfe80882 100644 --- a/src/plugins/coreplugin/externaltool.cpp +++ b/src/plugins/coreplugin/externaltool.cpp @@ -11,8 +11,6 @@ #include "documentmanager.h" #include "editormanager/editormanager.h" -#include <app/app_version.h> - #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/macroexpander.h> diff --git a/src/plugins/coreplugin/externaltool.h b/src/plugins/coreplugin/externaltool.h index d92d6099bc9..15b420f1808 100644 --- a/src/plugins/coreplugin/externaltool.h +++ b/src/plugins/coreplugin/externaltool.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "core_global.h" #include <utils/environment.h> #include <utils/filepath.h> diff --git a/src/plugins/coreplugin/externaltoolmanager.cpp b/src/plugins/coreplugin/externaltoolmanager.cpp index 0a42bfc9ab7..6f85a2b4c11 100644 --- a/src/plugins/coreplugin/externaltoolmanager.cpp +++ b/src/plugins/coreplugin/externaltoolmanager.cpp @@ -233,11 +233,11 @@ void ExternalToolManager::setToolsByCategory(const QMap<QString, QList<ExternalT void ExternalToolManager::readSettings(const QMap<QString, ExternalTool *> &tools, QMap<QString, QList<ExternalTool *> > *categoryMap) { - QSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("ExternalTools")); + QtcSettings *settings = ICore::settings(); + settings->beginGroup("ExternalTools"); if (categoryMap) { - settings->beginGroup(QLatin1String("OverrideCategories")); + settings->beginGroup("OverrideCategories"); const QStringList settingsCategories = settings->childGroups(); for (const QString &settingsCategory : settingsCategories) { QString displayCategory = settingsCategory; @@ -246,7 +246,7 @@ void ExternalToolManager::readSettings(const QMap<QString, ExternalTool *> &tool int count = settings->beginReadArray(settingsCategory); for (int i = 0; i < count; ++i) { settings->setArrayIndex(i); - const QString &toolId = settings->value(QLatin1String("Tool")).toString(); + const QString &toolId = settings->value("Tool").toString(); if (tools.contains(toolId)) { ExternalTool *tool = tools.value(toolId); // remove from old category @@ -267,11 +267,11 @@ void ExternalToolManager::readSettings(const QMap<QString, ExternalTool *> &tool static void writeSettings() { - QSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("ExternalTools")); - settings->remove(QLatin1String("")); + QtcSettings *settings = ICore::settings(); + settings->beginGroup("ExternalTools"); + settings->remove(""); - settings->beginGroup(QLatin1String("OverrideCategories")); + settings->beginGroup("OverrideCategories"); for (auto it = d->m_categoryMap.cbegin(), end = d->m_categoryMap.cend(); it != end; ++it) { QString category = it.key(); if (category.isEmpty()) @@ -281,7 +281,7 @@ static void writeSettings() const QList<ExternalTool *> values = it.value(); for (const ExternalTool *tool : values) { settings->setArrayIndex(i); - settings->setValue(QLatin1String("Tool"), tool->id()); + settings->setValue("Tool", tool->id()); ++i; } settings->endArray(); diff --git a/src/plugins/coreplugin/fancytabwidget.cpp b/src/plugins/coreplugin/fancytabwidget.cpp index 5d3bf71764f..85935b27987 100644 --- a/src/plugins/coreplugin/fancytabwidget.cpp +++ b/src/plugins/coreplugin/fancytabwidget.cpp @@ -7,13 +7,13 @@ #include "fancyactionbar.h" #include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> #include <utils/styledbar.h> #include <utils/stylehelper.h> #include <utils/theme/theme.h> #include <QDebug> -#include <QHBoxLayout> #include <QMouseEvent> #include <QPainter> #include <QPixmapCache> @@ -457,57 +457,44 @@ FancyTabWidget::FancyTabWidget(QWidget *parent) m_tabBar = new FancyTabBar(this); m_tabBar->setObjectName("ModeSelector"); // used for UI introduction - m_selectionWidget = new QWidget(this); - auto selectionLayout = new QVBoxLayout; - selectionLayout->setSpacing(0); - selectionLayout->setContentsMargins(0, 0, 0, 0); - auto bar = new StyledBar; - auto layout = new QHBoxLayout(bar); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - auto fancyButton = new FancyColorButton(this); + auto fancyButton = new FancyColorButton; connect(fancyButton, &FancyColorButton::clicked, this, &FancyTabWidget::topAreaClicked); - layout->addWidget(fancyButton); - selectionLayout->addWidget(bar); - - selectionLayout->addWidget(m_tabBar); - selectionLayout->addStretch(1); - m_selectionWidget->setLayout(selectionLayout); - m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); - - m_cornerWidgetContainer = new QWidget(this); - m_cornerWidgetContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - m_cornerWidgetContainer->setAutoFillBackground(false); - - auto cornerWidgetLayout = new QVBoxLayout; - cornerWidgetLayout->setSpacing(0); - cornerWidgetLayout->setContentsMargins(0, 0, 0, 0); - cornerWidgetLayout->addStretch(); - m_cornerWidgetContainer->setLayout(cornerWidgetLayout); - - selectionLayout->addWidget(m_cornerWidgetContainer, 0); m_modesStack = new QStackedLayout; m_statusBar = new QStatusBar; m_statusBar->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); - auto vlayout = new QVBoxLayout; - vlayout->setContentsMargins(0, 0, 0, 0); - vlayout->setSpacing(0); - vlayout->addLayout(m_modesStack); - vlayout->addWidget(m_statusBar); + QVBoxLayout *vlayout; + + using namespace Layouting; + Row { fancyButton, noMargin() }.attachTo(bar); + Row { + Widget { + bindTo(&m_selectionWidget), + Column { + bar, + m_tabBar, + st, + Widget { + bindTo(&m_cornerWidgetContainer), + Column { st, spacing(0), noMargin() }, + }, + spacing(0), noMargin(), + }, + }, + Column { bindTo(&vlayout), m_modesStack, m_statusBar, spacing(0) }, + spacing(1), noMargin(), + }.attachTo(this); + + m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + + m_cornerWidgetContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + m_cornerWidgetContainer->setAutoFillBackground(false); m_infoBarDisplay.setTarget(vlayout, 1); m_infoBarDisplay.setEdge(Qt::BottomEdge); - auto mainLayout = new QHBoxLayout; - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->setSpacing(1); - mainLayout->addWidget(m_selectionWidget); - mainLayout->addLayout(vlayout); - setLayout(mainLayout); - connect(m_tabBar, &FancyTabBar::currentAboutToChange, this, &FancyTabWidget::currentAboutToShow); connect(m_tabBar, &FancyTabBar::currentChanged, this, &FancyTabWidget::showWidget); connect(m_tabBar, &FancyTabBar::menuTriggered, this, &FancyTabWidget::menuTriggered); diff --git a/src/plugins/coreplugin/fileutils.cpp b/src/plugins/coreplugin/fileutils.cpp index 3df3309c318..e432543604f 100644 --- a/src/plugins/coreplugin/fileutils.cpp +++ b/src/plugins/coreplugin/fileutils.cpp @@ -31,7 +31,6 @@ #include <QRegularExpression> #include <QTextStream> #include <QTextCodec> -#include <QWidget> using namespace Utils; @@ -82,12 +81,8 @@ void FileUtils::showInGraphicalShell(QWidget *parent, const FilePath &pathIn) if (browserArgs.isEmpty()) { error = Tr::tr("The command for file browser is not set."); } else { - QProcess browserProc; - browserProc.setProgram(browserArgs.takeFirst()); - browserProc.setArguments(browserArgs); - const bool success = browserProc.startDetached(); - error = QString::fromLocal8Bit(browserProc.readAllStandardError()); - if (!success && error.isEmpty()) + const QString executable = browserArgs.takeFirst(); + if (!Process::startDetached({FilePath::fromString(executable), browserArgs})) error = Tr::tr("Error while starting file browser."); } if (!error.isEmpty()) @@ -106,7 +101,7 @@ void FileUtils::showInFileSystemView(const FilePath &path) void FileUtils::openTerminal(const FilePath &path, const Environment &env) { - Terminal::Hooks::instance().openTerminal({std::nullopt, path, env}); + Terminal::Hooks::instance().openTerminal({path, env}); } QString FileUtils::msgFindInDirectory() diff --git a/src/plugins/coreplugin/find/basetextfind.cpp b/src/plugins/coreplugin/find/basetextfind.cpp index 1a282fd4c80..a11bf7d33da 100644 --- a/src/plugins/coreplugin/find/basetextfind.cpp +++ b/src/plugins/coreplugin/find/basetextfind.cpp @@ -13,6 +13,8 @@ #include <QTextBlock> #include <QTextCursor> +using namespace Utils; + namespace Core { QRegularExpression BaseTextFind::regularExpression(const QString &txt, FindFlags flags) @@ -73,7 +75,7 @@ BaseTextFindPrivate::BaseTextFindPrivate(QPlainTextEdit *editor) */ /*! - \fn void Core::BaseTextFind::highlightAllRequested(const QString &txt, Core::FindFlags findFlags) + \fn void Core::BaseTextFind::highlightAllRequested(const QString &txt, Utils::FindFlags findFlags) This signal is emitted when the search results for \a txt using the given \a findFlags should be highlighted in the editor widget. @@ -329,7 +331,8 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, FindFl bool usesRegExp = (findFlags & FindRegularExpression); bool preserveCase = (findFlags & FindPreserveCase); QRegularExpression regexp = regularExpression(before, findFlags); - QTextCursor found = findOne(regexp, editCursor, textDocumentFlagsForFindFlags(findFlags)); + QTextCursor found = findOne(regexp, editCursor, + Utils::textDocumentFlagsForFindFlags(findFlags)); bool first = true; while (!found.isNull()) { if (found == editCursor && !first) { @@ -342,7 +345,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, FindFl newPosCursor.movePosition(findFlags & FindBackward ? QTextCursor::PreviousCharacter : QTextCursor::NextCharacter); - found = findOne(regexp, newPosCursor, textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, newPosCursor, Utils::textDocumentFlagsForFindFlags(findFlags)); continue; } if (first) @@ -360,7 +363,7 @@ int BaseTextFind::replaceAll(const QString &before, const QString &after, FindFl else realAfter = after; insertTextAfterSelection(realAfter, editCursor); - found = findOne(regexp, editCursor, textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, editCursor, Utils::textDocumentFlagsForFindFlags(findFlags)); } editCursor.endEditBlock(); return count; @@ -373,7 +376,7 @@ bool BaseTextFind::find(const QString &txt, FindFlags findFlags, QTextCursor sta return true; } QRegularExpression regexp = regularExpression(txt, findFlags); - QTextCursor found = findOne(regexp, start, textDocumentFlagsForFindFlags(findFlags)); + QTextCursor found = findOne(regexp, start, Utils::textDocumentFlagsForFindFlags(findFlags)); if (wrapped) *wrapped = false; @@ -382,7 +385,7 @@ bool BaseTextFind::find(const QString &txt, FindFlags findFlags, QTextCursor sta start.movePosition(QTextCursor::Start); else start.movePosition(QTextCursor::End); - found = findOne(regexp, start, textDocumentFlagsForFindFlags(findFlags)); + found = findOne(regexp, start, Utils::textDocumentFlagsForFindFlags(findFlags)); if (found.isNull()) return false; if (wrapped) @@ -422,9 +425,17 @@ bool BaseTextFind::inScope(const QTextCursor &candidate) const return false; if (d->m_scope.isNull()) return true; - return Utils::anyOf(d->m_scope, [candidate](const QTextCursor &scope){ - return candidate.selectionStart() >= scope.selectionStart() - && candidate.selectionEnd() <= scope.selectionEnd(); + return inScope(candidate.selectionStart(), candidate.selectionEnd()); +} + +bool BaseTextFind::inScope(int candidateStart, int candidateEnd) const +{ + if (d->m_scope.isNull()) + return true; + if (candidateStart > candidateEnd) + std::swap(candidateStart, candidateEnd); + return Utils::anyOf(d->m_scope, [&](const QTextCursor &scope) { + return candidateStart >= scope.selectionStart() && candidateEnd <= scope.selectionEnd(); }); } diff --git a/src/plugins/coreplugin/find/basetextfind.h b/src/plugins/coreplugin/find/basetextfind.h index 75b63cb3234..2c2abf90014 100644 --- a/src/plugins/coreplugin/find/basetextfind.h +++ b/src/plugins/coreplugin/find/basetextfind.h @@ -28,43 +28,48 @@ public: ~BaseTextFind() override; bool supportsReplace() const override; - FindFlags supportedFindFlags() const override; + Utils::FindFlags supportedFindFlags() const override; void resetIncrementalSearch() override; void clearHighlights() override; QString currentFindString() const override; QString completedFindString() const override; - Result findIncremental(const QString &txt, FindFlags findFlags) override; - Result findStep(const QString &txt, FindFlags findFlags) override; - void replace(const QString &before, const QString &after, FindFlags findFlags) override; - bool replaceStep(const QString &before, const QString &after, FindFlags findFlags) override; - int replaceAll(const QString &before, const QString &after, FindFlags findFlags) override; + Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Result findStep(const QString &txt, Utils::FindFlags findFlags) override; + void replace(const QString &before, const QString &after, Utils::FindFlags findFlags) override; + bool replaceStep(const QString &before, const QString &after, + Utils::FindFlags findFlags) override; + int replaceAll(const QString &before, const QString &after, + Utils::FindFlags findFlags) override; void defineFindScope() override; void clearFindScope() override; - void highlightAll(const QString &txt, FindFlags findFlags) override; + void highlightAll(const QString &txt, Utils::FindFlags findFlags) override; using CursorProvider = std::function<Utils::MultiTextCursor ()>; void setMultiTextCursorProvider(const CursorProvider &provider); bool inScope(const QTextCursor &candidate) const; + bool inScope(int candidateStart, int candidateEnd) const; - static QRegularExpression regularExpression(const QString &txt, FindFlags flags); + static QRegularExpression regularExpression(const QString &txt, Utils::FindFlags flags); signals: - void highlightAllRequested(const QString &txt, Core::FindFlags findFlags); + void highlightAllRequested(const QString &txt, Utils::FindFlags findFlags); void findScopeChanged(const Utils::MultiTextCursor &cursor); private: - bool find(const QString &txt, FindFlags findFlags, QTextCursor start, bool *wrapped); - QTextCursor replaceInternal(const QString &before, const QString &after, FindFlags findFlags); + bool find(const QString &txt, Utils::FindFlags findFlags, QTextCursor start, bool *wrapped); + QTextCursor replaceInternal(const QString &before, const QString &after, + Utils::FindFlags findFlags); Utils::MultiTextCursor multiTextCursor() const; QTextCursor textCursor() const; void setTextCursor(const QTextCursor&); QTextDocument *document() const; bool isReadOnly() const; - QTextCursor findOne(const QRegularExpression &expr, QTextCursor from, QTextDocument::FindFlags options) const; + QTextCursor findOne(const QRegularExpression &expr, QTextCursor from, + QTextDocument::FindFlags options) const; BaseTextFindPrivate *d; }; diff --git a/src/plugins/coreplugin/find/currentdocumentfind.cpp b/src/plugins/coreplugin/find/currentdocumentfind.cpp index 9a8c7864dbd..232c8904c0e 100644 --- a/src/plugins/coreplugin/find/currentdocumentfind.cpp +++ b/src/plugins/coreplugin/find/currentdocumentfind.cpp @@ -14,6 +14,8 @@ #include <QApplication> #include <QWidget> +using namespace Utils; + namespace Core::Internal { CurrentDocumentFind::CurrentDocumentFind() @@ -126,9 +128,8 @@ int CurrentDocumentFind::replaceAll(const QString &before, const QString &after, QTC_ASSERT(m_currentFind, return 0); QTC_CHECK(m_currentWidget); int count = m_currentFind->replaceAll(before, after, findFlags); - Utils::FadingIndicator::showText(m_currentWidget, - Tr::tr("%n occurrences replaced.", nullptr, count), - Utils::FadingIndicator::SmallText); + FadingIndicator::showText(m_currentWidget, Tr::tr("%n occurrences replaced.", nullptr, count), + FadingIndicator::SmallText); return count; } diff --git a/src/plugins/coreplugin/find/currentdocumentfind.h b/src/plugins/coreplugin/find/currentdocumentfind.h index ba0b92ea41c..31fbefa2baa 100644 --- a/src/plugins/coreplugin/find/currentdocumentfind.h +++ b/src/plugins/coreplugin/find/currentdocumentfind.h @@ -5,6 +5,8 @@ #include "ifindsupport.h" +#include <utils/filesearch.h> + #include <QPointer> namespace Core { @@ -21,19 +23,19 @@ public: void clearHighlights(); bool supportsReplace() const; bool supportsSelectAll() const; - FindFlags supportedFindFlags() const; + Utils::FindFlags supportedFindFlags() const; QString currentFindString() const; QString completedFindString() const; bool isEnabled() const; IFindSupport *candidate() const; - void highlightAll(const QString &txt, FindFlags findFlags); - IFindSupport::Result findIncremental(const QString &txt, FindFlags findFlags); - IFindSupport::Result findStep(const QString &txt, FindFlags findFlags); - void selectAll(const QString &txt, FindFlags findFlags); - void replace(const QString &before, const QString &after, FindFlags findFlags); - bool replaceStep(const QString &before, const QString &after, FindFlags findFlags); - int replaceAll(const QString &before, const QString &after, FindFlags findFlags); + void highlightAll(const QString &txt, Utils::FindFlags findFlags); + IFindSupport::Result findIncremental(const QString &txt, Utils::FindFlags findFlags); + IFindSupport::Result findStep(const QString &txt, Utils::FindFlags findFlags); + void selectAll(const QString &txt, Utils::FindFlags findFlags); + void replace(const QString &before, const QString &after, Utils::FindFlags findFlags); + bool replaceStep(const QString &before, const QString &after, Utils::FindFlags findFlags); + int replaceAll(const QString &before, const QString &after, Utils::FindFlags findFlags); void defineFindScope(); void clearFindScope(); void acceptCandidate(); diff --git a/src/plugins/coreplugin/find/findplugin.cpp b/src/plugins/coreplugin/find/findplugin.cpp index 3be3c8209e2..d7cb36e7ae2 100644 --- a/src/plugins/coreplugin/find/findplugin.cpp +++ b/src/plugins/coreplugin/find/findplugin.cpp @@ -8,6 +8,7 @@ #include "findtoolwindow.h" #include "ifindfilter.h" #include "searchresultwindow.h" +#include "textfindconstants.h" #include "../actionmanager/actioncontainer.h" #include "../actionmanager/actionmanager.h" #include "../actionmanager/command.h" @@ -26,7 +27,6 @@ #include <QStringListModel> #include <QVector> #include <QAction> -#include <QSettings> /*! \namespace Core::Internal::ItemDataRoles @@ -75,8 +75,8 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void writeSettings(QSettings *settings) const; - void readSettings(QSettings *settings); + void writeSettings(QtcSettings *settings) const; + void readSettings(QtcSettings *settings); void updateCompletion(const QString &text, FindFlags f); @@ -101,17 +101,17 @@ QVariant CompletionModel::data(const QModelIndex &index, int role) const return QVariant(); } -static inline QString completionSettingsArrayPrefix() { return QStringLiteral("FindCompletions"); } -static inline QString completionSettingsTextKey() { return QStringLiteral("Text"); } -static inline QString completionSettingsFlagsKey() { return QStringLiteral("Flags"); } +static Utils::Key completionSettingsArrayPrefix() { return "FindCompletions"; } +static Utils::Key completionSettingsTextKey() { return "Text"; } +static Utils::Key completionSettingsFlagsKey() { return "Flags"; } -void CompletionModel::writeSettings(QSettings *settings) const +void CompletionModel::writeSettings(QtcSettings *settings) const { if (m_entries.isEmpty()) { settings->remove(completionSettingsArrayPrefix()); } else { const int size = m_entries.size(); - settings->beginWriteArray(completionSettingsArrayPrefix(), size); + settings->beginWriteArray(stringFromKey(completionSettingsArrayPrefix()), size); for (int i = 0; i < size; ++i) { settings->setArrayIndex(i); settings->setValue(completionSettingsTextKey(), m_entries.at(i).text); @@ -121,10 +121,10 @@ void CompletionModel::writeSettings(QSettings *settings) const } } -void CompletionModel::readSettings(QSettings *settings) +void CompletionModel::readSettings(QtcSettings *settings) { beginResetModel(); - const int size = settings->beginReadArray(completionSettingsArrayPrefix()); + const int size = settings->beginReadArray(stringFromKey(completionSettingsArrayPrefix())); m_entries.clear(); m_entries.reserve(size); for (int i = 0; i < size; ++i) { @@ -156,7 +156,7 @@ class FindPrivate : public QObject public: bool isAnyFilterEnabled() const; void writeSettings(); - void setFindFlag(Core::FindFlag flag, bool enabled); + void setFindFlag(FindFlag flag, bool enabled); static void updateCompletion(const QString &text, QStringList &completions, QStringListModel *model); void setupMenu(); @@ -370,7 +370,7 @@ bool Find::hasFindFlag(FindFlag flag) void FindPrivate::writeSettings() { QtcSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("Find")); + settings->beginGroup("Find"); settings->setValueWithDefault("Backward", bool(m_findFlags & FindBackward), false); settings->setValueWithDefault("CaseSensitively", bool(m_findFlags & FindCaseSensitively), false); settings->setValueWithDefault("WholeWords", bool(m_findFlags & FindWholeWords), false); @@ -388,18 +388,18 @@ void FindPrivate::writeSettings() void FindPrivate::readSettings() { - QSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("Find")); + QtcSettings *settings = ICore::settings(); + settings->beginGroup("Find"); { QSignalBlocker blocker(m_instance); - Find::setBackward(settings->value(QLatin1String("Backward"), false).toBool()); - Find::setCaseSensitive(settings->value(QLatin1String("CaseSensitively"), false).toBool()); - Find::setWholeWord(settings->value(QLatin1String("WholeWords"), false).toBool()); - Find::setRegularExpression(settings->value(QLatin1String("RegularExpression"), false).toBool()); - Find::setPreserveCase(settings->value(QLatin1String("PreserveCase"), false).toBool()); + Find::setBackward(settings->value("Backward", false).toBool()); + Find::setCaseSensitive(settings->value("CaseSensitively", false).toBool()); + Find::setWholeWord(settings->value("WholeWords", false).toBool()); + Find::setRegularExpression(settings->value("RegularExpression", false).toBool()); + Find::setPreserveCase(settings->value("PreserveCase", false).toBool()); } m_findCompletionModel.readSettings(settings); - m_replaceCompletions = settings->value(QLatin1String("ReplaceStrings")).toStringList(); + m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); m_replaceCompletionModel.setStringList(m_replaceCompletions); settings->endGroup(); m_findToolBar->readSettings(); @@ -452,17 +452,4 @@ QStringListModel *Find::replaceCompletionModel() return &(d->m_replaceCompletionModel); } -// declared in textfindconstants.h -QTextDocument::FindFlags textDocumentFlagsForFindFlags(FindFlags flags) -{ - QTextDocument::FindFlags textDocFlags; - if (flags & FindBackward) - textDocFlags |= QTextDocument::FindBackward; - if (flags & FindCaseSensitively) - textDocFlags |= QTextDocument::FindCaseSensitively; - if (flags & FindWholeWords) - textDocFlags |= QTextDocument::FindWholeWords; - return textDocFlags; -} - } // namespace Core diff --git a/src/plugins/coreplugin/find/findplugin.h b/src/plugins/coreplugin/find/findplugin.h index 8ffb18be159..2ddb9b797a4 100644 --- a/src/plugins/coreplugin/find/findplugin.h +++ b/src/plugins/coreplugin/find/findplugin.h @@ -3,7 +3,9 @@ #pragma once -#include "textfindconstants.h" +#include "../core_global.h" + +#include <utils/filesearch.h> #include <QObject> @@ -30,9 +32,9 @@ public: enum { CompletionModelFindFlagsRole = Qt::UserRole + 1 }; - static FindFlags findFlags(); - static bool hasFindFlag(FindFlag flag); - static void updateFindCompletion(const QString &text, FindFlags flags = {}); + static Utils::FindFlags findFlags(); + static bool hasFindFlag(Utils::FindFlag flag); + static void updateFindCompletion(const QString &text, Utils::FindFlags flags = {}); static void updateReplaceCompletion(const QString &text); static QAbstractListModel *findCompletionModel(); static QStringListModel *replaceCompletionModel(); diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp index 96a99f6eba7..616f4c47719 100644 --- a/src/plugins/coreplugin/find/findtoolbar.cpp +++ b/src/plugins/coreplugin/find/findtoolbar.cpp @@ -6,11 +6,11 @@ #include "ifindfilter.h" #include "findplugin.h" #include "optionspopup.h" +#include "textfindconstants.h" #include "../actionmanager/actioncontainer.h" #include "../actionmanager/actionmanager.h" #include "../actionmanager/command.h" #include "../coreicons.h" -#include "../coreplugin.h" #include "../coreplugintr.h" #include "../findplaceholder.h" #include "../icontext.h" @@ -36,7 +36,6 @@ #include <QLabel> #include <QMenu> #include <QPainter> -#include <QSettings> #include <QSpacerItem> #include <QStringListModel> #include <QToolButton> @@ -256,7 +255,7 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind) connect(m_goToCurrentFindAction, &QAction::triggered, this, &FindToolBar::setFocusToCurrentFindSupport); - QIcon icon = QIcon::fromTheme(QLatin1String("edit-find-replace")); + QIcon icon = Icon::fromTheme("edit-find-replace"); m_findInDocumentAction = new QAction(icon, Tr::tr("Find/Replace"), this); cmd = ActionManager::registerAction(m_findInDocumentAction, Constants::FIND_IN_DOCUMENT); cmd->setDefaultKeySequence(QKeySequence::Find); @@ -1039,19 +1038,19 @@ void FindToolBar::writeSettings() void FindToolBar::readSettings() { - QSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("Find")); - settings->beginGroup(QLatin1String("FindToolBar")); + QtcSettings *settings = ICore::settings(); + settings->beginGroup("Find"); + settings->beginGroup("FindToolBar"); FindFlags flags; - if (settings->value(QLatin1String("Backward"), false).toBool()) + if (settings->value("Backward", false).toBool()) flags |= FindBackward; - if (settings->value(QLatin1String("CaseSensitively"), false).toBool()) + if (settings->value("CaseSensitively", false).toBool()) flags |= FindCaseSensitively; - if (settings->value(QLatin1String("WholeWords"), false).toBool()) + if (settings->value("WholeWords", false).toBool()) flags |= FindWholeWords; - if (settings->value(QLatin1String("RegularExpression"), false).toBool()) + if (settings->value("RegularExpression", false).toBool()) flags |= FindRegularExpression; - if (settings->value(QLatin1String("PreserveCase"), false).toBool()) + if (settings->value("PreserveCase", false).toBool()) flags |= FindPreserveCase; settings->endGroup(); settings->endGroup(); diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h index f7295b2334c..baca57597ca 100644 --- a/src/plugins/coreplugin/find/findtoolbar.h +++ b/src/plugins/coreplugin/find/findtoolbar.h @@ -111,9 +111,9 @@ private: void installEventFilters(); void invokeClearResults(); - void setFindFlag(FindFlag flag, bool enabled); - bool hasFindFlag(FindFlag flag); - FindFlags effectiveFindFlags(); + void setFindFlag(Utils::FindFlag flag, bool enabled); + bool hasFindFlag(Utils::FindFlag flag); + Utils::FindFlags effectiveFindFlags(); static FindToolBarPlaceHolder *findToolBarPlaceHolder(); bool toolBarHasFocus() const; ControlStyle controlStyle(bool replaceIsVisible); @@ -174,7 +174,7 @@ private: QToolButton *m_replaceNextButton; QToolButton *m_replaceAllButton; QToolButton *m_advancedButton; - FindFlags m_findFlags; + Utils::FindFlags m_findFlags; QTimer m_findIncrementalTimer; QTimer m_findStepTimer; diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp index d1e3a9fd235..a239d310be6 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.cpp +++ b/src/plugins/coreplugin/find/findtoolwindow.cpp @@ -22,7 +22,6 @@ #include <QPushButton> #include <QRegularExpression> #include <QScrollArea> -#include <QSettings> #include <QStringListModel> using namespace Utils; @@ -360,9 +359,9 @@ void FindToolWindow::writeSettings() void FindToolWindow::readSettings() { - QSettings *settings = ICore::settings(); - settings->beginGroup(QLatin1String("Find")); - const QString currentFilter = settings->value(QLatin1String("CurrentFilter")).toString(); + QtcSettings *settings = ICore::settings(); + settings->beginGroup("Find"); + const QString currentFilter = settings->value("CurrentFilter").toString(); for (int i = 0; i < m_filters.size(); ++i) { IFindFilter *filter = m_filters.at(i); filter->readSettings(settings); diff --git a/src/plugins/coreplugin/find/highlightscrollbarcontroller.h b/src/plugins/coreplugin/find/highlightscrollbarcontroller.h index 60177ae20a5..3b14c9269a4 100644 --- a/src/plugins/coreplugin/find/highlightscrollbarcontroller.h +++ b/src/plugins/coreplugin/find/highlightscrollbarcontroller.h @@ -3,15 +3,15 @@ #pragma once -#include <QHash> -#include <QPointer> -#include <QVector> - -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> #include <utils/theme/theme.h> +#include <QHash> +#include <QPointer> +#include <QVector> + QT_BEGIN_NAMESPACE class QAbstractScrollArea; class QScrollBar; diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index 66b7710586c..a849598cfdd 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -11,6 +11,8 @@ #include <QPainter> #include <QPixmap> +using namespace Utils; + /*! \class Core::IFindFilter \inheaderfile coreplugin/find/ifindfilter.h @@ -130,7 +132,7 @@ */ /*! - \fn void Core::IFindFilter::findAll(const QString &txt, Core::FindFlags findFlags) + \fn void Core::IFindFilter::findAll(const QString &txt, Utils::FindFlags findFlags) This function is called when the user selected this find scope and initiated a search. @@ -147,7 +149,7 @@ */ /*! - \fn void Core::IFindFilter::replaceAll(const QString &txt, Core::FindFlags findFlags) + \fn void Core::IFindFilter::replaceAll(const QString &txt, Utils::FindFlags findFlags) Override this function if you want to support search and replace. This function is called when the user selected this find scope and @@ -252,8 +254,8 @@ QKeySequence IFindFilter::defaultShortcut() const Depending on the returned value, the default find option widgets are enabled or disabled. - The default is Core::FindCaseSensitively, Core::FindRegularExpression - and Core::FindWholeWords. + The default is Utils::FindCaseSensitively, Utils::FindRegularExpression + and Uitls::FindWholeWords. */ FindFlags IFindFilter::supportedFindFlags() const { diff --git a/src/plugins/coreplugin/find/ifindfilter.h b/src/plugins/coreplugin/find/ifindfilter.h index 6ebe76ba22a..2d6325eda01 100644 --- a/src/plugins/coreplugin/find/ifindfilter.h +++ b/src/plugins/coreplugin/find/ifindfilter.h @@ -3,15 +3,18 @@ #pragma once -#include "textfindconstants.h" +#include "../core_global.h" + +#include <utils/filesearch.h> QT_BEGIN_NAMESPACE class QWidget; -class QSettings; class QKeySequence; class Pixmap; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace Core { class CORE_EXPORT IFindFilter : public QObject @@ -32,18 +35,18 @@ public: virtual QKeySequence defaultShortcut() const; virtual bool isReplaceSupported() const { return false; } virtual bool showSearchTermInput() const { return true; } - virtual FindFlags supportedFindFlags() const; + virtual Utils::FindFlags supportedFindFlags() const; - virtual void findAll(const QString &txt, FindFlags findFlags) = 0; - virtual void replaceAll(const QString &txt, FindFlags findFlags) + virtual void findAll(const QString &txt, Utils::FindFlags findFlags) = 0; + virtual void replaceAll(const QString &txt, Utils::FindFlags findFlags) { Q_UNUSED(txt) Q_UNUSED(findFlags) } virtual QWidget *createConfigWidget() { return nullptr; } - virtual void writeSettings(QSettings *settings) { Q_UNUSED(settings) } - virtual void readSettings(QSettings *settings) { Q_UNUSED(settings) } + virtual void writeSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) } + virtual void readSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) } - static QPixmap pixmapForFindFlags(FindFlags flags); - static QString descriptionForFindFlags(FindFlags flags); + static QPixmap pixmapForFindFlags(Utils::FindFlags flags); + static QString descriptionForFindFlags(Utils::FindFlags flags); signals: void enabledChanged(bool enabled); void validChanged(bool valid); diff --git a/src/plugins/coreplugin/find/ifindsupport.cpp b/src/plugins/coreplugin/find/ifindsupport.cpp index 54d8c0128ab..694ded20b3c 100644 --- a/src/plugins/coreplugin/find/ifindsupport.cpp +++ b/src/plugins/coreplugin/find/ifindsupport.cpp @@ -7,6 +7,7 @@ #include <utils/stylehelper.h> using namespace Core; +using namespace Utils; /*! \class Core::IFindSupport @@ -54,16 +55,16 @@ bool IFindSupport::supportsSelectAll() const } /*! - \fn Core::FindFlags Core::IFindSupport::supportedFindFlags() const + \fn Utils::FindFlags Core::IFindSupport::supportedFindFlags() const Returns the find flags, such as whole words or regular expressions, that this find filter supports. Depending on the returned value, the default find option widgets are enabled or disabled. - The default is Core::FindBackward, Core::FindCaseSensitively, - Core::FindRegularExpression, Core::FindWholeWords, and - Core::FindPreserveCase. + The default is Uitls::FindBackward, Utils::FindCaseSensitively, + Uitls::FindRegularExpression, Uitls::FindWholeWords, and + Uitls::FindPreserveCase. */ /*! @@ -87,17 +88,17 @@ bool IFindSupport::supportsSelectAll() const */ /*! - \fn void Core::IFindSupport::highlightAll(const QString &txt, Core::FindFlags findFlags) + \fn void Core::IFindSupport::highlightAll(const QString &txt, Utils::FindFlags findFlags) Highlights all search hits for \a txt when using \a findFlags. */ /*! - \fn Core::IFindSupport::Result Core::IFindSupport::findIncremental(const QString &txt, Core::FindFlags findFlags) + \fn Core::IFindSupport::Result Core::IFindSupport::findIncremental(const QString &txt, Utils::FindFlags findFlags) Performs an incremental search of the search term \a txt using \a findFlags. */ /*! - \fn Core::IFindSupport::Result Core::IFindSupport::findStep(const QString &txt, Core::FindFlags findFlags) + \fn Core::IFindSupport::Result Core::IFindSupport::findStep(const QString &txt, Utils::FindFlags findFlags) Searches for \a txt using \a findFlags. */ @@ -166,6 +167,6 @@ void IFindSupport::selectAll(const QString &txt, FindFlags findFlags) */ void IFindSupport::showWrapIndicator(QWidget *parent) { - Utils::FadingIndicator::showPixmap(parent, Utils::StyleHelper::dpiSpecificImageFile( - QLatin1String(":/find/images/wrapindicator.png"))); + FadingIndicator::showPixmap(parent, StyleHelper::dpiSpecificImageFile( + ":/find/images/wrapindicator.png")); } diff --git a/src/plugins/coreplugin/find/ifindsupport.h b/src/plugins/coreplugin/find/ifindsupport.h index 123cf59da94..39ed5b5af6d 100644 --- a/src/plugins/coreplugin/find/ifindsupport.h +++ b/src/plugins/coreplugin/find/ifindsupport.h @@ -3,7 +3,9 @@ #pragma once -#include "textfindconstants.h" +#include "../core_global.h" + +#include <utils/filesearch.h> #include <QObject> #include <QString> @@ -22,25 +24,23 @@ public: virtual bool supportsReplace() const = 0; virtual bool supportsSelectAll() const; - virtual FindFlags supportedFindFlags() const = 0; + virtual Utils::FindFlags supportedFindFlags() const = 0; virtual void resetIncrementalSearch() = 0; virtual void clearHighlights() = 0; virtual QString currentFindString() const = 0; virtual QString completedFindString() const = 0; - virtual void highlightAll(const QString &, FindFlags) {} - virtual Result findIncremental(const QString &txt, FindFlags findFlags) = 0; - virtual Result findStep(const QString &txt, FindFlags findFlags) = 0; - virtual void replace(const QString &before, const QString &after, - FindFlags findFlags); + virtual void highlightAll(const QString &, Utils::FindFlags) {} + virtual Result findIncremental(const QString &txt, Utils::FindFlags findFlags) = 0; + virtual Result findStep(const QString &txt, Utils::FindFlags findFlags) = 0; + virtual void replace(const QString &before, const QString &after, Utils::FindFlags findFlags); virtual bool replaceStep(const QString &before, const QString &after, - FindFlags findFlags); - virtual int replaceAll(const QString &before, const QString &after, - FindFlags findFlags); - virtual void selectAll(const QString &txt, FindFlags findFlags); + Utils::FindFlags findFlags); + virtual int replaceAll(const QString &before, const QString &after, Utils::FindFlags findFlags); + virtual void selectAll(const QString &txt, Utils::FindFlags findFlags); - virtual void defineFindScope(){} - virtual void clearFindScope(){} + virtual void defineFindScope() {} + virtual void clearFindScope() {} static void showWrapIndicator(QWidget *parent); diff --git a/src/plugins/coreplugin/find/itemviewfind.cpp b/src/plugins/coreplugin/find/itemviewfind.cpp index 8bba18ae692..2fe4ae31003 100644 --- a/src/plugins/coreplugin/find/itemviewfind.cpp +++ b/src/plugins/coreplugin/find/itemviewfind.cpp @@ -3,14 +3,17 @@ #include "itemviewfind.h" +#include "../findplaceholder.h" + #include <aggregation/aggregate.h> -#include <coreplugin/findplaceholder.h> #include <QModelIndex> #include <QTextCursor> #include <QTreeView> #include <QVBoxLayout> +using namespace Utils; + namespace Core { /*! @@ -163,7 +166,7 @@ IFindSupport::Result ItemViewFind::find(const QString &searchTxt, QModelIndex currentIndex = d->m_view->currentIndex(); if (!currentIndex.isValid()) // nothing selected, start from top currentIndex = d->m_view->model()->index(0, 0); - QTextDocument::FindFlags flags = textDocumentFlagsForFindFlags(findFlags); + QTextDocument::FindFlags flags = Utils::textDocumentFlagsForFindFlags(findFlags); QModelIndex resultIndex; QModelIndex index = currentIndex; int currentRow = currentIndex.row(); diff --git a/src/plugins/coreplugin/find/itemviewfind.h b/src/plugins/coreplugin/find/itemviewfind.h index 3637a6f5b31..9447c20a7a5 100644 --- a/src/plugins/coreplugin/find/itemviewfind.h +++ b/src/plugins/coreplugin/find/itemviewfind.h @@ -33,22 +33,22 @@ public: ~ItemViewFind() override; bool supportsReplace() const override; - FindFlags supportedFindFlags() const override; + Utils::FindFlags supportedFindFlags() const override; void resetIncrementalSearch() override; void clearHighlights() override; QString currentFindString() const override; QString completedFindString() const override; - void highlightAll(const QString &txt, FindFlags findFlags) override; - Result findIncremental(const QString &txt, FindFlags findFlags) override; - Result findStep(const QString &txt, FindFlags findFlags) override; + void highlightAll(const QString &txt, Utils::FindFlags findFlags) override; + Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Result findStep(const QString &txt, Utils::FindFlags findFlags) override; static QFrame *createSearchableWrapper(QAbstractItemView *treeView, ColorOption colorOption = DarkColored, FetchOption option = DoNotFetchMoreWhileSearching); static QFrame *createSearchableWrapper(ItemViewFind *finder, ColorOption colorOption = DarkColored); private: - Result find(const QString &txt, FindFlags findFlags, + Result find(const QString &txt, Utils::FindFlags findFlags, bool startFromCurrentIndex, bool *wrapped); QModelIndex nextIndex(const QModelIndex &idx, bool *wrapped) const; QModelIndex prevIndex(const QModelIndex &idx, bool *wrapped) const; diff --git a/src/plugins/coreplugin/find/optionspopup.cpp b/src/plugins/coreplugin/find/optionspopup.cpp index af2e306037c..2f61ebdbb2c 100644 --- a/src/plugins/coreplugin/find/optionspopup.cpp +++ b/src/plugins/coreplugin/find/optionspopup.cpp @@ -3,7 +3,7 @@ #include "optionspopup.h" -#include <coreplugin/actionmanager/actionmanager.h> +#include "../actionmanager/actionmanager.h" #include <utils/qtcassert.h> @@ -18,6 +18,21 @@ using namespace Utils; namespace Core { +static QCheckBox *createCheckboxForCommand(QObject *owner, Id id) +{ + QAction *action = ActionManager::command(id)->action(); + QCheckBox *checkbox = new QCheckBox(action->text()); + checkbox->setToolTip(action->toolTip()); + checkbox->setChecked(action->isChecked()); + checkbox->setEnabled(action->isEnabled()); + checkbox->installEventFilter(owner); // enter key handling + QObject::connect(checkbox, &QCheckBox::clicked, action, &QAction::setChecked); + QObject::connect(action, &QAction::changed, checkbox, [action, checkbox] { + checkbox->setEnabled(action->isEnabled()); + }); + return checkbox; +} + /*! \class Core::OptionsPopup \inmodule QtCreator @@ -35,7 +50,7 @@ OptionsPopup::OptionsPopup(QWidget *parent, const QVector<Id> &commands) bool first = true; for (const Id &command : commands) { - QCheckBox *checkBox = createCheckboxForCommand(command); + QCheckBox *checkBox = createCheckboxForCommand(this, command); if (first) { checkBox->setFocus(); first = false; @@ -73,19 +88,4 @@ bool OptionsPopup::eventFilter(QObject *obj, QEvent *ev) return QWidget::eventFilter(obj, ev); } -QCheckBox *OptionsPopup::createCheckboxForCommand(Id id) -{ - QAction *action = ActionManager::command(id)->action(); - QCheckBox *checkbox = new QCheckBox(action->text()); - checkbox->setToolTip(action->toolTip()); - checkbox->setChecked(action->isChecked()); - checkbox->setEnabled(action->isEnabled()); - checkbox->installEventFilter(this); // enter key handling - QObject::connect(checkbox, &QCheckBox::clicked, action, &QAction::setChecked); - QObject::connect(action, &QAction::changed, checkbox, [action, checkbox] { - checkbox->setEnabled(action->isEnabled()); - }); - return checkbox; -} - } // namespace Core diff --git a/src/plugins/coreplugin/find/optionspopup.h b/src/plugins/coreplugin/find/optionspopup.h index 6331de8914f..329e97db9d0 100644 --- a/src/plugins/coreplugin/find/optionspopup.h +++ b/src/plugins/coreplugin/find/optionspopup.h @@ -3,31 +3,22 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> #include <QWidget> -QT_BEGIN_NAMESPACE -class QCheckBox; -QT_END_NAMESPACE - namespace Core { class CORE_EXPORT OptionsPopup : public QWidget { - Q_OBJECT - public: OptionsPopup(QWidget *parent, const QVector<Utils::Id> &commands); protected: bool event(QEvent *ev) override; bool eventFilter(QObject *obj, QEvent *ev) override; - -private: - QCheckBox *createCheckboxForCommand(Utils::Id id); }; } // namespace Core diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.cpp b/src/plugins/coreplugin/find/searchresulttreeitems.cpp index 9aa8a765db9..6eafd49615d 100644 --- a/src/plugins/coreplugin/find/searchresulttreeitems.cpp +++ b/src/plugins/coreplugin/find/searchresulttreeitems.cpp @@ -82,9 +82,26 @@ int SearchResultTreeItem::insertionIndex(const QString &text, SearchResultTreeIt } int SearchResultTreeItem::insertionIndex(const Utils::SearchResultItem &item, - SearchResultTreeItem **existingItem) const + SearchResultTreeItem **existingItem, + SearchResult::AddMode mode) const { - return insertionIndex(item.lineText(), existingItem); + switch (mode) { + case SearchResult::AddSortedByContent: + return insertionIndex(item.lineText(), existingItem); + case SearchResult::AddSortedByPosition: + break; + case Core::SearchResult::AddOrdered: + QTC_ASSERT(false, return 0); + } + + static const auto cmp = [](const SearchResultTreeItem *a, const Utils::Text::Position b) { + return a->item.mainRange().begin < b; + }; + const auto insertionPosition = + std::lower_bound(m_children.begin(), m_children.end(), item.mainRange().begin, cmp); + if (existingItem) + *existingItem = nullptr; + return insertionPosition - m_children.begin(); } void SearchResultTreeItem::insertChild(int index, SearchResultTreeItem *child) diff --git a/src/plugins/coreplugin/find/searchresulttreeitems.h b/src/plugins/coreplugin/find/searchresulttreeitems.h index dbbc27d8861..e3ec8aa1eae 100644 --- a/src/plugins/coreplugin/find/searchresulttreeitems.h +++ b/src/plugins/coreplugin/find/searchresulttreeitems.h @@ -21,7 +21,8 @@ public: SearchResultTreeItem *parent() const; SearchResultTreeItem *childAt(int index) const; int insertionIndex(const QString &text, SearchResultTreeItem **existingItem) const; - int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem) const; + int insertionIndex(const Utils::SearchResultItem &item, SearchResultTreeItem **existingItem, + SearchResult::AddMode mode) const; void insertChild(int index, SearchResultTreeItem *child); void insertChild(int index, const Utils::SearchResultItem &item); void appendChild(const Utils::SearchResultItem &item); diff --git a/src/plugins/coreplugin/find/searchresulttreemodel.cpp b/src/plugins/coreplugin/find/searchresulttreemodel.cpp index b6d620b80b5..09f16f0453b 100644 --- a/src/plugins/coreplugin/find/searchresulttreemodel.cpp +++ b/src/plugins/coreplugin/find/searchresulttreemodel.cpp @@ -402,10 +402,10 @@ void SearchResultTreeModel::addResultsToCurrentParent(const SearchResultItems &i m_currentParent->appendChild(item); } endInsertRows(); - } else if (mode == SearchResult::AddSorted) { + } else { for (const SearchResultItem &item : items) { SearchResultTreeItem *existingItem; - const int insertionIndex = m_currentParent->insertionIndex(item, &existingItem); + const int insertionIndex = m_currentParent->insertionIndex(item, &existingItem, mode); if (existingItem) { existingItem->setGenerated(false); existingItem->item = item; diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp index d8026ccd847..c246d21fca5 100644 --- a/src/plugins/coreplugin/find/searchresulttreeview.cpp +++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp @@ -44,7 +44,6 @@ SearchResultTreeView::SearchResultTreeView(QWidget *parent) setItemDelegate(new SearchResultTreeItemDelegate(8, this)); setIndentation(14); - setUniformRowHeights(true); setExpandsOnDoubleClick(true); header()->setSectionResizeMode(QHeaderView::ResizeToContents); header()->setStretchLastSection(false); diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp index ec2ab2c0f64..06c59d95ecc 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.cpp +++ b/src/plugins/coreplugin/find/searchresultwidget.cpp @@ -478,7 +478,7 @@ void SearchResultWidget::doReplace() { m_infoBar.clear(); setShowReplaceUI(false); - emit replaceButtonClicked(m_replaceTextEdit->text(), checkedItems(), + emit replaceButtonClicked(m_replaceTextEdit->text(), items(true), m_preserveCaseSupported && m_preserveCaseCheck->isChecked()); } @@ -496,7 +496,7 @@ void SearchResultWidget::searchAgain() emit searchAgainRequested(); } -SearchResultItems SearchResultWidget::checkedItems() const +SearchResultItems SearchResultWidget::items(bool checkedOnly) const { SearchResultItems result; SearchResultFilterModel *model = m_searchResultTreeView->model(); @@ -508,7 +508,7 @@ SearchResultItems SearchResultWidget::checkedItems() const const QModelIndex textIndex = model->index(rowIndex, 0, fileIndex); const SearchResultTreeItem * const rowItem = model->itemForIndex(textIndex); QTC_ASSERT(rowItem != nullptr, continue); - if (rowItem->checkState()) + if (!checkedOnly || rowItem->checkState()) result << rowItem->item; } } diff --git a/src/plugins/coreplugin/find/searchresultwidget.h b/src/plugins/coreplugin/find/searchresultwidget.h index 6722af579eb..a9f87c467f1 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.h +++ b/src/plugins/coreplugin/find/searchresultwidget.h @@ -71,6 +71,7 @@ public: bool hasFilter() const; void showFilterWidget(QWidget *parent); void setReplaceEnabled(bool enabled); + Utils::SearchResultItems items(bool checkedOnly) const; public slots: void finishSearch(bool canceled, const QString &reason); @@ -103,7 +104,6 @@ private: void continueAfterSizeWarning(); void cancelAfterSizeWarning(); - Utils::SearchResultItems checkedItems() const; void updateMatchesFoundLabel(); SearchResultTreeView *m_searchResultTreeView = nullptr; diff --git a/src/plugins/coreplugin/find/searchresultwindow.cpp b/src/plugins/coreplugin/find/searchresultwindow.cpp index a096f452490..d2b23e6f1e4 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.cpp +++ b/src/plugins/coreplugin/find/searchresultwindow.cpp @@ -22,7 +22,6 @@ #include <QFont> #include <QLabel> #include <QScrollArea> -#include <QSettings> #include <QStackedWidget> #include <QToolButton> @@ -290,13 +289,6 @@ using namespace Core::Internal; This signal is emitted when the search status is set to \a paused. */ -/*! - \fn void Core::SearchResult::requestEnabledCheck() - - This signal is emitted when the enabled status of search results is - requested. -*/ - /*! \fn void Core::SearchResult::searchAgainRequested() @@ -370,6 +362,9 @@ SearchResultWindow *SearchResultWindow::m_instance = nullptr; SearchResultWindow::SearchResultWindow(QWidget *newSearchPanel) : d(new SearchResultWindowPrivate(this, newSearchPanel)) { + setId("SearchResults"); + setDisplayName(Tr::tr("Search Results")); + setPriorityInStatusBar(80); m_instance = this; readSettings(); } @@ -642,9 +637,9 @@ QList<QWidget *> SearchResultWindowPrivate::toolBarWidgets() */ void SearchResultWindow::readSettings() { - QSettings *s = ICore::settings(); - s->beginGroup(QLatin1String(SETTINGSKEYSECTIONNAME)); - d->m_expandCollapseAction->setChecked(s->value(QLatin1String(SETTINGSKEYEXPANDRESULTS), + Utils::QtcSettings *s = ICore::settings(); + s->beginGroup(SETTINGSKEYSECTIONNAME); + d->m_expandCollapseAction->setChecked(s->value(SETTINGSKEYEXPANDRESULTS, SearchResultWindowPrivate::m_initiallyExpand).toBool()); s->endGroup(); } @@ -662,14 +657,6 @@ void SearchResultWindow::writeSettings() s->endGroup(); } -/*! - \internal -*/ -int SearchResultWindow::priorityInStatusBar() const -{ - return 80; -} - /*! \internal */ @@ -716,14 +703,6 @@ bool SearchResultWindow::canNavigate() const return true; } -/*! - \internal -*/ -QString SearchResultWindow::displayName() const -{ - return Tr::tr("Search Results"); -} - /*! \internal */ @@ -907,6 +886,11 @@ void Core::SearchResult::makeNonInteractive(const std::function<void ()> &callba m_finishedHandler = callback; } +Utils::SearchResultItems SearchResult::allItems() const +{ + return m_widget->items(false); +} + } // namespace Core #include "searchresultwindow.moc" diff --git a/src/plugins/coreplugin/find/searchresultwindow.h b/src/plugins/coreplugin/find/searchresultwindow.h index ae611926818..c2b7a13b273 100644 --- a/src/plugins/coreplugin/find/searchresultwindow.h +++ b/src/plugins/coreplugin/find/searchresultwindow.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/ioutputpane.h> +#include "../ioutputpane.h" #include <utils/searchresultitem.h> @@ -43,7 +43,8 @@ class CORE_EXPORT SearchResult : public QObject public: enum AddMode { - AddSorted, + AddSortedByContent, + AddSortedByPosition, AddOrdered }; @@ -57,6 +58,7 @@ public: void setAdditionalReplaceWidget(QWidget *widget); void makeNonInteractive(const std::function<void()> &callback); bool isInteractive() const { return !m_finishedHandler; } + Utils::SearchResultItems allItems() const; public slots: void addResult(const Utils::SearchResultItem &item); @@ -79,7 +81,6 @@ signals: void visibilityChanged(bool visible); void countChanged(int count); void searchAgainRequested(); - void requestEnabledCheck(); private: SearchResult(Internal::SearchResultWidget *widget); @@ -113,8 +114,6 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget*> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void visibilityChanged(bool visible) override; bool hasFocus() const override; bool canFocus() const override; diff --git a/src/plugins/coreplugin/find/textfindconstants.h b/src/plugins/coreplugin/find/textfindconstants.h index 067bf5fc7f1..e058ae4df2e 100644 --- a/src/plugins/coreplugin/find/textfindconstants.h +++ b/src/plugins/coreplugin/find/textfindconstants.h @@ -3,8 +3,6 @@ #pragma once -#include <coreplugin/core_global.h> - #include <QMetaType> #include <QFlags> #include <QTextDocument> @@ -40,19 +38,4 @@ const char TASK_SEARCH[] = "Find.Task.Search"; } // namespace Constants -enum FindFlag { - FindBackward = 0x01, - FindCaseSensitively = 0x02, - FindWholeWords = 0x04, - FindRegularExpression = 0x08, - FindPreserveCase = 0x10 -}; -Q_DECLARE_FLAGS(FindFlags, FindFlag) - -// defined in findplugin.cpp -QTextDocument::FindFlags CORE_EXPORT textDocumentFlagsForFindFlags(FindFlags flags); - } // namespace Core - -Q_DECLARE_OPERATORS_FOR_FLAGS(Core::FindFlags) -Q_DECLARE_METATYPE(Core::FindFlags) diff --git a/src/plugins/coreplugin/foldernavigationwidget.cpp b/src/plugins/coreplugin/foldernavigationwidget.cpp index 7460f427b5c..b58b6c4e466 100644 --- a/src/plugins/coreplugin/foldernavigationwidget.cpp +++ b/src/plugins/coreplugin/foldernavigationwidget.cpp @@ -7,7 +7,6 @@ #include "actionmanager/command.h" #include "coreicons.h" #include "coreplugintr.h" -#include "diffservice.h" #include "documentmanager.h" #include "editormanager/editormanager.h" #include "editormanager/ieditor.h" @@ -19,8 +18,6 @@ #include <extensionsystem/pluginmanager.h> -#include <texteditor/textdocument.h> - #include <utils/algorithm.h> #include <utils/filecrumblabel.h> #include <utils/filepath.h> @@ -30,6 +27,7 @@ #include <utils/navigationtreeview.h> #include <utils/qtcassert.h> #include <utils/removefiledialog.h> +#include <utils/store.h> #include <utils/stringutils.h> #include <utils/styledbar.h> #include <utils/stylehelper.h> @@ -835,13 +833,13 @@ const bool kShowBreadCrumbsDefault = true; const bool kRootAutoSyncDefault = true; const bool kShowFoldersOnTopDefault = true; -void FolderNavigationWidgetFactory::saveSettings(Utils::QtcSettings *settings, +void FolderNavigationWidgetFactory::saveSettings(QtcSettings *settings, int position, QWidget *widget) { auto fnw = qobject_cast<FolderNavigationWidget *>(widget); QTC_ASSERT(fnw, return); - const QString base = kSettingsBase + QString::number(position); + const Key base = numberedKey(kSettingsBase, position); settings->setValueWithDefault(base + kHiddenFilesKey, fnw->hiddenFilesFilter(), kHiddenFilesDefault); @@ -857,11 +855,11 @@ void FolderNavigationWidgetFactory::saveSettings(Utils::QtcSettings *settings, kShowFoldersOnTopDefault); } -void FolderNavigationWidgetFactory::restoreSettings(QSettings *settings, int position, QWidget *widget) +void FolderNavigationWidgetFactory::restoreSettings(QtcSettings *settings, int position, QWidget *widget) { auto fnw = qobject_cast<FolderNavigationWidget *>(widget); QTC_ASSERT(fnw, return); - const QString base = kSettingsBase + QString::number(position); + const Key base = numberedKey(kSettingsBase, position); fnw->setHiddenFilesFilter(settings->value(base + kHiddenFilesKey, kHiddenFilesDefault).toBool()); fnw->setAutoSynchronization(settings->value(base + kSyncKey, kAutoSyncDefault).toBool()); fnw->setShowBreadCrumbs( diff --git a/src/plugins/coreplugin/foldernavigationwidget.h b/src/plugins/coreplugin/foldernavigationwidget.h index d725d4474e5..589582b00e4 100644 --- a/src/plugins/coreplugin/foldernavigationwidget.h +++ b/src/plugins/coreplugin/foldernavigationwidget.h @@ -56,7 +56,7 @@ public: Core::NavigationView createWidget() override; void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; - void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; void addRootPath(Utils::Id id, const QString &displayName, const QIcon &icon, const Utils::FilePath &path) override; void removeRootPath(Utils::Id path) override; diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp index eab29faf7d6..3087b0d8c4b 100644 --- a/src/plugins/coreplugin/generalsettings.cpp +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -1,14 +1,14 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "dialogs/restartdialog.h" +#include "dialogs/ioptionspage.h" #include "generalsettings.h" #include "coreconstants.h" #include "coreplugintr.h" #include "icore.h" #include "themechooser.h" -#include <coreplugin/dialogs/restartdialog.h> - #include <extensionsystem/pluginmanager.h> #include <utils/algorithm.h> @@ -35,18 +35,45 @@ using namespace Utils; using namespace Layouting; -namespace Core { -namespace Internal { +namespace Core::Internal { -const char settingsKeyDPI[] = "Core/EnableHighDpiScaling"; -const char settingsKeyShortcutsInContextMenu[] = "General/ShowShortcutsInContextMenu"; +const char settingsKeyDpiPolicy[] = "Core/HighDpiScaleFactorRoundingPolicy"; const char settingsKeyCodecForLocale[] = "General/OverrideCodecForLocale"; const char settingsKeyToolbarStyle[] = "General/ToolbarStyle"; +static bool defaultShowShortcutsInContextMenu() +{ + return QGuiApplication::styleHints()->showShortcutsInContextMenus(); +} + +GeneralSettings &generalSettings() +{ + static GeneralSettings theSettings; + return theSettings; +} + +GeneralSettings::GeneralSettings() +{ + setAutoApply(false); + + showShortcutsInContextMenus.setSettingsKey("General/ShowShortcutsInContextMenu"); + showShortcutsInContextMenus.setDefaultValue(defaultShowShortcutsInContextMenu()); + showShortcutsInContextMenus.setLabelText( + Tr::tr("Show keyboard shortcuts in context menus (default: %1)") + .arg(defaultShowShortcutsInContextMenu() ? Tr::tr("on") : Tr::tr("off"))); + + connect(&showShortcutsInContextMenus, &BaseAspect::changed, this, [this] { + QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, + !showShortcutsInContextMenus()); + }); + + readSettings(); +} + class GeneralSettingsWidget final : public IOptionsPageWidget { public: - explicit GeneralSettingsWidget(GeneralSettings *q); + GeneralSettingsWidget(); void apply() final; @@ -61,23 +88,21 @@ public: void fillCodecBox() const; static QByteArray codecForLocale(); static void setCodecForLocale(const QByteArray&); - void fillToolbarSyleBox() const; + void fillToolbarStyleBox() const; + static void setDpiPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy); - GeneralSettings *q; QComboBox *m_languageBox; QComboBox *m_codecBox; - QCheckBox *m_showShortcutsInContextMenus; QtColorButton *m_colorButton; ThemeChooser *m_themeChooser; QPushButton *m_resetWarningsButton; QComboBox *m_toolbarStyleBox; + QComboBox *m_policyComboBox = nullptr; }; -GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q) - : q(q) - , m_languageBox(new QComboBox) +GeneralSettingsWidget::GeneralSettingsWidget() + : m_languageBox(new QComboBox) , m_codecBox(new QComboBox) - , m_showShortcutsInContextMenus(new QCheckBox) , m_colorButton(new QtColorButton) , m_themeChooser(new ThemeChooser) , m_resetWarningsButton(new QPushButton) @@ -110,36 +135,39 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q) form.addRow({Tr::tr("Toolbar style:"), m_toolbarStyleBox, st}); form.addRow({Tr::tr("Language:"), m_languageBox, st}); - if (!Utils::HostOsInfo::isMacHost()) { - auto dpiCheckbox = new QCheckBox(Tr::tr("Enable high DPI scaling")); - form.addRow({empty, dpiCheckbox}); - const bool defaultValue = Utils::HostOsInfo::isWindowsHost(); - dpiCheckbox->setChecked(ICore::settings()->value(settingsKeyDPI, defaultValue).toBool()); - connect(dpiCheckbox, &QCheckBox::toggled, this, [defaultValue](bool checked) { - ICore::settings()->setValueWithDefault(settingsKeyDPI, checked, defaultValue); - QMessageBox::information(ICore::dialogParent(), - Tr::tr("Restart Required"), - Tr::tr("The high DPI settings will take effect after restart.")); - }); + if (StyleHelper::defaultHighDpiScaleFactorRoundingPolicy() + != Qt::HighDpiScaleFactorRoundingPolicy::Unset) { + using Policy = Qt::HighDpiScaleFactorRoundingPolicy; + m_policyComboBox = new QComboBox; + m_policyComboBox->addItem(Tr::tr("Round Up for .5 and Above"), int(Policy::Round)); + m_policyComboBox->addItem(Tr::tr("Always Round Up"), int(Policy::Ceil)); + m_policyComboBox->addItem(Tr::tr("Always Round Down"), int(Policy::Floor)); + m_policyComboBox->addItem(Tr::tr("Round Up for .75 and Above"), + int(Policy::RoundPreferFloor)); + m_policyComboBox->addItem(Tr::tr("Don't Round"), int(Policy::PassThrough)); + m_policyComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + + const Policy userPolicy = + ICore::settings()->value(settingsKeyDpiPolicy, + int(StyleHelper::defaultHighDpiScaleFactorRoundingPolicy())) + .value<Policy>(); + m_policyComboBox->setCurrentIndex(m_policyComboBox->findData(int(userPolicy))); + + form.addRow({Tr::tr("DPI rounding policy:"), m_policyComboBox, st}); } - form.addRow({empty, m_showShortcutsInContextMenus}); + form.addRow({empty, generalSettings().showShortcutsInContextMenus}); form.addRow({Row{m_resetWarningsButton, st}}); form.addRow({Tr::tr("Text codec for tools:"), m_codecBox, st}); Column{Group{title(Tr::tr("User Interface")), form}}.attachTo(this); fillLanguageBox(); fillCodecBox(); - fillToolbarSyleBox(); + fillToolbarStyleBox(); m_colorButton->setColor(StyleHelper::requestedBaseColor()); m_resetWarningsButton->setEnabled(canResetWarnings()); - m_showShortcutsInContextMenus->setText( - Tr::tr("Show keyboard shortcuts in context menus (default: %1)") - .arg(q->m_defaultShowShortcutsInContextMenu ? Tr::tr("on") : Tr::tr("off"))); - m_showShortcutsInContextMenus->setChecked(GeneralSettings::showShortcutsInContextMenu()); - connect(resetColorButton, &QAbstractButton::clicked, this, @@ -155,7 +183,7 @@ static bool hasQmFilesForLocale(const QString &locale, const QString &creatorTrP static const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); const QString trFile = QLatin1String("/qt_") + locale + QLatin1String(".qm"); - return QFile::exists(qtTrPath + trFile) || QFile::exists(creatorTrPath + trFile); + return QFileInfo::exists(qtTrPath + trFile) || QFileInfo::exists(creatorTrPath + trFile); } void GeneralSettingsWidget::fillLanguageBox() const @@ -191,11 +219,18 @@ void GeneralSettingsWidget::fillLanguageBox() const void GeneralSettingsWidget::apply() { + generalSettings().apply(); + generalSettings().writeSettings(); + int currentIndex = m_languageBox->currentIndex(); setLanguage(m_languageBox->itemData(currentIndex, Qt::UserRole).toString()); + if (m_policyComboBox) { + const Qt::HighDpiScaleFactorRoundingPolicy selectedPolicy = + m_policyComboBox->currentData().value<Qt::HighDpiScaleFactorRoundingPolicy>(); + setDpiPolicy(selectedPolicy); + } currentIndex = m_codecBox->currentIndex(); setCodecForLocale(m_codecBox->itemText(currentIndex).toLocal8Bit()); - q->setShowShortcutsInContextMenu(m_showShortcutsInContextMenus->isChecked()); // Apply the new base color if accepted StyleHelper::setBaseColor(m_colorButton->color()); m_themeChooser->apply(); @@ -210,14 +245,6 @@ void GeneralSettingsWidget::apply() } } -bool GeneralSettings::showShortcutsInContextMenu() -{ - return ICore::settings() - ->value(settingsKeyShortcutsInContextMenu, - QGuiApplication::styleHints()->showShortcutsInContextMenus()) - .toBool(); -} - void GeneralSettingsWidget::resetInterfaceColor() { m_colorButton->setColor(StyleHelper::DEFAULT_BASE_COLOR); @@ -243,20 +270,20 @@ void GeneralSettingsWidget::resetLanguage() QString GeneralSettingsWidget::language() { - QSettings *settings = ICore::settings(); - return settings->value(QLatin1String("General/OverrideLanguage")).toString(); + QtcSettings *settings = ICore::settings(); + return settings->value("General/OverrideLanguage").toString(); } void GeneralSettingsWidget::setLanguage(const QString &locale) { QtcSettings *settings = ICore::settings(); - if (settings->value(QLatin1String("General/OverrideLanguage")).toString() != locale) { + if (settings->value("General/OverrideLanguage").toString() != locale) { RestartDialog dialog(ICore::dialogParent(), Tr::tr("The language change will take effect after restart.")); dialog.exec(); } - settings->setValueWithDefault(QLatin1String("General/OverrideLanguage"), locale, {}); + settings->setValueWithDefault("General/OverrideLanguage", locale, {}); } void GeneralSettingsWidget::fillCodecBox() const @@ -273,7 +300,7 @@ void GeneralSettingsWidget::fillCodecBox() const QByteArray GeneralSettingsWidget::codecForLocale() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); QByteArray codec = settings->value(settingsKeyCodecForLocale).toByteArray(); if (codec.isEmpty()) codec = QTextCodec::codecForLocale()->name(); @@ -297,7 +324,7 @@ StyleHelper::ToolbarStyle toolbarStylefromSettings() StyleHelper::defaultToolbarStyle).toInt()); } -void GeneralSettingsWidget::fillToolbarSyleBox() const +void GeneralSettingsWidget::fillToolbarStyleBox() const { m_toolbarStyleBox->addItem(Tr::tr("Compact"), StyleHelper::ToolbarStyleCompact); m_toolbarStyleBox->addItem(Tr::tr("Relaxed"), StyleHelper::ToolbarStyleRelaxed); @@ -305,12 +332,21 @@ void GeneralSettingsWidget::fillToolbarSyleBox() const m_toolbarStyleBox->setCurrentIndex(curId); } -void GeneralSettings::setShowShortcutsInContextMenu(bool show) +void GeneralSettingsWidget::setDpiPolicy(Qt::HighDpiScaleFactorRoundingPolicy policy) { - ICore::settings()->setValueWithDefault(settingsKeyShortcutsInContextMenu, - show, - m_defaultShowShortcutsInContextMenu); - QCoreApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, !show); + QtcSettings *settings = ICore::settings(); + using Policy = Qt::HighDpiScaleFactorRoundingPolicy; + const Policy previousPolicy = settings->value( + settingsKeyDpiPolicy, + int(StyleHelper::defaultHighDpiScaleFactorRoundingPolicy())).value<Policy>(); + if (policy != previousPolicy) { + RestartDialog dialog(ICore::dialogParent(), + Tr::tr("The DPI rounding policy change will take effect after " + "restart.")); + dialog.exec(); + } + settings->setValueWithDefault(settingsKeyDpiPolicy, int(policy), + int(StyleHelper::defaultHighDpiScaleFactorRoundingPolicy())); } void GeneralSettings::applyToolbarStyleFromSettings() @@ -318,18 +354,22 @@ void GeneralSettings::applyToolbarStyleFromSettings() StyleHelper::setToolbarStyle(toolbarStylefromSettings()); } -GeneralSettings::GeneralSettings() +// GeneralSettingsPage + +class GeneralSettingsPage final : public IOptionsPage { - setId(Constants::SETTINGS_ID_INTERFACE); - setDisplayName(Tr::tr("Interface")); - setCategory(Constants::SETTINGS_CATEGORY_CORE); - setDisplayCategory(Tr::tr("Environment")); - setCategoryIconPath(":/core/images/settingscategory_core.png"); - setWidgetCreator([this] { return new GeneralSettingsWidget(this); }); +public: + GeneralSettingsPage() + { + setId(Constants::SETTINGS_ID_INTERFACE); + setDisplayName(Tr::tr("Interface")); + setCategory(Constants::SETTINGS_CATEGORY_CORE); + setDisplayCategory(Tr::tr("Environment")); + setCategoryIconPath(":/core/images/settingscategory_core.png"); + setWidgetCreator([] { return new GeneralSettingsWidget; }); + } +}; - m_defaultShowShortcutsInContextMenu = QGuiApplication::styleHints() - ->showShortcutsInContextMenus(); -} +const GeneralSettingsPage settingsPage; -} // namespace Internal -} // namespace Core +} // Core::Internal diff --git a/src/plugins/coreplugin/generalsettings.h b/src/plugins/coreplugin/generalsettings.h index 4587443eaca..498e243b2a4 100644 --- a/src/plugins/coreplugin/generalsettings.h +++ b/src/plugins/coreplugin/generalsettings.h @@ -3,25 +3,20 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> -namespace Core { -namespace Internal { +namespace Core::Internal { -class GeneralSettings : public IOptionsPage +class GeneralSettings : public Utils::AspectContainer { public: GeneralSettings(); - static bool showShortcutsInContextMenu(); - void setShowShortcutsInContextMenu(bool show); + Utils::BoolAspect showShortcutsInContextMenus{this}; static void applyToolbarStyleFromSettings(); - -private: - friend class GeneralSettingsWidget; - bool m_defaultShowShortcutsInContextMenu; }; -} // namespace Internal -} // namespace Core +GeneralSettings &generalSettings(); + +} // Core::Internal diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 3808b2413f7..1c04ebd2b6d 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -3,24 +3,97 @@ #include "icore.h" +#include "actionmanager/actioncontainer.h" +#include "actionmanager/actionmanager.h" +#include "actionmanager/command.h" +#include "coreicons.h" #include "coreplugintr.h" +#include "coreplugintr.h" +#include "dialogs/externaltoolconfig.h" #include "dialogs/settingsdialog.h" +#include "dialogs/shortcutsettings.h" +#include "documentmanager.h" +#include "editormanager/documentmodel_p.h" +#include "editormanager/editormanager.h" +#include "editormanager/editormanager_p.h" +#include "editormanager/ieditor.h" +#include "editormanager/ieditorfactory.h" +#include "editormanager/systemeditor.h" +#include "externaltoolmanager.h" +#include "fancytabwidget.h" +#include "fileutils.h" +#include "find/basetextfind.h" +#include "findplaceholder.h" +#include "helpmanager.h" +#include "icore.h" +#include "idocumentfactory.h" +#include "inavigationwidgetfactory.h" +#include "iwizardfactory.h" +#include "jsexpander.h" +#include "loggingviewer.h" +#include "manhattanstyle.h" +#include "messagemanager.h" +#include "mimetypesettings.h" +#include "modemanager.h" +#include "navigationwidget.h" +#include "outputpanemanager.h" +#include "plugindialog.h" +#include "progressmanager/progressmanager_p.h" +#include "progressmanager/progressview.h" +#include "rightpane.h" +#include "statusbarmanager.h" +#include "systemsettings.h" +#include "vcsmanager.h" +#include "versiondialog.h" #include "windowsupport.h" -#include <app/app_version.h> - #include <extensionsystem/pluginmanager.h> #include <utils/algorithm.h> +#include <utils/appinfo.h> +#include <utils/checkablemessagebox.h> #include <utils/environment.h> #include <utils/fileutils.h> +#include <utils/fsengine/fileiconprovider.h> +#include <utils/fsengine/fsengine.h> +#include <utils/historycompleter.h> +#include <utils/hostosinfo.h> +#include <utils/mimeutils.h> +#include <utils/proxyaction.h> #include <utils/qtcassert.h> +#include <utils/stringutils.h> +#include <utils/stylehelper.h> +#include <utils/terminalcommand.h> +#include <utils/theme/theme.h> +#include <utils/touchbar/touchbar.h> +#include <utils/utilsicons.h> +#include <nanotrace/nanotrace.h> + +#include <QActionGroup> #include <QApplication> +#include <QCloseEvent> +#include <QColorDialog> +#include <QComboBox> #include <QDebug> +#include <QDialogButtonBox> #include <QLibraryInfo> +#include <QMenu> +#include <QMenuBar> +#include <QMessageBox> +#include <QPrinter> #include <QStandardPaths> +#include <QStatusBar> +#include <QStyleFactory> #include <QSysInfo> +#include <QTextBrowser> +#include <QTimer> +#include <QToolButton> +#include <QVersionNumber> + +#ifdef Q_OS_LINUX +#include <malloc.h> +#endif /*! \namespace Core @@ -30,7 +103,7 @@ */ /*! - \enum Core::FindFlag + \enum Utils::FindFlag This enum holds the find flags. \value FindBackward @@ -128,7 +201,6 @@ #include "dialogs/newdialogwidget.h" #include "dialogs/newdialog.h" #include "iwizardfactory.h" -#include "mainwindow.h" #include "documentmanager.h" #include <utils/hostosinfo.h> @@ -146,15 +218,145 @@ using namespace Utils; namespace Core { +const char settingsGroup[] = "MainWindow"; +const char colorKey[] = "Color"; +const char windowGeometryKey[] = "WindowGeometry"; +const char windowStateKey[] = "WindowState"; +const char modeSelectorLayoutKey[] = "ModeSelectorLayout"; +const char menubarVisibleKey[] = "MenubarVisible"; + +namespace Internal { + +class MainWindow : public AppMainWindow +{ +public: + MainWindow() + { + setWindowTitle(QGuiApplication::applicationDisplayName()); + setDockNestingEnabled(true); + setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); + setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); + } + +private: + void closeEvent(QCloseEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; +}; + +} // Internal + // The Core Singleton -static ICore *m_instance = nullptr; -static MainWindow *m_mainwindow = nullptr; +static ICore *m_core = nullptr; static NewDialog *defaultDialogFactory(QWidget *parent) { return new NewDialogWidget(parent); } +namespace Internal { + +class ICorePrivate : public QObject +{ +public: + ICorePrivate() {} + + ~ICorePrivate(); + + void init(); + + static void openFile(); + void aboutToShowRecentFiles(); + + static void setFocusToEditor(); + void aboutQtCreator(); + void aboutPlugins(); + void changeLog(); + void contact(); + void updateFocusWidget(QWidget *old, QWidget *now); + NavigationWidget *navigationWidget(Side side) const; + void setSidebarVisible(bool visible, Side side); + void destroyVersionDialog(); + void openDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files); + void restoreWindowState(); + + void openFileFromDevice(); + + void updateContextObject(const QList<IContext *> &context); + void updateContext(); + + void registerDefaultContainers(); + void registerDefaultActions(); + void registerModeSelectorStyleActions(); + + void readSettings(); + void saveWindowSettings(); + + void updateModeSelectorStyleMenu(); + + MainWindow *m_mainwindow = nullptr; + QTimer m_trimTimer; + QStringList m_aboutInformation; + Context m_highPrioAdditionalContexts; + Context m_lowPrioAdditionalContexts{Constants::C_GLOBAL}; + mutable QPrinter *m_printer = nullptr; + WindowSupport *m_windowSupport = nullptr; + EditorManager *m_editorManager = nullptr; + ExternalToolManager *m_externalToolManager = nullptr; + MessageManager *m_messageManager = nullptr; + ProgressManagerPrivate *m_progressManager = nullptr; + JsExpander *m_jsExpander = nullptr; + VcsManager *m_vcsManager = nullptr; + ModeManager *m_modeManager = nullptr; + FancyTabWidget *m_modeStack = nullptr; + NavigationWidget *m_leftNavigationWidget = nullptr; + NavigationWidget *m_rightNavigationWidget = nullptr; + RightPaneWidget *m_rightPaneWidget = nullptr; + VersionDialog *m_versionDialog = nullptr; + + QList<IContext *> m_activeContext; + + std::unordered_map<QWidget *, IContext *> m_contextWidgets; + + ShortcutSettings *m_shortcutSettings = nullptr; + ToolSettings *m_toolSettings = nullptr; + MimeTypeSettings *m_mimeTypeSettings = nullptr; + SystemEditor *m_systemEditor = nullptr; + + // actions + QAction *m_focusToEditor = nullptr; + QAction *m_newAction = nullptr; + QAction *m_openAction = nullptr; + QAction *m_openWithAction = nullptr; + QAction *m_openFromDeviceAction = nullptr; + QAction *m_saveAllAction = nullptr; + QAction *m_exitAction = nullptr; + QAction *m_optionsAction = nullptr; + QAction *m_loggerAction = nullptr; + QAction *m_toggleLeftSideBarAction = nullptr; + QAction *m_toggleRightSideBarAction = nullptr; + QAction *m_toggleMenubarAction = nullptr; + QAction *m_cycleModeSelectorStyleAction = nullptr; + QAction *m_setModeSelectorStyleIconsAndTextAction = nullptr; + QAction *m_setModeSelectorStyleHiddenAction = nullptr; + QAction *m_setModeSelectorStyleIconsOnlyAction = nullptr; + QAction *m_themeAction = nullptr; + + QToolButton *m_toggleLeftSideBarButton = nullptr; + QToolButton *m_toggleRightSideBarButton = nullptr; + QColor m_overrideColor; + QList<std::function<bool()>> m_preCloseListeners; +}; + +static QMenuBar *globalMenuBar() +{ + return ActionManager::actionContainer(Constants::MENU_BAR)->menuBar(); +} + +} // Internal + +static ICorePrivate *d = nullptr; + static std::function<NewDialog *(QWidget *)> m_newDialogFactory = defaultDialogFactory; /*! @@ -162,7 +364,7 @@ static std::function<NewDialog *(QWidget *)> m_newDialogFactory = defaultDialogF */ ICore *ICore::instance() { - return m_instance; + return m_core; } /*! @@ -189,13 +391,13 @@ QWidget *ICore::newItemDialog() /*! \internal */ -ICore::ICore(MainWindow *mainwindow) +ICore::ICore() { - m_instance = this; - m_mainwindow = mainwindow; - // Save settings once after all plugins are initialized: - connect(PluginManager::instance(), &PluginManager::initializationDone, - this, [] { ICore::saveSettings(ICore::InitializationDone); }); + m_core = this; + + d = new ICorePrivate; + d->init(); // Separation needed for now as the call triggers other MainWindow calls. + connect(PluginManager::instance(), &PluginManager::testsFinished, this, [this](int failedTests) { emit coreAboutToClose(); @@ -209,7 +411,7 @@ ICore::ICore(MainWindow *mainwindow) QCoreApplication::exit(exitCode); }); - FileUtils::setDialogParentGetter(&ICore::dialogParent); + Utils::FileUtils::setDialogParentGetter(&ICore::dialogParent); } /*! @@ -217,8 +419,8 @@ ICore::ICore(MainWindow *mainwindow) */ ICore::~ICore() { - m_instance = nullptr; - m_mainwindow = nullptr; + delete d; + m_core = nullptr; } /*! @@ -260,7 +462,7 @@ void ICore::showNewItemDialog(const QString &title, dialogFactory = defaultDialogFactory; NewDialog *newDialog = dialogFactory(dialogParent()); - connect(newDialog->widget(), &QObject::destroyed, m_instance, &ICore::updateNewItemDialogState); + connect(newDialog->widget(), &QObject::destroyed, m_core, &ICore::updateNewItemDialogState); newDialog->setWizardFactories(factories, defaultLocation, extraVariables); newDialog->setWindowTitle(title); newDialog->showDialog(); @@ -326,7 +528,7 @@ bool ICore::showWarningWithOptions(const QString &title, const QString &text, const QString &details, Id settingsId, QWidget *parent) { if (!parent) - parent = m_mainwindow; + parent = d->m_mainwindow; QMessageBox msgBox(QMessageBox::Warning, title, text, QMessageBox::Ok, parent); msgBox.setEscapeButton(QMessageBox::Ok); @@ -371,21 +573,6 @@ QtcSettings *ICore::settings(QSettings::Scope scope) return PluginManager::globalSettings(); } -/*! - Returns the application's settings database. - - The settings database is meant as an alternative to the regular settings - object. It is more suitable for storing large amounts of data. The settings - are application wide. - - \sa SettingsDatabase - \sa settings() -*/ -SettingsDatabase *ICore::settingsDatabase() -{ - return m_mainwindow->settingsDatabase(); -} - /*! Returns the application's printer object. @@ -394,7 +581,9 @@ SettingsDatabase *ICore::settingsDatabase() */ QPrinter *ICore::printer() { - return m_mainwindow->printer(); + if (!d->m_printer) + d->m_printer = new QPrinter(QPrinter::HighResolution); + return d->m_printer; } /*! @@ -446,7 +635,7 @@ FilePath ICore::userResourcePath(const QString &rel) { // Create qtcreator dir if it doesn't yet exist const QString configDir = QFileInfo(settings(QSettings::UserScope)->fileName()).path(); - const QString urp = configDir + '/' + QLatin1String(Constants::IDE_ID); + const QString urp = configDir + '/' + appInfo().id; if (!QFileInfo::exists(urp + QLatin1Char('/'))) { QDir dir; @@ -473,7 +662,7 @@ FilePath ICore::cacheResourcePath(const QString &rel) FilePath ICore::installerResourcePath(const QString &rel) { return FilePath::fromString(settings(QSettings::SystemScope)->fileName()).parentDir() - / Constants::IDE_ID / rel; + / appInfo().id / rel; } /*! @@ -493,15 +682,18 @@ QString ICore::pluginPath() */ QString ICore::userPluginPath() { + const QVersionNumber appVersion = QVersionNumber::fromString( + QCoreApplication::applicationVersion()); QString pluginPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); if (Utils::HostOsInfo::isAnyUnixHost() && !Utils::HostOsInfo::isMacHost()) pluginPath += "/data"; - pluginPath += '/' + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR) + '/'; - pluginPath += QLatin1String(Utils::HostOsInfo::isMacHost() ? Core::Constants::IDE_DISPLAY_NAME - : Core::Constants::IDE_ID); + pluginPath += '/' + QCoreApplication::organizationName() + '/'; + pluginPath += Utils::HostOsInfo::isMacHost() ? QGuiApplication::applicationDisplayName() + : appInfo().id; pluginPath += "/plugins/"; - pluginPath += QString::number(IDE_VERSION_MAJOR) + '.' + QString::number(IDE_VERSION_MINOR) - + '.' + QString::number(IDE_VERSION_RELEASE); + pluginPath += QString::number(appVersion.majorVersion()) + '.' + + QString::number(appVersion.minorVersion()) + '.' + + QString::number(appVersion.microVersion()); return pluginPath; } @@ -524,11 +716,6 @@ FilePath ICore::crashReportsPath() return libexecPath("crashpad_reports/reports"); } -QString ICore::ideDisplayName() -{ - return Constants::IDE_DISPLAY_NAME; -} - static QString clangIncludePath(const QString &clangVersion) { return "/lib/clang/" + clangVersion + "/include"; @@ -625,10 +812,10 @@ static QString compilerString() QString ICore::versionString() { QString ideVersionDescription; - if (QLatin1String(Constants::IDE_VERSION_LONG) != QLatin1String(Constants::IDE_VERSION_DISPLAY)) - ideVersionDescription = Tr::tr(" (%1)").arg(QLatin1String(Constants::IDE_VERSION_LONG)); - return Tr::tr("%1 %2%3").arg(QLatin1String(Constants::IDE_DISPLAY_NAME), - QLatin1String(Constants::IDE_VERSION_DISPLAY), + if (QCoreApplication::applicationVersion() != appInfo().displayVersion) + ideVersionDescription = Tr::tr(" (%1)").arg(QCoreApplication::applicationVersion()); + return Tr::tr("%1 %2%3").arg(QGuiApplication::applicationDisplayName(), + appInfo().displayVersion, ideVersionDescription); } @@ -652,7 +839,7 @@ QString ICore::buildCompatibilityString() */ IContext *ICore::currentContextObject() { - return m_mainwindow->currentContextObject(); + return d->m_activeContext.isEmpty() ? nullptr : d->m_activeContext.first(); } /*! @@ -667,15 +854,6 @@ QWidget *ICore::currentContextWidget() return context ? context->widget() : nullptr; } -/*! - Returns the registered IContext instance for the specified \a widget, - if any. -*/ -IContext *ICore::contextObject(QWidget *widget) -{ - return m_mainwindow->contextObject(widget); -} - /*! Returns the main window of the application. @@ -685,7 +863,7 @@ IContext *ICore::contextObject(QWidget *widget) */ QMainWindow *ICore::mainWindow() { - return m_mainwindow; + return d->m_mainwindow; } /*! @@ -696,8 +874,10 @@ QWidget *ICore::dialogParent() QWidget *active = QApplication::activeModalWidget(); if (!active) active = QApplication::activeWindow(); - if (!active || (active && active->windowFlags().testAnyFlags(Qt::SplashScreen | Qt::Popup))) - active = m_mainwindow; + if (!active || active->windowFlags().testFlag(Qt::SplashScreen) + || active->windowFlags().testFlag(Qt::Popup)) { + active = d->m_mainwindow; + } return active; } @@ -706,7 +886,7 @@ QWidget *ICore::dialogParent() */ QStatusBar *ICore::statusBar() { - return m_mainwindow->statusBar(); + return d->m_modeStack->statusBar(); } /*! @@ -716,7 +896,7 @@ QStatusBar *ICore::statusBar() */ Utils::InfoBar *ICore::infoBar() { - return m_mainwindow->infoBar(); + return d->m_modeStack->infoBar(); } /*! @@ -730,14 +910,19 @@ void ICore::raiseWindow(QWidget *widget) QWidget *window = widget->window(); if (!window) return; - if (window == m_mainwindow) { - m_mainwindow->raiseWindow(); + if (window == d->m_mainwindow) { + d->m_mainwindow->raiseWindow(); } else { window->raise(); window->activateWindow(); } } +void ICore::raiseMainWindow() +{ + d->m_mainwindow->raiseWindow(); +} + /*! Removes the contexts specified by \a remove from the list of active additional contexts, and adds the contexts specified by \a add with \a @@ -755,7 +940,27 @@ void ICore::raiseWindow(QWidget *widget) void ICore::updateAdditionalContexts(const Context &remove, const Context &add, ContextPriority priority) { - m_mainwindow->updateAdditionalContexts(remove, add, priority); + for (const Id id : remove) { + if (!id.isValid()) + continue; + int index = d->m_lowPrioAdditionalContexts.indexOf(id); + if (index != -1) + d->m_lowPrioAdditionalContexts.removeAt(index); + index = d->m_highPrioAdditionalContexts.indexOf(id); + if (index != -1) + d->m_highPrioAdditionalContexts.removeAt(index); + } + + for (const Id id : add) { + if (!id.isValid()) + continue; + Context &cref = (priority == ICore::ContextPriority::High ? d->m_highPrioAdditionalContexts + : d->m_lowPrioAdditionalContexts); + if (!cref.contains(id)) + cref.prepend(id); + } + + d->updateContext(); } /*! @@ -765,7 +970,7 @@ void ICore::updateAdditionalContexts(const Context &remove, const Context &add, */ void ICore::addAdditionalContext(const Context &context, ContextPriority priority) { - m_mainwindow->updateAdditionalContexts(Context(), context, priority); + updateAdditionalContexts(Context(), context, priority); } /*! @@ -775,37 +980,7 @@ void ICore::addAdditionalContext(const Context &context, ContextPriority priorit */ void ICore::removeAdditionalContext(const Context &context) { - m_mainwindow->updateAdditionalContexts(context, Context(), ContextPriority::Low); -} - -/*! - Adds \a context to the list of registered IContext instances. - Whenever the IContext's \l{IContext::widget()}{widget} is in the application - focus widget's parent hierarchy, its \l{IContext::context()}{context} is - added to the list of active contexts. - - \sa removeContextObject() - \sa updateAdditionalContexts() - \sa currentContextObject() - \sa {The Action Manager and Commands} -*/ -void ICore::addContextObject(IContext *context) -{ - m_mainwindow->addContextObject(context); -} - -/*! - Unregisters a \a context object from the list of registered IContext - instances. IContext instances are automatically removed when they are - deleted. - - \sa addContextObject() - \sa updateAdditionalContexts() - \sa currentContextObject() -*/ -void ICore::removeContextObject(IContext *context) -{ - m_mainwindow->removeContextObject(context); + updateAdditionalContexts(context, Context(), ContextPriority::Low); } /*! @@ -822,22 +997,6 @@ void ICore::registerWindow(QWidget *window, const Context &context) new WindowSupport(window, context); // deletes itself when widget is destroyed } -void ICore::restartTrimmer() -{ - m_mainwindow->restartTrimmer(); -} - -/*! - Opens files using \a filePaths and \a flags like it would be - done if they were given to \QC on the command line, or - they were opened via \uicontrol File > \uicontrol Open. -*/ - -void ICore::openFiles(const FilePaths &filePaths, ICore::OpenFilesFlags flags) -{ - MainWindow::openFiles(filePaths, flags); -} - /*! Provides a hook for plugins to veto on closing the application. @@ -848,7 +1007,7 @@ void ICore::openFiles(const FilePaths &filePaths, ICore::OpenFilesFlags flags) */ void ICore::addPreCloseListener(const std::function<bool ()> &listener) { - m_mainwindow->addPreCloseListener(listener); + d->m_preCloseListeners.append(listener); } /*! @@ -859,9 +1018,8 @@ QString ICore::systemInformation() QString result = PluginManager::systemInformation() + '\n'; result += versionString() + '\n'; result += buildCompatibilityString() + '\n'; -#ifdef IDE_REVISION - result += QString("From revision %1\n").arg(QString::fromLatin1(Constants::IDE_REVISION_STR).left(10)); -#endif + if (!Utils::appInfo().revision.isEmpty()) + result += QString("From revision %1\n").arg(Utils::appInfo().revision.left(10)); #ifdef QTC_SHOW_BUILD_DATE result += QString("Built on %1 %2\n").arg(QLatin1String(__DATE__), QLatin1String(__TIME__)); #endif @@ -921,12 +1079,18 @@ void ICore::setupScreenShooter(const QString &name, QWidget *w, const QRect &rc) new ScreenShooter(w, name, rc); } +static void setRestart(bool restart) +{ + qApp->setProperty("restart", restart); +} + /*! Restarts \QC and restores the last session. */ void ICore::restart() { - m_mainwindow->restart(); + setRestart(true); + exit(); } /*! @@ -934,8 +1098,34 @@ void ICore::restart() */ void ICore::saveSettings(SaveSettingsReason reason) { - emit m_instance->saveSettingsRequested(reason); - m_mainwindow->saveSettings(); + emit m_core->saveSettingsRequested(reason); + + QtcSettings *settings = PluginManager::settings(); + settings->beginGroup(settingsGroup); + + if (!(d->m_overrideColor.isValid() && StyleHelper::baseColor() == d->m_overrideColor)) + settings->setValueWithDefault(colorKey, + StyleHelper::requestedBaseColor(), + QColor(StyleHelper::DEFAULT_BASE_COLOR)); + + if (Internal::globalMenuBar() && !Internal::globalMenuBar()->isNativeMenuBar()) + settings->setValue(menubarVisibleKey, Internal::globalMenuBar()->isVisible()); + + settings->endGroup(); + + DocumentManager::saveSettings(); + ActionManager::saveSettings(); + EditorManagerPrivate::saveSettings(); + d->m_leftNavigationWidget->saveSettings(settings); + d->m_rightNavigationWidget->saveSettings(settings); + + // TODO Remove some time after Qt Creator 11 + // Work around Qt Creator <= 10 writing the default terminal to the settings. + // TerminalCommand writes the terminal to the settings when changing it, which usually is + // enough. But because of the bug in Qt Creator <= 10 we want to clean up the settings + // even if the user never touched the terminal setting. + if (HostOsInfo::isMacHost()) + TerminalCommand::setTerminalEmulator(TerminalCommand::terminalEmulator()); ICore::settings(QSettings::SystemScope)->sync(); ICore::settings(QSettings::UserScope)->sync(); @@ -946,7 +1136,15 @@ void ICore::saveSettings(SaveSettingsReason reason) */ QStringList ICore::additionalAboutInformation() { - return m_mainwindow->additionalAboutInformation(); + return d->m_aboutInformation; +} + +/*! + \internal +*/ +void ICore::clearAboutInformation() +{ + d->m_aboutInformation.clear(); } /*! @@ -954,7 +1152,7 @@ QStringList ICore::additionalAboutInformation() */ void ICore::appendAboutInformation(const QString &line) { - m_mainwindow->appendAboutInformation(line); + d->m_aboutInformation.append(line); } void ICore::updateNewItemDialogState() @@ -976,4 +1174,1378 @@ void ICore::setNewDialogFactory(const std::function<NewDialog *(QWidget *)> &new m_newDialogFactory = newFactory; } +static bool hideToolsMenu() +{ + return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool(); +} + +enum { debugMainWindow = 0 }; + +namespace Internal { + +void ICorePrivate::init() +{ + m_mainwindow = new MainWindow; + + m_progressManager = new ProgressManagerPrivate; + m_jsExpander = JsExpander::createGlobalJsExpander(); + m_vcsManager = new VcsManager; + m_modeStack = new FancyTabWidget(m_mainwindow); + m_shortcutSettings = new ShortcutSettings; + m_toolSettings = new ToolSettings; + m_mimeTypeSettings = new MimeTypeSettings; + m_systemEditor = new SystemEditor; + m_toggleLeftSideBarButton = new QToolButton; + m_toggleRightSideBarButton = new QToolButton; + + (void) new DocumentManager(this); + + HistoryCompleter::setSettings(PluginManager::settings()); + + if (HostOsInfo::isLinuxHost()) + QApplication::setWindowIcon(Icons::QTCREATORLOGO_BIG.icon()); + QString baseName = QApplication::style()->objectName(); + // Sometimes we get the standard windows 95 style as a fallback + if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost() + && baseName == QLatin1String("windows")) { + baseName = QLatin1String("fusion"); + } + + // if the user has specified as base style in the theme settings, + // prefer that + const QStringList available = QStyleFactory::keys(); + const QStringList styles = Utils::creatorTheme()->preferredStyles(); + for (const QString &s : styles) { + if (available.contains(s, Qt::CaseInsensitive)) { + baseName = s; + break; + } + } + + QApplication::setStyle(new ManhattanStyle(baseName)); + + m_modeManager = new ModeManager(m_modeStack); + connect(m_modeStack, &FancyTabWidget::topAreaClicked, this, [](Qt::MouseButton, Qt::KeyboardModifiers modifiers) { + if (modifiers & Qt::ShiftModifier) { + QColor color = QColorDialog::getColor(StyleHelper::requestedBaseColor(), ICore::dialogParent()); + if (color.isValid()) + StyleHelper::setBaseColor(color); + } + }); + + m_mainwindow->setCentralWidget(d->m_modeStack); + + registerDefaultContainers(); + registerDefaultActions(); + + m_leftNavigationWidget = new NavigationWidget(m_toggleLeftSideBarAction, Side::Left); + m_rightNavigationWidget = new NavigationWidget(m_toggleRightSideBarAction, Side::Right); + m_rightPaneWidget = new RightPaneWidget(); + + m_messageManager = new MessageManager; + m_editorManager = new EditorManager(this); + m_externalToolManager = new ExternalToolManager(); + + m_progressManager->progressView()->setParent(m_mainwindow); + + connect(qApp, &QApplication::focusChanged, this, &ICorePrivate::updateFocusWidget); + + // Add small Toolbuttons for toggling the navigation widgets + StatusBarManager::addStatusBarWidget(m_toggleLeftSideBarButton, StatusBarManager::First); + int childsCount = m_modeStack->statusBar() + ->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly) + .count(); + m_modeStack->statusBar()->insertPermanentWidget(childsCount - 1, + m_toggleRightSideBarButton); // before QSizeGrip + + // setUnifiedTitleAndToolBarOnMac(true); + //if (HostOsInfo::isAnyUnixHost()) + //signal(SIGINT, handleSigInt); + + m_modeStack->statusBar()->setProperty("p_styled", true); + + /*auto dropSupport = new DropSupport(this, [](QDropEvent *event, DropSupport *) { + return event->source() == nullptr; // only accept drops from the "outside" (e.g. file manager) + }); + connect(dropSupport, &DropSupport::filesDropped, + this, &MainWindow::openDroppedFiles); +*/ + if (HostOsInfo::isLinuxHost()) { + m_trimTimer.setSingleShot(true); + m_trimTimer.setInterval(60000); + // glibc may not actually free memory in free(). +#ifdef Q_OS_LINUX + connect(&m_trimTimer, &QTimer::timeout, this, [] { malloc_trim(0); }); +#endif + } +} + + + +NavigationWidget *ICorePrivate::navigationWidget(Side side) const +{ + return side == Side::Left ? m_leftNavigationWidget : m_rightNavigationWidget; +} + +void ICorePrivate::setSidebarVisible(bool visible, Side side) +{ + if (NavigationWidgetPlaceHolder::current(side)) + navigationWidget(side)->setShown(visible); +} + +ICorePrivate::~ICorePrivate() +{ + // explicitly delete window support, because that calls methods from ICore that call methods + // from mainwindow, so mainwindow still needs to be alive + delete m_windowSupport; + m_windowSupport = nullptr; + + delete m_externalToolManager; + m_externalToolManager = nullptr; + delete m_messageManager; + m_messageManager = nullptr; + delete m_shortcutSettings; + m_shortcutSettings = nullptr; + delete m_toolSettings; + m_toolSettings = nullptr; + delete m_mimeTypeSettings; + m_mimeTypeSettings = nullptr; + delete m_systemEditor; + m_systemEditor = nullptr; + delete m_printer; + m_printer = nullptr; + delete m_vcsManager; + m_vcsManager = nullptr; + //we need to delete editormanager and statusbarmanager explicitly before the end of the destructor, + //because they might trigger stuff that tries to access data from editorwindow, like removeContextWidget + + // All modes are now gone + OutputPaneManager::destroy(); + + delete m_leftNavigationWidget; + delete m_rightNavigationWidget; + m_leftNavigationWidget = nullptr; + m_rightNavigationWidget = nullptr; + + delete m_editorManager; + m_editorManager = nullptr; + delete m_progressManager; + m_progressManager = nullptr; + + delete m_rightPaneWidget; + m_rightPaneWidget = nullptr; + + delete m_modeManager; + m_modeManager = nullptr; + + delete m_jsExpander; + m_jsExpander = nullptr; + + delete m_mainwindow; + m_mainwindow = nullptr; +} + +} // Internal + +void ICore::init() +{ + d->m_progressManager->init(); // needs the status bar manager + MessageManager::init(); + OutputPaneManager::create(); +} + +void ICore::extensionsInitialized() +{ + EditorManagerPrivate::extensionsInitialized(); + MimeTypeSettings::restoreSettings(); + d->m_windowSupport = new WindowSupport(d->m_mainwindow, Context("Core.MainWindow")); + d->m_windowSupport->setCloseActionEnabled(false); + OutputPaneManager::initialize(); + VcsManager::extensionsInitialized(); + d->m_leftNavigationWidget->setFactories(INavigationWidgetFactory::allNavigationFactories()); + d->m_rightNavigationWidget->setFactories(INavigationWidgetFactory::allNavigationFactories()); + + ModeManager::extensionsInitialized(); + + d->readSettings(); + d->updateContext(); + + emit m_core->coreAboutToOpen(); + // Delay restoreWindowState, since it is overridden by LayoutRequest event + QMetaObject::invokeMethod(d, &ICorePrivate::restoreWindowState, Qt::QueuedConnection); + QMetaObject::invokeMethod(m_core, &ICore::coreOpened, Qt::QueuedConnection); +} + +void ICore::aboutToShutdown() +{ + disconnect(qApp, &QApplication::focusChanged, d, &ICorePrivate::updateFocusWidget); + for (auto contextPair : d->m_contextWidgets) + disconnect(contextPair.second, &QObject::destroyed, d->m_mainwindow, nullptr); + d->m_activeContext.clear(); + d->m_mainwindow->hide(); +} + +void ICore::restartTrimmer() +{ + if (HostOsInfo::isLinuxHost() && !d->m_trimTimer.isActive()) + d->m_trimTimer.start(); +} + +namespace Internal { + +void MainWindow::closeEvent(QCloseEvent *event) +{ + const auto cancelClose = [event] { + event->ignore(); + setRestart(false); + }; + + // work around QTBUG-43344 + static bool alreadyClosed = false; + if (alreadyClosed) { + event->accept(); + return; + } + + if (systemSettings().askBeforeExit() + && (QMessageBox::question(this, + Tr::tr("Exit %1?").arg(QGuiApplication::applicationDisplayName()), + Tr::tr("Exit %1?").arg(QGuiApplication::applicationDisplayName()), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) + == QMessageBox::No)) { + event->ignore(); + return; + } + + ICore::saveSettings(ICore::MainWindowClosing); + + // Save opened files + if (!DocumentManager::saveAllModifiedDocuments()) { + cancelClose(); + return; + } + + const QList<std::function<bool()>> listeners = d->m_preCloseListeners; + for (const std::function<bool()> &listener : listeners) { + if (!listener()) { + cancelClose(); + return; + } + } + + emit m_core->coreAboutToClose(); + + d->saveWindowSettings(); + + d->m_leftNavigationWidget->closeSubWidgets(); + d->m_rightNavigationWidget->closeSubWidgets(); + + event->accept(); + alreadyClosed = true; +} + +void MainWindow::keyPressEvent(QKeyEvent *event) +{ + ICore::restartTrimmer(); + AppMainWindow::keyPressEvent(event); +} + +void MainWindow::mousePressEvent(QMouseEvent *event) +{ + ICore::restartTrimmer(); + AppMainWindow::mousePressEvent(event); +} + +void ICorePrivate::openDroppedFiles(const QList<DropSupport::FileSpec> &files) +{ + m_mainwindow->raiseWindow(); + const FilePaths filePaths = Utils::transform(files, &DropSupport::FileSpec::filePath); + ICore::openFiles(filePaths, ICore::SwitchMode); +} + +void ICorePrivate::registerDefaultContainers() +{ + ActionContainer *menubar = ActionManager::createMenuBar(Constants::MENU_BAR); + + if (!HostOsInfo::isMacHost()) // System menu bar on Mac + m_mainwindow->setMenuBar(menubar->menuBar()); + menubar->appendGroup(Constants::G_FILE); + menubar->appendGroup(Constants::G_EDIT); + menubar->appendGroup(Constants::G_VIEW); + menubar->appendGroup(Constants::G_TOOLS); + menubar->appendGroup(Constants::G_WINDOW); + menubar->appendGroup(Constants::G_HELP); + + // File Menu + ActionContainer *filemenu = ActionManager::createMenu(Constants::M_FILE); + menubar->addMenu(filemenu, Constants::G_FILE); + filemenu->menu()->setTitle(Tr::tr("&File")); + filemenu->appendGroup(Constants::G_FILE_NEW); + filemenu->appendGroup(Constants::G_FILE_OPEN); + filemenu->appendGroup(Constants::G_FILE_SESSION); + filemenu->appendGroup(Constants::G_FILE_PROJECT); + filemenu->appendGroup(Constants::G_FILE_SAVE); + filemenu->appendGroup(Constants::G_FILE_EXPORT); + filemenu->appendGroup(Constants::G_FILE_CLOSE); + filemenu->appendGroup(Constants::G_FILE_PRINT); + filemenu->appendGroup(Constants::G_FILE_OTHER); + connect(filemenu->menu(), &QMenu::aboutToShow, this, &ICorePrivate::aboutToShowRecentFiles); + + + // Edit Menu + ActionContainer *medit = ActionManager::createMenu(Constants::M_EDIT); + menubar->addMenu(medit, Constants::G_EDIT); + medit->menu()->setTitle(Tr::tr("&Edit")); + medit->appendGroup(Constants::G_EDIT_UNDOREDO); + medit->appendGroup(Constants::G_EDIT_COPYPASTE); + medit->appendGroup(Constants::G_EDIT_SELECTALL); + medit->appendGroup(Constants::G_EDIT_ADVANCED); + medit->appendGroup(Constants::G_EDIT_FIND); + medit->appendGroup(Constants::G_EDIT_OTHER); + + ActionContainer *mview = ActionManager::createMenu(Constants::M_VIEW); + menubar->addMenu(mview, Constants::G_VIEW); + mview->menu()->setTitle(Tr::tr("&View")); + mview->appendGroup(Constants::G_VIEW_VIEWS); + mview->appendGroup(Constants::G_VIEW_PANES); + + // Tools Menu + ActionContainer *ac = ActionManager::createMenu(Constants::M_TOOLS); + ac->setParent(this); + if (!hideToolsMenu()) + menubar->addMenu(ac, Constants::G_TOOLS); + + ac->menu()->setTitle(Tr::tr("&Tools")); + + // Window Menu + ActionContainer *mwindow = ActionManager::createMenu(Constants::M_WINDOW); + menubar->addMenu(mwindow, Constants::G_WINDOW); + mwindow->menu()->setTitle(Tr::tr("&Window")); + mwindow->appendGroup(Constants::G_WINDOW_SIZE); + mwindow->appendGroup(Constants::G_WINDOW_SPLIT); + mwindow->appendGroup(Constants::G_WINDOW_NAVIGATE); + mwindow->appendGroup(Constants::G_WINDOW_LIST); + mwindow->appendGroup(Constants::G_WINDOW_OTHER); + + // Help Menu + ac = ActionManager::createMenu(Constants::M_HELP); + menubar->addMenu(ac, Constants::G_HELP); + ac->menu()->setTitle(Tr::tr("&Help")); + Theme::setHelpMenu(ac->menu()); + ac->appendGroup(Constants::G_HELP_HELP); + ac->appendGroup(Constants::G_HELP_SUPPORT); + ac->appendGroup(Constants::G_HELP_ABOUT); + ac->appendGroup(Constants::G_HELP_UPDATES); + + // macOS touch bar + ac = ActionManager::createTouchBar(Constants::TOUCH_BAR, + QIcon(), + "Main TouchBar" /*never visible*/); + ac->appendGroup(Constants::G_TOUCHBAR_HELP); + ac->appendGroup(Constants::G_TOUCHBAR_NAVIGATION); + ac->appendGroup(Constants::G_TOUCHBAR_EDITOR); + ac->appendGroup(Constants::G_TOUCHBAR_OTHER); + ac->touchBar()->setApplicationTouchBar(); +} + +void ICorePrivate::registerDefaultActions() +{ + ActionContainer *mfile = ActionManager::actionContainer(Constants::M_FILE); + ActionContainer *medit = ActionManager::actionContainer(Constants::M_EDIT); + ActionContainer *mview = ActionManager::actionContainer(Constants::M_VIEW); + ActionContainer *mtools = ActionManager::actionContainer(Constants::M_TOOLS); + ActionContainer *mwindow = ActionManager::actionContainer(Constants::M_WINDOW); + ActionContainer *mhelp = ActionManager::actionContainer(Constants::M_HELP); + + // File menu separators + mfile->addSeparator(Constants::G_FILE_SAVE); + mfile->addSeparator(Constants::G_FILE_EXPORT); + mfile->addSeparator(Constants::G_FILE_PRINT); + mfile->addSeparator(Constants::G_FILE_CLOSE); + mfile->addSeparator(Constants::G_FILE_OTHER); + // Edit menu separators + medit->addSeparator(Constants::G_EDIT_COPYPASTE); + medit->addSeparator(Constants::G_EDIT_SELECTALL); + medit->addSeparator(Constants::G_EDIT_FIND); + medit->addSeparator(Constants::G_EDIT_ADVANCED); + + // Return to editor shortcut: Note this requires Qt to fix up + // handling of shortcut overrides in menus, item views, combos.... + m_focusToEditor = new QAction(Tr::tr("Return to Editor"), this); + Command *cmd = ActionManager::registerAction(m_focusToEditor, Constants::S_RETURNTOEDITOR); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Escape)); + connect(m_focusToEditor, &QAction::triggered, this, &ICorePrivate::setFocusToEditor); + + // New File Action + QIcon icon = Icon::fromTheme("document-new"); + + m_newAction = new QAction(icon, Tr::tr("&New Project..."), this); + cmd = ActionManager::registerAction(m_newAction, Constants::NEW); + cmd->setDefaultKeySequence(QKeySequence("Ctrl+Shift+N")); + mfile->addAction(cmd, Constants::G_FILE_NEW); + connect(m_newAction, &QAction::triggered, this, [] { + if (!ICore::isNewItemDialogRunning()) { + ICore::showNewItemDialog( + Tr::tr("New Project", "Title of dialog"), + Utils::filtered(Core::IWizardFactory::allWizardFactories(), + Utils::equal(&Core::IWizardFactory::kind, + Core::IWizardFactory::ProjectWizard)), + FilePath()); + } else { + ICore::raiseWindow(ICore::newItemDialog()); + } + }); + + auto action = new QAction(icon, Tr::tr("New File..."), this); + cmd = ActionManager::registerAction(action, Constants::NEW_FILE); + cmd->setDefaultKeySequence(QKeySequence::New); + mfile->addAction(cmd, Constants::G_FILE_NEW); + connect(action, &QAction::triggered, this, [] { + if (!ICore::isNewItemDialogRunning()) { + ICore::showNewItemDialog(Tr::tr("New File", "Title of dialog"), + Utils::filtered(Core::IWizardFactory::allWizardFactories(), + Utils::equal(&Core::IWizardFactory::kind, + Core::IWizardFactory::FileWizard)), + FilePath()); + } else { + ICore::raiseWindow(ICore::newItemDialog()); + } + }); + + // Open Action + icon = Icon::fromTheme("document-open"); + m_openAction = new QAction(icon, Tr::tr("&Open File or Project..."), this); + cmd = ActionManager::registerAction(m_openAction, Constants::OPEN); + cmd->setDefaultKeySequence(QKeySequence::Open); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openAction, &QAction::triggered, this, &ICorePrivate::openFile); + + // Open With Action + m_openWithAction = new QAction(Tr::tr("Open File &With..."), this); + cmd = ActionManager::registerAction(m_openWithAction, Constants::OPEN_WITH); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openWithAction, &QAction::triggered, m_core, &ICore::openFileWith); + + if (FSEngine::isAvailable()) { + // Open From Device Action + m_openFromDeviceAction = new QAction(Tr::tr("Open From Device..."), this); + cmd = ActionManager::registerAction(m_openFromDeviceAction, Constants::OPEN_FROM_DEVICE); + mfile->addAction(cmd, Constants::G_FILE_OPEN); + connect(m_openFromDeviceAction, &QAction::triggered, this, &ICorePrivate::openFileFromDevice); + } + + // File->Recent Files Menu + ActionContainer *ac = ActionManager::createMenu(Constants::M_FILE_RECENTFILES); + mfile->addMenu(ac, Constants::G_FILE_OPEN); + ac->menu()->setTitle(Tr::tr("Recent &Files")); + ac->setOnAllDisabledBehavior(ActionContainer::Show); + + // Save Action + icon = Icon::fromTheme("document-save"); + QAction *tmpaction = new QAction(icon, Tr::tr("&Save"), this); + tmpaction->setEnabled(false); + cmd = ActionManager::registerAction(tmpaction, Constants::SAVE); + cmd->setDefaultKeySequence(QKeySequence::Save); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(Tr::tr("Save")); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + + // Save As Action + icon = Icon::fromTheme("document-save-as"); + tmpaction = new QAction(icon, Tr::tr("Save &As..."), this); + tmpaction->setEnabled(false); + cmd = ActionManager::registerAction(tmpaction, Constants::SAVEAS); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Shift+S") : QString())); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(Tr::tr("Save As...")); + mfile->addAction(cmd, Constants::G_FILE_SAVE); + + // SaveAll Action + DocumentManager::registerSaveAllAction(); + + // Print Action + icon = Icon::fromTheme("document-print"); + tmpaction = new QAction(icon, Tr::tr("&Print..."), this); + tmpaction->setEnabled(false); + cmd = ActionManager::registerAction(tmpaction, Constants::PRINT); + cmd->setDefaultKeySequence(QKeySequence::Print); + mfile->addAction(cmd, Constants::G_FILE_PRINT); + + // Exit Action + icon = Icon::fromTheme("application-exit"); + m_exitAction = new QAction(icon, Tr::tr("E&xit"), this); + m_exitAction->setMenuRole(QAction::QuitRole); + cmd = ActionManager::registerAction(m_exitAction, Constants::EXIT); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Q"))); + mfile->addAction(cmd, Constants::G_FILE_OTHER); + connect(m_exitAction, &QAction::triggered, m_core, &ICore::exit); + + // Undo Action + icon = Icon::fromTheme("edit-undo"); + tmpaction = new QAction(icon, Tr::tr("&Undo"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::UNDO); + cmd->setDefaultKeySequence(QKeySequence::Undo); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(Tr::tr("Undo")); + medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); + tmpaction->setEnabled(false); + + // Redo Action + icon = Icon::fromTheme("edit-redo"); + tmpaction = new QAction(icon, Tr::tr("&Redo"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::REDO); + cmd->setDefaultKeySequence(QKeySequence::Redo); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(Tr::tr("Redo")); + medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); + tmpaction->setEnabled(false); + + // Cut Action + icon = Icon::fromTheme("edit-cut"); + tmpaction = new QAction(icon, Tr::tr("Cu&t"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::CUT); + cmd->setDefaultKeySequence(QKeySequence::Cut); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + tmpaction->setEnabled(false); + + // Copy Action + icon = Icon::fromTheme("edit-copy"); + tmpaction = new QAction(icon, Tr::tr("&Copy"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::COPY); + cmd->setDefaultKeySequence(QKeySequence::Copy); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + tmpaction->setEnabled(false); + + // Paste Action + icon = Icon::fromTheme("edit-paste"); + tmpaction = new QAction(icon, Tr::tr("&Paste"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::PASTE); + cmd->setDefaultKeySequence(QKeySequence::Paste); + medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); + tmpaction->setEnabled(false); + + // Select All + icon = Icon::fromTheme("edit-select-all"); + tmpaction = new QAction(icon, Tr::tr("Select &All"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::SELECTALL); + cmd->setDefaultKeySequence(QKeySequence::SelectAll); + medit->addAction(cmd, Constants::G_EDIT_SELECTALL); + tmpaction->setEnabled(false); + + // Goto Action + icon = Icon::fromTheme("go-jump"); + tmpaction = new QAction(icon, Tr::tr("&Go to Line..."), this); + cmd = ActionManager::registerAction(tmpaction, Constants::GOTO); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+L"))); + medit->addAction(cmd, Constants::G_EDIT_OTHER); + tmpaction->setEnabled(false); + + // Zoom In Action + icon = Icon::fromTheme("zoom-in"); + tmpaction = new QAction(icon, Tr::tr("Zoom In"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_IN); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl++"))); + tmpaction->setEnabled(false); + + // Zoom Out Action + icon = Icon::fromTheme("zoom-out"); + tmpaction = new QAction(icon, Tr::tr("Zoom Out"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_OUT); + if (useMacShortcuts) + cmd->setDefaultKeySequences({QKeySequence(Tr::tr("Ctrl+-")), QKeySequence(Tr::tr("Ctrl+Shift+-"))}); + else + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+-"))); + tmpaction->setEnabled(false); + + // Zoom Reset Action + icon = Icon::fromTheme("zoom-original"); + tmpaction = new QAction(icon, Tr::tr("Original Size"), this); + cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_RESET); + cmd->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+0") : Tr::tr("Ctrl+0"))); + tmpaction->setEnabled(false); + + // Debug Qt Creator menu + mtools->appendGroup(Constants::G_TOOLS_DEBUG); + ActionContainer *mtoolsdebug = ActionManager::createMenu(Constants::M_TOOLS_DEBUG); + mtoolsdebug->menu()->setTitle(Tr::tr("Debug %1").arg(QGuiApplication::applicationDisplayName())); + mtools->addMenu(mtoolsdebug, Constants::G_TOOLS_DEBUG); + + m_loggerAction = new QAction(Tr::tr("Show Logs..."), this); + cmd = ActionManager::registerAction(m_loggerAction, Constants::LOGGER); + mtoolsdebug->addAction(cmd); + connect(m_loggerAction, &QAction::triggered, this, [] { LoggingViewer::showLoggingView(); }); + + // Options Action + medit->appendGroup(Constants::G_EDIT_PREFERENCES); + medit->addSeparator(Constants::G_EDIT_PREFERENCES); + + m_optionsAction = new QAction(Tr::tr("Pr&eferences..."), this); + m_optionsAction->setMenuRole(QAction::PreferencesRole); + cmd = ActionManager::registerAction(m_optionsAction, Constants::OPTIONS); + cmd->setDefaultKeySequence(QKeySequence::Preferences); + medit->addAction(cmd, Constants::G_EDIT_PREFERENCES); + connect(m_optionsAction, &QAction::triggered, this, [] { ICore::showOptionsDialog(Id()); }); + + mwindow->addSeparator(Constants::G_WINDOW_LIST); + + if (useMacShortcuts) { + // Minimize Action + QAction *minimizeAction = new QAction(Tr::tr("Minimize"), this); + minimizeAction->setEnabled(false); // actual implementation in WindowSupport + cmd = ActionManager::registerAction(minimizeAction, Constants::MINIMIZE_WINDOW); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+M"))); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + + // Zoom Action + QAction *zoomAction = new QAction(Tr::tr("Zoom"), this); + zoomAction->setEnabled(false); // actual implementation in WindowSupport + cmd = ActionManager::registerAction(zoomAction, Constants::ZOOM_WINDOW); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + } + + // Full Screen Action + QAction *toggleFullScreenAction = new QAction(Tr::tr("Full Screen"), this); + toggleFullScreenAction->setCheckable(!HostOsInfo::isMacHost()); + toggleFullScreenAction->setEnabled(false); // actual implementation in WindowSupport + cmd = ActionManager::registerAction(toggleFullScreenAction, Constants::TOGGLE_FULLSCREEN); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+F") : Tr::tr("Ctrl+Shift+F11"))); + if (HostOsInfo::isMacHost()) + cmd->setAttribute(Command::CA_UpdateText); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + + if (useMacShortcuts) { + mwindow->addSeparator(Constants::G_WINDOW_SIZE); + + QAction *closeAction = new QAction(Tr::tr("Close Window"), this); + closeAction->setEnabled(false); + cmd = ActionManager::registerAction(closeAction, Constants::CLOSE_WINDOW); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Meta+W"))); + mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); + + mwindow->addSeparator(Constants::G_WINDOW_SIZE); + } + + // Show Left Sidebar Action + m_toggleLeftSideBarAction = new QAction(Utils::Icons::TOGGLE_LEFT_SIDEBAR.icon(), + Tr::tr(Constants::TR_SHOW_LEFT_SIDEBAR), + this); + m_toggleLeftSideBarAction->setCheckable(true); + cmd = ActionManager::registerAction(m_toggleLeftSideBarAction, Constants::TOGGLE_LEFT_SIDEBAR); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+0") : Tr::tr("Alt+0"))); + connect(m_toggleLeftSideBarAction, &QAction::triggered, + this, [this](bool visible) { setSidebarVisible(visible, Side::Left); }); + ProxyAction *toggleLeftSideBarProxyAction = + ProxyAction::proxyActionWithIcon(cmd->action(), Utils::Icons::TOGGLE_LEFT_SIDEBAR_TOOLBAR.icon()); + m_toggleLeftSideBarButton->setDefaultAction(toggleLeftSideBarProxyAction); + mview->addAction(cmd, Constants::G_VIEW_VIEWS); + m_toggleLeftSideBarAction->setEnabled(false); + + // Show Right Sidebar Action + m_toggleRightSideBarAction = new QAction(Utils::Icons::TOGGLE_RIGHT_SIDEBAR.icon(), + Tr::tr(Constants::TR_SHOW_RIGHT_SIDEBAR), + this); + m_toggleRightSideBarAction->setCheckable(true); + cmd = ActionManager::registerAction(m_toggleRightSideBarAction, Constants::TOGGLE_RIGHT_SIDEBAR); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Shift+0") : Tr::tr("Alt+Shift+0"))); + connect(m_toggleRightSideBarAction, &QAction::triggered, + this, [this](bool visible) { setSidebarVisible(visible, Side::Right); }); + ProxyAction *toggleRightSideBarProxyAction = + ProxyAction::proxyActionWithIcon(cmd->action(), Utils::Icons::TOGGLE_RIGHT_SIDEBAR_TOOLBAR.icon()); + m_toggleRightSideBarButton->setDefaultAction(toggleRightSideBarProxyAction); + mview->addAction(cmd, Constants::G_VIEW_VIEWS); + m_toggleRightSideBarButton->setEnabled(false); + + // Show Menubar Action + if (globalMenuBar() && !globalMenuBar()->isNativeMenuBar()) { + m_toggleMenubarAction = new QAction(Tr::tr("Show Menu Bar"), this); + m_toggleMenubarAction->setCheckable(true); + cmd = ActionManager::registerAction(m_toggleMenubarAction, Constants::TOGGLE_MENUBAR); + cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Alt+M"))); + connect(m_toggleMenubarAction, &QAction::toggled, this, [cmd](bool visible) { + if (!visible) { + CheckableMessageBox::information(Core::ICore::dialogParent(), + Tr::tr("Hide Menu Bar"), + Tr::tr("This will hide the menu bar completely. " + "You can show it again by typing %1.") + .arg(cmd->keySequence().toString( + QKeySequence::NativeText)), + Key("ToogleMenuBarHint")); + } + globalMenuBar()->setVisible(visible); + }); + mview->addAction(cmd, Constants::G_VIEW_VIEWS); + } + + registerModeSelectorStyleActions(); + + // Window->Views + ActionContainer *mviews = ActionManager::createMenu(Constants::M_VIEW_VIEWS); + mview->addMenu(mviews, Constants::G_VIEW_VIEWS); + mviews->menu()->setTitle(Tr::tr("&Views")); + + // "Help" separators + mhelp->addSeparator(Constants::G_HELP_SUPPORT); + if (!HostOsInfo::isMacHost()) + mhelp->addSeparator(Constants::G_HELP_ABOUT); + + // About IDE Action + icon = Icon::fromTheme("help-about"); + if (HostOsInfo::isMacHost()) + tmpaction = new QAction(icon, + Tr::tr("About &%1").arg(QGuiApplication::applicationDisplayName()), + this); // it's convention not to add dots to the about menu + else + tmpaction + = new QAction(icon, + Tr::tr("About &%1...").arg(QGuiApplication::applicationDisplayName()), + this); + tmpaction->setMenuRole(QAction::AboutRole); + cmd = ActionManager::registerAction(tmpaction, Constants::ABOUT_QTCREATOR); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); + connect(tmpaction, &QAction::triggered, this, &ICorePrivate::aboutQtCreator); + + //About Plugins Action + tmpaction = new QAction(Tr::tr("About &Plugins..."), this); + tmpaction->setMenuRole(QAction::ApplicationSpecificRole); + cmd = ActionManager::registerAction(tmpaction, Constants::ABOUT_PLUGINS); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); + connect(tmpaction, &QAction::triggered, this, &ICorePrivate::aboutPlugins); + // About Qt Action + // tmpaction = new QAction(Tr::tr("About &Qt..."), this); + // cmd = ActionManager::registerAction(tmpaction, Constants:: ABOUT_QT); + // mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + // tmpaction->setEnabled(true); + // connect(tmpaction, &QAction::triggered, qApp, &QApplication::aboutQt); + + // Change Log Action + tmpaction = new QAction(Tr::tr("Change Log..."), this); + tmpaction->setMenuRole(QAction::ApplicationSpecificRole); + cmd = ActionManager::registerAction(tmpaction, Constants::CHANGE_LOG); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); + connect(tmpaction, &QAction::triggered, this, &ICorePrivate::changeLog); + + // Contact + tmpaction = new QAction(Tr::tr("Contact..."), this); + cmd = ActionManager::registerAction(tmpaction, "QtCreator.Contact"); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + tmpaction->setEnabled(true); + connect(tmpaction, &QAction::triggered, this, &ICorePrivate::contact); + + // About sep + if (!HostOsInfo::isMacHost()) { // doesn't have the "About" actions in the Help menu + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + cmd = ActionManager::registerAction(tmpaction, "QtCreator.Help.Sep.About"); + mhelp->addAction(cmd, Constants::G_HELP_ABOUT); + } +} + +void ICorePrivate::registerModeSelectorStyleActions() +{ + ActionContainer *mview = ActionManager::actionContainer(Constants::M_VIEW); + + // Cycle Mode Selector Styles + m_cycleModeSelectorStyleAction = new QAction(Tr::tr("Cycle Mode Selector Styles"), this); + ActionManager::registerAction(m_cycleModeSelectorStyleAction, Constants::CYCLE_MODE_SELECTOR_STYLE); + connect(m_cycleModeSelectorStyleAction, &QAction::triggered, this, [this] { + ModeManager::cycleModeStyle(); + updateModeSelectorStyleMenu(); + }); + + // Mode Selector Styles + ActionContainer *mmodeLayouts = ActionManager::createMenu(Constants::M_VIEW_MODESTYLES); + mview->addMenu(mmodeLayouts, Constants::G_VIEW_VIEWS); + QMenu *styleMenu = mmodeLayouts->menu(); + styleMenu->setTitle(Tr::tr("Mode Selector Style")); + auto *stylesGroup = new QActionGroup(styleMenu); + stylesGroup->setExclusive(true); + + m_setModeSelectorStyleIconsAndTextAction = stylesGroup->addAction(Tr::tr("Icons and Text")); + connect(m_setModeSelectorStyleIconsAndTextAction, &QAction::triggered, + [] { ModeManager::setModeStyle(ModeManager::Style::IconsAndText); }); + m_setModeSelectorStyleIconsAndTextAction->setCheckable(true); + m_setModeSelectorStyleIconsOnlyAction = stylesGroup->addAction(Tr::tr("Icons Only")); + connect(m_setModeSelectorStyleIconsOnlyAction, &QAction::triggered, + [] { ModeManager::setModeStyle(ModeManager::Style::IconsOnly); }); + m_setModeSelectorStyleIconsOnlyAction->setCheckable(true); + m_setModeSelectorStyleHiddenAction = stylesGroup->addAction(Tr::tr("Hidden")); + connect(m_setModeSelectorStyleHiddenAction, &QAction::triggered, + [] { ModeManager::setModeStyle(ModeManager::Style::Hidden); }); + m_setModeSelectorStyleHiddenAction->setCheckable(true); + + styleMenu->addActions(stylesGroup->actions()); +} + +void ICorePrivate::openFile() +{ + ICore::openFiles(EditorManager::getOpenFilePaths(), ICore::SwitchMode); +} + +static IDocumentFactory *findDocumentFactory(const QList<IDocumentFactory*> &fileFactories, + const FilePath &filePath) +{ + const QString typeName = Utils::mimeTypeForFile(filePath, MimeMatchMode::MatchDefaultAndRemote) + .name(); + return Utils::findOrDefault(fileFactories, [typeName](IDocumentFactory *f) { + return f->mimeTypes().contains(typeName); + }); +} + +} // Internal + +/*! + * \internal + * Either opens \a filePaths with editors or loads a project. + * + * \a flags can be used to stop on first failure, indicate that a file name + * might include line numbers and/or switch mode to edit mode. + * + * \a workingDirectory is used when files are opened by a remote client, since + * the file names are relative to the client working directory. + * + * Returns the first opened document. Required to support the \c -block flag + * for client mode. + * + * \sa IPlugin::remoteArguments() + */ + +IDocument *ICore::openFiles(const FilePaths &filePaths, + ICore::OpenFilesFlags flags, + const FilePath &workingDirectory) +{ + const QList<IDocumentFactory*> documentFactories = IDocumentFactory::allDocumentFactories(); + IDocument *res = nullptr; + + const FilePath workingDirBase = + workingDirectory.isEmpty() ? FilePath::currentWorkingPath() : workingDirectory; + for (const FilePath &filePath : filePaths) { + const FilePath absoluteFilePath = workingDirBase.resolvePath(filePath); + if (IDocumentFactory *documentFactory = findDocumentFactory(documentFactories, filePath)) { + IDocument *document = documentFactory->open(absoluteFilePath); + if (!document) { + if (flags & ICore::StopOnLoadFail) + return res; + } else { + if (!res) + res = document; + if (flags & ICore::SwitchMode) + ModeManager::activateMode(Id(Constants::MODE_EDIT)); + } + } else if (flags & (ICore::SwitchSplitIfAlreadyVisible | ICore::CanContainLineAndColumnNumbers) + || !res) { + QFlags<EditorManager::OpenEditorFlag> emFlags; + if (flags & ICore::SwitchSplitIfAlreadyVisible) + emFlags |= EditorManager::SwitchSplitIfAlreadyVisible; + IEditor *editor = nullptr; + if (flags & ICore::CanContainLineAndColumnNumbers) { + const Link &link = Link::fromString(absoluteFilePath.toString(), true); + editor = EditorManager::openEditorAt(link, {}, emFlags); + } else { + editor = EditorManager::openEditor(absoluteFilePath, {}, emFlags); + } + if (!editor) { + if (flags & ICore::StopOnLoadFail) + return res; + } else if (!res) { + res = editor->document(); + } + } else { + auto factory = IEditorFactory::preferredEditorFactories(absoluteFilePath).value(0); + DocumentModelPrivate::addSuspendedDocument(absoluteFilePath, {}, + factory ? factory->id() : Id()); + } + } + return res; +} + +namespace Internal { + +void ICorePrivate::setFocusToEditor() +{ + EditorManagerPrivate::doEscapeKeyFocusMoveMagic(); +} + +void ICorePrivate::openFileFromDevice() +{ + ICore::openFiles(EditorManager::getOpenFilePaths(QFileDialog::DontUseNativeDialog), ICore::SwitchMode); +} + +static void acceptModalDialogs() +{ + const QWidgetList topLevels = QApplication::topLevelWidgets(); + QList<QDialog *> dialogsToClose; + for (QWidget *topLevel : topLevels) { + if (auto dialog = qobject_cast<QDialog *>(topLevel)) { + if (dialog->isModal()) + dialogsToClose.append(dialog); + } + } + for (QDialog *dialog : dialogsToClose) + dialog->accept(); +} + +} // Internal + +void ICore::exit() +{ + // this function is most likely called from a user action + // that is from an event handler of an object + // since on close we are going to delete everything + // so to prevent the deleting of that object we + // just append it + QMetaObject::invokeMethod( + d->m_mainwindow, + [] { + // Modal dialogs block the close event. So close them, in case this was triggered from + // a RestartDialog in the settings dialog. + acceptModalDialogs(); + d->m_mainwindow->close(); + }, + Qt::QueuedConnection); +} + +void ICore::openFileWith() +{ + const FilePaths filePaths = EditorManager::getOpenFilePaths(); + for (const FilePath &filePath : filePaths) { + bool isExternal; + const Id editorId = EditorManagerPrivate::getOpenWithEditorId(filePath, &isExternal); + if (!editorId.isValid()) + continue; + if (isExternal) + EditorManager::openExternalEditor(filePath, editorId); + else + EditorManagerPrivate::openEditorWith(filePath, editorId); + } +} + +/*! + Returns the registered IContext instance for the specified \a widget, + if any. +*/ +IContext *ICore::contextObject(QWidget *widget) +{ + const auto it = d->m_contextWidgets.find(widget); + return it == d->m_contextWidgets.end() ? nullptr : it->second; +} + +/*! + Adds \a context to the list of registered IContext instances. + Whenever the IContext's \l{IContext::widget()}{widget} is in the application + focus widget's parent hierarchy, its \l{IContext::context()}{context} is + added to the list of active contexts. + + \sa removeContextObject() + \sa updateAdditionalContexts() + \sa currentContextObject() + \sa {The Action Manager and Commands} +*/ + +void ICore::addContextObject(IContext *context) +{ + if (!context) + return; + QWidget *widget = context->widget(); + if (d->m_contextWidgets.find(widget) != d->m_contextWidgets.end()) + return; + + d->m_contextWidgets.insert({widget, context}); + connect(context, &QObject::destroyed, m_core, [context] { removeContextObject(context); }); +} + +/*! + Unregisters a \a context object from the list of registered IContext + instances. IContext instances are automatically removed when they are + deleted. + + \sa addContextObject() + \sa updateAdditionalContexts() + \sa currentContextObject() +*/ + +void ICore::removeContextObject(IContext *context) +{ + if (!context) + return; + + disconnect(context, &QObject::destroyed, m_core, nullptr); + + const auto it = std::find_if(d->m_contextWidgets.cbegin(), + d->m_contextWidgets.cend(), + [context](const std::pair<QWidget *, IContext *> &v) { + return v.second == context; + }); + if (it == d->m_contextWidgets.cend()) + return; + + d->m_contextWidgets.erase(it); + if (d->m_activeContext.removeAll(context) > 0) + d->updateContextObject(d->m_activeContext); +} + +namespace Internal { + +void ICorePrivate::updateFocusWidget(QWidget *old, QWidget *now) +{ + Q_UNUSED(old) + + // Prevent changing the context object just because the menu or a menu item is activated + if (qobject_cast<QMenuBar*>(now) || qobject_cast<QMenu*>(now)) + return; + + QList<IContext *> newContext; + if (QWidget *p = QApplication::focusWidget()) { + IContext *context = nullptr; + while (p) { + context = ICore::contextObject(p); + if (context) + newContext.append(context); + p = p->parentWidget(); + } + } + + // ignore toplevels that define no context, like popups without parent + if (!newContext.isEmpty() || QApplication::focusWidget() == m_mainwindow->focusWidget()) + updateContextObject(newContext); +} + +void ICorePrivate::updateContextObject(const QList<IContext *> &context) +{ + emit m_core->contextAboutToChange(context); + m_activeContext = context; + updateContext(); + if (debugMainWindow) { + qDebug() << "new context objects =" << context; + for (const IContext *c : context) + qDebug() << (c ? c->widget() : nullptr) << (c ? c->widget()->metaObject()->className() : nullptr); + } +} + +void ICorePrivate::readSettings() +{ + QtcSettings *settings = PluginManager::settings(); + settings->beginGroup(settingsGroup); + + if (m_overrideColor.isValid()) { + StyleHelper::setBaseColor(m_overrideColor); + // Get adapted base color. + m_overrideColor = StyleHelper::baseColor(); + } else { + StyleHelper::setBaseColor(settings->value(colorKey, + QColor(StyleHelper::DEFAULT_BASE_COLOR)).value<QColor>()); + } + + { + ModeManager::Style modeStyle = + ModeManager::Style(settings->value(modeSelectorLayoutKey, int(ModeManager::Style::IconsAndText)).toInt()); + + // Migrate legacy setting from Qt Creator 4.6 and earlier + static const char modeSelectorVisibleKey[] = "ModeSelectorVisible"; + if (!settings->contains(modeSelectorLayoutKey) && settings->contains(modeSelectorVisibleKey)) { + bool visible = settings->value(modeSelectorVisibleKey, true).toBool(); + modeStyle = visible ? ModeManager::Style::IconsAndText : ModeManager::Style::Hidden; + } + + ModeManager::setModeStyle(modeStyle); + updateModeSelectorStyleMenu(); + } + + if (globalMenuBar() && !globalMenuBar()->isNativeMenuBar()) { + const bool isVisible = settings->value(menubarVisibleKey, true).toBool(); + + globalMenuBar()->setVisible(isVisible); + if (m_toggleMenubarAction) + m_toggleMenubarAction->setChecked(isVisible); + } + + settings->endGroup(); + + EditorManagerPrivate::readSettings(); + m_leftNavigationWidget->restoreSettings(settings); + m_rightNavigationWidget->restoreSettings(settings); + m_rightPaneWidget->readSettings(settings); +} + +void ICorePrivate::saveWindowSettings() +{ + QtcSettings *settings = PluginManager::settings(); + settings->beginGroup(settingsGroup); + + // On OS X applications usually do not restore their full screen state. + // To be able to restore the correct non-full screen geometry, we have to put + // the window out of full screen before saving the geometry. + // Works around QTBUG-45241 + if (Utils::HostOsInfo::isMacHost() && m_mainwindow->isFullScreen()) + m_mainwindow->setWindowState(m_mainwindow->windowState() & ~Qt::WindowFullScreen); + settings->setValue(windowGeometryKey, m_mainwindow->saveGeometry()); + settings->setValue(windowStateKey, m_mainwindow->saveState()); + settings->setValue(modeSelectorLayoutKey, int(ModeManager::modeStyle())); + + settings->endGroup(); +} + +void ICorePrivate::updateModeSelectorStyleMenu() +{ + switch (ModeManager::modeStyle()) { + case ModeManager::Style::IconsAndText: + m_setModeSelectorStyleIconsAndTextAction->setChecked(true); + break; + case ModeManager::Style::IconsOnly: + m_setModeSelectorStyleIconsOnlyAction->setChecked(true); + break; + case ModeManager::Style::Hidden: + m_setModeSelectorStyleHiddenAction->setChecked(true); + break; + } +} + +void ICorePrivate::updateContext() +{ + Context contexts = m_highPrioAdditionalContexts; + + for (IContext *context : std::as_const(m_activeContext)) + contexts.add(context->context()); + + contexts.add(m_lowPrioAdditionalContexts); + + Context uniquecontexts; + for (const Id &id : std::as_const(contexts)) { + if (!uniquecontexts.contains(id)) + uniquecontexts.add(id); + } + + ActionManager::setContext(uniquecontexts); + emit m_core->contextChanged(uniquecontexts); +} + +void ICorePrivate::aboutToShowRecentFiles() +{ + ActionContainer *aci = ActionManager::actionContainer(Constants::M_FILE_RECENTFILES); + QMenu *menu = aci->menu(); + menu->clear(); + + const QList<DocumentManager::RecentFile> recentFiles = DocumentManager::recentFiles(); + for (int i = 0; i < recentFiles.count(); ++i) { + const DocumentManager::RecentFile file = recentFiles[i]; + + const QString filePath = Utils::quoteAmpersands(file.first.shortNativePath()); + const QString actionText = ActionManager::withNumberAccelerator(filePath, i + 1); + QAction *action = menu->addAction(actionText); + connect(action, &QAction::triggered, this, [file] { + EditorManager::openEditor(file.first, file.second); + }); + } + + bool hasRecentFiles = !recentFiles.isEmpty(); + menu->setEnabled(hasRecentFiles); + + // add the Clear Menu item + if (hasRecentFiles) { + menu->addSeparator(); + QAction *action = menu->addAction(Tr::tr(Constants::TR_CLEAR_MENU)); + connect(action, &QAction::triggered, + DocumentManager::instance(), &DocumentManager::clearRecentFiles); + } +} + +void ICorePrivate::aboutQtCreator() +{ + if (!m_versionDialog) { + m_versionDialog = new VersionDialog(m_mainwindow); + connect(m_versionDialog, &QDialog::finished, + this, &ICorePrivate::destroyVersionDialog); + ICore::registerWindow(m_versionDialog, Context("Core.VersionDialog")); + m_versionDialog->show(); + } else { + ICore::raiseWindow(m_versionDialog); + } +} + +void ICorePrivate::destroyVersionDialog() +{ + if (m_versionDialog) { + m_versionDialog->deleteLater(); + m_versionDialog = nullptr; + } +} + +void ICorePrivate::aboutPlugins() +{ + PluginDialog dialog(m_mainwindow); + dialog.exec(); +} + +class LogDialog : public QDialog +{ +public: + LogDialog(QWidget *parent) + : QDialog(parent) + {} + bool event(QEvent *event) override + { + if (event->type() == QEvent::ShortcutOverride) { + auto ke = static_cast<QKeyEvent *>(event); + if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { + ke->accept(); + return true; + } + } + return QDialog::event(event); + } +}; + +void ICorePrivate::changeLog() +{ + static QPointer<LogDialog> dialog; + if (dialog) { + ICore::raiseWindow(dialog); + return; + } + const FilePaths files = + ICore::resourcePath("changelog").dirEntries({{"changes-*"}, QDir::Files}); + static const QRegularExpression versionRegex("\\d+[.]\\d+[.]\\d+"); + using VersionFilePair = std::pair<QVersionNumber, FilePath>; + QList<VersionFilePair> versionedFiles = Utils::transform(files, [](const FilePath &fp) { + const QRegularExpressionMatch match = versionRegex.match(fp.fileName()); + const QVersionNumber version = match.hasMatch() + ? QVersionNumber::fromString(match.captured()) + : QVersionNumber(); + return std::make_pair(version, fp); + }); + Utils::sort(versionedFiles, [](const VersionFilePair &a, const VersionFilePair &b) { + return a.first > b.first; + }); + + auto versionCombo = new QComboBox; + for (const VersionFilePair &f : versionedFiles) + versionCombo->addItem(f.first.toString()); + dialog = new LogDialog(ICore::dialogParent()); + auto versionLayout = new QHBoxLayout; + versionLayout->addWidget(new QLabel(Tr::tr("Version:"))); + versionLayout->addWidget(versionCombo); + versionLayout->addStretch(1); + auto showInExplorer = new QPushButton(FileUtils::msgGraphicalShellAction()); + versionLayout->addWidget(showInExplorer); + auto textEdit = new QTextBrowser; + textEdit->setOpenExternalLinks(true); + + auto aggregate = new Aggregation::Aggregate; + aggregate->add(textEdit); + aggregate->add(new Core::BaseTextFind(textEdit)); + + new MarkdownHighlighter(textEdit->document()); + + auto textEditWidget = new QFrame; + textEditWidget->setFrameStyle(QFrame::NoFrame); + auto findToolBar = new FindToolBarPlaceHolder(dialog); + findToolBar->setLightColored(true); + auto textEditLayout = new QVBoxLayout; + textEditLayout->setContentsMargins(0, 0, 0, 0); + textEditLayout->setSpacing(0); + textEditLayout->addWidget(textEdit); + textEditLayout->addWidget(findToolBar); + textEditWidget->setLayout(textEditLayout); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + auto dialogLayout = new QVBoxLayout; + dialogLayout->addLayout(versionLayout); + dialogLayout->addWidget(textEditWidget); + dialogLayout->addWidget(buttonBox); + dialog->setLayout(dialogLayout); + dialog->resize(700, 600); + dialog->setWindowTitle(Tr::tr("Change Log")); + dialog->setAttribute(Qt::WA_DeleteOnClose); + ICore::registerWindow(dialog, Context("CorePlugin.VersionDialog")); + + connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::close); + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + if (QTC_GUARD(closeButton)) + closeButton->setDefault(true); // grab from "Open in Explorer" button + + const auto showLog = [textEdit, versionedFiles](int index) { + if (index < 0 || index >= versionedFiles.size()) + return; + const FilePath file = versionedFiles.at(index).second; + QString contents = QString::fromUtf8(file.fileContents().value_or(QByteArray())); + // (?<![[\/]) == don't replace if it is preceded by "[" or "/" + // i.e. if it already is part of a link + static const QRegularExpression bugexpr(R"((?<![[\/])((QT(CREATOR)?BUG|PYSIDE)-\d+))"); + contents.replace(bugexpr, R"([\1](https://bugreports.qt.io/browse/\1))"); + static const QRegularExpression docexpr("https://doc[.]qt[.]io/qtcreator/([.a-zA-Z/_-]*)"); + QList<QRegularExpressionMatch> matches; + for (const QRegularExpressionMatch &m : docexpr.globalMatch(contents)) + matches.append(m); + Utils::reverseForeach(matches, [&contents](const QRegularExpressionMatch &match) { + const QString qthelpUrl = "qthelp://org.qt-project.qtcreator/doc/" + match.captured(1); + if (!HelpManager::fileData(qthelpUrl).isEmpty()) + contents.replace(match.capturedStart(), match.capturedLength(), qthelpUrl); + }); + textEdit->setMarkdown(contents); + }; + connect(versionCombo, &QComboBox::currentIndexChanged, textEdit, showLog); + showLog(versionCombo->currentIndex()); + + connect(showInExplorer, &QPushButton::clicked, this, [versionCombo, versionedFiles] { + const int index = versionCombo->currentIndex(); + if (index >= 0 && index < versionedFiles.size()) + FileUtils::showInGraphicalShell(ICore::dialogParent(), versionedFiles.at(index).second); + else + FileUtils::showInGraphicalShell(ICore::dialogParent(), ICore::resourcePath("changelog")); + }); + + dialog->show(); +} + +void ICorePrivate::contact() +{ + QMessageBox dlg(QMessageBox::Information, Tr::tr("Contact"), + Tr::tr("<p>Qt Creator developers can be reached at the Qt Creator mailing list:</p>" + "%1" + "<p>or the #qt-creator channel on Libera.Chat IRC:</p>" + "%2" + "<p>Our bug tracker is located at %3.</p>" + "<p>Please use %4 for bigger chunks of text.</p>") + .arg("<p>    " + "<a href=\"https://lists.qt-project.org/listinfo/qt-creator\">" + "mailto:qt-creator@qt-project.org" + "</a></p>") + .arg("<p>    " + "<a href=\"https://web.libera.chat/#qt-creator\">" + "https://web.libera.chat/#qt-creator" + "</a></p>") + .arg("<a href=\"https://bugreports.qt.io/projects/QTCREATORBUG\">" + "https://bugreports.qt.io" + "</a>") + .arg("<a href=\"https://pastebin.com\">" + "https://pastebin.com" + "</a>"), + QMessageBox::Ok, m_mainwindow); + dlg.exec(); +} + +void ICorePrivate::restoreWindowState() +{ + NANOTRACE_SCOPE("Core", "MainWindow::restoreWindowState"); + QtcSettings *settings = PluginManager::settings(); + settings->beginGroup(settingsGroup); + if (!m_mainwindow->restoreGeometry(settings->value(windowGeometryKey).toByteArray())) + m_mainwindow->resize(1260, 700); // size without window decoration + m_mainwindow->restoreState(settings->value(windowStateKey).toByteArray()); + settings->endGroup(); + m_mainwindow->show(); + StatusBarManager::restoreSettings(); +} + +} // Internal + +void ICore::setOverrideColor(const QColor &color) +{ + d->m_overrideColor = color; +} + } // namespace Core diff --git a/src/plugins/coreplugin/icore.h b/src/plugins/coreplugin/icore.h index 5e28693904d..cff938f5393 100644 --- a/src/plugins/coreplugin/icore.h +++ b/src/plugins/coreplugin/icore.h @@ -6,17 +6,17 @@ #include "core_global.h" #include "icontext.h" +#include <utils/appmainwindow.h> #include <utils/filepath.h> #include <utils/qtcsettings.h> #include <QList> -#include <QObject> #include <QRect> -#include <QSettings> #include <functional> QT_BEGIN_NAMESPACE +class QColor; class QMainWindow; class QPrinter; class QStatusBar; @@ -26,25 +26,20 @@ QT_END_NAMESPACE namespace Utils { class InfoBar; } namespace Core { + class Context; +class IDocument; class IWizardFactory; -class SettingsDatabase; - -namespace Internal { class MainWindow; } - class NewDialog; class CORE_EXPORT ICore : public QObject { Q_OBJECT - friend class Internal::MainWindow; - friend class IWizardFactory; - - explicit ICore(Internal::MainWindow *mw); +public: + ICore(); ~ICore() override; -public: enum class ContextPriority { High, Low @@ -70,7 +65,6 @@ public: static bool isQtDesignStudio(); static Utils::QtcSettings *settings(QSettings::Scope scope = QSettings::UserScope); - static SettingsDatabase *settingsDatabase(); static QPrinter *printer(); static QString userInterfaceLanguage(); @@ -81,8 +75,6 @@ public: static Utils::FilePath libexecPath(const QString &rel = {}); static Utils::FilePath crashReportsPath(); - static QString ideDisplayName(); - static QString versionString(); static QMainWindow *mainWindow(); @@ -90,6 +82,7 @@ public: static Utils::InfoBar *infoBar(); static void raiseWindow(QWidget *widget); + static void raiseMainWindow(); static IContext *currentContextObject(); static QWidget *currentContextWidget(); @@ -113,19 +106,21 @@ public: StopOnLoadFail = 4, SwitchSplitIfAlreadyVisible = 8 }; - static void openFiles(const Utils::FilePaths &filePaths, OpenFilesFlags flags = None); static void addPreCloseListener(const std::function<bool()> &listener); static void restart(); enum SaveSettingsReason { - InitializationDone, SettingsDialogDone, ModeChanged, MainWindowClosing, }; +public slots: + static void openFileWith(); + static void exit(); + signals: void coreAboutToOpen(); void coreOpened(); @@ -139,6 +134,7 @@ signals: public: /* internal use */ static QStringList additionalAboutInformation(); + static void clearAboutInformation(); static void appendAboutInformation(const QString &line); static QString systemInformation(); static void setupScreenShooter(const QString &name, QWidget *w, const QRect &rc = QRect()); @@ -155,9 +151,18 @@ public: static void saveSettings(SaveSettingsReason reason); static void setNewDialogFactory(const std::function<NewDialog *(QWidget *)> &newFactory); - -private: static void updateNewItemDialogState(); + + static void setOverrideColor(const QColor &color); + + static void init(); + static void extensionsInitialized(); + static void aboutToShutdown(); + static void saveSettings(); + + static IDocument *openFiles(const Utils::FilePaths &filePaths, + OpenFilesFlags flags = None, + const Utils::FilePath &workingDirectory = {}); }; } // namespace Core diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index 7be7e410116..d16f284b4a4 100644 --- a/src/plugins/coreplugin/idocument.cpp +++ b/src/plugins/coreplugin/idocument.cpp @@ -326,6 +326,7 @@ IDocument::OpenResult IDocument::open(QString *errorString, const Utils::FilePat /*! Saves the contents of the document to the \a filePath on disk. + If \a filePath is empty filePath() is used. If \a autoSave is \c true, the saving is done for an auto-save, so the document should avoid cleanups or other operations that it does for @@ -340,13 +341,15 @@ IDocument::OpenResult IDocument::open(QString *errorString, const Utils::FilePat \sa shouldAutoSave() \sa aboutToSave() \sa saved() + \sa filePath() */ bool IDocument::save(QString *errorString, const Utils::FilePath &filePath, bool autoSave) { - emit aboutToSave(filePath.isEmpty() ? this->filePath() : filePath, autoSave); - const bool success = saveImpl(errorString, filePath, autoSave); + const Utils::FilePath savePath = filePath.isEmpty() ? this->filePath() : filePath; + emit aboutToSave(savePath, autoSave); + const bool success = saveImpl(errorString, savePath, autoSave); if (success) - emit saved(filePath.isEmpty() ? this->filePath() : filePath, autoSave); + emit saved(savePath, autoSave); return success; } @@ -401,6 +404,13 @@ bool IDocument::setContents(const QByteArray &contents) return false; } +/*! + Formats the contents of the document, if the implementation supports such functionality. +*/ +void IDocument::formatContents() +{ +} + /*! Returns the absolute path of the file that this document refers to. May be empty for documents that are not backed by a file. diff --git a/src/plugins/coreplugin/idocument.h b/src/plugins/coreplugin/idocument.h index b771843056b..8fbca1d02f7 100644 --- a/src/plugins/coreplugin/idocument.h +++ b/src/plugins/coreplugin/idocument.h @@ -72,6 +72,7 @@ public: virtual QByteArray contents() const; virtual bool setContents(const QByteArray &contents); + virtual void formatContents(); const Utils::FilePath &filePath() const; virtual void setFilePath(const Utils::FilePath &filePath); diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.cpp b/src/plugins/coreplugin/inavigationwidgetfactory.cpp index 663fbb9829b..8517676e9e6 100644 --- a/src/plugins/coreplugin/inavigationwidgetfactory.cpp +++ b/src/plugins/coreplugin/inavigationwidgetfactory.cpp @@ -59,7 +59,9 @@ to the caller. */ -using namespace Core; +using namespace Utils; + +namespace Core { static QList<INavigationWidgetFactory *> g_navigationWidgetFactories; @@ -106,7 +108,7 @@ void INavigationWidgetFactory::setPriority(int priority) \sa id() */ -void INavigationWidgetFactory::setId(Utils::Id id) +void INavigationWidgetFactory::setId(Id id) { m_id = id; } @@ -135,7 +137,7 @@ QKeySequence INavigationWidgetFactory::activationSequence() const \sa INavigationWidgetFactory::restoreSettings() */ -void INavigationWidgetFactory::saveSettings(Utils::QtcSettings * /* settings */, +void INavigationWidgetFactory::saveSettings(QtcSettings * /* settings */, int /* position */, QWidget * /* widget */) { @@ -147,18 +149,20 @@ void INavigationWidgetFactory::saveSettings(Utils::QtcSettings * /* settings */, \sa INavigationWidgetFactory::saveSettings() */ -void INavigationWidgetFactory::restoreSettings(QSettings * /* settings */, int /* position */, QWidget * /* widget */) +void INavigationWidgetFactory::restoreSettings(QtcSettings * /* settings */, int /* position */, + QWidget * /* widget */) { } // Registers a new root path in the factory -void INavigationWidgetFactory::addRootPath(Utils::Id /*id*/, const QString & /*displayName*/, const QIcon & /*icon*/, const Utils::FilePath & /*path*/) +void INavigationWidgetFactory::addRootPath(Id /*id*/, const QString & /*displayName*/, + const QIcon & /*icon*/, const FilePath & /*path*/) { - } // Removes a root path from the factory -void INavigationWidgetFactory::removeRootPath(Utils::Id /*path*/) +void INavigationWidgetFactory::removeRootPath(Id /*path*/) { - } + +} // Core diff --git a/src/plugins/coreplugin/inavigationwidgetfactory.h b/src/plugins/coreplugin/inavigationwidgetfactory.h index 2f383afecd4..6be77939fbd 100644 --- a/src/plugins/coreplugin/inavigationwidgetfactory.h +++ b/src/plugins/coreplugin/inavigationwidgetfactory.h @@ -13,7 +13,6 @@ #include <QKeySequence> QT_BEGIN_NAMESPACE -class QSettings; class QToolButton; class QWidget; QT_END_NAMESPACE @@ -57,7 +56,7 @@ public: virtual NavigationView createWidget() = 0; virtual void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget); - virtual void restoreSettings(QSettings *settings, int position, QWidget *widget); + virtual void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget); virtual void addRootPath(Utils::Id id, const QString &displayName, const QIcon &icon, const Utils::FilePath &path); virtual void removeRootPath(Utils::Id id); diff --git a/src/plugins/coreplugin/ioutputpane.cpp b/src/plugins/coreplugin/ioutputpane.cpp index 1505a2b923e..d10c55eaad0 100644 --- a/src/plugins/coreplugin/ioutputpane.cpp +++ b/src/plugins/coreplugin/ioutputpane.cpp @@ -49,22 +49,6 @@ namespace Core { Returns the toolbar widgets for the output pane. */ -/*! - \fn QString IOutputPane::displayName() const - - Returns the translated display name of the output pane. -*/ - -/*! - \fn int IOutputPane::priorityInStatusBar() const - - Determines the position of the output pane on the status bar. - \list - \li 100 to 0 from front to end - \li -1 do not show in status bar - \endlist -*/ - /*! \fn void IOutputPane::clearContents() diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h index 9193699ca4a..07f32ded5b6 100644 --- a/src/plugins/coreplugin/ioutputpane.h +++ b/src/plugins/coreplugin/ioutputpane.h @@ -33,11 +33,12 @@ public: virtual QWidget *outputWidget(QWidget *parent) = 0; virtual QList<QWidget *> toolBarWidgets() const; - virtual QString displayName() const = 0; + Utils::Id id() const; + QString displayName() const; virtual const QList<OutputWindow *> outputWindows() const { return {}; } virtual void ensureWindowVisible(OutputWindow *) { } - virtual int priorityInStatusBar() const = 0; + int priorityInStatusBar() const; virtual void clearContents() = 0; virtual void visibilityChanged(bool visible); @@ -81,7 +82,11 @@ signals: void fontChanged(const QFont &font); protected: - void setupFilterUi(const QString &historyKey); + void setId(const Utils::Id &id); + void setDisplayName(const QString &name); + void setPriorityInStatusBar(int priority); + + void setupFilterUi(const Utils::Key &historyKey); QString filterText() const; bool filterUsesRegexp() const { return m_filterRegexp; } bool filterIsInverted() const { return m_invertFilter; } @@ -104,6 +109,9 @@ private: Utils::Id filterCaseSensitivityActionId() const; Utils::Id filterInvertedActionId() const; + Utils::Id m_id; + QString m_displayName; + int m_priority = -1; Core::CommandButton * const m_zoomInButton; Core::CommandButton * const m_zoomOutButton; QAction *m_filterActionRegexp = nullptr; diff --git a/src/plugins/coreplugin/iversioncontrol.cpp b/src/plugins/coreplugin/iversioncontrol.cpp index 35db42ab4cf..483dd211e83 100644 --- a/src/plugins/coreplugin/iversioncontrol.cpp +++ b/src/plugins/coreplugin/iversioncontrol.cpp @@ -11,7 +11,6 @@ #include <utils/qtcassert.h> #include <QDir> -#include <QFileInfo> #include <QRegularExpression> #include <QStringList> @@ -182,8 +181,6 @@ bool IVersionControl::handleLink(const FilePath &workingDirectory, const QString #if defined(WITH_TESTS) -#include <QFileInfo> - namespace Core { TestVersionControl::~TestVersionControl() diff --git a/src/plugins/coreplugin/iwizardfactory.h b/src/plugins/coreplugin/iwizardfactory.h index 26dfe8bbd85..e9771f7aa1d 100644 --- a/src/plugins/coreplugin/iwizardfactory.h +++ b/src/plugins/coreplugin/iwizardfactory.h @@ -3,8 +3,8 @@ #pragma once -#include <coreplugin/core_global.h> -#include <coreplugin/featureprovider.h> +#include "core_global.h" +#include "featureprovider.h" #include <QIcon> #include <QObject> diff --git a/src/plugins/coreplugin/jsexpander.h b/src/plugins/coreplugin/jsexpander.h index 780b0dda742..f5737588b74 100644 --- a/src/plugins/coreplugin/jsexpander.h +++ b/src/plugins/coreplugin/jsexpander.h @@ -13,14 +13,12 @@ class QObject; class QString; QT_END_NAMESPACE -namespace Utils { -class MacroExpander; -} +namespace Utils { class MacroExpander; } namespace Core { namespace Internal { -class MainWindow; +class ICorePrivate; class JsExpanderPrivate; } // namespace Internal @@ -50,7 +48,7 @@ private: static JsExpander *createGlobalJsExpander(); Internal::JsExpanderPrivate *d; - friend class Core::Internal::MainWindow; + friend class Internal::ICorePrivate; }; } // namespace Core diff --git a/src/plugins/coreplugin/locator/commandlocator.cpp b/src/plugins/coreplugin/locator/commandlocator.cpp index e960b4df85d..6fd21a524ba 100644 --- a/src/plugins/coreplugin/locator/commandlocator.cpp +++ b/src/plugins/coreplugin/locator/commandlocator.cpp @@ -3,11 +3,12 @@ #include "commandlocator.h" -#include <coreplugin/actionmanager/command.h> +#include "../actionmanager/command.h" #include <utils/stringutils.h> #include <QAction> +#include <QPointer> using namespace Utils; diff --git a/src/plugins/coreplugin/locator/directoryfilter.cpp b/src/plugins/coreplugin/locator/directoryfilter.cpp index 6acdb74720a..4b22bcbf63d 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.cpp +++ b/src/plugins/coreplugin/locator/directoryfilter.cpp @@ -6,6 +6,8 @@ #include "locator.h" #include "../coreplugintr.h" +#include <extensionsystem/pluginmanager.h> + #include <utils/algorithm.h> #include <utils/async.h> #include <utils/fileutils.h> @@ -50,21 +52,21 @@ static void refresh(QPromise<FilePaths> &promise, const FilePaths &directories, const QStringList &filters, const QStringList &exclusionFilters, const QString &displayName) { - SubDirFileIterator subDirIterator(directories, filters, exclusionFilters); - promise.setProgressRange(0, subDirIterator.maxProgress()); + SubDirFileContainer fileContainer(directories, filters, exclusionFilters); + promise.setProgressRange(0, fileContainer.progressMaximum()); FilePaths files; - const auto end = subDirIterator.end(); - for (auto it = subDirIterator.begin(); it != end; ++it) { + const auto end = fileContainer.end(); + for (auto it = fileContainer.begin(); it != end; ++it) { if (promise.isCanceled()) { - promise.setProgressValueAndText(subDirIterator.currentProgress(), + promise.setProgressValueAndText(it.progressValue(), Tr::tr("%1 filter update: canceled").arg(displayName)); return; } - files << (*it).filePath; - promise.setProgressValueAndText(subDirIterator.currentProgress(), - Tr::tr("%1 filter update: %n files", nullptr, files.size()).arg(displayName)); + files << it->filePath; + promise.setProgressValueAndText(it.progressValue(), + Tr::tr("%1 filter update: %n files", nullptr, files.size()).arg(displayName)); } - promise.setProgressValue(subDirIterator.maxProgress()); + promise.setProgressValue(fileContainer.progressMaximum()); promise.addResult(files); } @@ -87,6 +89,7 @@ DirectoryFilter::DirectoryFilter(Id id) return SetupResult::StopWithDone; // Group stops, skips async task }; const auto asyncSetup = [this](Async<FilePaths> &async) { + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters, displayName()); }; diff --git a/src/plugins/coreplugin/locator/directoryfilter.h b/src/plugins/coreplugin/locator/directoryfilter.h index 485377d2a15..452a1b20e75 100644 --- a/src/plugins/coreplugin/locator/directoryfilter.h +++ b/src/plugins/coreplugin/locator/directoryfilter.h @@ -3,10 +3,9 @@ #pragma once +#include "../core_global.h" #include "ilocatorfilter.h" -#include <coreplugin/core_global.h> - namespace Core { class CORE_EXPORT DirectoryFilter : public ILocatorFilter diff --git a/src/plugins/coreplugin/locator/filesystemfilter.cpp b/src/plugins/coreplugin/locator/filesystemfilter.cpp index 7c6ac13abda..39eea1200fe 100644 --- a/src/plugins/coreplugin/locator/filesystemfilter.cpp +++ b/src/plugins/coreplugin/locator/filesystemfilter.cpp @@ -63,7 +63,7 @@ static bool askForCreating(const QString &title, const FilePath &filePath) = CheckableMessageBox::question(ICore::dialogParent(), title, Tr::tr("Create \"%1\"?").arg(filePath.shortNativePath()), - QString(kAlwaysCreate), + Key(kAlwaysCreate), QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel, QMessageBox::Yes, diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp index 7f272fd108d..feff0d9d1ca 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp @@ -327,11 +327,7 @@ public: void start() final { task()->start(); } }; -} // namespace Core - -TASKING_DECLARE_TASK(ResultsCollectorTask, Core::ResultsCollectorTaskAdapter); - -namespace Core { +using ResultsCollectorTask = CustomTask<ResultsCollectorTaskAdapter>; class LocatorStoragePrivate { diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h index fbc68bc647c..57da6a2d85f 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.h +++ b/src/plugins/coreplugin/locator/ilocatorfilter.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <solutions/tasking/tasktree.h> diff --git a/src/plugins/coreplugin/locator/javascriptfilter.cpp b/src/plugins/coreplugin/locator/javascriptfilter.cpp index a15cabd8d9d..21459d41dc6 100644 --- a/src/plugins/coreplugin/locator/javascriptfilter.cpp +++ b/src/plugins/coreplugin/locator/javascriptfilter.cpp @@ -349,7 +349,7 @@ public: void start() final { task()->start(); } }; -TASKING_DECLARE_TASK(JavaScriptRequestTask, JavaScriptRequestAdapter); +using JavaScriptRequestTask = CustomTask<JavaScriptRequestAdapter>; namespace Core::Internal { diff --git a/src/plugins/coreplugin/locator/javascriptfilter.h b/src/plugins/coreplugin/locator/javascriptfilter.h index 8b7112fd27d..f5013285cc1 100644 --- a/src/plugins/coreplugin/locator/javascriptfilter.h +++ b/src/plugins/coreplugin/locator/javascriptfilter.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/locator/ilocatorfilter.h> +#include "ilocatorfilter.h" class JavaScriptEngine; diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp index cefce35a5b1..f4ee927e13b 100644 --- a/src/plugins/coreplugin/locator/locator.cpp +++ b/src/plugins/coreplugin/locator/locator.cpp @@ -155,26 +155,26 @@ void Locator::aboutToShutdown() void Locator::loadSettings() { - SettingsDatabase *settings = ICore::settingsDatabase(); + namespace DB = SettingsDatabase; // check if we have to read old settings // TOOD remove a few versions after 4.15 - const QString settingsGroup = settings->contains("Locator") ? QString("Locator") + const QString settingsGroup = DB::contains("Locator") ? QString("Locator") : QString("QuickOpen"); const Settings def; - settings->beginGroup(settingsGroup); - m_refreshTimer.setInterval(settings->value("RefreshInterval", 60).toInt() * 60000); - m_settings.useCenteredPopup = settings->value(kUseCenteredPopup, def.useCenteredPopup).toBool(); + DB::beginGroup(settingsGroup); + m_refreshTimer.setInterval(DB::value("RefreshInterval", 60).toInt() * 60000); + m_settings.useCenteredPopup = DB::value(kUseCenteredPopup, def.useCenteredPopup).toBool(); for (ILocatorFilter *filter : std::as_const(m_filters)) { - if (settings->contains(filter->id().toString())) { - const QByteArray state = settings->value(filter->id().toString()).toByteArray(); + if (DB::contains(filter->id().toString())) { + const QByteArray state = DB::value(filter->id().toString()).toByteArray(); if (!state.isEmpty()) filter->restoreState(state); } } - settings->beginGroup("CustomFilters"); + DB::beginGroup("CustomFilters"); QList<ILocatorFilter *> customFilters; - const QStringList keys = settings->childKeys(); + const QStringList keys = DB::childKeys(); int count = 0; const Id directoryBaseId(Constants::CUSTOM_DIRECTORY_FILTER_BASEID); const Id urlBaseId(Constants::CUSTOM_URL_FILTER_BASEID); @@ -188,12 +188,12 @@ void Locator::loadSettings() urlFilter->setIsCustomFilter(true); filter = urlFilter; } - filter->restoreState(settings->value(key).toByteArray()); + filter->restoreState(DB::value(key).toByteArray()); customFilters.append(filter); } setCustomFilters(customFilters); - settings->endGroup(); - settings->endGroup(); + DB::endGroup(); + DB::endGroup(); if (m_refreshTimer.interval() > 0) m_refreshTimer.start(); @@ -288,19 +288,19 @@ void Locator::saveSettings() const return; const Settings def; - SettingsDatabase *s = ICore::settingsDatabase(); - s->beginTransaction(); - s->beginGroup("Locator"); - s->remove(QString()); - s->setValue("RefreshInterval", refreshInterval()); - s->setValueWithDefault(kUseCenteredPopup, m_settings.useCenteredPopup, def.useCenteredPopup); + namespace DB = SettingsDatabase; + DB::beginTransaction(); + DB::beginGroup("Locator"); + DB::remove(QString()); + DB::setValue("RefreshInterval", refreshInterval()); + DB::setValueWithDefault(kUseCenteredPopup, m_settings.useCenteredPopup, def.useCenteredPopup); for (ILocatorFilter *filter : m_filters) { if (!m_customFilters.contains(filter) && filter->id().isValid()) { const QByteArray state = filter->saveState(); - s->setValueWithDefault(filter->id().toString(), state); + DB::setValueWithDefault(filter->id().toString(), state); } } - s->beginGroup("CustomFilters"); + DB::beginGroup("CustomFilters"); int i = 0; for (ILocatorFilter *filter : m_customFilters) { const char *prefix = filter->id().name().startsWith( @@ -308,12 +308,12 @@ void Locator::saveSettings() const ? kDirectoryFilterPrefix : kUrlFilterPrefix; const QByteArray state = filter->saveState(); - s->setValueWithDefault(prefix + QString::number(i), state); + DB::setValueWithDefault(prefix + QString::number(i), state); ++i; } - s->endGroup(); - s->endGroup(); - s->endTransaction(); + DB::endGroup(); + DB::endGroup(); + DB::endTransaction(); } /*! diff --git a/src/plugins/coreplugin/locator/locator.h b/src/plugins/coreplugin/locator/locator.h index 45d4b650508..e2069e99e99 100644 --- a/src/plugins/coreplugin/locator/locator.h +++ b/src/plugins/coreplugin/locator/locator.h @@ -4,15 +4,13 @@ #pragma once #include "ilocatorfilter.h" +#include "../actionmanager/command.h" -#include <coreplugin/actionmanager/command.h> #include <extensionsystem/iplugin.h> #include <QObject> #include <QTimer> -#include <functional> - namespace Tasking { class TaskTree; } namespace Core { diff --git a/src/plugins/coreplugin/locator/locator_test.cpp b/src/plugins/coreplugin/locator/locator_test.cpp index c83a2d8779c..26a6b142b02 100644 --- a/src/plugins/coreplugin/locator/locator_test.cpp +++ b/src/plugins/coreplugin/locator/locator_test.cpp @@ -2,10 +2,9 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "../coreplugin.h" - +#include "../testdatadir.h" #include "locatorfiltertest.h" -#include <coreplugin/testdatadir.h> #include <utils/algorithm.h> #include <utils/fileutils.h> diff --git a/src/plugins/coreplugin/locator/locatormanager.cpp b/src/plugins/coreplugin/locator/locatormanager.cpp index d8ea9b36f9e..e4fc9edfc36 100644 --- a/src/plugins/coreplugin/locator/locatormanager.cpp +++ b/src/plugins/coreplugin/locator/locatormanager.cpp @@ -6,9 +6,9 @@ #include "ilocatorfilter.h" #include "locator.h" #include "locatorwidget.h" +#include "../icore.h" #include <aggregation/aggregate.h> -#include <coreplugin/icore.h> #include <extensionsystem/pluginmanager.h> #include <utils/qtcassert.h> diff --git a/src/plugins/coreplugin/locator/locatormanager.h b/src/plugins/coreplugin/locator/locatormanager.h index 256e4327643..0b17de23a10 100644 --- a/src/plugins/coreplugin/locator/locatormanager.h +++ b/src/plugins/coreplugin/locator/locatormanager.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <QObject> diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.cpp b/src/plugins/coreplugin/locator/locatorsettingspage.cpp index 74c3b45692e..e06d26a783c 100644 --- a/src/plugins/coreplugin/locator/locatorsettingspage.cpp +++ b/src/plugins/coreplugin/locator/locatorsettingspage.cpp @@ -277,6 +277,7 @@ public: filterEdit->setFiltering(true); m_filterList = new TreeView; + m_filterList->setUniformRowHeights(false); m_filterList->setSelectionMode(QAbstractItemView::SingleSelection); m_filterList->setSelectionBehavior(QAbstractItemView::SelectRows); m_filterList->setSortingEnabled(true); diff --git a/src/plugins/coreplugin/locator/locatorsettingspage.h b/src/plugins/coreplugin/locator/locatorsettingspage.h index 408fe7b91f8..b4f9e4a4a33 100644 --- a/src/plugins/coreplugin/locator/locatorsettingspage.h +++ b/src/plugins/coreplugin/locator/locatorsettingspage.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include "../dialogs/ioptionspage.h" namespace Core::Internal { diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp index 8af3f4b753d..5876c20827c 100644 --- a/src/plugins/coreplugin/locator/locatorwidget.cpp +++ b/src/plugins/coreplugin/locator/locatorwidget.cpp @@ -13,6 +13,7 @@ #include "../modemanager.h" #include <utils/algorithm.h> +#include <utils/execmenu.h> #include <utils/fancylineedit.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/highlightingitemdelegate.h> @@ -22,7 +23,6 @@ #include <utils/qtcassert.h> #include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> - #include <QAction> #include <QApplication> #include <QColor> @@ -250,7 +250,6 @@ CompletionList::CompletionList(QWidget *parent) setItemDelegate(new CompletionDelegate(this)); setRootIsDecorated(false); - setUniformRowHeights(true); header()->hide(); header()->setStretchLastSection(true); // This is too slow when done on all results @@ -584,13 +583,12 @@ LocatorWidget::LocatorWidget(Locator *locator) m_centeredPopupAction->setCheckable(true); m_centeredPopupAction->setChecked(Locator::useCenteredPopupForShortcut()); + connect(m_filterMenu, &QMenu::aboutToShow, this, [this] { m_centeredPopupAction->setChecked(Locator::useCenteredPopupForShortcut()); }); - connect(m_filterMenu, &QMenu::hovered, this, [this](QAction *action) { - ToolTip::show(m_filterMenu->mapToGlobal(m_filterMenu->actionGeometry(action).topRight()), - action->toolTip()); - }); + Utils::addToolTipsToMenu(m_filterMenu); + connect(m_centeredPopupAction, &QAction::toggled, locator, [locator](bool toggled) { if (toggled != Locator::useCenteredPopupForShortcut()) { Locator::setUseCenteredPopupForShortcut(toggled); diff --git a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp index 95e076ec2bd..a13f51484bf 100644 --- a/src/plugins/coreplugin/locator/opendocumentsfilter.cpp +++ b/src/plugins/coreplugin/locator/opendocumentsfilter.cpp @@ -4,8 +4,7 @@ #include "opendocumentsfilter.h" #include "../coreplugintr.h" - -#include <coreplugin/editormanager/documentmodel.h> +#include "../editormanager/documentmodel.h" #include <extensionsystem/pluginmanager.h> diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp index d89bec2263b..a7c18867ff9 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.cpp @@ -21,6 +21,7 @@ #include <utils/stringutils.h> #include <utils/variablechooser.h> +#include <QCheckBox> #include <QFormLayout> #include <QJsonObject> #include <QRegularExpression> @@ -59,9 +60,12 @@ static QString defaultArguments(Qt::CaseSensitivity sens = Qt::CaseInsensitive) .arg(sens == Qt::CaseInsensitive ? QString() : "-i "); } +const bool kSortResultsDefault = true; + const char kCommandKey[] = "command"; const char kArgumentsKey[] = "arguments"; const char kCaseSensitiveKey[] = "caseSensitive"; +const char kSortResultsKey[] = "sortResults"; static QString escaped(const QString &query) { @@ -112,8 +116,10 @@ SpotlightLocatorFilter::SpotlightLocatorFilter() m_caseSensitiveArguments = defaultArguments(Qt::CaseSensitive); } -static void matches(QPromise<void> &promise, const LocatorStorage &storage, - const CommandLine &command) +static void matches(QPromise<void> &promise, + const LocatorStorage &storage, + const CommandLine &command, + bool sortResults) { // If search string contains spaces, treat them as wildcard '*' and search in full path const QString wildcardInput = QDir::fromNativeSeparators(storage.input()).replace(' ', '*'); @@ -156,11 +162,13 @@ static void matches(QPromise<void> &promise, const LocatorStorage &storage, process.start(); loop.exec(); - for (auto &entry : entries) { - if (promise.isCanceled()) - return; - if (entry.size() < 1000) - Utils::sort(entry, LocatorFilterEntry::compareLexigraphically); + if (sortResults) { + for (auto &entry : entries) { + if (promise.isCanceled()) + return; + if (entry.size() < 1000) + Utils::sort(entry, LocatorFilterEntry::compareLexigraphically); + } } if (promise.isCanceled()) return; @@ -174,8 +182,11 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers() TreeStorage<LocatorStorage> storage; - const auto onSetup = [storage, command = m_command, insensArgs = m_arguments, - sensArgs = m_caseSensitiveArguments](Async<void> &async) { + const auto onSetup = [storage, + command = m_command, + insensArgs = m_arguments, + sensArgs = m_caseSensitiveArguments, + sortResults = m_sortResults](Async<void> &async) { const Link link = Link::fromString(storage->input(), true); const FilePath input = link.targetFilePath; if (input.isEmpty()) @@ -188,7 +199,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers() const CommandLine cmd(FilePath::fromString(command), expander->expand(args), CommandLine::Raw); async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); - async.setConcurrentCallData(matches, *storage, cmd); + async.setConcurrentCallData(matches, *storage, cmd, sortResults); return SetupResult::Continue; }; @@ -210,9 +221,12 @@ bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefres argumentsEdit->setText(m_arguments); FancyLineEdit *caseSensitiveArgumentsEdit = new FancyLineEdit; caseSensitiveArgumentsEdit->setText(m_caseSensitiveArguments); + auto sortResults = new QCheckBox(Tr::tr("Sort results")); + sortResults->setChecked(m_sortResults); layout->addRow(Tr::tr("Executable:"), commandEdit); layout->addRow(Tr::tr("Arguments:"), argumentsEdit); layout->addRow(Tr::tr("Case sensitive:"), caseSensitiveArgumentsEdit); + layout->addRow({}, sortResults); std::unique_ptr<MacroExpander> expander(createMacroExpander("")); auto chooser = new VariableChooser(&configWidget); chooser->addMacroExpanderProvider([expander = expander.get()] { return expander; }); @@ -223,6 +237,7 @@ bool SpotlightLocatorFilter::openConfigDialog(QWidget *parent, bool &needsRefres m_command = commandEdit->rawFilePath().toString(); m_arguments = argumentsEdit->text(); m_caseSensitiveArguments = caseSensitiveArgumentsEdit->text(); + m_sortResults = sortResults->isChecked(); } return accepted; } @@ -235,6 +250,8 @@ void SpotlightLocatorFilter::saveState(QJsonObject &obj) const obj.insert(kArgumentsKey, m_arguments); if (m_caseSensitiveArguments != defaultArguments(Qt::CaseSensitive)) obj.insert(kCaseSensitiveKey, m_caseSensitiveArguments); + if (m_sortResults != kSortResultsDefault) + obj.insert(kSortResultsKey, m_sortResults); } void SpotlightLocatorFilter::restoreState(const QJsonObject &obj) @@ -242,6 +259,7 @@ void SpotlightLocatorFilter::restoreState(const QJsonObject &obj) m_command = obj.value(kCommandKey).toString(defaultCommand()); m_arguments = obj.value(kArgumentsKey).toString(defaultArguments()); m_caseSensitiveArguments = obj.value(kCaseSensitiveKey).toString(defaultArguments(Qt::CaseSensitive)); + m_sortResults = obj.value(kSortResultsKey).toBool(kSortResultsDefault); } } // namespace Core::Internal diff --git a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h index 06114d4e264..65570cc95d4 100644 --- a/src/plugins/coreplugin/locator/spotlightlocatorfilter.h +++ b/src/plugins/coreplugin/locator/spotlightlocatorfilter.h @@ -24,6 +24,7 @@ private: QString m_command; QString m_arguments; QString m_caseSensitiveArguments; + bool m_sortResults = true; }; } // namespace Core::Internal diff --git a/src/plugins/coreplugin/locator/urllocatorfilter.h b/src/plugins/coreplugin/locator/urllocatorfilter.h index 57f652aeca1..18535569c5b 100644 --- a/src/plugins/coreplugin/locator/urllocatorfilter.h +++ b/src/plugins/coreplugin/locator/urllocatorfilter.h @@ -5,7 +5,7 @@ #include "ilocatorfilter.h" -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <QDialog> diff --git a/src/plugins/coreplugin/loggingmanager.cpp b/src/plugins/coreplugin/loggingmanager.cpp deleted file mode 100644 index 608e7ce4706..00000000000 --- a/src/plugins/coreplugin/loggingmanager.cpp +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "loggingmanager.h" - -#include <utils/environment.h> -#include <utils/filepath.h> - -#include <QCoreApplication> -#include <QDateTime> -#include <QLibraryInfo> -#include <QRegularExpression> -#include <QSettings> -#include <QStandardPaths> - -// -// WARNING! Do not use qDebug(), qWarning() or similar inside this file - -// same applies for indirect usages (e.g. QTC_ASSERT() and the like). -// Using static functions of QLoggingCategory may cause dead locks as well. -// - -using namespace Utils; - -namespace Core { -namespace Internal { - -static QtMessageHandler s_originalMessageHandler = nullptr; - -static LoggingViewManager *s_instance = nullptr; - -static QString levelToString(QtMsgType t) -{ - switch (t) { - case QtMsgType::QtCriticalMsg: return {"critical"}; - case QtMsgType::QtDebugMsg: return {"debug"}; - case QtMsgType::QtInfoMsg: return {"info"}; - case QtMsgType::QtWarningMsg: return {"warning"}; - default: - return {"fatal"}; // wrong but we don't care - } -} - -static QtMsgType parseLevel(const QString &level) -{ - switch (level.at(0).toLatin1()) { - case 'c': return QtMsgType::QtCriticalMsg; - case 'd': return QtMsgType::QtDebugMsg; - case 'i': return QtMsgType::QtInfoMsg; - case 'w': return QtMsgType::QtWarningMsg; - default: - return QtMsgType::QtFatalMsg; // wrong but we don't care - } -} - -static bool parseLine(const QString &line, FilterRuleSpec *filterRule) -{ - const QStringList parts = line.split('='); - if (parts.size() != 2) - return false; - - const QString category = parts.at(0); - static const QRegularExpression regex("^(.+?)(\\.(debug|info|warning|critical))?$"); - const QRegularExpressionMatch match = regex.match(category); - if (!match.hasMatch()) - return false; - - const QString categoryName = match.captured(1); - if (categoryName.size() > 2) { - if (categoryName.mid(1, categoryName.size() - 2).contains('*')) - return false; - } else if (categoryName.size() == 2) { - if (categoryName.count('*') == 2) - return false; - } - filterRule->category = categoryName; - - if (match.capturedLength(2) == 0) - filterRule->level = std::nullopt; - else - filterRule->level = std::make_optional(parseLevel(match.captured(2).mid(1))); - - const QString enabled = parts.at(1); - if (enabled == "true" || enabled == "false") { - filterRule->enabled = (enabled == "true"); - return true; - } - return false; -} - -static QList<FilterRuleSpec> fetchOriginalRules() -{ - QList<FilterRuleSpec> rules; - - auto appendRulesFromFile = [&rules](const QString &fileName) { - QSettings iniSettings(fileName, QSettings::IniFormat); - iniSettings.beginGroup("Rules"); - const QStringList keys = iniSettings.allKeys(); - for (const QString &key : keys) { - const QString value = iniSettings.value(key).toString(); - FilterRuleSpec filterRule; - if (parseLine(key + "=" + value, &filterRule)) - rules.append(filterRule); - } - iniSettings.endGroup(); - }; - - Utils::FilePath iniFile = Utils::FilePath::fromString( - QLibraryInfo::path(QLibraryInfo::DataPath)).pathAppended("qtlogging.ini"); - if (iniFile.exists()) - appendRulesFromFile(iniFile.toString()); - - const QString qtProjectString = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, - "QtProject/qtlogging.ini"); - if (!qtProjectString.isEmpty()) - appendRulesFromFile(qtProjectString); - - iniFile = Utils::FilePath::fromString(qtcEnvironmentVariable("QT_LOGGING_CONF")); - if (iniFile.exists()) - appendRulesFromFile(iniFile.toString()); - - if (qtcEnvironmentVariableIsSet("QT_LOGGING_RULES")) { - const QStringList rulesStrings = qtcEnvironmentVariable("QT_LOGGING_RULES").split(';'); - for (const QString &rule : rulesStrings) { - FilterRuleSpec filterRule; - if (parseLine(rule, &filterRule)) - rules.append(filterRule); - } - } - return rules; -} - -LoggingViewManager::LoggingViewManager(QObject *parent) - : QObject(parent) - , m_originalLoggingRules(qtcEnvironmentVariable("QT_LOGGING_RULES")) -{ - qRegisterMetaType<Core::Internal::LoggingCategoryEntry>(); - s_instance = this; - s_originalMessageHandler = qInstallMessageHandler(logMessageHandler); - m_enabled = true; - m_originalRules = fetchOriginalRules(); - prefillCategories(); - QLoggingCategory::setFilterRules("*=true"); -} - -LoggingViewManager::~LoggingViewManager() -{ - m_enabled = false; - qInstallMessageHandler(s_originalMessageHandler); - s_originalMessageHandler = nullptr; - qputenv("QT_LOGGING_RULES", m_originalLoggingRules.toLocal8Bit()); - QLoggingCategory::setFilterRules("*=false"); - resetFilterRules(); - s_instance = nullptr; -} - -LoggingViewManager *LoggingViewManager::instance() -{ - return s_instance; -} - -void LoggingViewManager::logMessageHandler(QtMsgType type, const QMessageLogContext &context, - const QString &mssg) -{ - if (!s_instance->m_enabled) { - if (s_instance->enabledInOriginalRules(context, type)) - s_originalMessageHandler(type, context, mssg); - return; - } - - if (!context.category) { - s_originalMessageHandler(type, context, mssg); - return; - } - - const QString category = QString::fromLocal8Bit(context.category); - auto it = s_instance->m_categories.find(category); - if (it == s_instance->m_categories.end()) { - if (!s_instance->m_listQtInternal && category.startsWith("qt.")) - return; - LoggingCategoryEntry entry; - entry.level = QtMsgType::QtDebugMsg; - entry.enabled = (category == "default") || s_instance->enabledInOriginalRules(context, type); - it = s_instance->m_categories.insert(category, entry); - emit s_instance->foundNewCategory(category, entry); - } - - const LoggingCategoryEntry entry = it.value(); - if (entry.enabled && enabled(type, entry.level)) { - const QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz"); - emit s_instance->receivedLog(timestamp, category, - LoggingViewManager::messageTypeToString(type), mssg); - } -} - -bool LoggingViewManager::isCategoryEnabled(const QString &category) -{ - auto entry = m_categories.find(category); - if (entry == m_categories.end()) // shall not happen - paranoia - return false; - - return entry.value().enabled; -} - -void LoggingViewManager::setCategoryEnabled(const QString &category, bool enabled) -{ - auto entry = m_categories.find(category); - if (entry == m_categories.end()) // shall not happen - paranoia - return; - - entry->enabled = enabled; -} - -void LoggingViewManager::setLogLevel(const QString &category, QtMsgType type) -{ - auto entry = m_categories.find(category); - if (entry == m_categories.end()) // shall not happen - paranoia - return; - - entry->level = type; -} - -void LoggingViewManager::setListQtInternal(bool listQtInternal) -{ - m_listQtInternal = listQtInternal; -} - -void LoggingViewManager::appendOrUpdate(const QString &category, const LoggingCategoryEntry &entry) -{ - auto it = m_categories.find(category); - bool append = it == m_categories.end(); - m_categories.insert(category, entry); - if (append) - emit foundNewCategory(category, entry); - else - emit updatedCategory(category, entry); -} - -/* - * Does not check categories for being present, will perform early exit if m_categories is not empty - */ -void LoggingViewManager::prefillCategories() -{ - if (!m_categories.isEmpty()) - return; - - for (int i = 0, end = m_originalRules.size(); i < end; ++i) { - const FilterRuleSpec &rule = m_originalRules.at(i); - if (rule.category.startsWith('*') || rule.category.endsWith('*')) - continue; - - bool enabled = rule.enabled; - // check following rules whether they might overwrite - for (int j = i + 1; j < end; ++j) { - const FilterRuleSpec &secondRule = m_originalRules.at(j); - const QRegularExpression regex( - QRegularExpression::wildcardToRegularExpression(secondRule.category)); - if (!regex.match(rule.category).hasMatch()) - continue; - - if (secondRule.level.has_value() && rule.level != secondRule.level) - continue; - - enabled = secondRule.enabled; - } - LoggingCategoryEntry entry; - entry.level = rule.level.value_or(QtMsgType::QtInfoMsg); - entry.enabled = enabled; - m_categories.insert(rule.category, entry); - } -} - -void LoggingViewManager::resetFilterRules() -{ - for (const FilterRuleSpec &rule : std::as_const(m_originalRules)) { - const QString level = rule.level.has_value() ? '.' + levelToString(rule.level.value()) - : QString(); - const QString ruleString = rule.category + level + '=' + (rule.enabled ? "true" : "false"); - QLoggingCategory::setFilterRules(ruleString); - } -} - -bool LoggingViewManager::enabledInOriginalRules(const QMessageLogContext &context, QtMsgType type) -{ - if (!context.category) - return false; - const QString category = QString::fromUtf8(context.category); - bool result = false; - for (const FilterRuleSpec &rule : std::as_const(m_originalRules)) { - const QRegularExpression regex( - QRegularExpression::wildcardToRegularExpression(rule.category)); - if (regex.match(category).hasMatch()) { - if (rule.level.has_value()) { - if (rule.level.value() == type) - result = rule.enabled; - } else { - result = rule.enabled; - } - } - } - return result; -} - -} // namespace Internal -} // namespace Core diff --git a/src/plugins/coreplugin/loggingmanager.h b/src/plugins/coreplugin/loggingmanager.h deleted file mode 100644 index 1b8830c0aa1..00000000000 --- a/src/plugins/coreplugin/loggingmanager.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QColor> -#include <QLoggingCategory> -#include <QMap> -#include <QObject> - -#include <optional> - -namespace Core { -namespace Internal { - -struct FilterRuleSpec -{ - QString category; - std::optional<QtMsgType> level; - bool enabled; -}; - -class LoggingCategoryEntry -{ -public: - QtMsgType level = QtDebugMsg; - bool enabled = false; - QColor color; -}; - -class LoggingViewManager : public QObject -{ - Q_OBJECT -public: - static inline QString messageTypeToString(QtMsgType type) - { - switch (type) { - case QtDebugMsg: return {"Debug"}; - case QtInfoMsg: return {"Info"}; - case QtCriticalMsg: return {"Critical"}; - case QtWarningMsg: return {"Warning"}; - case QtFatalMsg: return {"Fatal"}; - default: return {"Unknown"}; - } - } - - static inline QtMsgType messageTypeFromString(const QString &type) - { - if (type.isEmpty()) - return QtDebugMsg; - - // shortcut - only handle expected - switch (type.at(0).toLatin1()) { - case 'I': - return QtInfoMsg; - case 'C': - return QtCriticalMsg; - case 'W': - return QtWarningMsg; - case 'D': - default: - return QtDebugMsg; - } - } - - explicit LoggingViewManager(QObject *parent = nullptr); - ~LoggingViewManager(); - - static LoggingViewManager *instance(); - - static inline bool enabled(QtMsgType current, QtMsgType stored) - { - if (stored == QtMsgType::QtInfoMsg) - return true; - if (current == stored) - return true; - if (stored == QtMsgType::QtDebugMsg) - return current != QtMsgType::QtInfoMsg; - if (stored == QtMsgType::QtWarningMsg) - return current == QtMsgType::QtCriticalMsg || current == QtMsgType::QtFatalMsg; - if (stored == QtMsgType::QtCriticalMsg) - return current == QtMsgType::QtFatalMsg; - return false; - } - - static void logMessageHandler(QtMsgType type, const QMessageLogContext &context, - const QString &mssg); - - void setEnabled(bool enabled) { m_enabled = enabled; } - bool isEnabled() const { return m_enabled; } - bool isCategoryEnabled(const QString &category); - void setCategoryEnabled(const QString &category, bool enabled); - void setLogLevel(const QString &category, QtMsgType type); - void setListQtInternal(bool listQtInternal); - QList<FilterRuleSpec> originalRules() const { return m_originalRules; } - - QMap<QString, LoggingCategoryEntry> categories() const { return m_categories; } - void appendOrUpdate(const QString &category, const LoggingCategoryEntry &entry); - -signals: - void receivedLog(const QString ×tamp, const QString &type, const QString &category, - const QString &msg); - void foundNewCategory(const QString &category, const LoggingCategoryEntry &entry); - void updatedCategory(const QString &category, const LoggingCategoryEntry &entry); - -private: - void prefillCategories(); - void resetFilterRules(); - bool enabledInOriginalRules(const QMessageLogContext &context, QtMsgType type); - - QMap<QString, LoggingCategoryEntry> m_categories; - const QString m_originalLoggingRules; - QList<FilterRuleSpec> m_originalRules; - bool m_enabled = false; - bool m_listQtInternal = false; -}; - -} // namespace Internal -} // namespace Core - -Q_DECLARE_METATYPE(Core::Internal::LoggingCategoryEntry) diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp index 742e65a34c5..5e83c76df3e 100644 --- a/src/plugins/coreplugin/loggingviewer.cpp +++ b/src/plugins/coreplugin/loggingviewer.cpp @@ -3,26 +3,24 @@ #include "loggingviewer.h" -#include "actionmanager/actionmanager.h" #include "coreicons.h" #include "coreplugintr.h" #include "icore.h" -#include "loggingmanager.h" -#include <utils/algorithm.h> +#include <utils/async.h> #include <utils/basetreeview.h> +#include <utils/fancylineedit.h> #include <utils/fileutils.h> +#include <utils/layoutbuilder.h> #include <utils/listmodel.h> #include <utils/qtcassert.h> +#include <utils/stringutils.h> #include <utils/theme/theme.h> #include <utils/utilsicons.h> #include <QAction> -#include <QClipboard> #include <QColorDialog> -#include <QComboBox> #include <QDialog> -#include <QGuiApplication> #include <QHBoxLayout> #include <QJsonArray> #include <QJsonDocument> @@ -31,138 +29,368 @@ #include <QMenu> #include <QMessageBox> #include <QPushButton> -#include <QRegularExpression> -#include <QScopeGuard> #include <QSortFilterProxyModel> -#include <QStyledItemDelegate> +#include <QSplitter> #include <QToolButton> -#include <QTreeView> #include <QVBoxLayout> +namespace Core::Internal { -namespace Core { -namespace Internal { +static QColor colorForCategory(const QString &category); +void setCategoryColor(const QString &category, const QColor &color); -class LoggingCategoryItem +QHash<QString, QColor> s_categoryColor; + +static inline QString messageTypeToString(QtMsgType type) { -public: - QString name; - LoggingCategoryEntry entry; + switch (type) { + case QtDebugMsg: + return {"Debug"}; + case QtInfoMsg: + return {"Info"}; + case QtCriticalMsg: + return {"Critical"}; + case QtWarningMsg: + return {"Warning"}; + case QtFatalMsg: + return {"Fatal"}; + default: + return {"Unknown"}; + } +} - static LoggingCategoryItem fromJson(const QJsonObject &object, bool *ok); +class LogCategoryRegistry : public QObject +{ + Q_OBJECT +public: + static LogCategoryRegistry &instance() + { + static LogCategoryRegistry s_instance; + return s_instance; + } + + static void filter(QLoggingCategory *category) + { + if (s_oldFilter) + s_oldFilter(category); + + LogCategoryRegistry::instance().onFilter(category); + } + + void start() + { + if (m_started) + return; + + m_started = true; + s_oldFilter = QLoggingCategory::installFilter(&LogCategoryRegistry::filter); + } + + QList<QLoggingCategory *> categories() { return m_categories; } + +signals: + void newLogCategory(QLoggingCategory *category); + +private: + LogCategoryRegistry() = default; + + void onFilter(QLoggingCategory *category) + { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod( + this, [category, this] { onFilter(category); }, Qt::QueuedConnection); + return; + } + + if (!m_categories.contains(category)) { + m_categories.append(category); + emit newLogCategory(category); + } + } + +private: + static QLoggingCategory::CategoryFilter s_oldFilter; + + QList<QLoggingCategory *> m_categories; + bool m_started{false}; }; -LoggingCategoryItem LoggingCategoryItem::fromJson(const QJsonObject &object, bool *ok) +QLoggingCategory::CategoryFilter LogCategoryRegistry::s_oldFilter = nullptr; + +struct SavedEntry { - if (!object.contains("name")) { - *ok = false; - return {}; + QColor color; + QString name; + QtMsgType level{QtFatalMsg}; + std::optional<std::array<bool, 5>> levels; + + static Utils::expected_str<SavedEntry> fromJson(const QJsonObject &obj) + { + if (!obj.contains("name")) + return Utils::make_unexpected(Tr::tr("Entry is missing a logging category name.")); + + SavedEntry result; + result.name = obj.value("name").toString(); + + if (!obj.contains("entry")) + return Utils::make_unexpected(Tr::tr("Entry is missing data.")); + + auto entry = obj.value("entry").toObject(); + if (entry.contains("color")) + result.color = QColor(entry.value("color").toString()); + + if (entry.contains("level")) { + int lvl = entry.value("level").toInt(0); + if (lvl < QtDebugMsg || lvl > QtInfoMsg) + return Utils::make_unexpected(Tr::tr("Invalid level: %1").arg(lvl)); + result.level = static_cast<QtMsgType>(lvl); + } + + if (entry.contains("levels")) { + QVariantMap map = entry.value("levels").toVariant().toMap(); + std::array<bool, 5> levels{ + map.contains("Debug") && map["Debug"].toBool(), + map.contains("Warning") && map["Warning"].toBool(), + map.contains("Critical") && map["Critical"].toBool(), + true, + map.contains("Info") && map["Info"].toBool(), + }; + result.levels = levels; + } + + return result; } - const QJsonValue entryVal = object.value("entry"); - if (entryVal.isUndefined()) { - *ok = false; - return {}; - } - const QJsonObject entryObj = entryVal.toObject(); - if (!entryObj.contains("level")) { - *ok = false; - return {}; +}; + +class LoggingCategoryEntry +{ +public: + LoggingCategoryEntry(const QString &name) + : m_name(name) + {} + LoggingCategoryEntry(QLoggingCategory *category) + : m_name(QString::fromUtf8(category->categoryName())) + { + setLogCategory(category); } - LoggingCategoryEntry entry; - entry.level = QtMsgType(entryObj.value("level").toInt()); - entry.enabled = true; - if (entryObj.contains("color")) - entry.color = QColor(entryObj.value("color").toString()); - LoggingCategoryItem item {object.value("name").toString(), entry}; - *ok = true; - return item; -} + LoggingCategoryEntry(const SavedEntry &savedEntry) + : m_name(savedEntry.name) + { + m_saved = savedEntry.levels; + m_color = savedEntry.color; + if (!m_saved) { + m_saved = std::array<bool, 5>(); + for (int i = QtDebugMsg; i <= QtInfoMsg; ++i) { + (*m_saved)[i] = savedEntry.level <= i; + } + } + } + + QString name() const { return m_name; } + QColor color() const { return m_color; } + void setColor(const QColor &c) { m_color = c; } + + void setUseOriginal(bool useOriginal) + { + if (!m_useOriginal && m_category && m_originalSettings) { + m_saved = std::array<bool, 5>{}; + + for (int i = QtDebugMsg; i < QtInfoMsg; i++) { + (*m_saved)[i] = m_category->isEnabled(static_cast<QtMsgType>(i)); + m_category->setEnabled(static_cast<QtMsgType>(i), (*m_originalSettings)[i]); + } + + } else if (!useOriginal && m_useOriginal && m_saved && m_category) { + for (int i = QtDebugMsg; i < QtInfoMsg; i++) + m_category->setEnabled(static_cast<QtMsgType>(i), (*m_saved)[i]); + } + m_useOriginal = useOriginal; + } + + bool isEnabled(QtMsgType msgType) const + { + if (m_category) + return m_category->isEnabled(msgType); + if (m_saved) + return (*m_saved)[msgType]; + return false; + } + + bool isEnabledOriginally(QtMsgType msgType) const + { + if (m_originalSettings) + return (*m_originalSettings)[msgType]; + return isEnabled(msgType); + } + + void setEnabled(QtMsgType msgType, bool isEnabled) + { + QTC_ASSERT(!m_useOriginal, return); + + if (m_category) + m_category->setEnabled(msgType, isEnabled); + + if (m_saved) + (*m_saved)[msgType] = isEnabled; + } + + void setSaved(const SavedEntry &entry) + { + QTC_ASSERT(entry.name == name(), return); + + m_saved = entry.levels; + m_color = entry.color; + if (!m_saved) { + m_saved = std::array<bool, 5>(); + for (int i = QtDebugMsg; i <= QtInfoMsg; ++i) { + (*m_saved)[i] = entry.level <= i; + } + } + if (m_category) + setLogCategory(m_category); + } + + void setLogCategory(QLoggingCategory *category) + { + QTC_ASSERT(QString::fromUtf8(category->categoryName()) == m_name, return); + + m_category = category; + if (!m_originalSettings) { + m_originalSettings = { + category->isDebugEnabled(), + category->isWarningEnabled(), + category->isCriticalEnabled(), + true, // always enable fatal + category->isInfoEnabled(), + }; + } + + if (m_saved && !m_useOriginal) { + m_category->setEnabled(QtDebugMsg, m_saved->at(0)); + m_category->setEnabled(QtWarningMsg, m_saved->at(1)); + m_category->setEnabled(QtCriticalMsg, m_saved->at(2)); + m_category->setEnabled(QtInfoMsg, m_saved->at(4)); + } + } + + bool isDebugEnabled() const { return isEnabled(QtDebugMsg); } + bool isWarningEnabled() const { return isEnabled(QtWarningMsg); } + bool isCriticalEnabled() const { return isEnabled(QtCriticalMsg); } + bool isInfoEnabled() const { return isEnabled(QtInfoMsg); } + +private: + QString m_name; + QLoggingCategory *m_category{nullptr}; + std::optional<std::array<bool, 5>> m_originalSettings; + std::optional<std::array<bool, 5>> m_saved; + QColor m_color; + bool m_useOriginal{false}; +}; class LoggingCategoryModel : public QAbstractListModel { Q_OBJECT public: - LoggingCategoryModel() = default; - ~LoggingCategoryModel() override; + LoggingCategoryModel(QObject *parent) + : QAbstractListModel(parent) + { + auto newCategory = [this](QLoggingCategory *category) { + QString name = QString::fromUtf8(category->categoryName()); + auto itExists = std::find_if(m_categories.begin(), + m_categories.end(), + [name](const auto &cat) { return name == cat.name(); }); - bool append(const QString &category, const LoggingCategoryEntry &entry = {}); - bool update(const QString &category, const LoggingCategoryEntry &entry); - int columnCount(const QModelIndex &) const final { return 3; } - int rowCount(const QModelIndex & = QModelIndex()) const final { return m_categories.count(); } + if (itExists != m_categories.end()) { + itExists->setLogCategory(category); + } else { + LoggingCategoryEntry entry(category); + append(entry); + } + }; + + for (QLoggingCategory *cat : LogCategoryRegistry::instance().categories()) + newCategory(cat); + + connect(&LogCategoryRegistry::instance(), + &LogCategoryRegistry::newLogCategory, + this, + newCategory); + + LogCategoryRegistry::instance().start(); + }; + + ~LoggingCategoryModel() override; + enum Column { Color, Name, Debug, Warning, Critical, Fatal, Info }; + + enum Role { OriginalStateRole = Qt::UserRole + 1 }; + + void append(const LoggingCategoryEntry &entry); + int columnCount(const QModelIndex &) const final { return 7; } + int rowCount(const QModelIndex & = QModelIndex()) const final { return m_categories.size(); } QVariant data(const QModelIndex &index, int role) const final; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) final; Qt::ItemFlags flags(const QModelIndex &index) const final; - QVariant headerData(int section, Qt::Orientation orientation, + QVariant headerData(int section, + Qt::Orientation orientation, int role = Qt::DisplayRole) const final; - void reset(); - void setFromManager(LoggingViewManager *manager); - QList<LoggingCategoryItem> enabledCategories() const; - void disableAll(); -signals: - void categoryChanged(const QString &category, bool enabled); - void colorChanged(const QString &category, const QColor &color); - void logLevelChanged(const QString &category, QtMsgType logLevel); + void saveEnabledCategoryPreset() const; + void loadAndUpdateFromPreset(); + + void setUseOriginal(bool useOriginal) + { + if (useOriginal != m_useOriginal) { + beginResetModel(); + for (auto &entry : m_categories) + entry.setUseOriginal(useOriginal); + + m_useOriginal = useOriginal; + endResetModel(); + } + } + + const QList<LoggingCategoryEntry> &categories() const { return m_categories; } private: - QList<LoggingCategoryItem *> m_categories; + QList<LoggingCategoryEntry> m_categories; + bool m_useOriginal{false}; }; -LoggingCategoryModel::~LoggingCategoryModel() -{ - reset(); -} +LoggingCategoryModel::~LoggingCategoryModel() {} -bool LoggingCategoryModel::append(const QString &category, const LoggingCategoryEntry &entry) +void LoggingCategoryModel::append(const LoggingCategoryEntry &entry) { - // no check? - beginInsertRows(QModelIndex(), m_categories.size(), m_categories.size()); - m_categories.append(new LoggingCategoryItem{category, entry}); + beginInsertRows(QModelIndex(), m_categories.size(), m_categories.size() + 1); + m_categories.push_back(entry); endInsertRows(); - return true; -} - -bool LoggingCategoryModel::update(const QString &category, const LoggingCategoryEntry &entry) -{ - if (m_categories.size() == 0) // should not happen - return false; - - int row = 0; - for (int end = m_categories.size(); row < end; ++row) { - if (m_categories.at(row)->name == category) - break; - } - if (row == m_categories.size()) // should not happen - return false; - - setData(index(row, 0), Qt::Checked, Qt::CheckStateRole); - setData(index(row, 1), LoggingViewManager::messageTypeToString(entry.level), Qt::EditRole); - setData(index(row, 2), entry.color, Qt::DecorationRole); - return true; } QVariant LoggingCategoryModel::data(const QModelIndex &index, int role) const { - static const QColor defaultColor = Utils::creatorTheme()->palette().text().color(); if (!index.isValid()) return {}; - if (role == Qt::DisplayRole) { - if (index.column() == 0) - return m_categories.at(index.row())->name; - if (index.column() == 1) { - return LoggingViewManager::messageTypeToString( - m_categories.at(index.row())->entry.level); - } - } - if (role == Qt::DecorationRole && index.column() == 2) { - const QColor color = m_categories.at(index.row())->entry.color; + + if (index.column() == Column::Name && role == Qt::DisplayRole) { + return m_categories.at(index.row()).name(); + } else if (role == Qt::DecorationRole && index.column() == Column::Color) { + const QColor color = m_categories.at(index.row()).color(); if (color.isValid()) return color; + + static const QColor defaultColor = Utils::creatorTheme()->palette().text().color(); return defaultColor; - } - if (role == Qt::CheckStateRole && index.column() == 0) { - const LoggingCategoryEntry entry = m_categories.at(index.row())->entry; - return entry.enabled ? Qt::Checked : Qt::Unchecked; + } else if (index.column() >= Column::Debug && index.column() <= Column::Info) { + if (role == Qt::CheckStateRole) { + const LoggingCategoryEntry &entry = m_categories.at(index.row()); + const bool isEnabled = entry.isEnabled( + static_cast<QtMsgType>(index.column() - Column::Debug)); + return isEnabled ? Qt::Checked : Qt::Unchecked; + } else if (role == OriginalStateRole) { + const LoggingCategoryEntry &entry = m_categories.at(index.row()); + return entry.isEnabledOriginally(static_cast<QtMsgType>(index.column() - Column::Debug)) + ? Qt::Checked + : Qt::Unchecked; + } } return {}; } @@ -172,27 +400,28 @@ bool LoggingCategoryModel::setData(const QModelIndex &index, const QVariant &val if (!index.isValid()) return false; - if (role == Qt::CheckStateRole && index.column() == 0) { - LoggingCategoryItem *item = m_categories.at(index.row()); - const Qt::CheckState current = item->entry.enabled ? Qt::Checked : Qt::Unchecked; + if (role == Qt::CheckStateRole && index.column() >= Column::Debug + && index.column() <= Column::Info) { + QtMsgType msgType = static_cast<QtMsgType>(index.column() - Column::Debug); + auto &entry = m_categories[index.row()]; + bool isEnabled = entry.isEnabled(msgType); + + const Qt::CheckState current = isEnabled ? Qt::Checked : Qt::Unchecked; + if (current != value.toInt()) { - item->entry.enabled = !item->entry.enabled; - emit categoryChanged(item->name, item->entry.enabled); + entry.setEnabled(msgType, value.toInt() == Qt::Checked); return true; } - } else if (role == Qt::DecorationRole && index.column() == 2) { - LoggingCategoryItem *item = m_categories.at(index.row()); + } else if (role == Qt::DecorationRole && index.column() == Column::Color) { + auto &category = m_categories[index.row()]; + QColor currentColor = category.color(); QColor color = value.value<QColor>(); - if (color.isValid() && color != item->entry.color) { - item->entry.color = color; - emit colorChanged(item->name, color); + if (color.isValid() && color != currentColor) { + category.setColor(color); + setCategoryColor(category.name(), color); + emit dataChanged(index, index, {Qt::DisplayRole}); return true; } - } else if (role == Qt::EditRole && index.column() == 1) { - LoggingCategoryItem *item = m_categories.at(index.row()); - item->entry.level = LoggingViewManager::messageTypeFromString(value.toString()); - emit logLevelChanged(item->name, item->entry.level); - return true; } return false; @@ -203,111 +432,47 @@ Qt::ItemFlags LoggingCategoryModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - // ItemIsEnabled should depend on availability (Qt logging enabled?) - if (index.column() == 0) - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - if (index.column() == 1) - return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (index.column() == LoggingCategoryModel::Column::Fatal) + return Qt::NoItemFlags; + + if (index.column() == Column::Name || index.column() == Column::Color) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if (m_useOriginal) + return Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; } QVariant LoggingCategoryModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section >= 0 && section < 3) { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal && section >= 0 && section < 8) { switch (section) { - case 0: return Tr::tr("Category"); - case 1: return Tr::tr("Type"); - case 2: return Tr::tr("Color"); + case Column::Name: + return Tr::tr("Category"); + case Column::Color: + return Tr::tr("Color"); + case Column::Debug: + return Tr::tr("Debug"); + case Column::Warning: + return Tr::tr("Warning"); + case Column::Critical: + return Tr::tr("Critical"); + case Column::Fatal: + return Tr::tr("Fatal"); + case Column::Info: + return Tr::tr("Info"); } } return {}; } -void LoggingCategoryModel::reset() -{ - beginResetModel(); - qDeleteAll(m_categories); - m_categories.clear(); - endResetModel(); -} - -void LoggingCategoryModel::setFromManager(LoggingViewManager *manager) -{ - beginResetModel(); - qDeleteAll(m_categories); - m_categories.clear(); - const QMap<QString, LoggingCategoryEntry> categories = manager->categories(); - auto it = categories.begin(); - for (auto end = categories.end() ; it != end; ++it) - m_categories.append(new LoggingCategoryItem{it.key(), it.value()}); - endResetModel(); -} - -QList<LoggingCategoryItem> LoggingCategoryModel::enabledCategories() const -{ - QList<LoggingCategoryItem> result; - for (auto item : m_categories) { - if (item->entry.enabled) - result.append({item->name, item->entry}); - } - return result; -} - -void LoggingCategoryModel::disableAll() -{ - for (int row = 0, end = m_categories.count(); row < end; ++row) - setData(index(row, 0), Qt::Unchecked, Qt::CheckStateRole); -} - -class LoggingLevelDelegate : public QStyledItemDelegate -{ -public: - explicit LoggingLevelDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} - ~LoggingLevelDelegate() = default; - -protected: - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - void setEditorData(QWidget *editor, const QModelIndex &index) const override; - void setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const override; -}; - -QWidget *LoggingLevelDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, - const QModelIndex &index) const -{ - if (!index.isValid() || index.column() != 1) - return nullptr; - QComboBox *combo = new QComboBox(parent); - combo->addItems({ {"Critical"}, {"Warning"}, {"Debug"}, {"Info"} }); - return combo; -} - -void LoggingLevelDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - QComboBox *combo = qobject_cast<QComboBox *>(editor); - if (!combo) - return; - - const int i = combo->findText(index.data().toString()); - if (i >= 0) - combo->setCurrentIndex(i); -} - -void LoggingLevelDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - QComboBox *combo = qobject_cast<QComboBox *>(editor); - if (combo) - model->setData(index, combo->currentText()); -} - class LogEntry { public: QString timestamp; - QString category; QString type; + QString category; QString message; QString outputLine(bool printTimestamp, bool printType) const @@ -325,95 +490,156 @@ public: } }; +class LoggingEntryModel : public Utils::ListModel<LogEntry> +{ +public: + ~LoggingEntryModel() { qInstallMessageHandler(m_originalMessageHandler); } + + static void logMessageHandler(QtMsgType type, + const QMessageLogContext &context, + const QString &mssg) + { + instance().msgHandler(type, context, mssg); + } + + static QVariant logEntryDataAccessor(const LogEntry &entry, int column, int role) + { + if (column >= 0 && column <= 3 && (role == Qt::DisplayRole || role == Qt::ToolTipRole)) { + switch (column) { + case 0: + return entry.timestamp; + case 1: + return entry.category; + case 2: + return entry.type; + case 3: { + if (role == Qt::ToolTipRole) + return entry.message; + return entry.message.left(1000); + } + } + } + if (role == Qt::TextAlignmentRole) + return Qt::AlignTop; + if (column == 1 && role == Qt::ForegroundRole) + return colorForCategory(entry.category); + return {}; + } + + static LoggingEntryModel &instance() + { + static LoggingEntryModel model; + return model; + } + + void setEnabled(bool enabled) { m_enabled = enabled; } + +private: + LoggingEntryModel() + { + setHeader({Tr::tr("Timestamp"), Tr::tr("Category"), Tr::tr("Type"), Tr::tr("Message")}); + setDataAccessor(&logEntryDataAccessor); + + m_originalMessageHandler = qInstallMessageHandler(logMessageHandler); + } + + void msgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) + { + if (!m_enabled) { + m_originalMessageHandler(type, context, msg); + return; + } + + if (!context.category) { + m_originalMessageHandler(type, context, msg); + return; + } + + const QString category = QString::fromLocal8Bit(context.category); + + const QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz"); + + if (rowCount() >= 1000000) // limit log to 1000000 items + destroyItem(itemForIndex(index(0, 0))); + + appendItem(LogEntry{timestamp, messageTypeToString(type), category, msg}); + } + +private: + QtMessageHandler m_originalMessageHandler{nullptr}; + bool m_enabled{true}; +}; + class LoggingViewManagerWidget : public QDialog { public: - explicit LoggingViewManagerWidget(QWidget *parent); - ~LoggingViewManagerWidget() + ~LoggingViewManagerWidget() { LoggingEntryModel::instance().setEnabled(false); } + + static LoggingViewManagerWidget *instance() { - setEnabled(false); - delete m_manager; + static QPointer<LoggingViewManagerWidget> instance = new LoggingViewManagerWidget( + Core::ICore::dialogParent()); + return instance; } - static QColor colorForCategory(const QString &category); +protected: + void showEvent(QShowEvent *) override + { + if (!m_stopLog->isChecked()) + m_categoryModel->setUseOriginal(false); + + LoggingEntryModel::instance().setEnabled(!m_stopLog->isChecked()); + } + void hideEvent(QHideEvent *) override + { + m_categoryModel->setUseOriginal(true); + LoggingEntryModel::instance().setEnabled(false); + } + +private: + explicit LoggingViewManagerWidget(QWidget *parent); + private: void showLogViewContextMenu(const QPoint &pos) const; void showLogCategoryContextMenu(const QPoint &pos) const; void saveLoggingsToFile() const; - void saveEnabledCategoryPreset() const; - void loadAndUpdateFromPreset(); - LoggingViewManager *m_manager = nullptr; - void setCategoryColor(const QString &category, const QColor &color); - // should category model be owned directly by the manager? or is this duplication of - // categories in manager and widget beneficial? + QSortFilterProxyModel *m_sortFilterModel = nullptr; LoggingCategoryModel *m_categoryModel = nullptr; Utils::BaseTreeView *m_logView = nullptr; Utils::BaseTreeView *m_categoryView = nullptr; - Utils::ListModel<LogEntry> *m_logModel = nullptr; QToolButton *m_timestamps = nullptr; QToolButton *m_messageTypes = nullptr; - static QHash<QString, QColor> m_categoryColor; + QToolButton *m_stopLog = nullptr; }; -QHash<QString, QColor> LoggingViewManagerWidget::m_categoryColor; - -static QVariant logEntryDataAccessor(const LogEntry &entry, int column, int role) -{ - if (column >= 0 && column <= 3 && (role == Qt::DisplayRole || role == Qt::ToolTipRole)) { - switch (column) { - case 0: return entry.timestamp; - case 1: return entry.category; - case 2: return entry.type; - case 3: { - if (role == Qt::ToolTipRole) - return entry.message; - return entry.message.left(1000); - } - } - } - if (role == Qt::TextAlignmentRole) - return Qt::AlignTop; - if (column == 1 && role == Qt::ForegroundRole) - return LoggingViewManagerWidget::colorForCategory(entry.category); - return {}; -} - LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent) : QDialog(parent) - , m_manager(new LoggingViewManager) { setWindowTitle(Tr::tr("Logging Category Viewer")); - setModal(false); - - auto mainLayout = new QVBoxLayout; - - auto buttonsLayout = new QHBoxLayout; - buttonsLayout->setSpacing(0); - // add further buttons.. auto save = new QToolButton; save->setIcon(Utils::Icons::SAVEFILE.icon()); save->setToolTip(Tr::tr("Save Log")); - buttonsLayout->addWidget(save); + auto clean = new QToolButton; clean->setIcon(Utils::Icons::CLEAN.icon()); clean->setToolTip(Tr::tr("Clear")); - buttonsLayout->addWidget(clean); - auto stop = new QToolButton; - stop->setIcon(Utils::Icons::STOP_SMALL.icon()); - stop->setToolTip(Tr::tr("Stop Logging")); - buttonsLayout->addWidget(stop); + + m_stopLog = new QToolButton; + m_stopLog->setIcon(Utils::Icons::STOP_SMALL.icon()); + m_stopLog->setToolTip(Tr::tr("Stop Logging")); + m_stopLog->setCheckable(true); + auto qtInternal = new QToolButton; qtInternal->setIcon(Core::Icons::QTLOGO.icon()); - qtInternal->setToolTip(Tr::tr("Toggle Qt Internal Logging")); - qtInternal->setCheckable(true); - qtInternal->setChecked(false); - buttonsLayout->addWidget(qtInternal); + qtInternal->setToolTip(Tr::tr("Filter Qt Internal Log Categories")); + qtInternal->setCheckable(false); + auto autoScroll = new QToolButton; autoScroll->setIcon(Utils::Icons::ARROW_DOWN.icon()); autoScroll->setToolTip(Tr::tr("Auto Scroll")); autoScroll->setCheckable(true); autoScroll->setChecked(true); - buttonsLayout->addWidget(autoScroll); + m_timestamps = new QToolButton; auto icon = Utils::Icon({{":/utils/images/stopwatch.png", Utils::Theme::PanelTextColorMid}}, Utils::Icon::Tint); @@ -421,7 +647,7 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent) m_timestamps->setToolTip(Tr::tr("Timestamps")); m_timestamps->setCheckable(true); m_timestamps->setChecked(true); - buttonsLayout->addWidget(m_timestamps); + m_messageTypes = new QToolButton; icon = Utils::Icon({{":/utils/images/message.png", Utils::Theme::PanelTextColorMid}}, Utils::Icon::Tint); @@ -429,18 +655,9 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent) m_messageTypes->setToolTip(Tr::tr("Message Types")); m_messageTypes->setCheckable(true); m_messageTypes->setChecked(false); - buttonsLayout->addWidget(m_messageTypes); - buttonsLayout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding)); - mainLayout->addLayout(buttonsLayout); - - auto horizontal = new QHBoxLayout; m_logView = new Utils::BaseTreeView; - m_logModel = new Utils::ListModel<LogEntry>; - m_logModel->setHeader({Tr::tr("Timestamp"), Tr::tr("Category"), Tr::tr("Type"), Tr::tr("Message")}); - m_logModel->setDataAccessor(&logEntryDataAccessor); - m_logView->setModel(m_logModel); - horizontal->addWidget(m_logView); + m_logView->setModel(&LoggingEntryModel::instance()); m_logView->setUniformRowHeights(true); m_logView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_logView->setFrameStyle(QFrame::Box); @@ -449,91 +666,154 @@ LoggingViewManagerWidget::LoggingViewManagerWidget(QWidget *parent) m_logView->setColumnHidden(2, true); m_logView->setContextMenuPolicy(Qt::CustomContextMenu); + m_categoryModel = new LoggingCategoryModel(this); + m_sortFilterModel = new QSortFilterProxyModel(m_categoryModel); + m_sortFilterModel->setSourceModel(m_categoryModel); + m_sortFilterModel->sort(LoggingCategoryModel::Column::Name); + m_sortFilterModel->setSortRole(Qt::DisplayRole); + m_sortFilterModel->setFilterKeyColumn(LoggingCategoryModel::Column::Name); + m_categoryView = new Utils::BaseTreeView; m_categoryView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_categoryView->setUniformRowHeights(true); m_categoryView->setFrameStyle(QFrame::Box); m_categoryView->setAttribute(Qt::WA_MacShowFocusRect, false); m_categoryView->setSelectionMode(QAbstractItemView::SingleSelection); m_categoryView->setContextMenuPolicy(Qt::CustomContextMenu); - m_categoryModel = new LoggingCategoryModel; - m_categoryModel->setFromManager(m_manager); - auto sortFilterModel = new QSortFilterProxyModel(this); - sortFilterModel->setSourceModel(m_categoryModel); - sortFilterModel->sort(0); - m_categoryView->setModel(sortFilterModel); - m_categoryView->setItemDelegateForColumn(1, new LoggingLevelDelegate(this)); - horizontal->addWidget(m_categoryView); - horizontal->setStretch(0, 5); - horizontal->setStretch(1, 3); + m_categoryView->setModel(m_sortFilterModel); + + for (int i = LoggingCategoryModel::Column::Color; i < LoggingCategoryModel::Column::Info; i++) + m_categoryView->resizeColumnToContents(i); + + auto filterEdit = new Utils::FancyLineEdit; + filterEdit->setHistoryCompleter("LogFilterCompletionHistory"); + filterEdit->setFiltering(true); + filterEdit->setPlaceholderText(Tr::tr("Filter categories by regular expression")); + filterEdit->setText("^(?!qt\\.).+"); + filterEdit->setValidationFunction( + [](const QString &input) { + return Utils::asyncRun([input]() -> Utils::expected_str<QString> { + QRegularExpression re(input); + if (re.isValid()) + return input; + + return Utils::make_unexpected( + Tr::tr("Invalid regular expression: %1").arg(re.errorString())); + }); + }); + + QSplitter *splitter{nullptr}; + + using namespace Layouting; + // clang-format off + Column { + Splitter { + bindTo(&splitter), + Column { + noMargin(), + Row { + spacing(0), + save, + clean, + m_stopLog, + autoScroll, + m_timestamps, + m_messageTypes, + st, + }, + m_logView + }, + Column { + noMargin(), + Row { + qtInternal, + filterEdit, + }, + m_categoryView, + } + } + }.attachTo(this); + // clang-format on + + splitter->setOrientation(Qt::Horizontal); - mainLayout->addLayout(horizontal); - setLayout(mainLayout); resize(800, 300); - connect(m_manager, &LoggingViewManager::receivedLog, - this, [this](const QString ×tamp, - const QString &type, - const QString &category, - const QString &msg) { - if (m_logModel->rowCount() >= 1000000) // limit log to 1000000 items - m_logModel->destroyItem(m_logModel->itemForIndex(m_logModel->index(0, 0))); - m_logModel->appendItem(LogEntry{timestamp, type, category, msg}); - }, Qt::QueuedConnection); - connect(m_logModel, &QAbstractItemModel::rowsInserted, this, [this, autoScroll] { + connect( + &LoggingEntryModel::instance(), + &LoggingEntryModel::rowsInserted, + this, + [this, autoScroll] { if (autoScroll->isChecked()) m_logView->scrollToBottom(); - }, Qt::QueuedConnection); - connect(m_manager, &LoggingViewManager::foundNewCategory, - m_categoryModel, &LoggingCategoryModel::append, Qt::QueuedConnection); - connect(m_manager, &LoggingViewManager::updatedCategory, - m_categoryModel, &LoggingCategoryModel::update, Qt::QueuedConnection); - connect(m_categoryModel, &LoggingCategoryModel::categoryChanged, - m_manager, &LoggingViewManager::setCategoryEnabled); - connect(m_categoryModel, &LoggingCategoryModel::colorChanged, - this, &LoggingViewManagerWidget::setCategoryColor); - connect(m_categoryModel, &LoggingCategoryModel::logLevelChanged, - m_manager, &LoggingViewManager::setLogLevel); - connect(m_categoryView, &Utils::BaseTreeView::activated, - this, [this, sortFilterModel](const QModelIndex &index) { - const QModelIndex modelIndex = sortFilterModel->mapToSource(index); - const QVariant value = m_categoryModel->data(modelIndex, Qt::DecorationRole); - if (!value.isValid()) - return; - const QColor original = value.value<QColor>(); - if (!original.isValid()) - return; - QColor changed = QColorDialog::getColor(original, this); - if (!changed.isValid()) - return; - if (original != changed) - m_categoryModel->setData(modelIndex, changed, Qt::DecorationRole); - }); - connect(save, &QToolButton::clicked, - this, &LoggingViewManagerWidget::saveLoggingsToFile); - connect(m_logView, &Utils::BaseTreeView::customContextMenuRequested, - this, &LoggingViewManagerWidget::showLogViewContextMenu); - connect(m_categoryView, &Utils::BaseTreeView::customContextMenuRequested, - this, &LoggingViewManagerWidget::showLogCategoryContextMenu); - connect(clean, &QToolButton::clicked, m_logModel, &Utils::ListModel<LogEntry>::clear); - connect(stop, &QToolButton::clicked, this, [this, stop] { - if (m_manager->isEnabled()) { - m_manager->setEnabled(false); - stop->setIcon(Utils::Icons::RUN_SMALL.icon()); - stop->setToolTip(Tr::tr("Start Logging")); + }, + Qt::QueuedConnection); + + connect(m_categoryView, + &QAbstractItemView::activated, + m_sortFilterModel, + [this](const QModelIndex &index) { + const QVariant value = m_sortFilterModel->data(index, Qt::DecorationRole); + if (!value.isValid()) + return; + const QColor original = value.value<QColor>(); + if (!original.isValid()) + return; + QColor changed = QColorDialog::getColor(original, this); + if (!changed.isValid()) + return; + if (original != changed) + m_sortFilterModel->setData(index, changed, Qt::DecorationRole); + }); + connect(save, &QToolButton::clicked, this, &LoggingViewManagerWidget::saveLoggingsToFile); + connect(m_logView, + &QAbstractItemView::customContextMenuRequested, + this, + &LoggingViewManagerWidget::showLogViewContextMenu); + connect(m_categoryView, + &QAbstractItemView::customContextMenuRequested, + this, + &LoggingViewManagerWidget::showLogCategoryContextMenu); + connect(clean, + &QToolButton::clicked, + &LoggingEntryModel::instance(), + &Utils::ListModel<LogEntry>::clear); + connect(m_stopLog, &QToolButton::toggled, this, [this](bool checked) { + LoggingEntryModel::instance().setEnabled(!checked); + + if (checked) { + m_stopLog->setIcon(Utils::Icons::RUN_SMALL.icon()); + m_stopLog->setToolTip(Tr::tr("Start Logging")); + m_categoryModel->setUseOriginal(true); } else { - m_manager->setEnabled(true); - stop->setIcon(Utils::Icons::STOP_SMALL.icon()); - stop->setToolTip(Tr::tr("Stop Logging")); + m_stopLog->setIcon(Utils::Icons::STOP_SMALL.icon()); + m_stopLog->setToolTip(Tr::tr("Stop Logging")); + m_categoryModel->setUseOriginal(false); } }); - connect(qtInternal, &QToolButton::toggled, m_manager, &LoggingViewManager::setListQtInternal); - connect(m_timestamps, &QToolButton::toggled, this, [this](bool checked){ + + m_sortFilterModel->setFilterRegularExpression("^(?!qt\\.).+"); + + connect(qtInternal, &QToolButton::clicked, filterEdit, [filterEdit] { + filterEdit->setText("^(?!qt\\.).+"); + }); + + connect(filterEdit, + &Utils::FancyLineEdit::textChanged, + m_sortFilterModel, + [this](const QString &f) { + QRegularExpression re(f); + if (re.isValid()) + m_sortFilterModel->setFilterRegularExpression(f); + }); + + connect(m_timestamps, &QToolButton::toggled, this, [this](bool checked) { m_logView->setColumnHidden(0, !checked); }); - connect(m_messageTypes, &QToolButton::toggled, this, [this](bool checked){ + connect(m_messageTypes, &QToolButton::toggled, this, [this](bool checked) { m_logView->setColumnHidden(2, !checked); }); + + ICore::registerWindow(this, Context("Qtc.LogViewer")); } void LoggingViewManagerWidget::showLogViewContextMenu(const QPoint &pos) const @@ -548,104 +828,193 @@ void LoggingViewManagerWidget::showLogViewContextMenu(const QPoint &pos) const QString copied; const bool useTS = m_timestamps->isChecked(); const bool useLL = m_messageTypes->isChecked(); - for (int row = 0, end = m_logModel->rowCount(); row < end; ++row) { + for (int row = 0, end = LoggingEntryModel::instance().rowCount(); row < end; ++row) { if (selectionModel->isRowSelected(row, QModelIndex())) - copied.append(m_logModel->dataAt(row).outputLine(useTS, useLL)); + copied.append(LoggingEntryModel::instance().dataAt(row).outputLine(useTS, useLL)); } - QGuiApplication::clipboard()->setText(copied); + Utils::setClipboardAndSelection(copied); }); connect(copyAll, &QAction::triggered, &m, [this] { QString copied; const bool useTS = m_timestamps->isChecked(); const bool useLL = m_messageTypes->isChecked(); - for (int row = 0, end = m_logModel->rowCount(); row < end; ++row) - copied.append(m_logModel->dataAt(row).outputLine(useTS, useLL)); + for (int row = 0, end = LoggingEntryModel::instance().rowCount(); row < end; ++row) + copied.append(LoggingEntryModel::instance().dataAt(row).outputLine(useTS, useLL)); - QGuiApplication::clipboard()->setText(copied); + Utils::setClipboardAndSelection(copied); }); m.exec(m_logView->mapToGlobal(pos)); } void LoggingViewManagerWidget::showLogCategoryContextMenu(const QPoint &pos) const { + QModelIndex idx = m_categoryView->indexAt(pos); + QMenu m; + auto uncheckAll = new QAction(Tr::tr("Uncheck All"), &m); + auto resetAll = new QAction(Tr::tr("Reset All"), &m); + + auto isTypeColumn = [](int column) { + return column >= LoggingCategoryModel::Column::Debug + && column <= LoggingCategoryModel::Column::Info; + }; + + auto setChecked = [this](std::initializer_list<LoggingCategoryModel::Column> columns, + Qt::CheckState checked) { + for (int row = 0, count = m_sortFilterModel->rowCount(); row < count; ++row) { + for (int column : columns) { + m_sortFilterModel->setData(m_sortFilterModel->index(row, column), + checked, + Qt::CheckStateRole); + } + } + }; + auto resetToOriginal = [this](std::initializer_list<LoggingCategoryModel::Column> columns) { + for (int row = 0, count = m_sortFilterModel->rowCount(); row < count; ++row) { + for (int column : columns) { + const QModelIndex id = m_sortFilterModel->index(row, column); + m_sortFilterModel->setData(id, + id.data(LoggingCategoryModel::OriginalStateRole), + Qt::CheckStateRole); + } + } + }; + + if (idx.isValid() && isTypeColumn(idx.column())) { + const LoggingCategoryModel::Column column = static_cast<LoggingCategoryModel::Column>( + idx.column()); + bool isChecked = idx.data(Qt::CheckStateRole).toInt() == Qt::Checked; + const QString uncheckText = isChecked ? Tr::tr("Uncheck All %1") : Tr::tr("Check All %1"); + + uncheckAll->setText(uncheckText.arg(messageTypeToString( + static_cast<QtMsgType>(column - LoggingCategoryModel::Column::Debug)))); + resetAll->setText(Tr::tr("Reset All %1") + .arg(messageTypeToString(static_cast<QtMsgType>( + column - LoggingCategoryModel::Column::Debug)))); + + Qt::CheckState newState = isChecked ? Qt::Unchecked : Qt::Checked; + + connect(uncheckAll, + &QAction::triggered, + m_sortFilterModel, + [setChecked, column, newState]() { setChecked({column}, newState); }); + + connect(resetAll, &QAction::triggered, m_sortFilterModel, [resetToOriginal, column]() { + resetToOriginal({column}); + }); + + } else { + // No need to add Fatal here, as it is read-only + static auto allColumns = {LoggingCategoryModel::Column::Debug, + LoggingCategoryModel::Column::Warning, + LoggingCategoryModel::Column::Critical, + LoggingCategoryModel::Column::Info}; + + connect(uncheckAll, &QAction::triggered, m_sortFilterModel, [setChecked]() { + setChecked(allColumns, Qt::Unchecked); + }); + connect(resetAll, &QAction::triggered, m_sortFilterModel, [resetToOriginal]() { + resetToOriginal(allColumns); + }); + } + // minimal load/save - plugins could later provide presets on their own? auto savePreset = new QAction(Tr::tr("Save Enabled as Preset..."), &m); m.addAction(savePreset); auto loadPreset = new QAction(Tr::tr("Update from Preset..."), &m); m.addAction(loadPreset); - auto uncheckAll = new QAction(Tr::tr("Uncheck All"), &m); m.addAction(uncheckAll); - connect(savePreset, &QAction::triggered, - this, &LoggingViewManagerWidget::saveEnabledCategoryPreset); - connect(loadPreset, &QAction::triggered, - this, &LoggingViewManagerWidget::loadAndUpdateFromPreset); - connect(uncheckAll, &QAction::triggered, - m_categoryModel, &LoggingCategoryModel::disableAll); + m.addAction(resetAll); + connect(savePreset, + &QAction::triggered, + m_categoryModel, + &LoggingCategoryModel::saveEnabledCategoryPreset); + connect(loadPreset, + &QAction::triggered, + m_categoryModel, + &LoggingCategoryModel::loadAndUpdateFromPreset); m.exec(m_categoryView->mapToGlobal(pos)); } void LoggingViewManagerWidget::saveLoggingsToFile() const { - // should we just let it continue without temporarily disabling? - const bool enabled = m_manager->isEnabled(); - const QScopeGuard cleanup([this, enabled] { m_manager->setEnabled(enabled); }); - if (enabled) - m_manager->setEnabled(false); const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), - Tr::tr("Save Logs As")); + Tr::tr("Save Logs As"), + {}, + "*.log"); if (fp.isEmpty()) return; + const bool useTS = m_timestamps->isChecked(); const bool useLL = m_messageTypes->isChecked(); QFile file(fp.path()); if (file.open(QIODevice::WriteOnly)) { - for (int row = 0, end = m_logModel->rowCount(); row < end; ++row) { - qint64 res = file.write( m_logModel->dataAt(row).outputLine(useTS, useLL).toUtf8()); + for (int row = 0, end = LoggingEntryModel::instance().rowCount(); row < end; ++row) { + qint64 res = file.write( + LoggingEntryModel::instance().dataAt(row).outputLine(useTS, useLL).toUtf8()); if (res == -1) { - QMessageBox::critical( - ICore::dialogParent(), Tr::tr("Error"), - Tr::tr("Failed to write logs to \"%1\".").arg(fp.toUserOutput())); + QMessageBox::critical(ICore::dialogParent(), + Tr::tr("Error"), + Tr::tr("Failed to write logs to \"%1\".") + .arg(fp.toUserOutput())); break; } } file.close(); } else { - QMessageBox::critical( - ICore::dialogParent(), Tr::tr("Error"), - Tr::tr("Failed to open file \"%1\" for writing logs.").arg(fp.toUserOutput())); + QMessageBox::critical(ICore::dialogParent(), + Tr::tr("Error"), + Tr::tr("Failed to open file \"%1\" for writing logs.") + .arg(fp.toUserOutput())); } } -void LoggingViewManagerWidget::saveEnabledCategoryPreset() const +void LoggingCategoryModel::saveEnabledCategoryPreset() const { Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), - Tr::tr("Save Enabled Categories As...")); + Tr::tr("Save Enabled Categories As..."), + {}, + "*.json"); if (fp.isEmpty()) return; - const QList<LoggingCategoryItem> enabled = m_categoryModel->enabledCategories(); - // write them to file + + auto minLevel = [](const LoggingCategoryEntry &logCategory) { + for (int i = QtDebugMsg; i <= QtInfoMsg; i++) { + if (logCategory.isEnabled(static_cast<QtMsgType>(i))) + return i; + } + return QtInfoMsg + 1; + }; + QJsonArray array; - for (const LoggingCategoryItem &item : enabled) { + + for (const auto &item : m_categories) { QJsonObject itemObj; - itemObj.insert("name", item.name); + itemObj.insert("name", item.name()); QJsonObject entryObj; - entryObj.insert("level", item.entry.level); - if (item.entry.color.isValid()) - entryObj.insert("color", item.entry.color.name(QColor::HexArgb)); + entryObj.insert("level", minLevel(item)); + if (item.color().isValid()) + entryObj.insert("color", item.color().name(QColor::HexArgb)); + + QVariantMap levels = {{"Debug", item.isDebugEnabled()}, + {"Warning", item.isWarningEnabled()}, + {"Critical", item.isCriticalEnabled()}, + {"Info", item.isInfoEnabled()}}; + entryObj.insert("levels", QJsonValue::fromVariant(levels)); + itemObj.insert("entry", entryObj); array.append(itemObj); } QJsonDocument doc(array); if (!fp.writeFileContents(doc.toJson(QJsonDocument::Compact))) - QMessageBox::critical( - ICore::dialogParent(), Tr::tr("Error"), - Tr::tr("Failed to write preset file \"%1\".").arg(fp.toUserOutput())); + QMessageBox::critical(ICore::dialogParent(), + Tr::tr("Error"), + Tr::tr("Failed to write preset file \"%1\".").arg(fp.toUserOutput())); } -void LoggingViewManagerWidget::loadAndUpdateFromPreset() +void LoggingCategoryModel::loadAndUpdateFromPreset() { Utils::FilePath fp = Utils::FileUtils::getOpenFilePath(ICore::dialogParent(), Tr::tr("Load Enabled Categories From")); @@ -663,13 +1032,15 @@ void LoggingViewManagerWidget::loadAndUpdateFromPreset() QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(*contents, &error); if (error.error != QJsonParseError::NoError) { - QMessageBox::critical(ICore::dialogParent(), Tr::tr("Error"), - Tr::tr("Failed to read preset file \"%1\": %2").arg(fp.toUserOutput()) - .arg(error.errorString())); + QMessageBox::critical(ICore::dialogParent(), + Tr::tr("Error"), + Tr::tr("Failed to read preset file \"%1\": %2") + .arg(fp.toUserOutput()) + .arg(error.errorString())); return; } bool formatError = false; - QList<LoggingCategoryItem> presetItems; + QList<SavedEntry> presetItems; if (doc.isArray()) { const QJsonArray array = doc.array(); for (const QJsonValue &value : array) { @@ -678,57 +1049,88 @@ void LoggingViewManagerWidget::loadAndUpdateFromPreset() break; } const QJsonObject itemObj = value.toObject(); - bool ok = true; - LoggingCategoryItem item = LoggingCategoryItem::fromJson(itemObj, &ok); - if (!ok) { + Utils::expected_str<SavedEntry> item = SavedEntry::fromJson(itemObj); + if (!item) { formatError = true; break; } - presetItems.append(item); + presetItems.append(*item); } } else { formatError = true; } if (formatError) { - QMessageBox::critical(ICore::dialogParent(), Tr::tr("Error"), + QMessageBox::critical(ICore::dialogParent(), + Tr::tr("Error"), Tr::tr("Unexpected preset file format.")); } - for (const LoggingCategoryItem &item : presetItems) - m_manager->appendOrUpdate(item.name, item.entry); + + int idx = 0; + + for (auto it = presetItems.begin(); it != presetItems.end(); ++it, ++idx) { + QList<LoggingCategoryEntry>::iterator itExisting + = std::find_if(m_categories.begin(), + m_categories.end(), + [e = *it](const LoggingCategoryEntry &cat) { + return cat.name() == e.name; + }); + + if (it->color.isValid()) + setCategoryColor(it->name, it->color); + + if (itExisting != m_categories.end()) { + itExisting->setSaved(*it); + emit dataChanged(createIndex(idx, Column::Color), createIndex(idx, Column::Info)); + } else { + LoggingCategoryEntry newEntry(*it); + append(newEntry); + } + } } -QColor LoggingViewManagerWidget::colorForCategory(const QString &category) +QColor colorForCategory(const QString &category) { - auto entry = m_categoryColor.find(category); - if (entry == m_categoryColor.end()) + auto entry = s_categoryColor.find(category); + if (entry == s_categoryColor.end()) return Utils::creatorTheme()->palette().text().color(); return entry.value(); } -void LoggingViewManagerWidget::setCategoryColor(const QString &category, const QColor &color) +void setCategoryColor(const QString &category, const QColor &color) { const QColor baseColor = Utils::creatorTheme()->palette().text().color(); if (color != baseColor) - m_categoryColor.insert(category, color); + s_categoryColor.insert(category, color); else - m_categoryColor.remove(category); + s_categoryColor.remove(category); } +static bool wasLogViewerShown = false; + void LoggingViewer::showLoggingView() { - ActionManager::command(Constants::LOGGER)->action()->setEnabled(false); - auto widget = new LoggingViewManagerWidget(ICore::dialogParent()); - QObject::connect(widget, &QDialog::finished, widget, [widget] { - ActionManager::command(Constants::LOGGER)->action()->setEnabled(true); - // explicitly disable manager again - widget->deleteLater(); - }); - ICore::registerWindow(widget, Context("Qtc.LogViewer")); - widget->show(); + LoggingViewManagerWidget *staticLogWidget = LoggingViewManagerWidget::instance(); + QTC_ASSERT(staticLogWidget, return); + + staticLogWidget->show(); + staticLogWidget->raise(); + staticLogWidget->activateWindow(); + + wasLogViewerShown = true; } -} // namespace Internal -} // namespace Core +void LoggingViewer::hideLoggingView() +{ + if (!wasLogViewerShown) + return; + + LoggingViewManagerWidget *staticLogWidget = LoggingViewManagerWidget::instance(); + QTC_ASSERT(staticLogWidget, return); + staticLogWidget->close(); + delete staticLogWidget; +} + +} // namespace Core::Internal #include "loggingviewer.moc" diff --git a/src/plugins/coreplugin/loggingviewer.h b/src/plugins/coreplugin/loggingviewer.h index cb8bf031ca4..4cde2a6501f 100644 --- a/src/plugins/coreplugin/loggingviewer.h +++ b/src/plugins/coreplugin/loggingviewer.h @@ -3,14 +3,13 @@ #pragma once -namespace Core { -namespace Internal { +namespace Core::Internal { class LoggingViewer { public: static void showLoggingView(); + static void hideLoggingView(); }; -} // Internal -} // Core +} // namespace Core::Internal diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index ee5138f62e8..e69de29bb2d 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -1,1545 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "mainwindow.h" - -#include "actionmanager/actioncontainer.h" -#include "actionmanager/actionmanager.h" -#include "actionmanager/command.h" -#include "coreicons.h" -#include "coreplugintr.h" -#include "dialogs/externaltoolconfig.h" -#include "dialogs/shortcutsettings.h" -#include "documentmanager.h" -#include "editormanager/documentmodel_p.h" -#include "editormanager/editormanager.h" -#include "editormanager/editormanager_p.h" -#include "editormanager/ieditor.h" -#include "editormanager/ieditorfactory.h" -#include "editormanager/systemeditor.h" -#include "externaltoolmanager.h" -#include "fancytabwidget.h" -#include "fileutils.h" -#include "find/basetextfind.h" -#include "findplaceholder.h" -#include "generalsettings.h" -#include "helpmanager.h" -#include "icore.h" -#include "idocumentfactory.h" -#include "inavigationwidgetfactory.h" -#include "iwizardfactory.h" -#include "jsexpander.h" -#include "loggingviewer.h" -#include "manhattanstyle.h" -#include "messagemanager.h" -#include "mimetypesettings.h" -#include "modemanager.h" -#include "navigationwidget.h" -#include "outputpanemanager.h" -#include "plugindialog.h" -#include "progressmanager/progressmanager_p.h" -#include "progressmanager/progressview.h" -#include "rightpane.h" -#include "settingsdatabase.h" -#include "statusbarmanager.h" -#include "systemsettings.h" -#include "vcsmanager.h" -#include "versiondialog.h" -#include "windowsupport.h" - -#include <app/app_version.h> - -#include <extensionsystem/pluginmanager.h> - -#include <utils/algorithm.h> -#include <utils/fsengine/fileiconprovider.h> -#include <utils/fsengine/fsengine.h> -#include <utils/historycompleter.h> -#include <utils/hostosinfo.h> -#include <utils/mimeutils.h> -#include <utils/proxyaction.h> -#include <utils/qtcassert.h> -#include <utils/stringutils.h> -#include <utils/stylehelper.h> -#include <utils/theme/theme.h> -#include <utils/touchbar/touchbar.h> -#include <utils/terminalcommand.h> -#include <utils/utilsicons.h> - -#include <QAbstractProxyModel> -#include <QActionGroup> -#include <QApplication> -#include <QBrush> -#include <QCloseEvent> -#include <QColorDialog> -#include <QComboBox> -#include <QDebug> -#include <QDialogButtonBox> -#include <QDir> -#include <QFileInfo> -#include <QFileSystemModel> -#include <QMenu> -#include <QMenuBar> -#include <QMessageBox> -#include <QPrinter> -#include <QSettings> -#include <QSortFilterProxyModel> -#include <QStatusBar> -#include <QStyleFactory> -#include <QSyntaxHighlighter> -#include <QTextBrowser> -#include <QTextList> -#include <QToolButton> -#include <QUrl> -#include <QVersionNumber> -#include <QWindow> - -#ifdef Q_OS_LINUX -#include <malloc.h> -#endif - -using namespace ExtensionSystem; -using namespace Utils; - -namespace Core { -namespace Internal { - -static const char settingsGroup[] = "MainWindow"; -static const char colorKey[] = "Color"; -static const char askBeforeExitKey[] = "AskBeforeExit"; -static const char windowGeometryKey[] = "WindowGeometry"; -static const char windowStateKey[] = "WindowState"; -static const char modeSelectorLayoutKey[] = "ModeSelectorLayout"; - -static const bool askBeforeExitDefault = false; - -static bool hideToolsMenu() -{ - return Core::ICore::settings()->value(Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool(); -} - -enum { debugMainWindow = 0 }; - -MainWindow::MainWindow() - : AppMainWindow() - , m_coreImpl(new ICore(this)) - , m_lowPrioAdditionalContexts(Constants::C_GLOBAL) - , m_settingsDatabase( - new SettingsDatabase(QFileInfo(PluginManager::settings()->fileName()).path(), - QLatin1String(Constants::IDE_CASED_ID), - this)) - , m_progressManager(new ProgressManagerPrivate) - , m_jsExpander(JsExpander::createGlobalJsExpander()) - , m_vcsManager(new VcsManager) - , m_modeStack(new FancyTabWidget(this)) - , m_generalSettings(new GeneralSettings) - , m_systemSettings(new SystemSettings) - , m_shortcutSettings(new ShortcutSettings) - , m_toolSettings(new ToolSettings) - , m_mimeTypeSettings(new MimeTypeSettings) - , m_systemEditor(new SystemEditor) - , m_toggleLeftSideBarButton(new QToolButton) - , m_toggleRightSideBarButton(new QToolButton) -{ - (void) new DocumentManager(this); - - HistoryCompleter::setSettings(PluginManager::settings()); - - setWindowTitle(Constants::IDE_DISPLAY_NAME); - if (HostOsInfo::isLinuxHost()) - QApplication::setWindowIcon(Icons::QTCREATORLOGO_BIG.icon()); - QString baseName = QApplication::style()->objectName(); - // Sometimes we get the standard windows 95 style as a fallback - if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost() - && baseName == QLatin1String("windows")) { - baseName = QLatin1String("fusion"); - } - - // if the user has specified as base style in the theme settings, - // prefer that - const QStringList available = QStyleFactory::keys(); - const QStringList styles = Utils::creatorTheme()->preferredStyles(); - for (const QString &s : styles) { - if (available.contains(s, Qt::CaseInsensitive)) { - baseName = s; - break; - } - } - - QApplication::setStyle(new ManhattanStyle(baseName)); - m_generalSettings->setShowShortcutsInContextMenu( - GeneralSettings::showShortcutsInContextMenu()); - - setDockNestingEnabled(true); - - setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea); - setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea); - - m_modeManager = new ModeManager(this, m_modeStack); - connect(m_modeStack, &FancyTabWidget::topAreaClicked, this, [](Qt::MouseButton, Qt::KeyboardModifiers modifiers) { - if (modifiers & Qt::ShiftModifier) { - QColor color = QColorDialog::getColor(StyleHelper::requestedBaseColor(), ICore::dialogParent()); - if (color.isValid()) - StyleHelper::setBaseColor(color); - } - }); - - registerDefaultContainers(); - registerDefaultActions(); - - m_leftNavigationWidget = new NavigationWidget(m_toggleLeftSideBarAction, Side::Left); - m_rightNavigationWidget = new NavigationWidget(m_toggleRightSideBarAction, Side::Right); - m_rightPaneWidget = new RightPaneWidget(); - - m_messageManager = new MessageManager; - m_editorManager = new EditorManager(this); - m_externalToolManager = new ExternalToolManager(); - setCentralWidget(m_modeStack); - - m_progressManager->progressView()->setParent(this); - - connect(qApp, &QApplication::focusChanged, this, &MainWindow::updateFocusWidget); - - // Add small Toolbuttons for toggling the navigation widgets - StatusBarManager::addStatusBarWidget(m_toggleLeftSideBarButton, StatusBarManager::First); - int childsCount = statusBar()->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly).count(); - statusBar()->insertPermanentWidget(childsCount - 1, m_toggleRightSideBarButton); // before QSizeGrip - -// setUnifiedTitleAndToolBarOnMac(true); - //if (HostOsInfo::isAnyUnixHost()) - //signal(SIGINT, handleSigInt); - - statusBar()->setProperty("p_styled", true); - - auto dropSupport = new DropSupport(this, [](QDropEvent *event, DropSupport *) { - return event->source() == nullptr; // only accept drops from the "outside" (e.g. file manager) - }); - connect(dropSupport, &DropSupport::filesDropped, - this, &MainWindow::openDroppedFiles); - - if (HostOsInfo::isLinuxHost()) { - m_trimTimer.setSingleShot(true); - m_trimTimer.setInterval(60000); - // glibc may not actually free memory in free(). -#ifdef Q_OS_LINUX - connect(&m_trimTimer, &QTimer::timeout, this, [] { malloc_trim(0); }); -#endif - } -} - -NavigationWidget *MainWindow::navigationWidget(Side side) const -{ - return side == Side::Left ? m_leftNavigationWidget : m_rightNavigationWidget; -} - -void MainWindow::setSidebarVisible(bool visible, Side side) -{ - if (NavigationWidgetPlaceHolder::current(side)) - navigationWidget(side)->setShown(visible); -} - -bool MainWindow::askConfirmationBeforeExit() const -{ - return m_askConfirmationBeforeExit; -} - -void MainWindow::setAskConfirmationBeforeExit(bool ask) -{ - m_askConfirmationBeforeExit = ask; -} - -void MainWindow::setOverrideColor(const QColor &color) -{ - m_overrideColor = color; -} - -QStringList MainWindow::additionalAboutInformation() const -{ - return m_aboutInformation; -} - -void MainWindow::appendAboutInformation(const QString &line) -{ - m_aboutInformation.append(line); -} - -void MainWindow::addPreCloseListener(const std::function<bool ()> &listener) -{ - m_preCloseListeners.append(listener); -} - -MainWindow::~MainWindow() -{ - // explicitly delete window support, because that calls methods from ICore that call methods - // from mainwindow, so mainwindow still needs to be alive - delete m_windowSupport; - m_windowSupport = nullptr; - - delete m_externalToolManager; - m_externalToolManager = nullptr; - delete m_messageManager; - m_messageManager = nullptr; - delete m_shortcutSettings; - m_shortcutSettings = nullptr; - delete m_generalSettings; - m_generalSettings = nullptr; - delete m_systemSettings; - m_systemSettings = nullptr; - delete m_toolSettings; - m_toolSettings = nullptr; - delete m_mimeTypeSettings; - m_mimeTypeSettings = nullptr; - delete m_systemEditor; - m_systemEditor = nullptr; - delete m_printer; - m_printer = nullptr; - delete m_vcsManager; - m_vcsManager = nullptr; - //we need to delete editormanager and statusbarmanager explicitly before the end of the destructor, - //because they might trigger stuff that tries to access data from editorwindow, like removeContextWidget - - // All modes are now gone - OutputPaneManager::destroy(); - - delete m_leftNavigationWidget; - delete m_rightNavigationWidget; - m_leftNavigationWidget = nullptr; - m_rightNavigationWidget = nullptr; - - delete m_editorManager; - m_editorManager = nullptr; - delete m_progressManager; - m_progressManager = nullptr; - - delete m_coreImpl; - m_coreImpl = nullptr; - - delete m_rightPaneWidget; - m_rightPaneWidget = nullptr; - - delete m_modeManager; - m_modeManager = nullptr; - - delete m_jsExpander; - m_jsExpander = nullptr; -} - -void MainWindow::init() -{ - m_progressManager->init(); // needs the status bar manager - MessageManager::init(); - OutputPaneManager::create(); -} - -void MainWindow::extensionsInitialized() -{ - EditorManagerPrivate::extensionsInitialized(); - MimeTypeSettings::restoreSettings(); - m_windowSupport = new WindowSupport(this, Context("Core.MainWindow")); - m_windowSupport->setCloseActionEnabled(false); - OutputPaneManager::initialize(); - VcsManager::extensionsInitialized(); - m_leftNavigationWidget->setFactories(INavigationWidgetFactory::allNavigationFactories()); - m_rightNavigationWidget->setFactories(INavigationWidgetFactory::allNavigationFactories()); - - ModeManager::extensionsInitialized(); - - readSettings(); - updateContext(); - - emit m_coreImpl->coreAboutToOpen(); - // Delay restoreWindowState, since it is overridden by LayoutRequest event - QMetaObject::invokeMethod(this, &MainWindow::restoreWindowState, Qt::QueuedConnection); - QMetaObject::invokeMethod(m_coreImpl, &ICore::coreOpened, Qt::QueuedConnection); -} - -static void setRestart(bool restart) -{ - qApp->setProperty("restart", restart); -} - -void MainWindow::restart() -{ - setRestart(true); - exit(); -} - -void MainWindow::restartTrimmer() -{ - if (HostOsInfo::isLinuxHost() && !m_trimTimer.isActive()) - m_trimTimer.start(); -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - const auto cancelClose = [event] { - event->ignore(); - setRestart(false); - }; - - // work around QTBUG-43344 - static bool alreadyClosed = false; - if (alreadyClosed) { - event->accept(); - return; - } - - if (m_askConfirmationBeforeExit && - (QMessageBox::question(this, - Tr::tr("Exit %1?").arg(Constants::IDE_DISPLAY_NAME), - Tr::tr("Exit %1?").arg(Constants::IDE_DISPLAY_NAME), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) - == QMessageBox::No)) { - event->ignore(); - return; - } - - ICore::saveSettings(ICore::MainWindowClosing); - - // Save opened files - if (!DocumentManager::saveAllModifiedDocuments()) { - cancelClose(); - return; - } - - const QList<std::function<bool()>> listeners = m_preCloseListeners; - for (const std::function<bool()> &listener : listeners) { - if (!listener()) { - cancelClose(); - return; - } - } - - emit m_coreImpl->coreAboutToClose(); - - saveWindowSettings(); - - m_leftNavigationWidget->closeSubWidgets(); - m_rightNavigationWidget->closeSubWidgets(); - - event->accept(); - alreadyClosed = true; -} - -void MainWindow::keyPressEvent(QKeyEvent *event) -{ - restartTrimmer(); - AppMainWindow::keyPressEvent(event); -} - -void MainWindow::mousePressEvent(QMouseEvent *event) -{ - restartTrimmer(); - AppMainWindow::mousePressEvent(event); -} - -void MainWindow::openDroppedFiles(const QList<DropSupport::FileSpec> &files) -{ - raiseWindow(); - const FilePaths filePaths = Utils::transform(files, &DropSupport::FileSpec::filePath); - openFiles(filePaths, ICore::SwitchMode); -} - -IContext *MainWindow::currentContextObject() const -{ - return m_activeContext.isEmpty() ? nullptr : m_activeContext.first(); -} - -QStatusBar *MainWindow::statusBar() const -{ - return m_modeStack->statusBar(); -} - -InfoBar *MainWindow::infoBar() const -{ - return m_modeStack->infoBar(); -} - -void MainWindow::registerDefaultContainers() -{ - ActionContainer *menubar = ActionManager::createMenuBar(Constants::MENU_BAR); - - if (!HostOsInfo::isMacHost()) // System menu bar on Mac - setMenuBar(menubar->menuBar()); - menubar->appendGroup(Constants::G_FILE); - menubar->appendGroup(Constants::G_EDIT); - menubar->appendGroup(Constants::G_VIEW); - menubar->appendGroup(Constants::G_TOOLS); - menubar->appendGroup(Constants::G_WINDOW); - menubar->appendGroup(Constants::G_HELP); - - // File Menu - ActionContainer *filemenu = ActionManager::createMenu(Constants::M_FILE); - menubar->addMenu(filemenu, Constants::G_FILE); - filemenu->menu()->setTitle(Tr::tr("&File")); - filemenu->appendGroup(Constants::G_FILE_NEW); - filemenu->appendGroup(Constants::G_FILE_OPEN); - filemenu->appendGroup(Constants::G_FILE_SESSION); - filemenu->appendGroup(Constants::G_FILE_PROJECT); - filemenu->appendGroup(Constants::G_FILE_SAVE); - filemenu->appendGroup(Constants::G_FILE_EXPORT); - filemenu->appendGroup(Constants::G_FILE_CLOSE); - filemenu->appendGroup(Constants::G_FILE_PRINT); - filemenu->appendGroup(Constants::G_FILE_OTHER); - connect(filemenu->menu(), &QMenu::aboutToShow, this, &MainWindow::aboutToShowRecentFiles); - - - // Edit Menu - ActionContainer *medit = ActionManager::createMenu(Constants::M_EDIT); - menubar->addMenu(medit, Constants::G_EDIT); - medit->menu()->setTitle(Tr::tr("&Edit")); - medit->appendGroup(Constants::G_EDIT_UNDOREDO); - medit->appendGroup(Constants::G_EDIT_COPYPASTE); - medit->appendGroup(Constants::G_EDIT_SELECTALL); - medit->appendGroup(Constants::G_EDIT_ADVANCED); - medit->appendGroup(Constants::G_EDIT_FIND); - medit->appendGroup(Constants::G_EDIT_OTHER); - - ActionContainer *mview = ActionManager::createMenu(Constants::M_VIEW); - menubar->addMenu(mview, Constants::G_VIEW); - mview->menu()->setTitle(Tr::tr("&View")); - mview->appendGroup(Constants::G_VIEW_VIEWS); - mview->appendGroup(Constants::G_VIEW_PANES); - - // Tools Menu - ActionContainer *ac = ActionManager::createMenu(Constants::M_TOOLS); - ac->setParent(this); - if (!hideToolsMenu()) - menubar->addMenu(ac, Constants::G_TOOLS); - - ac->menu()->setTitle(Tr::tr("&Tools")); - - // Window Menu - ActionContainer *mwindow = ActionManager::createMenu(Constants::M_WINDOW); - menubar->addMenu(mwindow, Constants::G_WINDOW); - mwindow->menu()->setTitle(Tr::tr("&Window")); - mwindow->appendGroup(Constants::G_WINDOW_SIZE); - mwindow->appendGroup(Constants::G_WINDOW_SPLIT); - mwindow->appendGroup(Constants::G_WINDOW_NAVIGATE); - mwindow->appendGroup(Constants::G_WINDOW_LIST); - mwindow->appendGroup(Constants::G_WINDOW_OTHER); - - // Help Menu - ac = ActionManager::createMenu(Constants::M_HELP); - menubar->addMenu(ac, Constants::G_HELP); - ac->menu()->setTitle(Tr::tr("&Help")); - Theme::setHelpMenu(ac->menu()); - ac->appendGroup(Constants::G_HELP_HELP); - ac->appendGroup(Constants::G_HELP_SUPPORT); - ac->appendGroup(Constants::G_HELP_ABOUT); - ac->appendGroup(Constants::G_HELP_UPDATES); - - // macOS touch bar - ac = ActionManager::createTouchBar(Constants::TOUCH_BAR, - QIcon(), - "Main TouchBar" /*never visible*/); - ac->appendGroup(Constants::G_TOUCHBAR_HELP); - ac->appendGroup(Constants::G_TOUCHBAR_NAVIGATION); - ac->appendGroup(Constants::G_TOUCHBAR_EDITOR); - ac->appendGroup(Constants::G_TOUCHBAR_OTHER); - ac->touchBar()->setApplicationTouchBar(); -} - -void MainWindow::registerDefaultActions() -{ - ActionContainer *mfile = ActionManager::actionContainer(Constants::M_FILE); - ActionContainer *medit = ActionManager::actionContainer(Constants::M_EDIT); - ActionContainer *mview = ActionManager::actionContainer(Constants::M_VIEW); - ActionContainer *mtools = ActionManager::actionContainer(Constants::M_TOOLS); - ActionContainer *mwindow = ActionManager::actionContainer(Constants::M_WINDOW); - ActionContainer *mhelp = ActionManager::actionContainer(Constants::M_HELP); - - // File menu separators - mfile->addSeparator(Constants::G_FILE_SAVE); - mfile->addSeparator(Constants::G_FILE_EXPORT); - mfile->addSeparator(Constants::G_FILE_PRINT); - mfile->addSeparator(Constants::G_FILE_CLOSE); - mfile->addSeparator(Constants::G_FILE_OTHER); - // Edit menu separators - medit->addSeparator(Constants::G_EDIT_COPYPASTE); - medit->addSeparator(Constants::G_EDIT_SELECTALL); - medit->addSeparator(Constants::G_EDIT_FIND); - medit->addSeparator(Constants::G_EDIT_ADVANCED); - - // Return to editor shortcut: Note this requires Qt to fix up - // handling of shortcut overrides in menus, item views, combos.... - m_focusToEditor = new QAction(Tr::tr("Return to Editor"), this); - Command *cmd = ActionManager::registerAction(m_focusToEditor, Constants::S_RETURNTOEDITOR); - cmd->setDefaultKeySequence(QKeySequence(Qt::Key_Escape)); - connect(m_focusToEditor, &QAction::triggered, this, &MainWindow::setFocusToEditor); - - // New File Action - QIcon icon = QIcon::fromTheme(QLatin1String("document-new"), Utils::Icons::NEWFILE.icon()); - - m_newAction = new QAction(icon, Tr::tr("&New Project..."), this); - cmd = ActionManager::registerAction(m_newAction, Constants::NEW); - cmd->setDefaultKeySequence(QKeySequence("Ctrl+Shift+N")); - mfile->addAction(cmd, Constants::G_FILE_NEW); - connect(m_newAction, &QAction::triggered, this, [] { - if (!ICore::isNewItemDialogRunning()) { - ICore::showNewItemDialog( - Tr::tr("New Project", "Title of dialog"), - Utils::filtered(Core::IWizardFactory::allWizardFactories(), - Utils::equal(&Core::IWizardFactory::kind, - Core::IWizardFactory::ProjectWizard)), - FilePath()); - } else { - ICore::raiseWindow(ICore::newItemDialog()); - } - }); - - auto action = new QAction(icon, Tr::tr("New File..."), this); - cmd = ActionManager::registerAction(action, Constants::NEW_FILE); - cmd->setDefaultKeySequence(QKeySequence::New); - mfile->addAction(cmd, Constants::G_FILE_NEW); - connect(action, &QAction::triggered, this, [] { - if (!ICore::isNewItemDialogRunning()) { - ICore::showNewItemDialog(Tr::tr("New File", "Title of dialog"), - Utils::filtered(Core::IWizardFactory::allWizardFactories(), - Utils::equal(&Core::IWizardFactory::kind, - Core::IWizardFactory::FileWizard)), - FilePath()); - } else { - ICore::raiseWindow(ICore::newItemDialog()); - } - }); - - // Open Action - icon = QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon()); - m_openAction = new QAction(icon, Tr::tr("&Open File or Project..."), this); - cmd = ActionManager::registerAction(m_openAction, Constants::OPEN); - cmd->setDefaultKeySequence(QKeySequence::Open); - mfile->addAction(cmd, Constants::G_FILE_OPEN); - connect(m_openAction, &QAction::triggered, this, &MainWindow::openFile); - - // Open With Action - m_openWithAction = new QAction(Tr::tr("Open File &With..."), this); - cmd = ActionManager::registerAction(m_openWithAction, Constants::OPEN_WITH); - mfile->addAction(cmd, Constants::G_FILE_OPEN); - connect(m_openWithAction, &QAction::triggered, this, &MainWindow::openFileWith); - - if (FSEngine::isAvailable()) { - // Open From Device Action - m_openFromDeviceAction = new QAction(Tr::tr("Open From Device..."), this); - cmd = ActionManager::registerAction(m_openFromDeviceAction, Constants::OPEN_FROM_DEVICE); - mfile->addAction(cmd, Constants::G_FILE_OPEN); - connect(m_openFromDeviceAction, &QAction::triggered, this, &MainWindow::openFileFromDevice); - } - - // File->Recent Files Menu - ActionContainer *ac = ActionManager::createMenu(Constants::M_FILE_RECENTFILES); - mfile->addMenu(ac, Constants::G_FILE_OPEN); - ac->menu()->setTitle(Tr::tr("Recent &Files")); - ac->setOnAllDisabledBehavior(ActionContainer::Show); - - // Save Action - icon = QIcon::fromTheme(QLatin1String("document-save"), Utils::Icons::SAVEFILE.icon()); - QAction *tmpaction = new QAction(icon, Tr::tr("&Save"), this); - tmpaction->setEnabled(false); - cmd = ActionManager::registerAction(tmpaction, Constants::SAVE); - cmd->setDefaultKeySequence(QKeySequence::Save); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDescription(Tr::tr("Save")); - mfile->addAction(cmd, Constants::G_FILE_SAVE); - - // Save As Action - icon = QIcon::fromTheme(QLatin1String("document-save-as")); - tmpaction = new QAction(icon, Tr::tr("Save &As..."), this); - tmpaction->setEnabled(false); - cmd = ActionManager::registerAction(tmpaction, Constants::SAVEAS); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Shift+S") : QString())); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDescription(Tr::tr("Save As...")); - mfile->addAction(cmd, Constants::G_FILE_SAVE); - - // SaveAll Action - DocumentManager::registerSaveAllAction(); - - // Print Action - icon = QIcon::fromTheme(QLatin1String("document-print")); - tmpaction = new QAction(icon, Tr::tr("&Print..."), this); - tmpaction->setEnabled(false); - cmd = ActionManager::registerAction(tmpaction, Constants::PRINT); - cmd->setDefaultKeySequence(QKeySequence::Print); - mfile->addAction(cmd, Constants::G_FILE_PRINT); - - // Exit Action - icon = QIcon::fromTheme(QLatin1String("application-exit")); - m_exitAction = new QAction(icon, Tr::tr("E&xit"), this); - m_exitAction->setMenuRole(QAction::QuitRole); - cmd = ActionManager::registerAction(m_exitAction, Constants::EXIT); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Q"))); - mfile->addAction(cmd, Constants::G_FILE_OTHER); - connect(m_exitAction, &QAction::triggered, this, &MainWindow::exit); - - // Undo Action - icon = QIcon::fromTheme(QLatin1String("edit-undo"), Utils::Icons::UNDO.icon()); - tmpaction = new QAction(icon, Tr::tr("&Undo"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::UNDO); - cmd->setDefaultKeySequence(QKeySequence::Undo); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDescription(Tr::tr("Undo")); - medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); - tmpaction->setEnabled(false); - - // Redo Action - icon = QIcon::fromTheme(QLatin1String("edit-redo"), Utils::Icons::REDO.icon()); - tmpaction = new QAction(icon, Tr::tr("&Redo"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::REDO); - cmd->setDefaultKeySequence(QKeySequence::Redo); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDescription(Tr::tr("Redo")); - medit->addAction(cmd, Constants::G_EDIT_UNDOREDO); - tmpaction->setEnabled(false); - - // Cut Action - icon = QIcon::fromTheme(QLatin1String("edit-cut"), Utils::Icons::CUT.icon()); - tmpaction = new QAction(icon, Tr::tr("Cu&t"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::CUT); - cmd->setDefaultKeySequence(QKeySequence::Cut); - medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); - tmpaction->setEnabled(false); - - // Copy Action - icon = QIcon::fromTheme(QLatin1String("edit-copy"), Utils::Icons::COPY.icon()); - tmpaction = new QAction(icon, Tr::tr("&Copy"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::COPY); - cmd->setDefaultKeySequence(QKeySequence::Copy); - medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); - tmpaction->setEnabled(false); - - // Paste Action - icon = QIcon::fromTheme(QLatin1String("edit-paste"), Utils::Icons::PASTE.icon()); - tmpaction = new QAction(icon, Tr::tr("&Paste"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::PASTE); - cmd->setDefaultKeySequence(QKeySequence::Paste); - medit->addAction(cmd, Constants::G_EDIT_COPYPASTE); - tmpaction->setEnabled(false); - - // Select All - icon = QIcon::fromTheme(QLatin1String("edit-select-all")); - tmpaction = new QAction(icon, Tr::tr("Select &All"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::SELECTALL); - cmd->setDefaultKeySequence(QKeySequence::SelectAll); - medit->addAction(cmd, Constants::G_EDIT_SELECTALL); - tmpaction->setEnabled(false); - - // Goto Action - icon = QIcon::fromTheme(QLatin1String("go-jump")); - tmpaction = new QAction(icon, Tr::tr("&Go to Line..."), this); - cmd = ActionManager::registerAction(tmpaction, Constants::GOTO); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+L"))); - medit->addAction(cmd, Constants::G_EDIT_OTHER); - tmpaction->setEnabled(false); - - // Zoom In Action - icon = QIcon::hasThemeIcon("zoom-in") ? QIcon::fromTheme("zoom-in") - : Utils::Icons::ZOOMIN_TOOLBAR.icon(); - tmpaction = new QAction(icon, Tr::tr("Zoom In"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_IN); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl++"))); - tmpaction->setEnabled(false); - - // Zoom Out Action - icon = QIcon::hasThemeIcon("zoom-out") ? QIcon::fromTheme("zoom-out") - : Utils::Icons::ZOOMOUT_TOOLBAR.icon(); - tmpaction = new QAction(icon, Tr::tr("Zoom Out"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_OUT); - if (useMacShortcuts) - cmd->setDefaultKeySequences({QKeySequence(Tr::tr("Ctrl+-")), QKeySequence(Tr::tr("Ctrl+Shift+-"))}); - else - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+-"))); - tmpaction->setEnabled(false); - - // Zoom Reset Action - icon = QIcon::hasThemeIcon("zoom-original") ? QIcon::fromTheme("zoom-original") - : Utils::Icons::EYE_OPEN_TOOLBAR.icon(); - tmpaction = new QAction(icon, Tr::tr("Original Size"), this); - cmd = ActionManager::registerAction(tmpaction, Constants::ZOOM_RESET); - cmd->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+0") : Tr::tr("Ctrl+0"))); - tmpaction->setEnabled(false); - - // Debug Qt Creator menu - mtools->appendGroup(Constants::G_TOOLS_DEBUG); - ActionContainer *mtoolsdebug = ActionManager::createMenu(Constants::M_TOOLS_DEBUG); - mtoolsdebug->menu()->setTitle(Tr::tr("Debug %1").arg(Constants::IDE_DISPLAY_NAME)); - mtools->addMenu(mtoolsdebug, Constants::G_TOOLS_DEBUG); - - m_loggerAction = new QAction(Tr::tr("Show Logs..."), this); - cmd = ActionManager::registerAction(m_loggerAction, Constants::LOGGER); - mtoolsdebug->addAction(cmd); - connect(m_loggerAction, &QAction::triggered, this, [] { LoggingViewer::showLoggingView(); }); - - // Options Action - medit->appendGroup(Constants::G_EDIT_PREFERENCES); - medit->addSeparator(Constants::G_EDIT_PREFERENCES); - - m_optionsAction = new QAction(Tr::tr("Pr&eferences..."), this); - m_optionsAction->setMenuRole(QAction::PreferencesRole); - cmd = ActionManager::registerAction(m_optionsAction, Constants::OPTIONS); - cmd->setDefaultKeySequence(QKeySequence::Preferences); - medit->addAction(cmd, Constants::G_EDIT_PREFERENCES); - connect(m_optionsAction, &QAction::triggered, this, [] { ICore::showOptionsDialog(Id()); }); - - mwindow->addSeparator(Constants::G_WINDOW_LIST); - - if (useMacShortcuts) { - // Minimize Action - QAction *minimizeAction = new QAction(Tr::tr("Minimize"), this); - minimizeAction->setEnabled(false); // actual implementation in WindowSupport - cmd = ActionManager::registerAction(minimizeAction, Constants::MINIMIZE_WINDOW); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+M"))); - mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); - - // Zoom Action - QAction *zoomAction = new QAction(Tr::tr("Zoom"), this); - zoomAction->setEnabled(false); // actual implementation in WindowSupport - cmd = ActionManager::registerAction(zoomAction, Constants::ZOOM_WINDOW); - mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); - } - - // Full Screen Action - QAction *toggleFullScreenAction = new QAction(Tr::tr("Full Screen"), this); - toggleFullScreenAction->setCheckable(!HostOsInfo::isMacHost()); - toggleFullScreenAction->setEnabled(false); // actual implementation in WindowSupport - cmd = ActionManager::registerAction(toggleFullScreenAction, Constants::TOGGLE_FULLSCREEN); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Meta+F") : Tr::tr("Ctrl+Shift+F11"))); - if (HostOsInfo::isMacHost()) - cmd->setAttribute(Command::CA_UpdateText); - mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); - - if (useMacShortcuts) { - mwindow->addSeparator(Constants::G_WINDOW_SIZE); - - QAction *closeAction = new QAction(Tr::tr("Close Window"), this); - closeAction->setEnabled(false); - cmd = ActionManager::registerAction(closeAction, Constants::CLOSE_WINDOW); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Meta+W"))); - mwindow->addAction(cmd, Constants::G_WINDOW_SIZE); - - mwindow->addSeparator(Constants::G_WINDOW_SIZE); - } - - // Show Left Sidebar Action - m_toggleLeftSideBarAction = new QAction(Utils::Icons::TOGGLE_LEFT_SIDEBAR.icon(), - Tr::tr(Constants::TR_SHOW_LEFT_SIDEBAR), - this); - m_toggleLeftSideBarAction->setCheckable(true); - cmd = ActionManager::registerAction(m_toggleLeftSideBarAction, Constants::TOGGLE_LEFT_SIDEBAR); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+0") : Tr::tr("Alt+0"))); - connect(m_toggleLeftSideBarAction, &QAction::triggered, - this, [this](bool visible) { setSidebarVisible(visible, Side::Left); }); - ProxyAction *toggleLeftSideBarProxyAction = - ProxyAction::proxyActionWithIcon(cmd->action(), Utils::Icons::TOGGLE_LEFT_SIDEBAR_TOOLBAR.icon()); - m_toggleLeftSideBarButton->setDefaultAction(toggleLeftSideBarProxyAction); - mview->addAction(cmd, Constants::G_VIEW_VIEWS); - m_toggleLeftSideBarAction->setEnabled(false); - - // Show Right Sidebar Action - m_toggleRightSideBarAction = new QAction(Utils::Icons::TOGGLE_RIGHT_SIDEBAR.icon(), - Tr::tr(Constants::TR_SHOW_RIGHT_SIDEBAR), - this); - m_toggleRightSideBarAction->setCheckable(true); - cmd = ActionManager::registerAction(m_toggleRightSideBarAction, Constants::TOGGLE_RIGHT_SIDEBAR); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Ctrl+Shift+0") : Tr::tr("Alt+Shift+0"))); - connect(m_toggleRightSideBarAction, &QAction::triggered, - this, [this](bool visible) { setSidebarVisible(visible, Side::Right); }); - ProxyAction *toggleRightSideBarProxyAction = - ProxyAction::proxyActionWithIcon(cmd->action(), Utils::Icons::TOGGLE_RIGHT_SIDEBAR_TOOLBAR.icon()); - m_toggleRightSideBarButton->setDefaultAction(toggleRightSideBarProxyAction); - mview->addAction(cmd, Constants::G_VIEW_VIEWS); - m_toggleRightSideBarButton->setEnabled(false); - - registerModeSelectorStyleActions(); - - // Window->Views - ActionContainer *mviews = ActionManager::createMenu(Constants::M_VIEW_VIEWS); - mview->addMenu(mviews, Constants::G_VIEW_VIEWS); - mviews->menu()->setTitle(Tr::tr("&Views")); - - // "Help" separators - mhelp->addSeparator(Constants::G_HELP_SUPPORT); - if (!HostOsInfo::isMacHost()) - mhelp->addSeparator(Constants::G_HELP_ABOUT); - - // About IDE Action - icon = QIcon::fromTheme(QLatin1String("help-about")); - if (HostOsInfo::isMacHost()) - tmpaction = new QAction(icon, Tr::tr("About &%1").arg(Constants::IDE_DISPLAY_NAME), this); // it's convention not to add dots to the about menu - else - tmpaction = new QAction(icon, Tr::tr("About &%1...").arg(Constants::IDE_DISPLAY_NAME), this); - tmpaction->setMenuRole(QAction::AboutRole); - cmd = ActionManager::registerAction(tmpaction, Constants::ABOUT_QTCREATOR); - mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - tmpaction->setEnabled(true); - connect(tmpaction, &QAction::triggered, this, &MainWindow::aboutQtCreator); - - //About Plugins Action - tmpaction = new QAction(Tr::tr("About &Plugins..."), this); - tmpaction->setMenuRole(QAction::ApplicationSpecificRole); - cmd = ActionManager::registerAction(tmpaction, Constants::ABOUT_PLUGINS); - mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - tmpaction->setEnabled(true); - connect(tmpaction, &QAction::triggered, this, &MainWindow::aboutPlugins); - // About Qt Action - // tmpaction = new QAction(Tr::tr("About &Qt..."), this); - // cmd = ActionManager::registerAction(tmpaction, Constants:: ABOUT_QT); - // mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - // tmpaction->setEnabled(true); - // connect(tmpaction, &QAction::triggered, qApp, &QApplication::aboutQt); - - // Change Log Action - tmpaction = new QAction(Tr::tr("Change Log..."), this); - tmpaction->setMenuRole(QAction::ApplicationSpecificRole); - cmd = ActionManager::registerAction(tmpaction, Constants::CHANGE_LOG); - mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - tmpaction->setEnabled(true); - connect(tmpaction, &QAction::triggered, this, &MainWindow::changeLog); - - // Contact - tmpaction = new QAction(Tr::tr("Contact..."), this); - cmd = ActionManager::registerAction(tmpaction, "QtCreator.Contact"); - mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - tmpaction->setEnabled(true); - connect(tmpaction, &QAction::triggered, this, &MainWindow::contact); - - // About sep - if (!HostOsInfo::isMacHost()) { // doesn't have the "About" actions in the Help menu - tmpaction = new QAction(this); - tmpaction->setSeparator(true); - cmd = ActionManager::registerAction(tmpaction, "QtCreator.Help.Sep.About"); - mhelp->addAction(cmd, Constants::G_HELP_ABOUT); - } -} - -void MainWindow::registerModeSelectorStyleActions() -{ - ActionContainer *mview = ActionManager::actionContainer(Constants::M_VIEW); - - // Cycle Mode Selector Styles - m_cycleModeSelectorStyleAction = new QAction(Tr::tr("Cycle Mode Selector Styles"), this); - ActionManager::registerAction(m_cycleModeSelectorStyleAction, Constants::CYCLE_MODE_SELECTOR_STYLE); - connect(m_cycleModeSelectorStyleAction, &QAction::triggered, this, [this] { - ModeManager::cycleModeStyle(); - updateModeSelectorStyleMenu(); - }); - - // Mode Selector Styles - ActionContainer *mmodeLayouts = ActionManager::createMenu(Constants::M_VIEW_MODESTYLES); - mview->addMenu(mmodeLayouts, Constants::G_VIEW_VIEWS); - QMenu *styleMenu = mmodeLayouts->menu(); - styleMenu->setTitle(Tr::tr("Mode Selector Style")); - auto *stylesGroup = new QActionGroup(styleMenu); - stylesGroup->setExclusive(true); - - m_setModeSelectorStyleIconsAndTextAction = stylesGroup->addAction(Tr::tr("Icons and Text")); - connect(m_setModeSelectorStyleIconsAndTextAction, &QAction::triggered, - [] { ModeManager::setModeStyle(ModeManager::Style::IconsAndText); }); - m_setModeSelectorStyleIconsAndTextAction->setCheckable(true); - m_setModeSelectorStyleIconsOnlyAction = stylesGroup->addAction(Tr::tr("Icons Only")); - connect(m_setModeSelectorStyleIconsOnlyAction, &QAction::triggered, - [] { ModeManager::setModeStyle(ModeManager::Style::IconsOnly); }); - m_setModeSelectorStyleIconsOnlyAction->setCheckable(true); - m_setModeSelectorStyleHiddenAction = stylesGroup->addAction(Tr::tr("Hidden")); - connect(m_setModeSelectorStyleHiddenAction, &QAction::triggered, - [] { ModeManager::setModeStyle(ModeManager::Style::Hidden); }); - m_setModeSelectorStyleHiddenAction->setCheckable(true); - - styleMenu->addActions(stylesGroup->actions()); -} - -void MainWindow::openFile() -{ - openFiles(EditorManager::getOpenFilePaths(), ICore::SwitchMode); -} - -static IDocumentFactory *findDocumentFactory(const QList<IDocumentFactory*> &fileFactories, - const FilePath &filePath) -{ - const QString typeName = Utils::mimeTypeForFile(filePath, MimeMatchMode::MatchDefaultAndRemote) - .name(); - return Utils::findOrDefault(fileFactories, [typeName](IDocumentFactory *f) { - return f->mimeTypes().contains(typeName); - }); -} - -/*! - * \internal - * Either opens \a filePaths with editors or loads a project. - * - * \a flags can be used to stop on first failure, indicate that a file name - * might include line numbers and/or switch mode to edit mode. - * - * \a workingDirectory is used when files are opened by a remote client, since - * the file names are relative to the client working directory. - * - * Returns the first opened document. Required to support the \c -block flag - * for client mode. - * - * \sa IPlugin::remoteArguments() - */ -IDocument *MainWindow::openFiles(const FilePaths &filePaths, - ICore::OpenFilesFlags flags, - const FilePath &workingDirectory) -{ - const QList<IDocumentFactory*> documentFactories = IDocumentFactory::allDocumentFactories(); - IDocument *res = nullptr; - - const FilePath workingDirBase = - workingDirectory.isEmpty() ? FilePath::currentWorkingPath() : workingDirectory; - for (const FilePath &filePath : filePaths) { - const FilePath absoluteFilePath = workingDirBase.resolvePath(filePath); - if (IDocumentFactory *documentFactory = findDocumentFactory(documentFactories, filePath)) { - IDocument *document = documentFactory->open(absoluteFilePath); - if (!document) { - if (flags & ICore::StopOnLoadFail) - return res; - } else { - if (!res) - res = document; - if (flags & ICore::SwitchMode) - ModeManager::activateMode(Id(Constants::MODE_EDIT)); - } - } else if (flags & (ICore::SwitchSplitIfAlreadyVisible | ICore::CanContainLineAndColumnNumbers) - || !res) { - QFlags<EditorManager::OpenEditorFlag> emFlags; - if (flags & ICore::SwitchSplitIfAlreadyVisible) - emFlags |= EditorManager::SwitchSplitIfAlreadyVisible; - IEditor *editor = nullptr; - if (flags & ICore::CanContainLineAndColumnNumbers) { - const Link &link = Link::fromString(absoluteFilePath.toString(), true); - editor = EditorManager::openEditorAt(link, {}, emFlags); - } else { - editor = EditorManager::openEditor(absoluteFilePath, {}, emFlags); - } - if (!editor) { - if (flags & ICore::StopOnLoadFail) - return res; - } else if (!res) { - res = editor->document(); - } - } else { - auto factory = IEditorFactory::preferredEditorFactories(absoluteFilePath).value(0); - DocumentModelPrivate::addSuspendedDocument(absoluteFilePath, {}, - factory ? factory->id() : Id()); - } - } - return res; -} - -void MainWindow::setFocusToEditor() -{ - EditorManagerPrivate::doEscapeKeyFocusMoveMagic(); -} - -static void acceptModalDialogs() -{ - const QWidgetList topLevels = QApplication::topLevelWidgets(); - QList<QDialog *> dialogsToClose; - for (QWidget *topLevel : topLevels) { - if (auto dialog = qobject_cast<QDialog *>(topLevel)) { - if (dialog->isModal()) - dialogsToClose.append(dialog); - } - } - for (QDialog *dialog : dialogsToClose) - dialog->accept(); -} - -void MainWindow::exit() -{ - // this function is most likely called from a user action - // that is from an event handler of an object - // since on close we are going to delete everything - // so to prevent the deleting of that object we - // just append it - QMetaObject::invokeMethod( - this, - [this] { - // Modal dialogs block the close event. So close them, in case this was triggered from - // a RestartDialog in the settings dialog. - acceptModalDialogs(); - close(); - }, - Qt::QueuedConnection); -} - -void MainWindow::openFileWith() -{ - const FilePaths filePaths = EditorManager::getOpenFilePaths(); - for (const FilePath &filePath : filePaths) { - bool isExternal; - const Id editorId = EditorManagerPrivate::getOpenWithEditorId(filePath, &isExternal); - if (!editorId.isValid()) - continue; - if (isExternal) - EditorManager::openExternalEditor(filePath, editorId); - else - EditorManagerPrivate::openEditorWith(filePath, editorId); - } -} - -void MainWindow::openFileFromDevice() -{ - openFiles(EditorManager::getOpenFilePaths(QFileDialog::DontUseNativeDialog), ICore::SwitchMode); -} - -IContext *MainWindow::contextObject(QWidget *widget) const -{ - const auto it = m_contextWidgets.find(widget); - return it == m_contextWidgets.end() ? nullptr : it->second; -} - -void MainWindow::addContextObject(IContext *context) -{ - if (!context) - return; - QWidget *widget = context->widget(); - if (m_contextWidgets.find(widget) != m_contextWidgets.end()) - return; - - m_contextWidgets.insert({widget, context}); - connect(context, &QObject::destroyed, this, [this, context] { removeContextObject(context); }); -} - -void MainWindow::removeContextObject(IContext *context) -{ - if (!context) - return; - - disconnect(context, &QObject::destroyed, this, nullptr); - - const auto it = std::find_if(m_contextWidgets.cbegin(), - m_contextWidgets.cend(), - [context](const std::pair<QWidget *, IContext *> &v) { - return v.second == context; - }); - if (it == m_contextWidgets.cend()) - return; - - m_contextWidgets.erase(it); - if (m_activeContext.removeAll(context) > 0) - updateContextObject(m_activeContext); -} - -void MainWindow::updateFocusWidget(QWidget *old, QWidget *now) -{ - Q_UNUSED(old) - - // Prevent changing the context object just because the menu or a menu item is activated - if (qobject_cast<QMenuBar*>(now) || qobject_cast<QMenu*>(now)) - return; - - QList<IContext *> newContext; - if (QWidget *p = QApplication::focusWidget()) { - IContext *context = nullptr; - while (p) { - context = contextObject(p); - if (context) - newContext.append(context); - p = p->parentWidget(); - } - } - - // ignore toplevels that define no context, like popups without parent - if (!newContext.isEmpty() || QApplication::focusWidget() == focusWidget()) - updateContextObject(newContext); -} - -void MainWindow::updateContextObject(const QList<IContext *> &context) -{ - emit m_coreImpl->contextAboutToChange(context); - m_activeContext = context; - updateContext(); - if (debugMainWindow) { - qDebug() << "new context objects =" << context; - for (const IContext *c : context) - qDebug() << (c ? c->widget() : nullptr) << (c ? c->widget()->metaObject()->className() : nullptr); - } -} - -void MainWindow::aboutToShutdown() -{ - disconnect(qApp, &QApplication::focusChanged, this, &MainWindow::updateFocusWidget); - for (auto contextPair : m_contextWidgets) - disconnect(contextPair.second, &QObject::destroyed, this, nullptr); - m_activeContext.clear(); - hide(); -} - -void MainWindow::readSettings() -{ - QSettings *settings = PluginManager::settings(); - settings->beginGroup(QLatin1String(settingsGroup)); - - if (m_overrideColor.isValid()) { - StyleHelper::setBaseColor(m_overrideColor); - // Get adapted base color. - m_overrideColor = StyleHelper::baseColor(); - } else { - StyleHelper::setBaseColor(settings->value(QLatin1String(colorKey), - QColor(StyleHelper::DEFAULT_BASE_COLOR)).value<QColor>()); - } - - m_askConfirmationBeforeExit = settings->value(askBeforeExitKey, askBeforeExitDefault).toBool(); - - { - ModeManager::Style modeStyle = - ModeManager::Style(settings->value(modeSelectorLayoutKey, int(ModeManager::Style::IconsAndText)).toInt()); - - // Migrate legacy setting from Qt Creator 4.6 and earlier - static const char modeSelectorVisibleKey[] = "ModeSelectorVisible"; - if (!settings->contains(modeSelectorLayoutKey) && settings->contains(modeSelectorVisibleKey)) { - bool visible = settings->value(modeSelectorVisibleKey, true).toBool(); - modeStyle = visible ? ModeManager::Style::IconsAndText : ModeManager::Style::Hidden; - } - - ModeManager::setModeStyle(modeStyle); - updateModeSelectorStyleMenu(); - } - - settings->endGroup(); - - EditorManagerPrivate::readSettings(); - m_leftNavigationWidget->restoreSettings(settings); - m_rightNavigationWidget->restoreSettings(settings); - m_rightPaneWidget->readSettings(settings); -} - -void MainWindow::saveSettings() -{ - QtcSettings *settings = PluginManager::settings(); - settings->beginGroup(QLatin1String(settingsGroup)); - - if (!(m_overrideColor.isValid() && StyleHelper::baseColor() == m_overrideColor)) - settings->setValueWithDefault(colorKey, - StyleHelper::requestedBaseColor(), - QColor(StyleHelper::DEFAULT_BASE_COLOR)); - - settings->setValueWithDefault(askBeforeExitKey, - m_askConfirmationBeforeExit, - askBeforeExitDefault); - - settings->endGroup(); - - DocumentManager::saveSettings(); - ActionManager::saveSettings(); - EditorManagerPrivate::saveSettings(); - m_leftNavigationWidget->saveSettings(settings); - m_rightNavigationWidget->saveSettings(settings); - - // TODO Remove some time after Qt Creator 11 - // Work around Qt Creator <= 10 writing the default terminal to the settings. - // TerminalCommand writes the terminal to the settings when changing it, which usually is - // enough. But because of the bug in Qt Creator <= 10 we want to clean up the settings - // even if the user never touched the terminal setting. - if (HostOsInfo::isMacHost()) - TerminalCommand::setTerminalEmulator(TerminalCommand::terminalEmulator()); -} - -void MainWindow::saveWindowSettings() -{ - QSettings *settings = PluginManager::settings(); - settings->beginGroup(QLatin1String(settingsGroup)); - - // On OS X applications usually do not restore their full screen state. - // To be able to restore the correct non-full screen geometry, we have to put - // the window out of full screen before saving the geometry. - // Works around QTBUG-45241 - if (Utils::HostOsInfo::isMacHost() && isFullScreen()) - setWindowState(windowState() & ~Qt::WindowFullScreen); - settings->setValue(QLatin1String(windowGeometryKey), saveGeometry()); - settings->setValue(QLatin1String(windowStateKey), saveState()); - settings->setValue(modeSelectorLayoutKey, int(ModeManager::modeStyle())); - - settings->endGroup(); -} - -void MainWindow::updateModeSelectorStyleMenu() -{ - switch (ModeManager::modeStyle()) { - case ModeManager::Style::IconsAndText: - m_setModeSelectorStyleIconsAndTextAction->setChecked(true); - break; - case ModeManager::Style::IconsOnly: - m_setModeSelectorStyleIconsOnlyAction->setChecked(true); - break; - case ModeManager::Style::Hidden: - m_setModeSelectorStyleHiddenAction->setChecked(true); - break; - } -} - -void MainWindow::updateAdditionalContexts(const Context &remove, const Context &add, - ICore::ContextPriority priority) -{ - for (const Id id : remove) { - if (!id.isValid()) - continue; - int index = m_lowPrioAdditionalContexts.indexOf(id); - if (index != -1) - m_lowPrioAdditionalContexts.removeAt(index); - index = m_highPrioAdditionalContexts.indexOf(id); - if (index != -1) - m_highPrioAdditionalContexts.removeAt(index); - } - - for (const Id id : add) { - if (!id.isValid()) - continue; - Context &cref = (priority == ICore::ContextPriority::High ? m_highPrioAdditionalContexts - : m_lowPrioAdditionalContexts); - if (!cref.contains(id)) - cref.prepend(id); - } - - updateContext(); -} - -void MainWindow::updateContext() -{ - Context contexts = m_highPrioAdditionalContexts; - - for (IContext *context : std::as_const(m_activeContext)) - contexts.add(context->context()); - - contexts.add(m_lowPrioAdditionalContexts); - - Context uniquecontexts; - for (const Id &id : std::as_const(contexts)) { - if (!uniquecontexts.contains(id)) - uniquecontexts.add(id); - } - - ActionManager::setContext(uniquecontexts); - emit m_coreImpl->contextChanged(uniquecontexts); -} - -void MainWindow::aboutToShowRecentFiles() -{ - ActionContainer *aci = ActionManager::actionContainer(Constants::M_FILE_RECENTFILES); - QMenu *menu = aci->menu(); - menu->clear(); - - const QList<DocumentManager::RecentFile> recentFiles = DocumentManager::recentFiles(); - for (int i = 0; i < recentFiles.count(); ++i) { - const DocumentManager::RecentFile file = recentFiles[i]; - - const QString filePath = Utils::quoteAmpersands(file.first.shortNativePath()); - const QString actionText = ActionManager::withNumberAccelerator(filePath, i + 1); - QAction *action = menu->addAction(actionText); - connect(action, &QAction::triggered, this, [file] { - EditorManager::openEditor(file.first, file.second); - }); - } - - bool hasRecentFiles = !recentFiles.isEmpty(); - menu->setEnabled(hasRecentFiles); - - // add the Clear Menu item - if (hasRecentFiles) { - menu->addSeparator(); - QAction *action = menu->addAction(Tr::tr(Constants::TR_CLEAR_MENU)); - connect(action, &QAction::triggered, - DocumentManager::instance(), &DocumentManager::clearRecentFiles); - } -} - -void MainWindow::aboutQtCreator() -{ - if (!m_versionDialog) { - m_versionDialog = new VersionDialog(this); - connect(m_versionDialog, &QDialog::finished, - this, &MainWindow::destroyVersionDialog); - ICore::registerWindow(m_versionDialog, Context("Core.VersionDialog")); - m_versionDialog->show(); - } else { - ICore::raiseWindow(m_versionDialog); - } -} - -void MainWindow::destroyVersionDialog() -{ - if (m_versionDialog) { - m_versionDialog->deleteLater(); - m_versionDialog = nullptr; - } -} - -void MainWindow::aboutPlugins() -{ - PluginDialog dialog(this); - dialog.exec(); -} - -class LogDialog : public QDialog -{ -public: - LogDialog(QWidget *parent) - : QDialog(parent) - {} - bool event(QEvent *event) override - { - if (event->type() == QEvent::ShortcutOverride) { - auto ke = static_cast<QKeyEvent *>(event); - if (ke->key() == Qt::Key_Escape && !ke->modifiers()) { - ke->accept(); - return true; - } - } - return QDialog::event(event); - } -}; - -void MainWindow::changeLog() -{ - static QPointer<LogDialog> dialog; - if (dialog) { - ICore::raiseWindow(dialog); - return; - } - const FilePaths files = - ICore::resourcePath("changelog").dirEntries({{"changes-*"}, QDir::Files}); - static const QRegularExpression versionRegex("\\d+[.]\\d+[.]\\d+"); - using VersionFilePair = std::pair<QVersionNumber, FilePath>; - QList<VersionFilePair> versionedFiles = Utils::transform(files, [](const FilePath &fp) { - const QRegularExpressionMatch match = versionRegex.match(fp.fileName()); - const QVersionNumber version = match.hasMatch() - ? QVersionNumber::fromString(match.captured()) - : QVersionNumber(); - return std::make_pair(version, fp); - }); - Utils::sort(versionedFiles, [](const VersionFilePair &a, const VersionFilePair &b) { - return a.first > b.first; - }); - - auto versionCombo = new QComboBox; - for (const VersionFilePair &f : versionedFiles) - versionCombo->addItem(f.first.toString()); - dialog = new LogDialog(ICore::dialogParent()); - auto versionLayout = new QHBoxLayout; - versionLayout->addWidget(new QLabel(Tr::tr("Version:"))); - versionLayout->addWidget(versionCombo); - versionLayout->addStretch(1); - auto showInExplorer = new QPushButton(FileUtils::msgGraphicalShellAction()); - versionLayout->addWidget(showInExplorer); - auto textEdit = new QTextBrowser; - textEdit->setOpenExternalLinks(true); - - auto aggregate = new Aggregation::Aggregate; - aggregate->add(textEdit); - aggregate->add(new Core::BaseTextFind(textEdit)); - - new MarkdownHighlighter(textEdit->document()); - - auto textEditWidget = new QFrame; - textEditWidget->setFrameStyle(QFrame::NoFrame); - auto findToolBar = new FindToolBarPlaceHolder(dialog); - findToolBar->setLightColored(true); - auto textEditLayout = new QVBoxLayout; - textEditLayout->setContentsMargins(0, 0, 0, 0); - textEditLayout->setSpacing(0); - textEditLayout->addWidget(textEdit); - textEditLayout->addWidget(findToolBar); - textEditWidget->setLayout(textEditLayout); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); - auto dialogLayout = new QVBoxLayout; - dialogLayout->addLayout(versionLayout); - dialogLayout->addWidget(textEditWidget); - dialogLayout->addWidget(buttonBox); - dialog->setLayout(dialogLayout); - dialog->resize(700, 600); - dialog->setWindowTitle(Tr::tr("Change Log")); - dialog->setAttribute(Qt::WA_DeleteOnClose); - ICore::registerWindow(dialog, Context("CorePlugin.VersionDialog")); - - connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::close); - QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); - if (QTC_GUARD(closeButton)) - closeButton->setDefault(true); // grab from "Open in Explorer" button - - const auto showLog = [textEdit, versionedFiles](int index) { - if (index < 0 || index >= versionedFiles.size()) - return; - const FilePath file = versionedFiles.at(index).second; - QString contents = QString::fromUtf8(file.fileContents().value_or(QByteArray())); - // (?<![[\/]) == don't replace if it is preceded by "[" or "/" - // i.e. if it already is part of a link - static const QRegularExpression bugexpr(R"((?<![[\/])((QT(CREATOR)?BUG|PYSIDE)-\d+))"); - contents.replace(bugexpr, R"([\1](https://bugreports.qt.io/browse/\1))"); - static const QRegularExpression docexpr("https://doc[.]qt[.]io/qtcreator/([.a-zA-Z/_-]*)"); - QList<QRegularExpressionMatch> matches; - for (const QRegularExpressionMatch &m : docexpr.globalMatch(contents)) - matches.append(m); - Utils::reverseForeach(matches, [&contents](const QRegularExpressionMatch &match) { - const QString qthelpUrl = "qthelp://org.qt-project.qtcreator/doc/" + match.captured(1); - if (!HelpManager::fileData(qthelpUrl).isEmpty()) - contents.replace(match.capturedStart(), match.capturedLength(), qthelpUrl); - }); - textEdit->setMarkdown(contents); - }; - connect(versionCombo, &QComboBox::currentIndexChanged, textEdit, showLog); - showLog(versionCombo->currentIndex()); - - connect(showInExplorer, &QPushButton::clicked, this, [versionCombo, versionedFiles] { - const int index = versionCombo->currentIndex(); - if (index >= 0 && index < versionedFiles.size()) - FileUtils::showInGraphicalShell(ICore::dialogParent(), versionedFiles.at(index).second); - else - FileUtils::showInGraphicalShell(ICore::dialogParent(), ICore::resourcePath("changelog")); - }); - - dialog->show(); -} - -void MainWindow::contact() -{ - QMessageBox dlg(QMessageBox::Information, Tr::tr("Contact"), - Tr::tr("<p>Qt Creator developers can be reached at the Qt Creator mailing list:</p>" - "%1" - "<p>or the #qt-creator channel on Libera.Chat IRC:</p>" - "%2" - "<p>Our bug tracker is located at %3.</p>" - "<p>Please use %4 for bigger chunks of text.</p>") - .arg("<p>    " - "<a href=\"https://lists.qt-project.org/listinfo/qt-creator\">" - "mailto:qt-creator@qt-project.org" - "</a></p>") - .arg("<p>    " - "<a href=\"https://web.libera.chat/#qt-creator\">" - "https://web.libera.chat/#qt-creator" - "</a></p>") - .arg("<a href=\"https://bugreports.qt.io/projects/QTCREATORBUG\">" - "https://bugreports.qt.io" - "</a>") - .arg("<a href=\"https://pastebin.com\">" - "https://pastebin.com" - "</a>"), - QMessageBox::Ok, this); - dlg.exec(); -} - -QPrinter *MainWindow::printer() const -{ - if (!m_printer) - m_printer = new QPrinter(QPrinter::HighResolution); - return m_printer; -} - -void MainWindow::restoreWindowState() -{ - QSettings *settings = PluginManager::settings(); - settings->beginGroup(QLatin1String(settingsGroup)); - if (!restoreGeometry(settings->value(QLatin1String(windowGeometryKey)).toByteArray())) - resize(1260, 700); // size without window decoration - restoreState(settings->value(QLatin1String(windowStateKey)).toByteArray()); - settings->endGroup(); - show(); - StatusBarManager::restoreSettings(); -} - -} // namespace Internal -} // namespace Core diff --git a/src/plugins/coreplugin/mainwindow.h b/src/plugins/coreplugin/mainwindow.h deleted file mode 100644 index b254cb268f3..00000000000 --- a/src/plugins/coreplugin/mainwindow.h +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "icontext.h" -#include "icore.h" - -#include <utils/appmainwindow.h> -#include <utils/dropsupport.h> - -#include <QColor> -#include <QTimer> - -#include <functional> -#include <unordered_map> - -QT_BEGIN_NAMESPACE -class QPrinter; -class QToolButton; -QT_END_NAMESPACE - -namespace Utils { -class InfoBar; -} - -namespace Core { - -class EditorManager; -class ExternalToolManager; -class IDocument; -class JsExpander; -class MessageManager; -class ModeManager; -class ProgressManager; -class NavigationWidget; -enum class Side; -class RightPaneWidget; -class SettingsDatabase; -class VcsManager; - -namespace Internal { - -class FancyTabWidget; -class GeneralSettings; -class ProgressManagerPrivate; -class ShortcutSettings; -class ToolSettings; -class MimeTypeSettings; -class VersionDialog; -class WindowSupport; -class SystemEditor; -class SystemSettings; - -class MainWindow : public Utils::AppMainWindow -{ - Q_OBJECT - -public: - MainWindow(); - ~MainWindow() override; - - void init(); - void extensionsInitialized(); - void aboutToShutdown(); - - IContext *contextObject(QWidget *widget) const; - void addContextObject(IContext *context); - void removeContextObject(IContext *context); - - static IDocument *openFiles(const Utils::FilePaths &filePaths, - ICore::OpenFilesFlags flags, - const Utils::FilePath &workingDirectory = {}); - - inline SettingsDatabase *settingsDatabase() const { return m_settingsDatabase; } - virtual QPrinter *printer() const; - IContext * currentContextObject() const; - QStatusBar *statusBar() const; - Utils::InfoBar *infoBar() const; - - void updateAdditionalContexts(const Context &remove, const Context &add, - ICore::ContextPriority priority); - - bool askConfirmationBeforeExit() const; - void setAskConfirmationBeforeExit(bool ask); - - void setOverrideColor(const QColor &color); - - QStringList additionalAboutInformation() const; - void appendAboutInformation(const QString &line); - - void addPreCloseListener(const std::function<bool()> &listener); - - void saveSettings(); - - void restart(); - - void openFileFromDevice(); - - void restartTrimmer(); - -public slots: - static void openFileWith(); - void exit(); - -protected: - void closeEvent(QCloseEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - -private: - static void openFile(); - void aboutToShowRecentFiles(); - static void setFocusToEditor(); - void aboutQtCreator(); - void aboutPlugins(); - void changeLog(); - void contact(); - void updateFocusWidget(QWidget *old, QWidget *now); - NavigationWidget *navigationWidget(Side side) const; - void setSidebarVisible(bool visible, Side side); - void destroyVersionDialog(); - void openDroppedFiles(const QList<Utils::DropSupport::FileSpec> &files); - void restoreWindowState(); - - void updateContextObject(const QList<IContext *> &context); - void updateContext(); - - void registerDefaultContainers(); - void registerDefaultActions(); - void registerModeSelectorStyleActions(); - - void readSettings(); - void saveWindowSettings(); - - void updateModeSelectorStyleMenu(); - - ICore *m_coreImpl = nullptr; - QTimer m_trimTimer; - QStringList m_aboutInformation; - Context m_highPrioAdditionalContexts; - Context m_lowPrioAdditionalContexts; - SettingsDatabase *m_settingsDatabase = nullptr; - mutable QPrinter *m_printer = nullptr; - WindowSupport *m_windowSupport = nullptr; - EditorManager *m_editorManager = nullptr; - ExternalToolManager *m_externalToolManager = nullptr; - MessageManager *m_messageManager = nullptr; - ProgressManagerPrivate *m_progressManager = nullptr; - JsExpander *m_jsExpander = nullptr; - VcsManager *m_vcsManager = nullptr; - ModeManager *m_modeManager = nullptr; - FancyTabWidget *m_modeStack = nullptr; - NavigationWidget *m_leftNavigationWidget = nullptr; - NavigationWidget *m_rightNavigationWidget = nullptr; - RightPaneWidget *m_rightPaneWidget = nullptr; - VersionDialog *m_versionDialog = nullptr; - - QList<IContext *> m_activeContext; - - std::unordered_map<QWidget *, IContext *> m_contextWidgets; - - GeneralSettings *m_generalSettings = nullptr; - SystemSettings *m_systemSettings = nullptr; - ShortcutSettings *m_shortcutSettings = nullptr; - ToolSettings *m_toolSettings = nullptr; - MimeTypeSettings *m_mimeTypeSettings = nullptr; - SystemEditor *m_systemEditor = nullptr; - - // actions - QAction *m_focusToEditor = nullptr; - QAction *m_newAction = nullptr; - QAction *m_openAction = nullptr; - QAction *m_openWithAction = nullptr; - QAction *m_openFromDeviceAction = nullptr; - QAction *m_saveAllAction = nullptr; - QAction *m_exitAction = nullptr; - QAction *m_optionsAction = nullptr; - QAction *m_loggerAction = nullptr; - QAction *m_toggleLeftSideBarAction = nullptr; - QAction *m_toggleRightSideBarAction = nullptr; - QAction *m_cycleModeSelectorStyleAction = nullptr; - QAction *m_setModeSelectorStyleIconsAndTextAction = nullptr; - QAction *m_setModeSelectorStyleHiddenAction = nullptr; - QAction *m_setModeSelectorStyleIconsOnlyAction = nullptr; - QAction *m_themeAction = nullptr; - - QToolButton *m_toggleLeftSideBarButton = nullptr; - QToolButton *m_toggleRightSideBarButton = nullptr; - bool m_askConfirmationBeforeExit = false; - QColor m_overrideColor; - QList<std::function<bool()>> m_preCloseListeners; -}; - -} // namespace Internal -} // namespace Core diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index fc4cd9459a0..8a2e0b43584 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -442,6 +442,10 @@ int ManhattanStyle::styleHint(StyleHint hint, const QStyleOption *option, const ret = QGuiApplication::keyboardModifiers() == (HostOsInfo::isMacHost() ? Qt::MetaModifier : Qt::ControlModifier); break; + case QStyle::SH_Slider_AbsoluteSetButtons: + // Make QSlider jump on left mouse click + ret = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton; + break; default: break; } diff --git a/src/plugins/coreplugin/messagemanager.h b/src/plugins/coreplugin/messagemanager.h index 1cd1950145b..98eb0635a12 100644 --- a/src/plugins/coreplugin/messagemanager.h +++ b/src/plugins/coreplugin/messagemanager.h @@ -4,7 +4,6 @@ #pragma once #include "core_global.h" -#include "ioutputpane.h" #include <QMetaType> #include <QObject> @@ -15,7 +14,12 @@ QT_END_NAMESPACE namespace Core { -namespace Internal { class MainWindow; } +class ICore; + +namespace Internal { +class ICorePrivate; +class MainWindow; +} class CORE_EXPORT MessageManager : public QObject { @@ -40,7 +44,10 @@ private: ~MessageManager() override; static void init(); - friend class Core::Internal::MainWindow; + + friend class ICore; + friend class Internal::ICorePrivate; + friend class Internal::MainWindow; }; } // namespace Core diff --git a/src/plugins/coreplugin/messageoutputwindow.cpp b/src/plugins/coreplugin/messageoutputwindow.cpp index 2d98164e763..1cfe384ac00 100644 --- a/src/plugins/coreplugin/messageoutputwindow.cpp +++ b/src/plugins/coreplugin/messageoutputwindow.cpp @@ -20,6 +20,10 @@ const char zoomSettingsKey[] = "Core/MessageOutput/Zoom"; MessageOutputWindow::MessageOutputWindow() { + setId("GeneralMessages"); + setDisplayName(Tr::tr("General Messages")); + setPriorityInStatusBar(-100); + m_widget = new OutputWindow(Context(Constants::C_GENERAL_OUTPUT_PANE), zoomSettingsKey); m_widget->setReadOnly(true); @@ -65,21 +69,11 @@ QWidget *MessageOutputWindow::outputWidget(QWidget *parent) return m_widget; } -QString MessageOutputWindow::displayName() const -{ - return Tr::tr("General Messages"); -} - void MessageOutputWindow::append(const QString &text) { m_widget->appendMessage(text, Utils::GeneralMessageFormat); } -int MessageOutputWindow::priorityInStatusBar() const -{ - return -1; -} - bool MessageOutputWindow::canNext() const { return false; diff --git a/src/plugins/coreplugin/messageoutputwindow.h b/src/plugins/coreplugin/messageoutputwindow.h index 515c8cee51b..ef0d93a97cf 100644 --- a/src/plugins/coreplugin/messageoutputwindow.h +++ b/src/plugins/coreplugin/messageoutputwindow.h @@ -20,8 +20,6 @@ public: QWidget *outputWidget(QWidget *parent) override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void append(const QString &text); diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index 7edba5625e9..c2bf8202ea4 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -98,13 +98,13 @@ public: void load(); - QList<EditorType *> handlersForMimeType(const Utils::MimeType &mimeType) const; - EditorType *defaultHandlerForMimeType(const Utils::MimeType &mimeType) const; + QList<IEditorFactory *> handlersForMimeType(const Utils::MimeType &mimeType) const; + IEditorFactory *defaultHandlerForMimeType(const Utils::MimeType &mimeType) const; void resetUserDefaults(); QList<Utils::MimeType> m_mimeTypes; - mutable QHash<Utils::MimeType, QList<EditorType *>> m_handlersByMimeType; - QHash<Utils::MimeType, EditorType *> m_userDefault; + mutable QHash<Utils::MimeType, QList<IEditorFactory *>> m_handlersByMimeType; + QHash<QString, IEditorFactory *> m_userDefault; }; int MimeTypeSettingsModel::rowCount(const QModelIndex &) const @@ -139,7 +139,7 @@ QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) co if (column == 0) { return type.name(); } else { - EditorType *defaultHandler = defaultHandlerForMimeType(type); + IEditorFactory *defaultHandler = defaultHandlerForMimeType(type); return defaultHandler ? defaultHandler->displayName() : QString(); } } else if (role == Qt::EditRole) { @@ -149,7 +149,7 @@ QVariant MimeTypeSettingsModel::data(const QModelIndex &modelIndex, int role) co } else if (role == Qt::FontRole) { if (column == 1) { const Utils::MimeType &type = m_mimeTypes.at(modelIndex.row()); - if (m_userDefault.contains(type)) { + if (m_userDefault.contains(type.name())) { QFont font = QGuiApplication::font(); font.setItalic(true); return font; @@ -166,17 +166,17 @@ bool MimeTypeSettingsModel::setData(const QModelIndex &index, const QVariant &va { if (role != int(Role::DefaultHandler) || index.column() != 1) return false; - auto factory = value.value<EditorType *>(); + auto factory = value.value<IEditorFactory *>(); QTC_ASSERT(factory, return false); const int row = index.row(); QTC_ASSERT(row >= 0 && row < m_mimeTypes.size(), return false); const Utils::MimeType mimeType = m_mimeTypes.at(row); - const QList<EditorType *> handlers = handlersForMimeType(mimeType); + const QList<IEditorFactory *> handlers = handlersForMimeType(mimeType); QTC_ASSERT(handlers.contains(factory), return false); if (handlers.first() == factory) // selection is the default anyhow - m_userDefault.remove(mimeType); + m_userDefault.remove(mimeType.name()); else - m_userDefault.insert(mimeType, factory); + m_userDefault.insert(mimeType.name(), factory); emit dataChanged(index, index); return true; } @@ -200,18 +200,18 @@ void MimeTypeSettingsModel::load() endResetModel(); } -QList<EditorType *> MimeTypeSettingsModel::handlersForMimeType(const Utils::MimeType &mimeType) const +QList<IEditorFactory *> MimeTypeSettingsModel::handlersForMimeType(const Utils::MimeType &mimeType) const { if (!m_handlersByMimeType.contains(mimeType)) - m_handlersByMimeType.insert(mimeType, EditorType::defaultEditorTypes(mimeType)); + m_handlersByMimeType.insert(mimeType, IEditorFactory::defaultEditorFactories(mimeType)); return m_handlersByMimeType.value(mimeType); } -EditorType *MimeTypeSettingsModel::defaultHandlerForMimeType(const Utils::MimeType &mimeType) const +IEditorFactory *MimeTypeSettingsModel::defaultHandlerForMimeType(const Utils::MimeType &mimeType) const { - if (m_userDefault.contains(mimeType)) - return m_userDefault.value(mimeType); - const QList<EditorType *> handlers = handlersForMimeType(mimeType); + if (m_userDefault.contains(mimeType.name())) + return m_userDefault.value(mimeType.name()); + const QList<IEditorFactory *> handlers = handlersForMimeType(mimeType); return handlers.isEmpty() ? nullptr : handlers.first(); } @@ -633,7 +633,7 @@ void MimeTypeSettingsPrivate::writeUserModifiedMimeTypes() { static Utils::FilePath modifiedMimeTypesFile = ICore::userResourcePath(kModifiedMimeTypesFile); - if (QFile::exists(modifiedMimeTypesFile.toString()) + if (QFileInfo::exists(modifiedMimeTypesFile.toString()) || QDir().mkpath(modifiedMimeTypesFile.parentDir().toString())) { QFile file(modifiedMimeTypesFile.toString()); if (file.open(QFile::WriteOnly | QFile::Truncate)) { @@ -746,19 +746,25 @@ MimeTypeSettingsPrivate::UserMimeTypeHash MimeTypeSettingsPrivate::readUserModif return userMimeTypes; } -void MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(const UserMimeTypeHash &mimeTypes) +static void registerUserModifiedMimeTypes(const MimeTypeSettingsPrivate::UserMimeTypeHash &mimeTypes) { - // register in mime data base, and remember for later for (auto it = mimeTypes.constBegin(); it != mimeTypes.constEnd(); ++it) { Utils::MimeType mt = Utils::mimeTypeForName(it.key()); - if (!mt.isValid()) // loaded from settings + if (!mt.isValid()) continue; - m_userModifiedMimeTypes.insert(it.key(), it.value()); Utils::setGlobPatternsForMimeType(mt, it.value().globPatterns); Utils::setMagicRulesForMimeType(mt, it.value().rules); } } +void MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(const UserMimeTypeHash &mimeTypes) +{ + // register in mime data base, and remember for later + for (auto it = mimeTypes.constBegin(); it != mimeTypes.constEnd(); ++it) + m_userModifiedMimeTypes.insert(it.key(), it.value()); + registerUserModifiedMimeTypes(mimeTypes); +} + // MimeTypeSettingsPage MimeTypeSettings::MimeTypeSettings() @@ -792,8 +798,9 @@ QStringList MimeTypeSettings::keywords() const void MimeTypeSettings::restoreSettings() { MimeTypeSettingsPrivate::UserMimeTypeHash mimetypes - = MimeTypeSettingsPrivate::readUserModifiedMimeTypes(); - MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(mimetypes); + = MimeTypeSettingsPrivate::readUserModifiedMimeTypes(); + MimeTypeSettingsPrivate::m_userModifiedMimeTypes = mimetypes; + Utils::addMimeInitializer([mimetypes] { registerUserModifiedMimeTypes(mimetypes); }); } QWidget *MimeEditorDelegate::createEditor(QWidget *parent, @@ -808,13 +815,13 @@ QWidget *MimeEditorDelegate::createEditor(QWidget *parent, void MimeEditorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { auto box = static_cast<QComboBox *>(editor); - const auto factories = index.model()->data(index, Qt::EditRole).value<QList<EditorType *>>(); - for (EditorType *factory : factories) + const auto factories = index.model()->data(index, Qt::EditRole).value<QList<IEditorFactory *>>(); + for (IEditorFactory *factory : factories) box->addItem(factory->displayName(), QVariant::fromValue(factory)); int currentIndex = factories.indexOf( index.model() ->data(index, int(MimeTypeSettingsModel::Role::DefaultHandler)) - .value<EditorType *>()); + .value<IEditorFactory *>()); if (QTC_GUARD(currentIndex != -1)) box->setCurrentIndex(currentIndex); } diff --git a/src/plugins/coreplugin/mimetypesettings.h b/src/plugins/coreplugin/mimetypesettings.h index fd4a69685e3..1080b0746b9 100644 --- a/src/plugins/coreplugin/mimetypesettings.h +++ b/src/plugins/coreplugin/mimetypesettings.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include "dialogs/ioptionspage.h" namespace Core::Internal { diff --git a/src/plugins/coreplugin/modemanager.cpp b/src/plugins/coreplugin/modemanager.cpp index 9608ed6caff..cebc5f8422d 100644 --- a/src/plugins/coreplugin/modemanager.cpp +++ b/src/plugins/coreplugin/modemanager.cpp @@ -10,7 +10,6 @@ #include "fancytabwidget.h" #include "icore.h" #include "imode.h" -#include "mainwindow.h" #include <extensionsystem/pluginmanager.h> @@ -69,7 +68,6 @@ struct ModeManagerPrivate void activateModeHelper(Id id); void extensionsInitializedHelper(); - Internal::MainWindow *m_mainWindow; Internal::FancyTabWidget *m_modeStack; Internal::FancyActionBar *m_actionBar; QMap<QAction*, int> m_actions; @@ -102,12 +100,10 @@ void ModeManagerPrivate::showMenu(int index, QMouseEvent *event) m_modes.at(index)->menu()->popup(event->globalPosition().toPoint()); } -ModeManager::ModeManager(Internal::MainWindow *mainWindow, - Internal::FancyTabWidget *modeStack) +ModeManager::ModeManager(Internal::FancyTabWidget *modeStack) { m_instance = this; d = new ModeManagerPrivate(); - d->m_mainWindow = mainWindow; d->m_modeStack = modeStack; d->m_oldCurrent = -1; d->m_actionBar = new Internal::FancyActionBar(modeStack); @@ -205,7 +201,7 @@ void ModeManagerPrivate::appendMode(IMode *mode) { const int index = m_modeCommands.count(); - m_mainWindow->addContextObject(mode); + ICore::addContextObject(mode); m_modeStack->insertTab(index, mode->widget(), mode->icon(), mode->displayName(), mode->menu() != nullptr); @@ -246,7 +242,7 @@ void ModeManager::removeMode(IMode *mode) d->m_modeCommands.remove(index); d->m_modeStack->removeTab(index); - d->m_mainWindow->removeContextObject(mode); + ICore::removeContextObject(mode); } void ModeManagerPrivate::enabledStateChanged(IMode *mode) diff --git a/src/plugins/coreplugin/modemanager.h b/src/plugins/coreplugin/modemanager.h index 5f013ffa507..96b8b252c23 100644 --- a/src/plugins/coreplugin/modemanager.h +++ b/src/plugins/coreplugin/modemanager.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "core_global.h" #include <utils/id.h> @@ -15,11 +15,12 @@ QT_END_NAMESPACE namespace Core { +class ICore; class IMode; namespace Internal { - class MainWindow; - class FancyTabWidget; +class FancyTabWidget; +class ICorePrivate; } class CORE_EXPORT ModeManager : public QObject @@ -47,7 +48,6 @@ public: static void removeMode(IMode *mode); -public slots: static void setModeStyle(Style layout); static void cycleModeStyle(); @@ -58,7 +58,7 @@ signals: void currentModeChanged(Utils::Id mode, Utils::Id oldMode = {}); private: - explicit ModeManager(Internal::MainWindow *mainWindow, Internal::FancyTabWidget *modeStack); + explicit ModeManager(Internal::FancyTabWidget *modeStack); ~ModeManager() override; static void extensionsInitialized(); @@ -67,8 +67,9 @@ private: void currentTabAboutToChange(int index); void currentTabChanged(int index); + friend class ICore; friend class IMode; - friend class Core::Internal::MainWindow; + friend class Internal::ICorePrivate; }; } // namespace Core diff --git a/src/plugins/coreplugin/navigationsubwidget.cpp b/src/plugins/coreplugin/navigationsubwidget.cpp index 838635c7b2f..fd9d0e5daed 100644 --- a/src/plugins/coreplugin/navigationsubwidget.cpp +++ b/src/plugins/coreplugin/navigationsubwidget.cpp @@ -165,7 +165,7 @@ void NavigationSubWidget::restoreSettings() if (!m_navigationWidget || !factory()) return; - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(m_parentWidget->settingsGroup()); factory()->restoreSettings(settings, position(), m_navigationWidget); settings->endGroup(); diff --git a/src/plugins/coreplugin/navigationwidget.cpp b/src/plugins/coreplugin/navigationwidget.cpp index 2c2634d9478..d2b1dafcd51 100644 --- a/src/plugins/coreplugin/navigationwidget.cpp +++ b/src/plugins/coreplugin/navigationwidget.cpp @@ -7,7 +7,6 @@ #include "coreplugintr.h" #include "icontext.h" #include "icore.h" -#include "imode.h" #include "inavigationwidgetfactory.h" #include "modemanager.h" #include "navigationsubwidget.h" @@ -15,11 +14,9 @@ #include <utils/qtcassert.h> #include <utils/utilsicons.h> +#include <QAction> #include <QCoreApplication> #include <QDebug> -#include <QSettings> - -#include <QAction> #include <QHBoxLayout> #include <QResizeEvent> #include <QStandardItemModel> @@ -249,11 +246,9 @@ void NavigationWidget::setFactories(const QList<INavigationWidgetFactory *> &fac updateToggleText(); } -QString NavigationWidget::settingsGroup() const +Key NavigationWidget::settingsGroup() const { - const QString side(d->m_side == Side::Left ? QStringLiteral("Left") - : QStringLiteral("Right")); - return QStringLiteral("Navigation%1").arg(side); + return d->m_side == Side::Left ? Key("NavigationLeft") : Key("NavigationRight"); } int NavigationWidget::storedWidth() @@ -301,7 +296,9 @@ static QIcon closeIconForSide(Side side, int itemCount) : Utils::Icons::CLOSE_SPLIT_RIGHT.icon(); } -Internal::NavigationSubWidget *NavigationWidget::insertSubItem(int position, int factoryIndex) +Internal::NavigationSubWidget *NavigationWidget::insertSubItem(int position, + int factoryIndex, + bool updateActivationsMap) { for (int pos = position + 1; pos < d->m_subWidgets.size(); ++pos) { Internal::NavigationSubWidget *nsw = d->m_subWidgets.at(pos); @@ -327,7 +324,8 @@ Internal::NavigationSubWidget *NavigationWidget::insertSubItem(int position, int d->m_subWidgets.insert(position, nsw); d->m_subWidgets.at(0)->setCloseIcon(closeIconForSide(d->m_side, d->m_subWidgets.size())); - NavigationWidgetPrivate::updateActivationsMap(nsw->factory()->id(), {d->m_side, position}); + if (updateActivationsMap) + NavigationWidgetPrivate::updateActivationsMap(nsw->factory()->id(), {d->m_side, position}); return nsw; } @@ -400,16 +398,19 @@ void NavigationWidget::saveSettings(QtcSettings *settings) settings->setValue(settingsKey("VerticalPosition"), saveState()); settings->setValue(settingsKey("Width"), d->m_width); - const QString activationKey = QStringLiteral("ActivationPosition."); + const Key activationKey = "ActivationPosition."; const auto keys = NavigationWidgetPrivate::s_activationsMap.keys(); for (const auto &factoryId : keys) { const auto &info = NavigationWidgetPrivate::s_activationsMap[factoryId]; + const Utils::Key key = settingsKey(activationKey + factoryId.name()); if (info.side == d->m_side) - settings->setValue(settingsKey(activationKey + factoryId.toString()), info.position); + settings->setValue(key, info.position); + else + settings->remove(key); } } -void NavigationWidget::restoreSettings(QSettings *settings) +void NavigationWidget::restoreSettings(QtcSettings *settings) { if (!d->m_factoryModel->rowCount()) { // We have no widgets to show! @@ -438,7 +439,7 @@ void NavigationWidget::restoreSettings(QSettings *settings) int index = factoryIndex(Id::fromString(id)); if (index >= 0) { // Only add if the id was actually found! - insertSubItem(position, index); + insertSubItem(position, index, /*updateActivationsMap=*/false); ++position; } else { restoreSplitterState = false; @@ -447,7 +448,9 @@ void NavigationWidget::restoreSettings(QSettings *settings) if (d->m_subWidgets.isEmpty()) // Make sure we have at least the projects widget or outline widget - insertSubItem(0, qMax(0, factoryIndex(Id::fromString(defaultFirstView(d->m_side))))); + insertSubItem(0, + qMax(0, factoryIndex(Id::fromString(defaultFirstView(d->m_side)))), + /*updateActivationsMap=*/false); setShown(settings->value(settingsKey("Visible"), defaultVisible(d->m_side)).toBool()); @@ -477,7 +480,7 @@ void NavigationWidget::restoreSettings(QSettings *settings) if (!key.startsWith(activationKey)) continue; - int position = settings->value(key).toInt(); + int position = settings->value(keyFromString(key)).toInt(); Id factoryId = Id::fromString(key.mid(activationKey.length())); NavigationWidgetPrivate::updateActivationsMap(factoryId, {d->m_side, position}); } @@ -524,9 +527,9 @@ int NavigationWidget::factoryIndex(Id id) return -1; } -QString NavigationWidget::settingsKey(const QString &key) const +Key NavigationWidget::settingsKey(const Key &key) const { - return QStringLiteral("%1/%2").arg(settingsGroup(), key); + return settingsGroup() + '/' + key; } QHash<Id, Command *> NavigationWidget::commandMap() const diff --git a/src/plugins/coreplugin/navigationwidget.h b/src/plugins/coreplugin/navigationwidget.h index 776fd682d49..40ea1a5fa0c 100644 --- a/src/plugins/coreplugin/navigationwidget.h +++ b/src/plugins/coreplugin/navigationwidget.h @@ -3,9 +3,10 @@ #pragma once -#include <coreplugin/minisplitter.h> +#include "minisplitter.h" #include <utils/id.h> +#include <utils/store.h> #include <QHash> @@ -69,9 +70,9 @@ public: void setFactories(const QList<INavigationWidgetFactory*> &factories); - QString settingsGroup() const; + Utils::Key settingsGroup() const; void saveSettings(Utils::QtcSettings *settings); - void restoreSettings(QSettings *settings); + void restoreSettings(Utils::QtcSettings *settings); QWidget *activateSubWidget(Utils::Id factoryId, int preferredPosition); void closeSubWidgets(); @@ -96,9 +97,11 @@ protected: private: void closeSubWidget(Internal::NavigationSubWidget *subWidget); void updateToggleText(); - Internal::NavigationSubWidget *insertSubItem(int position, int factoryIndex); + Internal::NavigationSubWidget *insertSubItem(int position, + int factoryIndex, + bool updateActivationsMap = true); int factoryIndex(Utils::Id id); - QString settingsKey(const QString &key) const; + Utils::Key settingsKey(const Utils::Key &key) const; NavigationWidgetPrivate *d; }; diff --git a/src/plugins/coreplugin/opendocumentstreeview.cpp b/src/plugins/coreplugin/opendocumentstreeview.cpp index 4c1ad171ee2..3b19d659a54 100644 --- a/src/plugins/coreplugin/opendocumentstreeview.cpp +++ b/src/plugins/coreplugin/opendocumentstreeview.cpp @@ -81,7 +81,6 @@ OpenDocumentsTreeView::OpenDocumentsTreeView(QWidget *parent) : m_delegate = new Internal::OpenDocumentsDelegate(this); setItemDelegate(m_delegate); setRootIsDecorated(false); - setUniformRowHeights(true); setTextElideMode(Qt::ElideMiddle); setFrameStyle(QFrame::NoFrame); setAttribute(Qt::WA_MacShowFocusRect, false); diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index 037779f0bfb..66fddd1b5d4 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -20,10 +20,11 @@ #include <utils/algorithm.h> #include <utils/hostosinfo.h> -#include <utils/styledbar.h> -#include <utils/stylehelper.h> +#include <utils/layoutbuilder.h> #include <utils/proxyaction.h> #include <utils/qtcassert.h> +#include <utils/styledbar.h> +#include <utils/stylehelper.h> #include <utils/theme/theme.h> #include <utils/utilsicons.h> @@ -32,14 +33,14 @@ #include <QComboBox> #include <QDebug> #include <QFocusEvent> -#include <QHBoxLayout> #include <QLabel> +#include <QLayout> #include <QMenu> #include <QPainter> -#include <QStyle> #include <QStackedWidget> -#include <QToolButton> +#include <QStyle> #include <QTimeLine> +#include <QToolButton> using namespace Utils; using namespace Core::Internal; @@ -102,6 +103,63 @@ QList<QWidget *> IOutputPane::toolBarWidgets() const return widgets << m_zoomInButton << m_zoomOutButton; } +/*! + Returns the ID of the output pane. +*/ +Id IOutputPane::id() const +{ + return m_id; +} + +/*! + Sets the ID of the output pane to \a id. + This is used for persisting the visibility state. +*/ +void IOutputPane::setId(const Utils::Id &id) +{ + m_id = id; +} + +/*! + Returns the translated display name of the output pane. +*/ +QString IOutputPane::displayName() const +{ + return m_displayName; +} + +/*! + Determines the position of the output pane on the status bar and the + default visibility. + \sa setPriorityInStatusBar() +*/ +int IOutputPane::priorityInStatusBar() const +{ + return m_priority; +} + +/*! + Sets the position of the output pane on the status bar and the default + visibility to \a priority. + \list + \li higher numbers are further to the front + \li >= 0 are shown in status bar by default + \li < 0 are not shown in status bar by default + \endlist +*/ +void IOutputPane::setPriorityInStatusBar(int priority) +{ + m_priority = priority; +} + +/*! + Sets the translated display name of the output pane to \a name. +*/ +void IOutputPane::setDisplayName(const QString &name) +{ + m_displayName = name; +} + void IOutputPane::visibilityChanged(bool /*visible*/) { } @@ -116,7 +174,7 @@ void IOutputPane::setWheelZoomEnabled(bool enabled) emit wheelZoomEnabledChanged(enabled); } -void IOutputPane::setupFilterUi(const QString &historyKey) +void IOutputPane::setupFilterUi(const Key &historyKey) { m_filterOutputLineEdit = new FancyLineEdit; m_filterActionRegexp = new QAction(this); @@ -284,10 +342,12 @@ void OutputPaneManager::updateStatusButtons(bool visible) void OutputPaneManager::updateMaximizeButton(bool maximized) { if (maximized) { - m_instance->m_minMaxAction->setIcon(m_instance->m_minimizeIcon); + static const QIcon icon = Utils::Icons::ARROW_DOWN.icon(); + m_instance->m_minMaxAction->setIcon(icon); m_instance->m_minMaxAction->setText(Tr::tr("Minimize")); } else { - m_instance->m_minMaxAction->setIcon(m_instance->m_maximizeIcon); + static const QIcon icon = Utils::Icons::ARROW_UP.icon(); + m_instance->m_minMaxAction->setIcon(icon); m_instance->m_minMaxAction->setText(Tr::tr("Maximize")); } } @@ -306,21 +366,17 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : QWidget(parent), m_titleLabel(new QLabel), m_manageButton(new OutputPaneManageButton), - m_closeButton(new QToolButton), - m_minMaxButton(new QToolButton), m_outputWidgetPane(new QStackedWidget), - m_opToolBarWidgets(new QStackedWidget), - m_minimizeIcon(Utils::Icons::ARROW_DOWN.icon()), - m_maximizeIcon(Utils::Icons::ARROW_UP.icon()) + m_opToolBarWidgets(new QStackedWidget) { setWindowTitle(Tr::tr("Output")); m_titleLabel->setContentsMargins(5, 0, 5, 0); - m_clearAction = new QAction(this); - m_clearAction->setIcon(Utils::Icons::CLEAN.icon()); - m_clearAction->setText(Tr::tr("Clear")); - connect(m_clearAction, &QAction::triggered, this, &OutputPaneManager::clearPage); + auto clearAction = new QAction(this); + clearAction->setIcon(Utils::Icons::CLEAN.icon()); + clearAction->setText(Tr::tr("Clear")); + connect(clearAction, &QAction::triggered, this, &OutputPaneManager::clearPage); m_nextAction = new QAction(this); m_nextAction->setIcon(Utils::Icons::ARROW_DOWN_TOOLBAR.icon()); @@ -333,43 +389,45 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : connect(m_prevAction, &QAction::triggered, this, &OutputPaneManager::slotPrev); m_minMaxAction = new QAction(this); - m_minMaxAction->setIcon(m_maximizeIcon); - m_minMaxAction->setText(Tr::tr("Maximize")); - m_closeButton->setIcon(Icons::CLOSE_SPLIT_BOTTOM.icon()); - connect(m_closeButton, &QAbstractButton::clicked, this, &OutputPaneManager::slotHide); + auto closeButton = new QToolButton; + closeButton->setIcon(Icons::CLOSE_SPLIT_BOTTOM.icon()); + connect(closeButton, &QAbstractButton::clicked, this, &OutputPaneManager::slotHide); connect(ICore::instance(), &ICore::saveSettingsRequested, this, &OutputPaneManager::saveSettings); - auto mainlayout = new QVBoxLayout; - mainlayout->setSpacing(0); - mainlayout->setContentsMargins(0, 0, 0, 0); - m_toolBar = new StyledBar; - auto toolLayout = new QHBoxLayout(m_toolBar); - toolLayout->setContentsMargins(0, 0, 0, 0); - toolLayout->setSpacing(0); - toolLayout->addWidget(m_titleLabel); - toolLayout->addWidget(new StyledSeparator); - m_clearButton = new QToolButton; - toolLayout->addWidget(m_clearButton); - m_prevToolButton = new QToolButton; - toolLayout->addWidget(m_prevToolButton); - m_nextToolButton = new QToolButton; - toolLayout->addWidget(m_nextToolButton); - toolLayout->addWidget(m_opToolBarWidgets); - toolLayout->addWidget(m_minMaxButton); - toolLayout->addWidget(m_closeButton); - mainlayout->addWidget(m_toolBar); - mainlayout->addWidget(m_outputWidgetPane, 10); - mainlayout->addWidget(new FindToolBarPlaceHolder(this)); - setLayout(mainlayout); + auto toolBar = new StyledBar; + auto clearButton = new QToolButton; + auto prevToolButton = new QToolButton; + auto nextToolButton = new QToolButton; + auto minMaxButton = new QToolButton; m_buttonsWidget = new QWidget; m_buttonsWidget->setObjectName("OutputPaneButtons"); // used for UI introduction - m_buttonsWidget->setLayout(new QHBoxLayout); - m_buttonsWidget->layout()->setContentsMargins(5,0,0,0); - m_buttonsWidget->layout()->setSpacing( - creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4); + + using namespace Layouting; + Row { + m_titleLabel, + new StyledSeparator, + clearButton, + prevToolButton, + nextToolButton, + m_opToolBarWidgets, + minMaxButton, + closeButton, + spacing(0), noMargin(), + }.attachTo(toolBar); + + Column { + toolBar, + m_outputWidgetPane, + new FindToolBarPlaceHolder(this), + spacing(0), noMargin(), + }.attachTo(this); + + Row { + spacing(creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4), customMargin({5, 0, 0, 0}), + }.attachTo(m_buttonsWidget); StatusBarManager::addStatusBarWidget(m_buttonsWidget, StatusBarManager::Second); @@ -384,19 +442,19 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : Command *cmd; - cmd = ActionManager::registerAction(m_clearAction, Constants::OUTPUTPANE_CLEAR); - m_clearButton->setDefaultAction( - ProxyAction::proxyActionWithIcon(m_clearAction, Utils::Icons::CLEAN_TOOLBAR.icon())); + cmd = ActionManager::registerAction(clearAction, Constants::OUTPUTPANE_CLEAR); + clearButton->setDefaultAction( + ProxyAction::proxyActionWithIcon(clearAction, Utils::Icons::CLEAN_TOOLBAR.icon())); mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup"); cmd = ActionManager::registerAction(m_prevAction, "Coreplugin.OutputPane.previtem"); cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Shift+F6"))); - m_prevToolButton->setDefaultAction( + prevToolButton->setDefaultAction( ProxyAction::proxyActionWithIcon(m_prevAction, Utils::Icons::ARROW_UP_TOOLBAR.icon())); mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup"); cmd = ActionManager::registerAction(m_nextAction, "Coreplugin.OutputPane.nextitem"); - m_nextToolButton->setDefaultAction( + nextToolButton->setDefaultAction( ProxyAction::proxyActionWithIcon(m_nextAction, Utils::Icons::ARROW_DOWN_TOOLBAR.icon())); cmd->setDefaultKeySequence(QKeySequence(Tr::tr("F6"))); mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup"); @@ -407,7 +465,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : cmd->setAttribute(Command::CA_UpdateIcon); mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup"); connect(m_minMaxAction, &QAction::triggered, this, &OutputPaneManager::toggleMaximized); - m_minMaxButton->setDefaultAction(cmd->action()); + minMaxButton->setDefaultAction(cmd->action()); mpanes->addSeparator("Coreplugin.OutputPane.ActionsGroup"); } @@ -452,22 +510,19 @@ void OutputPaneManager::initialize() }); QWidget *toolButtonsContainer = new QWidget(m_instance->m_opToolBarWidgets); - auto toolButtonsLayout = new QHBoxLayout; - toolButtonsLayout->setContentsMargins(0, 0, 0, 0); - toolButtonsLayout->setSpacing(0); + using namespace Layouting; + Row toolButtonsRow { spacing(0), noMargin() }; const QList<QWidget *> toolBarWidgets = outPane->toolBarWidgets(); for (QWidget *toolButton : toolBarWidgets) - toolButtonsLayout->addWidget(toolButton); - toolButtonsLayout->addStretch(5); - toolButtonsContainer->setLayout(toolButtonsLayout); + toolButtonsRow.addItem(toolButton); + toolButtonsRow.addItem(st); + toolButtonsRow.attachTo(toolButtonsContainer); m_instance->m_opToolBarWidgets->addWidget(toolButtonsContainer); minTitleWidth = qMax(minTitleWidth, titleFm.horizontalAdvance(outPane->displayName())); - QString suffix = outPane->displayName().simplified(); - suffix.remove(QLatin1Char(' ')); - data.id = baseId.withSuffix(suffix); + data.id = baseId.withSuffix(outPane->id().toString()); data.action = new QAction(outPane->displayName(), m_instance); Command *cmd = ActionManager::registerAction(data.action, data.id); @@ -478,6 +533,9 @@ void OutputPaneManager::initialize() outPane->displayName(), cmd->action()); data.button = button; + connect(button, &OutputPaneToggleButton::contextMenuRequested, m_instance, [] { + m_instance->popupMenu(); + }); connect(outPane, &IOutputPane::flashButton, button, [button] { button->flash(); }); connect(outPane, @@ -491,7 +549,7 @@ void OutputPaneManager::initialize() m_instance->buttonTriggered(i); }); - bool visible = outPane->priorityInStatusBar() != -1; + const bool visible = outPane->priorityInStatusBar() >= 0; data.button->setVisible(visible); connect(data.action, &QAction::triggered, m_instance, [i] { @@ -507,7 +565,7 @@ void OutputPaneManager::initialize() m_instance->m_titleLabel->setText(g_outputPanes[currentIdx].pane->displayName()); m_instance->m_buttonsWidget->layout()->addWidget(m_instance->m_manageButton); connect(m_instance->m_manageButton, - &QAbstractButton::clicked, + &OutputPaneManageButton::menuRequested, m_instance, &OutputPaneManager::popupMenu); @@ -570,23 +628,23 @@ void OutputPaneManager::buttonTriggered(int idx) void OutputPaneManager::readSettings() { - QSettings *settings = ICore::settings(); - int num = settings->beginReadArray(QLatin1String(outputPaneSettingsKeyC)); + QtcSettings *settings = ICore::settings(); + int num = settings->beginReadArray(outputPaneSettingsKeyC); for (int i = 0; i < num; ++i) { settings->setArrayIndex(i); - Id id = Id::fromSetting(settings->value(QLatin1String(outputPaneIdKeyC))); + Id id = Id::fromSetting(settings->value(outputPaneIdKeyC)); const int idx = Utils::indexOf(g_outputPanes, Utils::equal(&OutputPaneData::id, id)); if (idx < 0) // happens for e.g. disabled plugins (with outputpanes) that were loaded before continue; - const bool visible = settings->value(QLatin1String(outputPaneVisibleKeyC)).toBool(); + const bool visible = settings->value(outputPaneVisibleKeyC).toBool(); g_outputPanes[idx].button->setVisible(visible); } settings->endArray(); m_outputPaneHeightSetting - = settings->value(QLatin1String("OutputPanePlaceHolder/Height"), 0).toInt(); + = settings->value("OutputPanePlaceHolder/Height", 0).toInt(); const int currentIdx - = settings->value(QLatin1String("OutputPanePlaceHolder/CurrentIndex"), 0).toInt(); + = settings->value("OutputPanePlaceHolder/CurrentIndex", 0).toInt(); if (QTC_GUARD(currentIdx >= 0 && currentIdx < g_outputPanes.size())) setCurrentIndex(currentIdx); } @@ -716,42 +774,57 @@ void OutputPaneManager::popupMenu() QAction *act = menu.addAction(data.pane->displayName()); act->setCheckable(true); act->setChecked(data.button->isPaneVisible()); - act->setData(idx); + connect(act, &QAction::triggered, this, [this, data, idx] { + if (data.button->isPaneVisible()) { + data.pane->visibilityChanged(false); + data.button->setChecked(false); + data.button->hide(); + } else { + showPage(idx, IOutputPane::ModeSwitch); + } + }); ++idx; } - QAction *result = menu.exec(QCursor::pos()); - if (!result) - return; - idx = result->data().toInt(); - QTC_ASSERT(idx >= 0 && idx < g_outputPanes.size(), return); - OutputPaneData &data = g_outputPanes[idx]; - if (data.button->isPaneVisible()) { - data.pane->visibilityChanged(false); - data.button->setChecked(false); - data.button->hide(); - } else { - showPage(idx, IOutputPane::ModeSwitch); - } + + menu.addSeparator(); + QAction *reset = menu.addAction(Tr::tr("Reset to Default")); + connect(reset, &QAction::triggered, this, [this] { + for (int i = 0; i < g_outputPanes.size(); ++i) { + OutputPaneData &data = g_outputPanes[i]; + const bool buttonVisible = data.pane->priorityInStatusBar() >= 0; + const bool paneVisible = currentIndex() == i + && OutputPanePlaceHolder::isCurrentVisible(); + if (buttonVisible) { + data.button->setChecked(paneVisible); + data.button->setVisible(true); + } else { + data.button->setChecked(false); + data.button->hide(); + } + } + }); + + menu.exec(QCursor::pos()); } void OutputPaneManager::saveSettings() const { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); const int n = g_outputPanes.size(); - settings->beginWriteArray(QLatin1String(outputPaneSettingsKeyC), n); + settings->beginWriteArray(outputPaneSettingsKeyC, n); for (int i = 0; i < n; ++i) { const OutputPaneData &data = g_outputPanes.at(i); settings->setArrayIndex(i); - settings->setValue(QLatin1String(outputPaneIdKeyC), data.id.toSetting()); - settings->setValue(QLatin1String(outputPaneVisibleKeyC), data.button->isPaneVisible()); + settings->setValue(outputPaneIdKeyC, data.id.toSetting()); + settings->setValue(outputPaneVisibleKeyC, data.button->isPaneVisible()); } settings->endArray(); int heightSetting = m_outputPaneHeightSetting; // update if possible if (OutputPanePlaceHolder *curr = OutputPanePlaceHolder::getCurrent()) heightSetting = curr->nonMaximizedSize(); - settings->setValue(QLatin1String("OutputPanePlaceHolder/Height"), heightSetting); - settings->setValue(QLatin1String("OutputPanePlaceHolder/CurrentIndex"), currentIndex()); + settings->setValue("OutputPanePlaceHolder/Height", heightSetting); + settings->setValue("OutputPanePlaceHolder/CurrentIndex", currentIndex()); } void OutputPaneManager::clearPage() @@ -934,6 +1007,10 @@ bool OutputPaneToggleButton::isPaneVisible() const return isVisibleTo(parentWidget()); } +void OutputPaneToggleButton::contextMenuEvent(QContextMenuEvent *) +{ + emit contextMenuRequested(); +} /////////////////////////////////////////////////////////////////////// // @@ -946,6 +1023,7 @@ OutputPaneManageButton::OutputPaneManageButton() setFocusPolicy(Qt::NoFocus); setCheckable(true); setFixedWidth(StyleHelper::toolbarStyle() == Utils::StyleHelper::ToolbarStyleCompact ? 17 : 21); + connect(this, &QToolButton::clicked, this, &OutputPaneManageButton::menuRequested); } void OutputPaneManageButton::paintEvent(QPaintEvent*) @@ -967,6 +1045,11 @@ void OutputPaneManageButton::paintEvent(QPaintEvent*) s->drawPrimitive(QStyle::PE_IndicatorArrowDown, &arrowOpt, &p, this); } +void OutputPaneManageButton::contextMenuEvent(QContextMenuEvent *) +{ + emit menuRequested(); +} + BadgeLabel::BadgeLabel() { m_font = QApplication::font(); diff --git a/src/plugins/coreplugin/outputpanemanager.h b/src/plugins/coreplugin/outputpanemanager.h index 7dc7085d534..74c14012ccc 100644 --- a/src/plugins/coreplugin/outputpanemanager.h +++ b/src/plugins/coreplugin/outputpanemanager.h @@ -15,8 +15,12 @@ class QTimeLine; QT_END_NAMESPACE namespace Core { + +class ICore; + namespace Internal { +class ICorePrivate; class MainWindow; class OutputPaneToggleButton; class OutputPaneManageButton; @@ -33,6 +37,11 @@ public: static int outputPaneHeightSetting(); static void setOutputPaneHeightSetting(int value); + // FIXME: Hide again + static void create(); + static void initialize(); + static void destroy(); + public slots: void slotHide(); void slotNext(); @@ -44,13 +53,11 @@ protected: private: // the only class that is allowed to create and destroy + friend class ICore; + friend class ICorePrivate; friend class MainWindow; friend class OutputPaneManageButton; - static void create(); - static void initialize(); - static void destroy(); - explicit OutputPaneManager(QWidget *parent = nullptr); ~OutputPaneManager() override; @@ -67,24 +74,14 @@ private: QLabel *m_titleLabel = nullptr; OutputPaneManageButton *m_manageButton = nullptr; - QAction *m_clearAction = nullptr; - QToolButton *m_clearButton = nullptr; - QToolButton *m_closeButton = nullptr; QAction *m_minMaxAction = nullptr; - QToolButton *m_minMaxButton = nullptr; - QAction *m_nextAction = nullptr; QAction *m_prevAction = nullptr; - QToolButton *m_prevToolButton = nullptr; - QToolButton *m_nextToolButton = nullptr; - QWidget *m_toolBar = nullptr; QStackedWidget *m_outputWidgetPane = nullptr; QStackedWidget *m_opToolBarWidgets = nullptr; QWidget *m_buttonsWidget = nullptr; - QIcon m_minimizeIcon; - QIcon m_maximizeIcon; int m_outputPaneHeightSetting = 0; }; @@ -118,6 +115,11 @@ public: void setIconBadgeNumber(int number); bool isPaneVisible() const; + void contextMenuEvent(QContextMenuEvent *e) override; + +signals: + void contextMenuRequested(); + private: void updateToolTip(); void checkStateSet() override; @@ -134,7 +136,12 @@ class OutputPaneManageButton : public QToolButton Q_OBJECT public: OutputPaneManageButton(); - void paintEvent(QPaintEvent*) override; + void paintEvent(QPaintEvent *) override; + + void contextMenuEvent(QContextMenuEvent *e) override; + +signals: + void menuRequested(); }; } // namespace Internal diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp index 2de95d1b0ba..dfc7e0c9d87 100644 --- a/src/plugins/coreplugin/outputwindow.cpp +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -50,7 +50,7 @@ public: { } - QString settingsKey; + Key settingsKey; OutputFormatter formatter; QList<QPair<QString, OutputFormat>> queuedOutput; QTimer queueTimer; @@ -77,7 +77,7 @@ public: /*******************/ -OutputWindow::OutputWindow(Context context, const QString &settingsKey, QWidget *parent) +OutputWindow::OutputWindow(Context context, const Key &settingsKey, QWidget *parent) : QPlainTextEdit(parent) , d(new Internal::OutputWindowPrivate(document())) { diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h index baf58cc22c4..83681a988e4 100644 --- a/src/plugins/coreplugin/outputwindow.h +++ b/src/plugins/coreplugin/outputwindow.h @@ -6,6 +6,7 @@ #include "core_global.h" #include "icontext.h" +#include <utils/storekey.h> #include <utils/outputformat.h> #include <QPlainTextEdit> @@ -32,7 +33,7 @@ public: }; Q_DECLARE_FLAGS(FilterModeFlags, FilterModeFlag) - OutputWindow(Context context, const QString &settingsKey, QWidget *parent = nullptr); + OutputWindow(Context context, const Utils::Key &settingsKey, QWidget *parent = nullptr); ~OutputWindow() override; void setLineParsers(const QList<Utils::OutputLineParser *> &parsers); diff --git a/src/plugins/coreplugin/patchtool.cpp b/src/plugins/coreplugin/patchtool.cpp index 744282728a8..c17db9ba927 100644 --- a/src/plugins/coreplugin/patchtool.cpp +++ b/src/plugins/coreplugin/patchtool.cpp @@ -1,10 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "patchtool.h" + #include "coreplugintr.h" #include "icore.h" #include "messagemanager.h" -#include "patchtool.h" +#include "systemsettings.h" #include <utils/environment.h> #include <utils/process.h> @@ -12,41 +14,26 @@ #include <QMessageBox> using namespace Utils; +using namespace Core::Internal; namespace Core { -const char settingsGroupC[] = "General"; -const char patchCommandKeyC[] = "PatchCommand"; -const char patchCommandDefaultC[] = "patch"; - FilePath PatchTool::patchCommand() { - QSettings *s = ICore::settings(); + FilePath command = systemSettings().patchCommand(); - s->beginGroup(settingsGroupC); - FilePath command = FilePath::fromSettings(s->value(patchCommandKeyC, patchCommandDefaultC)); - s->endGroup(); - - if (HostOsInfo::isWindowsHost() && command.path() == patchCommandDefaultC) { + if (HostOsInfo::isWindowsHost() && systemSettings().patchCommand.isDefaultValue()) { const QSettings settings(R"(HKEY_LOCAL_MACHINE\SOFTWARE\GitForWindows)", QSettings::NativeFormat); const FilePath gitInstall = FilePath::fromUserInput(settings.value("InstallPath").toString()); if (gitInstall.exists()) command = command.searchInPath({gitInstall.pathAppended("usr/bin")}, - Utils::FilePath::PrependToPath); + FilePath::PrependToPath); } return command; } -void PatchTool::setPatchCommand(const FilePath &newCommand) -{ - Utils::QtcSettings *s = ICore::settings(); - s->beginGroup(settingsGroupC); - s->setValueWithDefault(patchCommandKeyC, newCommand.toSettings(), QVariant(QString(patchCommandDefaultC))); - s->endGroup(); -} - bool PatchTool::confirmPatching(QWidget *parent, PatchAction patchAction, bool isModified) { const QString title = patchAction == PatchAction::Apply ? Tr::tr("Apply Chunk") @@ -110,8 +97,8 @@ static bool runPatchHelper(const QByteArray &input, const FilePath &workingDirec if (!patchProcess.readDataFromProcess(&stdOut, &stdErr)) { patchProcess.stop(); patchProcess.waitForFinished(); - MessageManager::writeFlashing(Tr::tr("A timeout occurred running \"%1\"") - .arg(patch.toUserOutput())); + MessageManager::writeFlashing( + Tr::tr("A timeout occurred running \"%1\".").arg(patch.toUserOutput())); return false; } diff --git a/src/plugins/coreplugin/patchtool.h b/src/plugins/coreplugin/patchtool.h index 020ab72eb6e..c509b502a2f 100644 --- a/src/plugins/coreplugin/patchtool.h +++ b/src/plugins/coreplugin/patchtool.h @@ -18,7 +18,6 @@ class CORE_EXPORT PatchTool { public: static Utils::FilePath patchCommand(); - static void setPatchCommand(const Utils::FilePath &newCommand); static bool confirmPatching(QWidget *parent, PatchAction patchAction, bool isModified); diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index 24d8c8dff12..c7f7dab2311 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -8,8 +8,6 @@ #include "icore.h" #include "plugininstallwizard.h" -#include <app/app_version.h> - #include <extensionsystem/plugindetailsview.h> #include <extensionsystem/pluginerrorview.h> #include <extensionsystem/pluginmanager.h> diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index ac2dfd6c240..9a0b92c871e 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -10,7 +10,6 @@ #include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginspec.h> -#include <utils/archive.h> #include <utils/async.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> @@ -20,15 +19,14 @@ #include <utils/process.h> #include <utils/qtcassert.h> #include <utils/temporarydirectory.h> +#include <utils/unarchiver.h> #include <utils/wizard.h> #include <utils/wizardpage.h> -#include <app/app_version.h> - #include <QButtonGroup> #include <QDir> #include <QDirIterator> -#include <QFileInfo> +#include <QGuiApplication> #include <QLabel> #include <QMessageBox> #include <QPushButton> @@ -38,6 +36,7 @@ #include <memory> using namespace ExtensionSystem; +using namespace Tasking; using namespace Utils; struct Data @@ -59,7 +58,7 @@ static QStringList libraryNameFilter() static bool hasLibSuffix(const FilePath &path) { return (HostOsInfo::isWindowsHost() && path.endsWith(".dll")) - || (HostOsInfo::isLinuxHost() && path.completeSuffix().startsWith(".so")) + || (HostOsInfo::isLinuxHost() && path.completeSuffix().startsWith("so")) || (HostOsInfo::isMacHost() && path.endsWith(".dylib")); } @@ -110,19 +109,18 @@ public: bool isComplete() const final { const FilePath path = m_data->sourcePath; - if (!QFile::exists(path.toString())) { + if (!QFileInfo::exists(path.toString())) { m_info->setText(Tr::tr("File does not exist.")); return false; } if (hasLibSuffix(path)) return true; - QString error; - if (!Archive::supportsFile(path, &error)) { - m_info->setText(error); - return false; - } - return true; + const auto sourceAndCommand = Unarchiver::sourceAndCommand(path); + if (!sourceAndCommand) + m_info->setText(sourceAndCommand.error()); + + return bool(sourceAndCommand); } int nextId() const final @@ -136,15 +134,50 @@ public: Data *m_data = nullptr; }; +struct ArchiveIssue +{ + QString message; + InfoLabel::InfoType type; +}; + +// Async. Result is set if any issue was found. +void checkContents(QPromise<ArchiveIssue> &promise, const FilePath &tempDir) +{ + PluginSpec *coreplugin = PluginManager::specForPlugin(CorePlugin::instance()); + + // look for plugin + QDirIterator it(tempDir.path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks, + QDirIterator::Subdirectories); + while (it.hasNext()) { + if (promise.isCanceled()) + return; + it.next(); + PluginSpec *spec = PluginSpec::read(it.filePath()); + if (spec) { + // Is a Qt Creator plugin. Let's see if we find a Core dependency and check the + // version + const QVector<PluginDependency> dependencies = spec->dependencies(); + const auto found = std::find_if(dependencies.constBegin(), dependencies.constEnd(), + [coreplugin](const PluginDependency &d) { return d.name == coreplugin->name(); }); + if (found == dependencies.constEnd()) + return; + if (coreplugin->provides(found->name, found->version)) + return; + promise.addResult( + ArchiveIssue{Tr::tr("Plugin requires an incompatible version of %1 (%2).") + .arg(QGuiApplication::applicationDisplayName(), found->version), + InfoLabel::Error}); + return; // successful / no error + } + } + promise.addResult( + ArchiveIssue{Tr::tr("Did not find %1 plugin.").arg(QGuiApplication::applicationDisplayName()), + InfoLabel::Error}); +} + class CheckArchivePage : public WizardPage { public: - struct ArchiveIssue - { - QString message; - InfoLabel::InfoType type; - }; - CheckArchivePage(Data *data, QWidget *parent) : WizardPage(parent) , m_data(data) @@ -155,6 +188,12 @@ public: m_label->setElideMode(Qt::ElideNone); m_label->setWordWrap(true); m_cancelButton = new QPushButton(Tr::tr("Cancel")); + connect(m_cancelButton, &QPushButton::clicked, this, [this] { + m_taskTree.reset(); + m_cancelButton->setVisible(false); + m_label->setType(InfoLabel::Information); + m_label->setText(Tr::tr("Canceled.")); + }); m_output = new QTextEdit; m_output->setReadOnly(true); @@ -169,142 +208,87 @@ public: { m_isComplete = false; emit completeChanged(); - m_canceled = false; m_tempDir = std::make_unique<TemporaryDirectory>("plugininstall"); m_data->extractedPath = m_tempDir->path(); m_label->setText(Tr::tr("Checking archive...")); m_label->setType(InfoLabel::None); - m_cancelButton->setVisible(true); m_output->clear(); - m_archive.reset(new Archive(m_data->sourcePath, m_tempDir->path())); - if (!m_archive->isValid()) { + const auto sourceAndCommand = Unarchiver::sourceAndCommand(m_data->sourcePath); + if (!sourceAndCommand) { m_label->setType(InfoLabel::Error); - m_label->setText(Tr::tr("The file is not an archive.")); + m_label->setText(sourceAndCommand.error()); return; } - QObject::connect(m_archive.get(), &Archive::outputReceived, this, - [this](const QString &output) { - m_output->append(output); - }); - QObject::connect(m_archive.get(), &Archive::finished, this, [this](bool success) { - m_archive.release()->deleteLater(); - handleFinished(success); - }); - QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] { - m_canceled = true; - m_archive.reset(); - handleFinished(false); - }); - m_archive->unarchive(); - } - void handleFinished(bool success) - { - m_cancelButton->disconnect(); - if (!success) { // unarchiving failed - m_cancelButton->setVisible(false); - if (m_canceled) { - m_label->setType(InfoLabel::Information); - m_label->setText(Tr::tr("Canceled.")); + const auto onUnarchiverSetup = [this, sourceAndCommand](Unarchiver &unarchiver) { + unarchiver.setSourceAndCommand(*sourceAndCommand); + unarchiver.setDestDir(m_tempDir->path()); + connect(&unarchiver, &Unarchiver::outputReceived, this, [this](const QString &output) { + m_output->append(output); + }); + }; + const auto onUnarchiverError = [this](const Unarchiver &) { + m_label->setType(InfoLabel::Error); + m_label->setText(Tr::tr("There was an error while unarchiving.")); + }; + + const auto onCheckerSetup = [this](Async<ArchiveIssue> &async) { + if (!m_tempDir) + return SetupResult::StopWithError; + + async.setConcurrentCallData(checkContents, m_tempDir->path()); + async.setFutureSynchronizer(PluginManager::futureSynchronizer()); + return SetupResult::Continue; + }; + const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) { + m_isComplete = !async.isResultAvailable(); + if (m_isComplete) { + m_label->setType(InfoLabel::Ok); + m_label->setText(Tr::tr("Archive is OK.")); } else { - m_label->setType(InfoLabel::Error); - m_label->setText(Tr::tr("There was an error while unarchiving.")); + const ArchiveIssue issue = async.result(); + m_label->setType(issue.type); + m_label->setText(issue.message); } - } else { // unarchiving was successful, run a check - m_archiveCheck = Utils::asyncRun([this](QPromise<ArchiveIssue> &promise) - { return checkContents(promise); }); - Utils::onFinished(m_archiveCheck, this, [this](const QFuture<ArchiveIssue> &f) { - m_cancelButton->setVisible(false); - m_cancelButton->disconnect(); - const bool ok = f.resultCount() == 0 && !f.isCanceled(); - if (f.isCanceled()) { - m_label->setType(InfoLabel::Information); - m_label->setText(Tr::tr("Canceled.")); - } else if (ok) { - m_label->setType(InfoLabel::Ok); - m_label->setText(Tr::tr("Archive is OK.")); - } else { - const ArchiveIssue issue = f.result(); - m_label->setType(issue.type); - m_label->setText(issue.message); - } - m_isComplete = ok; - emit completeChanged(); - }); - QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] { - m_archiveCheck.cancel(); - }); - } - } + emit completeChanged(); + }; - // Async. Result is set if any issue was found. - void checkContents(QPromise<ArchiveIssue> &promise) - { - QTC_ASSERT(m_tempDir.get(), return ); + const Group root { + UnarchiverTask(onUnarchiverSetup, {}, onUnarchiverError), + AsyncTask<ArchiveIssue>(onCheckerSetup, onCheckerDone) + }; + m_taskTree.reset(new TaskTree(root)); - PluginSpec *coreplugin = PluginManager::specForPlugin(CorePlugin::instance()); + const auto onEnd = [this] { + m_cancelButton->setVisible(false); + m_taskTree.release()->deleteLater(); + }; + connect(m_taskTree.get(), &TaskTree::done, this, onEnd); + connect(m_taskTree.get(), &TaskTree::errorOccurred, this, onEnd); - // look for plugin - QDirIterator it(m_tempDir->path().path(), - libraryNameFilter(), - QDir::Files | QDir::NoSymLinks, - QDirIterator::Subdirectories); - while (it.hasNext()) { - if (promise.isCanceled()) - return; - it.next(); - PluginSpec *spec = PluginSpec::read(it.filePath()); - if (spec) { - // Is a Qt Creator plugin. Let's see if we find a Core dependency and check the - // version - const QVector<PluginDependency> dependencies = spec->dependencies(); - const auto found = std::find_if(dependencies.constBegin(), - dependencies.constEnd(), - [coreplugin](const PluginDependency &d) { - return d.name == coreplugin->name(); - }); - if (found != dependencies.constEnd()) { - if (!coreplugin->provides(found->name, found->version)) { - promise.addResult(ArchiveIssue{ - Tr::tr("Plugin requires an incompatible version of %1 (%2).") - .arg(Constants::IDE_DISPLAY_NAME).arg(found->version), - InfoLabel::Error}); - return; - } - } - return; // successful / no error - } - } - promise.addResult(ArchiveIssue{Tr::tr("Did not find %1 plugin.") - .arg(Constants::IDE_DISPLAY_NAME), InfoLabel::Error}); + m_cancelButton->setVisible(true); + m_taskTree->start(); } void cleanupPage() final { // back button pressed - m_cancelButton->disconnect(); - m_archive.reset(); - if (m_archiveCheck.isRunning()) { - m_archiveCheck.cancel(); - m_archiveCheck.waitForFinished(); - } + m_taskTree.reset(); m_tempDir.reset(); } bool isComplete() const final { return m_isComplete; } std::unique_ptr<TemporaryDirectory> m_tempDir; - std::unique_ptr<Archive> m_archive; - QFuture<ArchiveIssue> m_archiveCheck; + std::unique_ptr<TaskTree> m_taskTree; InfoLabel *m_label = nullptr; QPushButton *m_cancelButton = nullptr; QTextEdit *m_output = nullptr; Data *m_data = nullptr; bool m_isComplete = false; - bool m_canceled = false; }; class InstallLocationPage : public WizardPage @@ -323,17 +307,16 @@ public: localInstall->setChecked(!m_data->installIntoApplication); auto localLabel = new QLabel(Tr::tr("The plugin will be available to all compatible %1 " "installations, but only for the current user.") - .arg(Constants::IDE_DISPLAY_NAME)); + .arg(QGuiApplication::applicationDisplayName())); localLabel->setWordWrap(true); localLabel->setAttribute(Qt::WA_MacSmallSize, true); auto appInstall = new QRadioButton( - Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME)); + Tr::tr("%1 installation").arg(QGuiApplication::applicationDisplayName())); appInstall->setChecked(m_data->installIntoApplication); - auto appLabel = new QLabel( - Tr::tr("The plugin will be available only to this %1 " - "installation, but for all users that can access it.") - .arg(Constants::IDE_DISPLAY_NAME)); + auto appLabel = new QLabel(Tr::tr("The plugin will be available only to this %1 " + "installation, but for all users that can access it.") + .arg(QGuiApplication::applicationDisplayName())); appLabel->setWordWrap(true); appLabel->setAttribute(Qt::WA_MacSmallSize, true); diff --git a/src/plugins/coreplugin/progressmanager/futureprogress.h b/src/plugins/coreplugin/progressmanager/futureprogress.h index 5687fa95a15..d2b9e7facb0 100644 --- a/src/plugins/coreplugin/progressmanager/futureprogress.h +++ b/src/plugins/coreplugin/progressmanager/futureprogress.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> diff --git a/src/plugins/coreplugin/progressmanager/processprogress.cpp b/src/plugins/coreplugin/progressmanager/processprogress.cpp index 9aeebf4ecf0..6a82f5dde82 100644 --- a/src/plugins/coreplugin/progressmanager/processprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/processprogress.cpp @@ -10,6 +10,7 @@ #include <utils/qtcassert.h> #include <QFutureWatcher> +#include <QPointer> using namespace Utils; diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.cpp b/src/plugins/coreplugin/progressmanager/progressmanager.cpp index 5504f8fe0ce..a2d61902b1c 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager.cpp @@ -24,7 +24,8 @@ #include <QAction> #include <QEvent> -#include <QHBoxLayout> +#include <QFuture> +#include <QFutureInterfaceBase> #include <QMouseEvent> #include <QPainter> #include <QPropertyAnimation> @@ -42,6 +43,38 @@ using namespace Core::Internal; using namespace Utils; namespace Core { + +class ProgressTimer : public QObject +{ +public: + ProgressTimer(const QFutureInterfaceBase &futureInterface, int expectedSeconds, QObject *parent) + : QObject(parent), + m_futureInterface(futureInterface), + m_expectedTime(expectedSeconds) + { + m_futureInterface.setProgressRange(0, 100); + m_futureInterface.setProgressValue(0); + + m_timer.setInterval(TimerInterval); + connect(&m_timer, &QTimer::timeout, this, &ProgressTimer::handleTimeout); + m_timer.start(); + } + +private: + void handleTimeout() + { + ++m_currentTime; + const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval); + const int progress = MathUtils::interpolateTangential(m_currentTime, halfLife, 0, 100); + m_futureInterface.setProgressValue(progress); + } + + QFutureInterfaceBase m_futureInterface; + int m_expectedTime; + int m_currentTime = 0; + QTimer m_timer; +}; + /*! \class Core::ProgressManager \inheaderfile coreplugin/progressmanager/progressmanager.h @@ -250,7 +283,7 @@ ProgressManagerPrivate::~ProgressManagerPrivate() void ProgressManagerPrivate::readSettings() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(kSettingsGroup); m_progressViewPinned = settings->value(kDetailsPinned, kDetailsPinnedDefault).toBool(); settings->endGroup(); @@ -789,29 +822,4 @@ void ProgressManager::cancelTasks(Id type) m_instance->doCancelTasks(type); } - -ProgressTimer::ProgressTimer(const QFutureInterfaceBase &futureInterface, - int expectedSeconds, - QObject *parent) - : QObject(parent), - m_futureInterface(futureInterface), - m_expectedTime(expectedSeconds) -{ - m_futureInterface.setProgressRange(0, 100); - m_futureInterface.setProgressValue(0); - - m_timer = new QTimer(this); - m_timer->setInterval(TimerInterval); - connect(m_timer, &QTimer::timeout, this, &ProgressTimer::handleTimeout); - m_timer->start(); -} - -void ProgressTimer::handleTimeout() -{ - ++m_currentTime; - const int halfLife = qRound(1000.0 * m_expectedTime / TimerInterval); - const int progress = MathUtils::interpolateTangential(m_currentTime, halfLife, 0, 100); - m_futureInterface.setProgressValue(progress); -} - } // Core diff --git a/src/plugins/coreplugin/progressmanager/progressmanager.h b/src/plugins/coreplugin/progressmanager/progressmanager.h index 8c51bf4ccd4..f59fb0562fd 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager.h +++ b/src/plugins/coreplugin/progressmanager/progressmanager.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/core_global.h> +#include "../core_global.h" #include <utils/id.h> @@ -11,8 +11,6 @@ #include <QFutureInterfaceBase> #include <QObject> -QT_FORWARD_DECLARE_CLASS(QTimer) - namespace Core { class FutureProgress; @@ -58,21 +56,6 @@ private: friend class Core::Internal::ProgressManagerPrivate; }; -class CORE_EXPORT ProgressTimer : public QObject -{ -public: - ProgressTimer(const QFutureInterfaceBase &futureInterface, int expectedSeconds, - QObject *parent = nullptr); - -private: - void handleTimeout(); - - QFutureInterfaceBase m_futureInterface; - int m_expectedTime; - int m_currentTime = 0; - QTimer *m_timer; -}; - } // namespace Core Q_DECLARE_OPERATORS_FOR_FLAGS(Core::ProgressManager::ProgressFlags) diff --git a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp index 58e350506ce..64e9fdfc5a8 100644 --- a/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp +++ b/src/plugins/coreplugin/progressmanager/progressmanager_win.cpp @@ -1,6 +1,12 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "progressmanager_p.h" + +#include "../icore.h" + +#include <utils/utilsicons.h> + #include <QGuiApplication> #include <QVariant> #include <QMainWindow> @@ -12,11 +18,6 @@ #include <QLabel> #include <qpa/qplatformnativeinterface.h> -#include <coreplugin/icore.h> -#include <utils/utilsicons.h> - -#include "progressmanager_p.h" - // for windows progress bar #ifndef __GNUC__ # define CALLBACK WINAPI diff --git a/src/plugins/coreplugin/progressmanager/taskprogress.cpp b/src/plugins/coreplugin/progressmanager/taskprogress.cpp index 5ee5feb24a8..d60fa71e125 100644 --- a/src/plugins/coreplugin/progressmanager/taskprogress.cpp +++ b/src/plugins/coreplugin/progressmanager/taskprogress.cpp @@ -12,6 +12,7 @@ #include <utils/qtcassert.h> #include <QFutureWatcher> +#include <QPointer> #include <QTimer> using namespace Utils; diff --git a/src/plugins/coreplugin/rightpane.cpp b/src/plugins/coreplugin/rightpane.cpp index 24322b6b00b..4363735ceab 100644 --- a/src/plugins/coreplugin/rightpane.cpp +++ b/src/plugins/coreplugin/rightpane.cpp @@ -3,8 +3,7 @@ #include "rightpane.h" -#include <coreplugin/imode.h> -#include <coreplugin/modemanager.h> +#include "modemanager.h" #include <utils/qtcsettings.h> @@ -158,9 +157,9 @@ void RightPaneWidget::saveSettings(Utils::QtcSettings *settings) settings->setValueWithDefault("RightPane/Width", m_width, kWidthDefault); } -void RightPaneWidget::readSettings(QSettings *settings) +void RightPaneWidget::readSettings(QtcSettings *settings) { - setShown(settings->value(QLatin1String("RightPane/Visible"), kVisibleDefault).toBool()); + setShown(settings->value("RightPane/Visible", kVisibleDefault).toBool()); m_width = settings->value("RightPane/Width", kWidthDefault).toInt(); // Apply diff --git a/src/plugins/coreplugin/rightpane.h b/src/plugins/coreplugin/rightpane.h index a7371f406ad..0dd11669515 100644 --- a/src/plugins/coreplugin/rightpane.h +++ b/src/plugins/coreplugin/rightpane.h @@ -48,7 +48,7 @@ public: ~RightPaneWidget() override; void saveSettings(Utils::QtcSettings *settings); - void readSettings(QSettings *settings); + void readSettings(Utils::QtcSettings *settings); bool isShown() const; void setShown(bool b); diff --git a/src/plugins/coreplugin/session.cpp b/src/plugins/coreplugin/session.cpp index 7954b50e341..c188f6812c3 100644 --- a/src/plugins/coreplugin/session.cpp +++ b/src/plugins/coreplugin/session.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "session.h" -#include "session_p.h" #include "sessiondialog.h" @@ -18,17 +17,20 @@ #include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginspec.h> -#include <texteditor/texteditor.h> - #include <utils/algorithm.h> #include <utils/filepath.h> #include <utils/macroexpander.h> +#include <utils/persistentsettings.h> #include <utils/qtcassert.h> +#include <utils/store.h> #include <utils/stringutils.h> #include <utils/stylehelper.h> +#include <nanotrace/nanotrace.h> + #include <QAction> #include <QActionGroup> +#include <QFutureInterface> #include <QDebug> #include <QMenu> #include <QMessageBox> @@ -67,23 +69,64 @@ const char M_SESSION[] = "ProjectExplorer.Menu.Session"; This could be improved. */ +class SessionManagerPrivate +{ +public: + void restoreStartupSession(); + + void restoreValues(const PersistentSettingsReader &reader); + void restoreSessionValues(const PersistentSettingsReader &reader); + void restoreEditors(); + + void saveSettings(); + void restoreSettings(); + bool isAutoRestoreLastSession(); + void setAutoRestoreLastSession(bool restore); + + void updateSessionMenu(); + + static QString windowTitleAddition(const FilePath &filePath); + static QString sessionTitle(const FilePath &filePath); + + QString m_sessionName = "default"; + bool m_isAutoRestoreLastSession = false; + bool m_virginSession = true; + bool m_loadingSession = false; + + mutable QStringList m_sessions; + mutable QHash<QString, QDateTime> m_sessionDateTimes; + QHash<QString, QDateTime> m_lastActiveTimes; + + QMap<Utils::Key, QVariant> m_values; + QMap<Utils::Key, QVariant> m_sessionValues; + QFutureInterface<void> m_future; + PersistentSettingsWriter *m_writer = nullptr; + + QMenu *m_sessionMenu; + QAction *m_sessionManagerAction; +}; + static SessionManager *m_instance = nullptr; -SessionManagerPrivate *sb_d = nullptr; +static SessionManagerPrivate *d = nullptr; SessionManager::SessionManager() { m_instance = this; - sb_d = new SessionManagerPrivate; + d = new SessionManagerPrivate; - connect(ICore::instance(), &ICore::coreOpened, this, [] { sb_d->restoreStartupSession(); }); + connect(PluginManager::instance(), &PluginManager::initializationDone, this, [] { + d->restoreStartupSession(); + }); - connect(ModeManager::instance(), &ModeManager::currentModeChanged, - this, &SessionManager::saveActiveMode); + connect(ModeManager::instance(), &ModeManager::currentModeChanged, [](Id mode) { + if (mode != Core::Constants::MODE_WELCOME) + setValue("ActiveMode", mode.toString()); + }); connect(ICore::instance(), &ICore::saveSettingsRequested, this, [] { if (!SessionManager::isLoadingSession()) SessionManager::saveSession(); - sb_d->saveSettings(); + d->saveSettings(); }); connect(EditorManager::instance(), &EditorManager::editorOpened, @@ -101,17 +144,17 @@ SessionManager::SessionManager() msession->menu()->setTitle(PE::Tr::tr("S&essions")); msession->setOnAllDisabledBehavior(ActionContainer::Show); mfile->addMenu(msession, Core::Constants::G_FILE_SESSION); - sb_d->m_sessionMenu = msession->menu(); - connect(mfile->menu(), &QMenu::aboutToShow, this, [] { sb_d->updateSessionMenu(); }); + d->m_sessionMenu = msession->menu(); + connect(mfile->menu(), &QMenu::aboutToShow, this, [] { d->updateSessionMenu(); }); // session manager action - sb_d->m_sessionManagerAction = new QAction(PE::Tr::tr("&Manage..."), this); - sb_d->m_sessionMenu->addAction(sb_d->m_sessionManagerAction); - sb_d->m_sessionMenu->addSeparator(); - Command *cmd = ActionManager::registerAction(sb_d->m_sessionManagerAction, + d->m_sessionManagerAction = new QAction(PE::Tr::tr("&Manage..."), this); + d->m_sessionMenu->addAction(d->m_sessionManagerAction); + d->m_sessionMenu->addSeparator(); + Command *cmd = ActionManager::registerAction(d->m_sessionManagerAction, "ProjectExplorer.ManageSessions"); cmd->setDefaultKeySequence(QKeySequence()); - connect(sb_d->m_sessionManagerAction, + connect(d->m_sessionManagerAction, &QAction::triggered, SessionManager::instance(), &SessionManager::showSessionManager); @@ -127,15 +170,15 @@ SessionManager::SessionManager() return SessionManager::activeSession(); }); - sb_d->restoreSettings(); + d->restoreSettings(); } SessionManager::~SessionManager() { - emit m_instance->aboutToUnloadSession(sb_d->m_sessionName); - delete sb_d->m_writer; - delete sb_d; - sb_d = nullptr; + emit m_instance->aboutToUnloadSession(d->m_sessionName); + delete d->m_writer; + delete d; + d = nullptr; } SessionManager *SessionManager::instance() @@ -145,7 +188,7 @@ SessionManager *SessionManager::instance() bool SessionManager::isDefaultVirgin() { - return isDefaultSession(sb_d->m_sessionName) && sb_d->m_virginSession; + return isDefaultSession(d->m_sessionName) && d->m_virginSession; } bool SessionManager::isDefaultSession(const QString &session) @@ -153,15 +196,9 @@ bool SessionManager::isDefaultSession(const QString &session) return session == QLatin1String(DEFAULT_SESSION); } -void SessionManager::saveActiveMode(Id mode) -{ - if (mode != Core::Constants::MODE_WELCOME) - setValue(QLatin1String("ActiveMode"), mode.toString()); -} - bool SessionManager::isLoadingSession() { - return sb_d->m_loadingSession; + return d->m_loadingSession; } /*! @@ -169,65 +206,65 @@ bool SessionManager::isLoadingSession() within the session file. */ -void SessionManager::setValue(const QString &name, const QVariant &value) +void SessionManager::setValue(const Key &name, const QVariant &value) { - if (sb_d->m_values.value(name) == value) + if (d->m_values.value(name) == value) return; - sb_d->m_values.insert(name, value); + d->m_values.insert(name, value); } -QVariant SessionManager::value(const QString &name) +QVariant SessionManager::value(const Key &name) { - auto it = sb_d->m_values.constFind(name); - return (it == sb_d->m_values.constEnd()) ? QVariant() : *it; + auto it = d->m_values.constFind(name); + return (it == d->m_values.constEnd()) ? QVariant() : *it; } -void SessionManager::setSessionValue(const QString &name, const QVariant &value) +void SessionManager::setSessionValue(const Key &name, const QVariant &value) { - sb_d->m_sessionValues.insert(name, value); + d->m_sessionValues.insert(name, value); } -QVariant SessionManager::sessionValue(const QString &name, const QVariant &defaultValue) +QVariant SessionManager::sessionValue(const Key &name, const QVariant &defaultValue) { - auto it = sb_d->m_sessionValues.constFind(name); - return (it == sb_d->m_sessionValues.constEnd()) ? defaultValue : *it; + auto it = d->m_sessionValues.constFind(name); + return (it == d->m_sessionValues.constEnd()) ? defaultValue : *it; } QString SessionManager::activeSession() { - return sb_d->m_sessionName; + return d->m_sessionName; } QStringList SessionManager::sessions() { - if (sb_d->m_sessions.isEmpty()) { + if (d->m_sessions.isEmpty()) { // We are not initialized yet, so do that now const FilePaths sessionFiles = ICore::userResourcePath().dirEntries({{"*qws"}}, QDir::Time | QDir::Reversed); const QVariantMap lastActiveTimes = ICore::settings()->value(LAST_ACTIVE_TIMES_KEY).toMap(); for (const FilePath &file : sessionFiles) { const QString &name = file.completeBaseName(); - sb_d->m_sessionDateTimes.insert(name, file.lastModified()); + d->m_sessionDateTimes.insert(name, file.lastModified()); const auto lastActiveTime = lastActiveTimes.find(name); - sb_d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end() + d->m_lastActiveTimes.insert(name, lastActiveTime != lastActiveTimes.end() ? lastActiveTime->toDateTime() : file.lastModified()); if (name != QLatin1String(DEFAULT_SESSION)) - sb_d->m_sessions << name; + d->m_sessions << name; } - sb_d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION)); + d->m_sessions.prepend(QLatin1String(DEFAULT_SESSION)); } - return sb_d->m_sessions; + return d->m_sessions; } QDateTime SessionManager::sessionDateTime(const QString &session) { - return sb_d->m_sessionDateTimes.value(session); + return d->m_sessionDateTimes.value(session); } QDateTime SessionManager::lastActiveTime(const QString &session) { - return sb_d->m_lastActiveTimes.value(session); + return d->m_lastActiveTimes.value(session); } FilePath SessionManager::sessionNameToFileName(const QString &session) @@ -246,9 +283,9 @@ bool SessionManager::createSession(const QString &session) { if (sessions().contains(session)) return false; - Q_ASSERT(sb_d->m_sessions.size() > 0); - sb_d->m_sessions.insert(1, session); - sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); + Q_ASSERT(d->m_sessions.size() > 0); + d->m_sessions.insert(1, session); + d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); emit instance()->sessionCreated(session); return true; } @@ -267,9 +304,9 @@ void SessionManager::showSessionManager() { saveSession(); Internal::SessionDialog sessionDialog(ICore::dialogParent()); - sessionDialog.setAutoLoadSession(sb_d->isAutoRestoreLastSession()); + sessionDialog.setAutoLoadSession(d->isAutoRestoreLastSession()); sessionDialog.exec(); - sb_d->setAutoRestoreLastSession(sessionDialog.autoLoadSession()); + d->setAutoRestoreLastSession(sessionDialog.autoLoadSession()); } /*! @@ -299,10 +336,10 @@ bool SessionManager::confirmSessionDelete(const QStringList &sessions) */ bool SessionManager::deleteSession(const QString &session) { - if (!sb_d->m_sessions.contains(session)) + if (!d->m_sessions.contains(session)) return false; - sb_d->m_sessions.removeOne(session); - sb_d->m_lastActiveTimes.remove(session); + d->m_sessions.removeOne(session); + d->m_lastActiveTimes.remove(session); emit instance()->sessionRemoved(session); FilePath sessionFile = sessionNameToFileName(session); if (sessionFile.exists()) @@ -318,14 +355,14 @@ void SessionManager::deleteSessions(const QStringList &sessions) bool SessionManager::cloneSession(const QString &original, const QString &clone) { - if (!sb_d->m_sessions.contains(original)) + if (!d->m_sessions.contains(original)) return false; FilePath sessionFile = sessionNameToFileName(original); // If the file does not exist, we can still clone if (!sessionFile.exists() || sessionFile.copyFile(sessionNameToFileName(clone))) { - sb_d->m_sessions.insert(1, clone); - sb_d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified()); + d->m_sessions.insert(1, clone); + d->m_sessionDateTimes.insert(clone, sessionNameToFileName(clone).lastModified()); emit instance()->sessionCreated(clone); return true; @@ -353,14 +390,14 @@ static QString determineSessionToRestoreAtStartup() } } // Handle settings only after command line arguments: - if (sb_d->m_isAutoRestoreLastSession) + if (d->m_isAutoRestoreLastSession) return SessionManager::startupSession(); return {}; } void SessionManagerPrivate::restoreStartupSession() { - m_isStartupSessionRestored = true; + NANOTRACE_SCOPE("Core", "SessionManagerPrivate::restoreStartupSession"); QString sessionToRestoreAtStartup = determineSessionToRestoreAtStartup(); if (!sessionToRestoreAtStartup.isEmpty()) ModeManager::activateMode(Core::Constants::MODE_EDIT); @@ -406,20 +443,17 @@ void SessionManagerPrivate::restoreStartupSession() : QString(), true); - // delay opening projects from the command line even more - QTimer::singleShot(0, m_instance, [arguments] { - ICore::openFiles(Utils::transform(arguments, &FilePath::fromUserInput), - ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers - | ICore::SwitchMode)); - emit m_instance->startupSessionRestored(); - }); + ICore::openFiles(Utils::transform(arguments, &FilePath::fromUserInput), + ICore::OpenFilesFlags(ICore::CanContainLineAndColumnNumbers + | ICore::SwitchMode)); + emit m_instance->startupSessionRestored(); } void SessionManagerPrivate::saveSettings() { QtcSettings *s = ICore::settings(); QVariantMap times; - for (auto it = sb_d->m_lastActiveTimes.cbegin(); it != sb_d->m_lastActiveTimes.cend(); ++it) + for (auto it = d->m_lastActiveTimes.cbegin(); it != d->m_lastActiveTimes.cend(); ++it) times.insert(it.key(), it.value()); s->setValue(LAST_ACTIVE_TIMES_KEY, times); if (SessionManager::isDefaultVirgin()) { @@ -429,13 +463,13 @@ void SessionManagerPrivate::saveSettings() s->setValue(LASTSESSION_KEY, SessionManager::activeSession()); } s->setValueWithDefault(AUTO_RESTORE_SESSION_SETTINGS_KEY, - sb_d->m_isAutoRestoreLastSession, + d->m_isAutoRestoreLastSession, kIsAutoRestoreLastSessionDefault); } void SessionManagerPrivate::restoreSettings() { - sb_d->m_isAutoRestoreLastSession = ICore::settings() + d->m_isAutoRestoreLastSession = ICore::settings() ->value(AUTO_RESTORE_SESSION_SETTINGS_KEY, kIsAutoRestoreLastSessionDefault) .toBool(); @@ -443,12 +477,12 @@ void SessionManagerPrivate::restoreSettings() bool SessionManagerPrivate::isAutoRestoreLastSession() { - return sb_d->m_isAutoRestoreLastSession; + return d->m_isAutoRestoreLastSession; } void SessionManagerPrivate::setAutoRestoreLastSession(bool restore) { - sb_d->m_isAutoRestoreLastSession = restore; + d->m_isAutoRestoreLastSession = restore; } void SessionManagerPrivate::updateSessionMenu() @@ -488,20 +522,20 @@ void SessionManagerPrivate::updateSessionMenu() void SessionManagerPrivate::restoreValues(const PersistentSettingsReader &reader) { - const QStringList keys = reader.restoreValue(QLatin1String("valueKeys")).toStringList(); - for (const QString &key : keys) { - QVariant value = reader.restoreValue(QLatin1String("value-") + key); + const KeyList keys = keysFromStrings(reader.restoreValue("valueKeys").toStringList()); + for (const Key &key : keys) { + QVariant value = reader.restoreValue("value-" + key); m_values.insert(key, value); } } void SessionManagerPrivate::restoreSessionValues(const PersistentSettingsReader &reader) { - const QVariantMap values = reader.restoreValues(); + const Store values = reader.restoreValues(); // restore toplevel items that are not restored by restoreValues const auto end = values.constEnd(); for (auto it = values.constBegin(); it != end; ++it) { - if (it.key() == "valueKeys" || it.key().startsWith("value-")) + if (it.key() == "valueKeys" || it.key().view().startsWith("value-")) continue; m_sessionValues.insert(it.key(), it.value()); } @@ -534,18 +568,18 @@ QString SessionManager::startupSession() void SessionManager::markSessionFileDirty() { - sb_d->m_virginSession = false; + d->m_virginSession = false; } void SessionManager::sessionLoadingProgress() { - sb_d->m_future.setProgressValue(sb_d->m_future.progressValue() + 1); + d->m_future.setProgressValue(d->m_future.progressValue() + 1); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } void SessionManager::addSessionLoadingSteps(int steps) { - sb_d->m_future.setProgressRange(0, sb_d->m_future.progressMaximum() + steps); + d->m_future.setProgressRange(0, d->m_future.progressMaximum() + steps); } /* @@ -577,13 +611,13 @@ bool SessionManager::loadSession(const QString &session, bool initial) { const bool loadImplicitDefault = session.isEmpty(); const bool switchFromImplicitToExplicitDefault = session == DEFAULT_SESSION - && sb_d->m_sessionName == DEFAULT_SESSION + && d->m_sessionName == DEFAULT_SESSION && !initial; // Do nothing if we have that session already loaded, // exception if the session is the default virgin session // we still want to be able to load the default session - if (session == sb_d->m_sessionName && !SessionManager::isDefaultVirgin()) + if (session == d->m_sessionName && !SessionManager::isDefaultVirgin()) return true; if (!loadImplicitDefault && !SessionManager::sessions().contains(session)) @@ -604,7 +638,7 @@ bool SessionManager::loadSession(const QString &session, bool initial) } if (loadImplicitDefault) { - sb_d->restoreValues(reader); + d->restoreValues(reader); emit SessionManager::instance()->sessionLoaded(DEFAULT_SESSION); return true; } @@ -613,44 +647,44 @@ bool SessionManager::loadSession(const QString &session, bool initial) return true; } - sb_d->m_loadingSession = true; + d->m_loadingSession = true; // Allow everyone to set something in the session and before saving - emit SessionManager::instance()->aboutToUnloadSession(sb_d->m_sessionName); + emit SessionManager::instance()->aboutToUnloadSession(d->m_sessionName); if (!saveSession()) { - sb_d->m_loadingSession = false; + d->m_loadingSession = false; return false; } // Clean up if (!EditorManager::closeAllEditors()) { - sb_d->m_loadingSession = false; + d->m_loadingSession = false; return false; } if (!switchFromImplicitToExplicitDefault) - sb_d->m_values.clear(); - sb_d->m_sessionValues.clear(); + d->m_values.clear(); + d->m_sessionValues.clear(); - sb_d->m_sessionName = session; - delete sb_d->m_writer; - sb_d->m_writer = nullptr; + d->m_sessionName = session; + delete d->m_writer; + d->m_writer = nullptr; EditorManager::updateWindowTitles(); - sb_d->m_virginSession = false; + d->m_virginSession = false; - ProgressManager::addTask(sb_d->m_future.future(), + ProgressManager::addTask(d->m_future.future(), PE::Tr::tr("Loading Session"), "ProjectExplorer.SessionFile.Load"); - sb_d->m_future.setProgressRange(0, 1 /*initialization*/ + 1 /*editors*/); - sb_d->m_future.setProgressValue(0); + d->m_future.setProgressRange(0, 1 /*initialization*/ + 1 /*editors*/); + d->m_future.setProgressValue(0); if (fileName.exists()) { if (!switchFromImplicitToExplicitDefault) - sb_d->restoreValues(reader); - sb_d->restoreSessionValues(reader); + d->restoreValues(reader); + d->restoreSessionValues(reader); } QColor c = QColor(SessionManager::sessionValue("Color").toString()); @@ -659,19 +693,19 @@ bool SessionManager::loadSession(const QString &session, bool initial) SessionManager::sessionLoadingProgress(); - sb_d->restoreEditors(); + d->restoreEditors(); // let other code restore the session emit SessionManager::instance()->aboutToLoadSession(session); - sb_d->m_future.reportFinished(); - sb_d->m_future = QFutureInterface<void>(); + d->m_future.reportFinished(); + d->m_future = QFutureInterface<void>(); - sb_d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); + d->m_lastActiveTimes.insert(session, QDateTime::currentDateTime()); emit SessionManager::instance()->sessionLoaded(session); - sb_d->m_loadingSession = false; + d->m_loadingSession = false; return true; } @@ -679,8 +713,8 @@ bool SessionManager::saveSession() { emit SessionManager::instance()->aboutToSaveSession(); - const FilePath filePath = SessionManager::sessionNameToFileName(sb_d->m_sessionName); - QVariantMap data; + const FilePath filePath = SessionManager::sessionNameToFileName(d->m_sessionName); + Store data; // See the explanation at loadSession() for how we handle the implicit default session. if (SessionManager::isDefaultVirgin()) { @@ -706,41 +740,36 @@ bool SessionManager::saveSession() } setSessionValue("EditorSettings", EditorManager::saveState().toBase64()); - const auto end = sb_d->m_sessionValues.constEnd(); - for (auto it = sb_d->m_sessionValues.constBegin(); it != end; ++it) + const auto end = d->m_sessionValues.constEnd(); + for (auto it = d->m_sessionValues.constBegin(); it != end; ++it) data.insert(it.key(), it.value()); } - const auto end = sb_d->m_values.constEnd(); - QStringList keys; - for (auto it = sb_d->m_values.constBegin(); it != end; ++it) { + const auto end = d->m_values.constEnd(); + KeyList keys; + for (auto it = d->m_values.constBegin(); it != end; ++it) { data.insert("value-" + it.key(), it.value()); keys << it.key(); } - data.insert("valueKeys", keys); + data.insert("valueKeys", stringsFromKeys(keys)); - if (!sb_d->m_writer || sb_d->m_writer->fileName() != filePath) { - delete sb_d->m_writer; - sb_d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession"); + if (!d->m_writer || d->m_writer->fileName() != filePath) { + delete d->m_writer; + d->m_writer = new PersistentSettingsWriter(filePath, "QtCreatorSession"); } - const bool result = sb_d->m_writer->save(data, ICore::dialogParent()); + const bool result = d->m_writer->save(data, ICore::dialogParent()); if (result) { if (!SessionManager::isDefaultVirgin()) - sb_d->m_sessionDateTimes.insert(SessionManager::activeSession(), + d->m_sessionDateTimes.insert(SessionManager::activeSession(), QDateTime::currentDateTime()); } else { QMessageBox::warning(ICore::dialogParent(), PE::Tr::tr("Error while saving session"), PE::Tr::tr("Could not save session to file \"%1\"") - .arg(sb_d->m_writer->fileName().toUserOutput())); + .arg(d->m_writer->fileName().toUserOutput())); } return result; } -bool SessionManager::isStartupSessionRestored() -{ - return sb_d->m_isStartupSessionRestored; -} - } // namespace Core diff --git a/src/plugins/coreplugin/session.h b/src/plugins/coreplugin/session.h index 30dc70edf4d..962db5172b5 100644 --- a/src/plugins/coreplugin/session.h +++ b/src/plugins/coreplugin/session.h @@ -5,13 +5,16 @@ #include "core_global.h" -#include <utils/id.h> -#include <utils/persistentsettings.h> - #include <QDateTime> +#include <QObject> #include <QString> #include <QStringList> +namespace Utils { +class FilePath; +class Key; +} // Utils + namespace Core { class CORE_EXPORT SessionManager : public QObject @@ -50,13 +53,13 @@ public: // Let other plugins store persistent values within the session file // These are settings that are also saved and loaded at startup, and are taken over // to the default session when switching from implicit to explicit default session - static void setValue(const QString &name, const QVariant &value); - static QVariant value(const QString &name); + static void setValue(const Utils::Key &name, const QVariant &value); + static QVariant value(const Utils::Key &name); // These are settings that are specific to a session and are not loaded // at startup and also not taken over to the default session when switching from implicit - static void setSessionValue(const QString &name, const QVariant &value); - static QVariant sessionValue(const QString &name, const QVariant &defaultValue = {}); + static void setSessionValue(const Utils::Key &name, const QVariant &value); + static QVariant sessionValue(const Utils::Key &name, const QVariant &defaultValue = {}); static bool isLoadingSession(); static void markSessionFileDirty(); @@ -79,12 +82,6 @@ signals: void sessionCreated(const QString &name); void sessionRenamed(const QString &oldName, const QString &newName); void sessionRemoved(const QString &name); - -public: // internal - static bool isStartupSessionRestored(); - -private: - static void saveActiveMode(Utils::Id mode); }; } // namespace Core diff --git a/src/plugins/coreplugin/session_p.h b/src/plugins/coreplugin/session_p.h deleted file mode 100644 index 20ceecbcbc3..00000000000 --- a/src/plugins/coreplugin/session_p.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <utils/persistentsettings.h> - -#include <QFutureInterface> - -QT_BEGIN_NAMESPACE -class QAction; -class QMenu; -QT_END_NAMESPACE - -using namespace Utils; - -namespace Core { - -class SessionManagerPrivate -{ -public: - void restoreStartupSession(); - - void restoreValues(const PersistentSettingsReader &reader); - void restoreSessionValues(const PersistentSettingsReader &reader); - void restoreEditors(); - - void saveSettings(); - void restoreSettings(); - bool isAutoRestoreLastSession(); - void setAutoRestoreLastSession(bool restore); - - void updateSessionMenu(); - - static QString windowTitleAddition(const FilePath &filePath); - static QString sessionTitle(const FilePath &filePath); - - QString m_sessionName = "default"; - bool m_isStartupSessionRestored = false; - bool m_isAutoRestoreLastSession = false; - bool m_virginSession = true; - bool m_loadingSession = false; - - mutable QStringList m_sessions; - mutable QHash<QString, QDateTime> m_sessionDateTimes; - QHash<QString, QDateTime> m_lastActiveTimes; - - QMap<QString, QVariant> m_values; - QMap<QString, QVariant> m_sessionValues; - QFutureInterface<void> m_future; - PersistentSettingsWriter *m_writer = nullptr; - - QMenu *m_sessionMenu; - QAction *m_sessionManagerAction; -}; - -extern SessionManagerPrivate *sb_d; - -} // namespace Core diff --git a/src/plugins/coreplugin/sessiondialog.cpp b/src/plugins/coreplugin/sessiondialog.cpp index dd81f43fd09..5ab1343a963 100644 --- a/src/plugins/coreplugin/sessiondialog.cpp +++ b/src/plugins/coreplugin/sessiondialog.cpp @@ -93,7 +93,7 @@ SessionNameInputDialog::SessionNameInputDialog(QWidget *parent) }.attachTo(this); // clang-format on - connect(m_newSessionLineEdit, &QLineEdit::textChanged, [this](const QString &text) { + connect(m_newSessionLineEdit, &QLineEdit::textChanged, this, [this](const QString &text) { m_okButton->setEnabled(!text.isEmpty()); m_switchToButton->setEnabled(!text.isEmpty()); }); @@ -136,14 +136,14 @@ SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) sessionView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); sessionView->setActivationMode(Utils::DoubleClickActivation); - auto createNewButton = new QPushButton(PE::Tr::tr("&New")); + auto createNewButton = new QPushButton(PE::Tr::tr("&New...")); createNewButton->setObjectName("btCreateNew"); m_openButton = new QPushButton(PE::Tr::tr("&Open")); m_openButton->setObjectName("btOpen"); - m_renameButton = new QPushButton(PE::Tr::tr("&Rename")); - m_cloneButton = new QPushButton(PE::Tr::tr("C&lone")); - m_deleteButton = new QPushButton(PE::Tr::tr("&Delete")); + m_renameButton = new QPushButton(PE::Tr::tr("&Rename...")); + m_cloneButton = new QPushButton(PE::Tr::tr("C&lone...")); + m_deleteButton = new QPushButton(PE::Tr::tr("&Delete...")); m_autoLoadCheckBox = new QCheckBox(PE::Tr::tr("Restore last session on startup")); diff --git a/src/plugins/coreplugin/sessionmodel.cpp b/src/plugins/coreplugin/sessionmodel.cpp index fa0d8ee087e..1d97762eb0f 100644 --- a/src/plugins/coreplugin/sessionmodel.cpp +++ b/src/plugins/coreplugin/sessionmodel.cpp @@ -3,11 +3,10 @@ #include "sessionmodel.h" +#include "actionmanager/actionmanager.h" #include "session.h" #include "sessiondialog.h" -#include <coreplugin/actionmanager/actionmanager.h> - #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/stringutils.h> diff --git a/src/plugins/coreplugin/sessionview.cpp b/src/plugins/coreplugin/sessionview.cpp index c3d7ed25b30..589d725156b 100644 --- a/src/plugins/coreplugin/sessionview.cpp +++ b/src/plugins/coreplugin/sessionview.cpp @@ -36,6 +36,7 @@ void RemoveItemFocusDelegate::paint(QPainter* painter, const QStyleOptionViewIte SessionView::SessionView(QWidget *parent) : Utils::TreeView(parent) { + setUniformRowHeights(false); setItemDelegate(new RemoveItemFocusDelegate(this)); setSelectionBehavior(QAbstractItemView::SelectRows); setSelectionMode(QAbstractItemView::ExtendedSelection); @@ -54,10 +55,10 @@ SessionView::SessionView(QWidget *parent) selectionModel()->select(firstRow, QItemSelectionModel::QItemSelectionModel:: SelectCurrent); - connect(this, &Utils::TreeView::activated, [this](const QModelIndex &index){ + connect(this, &Utils::TreeView::activated, this, [this](const QModelIndex &index){ emit sessionActivated(m_sessionModel.sessionAt(index.row())); }); - connect(selectionModel(), &QItemSelectionModel::selectionChanged, [this] { + connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [this] { emit sessionsSelected(selectedSessions()); }); diff --git a/src/plugins/coreplugin/settingsdatabase.cpp b/src/plugins/coreplugin/settingsdatabase.cpp index 281940d689b..d5be946796d 100644 --- a/src/plugins/coreplugin/settingsdatabase.cpp +++ b/src/plugins/coreplugin/settingsdatabase.cpp @@ -3,6 +3,8 @@ #include "settingsdatabase.h" +#include <extensionsystem/pluginmanager.h> + #include <QDir> #include <QMap> #include <QString> @@ -13,13 +15,14 @@ #include <QSqlError> #include <QSqlQuery> #include <QDebug> +#include <QCoreApplication> /*! - \class Core::SettingsDatabase + \namespace Core::SettingsDatabase \inheaderfile coreplugin/settingsdatabase.h \inmodule QtCreator - \brief The SettingsDatabase class offers an alternative to the + \brief The SettingsDatabase namespace offers an alternative to the application-wide QSettings that is more suitable for storing large amounts of data. @@ -28,19 +31,20 @@ rewriting the whole file each time one of the settings change. The SettingsDatabase API mimics that of QSettings. + + \sa settings() */ using namespace Core; -using namespace Core::Internal; +using namespace ExtensionSystem; enum { debug_settings = 0 }; -namespace Core { -namespace Internal { +namespace Core::SettingsDatabase { using SettingsMap = QMap<QString, QVariant>; -class SettingsDatabasePrivate +class SettingsDatabaseImpl { public: QString effectiveGroup() const @@ -65,15 +69,17 @@ public: QSqlDatabase m_db; }; -} // namespace Internal -} // namespace Core +static SettingsDatabaseImpl *d; -SettingsDatabase::SettingsDatabase(const QString &path, - const QString &application, - QObject *parent) - : QObject(parent) - , d(new SettingsDatabasePrivate) +void ensureImpl() { + if (d) + return; + + d = new SettingsDatabaseImpl; + + const QString path = QFileInfo(PluginManager::settings()->fileName()).path(); + const QString application = QCoreApplication::applicationName(); const QLatin1Char slash('/'); // TODO: Don't rely on a path, but determine automatically @@ -114,16 +120,21 @@ SettingsDatabase::SettingsDatabase(const QString &path, } } -SettingsDatabase::~SettingsDatabase() +void destroy() { - sync(); + if (!d) + return; + + // TODO: Delay writing of dirty keys and save them here delete d; + d = nullptr; QSqlDatabase::removeDatabase(QLatin1String("settings")); } -void SettingsDatabase::setValue(const QString &key, const QVariant &value) +void setValue(const QString &key, const QVariant &value) { + ensureImpl(); const QString effectiveKey = d->effectiveKey(key); // Add to cache @@ -143,8 +154,9 @@ void SettingsDatabase::setValue(const QString &key, const QVariant &value) qDebug() << "Stored:" << effectiveKey << "=" << value; } -QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const +QVariant value(const QString &key, const QVariant &defaultValue) { + ensureImpl(); const QString effectiveKey = d->effectiveKey(key); QVariant value = defaultValue; @@ -171,8 +183,9 @@ QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValu return value; } -bool SettingsDatabase::contains(const QString &key) const +bool contains(const QString &key) { + ensureImpl(); // check exact key // this already caches the value if (value(key).isValid()) @@ -190,8 +203,9 @@ bool SettingsDatabase::contains(const QString &key) const return false; } -void SettingsDatabase::remove(const QString &key) +void remove(const QString &key) { + ensureImpl(); const QString effectiveKey = d->effectiveKey(key); // Remove keys from the cache @@ -217,23 +231,27 @@ void SettingsDatabase::remove(const QString &key) query.exec(); } -void SettingsDatabase::beginGroup(const QString &prefix) +void beginGroup(const QString &prefix) { + ensureImpl(); d->m_groups.append(prefix); } -void SettingsDatabase::endGroup() +void endGroup() { + ensureImpl(); d->m_groups.removeLast(); } -QString SettingsDatabase::group() const +QString group() { + ensureImpl(); return d->effectiveGroup(); } -QStringList SettingsDatabase::childKeys() const +QStringList childKeys() { + ensureImpl(); QStringList children; const QString g = group(); @@ -246,21 +264,20 @@ QStringList SettingsDatabase::childKeys() const return children; } -void SettingsDatabase::beginTransaction() +void beginTransaction() { + ensureImpl(); if (!d->m_db.isOpen()) return; d->m_db.transaction(); } -void SettingsDatabase::endTransaction() +void endTransaction() { + ensureImpl(); if (!d->m_db.isOpen()) return; d->m_db.commit(); } -void SettingsDatabase::sync() -{ - // TODO: Delay writing of dirty keys and save them here -} +} // Core::SettingsDatabase diff --git a/src/plugins/coreplugin/settingsdatabase.h b/src/plugins/coreplugin/settingsdatabase.h index 6cde9e0080c..e4c4f5a6622 100644 --- a/src/plugins/coreplugin/settingsdatabase.h +++ b/src/plugins/coreplugin/settingsdatabase.h @@ -5,48 +5,28 @@ #include "core_global.h" -#include <QObject> -#include <QString> -#include <QStringList> #include <QVariant> -namespace Core { +namespace Core::SettingsDatabase { -namespace Internal { class SettingsDatabasePrivate; } +CORE_EXPORT void setValue(const QString &key, const QVariant &value); +CORE_EXPORT QVariant value(const QString &key, const QVariant &defaultValue = {}); -class CORE_EXPORT SettingsDatabase : public QObject -{ -public: - SettingsDatabase(const QString &path, const QString &application, QObject *parent = nullptr); - ~SettingsDatabase() override; +CORE_EXPORT bool contains(const QString &key); +CORE_EXPORT void remove(const QString &key); - void setValue(const QString &key, const QVariant &value); - QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; +CORE_EXPORT void beginGroup(const QString &prefix); +CORE_EXPORT void endGroup(); +CORE_EXPORT QString group(); +CORE_EXPORT QStringList childKeys(); - template<typename T> - void setValueWithDefault(const QString &key, const T &val, const T &defaultValue); - template<typename T> - void setValueWithDefault(const QString &key, const T &val); +CORE_EXPORT void beginTransaction(); +CORE_EXPORT void endTransaction(); - bool contains(const QString &key) const; - void remove(const QString &key); - - void beginGroup(const QString &prefix); - void endGroup(); - QString group() const; - QStringList childKeys() const; - - void beginTransaction(); - void endTransaction(); - - void sync(); - -private: - Internal::SettingsDatabasePrivate *d; -}; +CORE_EXPORT void destroy(); template<typename T> -void SettingsDatabase::setValueWithDefault(const QString &key, const T &val, const T &defaultValue) +void setValueWithDefault(const QString &key, const T &val, const T &defaultValue) { if (val == defaultValue) remove(key); @@ -55,7 +35,7 @@ void SettingsDatabase::setValueWithDefault(const QString &key, const T &val, con } template<typename T> -void SettingsDatabase::setValueWithDefault(const QString &key, const T &val) +void setValueWithDefault(const QString &key, const T &val) { if (val == T()) remove(key); @@ -63,4 +43,4 @@ void SettingsDatabase::setValueWithDefault(const QString &key, const T &val) setValue(key, QVariant::fromValue(val)); } -} // namespace Core +} // Core::SettingsDatabase diff --git a/src/plugins/coreplugin/sidebar.cpp b/src/plugins/coreplugin/sidebar.cpp index c7a9b922647..8e38ec224ce 100644 --- a/src/plugins/coreplugin/sidebar.cpp +++ b/src/plugins/coreplugin/sidebar.cpp @@ -5,14 +5,17 @@ #include "sidebarwidget.h" #include "actionmanager/command.h" + #include <utils/algorithm.h> #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> #include <utils/utilsicons.h> -#include <QSettings> #include <QPointer> #include <QToolButton> +using namespace Utils; + namespace Core { SideBarItem::SideBarItem(QWidget *widget, const QString &id) : @@ -42,7 +45,7 @@ QString SideBarItem::title() const QList<QToolButton *> SideBarItem::createToolBarWidgets() { - return QList<QToolButton *>(); + return {}; } struct SideBarPrivate { @@ -228,9 +231,9 @@ void SideBar::updateWidgets() i->updateAvailableItems(); } -void SideBar::saveSettings(QSettings *settings, const QString &name) +void SideBar::saveSettings(QtcSettings *settings, const QString &name) { - const QString prefix = name.isEmpty() ? name : (name + QLatin1Char('/')); + const Key prefix = keyFromString(name.isEmpty() ? name : (name + QLatin1Char('/'))); QStringList views; for (int i = 0; i < d->m_widgets.count(); ++i) { @@ -241,11 +244,10 @@ void SideBar::saveSettings(QSettings *settings, const QString &name) if (views.isEmpty() && !d->m_itemMap.isEmpty()) views.append(d->m_itemMap.cbegin().key()); - settings->setValue(prefix + QLatin1String("Views"), views); - settings->setValue(prefix + QLatin1String("Visible"), - parentWidget() ? isVisibleTo(parentWidget()) : true); - settings->setValue(prefix + QLatin1String("VerticalPosition"), saveState()); - settings->setValue(prefix + QLatin1String("Width"), width()); + settings->setValue(prefix + "Views", views); + settings->setValue(prefix + "Visible", parentWidget() ? isVisibleTo(parentWidget()) : true); + settings->setValue(prefix + "VerticalPosition", saveState()); + settings->setValue(prefix + "Width", width()); } void SideBar::closeAllWidgets() @@ -254,13 +256,13 @@ void SideBar::closeAllWidgets() removeSideBarWidget(widget); } -void SideBar::readSettings(QSettings *settings, const QString &name) +void SideBar::readSettings(QtcSettings *settings, const QString &name) { - const QString prefix = name.isEmpty() ? name : (name + QLatin1Char('/')); + const Key prefix = keyFromString(name.isEmpty() ? name : (name + QLatin1Char('/'))); closeAllWidgets(); - const QString viewsKey = prefix + QLatin1String("Views"); + const Key viewsKey = prefix + "Views"; if (settings->contains(viewsKey)) { const QStringList views = settings->value(viewsKey).toStringList(); if (!views.isEmpty()) { @@ -277,15 +279,15 @@ void SideBar::readSettings(QSettings *settings, const QString &name) insertSideBarWidget(d->m_widgets.count(), id); } - const QString visibleKey = prefix + QLatin1String("Visible"); + const Key visibleKey = prefix + "Visible"; if (settings->contains(visibleKey)) setVisible(settings->value(visibleKey).toBool()); - const QString positionKey = prefix + QLatin1String("VerticalPosition"); + const Key positionKey = prefix + "VerticalPosition"; if (settings->contains(positionKey)) restoreState(settings->value(positionKey).toByteArray()); - const QString widthKey = prefix + QLatin1String("Width"); + const Key widthKey = prefix + "Width"; if (settings->contains(widthKey)) { QSize s = size(); s.setWidth(settings->value(widthKey).toInt()); @@ -318,5 +320,5 @@ QMap<QString, Command*> SideBar::shortcutMap() const { return d->m_shortcutMap; } -} // namespace Core +} // namespace Core diff --git a/src/plugins/coreplugin/sidebar.h b/src/plugins/coreplugin/sidebar.h index adbfde1b1dd..733d9c9cc62 100644 --- a/src/plugins/coreplugin/sidebar.h +++ b/src/plugins/coreplugin/sidebar.h @@ -10,10 +10,11 @@ #include <QList> QT_BEGIN_NAMESPACE -class QSettings; class QToolButton; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace Core { class Command; @@ -78,8 +79,8 @@ public: bool closeWhenEmpty() const; void setCloseWhenEmpty(bool value); - void saveSettings(QSettings *settings, const QString &name); - void readSettings(QSettings *settings, const QString &name); + void saveSettings(Utils::QtcSettings *settings, const QString &name); + void readSettings(Utils::QtcSettings *settings, const QString &name); void closeAllWidgets(); void activateItem(const QString &id); diff --git a/src/plugins/coreplugin/statusbarmanager.cpp b/src/plugins/coreplugin/statusbarmanager.cpp index 83d910867bf..c590b3f29ef 100644 --- a/src/plugins/coreplugin/statusbarmanager.cpp +++ b/src/plugins/coreplugin/statusbarmanager.cpp @@ -3,8 +3,8 @@ #include "statusbarmanager.h" +#include "icore.h" #include "imode.h" -#include "mainwindow.h" #include "minisplitter.h" #include "modemanager.h" @@ -16,6 +16,8 @@ #include <QSplitter> #include <QStatusBar> +using namespace Utils; + namespace Core { const char kSettingsGroup[] = "StatusBar"; @@ -80,9 +82,9 @@ static void createStatusBarManager() ICore::addContextObject(statusContext); QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, ICore::instance(), [] { - QSettings *s = ICore::settings(); - s->beginGroup(QLatin1String(kSettingsGroup)); - s->setValue(QLatin1String(kLeftSplitWidthKey), m_splitter->sizes().at(0)); + QtcSettings *s = ICore::settings(); + s->beginGroup(kSettingsGroup); + s->setValue(kLeftSplitWidthKey, m_splitter->sizes().at(0)); s->endGroup(); }); @@ -132,9 +134,9 @@ void StatusBarManager::destroyStatusBarWidget(QWidget *widget) void StatusBarManager::restoreSettings() { - QSettings *s = ICore::settings(); - s->beginGroup(QLatin1String(kSettingsGroup)); - int leftSplitWidth = s->value(QLatin1String(kLeftSplitWidthKey), -1).toInt(); + QtcSettings *s = ICore::settings(); + s->beginGroup(kSettingsGroup); + int leftSplitWidth = s->value(kLeftSplitWidthKey, -1).toInt(); s->endGroup(); if (leftSplitWidth < 0) { // size first split after its sizeHint + a bit of buffer diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp index f6641bad08f..1477fcf747d 100644 --- a/src/plugins/coreplugin/systemsettings.cpp +++ b/src/plugins/coreplugin/systemsettings.cpp @@ -8,15 +8,12 @@ #include "coreplugintr.h" #include "editormanager/editormanager_p.h" #include "dialogs/restartdialog.h" +#include "dialogs/ioptionspage.h" #include "fileutils.h" #include "icore.h" #include "iversioncontrol.h" -#include "mainwindow.h" -#include "patchtool.h" #include "vcsmanager.h" -#include <app/app_version.h> - #include <utils/algorithm.h> #include <utils/checkablemessagebox.h> #include <utils/elidinglabel.h> @@ -28,26 +25,19 @@ #include <utils/terminalcommand.h> #include <utils/unixutils.h> -#include <QCheckBox> #include <QComboBox> -#include <QCoreApplication> +#include <QGuiApplication> #include <QLineEdit> #include <QMessageBox> #include <QPushButton> -#include <QSettings> -#include <QSpinBox> #include <QToolButton> using namespace Utils; using namespace Layouting; -namespace Core { -namespace Internal { +namespace Core::Internal { #ifdef ENABLE_CRASHPAD -const char crashReportingEnabledKey[] = "CrashReportingEnabled"; -const char showCrashButtonKey[] = "ShowCrashButton"; - // TODO: move to somewhere in Utils static QString formatSize(qint64 size) { @@ -64,55 +54,131 @@ static QString formatSize(qint64 size) } #endif // ENABLE_CRASHPAD +SystemSettings &systemSettings() +{ + static SystemSettings theSettings; + return theSettings; +} + +SystemSettings::SystemSettings() +{ + setAutoApply(false); + + patchCommand.setSettingsKey("General/PatchCommand"); + patchCommand.setDefaultValue("patch"); + patchCommand.setExpectedKind(PathChooser::ExistingCommand); + patchCommand.setHistoryCompleter("General.PatchCommand.History"); + patchCommand.setLabelText(Tr::tr("Patch command:")); + patchCommand.setToolTip(Tr::tr("Command used for reverting diff chunks.")); + + autoSaveModifiedFiles.setSettingsKey("EditorManager/AutoSaveEnabled"); + autoSaveModifiedFiles.setDefaultValue(true); + autoSaveModifiedFiles.setLabelText(Tr::tr("Auto-save modified files")); + autoSaveModifiedFiles.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + autoSaveModifiedFiles.setToolTip( + Tr::tr("Automatically creates temporary copies of modified files. " + "If %1 is restarted after a crash or power failure, it asks whether to " + "recover the auto-saved content.") + .arg(QGuiApplication::applicationDisplayName())); + + autoSaveInterval.setSettingsKey("EditorManager/AutoSaveInterval"); + autoSaveInterval.setSuffix(Tr::tr("min")); + autoSaveInterval.setRange(1, 1000000); + autoSaveInterval.setDefaultValue(5); + autoSaveInterval.setEnabler(&autoSaveModifiedFiles); + autoSaveInterval.setLabelText(Tr::tr("Interval:")); + + autoSaveAfterRefactoring.setSettingsKey("EditorManager/AutoSaveAfterRefactoring"); + autoSaveAfterRefactoring.setDefaultValue(true); + autoSaveAfterRefactoring.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + autoSaveAfterRefactoring.setLabelText(Tr::tr("Auto-save files after refactoring")); + autoSaveAfterRefactoring.setToolTip( + Tr::tr("Automatically saves all open files affected by a refactoring operation,\n" + "provided they were unmodified before the refactoring.")); + + autoSuspendEnabled.setSettingsKey("EditorManager/AutoSuspendEnabled"); + autoSuspendEnabled.setDefaultValue(true); + autoSuspendEnabled.setLabelText(Tr::tr("Auto-suspend unmodified files")); + autoSuspendEnabled.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + autoSuspendEnabled.setToolTip( + Tr::tr("Automatically free resources of old documents that are not visible and not " + "modified. They stay visible in the list of open documents.")); + + autoSuspendMinDocumentCount.setSettingsKey("EditorManager/AutoSuspendMinDocuments"); + autoSuspendMinDocumentCount.setRange(1, 500); + autoSuspendMinDocumentCount.setDefaultValue(30); + autoSuspendMinDocumentCount.setEnabler(&autoSuspendEnabled); + autoSuspendMinDocumentCount.setLabelText(Tr::tr("Files to keep open:")); + autoSuspendMinDocumentCount.setToolTip( + Tr::tr("Minimum number of open documents that should be kept in memory. Increasing this " + "number will lead to greater resource usage when not manually closing documents.")); + + warnBeforeOpeningBigFiles.setSettingsKey("EditorManager/WarnBeforeOpeningBigTextFiles"); + warnBeforeOpeningBigFiles.setDefaultValue(true); + warnBeforeOpeningBigFiles.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + warnBeforeOpeningBigFiles.setLabelText(Tr::tr("Warn before opening text files greater than")); + + bigFileSizeLimitInMB.setSettingsKey("EditorManager/BigTextFileSizeLimitInMB"); + bigFileSizeLimitInMB.setSuffix(Tr::tr("MB")); + bigFileSizeLimitInMB.setRange(1, 500); + bigFileSizeLimitInMB.setDefaultValue(5); + bigFileSizeLimitInMB.setEnabler(&warnBeforeOpeningBigFiles); + + maxRecentFiles.setSettingsKey("EditorManager/MaxRecentFiles"); + maxRecentFiles.setRange(1, 99); + maxRecentFiles.setDefaultValue(8); + + reloadSetting.setSettingsKey("EditorManager/ReloadBehavior"); + reloadSetting.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + reloadSetting.addOption(Tr::tr("Always Ask")); + reloadSetting.addOption(Tr::tr("Reload All Unchanged Editors")); + reloadSetting.addOption(Tr::tr("Ignore Modifications")); + reloadSetting.setDefaultValue(IDocument::AlwaysAsk); + reloadSetting.setLabelText(Tr::tr("When files are externally modified:")); + + askBeforeExit.setSettingsKey("AskBeforeExit"); + askBeforeExit.setLabelText(Tr::tr("Ask for confirmation before exiting")); + askBeforeExit.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + +#ifdef ENABLE_CRASHPAD + enableCrashReporting.setSettingsKey("CrashReportingEnabled"); + enableCrashReporting.setLabelPlacement(BoolAspect::LabelPlacement::Compact); + enableCrashReporting.setLabelText(Tr::tr("Enable crash reporting")); + enableCrashReporting.setToolTip( + Tr::tr("Allow crashes to be automatically reported. Collected reports are " + "used for the sole purpose of fixing bugs.")); + + showCrashButton.setSettingsKey("ShowCrashButton"); +#endif + readSettings(); + connect(&autoSaveModifiedFiles, &BaseAspect::changed, + this, &EditorManagerPrivate::updateAutoSave); + connect(&autoSaveInterval, &BaseAspect::changed, this, &EditorManagerPrivate::updateAutoSave); +} + class SystemSettingsWidget : public IOptionsPageWidget { public: SystemSettingsWidget() : m_fileSystemCaseSensitivityChooser(new QComboBox) - , m_autoSuspendMinDocumentCount(new QSpinBox) , m_externalFileBrowserEdit(new QLineEdit) - , m_autoSuspendCheckBox(new QCheckBox(Tr::tr("Auto-suspend unmodified files"))) - , m_maxRecentFilesSpinBox(new QSpinBox) - , m_enableCrashReportingCheckBox(new QCheckBox(Tr::tr("Enable crash reporting"))) - , m_warnBeforeOpeningBigFiles( - new QCheckBox(Tr::tr("Warn before opening text files greater than"))) - , m_bigFilesLimitSpinBox(new QSpinBox) , m_terminalComboBox(new QComboBox) , m_terminalOpenArgs(new QLineEdit) , m_terminalExecuteArgs(new QLineEdit) - , m_patchChooser(new Utils::PathChooser) , m_environmentChangesLabel(new Utils::ElidingLabel) - , m_askBeforeExitCheckBox(new QCheckBox(Tr::tr("Ask for confirmation before exiting"))) - , m_reloadBehavior(new QComboBox) - , m_autoSaveCheckBox(new QCheckBox(Tr::tr("Auto-save modified files"))) - , m_clearCrashReportsButton(new QPushButton(Tr::tr("Clear Local Crash Reports"))) - , m_crashReportsSizeText(new QLabel) - , m_autoSaveInterval(new QSpinBox) - , m_autoSaveRefactoringCheckBox(new QCheckBox(Tr::tr("Auto-save files after refactoring"))) +#ifdef ENABLE_CRASHPAD + , m_clearCrashReportsButton(new QPushButton(Tr::tr("Clear Local Crash Reports"), this)) + , m_crashReportsSizeText(new QLabel(this)) +#endif { - m_autoSuspendCheckBox->setToolTip( - Tr::tr("Automatically free resources of old documents that are not visible and not " - "modified. They stay visible in the list of open documents.")); - m_autoSuspendMinDocumentCount->setMinimum(1); - m_autoSuspendMinDocumentCount->setMaximum(500); - m_autoSuspendMinDocumentCount->setValue(30); - m_enableCrashReportingCheckBox->setToolTip( - Tr::tr("Allow crashes to be automatically reported. Collected reports are " - "used for the sole purpose of fixing bugs.")); - m_bigFilesLimitSpinBox->setSuffix(Tr::tr("MB")); - m_bigFilesLimitSpinBox->setMinimum(1); - m_bigFilesLimitSpinBox->setMaximum(500); - m_bigFilesLimitSpinBox->setValue(5); + SystemSettings &s = systemSettings(); + m_terminalExecuteArgs->setToolTip( Tr::tr("Command line arguments used for \"Run in terminal\".")); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(5); m_environmentChangesLabel->setSizePolicy(sizePolicy); - m_reloadBehavior->addItem(Tr::tr("Always Ask")); - m_reloadBehavior->addItem(Tr::tr("Reload All Unchanged Editors")); - m_reloadBehavior->addItem(Tr::tr("Ignore Modifications")); - m_autoSaveInterval->setSuffix(Tr::tr("min")); QSizePolicy termSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); termSizePolicy.setHorizontalStretch(3); m_terminalComboBox->setSizePolicy(termSizePolicy); @@ -120,77 +186,67 @@ public: m_terminalComboBox->setEditable(true); m_terminalOpenArgs->setToolTip( Tr::tr("Command line arguments used for \"%1\".").arg(FileUtils::msgTerminalHereAction())); - m_autoSaveInterval->setMinimum(1); auto fileSystemCaseSensitivityLabel = new QLabel(Tr::tr("File system case sensitivity:")); fileSystemCaseSensitivityLabel->setToolTip( Tr::tr("Influences how file names are matched to decide if they are the same.")); - auto autoSuspendLabel = new QLabel(Tr::tr("Files to keep open:")); - autoSuspendLabel->setToolTip( - Tr::tr("Minimum number of open documents that should be kept in memory. Increasing this " - "number will lead to greater resource usage when not manually closing documents.")); auto resetFileBrowserButton = new QPushButton(Tr::tr("Reset")); resetFileBrowserButton->setToolTip(Tr::tr("Reset to default.")); auto helpExternalFileBrowserButton = new QToolButton; helpExternalFileBrowserButton->setText(Tr::tr("?")); - auto helpCrashReportingButton = new QToolButton; +#ifdef ENABLE_CRASHPAD + auto helpCrashReportingButton = new QToolButton(this); helpCrashReportingButton->setText(Tr::tr("?")); +#endif auto resetTerminalButton = new QPushButton(Tr::tr("Reset")); resetTerminalButton->setToolTip(Tr::tr("Reset to default.", "Terminal")); - auto patchCommandLabel = new QLabel(Tr::tr("Patch command:")); auto environmentButton = new QPushButton(Tr::tr("Change...")); environmentButton->setSizePolicy(QSizePolicy::Fixed, environmentButton->sizePolicy().verticalPolicy()); - Grid form; - form.addRow( - {Tr::tr("Environment:"), Span(2, Row{m_environmentChangesLabel, environmentButton})}); + Grid grid; + grid.addRow({Tr::tr("Environment:"), + Span(3, m_environmentChangesLabel), environmentButton}); if (HostOsInfo::isAnyUnixHost()) { - form.addRow({Tr::tr("Terminal:"), - Span(2, - Row{m_terminalComboBox, - m_terminalOpenArgs, - m_terminalExecuteArgs, - resetTerminalButton})}); + grid.addRow({Tr::tr("Terminal:"), + m_terminalComboBox, + m_terminalOpenArgs, + m_terminalExecuteArgs, + resetTerminalButton}); } if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) { - form.addRow({Tr::tr("External file browser:"), - Span(2, - Row{m_externalFileBrowserEdit, - resetFileBrowserButton, - helpExternalFileBrowserButton})}); + grid.addRow({Tr::tr("External file browser:"), + Span(3, m_externalFileBrowserEdit), + resetFileBrowserButton, + helpExternalFileBrowserButton}); } - form.addRow({patchCommandLabel, Span(2, m_patchChooser)}); + grid.addRow({Span(4, s.patchCommand)}); if (HostOsInfo::isMacHost()) { - form.addRow({fileSystemCaseSensitivityLabel, - Span(2, Row{m_fileSystemCaseSensitivityChooser, st})}); + grid.addRow({fileSystemCaseSensitivityLabel, + m_fileSystemCaseSensitivityChooser}); } - form.addRow( - {Tr::tr("When files are externally modified:"), Span(2, Row{m_reloadBehavior, st})}); - form.addRow( - {m_autoSaveCheckBox, Span(2, Row{Tr::tr("Interval:"), m_autoSaveInterval, st})}); - form.addRow({Span(3, m_autoSaveRefactoringCheckBox)}); - form.addRow({m_autoSuspendCheckBox, - Span(2, Row{autoSuspendLabel, m_autoSuspendMinDocumentCount, st})}); - form.addRow({Span(3, Row{m_warnBeforeOpeningBigFiles, m_bigFilesLimitSpinBox, st})}); - form.addRow({Span(3, - Row{Tr::tr("Maximum number of entries in \"Recent Files\":"), - m_maxRecentFilesSpinBox, - st})}); - form.addRow({m_askBeforeExitCheckBox}); + grid.addRow({s.reloadSetting}); + grid.addRow({s.autoSaveModifiedFiles, Row{s.autoSaveInterval, st}}); + grid.addRow({s.autoSuspendEnabled, Row{s.autoSuspendMinDocumentCount, st}}); + grid.addRow({s.autoSaveAfterRefactoring}); + grid.addRow({s.warnBeforeOpeningBigFiles, Row{s.bigFileSizeLimitInMB, st}}); + grid.addRow({Tr::tr("Maximum number of entries in \"Recent Files\":"), + Row{s.maxRecentFiles, st}}); + grid.addRow({s.askBeforeExit}); #ifdef ENABLE_CRASHPAD - form.addRow({Span(3, Row{m_enableCrashReportingCheckBox, helpCrashReportingButton, st})}); - form.addRow({Span(3, Row{m_clearCrashReportsButton, m_crashReportsSizeText, st})}); + grid.addRow({s.enableCrashReporting, + Row{m_clearCrashReportsButton, + m_crashReportsSizeText, + helpCrashReportingButton, st}}); #endif Column { Group { title(Tr::tr("System")), - Column { form, st } + Column { grid, st } } }.attachTo(this); - m_reloadBehavior->setCurrentIndex(EditorManager::reloadSetting()); if (HostOsInfo::isAnyUnixHost()) { const QVector<TerminalCommand> availableTerminals = TerminalCommand::availableTerminalEmulators(); @@ -206,34 +262,8 @@ public: m_externalFileBrowserEdit->setText(UnixUtils::fileBrowser(ICore::settings())); } - const QString patchToolTip = Tr::tr("Command used for reverting diff chunks."); - patchCommandLabel->setToolTip(patchToolTip); - m_patchChooser->setToolTip(patchToolTip); - m_patchChooser->setExpectedKind(PathChooser::ExistingCommand); - m_patchChooser->setHistoryCompleter(QLatin1String("General.PatchCommand.History")); - m_patchChooser->setFilePath(PatchTool::patchCommand()); - m_autoSaveCheckBox->setChecked(EditorManagerPrivate::autoSaveEnabled()); - m_autoSaveCheckBox->setToolTip(Tr::tr("Automatically creates temporary copies of " - "modified files. If %1 is restarted after " - "a crash or power failure, it asks whether to " - "recover the auto-saved content.") - .arg(Constants::IDE_DISPLAY_NAME)); - m_autoSaveRefactoringCheckBox->setChecked(EditorManager::autoSaveAfterRefactoring()); - m_autoSaveRefactoringCheckBox->setToolTip( - Tr::tr("Automatically saves all open files " - "affected by a refactoring operation,\nprovided they were unmodified before the " - "refactoring.")); - m_autoSaveInterval->setValue(EditorManagerPrivate::autoSaveInterval()); - m_autoSuspendCheckBox->setChecked(EditorManagerPrivate::autoSuspendEnabled()); - m_autoSuspendMinDocumentCount->setValue(EditorManagerPrivate::autoSuspendMinDocumentCount()); - m_warnBeforeOpeningBigFiles->setChecked( - EditorManagerPrivate::warnBeforeOpeningBigFilesEnabled()); - m_bigFilesLimitSpinBox->setValue(EditorManagerPrivate::bigFileSizeLimit()); - m_maxRecentFilesSpinBox->setMinimum(1); - m_maxRecentFilesSpinBox->setMaximum(99); - m_maxRecentFilesSpinBox->setValue(EditorManagerPrivate::maxRecentFiles()); #ifdef ENABLE_CRASHPAD - if (ICore::settings()->value(showCrashButtonKey).toBool()) { + if (s.showCrashButton()) { auto crashButton = new QPushButton("CRASH!!!"); crashButton->show(); connect(crashButton, &QPushButton::clicked, [] { @@ -242,12 +272,10 @@ public: }); } - m_enableCrashReportingCheckBox->setChecked( - ICore::settings()->value(crashReportingEnabledKey).toBool()); connect(helpCrashReportingButton, &QAbstractButton::clicked, this, [this] { showHelpDialog(Tr::tr("Crash Reporting"), CorePlugin::msgCrashpadInformation()); }); - connect(m_enableCrashReportingCheckBox, &QCheckBox::stateChanged, this, [this] { + connect(&s.enableCrashReporting, &BaseAspect::changed, this, [this] { const QString restartText = Tr::tr("The change will take effect after restart."); Core::RestartDialog restartDialog(Core::ICore::dialogParent(), restartText); restartDialog.exec(); @@ -264,10 +292,6 @@ public: }); #endif - m_askBeforeExitCheckBox->setChecked( - static_cast<Core::Internal::MainWindow *>(ICore::mainWindow()) - ->askConfirmationBeforeExit()); - if (HostOsInfo::isAnyUnixHost()) { connect(resetTerminalButton, &QAbstractButton::clicked, @@ -343,33 +367,25 @@ private: void showHelpDialog(const QString &title, const QString &helpText); QComboBox *m_fileSystemCaseSensitivityChooser; - QSpinBox *m_autoSuspendMinDocumentCount; QLineEdit *m_externalFileBrowserEdit; - QCheckBox *m_autoSuspendCheckBox; - QSpinBox *m_maxRecentFilesSpinBox; - QCheckBox *m_enableCrashReportingCheckBox; - QCheckBox *m_warnBeforeOpeningBigFiles; - QSpinBox *m_bigFilesLimitSpinBox; QComboBox *m_terminalComboBox; QLineEdit *m_terminalOpenArgs; QLineEdit *m_terminalExecuteArgs; - Utils::PathChooser *m_patchChooser; Utils::ElidingLabel *m_environmentChangesLabel; - QCheckBox *m_askBeforeExitCheckBox; - QComboBox *m_reloadBehavior; - QCheckBox *m_autoSaveCheckBox; +#ifdef ENABLE_CRASHPAD QPushButton *m_clearCrashReportsButton; QLabel *m_crashReportsSizeText; - QSpinBox *m_autoSaveInterval; - QCheckBox *m_autoSaveRefactoringCheckBox; +#endif QPointer<QMessageBox> m_dialog; EnvironmentItems m_environmentChanges; }; void SystemSettingsWidget::apply() { + systemSettings().apply(); + systemSettings().writeSettings(); + QtcSettings *settings = ICore::settings(); - EditorManager::setReloadSetting(IDocument::ReloadSetting(m_reloadBehavior->currentIndex())); if (HostOsInfo::isAnyUnixHost()) { TerminalCommand::setTerminalEmulator({ FilePath::fromUserInput(m_terminalComboBox->lineEdit()->text()), @@ -380,23 +396,6 @@ void SystemSettingsWidget::apply() UnixUtils::setFileBrowser(settings, m_externalFileBrowserEdit->text()); } } - PatchTool::setPatchCommand(m_patchChooser->filePath()); - EditorManagerPrivate::setAutoSaveEnabled(m_autoSaveCheckBox->isChecked()); - EditorManagerPrivate::setAutoSaveInterval(m_autoSaveInterval->value()); - EditorManagerPrivate::setAutoSaveAfterRefactoring(m_autoSaveRefactoringCheckBox->isChecked()); - EditorManagerPrivate::setAutoSuspendEnabled(m_autoSuspendCheckBox->isChecked()); - EditorManagerPrivate::setAutoSuspendMinDocumentCount(m_autoSuspendMinDocumentCount->value()); - EditorManagerPrivate::setWarnBeforeOpeningBigFilesEnabled( - m_warnBeforeOpeningBigFiles->isChecked()); - EditorManagerPrivate::setBigFileSizeLimit(m_bigFilesLimitSpinBox->value()); - EditorManagerPrivate::setMaxRecentFiles(m_maxRecentFilesSpinBox->value()); -#ifdef ENABLE_CRASHPAD - ICore::settings()->setValue(crashReportingEnabledKey, - m_enableCrashReportingCheckBox->isChecked()); -#endif - - static_cast<Core::Internal::MainWindow *>(ICore::mainWindow()) - ->setAskConfirmationBeforeExit(m_askBeforeExitCheckBox->isChecked()); if (HostOsInfo::isMacHost()) { const Qt::CaseSensitivity sensitivity = EditorManagerPrivate::readFileSystemSensitivity( @@ -438,7 +437,7 @@ void SystemSettingsWidget::updatePath() { Environment env; env.appendToPath(VcsManager::additionalToolsPath()); - m_patchChooser->setEnvironment(env); + systemSettings().patchCommand.setEnvironment(env); } void SystemSettingsWidget::updateEnvironmentChangesLabel() @@ -488,13 +487,20 @@ void SystemSettingsWidget::showHelpForFileBrowser() showHelpDialog(Tr::tr("Variables"), UnixUtils::fileBrowserHelpText()); } -SystemSettings::SystemSettings() -{ - setId(Constants::SETTINGS_ID_SYSTEM); - setDisplayName(Tr::tr("System")); - setCategory(Constants::SETTINGS_CATEGORY_CORE); - setWidgetCreator([] { return new SystemSettingsWidget; }); -} +// SystemSettingsPage -} // namespace Internal -} // namespace Core +class SystemSettingsPage final : public IOptionsPage +{ +public: + SystemSettingsPage() + { + setId(Constants::SETTINGS_ID_SYSTEM); + setDisplayName(Tr::tr("System")); + setCategory(Constants::SETTINGS_CATEGORY_CORE); + setWidgetCreator([] { return new SystemSettingsWidget; }); + } +}; + +const SystemSettingsPage settingsPage; + +} // Core::Internal diff --git a/src/plugins/coreplugin/systemsettings.h b/src/plugins/coreplugin/systemsettings.h index c28c47e990c..1b0415faf91 100644 --- a/src/plugins/coreplugin/systemsettings.h +++ b/src/plugins/coreplugin/systemsettings.h @@ -3,16 +3,40 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> -namespace Core { -namespace Internal { +namespace Core::Internal { -class SystemSettings final : public IOptionsPage +class SystemSettings final : public Utils::AspectContainer { public: SystemSettings(); + + Utils::FilePathAspect patchCommand{this}; + + Utils::BoolAspect autoSaveModifiedFiles{this}; + Utils::IntegerAspect autoSaveInterval{this}; + + Utils::BoolAspect autoSaveAfterRefactoring{this}; + + Utils::BoolAspect autoSuspendEnabled{this}; + Utils::IntegerAspect autoSuspendMinDocumentCount{this}; + + Utils::BoolAspect warnBeforeOpeningBigFiles{this}; + Utils::IntegerAspect bigFileSizeLimitInMB{this}; + + Utils::IntegerAspect maxRecentFiles{this}; + + Utils::SelectionAspect reloadSetting{this}; + +#ifdef ENABLE_CRASHPAD + Utils::BoolAspect enableCrashReporting{this}; + Utils::BoolAspect showCrashButton{this}; +#endif + + Utils::BoolAspect askBeforeExit{this}; }; -} // namespace Internal -} // namespace Core +SystemSettings &systemSettings(); + +} // Core::Internal diff --git a/src/plugins/terminal/terminalsearch.cpp b/src/plugins/coreplugin/terminal/searchableterminal.cpp similarity index 64% rename from src/plugins/terminal/terminalsearch.cpp rename to src/plugins/coreplugin/terminal/searchableterminal.cpp index 69a412b1948..5a98d3e57d4 100644 --- a/src/plugins/terminal/terminalsearch.cpp +++ b/src/plugins/coreplugin/terminal/searchableterminal.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "terminalsearch.h" +#include "searchableterminal.h" #include <QElapsedTimer> #include <QLoggingCategory> @@ -11,21 +11,24 @@ Q_LOGGING_CATEGORY(terminalSearchLog, "qtc.terminal.search", QtWarningMsg) +using namespace Utils; + using namespace std::chrono_literals; -namespace Terminal { - -using namespace Terminal::Internal; +namespace Core { constexpr std::chrono::milliseconds debounceInterval = 100ms; -TerminalSearch::TerminalSearch(TerminalSurface *surface) +TerminalSearch::TerminalSearch(TerminalSolution::TerminalSurface *surface) : m_surface(surface) { m_debounceTimer.setInterval(debounceInterval); m_debounceTimer.setSingleShot(true); - connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits); + connect(surface, + &TerminalSolution::TerminalSurface::invalidated, + this, + &TerminalSearch::updateHits); connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits); } @@ -34,7 +37,7 @@ void TerminalSearch::setCurrentSelection(std::optional<SearchHitWithText> select m_currentSelection = selection; } -void TerminalSearch::setSearchString(const QString &searchString, Core::FindFlags findFlags) +void TerminalSearch::setSearchString(const QString &searchString, FindFlags findFlags) { if (m_currentSearchString != searchString || m_findFlags != findFlags) { m_currentSearchString = searchString; @@ -83,13 +86,13 @@ bool isSpace(char32_t a, char32_t b) return false; } -QList<SearchHit> TerminalSearch::search() +QList<TerminalSolution::SearchHit> TerminalSearch::search() { - QList<SearchHit> hits; + QList<TerminalSolution::SearchHit> hits; std::function<bool(char32_t, char32_t)> compare; - if (m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively)) { + if (m_findFlags.testFlag(FindFlag::FindCaseSensitively)) { compare = [](char32_t a, char32_t b) { return a == b || isSpace(a, b); }; } else { compare = [](char32_t a, char32_t b) { @@ -101,19 +104,20 @@ QList<SearchHit> TerminalSearch::search() const QList<uint> asUcs4 = m_currentSearchString.toUcs4(); std::u32string searchString(asUcs4.begin(), asUcs4.end()); - if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) { + if (m_findFlags.testFlag(FindFlag::FindWholeWords)) { searchString.push_back(std::numeric_limits<char32_t>::max()); searchString.insert(searchString.begin(), std::numeric_limits<char32_t>::max()); } - Internal::CellIterator it = m_surface->begin(); + TerminalSolution::CellIterator it = m_surface->begin(); while (it != m_surface->end()) { it = std::search(it, m_surface->end(), searchString.begin(), searchString.end(), compare); if (it != m_surface->end()) { - auto hit = SearchHit{it.position(), - static_cast<int>(it.position() + searchString.size())}; - if (m_findFlags.testFlag(Core::FindFlag::FindWholeWords)) { + auto hit = TerminalSolution::SearchHit{it.position(), + static_cast<int>(it.position() + + searchString.size())}; + if (m_findFlags.testFlag(FindFlag::FindWholeWords)) { hit.start++; hit.end--; } @@ -125,9 +129,9 @@ QList<SearchHit> TerminalSearch::search() return hits; } -QList<SearchHit> TerminalSearch::searchRegex() +QList<TerminalSolution::SearchHit> TerminalSearch::searchRegex() { - QList<SearchHit> hits; + QList<TerminalSolution::SearchHit> hits; QString allText; allText.reserve(1000); @@ -143,7 +147,7 @@ QList<SearchHit> TerminalSearch::searchRegex() } QRegularExpression re(m_currentSearchString, - m_findFlags.testFlag(Core::FindFlag::FindCaseSensitively) + m_findFlags.testFlag(FindFlag::FindCaseSensitively) ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); @@ -168,7 +172,7 @@ QList<SearchHit> TerminalSearch::searchRegex() } e -= adjust; } - hits << SearchHit{s, e}; + hits << TerminalSolution::SearchHit{s, e}; } return hits; @@ -181,9 +185,9 @@ void TerminalSearch::debouncedUpdateHits() m_currentHit = -1; - const bool regex = m_findFlags.testFlag(Core::FindFlag::FindRegularExpression); + const bool regex = m_findFlags.testFlag(FindFlag::FindRegularExpression); - QList<SearchHit> hits = regex ? searchRegex() : search(); + QList<TerminalSolution::SearchHit> hits = regex ? searchRegex() : search(); if (hits != m_hits) { m_currentHit = -1; @@ -202,10 +206,10 @@ void TerminalSearch::debouncedUpdateHits() qCDebug(terminalSearchLog) << "Search took" << t.elapsed() << "ms"; } -Core::FindFlags TerminalSearch::supportedFindFlags() const +FindFlags TerminalSearch::supportedFindFlags() const { - return Core::FindFlag::FindCaseSensitively | Core::FindFlag::FindBackward - | Core::FindFlag::FindRegularExpression | Core::FindFlag::FindWholeWords; + return FindFlag::FindCaseSensitively | FindFlag::FindBackward | FindFlag::FindRegularExpression + | FindFlag::FindWholeWords; } void TerminalSearch::resetIncrementalSearch() @@ -231,8 +235,7 @@ QString TerminalSearch::completedFindString() const return {}; } -Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt, - Core::FindFlags findFlags) +Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt, FindFlags findFlags) { if (txt == m_currentSearchString) { if (m_debounceTimer.isActive()) @@ -247,7 +250,7 @@ Core::IFindSupport::Result TerminalSearch::findIncremental(const QString &txt, return Result::NotYetFound; } -Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::FindFlags findFlags) +Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, FindFlags findFlags) { if (txt == m_currentSearchString) { if (m_debounceTimer.isActive()) @@ -255,7 +258,7 @@ Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::Fi else if (m_hits.isEmpty()) return Result::NotFound; - if (findFlags.testFlag(Core::FindFlag::FindBackward)) + if (findFlags.testFlag(FindFlag::FindBackward)) previousHit(); else nextHit(); @@ -266,9 +269,62 @@ Core::IFindSupport::Result TerminalSearch::findStep(const QString &txt, Core::Fi return findIncremental(txt, findFlags); } -void TerminalSearch::highlightAll(const QString &txt, Core::FindFlags findFlags) +void TerminalSearch::highlightAll(const QString &txt, FindFlags findFlags) { setSearchString(txt, findFlags); } -} // namespace Terminal +SearchableTerminal::SearchableTerminal(QWidget *parent) + : TerminalSolution::TerminalView(parent) +{ + m_aggregate = new Aggregation::Aggregate(this); + m_aggregate->add(this); + + surfaceChanged(); +} + +SearchableTerminal::~SearchableTerminal() = default; + +void SearchableTerminal::surfaceChanged() +{ + TerminalView::surfaceChanged(); + + m_search = TerminalSearchPtr(new TerminalSearch(surface()), [this](TerminalSearch *p) { + m_aggregate->remove(p); + delete p; + }); + + m_aggregate->add(m_search.get()); + + connect(m_search.get(), &TerminalSearch::hitsChanged, this, &SearchableTerminal::updateViewport); + connect(m_search.get(), &TerminalSearch::currentHitChanged, this, [this] { + TerminalSolution::SearchHit hit = m_search->currentHit(); + if (hit.start >= 0) { + setSelection(Selection{hit.start, hit.end, true}, hit != m_lastSelectedHit); + m_lastSelectedHit = hit; + } + }); +} + +void SearchableTerminal::selectionChanged(const std::optional<Selection> &newSelection) +{ + TerminalView::selectionChanged(newSelection); + + if (selection() && selection()->final) { + QString text = textFromSelection(); + + if (m_search) { + m_search->setCurrentSelection( + SearchHitWithText{{newSelection->start, newSelection->end}, text}); + } + } +} + +const QList<TerminalSolution::SearchHit> &SearchableTerminal::searchHits() const +{ + if (!m_search) + return TerminalSolution::TerminalView::searchHits(); + return m_search->hits(); +} + +} // namespace Core diff --git a/src/plugins/coreplugin/terminal/searchableterminal.h b/src/plugins/coreplugin/terminal/searchableterminal.h new file mode 100644 index 00000000000..d1747fd99b2 --- /dev/null +++ b/src/plugins/coreplugin/terminal/searchableterminal.h @@ -0,0 +1,89 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../core_global.h" +#include "../find/ifindsupport.h" + +#include <aggregation/aggregate.h> + +#include <solutions/terminal/terminalview.h> + +namespace Core { + +struct SearchHitWithText : TerminalSolution::SearchHit +{ + QString text; +}; + +class TerminalSearch : public IFindSupport +{ + Q_OBJECT +public: + TerminalSearch(TerminalSolution::TerminalSurface *surface); + + void setCurrentSelection(std::optional<SearchHitWithText> selection); + void setSearchString(const QString &searchString, Utils::FindFlags findFlags); + void nextHit(); + void previousHit(); + + const QList<TerminalSolution::SearchHit> &hits() const { return m_hits; } + TerminalSolution::SearchHit currentHit() const + { + return m_currentHit >= 0 ? m_hits.at(m_currentHit) : TerminalSolution::SearchHit{}; + } + +public: + bool supportsReplace() const override { return false; } + Utils::FindFlags supportedFindFlags() const override; + void resetIncrementalSearch() override; + void clearHighlights() override; + QString currentFindString() const override; + QString completedFindString() const override; + Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Result findStep(const QString &txt, Utils::FindFlags findFlags) override; + + void highlightAll(const QString &, Utils::FindFlags) override; + +signals: + void hitsChanged(); + void currentHitChanged(); + +protected: + void updateHits(); + void debouncedUpdateHits(); + QList<TerminalSolution::SearchHit> search(); + QList<TerminalSolution::SearchHit> searchRegex(); + +private: + std::optional<SearchHitWithText> m_currentSelection; + QString m_currentSearchString; + Utils::FindFlags m_findFlags; + TerminalSolution::TerminalSurface *m_surface; + + int m_currentHit{-1}; + QList<TerminalSolution::SearchHit> m_hits; + QTimer m_debounceTimer; +}; + +class CORE_EXPORT SearchableTerminal : public TerminalSolution::TerminalView +{ +public: + SearchableTerminal(QWidget *parent = nullptr); + ~SearchableTerminal() override; + +protected: + void surfaceChanged() override; + const QList<TerminalSolution::SearchHit> &searchHits() const override; + void selectionChanged(const std::optional<Selection> &newSelection) override; + +private: + using TerminalSearchPtr = std::unique_ptr<TerminalSearch, std::function<void(TerminalSearch *)>>; + TerminalSearchPtr m_search; + TerminalSolution::SearchHit m_lastSelectedHit{}; + + Aggregation::Aggregate *m_aggregate{nullptr}; +}; + +} // namespace Core diff --git a/src/plugins/coreplugin/textdocument.cpp b/src/plugins/coreplugin/textdocument.cpp index ddbf05fb18b..cf4dd82939e 100644 --- a/src/plugins/coreplugin/textdocument.cpp +++ b/src/plugins/coreplugin/textdocument.cpp @@ -2,7 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "textdocument.h" -#include <coreplugin/editormanager/editormanager.h> + +#include "editormanager/editormanager.h" #include <QDebug> #include <QTextCodec> diff --git a/src/plugins/coreplugin/themechooser.h b/src/plugins/coreplugin/themechooser.h index 1188fcb7e0d..360be3100c0 100644 --- a/src/plugins/coreplugin/themechooser.h +++ b/src/plugins/coreplugin/themechooser.h @@ -3,9 +3,9 @@ #pragma once -#include <utils/id.h> +#include "dialogs/ioptionspage.h" -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/id.h> #include <QWidget> diff --git a/src/plugins/coreplugin/vcsmanager.cpp b/src/plugins/coreplugin/vcsmanager.cpp index 4b5e60b2fcc..4e04a687d80 100644 --- a/src/plugins/coreplugin/vcsmanager.cpp +++ b/src/plugins/coreplugin/vcsmanager.cpp @@ -18,8 +18,6 @@ #include <utils/infobar.h> #include <utils/qtcassert.h> -#include <vcsbase/vcsbaseconstants.h> - #include <QList> #include <QMap> #include <QMessageBox> @@ -335,8 +333,9 @@ FilePaths VcsManager::promptToDelete(IVersionControl *vc, const FilePaths &fileP return fp.toUserOutput(); }).join("</li><li>") + "</li></ul>"; const QString title = Tr::tr("Version Control"); - const QString msg = Tr::tr("Remove the following files from the version control system (%2)? " - "%1Note: This might remove the local file.").arg(fileListForUi, vc->displayName()); + const QString msg = Tr::tr("Remove the following files from the version control system (%1)?") + .arg(vc->displayName()) + + fileListForUi + Tr::tr("Note: This might remove the local file."); const QMessageBox::StandardButton button = QMessageBox::question(ICore::dialogParent(), title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (button != QMessageBox::Yes) diff --git a/src/plugins/coreplugin/vcsmanager.h b/src/plugins/coreplugin/vcsmanager.h index 0afaf05ff61..5bb5a2e9c7d 100644 --- a/src/plugins/coreplugin/vcsmanager.h +++ b/src/plugins/coreplugin/vcsmanager.h @@ -15,7 +15,7 @@ namespace Core { class IVersionControl; -namespace Internal { class MainWindow; } +namespace Internal { class ICorePrivate; } /* VcsManager: * 1) Provides functionality for finding the IVersionControl * for a given @@ -87,8 +87,8 @@ private: void handleConfigurationChanges(IVersionControl *vc); static void addVersionControl(IVersionControl *vc); - friend class Core::Internal::MainWindow; - friend class Core::IVersionControl; + friend class Internal::ICorePrivate; + friend class IVersionControl; }; } // namespace Core diff --git a/src/plugins/coreplugin/versiondialog.cpp b/src/plugins/coreplugin/versiondialog.cpp index 2846be849af..8e68172de2c 100644 --- a/src/plugins/coreplugin/versiondialog.cpp +++ b/src/plugins/coreplugin/versiondialog.cpp @@ -8,15 +8,15 @@ #include "coreicons.h" #include "icore.h" -#include <app/app_version.h> - #include <utils/algorithm.h> +#include <utils/appinfo.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> #include <QDialogButtonBox> #include <QGridLayout> +#include <QGuiApplication> #include <QKeyEvent> #include <QLabel> #include <QPushButton> @@ -32,19 +32,19 @@ VersionDialog::VersionDialog(QWidget *parent) if (Utils::HostOsInfo::isLinuxHost()) setWindowIcon(Icons::QTCREATORLOGO_BIG.icon()); - setWindowTitle(Tr::tr("About %1").arg(Core::Constants::IDE_DISPLAY_NAME)); + setWindowTitle(Tr::tr("About %1").arg(QGuiApplication::applicationDisplayName())); auto layout = new QGridLayout(this); layout->setSizeConstraint(QLayout::SetFixedSize); + const Utils::AppInfo appInfo = Utils::appInfo(); QString ideRev; -#ifdef IDE_REVISION - const QString revUrl = QString::fromLatin1(Constants::IDE_REVISION_URL); - const QString rev = QString::fromLatin1(Constants::IDE_REVISION_STR); - ideRev = Tr::tr("<br/>From revision %1<br/>") - .arg(revUrl.isEmpty() ? rev - : QString::fromLatin1("<a href=\"%1\">%2</a>").arg(revUrl, rev)); -#endif - QString buildDateInfo; + if (!appInfo.revision.isEmpty()) + ideRev = Tr::tr("<br/>From revision %1<br/>") + .arg(appInfo.revisionUrl.isEmpty() + ? appInfo.revision + : QString::fromLatin1("<a href=\"%1\">%2</a>") + .arg(appInfo.revisionUrl, appInfo.revision)); + QString buildDateInfo; #ifdef QTC_SHOW_BUILD_DATE buildDateInfo = Tr::tr("<br/>Built on %1 %2<br/>").arg(QLatin1String(__DATE__), QLatin1String(__TIME__)); #endif @@ -56,27 +56,27 @@ VersionDialog::VersionDialog(QWidget *parent) const QString description = Tr::tr("<h3>%1</h3>" - "%2<br/>" - "%3" - "%4" - "%5" - "<br/>" - "Copyright 2008-%6 %7. All rights reserved.<br/>" - "<br/>" - "The program is provided AS IS with NO WARRANTY OF ANY KIND, " - "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A " - "PARTICULAR PURPOSE.<br/>") + "%2<br/>" + "%3" + "%4" + "%5" + "<br/>" + "Copyright 2008-%6 %7. All rights reserved.<br/>" + "<br/>" + "The program is provided AS IS with NO WARRANTY OF ANY KIND, " + "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A " + "PARTICULAR PURPOSE.<br/>") .arg(ICore::versionString(), ICore::buildCompatibilityString(), buildDateInfo, ideRev, additionalInfo.isEmpty() ? QString() : br + additionalInfo + br, - QLatin1String(Constants::IDE_YEAR), - QLatin1String(Constants::IDE_AUTHOR)) + appInfo.year, + appInfo.author) + "<br/>" + Tr::tr("The Qt logo as well as Qt®, Qt Quick®, Built with Qt®, Boot to Qt®, " - "Qt Quick Compiler®, Qt Enterprise®, Qt Mobile® and Qt Embedded® are " - "registered trademarks of The Qt Company Ltd."); + "Qt Quick Compiler®, Qt Enterprise®, Qt Mobile® and Qt Embedded® are " + "registered trademarks of The Qt Company Ltd."); QLabel *copyRightLabel = new QLabel(description); copyRightLabel->setWordWrap(true); diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index d0b1c9f3f91..d80942f13d4 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -298,24 +298,6 @@ bool ListModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceP return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } -void ListModelFilter::delayedUpdateFilter() -{ - if (m_timerId != 0) - killTimer(m_timerId); - - m_timerId = startTimer(320); -} - -void ListModelFilter::timerEvent(QTimerEvent *timerEvent) -{ - if (m_timerId == timerEvent->timerId()) { - invalidateFilter(); - emit layoutChanged(); - killTimer(m_timerId); - m_timerId = 0; - } -} - struct SearchStringLexer { QString code; @@ -427,7 +409,8 @@ void ListModelFilter::setSearchString(const QString &arg) } } - delayedUpdateFilter(); + invalidateFilter(); + emit layoutChanged(); } ListModel *ListModelFilter::sourceListModel() const @@ -677,6 +660,13 @@ void ListItemDelegate::goon() SectionedGridView::SectionedGridView(QWidget *parent) : QStackedWidget(parent) { + m_searchTimer.setInterval(320); + m_searchTimer.setSingleShot(true); + connect(&m_searchTimer, &QTimer::timeout, this, [this] { + setSearchString(m_delayedSearchString); + m_delayedSearchString.clear(); + }); + m_allItemsModel.reset(new ListModel); m_allItemsModel->setPixmapFunction(m_pixmapFunction); @@ -718,6 +708,12 @@ void SectionedGridView::setPixmapFunction(const Core::ListModel::PixmapFunction model->setPixmapFunction(pixmapFunction); } +void SectionedGridView::setSearchStringDelayed(const QString &searchString) +{ + m_delayedSearchString = searchString; + m_searchTimer.start(); +} + void SectionedGridView::setSearchString(const QString &searchString) { if (searchString.isEmpty()) { @@ -733,6 +729,7 @@ void SectionedGridView::setSearchString(const QString &searchString) // We don't have a grid set for searching yet. // Create all items view for filtering. m_allItemsView.reset(new GridView); + m_allItemsView->setObjectName("AllItemsView"); // used by Squish m_allItemsView->setModel(new ListModelFilter(m_allItemsModel.get(), m_allItemsView.get())); if (m_itemDelegate) m_allItemsView->setItemDelegate(m_itemDelegate); diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h index 8599c227b90..1677e1e893a 100644 --- a/src/plugins/coreplugin/welcomepagehelper.h +++ b/src/plugins/coreplugin/welcomepagehelper.h @@ -12,6 +12,7 @@ #include <QSortFilterProxyModel> #include <QStackedWidget> #include <QStyledItemDelegate> +#include <QTimer> #include <functional> #include <optional> @@ -132,14 +133,10 @@ protected: private: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const final; - void timerEvent(QTimerEvent *event) final; - - void delayedUpdateFilter(); QString m_searchString; QStringList m_filterTags; QStringList m_filterStrings; - int m_timerId = 0; }; class CORE_EXPORT ListItemDelegate : public QStyledItemDelegate @@ -211,6 +208,7 @@ public: void setItemDelegate(QAbstractItemDelegate *delegate); void setPixmapFunction(const Core::ListModel::PixmapFunction &pixmapFunction); + void setSearchStringDelayed(const QString &searchString); void setSearchString(const QString &searchString); Core::ListModel *addSection(const Section §ion, const QList<Core::ListItem *> &items); @@ -228,6 +226,8 @@ private: QPointer<QWidget> m_zoomedInWidget; Core::ListModel::PixmapFunction m_pixmapFunction; QAbstractItemDelegate *m_itemDelegate = nullptr; + QTimer m_searchTimer; + QString m_delayedSearchString; }; } // namespace Core diff --git a/src/plugins/coreplugin/windowsupport.cpp b/src/plugins/coreplugin/windowsupport.cpp index fd80fe23181..4cbfb57d038 100644 --- a/src/plugins/coreplugin/windowsupport.cpp +++ b/src/plugins/coreplugin/windowsupport.cpp @@ -10,14 +10,13 @@ #include "coreplugintr.h" #include "icore.h" -#include <app/app_version.h> - #include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> #include <QAction> #include <QEvent> +#include <QGuiApplication> #include <QMenu> #include <QWidget> #include <QWindowStateChangeEvent> @@ -146,7 +145,7 @@ WindowList::~WindowList() void WindowList::addWindow(QWidget *window) { -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS if (!m_dockMenu) { m_dockMenu = new QMenu; m_dockMenu->setAsDockMenu(); @@ -188,7 +187,7 @@ void WindowList::updateTitle(QWidget *window) QTC_ASSERT(index >= 0, return); QTC_ASSERT(index < m_windowActions.size(), return); QString title = window->windowTitle(); - if (title.endsWith(QStringLiteral("- ") + Constants::IDE_DISPLAY_NAME)) + if (title.endsWith(QStringLiteral("- ") + QGuiApplication::applicationDisplayName())) title.chop(12); m_windowActions.at(index)->setText(Utils::quoteAmpersands(title.trimmed())); } diff --git a/src/plugins/cpaster/CodePaster.json.in b/src/plugins/cpaster/CodePaster.json.in index 1e569634c4d..af6025becbb 100644 --- a/src/plugins/cpaster/CodePaster.json.in +++ b/src/plugins/cpaster/CodePaster.json.in @@ -1,18 +1,18 @@ { - \"Name\" : \"CodePaster\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CodePaster", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Codepaster plugin for pushing/fetching diff from server.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Codepaster plugin for pushing/fetching diff from server.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/cpaster/cpasterplugin.cpp b/src/plugins/cpaster/cpasterplugin.cpp index a18b454c860..4d829589e15 100644 --- a/src/plugins/cpaster/cpasterplugin.cpp +++ b/src/plugins/cpaster/cpasterplugin.cpp @@ -60,7 +60,6 @@ public: void fetchUrl(); - Settings m_settings; QAction *m_postEditorAction = nullptr; QAction *m_fetchAction = nullptr; QAction *m_fetchUrlAction = nullptr; @@ -124,15 +123,15 @@ CodePasterPluginPrivate::CodePasterPluginPrivate() // Connect protocols if (!m_protocols.isEmpty()) { for (Protocol *proto : m_protocols) { - m_settings.protocols.addOption(proto->name()); + settings().protocols.addOption(proto->name()); connect(proto, &Protocol::pasteDone, this, &CodePasterPluginPrivate::finishPost); connect(proto, &Protocol::fetchDone, this, &CodePasterPluginPrivate::finishFetch); } - m_settings.protocols.setDefaultValue(m_protocols.at(0)->name()); + settings().protocols.setDefaultValue(m_protocols.at(0)->name()); } // Create the settings Page - m_settings.readSettings(ICore::settings()); + settings().readSettings(); connect(&m_urlOpen, &Protocol::fetchDone, this, &CodePasterPluginPrivate::finishFetch); @@ -238,20 +237,20 @@ void CodePasterPluginPrivate::post(QString data, const QString &mimeType) { fixSpecialCharacters(data); - const QString username = m_settings.username.value(); + const QString username = settings().username(); PasteView view(m_protocols, mimeType, ICore::dialogParent()); - view.setProtocol(m_settings.protocols.stringValue()); + view.setProtocol(settings().protocols.stringValue()); const FileDataList diffChunks = splitDiffToFiles(data); const int dialogResult = diffChunks.isEmpty() ? - view.show(username, {}, {}, m_settings.expiryDays(), data) : - view.show(username, {}, {}, m_settings.expiryDays(), diffChunks); + view.show(username, {}, {}, settings().expiryDays(), data) : + view.show(username, {}, {}, settings().expiryDays(), diffChunks); // Save new protocol in case user changed it. - if (dialogResult == QDialog::Accepted && m_settings.protocols.value() != view.protocol()) { - m_settings.protocols.setValue(view.protocol()); - m_settings.writeSettings(ICore::settings()); + if (dialogResult == QDialog::Accepted && settings().protocols() != view.protocol()) { + settings().protocols.setValue(view.protocol()); + settings().writeSettings(); } } @@ -275,14 +274,14 @@ void CodePasterPluginPrivate::pasteSnippet() void CodePasterPluginPrivate::fetch() { PasteSelectDialog dialog(m_protocols, ICore::dialogParent()); - dialog.setProtocol(m_settings.protocols.stringValue()); + dialog.setProtocol(settings().protocols.stringValue()); if (dialog.exec() != QDialog::Accepted) return; // Save new protocol in case user changed it. - if (m_settings.protocols.value() != dialog.protocol()) { - m_settings.protocols.setValue(dialog.protocol()); - m_settings.writeSettings(ICore::settings()); + if (settings().protocols() != dialog.protocol()) { + settings().protocols.setValue(dialog.protocol()); + settings().writeSettings(); } const QString pasteID = dialog.pasteId(); @@ -295,10 +294,10 @@ void CodePasterPluginPrivate::fetch() void CodePasterPluginPrivate::finishPost(const QString &link) { - if (m_settings.copyToClipboard.value()) + if (settings().copyToClipboard()) Utils::setClipboardAndSelection(link); - if (m_settings.displayOutput.value()) + if (settings().displayOutput()) MessageManager::writeDisrupting(link); else MessageManager::writeFlashing(link); diff --git a/src/plugins/cpaster/fileshareprotocol.cpp b/src/plugins/cpaster/fileshareprotocol.cpp index e9b85ee1af1..853805d0c99 100644 --- a/src/plugins/cpaster/fileshareprotocol.cpp +++ b/src/plugins/cpaster/fileshareprotocol.cpp @@ -34,7 +34,7 @@ FileShareProtocol::~FileShareProtocol() = default; QString FileShareProtocol::name() const { - return m_settings.displayName(); + return fileShareSettingsPage().displayName(); } unsigned FileShareProtocol::capabilities() const @@ -49,7 +49,7 @@ bool FileShareProtocol::hasSettings() const const Core::IOptionsPage *FileShareProtocol::settingsPage() const { - return &m_settings; + return &fileShareSettingsPage(); } static bool parse(const QString &fileName, @@ -100,7 +100,7 @@ static bool parse(const QString &fileName, bool FileShareProtocol::checkConfiguration(QString *errorMessage) { - if (m_settings.path.value().isEmpty()) { + if (fileShareSettings().path().isEmpty()) { if (errorMessage) *errorMessage = Tr::tr("Please configure a path."); return false; @@ -113,7 +113,7 @@ void FileShareProtocol::fetch(const QString &id) // Absolute or relative path name. QFileInfo fi(id); if (fi.isRelative()) - fi = QFileInfo(m_settings.path.value() + '/' + id); + fi = fileShareSettings().path().pathAppended(id).toFileInfo(); QString errorMessage; QString text; if (parse(fi.absoluteFilePath(), &errorMessage, nullptr, nullptr, &text)) @@ -125,7 +125,7 @@ void FileShareProtocol::fetch(const QString &id) void FileShareProtocol::list() { // Read out directory, display by date (latest first) - QDir dir(m_settings.path.value(), tempGlobPatternC, + QDir dir(fileShareSettings().path().toFSPathString(), tempGlobPatternC, QDir::Time, QDir::Files|QDir::NoDotAndDotDot|QDir::Readable); QStringList entries; QString user; @@ -133,7 +133,7 @@ void FileShareProtocol::list() QString errorMessage; const QChar blank = QLatin1Char(' '); const QFileInfoList entryInfoList = dir.entryInfoList(); - const int count = qMin(int(m_settings.displayCount()), entryInfoList.size()); + const int count = qMin(int(fileShareSettings().displayCount()), entryInfoList.size()); for (int i = 0; i < count; i++) { const QFileInfo& entryFi = entryInfoList.at(i); if (parse(entryFi.absoluteFilePath(), &errorMessage, &user, &description)) { @@ -160,7 +160,7 @@ void FileShareProtocol::paste( ) { // Write out temp XML file - Utils::TempFileSaver saver(m_settings.path.value() + '/' + tempPatternC); + Utils::TempFileSaver saver(fileShareSettings().path().pathAppended(tempPatternC).toFSPathString()); saver.setAutoRemove(false); if (!saver.hasError()) { // Flat text sections embedded into pasterElement diff --git a/src/plugins/cpaster/fileshareprotocol.h b/src/plugins/cpaster/fileshareprotocol.h index db03bb11bf6..71d98d078f8 100644 --- a/src/plugins/cpaster/fileshareprotocol.h +++ b/src/plugins/cpaster/fileshareprotocol.h @@ -4,7 +4,6 @@ #pragma once #include "protocol.h" -#include "fileshareprotocolsettingspage.h" namespace CodePaster { @@ -30,9 +29,6 @@ public: const QString &username = QString(), const QString &comment = QString(), const QString &description = QString()) override; - -private: - FileShareProtocolSettings m_settings; }; } // CodePaster diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp index 99599a3cd80..ad60a2ad840 100644 --- a/src/plugins/cpaster/fileshareprotocolsettingspage.cpp +++ b/src/plugins/cpaster/fileshareprotocolsettingspage.cpp @@ -13,11 +13,15 @@ using namespace Utils; namespace CodePaster { +FileShareProtocolSettings &fileShareSettings() +{ + static FileShareProtocolSettings theSettings; + return theSettings; +} + FileShareProtocolSettings::FileShareProtocolSettings() { - setId("X.CodePaster.FileSharePaster"); - setDisplayName(Tr::tr("Fileshare")); - setCategory(Constants::CPASTER_SETTINGS_CATEGORY); + setAutoApply(false); setSettingsGroup("FileSharePasterSettings"); path.setSettingsKey("Path"); @@ -51,4 +55,22 @@ FileShareProtocolSettings::FileShareProtocolSettings() readSettings(); } +class FileShareProtocolSettingsPage final : public Core::IOptionsPage +{ +public: + FileShareProtocolSettingsPage() + { + setId("X.CodePaster.FileSharePaster"); + setDisplayName(Tr::tr("Fileshare")); + setCategory(Constants::CPASTER_SETTINGS_CATEGORY); + setSettingsProvider([] { return &fileShareSettings(); }); + } +}; + +Core::IOptionsPage &fileShareSettingsPage() +{ + static FileShareProtocolSettingsPage theSettings; + return theSettings; +} + } // namespace CodePaster diff --git a/src/plugins/cpaster/fileshareprotocolsettingspage.h b/src/plugins/cpaster/fileshareprotocolsettingspage.h index 8775fe16096..564aa7f8251 100644 --- a/src/plugins/cpaster/fileshareprotocolsettingspage.h +++ b/src/plugins/cpaster/fileshareprotocolsettingspage.h @@ -7,7 +7,7 @@ namespace CodePaster { -class FileShareProtocolSettings : public Core::PagedSettings +class FileShareProtocolSettings final : public Utils::AspectContainer { public: FileShareProtocolSettings(); @@ -16,4 +16,8 @@ public: Utils::IntegerAspect displayCount{this}; }; +FileShareProtocolSettings &fileShareSettings(); + +Core::IOptionsPage &fileShareSettingsPage(); + } // CodePaster diff --git a/src/plugins/cpaster/pasteview.cpp b/src/plugins/cpaster/pasteview.cpp index b1f4864b6e0..6602efe6ba1 100644 --- a/src/plugins/cpaster/pasteview.cpp +++ b/src/plugins/cpaster/pasteview.cpp @@ -11,6 +11,7 @@ #include <utils/layoutbuilder.h> #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> #include <QApplication> #include <QComboBox> @@ -27,6 +28,8 @@ #include <QStackedWidget> #include <QTextEdit> +using namespace Utils; + namespace CodePaster { const char groupC[] = "CPaster"; @@ -197,11 +200,11 @@ int PasteView::showDialog() m_uiDescription->selectAll(); // (Re)store dialog size - const QSettings *settings = Core::ICore::settings(); - const QString rootKey = QLatin1String(groupC) + QLatin1Char('/'); - const int h = settings->value(rootKey + QLatin1String(heightKeyC), height()).toInt(); + const QtcSettings *settings = Core::ICore::settings(); + const Key rootKey = Key(groupC) + '/'; + const int h = settings->value(rootKey + heightKeyC, height()).toInt(); const int defaultWidth = m_uiPatchView->columnIndicator() + 50; - const int w = settings->value(rootKey + QLatin1String(widthKeyC), defaultWidth).toInt(); + const int w = settings->value(rootKey + widthKeyC, defaultWidth).toInt(); resize(w, h); @@ -273,10 +276,10 @@ void PasteView::accept() const Protocol::ContentType ct = Protocol::contentType(m_mimeType); protocol->paste(data, ct, expiryDays(), user(), comment(), description()); // Store settings and close - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String(groupC)); - settings->setValue(QLatin1String(heightKeyC), height()); - settings->setValue(QLatin1String(widthKeyC), width()); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(groupC); + settings->setValue(heightKeyC, height()); + settings->setValue(widthKeyC, width()); settings->endGroup(); QDialog::accept(); } diff --git a/src/plugins/cpaster/settings.cpp b/src/plugins/cpaster/settings.cpp index 1c3d9e5e4db..597b3080f9b 100644 --- a/src/plugins/cpaster/settings.cpp +++ b/src/plugins/cpaster/settings.cpp @@ -6,21 +6,24 @@ #include "cpasterconstants.h" #include "cpastertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/layoutbuilder.h> using namespace Utils; namespace CodePaster { +Settings &settings() +{ + static Settings theSettings; + return theSettings; +} + Settings::Settings() { setSettingsGroup("CodePaster"); setAutoApply(false); - setId("A.CodePaster.General"); - setDisplayName(Tr::tr("General")); - setCategory(Constants::CPASTER_SETTINGS_CATEGORY); - setDisplayCategory(Tr::tr("Code Pasting")); - setCategoryIconPath(":/cpaster/images/settingscategory_cpaster.png"); username.setDisplayStyle(StringAspect::LineEditDisplay); username.setSettingsKey("UserName"); @@ -64,4 +67,20 @@ Settings::Settings() }); } +class CPasterSettingsPage final : public Core::IOptionsPage +{ +public: + CPasterSettingsPage() + { + setId("A.CodePaster.General"); + setDisplayName(Tr::tr("General")); + setCategory(Constants::CPASTER_SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("Code Pasting")); + setCategoryIconPath(":/cpaster/images/settingscategory_cpaster.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const CPasterSettingsPage settingsPage; + } // CodePaster diff --git a/src/plugins/cpaster/settings.h b/src/plugins/cpaster/settings.h index 1e7d03be761..f17df36caef 100644 --- a/src/plugins/cpaster/settings.h +++ b/src/plugins/cpaster/settings.h @@ -3,11 +3,11 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace CodePaster { -class Settings : public Core::PagedSettings +class Settings final : public Utils::AspectContainer { public: Settings(); @@ -19,4 +19,6 @@ public: Utils::BoolAspect displayOutput{this}; }; +Settings &settings(); + } // CodePaster diff --git a/src/plugins/cppcheck/CMakeLists.txt b/src/plugins/cppcheck/CMakeLists.txt index 6cd06dc9c67..fb1aea634cc 100644 --- a/src/plugins/cppcheck/CMakeLists.txt +++ b/src/plugins/cppcheck/CMakeLists.txt @@ -8,9 +8,9 @@ add_qtc_plugin(Cppcheck cppcheckdiagnosticsmodel.cpp cppcheckdiagnosticsmodel.h cppcheckdiagnosticview.cpp cppcheckdiagnosticview.h cppcheckmanualrundialog.cpp cppcheckmanualrundialog.h - cppcheckoptions.cpp cppcheckoptions.h cppcheckplugin.cpp cppcheckplugin.h cppcheckrunner.cpp cppcheckrunner.h + cppchecksettings.cpp cppchecksettings.h cppchecktextmark.cpp cppchecktextmark.h cppchecktextmarkmanager.cpp cppchecktextmarkmanager.h cppchecktool.cpp cppchecktool.h diff --git a/src/plugins/cppcheck/Cppcheck.json.in b/src/plugins/cppcheck/Cppcheck.json.in index ef2b1f48ca9..f2206e0fcae 100644 --- a/src/plugins/cppcheck/Cppcheck.json.in +++ b/src/plugins/cppcheck/Cppcheck.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"Cppcheck\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"Sergey Morozov\", - \"Copyright\" : \"(C) 2018 Sergey Morozov, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial 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.\" + "Name" : "Cppcheck", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "Sergey Morozov", + "Copyright" : "(C) 2018 Sergey Morozov, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"Cppcheck static analyzer tool integration. See http://cppcheck.sourceforge.net.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "Cppcheck static analyzer tool integration. See http://cppcheck.sourceforge.net.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/cppcheck/cppcheck.qbs b/src/plugins/cppcheck/cppcheck.qbs index e7b5f185baa..f1a8026fedb 100644 --- a/src/plugins/cppcheck/cppcheck.qbs +++ b/src/plugins/cppcheck/cppcheck.qbs @@ -23,12 +23,12 @@ QtcPlugin { "cppcheckdiagnosticview.h", "cppcheckmanualrundialog.cpp", "cppcheckmanualrundialog.h", - "cppcheckoptions.cpp", - "cppcheckoptions.h", "cppcheckplugin.cpp", "cppcheckplugin.h", "cppcheckrunner.cpp", "cppcheckrunner.h", + "cppchecksettings.cpp", + "cppchecksettings.h", "cppchecktextmark.cpp", "cppchecktextmark.h", "cppchecktextmarkmanager.cpp", diff --git a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp index a2f87b8cd8b..e2bc8434fb6 100644 --- a/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp +++ b/src/plugins/cppcheck/cppcheckdiagnosticsmodel.cpp @@ -7,6 +7,7 @@ #include <debugger/analyzer/diagnosticlocation.h> +#include <utils/algorithm.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/utilsicons.h> @@ -99,12 +100,10 @@ void DiagnosticsModel::clear() void DiagnosticsModel::add(const Diagnostic &diagnostic) { - if (m_diagnostics.contains(diagnostic)) + if (!Utils::insert(m_diagnostics, diagnostic)) return; - const auto hasData = !m_diagnostics.isEmpty(); - m_diagnostics.insert(diagnostic); - if (!hasData) + if (m_diagnostics.size() == 1) emit hasDataChanged(true); const QString filePath = diagnostic.fileName.toString(); diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp index 060f888cc25..35a057ccffd 100644 --- a/src/plugins/cppcheck/cppcheckmanualrundialog.cpp +++ b/src/plugins/cppcheck/cppcheckmanualrundialog.cpp @@ -3,13 +3,14 @@ #include "cppcheckmanualrundialog.h" -#include "cppcheckoptions.h" +#include "cppchecksettings.h" #include "cppchecktr.h" #include <projectexplorer/selectablefilesmodel.h> #include <cppeditor/projectinfo.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> #include <QBoxLayout> @@ -18,8 +19,7 @@ namespace Cppcheck::Internal { -ManualRunDialog::ManualRunDialog(QWidget *optionsWidget, - const ProjectExplorer::Project *project) +ManualRunDialog::ManualRunDialog(const ProjectExplorer::Project *project) : m_model(new ProjectExplorer::SelectableFilesFromDirModel(this)) { QTC_ASSERT(project, return ); @@ -52,6 +52,8 @@ ManualRunDialog::ManualRunDialog(QWidget *optionsWidget, analyzeButton->setEnabled(m_model->hasCheckedFiles()); }); + auto optionsWidget = settings().layouter()().emerge(); + auto layout = new QVBoxLayout(this); layout->addWidget(optionsWidget); layout->addWidget(view); diff --git a/src/plugins/cppcheck/cppcheckmanualrundialog.h b/src/plugins/cppcheck/cppcheckmanualrundialog.h index 460a85e75c4..76206578e34 100644 --- a/src/plugins/cppcheck/cppcheckmanualrundialog.h +++ b/src/plugins/cppcheck/cppcheckmanualrundialog.h @@ -20,7 +20,7 @@ namespace Cppcheck::Internal { class ManualRunDialog : public QDialog { public: - ManualRunDialog(QWidget *optionsWidget, const ProjectExplorer::Project *project); + explicit ManualRunDialog(const ProjectExplorer::Project *project); Utils::FilePaths filePaths() const; QSize sizeHint() const override; diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp index b025762c8c6..44381c235ee 100644 --- a/src/plugins/cppcheck/cppcheckplugin.cpp +++ b/src/plugins/cppcheck/cppcheckplugin.cpp @@ -5,6 +5,7 @@ #include "cppcheckconstants.h" #include "cppcheckdiagnosticview.h" +#include "cppchecksettings.h" #include "cppchecktextmarkmanager.h" #include "cppchecktool.h" #include "cppchecktr.h" @@ -12,7 +13,7 @@ #include "cppcheckdiagnosticsmodel.h" #include "cppcheckmanualrundialog.h" -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> @@ -39,11 +40,10 @@ public: explicit CppcheckPluginPrivate(); CppcheckTextMarkManager marks; - CppcheckOptions options; - CppcheckTool tool{options, marks, Constants::CHECK_PROGRESS_ID}; + CppcheckTool tool{marks, Constants::CHECK_PROGRESS_ID}; CppcheckTrigger trigger{marks, tool}; DiagnosticsModel manualRunModel; - CppcheckTool manualRunTool{options, manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID}; + CppcheckTool manualRunTool{manualRunModel, Constants::MANUAL_CHECK_PROGRESS_ID}; Utils::Perspective perspective{Constants::PERSPECTIVE_ID, ::Cppcheck::Tr::tr("Cppcheck")}; QAction *manualRunAction; @@ -55,7 +55,7 @@ public: CppcheckPluginPrivate::CppcheckPluginPrivate() { tool.updateOptions(); - connect(&options, &AspectContainer::changed, [this] { + connect(&settings(), &AspectContainer::changed, this, [this] { tool.updateOptions(); trigger.recheck(); }); @@ -112,9 +112,7 @@ void CppcheckPluginPrivate::startManualRun() manualRunTool.updateOptions(); - auto optionsWidget = options.layouter()().emerge(); - - ManualRunDialog dialog(optionsWidget, project); + ManualRunDialog dialog(project); if (dialog.exec() == ManualRunDialog::Rejected) return; diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppchecksettings.cpp similarity index 81% rename from src/plugins/cppcheck/cppcheckoptions.cpp rename to src/plugins/cppcheck/cppchecksettings.cpp index 1289ba0e0e2..142d3ac236f 100644 --- a/src/plugins/cppcheck/cppcheckoptions.cpp +++ b/src/plugins/cppcheck/cppchecksettings.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2018 Sergey Morozov // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "cppcheckoptions.h" +#include "cppchecksettings.h" #include "cppcheckconstants.h" #include "cppchecktool.h" @@ -16,6 +16,7 @@ #include <utils/qtcassert.h> #include <utils/variablechooser.h> +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <debugger/analyzer/analyzericons.h> @@ -25,14 +26,16 @@ using namespace Utils; namespace Cppcheck::Internal { -CppcheckOptions::CppcheckOptions() +CppcheckSettings &settings() +{ + static CppcheckSettings theSettings; + return theSettings; +} + +CppcheckSettings::CppcheckSettings() { - setId(Constants::OPTIONS_PAGE_ID); - setDisplayName(Tr::tr("Cppcheck")); - setCategory("T.Analyzer"); - setDisplayCategory(::Debugger::Tr::tr("Analyzer")); - setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); setSettingsGroup("Cppcheck"); + setAutoApply(false); binary.setSettingsKey("binary"); binary.setExpectedKind(PathChooser::ExistingCommand); @@ -41,10 +44,10 @@ CppcheckOptions::CppcheckOptions() if (HostOsInfo::isAnyUnixHost()) { binary.setDefaultValue("cppcheck"); } else { - FilePath programFiles = FilePath::fromUserInput(qtcEnvironmentVariable("PROGRAMFILES")); + QString programFiles = qtcEnvironmentVariable("PROGRAMFILES"); if (programFiles.isEmpty()) programFiles = "C:/Program Files"; - binary.setDefaultValue(programFiles.pathAppended("Cppcheck/cppcheck.exe").toString()); + binary.setDefaultValue(programFiles + "/Cppcheck/cppcheck.exe"); } warning.setSettingsKey("warning"); @@ -108,7 +111,7 @@ CppcheckOptions::CppcheckOptions() readSettings(); } -std::function<Layouting::LayoutItem()> CppcheckOptions::layouter() +std::function<Layouting::LayoutItem()> CppcheckSettings::layouter() { return [this] { using namespace Layouting; @@ -136,4 +139,20 @@ std::function<Layouting::LayoutItem()> CppcheckOptions::layouter() }; } +class CppCheckSettingsPage final : public Core::IOptionsPage +{ +public: + CppCheckSettingsPage() + { + setId(Constants::OPTIONS_PAGE_ID); + setDisplayName(Tr::tr("Cppcheck")); + setCategory("T.Analyzer"); + setDisplayCategory(::Debugger::Tr::tr("Analyzer")); + setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); + setSettingsProvider([] { return &settings(); }); + } +}; + +const CppCheckSettingsPage settingsPage; + } // Cppcheck::Internal diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppchecksettings.h similarity index 84% rename from src/plugins/cppcheck/cppcheckoptions.h rename to src/plugins/cppcheck/cppchecksettings.h index eb3e1c7ddbf..8842b2b65f2 100644 --- a/src/plugins/cppcheck/cppcheckoptions.h +++ b/src/plugins/cppcheck/cppchecksettings.h @@ -3,14 +3,16 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> + +#include <functional> namespace Cppcheck::Internal { -class CppcheckOptions final : public Core::PagedSettings +class CppcheckSettings final : public Utils::AspectContainer { public: - CppcheckOptions(); + CppcheckSettings(); std::function<Layouting::LayoutItem()> layouter(); @@ -32,4 +34,7 @@ public: Utils::BoolAspect guessArguments{this}; }; +CppcheckSettings &settings(); + + } // Cppcheck::Internal diff --git a/src/plugins/cppcheck/cppchecktextmark.cpp b/src/plugins/cppcheck/cppchecktextmark.cpp index 774d09bde76..f4e7802cee2 100644 --- a/src/plugins/cppcheck/cppchecktextmark.cpp +++ b/src/plugins/cppcheck/cppchecktextmark.cpp @@ -65,7 +65,7 @@ CppcheckTextMark::CppcheckTextMark(const Diagnostic &diagnostic) setActionsProvider([diagnostic] { // Copy to clipboard action QAction *action = new QAction; - action->setIcon(QIcon::fromTheme("edit-copy", Icons::COPY.icon())); + action->setIcon(Icon::fromTheme("edit-copy")); action->setToolTip(TextEditor::Tr::tr("Copy to Clipboard")); QObject::connect(action, &QAction::triggered, [diagnostic]() { const QString text = QString("%1:%2: %3") diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp index cd824b3db47..5bfcf941a3f 100644 --- a/src/plugins/cppcheck/cppchecktool.cpp +++ b/src/plugins/cppcheck/cppchecktool.cpp @@ -4,8 +4,8 @@ #include "cppchecktool.h" #include "cppcheckdiagnostic.h" -#include "cppcheckoptions.h" #include "cppcheckrunner.h" +#include "cppchecksettings.h" #include "cppchecktextmarkmanager.h" #include "cppchecktr.h" @@ -26,8 +26,7 @@ using namespace Utils; namespace Cppcheck::Internal { -CppcheckTool::CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Id &progressId) : - m_options(options), +CppcheckTool::CppcheckTool(CppcheckDiagnosticManager &manager, const Id &progressId) : m_manager(manager), m_progressRegexp("^.* checked (\\d+)% done$"), m_messageRegexp("^(.+),(\\d+),(\\w+),(\\w+),(.*)$"), @@ -43,7 +42,7 @@ CppcheckTool::~CppcheckTool() = default; void CppcheckTool::updateOptions() { m_filters.clear(); - for (const QString &pattern : m_options.ignoredPatterns().split(',')) { + for (const QString &pattern : settings().ignoredPatterns().split(',')) { const QString trimmedPattern = pattern.trimmed(); if (trimmedPattern.isEmpty()) continue; @@ -69,45 +68,47 @@ void CppcheckTool::updateArguments() m_cachedAdditionalArguments.clear(); + CppcheckSettings &s = settings(); + QStringList arguments; - if (!m_options.customArguments().isEmpty()) { + if (!s.customArguments().isEmpty()) { Utils::MacroExpander *expander = Utils::globalMacroExpander(); - const QString expanded = expander->expand(m_options.customArguments()); + const QString expanded = expander->expand(s.customArguments()); arguments.push_back(expanded); } - if (m_options.warning()) + if (s.warning()) arguments.push_back("--enable=warning"); - if (m_options.style()) + if (s.style()) arguments.push_back("--enable=style"); - if (m_options.performance()) + if (s.performance()) arguments.push_back("--enable=performance"); - if (m_options.portability()) + if (s.portability()) arguments.push_back("--enable=portability"); - if (m_options.information()) + if (s.information()) arguments.push_back("--enable=information"); - if (m_options.unusedFunction()) + if (s.unusedFunction()) arguments.push_back("--enable=unusedFunction"); - if (m_options.missingInclude()) + if (s.missingInclude()) arguments.push_back("--enable=missingInclude"); - if (m_options.inconclusive()) + if (s.inconclusive()) arguments.push_back("--inconclusive"); - if (m_options.forceDefines()) + if (s.forceDefines()) arguments.push_back("--force"); - if (!m_options.unusedFunction() && !m_options.customArguments().contains("-j ")) + if (!s.unusedFunction() && !s.customArguments().contains("-j ")) arguments.push_back("-j " + QString::number(QThread::idealThreadCount())); arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\""); - m_runner->reconfigure(m_options.binary(), arguments.join(' ')); + m_runner->reconfigure(s.binary(), arguments.join(' ')); } QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const { QStringList result; - if (m_options.addIncludePaths()) { + if (settings().addIncludePaths()) { for (const ProjectExplorer::HeaderPath &path : part.headerPaths) { const QString projectDir = m_project->projectDirectory().toString(); if (path.type == ProjectExplorer::HeaderPathType::User @@ -116,7 +117,7 @@ QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part } } - if (!m_options.guessArguments()) + if (!settings().guessArguments()) return result; using Version = Utils::LanguageVersion; @@ -180,7 +181,7 @@ void CppcheckTool::check(const Utils::FilePaths &files) return; const CppEditor::ProjectInfo::ConstPtr info - = CppEditor::CppModelManager::instance()->projectInfo(m_project); + = CppEditor::CppModelManager::projectInfo(m_project); if (!info) return; const QVector<CppEditor::ProjectPart::ConstPtr> parts = info->projectParts(); @@ -221,7 +222,7 @@ void CppcheckTool::stop(const Utils::FilePaths &files) void CppcheckTool::startParsing() { - if (m_options.showOutput()) { + if (settings().showOutput()) { const QString message = Tr::tr("Cppcheck started: \"%1\".").arg(m_runner->currentCommand()); Core::MessageManager::writeSilently(message); } @@ -240,7 +241,7 @@ void CppcheckTool::parseOutputLine(const QString &line) if (line.isEmpty()) return; - if (m_options.showOutput()) + if (settings().showOutput()) Core::MessageManager::writeSilently(line); enum Matches { Percentage = 1 }; @@ -271,7 +272,7 @@ void CppcheckTool::parseErrorLine(const QString &line) if (line.isEmpty()) return; - if (m_options.showOutput()) + if (settings().showOutput()) Core::MessageManager::writeSilently(line); enum Matches { File = 1, Line, Severity, Id, Message }; @@ -296,7 +297,7 @@ void CppcheckTool::parseErrorLine(const QString &line) void CppcheckTool::finishParsing() { - if (m_options.showOutput()) + if (settings().showOutput()) Core::MessageManager::writeSilently(Tr::tr("Cppcheck finished.")); QTC_ASSERT(m_progress, return); diff --git a/src/plugins/cppcheck/cppchecktool.h b/src/plugins/cppcheck/cppchecktool.h index 4dc6699e336..d14485fd5e8 100644 --- a/src/plugins/cppcheck/cppchecktool.h +++ b/src/plugins/cppcheck/cppchecktool.h @@ -3,7 +3,7 @@ #pragma once -#include <cppcheck/cppcheckoptions.h> +#include <utils/id.h> #include <QFutureInterface> #include <QPointer> @@ -24,14 +24,13 @@ namespace Cppcheck::Internal { class CppcheckRunner; class CppcheckDiagnosticManager; -class CppcheckOptions; class CppcheckTool final : public QObject { Q_OBJECT public: - CppcheckTool(CppcheckOptions &options, CppcheckDiagnosticManager &manager, const Utils::Id &progressId); + CppcheckTool(CppcheckDiagnosticManager &manager, const Utils::Id &progressId); ~CppcheckTool() override; void updateOptions(); @@ -49,7 +48,6 @@ private: void addToQueue(const Utils::FilePaths &files, const CppEditor::ProjectPart &part); QStringList additionalArguments(const CppEditor::ProjectPart &part) const; - CppcheckOptions &m_options; CppcheckDiagnosticManager &m_manager; QPointer<ProjectExplorer::Project> m_project; std::unique_ptr<CppcheckRunner> m_runner; diff --git a/src/plugins/cppcheck/cppchecktrigger.cpp b/src/plugins/cppcheck/cppchecktrigger.cpp index 8be098df558..c7317a958d0 100644 --- a/src/plugins/cppcheck/cppchecktrigger.cpp +++ b/src/plugins/cppcheck/cppchecktrigger.cpp @@ -56,7 +56,7 @@ void CppcheckTrigger::checkEditors(const QList<IEditor *> &editors) using CppModelManager = CppEditor::CppModelManager; const CppEditor::ProjectInfo::ConstPtr info - = CppModelManager::instance()->projectInfo(m_currentProject); + = CppModelManager::projectInfo(m_currentProject); if (!info) return; diff --git a/src/plugins/cppeditor/CMakeLists.txt b/src/plugins/cppeditor/CMakeLists.txt index 021bf0b7630..0c83b99b58f 100644 --- a/src/plugins/cppeditor/CMakeLists.txt +++ b/src/plugins/cppeditor/CMakeLists.txt @@ -129,6 +129,7 @@ extend_qtc_plugin(CppEditor cppmodelmanager_test.cpp cppmodelmanager_test.h cpppointerdeclarationformatter_test.cpp cpppointerdeclarationformatter_test.h cppquickfix_test.cpp cppquickfix_test.h + cpprenaming_test.cpp cpprenaming_test.h cppsourceprocessertesthelper.cpp cppsourceprocessertesthelper.h cppsourceprocessor_test.cpp cppsourceprocessor_test.h cpptoolstestcase.cpp cpptoolstestcase.h diff --git a/src/plugins/cppeditor/CppEditor.json.in b/src/plugins/cppeditor/CppEditor.json.in index b3c0149291b..0e9d50eb4b3 100644 --- a/src/plugins/cppeditor/CppEditor.json.in +++ b/src/plugins/cppeditor/CppEditor.json.in @@ -1,125 +1,125 @@ { - \"Name\" : \"CppEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CppEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"C++\", - \"Description\" : \"C/C++ editor component.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "C++", + "Description" : "C/C++ editor component.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-csrc\'>\", - \" <comment>C source code</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <alias type=\'text/x-c\'/>\", - \" <glob pattern=\'*.c\' case-sensitive=\'true\' weight=\'70\'/>\", - \" </mime-type>\", + "Mimetypes" : [ + "<?xml version='1.0' encoding='UTF-8'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-csrc'>", + " <comment>C source code</comment>", + " <sub-class-of type='text/plain'/>", + " <alias type='text/x-c'/>", + " <glob pattern='*.c' case-sensitive='true' weight='70'/>", + " </mime-type>", - \" <mime-type type=\'text/vnd.nvidia.cuda.csrc\'>\", - \" <sub-class-of type=\'text/x-csrc\'/>\", - \" <comment>NVIDIA CUDA C source code</comment>\", - \" <glob pattern=\'*.cu\'/>\", - \" </mime-type>\", + " <mime-type type='text/vnd.nvidia.cuda.csrc'>", + " <sub-class-of type='text/x-csrc'/>", + " <comment>NVIDIA CUDA C source code</comment>", + " <glob pattern='*.cu'/>", + " </mime-type>", - \" <mime-type type=\'text/x-chdr\'>\", - \" <comment>C header</comment>\", - \" <sub-class-of type=\'text/x-csrc\'/>\", - \" <!-- reduce weight from freedesktop to avoid conflict with text/x-c++hdr -->\", - \" <glob pattern=\'*.h\' weight=\'30\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-chdr'>", + " <comment>C header</comment>", + " <sub-class-of type='text/x-csrc'/>", + " <!-- reduce weight from freedesktop to avoid conflict with text/x-c++hdr -->", + " <glob pattern='*.h' weight='30'/>", + " </mime-type>", - \" <!-- Those are used to find matching headers by the CppEditor plugin,\", - \" so, they should match -->\", - \" <mime-type type=\'text/x-c++hdr\'>\", - \" <sub-class-of type=\'text/x-chdr\'/>\", - \" <comment>C++ header</comment>\", - \" <glob pattern=\'*.hh\' weight=\'70\'/>\", - \" <glob pattern=\'*.hxx\' weight=\'70\'/>\", - \" <glob pattern=\'*.h++\' weight=\'70\'/>\", - \" <glob pattern=\'*.hpp\' weight=\'70\'/>\", - \" <glob pattern=\'*.hp\' weight=\'70\'/>\", - \" <!-- Additions to freedesktop: -->\", - \" <glob pattern=\'*.h\' weight=\'70\'/>\", - \" <glob pattern=\'*.H\' weight=\'70\'/>\", - \" <glob pattern=\'*.inl\' weight=\'70\'/>\", - \" <glob pattern=\'*.tcc\' weight=\'70\'/>\", - \" <glob pattern=\'*.tpp\' weight=\'70\'/>\", - \" <glob pattern=\'*.t++\' weight=\'70\'/>\", - \" <glob pattern=\'*.txx\' weight=\'70\'/>\", - \" <!-- Find include guards of header files without extension, for\", - \" example, STL ones like <string>. Those can have a big initial\", - \" comment exceeding 1000 chars, though. -->\", - \" <magic priority=\'40\'>\", - \" <match value=\'#ifndef \' type=\'string\' offset=\'0:2000\'/>\", - \" <match value=\'#if \' type=\'string\' offset=\'0:2000\'/>\", - \" <match value=\'#include \' type=\'string\' offset=\'0:2000\'/>\", - \" </magic>\", - \" </mime-type>\", + " <!-- Those are used to find matching headers by the CppEditor plugin,", + " so, they should match -->", + " <mime-type type='text/x-c++hdr'>", + " <sub-class-of type='text/x-chdr'/>", + " <comment>C++ header</comment>", + " <glob pattern='*.hh' weight='70'/>", + " <glob pattern='*.hxx' weight='70'/>", + " <glob pattern='*.h++' weight='70'/>", + " <glob pattern='*.hpp' weight='70'/>", + " <glob pattern='*.hp' weight='70'/>", + " <!-- Additions to freedesktop: -->", + " <glob pattern='*.h' weight='70'/>", + " <glob pattern='*.H' weight='70'/>", + " <glob pattern='*.inl' weight='70'/>", + " <glob pattern='*.tcc' weight='70'/>", + " <glob pattern='*.tpp' weight='70'/>", + " <glob pattern='*.t++' weight='70'/>", + " <glob pattern='*.txx' weight='70'/>", + " <!-- Find include guards of header files without extension, for", + " example, STL ones like <string>. Those can have a big initial", + " comment exceeding 1000 chars, though. -->", + " <magic priority='40'>", + " <match value='#ifndef ' type='string' offset='0:2000'/>", + " <match value='#if ' type='string' offset='0:2000'/>", + " <match value='#include ' type='string' offset='0:2000'/>", + " </magic>", + " </mime-type>", - \" <mime-type type=\'text/x-c++src\'>\", - \" <comment>C++ source code</comment>\", - \" <sub-class-of type=\'text/x-csrc\'/>\", - \" <glob pattern=\'*.cpp\' weight=\'70\'/>\", - \" <glob pattern=\'*.cxx\' weight=\'70\'/>\", - \" <glob pattern=\'*.cc\' weight=\'70\'/>\", - \" <glob pattern=\'*.C\' case-sensitive=\'true\' weight=\'70\'/>\", - \" <glob pattern=\'*.c++\' weight=\'70\'/>\", - \" <!-- Additions to freedesktop: -->\", - \" <glob pattern=\'*.cp\' weight=\'70\'/>\", - \" <magic priority=\'30\'>\", - \" <match value=\'-*- C++ -*-\' type=\'string\' offset=\'0:30\'/>\", - \" </magic>\", - \" </mime-type>\", + " <mime-type type='text/x-c++src'>", + " <comment>C++ source code</comment>", + " <sub-class-of type='text/x-csrc'/>", + " <glob pattern='*.cpp' weight='70'/>", + " <glob pattern='*.cxx' weight='70'/>", + " <glob pattern='*.cc' weight='70'/>", + " <glob pattern='*.C' case-sensitive='true' weight='70'/>", + " <glob pattern='*.c++' weight='70'/>", + " <!-- Additions to freedesktop: -->", + " <glob pattern='*.cp' weight='70'/>", + " <magic priority='30'>", + " <match value='-*- C++ -*-' type='string' offset='0:30'/>", + " </magic>", + " </mime-type>", - \" <mime-type type=\'text/x-qdoc\'>\", - \" <comment>Qt documentation file</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <glob pattern=\'*.qdoc\' weight=\'70\'/>\", - \" <glob pattern=\'*.qdocinc\' weight=\'70\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-qdoc'>", + " <comment>Qt documentation file</comment>", + " <sub-class-of type='text/plain'/>", + " <glob pattern='*.qdoc' weight='70'/>", + " <glob pattern='*.qdocinc' weight='70'/>", + " </mime-type>", - \" <mime-type type=\'text/x-moc\'>\", - \" <comment>Qt MOC file</comment>\", - \" <!-- Fix to freedesktop: moc is C++ source -->\", - \" <sub-class-of type=\'text/x-c++src\'/>\", - \" <glob pattern=\'*.moc\' weight=\'70\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-moc'>", + " <comment>Qt MOC file</comment>", + " <!-- Fix to freedesktop: moc is C++ source -->", + " <sub-class-of type='text/x-c++src'/>", + " <glob pattern='*.moc' weight='70'/>", + " </mime-type>", - \" <mime-type type=\'text/x-objc++src\'>\", - \" <comment>Objective-C++ source code</comment>\", - \" <sub-class-of type=\'text/x-c++src\'/>\", - \" <sub-class-of type=\'text/x-objcsrc\'/>\", - \" <glob pattern=\'*.mm\' weight=\'70\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-objc++src'>", + " <comment>Objective-C++ source code</comment>", + " <sub-class-of type='text/x-c++src'/>", + " <sub-class-of type='text/x-objcsrc'/>", + " <glob pattern='*.mm' weight='70'/>", + " </mime-type>", - \" <mime-type type=\'text/x-objcsrc\'>\", - \" <comment>Objective-C source code</comment>\", - \" <sub-class-of type=\'text/x-csrc\'/>\", - \" <glob pattern=\'*.m\' weight=\'70\'/>\", - \" <magic priority=\'30\'>\", - \" <match value=\'#import\' type=\'string\' offset=\'0\'/>\", - \" </magic>\", - \" </mime-type>\", + " <mime-type type='text/x-objcsrc'>", + " <comment>Objective-C source code</comment>", + " <sub-class-of type='text/x-csrc'/>", + " <glob pattern='*.m' weight='70'/>", + " <magic priority='30'>", + " <match value='#import' type='string' offset='0'/>", + " </magic>", + " </mime-type>", - \" <mime-type type=\'text/x-dsrc\'>\", - \" <comment>D source code</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <glob pattern=\'*.d\'/>\", - \" <glob pattern=\'*.di\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-dsrc'>", + " <comment>D source code</comment>", + " <sub-class-of type='text/plain'/>", + " <glob pattern='*.d'/>", + " <glob pattern='*.di'/>", + " </mime-type>", - \"</mime-info>\" + "</mime-info>" ] } diff --git a/src/plugins/cppeditor/abstracteditorsupport.cpp b/src/plugins/cppeditor/abstracteditorsupport.cpp index a47fb412f32..ff43c5895aa 100644 --- a/src/plugins/cppeditor/abstracteditorsupport.cpp +++ b/src/plugins/cppeditor/abstracteditorsupport.cpp @@ -16,32 +16,33 @@ using namespace Utils; namespace CppEditor { -AbstractEditorSupport::AbstractEditorSupport(CppModelManager *modelmanager, QObject *parent) : - QObject(parent), m_modelmanager(modelmanager), m_revision(1) +AbstractEditorSupport::AbstractEditorSupport(QObject *parent) : + QObject(parent), m_revision(1) { - modelmanager->addExtraEditorSupport(this); + CppModelManager::addExtraEditorSupport(this); } AbstractEditorSupport::~AbstractEditorSupport() { - m_modelmanager->removeExtraEditorSupport(this); + CppModelManager::removeExtraEditorSupport(this); } void AbstractEditorSupport::updateDocument() { ++m_revision; - m_modelmanager->updateSourceFiles({filePath()}); + CppModelManager::updateSourceFiles({filePath()}); } void AbstractEditorSupport::notifyAboutUpdatedContents() const { - m_modelmanager->emitAbstractEditorSupportContentsUpdated( + CppModelManager::emitAbstractEditorSupportContentsUpdated( filePath().toString(), sourceFilePath().toString(), contents()); } -QString AbstractEditorSupport::licenseTemplate(const FilePath &filePath, const QString &className) +QString AbstractEditorSupport::licenseTemplate(ProjectExplorer::Project *project, + const FilePath &filePath, const QString &className) { - const QString license = Internal::CppFileSettings::licenseTemplate(); + const QString license = Internal::CppEditorPlugin::licenseTemplate(project); Utils::MacroExpander expander; expander.registerVariable("Cpp:License:FileName", Tr::tr("The file name."), [filePath] { return filePath.fileName(); }); @@ -51,9 +52,9 @@ QString AbstractEditorSupport::licenseTemplate(const FilePath &filePath, const Q return Utils::TemplateEngine::processText(&expander, license, nullptr); } -bool AbstractEditorSupport::usePragmaOnce() +bool AbstractEditorSupport::usePragmaOnce(ProjectExplorer::Project *project) { - return Internal::CppEditorPlugin::usePragmaOnce(); + return Internal::CppEditorPlugin::usePragmaOnce(project); } } // CppEditor diff --git a/src/plugins/cppeditor/abstracteditorsupport.h b/src/plugins/cppeditor/abstracteditorsupport.h index 2b18873238b..0f8a8d179b0 100644 --- a/src/plugins/cppeditor/abstracteditorsupport.h +++ b/src/plugins/cppeditor/abstracteditorsupport.h @@ -9,16 +9,16 @@ #include <QObject> -namespace CppEditor { +namespace ProjectExplorer { class Project; } -class CppModelManager; +namespace CppEditor { class CPPEDITOR_EXPORT AbstractEditorSupport : public QObject { Q_OBJECT public: - explicit AbstractEditorSupport(CppModelManager *modelmanager, QObject *parent = nullptr); + explicit AbstractEditorSupport(QObject *parent = nullptr); ~AbstractEditorSupport() override; /// \returns the contents, encoded as UTF-8 @@ -30,11 +30,12 @@ public: void notifyAboutUpdatedContents() const; unsigned revision() const { return m_revision; } - static QString licenseTemplate(const Utils::FilePath &filePath = {}, const QString &className = {}); - static bool usePragmaOnce(); + static QString licenseTemplate(ProjectExplorer::Project *project, + const Utils::FilePath &filePath = {}, + const QString &className = {}); + static bool usePragmaOnce(ProjectExplorer::Project *project); private: - CppModelManager *m_modelmanager; unsigned m_revision; }; diff --git a/src/plugins/cppeditor/baseeditordocumentparser.cpp b/src/plugins/cppeditor/baseeditordocumentparser.cpp index cbfa6f96859..109a478871f 100644 --- a/src/plugins/cppeditor/baseeditordocumentparser.cpp +++ b/src/plugins/cppeditor/baseeditordocumentparser.cpp @@ -92,8 +92,7 @@ ProjectPartInfo BaseEditorDocumentParser::projectPartInfo() const BaseEditorDocumentParser::Ptr BaseEditorDocumentParser::get(const FilePath &filePath) { - CppModelManager *cmmi = CppModelManager::instance(); - if (CppEditorDocumentHandle *cppEditorDocument = cmmi->cppEditorDocument(filePath)) { + if (CppEditorDocumentHandle *cppEditorDocument = CppModelManager::cppEditorDocument(filePath)) { if (BaseEditorDocumentProcessor *processor = cppEditorDocument->processor()) return processor->parser(); } @@ -109,14 +108,14 @@ ProjectPartInfo BaseEditorDocumentParser::determineProjectPart(const QString &fi { Internal::ProjectPartChooser chooser; chooser.setFallbackProjectPart([](){ - return CppModelManager::instance()->fallbackProjectPart(); + return CppModelManager::fallbackProjectPart(); }); chooser.setProjectPartsForFile([](const QString &filePath) { - return CppModelManager::instance()->projectPart(filePath); + return CppModelManager::projectPart(filePath); }); chooser.setProjectPartsFromDependenciesForFile([&](const QString &filePath) { const auto fileName = Utils::FilePath::fromString(filePath); - return CppModelManager::instance()->projectPartFromDependencies(fileName); + return CppModelManager::projectPartFromDependencies(fileName); }); const ProjectPartInfo chooserResult diff --git a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp index c6c51675eb9..f48c5a710be 100644 --- a/src/plugins/cppeditor/baseeditordocumentprocessor.cpp +++ b/src/plugins/cppeditor/baseeditordocumentprocessor.cpp @@ -39,7 +39,7 @@ void BaseEditorDocumentProcessor::run(bool projectsUpdated) ? Utils::Language::C : Utils::Language::Cxx; - runImpl({CppModelManager::instance()->workingCopy(), + runImpl({CppModelManager::workingCopy(), ProjectExplorer::ProjectManager::startupProject(), languagePreference, projectsUpdated}); @@ -72,7 +72,7 @@ void BaseEditorDocumentProcessor::runParser(QPromise<void> &promise, } parser->update(promise, updateParams); - CppModelManager::instance()->finishedRefreshingSourceFiles({parser->filePath().toString()}); + CppModelManager::finishedRefreshingSourceFiles({parser->filePath().toString()}); promise.setProgressValue(1); } diff --git a/src/plugins/cppeditor/builtincursorinfo.cpp b/src/plugins/cppeditor/builtincursorinfo.cpp index 4545ebe748d..15e1e0f4921 100644 --- a/src/plugins/cppeditor/builtincursorinfo.cpp +++ b/src/plugins/cppeditor/builtincursorinfo.cpp @@ -218,8 +218,8 @@ private: if (Symbol *s = Internal::CanonicalSymbol::canonicalSymbol( m_scope, m_expression, typeOfExpression)) { - const QList<int> tokenIndices = CppModelManager::instance() - ->references(s, typeOfExpression.context()); + const QList<int> tokenIndices = + CppModelManager::references(s, typeOfExpression.context()); result = toRanges(tokenIndices, m_document->translationUnit()); } diff --git a/src/plugins/cppeditor/builtineditordocumentparser.cpp b/src/plugins/cppeditor/builtineditordocumentparser.cpp index f6d7d6faf22..7c8444098e0 100644 --- a/src/plugins/cppeditor/builtineditordocumentparser.cpp +++ b/src/plugins/cppeditor/builtineditordocumentparser.cpp @@ -59,8 +59,7 @@ void BuiltinEditorDocumentParser::updateImpl(const QPromise<void> &promise, bool invalidateSnapshot = false, invalidateConfig = false; - CppModelManager *modelManager = CppModelManager::instance(); - QByteArray configFile = modelManager->codeModelConfiguration(); + QByteArray configFile = CppModelManager::codeModelConfiguration(); ProjectExplorer::HeaderPaths headerPaths; FilePaths includedFiles; FilePaths precompiledHeaders; @@ -131,7 +130,7 @@ void BuiltinEditorDocumentParser::updateImpl(const QPromise<void> &promise, else invalidateSnapshot = true; - Snapshot globalSnapshot = modelManager->snapshot(); + Snapshot globalSnapshot = CppModelManager::snapshot(); if (invalidateSnapshot) { state.snapshot = Snapshot(); @@ -172,19 +171,19 @@ void BuiltinEditorDocumentParser::updateImpl(const QPromise<void> &promise, Internal::CppSourceProcessor sourceProcessor(state.snapshot, [&](const Document::Ptr &doc) { const bool isInEditor = doc->filePath() == filePath(); - Document::Ptr otherDoc = modelManager->document(doc->filePath()); + Document::Ptr otherDoc = CppModelManager::document(doc->filePath()); unsigned newRev = otherDoc.isNull() ? 1U : otherDoc->revision() + 1; if (isInEditor) newRev = qMax(rev + 1, newRev); doc->setRevision(newRev); - modelManager->emitDocumentUpdated(doc); + CppModelManager::emitDocumentUpdated(doc); if (releaseSourceAndAST_) doc->releaseSourceAndAST(); }); sourceProcessor.setFileSizeLimitInMb(m_fileSizeLimitInMb); sourceProcessor.setCancelChecker([&promise] { return promise.isCanceled(); }); - Snapshot globalSnapshot = modelManager->snapshot(); + Snapshot globalSnapshot = CppModelManager::snapshot(); globalSnapshot.remove(filePath()); sourceProcessor.setGlobalSnapshot(globalSnapshot); sourceProcessor.setWorkingCopy(workingCopy); diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp index f4df1fcd0cd..94f86815f26 100644 --- a/src/plugins/cppeditor/builtineditordocumentprocessor.cpp +++ b/src/plugins/cppeditor/builtineditordocumentprocessor.cpp @@ -183,7 +183,7 @@ BuiltinEditorDocumentProcessor::~BuiltinEditorDocumentProcessor() void BuiltinEditorDocumentProcessor::runImpl( const BaseEditorDocumentParser::UpdateParams &updateParams) { - m_parserFuture = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(), + m_parserFuture = Utils::asyncRun(CppModelManager::sharedThreadPool(), runParser, parser(), updateParams); } @@ -277,13 +277,13 @@ void BuiltinEditorDocumentProcessor::onParserFinished(CPlusPlus::Document::Ptr d continue; if (cppEditorDoc->filePath() == document->filePath()) continue; - CPlusPlus::Document::Ptr cppDoc = CppModelManager::instance()->document( - cppEditorDoc->filePath()); + CPlusPlus::Document::Ptr cppDoc = CppModelManager::document(cppEditorDoc->filePath()); if (!cppDoc) continue; if (!cppDoc->includedFiles().contains(document->filePath())) continue; cppEditorDoc->scheduleProcessDocument(); + forceUpdate(cppEditorDoc); } } @@ -325,7 +325,7 @@ SemanticInfo::Source BuiltinEditorDocumentProcessor::createSemanticInfoSource(bo { QByteArray source; int revision = 0; - if (const auto entry = CppModelManager::instance()->workingCopy().get(filePath())) { + if (const auto entry = CppModelManager::workingCopy().get(filePath())) { source = entry->first; revision = entry->second; } diff --git a/src/plugins/cppeditor/builtineditordocumentprocessor.h b/src/plugins/cppeditor/builtineditordocumentprocessor.h index a061336587e..b33951aabe1 100644 --- a/src/plugins/cppeditor/builtineditordocumentprocessor.h +++ b/src/plugins/cppeditor/builtineditordocumentprocessor.h @@ -43,6 +43,8 @@ private: SemanticInfo::Source createSemanticInfoSource(bool force) const; + virtual void forceUpdate(TextEditor::TextDocument *) {} + private: BuiltinEditorDocumentParser::Ptr m_parser; QFuture<void> m_parserFuture; diff --git a/src/plugins/cppeditor/clangdiagnosticconfig.cpp b/src/plugins/cppeditor/clangdiagnosticconfig.cpp index d97ece6616a..bfc7b0436e9 100644 --- a/src/plugins/cppeditor/clangdiagnosticconfig.cpp +++ b/src/plugins/cppeditor/clangdiagnosticconfig.cpp @@ -5,8 +5,9 @@ #include "cpptoolsreuse.h" #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> -#include <QSettings> +using namespace Utils; namespace CppEditor { @@ -215,7 +216,7 @@ static const char diagnosticConfigsTidyModeKey[] = "clangTidyMode"; static const char diagnosticConfigsClazyModeKey[] = "clazyMode"; static const char diagnosticConfigsClazyChecksKey[] = "clazyChecks"; -void diagnosticConfigsToSettings(QSettings *s, const ClangDiagnosticConfigs &configs) +void diagnosticConfigsToSettings(QtcSettings *s, const ClangDiagnosticConfigs &configs) { s->beginWriteArray(diagnosticConfigsArrayKey); for (int i = 0, size = configs.size(); i < size; ++i) { @@ -234,7 +235,7 @@ void diagnosticConfigsToSettings(QSettings *s, const ClangDiagnosticConfigs &con s->endArray(); } -ClangDiagnosticConfigs diagnosticConfigsFromSettings(QSettings *s) +ClangDiagnosticConfigs diagnosticConfigsFromSettings(QtcSettings *s) { ClangDiagnosticConfigs configs; diff --git a/src/plugins/cppeditor/clangdiagnosticconfig.h b/src/plugins/cppeditor/clangdiagnosticconfig.h index 3442c95db50..5c1070de366 100644 --- a/src/plugins/cppeditor/clangdiagnosticconfig.h +++ b/src/plugins/cppeditor/clangdiagnosticconfig.h @@ -12,9 +12,7 @@ #include <QStringList> #include <QVector> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace CppEditor { @@ -88,8 +86,8 @@ private: using ClangDiagnosticConfigs = QVector<ClangDiagnosticConfig>; -ClangDiagnosticConfigs CPPEDITOR_EXPORT diagnosticConfigsFromSettings(QSettings *s); -void CPPEDITOR_EXPORT diagnosticConfigsToSettings(QSettings *s, +ClangDiagnosticConfigs CPPEDITOR_EXPORT diagnosticConfigsFromSettings(Utils::QtcSettings *s); +void CPPEDITOR_EXPORT diagnosticConfigsToSettings(Utils::QtcSettings *s, const ClangDiagnosticConfigs &configs); } // namespace CppEditor diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp index 317cf7b25dd..12403bd1533 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp @@ -376,7 +376,7 @@ void CompilerOptionsBuilder::addHeaderPathOptions() void CompilerOptionsBuilder::addIncludeFile(const QString &file) { - if (QFile::exists(file)) { + if (QFileInfo::exists(file)) { add({isClStyle() ? QLatin1String(includeFileOptionCl) : QLatin1String(includeFileOptionGcc), QDir::toNativeSeparators(file)}); @@ -900,6 +900,10 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() continue; } + // GCC option that clang doesn't know. + if (option.contains("direct-extern-access")) + continue; + // These were already parsed into ProjectPart::includedFiles. if (option == includeFileOptionCl || option == includeFileOptionGcc) { skipNext = true; diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp index efcbd51569d..01a208f7032 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp @@ -13,7 +13,6 @@ #include "cpptoolsreuse.h" #include "symbolfinder.h" -#include <app/app_version.h> #include <coreplugin/messagemanager.h> #include <texteditor/basehoverhandler.h> #include <utils/qtcassert.h> @@ -106,7 +105,7 @@ void BuiltinModelManagerSupport::followSymbol(const CursorInEditor &data, { SymbolFinder finder; m_followSymbol->findLink(data, processLinkCallback, - resolveTarget, CppModelManager::instance()->snapshot(), + resolveTarget, CppModelManager::snapshot(), data.editorWidget()->semanticInfo().doc, &finder, inNextSplit); } @@ -126,7 +125,7 @@ void BuiltinModelManagerSupport::switchDeclDef(const CursorInEditor &data, { SymbolFinder finder; m_followSymbol->switchDeclDef(data, processLinkCallback, - CppModelManager::instance()->snapshot(), data.editorWidget()->semanticInfo().doc, + CppModelManager::snapshot(), data.editorWidget()->semanticInfo().doc, &finder); } @@ -145,47 +144,39 @@ void BuiltinModelManagerSupport::globalRename(const CursorInEditor &data, const QString &replacement, const std::function<void()> &callback) { - CppModelManager *modelManager = CppModelManager::instance(); - if (!modelManager) - return; - CppEditorWidget *editorWidget = data.editorWidget(); QTC_ASSERT(editorWidget, return;); SemanticInfo info = editorWidget->semanticInfo(); - info.snapshot = modelManager->snapshot(); + info.snapshot = CppModelManager::snapshot(); info.snapshot.insert(info.doc); const QTextCursor &cursor = data.cursor(); if (const CPlusPlus::Macro *macro = findCanonicalMacro(cursor, info.doc)) { - modelManager->renameMacroUsages(*macro, replacement); + CppModelManager::renameMacroUsages(*macro, replacement); } else { Internal::CanonicalSymbol cs(info.doc, info.snapshot); CPlusPlus::Symbol *canonicalSymbol = cs(cursor); if (canonicalSymbol) - modelManager->renameUsages(canonicalSymbol, cs.context(), replacement, callback); + CppModelManager::renameUsages(canonicalSymbol, cs.context(), replacement, callback); } } void BuiltinModelManagerSupport::findUsages(const CursorInEditor &data) const { - CppModelManager *modelManager = CppModelManager::instance(); - if (!modelManager) - return; - CppEditorWidget *editorWidget = data.editorWidget(); QTC_ASSERT(editorWidget, return;); SemanticInfo info = editorWidget->semanticInfo(); - info.snapshot = modelManager->snapshot(); + info.snapshot = CppModelManager::snapshot(); info.snapshot.insert(info.doc); const QTextCursor &cursor = data.cursor(); if (const CPlusPlus::Macro *macro = findCanonicalMacro(cursor, info.doc)) { - modelManager->findMacroUsages(*macro); + CppModelManager::findMacroUsages(*macro); } else { Internal::CanonicalSymbol cs(info.doc, info.snapshot); CPlusPlus::Symbol *canonicalSymbol = cs(cursor); if (canonicalSymbol) - modelManager->findUsages(canonicalSymbol, cs.context()); + CppModelManager::findUsages(canonicalSymbol, cs.context()); } } @@ -200,7 +191,7 @@ void BuiltinModelManagerSupport::switchHeaderSource(const FilePath &filePath, void BuiltinModelManagerSupport::checkUnused(const Utils::Link &link, SearchResult *search, const Utils::LinkHandler &callback) { - CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot(); + CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); QFile file(link.targetFilePath.toString()); if (!file.open(QIODevice::ReadOnly)) return callback(link); diff --git a/src/plugins/cppeditor/cppcanonicalsymbol.cpp b/src/plugins/cppeditor/cppcanonicalsymbol.cpp index 102a5d07819..4728f643096 100644 --- a/src/plugins/cppeditor/cppcanonicalsymbol.cpp +++ b/src/plugins/cppeditor/cppcanonicalsymbol.cpp @@ -54,7 +54,7 @@ Scope *CanonicalSymbol::getScopeAndExpression(const QTextCursor &cursor, QString return m_document->scopeAt(line, column); } -Symbol *CanonicalSymbol::operator()(const QTextCursor &cursor) +Symbol *CanonicalSymbol::operator()(const QTextCursor &cursor) & { QString code; diff --git a/src/plugins/cppeditor/cppcanonicalsymbol.h b/src/plugins/cppeditor/cppcanonicalsymbol.h index 010949bbbb3..6bf95c3181e 100644 --- a/src/plugins/cppeditor/cppcanonicalsymbol.h +++ b/src/plugins/cppeditor/cppcanonicalsymbol.h @@ -21,7 +21,7 @@ public: CPlusPlus::Scope *getScopeAndExpression(const QTextCursor &cursor, QString *code); - CPlusPlus::Symbol *operator()(const QTextCursor &cursor); + CPlusPlus::Symbol *operator()(const QTextCursor &cursor) &; CPlusPlus::Symbol *operator()(CPlusPlus::Scope *scope, const QString &code); public: diff --git a/src/plugins/cppeditor/cppchecksymbols.cpp b/src/plugins/cppeditor/cppchecksymbols.cpp index 7728ea308c1..e9334b0db29 100644 --- a/src/plugins/cppeditor/cppchecksymbols.cpp +++ b/src/plugins/cppeditor/cppchecksymbols.cpp @@ -73,9 +73,7 @@ protected: { if (!doc) return; - if (!processed->contains(doc->globalNamespace())) { - processed->insert(doc->globalNamespace()); - + if (Utils::insert(*processed, doc->globalNamespace())) { const QList<Document::Include> includes = doc->resolvedIncludes(); for (const Document::Include &i : includes) process(_snapshot.document(i.resolvedFileName()), processed); @@ -806,8 +804,7 @@ bool CheckSymbols::hasVirtualDestructor(ClassOrNamespace *binding) const while (!todo.isEmpty()) { ClassOrNamespace *b = todo.takeFirst(); - if (b && !processed.contains(b)) { - processed.insert(b); + if (b && Utils::insert(processed, b)) { const QList<Symbol *> symbols = b->symbols(); for (Symbol *s : symbols) { if (Class *k = s->asClass()) { diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp index 48ee4c80f1e..0b4c5d9af38 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -1642,8 +1642,6 @@ void CppCodeModelInspectorDialog::onWorkingCopyDocumentSelected(const QModelInde void CppCodeModelInspectorDialog::refresh() { - CppModelManager *cmmi = CppModelManager::instance(); - const int oldSnapshotIndex = m_snapshotSelector->currentIndex(); const bool selectEditorRelevant = m_selectEditorRelevantEntriesAfterRefreshCheckBox->isChecked(); @@ -1652,7 +1650,7 @@ void CppCodeModelInspectorDialog::refresh() m_snapshotInfos->clear(); m_snapshotSelector->clear(); - const Snapshot globalSnapshot = cmmi->snapshot(); + const Snapshot globalSnapshot = CppModelManager::snapshot(); CppCodeModelInspector::Dumper dumper(globalSnapshot); m_snapshotModel->setGlobalSnapshot(globalSnapshot); @@ -1666,7 +1664,7 @@ void CppCodeModelInspectorDialog::refresh() CppEditorDocumentHandle *cppEditorDocument = nullptr; if (editor) { const FilePath editorFilePath = editor->document()->filePath(); - cppEditorDocument = cmmi->cppEditorDocument(editorFilePath); + cppEditorDocument = CppModelManager::cppEditorDocument(editorFilePath); if (auto documentProcessor = CppModelManager::cppEditorDocumentProcessor(editorFilePath)) { const Snapshot editorSnapshot = documentProcessor->snapshot(); m_snapshotInfos->append(SnapshotInfo(editorSnapshot, SnapshotInfo::EditorSnapshot)); @@ -1721,7 +1719,7 @@ void CppCodeModelInspectorDialog::refresh() ? cppEditorDocument->processor()->parser()->projectPartInfo().projectPart : ProjectPart::ConstPtr(); - const QList<ProjectInfo::ConstPtr> projectInfos = cmmi->projectInfos(); + const QList<ProjectInfo::ConstPtr> projectInfos = CppModelManager::projectInfos(); dumper.dumpProjectInfos(projectInfos); m_projectPartsModel->configure(projectInfos, editorsProjectPart); m_projectPartsView->resizeColumns(ProjectPartsModel::ColumnCount); @@ -1737,7 +1735,7 @@ void CppCodeModelInspectorDialog::refresh() } // Working Copy - const WorkingCopy workingCopy = cmmi->workingCopy(); + const WorkingCopy workingCopy = CppModelManager::workingCopy(); dumper.dumpWorkingCopy(workingCopy); m_workingCopyModel->configure(workingCopy); m_workingCopyView->resizeColumns(WorkingCopyModel::ColumnCount); @@ -1752,8 +1750,8 @@ void CppCodeModelInspectorDialog::refresh() } // Merged entities - dumper.dumpMergedEntities(cmmi->headerPaths(), - ProjectExplorer::Macro::toByteArray(cmmi->definedMacros())); + dumper.dumpMergedEntities(CppModelManager::headerPaths(), + ProjectExplorer::Macro::toByteArray(CppModelManager::definedMacros())); } enum DocumentTabs { diff --git a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp index 2a6dfb6e1e8..28d96779d66 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp @@ -8,7 +8,6 @@ #include "cpptoolsreuse.h" #include "cppworkingcopy.h" -#include <app/app_version.h> #include <coreplugin/icore.h> #include <projectexplorer/projectmacro.h> #include <projectexplorer/project.h> @@ -396,7 +395,7 @@ QString Utils::toString(const ProjectExplorer::Abi &abi) QString Utils::partsForFile(const ::Utils::FilePath &filePath) { const QList<ProjectPart::ConstPtr> parts - = CppModelManager::instance()->projectPart(filePath); + = CppModelManager::projectPart(filePath); QString result; for (const ProjectPart::ConstPtr &part : parts) result += part->displayName + QLatin1Char(','); diff --git a/src/plugins/cppeditor/cppcodemodelsettings.cpp b/src/plugins/cppeditor/cppcodemodelsettings.cpp index 7a32011cf54..2d94e1c86ec 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettings.cpp @@ -17,7 +17,6 @@ #include <utils/hostosinfo.h> #include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/settingsutils.h> #include <QDateTime> #include <QHash> @@ -29,55 +28,37 @@ using namespace Utils; namespace CppEditor { -static Id initialClangDiagnosticConfigId() -{ return Constants::CPP_CLANG_DIAG_CONFIG_BUILDSYSTEM; } - +static Id initialClangDiagnosticConfigId() { return Constants::CPP_CLANG_DIAG_CONFIG_BUILDSYSTEM; } static CppCodeModelSettings::PCHUsage initialPchUsage() -{ return CppCodeModelSettings::PchUse_BuildSystem; } + { return CppCodeModelSettings::PchUse_BuildSystem; } +static Key enableLowerClazyLevelsKey() { return "enableLowerClazyLevels"; } +static Key pchUsageKey() { return Constants::CPPEDITOR_MODEL_MANAGER_PCH_USAGE; } +static Key interpretAmbiguousHeadersAsCHeadersKey() + { return Constants::CPPEDITOR_INTERPRET_AMBIGIUOUS_HEADERS_AS_C_HEADERS; } +static Key skipIndexingBigFilesKey() { return Constants::CPPEDITOR_SKIP_INDEXING_BIG_FILES; } +static Key ignoreFilesKey() { return Constants::CPPEDITOR_IGNORE_FILES; } +static Key ignorePatternKey() { return Constants::CPPEDITOR_IGNORE_PATTERN; } +static Key useBuiltinPreprocessorKey() { return Constants::CPPEDITOR_USE_BUILTIN_PREPROCESSOR; } +static Key indexerFileSizeLimitKey() { return Constants::CPPEDITOR_INDEXER_FILE_SIZE_LIMIT; } -static QString enableLowerClazyLevelsKey() -{ return QLatin1String("enableLowerClazyLevels"); } - -static QString pchUsageKey() -{ return QLatin1String(Constants::CPPEDITOR_MODEL_MANAGER_PCH_USAGE); } - -static QString interpretAmbiguousHeadersAsCHeadersKey() -{ return QLatin1String(Constants::CPPEDITOR_INTERPRET_AMBIGIUOUS_HEADERS_AS_C_HEADERS); } - -static QString skipIndexingBigFilesKey() -{ return QLatin1String(Constants::CPPEDITOR_SKIP_INDEXING_BIG_FILES); } - -static QString ignoreFilesKey() -{ return QLatin1String(Constants::CPPEDITOR_IGNORE_FILES); } - -static QString ignorePatternKey() -{ return QLatin1String(Constants::CPPEDITOR_IGNORE_PATTERN); } - -static QString useBuiltinPreprocessorKey() -{ return QLatin1String(Constants::CPPEDITOR_USE_BUILTIN_PREPROCESSOR); } - -static QString indexerFileSizeLimitKey() -{ return QLatin1String(Constants::CPPEDITOR_INDEXER_FILE_SIZE_LIMIT); } - -static QString clangdSettingsKey() { return QLatin1String("ClangdSettings"); } -static QString useClangdKey() { return QLatin1String("UseClangdV7"); } -static QString clangdPathKey() { return QLatin1String("ClangdPath"); } -static QString clangdIndexingKey() { return QLatin1String("ClangdIndexing"); } -static QString clangdIndexingPriorityKey() { return QLatin1String("ClangdIndexingPriority"); } -static QString clangdHeaderSourceSwitchModeKey() { - return QLatin1String("ClangdHeaderSourceSwitchMode"); -} -static QString clangdHeaderInsertionKey() { return QLatin1String("ClangdHeaderInsertion"); } -static QString clangdThreadLimitKey() { return QLatin1String("ClangdThreadLimit"); } -static QString clangdDocumentThresholdKey() { return QLatin1String("ClangdDocumentThreshold"); } -static QString clangdSizeThresholdEnabledKey() { return QLatin1String("ClangdSizeThresholdEnabled"); } -static QString clangdSizeThresholdKey() { return QLatin1String("ClangdSizeThreshold"); } -static QString clangdUseGlobalSettingsKey() { return QLatin1String("useGlobalSettings"); } -static QString clangdblockIndexingSettingsKey() { return QLatin1String("blockIndexing"); } -static QString sessionsWithOneClangdKey() { return QLatin1String("SessionsWithOneClangd"); } -static QString diagnosticConfigIdKey() { return QLatin1String("diagnosticConfigId"); } -static QString checkedHardwareKey() { return QLatin1String("checkedHardware"); } -static QString completionResultsKey() { return QLatin1String("completionResults"); } +static Key clangdSettingsKey() { return "ClangdSettings"; } +static Key useClangdKey() { return "UseClangdV7"; } +static Key clangdPathKey() { return "ClangdPath"; } +static Key clangdIndexingKey() { return "ClangdIndexing"; } +static Key clangdIndexingPriorityKey() { return "ClangdIndexingPriority"; } +static Key clangdHeaderSourceSwitchModeKey() { return "ClangdHeaderSourceSwitchMode"; } +static Key clangdCompletionRankingModelKey() { return "ClangdCompletionRankingModel"; } +static Key clangdHeaderInsertionKey() { return "ClangdHeaderInsertion"; } +static Key clangdThreadLimitKey() { return "ClangdThreadLimit"; } +static Key clangdDocumentThresholdKey() { return "ClangdDocumentThreshold"; } +static Key clangdSizeThresholdEnabledKey() { return "ClangdSizeThresholdEnabled"; } +static Key clangdSizeThresholdKey() { return "ClangdSizeThreshold"; } +static Key clangdUseGlobalSettingsKey() { return "useGlobalSettings"; } +static Key clangdblockIndexingSettingsKey() { return "blockIndexing"; } +static Key sessionsWithOneClangdKey() { return "SessionsWithOneClangd"; } +static Key diagnosticConfigIdKey() { return "diagnosticConfigId"; } +static Key checkedHardwareKey() { return "checkedHardware"; } +static Key completionResultsKey() { return "completionResults"; } static FilePath g_defaultClangdFilePath; static FilePath fallbackClangdFilePath() @@ -87,9 +68,9 @@ static FilePath fallbackClangdFilePath() return Environment::systemEnvironment().searchInPath("clangd"); } -void CppCodeModelSettings::fromSettings(QSettings *s) +void CppCodeModelSettings::fromSettings(QtcSettings *s) { - s->beginGroup(QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP)); + s->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); setEnableLowerClazyLevels(s->value(enableLowerClazyLevelsKey(), true).toBool()); @@ -119,9 +100,9 @@ void CppCodeModelSettings::fromSettings(QSettings *s) emit changed(); } -void CppCodeModelSettings::toSettings(QSettings *s) +void CppCodeModelSettings::toSettings(QtcSettings *s) { - s->beginGroup(QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP)); + s->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); s->setValue(enableLowerClazyLevelsKey(), enableLowerClazyLevels()); s->setValue(pchUsageKey(), pchUsage()); @@ -242,6 +223,26 @@ QString ClangdSettings::headerSourceSwitchModeToDisplayString(HeaderSourceSwitch return {}; } +QString ClangdSettings::rankingModelToCmdLineString(CompletionRankingModel model) +{ + switch (model) { + case CompletionRankingModel::Default: break; + case CompletionRankingModel::DecisionForest: return "decision_forest"; + case CompletionRankingModel::Heuristics: return "heuristics"; + } + QTC_ASSERT(false, return {}); +} + +QString ClangdSettings::rankingModelToDisplayString(CompletionRankingModel model) +{ + switch (model) { + case CompletionRankingModel::Default: return Tr::tr("Default"); + case CompletionRankingModel::DecisionForest: return Tr::tr("Decision Forest"); + case CompletionRankingModel::Heuristics: return Tr::tr("Heuristics"); + } + QTC_ASSERT(false, return {}); +} + ClangdSettings &ClangdSettings::instance() { static ClangdSettings settings; @@ -419,13 +420,14 @@ FilePath ClangdSettings::clangdUserConfigFilePath() void ClangdSettings::loadSettings() { const auto settings = Core::ICore::settings(); - Utils::fromSettings(clangdSettingsKey(), {}, settings, &m_data); - settings->beginGroup(QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP)); + m_data.fromMap(Utils::storeFromSettings(clangdSettingsKey(), settings)); + + settings->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); m_data.customDiagnosticConfigs = diagnosticConfigsFromSettings(settings); // Pre-8.0 compat - static const QString oldKey("ClangDiagnosticConfig"); + static const Key oldKey("ClangDiagnosticConfig"); const QVariant configId = settings->value(oldKey); if (configId.isValid()) { m_data.diagnosticConfigId = Id::fromSetting(configId); @@ -438,8 +440,8 @@ void ClangdSettings::loadSettings() void ClangdSettings::saveSettings() { const auto settings = Core::ICore::settings(); - Utils::toSettings(clangdSettingsKey(), {}, settings, &m_data); - settings->beginGroup(QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP)); + Utils::storeToSettings(clangdSettingsKey(), settings, m_data.toMap()); + settings->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); diagnosticConfigsToSettings(settings, m_data.customDiagnosticConfigs); settings->endGroup(); } @@ -517,7 +519,7 @@ void ClangdProjectSettings::loadSettings() { if (!m_project) return; - const QVariantMap data = m_project->namedSettings(clangdSettingsKey()).toMap(); + const Store data = storeFromVariant(m_project->namedSettings(clangdSettingsKey())); m_useGlobalSettings = data.value(clangdUseGlobalSettingsKey(), true).toBool(); m_blockIndexing = data.value(clangdblockIndexingSettingsKey(), false).toBool(); if (!m_useGlobalSettings) @@ -528,17 +530,17 @@ void ClangdProjectSettings::saveSettings() { if (!m_project) return; - QVariantMap data; + Store data; if (!m_useGlobalSettings) data = m_customSettings.toMap(); data.insert(clangdUseGlobalSettingsKey(), m_useGlobalSettings); data.insert(clangdblockIndexingSettingsKey(), m_blockIndexing); - m_project->setNamedSettings(clangdSettingsKey(), data); + m_project->setNamedSettings(clangdSettingsKey(), variantFromStore(data)); } -QVariantMap ClangdSettings::Data::toMap() const +Store ClangdSettings::Data::toMap() const { - QVariantMap map; + Store map; map.insert(useClangdKey(), useClangd); map.insert(clangdPathKey(), executableFilePath != fallbackClangdFilePath() ? executableFilePath.toString() @@ -546,6 +548,7 @@ QVariantMap ClangdSettings::Data::toMap() const map.insert(clangdIndexingKey(), indexingPriority != IndexingPriority::Off); map.insert(clangdIndexingPriorityKey(), int(indexingPriority)); map.insert(clangdHeaderSourceSwitchModeKey(), int(headerSourceSwitchMode)); + map.insert(clangdCompletionRankingModelKey(), int(completionRankingModel)); map.insert(clangdHeaderInsertionKey(), autoIncludeHeaders); map.insert(clangdThreadLimitKey(), workerThreadLimit); map.insert(clangdDocumentThresholdKey(), documentUpdateThreshold); @@ -558,7 +561,7 @@ QVariantMap ClangdSettings::Data::toMap() const return map; } -void ClangdSettings::Data::fromMap(const QVariantMap &map) +void ClangdSettings::Data::fromMap(const Store &map) { useClangd = map.value(useClangdKey(), true).toBool(); executableFilePath = FilePath::fromString(map.value(clangdPathKey()).toString()); @@ -569,6 +572,8 @@ void ClangdSettings::Data::fromMap(const QVariantMap &map) indexingPriority = IndexingPriority::Off; headerSourceSwitchMode = HeaderSourceSwitchMode(map.value(clangdHeaderSourceSwitchModeKey(), int(headerSourceSwitchMode)).toInt()); + completionRankingModel = CompletionRankingModel(map.value(clangdCompletionRankingModelKey(), + int(completionRankingModel)).toInt()); autoIncludeHeaders = map.value(clangdHeaderInsertionKey(), false).toBool(); workerThreadLimit = map.value(clangdThreadLimitKey(), 0).toInt(); documentUpdateThreshold = map.value(clangdDocumentThresholdKey(), 500).toInt(); diff --git a/src/plugins/cppeditor/cppcodemodelsettings.h b/src/plugins/cppeditor/cppcodemodelsettings.h index c22fb88faa5..33ba3fae235 100644 --- a/src/plugins/cppeditor/cppcodemodelsettings.h +++ b/src/plugins/cppeditor/cppcodemodelsettings.h @@ -7,17 +7,15 @@ #include "cppeditor_global.h" #include <utils/clangutils.h> -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <utils/id.h> +#include <utils/store.h> +#include <utils/qtcsettings.h> #include <QObject> #include <QStringList> #include <QVersionNumber> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace ProjectExplorer { class Project; } namespace CppEditor { @@ -33,8 +31,8 @@ public: }; public: - void fromSettings(QSettings *s); - void toSettings(QSettings *s); + void fromSettings(Utils::QtcSettings *s); + void toSettings(Utils::QtcSettings *s); public: bool enableLowerClazyLevels() const; @@ -85,16 +83,19 @@ class CPPEDITOR_EXPORT ClangdSettings : public QObject public: enum class IndexingPriority { Off, Background, Normal, Low, }; enum class HeaderSourceSwitchMode { BuiltinOnly, ClangdOnly, Both }; + enum class CompletionRankingModel { Default, DecisionForest, Heuristics }; static QString priorityToString(const IndexingPriority &priority); static QString priorityToDisplayString(const IndexingPriority &priority); static QString headerSourceSwitchModeToDisplayString(HeaderSourceSwitchMode mode); + static QString rankingModelToCmdLineString(CompletionRankingModel model); + static QString rankingModelToDisplayString(CompletionRankingModel model); class CPPEDITOR_EXPORT Data { public: - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); friend bool operator==(const Data &s1, const Data &s2) { @@ -106,6 +107,7 @@ public: && s1.workerThreadLimit == s2.workerThreadLimit && s1.indexingPriority == s2.indexingPriority && s1.headerSourceSwitchMode == s2.headerSourceSwitchMode + && s1.completionRankingModel == s2.completionRankingModel && s1.autoIncludeHeaders == s2.autoIncludeHeaders && s1.documentUpdateThreshold == s2.documentUpdateThreshold && s1.sizeThresholdEnabled == s2.sizeThresholdEnabled @@ -127,6 +129,7 @@ public: bool useClangd = true; IndexingPriority indexingPriority = IndexingPriority::Low; HeaderSourceSwitchMode headerSourceSwitchMode = HeaderSourceSwitchMode::Both; + CompletionRankingModel completionRankingModel = CompletionRankingModel::Default; bool autoIncludeHeaders = false; bool sizeThresholdEnabled = false; bool haveCheckedHardwareReqirements = false; @@ -148,6 +151,7 @@ public: Utils::FilePath clangdFilePath() const; IndexingPriority indexingPriority() const { return m_data.indexingPriority; } HeaderSourceSwitchMode headerSourceSwitchMode() const { return m_data.headerSourceSwitchMode; } + CompletionRankingModel completionRankingModel() const { return m_data.completionRankingModel; } bool autoIncludeHeaders() const { return m_data.autoIncludeHeaders; } int workerThreadLimit() const { return m_data.workerThreadLimit; } int documentUpdateThreshold() const { return m_data.documentUpdateThreshold; } diff --git a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp index 289cd3dc86c..2c46504f17f 100644 --- a/src/plugins/cppeditor/cppcodemodelsettingspage.cpp +++ b/src/plugins/cppeditor/cppcodemodelsettingspage.cpp @@ -24,14 +24,15 @@ #include <QDesktopServices> #include <QFormLayout> #include <QGroupBox> +#include <QGuiApplication> #include <QInputDialog> #include <QPushButton> #include <QSpinBox> #include <QStringListModel> +#include <QTextBlock> #include <QTextStream> #include <QVBoxLayout> #include <QVersionNumber> -#include <QTextBlock> #include <limits> @@ -82,7 +83,7 @@ CppCodeModelSettingsWidget::CppCodeModelSettingsWidget(CppCodeModelSettings *s) m_ignorePatternTextEdit->setToolTip(m_ignoreFilesCheckBox->toolTip()); m_ignorePatternTextEdit->setEnabled(m_ignoreFilesCheckBox->isChecked()); - connect(m_ignoreFilesCheckBox, &QCheckBox::stateChanged, [this] { + connect(m_ignoreFilesCheckBox, &QCheckBox::stateChanged, this, [this] { m_ignorePatternTextEdit->setEnabled(m_ignoreFilesCheckBox->isChecked()); }); @@ -194,6 +195,7 @@ public: QCheckBox useClangdCheckBox; QComboBox indexingComboBox; QComboBox headerSourceSwitchComboBox; + QComboBox completionRankingModelComboBox; QCheckBox autoIncludeHeadersCheckBox; QCheckBox sizeThresholdCheckBox; QSpinBox threadLimitSpinBox; @@ -221,22 +223,34 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD "<p>Background Priority: Minimum priority, runs on idle CPUs. May leave 'performance' " "cores unused.</p>" "<p>Normal Priority: Reduced priority compared to interactive work.</p>" - "Low Priority: Same priority as other clangd work."); + "<p>Low Priority: Same priority as other clangd work.</p>"); const QString headerSourceSwitchToolTip = Tr::tr( - "<p>Which C/C++ backend to use when switching between header and source file." - "<p>The clangd implementation has more capabilities, but also has some bugs not present " - "in the built-in variant." - "<p>When \"Try Both\" is selected, clangd will be employed only if the built-in variant " - "does not find anything."); + "<p>The C/C++ backend to use for switching between header and source files.</p>" + "<p>While the clangd implementation has more capabilities than the built-in " + "code model, it tends to find false positives.</p>" + "<p>When \"Try Both\" is selected, clangd is used only if the built-in variant " + "does not find anything.</p>"); + using RankingModel = ClangdSettings::CompletionRankingModel; + const QString completionRankingModelToolTip = Tr::tr( + "<p>Which model clangd should use to rank possible completions.</p>" + "<p>This determines the order of candidates in the combo box when doing code completion.</p>" + "<p>The \"%1\" model used by default results from (pre-trained) machine learning and " + "provides superior results on average.</p>" + "<p>If you feel that its suggestions stray too much from your expectations for your " + "code base, you can try switching to the hand-crafted \"%2\" model.</p>").arg( + ClangdSettings::rankingModelToDisplayString(RankingModel::DecisionForest), + ClangdSettings::rankingModelToDisplayString(RankingModel::Heuristics)); const QString workerThreadsToolTip = Tr::tr( "Number of worker threads used by clangd. Background indexing also uses this many " "worker threads."); const QString autoIncludeToolTip = Tr::tr( "Controls whether clangd may insert header files as part of symbol completion."); - const QString documentUpdateToolTip = Tr::tr( - "Defines the amount of time Qt Creator waits before sending document changes to the " - "server.\n" - "If the document changes again while waiting, this timeout resets."); + const QString documentUpdateToolTip + //: %1 is the application name (Qt Creator) + = Tr::tr("Defines the amount of time %1 waits before sending document changes to the " + "server.\n" + "If the document changes again while waiting, this timeout resets.") + .arg(QGuiApplication::applicationDisplayName()); const QString sizeThresholdToolTip = Tr::tr( "Files greater than this will not be opened as documents in clangd.\n" "The built-in code model will handle highlighting, completion and so on."); @@ -266,6 +280,16 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD d->headerSourceSwitchComboBox.count() - 1); } d->headerSourceSwitchComboBox.setToolTip(headerSourceSwitchToolTip); + for (RankingModel model : {RankingModel::Default, RankingModel::DecisionForest, + RankingModel::Heuristics}) { + d->completionRankingModelComboBox.addItem( + ClangdSettings::rankingModelToDisplayString(model), int(model)); + if (model == settings.completionRankingModel()) + d->completionRankingModelComboBox.setCurrentIndex( + d->completionRankingModelComboBox.count() - 1); + } + d->completionRankingModelComboBox.setToolTip(completionRankingModelToolTip); + d->autoIncludeHeadersCheckBox.setText(Tr::tr("Insert header files on completion")); d->autoIncludeHeadersCheckBox.setChecked(settings.autoIncludeHeaders()); d->autoIncludeHeadersCheckBox.setToolTip(autoIncludeToolTip); @@ -331,6 +355,13 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD limitResultsLayout->addStretch(1); formLayout->addRow(completionResultsLabel, limitResultsLayout); + const auto completionRankingModelLayout = new QHBoxLayout; + completionRankingModelLayout->addWidget(&d->completionRankingModelComboBox); + completionRankingModelLayout->addStretch(1); + const auto completionRankingModelLabel = new QLabel(Tr::tr("Completion ranking model:")); + completionRankingModelLabel->setToolTip(completionRankingModelToolTip); + formLayout->addRow(completionRankingModelLabel, completionRankingModelLayout); + const auto documentUpdateThresholdLayout = new QHBoxLayout; documentUpdateThresholdLayout->addWidget(&d->documentUpdateThreshold); documentUpdateThresholdLayout->addStretch(1); @@ -467,6 +498,7 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD labelSetter.setWarning(errorMessage); }; connect(&d->clangdChooser, &Utils::PathChooser::textChanged, this, updateWarningLabel); + connect(&d->clangdChooser, &Utils::PathChooser::validChanged, this, updateWarningLabel); updateWarningLabel(); connect(&d->useClangdCheckBox, &QCheckBox::toggled, @@ -475,6 +507,8 @@ ClangdSettingsWidget::ClangdSettingsWidget(const ClangdSettings::Data &settingsD this, &ClangdSettingsWidget::settingsDataChanged); connect(&d->headerSourceSwitchComboBox, &QComboBox::currentIndexChanged, this, &ClangdSettingsWidget::settingsDataChanged); + connect(&d->completionRankingModelComboBox, &QComboBox::currentIndexChanged, + this, &ClangdSettingsWidget::settingsDataChanged); connect(&d->autoIncludeHeadersCheckBox, &QCheckBox::toggled, this, &ClangdSettingsWidget::settingsDataChanged); connect(&d->threadLimitSpinBox, &QSpinBox::valueChanged, @@ -507,6 +541,8 @@ ClangdSettings::Data ClangdSettingsWidget::settingsData() const d->indexingComboBox.currentData().toInt()); data.headerSourceSwitchMode = ClangdSettings::HeaderSourceSwitchMode( d->headerSourceSwitchComboBox.currentData().toInt()); + data.completionRankingModel = ClangdSettings::CompletionRankingModel( + d->completionRankingModelComboBox.currentData().toInt()); data.autoIncludeHeaders = d->autoIncludeHeadersCheckBox.isChecked(); data.workerThreadLimit = d->threadLimitSpinBox.value(); data.documentUpdateThreshold = d->documentUpdateThreshold.value(); diff --git a/src/plugins/cppeditor/cppcodestylepreferences.cpp b/src/plugins/cppeditor/cppcodestylepreferences.cpp index b09537bc72c..59d2bd232fe 100644 --- a/src/plugins/cppeditor/cppcodestylepreferences.cpp +++ b/src/plugins/cppeditor/cppcodestylepreferences.cpp @@ -3,6 +3,8 @@ #include "cppcodestylepreferences.h" +using namespace Utils; + namespace CppEditor { CppCodeStylePreferences::CppCodeStylePreferences(QObject *parent) : @@ -67,18 +69,18 @@ void CppCodeStylePreferences::slotCurrentValueChanged(const QVariant &value) emit currentCodeStyleSettingsChanged(value.value<CppCodeStyleSettings>()); } -QVariantMap CppCodeStylePreferences::toMap() const +Store CppCodeStylePreferences::toMap() const { - QVariantMap map = ICodeStylePreferences::toMap(); + Store map = ICodeStylePreferences::toMap(); if (!currentDelegate()) { - const QVariantMap dataMap = m_data.toMap(); + const Store dataMap = m_data.toMap(); for (auto it = dataMap.begin(), end = dataMap.end(); it != end; ++it) map.insert(it.key(), it.value()); } return map; } -void CppCodeStylePreferences::fromMap(const QVariantMap &map) +void CppCodeStylePreferences::fromMap(const Store &map) { ICodeStylePreferences::fromMap(map); if (!currentDelegate()) diff --git a/src/plugins/cppeditor/cppcodestylepreferences.h b/src/plugins/cppeditor/cppcodestylepreferences.h index e496de7c6b4..6ee7d8fc136 100644 --- a/src/plugins/cppeditor/cppcodestylepreferences.h +++ b/src/plugins/cppeditor/cppcodestylepreferences.h @@ -25,8 +25,8 @@ public: // tracks parent hierarchy until currentParentSettings is null CppCodeStyleSettings currentCodeStyleSettings() const; - QVariantMap toMap() const override; - void fromMap(const QVariantMap &map) override; + Utils::Store toMap() const override; + void fromMap(const Utils::Store &map) override; public slots: void setCodeStyleSettings(const CppCodeStyleSettings &data); diff --git a/src/plugins/cppeditor/cppcodestylesettings.cpp b/src/plugins/cppeditor/cppcodestylesettings.cpp index 3a705091411..733f066eaca 100644 --- a/src/plugins/cppeditor/cppcodestylesettings.cpp +++ b/src/plugins/cppeditor/cppcodestylesettings.cpp @@ -16,7 +16,6 @@ #include <cplusplus/Overview.h> #include <utils/qtcassert.h> -#include <utils/settingsutils.h> static const char indentBlockBracesKey[] = "IndentBlockBraces"; static const char indentBlockBodyKey[] = "IndentBlockBody"; @@ -40,13 +39,15 @@ static const char extraPaddingForConditionsIfConfusingAlignKey[] = "ExtraPadding static const char alignAssignmentsKey[] = "AlignAssignments"; static const char shortGetterNameKey[] = "ShortGetterName"; +using namespace Utils; + namespace CppEditor { // ------------------ CppCodeStyleSettingsWidget CppCodeStyleSettings::CppCodeStyleSettings() = default; -QVariantMap CppCodeStyleSettings::toMap() const +Store CppCodeStyleSettings::toMap() const { return { {indentBlockBracesKey, indentBlockBraces}, @@ -73,7 +74,7 @@ QVariantMap CppCodeStyleSettings::toMap() const }; } -void CppCodeStyleSettings::fromMap(const QVariantMap &map) +void CppCodeStyleSettings::fromMap(const Store &map) { indentBlockBraces = map.value(indentBlockBracesKey, indentBlockBraces).toBool(); indentBlockBody = map.value(indentBlockBodyKey, indentBlockBody).toBool(); @@ -128,6 +129,9 @@ bool CppCodeStyleSettings::equals(const CppCodeStyleSettings &rhs) const && extraPaddingForConditionsIfConfusingAlign == rhs.extraPaddingForConditionsIfConfusingAlign && alignAssignments == rhs.alignAssignments && preferGetterNameWithoutGetPrefix == rhs.preferGetterNameWithoutGetPrefix +#ifdef WITH_TESTS + && forceFormatting == rhs.forceFormatting +#endif ; } @@ -158,7 +162,7 @@ CppCodeStyleSettings CppCodeStyleSettings::currentProjectCodeStyle() CppCodeStyleSettings CppCodeStyleSettings::currentGlobalCodeStyle() { - CppCodeStylePreferences *cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); + CppCodeStylePreferences *cppCodeStylePreferences = CppToolsSettings::cppCodeStyle(); QTC_ASSERT(cppCodeStylePreferences, return CppCodeStyleSettings()); return cppCodeStylePreferences->currentCodeStyleSettings(); @@ -185,8 +189,7 @@ TextEditor::TabSettings CppCodeStyleSettings::currentProjectTabSettings() TextEditor::TabSettings CppCodeStyleSettings::currentGlobalTabSettings() { - CppCodeStylePreferences *cppCodeStylePreferences - = CppToolsSettings::instance()->cppCodeStyle(); + CppCodeStylePreferences *cppCodeStylePreferences = CppToolsSettings::cppCodeStyle(); QTC_ASSERT(cppCodeStylePreferences, return TextEditor::TabSettings()); return cppCodeStylePreferences->currentTabSettings(); diff --git a/src/plugins/cppeditor/cppcodestylesettings.h b/src/plugins/cppeditor/cppcodestylesettings.h index 08f1dc1ff26..8369df099e8 100644 --- a/src/plugins/cppeditor/cppcodestylesettings.h +++ b/src/plugins/cppeditor/cppcodestylesettings.h @@ -5,9 +5,7 @@ #include "cppeditor_global.h" -#include <QVariantMap> - -#include <optional> +#include <utils/store.h> namespace CPlusPlus { class Overview; } namespace TextEditor { class TabSettings; } @@ -62,8 +60,12 @@ public: // CppEditor/QuickFixSetting. Remove in 4.16 bool preferGetterNameWithoutGetPrefix = true; - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); +#ifdef WITH_TESTS + bool forceFormatting = false; +#endif + + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const CppCodeStyleSettings &rhs) const; bool operator==(const CppCodeStyleSettings &s) const { return equals(s); } diff --git a/src/plugins/cppeditor/cppcodestylesettingspage.cpp b/src/plugins/cppeditor/cppcodestylesettingspage.cpp index 84573bdeed5..383efef58df 100644 --- a/src/plugins/cppeditor/cppcodestylesettingspage.cpp +++ b/src/plugins/cppeditor/cppcodestylesettingspage.cpp @@ -11,8 +11,13 @@ #include "cpppointerdeclarationformatter.h" #include "cpptoolssettings.h" -#include <coreplugin/icore.h> #include <cppeditor/cppeditorconstants.h> + +#include <cplusplus/Overview.h> +#include <cplusplus/pp.h> + +#include <extensionsystem/pluginmanager.h> + #include <texteditor/codestyleeditor.h> #include <texteditor/displaysettings.h> #include <texteditor/fontsettings.h> @@ -23,14 +28,10 @@ #include <texteditor/tabsettingswidget.h> #include <texteditor/textdocument.h> #include <texteditor/texteditorsettings.h> + #include <utils/layoutbuilder.h> #include <utils/qtcassert.h> -#include <cplusplus/Overview.h> -#include <cplusplus/pp.h> - -#include <extensionsystem/pluginmanager.h> - #include <QCheckBox> #include <QTabWidget> #include <QTextBlock> @@ -76,8 +77,7 @@ static void applyRefactorings(QTextDocument *textDocument, TextEditorWidget *edi Utils::ChangeSet change = formatter.format(cppDocument->translationUnit()->ast()); // Apply change - QTextCursor cursor(textDocument); - change.apply(&cursor); + change.apply(textDocument); } // ------------------ CppCodeStyleSettingsWidget @@ -469,7 +469,7 @@ void CppCodeStylePreferencesWidget::updatePreview() { CppCodeStylePreferences *cppCodeStylePreferences = m_preferences ? m_preferences - : CppToolsSettings::instance()->cppCodeStyle(); + : CppToolsSettings::cppCodeStyle(); const CppCodeStyleSettings ccss = cppCodeStylePreferences->currentCodeStyleSettings(); const TabSettings ts = cppCodeStylePreferences->currentTabSettings(); QtStyleCodeFormatter formatter(ts, ccss); @@ -568,8 +568,7 @@ class CppCodeStyleSettingsPageWidget : public Core::IOptionsPageWidget public: CppCodeStyleSettingsPageWidget() { - CppCodeStylePreferences *originalCodeStylePreferences = CppToolsSettings::instance() - ->cppCodeStyle(); + CppCodeStylePreferences *originalCodeStylePreferences = CppToolsSettings::cppCodeStyle(); m_pageCppCodeStylePreferences = new CppCodeStylePreferences(); m_pageCppCodeStylePreferences->setDelegatingPool( originalCodeStylePreferences->delegatingPool()); @@ -589,20 +588,18 @@ public: void apply() final { - QSettings *s = Core::ICore::settings(); - - CppCodeStylePreferences *originalCppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); + CppCodeStylePreferences *originalCppCodeStylePreferences = CppToolsSettings::cppCodeStyle(); if (originalCppCodeStylePreferences->codeStyleSettings() != m_pageCppCodeStylePreferences->codeStyleSettings()) { originalCppCodeStylePreferences->setCodeStyleSettings(m_pageCppCodeStylePreferences->codeStyleSettings()); - originalCppCodeStylePreferences->toSettings(QLatin1String(CppEditor::Constants::CPP_SETTINGS_ID), s); + originalCppCodeStylePreferences->toSettings(CppEditor::Constants::CPP_SETTINGS_ID); } if (originalCppCodeStylePreferences->tabSettings() != m_pageCppCodeStylePreferences->tabSettings()) { originalCppCodeStylePreferences->setTabSettings(m_pageCppCodeStylePreferences->tabSettings()); - originalCppCodeStylePreferences->toSettings(QLatin1String(CppEditor::Constants::CPP_SETTINGS_ID), s); + originalCppCodeStylePreferences->toSettings(CppEditor::Constants::CPP_SETTINGS_ID); } if (originalCppCodeStylePreferences->currentDelegate() != m_pageCppCodeStylePreferences->currentDelegate()) { originalCppCodeStylePreferences->setCurrentDelegate(m_pageCppCodeStylePreferences->currentDelegate()); - originalCppCodeStylePreferences->toSettings(QLatin1String(CppEditor::Constants::CPP_SETTINGS_ID), s); + originalCppCodeStylePreferences->toSettings(CppEditor::Constants::CPP_SETTINGS_ID); } m_codeStyleEditor->apply(); diff --git a/src/plugins/cppeditor/cppcompletion_test.cpp b/src/plugins/cppeditor/cppcompletion_test.cpp index 1e212dde609..a61f0707ec3 100644 --- a/src/plugins/cppeditor/cppcompletion_test.cpp +++ b/src/plugins/cppeditor/cppcompletion_test.cpp @@ -135,8 +135,7 @@ public: { Utils::ChangeSet change; change.insert(m_position, QLatin1String(text)); - QTextCursor cursor(m_textDocument); - change.apply(&cursor); + change.apply(m_textDocument); m_position += text.length(); } diff --git a/src/plugins/cppeditor/cppcompletionassist.cpp b/src/plugins/cppeditor/cppcompletionassist.cpp index 4865f3dfedc..82d15774330 100644 --- a/src/plugins/cppeditor/cppcompletionassist.cpp +++ b/src/plugins/cppeditor/cppcompletionassist.cpp @@ -20,6 +20,7 @@ #include <texteditor/texteditorsettings.h> #include <texteditor/completionsettings.h> +#include <utils/algorithm.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <utils/textutils.h> @@ -416,7 +417,7 @@ std::unique_ptr<AssistInterface> InternalCompletionAssistProvider::createAssistI BuiltinEditorDocumentParser::get(filePath), languageFeatures, reason, - CppModelManager::instance()->workingCopy()); + CppModelManager::workingCopy()); } // ----------------- @@ -1459,9 +1460,8 @@ bool InternalCppCompletionAssistProcessor::globalCompletion(Scope *currentScope) QSet<ClassOrNamespace *> processed; for (; currentBinding; currentBinding = currentBinding->parent()) { - if (processed.contains(currentBinding)) + if (!Utils::insert(processed, currentBinding)) break; - processed.insert(currentBinding); const QList<ClassOrNamespace*> usings = currentBinding->usings(); for (ClassOrNamespace* u : usings) @@ -1598,10 +1598,9 @@ void InternalCppCompletionAssistProcessor::completeNamespace(ClassOrNamespace *b while (!bindingsToVisit.isEmpty()) { ClassOrNamespace *binding = bindingsToVisit.takeFirst(); - if (!binding || bindingsVisited.contains(binding)) + if (!binding || !Utils::insert(bindingsVisited, binding)) continue; - bindingsVisited.insert(binding); bindingsToVisit += binding->usings(); QList<Scope *> scopesToVisit; @@ -1619,11 +1618,9 @@ void InternalCppCompletionAssistProcessor::completeNamespace(ClassOrNamespace *b while (!scopesToVisit.isEmpty()) { Scope *scope = scopesToVisit.takeFirst(); - if (!scope || scopesVisited.contains(scope)) + if (!scope || !Utils::insert(scopesVisited, scope)) continue; - scopesVisited.insert(scope); - for (Scope::iterator it = scope->memberBegin(); it != scope->memberEnd(); ++it) { Symbol *member = *it; addCompletionItem(member); @@ -1640,10 +1637,9 @@ void InternalCppCompletionAssistProcessor::completeClass(ClassOrNamespace *b, bo while (!bindingsToVisit.isEmpty()) { ClassOrNamespace *binding = bindingsToVisit.takeFirst(); - if (!binding || bindingsVisited.contains(binding)) + if (!binding || !Utils::insert(bindingsVisited, binding)) continue; - bindingsVisited.insert(binding); bindingsToVisit += binding->usings(); QList<Scope *> scopesToVisit; @@ -1663,11 +1659,9 @@ void InternalCppCompletionAssistProcessor::completeClass(ClassOrNamespace *b, bo while (!scopesToVisit.isEmpty()) { Scope *scope = scopesToVisit.takeFirst(); - if (!scope || scopesVisited.contains(scope)) + if (!scope || !Utils::insert(scopesVisited, scope)) continue; - scopesVisited.insert(scope); - if (staticLookup) addCompletionItem(scope, InjectedClassNameOrder); // add a completion item for the injected class name. @@ -1737,9 +1731,7 @@ bool InternalCppCompletionAssistProcessor::completeQtMethod(const QList<LookupIt todo.append(b); while (!todo.isEmpty()) { ClassOrNamespace *binding = todo.takeLast(); - if (!processed.contains(binding)) { - processed.insert(binding); - + if (Utils::insert(processed, binding)) { const QList<Symbol *> symbols = binding->symbols(); for (Symbol *s : symbols) if (Class *clazz = s->asClass()) @@ -1862,11 +1854,9 @@ void InternalCppCompletionAssistProcessor::addMacros_helper(const Snapshot &snap { Document::Ptr doc = snapshot.document(filePath); - if (!doc || processed->contains(doc->filePath())) + if (!doc || !Utils::insert(*processed, doc->filePath())) return; - processed->insert(doc->filePath()); - const QList<Document::Include> includes = doc->resolvedIncludes(); for (const Document::Include &i : includes) addMacros_helper(snapshot, i.resolvedFileName(), processed, definedMacros); @@ -2086,7 +2076,7 @@ void CppCompletionAssistInterface::getCppSpecifics() const m_gotCppSpecifics = true; if (m_parser) { - m_parser->update({CppModelManager::instance()->workingCopy(), + m_parser->update({CppModelManager::workingCopy(), nullptr, Utils::Language::Cxx, false}); diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 19d1dee42b4..2cab76ee493 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.cpp +++ b/src/plugins/cppeditor/cppdoxygen_test.cpp @@ -8,6 +8,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/documentmodel.h> +#include <texteditor/commentssettings.h> #include <QCoreApplication> #include <QDebug> @@ -20,6 +21,7 @@ namespace { typedef QByteArray _; } using CppEditor::Tests::TemporaryDir; using CppEditor::Tests::TestCase; using CppEditor::Internal::Tests::VerifyCleanCppModelManager; +using namespace TextEditor; using namespace Utils; @@ -27,6 +29,19 @@ namespace CppEditor { namespace Internal { namespace Tests { +class SettingsInjector +{ +public: + SettingsInjector(const CommentsSettings::Data &tempSettings) + { + CommentsSettings::setData(tempSettings); + } + ~SettingsInjector() { CommentsSettings::setData(m_oldSettings); } + +private: + const CommentsSettings::Data m_oldSettings = CommentsSettings::data(); +}; + void DoxygenTest::initTestCase() { verifyCleanState(); @@ -39,8 +54,6 @@ void DoxygenTest::cleanTestCase() void DoxygenTest::cleanup() { - if (oldSettings) - CppToolsSettings::instance()->setCommentsSettings(*oldSettings); QVERIFY(Core::EditorManager::closeAllEditors(false)); QVERIFY(TestCase::garbageCollectGlobalSnapshot()); } @@ -49,7 +62,9 @@ void DoxygenTest::testBasic_data() { QTest::addColumn<QByteArray>("given"); QTest::addColumn<QByteArray>("expected"); + QTest::addColumn<int>("commandPrefix"); + using CommandPrefix = CommentsSettings::CommandPrefix; QTest::newRow("qt_style") << _( "bool preventFolding;\n" "/*!|\n" @@ -59,8 +74,29 @@ void DoxygenTest::testBasic_data() "/*!\n" " * \\brief a\n" " */\n" + "int a;\n") << int(CommandPrefix::Auto); + + QTest::newRow("qt_style_settings_override") << _( + "bool preventFolding;\n" + "/*!|\n" "int a;\n" - ); + ) << _( + "bool preventFolding;\n" + "/*!\n" + " * @brief a\n" + " */\n" + "int a;\n") << int(CommandPrefix::At); + + QTest::newRow("qt_style_settings_override_redundant") << _( + "bool preventFolding;\n" + "/*!|\n" + "int a;\n" + ) << _( + "bool preventFolding;\n" + "/*!\n" + " * \\brief a\n" + " */\n" + "int a;\n") << int(CommandPrefix::Backslash); QTest::newRow("qt_style_cursor_before_existing_comment") << _( "bool preventFolding;\n" @@ -74,8 +110,7 @@ void DoxygenTest::testBasic_data() " * \n" " * \\brief something\n" " */\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("qt_style_continuation") << _( "bool preventFolding;\n" @@ -89,8 +124,7 @@ void DoxygenTest::testBasic_data() " * \\brief a\n" " * \n" " */\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("java_style") << _( "bool preventFolding;\n" @@ -101,8 +135,29 @@ void DoxygenTest::testBasic_data() "/**\n" " * @brief a\n" " */\n" + "int a;\n") << int(CommandPrefix::Auto); + + QTest::newRow("java_style_settings_override") << _( + "bool preventFolding;\n" + "/**|\n" "int a;\n" - ); + ) << _( + "bool preventFolding;\n" + "/**\n" + " * \\brief a\n" + " */\n" + "int a;\n") << int(CommandPrefix::Backslash); + + QTest::newRow("java_style_settings_override_redundant") << _( + "bool preventFolding;\n" + "/**|\n" + "int a;\n" + ) << _( + "bool preventFolding;\n" + "/**\n" + " * @brief a\n" + " */\n" + "int a;\n") << int(CommandPrefix::At); QTest::newRow("java_style_continuation") << _( "bool preventFolding;\n" @@ -116,8 +171,7 @@ void DoxygenTest::testBasic_data() " * @brief a\n" " * \n" " */\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleA") << _( "bool preventFolding;\n" @@ -128,8 +182,7 @@ void DoxygenTest::testBasic_data() "///\n" "/// \\brief a\n" "///\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleB") << _( "bool preventFolding;\n" @@ -140,8 +193,7 @@ void DoxygenTest::testBasic_data() "//!\n" "//! \\brief a\n" "//!\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleA_continuation") << _( "bool preventFolding;\n" @@ -155,8 +207,7 @@ void DoxygenTest::testBasic_data() "/// \\brief a\n" "/// \n" "///\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleB_continuation") << _( "bool preventFolding;\n" @@ -170,8 +221,7 @@ void DoxygenTest::testBasic_data() "//! \\brief a\n" "//! \n" "//!\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); /// test cpp style doxygen comment when inside a indented scope QTest::newRow("cpp_styleA_indented") << _( @@ -183,8 +233,7 @@ void DoxygenTest::testBasic_data() " ///\n" " /// \\brief a\n" " ///\n" - " int a;\n" - ); + " int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleB_indented") << _( " bool preventFolding;\n" @@ -195,8 +244,7 @@ void DoxygenTest::testBasic_data() " //!\n" " //! \\brief a\n" " //!\n" - " int a;\n" - ); + " int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleA_indented_preserve_mixed_indention_continuation") << _( "\t bool preventFolding;\n" @@ -206,8 +254,7 @@ void DoxygenTest::testBasic_data() "\t bool preventFolding;\n" "\t /// \\brief a\n" "\t /// \n" - "\t int a;\n" - ); + "\t int a;\n") << int(CommandPrefix::Auto); /// test cpp style doxygen comment continuation when inside a indented scope QTest::newRow("cpp_styleA_indented_continuation") << _( @@ -222,8 +269,7 @@ void DoxygenTest::testBasic_data() " /// \\brief a\n" " /// \n" " ///\n" - " int a;\n" - ); + " int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleB_indented_continuation") << _( " bool preventFolding;\n" @@ -237,8 +283,7 @@ void DoxygenTest::testBasic_data() " //! \\brief a\n" " //! \n" " //!\n" - " int a;\n" - ); + " int a;\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleA_corner_case") << _( "bool preventFolding;\n" @@ -248,8 +293,7 @@ void DoxygenTest::testBasic_data() "bool preventFolding;\n" "///\n" "void d(); ///\n" - "\n" - ); + "\n") << int(CommandPrefix::Auto); QTest::newRow("cpp_styleB_corner_case") << _( "bool preventFolding;\n" @@ -259,8 +303,7 @@ void DoxygenTest::testBasic_data() "bool preventFolding;\n" "//!\n" "void d(); //!\n" - "\n" - ); + "\n") << int(CommandPrefix::Auto); QTest::newRow("noContinuationForExpressionAndComment1") << _( "bool preventFolding;\n" @@ -268,8 +311,7 @@ void DoxygenTest::testBasic_data() ) << _( "bool preventFolding;\n" "*foo //\n" - "\n" - ); + "\n") << int(CommandPrefix::Auto); QTest::newRow("noContinuationForExpressionAndComment2") << _( "bool preventFolding;\n" @@ -277,8 +319,7 @@ void DoxygenTest::testBasic_data() ) << _( "bool preventFolding;\n" "*foo /*\n" - " \n" - ); + " \n") << int(CommandPrefix::Auto); QTest::newRow("withMacroFromDocumentBeforeFunction") << _( "#define API\n" @@ -289,8 +330,7 @@ void DoxygenTest::testBasic_data() "/**\n" " * @brief f\n" " */\n" - "API void f();\n" - ); + "API void f();\n") << int(CommandPrefix::Auto); QTest::newRow("withAccessSpecifierBeforeFunction") << _( "class C {\n" @@ -303,8 +343,7 @@ void DoxygenTest::testBasic_data() " * @brief f\n" " */\n" " public: void f();\n" - "};\n" - ); + "};\n") << int(CommandPrefix::Auto); QTest::newRow("classTemplate") << _( "bool preventFolding;\n" @@ -317,8 +356,7 @@ void DoxygenTest::testBasic_data() " * @brief The C class\n" " */\n" "template<typename T> class C {\n" - "};\n" - ); + "};\n") << int(CommandPrefix::Auto); QTest::newRow("continuation_after_text_in_first_line") << _( "bool preventFolding;\n" @@ -330,8 +368,7 @@ void DoxygenTest::testBasic_data() "/*! leading comment\n" " * \n" " */\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); QTest::newRow("continuation_after_extra_indent") << _( "bool preventFolding;\n" @@ -345,14 +382,19 @@ void DoxygenTest::testBasic_data() " * cont\n" " * \n" " */\n" - "int a;\n" - ); + "int a;\n") << int(CommandPrefix::Auto); } void DoxygenTest::testBasic() { QFETCH(QByteArray, given); QFETCH(QByteArray, expected); + QFETCH(int, commandPrefix); + + CommentsSettings::Data settings = CommentsSettings::data(); + settings.commandPrefix = static_cast<CommentsSettings::CommandPrefix>(commandPrefix); + const SettingsInjector injector(settings); + runTest(given, expected); } @@ -372,7 +414,7 @@ void DoxygenTest::testWithMacroFromHeaderBeforeFunction() const CppTestDocument headerDocumentDefiningMacro("header.h", "#define API\n"); - runTest(given, expected, /*settings=*/ 0, {headerDocumentDefiningMacro}); + runTest(given, expected, {headerDocumentDefiningMacro}); } void DoxygenTest::testNoLeadingAsterisks_data() @@ -393,11 +435,12 @@ void DoxygenTest::testNoLeadingAsterisks() QFETCH(QByteArray, given); QFETCH(QByteArray, expected); - TextEditor::CommentsSettings injection; - injection.m_enableDoxygen = true; - injection.m_leadingAsterisks = false; + TextEditor::CommentsSettings::Data injection; + injection.enableDoxygen = true; + injection.leadingAsterisks = false; + const SettingsInjector injector(injection); - runTest(given, expected, &injection); + runTest(given, expected); } void DoxygenTest::verifyCleanState() const @@ -410,7 +453,6 @@ void DoxygenTest::verifyCleanState() const /// The '|' in the input denotes the cursor position. void DoxygenTest::runTest(const QByteArray &original, const QByteArray &expected, - TextEditor::CommentsSettings *settings, const TestDocuments &includedHeaderDocuments) { // Write files to disk @@ -433,12 +475,6 @@ void DoxygenTest::runTest(const QByteArray &original, QVERIFY(TestCase::openCppEditor(testDocument.filePath(), &testDocument.m_editor, &testDocument.m_editorWidget)); - if (settings) { - auto *cts = CppToolsSettings::instance(); - oldSettings.reset(new TextEditor::CommentsSettings(cts->commentsSettings())); - cts->setCommentsSettings(*settings); - } - // We want to test documents that start with a comment. By default, the // editor will fold the very first comment it encounters, assuming // it is a license header. Currently unfoldAll() does not work as diff --git a/src/plugins/cppeditor/cppdoxygen_test.h b/src/plugins/cppeditor/cppdoxygen_test.h index 8a323a526c9..512d37a03ef 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.h +++ b/src/plugins/cppeditor/cppdoxygen_test.h @@ -5,8 +5,6 @@ #include "cpptoolstestcase.h" -#include <texteditor/commentssettings.h> - #include <QObject> #include <QScopedPointer> @@ -36,10 +34,7 @@ private: void verifyCleanState() const; void runTest(const QByteArray &original, const QByteArray &expected, - TextEditor::CommentsSettings *settings = 0, const TestDocuments &includedHeaderDocuments = TestDocuments()); - - QScopedPointer<TextEditor::CommentsSettings> oldSettings; }; } // namespace Tests diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index b7d2aea0314..160fd70aee2 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -4,7 +4,7 @@ QtcPlugin { name: "CppEditor" Depends { name: "Qt.widgets" } - Depends { condition: project.withAutotests; name: "Qt.testlib" } + Depends { condition: project.withPluginTests; name: "Qt.testlib" } Depends { name: "CPlusPlus" } Depends { name: "Utils" } @@ -13,8 +13,6 @@ QtcPlugin { Depends { name: "TextEditor" } Depends { name: "ProjectExplorer" } - Depends { name: "app_version_header" } - pluginTestDepends: [ "QmakeProjectManager", "QbsProjectManager", @@ -235,7 +233,7 @@ QtcPlugin { Group { name: "TestCase" - condition: qtc.testsEnabled || project.withAutotests + condition: qtc.withPluginTests || qtc.withAutotests files: [ "cpptoolstestcase.cpp", "cpptoolstestcase.h", @@ -267,6 +265,8 @@ QtcPlugin { "cpppointerdeclarationformatter_test.h", "cppquickfix_test.cpp", "cppquickfix_test.h", + "cpprenaming_test.cpp", + "cpprenaming_test.h", "cppsourceprocessor_test.cpp", "cppsourceprocessor_test.h", "cppsourceprocessertesthelper.cpp", diff --git a/src/plugins/cppeditor/cppeditorconstants.h b/src/plugins/cppeditor/cppeditorconstants.h index 179c3b3b9b0..3e3c02dc434 100644 --- a/src/plugins/cppeditor/cppeditorconstants.h +++ b/src/plugins/cppeditor/cppeditorconstants.h @@ -9,12 +9,13 @@ namespace CppEditor { namespace Constants { const char M_CONTEXT[] = "CppEditor.ContextMenu"; -const char G_CONTEXT_FIRST[] = "CppEditor.GFirst"; +const char G_SYMBOL[] = "CppEditor.GSymbol"; +const char G_SELECTION[] = "CppEditor.GSelection"; +const char G_FILE[] = "CppEditor.GFile"; +const char G_GLOBAL[] = "CppEditor.GGlobal"; const char CPPEDITOR_ID[] = "CppEditor.C++Editor"; const char SWITCH_DECLARATION_DEFINITION[] = "CppEditor.SwitchDeclarationDefinition"; const char OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT[] = "CppEditor.OpenDeclarationDefinitionInNextSplit"; -const char FOLLOW_SYMBOL_TO_TYPE[] = "TextEditor.FollowSymbolToType"; -const char FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolToTypeInNextSplit"; const char OPEN_PREPROCESSOR_DIALOG[] = "CppEditor.OpenPreprocessorDialog"; const char MULTIPLE_PARSE_CONTEXTS_AVAILABLE[] = "CppEditor.MultipleParseContextsAvailable"; const char M_REFACTORING_MENU_INSERTION_POINT[] = "CppEditor.RefactorGroup"; diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 0398674941c..16cae978a82 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -41,11 +41,6 @@ using namespace Utils; namespace CppEditor { namespace Internal { -static CppEditor::CppModelManager *mm() -{ - return CppEditor::CppModelManager::instance(); -} - enum { processDocumentIntervalInMs = 150 }; class CppEditorDocumentHandleImpl : public CppEditorDocumentHandle @@ -55,12 +50,12 @@ public: : m_cppEditorDocument(cppEditorDocument) , m_registrationFilePath(cppEditorDocument->filePath().toString()) { - mm()->registerCppEditorDocument(this); + CppModelManager::registerCppEditorDocument(this); } ~CppEditorDocumentHandleImpl() override { - mm()->unregisterCppEditorDocument(m_registrationFilePath); + CppModelManager::unregisterCppEditorDocument(m_registrationFilePath); } FilePath filePath() const override { return m_cppEditorDocument->filePath(); } @@ -101,7 +96,7 @@ CppEditorDocument::CppEditorDocument() connect(this, &IDocument::filePathChanged, this, &CppEditorDocument::onFilePathChanged); - connect(mm(), &CppModelManager::diagnosticsChanged, + connect(CppModelManager::instance(), &CppModelManager::diagnosticsChanged, this, &CppEditorDocument::onDiagnosticsChanged); connect(&m_parseContextModel, &ParseContextModel::preferredParseContextChanged, @@ -187,7 +182,7 @@ void CppEditorDocument::onMimeTypeChanged() const QString &mt = mimeType(); m_isObjCEnabled = (mt == QLatin1String(Constants::OBJECTIVE_C_SOURCE_MIMETYPE) || mt == QLatin1String(Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE)); - m_completionAssistProvider = mm()->completionAssistProvider(); + m_completionAssistProvider = CppModelManager::completionAssistProvider(); initializeTimer(); } @@ -215,7 +210,7 @@ void CppEditorDocument::reparseWithPreferredParseContext(const QString &parseCon setPreferredParseContext(parseContextId); // Remember the setting - const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString(); + const Key key = Constants::PREFERRED_PARSE_CONTEXT + keyFromString(filePath().toString()); Core::SessionManager::setValue(key, parseContextId); // Reprocess @@ -282,7 +277,7 @@ void CppEditorDocument::applyPreferredParseContextFromSettings() if (filePath().isEmpty()) return; - const QString key = Constants::PREFERRED_PARSE_CONTEXT + filePath().toString(); + const Key key = Constants::PREFERRED_PARSE_CONTEXT + keyFromString(filePath().toString()); const QString parseContextId = Core::SessionManager::value(key).toString(); setPreferredParseContext(parseContextId); @@ -293,7 +288,7 @@ void CppEditorDocument::applyExtraPreprocessorDirectivesFromSettings() if (filePath().isEmpty()) return; - const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + filePath().toString(); + const Key key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + keyFromString(filePath().toString()); const QByteArray directives = Core::SessionManager::value(key).toString().toUtf8(); setExtraPreprocessorDirectives(directives); @@ -380,7 +375,7 @@ void CppEditorDocument::updateOutline() { CPlusPlus::Document::Ptr document; if (!usesClangd()) - document = CppModelManager::instance()->snapshot().document(filePath()); + document = CppModelManager::snapshot().document(filePath()); m_overviewModel.update(document); } @@ -392,9 +387,8 @@ QFuture<CursorInfo> CppEditorDocument::cursorInfo(const CursorInfoParams ¶ms BaseEditorDocumentProcessor *CppEditorDocument::processor() { if (!m_processor) { - m_processor.reset(mm()->createEditorDocumentProcessor(this)); - connect(m_processor.data(), - &BaseEditorDocumentProcessor::projectPartInfoUpdated, + m_processor.reset(CppModelManager::createEditorDocumentProcessor(this)); + connect(m_processor.data(), &BaseEditorDocumentProcessor::projectPartInfoUpdated, this, [this](const ProjectPartInfo &info) { const bool hasProjectPart = !(info.hints & ProjectPartInfo::IsFallbackMatch); minimizableInfoBars()->setInfoVisible(NO_PROJECT_CONFIGURATION, !hasProjectPart); @@ -403,15 +397,15 @@ BaseEditorDocumentProcessor *CppEditorDocument::processor() const bool isProjectFile = info.hints & ProjectPartInfo::IsFromProjectMatch; showHideInfoBarAboutMultipleParseContexts(isAmbiguous && isProjectFile); }); - connect(m_processor.data(), &BaseEditorDocumentProcessor::codeWarningsUpdated, - [this] (unsigned revision, - const QList<QTextEdit::ExtraSelection> selections, - const TextEditor::RefactorMarkers &refactorMarkers) { + connect(m_processor.data(), &BaseEditorDocumentProcessor::codeWarningsUpdated, this, + [this](unsigned revision, + const QList<QTextEdit::ExtraSelection> selections, + const TextEditor::RefactorMarkers &refactorMarkers) { emit codeWarningsUpdated(revision, selections, refactorMarkers); }); connect(m_processor.data(), &BaseEditorDocumentProcessor::ifdefedOutBlocksUpdated, this, &CppEditorDocument::ifdefedOutBlocksUpdated); - connect(m_processor.data(), &BaseEditorDocumentProcessor::cppDocumentUpdated, + connect(m_processor.data(), &BaseEditorDocumentProcessor::cppDocumentUpdated, this, [this](const CPlusPlus::Document::Ptr document) { // Update syntax highlighter auto *highlighter = qobject_cast<CppHighlighter *>(syntaxHighlighter()); @@ -467,7 +461,7 @@ bool CppEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, if (!editedRanges.empty()) { QTextCursor cursor(document()); cursor.joinPreviousEditBlock(); - indenter()->format(editedRanges); + indenter()->format(editedRanges, Indenter::FormattingMode::Forced); cursor.endEditBlock(); } @@ -484,16 +478,16 @@ bool CppEditorDocument::usesClangd() const return CppModelManager::usesClangd(this); } -void CppEditorDocument::onDiagnosticsChanged(const QString &fileName, const QString &kind) +void CppEditorDocument::onDiagnosticsChanged(const FilePath &fileName, const QString &kind) { - if (FilePath::fromString(fileName) != filePath()) + if (fileName != filePath()) return; TextMarks removedMarks = marks(); const Utils::Id category = Utils::Id::fromString(kind); - for (const auto &diagnostic : mm()->diagnosticMessages()) { + for (const auto &diagnostic : CppModelManager::diagnosticMessages()) { if (diagnostic.filePath() == filePath()) { auto it = std::find_if(std::begin(removedMarks), std::end(removedMarks), diff --git a/src/plugins/cppeditor/cppeditordocument.h b/src/plugins/cppeditor/cppeditordocument.h index 734c64b665f..7a0a8f43cc5 100644 --- a/src/plugins/cppeditor/cppeditordocument.h +++ b/src/plugins/cppeditor/cppeditordocument.h @@ -76,7 +76,7 @@ private: void onAboutToReload(); void onReloadFinished(); - void onDiagnosticsChanged(const QString &fileName, const QString &kind); + void onDiagnosticsChanged(const Utils::FilePath &fileName, const QString &kind); void reparseWithPreferredParseContext(const QString &id); diff --git a/src/plugins/cppeditor/cppeditoroutline.cpp b/src/plugins/cppeditor/cppeditoroutline.cpp index a34d2543612..dec97045639 100644 --- a/src/plugins/cppeditor/cppeditoroutline.cpp +++ b/src/plugins/cppeditor/cppeditoroutline.cpp @@ -79,7 +79,7 @@ CppEditorOutline::CppEditorOutline(CppEditorWidget *editorWidget) m_proxyModel->setSourceModel(m_model); // Set up proxy model - if (CppToolsSettings::instance()->sortedEditorDocumentOutline()) + if (CppToolsSettings::sortedEditorDocumentOutline()) m_proxyModel->sort(0, Qt::AscendingOrder); else m_proxyModel->sort(-1, Qt::AscendingOrder); // don't sort yet, but set column for sortedOutline() @@ -125,7 +125,7 @@ QWidget *CppEditorOutline::widget() const QSharedPointer<CPlusPlus::Document> getDocument(const Utils::FilePath &filePath) { - const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); return snapshot.document(filePath); } diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 5791a5cb62a..13d62e4cee2 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -42,6 +42,7 @@ #include "cppmodelmanager_test.h" #include "cpppointerdeclarationformatter_test.h" #include "cppquickfix_test.h" +#include "cpprenaming_test.h" #include "cppsourceprocessor_test.h" #include "cppuseselections_test.h" #include "fileandtokenactions_test.h" @@ -72,6 +73,7 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectmanager.h> #include <projectexplorer/projectpanelfactory.h> #include <projectexplorer/projecttree.h> @@ -150,6 +152,7 @@ public: | TextEditorActionHandler::UnCommentSelection | TextEditorActionHandler::UnCollapseAll | TextEditorActionHandler::FollowSymbolUnderCursor + | TextEditorActionHandler::FollowTypeUnderCursor | TextEditorActionHandler::RenameSymbol | TextEditorActionHandler::FindUsage); } @@ -166,7 +169,6 @@ public: delete m_clangdSettingsPage; } - void initialize() { m_codeModelSettings.fromSettings(ICore::settings()); } void onTaskStarted(Utils::Id type); void onAllTasksFinished(Utils::Id type); void inspectCppCodeModel(); @@ -181,8 +183,6 @@ public: QPointer<CppCodeModelInspectorDialog> m_cppCodeModelInspectorDialog; - QPointer<TextEditor::BaseTextEditor> m_currentEditor; - CppOutlineWidgetFactory m_cppOutlineWidgetFactory; CppTypeHierarchyFactory m_cppTypeHierarchyFactory; CppIncludeHierarchyFactory m_cppIncludeHierarchyFactory; @@ -200,7 +200,7 @@ public: }; static CppEditorPlugin *m_instance = nullptr; -static QHash<QString, QString> m_headerSourceMapping; +static QHash<FilePath, FilePath> m_headerSourceMapping; CppEditorPlugin::CppEditorPlugin() { @@ -228,142 +228,144 @@ CppQuickFixAssistProvider *CppEditorPlugin::quickFixProvider() const void CppEditorPlugin::initialize() { d = new CppEditorPluginPrivate; - d->initialize(); + d->m_codeModelSettings.fromSettings(ICore::settings()); - CppModelManager::instance()->registerJsExtension(); + CppModelManager::registerJsExtension(); ExtensionSystem::PluginManager::addObject(&d->m_cppProjectUpdaterFactory); - // Menus - ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); - ActionContainer *mcpptools = ActionManager::createMenu(Constants::M_TOOLS_CPP); - QMenu *menu = mcpptools->menu(); - menu->setTitle(Tr::tr("&C++")); - menu->setEnabled(true); - mtools->addMenu(mcpptools); - - // Actions - Context context(Constants::CPPEDITOR_ID); - - QAction *switchAction = new QAction(Tr::tr("Switch Header/Source"), this); - Command *command = ActionManager::registerAction(switchAction, Constants::SWITCH_HEADER_SOURCE, context, true); - command->setDefaultKeySequence(QKeySequence(Qt::Key_F4)); - mcpptools->addAction(command); - connect(switchAction, &QAction::triggered, - this, [] { CppModelManager::switchHeaderSource(false); }); - - QAction *openInNextSplitAction = new QAction(Tr::tr("Open Corresponding Header/Source in Next Split"), this); - command = ActionManager::registerAction(openInNextSplitAction, Constants::OPEN_HEADER_SOURCE_IN_NEXT_SPLIT, context, true); - command->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost() - ? Tr::tr("Meta+E, F4") - : Tr::tr("Ctrl+E, F4"))); - mcpptools->addAction(command); - connect(openInNextSplitAction, &QAction::triggered, - this, [] { CppModelManager::switchHeaderSource(true); }); - - MacroExpander *expander = globalMacroExpander(); - expander->registerVariable("Cpp:LicenseTemplate", - Tr::tr("The license template."), - []() { return CppEditorPlugin::licenseTemplate(); }); - expander->registerFileVariables("Cpp:LicenseTemplatePath", - Tr::tr("The configured path to the license template"), - []() { return CppEditorPlugin::licenseTemplatePath(); }); - - expander->registerVariable( - "Cpp:PragmaOnce", - Tr::tr("Insert \"#pragma once\" instead of \"#ifndef\" include guards into header file"), - [] { return usePragmaOnce() ? QString("true") : QString(); }); - - const auto quickFixSettingsPanelFactory = new ProjectPanelFactory; - quickFixSettingsPanelFactory->setPriority(100); - quickFixSettingsPanelFactory->setId(Constants::QUICK_FIX_PROJECT_PANEL_ID); - quickFixSettingsPanelFactory->setDisplayName(Tr::tr(Constants::QUICK_FIX_SETTINGS_DISPLAY_NAME)); - quickFixSettingsPanelFactory->setCreateWidgetFunction([](Project *project) { - return new CppQuickFixProjectSettingsWidget(project); - }); - ProjectPanelFactory::registerFactory(quickFixSettingsPanelFactory); + setupMenus(); + registerVariables(); + createCppQuickFixes(); + registerTests(); SnippetProvider::registerGroup(Constants::CPP_SNIPPETS_GROUP_ID, Tr::tr("C++", "SnippetProvider"), &decorateCppEditor); - createCppQuickFixes(); + connect(ProgressManager::instance(), &ProgressManager::taskStarted, + d, &CppEditorPluginPrivate::onTaskStarted); + connect(ProgressManager::instance(), &ProgressManager::allTasksFinished, + d, &CppEditorPluginPrivate::onAllTasksFinished); +} - ActionContainer *contextMenu = ActionManager::createMenu(Constants::M_CONTEXT); - contextMenu->insertGroup(Core::Constants::G_DEFAULT_ONE, Constants::G_CONTEXT_FIRST); +void CppEditorPlugin::extensionsInitialized() +{ + setupProjectPanels(); - Command *cmd; - ActionContainer *cppToolsMenu = ActionManager::actionContainer(Constants::M_TOOLS_CPP); - ActionContainer *touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); + d->m_fileSettings.fromSettings(ICore::settings()); + d->m_fileSettings.addMimeInitializer(); - cmd = ActionManager::command(Constants::SWITCH_HEADER_SOURCE); - cmd->setTouchBarText(Tr::tr("Header/Source", "text on macOS touch bar")); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION); + // Add the hover handler factories here instead of in initialize() + // so that the Clang Code Model has a chance to hook in. + d->m_cppEditorFactory.addHoverHandler(CppModelManager::createHoverHandler()); + d->m_cppEditorFactory.addHoverHandler(new ColorPreviewHoverHandler); + d->m_cppEditorFactory.addHoverHandler(new ResourcePreviewHoverHandler); - cmd = ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR); + FileIconProvider::registerIconOverlayForMimeType( + creatorTheme()->imageFile(Theme::IconOverlayCppSource, + ProjectExplorer::Constants::FILEOVERLAY_CPP), + Constants::CPP_SOURCE_MIMETYPE); + FileIconProvider::registerIconOverlayForMimeType( + creatorTheme()->imageFile(Theme::IconOverlayCSource, + ProjectExplorer::Constants::FILEOVERLAY_C), + Constants::C_SOURCE_MIMETYPE); + FileIconProvider::registerIconOverlayForMimeType( + creatorTheme()->imageFile(Theme::IconOverlayCppHeader, + ProjectExplorer::Constants::FILEOVERLAY_H), + Constants::CPP_HEADER_MIMETYPE); +} + +static void insertIntoMenus(const QList<ActionContainer *> &menus, + const std::function<void(ActionContainer *)> &func) +{ + for (ActionContainer * const menu : menus) + func(menu); +} + +static void addActionToMenus(const QList<ActionContainer *> &menus, Command *cmd, Id id) +{ + insertIntoMenus(menus, [cmd, id](ActionContainer *menu) { menu->addAction(cmd, id); }); +} + +void CppEditorPlugin::setupMenus() +{ + ActionContainer * const cppToolsMenu = ActionManager::createMenu(Constants::M_TOOLS_CPP); + cppToolsMenu->menu()->setTitle(Tr::tr("&C++")); + cppToolsMenu->menu()->setEnabled(true); + ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(cppToolsMenu); + ActionContainer * const contextMenu = ActionManager::createMenu(Constants::M_CONTEXT); + + insertIntoMenus({cppToolsMenu, contextMenu}, [](ActionContainer *menu) { + menu->insertGroup(Core::Constants::G_DEFAULT_ONE, Constants::G_SYMBOL); + menu->insertGroup(Core::Constants::G_DEFAULT_ONE, Constants::G_SELECTION); + menu->insertGroup(Core::Constants::G_DEFAULT_ONE, Constants::G_FILE); + menu->insertGroup(Core::Constants::G_DEFAULT_ONE, Constants::G_GLOBAL); + menu->addSeparator(Constants::G_SELECTION); + menu->addSeparator(Constants::G_FILE); + menu->addSeparator(Constants::G_GLOBAL); + }); + + addPerSymbolActions(); + addActionsForSelections(); + addPerFileActions(); + addGlobalActions(); + + ActionContainer * const toolsDebug + = ActionManager::actionContainer(Core::Constants::M_TOOLS_DEBUG); + QAction * const inspectCppCodeModel = new QAction(Tr::tr("Inspect C++ Code Model..."), this); + Command * const cmd = ActionManager::registerAction(inspectCppCodeModel, + Constants::INSPECT_CPP_CODEMODEL); + cmd->setDefaultKeySequence({useMacShortcuts ? Tr::tr("Meta+Shift+F12") + : Tr::tr("Ctrl+Shift+F12")}); + connect(inspectCppCodeModel, &QAction::triggered, + d, &CppEditorPluginPrivate::inspectCppCodeModel); + toolsDebug->addAction(cmd); +} + +void CppEditorPlugin::addPerSymbolActions() +{ + const QList<ActionContainer *> menus{ActionManager::actionContainer(Constants::M_TOOLS_CPP), + ActionManager::actionContainer(Constants::M_CONTEXT)}; + ActionContainer * const touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); + const Context context(Constants::CPPEDITOR_ID); + const auto addSymbolActionToMenus = [&menus](Command *cmd) { + addActionToMenus(menus, cmd, Constants::G_SYMBOL); + }; + + Command *cmd = ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR); cmd->setTouchBarText(Tr::tr("Follow", "text on macOS touch bar")); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); + addSymbolActionToMenus(cmd); touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION); + addSymbolActionToMenus(ActionManager::command( + TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT)); + addSymbolActionToMenus(ActionManager::command( + TextEditor::Constants::FOLLOW_SYMBOL_TO_TYPE)); + addSymbolActionToMenus(ActionManager::command( + TextEditor::Constants::FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT)); - QAction *openPreprocessorDialog = new QAction(Tr::tr("Additional Preprocessor Directives..."), this); - cmd = ActionManager::registerAction(openPreprocessorDialog, - Constants::OPEN_PREPROCESSOR_DIALOG, context); - cmd->setDefaultKeySequence(QKeySequence()); - connect(openPreprocessorDialog, &QAction::triggered, this, &CppEditorPlugin::showPreProcessorDialog); - cppToolsMenu->addAction(cmd); - - QAction *switchDeclarationDefinition = new QAction(Tr::tr("Switch Between Function Declaration/Definition"), this); + QAction * const switchDeclarationDefinition + = new QAction(Tr::tr("Switch Between Function Declaration/Definition"), this); cmd = ActionManager::registerAction(switchDeclarationDefinition, - Constants::SWITCH_DECLARATION_DEFINITION, context, true); + Constants::SWITCH_DECLARATION_DEFINITION, context, true); cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Shift+F2"))); cmd->setTouchBarText(Tr::tr("Decl/Def", "text on macOS touch bar")); connect(switchDeclarationDefinition, &QAction::triggered, this, &CppEditorPlugin::switchDeclarationDefinition); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); + addSymbolActionToMenus(cmd); touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION); - cmd = ActionManager::command(TextEditor::Constants::FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT); - cppToolsMenu->addAction(cmd); - - QAction *openDeclarationDefinitionInNextSplit = - new QAction(Tr::tr("Open Function Declaration/Definition in Next Split"), this); + QAction * const openDeclarationDefinitionInNextSplit = + new QAction(Tr::tr("Open Function Declaration/Definition in Next Split"), this); cmd = ActionManager::registerAction(openDeclarationDefinitionInNextSplit, - Constants::OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT, context, true); + Constants::OPEN_DECLARATION_DEFINITION_IN_NEXT_SPLIT, + context, true); cmd->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost() - ? Tr::tr("Meta+E, Shift+F2") - : Tr::tr("Ctrl+E, Shift+F2"))); + ? Tr::tr("Meta+E, Shift+F2") + : Tr::tr("Ctrl+E, Shift+F2"))); connect(openDeclarationDefinitionInNextSplit, &QAction::triggered, this, &CppEditorPlugin::openDeclarationDefinitionInNextSplit); - cppToolsMenu->addAction(cmd); + addSymbolActionToMenus(cmd); - QAction * const followSymbolToType = new QAction(Tr::tr("Follow Symbol Under Cursor to Type"), - this); - cmd = ActionManager::registerAction(followSymbolToType, Constants::FOLLOW_SYMBOL_TO_TYPE, - context, true); - cmd->setDefaultKeySequence(QKeySequence(Tr::tr("Ctrl+Shift+F2"))); - connect(followSymbolToType, &QAction::triggered, this, []{ - if (CppEditorWidget *editorWidget = currentCppEditorWidget()) - editorWidget->followSymbolToType(false); - }); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); - QAction * const followSymbolToTypeInNextSplit = - new QAction(Tr::tr("Follow Symbol to Type in Next Split"), this); - cmd = ActionManager::registerAction(followSymbolToTypeInNextSplit, - Constants::FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT, context, true); - cmd->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost() - ? Tr::tr("Meta+E, Ctrl+Shift+F2") - : Tr::tr("Ctrl+E, Ctrl+Shift+F2"))); - connect(followSymbolToTypeInNextSplit, &QAction::triggered, this, []{ - if (CppEditorWidget *editorWidget = currentCppEditorWidget()) - editorWidget->followSymbolToType(true); - }); - cppToolsMenu->addAction(cmd); - - cmd = ActionManager::command(TextEditor::Constants::FIND_USAGES); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); + addSymbolActionToMenus(ActionManager::command(TextEditor::Constants::FIND_USAGES)); d->m_findRefsCategorizedAction = new QAction(Tr::tr("Find References With Access Type"), this); cmd = ActionManager::registerAction(d->m_findRefsCategorizedAction, @@ -375,100 +377,204 @@ void CppEditorPlugin::initialize() codeModelSettings()->setCategorizeFindReferences(false); } }); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); + addSymbolActionToMenus(cmd); + + addSymbolActionToMenus(ActionManager::command(TextEditor::Constants::RENAME_SYMBOL)); + + d->m_openTypeHierarchyAction = new QAction(Tr::tr("Open Type Hierarchy"), this); + cmd = ActionManager::registerAction(d->m_openTypeHierarchyAction, + Constants::OPEN_TYPE_HIERARCHY, context); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts + ? Tr::tr("Meta+Shift+T") : Tr::tr("Ctrl+Shift+T"))); + connect(d->m_openTypeHierarchyAction, &QAction::triggered, + this, &CppEditorPlugin::openTypeHierarchy); + addSymbolActionToMenus(cmd); + + addSymbolActionToMenus(ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY)); + + // Refactoring sub-menu + Command *sep = menus.last()->addSeparator(Constants::G_SYMBOL); + sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)); +} + +void CppEditorPlugin::addActionsForSelections() +{ + const QList<ActionContainer *> menus{ActionManager::actionContainer(Constants::M_TOOLS_CPP), + ActionManager::actionContainer(Constants::M_CONTEXT)}; + const auto addSelectionActionToMenus = [&menus](Command *cmd) { + addActionToMenus(menus, cmd, Constants::G_SELECTION); + }; + addSelectionActionToMenus(ActionManager::command(TextEditor::Constants::AUTO_INDENT_SELECTION)); + addSelectionActionToMenus(ActionManager::command(TextEditor::Constants::UN_COMMENT_SELECTION)); +} + +void CppEditorPlugin::addPerFileActions() +{ + const QList<ActionContainer *> menus{ActionManager::actionContainer(Constants::M_TOOLS_CPP), + ActionManager::actionContainer(Constants::M_CONTEXT)}; + ActionContainer * const touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); + const auto addFileActionToMenus = [&menus](Command *cmd) { + addActionToMenus(menus, cmd, Constants::G_FILE); + }; + const Context context(Constants::CPPEDITOR_ID); + + QAction * const switchAction = new QAction(Tr::tr("Switch Header/Source"), this); + Command *cmd = ActionManager::registerAction(switchAction, Constants::SWITCH_HEADER_SOURCE, + context, true); + cmd->setTouchBarText(Tr::tr("Header/Source", "text on macOS touch bar")); + addFileActionToMenus(cmd); + touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_NAVIGATION); + cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F4)); + connect(switchAction, &QAction::triggered, + this, [] { CppModelManager::switchHeaderSource(false); }); + + QAction * const switchInNextSplitAction + = new QAction(Tr::tr("Open Corresponding Header/Source in Next Split"), this); + cmd = ActionManager::registerAction( + switchInNextSplitAction, Constants::OPEN_HEADER_SOURCE_IN_NEXT_SPLIT, context, true); + cmd->setDefaultKeySequence(QKeySequence(HostOsInfo::isMacHost() + ? Tr::tr("Meta+E, F4") + : Tr::tr("Ctrl+E, F4"))); + addFileActionToMenus(cmd); + connect(switchInNextSplitAction, &QAction::triggered, + this, [] { CppModelManager::switchHeaderSource(true); }); + + QAction * const openPreprocessorDialog + = new QAction(Tr::tr("Additional Preprocessor Directives..."), this); + cmd = ActionManager::registerAction(openPreprocessorDialog, + Constants::OPEN_PREPROCESSOR_DIALOG, context); + cmd->setDefaultKeySequence(QKeySequence()); + connect(openPreprocessorDialog, &QAction::triggered, + this, &CppEditorPlugin::showPreProcessorDialog); + addFileActionToMenus(cmd); QAction * const showPreprocessedAction = new QAction(Tr::tr("Show Preprocessed Source"), this); - command = ActionManager::registerAction(showPreprocessedAction, - Constants::SHOW_PREPROCESSED_FILE, context); - mcpptools->addAction(command); - contextMenu->addAction(command, Constants::G_CONTEXT_FIRST); + cmd = ActionManager::registerAction(showPreprocessedAction, + Constants::SHOW_PREPROCESSED_FILE, context); + addFileActionToMenus(cmd); connect(showPreprocessedAction, &QAction::triggered, this, [] { CppModelManager::showPreprocessedFile(false); }); QAction * const showPreprocessedInSplitAction = new QAction - (Tr::tr("Show Preprocessed Source in Next Split"), this); - command = ActionManager::registerAction(showPreprocessedInSplitAction, - Constants::SHOW_PREPROCESSED_FILE_SPLIT, context); - mcpptools->addAction(command); + (Tr::tr("Show Preprocessed Source in Next Split"), this); + cmd = ActionManager::registerAction(showPreprocessedInSplitAction, + Constants::SHOW_PREPROCESSED_FILE_SPLIT, context); + addFileActionToMenus(cmd); connect(showPreprocessedInSplitAction, &QAction::triggered, this, [] { CppModelManager::showPreprocessedFile(true); }); - QAction *const findUnusedFunctionsAction = new QAction(Tr::tr("Find Unused Functions"), this); - command = ActionManager::registerAction(findUnusedFunctionsAction, - "CppTools.FindUnusedFunctions"); - mcpptools->addAction(command); + QAction * const foldCommentsAction = new QAction(Tr::tr("Fold All Comment Blocks"), this); + cmd = ActionManager::registerAction(foldCommentsAction, + "CppTools.FoldCommentBlocks", context); + addFileActionToMenus(cmd); + connect(foldCommentsAction, &QAction::triggered, this, [] { CppModelManager::foldComments(); }); + QAction * const unfoldCommentsAction = new QAction(Tr::tr("Unfold All Comment Blocks"), this); + cmd = ActionManager::registerAction(unfoldCommentsAction, + "CppTools.UnfoldCommentBlocks", context); + addFileActionToMenus(cmd); + connect(unfoldCommentsAction, &QAction::triggered, + this, [] { CppModelManager::unfoldComments(); }); + + d->m_openIncludeHierarchyAction = new QAction(Tr::tr("Open Include Hierarchy"), this); + cmd = ActionManager::registerAction(d->m_openIncludeHierarchyAction, + Constants::OPEN_INCLUDE_HIERARCHY, context); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts + ? Tr::tr("Meta+Shift+I") : Tr::tr("Ctrl+Shift+I"))); + connect(d->m_openIncludeHierarchyAction, &QAction::triggered, + this, &CppEditorPlugin::openIncludeHierarchy); + addFileActionToMenus(cmd); +} + +void CppEditorPlugin::addGlobalActions() +{ + const QList<ActionContainer *> menus{ActionManager::actionContainer(Constants::M_TOOLS_CPP), + ActionManager::actionContainer(Constants::M_CONTEXT)}; + const auto addGlobalActionToMenus = [&menus](Command *cmd) { + addActionToMenus(menus, cmd, Constants::G_GLOBAL); + }; + + QAction * const findUnusedFunctionsAction = new QAction(Tr::tr("Find Unused Functions"), this); + Command *cmd = ActionManager::registerAction(findUnusedFunctionsAction, + "CppTools.FindUnusedFunctions"); + addGlobalActionToMenus(cmd); connect(findUnusedFunctionsAction, &QAction::triggered, this, [] { CppModelManager::findUnusedFunctions({}); }); - QAction *const findUnusedFunctionsInSubProjectAction + + QAction * const findUnusedFunctionsInSubProjectAction = new QAction(Tr::tr("Find Unused C/C++ Functions"), this); - command = ActionManager::registerAction(findUnusedFunctionsInSubProjectAction, - "CppTools.FindUnusedFunctionsInSubProject"); + cmd = ActionManager::registerAction(findUnusedFunctionsInSubProjectAction, + "CppTools.FindUnusedFunctionsInSubProject"); for (ActionContainer *const projectContextMenu : {ActionManager::actionContainer(ProjectExplorer::Constants::M_SUBPROJECTCONTEXT), ActionManager::actionContainer(ProjectExplorer::Constants::M_PROJECTCONTEXT)}) { projectContextMenu->addSeparator(ProjectExplorer::Constants::G_PROJECT_TREE); - projectContextMenu->addAction(command, ProjectExplorer::Constants::G_PROJECT_TREE); + projectContextMenu->addAction(cmd, ProjectExplorer::Constants::G_PROJECT_TREE); } connect(findUnusedFunctionsInSubProjectAction, &QAction::triggered, this, [] { if (const Node *const node = ProjectTree::currentNode(); node && node->asFolderNode()) CppModelManager::findUnusedFunctions(node->directory()); }); - d->m_openTypeHierarchyAction = new QAction(Tr::tr("Open Type Hierarchy"), this); - cmd = ActionManager::registerAction(d->m_openTypeHierarchyAction, Constants::OPEN_TYPE_HIERARCHY, context); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+T") : Tr::tr("Ctrl+Shift+T"))); - connect(d->m_openTypeHierarchyAction, &QAction::triggered, this, &CppEditorPlugin::openTypeHierarchy); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); - - d->m_openIncludeHierarchyAction = new QAction(Tr::tr("Open Include Hierarchy"), this); - cmd = ActionManager::registerAction(d->m_openIncludeHierarchyAction, Constants::OPEN_INCLUDE_HIERARCHY, context); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+I") : Tr::tr("Ctrl+Shift+I"))); - connect(d->m_openIncludeHierarchyAction, &QAction::triggered, this, &CppEditorPlugin::openIncludeHierarchy); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); - - cmd = ActionManager::command(TextEditor::Constants::OPEN_CALL_HIERARCHY); - contextMenu->addAction(cmd, Constants::G_CONTEXT_FIRST); - cppToolsMenu->addAction(cmd); - - // Refactoring sub-menu - Command *sep = contextMenu->addSeparator(); - sep->action()->setObjectName(QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)); - contextMenu->addSeparator(); - cppToolsMenu->addAction(ActionManager::command(TextEditor::Constants::RENAME_SYMBOL)); - - // Update context in global context - cppToolsMenu->addSeparator(Core::Constants::G_DEFAULT_THREE); - d->m_reparseExternallyChangedFiles = new QAction(Tr::tr("Reparse Externally Changed Files"), this); - cmd = ActionManager::registerAction(d->m_reparseExternallyChangedFiles, Constants::UPDATE_CODEMODEL); - CppModelManager *cppModelManager = CppModelManager::instance(); + d->m_reparseExternallyChangedFiles = new QAction(Tr::tr("Reparse Externally Changed Files"), + this); + cmd = ActionManager::registerAction(d->m_reparseExternallyChangedFiles, + Constants::UPDATE_CODEMODEL); connect(d->m_reparseExternallyChangedFiles, &QAction::triggered, - cppModelManager, &CppModelManager::updateModifiedSourceFiles); - cppToolsMenu->addAction(cmd, Core::Constants::G_DEFAULT_THREE); + CppModelManager::instance(), &CppModelManager::updateModifiedSourceFiles); + addGlobalActionToMenus(cmd); +} - ActionContainer *toolsDebug = ActionManager::actionContainer(Core::Constants::M_TOOLS_DEBUG); - QAction *inspectCppCodeModel = new QAction(Tr::tr("Inspect C++ Code Model..."), this); - cmd = ActionManager::registerAction(inspectCppCodeModel, Constants::INSPECT_CPP_CODEMODEL); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+F12") : Tr::tr("Ctrl+Shift+F12"))); - connect(inspectCppCodeModel, &QAction::triggered, d, &CppEditorPluginPrivate::inspectCppCodeModel); - toolsDebug->addAction(cmd); +void CppEditorPlugin::setupProjectPanels() +{ + const auto quickFixSettingsPanelFactory = new ProjectPanelFactory; + quickFixSettingsPanelFactory->setPriority(100); + quickFixSettingsPanelFactory->setId(Constants::QUICK_FIX_PROJECT_PANEL_ID); + quickFixSettingsPanelFactory->setDisplayName(Tr::tr(Constants::QUICK_FIX_SETTINGS_DISPLAY_NAME)); + quickFixSettingsPanelFactory->setCreateWidgetFunction([](Project *project) { + return new CppQuickFixProjectSettingsWidget(project); + }); + ProjectPanelFactory::registerFactory(quickFixSettingsPanelFactory); - contextMenu->addSeparator(context); + const auto fileNamesPanelFactory = new ProjectPanelFactory; + fileNamesPanelFactory->setPriority(99); + fileNamesPanelFactory->setDisplayName(Tr::tr("C++ File Naming")); + fileNamesPanelFactory->setCreateWidgetFunction([](Project *project) { + return new CppFileSettingsForProjectWidget(project); + }); + ProjectPanelFactory::registerFactory(fileNamesPanelFactory); - cmd = ActionManager::command(TextEditor::Constants::AUTO_INDENT_SELECTION); - contextMenu->addAction(cmd); + if (CppModelManager::isClangCodeModelActive()) { + d->m_clangdSettingsPage = new ClangdSettingsPage; + const auto clangdPanelFactory = new ProjectPanelFactory; + clangdPanelFactory->setPriority(100); + clangdPanelFactory->setDisplayName(Tr::tr("Clangd")); + clangdPanelFactory->setCreateWidgetFunction([](Project *project) { + return new ClangdProjectSettingsWidget(project); + }); + ProjectPanelFactory::registerFactory(clangdPanelFactory); + } +} - cmd = ActionManager::command(TextEditor::Constants::UN_COMMENT_SELECTION); - contextMenu->addAction(cmd); +void CppEditorPlugin::registerVariables() +{ + MacroExpander * const expander = globalMacroExpander(); - connect(ProgressManager::instance(), &ProgressManager::taskStarted, - d, &CppEditorPluginPrivate::onTaskStarted); - connect(ProgressManager::instance(), &ProgressManager::allTasksFinished, - d, &CppEditorPluginPrivate::onAllTasksFinished); + // TODO: Per-project variants of these three? + expander->registerVariable("Cpp:LicenseTemplate", + Tr::tr("The license template."), + []() { return CppEditorPlugin::licenseTemplate(nullptr); }); + expander->registerFileVariables("Cpp:LicenseTemplatePath", + Tr::tr("The configured path to the license template"), + []() { return CppEditorPlugin::licenseTemplatePath(nullptr); }); + expander->registerVariable( + "Cpp:PragmaOnce", + Tr::tr("Insert \"#pragma once\" instead of \"#ifndef\" include guards into header file"), + [] { return usePragmaOnce(nullptr) ? QString("true") : QString(); }); +} +void CppEditorPlugin::registerTests() +{ #ifdef WITH_TESTS addTest<CodegenTest>(); addTest<CompilerOptionsBuilderTest>(); @@ -495,47 +601,11 @@ void CppEditorPlugin::initialize() addTest<Tests::IncludeHierarchyTest>(); addTest<Tests::InsertVirtualMethodsTest>(); addTest<Tests::QuickfixTest>(); + addTest<Tests::GlobalRenamingTest>(); addTest<Tests::SelectionsTest>(); #endif } -void CppEditorPlugin::extensionsInitialized() -{ - d->m_fileSettings.fromSettings(ICore::settings()); - if (!d->m_fileSettings.applySuffixesToMimeDB()) - qWarning("Unable to apply cpp suffixes to mime database (cpp mime types not found).\n"); - - if (CppModelManager::instance()->isClangCodeModelActive()) { - d->m_clangdSettingsPage = new ClangdSettingsPage; - const auto clangdPanelFactory = new ProjectPanelFactory; - clangdPanelFactory->setPriority(100); - clangdPanelFactory->setDisplayName(Tr::tr("Clangd")); - clangdPanelFactory->setCreateWidgetFunction([](Project *project) { - return new ClangdProjectSettingsWidget(project); - }); - ProjectPanelFactory::registerFactory(clangdPanelFactory); - } - - // Add the hover handler factories here instead of in initialize() - // so that the Clang Code Model has a chance to hook in. - d->m_cppEditorFactory.addHoverHandler(CppModelManager::instance()->createHoverHandler()); - d->m_cppEditorFactory.addHoverHandler(new ColorPreviewHoverHandler); - d->m_cppEditorFactory.addHoverHandler(new ResourcePreviewHoverHandler); - - FileIconProvider::registerIconOverlayForMimeType( - creatorTheme()->imageFile(Theme::IconOverlayCppSource, - ProjectExplorer::Constants::FILEOVERLAY_CPP), - Constants::CPP_SOURCE_MIMETYPE); - FileIconProvider::registerIconOverlayForMimeType( - creatorTheme()->imageFile(Theme::IconOverlayCSource, - ProjectExplorer::Constants::FILEOVERLAY_C), - Constants::C_SOURCE_MIMETYPE); - FileIconProvider::registerIconOverlayForMimeType( - creatorTheme()->imageFile(Theme::IconOverlayCppHeader, - ProjectExplorer::Constants::FILEOVERLAY_H), - Constants::CPP_HEADER_MIMETYPE); -} - void CppEditorPlugin::switchDeclarationDefinition() { if (CppEditorWidget *editorWidget = currentCppEditorWidget()) @@ -614,39 +684,19 @@ void CppEditorPlugin::clearHeaderSourceCache() m_headerSourceMapping.clear(); } -FilePath CppEditorPlugin::licenseTemplatePath() +FilePath CppEditorPlugin::licenseTemplatePath(Project *project) { - return FilePath::fromString(m_instance->d->m_fileSettings.licenseTemplatePath); + return FilePath::fromString(fileSettings(project).licenseTemplatePath); } -QString CppEditorPlugin::licenseTemplate() +QString CppEditorPlugin::licenseTemplate(Project *project) { - return CppFileSettings::licenseTemplate(); + return fileSettings(project).licenseTemplate(); } -bool CppEditorPlugin::usePragmaOnce() +bool CppEditorPlugin::usePragmaOnce(Project *project) { - return m_instance->d->m_fileSettings.headerPragmaOnce; -} - -const QStringList &CppEditorPlugin::headerSearchPaths() -{ - return m_instance->d->m_fileSettings.headerSearchPaths; -} - -const QStringList &CppEditorPlugin::sourceSearchPaths() -{ - return m_instance->d->m_fileSettings.sourceSearchPaths; -} - -const QStringList &CppEditorPlugin::headerPrefixes() -{ - return m_instance->d->m_fileSettings.headerPrefixes; -} - -const QStringList &CppEditorPlugin::sourcePrefixes() -{ - return m_instance->d->m_fileSettings.sourcePrefixes; + return fileSettings(project).headerPragmaOnce; } CppCodeModelSettings *CppEditorPlugin::codeModelSettings() @@ -654,30 +704,34 @@ CppCodeModelSettings *CppEditorPlugin::codeModelSettings() return &d->m_codeModelSettings; } -CppFileSettings *CppEditorPlugin::fileSettings() +CppFileSettings CppEditorPlugin::fileSettings(Project *project) { - return &instance()->d->m_fileSettings; + if (!project) + return instance()->d->m_fileSettings; + return CppFileSettingsForProject(project).settings(); } -static QStringList findFilesInProject(const QString &name, const Project *project) +#ifdef WITH_TESTS +void CppEditorPlugin::setGlobalFileSettings(const CppFileSettings &settings) +{ + instance()->d->m_fileSettings = settings; +} +#endif + +static FilePaths findFilesInProject(const QStringList &names, const Project *project, + FileType fileType) { if (debug) - qDebug() << Q_FUNC_INFO << name << project; + qDebug() << Q_FUNC_INFO << names << project; if (!project) - return QStringList(); + return {}; - QString pattern = QString(1, QLatin1Char('/')); - pattern += name; - const QStringList projectFiles - = transform(project->files(Project::AllFiles), &FilePath::toString); - const QStringList::const_iterator pcend = projectFiles.constEnd(); - QStringList candidateList; - for (QStringList::const_iterator it = projectFiles.constBegin(); it != pcend; ++it) { - if (it->endsWith(pattern, HostOsInfo::fileNameCaseSensitivity())) - candidateList.append(*it); - } - return candidateList; + const auto filter = [&](const Node *n) { + const auto fn = n->asFileNode(); + return fn && fn->fileType() == fileType && names.contains(fn->filePath().fileName()); + }; + return project->files(filter); } // Return the suffixes that should be checked when trying to find a @@ -704,7 +758,7 @@ static QStringList matchingCandidateSuffixes(ProjectFile::Kind kind) case ProjectFile::OpenCLSource: return mimeTypeForName(Constants::CPP_HEADER_MIMETYPE).suffixes(); default: - return QStringList(); + return {}; } } @@ -721,11 +775,12 @@ static QStringList baseNameWithAllSuffixes(const QString &baseName, const QStrin return result; } -static QStringList baseNamesWithAllPrefixes(const QStringList &baseNames, bool isHeader) +static QStringList baseNamesWithAllPrefixes(const CppFileSettings &settings, + const QStringList &baseNames, bool isHeader) { QStringList result; - const QStringList &sourcePrefixes = m_instance->sourcePrefixes(); - const QStringList &headerPrefixes = m_instance->headerPrefixes(); + const QStringList &sourcePrefixes = settings.sourcePrefixes; + const QStringList &headerPrefixes = settings.headerPrefixes; for (const QString &name : baseNames) { for (const QString &prefix : isHeader ? headerPrefixes : sourcePrefixes) { @@ -765,33 +820,31 @@ static int commonFilePathLength(const QString &s1, const QString &s2) return length; } -static FilePath correspondingHeaderOrSourceInProject(const QFileInfo &fileInfo, +static FilePath correspondingHeaderOrSourceInProject(const FilePath &filePath, const QStringList &candidateFileNames, const Project *project, + FileType fileType, CacheUsage cacheUsage) { - QString bestFileName; + const FilePaths projectFiles = findFilesInProject(candidateFileNames, project, fileType); + + // Find the file having the most common path with fileName + FilePath bestFilePath; int compareValue = 0; - const QString filePath = fileInfo.filePath(); - for (const QString &candidateFileName : candidateFileNames) { - const QStringList projectFiles = findFilesInProject(candidateFileName, project); - // Find the file having the most common path with fileName - for (const QString &projectFile : projectFiles) { - int value = commonFilePathLength(filePath, projectFile); - if (value > compareValue) { - compareValue = value; - bestFileName = projectFile; - } + for (const FilePath &projectFile : projectFiles) { + int value = commonFilePathLength(filePath.toString(), projectFile.toString()); + if (value > compareValue) { + compareValue = value; + bestFilePath = projectFile; } } - if (!bestFileName.isEmpty()) { - const QFileInfo candidateFi(bestFileName); - QTC_ASSERT(candidateFi.isFile(), return {}); + if (!bestFilePath.isEmpty()) { + QTC_ASSERT(bestFilePath.isFile(), return {}); if (cacheUsage == CacheUsage::ReadWrite) { - m_headerSourceMapping[fileInfo.absoluteFilePath()] = candidateFi.absoluteFilePath(); - m_headerSourceMapping[candidateFi.absoluteFilePath()] = fileInfo.absoluteFilePath(); + m_headerSourceMapping[filePath] = bestFilePath; + m_headerSourceMapping[bestFilePath] = filePath; } - return FilePath::fromString(candidateFi.absoluteFilePath()); + return bestFilePath; } return {}; @@ -803,22 +856,25 @@ using namespace Internal; FilePath correspondingHeaderOrSource(const FilePath &filePath, bool *wasHeader, CacheUsage cacheUsage) { - const QString fileName = filePath.toString(); - const QFileInfo fi(fileName); - ProjectFile::Kind kind = ProjectFile::classify(fileName); + ProjectFile::Kind kind = ProjectFile::classify(filePath.fileName()); const bool isHeader = ProjectFile::isHeader(kind); if (wasHeader) *wasHeader = isHeader; - if (m_headerSourceMapping.contains(fi.absoluteFilePath())) - return FilePath::fromString(m_headerSourceMapping.value(fi.absoluteFilePath())); + if (const auto it = m_headerSourceMapping.constFind(filePath); + it != m_headerSourceMapping.constEnd()) { + return it.value(); + } + + Project * const projectForFile = ProjectManager::projectForFile(filePath); + const CppFileSettings settings = CppEditorPlugin::fileSettings(projectForFile); if (debug) - qDebug() << Q_FUNC_INFO << fileName << kind; + qDebug() << Q_FUNC_INFO << filePath.fileName() << kind; if (kind == ProjectFile::Unsupported) return {}; - const QString baseName = fi.completeBaseName(); + const QString baseName = filePath.completeBaseName(); const QString privateHeaderSuffix = QLatin1String("_p"); const QStringList suffixes = matchingCandidateSuffixes(kind); @@ -835,51 +891,52 @@ FilePath correspondingHeaderOrSource(const FilePath &filePath, bool *wasHeader, candidateFileNames += baseNameWithAllSuffixes(privateHeaderBaseName, suffixes); } - const QDir absoluteDir = fi.absoluteDir(); + const QDir absoluteDir = filePath.toFileInfo().absoluteDir(); QStringList candidateDirs(absoluteDir.absolutePath()); // If directory is not root, try matching against its siblings - const QStringList searchPaths = isHeader ? m_instance->sourceSearchPaths() - : m_instance->headerSearchPaths(); + const QStringList searchPaths = isHeader ? settings.sourceSearchPaths + : settings.headerSearchPaths; candidateDirs += baseDirWithAllDirectories(absoluteDir, searchPaths); - candidateFileNames += baseNamesWithAllPrefixes(candidateFileNames, isHeader); + candidateFileNames += baseNamesWithAllPrefixes(settings, candidateFileNames, isHeader); // Try to find a file in the same or sibling directories first for (const QString &candidateDir : std::as_const(candidateDirs)) { for (const QString &candidateFileName : std::as_const(candidateFileNames)) { - const QString candidateFilePath = candidateDir + QLatin1Char('/') + candidateFileName; - const QString normalized = FileUtils::normalizedPathName(candidateFilePath); - const QFileInfo candidateFi(normalized); - if (candidateFi.isFile()) { + const FilePath candidateFilePath + = FilePath::fromString(candidateDir + '/' + candidateFileName).normalizedPathName(); + if (candidateFilePath.isFile()) { if (cacheUsage == CacheUsage::ReadWrite) { - m_headerSourceMapping[fi.absoluteFilePath()] = candidateFi.absoluteFilePath(); + m_headerSourceMapping[filePath] = candidateFilePath; if (!isHeader || !baseName.endsWith(privateHeaderSuffix)) - m_headerSourceMapping[candidateFi.absoluteFilePath()] = fi.absoluteFilePath(); + m_headerSourceMapping[candidateFilePath] = filePath; } - return FilePath::fromString(candidateFi.absoluteFilePath()); + return candidateFilePath; } } } // Find files in the current project - Project *currentProject = ProjectTree::currentProject(); + Project *currentProject = projectForFile; + if (!projectForFile) + currentProject = ProjectTree::currentProject(); + const FileType requestedFileType = isHeader ? FileType::Source : FileType::Header; if (currentProject) { - const FilePath path = correspondingHeaderOrSourceInProject(fi, candidateFileNames, - currentProject, cacheUsage); + const FilePath path = correspondingHeaderOrSourceInProject( + filePath, candidateFileNames, currentProject, requestedFileType, cacheUsage); if (!path.isEmpty()) return path; // Find files in other projects } else { - CppModelManager *modelManager = CppModelManager::instance(); - const QList<ProjectInfo::ConstPtr> projectInfos = modelManager->projectInfos(); + const QList<ProjectInfo::ConstPtr> projectInfos = CppModelManager::projectInfos(); for (const ProjectInfo::ConstPtr &projectInfo : projectInfos) { const Project *project = projectForProjectInfo(*projectInfo); if (project == currentProject) continue; // We have already checked the current project. - const FilePath path = correspondingHeaderOrSourceInProject(fi, candidateFileNames, - project, cacheUsage); + const FilePath path = correspondingHeaderOrSourceInProject( + filePath, candidateFileNames, project, requestedFileType, cacheUsage); if (!path.isEmpty()) return path; } diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h index c28ee16b1e1..463455fb655 100644 --- a/src/plugins/cppeditor/cppeditorplugin.h +++ b/src/plugins/cppeditor/cppeditorplugin.h @@ -5,6 +5,7 @@ #include <extensionsystem/iplugin.h> +namespace ProjectExplorer { class Project; } namespace Utils { class FilePath; } namespace CppEditor { @@ -29,14 +30,10 @@ public: CppQuickFixAssistProvider *quickFixProvider() const; - static const QStringList &headerSearchPaths(); - static const QStringList &sourceSearchPaths(); - static const QStringList &headerPrefixes(); - static const QStringList &sourcePrefixes(); static void clearHeaderSourceCache(); - static Utils::FilePath licenseTemplatePath(); - static QString licenseTemplate(); - static bool usePragmaOnce(); + static Utils::FilePath licenseTemplatePath(ProjectExplorer::Project *project); + static QString licenseTemplate(ProjectExplorer::Project *project); + static bool usePragmaOnce(ProjectExplorer::Project *project); void openDeclarationDefinitionInNextSplit(); void openTypeHierarchy(); @@ -46,7 +43,10 @@ public: void switchDeclarationDefinition(); CppCodeModelSettings *codeModelSettings(); - static CppFileSettings *fileSettings(); + static CppFileSettings fileSettings(ProjectExplorer::Project *project); +#ifdef WITH_TESTS + static void setGlobalFileSettings(const CppFileSettings &settings); +#endif signals: void typeHierarchyRequested(); @@ -56,6 +56,15 @@ private: void initialize() override; void extensionsInitialized() override; + void setupMenus(); + void addPerSymbolActions(); + void addActionsForSelections(); + void addPerFileActions(); + void addGlobalActions(); + void setupProjectPanels(); + void registerVariables(); + void registerTests(); + CppEditorPluginPrivate *d = nullptr; }; diff --git a/src/plugins/cppeditor/cppeditorwidget.cpp b/src/plugins/cppeditor/cppeditorwidget.cpp index a6daf681db5..0419ef6b035 100644 --- a/src/plugins/cppeditor/cppeditorwidget.cpp +++ b/src/plugins/cppeditor/cppeditorwidget.cpp @@ -307,8 +307,9 @@ bool handleDoxygenContinuation(QTextCursor &cursor, static bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, const CPlusPlus::Snapshot &snapshot) { - const TextEditor::CommentsSettings &settings = CppToolsSettings::instance()->commentsSettings(); - if (!settings.m_enableDoxygen && !settings.m_leadingAsterisks) + const TextEditor::CommentsSettings::Data &settings + = TextEditorSettings::commentsSettings(editorWidget->textDocument()->filePath()); + if (!settings.enableDoxygen && !settings.leadingAsterisks) return false; if (editorWidget->multiTextCursor().hasMultipleCursors()) @@ -325,7 +326,7 @@ static bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, // enter. If leading asterisk(s) is set we need to write a comment continuation // with those. - if (settings.m_enableDoxygen && cursor.positionInBlock() >= 3) { + if (settings.enableDoxygen && cursor.positionInBlock() >= 3) { const int pos = cursor.position(); if (isStartOfDoxygenComment(cursor)) { QTextDocument *textDocument = editorWidget->document(); @@ -340,9 +341,7 @@ static bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, DoxygenGenerator doxygen; doxygen.setStyle(style); - doxygen.setAddLeadingAsterisks(settings.m_leadingAsterisks); - doxygen.setGenerateBrief(settings.m_generateBrief); - doxygen.setStartComment(false); + doxygen.setSettings(settings); // Move until we reach any possibly meaningful content. while (textDocument->characterAt(cursor.position()).isSpace() @@ -369,8 +368,8 @@ static bool trySplitComment(TextEditor::TextEditorWidget *editorWidget, return handleDoxygenContinuation(cursor, editorWidget, - settings.m_enableDoxygen, - settings.m_leadingAsterisks); + settings.enableDoxygen, + settings.leadingAsterisks); } } // anonymous namespace @@ -383,8 +382,6 @@ public: bool shouldOfferOutline() const { return !CppModelManager::usesClangd(m_cppEditorDocument); } public: - QPointer<CppModelManager> m_modelManager; - CppEditorDocument *m_cppEditorDocument; CppEditorOutline *m_cppEditorOutline = nullptr; @@ -405,8 +402,7 @@ public: }; CppEditorWidgetPrivate::CppEditorWidgetPrivate(CppEditorWidget *q) - : m_modelManager(CppModelManager::instance()) - , m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument())) + : m_cppEditorDocument(qobject_cast<CppEditorDocument *>(q->textDocument())) , m_declDefLinkFinder(new FunctionDeclDefLinkFinder(q)) , m_localRenaming(q) , m_useSelectionsUpdater(q) @@ -457,7 +453,7 @@ void CppEditorWidget::finalizeInitialization() connect(document(), &QTextDocument::contentsChange, &d->m_localRenaming, &CppLocalRenaming::onContentsChangeOfEditorWidgetDocument); - connect(&d->m_localRenaming, &CppLocalRenaming::finished, [this] { + connect(&d->m_localRenaming, &CppLocalRenaming::finished, this, [this] { cppEditorDocument()->recalculateSemanticInfoDetached(); }); connect(&d->m_localRenaming, &CppLocalRenaming::processKeyPressNormally, @@ -482,8 +478,7 @@ void CppEditorWidget::finalizeInitialization() // set up the use highlighitng connect(this, &CppEditorWidget::cursorPositionChanged, this, [this] { - if (!d->m_localRenaming.isActive()) - d->m_useSelectionsUpdater.scheduleUpdate(); + d->m_useSelectionsUpdater.scheduleUpdate(); // Notify selection expander about the changed cursor. d->m_cppSelectionChanger.onCursorPositionChanged(textCursor()); @@ -611,7 +606,7 @@ void CppEditorWidget::findUsages(QTextCursor cursor) // 'this' in cursorInEditor is never used (and must never be used) asynchronously. const CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this, textDocument()}; QPointer<CppEditorWidget> cppEditorWidget = this; - d->m_modelManager->findUsages(cursorInEditor); + CppModelManager::findUsages(cursorInEditor); } void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor cursor) @@ -626,7 +621,7 @@ void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor curso showRenameWarningIfFileIsGenerated(link.targetFilePath); CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this, textDocument()}; QPointer<CppEditorWidget> cppEditorWidget = this; - d->m_modelManager->globalRename(cursorInEditor, replacement); + CppModelManager::globalRename(cursorInEditor, replacement); }; CppModelManager::followSymbol( CursorInEditor{cursor, textDocument()->filePath(), this, textDocument()}, @@ -640,7 +635,7 @@ void CppEditorWidget::renameUsages(const Utils::FilePath &filePath, const QStrin cursor = textCursor(); CursorInEditor cursorInEditor{cursor, filePath, this, textDocument()}; QPointer<CppEditorWidget> cppEditorWidget = this; - d->m_modelManager->globalRename(cursorInEditor, replacement, callback); + CppModelManager::globalRename(cursorInEditor, replacement, callback); } bool CppEditorWidget::selectBlockUp() @@ -695,15 +690,14 @@ bool CppEditorWidget::isWidgetHighlighted(QWidget *widget) namespace { -QList<ProjectPart::ConstPtr> fetchProjectParts(CppModelManager *modelManager, - const Utils::FilePath &filePath) +QList<ProjectPart::ConstPtr> fetchProjectParts(const Utils::FilePath &filePath) { - QList<ProjectPart::ConstPtr> projectParts = modelManager->projectPart(filePath); + QList<ProjectPart::ConstPtr> projectParts = CppModelManager::projectPart(filePath); if (projectParts.isEmpty()) - projectParts = modelManager->projectPartFromDependencies(filePath); + projectParts = CppModelManager::projectPartFromDependencies(filePath); if (projectParts.isEmpty()) - projectParts.append(modelManager->fallbackProjectPart()); + projectParts.append(CppModelManager::fallbackProjectPart()); return projectParts; } @@ -728,10 +722,10 @@ const ProjectPart *findProjectPartForCurrentProject( const ProjectPart *CppEditorWidget::projectPart() const { - if (!d->m_modelManager) + if (!CppModelManager::instance()) return nullptr; - auto projectParts = fetchProjectParts(d->m_modelManager, textDocument()->filePath()); + auto projectParts = fetchProjectParts(textDocument()->filePath()); return findProjectPartForCurrentProject(projectParts, ProjectExplorer::ProjectTree::currentProject()); @@ -816,7 +810,7 @@ QList<QTextEdit::ExtraSelection> sourceLocationsToExtraSelections( selection.cursor = selectAt(cppEditorWidget->textCursor(), sourceLocation.targetLine, - sourceLocation.targetColumn + 1, + sourceLocation.targetColumn, selectionLength); selection.format = textCharFormat; @@ -868,11 +862,11 @@ void CppEditorWidget::renameSymbolUnderCursor() }; viewport()->setCursor(Qt::BusyCursor); - d->m_modelManager->startLocalRenaming(CursorInEditor{textCursor(), - textDocument()->filePath(), - this, textDocument()}, - projPart, - std::move(renameSymbols)); + CppModelManager::startLocalRenaming(CursorInEditor{textCursor(), + textDocument()->filePath(), + this, textDocument()}, + projPart, + std::move(renameSymbols)); } void CppEditorWidget::updatePreprocessorButtonTooltip() @@ -887,7 +881,7 @@ void CppEditorWidget::updatePreprocessorButtonTooltip() void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit) { - if (!d->m_modelManager) + if (!CppModelManager::instance()) return; const CursorInEditor cursor(textCursor(), textDocument()->filePath(), this, textDocument()); @@ -899,20 +893,6 @@ void CppEditorWidget::switchDeclarationDefinition(bool inNextSplit) CppModelManager::switchDeclDef(cursor, std::move(callback)); } -void CppEditorWidget::followSymbolToType(bool inNextSplit) -{ - if (!d->m_modelManager) - return; - - const CursorInEditor cursor(textCursor(), textDocument()->filePath(), this, textDocument()); - const auto callback = [self = QPointer(this), - split = inNextSplit != alwaysOpenLinksInNextSplit()](const Link &link) { - if (self && link.hasValidTarget()) - self->openLink(link, split); - }; - CppModelManager::followSymbolToType(cursor, callback, inNextSplit); -} - bool CppEditorWidget::followUrl(const QTextCursor &cursor, const Utils::LinkHandler &processLinkCallback) { @@ -968,7 +948,7 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor, bool resolveTarget, bool inNextSplit) { - if (!d->m_modelManager) + if (!CppModelManager::instance()) return processLinkCallback(Utils::Link()); if (followUrl(cursor, processLinkCallback)) @@ -1003,11 +983,22 @@ void CppEditorWidget::findLinkAt(const QTextCursor &cursor, } callback(link); }; - CppModelManager::followSymbol( - CursorInEditor{cursor, filePath, this, textDocument()}, - callbackWrapper, - resolveTarget, - inNextSplit); + CppModelManager::followSymbol(CursorInEditor{cursor, filePath, this, textDocument()}, + callbackWrapper, + resolveTarget, + inNextSplit); +} + +void CppEditorWidget::findTypeAt(const QTextCursor &cursor, + const Utils::LinkHandler &processLinkCallback, + bool /*resolveTarget*/, + bool inNextSplit) +{ + if (!CppModelManager::instance()) + return; + + const CursorInEditor cursorInEditor(cursor, textDocument()->filePath(), this, textDocument()); + CppModelManager::followSymbolToType(cursorInEditor, processLinkCallback, inNextSplit); } unsigned CppEditorWidget::documentRevision() const @@ -1058,18 +1049,20 @@ void CppEditorWidget::processKeyNormally(QKeyEvent *e) TextEditorWidget::keyPressEvent(e); } -static void addRefactoringActions(QMenu *menu, std::unique_ptr<AssistInterface> iface) +void CppEditorWidget::addRefactoringActions(QMenu *menu) const { - if (!iface || !menu) + if (!menu) return; - using Processor = QScopedPointer<IAssistProcessor>; - using Proposal = QScopedPointer<IAssistProposal>; - - const Processor processor( - CppEditorPlugin::instance()->quickFixProvider()->createProcessor(iface.get())); - const Proposal proposal(processor->start(std::move(iface))); - if (proposal) { + auto iface = createAssistInterface(QuickFix, ExplicitlyInvoked); + IAssistProcessor * const processor + = textDocument()->quickFixAssistProvider()->createProcessor(iface.get()); + IAssistProposal* const proposal(processor->start(std::move(iface))); + const auto handleProposal = [menu = QPointer(menu), processor](IAssistProposal *proposal) { + QScopedPointer<IAssistProposal> proposalHolder(proposal); + QScopedPointer<IAssistProcessor> processorHolder(processor); + if (!menu || !proposal) + return; auto model = proposal->model().staticCast<GenericProposalModel>(); for (int index = 0; index < model->size(); ++index) { const auto item = static_cast<AssistProposalItem *>(model->proposalItem(index)); @@ -1077,7 +1070,12 @@ static void addRefactoringActions(QMenu *menu, std::unique_ptr<AssistInterface> const QAction *action = menu->addAction(op->description()); QObject::connect(action, &QAction::triggered, menu, [op] { op->perform(); }); } - } + }; + + if (proposal) + handleProposal(proposal); + else + processor->setAsyncCompletionAvailableHandler(handleProposal); } class ProgressIndicatorMenuItem : public QWidgetAction @@ -1095,18 +1093,21 @@ protected: QMenu *CppEditorWidget::createRefactorMenu(QWidget *parent) const { auto *menu = new QMenu(Tr::tr("&Refactor"), parent); - menu->addAction(ActionManager::command(TextEditor::Constants::RENAME_SYMBOL)->action()); + connect(menu, &QMenu::aboutToShow, this, [this, menu] { + menu->disconnect(this); - // ### enable - // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); + // ### enable + // updateSemanticInfo(m_semanticHighlighter->semanticInfo(currentSource())); + + if (!isSemanticInfoValidExceptLocalUses()) + return; - if (isSemanticInfoValidExceptLocalUses()) { d->m_useSelectionsUpdater.abortSchedule(); const CppUseSelectionsUpdater::RunnerInfo runnerInfo = d->m_useSelectionsUpdater.update(); switch (runnerInfo) { case CppUseSelectionsUpdater::RunnerInfo::AlreadyUpToDate: - addRefactoringActions(menu, createAssistInterface(QuickFix, ExplicitlyInvoked)); + addRefactoringActions(menu); break; case CppUseSelectionsUpdater::RunnerInfo::Started: { // Update the refactor menu once we get the results. @@ -1117,7 +1118,7 @@ QMenu *CppEditorWidget::createRefactorMenu(QWidget *parent) const menu, [=] (SemanticInfo::LocalUseMap, bool success) { QTC_CHECK(success); menu->removeAction(progressIndicatorMenuItem); - addRefactoringActions(menu, createAssistInterface(QuickFix, ExplicitlyInvoked)); + addRefactoringActions(menu); }); break; } @@ -1125,7 +1126,7 @@ QMenu *CppEditorWidget::createRefactorMenu(QWidget *parent) const case CppUseSelectionsUpdater::RunnerInfo::Invalid: QTC_CHECK(false && "Unexpected CppUseSelectionsUpdater runner result"); } - } + }); return menu; } @@ -1135,10 +1136,11 @@ static void appendCustomContextMenuActionsAndMenus(QMenu *menu, QMenu *refactorM bool isRefactoringMenuAdded = false; const QMenu *contextMenu = ActionManager::actionContainer(Constants::M_CONTEXT)->menu(); for (QAction *action : contextMenu->actions()) { - menu->addAction(action); if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT)) { isRefactoringMenuAdded = true; menu->addMenu(refactorMenu); + } else { + menu->addAction(action); } } @@ -1228,12 +1230,10 @@ void CppEditorWidget::updateSemanticInfo(const SemanticInfo &semanticInfo, d->m_lastSemanticInfo = semanticInfo; - if (!d->m_localRenaming.isActive()) { - const CppUseSelectionsUpdater::CallType type = updateUseSelectionSynchronously - ? CppUseSelectionsUpdater::CallType::Synchronous - : CppUseSelectionsUpdater::CallType::Asynchronous; - d->m_useSelectionsUpdater.update(type); - } + const CppUseSelectionsUpdater::CallType type + = updateUseSelectionSynchronously ? CppUseSelectionsUpdater::CallType::Synchronous + : CppUseSelectionsUpdater::CallType::Asynchronous; + d->m_useSelectionsUpdater.update(type); // schedule a check for a decl/def link updateFunctionDeclDefLink(); @@ -1269,8 +1269,7 @@ std::unique_ptr<AssistInterface> CppEditorWidget::createAssistInterface(AssistKi return cap->createAssistInterface(textDocument()->filePath(), this, getFeatures(), reason); if (isOldStyleSignalOrSlot()) { - return CppModelManager::instance() - ->completionAssistProvider() + return CppModelManager::completionAssistProvider() ->createAssistInterface(textDocument()->filePath(), this, getFeatures(), reason); } } @@ -1331,7 +1330,7 @@ void CppEditorWidget::updateFunctionDeclDefLinkNow() if (!isSemanticInfoValidExceptLocalUses()) return; - Snapshot snapshot = d->m_modelManager->snapshot(); + Snapshot snapshot = CppModelManager::snapshot(); snapshot.insert(semanticDoc); d->m_declDefLinkFinder->startFindLinkAt(textCursor(), semanticDoc, snapshot); diff --git a/src/plugins/cppeditor/cppeditorwidget.h b/src/plugins/cppeditor/cppeditorwidget.h index e9c0d11275c..e9259f8f4c7 100644 --- a/src/plugins/cppeditor/cppeditorwidget.h +++ b/src/plugins/cppeditor/cppeditorwidget.h @@ -5,6 +5,7 @@ #include "cppeditor_global.h" +#include <texteditor/blockrange.h> #include <texteditor/codeassist/assistenums.h> #include <texteditor/texteditor.h> @@ -56,7 +57,6 @@ public: void selectAll() override; void switchDeclarationDefinition(bool inNextSplit); - void followSymbolToType(bool inNextSplit); void showPreProcessorWidget(); void findUsages() override; @@ -104,6 +104,11 @@ protected: bool resolveTarget = true, bool inNextSplit = false) override; + void findTypeAt(const QTextCursor &cursor, + const Utils::LinkHandler &processLinkCallback, + bool resolveTarget = true, + bool inNextSplit = false) override; + void slotCodeStyleSettingsChanged(const QVariant &) override; private: @@ -137,6 +142,7 @@ private: void handleOutlineChanged(const QWidget* newOutline); void showRenameWarningIfFileIsGenerated(const Utils::FilePath &filePath); + void addRefactoringActions(QMenu *menu) const; private: QScopedPointer<Internal::CppEditorWidgetPrivate> d; diff --git a/src/plugins/cppeditor/cppelementevaluator.cpp b/src/plugins/cppeditor/cppelementevaluator.cpp index 27b481cd0c7..2d5d1c4af48 100644 --- a/src/plugins/cppeditor/cppelementevaluator.cpp +++ b/src/plugins/cppeditor/cppelementevaluator.cpp @@ -475,7 +475,7 @@ static QFuture<QSharedPointer<CppElement>> exec(SourceFunction &&sourceFunction, ExecFunction &&execFunction, bool followTypedef = true) { - const Snapshot &snapshot = CppModelManager::instance()->snapshot(); + const Snapshot &snapshot = CppModelManager::snapshot(); Document::Ptr doc; QString expression; @@ -497,7 +497,7 @@ static QFuture<QSharedPointer<CppElement>> asyncExec( const CPlusPlus::LookupContext &lookupContext) { return Utils::asyncRun(&createTypeHierarchy, snapshot, lookupItem, lookupContext, - *CppModelManager::instance()->symbolFinder()); + *CppModelManager::symbolFinder()); } class FromExpressionFunctor @@ -589,7 +589,7 @@ QFuture<QSharedPointer<CppElement>> FromGuiFunctor::syncExec( QFutureInterface<QSharedPointer<CppElement>> futureInterface; futureInterface.reportStarted(); m_element = handleLookupItemMatch(snapshot, lookupItem, lookupContext, - *CppModelManager::instance()->symbolFinder()); + *CppModelManager::symbolFinder()); futureInterface.reportResult(m_element); futureInterface.reportFinished(); return futureInterface.future(); @@ -694,7 +694,7 @@ const QString &CppElementEvaluator::diagnosis() const Utils::Link CppElementEvaluator::linkFromExpression(const QString &expression, const FilePath &filePath) { - const Snapshot &snapshot = CppModelManager::instance()->snapshot(); + const Snapshot &snapshot = CppModelManager::snapshot(); Document::Ptr doc = snapshot.document(filePath); if (doc.isNull()) return Utils::Link(); diff --git a/src/plugins/cppeditor/cppfilesettingspage.cpp b/src/plugins/cppeditor/cppfilesettingspage.cpp index 447d6083697..4303fe67d4f 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.cpp +++ b/src/plugins/cppeditor/cppfilesettingspage.cpp @@ -6,30 +6,33 @@ #include "cppeditorplugin.h" #include "cppeditortr.h" -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <coreplugin/editormanager/editormanager.h> +#include <projectexplorer/project.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> #include <utils/mimeutils.h> #include <utils/pathchooser.h> +#include <utils/qtcsettings.h> #include <QCheckBox> #include <QComboBox> #include <QCoreApplication> #include <QFile> +#include <QGuiApplication> #include <QLineEdit> #include <QLocale> -#include <QSettings> #include <QTextStream> +#include <QVBoxLayout> using namespace Utils; namespace CppEditor::Internal { +const char projectSettingsKeyC[] = "CppEditorFileNames"; +const char useGlobalKeyC[] = "UseGlobal"; const char headerPrefixesKeyC[] = "HeaderPrefixes"; const char sourcePrefixesKeyC[] = "SourcePrefixes"; const char headerSuffixKeyC[] = "HeaderSuffix"; @@ -47,35 +50,23 @@ const char *licenseTemplateTemplate = QT_TRANSLATE_NOOP("QtC::CppEditor", "** To protect a percent sign, use '%%'.\n" "**************************************************************************/\n"); -void CppFileSettings::toSettings(QSettings *s) const +void CppFileSettings::toSettings(QtcSettings *s) const { const CppFileSettings def; s->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); - QtcSettings::setValueWithDefault(s, headerPrefixesKeyC, headerPrefixes, def.headerPrefixes); - QtcSettings::setValueWithDefault(s, sourcePrefixesKeyC, sourcePrefixes, def.sourcePrefixes); - QtcSettings::setValueWithDefault(s, headerSuffixKeyC, headerSuffix, def.headerSuffix); - QtcSettings::setValueWithDefault(s, sourceSuffixKeyC, sourceSuffix, def.sourceSuffix); - QtcSettings::setValueWithDefault(s, - headerSearchPathsKeyC, - headerSearchPaths, - def.headerSearchPaths); - QtcSettings::setValueWithDefault(s, - sourceSearchPathsKeyC, - sourceSearchPaths, - def.sourceSearchPaths); - QtcSettings::setValueWithDefault(s, - Constants::LOWERCASE_CPPFILES_KEY, - lowerCaseFiles, - def.lowerCaseFiles); - QtcSettings::setValueWithDefault(s, headerPragmaOnceC, headerPragmaOnce, def.headerPragmaOnce); - QtcSettings::setValueWithDefault(s, - licenseTemplatePathKeyC, - licenseTemplatePath, - def.licenseTemplatePath); + s->setValueWithDefault(headerPrefixesKeyC, headerPrefixes, def.headerPrefixes); + s->setValueWithDefault(sourcePrefixesKeyC, sourcePrefixes, def.sourcePrefixes); + s->setValueWithDefault(headerSuffixKeyC, headerSuffix, def.headerSuffix); + s->setValueWithDefault(sourceSuffixKeyC, sourceSuffix, def.sourceSuffix); + s->setValueWithDefault(headerSearchPathsKeyC, headerSearchPaths, def.headerSearchPaths); + s->setValueWithDefault(sourceSearchPathsKeyC, sourceSearchPaths, def.sourceSearchPaths); + s->setValueWithDefault(Constants::LOWERCASE_CPPFILES_KEY, lowerCaseFiles, def.lowerCaseFiles); + s->setValueWithDefault(headerPragmaOnceC, headerPragmaOnce, def.headerPragmaOnce); + s->setValueWithDefault(licenseTemplatePathKeyC, licenseTemplatePath, def.licenseTemplatePath); s->endGroup(); } -void CppFileSettings::fromSettings(QSettings *s) +void CppFileSettings::fromSettings(QtcSettings *s) { const CppFileSettings def; s->beginGroup(Constants::CPPEDITOR_SETTINGSGROUP); @@ -91,7 +82,7 @@ void CppFileSettings::fromSettings(QSettings *s) s->endGroup(); } -bool CppFileSettings::applySuffixesToMimeDB() +static bool applySuffixes(const QString &sourceSuffix, const QString &headerSuffix) { Utils::MimeType mt; mt = Utils::mimeTypeForName(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); @@ -105,6 +96,19 @@ bool CppFileSettings::applySuffixesToMimeDB() return true; } +void CppFileSettings::addMimeInitializer() const +{ + Utils::addMimeInitializer([sourceSuffix = sourceSuffix, headerSuffix = headerSuffix] { + if (!applySuffixes(sourceSuffix, headerSuffix)) + qWarning("Unable to apply cpp suffixes to mime database (cpp mime types not found).\n"); + }); +} + +bool CppFileSettings::applySuffixesToMimeDB() +{ + return applySuffixes(sourceSuffix, headerSuffix); +} + bool CppFileSettings::equals(const CppFileSettings &rhs) const { return lowerCaseFiles == rhs.lowerCaseFiles @@ -202,18 +206,14 @@ static void parseLicenseTemplatePlaceholders(QString *t) } // Convenience that returns the formatted license template. -QString CppFileSettings::licenseTemplate() +QString CppFileSettings::licenseTemplate() const { - const QSettings *s = Core::ICore::settings(); - QString key = QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP); - key += QLatin1Char('/'); - key += QLatin1String(licenseTemplatePathKeyC); - const QString path = s->value(key, QString()).toString(); - if (path.isEmpty()) + if (licenseTemplatePath.isEmpty()) return QString(); - QFile file(path); + QFile file(licenseTemplatePath); if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { - qWarning("Unable to open the license template %s: %s", qPrintable(path), qPrintable(file.errorString())); + qWarning("Unable to open the license template %s: %s", qPrintable(licenseTemplatePath), + qPrintable(file.errorString())); return QString(); } @@ -235,11 +235,17 @@ QString CppFileSettings::licenseTemplate() class CppFileSettingsWidget final : public Core::IOptionsPageWidget { + Q_OBJECT + public: explicit CppFileSettingsWidget(CppFileSettings *settings); void apply() final; void setSettings(const CppFileSettings &s); + CppFileSettings currentSettings() const; + +signals: + void userChange(); private: void slotEdit(); @@ -332,10 +338,29 @@ CppFileSettingsWidget::CppFileSettingsWidget(CppFileSettings *settings) m_headerSuffixComboBox->addItem(suffix); } m_licenseTemplatePathChooser->setExpectedKind(PathChooser::File); - m_licenseTemplatePathChooser->setHistoryCompleter(QLatin1String("Cpp.LicenseTemplate.History")); + m_licenseTemplatePathChooser->setHistoryCompleter("Cpp.LicenseTemplate.History"); m_licenseTemplatePathChooser->addButton(Tr::tr("Edit..."), this, [this] { slotEdit(); }); setSettings(*m_settings); + + connect(m_headerSuffixComboBox, &QComboBox::currentIndexChanged, + this, &CppFileSettingsWidget::userChange); + connect(m_sourceSuffixComboBox, &QComboBox::currentIndexChanged, + this, &CppFileSettingsWidget::userChange); + connect(m_headerSearchPathsEdit, &QLineEdit::textEdited, + this, &CppFileSettingsWidget::userChange); + connect(m_sourceSearchPathsEdit, &QLineEdit::textEdited, + this, &CppFileSettingsWidget::userChange); + connect(m_headerPrefixesEdit, &QLineEdit::textEdited, + this, &CppFileSettingsWidget::userChange); + connect(m_sourcePrefixesEdit, &QLineEdit::textEdited, + this, &CppFileSettingsWidget::userChange); + connect(m_headerPragmaOnceCheckBox, &QCheckBox::stateChanged, + this, &CppFileSettingsWidget::userChange); + connect(m_lowerCaseFileNamesCheckBox, &QCheckBox::stateChanged, + this, &CppFileSettingsWidget::userChange); + connect(m_licenseTemplatePathChooser, &PathChooser::textChanged, + this, &CppFileSettingsWidget::userChange); } FilePath CppFileSettingsWidget::licenseTemplatePath() const @@ -358,17 +383,7 @@ static QStringList trimmedPaths(const QString &paths) void CppFileSettingsWidget::apply() { - CppFileSettings rc; - rc.lowerCaseFiles = m_lowerCaseFileNamesCheckBox->isChecked(); - rc.headerPragmaOnce = m_headerPragmaOnceCheckBox->isChecked(); - rc.headerPrefixes = trimmedPaths(m_headerPrefixesEdit->text()); - rc.sourcePrefixes = trimmedPaths(m_sourcePrefixesEdit->text()); - rc.headerSuffix = m_headerSuffixComboBox->currentText(); - rc.sourceSuffix = m_sourceSuffixComboBox->currentText(); - rc.headerSearchPaths = trimmedPaths(m_headerSearchPathsEdit->text()); - rc.sourceSearchPaths = trimmedPaths(m_sourceSearchPathsEdit->text()); - rc.licenseTemplatePath = licenseTemplatePath().toString(); - + const CppFileSettings rc = currentSettings(); if (rc == *m_settings) return; @@ -398,6 +413,21 @@ void CppFileSettingsWidget::setSettings(const CppFileSettings &s) setLicenseTemplatePath(FilePath::fromString(s.licenseTemplatePath)); } +CppFileSettings CppFileSettingsWidget::currentSettings() const +{ + CppFileSettings rc; + rc.lowerCaseFiles = m_lowerCaseFileNamesCheckBox->isChecked(); + rc.headerPragmaOnce = m_headerPragmaOnceCheckBox->isChecked(); + rc.headerPrefixes = trimmedPaths(m_headerPrefixesEdit->text()); + rc.sourcePrefixes = trimmedPaths(m_sourcePrefixesEdit->text()); + rc.headerSuffix = m_headerSuffixComboBox->currentText(); + rc.sourceSuffix = m_sourceSuffixComboBox->currentText(); + rc.headerSearchPaths = trimmedPaths(m_headerSearchPathsEdit->text()); + rc.sourceSearchPaths = trimmedPaths(m_sourceSearchPathsEdit->text()); + rc.licenseTemplatePath = licenseTemplatePath().toString(); + return rc; +} + void CppFileSettingsWidget::slotEdit() { FilePath path = licenseTemplatePath(); @@ -407,7 +437,8 @@ void CppFileSettingsWidget::slotEdit() if (path.isEmpty()) return; FileSaver saver(path, QIODevice::Text); - saver.write(Tr::tr(licenseTemplateTemplate).arg(Core::Constants::IDE_DISPLAY_NAME).toUtf8()); + saver.write( + Tr::tr(licenseTemplateTemplate).arg(QGuiApplication::applicationDisplayName()).toUtf8()); if (!saver.finalize(this)) return; setLicenseTemplatePath(path); @@ -426,4 +457,133 @@ CppFileSettingsPage::CppFileSettingsPage(CppFileSettings *settings) setWidgetCreator([settings] { return new CppFileSettingsWidget(settings); }); } +CppFileSettingsForProject::CppFileSettingsForProject(ProjectExplorer::Project *project) + : m_project(project) +{ + loadSettings(); +} + +CppFileSettings CppFileSettingsForProject::settings() const +{ + return m_useGlobalSettings ? CppEditorPlugin::fileSettings(nullptr) : m_customSettings; +} + +void CppFileSettingsForProject::setSettings(const CppFileSettings &settings) +{ + m_customSettings = settings; + saveSettings(); +} + +void CppFileSettingsForProject::setUseGlobalSettings(bool useGlobal) +{ + m_useGlobalSettings = useGlobal; + saveSettings(); +} + +void CppFileSettingsForProject::loadSettings() +{ + if (!m_project) + return; + + const QVariant entry = m_project->namedSettings(projectSettingsKeyC); + if (!entry.isValid()) + return; + + const QVariantMap data = entry.toMap(); + m_useGlobalSettings = data.value(useGlobalKeyC, true).toBool(); + m_customSettings.headerPrefixes = data.value(headerPrefixesKeyC, + m_customSettings.headerPrefixes).toStringList(); + m_customSettings.sourcePrefixes = data.value(sourcePrefixesKeyC, + m_customSettings.sourcePrefixes).toStringList(); + m_customSettings.headerSuffix = data.value(headerSuffixKeyC, m_customSettings.headerSuffix) + .toString(); + m_customSettings.sourceSuffix = data.value(sourceSuffixKeyC, m_customSettings.sourceSuffix) + .toString(); + m_customSettings.headerSearchPaths + = data.value(headerSearchPathsKeyC, m_customSettings.headerSearchPaths).toStringList(); + m_customSettings.sourceSearchPaths + = data.value(sourceSearchPathsKeyC, m_customSettings.sourceSearchPaths).toStringList(); + m_customSettings.lowerCaseFiles = data.value(Constants::LOWERCASE_CPPFILES_KEY, + m_customSettings.lowerCaseFiles).toBool(); + m_customSettings.headerPragmaOnce = data.value(headerPragmaOnceC, + m_customSettings.headerPragmaOnce).toBool(); + m_customSettings.licenseTemplatePath + = data.value(licenseTemplatePathKeyC, m_customSettings.licenseTemplatePath).toString(); +} + +void CppFileSettingsForProject::saveSettings() +{ + if (!m_project) + return; + + // Optimization: Don't save anything if the user never switched away from the default. + if (m_useGlobalSettings && !m_project->namedSettings(projectSettingsKeyC).isValid()) + return; + + QVariantMap data; + data.insert(useGlobalKeyC, m_useGlobalSettings); + data.insert(headerPrefixesKeyC, m_customSettings.headerPrefixes); + data.insert(sourcePrefixesKeyC, m_customSettings.sourcePrefixes); + data.insert(headerSuffixKeyC, m_customSettings.headerSuffix); + data.insert(sourceSuffixKeyC, m_customSettings.sourceSuffix); + data.insert(headerSearchPathsKeyC, m_customSettings.headerSearchPaths); + data.insert(sourceSearchPathsKeyC, m_customSettings.sourceSearchPaths); + data.insert(Constants::LOWERCASE_CPPFILES_KEY, m_customSettings.lowerCaseFiles); + data.insert(headerPragmaOnceC, m_customSettings.headerPragmaOnce); + data.insert(licenseTemplatePathKeyC, m_customSettings.licenseTemplatePath); + m_project->setNamedSettings(projectSettingsKeyC, data); +} + +class CppFileSettingsForProjectWidget::Private +{ +public: + Private(const CppFileSettingsForProject &s) : settings(s) {} + + void maybeClearHeaderSourceCache(); + void updateSubWidgetState() { widget.setEnabled(!settings.useGlobalSettings()); } + + CppFileSettingsForProject settings; + CppFileSettings initialSettings = settings.settings(); + CppFileSettingsWidget widget{&initialSettings}; + QCheckBox useGlobalSettingsCheckBox; + const bool wasGlobal = settings.useGlobalSettings(); +}; + +CppFileSettingsForProjectWidget::CppFileSettingsForProjectWidget( + const CppFileSettingsForProject &settings) : d(new Private(settings)) +{ + setGlobalSettingsId(Constants::CPP_FILE_SETTINGS_ID); + const auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(&d->widget); + + connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged, this, + [this](bool checked) { + d->settings.setUseGlobalSettings(checked); + if (!checked) + d->settings.setSettings(d->widget.currentSettings()); + d->maybeClearHeaderSourceCache(); + d->updateSubWidgetState(); + }); + connect(&d->widget, &CppFileSettingsWidget::userChange, this, [this] { + d->settings.setSettings(d->widget.currentSettings()); + d->maybeClearHeaderSourceCache(); + }); + d->updateSubWidgetState(); +} + +CppFileSettingsForProjectWidget::~CppFileSettingsForProjectWidget() { delete d; } + +void CppFileSettingsForProjectWidget::Private::maybeClearHeaderSourceCache() +{ + const CppFileSettings &s = settings.settings(); + if (settings.useGlobalSettings() != wasGlobal + || s.headerSearchPaths != initialSettings.headerSearchPaths + || s.sourceSearchPaths != initialSettings.sourceSearchPaths) { + CppEditorPlugin::clearHeaderSourceCache(); + } +} + } // namespace CppEditor::Internal + +#include <cppfilesettingspage.moc> diff --git a/src/plugins/cppeditor/cppfilesettingspage.h b/src/plugins/cppeditor/cppfilesettingspage.h index 50cc38133a0..d82ca6e8bb3 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.h +++ b/src/plugins/cppeditor/cppfilesettingspage.h @@ -6,6 +6,7 @@ #include "cppeditorconstants.h" #include <coreplugin/dialogs/ioptionspage.h> +#include <projectexplorer/projectsettingswidget.h> #include <QDir> @@ -13,6 +14,8 @@ QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE +namespace ProjectExplorer { class Project; } + namespace CppEditor::Internal { class CppFileSettings @@ -33,23 +36,53 @@ public: bool headerPragmaOnce = false; bool lowerCaseFiles = Constants::LOWERCASE_CPPFILES_DEFAULT; - void toSettings(QSettings *) const; - void fromSettings(QSettings *); + void toSettings(Utils::QtcSettings *) const; + void fromSettings(Utils::QtcSettings *); + void addMimeInitializer() const; bool applySuffixesToMimeDB(); // Convenience to return a license template completely formatted. - // Currently made public in - static QString licenseTemplate(); + QString licenseTemplate() const; bool equals(const CppFileSettings &rhs) const; bool operator==(const CppFileSettings &s) const { return equals(s); } bool operator!=(const CppFileSettings &s) const { return !equals(s); } }; +class CppFileSettingsForProject +{ +public: + CppFileSettingsForProject(ProjectExplorer::Project *project); + + CppFileSettings settings() const; + void setSettings(const CppFileSettings &settings); + bool useGlobalSettings() const { return m_useGlobalSettings; } + void setUseGlobalSettings(bool useGlobal); + +private: + void loadSettings(); + void saveSettings(); + + ProjectExplorer::Project * const m_project; + CppFileSettings m_customSettings; + bool m_useGlobalSettings = true; +}; + class CppFileSettingsPage : public Core::IOptionsPage { public: explicit CppFileSettingsPage(CppFileSettings *settings); }; +class CppFileSettingsForProjectWidget : public ProjectExplorer::ProjectSettingsWidget +{ +public: + CppFileSettingsForProjectWidget(const CppFileSettingsForProject &settings); + ~CppFileSettingsForProjectWidget(); + +private: + class Private; + Private * const d; +}; + } // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/cppfindreferences.cpp b/src/plugins/cppeditor/cppfindreferences.cpp index 5da22854386..25b3e110d53 100644 --- a/src/plugins/cppeditor/cppfindreferences.cpp +++ b/src/plugins/cppeditor/cppfindreferences.cpp @@ -398,6 +398,8 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, parameters.symbolId = fullIdForSymbol(symbol); parameters.symbolFilePath = symbol->filePath(); parameters.categorize = codeModelSettings()->categorizeFindReferences(); + parameters.preferLowerCaseFileNames = preferLowerCaseFileNames( + ProjectManager::projectForFile(symbol->filePath())); if (symbol->asClass() || symbol->asForwardClassDeclaration()) { CPlusPlus::Overview overview; @@ -435,9 +437,9 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol * if (search->isInteractive()) SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); - const WorkingCopy workingCopy = m_modelManager->workingCopy(); + const WorkingCopy workingCopy = CppModelManager::workingCopy(); QFuture<CPlusPlus::Usage> result; - result = Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper, + result = Utils::asyncRun(CppModelManager::sharedThreadPool(), find_helper, workingCopy, context, symbol, categorize); createWatcher(result, search); @@ -475,14 +477,14 @@ void CppFindReferences::onReplaceButtonClicked(Core::SearchResult *search, const ProjectExplorerPlugin::renameFilesForSymbol( parameters.prettySymbolName, text, parameters.filesToRename, - preferLowerCaseFileNames()); + parameters.preferLowerCaseFileNames); } void CppFindReferences::searchAgain(SearchResult *search) { CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>(); parameters.filesToRename.clear(); - CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot(); + CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); search->restart(); CPlusPlus::LookupContext context; CPlusPlus::Symbol *symbol = findSymbol(parameters, snapshot, &context); @@ -542,7 +544,7 @@ CPlusPlus::Symbol *CppFindReferences::findSymbol(const CppFindReferencesParamete CPlusPlus::Document::Ptr newSymbolDocument = snapshot.document(parameters.symbolFilePath); // document is not parsed and has no bindings yet, do it - QByteArray source = getSource(newSymbolDocument->filePath(), m_modelManager->workingCopy()); + QByteArray source = getSource(newSymbolDocument->filePath(), CppModelManager::workingCopy()); CPlusPlus::Document::Ptr doc = snapshot.preprocessedDocument(source, newSymbolDocument->filePath()); doc->check(); @@ -574,8 +576,11 @@ static void displayResults(SearchResult *search, item.setContainingFunctionName(result.containingFunction); item.setStyle(colorStyleForUsageType(result.tags)); item.setUseTextEditorFont(true); - if (search->supportsReplace()) - item.setSelectForReplacement(ProjectManager::projectForFile(result.path)); + if (search->supportsReplace()) { + const Node * const node = ProjectTree::nodeForFile(result.path); + item.setSelectForReplacement(!ProjectManager::hasProjects() + || (node && !node->isGenerated())); + } search->addResult(item); if (parameters.prettySymbolName.isEmpty()) @@ -596,6 +601,10 @@ static void displayResults(SearchResult *search, static void searchFinished(SearchResult *search, QFutureWatcher<CPlusPlus::Usage> *watcher) { + if (!watcher->isCanceled() && search->supportsReplace()) { + search->addResults(symbolOccurrencesInDeclarationComments(search->allItems()), + SearchResult::AddSortedByPosition); + } search->finishSearch(watcher->isCanceled()); CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>(); @@ -745,8 +754,8 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro ¯o, const QStri Core::EditorManager::openEditorAtSearchResult(item); }); - const CPlusPlus::Snapshot snapshot = m_modelManager->snapshot(); - const WorkingCopy workingCopy = m_modelManager->workingCopy(); + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); + const WorkingCopy workingCopy = CppModelManager::workingCopy(); // add the macro definition itself { @@ -766,7 +775,7 @@ void CppFindReferences::findMacroUses(const CPlusPlus::Macro ¯o, const QStri } QFuture<CPlusPlus::Usage> result; - result = Utils::asyncRun(m_modelManager->sharedThreadPool(), findMacroUses_helper, + result = Utils::asyncRun(CppModelManager::sharedThreadPool(), findMacroUses_helper, workingCopy, snapshot, macro); createWatcher(result, search); @@ -827,8 +836,8 @@ void CppFindReferences::checkUnused(Core::SearchResult *search, const Link &link }); connect(search, &SearchResult::canceled, watcher, [watcher] { watcher->cancel(); }); connect(search, &SearchResult::destroyed, watcher, [watcher] { watcher->cancel(); }); - watcher->setFuture(Utils::asyncRun(m_modelManager->sharedThreadPool(), find_helper, - m_modelManager->workingCopy(), context, symbol, true)); + watcher->setFuture(Utils::asyncRun(CppModelManager::sharedThreadPool(), find_helper, + CppModelManager::workingCopy(), context, symbol, true)); } void CppFindReferences::createWatcher(const QFuture<CPlusPlus::Usage> &future, SearchResult *search) diff --git a/src/plugins/cppeditor/cppfindreferences.h b/src/plugins/cppeditor/cppfindreferences.h index b05ce723c4e..37e105ab364 100644 --- a/src/plugins/cppeditor/cppfindreferences.h +++ b/src/plugins/cppeditor/cppfindreferences.h @@ -4,6 +4,7 @@ #pragma once #include "cppeditor_global.h" +#include "cppeditorconstants.h" #include <coreplugin/find/searchresultwindow.h> #include <cplusplus/FindUsages.h> @@ -51,6 +52,7 @@ public: QString prettySymbolName; Utils::FilePaths filesToRename; bool categorize = false; + bool preferLowerCaseFileNames = Constants::LOWERCASE_CPPFILES_DEFAULT; }; class CppFindReferences: public QObject diff --git a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp index 0353e16c302..f61f0276410 100644 --- a/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp +++ b/src/plugins/cppeditor/cppfollowsymbolundercursor.cpp @@ -17,6 +17,7 @@ #include <cplusplus/SimpleLexer.h> #include <cplusplus/TypeOfExpression.h> #include <texteditor/textdocumentlayout.h> +#include <utils/algorithm.h> #include <utils/textutils.h> #include <utils/qtcassert.h> @@ -181,9 +182,7 @@ Class *VirtualFunctionHelper::staticClassOfFunctionCallExpression_internal() con Link findMacroLink_helper(const QByteArray &name, Document::Ptr doc, const Snapshot &snapshot, QSet<QString> *processed) { - if (doc && !name.startsWith('<') && !processed->contains(doc->filePath().path())) { - processed->insert(doc->filePath().path()); - + if (doc && !name.startsWith('<') && Utils::insert(*processed, doc->filePath().path())) { for (const Macro ¯o : doc->definedMacros()) { if (macro.name() == name) { Link link; @@ -210,7 +209,7 @@ Link findMacroLink(const QByteArray &name, const Document::Ptr &doc) { if (!name.isEmpty()) { if (doc) { - const Snapshot snapshot = CppModelManager::instance()->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); QSet<QString> processed; return findMacroLink_helper(name, doc, snapshot, &processed); } @@ -343,7 +342,7 @@ Link attemptDeclDef(const QTextCursor &cursor, Snapshot snapshot, result = target->toLink(); int startLine, startColumn, endLine, endColumn; - document->translationUnit()->getTokenStartPosition(name->firstToken(), &startLine, + document->translationUnit()->getTokenPosition(name->firstToken(), &startLine, &startColumn); document->translationUnit()->getTokenEndPosition(name->lastToken() - 1, &endLine, &endColumn); diff --git a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp index aa17721ea37..7055b00d038 100644 --- a/src/plugins/cppeditor/cppfunctiondecldeflink.cpp +++ b/src/plugins/cppeditor/cppfunctiondecldeflink.cpp @@ -9,6 +9,7 @@ #include "cppeditorwidget.h" #include "cpplocalsymbols.h" #include "cppquickfixassistant.h" +#include "cpptoolsreuse.h" #include "symbolfinder.h" #include <coreplugin/actionmanager/actionmanager.h> @@ -18,12 +19,14 @@ #include <cplusplus/ASTPath.h> #include <cplusplus/CppRewriter.h> +#include <cplusplus/declarationcomments.h> #include <cplusplus/Overview.h> #include <cplusplus/TypeOfExpression.h> #include <utils/async.h> #include <utils/proxyaction.h> #include <utils/qtcassert.h> +#include <utils/textutils.h> #include <utils/tooltip/tooltip.h> #include <QRegularExpression> @@ -847,6 +850,41 @@ ChangeSet FunctionDeclDefLink::changes(const Snapshot &snapshot, int targetOffse newTargetParameters); } + // Change parameter names in function documentation. + [&] { + if (renamedTargetParameters.isEmpty()) + return; + const QList<Token> functionComments = commentsForDeclaration( + targetFunction, targetDeclaration, *targetFile->document(), + targetFile->cppDocument()); + if (functionComments.isEmpty()) + return; + const QString &content = targetFile->document()->toPlainText(); + const QStringView docView = QStringView(content); + for (auto it = renamedTargetParameters.cbegin(); + it != renamedTargetParameters.cend(); ++it) { + const QString paramName = Overview().prettyName(it.key()->name()); + for (const Token &tok : functionComments) { + const TranslationUnit * const tu = targetFile->cppDocument()->translationUnit(); + const int tokenStartPos = tu->getTokenPositionInDocument( + tok, targetFile->document()); + const int tokenEndPos = tu->getTokenEndPositionInDocument( + tok, targetFile->document()); + const QStringView tokenView = docView.mid(tokenStartPos, + tokenEndPos - tokenStartPos); + const QList<Text::Range> ranges = symbolOccurrencesInText( + *targetFile->document(), tokenView, tokenStartPos, paramName); + for (const Text::Range &r : ranges) { + const int startPos = Text::positionInText( + targetFile->document(), r.begin.line, r.begin.column + 1); + const int endPos = Text::positionInText( + targetFile->document(), r.end.line, r.end.column + 1); + changes.replace(startPos, endPos, it.value()); + } + } + } + }(); + // for function definitions, rename the local usages FunctionDefinitionAST *targetDefinition = targetDeclaration->asFunctionDefinition(); if (targetDefinition && !renamedTargetParameters.isEmpty()) { diff --git a/src/plugins/cppeditor/cppheadersource_test.cpp b/src/plugins/cppeditor/cppheadersource_test.cpp index d94920a3295..28f5a8b6fb7 100644 --- a/src/plugins/cppeditor/cppheadersource_test.cpp +++ b/src/plugins/cppeditor/cppheadersource_test.cpp @@ -71,25 +71,27 @@ void HeaderSourceTest::test_data() void HeaderSourceTest::initTestCase() { QDir(baseTestDir()).mkpath(_(".")); - CppFileSettings *fs = CppEditorPlugin::fileSettings(); - fs->headerSearchPaths.append(QLatin1String("include")); - fs->headerSearchPaths.append(QLatin1String("../include")); - fs->sourceSearchPaths.append(QLatin1String("src")); - fs->sourceSearchPaths.append(QLatin1String("../src")); - fs->headerPrefixes.append(QLatin1String("testh_")); - fs->sourcePrefixes.append(QLatin1String("testc_")); + CppFileSettings fs = CppEditorPlugin::fileSettings(nullptr); + fs.headerSearchPaths.append(QLatin1String("include")); + fs.headerSearchPaths.append(QLatin1String("../include")); + fs.sourceSearchPaths.append(QLatin1String("src")); + fs.sourceSearchPaths.append(QLatin1String("../src")); + fs.headerPrefixes.append(QLatin1String("testh_")); + fs.sourcePrefixes.append(QLatin1String("testc_")); + CppEditorPlugin::setGlobalFileSettings(fs); } void HeaderSourceTest::cleanupTestCase() { Utils::FilePath::fromString(baseTestDir()).removeRecursively(); - CppFileSettings *fs = CppEditorPlugin::fileSettings(); - fs->headerSearchPaths.removeLast(); - fs->headerSearchPaths.removeLast(); - fs->sourceSearchPaths.removeLast(); - fs->sourceSearchPaths.removeLast(); - fs->headerPrefixes.removeLast(); - fs->sourcePrefixes.removeLast(); + CppFileSettings fs = CppEditorPlugin::fileSettings(nullptr); + fs.headerSearchPaths.removeLast(); + fs.headerSearchPaths.removeLast(); + fs.sourceSearchPaths.removeLast(); + fs.sourceSearchPaths.removeLast(); + fs.headerPrefixes.removeLast(); + fs.sourcePrefixes.removeLast(); + CppEditorPlugin::setGlobalFileSettings(fs); } } // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index caad2e0937b..a2009c89900 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -33,7 +33,6 @@ #include <QCoreApplication> #include <QKeyEvent> #include <QLabel> -#include <QSettings> #include <QStackedWidget> #include <QTimer> #include <QToolButton> @@ -54,7 +53,7 @@ enum { static Snapshot globalSnapshot() { - return CppModelManager::instance()->snapshot(); + return CppModelManager::snapshot(); } struct FileAndLine @@ -333,8 +332,8 @@ public: void perform(); - void saveSettings(QSettings *settings, int position); - void restoreSettings(QSettings *settings, int position); + void saveSettings(QtcSettings *settings, int position); + void restoreSettings(QtcSettings *settings, int position); private: void onItemActivated(const QModelIndex &index); @@ -430,15 +429,15 @@ void CppIncludeHierarchyWidget::perform() const bool kSyncDefault = false; -void CppIncludeHierarchyWidget::saveSettings(QSettings *settings, int position) +void CppIncludeHierarchyWidget::saveSettings(QtcSettings *settings, int position) { - const QString key = QString("IncludeHierarchy.%1.SyncWithEditor").arg(position); - QtcSettings::setValueWithDefault(settings, key, m_toggleSync->isChecked(), kSyncDefault); + const Key key = keyFromString(QString("IncludeHierarchy.%1.SyncWithEditor").arg(position)); + settings->setValueWithDefault(key, m_toggleSync->isChecked(), kSyncDefault); } -void CppIncludeHierarchyWidget::restoreSettings(QSettings *settings, int position) +void CppIncludeHierarchyWidget::restoreSettings(QtcSettings *settings, int position) { - const QString key = QString("IncludeHierarchy.%1.SyncWithEditor").arg(position); + const Key key = keyFromString(QString("IncludeHierarchy.%1.SyncWithEditor").arg(position)); m_toggleSync->setChecked(settings->value(key, kSyncDefault).toBool()); } @@ -532,7 +531,7 @@ void CppIncludeHierarchyFactory::saveSettings(QtcSettings *settings, int positio hierarchyWidget(widget)->saveSettings(settings, position); } -void CppIncludeHierarchyFactory::restoreSettings(QSettings *settings, int position, QWidget *widget) +void CppIncludeHierarchyFactory::restoreSettings(QtcSettings *settings, int position, QWidget *widget) { hierarchyWidget(widget)->restoreSettings(settings, position); } diff --git a/src/plugins/cppeditor/cppincludehierarchy.h b/src/plugins/cppeditor/cppincludehierarchy.h index 0718af3b49f..e1e5e7d84d3 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.h +++ b/src/plugins/cppeditor/cppincludehierarchy.h @@ -51,7 +51,7 @@ public: Core::NavigationView createWidget() override; void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; - void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; }; } // namespace Internal diff --git a/src/plugins/cppeditor/cppincludesfilter.cpp b/src/plugins/cppeditor/cppincludesfilter.cpp index 9711b6f52de..e1df13f7a2d 100644 --- a/src/plugins/cppeditor/cppincludesfilter.cpp +++ b/src/plugins/cppeditor/cppincludesfilter.cpp @@ -90,7 +90,7 @@ CppIncludesFilter::CppIncludesFilter() if (entry) inputFilePaths.insert(entry->filePath()); } - const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); return [snapshot, inputFilePaths](const QFuture<void> &future) { // This body runs in non-main thread return generateFilePaths(future, snapshot, inputFilePaths); diff --git a/src/plugins/cppeditor/cppindexingsupport.cpp b/src/plugins/cppeditor/cppindexingsupport.cpp index 22ff93cdaa0..ada400556e5 100644 --- a/src/plugins/cppeditor/cppindexingsupport.cpp +++ b/src/plugins/cppeditor/cppindexingsupport.cpp @@ -31,7 +31,7 @@ static Q_LOGGING_CATEGORY(indexerLog, "qtc.cppeditor.indexer", QtWarningMsg) SymbolSearcher::SymbolSearcher(const SymbolSearcher::Parameters ¶meters, const QSet<QString> &fileNames) - : m_snapshot(CppModelManager::instance()->snapshot()) + : m_snapshot(CppModelManager::snapshot()) , m_parameters(parameters) , m_fileNames(fileNames) {} @@ -106,7 +106,7 @@ private: int m_processedDiagnostics = 0; }; -void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList *sources) +static void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList *sources) { for (const QString &file : files) { if (ProjectFile::isSource(ProjectFile::classify(file))) @@ -116,7 +116,7 @@ void classifyFiles(const QSet<QString> &files, QStringList *headers, QStringList } } -void indexFindErrors(QPromise<void> &promise, const ParseParams params) +static void indexFindErrors(QPromise<void> &promise, const ParseParams params) { QStringList sources, headers; classifyFiles(params.sourceFiles, &headers, &sources); @@ -138,8 +138,7 @@ void indexFindErrors(QPromise<void> &promise, const ParseParams params) // Parse the file as precisely as possible BuiltinEditorDocumentParser parser(FilePath::fromString(file)); parser.setReleaseSourceAndAST(false); - parser.update({CppModelManager::instance()->workingCopy(), nullptr, - Language::Cxx, false}); + parser.update({CppModelManager::workingCopy(), nullptr, Language::Cxx, false}); CPlusPlus::Document::Ptr document = parser.document(); QTC_ASSERT(document, return); @@ -159,7 +158,7 @@ void indexFindErrors(QPromise<void> &promise, const ParseParams params) qDebug("FindErrorsIndexing: %s", qPrintable(elapsedTime)); } -void index(QPromise<void> &promise, const ParseParams params) +static void index(QPromise<void> &promise, const ParseParams params) { QScopedPointer<Internal::CppSourceProcessor> sourceProcessor(CppModelManager::createSourceProcessor()); sourceProcessor->setFileSizeLimitInMb(params.indexerFileSizeLimitInMb); @@ -181,8 +180,7 @@ void index(QPromise<void> &promise, const ParseParams params) const FilePath &conf = CppModelManager::configurationFileName(); bool processingHeaders = false; - CppModelManager *cmm = CppModelManager::instance(); - const ProjectExplorer::HeaderPaths fallbackHeaderPaths = cmm->headerPaths(); + const ProjectExplorer::HeaderPaths fallbackHeaderPaths = CppModelManager::headerPaths(); const CPlusPlus::LanguageFeatures defaultFeatures = CPlusPlus::LanguageFeatures::defaultFeatures(); @@ -192,7 +190,7 @@ void index(QPromise<void> &promise, const ParseParams params) break; const QString fileName = files.at(i); - const QList<ProjectPart::ConstPtr> parts = cmm->projectPart(fileName); + const QList<ProjectPart::ConstPtr> parts = CppModelManager::projectPart(fileName); const CPlusPlus::LanguageFeatures languageFeatures = parts.isEmpty() ? defaultFeatures : parts.first()->languageFeatures; @@ -222,7 +220,7 @@ void index(QPromise<void> &promise, const ParseParams params) qCDebug(indexerLog) << "Indexing finished."; } -void parse(QPromise<void> &promise, const ParseParams params) +static void parse(QPromise<void> &promise, const ParseParams ¶ms) { const QSet<QString> &files = params.sourceFiles; if (files.isEmpty()) @@ -236,7 +234,7 @@ void parse(QPromise<void> &promise, const ParseParams params) index(promise, params); promise.setProgressValue(files.size()); - CppModelManager::instance()->finishedRefreshingSourceFiles(files); + CppModelManager::finishedRefreshingSourceFiles(files); } } // anonymous namespace @@ -251,11 +249,11 @@ void SymbolSearcher::runSearch(QPromise<SearchResultItem> &promise) search.setSymbolsToSearchFor(m_parameters.types); CPlusPlus::Snapshot::const_iterator it = m_snapshot.begin(); - QString findString = (m_parameters.flags & Core::FindRegularExpression + QString findString = (m_parameters.flags & FindRegularExpression ? m_parameters.text : QRegularExpression::escape(m_parameters.text)); - if (m_parameters.flags & Core::FindWholeWords) + if (m_parameters.flags & FindWholeWords) findString = QString::fromLatin1("\\b%1\\b").arg(findString); - QRegularExpression matcher(findString, (m_parameters.flags & Core::FindCaseSensitively + QRegularExpression matcher(findString, (m_parameters.flags & FindCaseSensitively ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption)); matcher.optimize(); @@ -306,15 +304,13 @@ bool CppIndexingSupport::isFindErrorsIndexingActive() QFuture<void> CppIndexingSupport::refreshSourceFiles(const QSet<QString> &sourceFiles, CppModelManager::ProgressNotificationMode mode) { - CppModelManager *mgr = CppModelManager::instance(); - ParseParams params; params.indexerFileSizeLimitInMb = indexerFileSizeLimitInMb(); - params.headerPaths = mgr->headerPaths(); - params.workingCopy = mgr->workingCopy(); + params.headerPaths = CppModelManager::headerPaths(); + params.workingCopy = CppModelManager::workingCopy(); params.sourceFiles = sourceFiles; - QFuture<void> result = Utils::asyncRun(mgr->sharedThreadPool(), parse, params); + QFuture<void> result = Utils::asyncRun(CppModelManager::sharedThreadPool(), parse, params); m_synchronizer.addFuture(result); if (mode == CppModelManager::ForcedProgressNotification || sourceFiles.count() > 1) { diff --git a/src/plugins/cppeditor/cppindexingsupport.h b/src/plugins/cppeditor/cppindexingsupport.h index b5aa68585d6..9995ca631cd 100644 --- a/src/plugins/cppeditor/cppindexingsupport.h +++ b/src/plugins/cppeditor/cppindexingsupport.h @@ -38,7 +38,7 @@ public: struct Parameters { QString text; - Core::FindFlags flags; + Utils::FindFlags flags; SymbolTypes types; SearchScope scope; }; diff --git a/src/plugins/cppeditor/cppinsertvirtualmethods.cpp b/src/plugins/cppeditor/cppinsertvirtualmethods.cpp index eabd00c2f1c..24e7d6b0166 100644 --- a/src/plugins/cppeditor/cppinsertvirtualmethods.cpp +++ b/src/plugins/cppeditor/cppinsertvirtualmethods.cpp @@ -286,7 +286,7 @@ class VirtualMethodsSettings public: void read() { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); s->beginGroup(group()); insertVirtualKeyword = s->value(insertVirtualKeywordKey(), kInsertVirtualKeywordDefault) .toBool(); @@ -304,7 +304,7 @@ public: void write() const { - Utils::QtcSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); s->beginGroup(group()); s->setValueWithDefault(insertVirtualKeywordKey(), insertVirtualKeyword, @@ -334,14 +334,13 @@ public: bool insertOverrideReplacement = kInsertOVerrideReplacementDefault; private: - using _ = QLatin1String; - static QString group() { return _("QuickFix/InsertVirtualMethods"); } - static QString insertVirtualKeywordKey() { return _("insertKeywordVirtual"); } - static QString insertOverrideReplacementKey() { return _("insertOverrideReplacement"); } - static QString overrideReplacementIndexKey() { return _("overrideReplacementIndex"); } - static QString userAddedOverrideReplacementsKey() { return _("userAddedOverrideReplacements"); } - static QString implementationModeKey() { return _("implementationMode"); } - static QString hideReimplementedFunctionsKey() { return _("hideReimplementedFunctions"); } + static Key group() { return "QuickFix/InsertVirtualMethods"; } + static Key insertVirtualKeywordKey() { return "insertKeywordVirtual"; } + static Key insertOverrideReplacementKey() { return "insertOverrideReplacement"; } + static Key overrideReplacementIndexKey() { return "overrideReplacementIndex"; } + static Key userAddedOverrideReplacementsKey() { return "userAddedOverrideReplacements"; } + static Key implementationModeKey() { return "implementationMode"; } + static Key hideReimplementedFunctionsKey() { return "hideReimplementedFunctions"; } }; class InsertVirtualMethodsModel : public QAbstractItemModel @@ -1048,7 +1047,7 @@ void InsertVirtualMethodsDialog::initGui() auto clearUserAddedReplacements = new QAction(this); clearUserAddedReplacements->setIcon(Utils::Icons::CLEAN_TOOLBAR.icon()); clearUserAddedReplacements->setText(Tr::tr("Clear Added \"override\" Equivalents")); - connect(clearUserAddedReplacements, &QAction::triggered, [this] { + connect(clearUserAddedReplacements, &QAction::triggered, this, [this] { m_availableOverrideReplacements = defaultOverrideReplacements(); updateOverrideReplacementsComboBox(); m_clearUserAddedReplacementsButton->setEnabled(false); diff --git a/src/plugins/cppeditor/cpplocalsymbols.cpp b/src/plugins/cppeditor/cpplocalsymbols.cpp index 66200b3f3dc..9983f9fba11 100644 --- a/src/plugins/cppeditor/cpplocalsymbols.cpp +++ b/src/plugins/cppeditor/cpplocalsymbols.cpp @@ -4,8 +4,15 @@ #include "cpplocalsymbols.h" #include "cppsemanticinfo.h" +#include "cpptoolsreuse.h" #include "semantichighlighter.h" +#include <coreplugin/documentmanager.h> +#include <cplusplus/declarationcomments.h> +#include <cplusplus/Overview.h> +#include <texteditor/textdocument.h> +#include <utils/textutils.h> + using namespace CPlusPlus; namespace CppEditor::Internal { @@ -16,7 +23,7 @@ class FindLocalSymbols: protected ASTVisitor { public: explicit FindLocalSymbols(Document::Ptr doc) - : ASTVisitor(doc->translationUnit()) + : ASTVisitor(doc->translationUnit()), _doc(doc) { } // local and external uses. @@ -38,6 +45,46 @@ public: accept(ast); } } + + if (localUses.isEmpty()) + return; + + // For tst_checkSymbols + if (!Core::DocumentManager::instance()) + return; + + // Look for parameter occurrences in function comments. + const TextEditor::TextDocument * const editorDoc + = TextEditor::TextDocument::textDocumentForFilePath(_doc->filePath()); + if (!editorDoc) + return; + QTextDocument * const textDoc = editorDoc->document(); + if (!textDoc) + return; + const QString &content = textDoc->toPlainText(); + const QStringView docView(content); + for (auto it = localUses.begin(); it != localUses.end(); ++it) { + Symbol * const symbol = it.key(); + if (!symbol->asArgument()) + continue; + const QList<Token> commentTokens = commentsForDeclaration(symbol, ast, *textDoc, _doc); + if (commentTokens.isEmpty()) + continue; + const QString symbolName = Overview().prettyName(symbol->name()); + for (const Token &tok : commentTokens) { + const int commentPos = translationUnit()->getTokenPositionInDocument(tok, textDoc); + const int commentEndPos = translationUnit()->getTokenEndPositionInDocument( + tok, textDoc); + const QStringView commentView = docView.mid(commentPos, commentEndPos - commentPos); + const QList<Utils::Text::Range> ranges = symbolOccurrencesInText( + *textDoc, commentView, commentPos, symbolName); + for (const Utils::Text::Range &range : ranges) { + it.value().append(HighlightingResult(range.begin.line, range.begin.column + 1, + symbolName.size(), + SemanticHighlighter::LocalUse)); + } + } + } } protected: @@ -275,6 +322,7 @@ protected: private: QList<Scope *> _scopeStack; + Document::Ptr _doc; }; } // end of anonymous namespace diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp index cd1add7786e..5e429ece522 100644 --- a/src/plugins/cppeditor/cpplocatorfilter.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter.cpp @@ -41,7 +41,7 @@ void matchesFor(QPromise<void> &promise, const LocatorStorage &storage, const bool hasColonColon = input.contains("::"); const QRegularExpression shortRegexp = hasColonColon ? ILocatorFilter::createRegExp(input.mid(input.lastIndexOf("::") + 2)) : regexp; - CppLocatorData *locatorData = CppModelManager::instance()->locatorData(); + CppLocatorData *locatorData = CppModelManager::locatorData(); locatorData->filterAllFiles([&](const IndexItem::Ptr &info) { if (promise.isCanceled()) return IndexItem::Break; @@ -119,10 +119,12 @@ LocatorMatcherTask allSymbolsMatcher() { const auto converter = [](const IndexItem::Ptr &info) { LocatorFilterEntry filterEntry; - filterEntry.displayName = info->scopedSymbolName(); + filterEntry.displayName = info->symbolName(); filterEntry.displayIcon = info->icon(); filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; - if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum) + if (!info->symbolScope().isEmpty()) + filterEntry.extraInfo = info->symbolScope(); + else if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum) filterEntry.extraInfo = info->shortNativeFilePath(); else filterEntry.extraInfo = info->symbolType(); @@ -174,7 +176,7 @@ QList<IndexItem::Ptr> itemsOfCurrentDocument(const FilePath ¤tFileName) return {}; QList<IndexItem::Ptr> results; - const Snapshot snapshot = CppModelManager::instance()->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); if (const Document::Ptr thisDocument = snapshot.document(currentFileName)) { SearchSymbols search; search.setSymbolsToSearchFor(SymbolSearcher::Declarations | diff --git a/src/plugins/cppeditor/cpplocatorfilter_test.cpp b/src/plugins/cppeditor/cpplocatorfilter_test.cpp index 18e9b933cbe..a621d1c1103 100644 --- a/src/plugins/cppeditor/cpplocatorfilter_test.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter_test.cpp @@ -220,25 +220,18 @@ void LocatorFilterTest::testLocatorFilter_data() << MatcherType::AllSymbols << "my" << ResultDataList{ + ResultData("MyClass", "<anonymous namespace>"), + ResultData("MyClass", "<anonymous namespace>::MyClass"), + ResultData("MyClass", "MyClass"), + ResultData("MyClass", "MyNamespace"), + ResultData("MyClass", "MyNamespace::MyClass"), ResultData("MyClass", testFileShort), - ResultData("MyClass::MyClass", "()"), - ResultData("MyClass::functionDefinedOutSideClass", "(char)"), + ResultData("MyEnum", "<anonymous namespace>"), + ResultData("MyEnum", "MyNamespace"), ResultData("MyEnum", testFileShort), - ResultData("MyNamespace::MyClass", testFileShort), - ResultData("MyNamespace::MyClass::MyClass", "()"), - ResultData("MyNamespace::MyClass::functionDefinedOutSideClass", - "(char)"), - ResultData("MyNamespace::MyClass::functionDefinedOutSideClassAndNamespace", - "(float)"), - ResultData("MyNamespace::MyEnum", testFileShort), - ResultData("MyNamespace::myFunction", "(bool, int)"), ResultData("myFunction", "(bool, int)"), - ResultData("<anonymous namespace>::MyClass", testFileShort), - ResultData("<anonymous namespace>::MyClass::MyClass", "()"), - ResultData("<anonymous namespace>::MyClass::functionDefinedOutSideClass", - "(char)"), - ResultData("<anonymous namespace>::MyEnum", testFileShort), - ResultData("<anonymous namespace>::myFunction", "(bool, int)") + ResultData("myFunction", "<anonymous namespace>"), + ResultData("myFunction", "MyNamespace"), }; QTest::newRow("CppClassesFilter-ObjC") diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index 2b5d4e3fb82..e72c630cbf7 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -12,6 +12,7 @@ #include "cppcodemodelsettings.h" #include "cppeditorconstants.h" #include "cppeditortr.h" +#include "cppeditorwidget.h" #include "cppfindreferences.h" #include "cppincludesfilter.h" #include "cppindexingsupport.h" @@ -47,7 +48,7 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/gcctoolchain.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -59,7 +60,9 @@ #include <projectexplorer/target.h> #include <texteditor/textdocument.h> +#include <texteditor/textdocumentlayout.h> +#include <utils/algorithm.h> #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> @@ -214,6 +217,8 @@ public: QList<Document::DiagnosticMessage> m_diagnosticMessages; }; +static CppModelManagerPrivate *d; + } // namespace Internal using namespace Internal; @@ -295,14 +300,13 @@ QSet<FilePath> CppModelManager::timeStampModifiedFiles(const QList<Document::Ptr */ CppSourceProcessor *CppModelManager::createSourceProcessor() { - CppModelManager *that = instance(); - return new CppSourceProcessor(that->snapshot(), [that](const Document::Ptr &doc) { - const Document::Ptr previousDocument = that->document(doc->filePath()); + return new CppSourceProcessor(snapshot(), [](const Document::Ptr &doc) { + const Document::Ptr previousDocument = document(doc->filePath()); const unsigned newRevision = previousDocument.isNull() ? 1U : previousDocument->revision() + 1; doc->setRevision(newRevision); - that->emitDocumentUpdated(doc); + emitDocumentUpdated(doc); doc->releaseSourceAndAST(); }); } @@ -313,7 +317,7 @@ const FilePath &CppModelManager::editorConfigurationFileName() return config; } -ModelManagerSupport *CppModelManager::modelManagerSupport(Backend backend) const +ModelManagerSupport *CppModelManager::modelManagerSupport(Backend backend) { return backend == Backend::Builtin ? &d->m_builtinModelManagerSupport : d->m_activeModelManagerSupport; @@ -324,27 +328,26 @@ void CppModelManager::startLocalRenaming(const CursorInEditor &data, RenameCallback &&renameSymbolsCallback, Backend backend) { - instance()->modelManagerSupport(backend) + modelManagerSupport(backend) ->startLocalRenaming(data, projectPart, std::move(renameSymbolsCallback)); } void CppModelManager::globalRename(const CursorInEditor &data, const QString &replacement, const std::function<void()> &callback, Backend backend) { - instance()->modelManagerSupport(backend)->globalRename(data, replacement, callback); + modelManagerSupport(backend)->globalRename(data, replacement, callback); } void CppModelManager::findUsages(const CursorInEditor &data, Backend backend) { - instance()->modelManagerSupport(backend)->findUsages(data); + modelManagerSupport(backend)->findUsages(data); } void CppModelManager::switchHeaderSource(bool inNextSplit, Backend backend) { const IDocument *currentDocument = EditorManager::currentDocument(); QTC_ASSERT(currentDocument, return); - instance()->modelManagerSupport(backend)->switchHeaderSource(currentDocument->filePath(), - inNextSplit); + modelManagerSupport(backend)->switchHeaderSource(currentDocument->filePath(), inNextSplit); } void CppModelManager::showPreprocessedFile(bool inNextSplit) @@ -382,7 +385,7 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit) TemporaryDirectory::masterTemporaryDirectory()->filePath(outFileName)); const auto useBuiltinPreprocessor = [filePath, outFilePath, inNextSplit, contents = doc->contents()] { - const Document::Ptr preprocessedDoc = instance()->snapshot() + const Document::Ptr preprocessedDoc = snapshot() .preprocessedDocument(contents, filePath); QByteArray content = R"(/* Created using Qt Creator's built-in preprocessor. */ /* See Tools -> Debug Qt Creator -> Inspect C++ Code Model for the parameters used. @@ -429,7 +432,7 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit) } const ProjectPart::ConstPtr projectPart = Utils::findOrDefault( - instance()->projectPart(filePath), [](const ProjectPart::ConstPtr &pp) { + CppModelManager::projectPart(filePath), [](const ProjectPart::ConstPtr &pp) { return pp->belongsToProject(ProjectTree::currentProject()); }); if (!projectPart) { @@ -468,6 +471,53 @@ void CppModelManager::showPreprocessedFile(bool inNextSplit) compiler->start(); } +static void foldOrUnfoldComments(bool unfold) +{ + IEditor * const currentEditor = EditorManager::currentEditor(); + if (!currentEditor) + return; + const auto editorWidget = qobject_cast<CppEditorWidget*>(currentEditor->widget()); + if (!editorWidget) + return; + TextEditor::TextDocument * const textDoc = editorWidget->textDocument(); + QTC_ASSERT(textDoc, return); + + const Document::Ptr cppDoc = CppModelManager::snapshot().preprocessedDocument( + textDoc->contents(), textDoc->filePath()); + QTC_ASSERT(cppDoc, return); + cppDoc->tokenize(); + TranslationUnit * const tu = cppDoc->translationUnit(); + if (!tu || !tu->isTokenized()) + return; + + for (int commentTokIndex = 0; commentTokIndex < tu->commentCount(); ++commentTokIndex) { + const Token &tok = tu->commentAt(commentTokIndex); + if (tok.kind() != T_COMMENT && tok.kind() != T_DOXY_COMMENT) + continue; + const int tokenPos = tu->getTokenPositionInDocument(tok, textDoc->document()); + const int tokenEndPos = tu->getTokenEndPositionInDocument(tok, textDoc->document()); + const QTextBlock tokenBlock = textDoc->document()->findBlock(tokenPos); + if (!tokenBlock.isValid()) + continue; + const QTextBlock nextBlock = tokenBlock.next(); + if (!nextBlock.isValid()) + continue; + if (tokenEndPos < nextBlock.position()) + continue; + if (TextEditor::TextDocumentLayout::foldingIndent(tokenBlock) + >= TextEditor::TextDocumentLayout::foldingIndent(nextBlock)) { + continue; + } + if (unfold) + editorWidget->unfold(tokenBlock); + else + editorWidget->fold(tokenBlock); + } +} + +void CppModelManager::foldComments() { foldOrUnfoldComments(false); } +void CppModelManager::unfoldComments() { foldOrUnfoldComments(true); } + class FindUnusedActionsEnabledSwitcher { public: @@ -512,13 +562,16 @@ static void checkNextFunctionForUnused( remainingLinks = remainingLinksList; activeLinks = activeLinksList; search->setUserData(data); - CppModelManager::instance()->modelManagerSupport(CppModelManager::Backend::Best) + CppModelManager::modelManagerSupport(CppModelManager::Backend::Best) ->checkUnused(link, search, [search, link, findRefsFuture, actionsSwitcher](const Link &) { if (!search || findRefsFuture->isCanceled()) return; const int newProgress = findRefsFuture->progressValue() + 1; - findRefsFuture->setProgressValueAndText(newProgress, Tr::tr("Checked %1 of %2 functions") - .arg(newProgress).arg(findRefsFuture->progressMaximum())); + findRefsFuture->setProgressValueAndText(newProgress, + Tr::tr("Checked %1 of %n function(s)", + nullptr, + findRefsFuture->progressMaximum()) + .arg(newProgress)); QVariantMap data = search->userData().toMap(); QVariant &activeLinks = data["active"]; QVariantList activeLinksList = activeLinks.toList(); @@ -619,7 +672,7 @@ void CppModelManager::checkForUnusedSymbol(SearchResult *search, const CPlusPlus::LookupContext &context, const LinkHandler &callback) { - instance()->d->m_findReferences->checkUnused(search, link, symbol, context, callback); + d->m_findReferences->checkUnused(search, link, symbol, context, callback); } int argumentPositionOf(const AST *last, const CallAST *callAst) @@ -641,7 +694,7 @@ int argumentPositionOf(const AST *last, const CallAST *callAst) SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, const QByteArray &content, - int position) const + int position) { if (content.isEmpty()) return SignalSlotType::None; @@ -652,7 +705,7 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, if (position > 2 && content.mid(position - 2, 2) == "::") fixedContent.insert(position, 'x'); - const Snapshot snapshot = this->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); const Document::Ptr document = snapshot.preprocessedDocument(fixedContent, filePath); document->check(); QTextDocument textDocument(QString::fromUtf8(fixedContent)); @@ -762,7 +815,7 @@ SignalSlotType CppModelManager::getSignalSlotType(const FilePath &filePath, FollowSymbolUnderCursor &CppModelManager::builtinFollowSymbol() { - return instance()->d->m_builtinModelManagerSupport.followSymbolInterface(); + return d->m_builtinModelManagerSupport.followSymbolInterface(); } template<class FilterClass> @@ -803,32 +856,32 @@ void CppModelManager::setCurrentDocumentFilter(std::unique_ptr<ILocatorFilter> & setFilter(d->m_currentDocumentFilter, std::move(filter)); } -ILocatorFilter *CppModelManager::locatorFilter() const +ILocatorFilter *CppModelManager::locatorFilter() { return d->m_locatorFilter.get(); } -ILocatorFilter *CppModelManager::classesFilter() const +ILocatorFilter *CppModelManager::classesFilter() { return d->m_classesFilter.get(); } -ILocatorFilter *CppModelManager::includesFilter() const +ILocatorFilter *CppModelManager::includesFilter() { return d->m_includesFilter.get(); } -ILocatorFilter *CppModelManager::functionsFilter() const +ILocatorFilter *CppModelManager::functionsFilter() { return d->m_functionsFilter.get(); } -IFindFilter *CppModelManager::symbolsFindFilter() const +IFindFilter *CppModelManager::symbolsFindFilter() { return d->m_symbolsFindFilter.get(); } -ILocatorFilter *CppModelManager::currentDocumentFilter() const +ILocatorFilter *CppModelManager::currentDocumentFilter() { return d->m_currentDocumentFilter.get(); } @@ -840,7 +893,7 @@ const FilePath &CppModelManager::configurationFileName() void CppModelManager::updateModifiedSourceFiles() { - const Snapshot snapshot = this->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); QList<Document::Ptr> documentsToCheck; for (const Document::Ptr &document : snapshot) documentsToCheck << document; @@ -868,7 +921,7 @@ CppModelManager *CppModelManager::instance() void CppModelManager::registerJsExtension() { - JsExpander::registerGlobalObject("Cpp", [this] { + JsExpander::registerGlobalObject("Cpp", [] { return new CppToolsJsExtension(&d->m_locatorData); }); } @@ -877,16 +930,16 @@ void CppModelManager::initCppTools() { // Objects connect(VcsManager::instance(), &VcsManager::repositoryChanged, - this, &CppModelManager::updateModifiedSourceFiles); + m_instance, &CppModelManager::updateModifiedSourceFiles); connect(DocumentManager::instance(), &DocumentManager::filesChangedInternally, - this, [this](const FilePaths &filePaths) { + m_instance, [](const FilePaths &filePaths) { updateSourceFiles(toSet(filePaths)); }); - connect(this, &CppModelManager::documentUpdated, + connect(m_instance, &CppModelManager::documentUpdated, &d->m_locatorData, &CppLocatorData::onDocumentUpdated); - connect(this, &CppModelManager::aboutToRemoveFiles, + connect(m_instance, &CppModelManager::aboutToRemoveFiles, &d->m_locatorData, &CppLocatorData::onAboutToRemoveFiles); // Set up builtin filters @@ -894,7 +947,7 @@ void CppModelManager::initCppTools() setClassesFilter(std::make_unique<CppClassesFilter>()); setIncludesFilter(std::make_unique<CppIncludesFilter>()); setFunctionsFilter(std::make_unique<CppFunctionsFilter>()); - setSymbolsFindFilter(std::make_unique<SymbolsFindFilter>(this)); + setSymbolsFindFilter(std::make_unique<SymbolsFindFilter>()); setCurrentDocumentFilter(std::make_unique<CppCurrentDocumentFilter>()); // Setup matchers LocatorMatcher::addMatcherCreator(MatcherType::AllSymbols, [] { @@ -912,11 +965,13 @@ void CppModelManager::initCppTools() } CppModelManager::CppModelManager() - : CppModelManagerBase(nullptr) - , d(new CppModelManagerPrivate) { + d = new CppModelManagerPrivate; m_instance = this; + CppModelManagerBase::registerSetExtraDiagnosticsCallback(&CppModelManager::setExtraDiagnostics); + CppModelManagerBase::registerSnapshotCallback(&CppModelManager::snapshot); + // Used for weak dependency in VcsBaseSubmitEditor setObjectName("CppModelManager"); ExtensionSystem::PluginManager::addObject(this); @@ -988,13 +1043,13 @@ CppModelManager::~CppModelManager() delete d; } -Snapshot CppModelManager::snapshot() const +Snapshot CppModelManager::snapshot() { QMutexLocker locker(&d->m_snapshotMutex); return d->m_snapshot; } -Document::Ptr CppModelManager::document(const FilePath &filePath) const +Document::Ptr CppModelManager::document(const FilePath &filePath) { QMutexLocker locker(&d->m_snapshotMutex); return d->m_snapshot.document(filePath); @@ -1028,7 +1083,7 @@ void CppModelManager::ensureUpdated() d->m_dirty = false; } -FilePaths CppModelManager::internalProjectFiles() const +FilePaths CppModelManager::internalProjectFiles() { FilePaths files; for (const ProjectData &projectData : std::as_const(d->m_projectData)) { @@ -1041,7 +1096,7 @@ FilePaths CppModelManager::internalProjectFiles() const return files; } -HeaderPaths CppModelManager::internalHeaderPaths() const +HeaderPaths CppModelManager::internalHeaderPaths() { HeaderPaths headerPaths; for (const ProjectData &projectData: std::as_const(d->m_projectData)) { @@ -1060,14 +1115,12 @@ static void addUnique(const Macros &newMacros, Macros ¯os, QSet<ProjectExplorer::Macro> &alreadyIn) { for (const ProjectExplorer::Macro ¯o : newMacros) { - if (!alreadyIn.contains(macro)) { + if (Utils::insert(alreadyIn, macro)) macros += macro; - alreadyIn.insert(macro); - } } } -Macros CppModelManager::internalDefinedMacros() const +Macros CppModelManager::internalDefinedMacros() { Macros macros; QSet<ProjectExplorer::Macro> alreadyIn; @@ -1095,7 +1148,7 @@ void CppModelManager::dumpModelManagerConfiguration(const QString &logFileId) ProjectExplorer::Macro::toByteArray(definedMacros())); } -QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() const +QSet<AbstractEditorSupport *> CppModelManager::abstractEditorSupports() { return d->m_extraEditorSupports; } @@ -1110,7 +1163,7 @@ void CppModelManager::removeExtraEditorSupport(AbstractEditorSupport *editorSupp d->m_extraEditorSupports.remove(editorSupport); } -CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const FilePath &filePath) const +CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const FilePath &filePath) { if (filePath.isEmpty()) return nullptr; @@ -1121,7 +1174,7 @@ CppEditorDocumentHandle *CppModelManager::cppEditorDocument(const FilePath &file BaseEditorDocumentProcessor *CppModelManager::cppEditorDocumentProcessor(const FilePath &filePath) { - const auto document = instance()->cppEditorDocument(filePath); + const auto document = cppEditorDocument(filePath); return document ? document->processor() : nullptr; } @@ -1225,17 +1278,17 @@ WorkingCopy CppModelManager::buildWorkingCopyList() return workingCopy; } -WorkingCopy CppModelManager::workingCopy() const +WorkingCopy CppModelManager::workingCopy() { - return const_cast<CppModelManager *>(this)->buildWorkingCopyList(); + return buildWorkingCopyList(); } -QByteArray CppModelManager::codeModelConfiguration() const +QByteArray CppModelManager::codeModelConfiguration() { return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration)); } -CppLocatorData *CppModelManager::locatorData() const +CppLocatorData *CppModelManager::locatorData() { return &d->m_locatorData; } @@ -1296,14 +1349,14 @@ QFuture<void> CppModelManager::updateSourceFiles(const QSet<FilePath> &sourceFil return d->m_internalIndexingSupport->refreshSourceFiles(filteredFiles, mode); } -ProjectInfoList CppModelManager::projectInfos() const +ProjectInfoList CppModelManager::projectInfos() { QReadLocker locker(&d->m_projectLock); return Utils::transform<QList<ProjectInfo::ConstPtr>>(d->m_projectData, [](const ProjectData &d) { return d.projectInfo; }); } -ProjectInfo::ConstPtr CppModelManager::projectInfo(Project *project) const +ProjectInfo::ConstPtr CppModelManager::projectInfo(Project *project) { QReadLocker locker(&d->m_projectLock); return d->m_projectData.value(project).projectInfo; @@ -1323,7 +1376,7 @@ void CppModelManager::removeProjectInfoFilesAndIncludesFromSnapshot(const Projec } } -const QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() const +const QList<CppEditorDocumentHandle *> CppModelManager::cppEditorDocuments() { QMutexLocker locker(&d->m_cppEditorDocumentsMutex); return d->m_cppEditorDocuments.values(); @@ -1415,8 +1468,11 @@ void CppModelManager::recalculateProjectPartMappings() for (const ProjectData &projectData : std::as_const(d->m_projectData)) { for (const ProjectPart::ConstPtr &projectPart : projectData.projectInfo->projectParts()) { d->m_projectPartIdToProjectProjectPart[projectPart->id()] = projectPart; - for (const ProjectFile &cxxFile : projectPart->files) - d->m_fileToProjectParts[cxxFile.path.canonicalPath()].append(projectPart); + for (const ProjectFile &cxxFile : projectPart->files) { + d->m_fileToProjectParts[cxxFile.path].append(projectPart); + if (FilePath canonical = cxxFile.path.canonicalPath(); canonical != cxxFile.path) + d->m_fileToProjectParts[canonical].append(projectPart); + } } } @@ -1441,7 +1497,7 @@ void CppModelManagerPrivate::setupWatcher(const QFuture<void> &future, Project * projectData->indexer->setFuture(future); } -void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const +void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) { // Refresh visible documents QSet<IDocument *> visibleCppEditorDocuments; @@ -1522,7 +1578,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::ConstPtr &ne const QSet<FilePath> removedFiles = comparer.removedFiles(); if (!removedFiles.isEmpty()) { filesRemoved = true; - emit aboutToRemoveFiles(transform<QStringList>(removedFiles, &FilePath::toString)); + emit m_instance->aboutToRemoveFiles(transform<QStringList>(removedFiles, &FilePath::toString)); removeFilesFromSnapshot(removedFiles); } } @@ -1558,10 +1614,10 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::ConstPtr &ne // Announce removed project parts if (!removedProjectParts.isEmpty()) - emit projectPartsRemoved(removedProjectParts); + emit m_instance->projectPartsRemoved(removedProjectParts); // Announce added project parts - emit projectPartsUpdated(project); + emit m_instance->projectPartsUpdated(project); // Ideally, we would update all the editor documents that depend on the 'filesToReindex'. // However, on e.g. a session restore first the editor documents are created and then the @@ -1576,32 +1632,39 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo::ConstPtr &ne // It's safe to do this here, as only the UI thread writes to the map and no other thread // uses the indexer value. - d->setupWatcher(indexingFuture, project, projectData, this); + d->setupWatcher(indexingFuture, project, projectData, m_instance); return indexingFuture; } -ProjectPart::ConstPtr CppModelManager::projectPartForId(const QString &projectPartId) const +ProjectPart::ConstPtr CppModelManager::projectPartForId(const QString &projectPartId) { QReadLocker locker(&d->m_projectLock); return d->m_projectPartIdToProjectProjectPart.value(projectPartId); } -QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const FilePath &fileName) const +QList<ProjectPart::ConstPtr> CppModelManager::projectPart(const FilePath &fileName) { - QReadLocker locker(&d->m_projectLock); - return d->m_fileToProjectParts.value(fileName.canonicalPath()); + { + QReadLocker locker(&d->m_projectLock); + auto it = d->m_fileToProjectParts.find(fileName); + if (it != d->m_fileToProjectParts.end()) + return it.value(); + } + const FilePath canonicalPath = fileName.canonicalPath(); + QWriteLocker locker(&d->m_projectLock); + auto it = d->m_fileToProjectParts.insert(fileName, d->m_fileToProjectParts.value(canonicalPath)); + return it.value(); } QList<ProjectPart::ConstPtr> CppModelManager::projectPartFromDependencies( - const FilePath &fileName) const + const FilePath &fileName) { QSet<ProjectPart::ConstPtr> parts; const FilePaths deps = snapshot().filesDependingOn(fileName); - QReadLocker locker(&d->m_projectLock); for (const FilePath &dep : deps) - parts.unite(Utils::toSet(d->m_fileToProjectParts.value(dep.canonicalPath()))); + parts.unite(Utils::toSet(projectPart(dep))); return parts.values(); } @@ -1619,10 +1682,10 @@ bool CppModelManager::isCppEditor(IEditor *editor) bool CppModelManager::usesClangd(const TextEditor::TextDocument *document) { - return instance()->d->m_activeModelManagerSupport->usesClangd(document); + return d->m_activeModelManagerSupport->usesClangd(document); } -bool CppModelManager::isClangCodeModelActive() const +bool CppModelManager::isClangCodeModelActive() { return d->m_activeModelManagerSupport != &d->m_builtinModelManagerSupport; } @@ -1630,19 +1693,19 @@ bool CppModelManager::isClangCodeModelActive() const void CppModelManager::emitDocumentUpdated(Document::Ptr doc) { if (replaceDocument(doc)) - emit documentUpdated(doc); + emit m_instance->documentUpdated(doc); } void CppModelManager::emitAbstractEditorSupportContentsUpdated(const QString &filePath, const QString &sourcePath, const QByteArray &contents) { - emit abstractEditorSupportContentsUpdated(filePath, sourcePath, contents); + emit m_instance->abstractEditorSupportContentsUpdated(filePath, sourcePath, contents); } void CppModelManager::emitAbstractEditorSupportRemoved(const QString &filePath) { - emit abstractEditorSupportRemoved(filePath); + emit m_instance->abstractEditorSupportRemoved(filePath); } void CppModelManager::onProjectAdded(Project *) @@ -1682,7 +1745,7 @@ void CppModelManager::onAboutToRemoveProject(Project *project) } if (!idsOfRemovedProjectParts.isEmpty()) - emit projectPartsRemoved(idsOfRemovedProjectParts); + emit m_instance->projectPartsRemoved(idsOfRemovedProjectParts); delayedGC(); } @@ -1701,7 +1764,7 @@ void CppModelManager::onActiveProjectChanged(Project *project) updateCppEditorDocuments(); } -void CppModelManager::onSourceFilesRefreshed() const +void CppModelManager::onSourceFilesRefreshed() { if (CppIndexingSupport::isFindErrorsIndexingActive()) { QTimer::singleShot(1, QCoreApplication::instance(), &QCoreApplication::quit); @@ -1733,10 +1796,10 @@ void CppModelManager::onAboutToLoadSession() GC(); } -QSet<QString> CppModelManager::dependingInternalTargets(const FilePath &file) const +QSet<QString> CppModelManager::dependingInternalTargets(const FilePath &file) { QSet<QString> result; - const Snapshot snapshot = this->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); QTC_ASSERT(snapshot.contains(file), return result); bool wasHeader; const FilePath correspondingFile @@ -1750,8 +1813,10 @@ QSet<QString> CppModelManager::dependingInternalTargets(const FilePath &file) co return result; } -QSet<QString> CppModelManager::internalTargets(const FilePath &filePath) const +QSet<QString> CppModelManager::internalTargets(const FilePath &filePath) { + QTC_ASSERT(m_instance, return {}); + const QList<ProjectPart::ConstPtr> projectParts = projectPart(filePath); // if we have no project parts it's most likely a header with declarations only and CMake based if (projectParts.isEmpty()) @@ -1878,6 +1943,8 @@ QSet<QString> CppModelManager::symbolsInFiles(const QSet<FilePath> &files) const void CppModelManager::onCoreAboutToClose() { + d->m_fallbackProjectPartTimer.disconnect(); + d->m_fallbackProjectPartTimer.stop(); ProgressManager::cancelTasks(Constants::TASK_INDEX); d->m_enableGC = false; } @@ -1919,7 +1986,7 @@ void CppModelManager::setupFallbackProjectPart() QMutexLocker locker(&d->m_fallbackProjectPartMutex); d->m_fallbackProjectPart = part; } - emit fallbackProjectPartUpdated(); + emit m_instance->fallbackProjectPartUpdated(); } void CppModelManager::GC() @@ -1949,9 +2016,8 @@ void CppModelManager::GC() const FilePath filePath = todo.last(); todo.removeLast(); - if (reachableFiles.contains(filePath)) + if (!Utils::insert(reachableFiles, filePath)) continue; - reachableFiles.insert(filePath); if (Document::Ptr doc = currentSnapshot.document(filePath)) todo += doc->includedFiles(); @@ -1970,14 +2036,14 @@ void CppModelManager::GC() } // Announce removing files and replace the snapshot - emit aboutToRemoveFiles(notReachableFiles); + emit m_instance->aboutToRemoveFiles(notReachableFiles); replaceSnapshot(newSnapshot); - emit gcFinished(); + emit m_instance->gcFinished(); } void CppModelManager::finishedRefreshingSourceFiles(const QSet<QString> &files) { - emit sourceFilesRefreshed(files); + emit m_instance->sourceFilesRefreshed(files); } void CppModelManager::activateClangCodeModel( @@ -1987,12 +2053,12 @@ void CppModelManager::activateClangCodeModel( d->m_activeModelManagerSupport = d->m_extendedModelManagerSupport.get(); } -CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const +CppCompletionAssistProvider *CppModelManager::completionAssistProvider() { return d->m_builtinModelManagerSupport.completionAssistProvider(); } -TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const +TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() { return d->m_builtinModelManagerSupport.createHoverHandler(); } @@ -2001,7 +2067,7 @@ void CppModelManager::followSymbol(const CursorInEditor &data, const LinkHandler &processLinkCallback, bool resolveTarget, bool inNextSplit, Backend backend) { - instance()->modelManagerSupport(backend)->followSymbol(data, processLinkCallback, + modelManagerSupport(backend)->followSymbol(data, processLinkCallback, resolveTarget, inNextSplit); } @@ -2009,7 +2075,7 @@ void CppModelManager::followSymbolToType(const CursorInEditor &data, const LinkHandler &processLinkCallback, bool inNextSplit, Backend backend) { - instance()->modelManagerSupport(backend)->followSymbolToType(data, processLinkCallback, + modelManagerSupport(backend)->followSymbolToType(data, processLinkCallback, inNextSplit); } @@ -2017,11 +2083,11 @@ void CppModelManager::switchDeclDef(const CursorInEditor &data, const LinkHandler &processLinkCallback, Backend backend) { - instance()->modelManagerSupport(backend)->switchDeclDef(data, processLinkCallback); + modelManagerSupport(backend)->switchDeclDef(data, processLinkCallback); } BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor( - TextEditor::TextDocument *baseTextDocument) const + TextEditor::TextDocument *baseTextDocument) { return d->m_activeModelManagerSupport->createEditorDocumentProcessor(baseTextDocument); } @@ -2077,12 +2143,12 @@ QThreadPool *CppModelManager::sharedThreadPool() return &d->m_threadPool; } -bool CppModelManager::setExtraDiagnostics(const QString &fileName, +bool CppModelManager::setExtraDiagnostics(const FilePath &filePath, const QString &kind, const QList<Document::DiagnosticMessage> &diagnostics) { d->m_diagnosticMessages = diagnostics; - emit diagnosticsChanged(fileName, kind); + emit m_instance->diagnosticsChanged(filePath, kind); return true; } diff --git a/src/plugins/cppeditor/cppmodelmanager.h b/src/plugins/cppeditor/cppmodelmanager.h index 3ab9ee26898..bdda63ee550 100644 --- a/src/plugins/cppeditor/cppmodelmanager.h +++ b/src/plugins/cppeditor/cppmodelmanager.h @@ -66,7 +66,7 @@ enum class SignalSlotType { None }; -class CPPEDITOR_EXPORT CppModelManager final : public CPlusPlus::CppModelManagerBase +class CPPEDITOR_EXPORT CppModelManager final : public QObject { Q_OBJECT @@ -80,95 +80,95 @@ public: static CppModelManager *instance(); - void registerJsExtension(); + static void registerJsExtension(); - // Documented in source file. - enum ProgressNotificationMode { + // Documented in source file. + enum ProgressNotificationMode { ForcedProgressNotification, ReservedProgressNotification }; - QFuture<void> updateSourceFiles(const QSet<Utils::FilePath> &sourceFiles, - ProgressNotificationMode mode = ReservedProgressNotification); - void updateCppEditorDocuments(bool projectsUpdated = false) const; - WorkingCopy workingCopy() const; - QByteArray codeModelConfiguration() const; - CppLocatorData *locatorData() const; + static QFuture<void> updateSourceFiles(const QSet<Utils::FilePath> &sourceFiles, + ProgressNotificationMode mode = ReservedProgressNotification); + static void updateCppEditorDocuments(bool projectsUpdated = false); + static WorkingCopy workingCopy(); + static QByteArray codeModelConfiguration(); + static CppLocatorData *locatorData(); - bool setExtraDiagnostics(const QString &fileName, - const QString &kind, - const QList<Document::DiagnosticMessage> &diagnostics) override; + static bool setExtraDiagnostics(const Utils::FilePath &filePath, + const QString &kind, + const QList<Document::DiagnosticMessage> &diagnostics); - const QList<Document::DiagnosticMessage> diagnosticMessages(); + static const QList<Document::DiagnosticMessage> diagnosticMessages(); - ProjectInfoList projectInfos() const; - ProjectInfo::ConstPtr projectInfo(ProjectExplorer::Project *project) const; - QFuture<void> updateProjectInfo(const ProjectInfo::ConstPtr &newProjectInfo, - const QSet<Utils::FilePath> &additionalFiles = {}); + static ProjectInfoList projectInfos(); + static ProjectInfo::ConstPtr projectInfo(ProjectExplorer::Project *project); + static QFuture<void> updateProjectInfo(const ProjectInfo::ConstPtr &newProjectInfo, + const QSet<Utils::FilePath> &additionalFiles = {}); /// \return The project part with the given project file - ProjectPart::ConstPtr projectPartForId(const QString &projectPartId) const; + static ProjectPart::ConstPtr projectPartForId(const QString &projectPartId); /// \return All project parts that mention the given file name as one of the sources/headers. - QList<ProjectPart::ConstPtr> projectPart(const Utils::FilePath &fileName) const; - QList<ProjectPart::ConstPtr> projectPart(const QString &fileName) const + static QList<ProjectPart::ConstPtr> projectPart(const Utils::FilePath &fileName); + static QList<ProjectPart::ConstPtr> projectPart(const QString &fileName) { return projectPart(Utils::FilePath::fromString(fileName)); } /// This is a fall-back function: find all files that includes the file directly or indirectly, /// and return its \c ProjectPart list for use with this file. - QList<ProjectPart::ConstPtr> projectPartFromDependencies(const Utils::FilePath &fileName) const; + static QList<ProjectPart::ConstPtr> projectPartFromDependencies(const Utils::FilePath &fileName); /// \return A synthetic \c ProjectPart which consists of all defines/includes/frameworks from /// all loaded projects. - ProjectPart::ConstPtr fallbackProjectPart(); + static ProjectPart::ConstPtr fallbackProjectPart(); - CPlusPlus::Snapshot snapshot() const override; - Document::Ptr document(const Utils::FilePath &filePath) const; - bool replaceDocument(Document::Ptr newDoc); + static CPlusPlus::Snapshot snapshot(); + static Document::Ptr document(const Utils::FilePath &filePath); + static bool replaceDocument(Document::Ptr newDoc); - void emitDocumentUpdated(Document::Ptr doc); - void emitAbstractEditorSupportContentsUpdated(const QString &filePath, + static void emitDocumentUpdated(Document::Ptr doc); + static void emitAbstractEditorSupportContentsUpdated(const QString &filePath, const QString &sourcePath, const QByteArray &contents); - void emitAbstractEditorSupportRemoved(const QString &filePath); + static void emitAbstractEditorSupportRemoved(const QString &filePath); static bool isCppEditor(Core::IEditor *editor); static bool usesClangd(const TextEditor::TextDocument *document); - bool isClangCodeModelActive() const; + static bool isClangCodeModelActive(); - QSet<AbstractEditorSupport*> abstractEditorSupports() const; - void addExtraEditorSupport(AbstractEditorSupport *editorSupport); - void removeExtraEditorSupport(AbstractEditorSupport *editorSupport); + static QSet<AbstractEditorSupport*> abstractEditorSupports(); + static void addExtraEditorSupport(AbstractEditorSupport *editorSupport); + static void removeExtraEditorSupport(AbstractEditorSupport *editorSupport); - const QList<CppEditorDocumentHandle *> cppEditorDocuments() const; - CppEditorDocumentHandle *cppEditorDocument(const Utils::FilePath &filePath) const; + static const QList<CppEditorDocumentHandle *> cppEditorDocuments(); + static CppEditorDocumentHandle *cppEditorDocument(const Utils::FilePath &filePath); static BaseEditorDocumentProcessor *cppEditorDocumentProcessor(const Utils::FilePath &filePath); - void registerCppEditorDocument(CppEditorDocumentHandle *cppEditorDocument); - void unregisterCppEditorDocument(const QString &filePath); + static void registerCppEditorDocument(CppEditorDocumentHandle *cppEditorDocument); + static void unregisterCppEditorDocument(const QString &filePath); - QList<int> references(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); + static QList<int> references(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); - SignalSlotType getSignalSlotType(const Utils::FilePath &filePath, - const QByteArray &content, - int position) const; + static SignalSlotType getSignalSlotType(const Utils::FilePath &filePath, + const QByteArray &content, + int position); - void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, - const QString &replacement = QString(), - const std::function<void()> &callback = {}); - void renameUsages(const CPlusPlus::Document::Ptr &doc, - const QTextCursor &cursor, - const CPlusPlus::Snapshot &snapshot, - const QString &replacement, - const std::function<void()> &callback); - void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); + static void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, + const QString &replacement = QString(), + const std::function<void()> &callback = {}); + static void renameUsages(const CPlusPlus::Document::Ptr &doc, + const QTextCursor &cursor, + const CPlusPlus::Snapshot &snapshot, + const QString &replacement, + const std::function<void()> &callback); + static void findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context); - void findMacroUsages(const CPlusPlus::Macro ¯o); - void renameMacroUsages(const CPlusPlus::Macro ¯o, const QString &replacement); + static void findMacroUsages(const CPlusPlus::Macro ¯o); + static void renameMacroUsages(const CPlusPlus::Macro ¯o, const QString &replacement); - void finishedRefreshingSourceFiles(const QSet<QString> &files); + static void finishedRefreshingSourceFiles(const QSet<QString> &files); - void activateClangCodeModel(std::unique_ptr<ModelManagerSupport> &&modelManagerSupport); - CppCompletionAssistProvider *completionAssistProvider() const; - BaseEditorDocumentProcessor *createEditorDocumentProcessor( - TextEditor::TextDocument *baseTextDocument) const; - TextEditor::BaseHoverHandler *createHoverHandler() const; + static void activateClangCodeModel(std::unique_ptr<ModelManagerSupport> &&modelManagerSupport); + static CppCompletionAssistProvider *completionAssistProvider(); + static BaseEditorDocumentProcessor *createEditorDocumentProcessor( + TextEditor::TextDocument *baseTextDocument); + static TextEditor::BaseHoverHandler *createHoverHandler(); static FollowSymbolUnderCursor &builtinFollowSymbol(); enum class Backend { Builtin, Best }; @@ -190,28 +190,29 @@ public: static void findUsages(const CursorInEditor &data, Backend backend = Backend::Best); static void switchHeaderSource(bool inNextSplit, Backend backend = Backend::Best); static void showPreprocessedFile(bool inNextSplit); + static void foldComments(); + static void unfoldComments(); static void findUnusedFunctions(const Utils::FilePath &folder); static void checkForUnusedSymbol(Core::SearchResult *search, const Utils::Link &link, CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, const Utils::LinkHandler &callback); + static CppIndexingSupport *indexingSupport(); - CppIndexingSupport *indexingSupport(); + static Utils::FilePaths projectFiles(); - Utils::FilePaths projectFiles(); - - ProjectExplorer::HeaderPaths headerPaths(); + static ProjectExplorer::HeaderPaths headerPaths(); // Use this *only* for auto tests - void setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths); + static void setHeaderPaths(const ProjectExplorer::HeaderPaths &headerPaths); - ProjectExplorer::Macros definedMacros(); + static ProjectExplorer::Macros definedMacros(); - void enableGarbageCollector(bool enable); + static void enableGarbageCollector(bool enable); - SymbolFinder *symbolFinder(); + static SymbolFinder *symbolFinder(); - QThreadPool *sharedThreadPool(); + static QThreadPool *sharedThreadPool(); static QSet<Utils::FilePath> timeStampModifiedFiles(const QList<Document::Ptr> &documentsToCheck); @@ -219,34 +220,34 @@ public: static const Utils::FilePath &configurationFileName(); static const Utils::FilePath &editorConfigurationFileName(); - void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); - void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); - void setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); - void setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); - void setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> &&filter); - void setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); + static void setLocatorFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); + static void setClassesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); + static void setIncludesFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); + static void setFunctionsFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); + static void setSymbolsFindFilter(std::unique_ptr<Core::IFindFilter> &&filter); + static void setCurrentDocumentFilter(std::unique_ptr<Core::ILocatorFilter> &&filter); - Core::ILocatorFilter *locatorFilter() const; - Core::ILocatorFilter *classesFilter() const; - Core::ILocatorFilter *includesFilter() const; - Core::ILocatorFilter *functionsFilter() const; - Core::IFindFilter *symbolsFindFilter() const; - Core::ILocatorFilter *currentDocumentFilter() const; + static Core::ILocatorFilter *locatorFilter(); + static Core::ILocatorFilter *classesFilter(); + static Core::ILocatorFilter *includesFilter(); + static Core::ILocatorFilter *functionsFilter(); + static Core::IFindFilter *symbolsFindFilter(); + static Core::ILocatorFilter *currentDocumentFilter(); /* * try to find build system target that depends on the given file - if the file is no header * try to find the corresponding header and use this instead to find the respective target */ - QSet<QString> dependingInternalTargets(const Utils::FilePath &file) const; + static QSet<QString> dependingInternalTargets(const Utils::FilePath &file); - QSet<QString> internalTargets(const Utils::FilePath &filePath) const; + static QSet<QString> internalTargets(const Utils::FilePath &filePath); - void renameIncludes(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath); + static void renameIncludes(const Utils::FilePath &oldFilePath, const Utils::FilePath &newFilePath); // for VcsBaseSubmitEditor Q_INVOKABLE QSet<QString> symbolsInFiles(const QSet<Utils::FilePath> &files) const; - ModelManagerSupport *modelManagerSupport(Backend backend) const; + static ModelManagerSupport *modelManagerSupport(Backend backend); signals: /// Project data might be locked while this is emitted. @@ -268,43 +269,40 @@ signals: void abstractEditorSupportRemoved(const QString &filePath); void fallbackProjectPartUpdated(); - void diagnosticsChanged(const QString &fileName, const QString &kind); + void diagnosticsChanged(const Utils::FilePath &filePath, const QString &kind); public slots: - void updateModifiedSourceFiles(); - void GC(); + static void updateModifiedSourceFiles(); + static void GC(); private: // This should be executed in the GUI thread. friend class Tests::ModelManagerTestHelper; - void onAboutToLoadSession(); - void onProjectAdded(ProjectExplorer::Project *project); - void onAboutToRemoveProject(ProjectExplorer::Project *project); - void onActiveProjectChanged(ProjectExplorer::Project *project); - void onSourceFilesRefreshed() const; - void onCurrentEditorChanged(Core::IEditor *editor); - void onCoreAboutToClose(); - void setupFallbackProjectPart(); + static void onAboutToLoadSession(); + static void onProjectAdded(ProjectExplorer::Project *project); + static void onAboutToRemoveProject(ProjectExplorer::Project *project); + static void onActiveProjectChanged(ProjectExplorer::Project *project); + static void onSourceFilesRefreshed(); + static void onCurrentEditorChanged(Core::IEditor *editor); + static void onCoreAboutToClose(); + static void setupFallbackProjectPart(); - void delayedGC(); - void recalculateProjectPartMappings(); + static void delayedGC(); + static void recalculateProjectPartMappings(); - void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot); - void removeFilesFromSnapshot(const QSet<Utils::FilePath> &removedFiles); - void removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo); + static void replaceSnapshot(const CPlusPlus::Snapshot &newSnapshot); + static void removeFilesFromSnapshot(const QSet<Utils::FilePath> &removedFiles); + static void removeProjectInfoFilesAndIncludesFromSnapshot(const ProjectInfo &projectInfo); - WorkingCopy buildWorkingCopyList(); + static WorkingCopy buildWorkingCopyList(); - void ensureUpdated(); - Utils::FilePaths internalProjectFiles() const; - ProjectExplorer::HeaderPaths internalHeaderPaths() const; - ProjectExplorer::Macros internalDefinedMacros() const; + static void ensureUpdated(); + static Utils::FilePaths internalProjectFiles(); + static ProjectExplorer::HeaderPaths internalHeaderPaths(); + static ProjectExplorer::Macros internalDefinedMacros(); - void dumpModelManagerConfiguration(const QString &logFileId); - void initCppTools(); - -private: - Internal::CppModelManagerPrivate *d; + static void dumpModelManagerConfiguration(const QString &logFileId); + static void initCppTools(); }; } // CppEditor diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp index cb292293e44..c2a9189fd63 100644 --- a/src/plugins/cppeditor/cppmodelmanager_test.cpp +++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp @@ -164,7 +164,7 @@ private: static ProjectPart::ConstPtr projectPartOfEditorDocument(const FilePath &filePath) { - auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + auto *editorDocument = CppModelManager::cppEditorDocument(filePath); QTC_ASSERT(editorDocument, return ProjectPart::ConstPtr()); return editorDocument->processor()->parser()->projectPartInfo().projectPart; } @@ -173,7 +173,6 @@ static ProjectPart::ConstPtr projectPartOfEditorDocument(const FilePath &filePat void ModelManagerTest::testPathsAreClean() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata")); @@ -188,9 +187,9 @@ void ModelManagerTest::testPathsAreClean() const auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); - mm->updateProjectInfo(pi); + CppModelManager::updateProjectInfo(pi); - ProjectExplorer::HeaderPaths headerPaths = mm->headerPaths(); + ProjectExplorer::HeaderPaths headerPaths = CppModelManager::headerPaths(); QCOMPARE(headerPaths.size(), 2); QVERIFY(headerPaths.contains(HeaderPath::makeUser(testDataDir.includeDir()))); QVERIFY(headerPaths.contains(HeaderPath::makeFramework(testDataDir.frameworksDir()))); @@ -203,7 +202,6 @@ void ModelManagerTest::testFrameworkHeaders() QSKIP("Can't resolve framework soft links on Windows."); ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata")); @@ -221,11 +219,11 @@ void ModelManagerTest::testFrameworkHeaders() const auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); - mm->updateProjectInfo(pi).waitForFinished(); + CppModelManager::updateProjectInfo(pi).waitForFinished(); QCoreApplication::processEvents(); - QVERIFY(mm->snapshot().contains(source)); - Document::Ptr doc = mm->document(source); + QVERIFY(CppModelManager::snapshot().contains(source)); + Document::Ptr doc = CppModelManager::document(source); QVERIFY(!doc.isNull()); CPlusPlus::Namespace *ns = doc->globalNamespace(); QVERIFY(ns); @@ -247,7 +245,6 @@ void ModelManagerTest::testFrameworkHeaders() void ModelManagerTest::testRefreshAlsoIncludesOfProjectFiles() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata")); @@ -268,7 +265,7 @@ void ModelManagerTest::testRefreshAlsoIncludesOfProjectFiles() QSet<FilePath> refreshedFiles = helper.updateProjectInfo(pi); QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testCpp)); - CPlusPlus::Snapshot snapshot = mm->snapshot(); + CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); QVERIFY(snapshot.contains(testHeader)); QVERIFY(snapshot.contains(testCpp)); @@ -287,7 +284,7 @@ void ModelManagerTest::testRefreshAlsoIncludesOfProjectFiles() QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testCpp)); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); QVERIFY(snapshot.contains(testHeader)); QVERIFY(snapshot.contains(testCpp)); @@ -304,7 +301,6 @@ void ModelManagerTest::testRefreshAlsoIncludesOfProjectFiles() void ModelManagerTest::testRefreshSeveralTimes() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata_refresh")); @@ -323,7 +319,7 @@ void ModelManagerTest::testRefreshSeveralTimes() }; const auto part = ProjectPart::create(project->projectFilePath(), rpp, {}, files); auto pi = ProjectInfo::create(ProjectUpdateInfo(project, KitInfo(nullptr), {}, {}), {part}); - mm->updateProjectInfo(pi); + CppModelManager::updateProjectInfo(pi); CPlusPlus::Snapshot snapshot; QSet<FilePath> refreshedFiles; @@ -344,7 +340,7 @@ void ModelManagerTest::testRefreshSeveralTimes() QVERIFY(refreshedFiles.contains(testHeader2)); QVERIFY(refreshedFiles.contains(testCpp)); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); QVERIFY(snapshot.contains(testHeader1)); QVERIFY(snapshot.contains(testHeader2)); QVERIFY(snapshot.contains(testCpp)); @@ -366,7 +362,6 @@ void ModelManagerTest::testRefreshSeveralTimes() void ModelManagerTest::testRefreshTestForChanges() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata_refresh")); const FilePath testCpp = testDataDir.filePath("source.cpp"); @@ -381,7 +376,7 @@ void ModelManagerTest::testRefreshTestForChanges() // Reindexing triggers a reparsing thread helper.resetRefreshedSourceFiles(); - QFuture<void> firstFuture = mm->updateProjectInfo(pi); + QFuture<void> firstFuture = CppModelManager::updateProjectInfo(pi); QVERIFY(firstFuture.isStarted() || firstFuture.isRunning()); firstFuture.waitForFinished(); const QSet<FilePath> refreshedFiles = helper.waitForRefreshedSourceFiles(); @@ -389,7 +384,7 @@ void ModelManagerTest::testRefreshTestForChanges() QVERIFY(refreshedFiles.contains(testCpp)); // No reindexing since nothing has changed - QFuture<void> subsequentFuture = mm->updateProjectInfo(pi); + QFuture<void> subsequentFuture = CppModelManager::updateProjectInfo(pi); QVERIFY(subsequentFuture.isCanceled() && subsequentFuture.isFinished()); } @@ -398,7 +393,6 @@ void ModelManagerTest::testRefreshTestForChanges() void ModelManagerTest::testRefreshAddedAndPurgeRemoved() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const MyTestDataDir testDataDir(_("testdata_refresh")); @@ -424,7 +418,7 @@ void ModelManagerTest::testRefreshAddedAndPurgeRemoved() QVERIFY(refreshedFiles.contains(testHeader1)); QVERIFY(refreshedFiles.contains(testCpp)); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); QVERIFY(snapshot.contains(testHeader1)); QVERIFY(snapshot.contains(testCpp)); @@ -440,7 +434,7 @@ void ModelManagerTest::testRefreshAddedAndPurgeRemoved() QCOMPARE(refreshedFiles.size(), 1); QVERIFY(refreshedFiles.contains(testHeader2)); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); QVERIFY(snapshot.contains(testHeader2)); QVERIFY(snapshot.contains(testCpp)); // The removed project file is not anymore in the snapshot @@ -461,7 +455,6 @@ void ModelManagerTest::testRefreshTimeStampModifiedIfSourcefilesChange() const FilePaths finalProjectFilePaths = toAbsolutePaths(finalProjectFiles, temporaryDir); ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); const auto project = helper.createProject(_("test_modelmanager_refresh_timeStampModified"), FilePath::fromString("blubb.pro")); @@ -480,7 +473,7 @@ void ModelManagerTest::testRefreshTimeStampModifiedIfSourcefilesChange() refreshedFiles = helper.updateProjectInfo(pi); QCOMPARE(refreshedFiles.size(), initialProjectFilePaths.size()); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); for (const FilePath &file : initialProjectFilePaths) { QVERIFY(refreshedFiles.contains(file)); QVERIFY(snapshot.contains(file)); @@ -509,7 +502,7 @@ void ModelManagerTest::testRefreshTimeStampModifiedIfSourcefilesChange() refreshedFiles = helper.updateProjectInfo(pi); QCOMPARE(refreshedFiles.size(), finalProjectFilePaths.size()); - snapshot = mm->snapshot(); + snapshot = CppModelManager::snapshot(); for (const FilePath &file : finalProjectFilePaths) { QVERIFY(refreshedFiles.contains(file)); QVERIFY(snapshot.contains(file)); @@ -550,7 +543,6 @@ void ModelManagerTest::testSnapshotAfterTwoProjects() ModelManagerTestHelper helper; ProjectCreator project1(&helper); ProjectCreator project2(&helper); - CppModelManager *mm = CppModelManager::instance(); // Project 1 project1.create(_("test_modelmanager_snapshot_after_two_projects.1"), @@ -559,10 +551,10 @@ void ModelManagerTest::testSnapshotAfterTwoProjects() refreshedFiles = helper.updateProjectInfo(project1.projectInfo); QCOMPARE(refreshedFiles, Utils::toSet(project1.projectFiles)); - const int snapshotSizeAfterProject1 = mm->snapshot().size(); + const int snapshotSizeAfterProject1 = CppModelManager::snapshot().size(); for (const FilePath &file : std::as_const(project1.projectFiles)) - QVERIFY(mm->snapshot().contains(file)); + QVERIFY(CppModelManager::snapshot().contains(file)); // Project 2 project2.create(_("test_modelmanager_snapshot_after_two_projects.2"), @@ -572,14 +564,14 @@ void ModelManagerTest::testSnapshotAfterTwoProjects() refreshedFiles = helper.updateProjectInfo(project2.projectInfo); QCOMPARE(refreshedFiles, Utils::toSet(project2.projectFiles)); - const int snapshotSizeAfterProject2 = mm->snapshot().size(); + const int snapshotSizeAfterProject2 = CppModelManager::snapshot().size(); QVERIFY(snapshotSizeAfterProject2 > snapshotSizeAfterProject1); QVERIFY(snapshotSizeAfterProject2 >= snapshotSizeAfterProject1 + project2.projectFiles.size()); for (const FilePath &file : std::as_const(project1.projectFiles)) - QVERIFY(mm->snapshot().contains(file)); + QVERIFY(CppModelManager::snapshot().contains(file)); for (const FilePath &file : std::as_const(project2.projectFiles)) - QVERIFY(mm->snapshot().contains(file)); + QVERIFY(CppModelManager::snapshot().contains(file)); } /// Check: (1) For a project with a *.ui file an AbstractEditorSupport object @@ -601,10 +593,9 @@ void ModelManagerTest::testExtraeditorsupportUiFiles() // Check working copy. // An AbstractEditorSupport object should have been added for the ui_* file. - CppModelManager *mm = CppModelManager::instance(); - WorkingCopy workingCopy = mm->workingCopy(); + WorkingCopy workingCopy = CppModelManager::workingCopy(); - QCOMPARE(workingCopy.size(), 2); // mm->configurationFileName() and "ui_*.h" + QCOMPARE(workingCopy.size(), 2); // CppModelManager::configurationFileName() and "ui_*.h" QStringList fileNamesInWorkinCopy; const WorkingCopy::Table &elements = workingCopy.elements(); @@ -613,16 +604,16 @@ void ModelManagerTest::testExtraeditorsupportUiFiles() fileNamesInWorkinCopy.sort(); const QString expectedUiHeaderFileName = _("ui_mainwindow.h"); - QCOMPARE(fileNamesInWorkinCopy.at(0), mm->configurationFileName().toString()); + QCOMPARE(fileNamesInWorkinCopy.at(0), CppModelManager::configurationFileName().toString()); QCOMPARE(fileNamesInWorkinCopy.at(1), expectedUiHeaderFileName); // Check CppSourceProcessor / includes. // The CppSourceProcessor is expected to find the ui_* file in the working copy. const FilePath fileIncludingTheUiFile = temporaryDir.absolutePath("mainwindow.cpp"); - while (!mm->snapshot().document(fileIncludingTheUiFile)) + while (!CppModelManager::snapshot().document(fileIncludingTheUiFile)) QCoreApplication::processEvents(); - const CPlusPlus::Snapshot snapshot = mm->snapshot(); + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); const Document::Ptr document = snapshot.document(fileIncludingTheUiFile); QVERIFY(document); const FilePaths includedFiles = document->includedFiles(); @@ -640,7 +631,6 @@ void ModelManagerTest::testGcIfLastCppeditorClosed() MyTestDataDir testDataDirectory(_("testdata_guiproject1")); const FilePath file = testDataDirectory.filePath("main.cpp"); - CppModelManager *mm = CppModelManager::instance(); helper.resetRefreshedSourceFiles(); // Open a file in the editor @@ -648,8 +638,8 @@ void ModelManagerTest::testGcIfLastCppeditorClosed() Core::IEditor *editor = Core::EditorManager::openEditor(file); QVERIFY(editor); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); - QVERIFY(mm->workingCopy().get(file)); + QVERIFY(CppModelManager::isCppEditor(editor)); + QVERIFY(CppModelManager::workingCopy().get(file)); // Wait until the file is refreshed helper.waitForRefreshedSourceFiles(); @@ -659,8 +649,8 @@ void ModelManagerTest::testGcIfLastCppeditorClosed() helper.waitForFinishedGc(); // Check: File is removed from the snapshpt - QVERIFY(!mm->workingCopy().get(file)); - QVERIFY(!mm->snapshot().contains(file)); + QVERIFY(!CppModelManager::workingCopy().get(file)); + QVERIFY(!CppModelManager::snapshot().contains(file)); } /// Check: Files that are open in the editor are not garbage collected. @@ -671,7 +661,6 @@ void ModelManagerTest::testDontGcOpenedFiles() MyTestDataDir testDataDirectory(_("testdata_guiproject1")); const FilePath file = testDataDirectory.filePath("main.cpp"); - CppModelManager *mm = CppModelManager::instance(); helper.resetRefreshedSourceFiles(); // Open a file in the editor @@ -679,24 +668,24 @@ void ModelManagerTest::testDontGcOpenedFiles() Core::IEditor *editor = Core::EditorManager::openEditor(file); QVERIFY(editor); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); + QVERIFY(CppModelManager::isCppEditor(editor)); // Wait until the file is refreshed and check whether it is in the working copy helper.waitForRefreshedSourceFiles(); - QVERIFY(mm->workingCopy().get(file)); + QVERIFY(CppModelManager::workingCopy().get(file)); // Run the garbage collector - mm->GC(); + CppModelManager::GC(); // Check: File is still there - QVERIFY(mm->workingCopy().get(file)); - QVERIFY(mm->snapshot().contains(file)); + QVERIFY(CppModelManager::workingCopy().get(file)); + QVERIFY(CppModelManager::snapshot().contains(file)); // Close editor Core::EditorManager::closeDocuments({editor->document()}); helper.waitForFinishedGc(); - QVERIFY(mm->snapshot().isEmpty()); + QVERIFY(CppModelManager::snapshot().isEmpty()); } namespace { @@ -735,8 +724,6 @@ void ModelManagerTest::testDefinesPerProject() const FilePath main2File = testDataDirectory.filePath("main2.cpp"); const FilePath header = testDataDirectory.filePath("header.h"); - CppModelManager *mm = CppModelManager::instance(); - const auto project = helper.createProject(_("test_modelmanager_defines_per_project"), Utils::FilePath::fromString("blubb.pro")); @@ -758,7 +745,7 @@ void ModelManagerTest::testDefinesPerProject() const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); - QCOMPARE(mm->snapshot().size(), 4); + QCOMPARE(CppModelManager::snapshot().size(), 4); // Open a file in the editor QCOMPARE(Core::DocumentModel::openedDocuments().size(), 0); @@ -778,9 +765,9 @@ void ModelManagerTest::testDefinesPerProject() EditorCloser closer(editor); QVERIFY(editor); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); + QVERIFY(CppModelManager::isCppEditor(editor)); - Document::Ptr doc = mm->document(i.filePath); + Document::Ptr doc = CppModelManager::document(i.filePath); QCOMPARE(nameOfFirstDeclaration(doc), firstDeclarationName); } } @@ -796,8 +783,6 @@ void ModelManagerTest::testPrecompiledHeaders() const FilePath pch1File = testDataDirectory.filePath("pch1.h"); const FilePath pch2File = testDataDirectory.filePath("pch2.h"); - CppModelManager *mm = CppModelManager::instance(); - const auto project = helper.createProject(_("test_modelmanager_defines_per_project_pch"), Utils::FilePath::fromString("blubb.pro")); @@ -820,7 +805,7 @@ void ModelManagerTest::testPrecompiledHeaders() const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); - QCOMPARE(mm->snapshot().size(), 4); + QCOMPARE(CppModelManager::snapshot().size(), 4); // Open a file in the editor QCOMPARE(Core::DocumentModel::openedDocuments().size(), 0); @@ -842,18 +827,17 @@ void ModelManagerTest::testPrecompiledHeaders() EditorCloser closer(editor); QVERIFY(editor); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); + QVERIFY(CppModelManager::isCppEditor(editor)); auto parser = BuiltinEditorDocumentParser::get(filePath); QVERIFY(parser); BaseEditorDocumentParser::Configuration config = parser->configuration(); config.usePrecompiledHeaders = true; parser->setConfiguration(config); - parser->update({CppModelManager::instance()->workingCopy(), nullptr, - Utils::Language::Cxx, false}); + parser->update({CppModelManager::workingCopy(), nullptr,Utils::Language::Cxx, false}); // Check if defines from pch are considered - Document::Ptr document = mm->document(filePath); + Document::Ptr document = CppModelManager::document(filePath); QCOMPARE(nameOfFirstDeclaration(document), firstDeclarationName); // Check if declarations from pch are considered @@ -876,8 +860,6 @@ void ModelManagerTest::testDefinesPerEditor() const FilePath main2File = testDataDirectory.filePath("main2.cpp"); const FilePath header = testDataDirectory.filePath("header.h"); - CppModelManager *mm = CppModelManager::instance(); - const auto project = helper.createProject(_("test_modelmanager_defines_per_editor"), Utils::FilePath::fromString("blubb.pro")); @@ -896,7 +878,7 @@ void ModelManagerTest::testDefinesPerEditor() const auto pi = ProjectInfo::create({project, KitInfo(nullptr), {}, {}}, {part1, part2}); helper.updateProjectInfo(pi); - QCOMPARE(mm->snapshot().size(), 4); + QCOMPARE(CppModelManager::snapshot().size(), 4); // Open a file in the editor QCOMPARE(Core::DocumentModel::openedDocuments().size(), 0); @@ -916,17 +898,16 @@ void ModelManagerTest::testDefinesPerEditor() EditorCloser closer(editor); QVERIFY(editor); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(mm->isCppEditor(editor)); + QVERIFY(CppModelManager::isCppEditor(editor)); const FilePath filePath = editor->document()->filePath(); const auto parser = BaseEditorDocumentParser::get(filePath); BaseEditorDocumentParser::Configuration config = parser->configuration(); config.editorDefines = editorDefines.toUtf8(); parser->setConfiguration(config); - parser->update({CppModelManager::instance()->workingCopy(), nullptr, - Utils::Language::Cxx, false}); + parser->update({CppModelManager::workingCopy(), nullptr, Utils::Language::Cxx, false}); - Document::Ptr doc = mm->document(main1File); + Document::Ptr doc = CppModelManager::document(main1File); QCOMPARE(nameOfFirstDeclaration(doc), firstDeclarationName); } } @@ -986,7 +967,7 @@ void ModelManagerTest::testUpdateEditorsAfterProjectUpdate() void ModelManagerTest::testRenameIncludes() { struct ModelManagerGCHelper { - ~ModelManagerGCHelper() { CppModelManager::instance()->GC(); } + ~ModelManagerGCHelper() { CppModelManager::GC(); } } GCHelper; Q_UNUSED(GCHelper) // do not warn about being unused @@ -997,7 +978,6 @@ void ModelManagerTest::testRenameIncludes() const QStringList fileNames = {"foo.h", "foo.cpp", "main.cpp"}; const FilePath oldHeader = FilePath::fromString(workingDir.filePath("foo.h")); const FilePath newHeader = FilePath::fromString(workingDir.filePath("bar.h")); - CppModelManager *modelManager = CppModelManager::instance(); const MyTestDataDir testDir(_("testdata_project1")); // Copy test files to a temporary directory @@ -1012,9 +992,9 @@ void ModelManagerTest::testRenameIncludes() } // Update the c++ model manager and check for the old includes - modelManager->updateSourceFiles(sourceFiles).waitForFinished(); + CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); QCoreApplication::processEvents(); - CPlusPlus::Snapshot snapshot = modelManager->snapshot(); + CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); for (const FilePath &sourceFile : std::as_const(sourceFiles)) { QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{oldHeader}); } @@ -1025,9 +1005,9 @@ void ModelManagerTest::testRenameIncludes() Core::HandleIncludeGuards::Yes)); // Update the c++ model manager again and check for the new includes - modelManager->updateSourceFiles(sourceFiles).waitForFinished(); + CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); QCoreApplication::processEvents(); - snapshot = modelManager->snapshot(); + snapshot = CppModelManager::snapshot(); for (const FilePath &sourceFile : std::as_const(sourceFiles)) { QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{newHeader}); } @@ -1036,7 +1016,7 @@ void ModelManagerTest::testRenameIncludes() void ModelManagerTest::testRenameIncludesInEditor() { struct ModelManagerGCHelper { - ~ModelManagerGCHelper() { CppModelManager::instance()->GC(); } + ~ModelManagerGCHelper() { CppModelManager::GC(); } } GCHelper; Q_UNUSED(GCHelper) // do not warn about being unused @@ -1054,7 +1034,6 @@ void ModelManagerTest::testRenameIncludesInEditor() const QString headerWithMalformedGuard(workingDir.filePath(_("baz3.h"))); const QString renamedHeaderWithMalformedGuard(workingDir.filePath(_("foobar5000.h"))); const FilePath mainFile = FilePath::fromString(workingDir.filePath("main.cpp")); - CppModelManager *modelManager = CppModelManager::instance(); const MyTestDataDir testDir(_("testdata_project1")); ModelManagerTestHelper helper; @@ -1072,9 +1051,9 @@ void ModelManagerTest::testRenameIncludesInEditor() } // Update the c++ model manager and check for the old includes - modelManager->updateSourceFiles(sourceFiles).waitForFinished(); + CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); QCoreApplication::processEvents(); - CPlusPlus::Snapshot snapshot = modelManager->snapshot(); + CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); for (const FilePath &sourceFile : std::as_const(sourceFiles)) { QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{headerWithPragmaOnce}); @@ -1087,8 +1066,8 @@ void ModelManagerTest::testRenameIncludesInEditor() EditorCloser editorCloser(editor); const QScopeGuard cleanup([] { Core::DocumentManager::saveAllModifiedDocumentsSilently(); }); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - QVERIFY(modelManager->isCppEditor(editor)); - QVERIFY(modelManager->workingCopy().get(mainFile)); + QVERIFY(CppModelManager::isCppEditor(editor)); + QVERIFY(CppModelManager::workingCopy().get(mainFile)); // Test the renaming of a header file where a pragma once guard is present QVERIFY(Core::FileUtils::renameFile(headerWithPragmaOnce, @@ -1150,9 +1129,9 @@ void ModelManagerTest::testRenameIncludesInEditor() // Update the c++ model manager again and check for the new includes TestCase::waitForProcessedEditorDocument(mainFile); - modelManager->updateSourceFiles(sourceFiles).waitForFinished(); + CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); QCoreApplication::processEvents(); - snapshot = modelManager->snapshot(); + snapshot = CppModelManager::snapshot(); for (const FilePath &sourceFile : std::as_const(sourceFiles)) { QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{renamedHeaderWithPragmaOnce}); @@ -1170,35 +1149,34 @@ void ModelManagerTest::testDocumentsAndRevisions() const QSet<FilePath> filesToIndex = {filePath1,filePath2}; QVERIFY(TestCase::parseFiles(filesToIndex)); - CppModelManager *modelManager = CppModelManager::instance(); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 1U); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 1U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath1), 1U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath2), 1U); // Open editor for file 1 TextEditor::BaseTextEditor *editor1; QVERIFY(helper.openCppEditor(filePath1, &editor1)); helper.closeEditorAtEndOfTestCase(editor1); QVERIFY(TestCase::waitForProcessedEditorDocument(filePath1)); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 2U); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 1U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath1), 2U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath2), 1U); // Index again QVERIFY(TestCase::parseFiles(filesToIndex)); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 3U); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 2U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath1), 3U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath2), 2U); // Open editor for file 2 TextEditor::BaseTextEditor *editor2; QVERIFY(helper.openCppEditor(filePath2, &editor2)); helper.closeEditorAtEndOfTestCase(editor2); QVERIFY(TestCase::waitForProcessedEditorDocument(filePath2)); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 3U); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 3U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath1), 3U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath2), 3U); // Index again QVERIFY(TestCase::parseFiles(filesToIndex)); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 4U); - VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 4U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath1), 4U); + VERIFY_DOCUMENT_REVISION(CppModelManager::document(filePath2), 4U); } } // CppEditor::Internal diff --git a/src/plugins/cppeditor/cppoutline.cpp b/src/plugins/cppeditor/cppoutline.cpp index 0d8883f631d..bbb641dc959 100644 --- a/src/plugins/cppeditor/cppoutline.cpp +++ b/src/plugins/cppeditor/cppoutline.cpp @@ -112,7 +112,7 @@ CppOutlineWidget::CppOutlineWidget(CppEditorWidget *editor) : QList<QAction*> CppOutlineWidget::filterMenuActions() const { - return QList<QAction*>(); + return {}; } void CppOutlineWidget::setCursorSynchronization(bool syncWithCursor) diff --git a/src/plugins/cppeditor/cpppointerdeclarationformatter_test.cpp b/src/plugins/cppeditor/cpppointerdeclarationformatter_test.cpp index 9072938b0f7..22fb303c476 100644 --- a/src/plugins/cppeditor/cpppointerdeclarationformatter_test.cpp +++ b/src/plugins/cppeditor/cpppointerdeclarationformatter_test.cpp @@ -107,8 +107,7 @@ public: Utils::ChangeSet change = formatter.format(ast); // ChangeSet may be empty. // Apply change - QTextCursor changeCursor(qtextDocument); - change.apply(&changeCursor); + change.apply(qtextDocument); // Compare QCOMPARE(qtextDocument->toPlainText(), expectedSource); diff --git a/src/plugins/cppeditor/cpppreprocessordialog.cpp b/src/plugins/cppeditor/cpppreprocessordialog.cpp index 87afd9761a2..a7a39750e95 100644 --- a/src/plugins/cppeditor/cpppreprocessordialog.cpp +++ b/src/plugins/cppeditor/cpppreprocessordialog.cpp @@ -26,7 +26,7 @@ CppPreProcessorDialog::CppPreProcessorDialog(const FilePath &filePath, QWidget * resize(400, 300); setWindowTitle(Tr::tr("Additional C++ Preprocessor Directives")); - const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath.toString(); + const Key key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + keyFromString(m_filePath.toString()); const QString directives = Core::SessionManager::value(key).toString(); m_editWidget = new TextEditor::SnippetEditorWidget; @@ -55,7 +55,7 @@ int CppPreProcessorDialog::exec() if (QDialog::exec() == Rejected) return Rejected; - const QString key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + m_filePath.toString(); + const Key key = Constants::EXTRA_PREPROCESSOR_DIRECTIVES + keyFromString(m_filePath.toString()); Core::SessionManager::setValue(key, extraPreprocessorDirectives()); return Accepted; diff --git a/src/plugins/cppeditor/cppprojectupdater.cpp b/src/plugins/cppeditor/cppprojectupdater.cpp index e4b57c90c5a..c75ee0cb522 100644 --- a/src/plugins/cppeditor/cppprojectupdater.cpp +++ b/src/plugins/cppeditor/cppprojectupdater.cpp @@ -81,8 +81,7 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo, } } GeneratedCodeModelSupport::update(extraCompilers); - auto updateFuture = CppModelManager::instance()->updateProjectInfo(storage->projectInfo, - compilerFiles); + auto updateFuture = CppModelManager::updateProjectInfo(storage->projectInfo, compilerFiles); m_futureSynchronizer.addFuture(updateFuture); m_taskTree.release()->deleteLater(); }; @@ -91,7 +90,7 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo, }; const Group root { - Storage(storage), + Tasking::Storage(storage), Group(tasks), onGroupDone(onDone), onGroupError(onError) diff --git a/src/plugins/cppeditor/cppqtstyleindenter.cpp b/src/plugins/cppeditor/cppqtstyleindenter.cpp index cbcaad47a2f..b7bdfb69b55 100644 --- a/src/plugins/cppeditor/cppqtstyleindenter.cpp +++ b/src/plugins/cppeditor/cppqtstyleindenter.cpp @@ -19,7 +19,7 @@ CppQtStyleIndenter::CppQtStyleIndenter(QTextDocument *doc) { // Just for safety. setCodeStylePreferences should be called when the editor the // indenter belongs to gets initialized. - m_cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); + m_cppCodeStylePreferences = CppToolsSettings::cppCodeStyle(); } CppQtStyleIndenter::~CppQtStyleIndenter() = default; diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index a3a8111ee8f..0c4a07a2c27 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -13,6 +13,8 @@ #include "cppsourceprocessertesthelper.h" #include "cpptoolssettings.h" +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginspec.h> #include <utils/fileutils.h> #include <QDebug> @@ -55,12 +57,12 @@ public: QList<TestDocumentPtr> singleDocument(const QByteArray &original, const QByteArray &expected) { - return QList<TestDocumentPtr>() - << CppTestDocument::create("file.cpp", original, expected); + return {CppTestDocument::create("file.cpp", original, expected)}; } BaseQuickFixTestCase::BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments, - const ProjectExplorer::HeaderPaths &headerPaths) + const ProjectExplorer::HeaderPaths &headerPaths, + const QByteArray &clangFormatSettings) : m_testDocuments(testDocuments) , m_cppCodeStylePreferences(0) , m_restoreHeaderPaths(false) @@ -85,11 +87,15 @@ BaseQuickFixTestCase::BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDoc document->writeToDisk(); } + // Create .clang-format file + if (!clangFormatSettings.isEmpty()) + m_temporaryDirectory->createFile(".clang-format", clangFormatSettings); + // Set appropriate include paths if (!headerPaths.isEmpty()) { m_restoreHeaderPaths = true; - m_headerPathsToRestore = m_modelManager->headerPaths(); - m_modelManager->setHeaderPaths(headerPaths); + m_headerPathsToRestore = CppModelManager::headerPaths(); + CppModelManager::setHeaderPaths(headerPaths); } // Update Code Model @@ -122,7 +128,7 @@ BaseQuickFixTestCase::BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDoc // Enforce the default cpp code style, so we are independent of config file settings. // This is needed by e.g. the GenerateGetterSetter quick fix. - m_cppCodeStylePreferences = CppToolsSettings::instance()->cppCodeStyle(); + m_cppCodeStylePreferences = CppToolsSettings::cppCodeStyle(); QVERIFY(m_cppCodeStylePreferences); m_cppCodeStylePreferencesOriginalDelegateId = m_cppCodeStylePreferences->currentDelegateId(); m_cppCodeStylePreferences->setCurrentDelegate("qt"); @@ -147,7 +153,7 @@ BaseQuickFixTestCase::~BaseQuickFixTestCase() // Restore include paths if (m_restoreHeaderPaths) - m_modelManager->setHeaderPaths(m_headerPathsToRestore); + CppModelManager::setHeaderPaths(m_headerPathsToRestore); // Remove created files from file system for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments)) @@ -182,8 +188,9 @@ QuickFixOperationTest::QuickFixOperationTest(const QList<TestDocumentPtr> &testD CppQuickFixFactory *factory, const ProjectExplorer::HeaderPaths &headerPaths, int operationIndex, - const QByteArray &expectedFailMessage) - : BaseQuickFixTestCase(testDocuments, headerPaths) + const QByteArray &expectedFailMessage, + const QByteArray &clangFormatSettings) + : BaseQuickFixTestCase(testDocuments, headerPaths, clangFormatSettings) { QVERIFY(succeededSoFar()); @@ -2738,15 +2745,15 @@ void QuickfixTest::testGenerateGetterSetterCustomTemplate() const _ customTypeDecl = R"--( namespace N1 { - namespace N2 { - struct test{}; - } - template<typename T> - struct custom { - void assign(const custom<T>&); - bool equals(const custom<T>&); - T* get(); - }; +namespace N2 { +struct test{}; +} +template<typename T> +struct custom { + void assign(const custom<T>&); + bool equals(const custom<T>&); + T* get(); +}; )--"; // Header File original = customTypeDecl + R"--( @@ -3471,14 +3478,12 @@ void CppCodeStyleSettingsChanger::setSettings(const CppCodeStyleSettings &settin QVariant variant; variant.setValue(settings); - CppCodeStylePreferences *preferences = CppToolsSettings::instance()->cppCodeStyle(); - preferences->currentDelegate()->setValue(variant); + CppToolsSettings::cppCodeStyle()->currentDelegate()->setValue(variant); } CppCodeStyleSettings CppCodeStyleSettingsChanger::currentSettings() { - CppCodeStylePreferences *preferences = CppToolsSettings::instance()->cppCodeStyle(); - return preferences->currentDelegate()->value().value<CppCodeStyleSettings>(); + return CppToolsSettings::cppCodeStyle()->currentDelegate()->value().value<CppCodeStyleSettings>(); } void QuickfixTest::testGenerateGettersSetters_data() @@ -5202,77 +5207,77 @@ void QuickfixTest::testInsertDefsFromDecls_data() QTest::addColumn<int>("mode"); QByteArray origHeader = R"( - namespace N { - class @C - { - public: - friend void ignoredFriend(); - void ignoredImplemented() {}; - void ignoredImplemented2(); // Below - void ignoredImplemented3(); // In cpp file - void funcNotSelected(); - void funcInline(); - void funcBelow(); - void funcCppFile(); +namespace N { +class @C +{ +public: + friend void ignoredFriend(); + void ignoredImplemented() {}; + void ignoredImplemented2(); // Below + void ignoredImplemented3(); // In cpp file + void funcNotSelected(); + void funcInline(); + void funcBelow(); + void funcCppFile(); - signals: - void ignoredSignal(); - }; +signals: + void ignoredSignal(); +}; - inline void C::ignoredImplemented2() {} +inline void C::ignoredImplemented2() {} - } // namespace N)"; +} // namespace N)"; QByteArray origSource = R"( - #include "file.h" +#include "file.h" - namespace N { +namespace N { - void C::ignoredImplemented3() {} +void C::ignoredImplemented3() {} - } // namespace N)"; +} // namespace N)"; QByteArray expectedHeader = R"( - namespace N { - class C - { - public: - friend void ignoredFriend(); - void ignoredImplemented() {}; - void ignoredImplemented2(); // Below - void ignoredImplemented3(); // In cpp file - void funcNotSelected(); - void funcInline() - { +namespace N { +class C +{ +public: + friend void ignoredFriend(); + void ignoredImplemented() {}; + void ignoredImplemented2(); // Below + void ignoredImplemented3(); // In cpp file + void funcNotSelected(); + void funcInline() + { - } - void funcBelow(); - void funcCppFile(); + } + void funcBelow(); + void funcCppFile(); - signals: - void ignoredSignal(); - }; +signals: + void ignoredSignal(); +}; - inline void C::ignoredImplemented2() {} +inline void C::ignoredImplemented2() {} - inline void C::funcBelow() - { +inline void C::funcBelow() +{ - } +} - } // namespace N)"; +} // namespace N)"; QByteArray expectedSource = R"( - #include "file.h" +#include "file.h" - namespace N { +namespace N { - void C::ignoredImplemented3() {} +void C::ignoredImplemented3() {} - void C::funcCppFile() - { +void C::funcCppFile() +{ - } +} - } // namespace N)"; +} // namespace N)"; QTest::addRow("normal case") << QByteArrayList{origHeader, expectedHeader} << QByteArrayList{origSource, expectedSource} @@ -5336,6 +5341,63 @@ void QuickfixTest::testInsertDefsFromDecls() QuickFixOperationTest(testDocuments, &factory); } +void QuickfixTest::testInsertAndFormatDefsFromDecls() +{ + static const auto isClangFormatPresent = [] { + using namespace ExtensionSystem; + return Utils::contains(PluginManager::plugins(), [](const PluginSpec *plugin) { + return plugin->name() == "ClangFormat" && plugin->isEffectivelyEnabled(); + }); + }; + if (!isClangFormatPresent()) + QSKIP("This test reqires ClangFormat"); + + const QByteArray origHeader = R"( +class @C +{ +public: + void func1 (int const &i); + void func2 (double const d); +}; +)"; + const QByteArray origSource = R"( +#include "file.h" +)"; + + const QByteArray expectedSource = R"( +#include "file.h" + +void C::func1 (int const &i) +{ + +} + +void C::func2 (double const d) +{ + +} +)"; + + const QByteArray clangFormatSettings = R"( +BreakBeforeBraces: Allman +QualifierAlignment: Right +SpaceBeforeParens: Always +)"; + + const QList<TestDocumentPtr> testDocuments({ + CppTestDocument::create("file.h", origHeader, origHeader), + CppTestDocument::create("file.cpp", origSource, expectedSource)}); + InsertDefsFromDecls factory; + factory.setMode(InsertDefsFromDecls::Mode::Impl); + CppCodeStylePreferences * const prefs = CppToolsSettings::cppCodeStyle(); + const CppCodeStyleSettings settings = prefs->codeStyleSettings(); + CppCodeStyleSettings tempSettings = settings; + tempSettings.forceFormatting = true; + prefs->setCodeStyleSettings(tempSettings); + QuickFixOperationTest(testDocuments, &factory, {}, {}, {}, clangFormatSettings); + prefs->setCodeStyleSettings(settings); +} + // Function for one of InsertDeclDef section cases void insertToSectionDeclFromDef(const QByteArray §ion, int sectionIndex) { @@ -6807,15 +6869,15 @@ void QuickfixTest::testMoveFuncDefOutsideMemberFuncOutsideWithNs() QByteArray original = "namespace MyNs {\n" "class Foo {\n" - " inline int numbe@r() const\n" - " {\n" - " return 5;\n" - " }\n" + " inline int numbe@r() const\n" + " {\n" + " return 5;\n" + " }\n" "};}\n"; QByteArray expected = "namespace MyNs {\n" "class Foo {\n" - " int number() const;\n" + " int number() const;\n" "};\n" "\n" "int Foo::number() const\n" @@ -7781,7 +7843,8 @@ void QuickfixTest::testExtractFunction_data() QTest::addColumn<QByteArray>("expected"); QTest::newRow("basic") - << _("void f()\n" + << _("// Documentation for f\n" + "void f()\n" "{\n" " @{start}g();@{end}\n" "}\n") @@ -7790,6 +7853,7 @@ void QuickfixTest::testExtractFunction_data() " g();\n" "}\n" "\n" + "// Documentation for f\n" "void f()\n" "{\n" " extracted();\n" @@ -8920,4 +8984,352 @@ void QuickfixTest::testGenerateConstructor() QuickFixOperationTest(testDocuments, &factory); } +void QuickfixTest::testChangeCommentType_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("expectedOutput"); + + QTest::newRow("C -> C++ / no selection / single line") << R"( +int var1; +/* Other comment, unaffected */ +/* Our @comment */ +/* Another unaffected comment */ +int var2;)" << R"( +int var1; +/* Other comment, unaffected */ +// Our comment +/* Another unaffected comment */ +int var2;)"; + + QTest::newRow("C -> C++ / no selection / multi-line / preserved header and footer") << R"( +/**************************************************** + * some info + * more @info + ***************************************************/)" << R"( +///////////////////////////////////////////////////// +// some info +// more info +/////////////////////////////////////////////////////)"; + + QTest::newRow("C -> C++ / no selection / multi-line / non-preserved header and footer") << R"( +/* + * some info + * more @info + */)" << R"( +// some info +// more info +)"; + + QTest::newRow("C -> C++ / no selection / qdoc") << R"( +/*! + \qmlproperty string Type::element.name + \qmlproperty int Type::element.id + + \brief Holds the @element name and id. +*/)" << R"( +//! \qmlproperty string Type::element.name +//! \qmlproperty @int Type::element.id +//! +//! \brief Holds the element name and id. +)"; + + QTest::newRow("C -> C++ / no selection / doxygen") << R"( +/*! \class Test + \brief A test class. + + A more detailed @class description. +*/)" << R"( +//! \class Test +//! \brief A test class. +//! +//! A more detailed class description. +)"; + + QTest::newRow("C -> C++ / selection / single line") << R"( +int var1; +/* Other comment, unaffected */ +@{start}/* Our comment */@{end} +/* Another unaffected comment */ +int var2;)" << R"( +int var1; +/* Other comment, unaffected */ +// Our comment +/* Another unaffected comment */ +int var2;)"; + + QTest::newRow("C -> C++ / selection / multi-line / preserved header and footer") << R"( +/**************************************************** + * @{start}some info + * more info@{end} + ***************************************************/)" << R"( +///////////////////////////////////////////////////// +// some info +// more info +/////////////////////////////////////////////////////)"; + + QTest::newRow("C -> C++ / selection / multi-line / non-preserved header and footer") << R"( +/*@{start} + * some in@{end}fo + * more info + */)" << R"( +// some info +// more info +)"; + + QTest::newRow("C -> C++ / selection / qdoc") << R"( +/*!@{start} + \qmlproperty string Type::element.name + \qmlproperty int Type::element.id + + \brief Holds the element name and id. +*/@{end})" << R"( +//! \qmlproperty string Type::element.name +//! \qmlproperty int Type::element.id +//! +//! \brief Holds the element name and id. +)"; + + QTest::newRow("C -> C++ / selection / doxygen") << R"( +/** Expand envi@{start}ronment variables in a string. + * + * Environment variables are accepted in the @{end}following forms: + * $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows. + * No escapes and quoting are supported. + * If a variable is not found, it is not substituted. + */)" << R"( +//! Expand environment variables in a string. +//! +//! Environment variables are accepted in the following forms: +//! $SOMEVAR, ${SOMEVAR} on Unix and %SOMEVAR% on Windows. +//! No escapes and quoting are supported. +//! If a variable is not found, it is not substituted. +)"; + + QTest::newRow("C -> C++ / selection / multiple comments") << R"( +@{start}/* Affected comment */ +/* Another affected comment */ +/* A third affected comment */@{end} +/* An unaffected comment */)" << R"( +// Affected comment +// Another affected comment +// A third affected comment +/* An unaffected comment */)"; + + QTest::newRow("C++ -> C / no selection / single line") << R"( +// Other comment, unaffected +// Our @comment +// Another unaffected comment)" << R"( +// Other comment, unaffected +/* Our comment */ +// Another unaffected comment)"; + + QTest::newRow("C++ -> C / selection / single line") << R"( +// Other comment, unaffected +@{start}// Our comment@{end} +// Another unaffected comment)" << R"( +// Other comment, unaffected +/* Our comment */ +// Another unaffected comment)"; + + QTest::newRow("C++ -> C / selection / multi-line / preserved header and footer") << R"( +@{start}///////////////////////////////////////////////////// +// some info +// more info +/////////////////////////////////////////////////////@{end})" << R"( +/****************************************************/ +/* some info */ +/* more info */ +/****************************************************/)"; + + QTest::newRow("C++ -> C / selection / qdoc") << R"( +@{start}//! \qmlproperty string Type::element.name +//! \qmlproperty int Type::element.id +//! +//! \brief Holds the element name and id.@{end} +)" << R"( +/*! + \qmlproperty string Type::element.name + \qmlproperty int Type::element.id + + \brief Holds the element name and id. +*/ +)"; + + QTest::newRow("C++ -> C / selection / doxygen") << R"( +@{start}//! \class Test +//! \brief A test class. +//! +//! A more detailed class description.@{end} +)" << R"( +/*! + \class Test + \brief A test class. + + A more detailed class description. +*/ +)"; +} + +void QuickfixTest::testChangeCommentType() +{ + QFETCH(QString, input); + QFETCH(QString, expectedOutput); + + ConvertCommentStyle factory; + QuickFixOperationTest( + {CppTestDocument::create("file.h", input.toUtf8(), expectedOutput.toUtf8())}, + &factory); +} + +void QuickfixTest::testMoveComments_data() +{ + QTest::addColumn<QByteArrayList>("headers"); + QTest::addColumn<QByteArrayList>("sources"); + + const QByteArrayList headersFuncDecl2Def{R"( +// Function comment +void @aFunction(); +)", R"( +void aFunction(); +)"}; + const QByteArrayList sourcesFuncDecl2Def{R"( +#include "file.h" + +void aFunction() {} +)", R"( +#include "file.h" + +// Function comment +void aFunction() {} +)"}; + QTest::newRow("function: from decl to def") << headersFuncDecl2Def << sourcesFuncDecl2Def; + + const QByteArrayList headersFuncDef2Decl{R"( +void aFunction(); +)", R"( +/* function */ +/* comment */ +void aFunction(); +)"}; + const QByteArrayList sourcesFuncDef2Decl{R"( +#include "file.h" + +/* function */ +/* comment */ +void a@Function() {} +)", R"( +#include "file.h" + +void aFunction() {} +)"}; + QTest::newRow("function: from def to decl") << headersFuncDef2Decl << sourcesFuncDef2Decl; + + const QByteArrayList headersFuncNoDef{R"( +// Function comment +void @aFunction(); +)", R"( +// Function comment +void aFunction(); +)"}; + QTest::newRow("function: no def") << headersFuncNoDef << QByteArrayList(); + + const QByteArrayList headersFuncNoDecl{R"( +// Function comment +inline void @aFunction() {} +)", R"( +// Function comment +inline void aFunction() {} +)"}; + QTest::newRow("function: no decl") << headersFuncNoDecl << QByteArrayList(); + + const QByteArrayList headersFuncTemplateDecl2Def{R"( +// Function comment +template<typename T> T @aFunction(); + +template<typename T> inline T aFunction() { return T(); } +)", R"( +template<typename T> T aFunction(); + +// Function comment +template<typename T> inline T aFunction() { return T(); } +)"}; + QTest::newRow("function template: from decl to def") << headersFuncTemplateDecl2Def + << QByteArrayList(); + + const QByteArrayList headersFuncTemplateDef2Decl{R"( +template<typename T> T aFunction(); + +// Function comment +template<typename T> inline T @aFunction() { return T(); } +)", R"( +// Function comment +template<typename T> T aFunction(); + +template<typename T> inline T aFunction() { return T(); } +)"}; + QTest::newRow("function template: from def to decl") << headersFuncTemplateDef2Decl + << QByteArrayList(); + + const QByteArrayList headersMemberDecl2Def{R"( +class C { + // Member function comment + void @aMember(); +)", R"( +class C { + void aMember(); +)"}; + const QByteArrayList sourcesMemberDecl2Def{R"( +#include "file.h" + +void C::aMember() {} +)", R"( +#include "file.h" + +// Member function comment +void C::aMember() {} +)"}; + QTest::newRow("member function: from decl to def") << headersMemberDecl2Def + << sourcesMemberDecl2Def; + + const QByteArrayList headersMemberDef2Decl{R"( +class C { + void aMember(); +)", R"( +class C { + // Member function comment + void aMember(); +)"}; + const QByteArrayList sourcesMemberDef2Decl{R"( +#include "file.h" + +// Member function comment +void C::aMember() {@} +)", R"( +#include "file.h" + +void C::aMember() {} +)"}; + QTest::newRow("member function: from def to decl") << headersMemberDef2Decl + << sourcesMemberDef2Decl; +} + +void QuickfixTest::testMoveComments() +{ + QFETCH(QByteArrayList, headers); + QFETCH(QByteArrayList, sources); + + QList<TestDocumentPtr> documents; + QCOMPARE(headers.size(), 2); + documents << CppTestDocument::create("file.h", headers.at(0), headers.at(1)); + if (!sources.isEmpty()) { + QCOMPARE(sources.size(), 2); + documents << CppTestDocument::create("file.cpp", sources.at(0), sources.at(1)); + } + MoveFunctionComments factory; + QByteArray failMessage; + if (QByteArray(QTest::currentDataTag()) == "function template: from def to decl") + failMessage = "decl/def switch doesn't work for templates"; + QuickFixOperationTest(documents, &factory, {}, {}, failMessage); +} + } // namespace CppEditor::Internal::Tests diff --git a/src/plugins/cppeditor/cppquickfix_test.h b/src/plugins/cppeditor/cppquickfix_test.h index 2d0dd0ebc9d..89f05b0f160 100644 --- a/src/plugins/cppeditor/cppquickfix_test.h +++ b/src/plugins/cppeditor/cppquickfix_test.h @@ -1,6 +1,8 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#pragma once + #include "cppquickfix.h" #include "cpptoolstestcase.h" @@ -26,8 +28,8 @@ public: /// Exactly one QuickFixTestDocument must contain the cursor position marker '@' /// or "@{start}" and "@{end}" BaseQuickFixTestCase(const QList<TestDocumentPtr> &testDocuments, - const ProjectExplorer::HeaderPaths &headerPaths - = ProjectExplorer::HeaderPaths()); + const ProjectExplorer::HeaderPaths &headerPaths, + const QByteArray &clangFormatSettings = {}); ~BaseQuickFixTestCase(); @@ -54,7 +56,8 @@ public: const ProjectExplorer::HeaderPaths &headerPaths = ProjectExplorer::HeaderPaths(), int operationIndex = 0, - const QByteArray &expectedFailMessage = QByteArray()); + const QByteArray &expectedFailMessage = {}, + const QByteArray &clangFormatSettings = {}); static void run(const QList<TestDocumentPtr> &testDocuments, CppQuickFixFactory *factory, @@ -139,6 +142,7 @@ private slots: void testInsertDefFromDeclAliasTemplateAsReturnType(); void testInsertDefsFromDecls_data(); void testInsertDefsFromDecls(); + void testInsertAndFormatDefsFromDecls(); void testInsertDeclFromDef(); void testInsertDeclFromDefTemplateFuncTypename(); @@ -217,6 +221,12 @@ private slots: void testGenerateConstructor_data(); void testGenerateConstructor(); + + void testChangeCommentType_data(); + void testChangeCommentType(); + + void testMoveComments_data(); + void testMoveComments(); }; } // namespace Tests diff --git a/src/plugins/cppeditor/cppquickfixassistant.cpp b/src/plugins/cppeditor/cppquickfixassistant.cpp index 19514387d71..9202e639a28 100644 --- a/src/plugins/cppeditor/cppquickfixassistant.cpp +++ b/src/plugins/cppeditor/cppquickfixassistant.cpp @@ -51,7 +51,7 @@ CppQuickFixInterface::CppQuickFixInterface(CppEditorWidget *editor, AssistReason : AssistInterface(editor->textCursor(), editor->textDocument()->filePath(), reason) , m_editor(editor) , m_semanticInfo(editor->semanticInfo()) - , m_snapshot(CppModelManager::instance()->snapshot()) + , m_snapshot(CppModelManager::snapshot()) , m_currentFile(CppRefactoringChanges::file(editor, m_semanticInfo.doc)) , m_context(m_semanticInfo.doc, m_snapshot) { diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index a7134169d62..7f08b962009 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -27,6 +27,7 @@ #include <cplusplus/ASTPath.h> #include <cplusplus/CPlusPlusForwardDeclarations.h> #include <cplusplus/CppRewriter.h> +#include <cplusplus/declarationcomments.h> #include <cplusplus/NamePrettyPrinter.h> #include <cplusplus/TypeOfExpression.h> #include <cplusplus/TypePrettyPrinter.h> @@ -1899,10 +1900,9 @@ ProjectExplorer::HeaderPaths relevantHeaderPaths(const QString &filePath) { ProjectExplorer::HeaderPaths headerPaths; - CppModelManager *modelManager = CppModelManager::instance(); - const QList<ProjectPart::ConstPtr> projectParts = modelManager->projectPart(filePath); + const QList<ProjectPart::ConstPtr> projectParts = CppModelManager::projectPart(filePath); if (projectParts.isEmpty()) { // Not part of any project, better use all include paths than none - headerPaths += modelManager->headerPaths(); + headerPaths += CppModelManager::headerPaths(); } else { for (const ProjectPart::ConstPtr &part : projectParts) headerPaths += part->headerPaths; @@ -1942,7 +1942,7 @@ LookupResult lookUpDefinition(const CppQuickFixInterface &interface, const NameA // Find the enclosing scope int line, column; const Document::Ptr doc = interface.semanticInfo().doc; - doc->translationUnit()->getTokenStartPosition(nameAst->firstToken(), &line, &column); + doc->translationUnit()->getTokenPosition(nameAst->firstToken(), &line, &column); Scope *scope = doc->scopeAt(line, column); if (!scope) return LookupResult::NotDeclared; @@ -1997,7 +1997,7 @@ QList<IndexItem::Ptr> matchName(const Name *name, QString *className) QString simpleName; QList<IndexItem::Ptr> matches; - CppLocatorData *locatorData = CppModelManager::instance()->locatorData(); + CppLocatorData *locatorData = CppModelManager::locatorData(); const Overview oo; if (const QualifiedNameId *qualifiedName = name->asQualifiedNameId()) { const Name *name = qualifiedName->name(); @@ -3478,6 +3478,15 @@ private: settings = dlg.settings(); break; } + case InsertDefsFromDecls::Mode::Impl: { + for (Symbol * const func : std::as_const(unimplemented)) { + MemberFunctionImplSetting setting; + setting.func = func; + setting.defPos = DefPosImplementationFile; + settings << setting; + } + break; + } case InsertDefsFromDecls::Mode::Alternating: { int defPos = DefPosImplementationFile; const auto incDefPos = [&defPos] { @@ -5142,6 +5151,16 @@ public: // formatting) it's simpler to have two different change sets. ChangeSet change; int position = currentFile->startOf(m_refFuncDef); + + // Do not insert right between the function and an associated comment. + const QList<Token> functionDoc = commentsForDeclaration( + m_refFuncDef->symbol, m_refFuncDef, *currentFile->document(), + currentFile->cppDocument()); + if (!functionDoc.isEmpty()) { + position = currentFile->cppDocument()->translationUnit()->getTokenPositionInDocument( + functionDoc.first(), currentFile->document()); + } + change.insert(position, funcDef); change.replace(m_extractionStart, m_extractionEnd, funcCall); currentFile->setChangeSet(change); @@ -8342,14 +8361,13 @@ private: QList<Snapshot::IncludeLocation> includeLocationsOfDocument = refactoring.snapshot().includeLocationsOfDocument(filePath); for (Snapshot::IncludeLocation &loc : includeLocationsOfDocument) { - if (m_processed.contains(loc.first)) + if (!Utils::insert(m_processed, loc.first)) continue; CppRefactoringFilePtr file = refactoring.file(loc.first->filePath()); const bool noGlobalUsing = refactorFile(file, refactoring.snapshot(), file->position(loc.second, 1)); - m_processed.insert(loc.first); if (noGlobalUsing) processIncludes(refactoring, loc.first->filePath()); } @@ -8900,9 +8918,8 @@ public: QSizePolicy labelSizePolicy = errorLabel->sizePolicy(); labelSizePolicy.setRetainSizeWhenHidden(true); errorLabel->setSizePolicy(labelSizePolicy); - connect(constructorParamsModel, - &ConstructorParams::validOrder, - [=, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) { + connect(constructorParamsModel, &ConstructorParams::validOrder, this, + [errorLabel, button = buttonBox->button(QDialogButtonBox::Ok)](bool valid) { button->setEnabled(valid); errorLabel->setVisible(!valid); }); @@ -8910,7 +8927,8 @@ public: // setup select all/none checkbox QCheckBox *const checkBox = new QCheckBox(Tr::tr("Initialize all members")); checkBox->setChecked(true); - connect(checkBox, &QCheckBox::stateChanged, [model = constructorParamsModel](int state) { + connect(checkBox, &QCheckBox::stateChanged, this, + [model = constructorParamsModel](int state) { if (state != Qt::PartiallyChecked) { for (int i = 0; i < model->rowCount(); ++i) model->setData(model->index(i, ConstructorParams::ShouldInitColumn), @@ -9299,6 +9317,391 @@ void GenerateConstructor::match(const CppQuickFixInterface &interface, QuickFixO result << op; } +namespace { +class ConvertCommentStyleOp : public CppQuickFixOperation +{ +public: + ConvertCommentStyleOp(const CppQuickFixInterface &interface, const QList<Token> &tokens, + Kind kind) + : CppQuickFixOperation(interface), + m_tokens(tokens), + m_kind(kind), + m_wasCxxStyle(m_kind == T_CPP_COMMENT || m_kind == T_CPP_DOXY_COMMENT), + m_isDoxygen(m_kind == T_DOXY_COMMENT || m_kind == T_CPP_DOXY_COMMENT) + { + setDescription(m_wasCxxStyle ? Tr::tr("Convert Comment to C-Style") + : Tr::tr("Convert Comment to C++-Style")); + } + +private: + // Turns every line of a C-style comment into a C++-style comment and vice versa. + // For C++ -> C, we use one /* */ comment block per line. However, doxygen + // requires a single comment, so there we just replace the prefix with whitespace and + // add the start and end comment in extra lines. + // For cosmetic reasons, we offer some convenience functionality: + // - Turn /***** ... into ////// ... and vice versa + // - With C -> C++, remove leading asterisks. + // - With C -> C++, remove the first and last line of a block if they have no content + // other than the comment start and end characters. + // - With C++ -> C, try to align the end comment characters. + // These are obviously heuristics; we do not guarantee perfect results for everybody. + // We also don't second-guess the users's selection: E.g. if there is an empty + // line between the tokens, then it's not the same doxygen comment, but we merge + // it anyway in C++ to C mode. + void perform() override + { + TranslationUnit * const tu = currentFile()->cppDocument()->translationUnit(); + const QString newCommentStart = getNewCommentStart(); + ChangeSet changeSet; + int endCommentColumn = -1; + const QChar oldFillChar = m_wasCxxStyle ? '/' : '*'; + const QChar newFillChar = m_wasCxxStyle ? '*' : '/'; + + for (const Token &token : m_tokens) { + const int startPos = tu->getTokenPositionInDocument(token, textDocument()); + const int endPos = tu->getTokenEndPositionInDocument(token, textDocument()); + + if (m_wasCxxStyle && m_isDoxygen) { + // Replace "///" characters with whitespace (to keep alignment). + // The insertion of "/*" and "*/" is done once after the loop. + changeSet.replace(startPos, startPos + 3, " "); + continue; + } + + const QTextBlock firstBlock = textDocument()->findBlock(startPos); + const QTextBlock lastBlock = textDocument()->findBlock(endPos); + for (QTextBlock block = firstBlock; block.isValid() && block.position() <= endPos; + block = block.next()) { + const QString &blockText = block.text(); + const int firstColumn = block == firstBlock ? startPos - block.position() : 0; + const int endColumn = block == lastBlock ? endPos - block.position() + : block.length(); + + // Returns true if the current line looks like "/********/" or "//////////", + // as is often the case at the start and end of comment blocks. + const auto fillChecker = [&] { + if (m_isDoxygen) + return false; + QString textToCheck = blockText; + if (block == firstBlock) + textToCheck.remove(0, 1); + if (block == lastBlock) + textToCheck.chop(block.length() - endColumn); + return Utils::allOf(textToCheck, [oldFillChar](const QChar &c) + { return c == oldFillChar || c == ' '; + }) && textToCheck.count(oldFillChar) > 2; + }; + + // Returns the index of the first character of actual comment content, + // as opposed to visual stuff like slashes, stars or whitespace. + const auto indexOfActualContent = [&] { + const int offset = block == firstBlock ? firstColumn + newCommentStart.length() + : firstColumn; + + for (int i = offset, lastFillChar = -1; i < blockText.length(); ++i) { + if (blockText.at(i) == oldFillChar) { + lastFillChar = i; + continue; + } + if (!blockText.at(i).isSpace()) + return lastFillChar + 1; + } + return -1; + }; + + if (fillChecker()) { + const QString replacement = QString(endColumn - 1 - firstColumn, newFillChar); + changeSet.replace(block.position() + firstColumn, + block.position() + endColumn - 1, + replacement); + if (m_wasCxxStyle) { + changeSet.replace(block.position() + firstColumn, + block.position() + firstColumn + 1, "/"); + changeSet.insert(block.position() + endColumn - 1, "*"); + endCommentColumn = endColumn - 1; + } + continue; + } + + // Remove leading noise or even the entire block, if applicable. + const bool blockIsRemovable = (block == firstBlock || block == lastBlock) + && firstBlock != lastBlock; + const auto removeBlock = [&] { + changeSet.remove(block.position() + firstColumn, block.position() + endColumn); + }; + const int contentIndex = indexOfActualContent(); + if (contentIndex == -1) { + if (blockIsRemovable) { + removeBlock(); + continue; + } else if (!m_wasCxxStyle) { + changeSet.replace(block.position() + firstColumn, + block.position() + endColumn - 1, newCommentStart); + continue; + } + } else if (block == lastBlock && contentIndex == endColumn - 1) { + if (blockIsRemovable) { + removeBlock(); + break; + } + } else { + changeSet.remove(block.position() + firstColumn, + block.position() + firstColumn + contentIndex); + } + + if (block == firstBlock) { + changeSet.replace(startPos, startPos + newCommentStart.length(), + newCommentStart); + } else { + // If the line starts with enough whitespace, replace it with the + // comment start characters, so we don't move the content to the right + // unnecessarily. Otherwise, insert the comment start characters. + if (blockText.startsWith(QString(newCommentStart.size() + 1, ' '))) { + changeSet.replace(block.position(), + block.position() + newCommentStart.length(), + newCommentStart); + } else { + changeSet.insert(block.position(), newCommentStart); + } + } + + if (block == lastBlock) { + if (m_wasCxxStyle) { + // This is for proper alignment of the end comment character. + if (endCommentColumn != -1) { + const int endCommentPos = block.position() + endCommentColumn; + if (endPos < endCommentPos) + changeSet.insert(endPos, QString(endCommentPos - endPos - 1, ' ')); + } + changeSet.insert(endPos, " */"); + } else { + changeSet.remove(endPos - 2, endPos); + } + } + } + } + + if (m_wasCxxStyle && m_isDoxygen) { + const int startPos = tu->getTokenPositionInDocument(m_tokens.first(), textDocument()); + const int endPos = tu->getTokenEndPositionInDocument(m_tokens.last(), textDocument()); + changeSet.insert(startPos, "/*!\n"); + changeSet.insert(endPos, "\n*/"); + } + + changeSet.apply(textDocument()); + } + + QString getNewCommentStart() const + { + if (m_wasCxxStyle) { + if (m_isDoxygen) + return "/*!"; + return "/*"; + } + if (m_isDoxygen) + return "//!"; + return "//"; + } + + const QList<Token> m_tokens; + const Kind m_kind; + const bool m_wasCxxStyle; + const bool m_isDoxygen; +}; +} // namespace + +void ConvertCommentStyle::match(const CppQuickFixInterface &interface, + TextEditor::QuickFixOperations &result) +{ + // If there's a selection, then it must entirely consist of comment tokens. + // If there's no selection, the cursor must be on a comment. + const QList<Token> &cursorTokens = interface.currentFile()->tokensForCursor(); + if (cursorTokens.empty()) + return; + if (!cursorTokens.front().isComment()) + return; + + // All tokens must be the same kind of comment, but we make an exception for doxygen comments + // that start with "///", as these are often not intended to be doxygen. For our purposes, + // we treat them as normal comments. + const auto effectiveKind = [&interface](const Token &token) { + if (token.kind() != T_CPP_DOXY_COMMENT) + return token.kind(); + TranslationUnit * const tu = interface.currentFile()->cppDocument()->translationUnit(); + const int startPos = tu->getTokenPositionInDocument(token, interface.textDocument()); + const QString commentStart = interface.textAt(startPos, 3); + return commentStart == "///" ? T_CPP_COMMENT : T_CPP_DOXY_COMMENT; + }; + const Kind kind = effectiveKind(cursorTokens.first()); + for (int i = 1; i < cursorTokens.count(); ++i) { + if (effectiveKind(cursorTokens.at(i)) != kind) + return; + } + + // Ok, all tokens are of same(ish) comment type, offer quickfix. + result << new ConvertCommentStyleOp(interface, cursorTokens, kind); +} + +namespace { +class MoveFunctionCommentsOp : public CppQuickFixOperation +{ +public: + enum class Direction { ToDecl, ToDef }; + MoveFunctionCommentsOp(const CppQuickFixInterface &interface, const Symbol *symbol, + const QList<Token> &commentTokens, Direction direction) + : CppQuickFixOperation(interface), m_symbol(symbol), m_commentTokens(commentTokens) + { + setDescription(direction == Direction::ToDecl + ? Tr::tr("Move Function Documentation to Declaration") + : Tr::tr("Move Function Documentation to Definition")); + } + +private: + void perform() override + { + const auto textDoc = const_cast<QTextDocument *>(currentFile()->document()); + const int pos = currentFile()->cppDocument()->translationUnit()->getTokenPositionInDocument( + m_symbol->sourceLocation(), textDoc); + QTextCursor cursor(textDoc); + cursor.setPosition(pos); + const CursorInEditor cursorInEditor(cursor, currentFile()->filePath(), editor(), + editor()->textDocument()); + const auto callback = [symbolLoc = m_symbol->toLink(), comments = m_commentTokens] + (const Link &link) { + moveComments(link, symbolLoc, comments); + }; + CppModelManager::followSymbol(cursorInEditor, callback, true, false); + } + + static void moveComments(const Link &targetLoc, const Link &symbolLoc, + const QList<Token> &comments) + { + if (!targetLoc.hasValidTarget() || targetLoc.hasSameLocation(symbolLoc)) + return; + + CppRefactoringChanges changes(CppModelManager::snapshot()); + const CppRefactoringFilePtr sourceFile = changes.file(symbolLoc.targetFilePath); + const CppRefactoringFilePtr targetFile + = targetLoc.targetFilePath == symbolLoc.targetFilePath + ? sourceFile + : changes.file(targetLoc.targetFilePath); + const Document::Ptr &targetCppDoc = targetFile->cppDocument(); + const QList<AST *> targetAstPath = ASTPath(targetCppDoc)( + targetLoc.targetLine, targetLoc.targetColumn + 1); + if (targetAstPath.isEmpty()) + return; + const AST *targetDeclAst = nullptr; + for (auto it = std::next(std::rbegin(targetAstPath)); + it != std::rend(targetAstPath); ++it) { + AST * const node = *it; + if (node->asDeclaration()) { + targetDeclAst = node; + continue; + } + if (targetDeclAst) + break; + } + if (!targetDeclAst) + return; + const int insertionPos = targetCppDoc->translationUnit()->getTokenPositionInDocument( + targetDeclAst->firstToken(), targetFile->document()); + const TranslationUnit * const sourceTu = sourceFile->cppDocument()->translationUnit(); + const int sourceCommentStartPos = sourceTu->getTokenPositionInDocument( + comments.first(), sourceFile->document()); + const int sourceCommentEndPos = sourceTu->getTokenEndPositionInDocument( + comments.last(), sourceFile->document()); + const QString functionDoc = sourceFile->textOf(sourceCommentStartPos, sourceCommentEndPos); + + // Remove comment plus leading and trailing whitespace, including trailing newline. + const auto removeAtSource = [&](ChangeSet &changeSet) { + int removalPos = sourceCommentStartPos; + const QChar newline(QChar::ParagraphSeparator); + while (true) { + const int prev = removalPos - 1; + if (prev < 0) + break; + const QChar prevChar = sourceFile->charAt(prev); + if (!prevChar.isSpace() || prevChar == newline) + break; + removalPos = prev; + } + int removalEndPos = sourceCommentEndPos; + while (true) { + if (removalEndPos == sourceFile->document()->characterCount()) + break; + const QChar nextChar = sourceFile->charAt(removalEndPos); + if (!nextChar.isSpace()) + break; + ++removalEndPos; + if (nextChar == newline) + break; + } + changeSet.remove(removalPos, removalEndPos); + }; + + ChangeSet targetChangeSet; + targetChangeSet.insert(insertionPos, functionDoc); + targetChangeSet.insert(insertionPos, "\n"); + if (targetFile == sourceFile) + removeAtSource(targetChangeSet); + targetFile->setChangeSet(targetChangeSet); + targetFile->appendIndentRange({insertionPos, insertionPos + int(functionDoc.length())}); + const bool targetFileSuccess = targetFile->apply(); + if (targetFile == sourceFile || !targetFileSuccess) + return; + ChangeSet sourceChangeSet; + removeAtSource(sourceChangeSet); + sourceFile->setChangeSet(sourceChangeSet); + sourceFile->apply(); + } + + const Symbol * const m_symbol; + const QList<Token> m_commentTokens; +}; +} // namespace + +void MoveFunctionComments::match(const CppQuickFixInterface &interface, + TextEditor::QuickFixOperations &result) +{ + const QList<AST *> &astPath = interface.path(); + if (astPath.isEmpty()) + return; + const Symbol *symbol = nullptr; + MoveFunctionCommentsOp::Direction direction = MoveFunctionCommentsOp::Direction::ToDecl; + for (auto it = std::next(std::rbegin(astPath)); it != std::rend(astPath); ++it) { + if (const auto func = (*it)->asFunctionDefinition()) { + symbol = func->symbol; + direction = MoveFunctionCommentsOp::Direction::ToDecl; + break; + } + const auto decl = (*it)->asSimpleDeclaration(); + if (!decl || !decl->declarator_list) + continue; + for (auto it = decl->declarator_list->begin(); + !symbol && it != decl->declarator_list->end(); ++it) { + PostfixDeclaratorListAST * const funcDecls = (*it)->postfix_declarator_list; + if (!funcDecls) + continue; + for (auto it = funcDecls->begin(); it != funcDecls->end(); ++it) { + if (const auto func = (*it)->asFunctionDeclarator()) { + symbol = func->symbol; + direction = MoveFunctionCommentsOp::Direction::ToDef; + break; + } + } + } + + } + if (!symbol) + return; + + if (const QList<Token> commentTokens = commentsForDeclaration( + symbol, *interface.textDocument(), interface.currentFile()->cppDocument()); + !commentTokens.isEmpty()) { + result << new MoveFunctionCommentsOp(interface, symbol, commentTokens, direction); + } +} + void createCppQuickFixes() { new AddIncludeForUndefinedIdentifier; @@ -9355,6 +9758,8 @@ void createCppQuickFixes() new RemoveUsingNamespace; new GenerateConstructor; + new ConvertCommentStyle; + new MoveFunctionComments; } void destroyCppQuickFixes() diff --git a/src/plugins/cppeditor/cppquickfixes.h b/src/plugins/cppeditor/cppquickfixes.h index 61db3ebf53d..8825e6df911 100644 --- a/src/plugins/cppeditor/cppquickfixes.h +++ b/src/plugins/cppeditor/cppquickfixes.h @@ -402,6 +402,7 @@ public: enum class Mode { Off, // Testing: simulates user canceling the dialog + Impl, // Testing: simulates user choosing cpp file for every function Alternating, // Testing: simulates user choosing a different DefPos for every function User // Normal interactive mode }; @@ -584,5 +585,21 @@ private: bool m_test = false; }; +//! Converts C-style to C++-style comments and vice versa +class ConvertCommentStyle : public CppQuickFixFactory +{ +private: + void match(const CppQuickFixInterface &interface, + TextEditor::QuickFixOperations &result) override; +}; + +//! Moves function documentation between declaration and implementation. +class MoveFunctionComments : public CppQuickFixFactory +{ +private: + void match(const CppQuickFixInterface &interface, + TextEditor::QuickFixOperations &result) override; +}; + } // namespace Internal } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppquickfixprojectsettings.cpp b/src/plugins/cppeditor/cppquickfixprojectsettings.cpp index 3dbcf8d2047..3f3f4bc366b 100644 --- a/src/plugins/cppeditor/cppquickfixprojectsettings.cpp +++ b/src/plugins/cppeditor/cppquickfixprojectsettings.cpp @@ -13,6 +13,8 @@ #include <QSettings> #include <QtDebug> +using namespace Utils; + namespace CppEditor { namespace Internal { @@ -36,7 +38,7 @@ CppQuickFixProjectsSettings::CppQuickFixProjectsSettings(ProjectExplorer::Projec m_useGlobalSettings = true; } } - connect(project, &ProjectExplorer::Project::aboutToSaveSettings, [this] { + connect(project, &ProjectExplorer::Project::aboutToSaveSettings, this, [this] { auto settings = m_project->namedSettings(QUICK_FIX_SETTINGS_ID).toMap(); settings.insert(USE_GLOBAL_SETTINGS, m_useGlobalSettings); m_project->setNamedSettings(QUICK_FIX_SETTINGS_ID, settings); @@ -64,7 +66,7 @@ const Utils::FilePath &CppQuickFixProjectsSettings::filePathOfSettingsFile() con CppQuickFixProjectsSettings::CppQuickFixProjectsSettingsPtr CppQuickFixProjectsSettings::getSettings( ProjectExplorer::Project *project) { - const QString key = "CppQuickFixProjectsSettings"; + const Key key = "CppQuickFixProjectsSettings"; QVariant v = project->extraData(key); if (v.isNull()) { v = QVariant::fromValue( @@ -146,7 +148,7 @@ bool CppQuickFixProjectsSettings::saveOwnSettings() if (m_settingsFile.isEmpty()) return false; - QSettings settings(m_settingsFile.toString(), QSettings::IniFormat); + QtcSettings settings(m_settingsFile.toString(), QSettings::IniFormat); if (settings.status() == QSettings::NoError) { m_ownSettings.saveSettingsTo(&settings); settings.sync(); @@ -158,7 +160,7 @@ bool CppQuickFixProjectsSettings::saveOwnSettings() void CppQuickFixProjectsSettings::loadOwnSettingsFromFile() { - QSettings settings(m_settingsFile.toString(), QSettings::IniFormat); + QtcSettings settings(m_settingsFile.toString(), QSettings::IniFormat); if (settings.status() == QSettings::NoError) { m_ownSettings.loadSettingsFrom(&settings); return; diff --git a/src/plugins/cppeditor/cppquickfixsettings.cpp b/src/plugins/cppeditor/cppquickfixsettings.cpp index 32377cc8fc9..4ce10822678 100644 --- a/src/plugins/cppeditor/cppquickfixsettings.cpp +++ b/src/plugins/cppeditor/cppquickfixsettings.cpp @@ -7,10 +7,13 @@ #include "cppeditorconstants.h" #include <coreplugin/icore.h> + #include <utils/qtcsettings.h> #include <QRegularExpression> +using namespace Utils; + namespace CppEditor { CppQuickFixSettings::CppQuickFixSettings(bool loadGlobalSettings) @@ -35,7 +38,7 @@ void CppQuickFixSettings::loadGlobalSettings() } } -void CppQuickFixSettings::loadSettingsFrom(QSettings *s) +void CppQuickFixSettings::loadSettingsFrom(QtcSettings *s) { CppQuickFixSettings def; s->beginGroup(Constants::QUICK_FIX_SETTINGS_ID); @@ -110,80 +113,62 @@ void CppQuickFixSettings::loadSettingsFrom(QSettings *s) s->endGroup(); } -void CppQuickFixSettings::saveSettingsTo(QSettings *s) +void CppQuickFixSettings::saveSettingsTo(QtcSettings *s) { - using Utils::QtcSettings; CppQuickFixSettings def; s->beginGroup(Constants::QUICK_FIX_SETTINGS_ID); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_GETTER_OUTSIDE_CLASS_FROM, - getterOutsideClassFrom, - def.getterOutsideClassFrom); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_GETTER_IN_CPP_FILE_FROM, - getterInCppFileFrom, - def.getterInCppFileFrom); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SETTER_OUTSIDE_CLASS_FROM, - setterOutsideClassFrom, - def.setterOutsideClassFrom); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SETTER_IN_CPP_FILE_FROM, - setterInCppFileFrom, - def.setterInCppFileFrom); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_GETTER_OUTSIDE_CLASS_FROM, + getterOutsideClassFrom, + def.getterOutsideClassFrom); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_GETTER_IN_CPP_FILE_FROM, + getterInCppFileFrom, + def.getterInCppFileFrom); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SETTER_OUTSIDE_CLASS_FROM, + setterOutsideClassFrom, + def.setterOutsideClassFrom); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SETTER_IN_CPP_FILE_FROM, + setterInCppFileFrom, + def.setterInCppFileFrom); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_GETTER_ATTRIBUTES, - getterAttributes, - def.getterAttributes); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_GETTER_NAME_TEMPLATE, - getterNameTemplate, - def.getterNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SETTER_NAME_TEMPLATE, - setterNameTemplate, - def.setterNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_RESET_NAME_TEMPLATE, - resetNameTemplate, - def.resetNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SIGNAL_NAME_TEMPLATE, - signalNameTemplate, - def.signalNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SIGNAL_WITH_NEW_VALUE, - signalWithNewValue, - def.signalWithNewValue); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_CPP_FILE_NAMESPACE_HANDLING, - int(cppFileNamespaceHandling), - int(def.cppFileNamespaceHandling)); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_MEMBER_VARIABEL_NAME_TEMPLATE, - memberVariableNameTemplate, - def.memberVariableNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SETTER_PARAMETER_NAME, - setterParameterNameTemplate, - def.setterParameterNameTemplate); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_SETTER_AS_SLOT, - setterAsSlot, - def.setterAsSlot); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_USE_AUTO, - useAuto, - def.useAuto); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_VALUE_TYPES, - valueTypes, - def.valueTypes); - QtcSettings::setValueWithDefault(s, - Constants::QUICK_FIX_SETTING_RETURN_BY_CONST_REF, - returnByConstRef, - def.returnByConstRef); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_GETTER_ATTRIBUTES, + getterAttributes, + def.getterAttributes); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_GETTER_NAME_TEMPLATE, + getterNameTemplate, + def.getterNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SETTER_NAME_TEMPLATE, + setterNameTemplate, + def.setterNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_RESET_NAME_TEMPLATE, + resetNameTemplate, + def.resetNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SIGNAL_NAME_TEMPLATE, + signalNameTemplate, + def.signalNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SIGNAL_WITH_NEW_VALUE, + signalWithNewValue, + def.signalWithNewValue); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_CPP_FILE_NAMESPACE_HANDLING, + int(cppFileNamespaceHandling), + int(def.cppFileNamespaceHandling)); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_MEMBER_VARIABEL_NAME_TEMPLATE, + memberVariableNameTemplate, + def.memberVariableNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SETTER_PARAMETER_NAME, + setterParameterNameTemplate, + def.setterParameterNameTemplate); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_SETTER_AS_SLOT, + setterAsSlot, + def.setterAsSlot); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_USE_AUTO, + useAuto, + def.useAuto); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_VALUE_TYPES, + valueTypes, + def.valueTypes); + s->setValueWithDefault(Constants::QUICK_FIX_SETTING_RETURN_BY_CONST_REF, + returnByConstRef, + def.returnByConstRef); if (customTemplates == def.customTemplates) { s->remove(Constants::QUICK_FIX_SETTING_CUSTOM_TEMPLATES); } else { diff --git a/src/plugins/cppeditor/cppquickfixsettings.h b/src/plugins/cppeditor/cppquickfixsettings.h index c2184289247..d0335162768 100644 --- a/src/plugins/cppeditor/cppquickfixsettings.h +++ b/src/plugins/cppeditor/cppquickfixsettings.h @@ -9,9 +9,7 @@ #include <optional> #include <vector> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace CppEditor { @@ -65,8 +63,8 @@ public: explicit CppQuickFixSettings(bool loadGlobalSettings = false); void loadGlobalSettings(); - void loadSettingsFrom(QSettings *); - void saveSettingsTo(QSettings *); + void loadSettingsFrom(Utils::QtcSettings *); + void saveSettingsTo(Utils::QtcSettings *); void saveAsGlobalSettings(); void setDefaultSettings(); diff --git a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp index 679d140967f..c589a26b9d2 100644 --- a/src/plugins/cppeditor/cppquickfixsettingswidget.cpp +++ b/src/plugins/cppeditor/cppquickfixsettingswidget.cpp @@ -190,24 +190,24 @@ e.g. name = "m_test_foo_": connect(m_listWidget_customTemplates, &QListWidget::currentItemChanged, this, &CppQuickFixSettingsWidget::currentCustomItemChanged); - connect(pushButton_addValueType, &QPushButton::clicked, [this] { + connect(pushButton_addValueType, &QPushButton::clicked, this, [this] { auto item = new QListWidgetItem("<type>", m_valueTypes); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren); m_valueTypes->scrollToItem(item); item->setSelected(true); }); - connect(pushButton_addCustomTemplate, &QPushButton::clicked, [this] { + connect(pushButton_addCustomTemplate, &QPushButton::clicked, this, [this] { auto item = new QListWidgetItem("<type>", m_listWidget_customTemplates); item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren); m_listWidget_customTemplates->scrollToItem(item); m_listWidget_customTemplates->setCurrentItem(item); m_lineEdit_customTemplateTypes->setText("<type>"); }); - connect(m_pushButton_removeCustomTemplate, &QPushButton::clicked, [this] { + connect(m_pushButton_removeCustomTemplate, &QPushButton::clicked, this, [this] { delete m_listWidget_customTemplates->currentItem(); }); - connect(pushButton_removeValueType, &QPushButton::clicked, [this] { + connect(pushButton_removeValueType, &QPushButton::clicked, this, [this] { delete m_valueTypes->currentItem(); }); diff --git a/src/plugins/cppeditor/cpprefactoringchanges.cpp b/src/plugins/cppeditor/cpprefactoringchanges.cpp index f27c0ce83c4..f4bcc471ad8 100644 --- a/src/plugins/cppeditor/cpprefactoringchanges.cpp +++ b/src/plugins/cppeditor/cpprefactoringchanges.cpp @@ -16,6 +16,8 @@ #include <QTextDocument> +#include <utility> + using namespace CPlusPlus; using namespace Utils; @@ -75,15 +77,20 @@ CppRefactoringFile::CppRefactoringFile(const FilePath &filePath, const QSharedPo { const Snapshot &snapshot = this->data()->m_snapshot; m_cppDocument = snapshot.document(filePath); + m_formattingEnabled = true; } CppRefactoringFile::CppRefactoringFile(QTextDocument *document, const FilePath &filePath) : RefactoringFile(document, filePath) -{ } +{ + m_formattingEnabled = true; +} CppRefactoringFile::CppRefactoringFile(TextEditor::TextEditorWidget *editor) : RefactoringFile(editor) -{ } +{ + m_formattingEnabled = true; +} Document::Ptr CppRefactoringFile::cppDocument() const { @@ -107,7 +114,7 @@ void CppRefactoringFile::setCppDocument(Document::Ptr document) Scope *CppRefactoringFile::scopeAt(unsigned index) const { int line, column; - cppDocument()->translationUnit()->getTokenStartPosition(index, &line, &column); + cppDocument()->translationUnit()->getTokenPosition(index, &line, &column); return cppDocument()->scopeAt(line, column); } @@ -136,6 +143,31 @@ bool CppRefactoringFile::isCursorOn(const AST *ast) const return cursorBegin >= start && cursorBegin <= end; } +QList<Token> CppRefactoringFile::tokensForCursor() const +{ + QTextCursor c = cursor(); + int pos = c.selectionStart(); + int endPos = c.selectionEnd(); + if (pos > endPos) + std::swap(pos, endPos); + + const std::vector<Token> &allTokens = m_cppDocument->translationUnit()->allTokens(); + const int firstIndex = tokenIndexForPosition(allTokens, pos, 0); + if (firstIndex == -1) + return {}; + + const int lastIndex = pos == endPos + ? firstIndex + : tokenIndexForPosition(allTokens, endPos, firstIndex); + if (lastIndex == -1) + return {}; + QTC_ASSERT(lastIndex >= firstIndex, return {}); + QList<Token> result; + for (int i = firstIndex; i <= lastIndex; ++i) + result.push_back(allTokens.at(i)); + return result; +} + ChangeSet::Range CppRefactoringFile::range(unsigned tokenIndex) const { const Token &token = tokenAt(tokenIndex); @@ -213,10 +245,32 @@ void CppRefactoringFile::fileChanged() RefactoringFile::fileChanged(); } +int CppRefactoringFile::tokenIndexForPosition(const std::vector<CPlusPlus::Token> &tokens, + int pos, int startIndex) const +{ + const TranslationUnit * const tu = m_cppDocument->translationUnit(); + + // Binary search + for (int l = startIndex, u = int(tokens.size()) - 1; l <= u; ) { + const int i = (l + u) / 2; + const int tokenPos = tu->getTokenPositionInDocument(tokens.at(i), document()); + if (pos < tokenPos) { + u = i - 1; + continue; + } + const int tokenEndPos = tu->getTokenEndPositionInDocument(tokens.at(i), document()); + if (pos > tokenEndPos) { + l = i + 1; + continue; + } + return i; + } + return -1; +} + CppRefactoringChangesData::CppRefactoringChangesData(const Snapshot &snapshot) : m_snapshot(snapshot) - , m_modelManager(CppModelManager::instance()) - , m_workingCopy(m_modelManager->workingCopy()) + , m_workingCopy(CppModelManager::workingCopy()) {} void CppRefactoringChangesData::indentSelection(const QTextCursor &selection, @@ -247,7 +301,7 @@ void CppRefactoringChangesData::reindentSelection(const QTextCursor &selection, void CppRefactoringChangesData::fileChanged(const FilePath &filePath) { - m_modelManager->updateSourceFiles({filePath}); + CppModelManager::updateSourceFiles({filePath}); } } // CppEditor diff --git a/src/plugins/cppeditor/cpprefactoringchanges.h b/src/plugins/cppeditor/cpprefactoringchanges.h index 4a79da2f73e..602efaf6d04 100644 --- a/src/plugins/cppeditor/cpprefactoringchanges.h +++ b/src/plugins/cppeditor/cpprefactoringchanges.h @@ -12,6 +12,8 @@ #include <texteditor/refactoringchanges.h> +#include <optional> + namespace CppEditor { class CppRefactoringChanges; @@ -44,6 +46,8 @@ public: void startAndEndOf(unsigned index, int *start, int *end) const; + QList<CPlusPlus::Token> tokensForCursor() const; + using TextEditor::RefactoringFile::textOf; QString textOf(const CPlusPlus::AST *ast) const; @@ -55,6 +59,9 @@ protected: CppRefactoringChangesData *data() const; void fileChanged() override; + int tokenIndexForPosition(const std::vector<CPlusPlus::Token> &tokens, int pos, + int startIndex) const; + mutable CPlusPlus::Document::Ptr m_cppDocument; friend class CppRefactoringChanges; // for access to constructor @@ -76,7 +83,6 @@ public: void fileChanged(const Utils::FilePath &filePath) override; CPlusPlus::Snapshot m_snapshot; - CppModelManager *m_modelManager; WorkingCopy m_workingCopy; }; diff --git a/src/plugins/cppeditor/cpprenaming_test.cpp b/src/plugins/cppeditor/cpprenaming_test.cpp new file mode 100644 index 00000000000..2827434be55 --- /dev/null +++ b/src/plugins/cppeditor/cpprenaming_test.cpp @@ -0,0 +1,146 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cpprenaming_test.h" + +#include "cppeditorwidget.h" +#include "cppmodelmanager.h" +#include "cppquickfix_test.h" + +#include <texteditor/texteditor.h> + +#include <QEventLoop> +#include <QTimer> +#include <QtTest> + +namespace CppEditor::Internal::Tests { + +class RenamingTestRunner : public BaseQuickFixTestCase +{ +public: + RenamingTestRunner(const QList<TestDocumentPtr> &testDocuments, const QString &replacement) + : BaseQuickFixTestCase(testDocuments, {}) + { + QVERIFY(succeededSoFar()); + const TestDocumentPtr &doc = m_documentWithMarker; + const CursorInEditor cursorInEditor(doc->m_editor->textCursor(), doc->filePath(), + doc->m_editorWidget, doc->m_editor->textDocument()); + + QEventLoop loop; + CppModelManager::globalRename(cursorInEditor, replacement, [&loop]{ loop.quit(); }); + QTimer::singleShot(10000, &loop, [&loop] { loop.exit(1); }); + QVERIFY(loop.exec() == 0); + + // Compare all files + for (const TestDocumentPtr &testDocument : std::as_const(m_testDocuments)) { + QString result = testDocument->m_editorWidget->document()->toPlainText(); + if (result != testDocument->m_expectedSource) { + qDebug() << "---" << testDocument->m_expectedSource; + qDebug() << "+++" << result; + } + QCOMPARE(result, testDocument->m_expectedSource); + + // Undo the change + for (int i = 0; i < 100; ++i) + testDocument->m_editorWidget->undo(); + result = testDocument->m_editorWidget->document()->toPlainText(); + QCOMPARE(result, testDocument->m_source); + } + } +}; + +void GlobalRenamingTest::test_data() +{ + QTest::addColumn<QByteArrayList>("headers"); + QTest::addColumn<QByteArrayList>("sources"); + QTest::addColumn<QString>("replacement"); + + const char testClassHeader[] = R"cpp( +/** + * \brief MyClass + */ +class MyClass { + /** \brief MyClass::MyClass */ + MyClass() {} + ~MyClass(); + /** \brief MyClass::run */ + void run(); +}; +)cpp"; + + const char testClassSource[] = R"cpp( +#include "file.h" +/** \brief MyClass::~MyClass */ +MyClass::~MyClass() {} + +void MyClass::run() {} +)cpp"; + + QByteArray origHeaderClassName(testClassHeader); + const int classOffset = origHeaderClassName.indexOf("class MyClass"); + QVERIFY(classOffset != -1); + origHeaderClassName.insert(classOffset + 6, '@'); + const QByteArray newHeaderClassName = R"cpp( +/** + * \brief MyNewClass + */ +class MyNewClass { + /** \brief MyNewClass::MyNewClass */ + MyNewClass() {} + ~MyNewClass(); + /** \brief MyNewClass::run */ + void run(); +}; +)cpp"; + const QByteArray newSourceClassName = R"cpp( +#include "file.h" +/** \brief MyNewClass::~MyNewClass */ +MyNewClass::~MyNewClass() {} + +void MyNewClass::run() {} +)cpp"; + QTest::newRow("class name") << QByteArrayList{origHeaderClassName, newHeaderClassName} + << QByteArrayList{testClassSource, newSourceClassName} + << QString("MyNewClass"); + + QByteArray origSourceMethodName(testClassSource); + const int methodOffset = origSourceMethodName.indexOf("::run()"); + QVERIFY(methodOffset != -1); + origSourceMethodName.insert(methodOffset + 2, '@'); + const QByteArray newHeaderMethodName = R"cpp( +/** + * \brief MyClass + */ +class MyClass { + /** \brief MyClass::MyClass */ + MyClass() {} + ~MyClass(); + /** \brief MyClass::runAgain */ + void runAgain(); +}; +)cpp"; + const QByteArray newSourceMethodName = R"cpp( +#include "file.h" +/** \brief MyClass::~MyClass */ +MyClass::~MyClass() {} + +void MyClass::runAgain() {} +)cpp"; + QTest::newRow("method name") << QByteArrayList{testClassHeader, newHeaderMethodName} + << QByteArrayList{origSourceMethodName, newSourceMethodName} + << QString("runAgain"); +} + +void GlobalRenamingTest::test() +{ + QFETCH(QByteArrayList, headers); + QFETCH(QByteArrayList, sources); + QFETCH(QString, replacement); + + QList<TestDocumentPtr> testDocuments( + {CppTestDocument::create("file.h", headers.at(0), headers.at(1)), + CppTestDocument::create("file.cpp", sources.at(0), sources.at(1))}); + RenamingTestRunner testRunner(testDocuments, replacement); +} + +} // namespace CppEditor::Internal::Tests diff --git a/src/plugins/cppeditor/cpprenaming_test.h b/src/plugins/cppeditor/cpprenaming_test.h new file mode 100644 index 00000000000..b7c651d306b --- /dev/null +++ b/src/plugins/cppeditor/cpprenaming_test.h @@ -0,0 +1,19 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <QObject> + +namespace CppEditor::Internal::Tests { + +class GlobalRenamingTest : public QObject +{ + Q_OBJECT + +private slots: + void test_data(); + void test(); +}; + +} // namespace CppEditor::Internal::Tests diff --git a/src/plugins/cppeditor/cppselectionchanger.cpp b/src/plugins/cppeditor/cppselectionchanger.cpp index cd199856925..7b9f6767451 100644 --- a/src/plugins/cppeditor/cppselectionchanger.cpp +++ b/src/plugins/cppeditor/cppselectionchanger.cpp @@ -114,7 +114,7 @@ int CppSelectionChanger::getTokenStartCursorPosition( const QTextCursor &cursor) const { int startLine, startColumn; - m_unit->getTokenStartPosition(tokenIndex, &startLine, &startColumn); + m_unit->getTokenPosition(tokenIndex, &startLine, &startColumn); const QTextDocument *document = cursor.document(); const int startPosition = document->findBlockByNumber(startLine - 1).position() @@ -144,7 +144,7 @@ void CppSelectionChanger::printTokenDebugInfo( { int line, column; const Token token = m_unit->tokenAt(tokenIndex); - m_unit->getTokenStartPosition(tokenIndex, &line, &column); + m_unit->getTokenPosition(tokenIndex, &line, &column); const int startPos = getTokenStartCursorPosition(tokenIndex, cursor); const int endPos = getTokenEndCursorPosition(tokenIndex, cursor); diff --git a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp index 4fc9dd7c66b..e398c14b861 100644 --- a/src/plugins/cppeditor/cppsemanticinfoupdater.cpp +++ b/src/plugins/cppeditor/cppsemanticinfoupdater.cpp @@ -5,18 +5,22 @@ #include "cppmodelmanager.h" -#include <utils/async.h> -#include <utils/qtcassert.h> - #include <cplusplus/Control.h> #include <cplusplus/CppDocument.h> #include <cplusplus/TranslationUnit.h> +#include <extensionsystem/pluginmanager.h> + +#include <utils/async.h> +#include <utils/futuresynchronizer.h> +#include <utils/qtcassert.h> + #include <QLoggingCategory> enum { debug = 0 }; using namespace CPlusPlus; +using namespace Utils; static Q_LOGGING_CATEGORY(log, "qtc.cppeditor.semanticinfoupdater", QtWarningMsg) @@ -25,96 +29,57 @@ namespace CppEditor { class SemanticInfoUpdaterPrivate { public: - class FuturizedTopLevelDeclarationProcessor: public TopLevelDeclarationProcessor - { - public: - explicit FuturizedTopLevelDeclarationProcessor(QPromise<void> &promise): m_promise(promise) {} - bool processDeclaration(DeclarationAST *) override { return !isCanceled(); } - bool isCanceled() { return m_promise.isCanceled(); } - private: - QPromise<void> &m_promise; - }; + ~SemanticInfoUpdaterPrivate() { cancelFuture(); } -public: - explicit SemanticInfoUpdaterPrivate(SemanticInfoUpdater *q); - ~SemanticInfoUpdaterPrivate(); + void cancelFuture(); - SemanticInfo semanticInfo() const; - void setSemanticInfo(const SemanticInfo &semanticInfo, bool emitSignal); - - SemanticInfo update(const SemanticInfo::Source &source, - bool emitSignalWhenFinished, - FuturizedTopLevelDeclarationProcessor *processor); - - bool reuseCurrentSemanticInfo(const SemanticInfo::Source &source, bool emitSignalWhenFinished); - - void update_helper(QPromise<void> &promise, const SemanticInfo::Source &source); - -public: - SemanticInfoUpdater *q; - mutable QMutex m_lock; SemanticInfo m_semanticInfo; + std::unique_ptr<QFutureWatcher<SemanticInfo>> m_watcher; +}; + +void SemanticInfoUpdaterPrivate::cancelFuture() +{ + if (!m_watcher) + return; + + m_watcher->cancel(); + m_watcher.reset(); +} + +class FuturizedTopLevelDeclarationProcessor: public TopLevelDeclarationProcessor +{ +public: + explicit FuturizedTopLevelDeclarationProcessor(const QFuture<void> &future): m_future(future) {} + bool processDeclaration(DeclarationAST *) override { return !m_future.isCanceled(); } +private: QFuture<void> m_future; }; -SemanticInfoUpdaterPrivate::SemanticInfoUpdaterPrivate(SemanticInfoUpdater *q) - : q(q) -{ -} - -SemanticInfoUpdaterPrivate::~SemanticInfoUpdaterPrivate() -{ - m_future.cancel(); - m_future.waitForFinished(); -} - -SemanticInfo SemanticInfoUpdaterPrivate::semanticInfo() const -{ - QMutexLocker locker(&m_lock); - return m_semanticInfo; -} - -void SemanticInfoUpdaterPrivate::setSemanticInfo(const SemanticInfo &semanticInfo, bool emitSignal) -{ - { - QMutexLocker locker(&m_lock); - m_semanticInfo = semanticInfo; - } - if (emitSignal) { - qCDebug(log) << "emiting new info"; - emit q->updated(semanticInfo); - } -} - -SemanticInfo SemanticInfoUpdaterPrivate::update(const SemanticInfo::Source &source, - bool emitSignalWhenFinished, - FuturizedTopLevelDeclarationProcessor *processor) +static void doUpdate(QPromise<SemanticInfo> &promise, const SemanticInfo::Source &source) { SemanticInfo newSemanticInfo; newSemanticInfo.revision = source.revision; newSemanticInfo.snapshot = source.snapshot; - Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument(source.code, - Utils::FilePath::fromString(source.fileName)); - if (processor) - doc->control()->setTopLevelDeclarationProcessor(processor); + Document::Ptr doc = newSemanticInfo.snapshot.preprocessedDocument( + source.code, FilePath::fromString(source.fileName)); + + FuturizedTopLevelDeclarationProcessor processor(QFuture<void>(promise.future())); + doc->control()->setTopLevelDeclarationProcessor(&processor); doc->check(); - if (processor && processor->isCanceled()) + if (promise.isCanceled()) newSemanticInfo.complete = false; newSemanticInfo.doc = doc; qCDebug(log) << "update() for source revision:" << source.revision << "canceled:" << !newSemanticInfo.complete; - setSemanticInfo(newSemanticInfo, emitSignalWhenFinished); - return newSemanticInfo; + promise.addResult(newSemanticInfo); } -bool SemanticInfoUpdaterPrivate::reuseCurrentSemanticInfo(const SemanticInfo::Source &source, - bool emitSignalWhenFinished) +static std::optional<SemanticInfo> canReuseSemanticInfo( + const SemanticInfo ¤tSemanticInfo, const SemanticInfo::Source &source) { - const SemanticInfo currentSemanticInfo = semanticInfo(); - if (!source.force && currentSemanticInfo.complete && currentSemanticInfo.revision == source.revision @@ -127,64 +92,63 @@ bool SemanticInfoUpdaterPrivate::reuseCurrentSemanticInfo(const SemanticInfo::So newSemanticInfo.revision = source.revision; newSemanticInfo.snapshot = source.snapshot; newSemanticInfo.doc = currentSemanticInfo.doc; - setSemanticInfo(newSemanticInfo, emitSignalWhenFinished); qCDebug(log) << "re-using current semantic info, source revision:" << source.revision; - return true; + return newSemanticInfo; } - - return false; -} - -void SemanticInfoUpdaterPrivate::update_helper(QPromise<void> &promise, - const SemanticInfo::Source &source) -{ - FuturizedTopLevelDeclarationProcessor processor(promise); - update(source, true, &processor); + return {}; } SemanticInfoUpdater::SemanticInfoUpdater() - : d(new SemanticInfoUpdaterPrivate(this)) -{ -} + : d(new SemanticInfoUpdaterPrivate) +{} -SemanticInfoUpdater::~SemanticInfoUpdater() -{ - d->m_future.cancel(); - d->m_future.waitForFinished(); -} +SemanticInfoUpdater::~SemanticInfoUpdater() = default; SemanticInfo SemanticInfoUpdater::update(const SemanticInfo::Source &source) { qCDebug(log) << "update() - synchronous"; - d->m_future.cancel(); + d->cancelFuture(); - const bool emitSignalWhenFinished = false; - if (d->reuseCurrentSemanticInfo(source, emitSignalWhenFinished)) { - d->m_future = QFuture<void>(); - return semanticInfo(); + const auto info = canReuseSemanticInfo(d->m_semanticInfo, source); + if (info) { + d->m_semanticInfo = *info; + return d->m_semanticInfo; } - return d->update(source, emitSignalWhenFinished, nullptr); + QPromise<SemanticInfo> dummy; + dummy.start(); + doUpdate(dummy, source); + const SemanticInfo result = dummy.future().result(); + d->m_semanticInfo = result; + return result; } void SemanticInfoUpdater::updateDetached(const SemanticInfo::Source &source) { qCDebug(log) << "updateDetached() - asynchronous"; - d->m_future.cancel(); + d->cancelFuture(); - const bool emitSignalWhenFinished = true; - if (d->reuseCurrentSemanticInfo(source, emitSignalWhenFinished)) { - d->m_future = QFuture<void>(); + const auto info = canReuseSemanticInfo(d->m_semanticInfo, source); + if (info) { + d->m_semanticInfo = *info; + emit updated(d->m_semanticInfo); return; } - d->m_future = Utils::asyncRun(CppModelManager::instance()->sharedThreadPool(), - &SemanticInfoUpdaterPrivate::update_helper, d.data(), source); + d->m_watcher.reset(new QFutureWatcher<SemanticInfo>); + connect(d->m_watcher.get(), &QFutureWatcherBase::finished, this, [this] { + d->m_semanticInfo = d->m_watcher->result(); + emit updated(d->m_semanticInfo); + d->m_watcher.release()->deleteLater(); + }); + const auto future = Utils::asyncRun(CppModelManager::sharedThreadPool(), doUpdate, source); + d->m_watcher->setFuture(future); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future); } SemanticInfo SemanticInfoUpdater::semanticInfo() const { - return d->semanticInfo(); + return d->m_semanticInfo; } } // namespace CppEditor diff --git a/src/plugins/cppeditor/cppsourceprocessor.cpp b/src/plugins/cppeditor/cppsourceprocessor.cpp index 55dc1f10e05..4cdc6962416 100644 --- a/src/plugins/cppeditor/cppsourceprocessor.cpp +++ b/src/plugins/cppeditor/cppsourceprocessor.cpp @@ -9,6 +9,7 @@ #include <coreplugin/editormanager/editormanager.h> +#include <utils/algorithm.h> #include <utils/filepath.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> @@ -363,13 +364,9 @@ void CppSourceProcessor::mergeEnvironment(Document::Ptr doc) if (!doc) return; - const QString fn = doc->filePath().path(); - - if (m_processed.contains(fn)) + if (!Utils::insert(m_processed, doc->filePath())) return; - m_processed.insert(fn); - const QList<Document::Include> includes = doc->resolvedIncludes(); for (const Document::Include &incl : includes) { const FilePath includedFile = incl.resolvedFileName(); diff --git a/src/plugins/cppeditor/cppsourceprocessor.h b/src/plugins/cppeditor/cppsourceprocessor.h index 75ea0fa8d21..d5a2763e30d 100644 --- a/src/plugins/cppeditor/cppsourceprocessor.h +++ b/src/plugins/cppeditor/cppsourceprocessor.h @@ -96,7 +96,7 @@ private: QSet<Utils::FilePath> m_included; CPlusPlus::Document::Ptr m_currentDoc; QSet<QString> m_todo; - QSet<QString> m_processed; + QSet<Utils::FilePath> m_processed; QHash<Utils::FilePath, Utils::FilePath> m_fileNameCache; int m_fileSizeLimitInMb = -1; QTextCodec *m_defaultCodec; diff --git a/src/plugins/cppeditor/cppsourceprocessor_test.cpp b/src/plugins/cppeditor/cppsourceprocessor_test.cpp index b6ec4a4095e..23c5e111f63 100644 --- a/src/plugins/cppeditor/cppsourceprocessor_test.cpp +++ b/src/plugins/cppeditor/cppsourceprocessor_test.cpp @@ -34,7 +34,6 @@ class SourcePreprocessor { public: SourcePreprocessor() - : m_cmm(CppModelManager::instance()) { cleanUp(); } @@ -47,7 +46,7 @@ public: TestIncludePaths::directoryOfTestFile())}); sourceProcessor->run(filePath); - Document::Ptr document = m_cmm->document(filePath); + Document::Ptr document = CppModelManager::document(filePath); return document; } @@ -59,12 +58,9 @@ public: private: void cleanUp() { - m_cmm->GC(); - QVERIFY(m_cmm->snapshot().isEmpty()); + CppModelManager::GC(); + QVERIFY(CppModelManager::snapshot().isEmpty()); } - -private: - CppModelManager *m_cmm; }; /// Check: Resolved and unresolved includes are properly tracked. diff --git a/src/plugins/cppeditor/cpptoolsjsextension.cpp b/src/plugins/cppeditor/cpptoolsjsextension.cpp index 73a846593c1..6e3c1f1a7dc 100644 --- a/src/plugins/cppeditor/cpptoolsjsextension.cpp +++ b/src/plugins/cppeditor/cpptoolsjsextension.cpp @@ -3,6 +3,7 @@ #include "cpptoolsjsextension.h" +#include "cppeditorplugin.h" #include "cppfilesettingspage.h" #include "cpplocatordata.h" #include "cppworkingcopy.h" @@ -12,6 +13,7 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projectnodes.h> +#include <projectexplorer/projecttree.h> #include <cplusplus/AST.h> #include <cplusplus/ASTPath.h> @@ -27,6 +29,13 @@ namespace CppEditor::Internal { +static CppFileSettings fileSettings() +{ + // Note that the user can set a different project in the wizard *after* the file names + // have been determined. There's nothing we can do about that here. + return CppEditorPlugin::fileSettings(ProjectExplorer::ProjectTree::currentProject()); +} + static QString fileName(const QString &path, const QString &extension) { return Utils::FilePath::fromStringWithExtension(path, extension).toString(); @@ -37,6 +46,16 @@ QString CppToolsJsExtension::headerGuard(const QString &in) const return Utils::headerGuard(in); } +QString CppToolsJsExtension::licenseTemplate() const +{ + return fileSettings().licenseTemplate(); +} + +bool CppToolsJsExtension::usePragmaOnce() const +{ + return fileSettings().headerPragmaOnce; +} + static QStringList parts(const QString &klass) { return klass.split(QStringLiteral("::")); @@ -63,8 +82,7 @@ QString CppToolsJsExtension::className(const QString &klass) const QString CppToolsJsExtension::classToFileName(const QString &klass, const QString &extension) const { const QString raw = fileName(className(klass), extension); - CppFileSettings settings; - settings.fromSettings(Core::ICore::settings()); + const CppFileSettings &settings = fileSettings(); if (!settings.lowerCaseFiles) return raw; @@ -133,8 +151,8 @@ bool CppToolsJsExtension::hasQObjectParent(const QString &klassName) const const IndexItem::Ptr item = candidates.first(); // Find class in AST. - const CPlusPlus::Snapshot snapshot = CppModelManager::instance()->snapshot(); - const WorkingCopy workingCopy = CppModelManager::instance()->workingCopy(); + const CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); + const WorkingCopy workingCopy = CppModelManager::workingCopy(); std::optional<QByteArray> source = workingCopy.source(item->filePath()); if (!source) { const Utils::expected_str<QByteArray> contents = item->filePath().fileContents(); @@ -249,4 +267,14 @@ QString CppToolsJsExtension::includeStatement( return {}; } +QString CppToolsJsExtension::cxxHeaderSuffix() const +{ + return fileSettings().headerSuffix; +} + +QString CppToolsJsExtension::cxxSourceSuffix() const +{ + return fileSettings().sourceSuffix; +} + } // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/cpptoolsjsextension.h b/src/plugins/cppeditor/cpptoolsjsextension.h index cdb7e56c813..593f9536411 100644 --- a/src/plugins/cppeditor/cpptoolsjsextension.h +++ b/src/plugins/cppeditor/cpptoolsjsextension.h @@ -26,6 +26,12 @@ public: // Generate header guard: Q_INVOKABLE QString headerGuard(const QString &in) const; + // Generate license template: + Q_INVOKABLE QString licenseTemplate() const; + + // Use #pragma once: + Q_INVOKABLE bool usePragmaOnce() const; + // Work with classes: Q_INVOKABLE QStringList namespaces(const QString &klass) const; Q_INVOKABLE bool hasNamespaces(const QString &klass) const; @@ -46,6 +52,10 @@ public: const QString &pathOfIncludingFile ); + // File suffixes: + Q_INVOKABLE QString cxxHeaderSuffix() const; + Q_INVOKABLE QString cxxSourceSuffix() const; + private: CppLocatorData * const m_locatorData; }; diff --git a/src/plugins/cppeditor/cpptoolsreuse.cpp b/src/plugins/cppeditor/cpptoolsreuse.cpp index 8f68ed3ec9d..543f046237d 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.cpp +++ b/src/plugins/cppeditor/cpptoolsreuse.cpp @@ -5,10 +5,12 @@ #include "clangdiagnosticconfigsmodel.h" #include "cppautocompleter.h" +#include "cppcanonicalsymbol.h" #include "cppcodemodelsettings.h" #include "cppcompletionassist.h" #include "cppeditorconstants.h" #include "cppeditorplugin.h" +#include "cppeditorwidget.h" #include "cppeditortr.h" #include "cppfilesettingspage.h" #include "cpphighlighter.h" @@ -28,20 +30,28 @@ #include <texteditor/textdocument.h> #include <cplusplus/BackwardsScanner.h> +#include <cplusplus/declarationcomments.h> #include <cplusplus/LookupContext.h> #include <cplusplus/Overview.h> #include <cplusplus/SimpleLexer.h> #include <utils/algorithm.h> -#include <utils/textutils.h> #include <utils/qtcassert.h> +#include <utils/textfileformat.h> +#include <utils/textutils.h> #include <QDebug> +#include <QElapsedTimer> +#include <QHash> #include <QRegularExpression> #include <QSet> +#include <QStringView> #include <QTextCursor> #include <QTextDocument> +#include <optional> +#include <vector> + using namespace CPlusPlus; using namespace Utils; @@ -611,9 +621,229 @@ void openEditor(const Utils::FilePath &filePath, bool inNextSplit, Utils::Id edi : EditorManager::NoFlags); } -bool preferLowerCaseFileNames() +bool preferLowerCaseFileNames(ProjectExplorer::Project *project) { - return Internal::CppEditorPlugin::fileSettings()->lowerCaseFiles; + return Internal::CppEditorPlugin::fileSettings(project).lowerCaseFiles; +} + +QString preferredCxxHeaderSuffix(ProjectExplorer::Project *project) +{ + return Internal::CppEditorPlugin::fileSettings(project).headerSuffix; +} + +QString preferredCxxSourceSuffix(ProjectExplorer::Project *project) +{ + return Internal::CppEditorPlugin::fileSettings(project).sourceSuffix; +} + +SearchResultItems symbolOccurrencesInDeclarationComments( + const Utils::SearchResultItems &symbolOccurrencesInCode) +{ + if (symbolOccurrencesInCode.isEmpty()) + return {}; + + // When using clangd, this function gets called every time the replacement string changes, + // so cache the results. + static QHash<SearchResultItems, SearchResultItems> resultCache; + if (const auto it = resultCache.constFind(symbolOccurrencesInCode); + it != resultCache.constEnd()) { + return it.value(); + } + if (resultCache.size() > 5) + resultCache.clear(); + + QElapsedTimer timer; + timer.start(); + Snapshot snapshot = CppModelManager::snapshot(); + std::vector<std::unique_ptr<QTextDocument>> docPool; + using FileData = std::tuple<QTextDocument *, QString, Document::Ptr, QList<Token>>; + QHash<FilePath, FileData> dataPerFile; + QString symbolName; + const auto fileData = [&](const FilePath &filePath) -> FileData & { + auto &data = dataPerFile[filePath]; + auto &[doc, content, cppDoc, allCommentTokens] = data; + if (!doc) { + if (TextEditor::TextDocument * const textDoc + = TextEditor::TextDocument::textDocumentForFilePath(filePath)) { + doc = textDoc->document(); + } else { + std::unique_ptr<QTextDocument> newDoc = std::make_unique<QTextDocument>(); + if (const auto content = TextFileFormat::readFile( + filePath, Core::EditorManager::defaultTextCodec())) { + newDoc->setPlainText(content.value()); + } + doc = newDoc.get(); + docPool.push_back(std::move(newDoc)); + } + content = doc->toPlainText(); + cppDoc = snapshot.preprocessedDocument(content.toUtf8(), filePath); + cppDoc->check(); + } + return data; + }; + static const auto addToken = [](QList<Token> &tokens, const Token &tok) { + if (!Utils::contains(tokens, [&tok](const Token &t) { + return t.byteOffset == tok.byteOffset; })) { + tokens << tok; + } + }; + + struct ClassInfo { + FilePath filePath; + int startOffset = -1; + int endOffset = -1; + }; + std::optional<ClassInfo> classInfo; + + // Collect comment blocks associated with replace locations. + for (const SearchResultItem &item : symbolOccurrencesInCode) { + const FilePath filePath = FilePath::fromUserInput(item.path().last()); + auto &[doc, content, cppDoc, allCommentTokens] = fileData(filePath); + const Text::Range &range = item.mainRange(); + if (symbolName.isEmpty()) { + const int symbolStartPos = Utils::Text::positionInText(doc, range.begin.line, + range.begin.column + 1); + const int symbolEndPos = Utils::Text::positionInText(doc, range.end.line, + range.end.column + 1); + symbolName = content.mid(symbolStartPos, symbolEndPos - symbolStartPos); + } + const QList<Token> commentTokens = commentsForDeclaration(symbolName, range.begin, + *doc, cppDoc); + for (const Token &tok : commentTokens) + addToken(allCommentTokens, tok); + + if (!classInfo) { + QTextCursor cursor(doc); + cursor.setPosition(Text::positionInText(doc, range.begin.line, range.begin.column + 1)); + Internal::CanonicalSymbol cs(cppDoc, snapshot); + Symbol * const canonicalSymbol = cs(cursor); + if (canonicalSymbol) { + classInfo.emplace(); + if (Class * const klass = canonicalSymbol->asClass()) { + classInfo->filePath = canonicalSymbol->filePath(); + classInfo->startOffset = klass->startOffset(); + classInfo->endOffset = klass->endOffset(); + } + } + } + + // We hook in between the end of the "regular" search and (possibly non-interactive) + // actions on it, so we must run synchronously in the UI thread and therefore be fast. + // If we notice we are lagging, just abort, as renaming the comments is not + // required for code correctness. + if (timer.elapsed() > 1000) { + resultCache.insert(symbolOccurrencesInCode, {}); + return {}; + } + } + + // If the symbol is a class, collect all comment blocks in the class body. + if (classInfo && !classInfo->filePath.isEmpty()) { + auto &[_1, _2, symbolCppDoc, commentTokens] = fileData(classInfo->filePath); + TranslationUnit * const tu = symbolCppDoc->translationUnit(); + for (int i = 0; i < tu->commentCount(); ++i) { + const Token &tok = tu->commentAt(i); + if (tok.bytesBegin() < classInfo->startOffset) + continue; + if (tok.bytesBegin() >= classInfo->endOffset) + break; + addToken(commentTokens, tok); + } + } + + // Create new replace items for occurrences of the symbol name in collected comment blocks. + SearchResultItems commentItems; + for (auto it = dataPerFile.cbegin(); it != dataPerFile.cend(); ++it) { + const auto &[doc, content, cppDoc, commentTokens] = it.value(); + const QStringView docView(content); + for (const Token &tok : commentTokens) { + const int tokenStartPos = cppDoc->translationUnit()->getTokenPositionInDocument( + tok, doc); + const int tokenEndPos = cppDoc->translationUnit()->getTokenEndPositionInDocument( + tok, doc); + const QStringView tokenView = docView.mid(tokenStartPos, tokenEndPos - tokenStartPos); + const QList<Text::Range> ranges = symbolOccurrencesInText( + *doc, tokenView, tokenStartPos, symbolName); + for (const Text::Range &range : ranges) { + SearchResultItem item; + item.setUseTextEditorFont(true); + item.setFilePath(it.key()); + item.setMainRange(range); + item.setLineText(doc->findBlockByNumber(range.begin.line - 1).text()); + commentItems << item; + } + } + } + + resultCache.insert(symbolOccurrencesInCode, commentItems); + return commentItems; +} + +QList<Text::Range> symbolOccurrencesInText(const QTextDocument &doc, QStringView text, int offset, + const QString &symbolName) +{ + QList<Text::Range> ranges; + int index = 0; + while (true) { + index = text.indexOf(symbolName, index); + if (index == -1) + break; + + // Prevent substring matching. + const auto checkAdjacent = [&](int i) { + if (i == -1 || i == text.size()) + return true; + const QChar c = text.at(i); + if (c.isLetterOrNumber() || c == '_') { + index += symbolName.length(); + return false; + } + return true; + }; + if (!checkAdjacent(index - 1)) + continue; + if (!checkAdjacent(index + symbolName.length())) + continue; + + const Text::Position startPos = Text::Position::fromPositionInDocument(&doc, offset + index); + index += symbolName.length(); + const Text::Position endPos = Text::Position::fromPositionInDocument(&doc, offset + index); + ranges << Text::Range{startPos, endPos}; + } + return ranges; +} + +QList<Text::Range> symbolOccurrencesInDeclarationComments(CppEditorWidget *editorWidget, + const QTextCursor &cursor) +{ + if (!editorWidget) + return {}; + const SemanticInfo &semanticInfo = editorWidget->semanticInfo(); + const Document::Ptr &cppDoc = semanticInfo.doc; + if (!cppDoc) + return {}; + Internal::CanonicalSymbol cs(cppDoc, semanticInfo.snapshot); + const Symbol * const symbol = cs(cursor); + if (!symbol || !symbol->asArgument()) + return {}; + const QTextDocument * const textDoc = editorWidget->textDocument()->document(); + QTC_ASSERT(textDoc, return {}); + const QList<Token> comments = commentsForDeclaration(symbol, *textDoc, cppDoc); + if (comments.isEmpty()) + return {}; + QList<Text::Range> ranges; + const QString &content = textDoc->toPlainText(); + const QStringView docView = QStringView(content); + const QString symbolName = Overview().prettyName(symbol->name()); + for (const Token &tok : comments) { + const int tokenStartPos = cppDoc->translationUnit()->getTokenPositionInDocument( + tok, textDoc); + const int tokenEndPos = cppDoc->translationUnit()->getTokenEndPositionInDocument( + tok, textDoc); + const QStringView tokenView = docView.mid(tokenStartPos, tokenEndPos - tokenStartPos); + ranges << symbolOccurrencesInText(*textDoc, tokenView, tokenStartPos, symbolName); + } + return ranges; } namespace Internal { diff --git a/src/plugins/cppeditor/cpptoolsreuse.h b/src/plugins/cppeditor/cpptoolsreuse.h index 9948ab6de70..fc90b900a24 100644 --- a/src/plugins/cppeditor/cpptoolsreuse.h +++ b/src/plugins/cppeditor/cpptoolsreuse.h @@ -12,6 +12,8 @@ #include <texteditor/quickfix.h> #include <texteditor/texteditor.h> +#include <utils/searchresultitem.h> + #include <cplusplus/ASTVisitor.h> #include <cplusplus/CppDocument.h> #include <cplusplus/Token.h> @@ -23,9 +25,10 @@ class LookupContext; } // namespace CPlusPlus namespace TextEditor { class AssistInterface; } +namespace Utils { namespace Text { class Range; } } namespace CppEditor { - +class CppEditorWidget; class CppRefactoringFile; class ProjectInfo; class CppCompletionAssistProcessor; @@ -67,7 +70,17 @@ void CPPEDITOR_EXPORT openEditor(const Utils::FilePath &filePath, bool inNextSpl class CppCodeModelSettings; CppCodeModelSettings CPPEDITOR_EXPORT *codeModelSettings(); -bool CPPEDITOR_EXPORT preferLowerCaseFileNames(); +QString CPPEDITOR_EXPORT preferredCxxHeaderSuffix(ProjectExplorer::Project *project); +QString CPPEDITOR_EXPORT preferredCxxSourceSuffix(ProjectExplorer::Project *project); +bool CPPEDITOR_EXPORT preferLowerCaseFileNames(ProjectExplorer::Project *project); + + +QList<Utils::Text::Range> CPPEDITOR_EXPORT symbolOccurrencesInText( + const QTextDocument &doc, QStringView text, int offset, const QString &symbolName); +Utils::SearchResultItems CPPEDITOR_EXPORT +symbolOccurrencesInDeclarationComments(const Utils::SearchResultItems &symbolOccurrencesInCode); +QList<Utils::Text::Range> CPPEDITOR_EXPORT symbolOccurrencesInDeclarationComments( + CppEditorWidget *editorWidget, const QTextCursor &cursor); UsePrecompiledHeaders CPPEDITOR_EXPORT getPchUsage(); diff --git a/src/plugins/cppeditor/cpptoolssettings.cpp b/src/plugins/cppeditor/cpptoolssettings.cpp index 0b5536fdd72..ad3dfd18388 100644 --- a/src/plugins/cppeditor/cpptoolssettings.cpp +++ b/src/plugins/cppeditor/cpptoolssettings.cpp @@ -9,48 +9,45 @@ #include "cppcodestylepreferencesfactory.h" #include <coreplugin/icore.h> -#include <texteditor/commentssettings.h> + +#include <extensionsystem/pluginmanager.h> + #include <texteditor/completionsettingspage.h> #include <texteditor/codestylepool.h> #include <texteditor/tabsettings.h> #include <texteditor/texteditorsettings.h> -#include <extensionsystem/pluginmanager.h> #include <utils/qtcassert.h> -#include <utils/settingsutils.h> - -#include <QSettings> static const char idKey[] = "CppGlobal"; const bool kSortEditorDocumentOutlineDefault = true; using namespace Core; using namespace TextEditor; +using namespace Utils; namespace CppEditor { namespace Internal { + class CppToolsSettingsPrivate { public: - CommentsSettings m_commentsSettings; CppCodeStylePreferences *m_globalCodeStyle = nullptr; }; -} // namespace Internal -CppToolsSettings *CppToolsSettings::m_instance = nullptr; +} // Internal + +CppToolsSettings *m_instance = nullptr; +Internal::CppToolsSettingsPrivate *d = nullptr; CppToolsSettings::CppToolsSettings() - : d(new Internal::CppToolsSettingsPrivate) { QTC_ASSERT(!m_instance, return); m_instance = this; + d = new Internal::CppToolsSettingsPrivate; qRegisterMetaType<CppCodeStyleSettings>("CppEditor::CppCodeStyleSettings"); - d->m_commentsSettings = TextEditorSettings::commentsSettings(); - connect(TextEditorSettings::instance(), &TextEditorSettings::commentsSettingsChanged, - this, &CppToolsSettings::setCommentsSettings); - // code style factory ICodeStylePreferencesFactory *factory = new CppCodeStylePreferencesFactory(); TextEditorSettings::registerCodeStyleFactory(factory); @@ -130,9 +127,8 @@ CppToolsSettings::CppToolsSettings() pool->loadCustomCodeStyles(); - QSettings *s = ICore::settings(); // load global settings (after built-in settings are added to the pool) - d->m_globalCodeStyle->fromSettings(QLatin1String(Constants::CPP_SETTINGS_ID), s); + d->m_globalCodeStyle->fromSettings(Constants::CPP_SETTINGS_ID); // mimetypes to be handled TextEditorSettings::registerMimeTypeForLanguageId(Constants::C_SOURCE_MIMETYPE, Constants::CPP_SETTINGS_ID); @@ -157,29 +153,18 @@ CppToolsSettings *CppToolsSettings::instance() return m_instance; } -CppCodeStylePreferences *CppToolsSettings::cppCodeStyle() const +CppCodeStylePreferences *CppToolsSettings::cppCodeStyle() { return d->m_globalCodeStyle; } -const CommentsSettings &CppToolsSettings::commentsSettings() const +static Key sortEditorDocumentOutlineKey() { - return d->m_commentsSettings; + return Key(Constants::CPPEDITOR_SETTINGSGROUP) + + '/' + Constants::CPPEDITOR_SORT_EDITOR_DOCUMENT_OUTLINE; } -void CppToolsSettings::setCommentsSettings(const CommentsSettings &commentsSettings) -{ - d->m_commentsSettings = commentsSettings; -} - -static QString sortEditorDocumentOutlineKey() -{ - return QLatin1String(Constants::CPPEDITOR_SETTINGSGROUP) - + QLatin1Char('/') - + QLatin1String(Constants::CPPEDITOR_SORT_EDITOR_DOCUMENT_OUTLINE); -} - -bool CppToolsSettings::sortedEditorDocumentOutline() const +bool CppToolsSettings::sortedEditorDocumentOutline() { return ICore::settings() ->value(sortEditorDocumentOutlineKey(), kSortEditorDocumentOutlineDefault) @@ -191,7 +176,6 @@ void CppToolsSettings::setSortedEditorDocumentOutline(bool sorted) ICore::settings()->setValueWithDefault(sortEditorDocumentOutlineKey(), sorted, kSortEditorDocumentOutlineDefault); - emit editorDocumentOutlineSortingChanged(sorted); } } // namespace CppEditor diff --git a/src/plugins/cppeditor/cpptoolssettings.h b/src/plugins/cppeditor/cpptoolssettings.h index 32a48234145..674b0d0053b 100644 --- a/src/plugins/cppeditor/cpptoolssettings.h +++ b/src/plugins/cppeditor/cpptoolssettings.h @@ -7,42 +7,24 @@ #include <QObject> -namespace TextEditor { class CommentsSettings; } +namespace CppEditor { -namespace CppEditor -{ class CppCodeStylePreferences; -namespace Internal { class CppToolsSettingsPrivate; } +// This class is meant to go away. -/** - * This class provides a central place for cpp tools settings. - */ class CPPEDITOR_EXPORT CppToolsSettings : public QObject { - Q_OBJECT - public: CppToolsSettings(); ~CppToolsSettings() override; static CppToolsSettings *instance(); - CppCodeStylePreferences *cppCodeStyle() const; + static CppCodeStylePreferences *cppCodeStyle(); - const TextEditor::CommentsSettings &commentsSettings() const; - void setCommentsSettings(const TextEditor::CommentsSettings &commentsSettings); - - bool sortedEditorDocumentOutline() const; - void setSortedEditorDocumentOutline(bool sorted); - -signals: - void editorDocumentOutlineSortingChanged(bool isSorted); - -private: - Internal::CppToolsSettingsPrivate *d; - - static CppToolsSettings *m_instance; + static bool sortedEditorDocumentOutline(); + static void setSortedEditorDocumentOutline(bool sorted); }; } // namespace CppEditor diff --git a/src/plugins/cppeditor/cpptoolstestcase.cpp b/src/plugins/cppeditor/cpptoolstestcase.cpp index 810f66db49c..420e551847d 100644 --- a/src/plugins/cppeditor/cpptoolstestcase.cpp +++ b/src/plugins/cppeditor/cpptoolstestcase.cpp @@ -149,15 +149,15 @@ VerifyCleanCppModelManager::~VerifyCleanCppModelManager() { bool VerifyCleanCppModelManager::isClean(bool testOnlyForCleanedProjects) { - CppModelManager *mm = CppModelManager::instance(); - RETURN_FALSE_IF_NOT(mm->projectInfos().isEmpty()); - RETURN_FALSE_IF_NOT(mm->headerPaths().isEmpty()); - RETURN_FALSE_IF_NOT(mm->definedMacros().isEmpty()); - RETURN_FALSE_IF_NOT(mm->projectFiles().isEmpty()); + RETURN_FALSE_IF_NOT(CppModelManager::projectInfos().isEmpty()); + RETURN_FALSE_IF_NOT(CppModelManager::headerPaths().isEmpty()); + RETURN_FALSE_IF_NOT(CppModelManager::definedMacros().isEmpty()); + RETURN_FALSE_IF_NOT(CppModelManager::projectFiles().isEmpty()); if (!testOnlyForCleanedProjects) { - RETURN_FALSE_IF_NOT(mm->snapshot().isEmpty()); - RETURN_FALSE_IF_NOT(mm->workingCopy().size() == 1); - RETURN_FALSE_IF_NOT(mm->workingCopy().get(mm->configurationFileName())); + RETURN_FALSE_IF_NOT(CppModelManager::snapshot().isEmpty()); + RETURN_FALSE_IF_NOT(CppModelManager::workingCopy().size() == 1); + RETURN_FALSE_IF_NOT(CppModelManager::workingCopy() + .get(CppModelManager::configurationFileName())); } return true; } @@ -170,9 +170,9 @@ namespace CppEditor::Tests { static bool closeEditorsWithoutGarbageCollectorInvocation(const QList<Core::IEditor *> &editors) { - CppModelManager::instance()->enableGarbageCollector(false); + CppModelManager::enableGarbageCollector(false); const bool closeEditorsSucceeded = Core::EditorManager::closeEditors(editors, false); - CppModelManager::instance()->enableGarbageCollector(true); + CppModelManager::enableGarbageCollector(true); return closeEditorsSucceeded; } @@ -188,8 +188,7 @@ static bool snapshotContains(const CPlusPlus::Snapshot &snapshot, const QSet<Fil } TestCase::TestCase(bool runGarbageCollector) - : m_modelManager(CppModelManager::instance()) - , m_succeededSoFar(false) + : m_succeededSoFar(false) , m_runGarbageCollector(runGarbageCollector) { if (m_runGarbageCollector) @@ -239,12 +238,12 @@ bool TestCase::openCppEditor(const FilePath &filePath, TextEditor::BaseTextEdito CPlusPlus::Snapshot TestCase::globalSnapshot() { - return CppModelManager::instance()->snapshot(); + return CppModelManager::snapshot(); } bool TestCase::garbageCollectGlobalSnapshot() { - CppModelManager::instance()->GC(); + CppModelManager::GC(); return globalSnapshot().isEmpty(); } @@ -269,7 +268,7 @@ static bool waitForProcessedEditorDocument_internal(CppEditorDocumentHandle *edi bool TestCase::waitForProcessedEditorDocument(const FilePath &filePath, int timeOutInMs) { - auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + auto *editorDocument = CppModelManager::cppEditorDocument(filePath); return waitForProcessedEditorDocument_internal(editorDocument, timeOutInMs); } @@ -282,7 +281,7 @@ CPlusPlus::Document::Ptr TestCase::waitForRehighlightedSemanticDocument(CppEdito bool TestCase::parseFiles(const QSet<FilePath> &filePaths) { - CppModelManager::instance()->updateSourceFiles(filePaths).waitForFinished(); + CppModelManager::updateSourceFiles(filePaths).waitForFinished(); QCoreApplication::processEvents(); const CPlusPlus::Snapshot snapshot = globalSnapshot(); if (snapshot.isEmpty()) { @@ -333,7 +332,7 @@ QList<CPlusPlus::Document::Ptr> TestCase::waitForFilesInGlobalSnapshot(const Fil break; } if (t.elapsed() > timeOutInMs) - return QList<CPlusPlus::Document::Ptr>(); + return {}; QCoreApplication::processEvents(); } } @@ -349,7 +348,7 @@ bool TestCase::waitUntilProjectIsFullyOpened(Project *project, int timeOutInMs) [project]() { return ProjectManager::startupBuildSystem() && !ProjectManager::startupBuildSystem()->isParsing() - && CppModelManager::instance()->projectInfo(project); + && CppModelManager::projectInfo(project); }, timeOutInMs); } @@ -405,7 +404,7 @@ ProjectInfo::ConstPtr ProjectOpenerAndCloser::open(const FilePath &projectFile, if (TestCase::waitUntilProjectIsFullyOpened(project)) { m_openProjects.append(project); - return CppModelManager::instance()->projectInfo(project); + return CppModelManager::projectInfo(project); } return {}; diff --git a/src/plugins/cppeditor/cpptoolstestcase.h b/src/plugins/cppeditor/cpptoolstestcase.h index 84cf0f088a8..13548630e65 100644 --- a/src/plugins/cppeditor/cpptoolstestcase.h +++ b/src/plugins/cppeditor/cpptoolstestcase.h @@ -32,7 +32,6 @@ class IAssistProposal; namespace CppEditor { class CppEditorWidget; -class CppModelManager; namespace Internal::Tests { @@ -152,7 +151,6 @@ public: static bool writeFile(const Utils::FilePath &filePath, const QByteArray &contents); protected: - CppModelManager *m_modelManager; bool m_succeededSoFar; private: diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp index 837375b6f37..9b907c6e439 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.cpp +++ b/src/plugins/cppeditor/cpptypehierarchy.cpp @@ -12,26 +12,82 @@ #include <coreplugin/find/itemviewfind.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/progressmanager/progressmanager.h> + #include <texteditor/texteditor.h> + #include <utils/algorithm.h> #include <utils/delegates.h> #include <utils/dropsupport.h> +#include <utils/futuresynchronizer.h> #include <utils/navigationtreeview.h> #include <utils/progressindicator.h> -#include <QApplication> +#include <QFuture> +#include <QFutureWatcher> #include <QLabel> #include <QLatin1String> #include <QMenu> #include <QModelIndex> +#include <QSharedPointer> #include <QStackedLayout> +#include <QStackedWidget> +#include <QStandardItemModel> #include <QVBoxLayout> -using namespace CppEditor; -using namespace CppEditor::Internal; using namespace Utils; -namespace { +namespace CppEditor::Internal { + +class CppClass; +class CppElement; + +class CppTypeHierarchyModel : public QStandardItemModel +{ +public: + CppTypeHierarchyModel(QObject *parent) + : QStandardItemModel(parent) + {} + + Qt::DropActions supportedDragActions() const override; + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; +}; + +class CppTypeHierarchyWidget : public QWidget +{ +public: + CppTypeHierarchyWidget(); + + void perform(); + +private: + void displayHierarchy(); + typedef QList<CppClass> CppClass::*HierarchyMember; + void performFromExpression(const QString &expression, const FilePath &filePath); + QStandardItem *buildHierarchy(const CppClass &cppClass, QStandardItem *parent, + bool isRoot, HierarchyMember member); + void showNoTypeHierarchyLabel(); + void showTypeHierarchy(); + void showProgress(); + void hideProgress(); + void clearTypeHierarchy(); + void onItemActivated(const QModelIndex &index); + void onItemDoubleClicked(const QModelIndex &index); + + NavigationTreeView *m_treeView = nullptr; + QWidget *m_hierarchyWidget = nullptr; + QStackedLayout *m_stackLayout = nullptr; + QStandardItemModel *m_model = nullptr; + AnnotatedItemDelegate *m_delegate = nullptr; + TextEditor::TextEditorLinkLabel *m_inspectedClass = nullptr; + QLabel *m_infoLabel = nullptr; + QFuture<QSharedPointer<CppElement>> m_future; + QFutureWatcher<void> m_futureWatcher; + FutureSynchronizer m_synchronizer; + ProgressIndicator *m_progressIndicator = nullptr; + QString m_oldClass; + bool m_showOldClass = false; +}; enum ItemRole { AnnotationRole = Qt::UserRole + 1, @@ -61,55 +117,44 @@ QList<CppClass> sortClasses(const QList<CppClass> &cppClasses) }); } -} // Anonymous - class CppTypeHierarchyTreeView : public NavigationTreeView { - Q_OBJECT public: - CppTypeHierarchyTreeView(QWidget *parent); + CppTypeHierarchyTreeView(QWidget *parent) + : NavigationTreeView(parent) + {} - void contextMenuEvent(QContextMenuEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override + { + if (!event) + return; + + QMenu contextMenu; + + QAction *action = contextMenu.addAction(Tr::tr("Open in Editor")); + connect(action, &QAction::triggered, this, [this] () { + emit activated(currentIndex()); + }); + action = contextMenu.addAction(Tr::tr("Open Type Hierarchy")); + connect(action, &QAction::triggered, this, [this] () { + emit doubleClicked(currentIndex()); + }); + + contextMenu.addSeparator(); + + action = contextMenu.addAction(Tr::tr("Expand All")); + connect(action, &QAction::triggered, this, &QTreeView::expandAll); + action = contextMenu.addAction(Tr::tr("Collapse All")); + connect(action, &QAction::triggered, this, &QTreeView::collapseAll); + + contextMenu.exec(event->globalPos()); + + event->accept(); + } }; - -CppTypeHierarchyTreeView::CppTypeHierarchyTreeView(QWidget *parent) : - NavigationTreeView(parent) -{ -} - -void CppTypeHierarchyTreeView::contextMenuEvent(QContextMenuEvent *event) -{ - if (!event) - return; - - QMenu contextMenu; - - QAction *action = contextMenu.addAction(Tr::tr("Open in Editor")); - connect(action, &QAction::triggered, this, [this] () { - emit activated(currentIndex()); - }); - action = contextMenu.addAction(Tr::tr("Open Type Hierarchy")); - connect(action, &QAction::triggered, this, [this] () { - emit doubleClicked(currentIndex()); - }); - - contextMenu.addSeparator(); - - action = contextMenu.addAction(Tr::tr("Expand All")); - connect(action, &QAction::triggered, this, &QTreeView::expandAll); - action = contextMenu.addAction(Tr::tr("Collapse All")); - connect(action, &QAction::triggered, this, &QTreeView::collapseAll); - - contextMenu.exec(event->globalPos()); - - event->accept(); -} - -namespace CppEditor { -namespace Internal { - // CppTypeHierarchyWidget + CppTypeHierarchyWidget::CppTypeHierarchyWidget() { m_inspectedClass = new TextEditor::TextEditorLinkLabel(this); @@ -323,26 +368,6 @@ void CppTypeHierarchyWidget::onItemDoubleClicked(const QModelIndex &index) performFromExpression(getExpression(index), link.targetFilePath); } -// CppTypeHierarchyFactory -CppTypeHierarchyFactory::CppTypeHierarchyFactory() -{ - setDisplayName(Tr::tr("Type Hierarchy")); - setPriority(700); - setId(Constants::TYPE_HIERARCHY_ID); -} - -Core::NavigationView CppTypeHierarchyFactory::createWidget() -{ - auto w = new CppTypeHierarchyWidget; - w->perform(); - return {w, {}}; -} - -CppTypeHierarchyModel::CppTypeHierarchyModel(QObject *parent) - : QStandardItemModel(parent) -{ -} - Qt::DropActions CppTypeHierarchyModel::supportedDragActions() const { // copy & move actions to avoid idiotic behavior of drag and drop: @@ -369,7 +394,20 @@ QMimeData *CppTypeHierarchyModel::mimeData(const QModelIndexList &indexes) const return data; } -} // namespace Internal -} // namespace CppEditor +// CppTypeHierarchyFactory -#include "cpptypehierarchy.moc" +CppTypeHierarchyFactory::CppTypeHierarchyFactory() +{ + setDisplayName(Tr::tr("Type Hierarchy")); + setPriority(700); + setId(Constants::TYPE_HIERARCHY_ID); +} + +Core::NavigationView CppTypeHierarchyFactory::createWidget() +{ + auto w = new CppTypeHierarchyWidget; + w->perform(); + return {w, {}}; +} + +} // CppEditor::Internal diff --git a/src/plugins/cppeditor/cpptypehierarchy.h b/src/plugins/cppeditor/cpptypehierarchy.h index 4e782242224..37eaec2a78e 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.h +++ b/src/plugins/cppeditor/cpptypehierarchy.h @@ -4,100 +4,15 @@ #pragma once #include <coreplugin/inavigationwidgetfactory.h> -#include <utils/futuresynchronizer.h> -#include <QFuture> -#include <QFutureWatcher> -#include <QList> -#include <QSharedPointer> -#include <QStackedWidget> -#include <QStandardItemModel> -#include <QString> -#include <QWidget> - -QT_BEGIN_NAMESPACE -class QLabel; -class QModelIndex; -class QStackedLayout; -class QStandardItem; -QT_END_NAMESPACE - -namespace TextEditor { class TextEditorLinkLabel; } - -namespace Utils { -class AnnotatedItemDelegate; -class NavigationTreeView; -class ProgressIndicator; -} - -namespace CppEditor { -class CppEditorWidget; - -namespace Internal { -class CppClass; -class CppElement; - -class CppTypeHierarchyModel : public QStandardItemModel -{ - Q_OBJECT - -public: - CppTypeHierarchyModel(QObject *parent); - - Qt::DropActions supportedDragActions() const override; - QStringList mimeTypes() const override; - QMimeData *mimeData(const QModelIndexList &indexes) const override; -}; - -class CppTypeHierarchyWidget : public QWidget -{ - Q_OBJECT -public: - CppTypeHierarchyWidget(); - - void perform(); - -private slots: - void displayHierarchy(); - -private: - typedef QList<CppClass> CppClass::*HierarchyMember; - void performFromExpression(const QString &expression, const Utils::FilePath &filePath); - QStandardItem *buildHierarchy(const CppClass &cppClass, QStandardItem *parent, - bool isRoot, HierarchyMember member); - void showNoTypeHierarchyLabel(); - void showTypeHierarchy(); - void showProgress(); - void hideProgress(); - void clearTypeHierarchy(); - void onItemActivated(const QModelIndex &index); - void onItemDoubleClicked(const QModelIndex &index); - - CppEditorWidget *m_cppEditor = nullptr; - Utils::NavigationTreeView *m_treeView = nullptr; - QWidget *m_hierarchyWidget = nullptr; - QStackedLayout *m_stackLayout = nullptr; - QStandardItemModel *m_model = nullptr; - Utils::AnnotatedItemDelegate *m_delegate = nullptr; - TextEditor::TextEditorLinkLabel *m_inspectedClass = nullptr; - QLabel *m_infoLabel = nullptr; - QFuture<QSharedPointer<CppElement>> m_future; - QFutureWatcher<void> m_futureWatcher; - Utils::FutureSynchronizer m_synchronizer; - Utils::ProgressIndicator *m_progressIndicator = nullptr; - QString m_oldClass; - bool m_showOldClass = false; -}; +namespace CppEditor::Internal { class CppTypeHierarchyFactory : public Core::INavigationWidgetFactory { - Q_OBJECT - public: CppTypeHierarchyFactory(); Core::NavigationView createWidget() override; }; -} // namespace Internal -} // namespace CppEditor +} // CppEditor::Internal diff --git a/src/plugins/cppeditor/cppuseselections_test.cpp b/src/plugins/cppeditor/cppuseselections_test.cpp index c9c56752844..2e5a0f2a0d8 100644 --- a/src/plugins/cppeditor/cppuseselections_test.cpp +++ b/src/plugins/cppeditor/cppuseselections_test.cpp @@ -81,7 +81,7 @@ UseSelectionsTestCase::UseSelectionsTestCase(CppTestDocument &testFile, bool hasTimedOut; const SelectionList selections = waitForUseSelections(&hasTimedOut); - const bool clangCodeModel = CppModelManager::instance()->isClangCodeModelActive(); + const bool clangCodeModel = CppModelManager::isClangCodeModelActive(); if (clangCodeModel) { QEXPECT_FAIL("local use as macro argument - argument eaten", "fails with CCM, find out why", Abort); diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index 7d7bfe98006..e03475ea80d 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -52,7 +52,8 @@ CppUseSelectionsUpdater::RunnerInfo CppUseSelectionsUpdater::update(CallType cal auto *cppEditorDocument = qobject_cast<CppEditorDocument *>(cppEditorWidget->textDocument()); QTC_ASSERT(cppEditorDocument, return RunnerInfo::FailedToStart); - m_updateSelections = !CppModelManager::usesClangd(cppEditorDocument); + m_updateSelections = !CppModelManager::usesClangd(cppEditorDocument) + && !m_editorWidget->isRenaming(); CursorInfoParams params; params.semanticInfo = cppEditorWidget->semanticInfo(); @@ -138,10 +139,6 @@ void CppUseSelectionsUpdater::onFindUsesFinished() emit finished(SemanticInfo::LocalUseMap(), false); return; } - if (m_editorWidget->isRenaming()) { - emit finished({}, false); - return; - } processResults(m_runnerWatcher->result()); diff --git a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp index 19fc0890257..34aa3d6fd07 100644 --- a/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp +++ b/src/plugins/cppeditor/cppvirtualfunctionassistprovider.cpp @@ -94,7 +94,7 @@ public: QTC_ASSERT(m_params.function, return nullptr); auto *hintItem = new VirtualFunctionProposalItem(Utils::Link()); - hintItem->setText(Tr::tr("collecting overrides ...")); + hintItem->setText(Tr::tr("collecting overrides...")); hintItem->setOrder(-1000); QList<AssistProposalItemInterface *> items; diff --git a/src/plugins/cppeditor/doxygengenerator.cpp b/src/plugins/cppeditor/doxygengenerator.cpp index 5a57fdc3a3f..c77f3de7769 100644 --- a/src/plugins/cppeditor/doxygengenerator.cpp +++ b/src/plugins/cppeditor/doxygengenerator.cpp @@ -24,26 +24,6 @@ namespace CppEditor::Internal { DoxygenGenerator::DoxygenGenerator() = default; -void DoxygenGenerator::setStyle(DocumentationStyle style) -{ - m_style = style; -} - -void DoxygenGenerator::setStartComment(bool start) -{ - m_startComment = start; -} - -void DoxygenGenerator::setGenerateBrief(bool get) -{ - m_generateBrief = get; -} - -void DoxygenGenerator::setAddLeadingAsterisks(bool add) -{ - m_addLeadingAsterisks = add; -} - QString DoxygenGenerator::generate(QTextCursor cursor, const CPlusPlus::Snapshot &snapshot, const Utils::FilePath &documentFilePath) @@ -136,8 +116,6 @@ QString DoxygenGenerator::generate(QTextCursor cursor, DeclarationAST *decl) assignCommentOffset(cursor); QString comment; - if (m_startComment) - writeStart(&comment); writeNewLine(&comment); writeContinuation(&comment); @@ -146,7 +124,7 @@ QString DoxygenGenerator::generate(QTextCursor cursor, DeclarationAST *decl) && decltr->core_declarator->asDeclaratorId() && decltr->core_declarator->asDeclaratorId()->name) { CoreDeclaratorAST *coreDecl = decltr->core_declarator; - if (m_generateBrief) + if (m_settings.generateBrief) writeBrief(&comment, m_printer.prettyName(coreDecl->asDeclaratorId()->name->name)); else writeNewLine(&comment); @@ -184,7 +162,7 @@ QString DoxygenGenerator::generate(QTextCursor cursor, DeclarationAST *decl) writeCommand(&comment, ReturnCommand); } } - } else if (spec && m_generateBrief) { + } else if (spec && m_settings.generateBrief) { bool briefWritten = false; if (ClassSpecifierAST *classSpec = spec->asClassSpecifier()) { if (classSpec->name) { @@ -221,15 +199,14 @@ QString DoxygenGenerator::generate(QTextCursor cursor, DeclarationAST *decl) return comment; } -QChar DoxygenGenerator::startMark() const -{ - if (m_style == QtStyle) - return QLatin1Char('!'); - return QLatin1Char('*'); -} - QChar DoxygenGenerator::styleMark() const { + switch (m_settings.commandPrefix) { + case TextEditor::CommentsSettings::CommandPrefix::At: return '@'; + case TextEditor::CommentsSettings::CommandPrefix::Backslash: return '\\'; + case TextEditor::CommentsSettings::CommandPrefix::Auto: break; + } + if (m_style == QtStyle || m_style == CppStyleA || m_style == CppStyleB) return QLatin1Char('\\'); return QLatin1Char('@'); @@ -246,16 +223,6 @@ QString DoxygenGenerator::commandSpelling(Command command) return QLatin1String("brief "); } -void DoxygenGenerator::writeStart(QString *comment) const -{ - if (m_style == CppStyleA) - comment->append(QLatin1String("///")); - if (m_style == CppStyleB) - comment->append(QLatin1String("//!")); - else - comment->append(offsetString() + "/*" + startMark()); -} - void DoxygenGenerator::writeEnd(QString *comment) const { if (m_style == CppStyleA) @@ -272,7 +239,7 @@ void DoxygenGenerator::writeContinuation(QString *comment) const comment->append(offsetString() + "///"); else if (m_style == CppStyleB) comment->append(offsetString() + "//!"); - else if (m_addLeadingAsterisks) + else if (m_settings.leadingAsterisks) comment->append(offsetString() + " *"); else comment->append(offsetString() + " "); diff --git a/src/plugins/cppeditor/doxygengenerator.h b/src/plugins/cppeditor/doxygengenerator.h index 3b80f83c73c..4bb11f72892 100644 --- a/src/plugins/cppeditor/doxygengenerator.h +++ b/src/plugins/cppeditor/doxygengenerator.h @@ -3,6 +3,8 @@ #pragma once +#include <texteditor/commentssettings.h> + #include <cplusplus/Overview.h> QT_FORWARD_DECLARE_CLASS(QTextCursor) @@ -25,18 +27,15 @@ public: CppStyleB ///< CppStyle comment variant B: //! }; - void setStyle(DocumentationStyle style); - void setStartComment(bool start); - void setGenerateBrief(bool gen); - void setAddLeadingAsterisks(bool add); + void setStyle(DocumentationStyle style) { m_style = style; } + void setSettings(const TextEditor::CommentsSettings::Data &settings) { m_settings = settings; } QString generate(QTextCursor cursor, const CPlusPlus::Snapshot &snapshot, const Utils::FilePath &documentFilePath); - QString generate(QTextCursor cursor, CPlusPlus::DeclarationAST *decl); private: - QChar startMark() const; + QString generate(QTextCursor cursor, CPlusPlus::DeclarationAST *decl); QChar styleMark() const; enum Command { @@ -46,7 +45,6 @@ private: }; static QString commandSpelling(Command command); - void writeStart(QString *comment) const; void writeEnd(QString *comment) const; void writeContinuation(QString *comment) const; void writeNewLine(QString *comment) const; @@ -61,12 +59,10 @@ private: void assignCommentOffset(QTextCursor cursor); QString offsetString() const; - bool m_addLeadingAsterisks = true; - bool m_generateBrief = true; - bool m_startComment = true; - DocumentationStyle m_style = QtStyle; + TextEditor::CommentsSettings::Data m_settings; CPlusPlus::Overview m_printer; QString m_commentOffset; + DocumentationStyle m_style = QtStyle; }; } // namespace CppEditor::Internal diff --git a/src/plugins/cppeditor/fileandtokenactions_test.cpp b/src/plugins/cppeditor/fileandtokenactions_test.cpp index 2ad6b6b4ef2..75dd0719a5c 100644 --- a/src/plugins/cppeditor/fileandtokenactions_test.cpp +++ b/src/plugins/cppeditor/fileandtokenactions_test.cpp @@ -110,7 +110,7 @@ static bool waitUntilAProjectIsLoaded(int timeOutInMs = 30000) timer.start(); while (timer.elapsed() < timeOutInMs) { - if (!CppModelManager::instance()->projectInfos().isEmpty()) + if (!CppModelManager::projectInfos().isEmpty()) return true; QCoreApplication::processEvents(); @@ -132,7 +132,7 @@ TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Acti // Collect files to process FilePaths filesToOpen; QList<QPointer<ProjectExplorer::Project> > projects; - const QList<ProjectInfo::ConstPtr> projectInfos = m_modelManager->projectInfos(); + const QList<ProjectInfo::ConstPtr> projectInfos = CppModelManager::projectInfos(); for (const ProjectInfo::ConstPtr &info : projectInfos) { qDebug() << "Project" << info->projectFilePath().toUserOutput() << "- files to process:" @@ -162,8 +162,8 @@ TestActionsTestCase::TestActionsTestCase(const Actions &tokenActions, const Acti QVERIFY(openCppEditor(filePath, &editor, &editorWidget)); QCOMPARE(DocumentModel::openedDocuments().size(), 1); - QVERIFY(m_modelManager->isCppEditor(editor)); - QVERIFY(m_modelManager->workingCopy().get(filePath)); + QVERIFY(CppModelManager::isCppEditor(editor)); + QVERIFY(CppModelManager::workingCopy().get(filePath)); // Rehighlight waitForRehighlightedSemanticDocument(editorWidget); diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 31d69f7363d..21664ea1402 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -174,7 +174,7 @@ private: QList<TestDocumentPtr> singleDocument(const QByteArray &source) { - return QList<TestDocumentPtr>() << CppTestDocument::create(source, "file.cpp"); + return {CppTestDocument::create(source, "file.cpp")}; } /** @@ -331,7 +331,7 @@ F2TestCase::F2TestCase(CppEditorAction action, switch (action) { case FollowSymbolUnderCursorAction: { CppEditorWidget *widget = initialTestFile->m_editorWidget; - if (CppModelManager::instance()->isClangCodeModelActive()) { + if (CppModelManager::isClangCodeModelActive()) { if (curTestName == "testFollowSymbolQTCREATORBUG7903") QSKIP((curTestName + " is not supported by Clang FollowSymbol").toLatin1()); widget->enableTestMode(); @@ -381,7 +381,7 @@ F2TestCase::F2TestCase(CppEditorAction action, QObject::connect(&t, &QTimer::timeout, &l, &QEventLoop::quit); const IAssistProposal *immediateProposal = nullptr; const IAssistProposal *finalProposal = nullptr; - QObject::connect(initialTestFile->m_editorWidget, &CppEditorWidget::proposalsReady, + QObject::connect(initialTestFile->m_editorWidget, &CppEditorWidget::proposalsReady, &l, [&](const IAssistProposal *i, const IAssistProposal *f) { immediateProposal = i; finalProposal = f; @@ -449,7 +449,7 @@ F2TestCase::F2TestCase(CppEditorAction action, first.text = "<base declaration>"; expectedImmediate << first; } - expectedImmediate << OverrideItem(QLatin1String("collecting overrides ...")); + expectedImmediate << OverrideItem(QLatin1String("collecting overrides...")); } QCOMPARE(immediateVirtualSymbolResults, expectedImmediate); if (useClangd) { diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.cpp b/src/plugins/cppeditor/generatedcodemodelsupport.cpp index 22a9b2735eb..63c9de66b91 100644 --- a/src/plugins/cppeditor/generatedcodemodelsupport.cpp +++ b/src/plugins/cppeditor/generatedcodemodelsupport.cpp @@ -49,10 +49,10 @@ private: QSet<QObject *> m_cache; }; -GeneratedCodeModelSupport::GeneratedCodeModelSupport(CppModelManager *modelmanager, - ExtraCompiler *generator, +GeneratedCodeModelSupport::GeneratedCodeModelSupport(ExtraCompiler *generator, const FilePath &generatedFile) : - AbstractEditorSupport(modelmanager, generator), m_generatedFilePath(generatedFile), + AbstractEditorSupport(generator), + m_generatedFilePath(generatedFile), m_generator(generator) { QLoggingCategory log("qtc.cppeditor.generatedcodemodelsupport", QtWarningMsg); @@ -66,8 +66,7 @@ GeneratedCodeModelSupport::GeneratedCodeModelSupport(CppModelManager *modelmanag GeneratedCodeModelSupport::~GeneratedCodeModelSupport() { - CppModelManager::instance()->emitAbstractEditorSupportRemoved( - m_generatedFilePath.toString()); + CppModelManager::emitAbstractEditorSupportRemoved(m_generatedFilePath.toString()); QLoggingCategory log("qtc.cppeditor.generatedcodemodelsupport", QtWarningMsg); qCDebug(log) << "dtor ~generatedcodemodelsupport for" << m_generatedFilePath; } @@ -99,15 +98,13 @@ void GeneratedCodeModelSupport::update(const QList<ExtraCompiler *> &generators) { static QObjectCache extraCompilerCache; - CppModelManager * const mm = CppModelManager::instance(); - for (ExtraCompiler *generator : generators) { if (extraCompilerCache.contains(generator)) continue; extraCompilerCache.insert(generator); - generator->forEachTarget([mm, generator](const FilePath &generatedFile) { - new GeneratedCodeModelSupport(mm, generator, generatedFile); + generator->forEachTarget([generator](const FilePath &generatedFile) { + new GeneratedCodeModelSupport(generator, generatedFile); }); } } diff --git a/src/plugins/cppeditor/generatedcodemodelsupport.h b/src/plugins/cppeditor/generatedcodemodelsupport.h index d37177d9910..618a837e8b2 100644 --- a/src/plugins/cppeditor/generatedcodemodelsupport.h +++ b/src/plugins/cppeditor/generatedcodemodelsupport.h @@ -15,8 +15,7 @@ class CPPEDITOR_EXPORT GeneratedCodeModelSupport : public AbstractEditorSupport Q_OBJECT public: - GeneratedCodeModelSupport(CppModelManager *modelmanager, - ProjectExplorer::ExtraCompiler *generator, + GeneratedCodeModelSupport(ProjectExplorer::ExtraCompiler *generator, const Utils::FilePath &generatedFile); ~GeneratedCodeModelSupport() override; diff --git a/src/plugins/cppeditor/includeutils.cpp b/src/plugins/cppeditor/includeutils.cpp index 1404f5bace3..c7daa4e9d8f 100644 --- a/src/plugins/cppeditor/includeutils.cpp +++ b/src/plugins/cppeditor/includeutils.cpp @@ -511,14 +511,13 @@ namespace Internal { static QList<Include> includesForSource(const FilePath &filePath) { - CppModelManager *cmm = CppModelManager::instance(); - cmm->GC(); + CppModelManager::GC(); QScopedPointer<CppSourceProcessor> sourceProcessor(CppModelManager::createSourceProcessor()); sourceProcessor->setHeaderPaths({ProjectExplorer::HeaderPath::makeUser( TestIncludePaths::globalIncludePath())}); sourceProcessor->run(filePath); - Document::Ptr document = cmm->document(filePath); + Document::Ptr document = CppModelManager::document(filePath); return document->resolvedIncludes(); } diff --git a/src/plugins/cppeditor/insertionpointlocator.cpp b/src/plugins/cppeditor/insertionpointlocator.cpp index ece52aa559a..3eb1d4aebe3 100644 --- a/src/plugins/cppeditor/insertionpointlocator.cpp +++ b/src/plugins/cppeditor/insertionpointlocator.cpp @@ -292,7 +292,7 @@ InsertionLocation InsertionPointLocator::methodDeclarationInClass(const Translat int line = 0, column = 0; if (pos == InsertionPointLocator::AccessSpecEnd) - tu->getTokenStartPosition(beforeToken, &line, &column); + tu->getTokenPosition(beforeToken, &line, &column); else tu->getTokenEndPosition(beforeToken, &line, &column); @@ -436,7 +436,7 @@ public: } if (lastToken == _bestToken.get()) // No matching namespace found - translationUnit()->getTokenStartPosition(lastToken, line, column); + translationUnit()->getTokenPosition(lastToken, line, column); else // Insert at end of matching namespace translationUnit()->getTokenEndPosition(_bestToken.get(), line, column); } @@ -497,7 +497,7 @@ protected: if (_result) return false; int line, column; - translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column); + translationUnit()->getTokenPosition(ast->firstToken(), &line, &column); if (line > _line || (line == _line && column > _column)) return false; translationUnit()->getTokenEndPosition(ast->lastToken() - 1, &line, &column); @@ -608,12 +608,12 @@ static InsertionLocation nextToSurroundingDefinitions(Symbol *declaration, if (!functionDefinition) return noResult; - targetFile->cppDocument()->translationUnit()->getTokenStartPosition(functionDefinition->firstToken(), &line, &column); + targetFile->cppDocument()->translationUnit()->getTokenPosition(functionDefinition->firstToken(), &line, &column); const QList<AST *> path = ASTPath(targetFile->cppDocument())(line, column); for (auto it = path.rbegin(); it != path.rend(); ++it) { if (const auto templateDecl = (*it)->asTemplateDeclaration()) { if (templateDecl->declaration == functionDefinition) { - targetFile->cppDocument()->translationUnit()->getTokenStartPosition( + targetFile->cppDocument()->translationUnit()->getTokenPosition( templateDecl->firstToken(), &line, &column); } break; @@ -779,7 +779,7 @@ InsertionLocation insertLocationForMethodDefinition(Symbol *symbol, int lastLine; if (hasIncludeGuard) { const TranslationUnit * const tu = file->cppDocument()->translationUnit(); - tu->getTokenStartPosition(tu->ast()->lastToken(), &lastLine); + tu->getTokenPosition(tu->ast()->lastToken(), &lastLine); } int i = 0; for ( ; i < list.count(); ++i) { diff --git a/src/plugins/cppeditor/modelmanagertesthelper.cpp b/src/plugins/cppeditor/modelmanagertesthelper.cpp index a133ac7fef2..bfc3998e8ee 100644 --- a/src/plugins/cppeditor/modelmanagertesthelper.cpp +++ b/src/plugins/cppeditor/modelmanagertesthelper.cpp @@ -81,7 +81,7 @@ QSet<FilePath> ModelManagerTestHelper::updateProjectInfo( const ProjectInfo::ConstPtr &projectInfo) { resetRefreshedSourceFiles(); - CppModelManager::instance()->updateProjectInfo(projectInfo).waitForFinished(); + CppModelManager::updateProjectInfo(projectInfo).waitForFinished(); QCoreApplication::processEvents(); return waitForRefreshedSourceFiles(); } diff --git a/src/plugins/cppeditor/projectinfo.cpp b/src/plugins/cppeditor/projectinfo.cpp index 3e1f5154e12..d7b0b58a7a1 100644 --- a/src/plugins/cppeditor/projectinfo.cpp +++ b/src/plugins/cppeditor/projectinfo.cpp @@ -4,7 +4,7 @@ #include "projectinfo.h" #include <projectexplorer/abi.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/rawprojectpart.h> #include <projectexplorer/toolchain.h> diff --git a/src/plugins/cppeditor/searchsymbols.cpp b/src/plugins/cppeditor/searchsymbols.cpp index 2aea7f8e1bf..ef2a4ed05fa 100644 --- a/src/plugins/cppeditor/searchsymbols.cpp +++ b/src/plugins/cppeditor/searchsymbols.cpp @@ -315,8 +315,17 @@ void SearchSymbols::processFunction(T *func) if (!(symbolsToSearchFor & SymbolSearcher::Functions) || !func->name()) return; QString name = overview.prettyName(func->name()); + QString scope = _scope; + const int scopeSep = name.lastIndexOf("::"); + if (scopeSep != -1) { + if (!scope.isEmpty()) + scope.append("::"); + scope.append(name.left(scopeSep)); + name.remove(0, scopeSep + 2); + } QString type = overview.prettyType(func->type()); - addChildItem(name, type, _scope, IndexItem::Function, func); + + addChildItem(name, type, scope, IndexItem::Function, func); } } // namespace CppEditor diff --git a/src/plugins/cppeditor/symbolfinder.cpp b/src/plugins/cppeditor/symbolfinder.cpp index 5da95a13621..dfc36a0e8aa 100644 --- a/src/plugins/cppeditor/symbolfinder.cpp +++ b/src/plugins/cppeditor/symbolfinder.cpp @@ -476,7 +476,7 @@ void SymbolFinder::checkCacheConsistency(const FilePath &referenceFile, const Sn const QString projectPartIdForFile(const FilePath &filePath) { - const QList<ProjectPart::ConstPtr> parts = CppModelManager::instance()->projectPart(filePath); + const QList<ProjectPart::ConstPtr> parts = CppModelManager::projectPart(filePath); if (!parts.isEmpty()) return parts.first()->id(); return QString(); diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp index eb6a4c81dab..bd05107c646 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.cpp +++ b/src/plugins/cppeditor/symbolsfindfilter.cpp @@ -7,11 +7,12 @@ #include "cppeditortr.h" #include "cppmodelmanager.h" +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/find/searchresultwindow.h> +#include <coreplugin/find/textfindconstants.h> #include <coreplugin/icore.h> #include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/progressmanager.h> -#include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/find/searchresultwindow.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -35,9 +36,8 @@ const char SETTINGS_GROUP[] = "CppSymbols"; const char SETTINGS_SYMBOLTYPES[] = "SymbolsToSearchFor"; const char SETTINGS_SEARCHSCOPE[] = "SearchScope"; -SymbolsFindFilter::SymbolsFindFilter(CppModelManager *manager) - : m_manager(manager), - m_enabled(true), +SymbolsFindFilter::SymbolsFindFilter() + : m_enabled(true), m_symbolsToSearch(SearchSymbols::AllTypes), m_scope(SymbolSearcher::SearchProjectsOnly) { @@ -121,7 +121,7 @@ void SymbolsFindFilter::startSearch(SearchResult *search) SymbolSearcher *symbolSearcher = new SymbolSearcher(parameters, projectFileNames); connect(watcher, &QFutureWatcherBase::finished, symbolSearcher, &QObject::deleteLater); - watcher->setFuture(Utils::asyncRun(m_manager->sharedThreadPool(), + watcher->setFuture(Utils::asyncRun(CppModelManager::sharedThreadPool(), &SymbolSearcher::runSearch, symbolSearcher)); FutureProgress *progress = ProgressManager::addTask(watcher->future(), Tr::tr("Searching for Symbol"), Core::Constants::TASK_SEARCH); @@ -139,7 +139,7 @@ void SymbolsFindFilter::addResults(QFutureWatcher<SearchResultItem> *watcher, in SearchResultItems items; for (int i = begin; i < end; ++i) items << watcher->resultAt(i); - search->addResults(items, SearchResult::AddSorted); + search->addResults(items, SearchResult::AddSortedByContent); } void SymbolsFindFilter::finish(QFutureWatcher<SearchResultItem> *watcher) @@ -166,22 +166,21 @@ QWidget *SymbolsFindFilter::createConfigWidget() return new SymbolsFindFilterConfigWidget(this); } -void SymbolsFindFilter::writeSettings(QSettings *settings) +void SymbolsFindFilter::writeSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String(SETTINGS_GROUP)); - settings->setValue(QLatin1String(SETTINGS_SYMBOLTYPES), int(m_symbolsToSearch)); - settings->setValue(QLatin1String(SETTINGS_SEARCHSCOPE), int(m_scope)); + settings->beginGroup(SETTINGS_GROUP); + settings->setValue(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch)); + settings->setValue(SETTINGS_SEARCHSCOPE, int(m_scope)); settings->endGroup(); } -void SymbolsFindFilter::readSettings(QSettings *settings) +void SymbolsFindFilter::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String(SETTINGS_GROUP)); + settings->beginGroup(SETTINGS_GROUP); m_symbolsToSearch = static_cast<SearchSymbols::SymbolTypes>( - settings->value(QLatin1String(SETTINGS_SYMBOLTYPES), - int(SearchSymbols::AllTypes)).toInt()); + settings->value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt()); m_scope = static_cast<SearchScope>( - settings->value(QLatin1String(SETTINGS_SEARCHSCOPE), + settings->value(SETTINGS_SEARCHSCOPE, int(SymbolSearcher::SearchProjectsOnly)).toInt()); settings->endGroup(); emit symbolsToSearchChanged(); diff --git a/src/plugins/cppeditor/symbolsfindfilter.h b/src/plugins/cppeditor/symbolsfindfilter.h index a77f5873606..0cdfee87c1c 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.h +++ b/src/plugins/cppeditor/symbolsfindfilter.h @@ -18,8 +18,6 @@ namespace Core { class SearchResult; } namespace Utils { class SearchResultItem; } namespace CppEditor { -class CppModelManager; - namespace Internal { class SymbolsFindFilter : public Core::IFindFilter @@ -30,17 +28,17 @@ public: using SearchScope = SymbolSearcher::SearchScope; public: - explicit SymbolsFindFilter(CppModelManager *manager); + SymbolsFindFilter(); QString id() const override; QString displayName() const override; bool isEnabled() const override; - void findAll(const QString &txt, Core::FindFlags findFlags) override; + void findAll(const QString &txt, Utils::FindFlags findFlags) override; QWidget *createConfigWidget() override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; void setSymbolsToSearch(const SearchSymbols::SymbolTypes &types) { m_symbolsToSearch = types; } SearchSymbols::SymbolTypes symbolsToSearch() const { return m_symbolsToSearch; } @@ -62,10 +60,9 @@ private: void onAllTasksFinished(Utils::Id type); QString label() const; - QString toolTip(Core::FindFlags findFlags) const; + QString toolTip(Utils::FindFlags findFlags) const; void startSearch(Core::SearchResult *search); - CppModelManager *m_manager; bool m_enabled; QMap<QFutureWatcher<Utils::SearchResultItem> *, QPointer<Core::SearchResult> > m_watchers; QPointer<Core::SearchResult> m_currentSearch; diff --git a/src/plugins/cppeditor/typehierarchybuilder.cpp b/src/plugins/cppeditor/typehierarchybuilder.cpp index 889d0eacfc0..a4266955054 100644 --- a/src/plugins/cppeditor/typehierarchybuilder.cpp +++ b/src/plugins/cppeditor/typehierarchybuilder.cpp @@ -5,6 +5,8 @@ #include <cplusplus/FindUsages.h> +#include <utils/algorithm.h> + using namespace CPlusPlus; using namespace Utils; @@ -170,11 +172,9 @@ void TypeHierarchyBuilder::buildDerived(const std::optional<QFuture<void>> &futu QHash<QString, QHash<QString, QString>> &cache) { Symbol *symbol = typeHierarchy->_symbol; - if (_visited.contains(symbol)) + if (!Utils::insert(_visited, symbol)) return; - _visited.insert(symbol); - const QString &symbolName = _overview.prettyName(LookupContext::fullyQualifiedName(symbol)); DerivedHierarchyVisitor visitor(symbolName, cache); diff --git a/src/plugins/ctfvisualizer/CtfVisualizer.json.in b/src/plugins/ctfvisualizer/CtfVisualizer.json.in index e581d227334..1b4b91a6d5a 100644 --- a/src/plugins/ctfvisualizer/CtfVisualizer.json.in +++ b/src/plugins/ctfvisualizer/CtfVisualizer.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"CtfVisualizer\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"KDAB Group, www.kdab.com\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CtfVisualizer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "KDAB Group, www.kdab.com", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"Chrome Trace Format Visualizer Plugin.\", - \"Url\" : \"https://www.kdab.com\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "Chrome Trace Format Visualizer Plugin.", + "Url" : "https://www.kdab.com", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp b/src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp index f9b16569034..6c29e787970 100644 --- a/src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp +++ b/src/plugins/ctfvisualizer/ctfstatisticsmodel.cpp @@ -2,21 +2,18 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "ctfstatisticsmodel.h" -#include "ctfvisualizerconstants.h" #include "ctfvisualizertr.h" +#include "json/json.hpp" #include <tracing/timelineformattime.h> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { using json = nlohmann::json; -using namespace Constants; CtfStatisticsModel::CtfStatisticsModel(QObject *parent) : QAbstractTableModel(parent) { - } void CtfStatisticsModel::beginLoading() @@ -59,7 +56,7 @@ int CtfStatisticsModel::columnCount(const QModelIndex &parent) const QVariant CtfStatisticsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) - return QVariant(); + return {}; auto it = m_data.cbegin(); std::advance(it, index.row()); @@ -79,7 +76,7 @@ QVariant CtfStatisticsModel::data(const QModelIndex &index, int role) const return Qt::AlignRight; default: Q_UNREACHABLE(); - return QVariant(); + return {}; } case SortRole: switch (index.column()) { @@ -106,7 +103,7 @@ QVariant CtfStatisticsModel::data(const QModelIndex &index, int role) const case Column::MaxDuration: return m_data.value(title).maxDuration; default: - return QVariant(); + return {}; } case Qt::DisplayRole: switch (index.column()) { @@ -158,7 +155,7 @@ QVariant CtfStatisticsModel::data(const QModelIndex &index, int role) const } } - return QVariant(); + return {}; } QVariant CtfStatisticsModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -186,5 +183,4 @@ QVariant CtfStatisticsModel::headerData(int section, Qt::Orientation orientation } } -} // Internal -} // CtfVisualizer +} // CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfstatisticsmodel.h b/src/plugins/ctfvisualizer/ctfstatisticsmodel.h index 49d7022ca1f..aaf0f65b8ff 100644 --- a/src/plugins/ctfvisualizer/ctfstatisticsmodel.h +++ b/src/plugins/ctfvisualizer/ctfstatisticsmodel.h @@ -3,16 +3,10 @@ #pragma once -#include "json/json.hpp" - -#include <QHash> -#include <QStack> -#include <QVector> -#include <QPointer> #include <QAbstractTableModel> +#include <QHash> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfStatisticsModel : public QAbstractTableModel { @@ -61,5 +55,4 @@ private: }; -} // Internal -} // CtfVisualizer +} // CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfstatisticsview.cpp b/src/plugins/ctfvisualizer/ctfstatisticsview.cpp index 3ca6a4738dd..a1bb52f7e7a 100644 --- a/src/plugins/ctfvisualizer/ctfstatisticsview.cpp +++ b/src/plugins/ctfvisualizer/ctfstatisticsview.cpp @@ -7,8 +7,7 @@ #include <QHeaderView> #include <QSortFilterProxyModel> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { CtfStatisticsView::CtfStatisticsView(CtfStatisticsModel *model, QWidget *parent) : Utils::TreeView(parent) @@ -28,11 +27,10 @@ CtfStatisticsView::CtfStatisticsView(CtfStatisticsModel *model, QWidget *parent) header()->setStretchLastSection(false); header()->setSectionResizeMode(CtfStatisticsModel::Column::Title, QHeaderView::Stretch); setRootIsDecorated(false); - setUniformRowHeights(true); setSortingEnabled(true); - connect(selectionModel(), &QItemSelectionModel::currentChanged, - [this] (const QModelIndex ¤t, const QModelIndex &previous) + connect(selectionModel(), &QItemSelectionModel::currentChanged, this, + [this](const QModelIndex ¤t, const QModelIndex &previous) { Q_UNUSED(previous); QModelIndex index = this->model()->index(current.row(), CtfStatisticsModel::Title); @@ -57,5 +55,4 @@ void CtfStatisticsView::selectByTitle(const QString &title) } } -} // Internal -} // CtfVisualizer +} // CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfstatisticsview.h b/src/plugins/ctfvisualizer/ctfstatisticsview.h index 5c3e5f85a25..1a178180bcb 100644 --- a/src/plugins/ctfvisualizer/ctfstatisticsview.h +++ b/src/plugins/ctfvisualizer/ctfstatisticsview.h @@ -3,12 +3,9 @@ #pragma once -#include "ctfvisualizerconstants.h" - #include <utils/itemviews.h> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfStatisticsModel; @@ -25,5 +22,4 @@ signals: void eventTypeSelected(const QString &title); }; -} // Internal -} // CtfVisualizer +} // CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctftimelinemodel.cpp b/src/plugins/ctfvisualizer/ctftimelinemodel.cpp index 8360c2e6f58..01288772760 100644 --- a/src/plugins/ctfvisualizer/ctftimelinemodel.cpp +++ b/src/plugins/ctfvisualizer/ctftimelinemodel.cpp @@ -8,30 +8,28 @@ #include "ctfvisualizertr.h" #include <tracing/timelineformattime.h> -#include <tracing/timelinemodelaggregator.h> -#include <utils/qtcassert.h> -#include <QDebug> +#include <utils/qtcassert.h> #include <string> - -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { using json = nlohmann::json; using namespace Constants; CtfTimelineModel::CtfTimelineModel(Timeline::TimelineModelAggregator *parent, - CtfTraceManager *traceManager, int tid, int pid) - : Timeline::TimelineModel (parent) + CtfTraceManager *traceManager, + const QString &tid, + const QString &pid) + : Timeline::TimelineModel(parent) , m_traceManager(traceManager) , m_threadId(tid) , m_processId(pid) { updateName(); setCollapsedRowCount(1); - setCategoryColor(colorByHue(pid * 25)); + setCategoryColor(colorByHue(qHash(pid))); setHasMixedTypesInExpandedState(true); } @@ -44,7 +42,7 @@ QVariantList CtfTimelineModel::labels() const { QVariantList result; - QVector<std::string> sortedCounterNames = m_counterNames; + QList<std::string> sortedCounterNames = m_counterNames; std::sort(sortedCounterNames.begin(), sortedCounterNames.end()); for (int i = 0; i < sortedCounterNames.size(); ++i) { QVariantMap element; @@ -103,7 +101,7 @@ int CtfTimelineModel::expandedRow(int index) const if (counterIdx > 0) { return m_counterIndexToRow[counterIdx - 1] + 1; } - return m_nestingLevels.value(index) + m_counterData.size() + 1; + return m_rows.value(index) + m_counterData.size() + 1; } int CtfTimelineModel::collapsedRow(int index) const @@ -185,8 +183,10 @@ void CtfTimelineModel::finalize(double traceBegin, double traceEnd, const QStrin m_details[index].insert(6, {reuse(Tr::tr("Unfinished")), reuse(Tr::tr("true"))}); } computeNesting(); + m_rows = computeRows(&m_maxStackSize); + ++m_maxStackSize; // index -> count - QVector<std::string> sortedCounterNames = m_counterNames; + QList<std::string> sortedCounterNames = m_counterNames; std::sort(sortedCounterNames.begin(), sortedCounterNames.end()); m_counterIndexToRow.resize(m_counterNames.size()); for (int i = 0; i < m_counterIndexToRow.size(); ++i) { @@ -199,7 +199,7 @@ void CtfTimelineModel::finalize(double traceBegin, double traceEnd, const QStrin emit contentChanged(); } -int CtfTimelineModel::tid() const +QString CtfTimelineModel::tid() const { return m_threadId; } @@ -218,21 +218,19 @@ void CtfTimelineModel::updateName() if (m_threadName.isEmpty()) { setDisplayName(Tr::tr("Thread %1").arg(m_threadId)); } else { - setDisplayName(QString("%1 (%2)").arg(m_threadName).arg(m_threadId)); + setDisplayName(QString("%1 (%2)").arg(m_threadName, m_threadId)); } - QString process = m_processName.isEmpty() ? QString::number(m_processId) : - QString("%1 (%2)").arg(m_processName).arg(m_processId); - QString thread = m_threadName.isEmpty() ? QString::number(m_threadId) : - QString("%1 (%2)").arg(m_threadName).arg(m_threadId); - setTooltip(QString("Process: %1\nThread: %2").arg(process).arg(thread)); + QString process = m_processName.isEmpty() ? m_processId + : QString("%1 (%2)").arg(m_processName, m_processId); + QString thread = m_threadName.isEmpty() ? m_threadId + : QString("%1 (%2)").arg(m_threadName, m_threadId); + setTooltip(QString("Process: %1\nThread: %2").arg(process, thread)); } qint64 CtfTimelineModel::newStackEvent(const json &event, qint64 normalizedTime, const std::string &eventPhase, const std::string &name, int selectionId) { - int nestingLevel = m_openEventIds.size(); - m_maxStackSize = std::max(qsizetype(m_maxStackSize), qsizetype(m_openEventIds.size() + 1)); int index = 0; qint64 duration = -1; if (eventPhase == CtfEventTypeBegin) { @@ -248,29 +246,21 @@ qint64 CtfTimelineModel::newStackEvent(const json &event, qint64 normalizedTime, duration = qint64(event[CtfDurationKey]) * 1000; index = insert(normalizedTime, duration, selectionId); for (int i = m_openEventIds.size() - 1; i >= 0; --i) { - if (m_openEventIds[i] >= index) { + if (m_openEventIds[i] >= index) ++m_openEventIds[i]; - // if the event is before an open event, the nesting level decreases: - --nestingLevel; - } } } else { index = insert(normalizedTime, 0, selectionId); for (int i = m_openEventIds.size() - 1; i >= 0; --i) { - if (m_openEventIds[i] >= index) { + if (m_openEventIds[i] >= index) ++m_openEventIds[i]; - --nestingLevel; - } } } if (index >= m_details.size()) { m_details.resize(index + 1); m_details[index] = QMap<int, QPair<QString, QString>>(); - m_nestingLevels.resize(index + 1); - m_nestingLevels[index] = nestingLevel; } else { m_details.insert(index, QMap<int, QPair<QString, QString>>()); - m_nestingLevels.insert(index, nestingLevel); } if (m_counterValues.size() > index) { // if the event was inserted before any counter, we need @@ -382,7 +372,5 @@ const QString &CtfTimelineModel::reuse(const QString &value) return *it; } - -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctftimelinemodel.h b/src/plugins/ctfvisualizer/ctftimelinemodel.h index 3df35f17894..00d8b80bacc 100644 --- a/src/plugins/ctfvisualizer/ctftimelinemodel.h +++ b/src/plugins/ctfvisualizer/ctftimelinemodel.h @@ -6,17 +6,14 @@ #include <tracing/timelinemodel.h> +#include <QList> #include <QMap> #include <QSet> #include <QStack> -#include <QVector> -namespace Timeline { -class TimelineModelAggregator; -} +namespace Timeline { class TimelineModelAggregator; } -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfTraceManager; @@ -28,7 +25,9 @@ class CtfTimelineModel : public Timeline::TimelineModel public: explicit CtfTimelineModel(Timeline::TimelineModelAggregator *parent, - CtfTraceManager *traceManager, int tid, int pid); + CtfTraceManager *traceManager, + const QString &tid, + const QString &pid); QRgb color(int index) const override; QVariantList labels() const override; @@ -44,7 +43,7 @@ public: void finalize(double traceBegin, double traceEnd, const QString &processName, const QString &threadName); - int tid() const; + QString tid() const; QString eventTitle(int index) const; signals: @@ -65,14 +64,14 @@ private: protected: CtfTraceManager *const m_traceManager; - int m_threadId; + QString m_threadId; QString m_threadName; - int m_processId; + QString m_processId; QString m_processName; int m_maxStackSize = 0; - QVector<int> m_nestingLevels; - QVector<QMap<int, QPair<QString, QString>>> m_details; + QList<int> m_rows; + QList<QMap<int, QPair<QString, QString>>> m_details; QSet<int> m_handledTypeIds; QStack<int> m_openEventIds; QSet<QString> m_reusableStrings; @@ -84,12 +83,11 @@ protected: float max = std::numeric_limits<float>::min(); }; - QVector<std::string> m_counterNames; - QVector<CounterData> m_counterData; - QVector<float> m_counterValues; - QVector<int> m_itemToCounterIdx; - QVector<int> m_counterIndexToRow; + QList<std::string> m_counterNames; + QList<CounterData> m_counterData; + QList<float> m_counterValues; + QList<int> m_itemToCounterIdx; + QList<int> m_counterIndexToRow; }; -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctftracemanager.cpp b/src/plugins/ctfvisualizer/ctftracemanager.cpp index a2faa108031..5a83a2f6c17 100644 --- a/src/plugins/ctfvisualizer/ctftracemanager.cpp +++ b/src/plugins/ctfvisualizer/ctftracemanager.cpp @@ -8,66 +8,16 @@ #include "ctfvisualizertr.h" #include <coreplugin/icore.h> + #include <tracing/timelinemodelaggregator.h> -#include <QByteArray> -#include <QDebug> -#include <QFile> -#include <QList> #include <QMessageBox> -#include <fstream> - - -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { using json = nlohmann::json; - using namespace Constants; - -class CtfJsonParserCallback -{ - -public: - - explicit CtfJsonParserCallback(CtfTraceManager *traceManager) - : m_traceManager(traceManager) - {} - - bool callback(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed) - { - if ((event == json::parse_event_t::array_start && depth == 0) - || (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) { - m_isInTraceArray = true; - m_traceArrayDepth = depth; - return true; - } - if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) { - m_isInTraceArray = false; - return false; - } - if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) { - m_traceManager->addEvent(parsed); - return false; - } - if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) { - // keep outer object and values in trace objects: - return true; - } - // discard any objects outside of trace array: - // TODO: parse other data, e.g. stack frames - return false; - } - -protected: - CtfTraceManager *m_traceManager; - - bool m_isInTraceArray = false; - int m_traceArrayDepth = 0; -}; - CtfTraceManager::CtfTraceManager(QObject *parent, Timeline::TimelineModelAggregator *modelAggregator, CtfStatisticsModel *statisticsModel) @@ -75,7 +25,6 @@ CtfTraceManager::CtfTraceManager(QObject *parent, , m_modelAggregator(modelAggregator) , m_statisticsModel(statisticsModel) { - } qint64 CtfTraceManager::traceDuration() const @@ -95,60 +44,57 @@ qint64 CtfTraceManager::traceEnd() const void CtfTraceManager::addEvent(const json &event) { - const double timestamp = event.value(CtfTracingClockTimestampKey, -1.0); - if (timestamp < 0) { - // events without or with negative timestamp will be ignored - return; - } - if (m_timeOffset < 0) { - // the timestamp of the first event is used as the global offset - m_timeOffset = timestamp; - } - - const int processId = event.value(CtfProcessIdKey, 0); - const int threadId = event.contains(CtfThreadIdKey) ? int(event[CtfThreadIdKey]) : processId; - if (!m_threadModels.contains(threadId)) { - addModelForThread(threadId, processId); - } - if (event.value(CtfEventPhaseKey, "") == CtfEventTypeMetadata) { - const std::string name = event[CtfEventNameKey]; - if (name == "thread_name") { - m_threadNames[threadId] = QString::fromStdString(event["args"]["name"]); - } else if (name == "process_name") { - m_processNames[processId] = QString::fromStdString(event["args"]["name"]); + try { + const double timestamp = event.value(CtfTracingClockTimestampKey, -1.0); + if (timestamp < 0) { + // events without or with negative timestamp will be ignored + return; + } + if (m_timeOffset < 0) { + // the timestamp of the first event is used as the global offset + m_timeOffset = timestamp; } - } - const QPair<bool, qint64> result = m_threadModels[threadId]->addEvent(event, m_timeOffset); - const bool visibleOnTimeline = result.first; - if (visibleOnTimeline) { - m_traceBegin = std::min(m_traceBegin, timestamp); - m_traceEnd = std::max(m_traceEnd, timestamp); - } else if (m_timeOffset == timestamp) { - // this timestamp was used as the time offset but it is not a visible element - // -> reset the time offset again: - m_timeOffset = -1.0; - } -} + static const auto getStringValue = + [](const json &event, const char *key, const QString &def) { + if (!event.contains(key)) + return def; + const json val = event[key]; + if (val.is_string()) + return QString::fromStdString(val); + if (val.is_number()) { + return QString::number(int(val)); + } + return def; + }; + const QString processId = getStringValue(event, CtfProcessIdKey, "0"); + const QString threadId = getStringValue(event, CtfThreadIdKey, processId); + if (!m_threadModels.contains(threadId)) { + addModelForThread(threadId, processId); + } + if (event.value(CtfEventPhaseKey, "") == CtfEventTypeMetadata) { + const std::string name = event[CtfEventNameKey]; + if (name == "thread_name") { + m_threadNames[threadId] = QString::fromStdString(event["args"]["name"]); + } else if (name == "process_name") { + m_processNames[processId] = QString::fromStdString(event["args"]["name"]); + } + } -void CtfTraceManager::load(const QString &filename) -{ - clearAll(); - - std::ifstream file(filename.toStdString()); - if (!file.is_open()) { - QMessageBox::warning(Core::ICore::dialogParent(), - Tr::tr("CTF Visualizer"), - Tr::tr("Cannot read the CTF file.")); - return; + const QPair<bool, qint64> result = m_threadModels[threadId]->addEvent(event, m_timeOffset); + const bool visibleOnTimeline = result.first; + if (visibleOnTimeline) { + m_traceBegin = std::min(m_traceBegin, timestamp); + m_traceEnd = std::max(m_traceEnd, timestamp); + } else if (m_timeOffset == timestamp) { + // this timestamp was used as the time offset but it is not a visible element + // -> reset the time offset again: + m_timeOffset = -1.0; + } + } catch (...) { + m_errorString = Tr::tr("Error while parsing CTF data: %1.") + .arg("<pre>" + QString::fromStdString(event.dump()) + "</pre>"); } - CtfJsonParserCallback ctfParser(this); - json::parser_callback_t callback = [&ctfParser](int depth, json::parse_event_t event, json &parsed) { - return ctfParser.callback(depth, event, parsed); - }; - json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false); - file.close(); - updateStatistics(); } void CtfTraceManager::finalize() @@ -202,14 +148,16 @@ int CtfTraceManager::getSelectionId(const std::string &name) QList<CtfTimelineModel *> CtfTraceManager::getSortedThreads() const { QList<CtfTimelineModel *> models = m_threadModels.values(); - std::sort(models.begin(), models.end(), [](const CtfTimelineModel *a, const CtfTimelineModel *b) -> bool { - return (a->m_processId != b->m_processId) ? (a->m_processId < b->m_processId) - : (std::abs(a->m_threadId) < std::abs(b->m_threadId)); - }); + std::sort(models.begin(), + models.end(), + [](const CtfTimelineModel *a, const CtfTimelineModel *b) { + return (a->m_processId != b->m_processId) ? (a->m_processId < b->m_processId) + : (a->m_threadId < b->m_threadId); + }); return models; } -void CtfTraceManager::setThreadRestriction(int tid, bool restrictToThisThread) +void CtfTraceManager::setThreadRestriction(const QString &tid, bool restrictToThisThread) { if (m_threadRestrictions.value(tid) == restrictToThisThread) return; @@ -218,12 +166,12 @@ void CtfTraceManager::setThreadRestriction(int tid, bool restrictToThisThread) addModelsToAggregator(); } -bool CtfTraceManager::isRestrictedTo(int tid) const +bool CtfTraceManager::isRestrictedTo(const QString &tid) const { return m_threadRestrictions.value(tid); } -void CtfTraceManager::addModelForThread(int threadId, int processId) +void CtfTraceManager::addModelForThread(const QString &threadId, const QString &processId) { CtfTimelineModel *model = new CtfTimelineModel(m_modelAggregator, this, threadId, processId); m_threadModels.insert(threadId, model); @@ -272,6 +220,7 @@ void CtfTraceManager::updateStatistics() void CtfTraceManager::clearAll() { + m_errorString.clear(); m_modelAggregator->clear(); for (CtfTimelineModel *model: std::as_const(m_threadModels)) { model->deleteLater(); @@ -282,6 +231,9 @@ void CtfTraceManager::clearAll() m_timeOffset = -1; } +QString CtfTraceManager::errorString() const +{ + return m_errorString; +} -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctftracemanager.h b/src/plugins/ctfvisualizer/ctftracemanager.h index 7c6c3cd6132..94b5b1d7ca1 100644 --- a/src/plugins/ctfvisualizer/ctftracemanager.h +++ b/src/plugins/ctfvisualizer/ctftracemanager.h @@ -5,16 +5,13 @@ #include "json/json.hpp" #include <QHash> +#include <QList> #include <QMap> #include <QObject> -#include <QVector> -namespace Timeline { -class TimelineModelAggregator; -} +namespace Timeline { class TimelineModelAggregator; } -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfStatisticsModel; class CtfTimelineModel; @@ -34,7 +31,6 @@ public: void addEvent(const nlohmann::json &event); - void load(const QString &filename); void finalize(); bool isEmpty() const; @@ -43,35 +39,35 @@ public: QList<CtfTimelineModel *> getSortedThreads() const; - void setThreadRestriction(int tid, bool restrictToThisThread); - bool isRestrictedTo(int tid) const; + void setThreadRestriction(const QString &tid, bool restrictToThisThread); + bool isRestrictedTo(const QString &tid) const; + + void updateStatistics(); + void clearAll(); + + QString errorString() const; signals: void detailsRequested(const QString &title); protected: - - void addModelForThread(int threadId, int processId); + void addModelForThread(const QString &threadId, const QString &processId); void addModelsToAggregator(); - void updateStatistics(); - - void clearAll(); - Timeline::TimelineModelAggregator *const m_modelAggregator; CtfStatisticsModel *const m_statisticsModel; - QHash<qint64, CtfTimelineModel *> m_threadModels; - QHash<qint64, QString> m_processNames; - QHash<qint64, QString> m_threadNames; + QHash<QString, CtfTimelineModel *> m_threadModels; + QHash<QString, QString> m_processNames; + QHash<QString, QString> m_threadNames; QMap<std::string, int> m_name2selectionId; - QHash<qint64, bool> m_threadRestrictions; + QHash<QString, bool> m_threadRestrictions; double m_traceBegin = std::numeric_limits<double>::max(); double m_traceEnd = std::numeric_limits<double>::min(); double m_timeOffset = -1.0; + QString m_errorString; }; -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizerconstants.h b/src/plugins/ctfvisualizer/ctfvisualizerconstants.h index f5fcca12a75..fae1ab7dd85 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizerconstants.h +++ b/src/plugins/ctfvisualizer/ctfvisualizerconstants.h @@ -4,8 +4,7 @@ #include <string> -namespace CtfVisualizer { -namespace Constants { +namespace CtfVisualizer::Constants { const char CtfVisualizerMenuId[] = "Analyzer.Menu.CtfVisualizer"; const char CtfVisualizerTaskLoadJson[] = @@ -31,5 +30,4 @@ const char CtfEventTypeInstant[] = "i"; const char CtfEventTypeInstantDeprecated[] = "I"; const char CtfEventTypeCounter[] = "C"; -} // namespace Constants -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Constants diff --git a/src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp b/src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp index 9c908536bb9..1ccbc210dfc 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp +++ b/src/plugins/ctfvisualizer/ctfvisualizerplugin.cpp @@ -5,8 +5,7 @@ #include "ctfvisualizertool.h" -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfVisualizerPluginPrivate { @@ -24,5 +23,4 @@ void CtfVisualizerPlugin::initialize() d = new CtfVisualizerPluginPrivate; } -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizerplugin.h b/src/plugins/ctfvisualizer/ctfvisualizerplugin.h index 4550606940b..54e759db580 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizerplugin.h +++ b/src/plugins/ctfvisualizer/ctfvisualizerplugin.h @@ -5,8 +5,7 @@ #include <extensionsystem/iplugin.h> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfVisualizerPlugin : public ExtensionSystem::IPlugin { @@ -21,5 +20,4 @@ public: class CtfVisualizerPluginPrivate *d = nullptr; }; -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp index 9ec24b5a337..7214e9130da 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp +++ b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp @@ -13,36 +13,32 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/icore.h> -#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/progressmanager/taskprogress.h> + #include <debugger/analyzer/analyzerconstants.h> +#include <utils/async.h> #include <utils/stylehelper.h> #include <utils/utilsicons.h> -#include <QAction> -#include <QApplication> #include <QFileDialog> -#include <QFutureInterface> #include <QMenu> #include <QMessageBox> -#include <QThread> + +#include <fstream> using namespace Core; using namespace CtfVisualizer::Constants; +using namespace Utils; +namespace CtfVisualizer::Internal { -namespace CtfVisualizer { -namespace Internal { +using json = nlohmann::json; CtfVisualizerTool::CtfVisualizerTool() - : QObject (nullptr) - , m_isLoading(false) - , m_loadJson(nullptr) - , m_traceView(nullptr) - , m_modelAggregator(new Timeline::TimelineModelAggregator(this)) + : m_modelAggregator(new Timeline::TimelineModelAggregator(this)) , m_zoomControl(new Timeline::TimelineZoomControl(this)) , m_statisticsModel(new CtfStatisticsModel(this)) - , m_statisticsView(nullptr) , m_traceManager(new CtfTraceManager(this, m_modelAggregator.get(), m_statisticsModel.get())) , m_restrictToThreadsButton(new QToolButton) , m_restrictToThreadsMenu(new QMenu(m_restrictToThreadsButton)) @@ -58,15 +54,23 @@ CtfVisualizerTool::CtfVisualizerTool() m_loadJson.reset(new QAction(Tr::tr("Load JSON File"), options)); Core::Command *command = Core::ActionManager::registerAction(m_loadJson.get(), Constants::CtfVisualizerTaskLoadJson, globalContext); - connect(m_loadJson.get(), &QAction::triggered, this, &CtfVisualizerTool::loadJson); + connect(m_loadJson.get(), &QAction::triggered, this, [this] { + QString filename = m_loadJson->data().toString(); + if (filename.isEmpty()) + filename = QFileDialog::getOpenFileName(ICore::dialogParent(), + Tr::tr("Load Chrome Trace Format File"), + "", + Tr::tr("JSON File (*.json)")); + loadJson(filename); + }); options->addAction(command); - m_perspective.setAboutToActivateCallback([this]() { createViews(); }); + m_perspective.setAboutToActivateCallback([this] { createViews(); }); - m_restrictToThreadsButton->setIcon(Utils::Icons::FILTER.icon()); + m_restrictToThreadsButton->setIcon(Icons::FILTER.icon()); m_restrictToThreadsButton->setToolTip(Tr::tr("Restrict to Threads")); m_restrictToThreadsButton->setPopupMode(QToolButton::InstantPopup); - m_restrictToThreadsButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true); + m_restrictToThreadsButton->setProperty(StyleHelper::C_NO_ARROW, true); m_restrictToThreadsButton->setMenu(m_restrictToThreadsMenu); connect(m_restrictToThreadsMenu, &QMenu::triggered, this, &CtfVisualizerTool::toggleThreadRestriction); @@ -83,7 +87,7 @@ void CtfVisualizerTool::createViews() QMenu *contextMenu = new QMenu(m_traceView); contextMenu->addAction(m_loadJson.get()); - connect(contextMenu->addAction(Tr::tr("Reset Zoom")), &QAction::triggered, this, [this](){ + connect(contextMenu->addAction(Tr::tr("Reset Zoom")), &QAction::triggered, this, [this] { m_zoomControl->setRange(m_zoomControl->traceStart(), m_zoomControl->traceEnd()); }); @@ -93,21 +97,20 @@ void CtfVisualizerTool::createViews() contextMenu->exec(m_traceView->mapToGlobal(pos)); }); - m_perspective.addWindow(m_traceView, Utils::Perspective::OperationType::SplitVertical, nullptr); + m_perspective.addWindow(m_traceView, Perspective::OperationType::SplitVertical, nullptr); m_statisticsView = new CtfStatisticsView(m_statisticsModel.get()); m_statisticsView->setWindowTitle(Tr::tr("Statistics")); - connect(m_statisticsView, &CtfStatisticsView::eventTypeSelected, [this] (QString title) - { + connect(m_statisticsView, &CtfStatisticsView::eventTypeSelected, this, [this](QString title) { int typeId = m_traceManager->getSelectionId(title.toStdString()); m_traceView->selectByTypeId(typeId); }); connect(m_traceManager.get(), &CtfTraceManager::detailsRequested, m_statisticsView, &CtfStatisticsView::selectByTitle); - m_perspective.addWindow(m_statisticsView, Utils::Perspective::AddToTab, m_traceView); + m_perspective.addWindow(m_statisticsView, Perspective::AddToTab, m_traceView); - m_perspective.setAboutToActivateCallback(Utils::Perspective::Callback()); + m_perspective.setAboutToActivateCallback(Perspective::Callback()); } void CtfVisualizerTool::setAvailableThreads(const QList<CtfTimelineModel *> &threads) @@ -124,7 +127,7 @@ void CtfVisualizerTool::setAvailableThreads(const QList<CtfTimelineModel *> &thr void CtfVisualizerTool::toggleThreadRestriction(QAction *action) { - const int tid = action->data().toInt(); + const QString tid = action->data().toString(); m_traceManager->setThreadRestriction(tid, action->isChecked()); } @@ -143,41 +146,89 @@ Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const return m_zoomControl.get(); } -void CtfVisualizerTool::loadJson() +class CtfJsonParserFunctor { - if (m_isLoading) - return; +public: + CtfJsonParserFunctor(QPromise<json> &promise) + : m_promise(promise) {} - m_isLoading = true; + bool operator()(int depth, json::parse_event_t event, json &parsed) + { + if ((event == json::parse_event_t::array_start && depth == 0) + || (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) { + m_isInTraceArray = true; + m_traceArrayDepth = depth; + return true; + } + if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) { + m_isInTraceArray = false; + return false; + } + if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) { + m_promise.addResult(parsed); + return false; + } + if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) { + // keep outer object and values in trace objects: + return true; + } + // discard any objects outside of trace array: + // TODO: parse other data, e.g. stack frames + return false; + } - QString filename = QFileDialog::getOpenFileName( - ICore::dialogParent(), Tr::tr("Load Chrome Trace Format File"), - "", Tr::tr("JSON File (*.json)")); - if (filename.isEmpty()) { - m_isLoading = false; +protected: + QPromise<json> &m_promise; + bool m_isInTraceArray = false; + int m_traceArrayDepth = 0; +}; + +static void load(QPromise<json> &promise, const QString &fileName) +{ + std::ifstream file(fileName.toStdString()); + if (!file.is_open()) { + promise.future().cancel(); return; } - auto *futureInterface = new QFutureInterface<void>(); - auto *task = new QFuture<void>(futureInterface); + CtfJsonParserFunctor functor(promise); + json::parser_callback_t callback = [&functor](int depth, json::parse_event_t event, json &parsed) { + return functor(depth, event, parsed); + }; - QThread *thread = QThread::create([this, filename, futureInterface]() { - try { - m_traceManager->load(filename); - } catch (...) { - // nlohmann::json can throw exceptions when requesting type that is wrong - } - m_modelAggregator->moveToThread(QApplication::instance()->thread()); - m_modelAggregator->setParent(this); - futureInterface->reportFinished(); - }); + try { + json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false); + } catch (...) { + // nlohmann::json can throw exceptions when requesting type that is wrong + } - connect(thread, &QThread::finished, this, [this, thread, task, futureInterface]() { - // in main thread: + file.close(); +} + +void CtfVisualizerTool::loadJson(const QString &fileName) +{ + using namespace Tasking; + + if (m_loader || fileName.isEmpty()) + return; + + const auto onSetup = [this, fileName](Async<json> &async) { + m_traceManager->clearAll(); + async.setConcurrentCallData(load, fileName); + connect(&async, &AsyncBase::resultReadyAt, this, [this, asyncPtr = &async](int index) { + m_traceManager->addEvent(asyncPtr->resultAt(index)); + }); + }; + const auto onDone = [this] { + m_traceManager->updateStatistics(); if (m_traceManager->isEmpty()) { QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("CTF Visualizer"), Tr::tr("The file does not contain any trace data.")); + } else if (!m_traceManager->errorString().isEmpty()) { + QMessageBox::warning(Core::ICore::dialogParent(), + Tr::tr("CTF Visualizer"), + m_traceManager->errorString()); } else { m_traceManager->finalize(); m_perspective.select(); @@ -185,18 +236,22 @@ void CtfVisualizerTool::loadJson() zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20); } setAvailableThreads(m_traceManager->getSortedThreads()); - thread->deleteLater(); - delete task; - delete futureInterface; - m_isLoading = false; - }, Qt::QueuedConnection); + m_loader.release()->deleteLater(); + }; + const auto onError = [this] { + QMessageBox::warning(Core::ICore::dialogParent(), + Tr::tr("CTF Visualizer"), + Tr::tr("Cannot read the CTF file.")); + m_loader.release()->deleteLater(); + }; - m_modelAggregator->setParent(nullptr); - m_modelAggregator->moveToThread(thread); - - thread->start(); - Core::ProgressManager::addTask(*task, Tr::tr("Loading CTF File"), CtfVisualizerTaskLoadJson); + const Group recipe { AsyncTask<json>(onSetup) }; + m_loader.reset(new TaskTree(recipe)); + connect(m_loader.get(), &TaskTree::done, this, onDone); + connect(m_loader.get(), &TaskTree::errorOccurred, this, onError); + auto progress = new TaskProgress(m_loader.get()); + progress->setDisplayName(Tr::tr("Loading CTF File")); + m_loader->start(); } -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.h b/src/plugins/ctfvisualizer/ctfvisualizertool.h index 647ea3cb74c..de2facffe75 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertool.h +++ b/src/plugins/ctfvisualizer/ctfvisualizertool.h @@ -6,14 +6,16 @@ #include "ctfvisualizerconstants.h" #include <debugger/debuggermainwindow.h> + #include <tracing/timelinemodelaggregator.h> #include <tracing/timelinezoomcontrol.h> #include <QCoreApplication> #include <QScopedPointer> -namespace CtfVisualizer { -namespace Internal { +namespace Tasking { class TaskTree; } + +namespace CtfVisualizer::Internal { class CtfTraceManager; class CtfStatisticsModel; @@ -21,7 +23,6 @@ class CtfStatisticsView; class CtfTimelineModel; class CtfVisualizerTraceView; - class CtfVisualizerTool : public QObject { Q_OBJECT @@ -34,7 +35,7 @@ public: CtfTraceManager *traceManager() const; Timeline::TimelineZoomControl *zoomControl() const; - void loadJson(); + void loadJson(const QString &fileName); private: void createViews(); @@ -49,15 +50,15 @@ private: QCoreApplication::translate("QtC::CtfVisualizer", "Chrome Trace Format Visualizer")}; - bool m_isLoading; + std::unique_ptr<Tasking::TaskTree> m_loader; QScopedPointer<QAction> m_loadJson; - CtfVisualizerTraceView *m_traceView; + CtfVisualizerTraceView *m_traceView = nullptr; const QScopedPointer<Timeline::TimelineModelAggregator> m_modelAggregator; const QScopedPointer<Timeline::TimelineZoomControl> m_zoomControl; const QScopedPointer<CtfStatisticsModel> m_statisticsModel; - CtfStatisticsView *m_statisticsView; + CtfStatisticsView *m_statisticsView = nullptr; const QScopedPointer<CtfTraceManager> m_traceManager; @@ -65,5 +66,4 @@ private: QMenu *const m_restrictToThreadsMenu; }; -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp b/src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp index fd43a5f1396..90aee8ef528 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp +++ b/src/plugins/ctfvisualizer/ctfvisualizertraceview.cpp @@ -13,8 +13,7 @@ #include <QQmlContext> #include <QQmlEngine> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { CtfVisualizerTraceView::CtfVisualizerTraceView(QWidget *parent, CtfVisualizerTool *tool) : QQuickWidget(parent) @@ -36,8 +35,8 @@ CtfVisualizerTraceView::CtfVisualizerTraceView(QWidget *parent, CtfVisualizerToo setSource(QUrl(QLatin1String("qrc:/qt/qml/QtCreator/Tracing/MainView.qml"))); // Avoid ugly warnings when reading from null properties in QML. - connect(tool->modelAggregator(), &QObject::destroyed, this, [this]{ setSource(QUrl()); }); - connect(tool->zoomControl(), &QObject::destroyed, this, [this]{ setSource(QUrl()); }); + connect(tool->modelAggregator(), &QObject::destroyed, this, [this] { setSource({}); }); + connect(tool->zoomControl(), &QObject::destroyed, this, [this] { setSource({}); }); } CtfVisualizerTraceView::~CtfVisualizerTraceView() = default; @@ -48,7 +47,5 @@ void CtfVisualizerTraceView::selectByTypeId(int typeId) Q_ARG(QVariant,QVariant::fromValue<int>(typeId))); } - -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizertraceview.h b/src/plugins/ctfvisualizer/ctfvisualizertraceview.h index d9976eb37d5..ebfaab885ac 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertraceview.h +++ b/src/plugins/ctfvisualizer/ctfvisualizertraceview.h @@ -3,10 +3,8 @@ #pragma once #include <QQuickWidget> -#include <QWidget> -namespace CtfVisualizer { -namespace Internal { +namespace CtfVisualizer::Internal { class CtfVisualizerTool; @@ -22,7 +20,5 @@ public: void selectByTypeId(int typeId); }; - -} // namespace Internal -} // namespace CtfVisualizer +} // namespace CtfVisualizer::Internal diff --git a/src/plugins/cvs/CVS.json.in b/src/plugins/cvs/CVS.json.in index 5aa6b1eb784..6d2f92ea78a 100644 --- a/src/plugins/cvs/CVS.json.in +++ b/src/plugins/cvs/CVS.json.in @@ -1,29 +1,30 @@ { - \"Name\" : \"CVS\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "CVS", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"CVS integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Version Control", + "Description" : "CVS integration.", + "DisabledByDefault" : true, + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/vnd.qtcreator.cvs.submit\'>\", - \" <comment>CVS submit template</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/vnd.qtcreator.cvs.submit'>", + " <comment>CVS submit template</comment>", + " <sub-class-of type='text/plain'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/cvs/cvseditor.cpp b/src/plugins/cvs/cvseditor.cpp index 5a62103fd57..d28144e3146 100644 --- a/src/plugins/cvs/cvseditor.cpp +++ b/src/plugins/cvs/cvseditor.cpp @@ -90,7 +90,7 @@ QString CvsEditorWidget::changeUnderCursor(const QTextCursor &c) const } break; } - return QString(); + return {}; } VcsBase::BaseAnnotationHighlighter *CvsEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const @@ -101,7 +101,7 @@ VcsBase::BaseAnnotationHighlighter *CvsEditorWidget::createAnnotationHighlighter QStringList CvsEditorWidget::annotationPreviousVersions(const QString &revision) const { if (isFirstRevision(revision)) - return QStringList(); + return {}; return QStringList(previousRevision(revision)); } diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index d9980328fc9..aae9acd1e86 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -148,7 +148,7 @@ public: QStringList arguments() const override { - return settings().diffOptions.value().split(' ', Qt::SkipEmptyParts) + return settings().diffOptions().split(' ', Qt::SkipEmptyParts) + VcsBaseEditorConfig::arguments(); } }; @@ -288,7 +288,6 @@ private: bool commit(const QString &messageFile, const QStringList &subVersionFileList); void cleanCommitMessageFile(); - CvsSettings m_setting; CvsClient *m_client = nullptr; QString m_commitMessageFileName; diff --git a/src/plugins/cvs/cvssettings.cpp b/src/plugins/cvs/cvssettings.cpp index 9e1523486fa..3a653d3804e 100644 --- a/src/plugins/cvs/cvssettings.cpp +++ b/src/plugins/cvs/cvssettings.cpp @@ -5,6 +5,7 @@ #include "cvstr.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <utils/hostosinfo.h> @@ -17,25 +18,20 @@ using namespace Utils; namespace Cvs::Internal { -static CvsSettings *theSettings; - CvsSettings &settings() { - return *theSettings; + static CvsSettings theSettings; + return theSettings; } CvsSettings::CvsSettings() { - theSettings = this; + setAutoApply(false); setSettingsGroup("CVS"); - setId(VcsBase::Constants::VCS_ID_CVS); - setDisplayName(Tr::tr("CVS")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); - binaryPath.setDefaultValue("cvs" QTC_HOST_EXE_SUFFIX); binaryPath.setExpectedKind(PathChooser::ExistingCommand); - binaryPath.setHistoryCompleter(QLatin1String("Cvs.Command.History")); + binaryPath.setHistoryCompleter("Cvs.Command.History"); binaryPath.setDisplayName(Tr::tr("CVS Command")); binaryPath.setLabelText(Tr::tr("CVS command:")); @@ -82,11 +78,13 @@ CvsSettings::CvsSettings() st }; }); + + readSettings(); } QStringList CvsSettings::addOptions(const QStringList &args) const { - const QString cvsRoot = this->cvsRoot.value(); + const QString cvsRoot = this->cvsRoot(); if (cvsRoot.isEmpty()) return args; @@ -97,4 +95,20 @@ QStringList CvsSettings::addOptions(const QStringList &args) const return rc; } +// CvsSettingsPage + +class CvsSettingsPage final : Core::IOptionsPage +{ +public: + CvsSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_CVS); + setDisplayName(Tr::tr("CVS")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const CvsSettingsPage settingsPage; + } // Cvs::Internal diff --git a/src/plugins/cvs/cvssubmiteditor.cpp b/src/plugins/cvs/cvssubmiteditor.cpp index d5ecf9fcf4c..983dadaee32 100644 --- a/src/plugins/cvs/cvssubmiteditor.cpp +++ b/src/plugins/cvs/cvssubmiteditor.cpp @@ -29,7 +29,7 @@ QString CvsSubmitEditor::stateName(State st) const case LocallyRemoved: return m_msgRemoved; } - return QString(); + return {}; } void CvsSubmitEditor::setStateList(const StateFilePairs &statusOutput) diff --git a/src/plugins/debugger/CMakeLists.txt b/src/plugins/debugger/CMakeLists.txt index d06e82a27e1..21920f8fef9 100644 --- a/src/plugins/debugger/CMakeLists.txt +++ b/src/plugins/debugger/CMakeLists.txt @@ -27,7 +27,11 @@ add_qtc_plugin(Debugger console/consoleitemmodel.cpp console/consoleitemmodel.h console/consoleproxymodel.cpp console/consoleproxymodel.h console/consoleview.cpp console/consoleview.h + dap/cmakedapengine.cpp dap/cmakedapengine.h + dap/dapclient.cpp dap/dapclient.h dap/dapengine.cpp dap/dapengine.h + dap/gdbdapengine.cpp dap/gdbdapengine.h + dap/pydapengine.cpp dap/pydapengine.h debugger.qrc debugger_global.h debuggeractions.cpp debuggeractions.h @@ -39,7 +43,7 @@ add_qtc_plugin(Debugger debuggerinternalconstants.h debuggeritem.cpp debuggeritem.h debuggeritemmanager.cpp debuggeritemmanager.h - debuggerkitinformation.cpp debuggerkitinformation.h + debuggerkitaspect.cpp debuggerkitaspect.h debuggermainwindow.cpp debuggermainwindow.h debuggerplugin.cpp debuggerplugin.h debuggerprotocol.cpp debuggerprotocol.h @@ -51,7 +55,7 @@ add_qtc_plugin(Debugger disassembleragent.cpp disassembleragent.h disassemblerlines.cpp disassemblerlines.h gdb/gdbengine.cpp gdb/gdbengine.h - gdb/gdboptionspage.cpp + gdb/gdbsettings.cpp gdb/gdbsettings.h imageviewer.cpp imageviewer.h enginemanager.cpp enginemanager.h lldb/lldbengine.cpp lldb/lldbengine.h diff --git a/src/plugins/debugger/Debugger.json.in b/src/plugins/debugger/Debugger.json.in index 96b9502ebe0..06df0489d6c 100644 --- a/src/plugins/debugger/Debugger.json.in +++ b/src/plugins/debugger/Debugger.json.in @@ -1,60 +1,60 @@ { - \"Name\" : \"Debugger\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Debugger", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Creator\", - \"Description\" : \"Debugger integration.\", - \"Url\" : \"http://www.qt.io\", - \"Arguments\" : [ + "Category" : "Qt Creator", + "Description" : "Debugger integration.", + "Url" : "http://www.qt.io", + "Arguments" : [ { - \"Name\" : \"-debug\", - \"Parameter\" : \"pid\", - \"Description\" : \"Attach to local process\" + "Name" : "-debug", + "Parameter" : "pid", + "Description" : "Attach to local process" }, { - \"Name\" : \"-debug <executable>[,kit=<kit>][,terminal={0,1}][,sysroot=<sysroot>]\", - \"Description\" : \"Start and debug executable\" + "Name" : "-debug <executable>[,kit=<kit>][,terminal={0,1}][,sysroot=<sysroot>]", + "Description" : "Start and debug executable" }, { - \"Name\" : \"-debug [executable,]core=<corefile>[,kit=<kit>][,sysroot=<sysroot>]\", - \"Description\" : \"Attach to core file\" + "Name" : "-debug [executable,]core=<corefile>[,kit=<kit>][,sysroot=<sysroot>]", + "Description" : "Attach to core file" }, { - \"Name\" : \"-debug <executable>,server=<server:port>[,kit=<kit>][,sysroot=<sysroot>]\", - \"Description\" : \"Attach to remote debug server\" + "Name" : "-debug <executable>,server=<server:port>[,kit=<kit>][,sysroot=<sysroot>]", + "Description" : "Attach to remote debug server" }, { - \"Name\" : \"-wincrashevent\", - \"Parameter\" : \"eventhandle:pid\", - \"Description\" : \"Event handle used for attaching to crashed processes\" + "Name" : "-wincrashevent", + "Parameter" : "eventhandle:pid", + "Description" : "Event handle used for attaching to crashed processes" } ], - $$dependencyList, + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-asm\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Assembler</comment>\", - \" <glob pattern=\'*.asm\'/>\", - \" </mime-type>\", - \" <!-- Catch-all for assemblers -->\", - \" <mime-type type=\'text/x-qtcreator-generic-asm\'>\", - \" <sub-class-of type=\'text/x-asm\'/>\", - \" <comment>Qt Creator Generic Assembler</comment>\", - \" <glob pattern=\'*.asm\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-asm'>", + " <sub-class-of type='text/plain'/>", + " <comment>Assembler</comment>", + " <glob pattern='*.asm'/>", + " </mime-type>", + " <!-- Catch-all for assemblers -->", + " <mime-type type='text/x-qtcreator-generic-asm'>", + " <sub-class-of type='text/x-asm'/>", + " <comment>Qt Creator Generic Assembler</comment>", + " <glob pattern='*.asm'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/debugger/analyzer/analyzerrunconfigwidget.cpp b/src/plugins/debugger/analyzer/analyzerrunconfigwidget.cpp index c74ea44a06b..61f81f46ce3 100644 --- a/src/plugins/debugger/analyzer/analyzerrunconfigwidget.cpp +++ b/src/plugins/debugger/analyzer/analyzerrunconfigwidget.cpp @@ -27,7 +27,7 @@ AnalyzerRunConfigWidget::AnalyzerRunConfigWidget(ProjectExplorer::GlobalOrProjec auto restoreButton = new QPushButton(Tr::tr("Restore Global")); auto innerPane = new QWidget; - auto configWidget = aspect->projectSettings()->createConfigWidget(); + auto configWidget = aspect->projectSettings()->layouter()().emerge(); auto details = new DetailsWidget; details->setWidget(innerPane); diff --git a/src/plugins/debugger/analyzer/analyzerutils.cpp b/src/plugins/debugger/analyzer/analyzerutils.cpp index dc87b2ad614..a2ea231755b 100644 --- a/src/plugins/debugger/analyzer/analyzerutils.cpp +++ b/src/plugins/debugger/analyzer/analyzerutils.cpp @@ -44,7 +44,7 @@ CPlusPlus::Symbol *AnalyzerUtils::findSymbolUnderCursor() const int pos = tc.position(); widget->convertPosition(pos, &line, &column); - const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot &snapshot = CppEditor::CppModelManager::snapshot(); CPlusPlus::Document::Ptr doc = snapshot.document(widget->textDocument()->filePath()); QTC_ASSERT(doc, return nullptr); diff --git a/src/plugins/debugger/analyzer/detailederrorview.cpp b/src/plugins/debugger/analyzer/detailederrorview.cpp index 6817ef75f34..06941ac1148 100644 --- a/src/plugins/debugger/analyzer/detailederrorview.cpp +++ b/src/plugins/debugger/analyzer/detailederrorview.cpp @@ -134,7 +134,7 @@ QList<QAction *> DetailedErrorView::commonActions() const QList<QAction *> DetailedErrorView::customActions() const { - return QList<QAction *>(); + return {}; } int DetailedErrorView::currentRow() const diff --git a/src/plugins/debugger/analyzer/startremotedialog.cpp b/src/plugins/debugger/analyzer/startremotedialog.cpp index 44447ad7017..3e7ec624cf1 100644 --- a/src/plugins/debugger/analyzer/startremotedialog.cpp +++ b/src/plugins/debugger/analyzer/startremotedialog.cpp @@ -9,8 +9,8 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/sshparameters.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitchooser.h> -#include <projectexplorer/kitinformation.h> #include <utils/commandline.h> @@ -67,7 +67,7 @@ StartRemoteDialog::StartRemoteDialog(QWidget *parent) verticalLayout->addLayout(formLayout); verticalLayout->addWidget(d->buttonBox); - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup("AnalyzerStartRemoteDialog"); d->kitChooser->populate(); d->kitChooser->setCurrentKitId(Id::fromSetting(settings->value("profile"))); @@ -93,7 +93,7 @@ StartRemoteDialog::~StartRemoteDialog() void StartRemoteDialog::accept() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup("AnalyzerStartRemoteDialog"); settings->setValue("profile", d->kitChooser->currentKitId().toString()); settings->setValue("executable", d->executable->text()); diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 963aced456c..53696f8e910 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -1101,10 +1101,10 @@ QVariant BreakpointItem::data(int column, int role) const break; } - if (role == Qt::ToolTipRole && debuggerSettings()->useToolTipsInBreakpointsView.value()) + if (role == Qt::ToolTipRole && settings().useToolTipsInBreakpointsView()) return toolTip(); - return QVariant(); + return {}; } void BreakpointItem::addToCommand(DebuggerCommand *cmd, BreakpointPathUsage defaultPathUsage) const @@ -1689,8 +1689,8 @@ bool BreakHandler::contextMenuEvent(const ItemViewEvent &ev) menu->addSeparator(); - menu->addAction(debuggerSettings()->useToolTipsInBreakpointsView.action()); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().useToolTipsInBreakpointsView.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); @@ -2219,10 +2219,10 @@ QVariant GlobalBreakpointItem::data(int column, int role) const break; } - if (role == Qt::ToolTipRole && debuggerSettings()->useToolTipsInBreakpointsView.value()) + if (role == Qt::ToolTipRole && settings().useToolTipsInBreakpointsView()) return toolTip(); - return QVariant(); + return {}; } QIcon GlobalBreakpointItem::icon() const @@ -2495,7 +2495,7 @@ void BreakpointManager::setOrRemoveBreakpoint(const ContextData &location, const BreakpointParameters data; if (location.type == LocationByFile) { data.type = BreakpointByFileAndLine; - if (debuggerSettings()->breakpointsFullPathByDefault.value()) + if (settings().breakpointsFullPathByDefault()) data.pathUsage = BreakpointUseFullPath; data.tracepoint = !tracePointMessage.isEmpty(); data.message = tracePointMessage; @@ -2686,8 +2686,8 @@ bool BreakpointManager::contextMenuEvent(const ItemViewEvent &ev) menu->addSeparator(); - menu->addAction(debuggerSettings()->useToolTipsInBreakpointsView.action()); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().useToolTipsInBreakpointsView.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); @@ -2709,7 +2709,7 @@ void BreakpointManager::executeDeleteAllBreakpointsDialog() Tr::tr("Remove All Breakpoints"), Tr::tr("Are you sure you want to remove all breakpoints " "from all files in the current session?"), - QString("RemoveAllBreakpoints")); + Key("RemoveAllBreakpoints")); if (pressed != QMessageBox::Yes) return; diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 28d90c747cf..7d126701d80 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -7,8 +7,6 @@ #include "cdbparsehelpers.h" #include "stringinputstream.h" -#include <app/app_version.h> - #include <debugger/breakhandler.h> #include <debugger/debuggeractions.h> #include <debugger/debuggercore.h> @@ -56,6 +54,7 @@ #include <cppeditor/cppworkingcopy.h> #include <QDir> +#include <QGuiApplication> #include <QRegularExpression> #include <cctype> @@ -179,18 +178,18 @@ CdbEngine::CdbEngine() : wh->addTypeFormats("QImage", imageFormats); wh->addTypeFormats("QImage *", imageFormats); - DebuggerSettings *s = debuggerSettings(); - connect(s->createFullBacktrace.action(), &QAction::triggered, + DebuggerSettings &s = settings(); + connect(s.createFullBacktrace.action(), &QAction::triggered, this, &CdbEngine::createFullBacktrace); connect(&m_process, &Process::started, this, &CdbEngine::processStarted); connect(&m_process, &Process::done, this, &CdbEngine::processDone); m_process.setStdOutLineCallback([this](const QString &line) { parseOutputLine(line); }); m_process.setStdErrLineCallback([this](const QString &line) { parseOutputLine(line); }); - connect(&s->useDebuggingHelpers, &BaseAspect::changed, + connect(&s.useDebuggingHelpers, &BaseAspect::changed, this, &CdbEngine::updateLocals); - if (s->useCodeModel.value()) - m_codeModelSnapshot = CppEditor::CppModelManager::instance()->snapshot(); + if (s.useCodeModel()) + m_codeModelSnapshot = CppEditor::CppModelManager::snapshot(); } void CdbEngine::init() @@ -226,7 +225,7 @@ void CdbEngine::init() } const SourcePathMap &sourcePathMap - = mergePlatformQtPath(runParameters(), debuggerSettings()->sourcePathMap.value()); + = mergePlatformQtPath(runParameters(), settings().sourcePathMap()); if (!sourcePathMap.isEmpty()) { for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) { m_sourcePathMappings.push_back({QDir::toNativeSeparators(it.key()), @@ -333,16 +332,16 @@ void CdbEngine::setupEngine() const QFileInfo extensionFi(CdbEngine::extensionLibraryName(cdbIs64Bit, cdbIsArm)); if (!extensionFi.isFile()) { handleSetupFailure(Tr::tr("Internal error: The extension %1 cannot be found.\n" - "If you have updated %2 via Maintenance Tool, you may " - "need to rerun the Tool and select \"Add or remove components\" " - "and then select the " - "Qt > Tools > Qt Creator CDB Debugger Support component.\n" - "If you build %2 from sources and want to use a CDB executable " - "with another bitness than your %2 build, " - "you will need to build a separate CDB extension with the " - "same bitness as the CDB you want to use."). - arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()), - QString(Core::Constants::IDE_DISPLAY_NAME))); + "If you have updated %2 via Maintenance Tool, you may " + "need to rerun the Tool and select \"Add or remove components\" " + "and then select the " + "Qt > Tools > Qt Creator CDB Debugger Support component.\n" + "If you build %2 from sources and want to use a CDB executable " + "with another bitness than your %2 build, " + "you will need to build a separate CDB extension with the " + "same bitness as the CDB you want to use.") + .arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()), + QGuiApplication::applicationDisplayName())); return; } @@ -365,17 +364,17 @@ void CdbEngine::setupEngine() if (sp.useTerminal) // Separate console debugger.addArg("-2"); - const DebuggerSettings &s = *debuggerSettings(); - if (s.ignoreFirstChanceAccessViolation.value()) + const DebuggerSettings &s = settings(); + if (s.ignoreFirstChanceAccessViolation()) debugger.addArg("-x"); - const QStringList &sourcePaths = s.cdbSourcePaths.value(); + const QStringList &sourcePaths = s.cdbSourcePaths(); if (!sourcePaths.isEmpty()) debugger.addArgs({"-srcpath", sourcePaths.join(';')}); - debugger.addArgs({"-y", QChar('"') + s.cdbSymbolPaths.value().join(';') + '"'}); + debugger.addArgs({"-y", QChar('"') + s.cdbSymbolPaths().join(';') + '"'}); - debugger.addArgs(expand(s.cdbAdditionalArguments.value()), CommandLine::Raw); + debugger.addArgs(expand(s.cdbAdditionalArguments()), CommandLine::Raw); switch (sp.startMode) { case StartInternal: @@ -473,8 +472,8 @@ void CdbEngine::handleInitialSessionIdle() // Take ownership of the breakpoint. Requests insertion. TODO: Cpp only? BreakpointManager::claimBreakpointsForEngine(this); - const DebuggerSettings &s = *debuggerSettings(); - QStringList symbolPaths = s.cdbSymbolPaths.value(); + const DebuggerSettings &s = settings(); + QStringList symbolPaths = s.cdbSymbolPaths(); QString symbolPath = rp.inferior.environment.expandedValueForKey("_NT_ALT_SYMBOL_PATH"); if (!symbolPath.isEmpty()) symbolPaths += symbolPath; @@ -494,7 +493,7 @@ void CdbEngine::handleInitialSessionIdle() + " secondChance=" + (s.secondChanceExceptionTaskEntry() ? "1" : "0") , NoFlags}); - if (s.cdbUsePythonDumper.value()) + if (s.cdbUsePythonDumper()) runCommand({"print(sys.version)", ScriptCommand, CB(setupScripting)}); runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) { @@ -552,13 +551,13 @@ void CdbEngine::runEngine() if (debug) qDebug("runEngine"); - const QStringList breakEvents = debuggerSettings()->cdbBreakEvents.value(); + const QStringList breakEvents = settings().cdbBreakEvents(); for (const QString &breakEvent : breakEvents) runCommand({"sxe " + breakEvent, NoFlags}); // Break functions: each function must be fully qualified, // else the debugger will slow down considerably. const auto cb = [this](const DebuggerResponse &r) { handleBreakInsert(r, Breakpoint()); }; - if (debuggerSettings()->cdbBreakOnCrtDbgReport.value()) { + if (settings().cdbBreakOnCrtDbgReport()) { Abi::OSFlavor flavor = runParameters().toolChainAbi.osFlavor(); // CrtDebugReport cannot be safely resolved for vc 19 if ((flavor > Abi::WindowsMsvc2005Flavor && flavor <= Abi::WindowsMsvc2013Flavor) || @@ -571,11 +570,11 @@ void CdbEngine::runEngine() runCommand({breakAtFunctionCommand(Constants::CRT_DEBUG_REPORT, debugModule), BuiltinCommand, cb}); } } -// if (debuggerSettings()->breakOnWarning.value())) { +// if (settings().breakOnWarning())) { // runCommand({"bm /( QtCored4!qWarning", BuiltinCommand}); // 'bm': All overloads. // runCommand({"bm /( Qt5Cored!QMessageLogger::warning", BuiltinCommand}); // } -// if (debuggerSettion()->breakOnFatal.value()) { +// if (settings().breakOnFatal()) { // runCommand({"bm /( QtCored4!qFatal", BuiltinCommand}); // 'bm': All overloads. // runCommand({"bm /( Qt5Cored!QMessageLogger::fatal", BuiltinCommand}); // } @@ -1059,7 +1058,7 @@ void CdbEngine::activateFrame(int index) void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) { - const DebuggerSettings &s = *debuggerSettings(); + const DebuggerSettings &s = settings(); if (m_pythonVersion > 0x030000) { watchHandler()->notifyUpdateStarted(updateParameters); @@ -1069,21 +1068,21 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE"); cmd.arg("passexceptions", alwaysVerbose); - cmd.arg("fancy", s.useDebuggingHelpers.value()); - cmd.arg("autoderef", s.autoDerefPointers.value()); - cmd.arg("dyntype", s.useDynamicType.value()); + cmd.arg("fancy", s.useDebuggingHelpers()); + cmd.arg("autoderef", s.autoDerefPointers()); + cmd.arg("dyntype", s.useDynamicType()); cmd.arg("partialvar", updateParameters.partialVariable); - cmd.arg("qobjectnames", s.showQObjectNames.value()); - cmd.arg("timestamps", s.logTimeStamps.value()); + cmd.arg("qobjectnames", s.showQObjectNames()); + cmd.arg("timestamps", s.logTimeStamps()); StackFrame frame = stackHandler()->currentFrame(); cmd.arg("context", frame.context); cmd.arg("nativemixed", isNativeMixedActive()); - cmd.arg("stringcutoff", s.maximalStringLength.value()); - cmd.arg("displaystringlimit", s.displayStringLimit.value()); + cmd.arg("stringcutoff", s.maximalStringLength()); + cmd.arg("displaystringlimit", s.displayStringLimit()); - if (s.useCodeModel.value()) { + if (s.useCodeModel()) { QStringList variables = getUninitializedVariables(m_codeModelSnapshot, frame.function, frame.file, frame.line); cmd.arg("uninitialized", variables); @@ -1142,9 +1141,9 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) } } str << blankSeparator << "-v"; - if (s.useDebuggingHelpers.value()) + if (s.useDebuggingHelpers()) str << blankSeparator << "-c"; - if (s.sortStructMembers.value()) + if (s.sortStructMembers()) str << blankSeparator << "-a"; const QString typeFormats = watchHandler()->typeFormatRequests(); if (!typeFormats.isEmpty()) @@ -1154,7 +1153,7 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) str << blankSeparator << "-I " << individualFormats; // Uninitialized variables if desired. Quote as safeguard against shadowed // variables in case of errors in uninitializedVariables(). - if (s.useCodeModel.value()) { + if (s.useCodeModel()) { const QStringList variables = getUninitializedVariables(m_codeModelSnapshot, frame.function, frame.file, frame.line); if (!variables.isEmpty()) { @@ -2119,7 +2118,7 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c if (what == "debuggee_output") { const QByteArray decoded = QByteArray::fromHex(message.toUtf8()); - showMessage(QString::fromUtf16(reinterpret_cast<const ushort *>(decoded.data()), decoded.size() / 2), + showMessage(QString::fromUtf16(reinterpret_cast<const char16_t *>(decoded.data()), decoded.size() / 2), AppOutput); return; } @@ -2265,7 +2264,7 @@ void CdbEngine::checkQtSdkPdbFiles(const QString &module) CheckableMessageBox::information(Core::ICore::dialogParent(), Tr::tr("Missing Qt Debug Information"), message, - QString("CdbQtSdkPdbHint")); + Key("CdbQtSdkPdbHint")); showMessage("Missing Qt Debug Information Files package for " + qtName, LogMisc); }; @@ -2293,7 +2292,7 @@ void CdbEngine::parseOutputLine(QString line) "Make sure that your antivirus solution is up to date and if that does not work " "consider adding an exception for \"%1\".") .arg(m_extensionFileName), - QString("SecureInfoCdbextCannotBeLoaded")); + Key("SecureInfoCdbextCannotBeLoaded")); notifyEngineSetupFailed(); } static const QString creatorExtPrefix = "<qtcreatorcdbext>|"; @@ -2501,10 +2500,10 @@ void CdbEngine::insertBreakpoint(const Breakpoint &bp) BreakpointParameters response = parameters; const QString responseId = breakPointCdbId(bp); QScopedPointer<BreakpointCorrectionContext> lineCorrection( - new BreakpointCorrectionContext(m_codeModelSnapshot, CppEditor::CppModelManager::instance()->workingCopy())); + new BreakpointCorrectionContext(m_codeModelSnapshot, CppEditor::CppModelManager::workingCopy())); if (!m_autoBreakPointCorrection && parameters.type == BreakpointByFileAndLine - && debuggerSettings()->cdbBreakPointCorrection.value()) { + && settings().cdbBreakPointCorrection()) { response.textPosition.line = int(lineCorrection->fixLineNumber(parameters.fileName, unsigned(parameters.textPosition.line))); @@ -2826,13 +2825,13 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) runCommand({"theDumper = Dumper()", ScriptCommand}); } - const FilePath path = debuggerSettings()->extraDumperFile(); + const FilePath path = settings().extraDumperFile(); if (!path.isEmpty() && path.isReadableFile()) { DebuggerCommand cmd("theDumper.addDumperModule", ScriptCommand); cmd.arg("path", path.path()); runCommand(cmd); } - const QString commands = debuggerSettings()->extraDumperCommands.value(); + const QString commands = settings().extraDumperCommands(); if (!commands.isEmpty()) { for (const auto &command : commands.split('\n', Qt::SkipEmptyParts)) runCommand({command, ScriptCommand}); diff --git a/src/plugins/debugger/cdb/cdboptionspage.cpp b/src/plugins/debugger/cdb/cdboptionspage.cpp index 8f21d670964..ef3586e61b6 100644 --- a/src/plugins/debugger/cdb/cdboptionspage.cpp +++ b/src/plugins/debugger/cdb/cdboptionspage.cpp @@ -10,8 +10,6 @@ #include <debugger/debuggertr.h> #include <debugger/shared/cdbsymbolpathlisteditor.h> -#include <coreplugin/icore.h> - #include <utils/aspects.h> #include <utils/layoutbuilder.h> @@ -161,7 +159,7 @@ private: void apply() final; void finish() final; - Utils::AspectContainer &m_group = debuggerSettings()->page5; + Utils::AspectContainer &m_group = settings().page5; CdbBreakEventWidget *m_breakEventWidget; }; @@ -169,9 +167,9 @@ CdbOptionsPageWidget::CdbOptionsPageWidget() : m_breakEventWidget(new CdbBreakEventWidget) { using namespace Layouting; - DebuggerSettings &s = *debuggerSettings(); + DebuggerSettings &s = settings(); - m_breakEventWidget->setBreakEvents(debuggerSettings()->cdbBreakEvents.value()); + m_breakEventWidget->setBreakEvents(settings().cdbBreakEvents()); Column { Row { @@ -215,13 +213,14 @@ CdbOptionsPageWidget::CdbOptionsPageWidget() void CdbOptionsPageWidget::apply() { - m_group.apply(); m_group.writeSettings(Core::ICore::settings()); - debuggerSettings()->cdbBreakEvents.setValue(m_breakEventWidget->breakEvents()); + m_group.apply(); + m_group.writeSettings(); + settings().cdbBreakEvents.setValue(m_breakEventWidget->breakEvents()); } void CdbOptionsPageWidget::finish() { - m_breakEventWidget->setBreakEvents(debuggerSettings()->cdbBreakEvents.value()); + m_breakEventWidget->setBreakEvents(settings().cdbBreakEvents()); m_group.finish(); } @@ -244,7 +243,7 @@ public: void apply() final; void finish() final; - AspectContainer &m_group = debuggerSettings()->page6; + AspectContainer &m_group = settings().page6; private: PathListEditor *m_symbolPaths = nullptr; @@ -267,15 +266,15 @@ CdbPathsPageWidget::CdbPathsPageWidget() void CdbPathsPageWidget::apply() { - debuggerSettings()->cdbSymbolPaths.setValue(m_symbolPaths->pathList()); - debuggerSettings()->cdbSourcePaths.setValue(m_sourcePaths->pathList()); - m_group.writeSettings(Core::ICore::settings()); + settings().cdbSymbolPaths.setValue(m_symbolPaths->pathList()); + settings().cdbSourcePaths.setValue(m_sourcePaths->pathList()); + m_group.writeSettings(); } void CdbPathsPageWidget::finish() { - m_symbolPaths->setPathList(debuggerSettings()->cdbSymbolPaths.value()); - m_sourcePaths->setPathList(debuggerSettings()->cdbSourcePaths.value()); + m_symbolPaths->setPathList(settings().cdbSymbolPaths()); + m_sourcePaths->setPathList(settings().cdbSourcePaths()); } CdbPathsPage::CdbPathsPage() diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index 97e9442266e..bb24d674a09 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -7,90 +7,176 @@ #include "debuggerinternalconstants.h" #include "debuggertr.h" -#include <coreplugin/icore.h> +#ifdef Q_OS_WIN +#include "registerpostmortemaction.h" +#endif + +#include <coreplugin/dialogs/ioptionspage.h> #include <utils/layoutbuilder.h> +#include <QGuiApplication> + using namespace Core; using namespace Debugger::Constants; using namespace Utils; namespace Debugger::Internal { -/////////////////////////////////////////////////////////////////////// -// -// CommonOptionsPage -// -/////////////////////////////////////////////////////////////////////// +// CommonSettings -class CommonOptionsPageWidget : public Core::IOptionsPageWidget +CommonSettings &commonSettings() { -public: - explicit CommonOptionsPageWidget() - { - DebuggerSettings &s = *debuggerSettings(); + static CommonSettings settings; + return settings; +} - setOnApply([&s] { - const bool originalPostMortem = s.registerForPostMortem->value(); - const bool currentPostMortem = s.registerForPostMortem->volatileValue().toBool(); - // explicitly trigger setValue() to override the setValueSilently() and trigger the registration - if (originalPostMortem != currentPostMortem) - s.registerForPostMortem->setValue(currentPostMortem); - s.page1.apply(); - s.page1.writeSettings(ICore::settings()); - }); +CommonSettings::CommonSettings() +{ + setAutoApply(false); + const Key debugModeGroup("DebugMode"); - setOnFinish([&s] { s.page1.finish(); }); + useAlternatingRowColors.setSettingsKey(debugModeGroup, "UseAlternatingRowColours"); + useAlternatingRowColors.setLabelText(Tr::tr("Use alternating row colors in debug views")); + stationaryEditorWhileStepping.setSettingsKey(debugModeGroup, "StationaryEditorWhileStepping"); + stationaryEditorWhileStepping.setLabelText(Tr::tr("Keep editor stationary when stepping")); + stationaryEditorWhileStepping.setToolTip( + Tr::tr("Scrolls the editor only when it is necessary to keep the current line in view, " + "instead of keeping the next statement centered at all times.")); + + forceLoggingToConsole.setSettingsKey(debugModeGroup, "ForceLoggingToConsole"); + forceLoggingToConsole.setLabelText(Tr::tr("Force logging to console")); + forceLoggingToConsole.setToolTip( + Tr::tr("Sets QT_LOGGING_TO_CONSOLE=1 in the environment of the debugged program, " + "preventing storing debug output in system logs.")); + + fontSizeFollowsEditor.setSettingsKey(debugModeGroup, "FontSizeFollowsEditor"); + fontSizeFollowsEditor.setToolTip(Tr::tr("Changes the font size in the debugger views when " + "the font size in the main editor changes.")); + fontSizeFollowsEditor.setLabelText(Tr::tr("Debugger font size follows main editor")); + +#ifdef Q_OS_WIN + registerForPostMortem = new RegisterPostMortemAction; + registerForPostMortem->setSettingsKey(debugModeGroup, "RegisterForPostMortem"); + registerForPostMortem->setToolTip(Tr::tr("Registers %1 for debugging crashed applications.") + .arg(QGuiApplication::applicationDisplayName())); + registerForPostMortem->setLabelText( + Tr::tr("Use %1 for post-mortem debugging").arg(QGuiApplication::applicationDisplayName())); + registerAspect(registerForPostMortem); +#else + // Some dummy. + registerForPostMortem = new BoolAspect; + registerForPostMortem->setVisible(false); +#endif + + maximalStackDepth.setSettingsKey(debugModeGroup, "MaximalStackDepth"); + maximalStackDepth.setDefaultValue(20); + maximalStackDepth.setSpecialValueText(Tr::tr("<unlimited>")); + maximalStackDepth.setRange(0, 1000); + maximalStackDepth.setSingleStep(5); + maximalStackDepth.setLabelText(Tr::tr("Maximum stack depth:")); + + showQmlObjectTree.setSettingsKey(debugModeGroup, "ShowQmlObjectTree"); + showQmlObjectTree.setDefaultValue(true); + showQmlObjectTree.setToolTip(Tr::tr("Shows QML object tree in Locals and Expressions " + "when connected and not stepping.")); + showQmlObjectTree.setLabelText(Tr::tr("Show QML object tree")); + + const QString t = Tr::tr("Stopping and stepping in the debugger " + "will automatically open views associated with the current location.") + '\n'; + + closeSourceBuffersOnExit.setSettingsKey(debugModeGroup, "CloseBuffersOnExit"); + closeSourceBuffersOnExit.setLabelText(Tr::tr("Close temporary source views on debugger exit")); + closeSourceBuffersOnExit.setToolTip(t + Tr::tr("Closes automatically opened source views when the debugger exits.")); + + closeMemoryBuffersOnExit.setSettingsKey(debugModeGroup, "CloseMemoryBuffersOnExit"); + closeMemoryBuffersOnExit.setDefaultValue(true); + closeMemoryBuffersOnExit.setLabelText(Tr::tr("Close temporary memory views on debugger exit")); + closeMemoryBuffersOnExit.setToolTip(t + Tr::tr("Closes automatically opened memory views when the debugger exits.")); + + switchModeOnExit.setSettingsKey(debugModeGroup, "SwitchModeOnExit"); + switchModeOnExit.setLabelText(Tr::tr("Switch to previous mode on debugger exit")); + + breakpointsFullPathByDefault.setSettingsKey(debugModeGroup, "BreakpointsFullPath"); + breakpointsFullPathByDefault.setToolTip(Tr::tr("Enables a full file path in breakpoints by default also for GDB.")); + breakpointsFullPathByDefault.setLabelText(Tr::tr("Set breakpoints using a full absolute path")); + + raiseOnInterrupt.setSettingsKey(debugModeGroup, "RaiseOnInterrupt"); + raiseOnInterrupt.setDefaultValue(true); + raiseOnInterrupt.setLabelText(Tr::tr("Bring %1 to foreground when application interrupts") + .arg(QGuiApplication::applicationDisplayName())); + + useAnnotationsInMainEditor.setSettingsKey(debugModeGroup, "UseAnnotations"); + useAnnotationsInMainEditor.setLabelText(Tr::tr("Use annotations in main editor when debugging")); + useAnnotationsInMainEditor.setToolTip( + "<p>" + + Tr::tr("Shows simple variable values " + "as annotations in the main editor during debugging.")); + useAnnotationsInMainEditor.setDefaultValue(true); + + warnOnReleaseBuilds.setSettingsKey(debugModeGroup, "WarnOnReleaseBuilds"); + warnOnReleaseBuilds.setDefaultValue(true); + warnOnReleaseBuilds.setLabelText(Tr::tr("Warn when debugging \"Release\" builds")); + warnOnReleaseBuilds.setToolTip(Tr::tr("Shows a warning when starting the debugger " + "on a binary with insufficient debug information.")); + + useToolTipsInMainEditor.setSettingsKey(debugModeGroup, "UseToolTips"); + useToolTipsInMainEditor.setLabelText(Tr::tr("Use tooltips in main editor when debugging")); + useToolTipsInMainEditor.setToolTip( + "<p>" + + Tr::tr("Enables tooltips for variable " + "values during debugging. Since this can slow down debugging and " + "does not provide reliable information as it does not use scope " + "information, it is switched off by default.")); + useToolTipsInMainEditor.setDefaultValue(true); + + setLayouter([this] { using namespace Layouting; Column col1 { - s.useAlternatingRowColors, - s.useAnnotationsInMainEditor, - s.useToolTipsInMainEditor, - s.closeSourceBuffersOnExit, - s.closeMemoryBuffersOnExit, - s.raiseOnInterrupt, - s.breakpointsFullPathByDefault, - s.warnOnReleaseBuilds, - Row { s.maximalStackDepth, st } + useAlternatingRowColors, + useAnnotationsInMainEditor, + useToolTipsInMainEditor, + closeSourceBuffersOnExit, + closeMemoryBuffersOnExit, + raiseOnInterrupt, + breakpointsFullPathByDefault, + warnOnReleaseBuilds, + Row { maximalStackDepth, st } }; Column col2 { - s.fontSizeFollowsEditor, - s.switchModeOnExit, - s.showQmlObjectTree, - s.stationaryEditorWhileStepping, - s.forceLoggingToConsole, - s.registerForPostMortem, + fontSizeFollowsEditor, + switchModeOnExit, + showQmlObjectTree, + stationaryEditorWhileStepping, + forceLoggingToConsole, + registerForPostMortem, st }; - Column { + return Column { Group { title("Behavior"), Row { col1, col2, st } }, - s.sourcePathMap, + sourcePathMap, st - }.attachTo(this); - } -}; + }; + }); -CommonOptionsPage::CommonOptionsPage() -{ - setId(DEBUGGER_COMMON_SETTINGS_ID); - setDisplayName(Tr::tr("General")); - setCategory(DEBUGGER_SETTINGS_CATEGORY); - setDisplayCategory(Tr::tr("Debugger")); - setCategoryIconPath(":/debugger/images/settingscategory_debugger.png"); - setWidgetCreator([] { return new CommonOptionsPageWidget; }); + readSettings(); } -QString CommonOptionsPage::msgSetBreakpointAtFunction(const char *function) +CommonSettings::~CommonSettings() +{ + delete registerForPostMortem; +} + +QString msgSetBreakpointAtFunction(const char *function) { return Tr::tr("Stop when %1() is called").arg(QLatin1String(function)); } -QString CommonOptionsPage::msgSetBreakpointAtFunctionToolTip(const char *function, - const QString &hint) +QString msgSetBreakpointAtFunctionToolTip(const char *function, const QString &hint) { QString result = "<html><head/><body>"; result += Tr::tr("Always adds a breakpoint on the <i>%1()</i> function.") @@ -103,22 +189,128 @@ QString CommonOptionsPage::msgSetBreakpointAtFunctionToolTip(const char *functio return result; } +// CommonSettingPage -/////////////////////////////////////////////////////////////////////// -// -// LocalsAndExpressionsOptionsPage -// -/////////////////////////////////////////////////////////////////////// - -class LocalsAndExpressionsOptionsPageWidget : public IOptionsPageWidget +class CommonSettingsPage final : public Core::IOptionsPage { public: - LocalsAndExpressionsOptionsPageWidget() + CommonSettingsPage() { - DebuggerSettings &s = *debuggerSettings(); - setOnApply([&s] { s.page4.apply(); s.page4.writeSettings(ICore::settings()); }); - setOnFinish([&s] { s.page4.finish(); }); + setId(DEBUGGER_COMMON_SETTINGS_ID); + setDisplayName(Tr::tr("General")); + setCategory(DEBUGGER_SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("Debugger")); + setCategoryIconPath(":/debugger/images/settingscategory_debugger.png"); + setSettingsProvider([] { return &commonSettings(); }); + } +}; +const CommonSettingsPage commonSettingsPage; + + +// LocalsAndExpressions + +LocalsAndExpressionsSettings &localsAndExpressionSettings() +{ + static LocalsAndExpressionsSettings settings; + return settings; +} + +LocalsAndExpressionsSettings::LocalsAndExpressionsSettings() +{ + setAutoApply(false); + + const Key debugModeGroup("DebugMode"); + + useDebuggingHelpers.setSettingsKey(debugModeGroup, "UseDebuggingHelper"); + useDebuggingHelpers.setDefaultValue(true); + useDebuggingHelpers.setLabelText(Tr::tr("Use Debugging Helpers")); + + useCodeModel.setSettingsKey(debugModeGroup, "UseCodeModel"); + useCodeModel.setDefaultValue(true); + useCodeModel.setLabelText(Tr::tr("Use code model")); + useCodeModel.setToolTip( + "<p>" + + Tr::tr("Selecting this causes the C++ Code Model being asked " + "for variable scope information. This might result in slightly faster " + "debugger operation but may fail for optimized code.")); + + showThreadNames.setSettingsKey(debugModeGroup, "ShowThreadNames"); + showThreadNames.setLabelText(Tr::tr("Display thread names")); + showThreadNames.setToolTip("<p>" + Tr::tr("Displays names of QThread based threads.")); + + showStdNamespace.setSettingsKey(debugModeGroup, "ShowStandardNamespace"); + showStdNamespace.setDefaultValue(true); + showStdNamespace.setDisplayName(Tr::tr("Show \"std::\" Namespace in Types")); + showStdNamespace.setLabelText(Tr::tr("Show \"std::\" namespace in types")); + showStdNamespace.setToolTip( + "<p>" + Tr::tr("Shows \"std::\" prefix for types from the standard library.")); + + showQtNamespace.setSettingsKey(debugModeGroup, "ShowQtNamespace"); + showQtNamespace.setDefaultValue(true); + showQtNamespace.setDisplayName(Tr::tr("Show Qt's Namespace in Types")); + showQtNamespace.setLabelText(Tr::tr("Show Qt's namespace in types")); + showQtNamespace.setToolTip("<p>" + + Tr::tr("Shows Qt namespace prefix for Qt types. This is only " + "relevant if Qt was configured with \"-qtnamespace\".")); + + showQObjectNames.setSettingsKey(debugModeGroup, "ShowQObjectNames2"); + showQObjectNames.setDefaultValue(true); + showQObjectNames.setDisplayName(Tr::tr("Show QObject names if available")); + showQObjectNames.setLabelText(Tr::tr("Show QObject names if available")); + showQObjectNames.setToolTip( + "<p>" + + Tr::tr("Displays the objectName property of QObject based items. " + "Note that this can negatively impact debugger performance " + "even if no QObjects are present.")); + + extraDumperCommands.setSettingsKey(debugModeGroup, "GdbCustomDumperCommands"); + extraDumperCommands.setDisplayStyle(StringAspect::TextEditDisplay); + extraDumperCommands.setUseGlobalMacroExpander(); + extraDumperCommands.setToolTip("<html><head/><body><p>" + + Tr::tr("Python commands entered here will be executed after built-in " + "debugging helpers have been loaded and fully initialized. You can " + "load additional debugging helpers or modify existing ones here.") + + "</p></body></html>"); + + extraDumperFile.setSettingsKey(debugModeGroup, "ExtraDumperFile"); + extraDumperFile.setDisplayName(Tr::tr("Extra Debugging Helpers")); + // Label text is intentional empty in the GUI. + extraDumperFile.setToolTip(Tr::tr("Path to a Python file containing additional data dumpers.")); + + displayStringLimit.setSettingsKey(debugModeGroup, "DisplayStringLimit"); + displayStringLimit.setDefaultValue(300); + displayStringLimit.setSpecialValueText(Tr::tr("<unlimited>")); + displayStringLimit.setRange(20, 10000); + displayStringLimit.setSingleStep(10); + displayStringLimit.setLabelText(Tr::tr("Display string length:")); + displayStringLimit.setToolTip( + "<p>" + + Tr::tr("The maximum length of string entries in the " + "Locals and Expressions views. Longer than that are cut off " + "and displayed with an ellipsis attached.")); + + maximalStringLength.setSettingsKey(debugModeGroup, "MaximalStringLength"); + maximalStringLength.setDefaultValue(10000); + maximalStringLength.setSpecialValueText(Tr::tr("<unlimited>")); + maximalStringLength.setRange(20, 10000000); + maximalStringLength.setSingleStep(20); + maximalStringLength.setLabelText(Tr::tr("Maximum string length:")); + maximalStringLength.setToolTip( + "<p>" + + Tr::tr("The maximum length for strings in separated windows. " + "Longer strings are cut off and displayed with an ellipsis attached.")); + + defaultArraySize.setSettingsKey(debugModeGroup, "DefaultArraySize"); + defaultArraySize.setDefaultValue(100); + defaultArraySize.setRange(10, 1000000000); + defaultArraySize.setSingleStep(100); + defaultArraySize.setLabelText(Tr::tr("Default array size:")); + defaultArraySize.setToolTip("<p>" + + Tr::tr("The number of array elements requested when expanding " + "entries in the Locals and Expressions views.")); + + setLayouter([this] { auto label = new QLabel; //(useHelperGroup); label->setTextFormat(Qt::AutoText); label->setWordWrap(true); @@ -131,9 +323,9 @@ public: using namespace Layouting; Column left { label, - s.useCodeModel, - s.showThreadNames, - Group { title(Tr::tr("Extra Debugging Helper")), Column { s.extraDumperFile } } + useCodeModel, + showThreadNames, + Group { title(Tr::tr("Extra Debugging Helper")), Column { extraDumperFile } } }; Group useHelper { @@ -141,38 +333,46 @@ public: left, Group { title(Tr::tr("Debugging Helper Customization")), - Column { s.extraDumperCommands } + Column { extraDumperCommands } } } }; Grid limits { - s.maximalStringLength, br, - s.displayStringLimit, br, - s.defaultArraySize + maximalStringLength, br, + displayStringLimit, br, + defaultArraySize }; - Column { - s.useDebuggingHelpers, + return Column { + useDebuggingHelpers, useHelper, Space(10), - s.showStdNamespace, - s.showQtNamespace, - s.showQObjectNames, + showStdNamespace, + showQtNamespace, + showQObjectNames, Space(10), Row { limits, st }, st - }.attachTo(this); + }; + }); + + readSettings(); +} + +class LocalsAndExpressionsSettingsPage final : public Core::IOptionsPage +{ +public: + LocalsAndExpressionsSettingsPage() + { + setId("Z.Debugger.LocalsAndExpressions"); + //: '&&' will appear as one (one is marking keyboard shortcut) + setDisplayName(Tr::tr("Locals && Expressions")); + setCategory(DEBUGGER_SETTINGS_CATEGORY); + setSettingsProvider([] { return &localsAndExpressionSettings(); }); } }; -LocalsAndExpressionsOptionsPage::LocalsAndExpressionsOptionsPage() -{ - setId("Z.Debugger.LocalsAndExpressions"); - //: '&&' will appear as one (one is marking keyboard shortcut) - setDisplayName(Tr::tr("Locals && Expressions")); - setCategory(DEBUGGER_SETTINGS_CATEGORY); - setWidgetCreator([] { return new LocalsAndExpressionsOptionsPageWidget; }); -} +const LocalsAndExpressionsSettingsPage localsAndExpressionSettingsPage; } // Debugger::Internal diff --git a/src/plugins/debugger/commonoptionspage.h b/src/plugins/debugger/commonoptionspage.h index 33349605115..5bf41af7a41 100644 --- a/src/plugins/debugger/commonoptionspage.h +++ b/src/plugins/debugger/commonoptionspage.h @@ -3,24 +3,95 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace Debugger::Internal { -class CommonOptionsPage final : public Core::IOptionsPage +class SourcePathMapAspectPrivate; + +// Entries starting with '(' are considered regular expressions in the ElfReader. +// This is useful when there are multiple build machines with different +// path, and the user would like to match anything up to some known +// directory to his local project. +// Syntax: (/home/.*)/KnownSubdir -> /home/my/project +using SourcePathMap = QMap<QString, QString>; + +class SourcePathMapAspect : public Utils::TypedAspect<SourcePathMap> { public: - CommonOptionsPage(); + explicit SourcePathMapAspect(Utils::AspectContainer *container); + ~SourcePathMapAspect() override; - static QString msgSetBreakpointAtFunction(const char *function); - static QString msgSetBreakpointAtFunctionToolTip(const char *function, - const QString &hint = {}); + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; + + void addToLayout(Layouting::LayoutItem &parent) override; + + void readSettings() override; + void writeSettings() const override; + + bool isDirty() override; + +private: + bool guiToBuffer() override; + void bufferToGui() override; + + SourcePathMapAspectPrivate *d = nullptr; }; -class LocalsAndExpressionsOptionsPage final : public Core::IOptionsPage +class CommonSettings final : public Utils::AspectContainer { public: - LocalsAndExpressionsOptionsPage(); + CommonSettings(); + ~CommonSettings(); + + Utils::BoolAspect useAlternatingRowColors{this}; + Utils::BoolAspect useAnnotationsInMainEditor{this}; + Utils::BoolAspect useToolTipsInMainEditor{this}; + Utils::BoolAspect closeSourceBuffersOnExit{this}; + Utils::BoolAspect closeMemoryBuffersOnExit{this}; + Utils::BoolAspect raiseOnInterrupt{this}; + Utils::BoolAspect breakpointsFullPathByDefault{this}; + Utils::BoolAspect warnOnReleaseBuilds{this}; + Utils::IntegerAspect maximalStackDepth{this}; + + Utils::BoolAspect fontSizeFollowsEditor{this}; + Utils::BoolAspect switchModeOnExit{this}; + Utils::BoolAspect showQmlObjectTree{this}; + Utils::BoolAspect stationaryEditorWhileStepping{this}; + Utils::BoolAspect forceLoggingToConsole{this}; + + SourcePathMapAspect sourcePathMap{this}; + + Utils::BoolAspect *registerForPostMortem = nullptr; }; +CommonSettings &commonSettings(); + + +class LocalsAndExpressionsSettings final : public Utils::AspectContainer +{ +public: + LocalsAndExpressionsSettings(); + + Utils::BoolAspect useDebuggingHelpers{this}; + Utils::BoolAspect useCodeModel{this}; + Utils::BoolAspect showThreadNames{this}; + Utils::FilePathAspect extraDumperFile{this}; // For loading a file. Recommended. + Utils::StringAspect extraDumperCommands{this}; // To modify an existing setup. + + Utils::BoolAspect showStdNamespace{this}; + Utils::BoolAspect showQtNamespace{this}; + Utils::BoolAspect showQObjectNames{this}; + + Utils::IntegerAspect maximalStringLength{this}; + Utils::IntegerAspect displayStringLimit{this}; + Utils::IntegerAspect defaultArraySize{this}; +}; + +LocalsAndExpressionsSettings &localsAndExpressionSettings(); + + } // Debugger::Internal + +Q_DECLARE_METATYPE(Debugger::Internal::SourcePathMap) diff --git a/src/plugins/debugger/console/console.cpp b/src/plugins/debugger/console/console.cpp index b1c3767f0e9..b1ee0dac83e 100644 --- a/src/plugins/debugger/console/console.cpp +++ b/src/plugins/debugger/console/console.cpp @@ -37,6 +37,10 @@ namespace Debugger::Internal { Console::Console() { + setId("QMLDebuggerConsole"); + setDisplayName(Tr::tr("QML Debugger Console")); + setPriorityInStatusBar(-40); + m_consoleItemModel = new ConsoleItemModel(this); m_consoleWidget = new QWidget; @@ -87,8 +91,8 @@ Console::Console() m_showDebug.setToolTip(Tr::tr("Show debug, log, and info messages.")); m_showDebug.setValue(true); m_showDebug.action()->setIcon(Utils::Icons::INFO_TOOLBAR.icon()); - connect(&m_showDebug, &Utils::BoolAspect::valueChanged, - proxyModel, &ConsoleProxyModel::setShowLogs); + connect(&m_showDebug, &Utils::BoolAspect::changed, + proxyModel, [this, proxyModel] { proxyModel->setShowLogs(m_showDebug()); }); m_showDebugButton->setDefaultAction(m_showDebug.action()); m_showWarningButton = new QToolButton(m_consoleWidget); @@ -100,7 +104,7 @@ Console::Console() m_showWarning.setValue(true); m_showWarning.action()->setIcon(Utils::Icons::WARNING_TOOLBAR.icon()); connect(m_showWarning.action(), &QAction::toggled, - proxyModel, &ConsoleProxyModel::setShowWarnings); + proxyModel, [this, proxyModel] { proxyModel->setShowWarnings(m_showWarning()); }); m_showWarningButton->setDefaultAction(m_showWarning.action()); m_showErrorButton = new QToolButton(m_consoleWidget); @@ -112,7 +116,7 @@ Console::Console() m_showError.setValue(true); m_showError.action()->setIcon(Utils::Icons::CRITICAL_TOOLBAR.icon()); connect(m_showError.action(), &QAction::toggled, - proxyModel, &ConsoleProxyModel::setShowErrors); + proxyModel, [this, proxyModel] { proxyModel->setShowErrors(m_showError()); }); m_showErrorButton->setDefaultAction(m_showError.action()); m_spacer = new QWidget(m_consoleWidget); @@ -142,16 +146,6 @@ QList<QWidget *> Console::toolBarWidgets() const m_spacer, m_statusLabel}; } -QString Console::displayName() const -{ - return Tr::tr("QML Debugger Console"); -} - -int Console::priorityInStatusBar() const -{ - return 20; -} - void Console::clearContents() { m_consoleItemModel->clear(); @@ -202,10 +196,9 @@ bool Console::canNavigate() const void Console::readSettings() { - QSettings *settings = Core::ICore::settings(); - m_showDebug.readSettings(settings); - m_showWarning.readSettings(settings); - m_showError.readSettings(settings); + m_showDebug.readSettings(); + m_showWarning.readSettings(); + m_showError.readSettings(); } void Console::setContext(const QString &context) @@ -215,10 +208,9 @@ void Console::setContext(const QString &context) void Console::writeSettings() const { - QSettings *settings = Core::ICore::settings(); - m_showDebug.writeSettings(settings); - m_showWarning.writeSettings(settings); - m_showError.writeSettings(settings); + m_showDebug.writeSettings(); + m_showWarning.writeSettings(); + m_showError.writeSettings(); } void Console::setScriptEvaluator(const ScriptEvaluator &evaluator) diff --git a/src/plugins/debugger/console/console.h b/src/plugins/debugger/console/console.h index 8e6791ee8ce..abf3c267c00 100644 --- a/src/plugins/debugger/console/console.h +++ b/src/plugins/debugger/console/console.h @@ -34,8 +34,6 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; bool canFocus() const override; bool hasFocus() const override; diff --git a/src/plugins/debugger/console/consoleview.cpp b/src/plugins/debugger/console/consoleview.cpp index 2ee80a03d5c..d42f4ae9185 100644 --- a/src/plugins/debugger/console/consoleview.cpp +++ b/src/plugins/debugger/console/consoleview.cpp @@ -32,6 +32,7 @@ namespace Internal { ConsoleView::ConsoleView(ConsoleItemModel *model, QWidget *parent) : Utils::TreeView(parent), m_model(model) { + setUniformRowHeights(false); setFrameStyle(QFrame::NoFrame); setHeaderHidden(true); setRootIsDecorated(false); diff --git a/src/plugins/debugger/dap/cmakedapengine.cpp b/src/plugins/debugger/dap/cmakedapengine.cpp new file mode 100644 index 00000000000..2c6400b0feb --- /dev/null +++ b/src/plugins/debugger/dap/cmakedapengine.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "cmakedapengine.h" + +#include "dapclient.h" + +#include <coreplugin/messagemanager.h> + +#include <debugger/debuggermainwindow.h> + +#include <utils/temporarydirectory.h> + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projecttree.h> + +#include <QDebug> +#include <QLocalSocket> +#include <QLoggingCategory> +#include <QTimer> + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +class LocalSocketDataProvider : public IDataProvider +{ +public: + LocalSocketDataProvider(const QString &socketName, QObject *parent = nullptr) + : IDataProvider(parent) + , m_socketName(socketName) + { + connect(&m_socket, &QLocalSocket::connected, this, &IDataProvider::started); + connect(&m_socket, &QLocalSocket::disconnected, this, &IDataProvider::done); + connect(&m_socket, &QLocalSocket::readyRead, this, &IDataProvider::readyReadStandardOutput); + connect(&m_socket, + &QLocalSocket::errorOccurred, + this, + &IDataProvider::readyReadStandardError); + } + + ~LocalSocketDataProvider() { m_socket.disconnectFromServer(); } + + void start() override { m_socket.connectToServer(m_socketName, QIODevice::ReadWrite); } + + bool isRunning() const override { return m_socket.isOpen(); } + void writeRaw(const QByteArray &data) override + { + if (m_socket.isOpen()) + m_socket.write(data); + } + void kill() override + { + if (m_socket.isOpen()) + m_socket.disconnectFromServer(); + else { + m_socket.abort(); + emit done(); + } + } + QByteArray readAllStandardOutput() override { return m_socket.readAll(); } + QString readAllStandardError() override { return QString(); } + int exitCode() const override { return 0; } + QString executable() const override { return m_socket.serverName(); } + + QProcess::ExitStatus exitStatus() const override { return QProcess::NormalExit; } + QProcess::ProcessError error() const override { return QProcess::UnknownError; } + Utils::ProcessResult result() const override { return ProcessResult::FinishedWithSuccess; } + QString exitMessage() const override { return QString(); }; + +private: + QLocalSocket m_socket; + const QString m_socketName; +}; + +class CMakeDapClient : public DapClient +{ +public: + CMakeDapClient(IDataProvider *provider, QObject *parent = nullptr) + : DapClient(provider, parent) + {} + + void sendInitialize() override + { + postRequest("initialize", + QJsonObject{{"clientID", "QtCreator"}, + {"clientName", "QtCreator"}, + {"adapterID", "cmake"}, + {"pathFormat", "path"}}); + } + +private: + const QLoggingCategory &logCategory() override { + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.cmake", + QtWarningMsg); + return logCategory; + } +}; + +CMakeDapEngine::CMakeDapEngine() + : DapEngine() +{ + setObjectName("CmakeDapEngine"); + setDebuggerName("CMake"); + setDebuggerType("DAP"); +} + +void CMakeDapEngine::setupEngine() +{ + QTC_ASSERT(state() == EngineSetupRequested, qCDebug(logCategory()) << state()); + + qCDebug(logCategory()) << "build system name" + << ProjectExplorer::ProjectTree::currentBuildSystem()->name(); + + IDataProvider *dataProvider; + if (TemporaryDirectory::masterDirectoryFilePath().osType() == Utils::OsType::OsTypeWindows) { + dataProvider = new LocalSocketDataProvider("\\\\.\\pipe\\cmake-dap", this); + } else { + dataProvider = new LocalSocketDataProvider(TemporaryDirectory::masterDirectoryPath() + + "/cmake-dap.sock", + this); + } + m_dapClient = new CMakeDapClient(dataProvider, this); + connectDataGeneratorSignals(); + + connect(ProjectExplorer::ProjectTree::currentBuildSystem(), + &ProjectExplorer::BuildSystem::debuggingStarted, + this, + [this] { m_dapClient->dataProvider()->start(); }); + + ProjectExplorer::ProjectTree::currentBuildSystem()->requestDebugging(); + + QTimer::singleShot(5000, this, [this] { + if (!m_dapClient->dataProvider()->isRunning()) { + m_dapClient->dataProvider()->kill(); + MessageManager::writeDisrupting( + "CMake server is not running. Please check that your CMake is 3.27 or higher."); + return; + } + }); +} + +bool CMakeDapEngine::hasCapability(unsigned cap) const +{ + return cap & (ReloadModuleCapability + | BreakConditionCapability + | ShowModuleSymbolsCapability + /*| AddWatcherCapability*/ // disable while the #25282 bug is not fixed + /*| RunToLineCapability*/); // disable while the #25176 bug is not fixed +} + +void CMakeDapEngine::insertBreakpoint(const Breakpoint &bp) +{ + DapEngine::insertBreakpoint(bp); + notifyBreakpointInsertOk(bp); // Needed for CMake support issue:25176 +} + +void CMakeDapEngine::removeBreakpoint(const Breakpoint &bp) +{ + DapEngine::removeBreakpoint(bp); + notifyBreakpointRemoveOk(bp); // Needed for CMake support issue:25176 +} + +void CMakeDapEngine::updateBreakpoint(const Breakpoint &bp) +{ + DapEngine::updateBreakpoint(bp); + + /* Needed for CMake support issue:25176 */ + BreakpointParameters parameters = bp->requestedParameters(); + if (parameters.enabled != bp->isEnabled()) { + parameters.pending = false; + bp->setParameters(parameters); + } + notifyBreakpointChangeOk(bp); + /* Needed for CMake support issue:25176 */ +} + +const QLoggingCategory &CMakeDapEngine::logCategory() +{ + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.cmake", + QtWarningMsg); + return logCategory; +} + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/cmakedapengine.h b/src/plugins/debugger/dap/cmakedapengine.h new file mode 100644 index 00000000000..c7d02e3b858 --- /dev/null +++ b/src/plugins/debugger/dap/cmakedapengine.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "dapengine.h" + +namespace Debugger::Internal { + +class CMakeDapEngine : public DapEngine +{ +public: + CMakeDapEngine(); + +private: + void setupEngine() override; + + /* Needed for CMake support issue:25176 */ + void insertBreakpoint(const Breakpoint &bp) override; + void updateBreakpoint(const Breakpoint &bp) override; + void removeBreakpoint(const Breakpoint &bp) override; + /* Needed for CMake support issue:25176 */ + + bool hasCapability(unsigned cap) const override; + + const QLoggingCategory &logCategory() override; +}; + +} // Debugger::Internal diff --git a/src/plugins/debugger/dap/dapclient.cpp b/src/plugins/debugger/dap/dapclient.cpp new file mode 100644 index 00000000000..f63d45b2bb9 --- /dev/null +++ b/src/plugins/debugger/dap/dapclient.cpp @@ -0,0 +1,254 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "dapclient.h" +#include "qjsonarray.h" + +#include <utils/process.h> + +#include <QDebug> +#include <QJsonDocument> +#include <QLoggingCategory> + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +DapClient::DapClient(IDataProvider *dataProvider, QObject *parent) + : QObject(parent) + , m_dataProvider(dataProvider) +{ + connect(m_dataProvider, + &IDataProvider::readyReadStandardOutput, + this, + &DapClient::readOutput); + connect(m_dataProvider, + &IDataProvider::readyReadStandardError, + this, + &DapClient::readyReadStandardError); + + connect(m_dataProvider, &IDataProvider::done, this, &DapClient::done); + connect(m_dataProvider, &IDataProvider::started, this, &DapClient::started); +} + +DapClient::~DapClient() = default; + + +void DapClient::postRequest(const QString &command, const QJsonObject &arguments) +{ + static int seq = 1; + + QJsonObject ob = { + {"command", command}, + {"type", "request"}, + {"seq", seq++}, + {"arguments", arguments} + }; + + const QByteArray data = QJsonDocument(ob).toJson(QJsonDocument::Compact); + const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data; + qCDebug(logCategory()) << msg; + + m_dataProvider->writeRaw(msg); +} + +void DapClient::sendInitialize() +{ + postRequest("initialize", QJsonObject{{"clientID", "QtCreator"}, {"clientName", "QtCreator"}}); +} + +void DapClient::sendLaunch(const Utils::FilePath &executable) +{ + postRequest("launch", + QJsonObject{{"noDebug", false}, {"program", executable.path()}, {"__restart", ""}}); +} + +void DapClient::sendAttach() +{ + postRequest("attach", + QJsonObject{{"__restart", ""}}); +} + +void DapClient::sendConfigurationDone() +{ + postRequest("configurationDone"); +} + +void DapClient::sendDisconnect() +{ + postRequest("disconnect", QJsonObject{{"restart", false}, {"terminateDebuggee", true}}); +} + +void DapClient::sendTerminate() +{ + postRequest("terminate", QJsonObject{{"restart", false}}); +} + +void DapClient::sendContinue(int threadId) +{ + QTC_ASSERT(threadId != -1, return); + postRequest("continue", QJsonObject{{"threadId", threadId}}); +} + +void DapClient::sendPause() +{ + postRequest("pause"); +} + +void DapClient::sendStepIn(int threadId) +{ + QTC_ASSERT(threadId != -1, return); + postRequest("stepIn", QJsonObject{{"threadId", threadId}}); +} + +void DapClient::sendStepOut(int threadId) +{ + QTC_ASSERT(threadId != -1, return); + postRequest("stepOut", QJsonObject{{"threadId", threadId}}); +} + +void DapClient::sendStepOver(int threadId) +{ + QTC_ASSERT(threadId != -1, return); + postRequest("next", QJsonObject{{"threadId", threadId}}); +} + +void DapClient::evaluateVariable(const QString &expression, int frameId) +{ + postRequest("evaluate", + QJsonObject{{"expression", expression}, + {"frameId", frameId}, + {"context", "variables"}}); +} + +void DapClient::stackTrace(int threadId) +{ + QTC_ASSERT(threadId != -1, return); + postRequest("stackTrace", + QJsonObject{{"threadId", threadId}, {"startFrame", 0}, {"levels", 10}}); +} + +void DapClient::scopes(int frameId) +{ + postRequest("scopes", QJsonObject{{"frameId", frameId}}); +} + +void DapClient::threads() +{ + postRequest("threads"); +} + +void DapClient::variables(int variablesReference) +{ + postRequest("variables", QJsonObject{{"variablesReference", variablesReference}}); +} + +void DapClient::setBreakpoints(const QJsonArray &breakpoints, const FilePath &fileName) +{ + postRequest("setBreakpoints", + QJsonObject{{"source", QJsonObject{{"path", fileName.path()}}}, + {"breakpoints", breakpoints}}); +} + +void DapClient::readOutput() +{ + m_inbuffer.append(m_dataProvider->readAllStandardOutput()); + + qCDebug(logCategory()) << m_inbuffer; + + while (true) { + // Something like + // Content-Length: 128\r\n + // {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n + // FIXME: There coud be more than one header line. + int pos1 = m_inbuffer.indexOf("Content-Length:"); + if (pos1 == -1) + break; + pos1 += 15; + + int pos2 = m_inbuffer.indexOf('\n', pos1); + if (pos2 == -1) + break; + + const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt(); + if (len < 4) + break; + + pos2 += 3; // Skip \r\n\r + + if (pos2 + len > m_inbuffer.size()) + break; + + QJsonParseError error; + const QJsonDocument doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error); + + m_inbuffer = m_inbuffer.mid(pos2 + len); + + emitSignals(doc); + } +} + +void DapClient::emitSignals(const QJsonDocument &doc) +{ + QJsonObject ob = doc.object(); + const QJsonValue t = ob.value("type"); + const QString type = t.toString(); + + qCDebug(logCategory()) << "dap response" << ob; + + if (type == "response") { + DapResponseType type = DapResponseType::Unknown; + const QString command = ob.value("command").toString(); + if (command == "initialize") { + type = DapResponseType::Initialize; + } else if (command == "configurationDone") { + type = DapResponseType::ConfigurationDone; + } else if (command == "continue") { + type = DapResponseType::Continue; + } else if (command == "stackTrace") { + type = DapResponseType::StackTrace; + } else if (command == "scopes") { + type = DapResponseType::Scopes; + } else if (command == "variables") { + type = DapResponseType::Variables; + } else if (command == "stepIn") { + type = DapResponseType::StepIn; + } else if (command == "stepOut") { + type = DapResponseType::StepOut; + } else if (command == "next") { + type = DapResponseType::StepOver; + } else if (command == "threads") { + type = DapResponseType::DapThreads; + } else if (command == "pause") { + type = DapResponseType::Pause; + } else if (command == "evaluate") { + type = DapResponseType::Evaluate; + } + emit responseReady(type, ob); + return; + } + + if (type == "event") { + const QString event = ob.value("event").toString(); + + DapEventType type = DapEventType::Unknown; + if (event == "initialized") { + type = DapEventType::Initialized; + } else if (event == "stopped") { + type = DapEventType::Stopped; + } else if (event == "thread") { + type = DapEventType::DapThread; + } else if (event == "output") { + type = DapEventType::Output; + } else if (event == "breakpoint") { + type = DapEventType::DapBreakpoint; + } else if (event == "exited") { + type = DapEventType::Exited; + } + emit eventReady(type, ob); + } +} + +} // namespace Debugger::Internal + diff --git a/src/plugins/debugger/dap/dapclient.h b/src/plugins/debugger/dap/dapclient.h new file mode 100644 index 00000000000..d2a6f52bb22 --- /dev/null +++ b/src/plugins/debugger/dap/dapclient.h @@ -0,0 +1,129 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <debugger/debuggerengine.h> + +#include <utils/process.h> + +#include <QLoggingCategory> + +namespace Debugger::Internal { + +class IDataProvider : public QObject +{ + Q_OBJECT +public: + IDataProvider(QObject *parent = nullptr) : QObject(parent) {} + ~IDataProvider() override = default; + virtual void start() = 0; + virtual bool isRunning() const = 0; + virtual void writeRaw(const QByteArray &input) = 0; + virtual void kill() = 0; + virtual QByteArray readAllStandardOutput() = 0; + virtual QString readAllStandardError() = 0; + virtual int exitCode() const = 0; + virtual QString executable() const = 0; + + virtual QProcess::ExitStatus exitStatus() const = 0; + virtual QProcess::ProcessError error() const = 0; + virtual Utils::ProcessResult result() const = 0; + virtual QString exitMessage() const = 0; + +signals: + void started(); + void done(); + void readyReadStandardOutput(); + void readyReadStandardError(); +}; + +enum class DapResponseType +{ + Initialize, + ConfigurationDone, + Continue, + StackTrace, + Scopes, + DapThreads, + Variables, + StepIn, + StepOut, + StepOver, + Pause, + Evaluate, + Unknown +}; + +enum class DapEventType +{ + Initialized, + Stopped, + Exited, + DapThread, + Output, + DapBreakpoint, + Unknown +}; + +class DapClient : public QObject +{ + Q_OBJECT + +public: + DapClient(IDataProvider *dataProvider, QObject *parent = nullptr); + ~DapClient() override; + + IDataProvider *dataProvider() const { return m_dataProvider; } + + void postRequest(const QString &command, const QJsonObject &arguments = {}); + + virtual void sendInitialize(); + + void sendLaunch(const Utils::FilePath &executable); + void sendAttach(); + void sendConfigurationDone(); + + void sendDisconnect(); + void sendTerminate(); + + void sendPause(); + void sendContinue(int threadId); + + void sendStepIn(int threadId); + void sendStepOut(int threadId); + void sendStepOver(int threadId); + + void evaluateVariable(const QString &expression, int frameId); + + void stackTrace(int threadId); + void scopes(int frameId); + void threads(); + void variables(int variablesReference); + void setBreakpoints(const QJsonArray &breakpoints, const Utils::FilePath &fileName); + + void emitSignals(const QJsonDocument &doc); + +signals: + void started(); + void done(); + void readyReadStandardError(); + + void responseReady(DapResponseType type, const QJsonObject &response); + void eventReady(DapEventType type, const QJsonObject &response); + +private: + void readOutput(); + + virtual const QLoggingCategory &logCategory() + { + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine", + QtWarningMsg); + return logCategory; + } + + IDataProvider *m_dataProvider = nullptr; + QByteArray m_inbuffer; +}; + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index 3f31129a5a8..b39e664f211 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -3,12 +3,20 @@ #include "dapengine.h" +#include "cmakedapengine.h" +#include "dapclient.h" +#include "gdbdapengine.h" +#include "pydapengine.h" + #include <debugger/breakhandler.h> #include <debugger/debuggeractions.h> #include <debugger/debuggercore.h> #include <debugger/debuggerdialogs.h> +#include <debugger/debuggerinternalconstants.h> +#include <debugger/debuggermainwindow.h> #include <debugger/debuggerplugin.h> #include <debugger/debuggerprotocol.h> +#include <debugger/debuggerruncontrol.h> #include <debugger/debuggertooltipmanager.h> #include <debugger/debuggertr.h> #include <debugger/moduleshandler.h> @@ -20,39 +28,117 @@ #include <debugger/watchhandler.h> #include <debugger/watchutils.h> +#include <extensionsystem/pluginmanager.h> + #include <utils/algorithm.h> #include <utils/environment.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <utils/temporarydirectory.h> -#include <coreplugin/idocument.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/icore.h> +#include <coreplugin/idocument.h> #include <coreplugin/messagebox.h> +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projecttree.h> + #include <QDateTime> #include <QDebug> #include <QDir> #include <QFileInfo> -#include <QTimer> -#include <QVariant> #include <QJsonArray> #include <QJsonDocument> +#include <QLocalSocket> #include <QThread> +#include <QTimer> +#include <QVariant> using namespace Core; using namespace Utils; namespace Debugger::Internal { -DapEngine::DapEngine() +VariablesHandler::VariablesHandler(DapEngine *dapEngine) + : m_dapEngine(dapEngine) +{} + +void VariablesHandler::addVariable(const QString &iname, int variablesReference) { - setObjectName("DapEngine"); - setDebuggerName("DAP"); + VariableItem varItem = {iname, variablesReference}; + bool wasEmpty = m_queue.empty(); + bool inserted = false; + + for (auto i = m_queue.begin(); i != m_queue.end(); ++i) { + if (i->iname > iname) { + m_queue.insert(i, varItem); + inserted = true; + break; + } + } + if (!inserted) + m_queue.push_back(varItem); + + if (wasEmpty) { + startHandling(); + } } +void VariablesHandler::handleNext() +{ + if (m_queue.empty()) + return; + + m_queue.pop_front(); + startHandling(); +} + +void VariablesHandler::startHandling() +{ + if (m_queue.empty()) + return; + + m_currentVarItem = m_queue.front(); + + WatchItem *watchItem = m_dapEngine->watchHandler()->findItem(m_currentVarItem.iname); + int variablesReference = m_currentVarItem.variablesReference; + + if (variablesReference == -1 && watchItem && watchItem->iname.startsWith("watch.") + && watchItem->iname.split('.').size() == 2) { + watchItem->removeChildren(); + m_dapEngine->dapClient()->evaluateVariable(watchItem->name, + m_dapEngine->currentStackFrameId()); + return; + } + + if (variablesReference == -1) { + if (watchItem) { + variablesReference = watchItem->variablesReference; + } else { + handleNext(); + return; + } + } + + if (variablesReference == 0) { + handleNext(); + return; + } + + m_dapEngine->dapClient()->variables(variablesReference); +} + +DapEngine::DapEngine() + : m_variablesHandler(std::make_unique<VariablesHandler>(this)) +{} + void DapEngine::executeDebuggerCommand(const QString &/*command*/) { - QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); + QTC_ASSERT(state() == InferiorStopOk, qCDebug(logCategory()) << state()); // if (state() == DebuggerNotReady) { // showMessage("DAP PROCESS NOT RUNNING, PLAIN CMD IGNORED: " + command); // return; @@ -61,28 +147,13 @@ void DapEngine::executeDebuggerCommand(const QString &/*command*/) // postDirectCommand(command); } -void DapEngine::postDirectCommand(const QJsonObject &ob) -{ - static int seq = 1; - QJsonObject obseq = ob; - obseq.insert("seq", seq++); - - const QByteArray data = QJsonDocument(obseq).toJson(QJsonDocument::Compact); - const QByteArray msg = "Content-Length: " + QByteArray::number(data.size()) + "\r\n\r\n" + data; - qDebug() << msg; - - m_proc.writeRaw(msg); - - showMessage(QString::fromUtf8(msg), LogInput); -} - void DapEngine::runCommand(const DebuggerCommand &cmd) { if (state() == EngineSetupRequested) { // cmd has been triggered too early showMessage("IGNORED COMMAND: " + cmd.function); return; } - QTC_ASSERT(m_proc.isRunning(), notifyEngineIll()); + QTC_ASSERT(m_dapClient->dataProvider()->isRunning(), notifyEngineIll()); // postDirectCommand(cmd.args.toObject()); // const QByteArray data = QJsonDocument(cmd.args.toObject()).toJson(QJsonDocument::Compact); // m_proc.writeRaw("Content-Length: " + QByteArray::number(data.size()) + "\r\n" + data + "\r\n"); @@ -92,151 +163,111 @@ void DapEngine::runCommand(const DebuggerCommand &cmd) void DapEngine::shutdownInferior() { - QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state()); - postDirectCommand({{"command", "terminate"}, - {"type", "request"}}); + QTC_ASSERT(state() == InferiorShutdownRequested, qCDebug(logCategory()) << state()); - qDebug() << "DapEngine::shutdownInferior()"; + m_dapClient->sendDisconnect(); + + qCDebug(logCategory()) << "DapEngine::shutdownInferior()"; notifyInferiorShutdownFinished(); } void DapEngine::shutdownEngine() { - QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state()); + QTC_ASSERT(state() == EngineShutdownRequested, qCDebug(logCategory()) << state()); - qDebug() << "DapEngine::shutdownEngine()"; - m_proc.kill(); + m_dapClient->sendTerminate(); + + qCDebug(logCategory()) << "DapEngine::shutdownEngine()"; + m_dapClient->dataProvider()->kill(); } -void DapEngine::setupEngine() -{ - QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); - - connect(&m_proc, &Process::started, this, &DapEngine::handleDabStarted); - connect(&m_proc, &Process::done, this, &DapEngine::handleDapDone); - connect(&m_proc, &Process::readyReadStandardOutput, this, &DapEngine::readDapStandardOutput); - connect(&m_proc, &Process::readyReadStandardError, this, &DapEngine::readDapStandardError); - - const DebuggerRunParameters &rp = runParameters(); - const CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}}; - showMessage("STARTING " + cmd.toUserOutput()); - m_proc.setProcessMode(ProcessMode::Writer); - m_proc.setEnvironment(rp.debugger.environment); - m_proc.setCommand(cmd); - m_proc.start(); - notifyEngineRunAndInferiorRunOk(); -} - -// From the docs: -// The sequence of events/requests is as follows: -// * adapters sends initialized event (after the initialize request has returned) -// * client sends zero or more setBreakpoints requests -// * client sends one setFunctionBreakpoints request -// (if corresponding capability supportsFunctionBreakpoints is true) -// * client sends a setExceptionBreakpoints request if one or more exceptionBreakpointFilters -// have been defined (or if supportsConfigurationDoneRequest is not true) -// * client sends other future configuration requests -// * client sends one configurationDone request to indicate the end of the configuration. - -void DapEngine::handleDabStarted() +void DapEngine::handleDapStarted() { notifyEngineSetupOk(); - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); -// CHECK_STATE(EngineRunRequested); + m_dapClient->sendInitialize(); - postDirectCommand({ - {"command", "initialize"}, - {"type", "request"}, - {"arguments", QJsonObject { - {"clientID", "QtCreator"}, // The ID of the client using this adapter. - {"clientName", "QtCreator"} // The human-readable name of the client using this adapter. - }} - }); - - qDebug() << "handleDabStarted"; + qCDebug(logCategory()) << "handleDapStarted"; } -void DapEngine::handleDabConfigurationDone() +void DapEngine::handleDapInitialize() { - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); - // CHECK_STATE(EngineRunRequested); + m_dapClient->sendLaunch(runParameters().inferior.command.executable()); - postDirectCommand({{"command", "configurationDone"}, {"type", "request"}}); - - qDebug() << "handleDabConfigurationDone"; + qCDebug(logCategory()) << "handleDapLaunch"; } - -void DapEngine::handleDabLaunch() +void DapEngine::handleDapEventInitialized() { - QTC_ASSERT(state() == EngineRunRequested, qDebug() << state()); + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); - // CHECK_STATE(EngineRunRequested); + m_dapClient->sendConfigurationDone(); - postDirectCommand( - {{"command", "launch"}, - {"type", "request"}, -// {"program", runParameters().inferior.command.executable().path()}, - {"arguments", - QJsonObject{ - {"noDebug", false}, - {"program", runParameters().inferior.command.executable().path()}, - {"__restart", ""} - }}}); - qDebug() << "handleDabLaunch"; + qCDebug(logCategory()) << "handleDapConfigurationDone"; +} + +void DapEngine::handleDapConfigurationDone() +{ + notifyEngineRunAndInferiorRunOk(); } void DapEngine::interruptInferior() { - postDirectCommand({{"command", "pause"}, - {"type", "request"}}); + m_dapClient->sendPause(); } void DapEngine::executeStepIn(bool) { + if (m_currentThreadId == -1) + return; + notifyInferiorRunRequested(); - -// postDirectCommand({{"command", "stepIn"}, -// {"type", "request"}, -// {"arguments", -// QJsonObject{ -// {"threadId", 1}, // The ID of the client using this adapter. -// }}}); - - notifyInferiorRunOk(); + m_dapClient->sendStepIn(m_currentThreadId); } void DapEngine::executeStepOut() { + if (m_currentThreadId == -1) + return; + notifyInferiorRunRequested(); - notifyInferiorRunOk(); -// postDirectCommand("return"); + m_dapClient->sendStepOut(m_currentThreadId); } void DapEngine::executeStepOver(bool) { + if (m_currentThreadId == -1) + return; + notifyInferiorRunRequested(); - notifyInferiorRunOk(); -// postDirectCommand("next"); + + m_dapClient->sendStepOver(m_currentThreadId); } void DapEngine::continueInferior() { notifyInferiorRunRequested(); - postDirectCommand({{"command", "continue"}, - {"type", "request"}, - {"arguments", - QJsonObject{ - {"threadId", 1}, // The ID of the client using this adapter. - }}}); + m_dapClient->sendContinue(m_currentThreadId); } void DapEngine::executeRunToLine(const ContextData &data) { - Q_UNUSED(data) - QTC_CHECK("FIXME: DapEngine::runToLineExec()" && false); + // Add one-shot breakpoint + BreakpointParameters bp; + bp.oneShot = true; + if (data.address) { + bp.type = BreakpointByAddress; + bp.address = data.address; + } else { + bp.type = BreakpointByFileAndLine; + bp.fileName = data.fileName; + bp.textPosition = data.textPosition; + } + + BreakpointManager::createBreakpointForEngine(bp, this); } void DapEngine::executeRunToFunction(const QString &functionName) @@ -260,12 +291,17 @@ void DapEngine::activateFrame(int frameIndex) QTC_ASSERT(frameIndex < handler->stackSize(), return); handler->setCurrentIndex(frameIndex); gotoLocation(handler->currentFrame()); - updateLocals(); + + m_currentStackFrameId = handler->currentFrame().debuggerId; + m_dapClient->scopes(m_currentStackFrameId); } void DapEngine::selectThread(const Thread &thread) { Q_UNUSED(thread) + m_currentThreadId = thread->id().toInt(); + threadsHandler()->setCurrentThread(thread); + updateLocals(); } bool DapEngine::acceptsBreakpoint(const BreakpointParameters &) const @@ -273,71 +309,55 @@ bool DapEngine::acceptsBreakpoint(const BreakpointParameters &) const return true; // FIXME: Too bold. } -static QJsonObject createBreakpoint(const Breakpoint &breakpoint) +static QJsonObject createBreakpoint(const BreakpointParameters ¶ms) { - const BreakpointParameters ¶ms = breakpoint->requestedParameters(); - if (params.fileName.isEmpty()) return QJsonObject(); QJsonObject bp; bp["line"] = params.textPosition.line; - bp["source"] = QJsonObject{{"name", params.fileName.fileName()}, - {"path", params.fileName.path()}}; return bp; } - void DapEngine::insertBreakpoint(const Breakpoint &bp) { QTC_ASSERT(bp, return); QTC_CHECK(bp->state() == BreakpointInsertionRequested); notifyBreakpointInsertProceeding(bp); - const BreakpointParameters ¶ms = bp->requestedParameters(); + dapInsertBreakpoint(bp); +} + +void DapEngine::dapInsertBreakpoint(const Breakpoint &bp) +{ bp->setResponseId(QString::number(m_nextBreakpointId++)); + const BreakpointParameters ¶ms = bp->requestedParameters(); QJsonArray breakpoints; for (const auto &breakpoint : breakHandler()->breakpoints()) { - QJsonObject jsonBp = createBreakpoint(breakpoint); - if (!jsonBp.isEmpty() - && params.fileName.path() == jsonBp["source"].toObject()["path"].toString()) { + const BreakpointParameters &bpParams = breakpoint->requestedParameters(); + QJsonObject jsonBp = createBreakpoint(bpParams); + if (!jsonBp.isEmpty() && params.fileName.path() == bpParams.fileName.path()) { breakpoints.append(jsonBp); } } - postDirectCommand( - {{"command", "setBreakpoints"}, - {"type", "request"}, - {"arguments", - QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}}, - {"breakpoints", breakpoints} + m_dapClient->setBreakpoints(breakpoints, params.fileName); - }}}); - - qDebug() << "insertBreakpoint" << bp->modelId() << bp->responseId(); + qCDebug(logCategory()) << "insertBreakpoint" << bp->modelId() << bp->responseId(); } void DapEngine::updateBreakpoint(const Breakpoint &bp) { + BreakpointParameters parameters = bp->requestedParameters(); notifyBreakpointChangeProceeding(bp); -// QTC_ASSERT(bp, return); -// const BreakpointState state = bp->state(); -// if (QTC_GUARD(state == BreakpointUpdateRequested)) -// if (bp->responseId().isEmpty()) // FIXME postpone update somehow (QTimer::singleShot?) -// return; -// // FIXME figure out what needs to be changed (there might be more than enabled state) -// const BreakpointParameters &requested = bp->requestedParameters(); -// if (requested.enabled != bp->isEnabled()) { -// if (bp->isEnabled()) -// postDirectCommand("disable " + bp->responseId()); -// else -// postDirectCommand("enable " + bp->responseId()); -// bp->setEnabled(!bp->isEnabled()); -// } -// // Pretend it succeeds without waiting for response. - notifyBreakpointChangeOk(bp); + if (parameters.enabled != bp->isEnabled()) { + if (bp->isEnabled()) + dapRemoveBreakpoint(bp); + else + dapInsertBreakpoint(bp); + } } void DapEngine::removeBreakpoint(const Breakpoint &bp) @@ -346,23 +366,25 @@ void DapEngine::removeBreakpoint(const Breakpoint &bp) QTC_CHECK(bp->state() == BreakpointRemoveRequested); notifyBreakpointRemoveProceeding(bp); + dapRemoveBreakpoint(bp); +} + +void DapEngine::dapRemoveBreakpoint(const Breakpoint &bp) +{ const BreakpointParameters ¶ms = bp->requestedParameters(); QJsonArray breakpoints; - for (const auto &breakpoint : breakHandler()->breakpoints()) - if (breakpoint->responseId() != bp->responseId() - && params.fileName == breakpoint->requestedParameters().fileName) { - QJsonObject jsonBp = createBreakpoint(breakpoint); + for (const auto &breakpoint : breakHandler()->breakpoints()) { + const BreakpointParameters &bpParams = breakpoint->requestedParameters(); + if (breakpoint->responseId() != bp->responseId() && params.fileName == bpParams.fileName) { + QJsonObject jsonBp = createBreakpoint(bpParams); breakpoints.append(jsonBp); } + } - postDirectCommand({{"command", "setBreakpoints"}, - {"type", "request"}, - {"arguments", - QJsonObject{{"source", QJsonObject{{"path", params.fileName.path()}}}, - {"breakpoints", breakpoints}}}}); + m_dapClient->setBreakpoints(breakpoints, params.fileName); - qDebug() << "removeBreakpoint" << bp->modelId() << bp->responseId(); + qCDebug(logCategory()) << "removeBreakpoint" << bp->modelId() << bp->responseId(); } void DapEngine::loadSymbols(const Utils::FilePath &/*moduleName*/) @@ -392,7 +414,7 @@ void DapEngine::refreshModules(const GdbMi &modules) if (path.size() >= 2) path.chop(2); } else if (path.startsWith("<module '") - && path.endsWith("' (built-in)>")) { + && path.endsWith("' (built-in)>")) { path = "(builtin)"; } module.modulePath = FilePath::fromString(path); @@ -401,13 +423,6 @@ void DapEngine::refreshModules(const GdbMi &modules) handler->endUpdateAll(); } -void DapEngine::requestModuleSymbols(const Utils::FilePath &/*moduleName*/) -{ -// DebuggerCommand cmd("listSymbols"); -// cmd.arg("module", moduleName); -// runCommand(cmd); -} - void DapEngine::refreshState(const GdbMi &reportedState) { QString newState = reportedState.data(); @@ -450,20 +465,33 @@ bool DapEngine::canHandleToolTip(const DebuggerToolTipContext &) const return state() == InferiorStopOk; } -void DapEngine::assignValueInDebugger(WatchItem *, const QString &/*expression*/, const QVariant &/*value*/) -{ - //DebuggerCommand cmd("assignValue"); - //cmd.arg("expression", expression); - //cmd.arg("value", value.toString()); - //runCommand(cmd); -// postDirectCommand("global " + expression + ';' + expression + "=" + value.toString()); - updateLocals(); -} - void DapEngine::updateItem(const QString &iname) { - Q_UNUSED(iname) - updateAll(); + WatchItem *item = watchHandler()->findItem(iname); + + if (item && m_variablesHandler->currentItem().iname != item->iname) + m_variablesHandler->addVariable(item->iname, item->variablesReference); +} + +void DapEngine::reexpandItems(const QSet<QString> &inames) +{ + QSet<QString> expandedInames = inames; + const QList<QString> &watcherNames = watchHandler()->watcherNames().keys(); + for (const QString &inames : watcherNames) + expandedInames.insert(watchHandler()->watcherName(inames)); + + QList<QString> inamesVector = expandedInames.values(); + inamesVector.sort(); + + for (const QString &iname : std::as_const(inamesVector)) { + if (iname.startsWith("local.") || iname.startsWith("watch.")) + m_variablesHandler->addVariable(iname, -1); + } +} + +void DapEngine::doUpdateLocals(const UpdateParameters ¶ms) +{ + m_variablesHandler->addVariable(params.partialVariable, -1); } QString DapEngine::errorMessage(QProcess::ProcessError error) const @@ -473,7 +501,7 @@ QString DapEngine::errorMessage(QProcess::ProcessError error) const return Tr::tr("The DAP process failed to start. Either the " "invoked program \"%1\" is missing, or you may have insufficient " "permissions to invoke the program.") - .arg(m_proc.commandLine().executable().toUserOutput()); + .arg(m_dapClient->dataProvider()->executable()); case QProcess::Crashed: return Tr::tr("The DAP process crashed some time after starting " "successfully."); @@ -495,14 +523,15 @@ QString DapEngine::errorMessage(QProcess::ProcessError error) const void DapEngine::handleDapDone() { - if (m_proc.result() == ProcessResult::StartFailed) { + if (m_dapClient->dataProvider()->result() == ProcessResult::StartFailed) { notifyEngineSetupFailed(); showMessage("ADAPTER START FAILED"); - ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), m_proc.exitMessage()); + ICore::showWarningWithOptions(Tr::tr("Adapter start failed"), + m_dapClient->dataProvider()->exitMessage()); return; } - const QProcess::ProcessError error = m_proc.error(); + const QProcess::ProcessError error = m_dapClient->dataProvider()->error(); if (error != QProcess::UnknownError) { showMessage("HANDLE DAP ERROR"); if (error != QProcess::Crashed) @@ -511,206 +540,332 @@ void DapEngine::handleDapDone() return; } showMessage(QString("DAP PROCESS FINISHED, status %1, code %2") - .arg(m_proc.exitStatus()).arg(m_proc.exitCode())); + .arg(m_dapClient->dataProvider()->exitStatus()).arg(m_dapClient->dataProvider()->exitCode())); notifyEngineSpontaneousShutdown(); } void DapEngine::readDapStandardError() { - QString err = m_proc.readAllStandardError(); + QString err = m_dapClient->dataProvider()->readAllStandardError(); + qCDebug(logCategory()) << "DAP STDERR:" << err; //qWarning() << "Unexpected DAP stderr:" << err; showMessage("Unexpected DAP stderr: " + err); //handleOutput(err); } -void DapEngine::readDapStandardOutput() +void DapEngine::handleResponse(DapResponseType type, const QJsonObject &response) { - m_inbuffer.append(m_proc.readAllStandardOutput().toUtf8()); -// qDebug() << m_inbuffer; + const QString command = response.value("command").toString(); - while (true) { - // Something like - // Content-Length: 128\r\n - // {"type": "event", "event": "output", "body": {"category": "stdout", "output": "...\n"}, "seq": 1}\r\n - // FIXME: There coud be more than one header line. - int pos1 = m_inbuffer.indexOf("Content-Length:"); - if (pos1 == -1) - break; - pos1 += 15; + switch (type) { + case DapResponseType::Initialize: + qCDebug(logCategory()) << "initialize success"; + handleDapInitialize(); + break; + case DapResponseType::ConfigurationDone: + showMessage("configurationDone", LogDebug); + qCDebug(logCategory()) << "configurationDone success"; + handleDapConfigurationDone(); + break; + case DapResponseType::Continue: + showMessage("continue", LogDebug); + qCDebug(logCategory()) << "continue success"; + notifyInferiorRunOk(); + break; + case DapResponseType::StackTrace: + handleStackTraceResponse(response); + break; + case DapResponseType::Scopes: + handleScopesResponse(response); + break; + case DapResponseType::Variables: { + auto variables = response.value("body").toObject().value("variables").toArray(); + refreshLocals(variables); + break; + } + case DapResponseType::StepIn: + case DapResponseType::StepOut: + case DapResponseType::StepOver: + if (response.value("success").toBool()) { + showMessage(command, LogDebug); + notifyInferiorRunOk(); + } else { + notifyInferiorRunFailed(); + } + break; + case DapResponseType::DapThreads: + handleThreadsResponse(response); + break; + case DapResponseType::Evaluate: + handleEvaluateResponse(response); + break; + default: + showMessage("UNKNOWN RESPONSE:" + command); + }; - int pos2 = m_inbuffer.indexOf('\n', pos1); - if (pos2 == -1) - break; - - const int len = m_inbuffer.mid(pos1, pos2 - pos1).trimmed().toInt(); - if (len < 4) - break; - - pos2 += 3; // Skip \r\n\r - - if (pos2 + len > m_inbuffer.size()) - break; - - QJsonParseError error; - const auto doc = QJsonDocument::fromJson(m_inbuffer.mid(pos2, len), &error); - - m_inbuffer = m_inbuffer.mid(pos2 + len); - - handleOutput(doc); + if (response.contains("success") && !response.value("success").toBool()) { + showMessage(QString("DAP COMMAND FAILED: %1").arg(command)); + qCDebug(logCategory()) << "DAP COMMAND FAILED:" << command; + return; } } -void DapEngine::handleOutput(const QJsonDocument &data) +void DapEngine::handleStackTraceResponse(const QJsonObject &response) { - QJsonObject ob = data.object(); + QJsonArray stackFrames = response.value("body").toObject().value("stackFrames").toArray(); + if (stackFrames.isEmpty()) + return; - const QJsonValue t = ob.value("type"); - const QString type = t.toString(); - qDebug() << "response" << ob; + QJsonObject stackFrame = stackFrames[0].toObject(); + const FilePath file = FilePath::fromString( + stackFrame.value("source").toObject().value("path").toString()); + const int line = stackFrame.value("line").toInt(); + qCDebug(logCategory()) << "stackTrace success" << file << line; + gotoLocation(Location(file, line)); - if (type == "response") { - const QString command = ob.value("command").toString(); - if (command == "configurationDone") { - showMessage("configurationDone", LogDebug); - qDebug() << "configurationDone success"; - notifyInferiorRunOk(); - return; - } + refreshStack(stackFrames); + m_currentStackFrameId = stackFrame.value("id").toInt(); + m_dapClient->scopes(m_currentStackFrameId); +} - if (command == "continue") { - showMessage("continue", LogDebug); - qDebug() << "continue success"; - notifyInferiorRunOk(); - return; - } +void DapEngine::handleScopesResponse(const QJsonObject &response) +{ + if (!response.value("success").toBool()) + return; + watchHandler()->resetValueCache(); + watchHandler()->notifyUpdateStarted(); + + QJsonArray scopes = response.value("body").toObject().value("scopes").toArray(); + for (const QJsonValueRef &scope : scopes) { + const QString name = scope.toObject().value("name").toString(); + if (name == "Registers") + continue; + m_variablesHandler->addVariable("", scope.toObject().value("variablesReference").toInt()); } - if (type == "event") { - const QString event = ob.value("event").toString(); - const QJsonObject body = ob.value("body").toObject(); + if (m_variablesHandler->queueSize() == 0) { + watchHandler()->notifyUpdateFinished(); + } +} - if (event == "output") { - const QString category = body.value("category").toString(); - const QString output = body.value("output").toString(); - if (category == "stdout") - showMessage(output, AppOutput); - else if (category == "stderr") - showMessage(output, AppError); - else - showMessage(output, LogDebug); +void DapEngine::handleThreadsResponse(const QJsonObject &response) +{ + QJsonArray threads = response.value("body").toObject().value("threads").toArray(); + + if (threads.isEmpty()) + return; + + ThreadsHandler *handler = threadsHandler(); + for (const QJsonValueRef &thread : threads) { + ThreadData threadData; + threadData.id = QString::number(thread.toObject().value("id").toInt()); + threadData.name = thread.toObject().value("name").toString(); + handler->updateThread(threadData); + } + + if (m_currentThreadId) { + Thread thread = threadsHandler()->threadForId(QString::number(m_currentThreadId)); + if (thread && thread != threadsHandler()->currentThread()) + handler->setCurrentThread(thread); + } +} + +void DapEngine::handleEvaluateResponse(const QJsonObject &response) +{ + WatchItem *watchItem = watchHandler()->findItem( + m_variablesHandler->currentItem().iname); + if (watchItem + && response.value("body").toObject().contains("variablesReference")) { + watchItem->variablesReference + = response.value("body").toObject().value("variablesReference").toInt(); + watchItem->value + = response.value("body").toObject().value("result").toString(); + watchItem->type = response.value("body").toObject().value("type").toString(); + watchItem->wantsChildren = watchItem->variablesReference > 0; + + watchItem->updateValueCache(); + watchItem->update(); + + m_variablesHandler->addVariable(watchItem->iname, + watchItem->variablesReference); + } + m_variablesHandler->handleNext(); +} + +void DapEngine::handleEvent(DapEventType type, const QJsonObject &event) +{ + const QString eventType = event.value("event").toString(); + const QJsonObject body = event.value("body").toObject(); + showMessage(eventType, LogDebug); + + switch (type) { + case DapEventType::Initialized: + qCDebug(logCategory()) << "initialize success"; + claimInitialBreakpoints(); + handleDapEventInitialized(); + break; + case DapEventType::Stopped: + handleStoppedEvent(event); + break; + case DapEventType::Exited: + notifyInferiorExited(); + break; + case DapEventType::DapThread: + m_dapClient->threads(); + if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1) + claimInitialBreakpoints(); + break; + case DapEventType::DapBreakpoint: + handleBreakpointEvent(event); + break; + case DapEventType::Output: { + const QString category = body.value("category").toString(); + const QString output = body.value("output").toString(); + if (category == "stdout") + showMessage(output, AppOutput); + else if (category == "stderr") + showMessage(output, AppError); + else + showMessage(output, LogDebug); + break; + } + default: + showMessage("UNKNOWN EVENT:" + eventType); + }; +} + +void DapEngine::handleStoppedEvent(const QJsonObject &event) +{ + const QJsonObject body = event.value("body").toObject(); + m_currentThreadId = body.value("threadId").toInt(); + + if (body.value("reason").toString() == "breakpoint") { + QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger()); + + Breakpoint bp = breakHandler()->findBreakpointByResponseId(id); + if (bp) { + const BreakpointParameters ¶ms = bp->requestedParameters(); + gotoLocation(Location(params.fileName, params.textPosition)); + if (params.oneShot) + removeBreakpoint(bp); + } + } + + if (state() == InferiorStopRequested) + notifyInferiorStopOk(); + else + notifyInferiorSpontaneousStop(); + + m_dapClient->stackTrace(m_currentThreadId); + m_dapClient->threads(); +} + +void DapEngine::handleBreakpointEvent(const QJsonObject &event) +{ + const QJsonObject body = event.value("body").toObject(); + QJsonObject breakpoint = body.value("breakpoint").toObject(); + + Breakpoint bp = breakHandler()->findBreakpointByResponseId( + QString::number(breakpoint.value("id").toInt())); + qCDebug(logCategory()) << "breakpoint id :" << breakpoint.value("id").toInt(); + + if (bp) { + BreakpointParameters parameters = bp->requestedParameters(); + if (parameters.enabled != bp->isEnabled()) { + parameters.pending = false; + bp->setParameters(parameters); + notifyBreakpointChangeOk(bp); return; } - qDebug() << data; + } - if (event == "initialized") { - showMessage(event, LogDebug); - qDebug() << "initialize success"; - handleDabLaunch(); - handleDabConfigurationDone(); - return; + if (body.value("reason").toString() == "new") { + if (breakpoint.value("verified").toBool()) { + notifyBreakpointInsertOk(bp); + const BreakpointParameters ¶ms = bp->requestedParameters(); + if (params.oneShot) + continueInferior(); + qCDebug(logCategory()) << "breakpoint inserted"; + } else { + notifyBreakpointInsertFailed(bp); + qCDebug(logCategory()) << "breakpoint insertion failed"; } - - if (event == "initialized") { - showMessage(event, LogDebug); - return; - } - - if (event == "stopped") { - showMessage(event, LogDebug); - if (body.value("reason").toString() == "breakpoint") { - QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger()); - const BreakpointParameters ¶ms - = breakHandler()->findBreakpointByResponseId(id)->requestedParameters(); - gotoLocation(Location(params.fileName, params.textPosition)); - } - - if (state() == InferiorStopRequested) - notifyInferiorStopOk(); - else - notifyInferiorSpontaneousStop(); - return; - } - - if (event == "thread") { - showMessage(event, LogDebug); - if (body.value("reason").toString() == "started" && body.value("threadId").toInt() == 1) - claimInitialBreakpoints(); - return; - } - - if (event == "breakpoint") { - showMessage(event, LogDebug); - QJsonObject breakpoint = body.value("breakpoint").toObject(); - Breakpoint bp = breakHandler()->findBreakpointByResponseId( - QString::number(breakpoint.value("id").toInt())); - qDebug() << "breakpoint id :" << breakpoint.value("id").toInt(); - - if (body.value("reason").toString() == "new") { - if (breakpoint.value("verified").toBool()) { -// bp->setPending(false); - notifyBreakpointInsertOk(bp); - qDebug() << "breakpoint inserted"; - } else { - notifyBreakpointInsertFailed(bp); - qDebug() << "breakpoint insertion failed"; - } - return; - } - - if (body.value("reason").toString() == "removed") { - if (breakpoint.value("verified").toBool()) { - notifyBreakpointRemoveOk(bp); - qDebug() << "breakpoint removed"; - } else { - notifyBreakpointRemoveFailed(bp); - qDebug() << "breakpoint remove failed"; - } - return; - } - return; - } - - - showMessage("UNKNOWN EVENT:" + event); return; } - showMessage("UNKNOWN TYPE:" + type); + if (body.value("reason").toString() == "removed") { + if (breakpoint.value("verified").toBool()) { + notifyBreakpointRemoveOk(bp); + qCDebug(logCategory()) << "breakpoint removed"; + } else { + notifyBreakpointRemoveFailed(bp); + qCDebug(logCategory()) << "breakpoint remove failed"; + } + return; + } } -void DapEngine::refreshLocals(const GdbMi &vars) +void DapEngine::refreshLocals(const QJsonArray &variables) { - WatchHandler *handler = watchHandler(); - handler->resetValueCache(); - handler->insertItems(vars); - handler->notifyUpdateFinished(); + WatchItem *currentItem = watchHandler()->findItem(m_variablesHandler->currentItem().iname); + if (currentItem && currentItem->iname.startsWith("watch")) + currentItem->removeChildren(); - updateToolTips(); + for (auto variable : variables) { + WatchItem *item = new WatchItem; + const QString name = variable.toObject().value("name").toString(); + + item->iname = (currentItem ? currentItem->iname : "local") + "." + + name; + item->name = name; + item->type = variable.toObject().value("type").toString(); + item->value = variable.toObject().value("value").toString(); + item->address = variable.toObject().value("address").toInt(); + item->type = variable.toObject().value("type").toString(); + item->variablesReference = variable.toObject().value("variablesReference").toInt(); + item->wantsChildren = item->variablesReference > 0; + + qCDebug(logCategory()) << "variable" << item->iname << item->variablesReference; + if (currentItem) + currentItem->appendChild(item); + else + watchHandler()->insertItem(item); + } + + if (currentItem) { + QModelIndex idx = watchHandler()->model()->indexForItem(currentItem); + if (idx.isValid() && idx.data(LocalsExpandedRole).toBool()) { + emit watchHandler()->model()->inameIsExpanded(currentItem->iname); + emit watchHandler()->model()->itemIsExpanded(idx); + } + } + + if (m_variablesHandler->queueSize() == 1 && currentItem == nullptr) { + watchHandler()->notifyUpdateFinished(); + } + + m_variablesHandler->handleNext(); } -void DapEngine::refreshStack(const GdbMi &stack) +void DapEngine::refreshStack(const QJsonArray &stackFrames) { StackHandler *handler = stackHandler(); StackFrames frames; - for (const GdbMi &item : stack["frames"]) { + for (const auto &value : stackFrames) { StackFrame frame; - frame.level = item["level"].data(); - frame.file = FilePath::fromString(item["file"].data()); - frame.function = item["function"].data(); - frame.module = item["function"].data(); - frame.line = item["line"].toInt(); - frame.address = item["address"].toAddress(); - GdbMi usable = item["usable"]; - if (usable.isValid()) - frame.usable = usable.data().toInt(); - else - frame.usable = frame.file.isReadableFile(); + QJsonObject item = value.toObject(); + frame.level = item.value("id").toString(); + frame.function = item.value("name").toString(); + frame.line = item.value("line").toInt(); + QJsonObject source = item.value("source").toObject(); + frame.file = FilePath::fromString(source.value("path").toString()); + frame.address = item.value("instructionPointerReference").toInt(); + frame.usable = frame.file.isReadableFile(); + frame.debuggerId = item.value("id").toInt(); frames.append(frame); } - bool canExpand = stack["hasmore"].toInt(); - //action(ExpandStack)->setEnabled(canExpand); - handler->setFrames(frames, canExpand); + handler->setFrames(frames, false); int index = stackHandler()->firstUsableIndex(); handler->setCurrentIndex(index); @@ -718,6 +873,11 @@ void DapEngine::refreshStack(const GdbMi &stack) gotoLocation(handler->frameAt(index)); } +void DapEngine::reloadFullStack() +{ + updateAll(); +} + void DapEngine::updateAll() { runCommand({"stackListFrames"}); @@ -726,74 +886,50 @@ void DapEngine::updateAll() void DapEngine::updateLocals() { -// DebuggerCommand cmd("updateData"); -// cmd.arg("nativeMixed", isNativeMixedActive()); -// watchHandler()->appendFormatRequests(&cmd); -// watchHandler()->appendWatchersAndTooltipRequests(&cmd); - -// const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE"); -// cmd.arg("passexceptions", alwaysVerbose); -// cmd.arg("fancy", debuggerSettings()->useDebuggingHelpers.value()); - -// //cmd.arg("resultvarname", m_resultVarName); -// //m_lastDebuggableCommand = cmd; -// //m_lastDebuggableCommand.args.replace("\"passexceptions\":0", "\"passexceptions\":1"); -// cmd.arg("frame", stackHandler()->currentIndex()); - -// watchHandler()->notifyUpdateStarted(); -// runCommand(cmd); + m_dapClient->stackTrace(m_currentThreadId); } bool DapEngine::hasCapability(unsigned cap) const { return cap & (ReloadModuleCapability - | BreakConditionCapability - | ShowModuleSymbolsCapability); + | BreakConditionCapability + | ShowModuleSymbolsCapability + | RunToLineCapability + | AddWatcherCapability); } void DapEngine::claimInitialBreakpoints() { BreakpointManager::claimBreakpointsForEngine(this); - qDebug() << "claimInitialBreakpoints"; -// const Breakpoints bps = breakHandler()->breakpoints(); -// for (const Breakpoint &bp : bps) -// qDebug() << "breakpoit: " << bp->fileName() << bp->lineNumber(); -// qDebug() << "claimInitialBreakpoints end"; - -// const DebuggerRunParameters &rp = runParameters(); -// if (rp.startMode != AttachToCore) { -// showStatusMessage(Tr::tr("Setting breakpoints...")); -// showMessage(Tr::tr("Setting breakpoints...")); -// BreakpointManager::claimBreakpointsForEngine(this); - -// const DebuggerSettings &s = *debuggerSettings(); -// const bool onAbort = s.breakOnAbort.value(); -// const bool onWarning = s.breakOnWarning.value(); -// const bool onFatal = s.breakOnFatal.value(); -// if (onAbort || onWarning || onFatal) { -// DebuggerCommand cmd("createSpecialBreakpoints"); -// cmd.arg("breakonabort", onAbort); -// cmd.arg("breakonwarning", onWarning); -// cmd.arg("breakonfatal", onFatal); -// runCommand(cmd); -// } -// } - -// // It is ok to cut corners here and not wait for createSpecialBreakpoints()'s -// // response, as the command is synchronous from Creator's point of view, -// // and even if it fails (e.g. due to stripped binaries), continuing with -// // the start up is the best we can do. - -// if (!rp.commandsAfterConnect.isEmpty()) { -// const QString commands = expand(rp.commandsAfterConnect); -// for (const QString &command : commands.split('\n')) -// runCommand({command, NativeCommand}); -// } + qCDebug(logCategory()) << "claimInitialBreakpoints"; } -DebuggerEngine *createDapEngine() +void DapEngine::connectDataGeneratorSignals() { - return new DapEngine; + if (!m_dapClient) + return; + + connect(m_dapClient, &DapClient::started, this, &DapEngine::handleDapStarted); + connect(m_dapClient, &DapClient::done, this, &DapEngine::handleDapDone); + connect(m_dapClient, + &DapClient::readyReadStandardError, + this, + &DapEngine::readDapStandardError); + + connect(m_dapClient, &DapClient::responseReady, this, &DapEngine::handleResponse); + connect(m_dapClient, &DapClient::eventReady, this, &DapEngine::handleEvent); +} + +DebuggerEngine *createDapEngine(Utils::Id runMode) +{ + if (runMode == ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE) + return new CMakeDapEngine; + if (runMode == ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE) + return new GdbDapEngine; + if (runMode == ProjectExplorer::Constants::DAP_PY_DEBUG_RUN_MODE) + return new PyDapEngine; + + return nullptr; } } // Debugger::Internal diff --git a/src/plugins/debugger/dap/dapengine.h b/src/plugins/debugger/dap/dapengine.h index 2fa3019cad9..6f0ae730c17 100644 --- a/src/plugins/debugger/dap/dapengine.h +++ b/src/plugins/debugger/dap/dapengine.h @@ -4,30 +4,64 @@ #pragma once #include <debugger/debuggerengine.h> + #include <utils/process.h> +#include <QLoggingCategory> #include <QVariant> +#include <queue> + namespace Debugger::Internal { +class DapClient; class DebuggerCommand; +class IDataProvider; class GdbMi; +enum class DapResponseType; +enum class DapEventType; +class DapEngine; + +class VariablesHandler { +public: + VariablesHandler(DapEngine *dapEngine); + + struct VariableItem { + QString iname; + int variablesReference; + }; + + void addVariable(const QString &iname, int variablesReference); + void handleNext(); + + VariableItem currentItem() const { return m_currentVarItem; } + int queueSize() const { return int(m_queue.size()); } + +private: + void startHandling(); + + DapEngine *m_dapEngine; + std::list<VariableItem> m_queue; + VariableItem m_currentVarItem; +}; /* * A debugger engine for the debugger adapter protocol. */ - class DapEngine : public DebuggerEngine { public: DapEngine(); + ~DapEngine() override = default; -private: + DapClient *dapClient() const { return m_dapClient; } + int currentStackFrameId() const { return m_currentStackFrameId; } + +protected: void executeStepIn(bool) override; void executeStepOut() override; void executeStepOver(bool) override; - void setupEngine() override; void shutdownInferior() override; void shutdownEngine() override; @@ -48,27 +82,26 @@ private: void updateBreakpoint(const Breakpoint &bp) override; void removeBreakpoint(const Breakpoint &bp) override; - void assignValueInDebugger(WatchItem *item, - const QString &expr, const QVariant &value) override; void executeDebuggerCommand(const QString &command) override; void loadSymbols(const Utils::FilePath &moduleName) override; void loadAllSymbols() override; - void requestModuleSymbols(const Utils::FilePath &moduleName) override; void reloadModules() override; void reloadRegisters() override {} void reloadSourceFiles() override {} - void reloadFullStack() override {} + void reloadFullStack() override; bool supportsThreads() const { return true; } void updateItem(const QString &iname) override; + void reexpandItems(const QSet<QString> &inames) override; + void doUpdateLocals(const UpdateParameters ¶ms) override; + void getVariableFromQueue(); void runCommand(const DebuggerCommand &cmd) override; - void postDirectCommand(const QJsonObject &ob); void refreshLocation(const GdbMi &reportedLocation); - void refreshStack(const GdbMi &stack); - void refreshLocals(const GdbMi &vars); + void refreshStack(const QJsonArray &stackFrames); + void refreshLocals(const QJsonArray &variables); void refreshModules(const GdbMi &modules); void refreshState(const GdbMi &reportedState); void refreshSymbols(const GdbMi &symbols); @@ -78,21 +111,47 @@ private: void claimInitialBreakpoints(); - void handleDabStarted(); - void handleDabLaunch(); - void handleDabConfigurationDone(); + void handleDapStarted(); + virtual void handleDapInitialize(); + void handleDapEventInitialized(); + virtual void handleDapConfigurationDone(); + + void dapRemoveBreakpoint(const Breakpoint &bp); + void dapInsertBreakpoint(const Breakpoint &bp); void handleDapDone(); void readDapStandardOutput(); void readDapStandardError(); - void handleOutput(const QJsonDocument &data); - void handleResponse(const QString &ba); + + void handleResponse(DapResponseType type, const QJsonObject &response); + void handleStackTraceResponse(const QJsonObject &response); + void handleScopesResponse(const QJsonObject &response); + void handleThreadsResponse(const QJsonObject &response); + void handleEvaluateResponse(const QJsonObject &response); + + void handleEvent(DapEventType type, const QJsonObject &event); + void handleBreakpointEvent(const QJsonObject &event); + void handleStoppedEvent(const QJsonObject &event); + void updateAll() override; void updateLocals() override; + void connectDataGeneratorSignals(); QByteArray m_inbuffer; - Utils::Process m_proc; + DapClient *m_dapClient = nullptr; + int m_nextBreakpointId = 1; + int m_currentThreadId = -1; + int m_currentStackFrameId = -1; + + std::unique_ptr<VariablesHandler> m_variablesHandler; + + virtual const QLoggingCategory &logCategory() + { + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine", + QtWarningMsg); + return logCategory; + } }; } // Debugger::Internal diff --git a/src/plugins/debugger/dap/gdbdapengine.cpp b/src/plugins/debugger/dap/gdbdapengine.cpp new file mode 100644 index 00000000000..3934aebe9d8 --- /dev/null +++ b/src/plugins/debugger/dap/gdbdapengine.cpp @@ -0,0 +1,171 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "gdbdapengine.h" + +#include "dapclient.h" + +#include <coreplugin/messagemanager.h> + +#include <debugger/debuggermainwindow.h> + +#include <utils/temporarydirectory.h> + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projecttree.h> + +#include <QDebug> +#include <QLocalSocket> +#include <QVersionNumber> + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +class ProcessDataProvider : public IDataProvider +{ +public: + ProcessDataProvider(const DebuggerRunParameters &rp, + const CommandLine &cmd, + QObject *parent = nullptr) + : IDataProvider(parent) + , m_runParameters(rp) + , m_cmd(cmd) + { + connect(&m_proc, &Process::started, this, &IDataProvider::started); + connect(&m_proc, &Process::done, this, &IDataProvider::done); + connect(&m_proc, + &Process::readyReadStandardOutput, + this, + &IDataProvider::readyReadStandardOutput); + connect(&m_proc, + &Process::readyReadStandardError, + this, + &IDataProvider::readyReadStandardError); + } + + ~ProcessDataProvider() + { + m_proc.kill(); + m_proc.waitForFinished(); + } + + void start() override + { + m_proc.setProcessMode(ProcessMode::Writer); + m_proc.setEnvironment(m_runParameters.debugger.environment); + m_proc.setCommand(m_cmd); + m_proc.start(); + } + + bool isRunning() const override { return m_proc.isRunning(); } + void writeRaw(const QByteArray &data) override + { + if (m_proc.state() == QProcess::Running) + m_proc.writeRaw(data); + } + void kill() override { m_proc.kill(); } + QByteArray readAllStandardOutput() override { return m_proc.readAllStandardOutput().toUtf8(); } + QString readAllStandardError() override { return m_proc.readAllStandardError(); } + int exitCode() const override { return m_proc.exitCode(); } + QString executable() const override { return m_proc.commandLine().executable().toUserOutput(); } + + QProcess::ExitStatus exitStatus() const override { return m_proc.exitStatus(); } + QProcess::ProcessError error() const override { return m_proc.error(); } + Utils::ProcessResult result() const override { return m_proc.result(); } + QString exitMessage() const override { return m_proc.exitMessage(); }; + +private: + Utils::Process m_proc; + const DebuggerRunParameters m_runParameters; + const CommandLine m_cmd; +}; + +class GdbDapClient : public DapClient +{ +public: + GdbDapClient(IDataProvider *provider, QObject *parent = nullptr) + : DapClient(provider, parent) + {} + +private: + const QLoggingCategory &logCategory() override + { + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.gdb", + QtWarningMsg); + return logCategory; + } +}; + +GdbDapEngine::GdbDapEngine() + : DapEngine() +{ + setObjectName("GdbDapEngine"); + setDebuggerName("Gdb"); + setDebuggerType("DAP"); +} + +void GdbDapEngine::handleDapInitialize() +{ + if (!isLocalAttachEngine()) { + DapEngine::handleDapInitialize(); + return; + } + + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); + m_dapClient->postRequest("attach", QJsonObject{{"__restart", ""}}); + qCDebug(logCategory()) << "handleDapAttach"; +} + +bool GdbDapEngine::isLocalAttachEngine() const +{ + return runParameters().startMode == AttachToLocalProcess; +} + +void GdbDapEngine::handleDapConfigurationDone() +{ + if (!isLocalAttachEngine()) { + DapEngine::handleDapConfigurationDone(); + return; + } + + notifyEngineRunAndInferiorStopOk(); +} + +void GdbDapEngine::setupEngine() +{ + QTC_ASSERT(state() == EngineSetupRequested, qCDebug(logCategory()) << state()); + + const DebuggerRunParameters &rp = runParameters(); + CommandLine cmd{rp.debugger.command.executable(), {"-i", "dap"}}; + + if (isLocalAttachEngine()) + cmd.addArgs({"-p", QString::number(rp.attachPID.pid())}); + + QVersionNumber oldestVersion(14, 0, 50); + QVersionNumber version = QVersionNumber::fromString(rp.version); + if (version < oldestVersion) { + notifyEngineSetupFailed(); + MessageManager::writeDisrupting("Debugger version " + rp.version + + " is too old. Please upgrade to at least " + + oldestVersion.toString()); + return; + } + + IDataProvider *dataProvider = new ProcessDataProvider(rp, cmd, this); + m_dapClient = new GdbDapClient(dataProvider, this); + + connectDataGeneratorSignals(); + m_dapClient->dataProvider()->start(); +} + +const QLoggingCategory &GdbDapEngine::logCategory() +{ + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.gdb", + QtWarningMsg); + return logCategory; +} + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/gdbdapengine.h b/src/plugins/debugger/dap/gdbdapengine.h new file mode 100644 index 00000000000..3638a6d3aba --- /dev/null +++ b/src/plugins/debugger/dap/gdbdapengine.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "dapengine.h" + +namespace Debugger::Internal { + +class GdbDapEngine : public DapEngine +{ +public: + GdbDapEngine(); + +private: + void setupEngine() override; + + void handleDapInitialize() override; + void handleDapConfigurationDone() override; + + bool isLocalAttachEngine() const; + + const QLoggingCategory &logCategory() override; +}; + +} // Debugger::Internal diff --git a/src/plugins/debugger/dap/pydapengine.cpp b/src/plugins/debugger/dap/pydapengine.cpp new file mode 100644 index 00000000000..4bbb479126f --- /dev/null +++ b/src/plugins/debugger/dap/pydapengine.cpp @@ -0,0 +1,301 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#include "pydapengine.h" + +#include "dapclient.h" + +#include <coreplugin/messagemanager.h> +#include <coreplugin/icore.h> + +#include <debugger/debuggermainwindow.h> +#include <debugger/debuggertr.h> + +#include <utils/infobar.h> +#include <utils/temporarydirectory.h> + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/projecttree.h> + +#include <QDebug> +#include <QTcpSocket> +#include <QTimer> +#include <QVersionNumber> + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +const char installDebugPyInfoBarId[] = "Python::InstallDebugPy"; + +static bool missingPySideInstallation(const FilePath &pythonPath, const QString &packageName) +{ + QTC_ASSERT(!packageName.isEmpty(), return false); + static QMap<FilePath, QSet<QString>> pythonWithPyside; + if (pythonWithPyside[pythonPath].contains(packageName)) + return false; + + Process pythonProcess; + pythonProcess.setCommand({pythonPath, {"-c", "import " + packageName}}); + pythonProcess.runBlocking(); + const bool missing = pythonProcess.result() != ProcessResult::FinishedWithSuccess; + if (!missing) + pythonWithPyside[pythonPath].insert(packageName); + return missing; +} + +class TcpSocketDataProvider : public IDataProvider +{ +public: + TcpSocketDataProvider(const DebuggerRunParameters &rp, + const CommandLine &cmd, + const QString &hostName, + const quint16 port, + QObject *parent = nullptr) + : IDataProvider(parent) + , m_runParameters(rp) + , m_cmd(cmd) + , m_hostName(hostName) + , m_port(port) + { + connect(&m_socket, &QTcpSocket::connected, this, &IDataProvider::started); + connect(&m_socket, &QTcpSocket::disconnected, this, &IDataProvider::done); + connect(&m_socket, &QTcpSocket::readyRead, this, &IDataProvider::readyReadStandardOutput); + connect(&m_socket, + &QTcpSocket::errorOccurred, + this, + &IDataProvider::readyReadStandardError); + connect(&m_proc, &Process::done, this, &TcpSocketDataProvider::kill); + } + + ~TcpSocketDataProvider() { m_socket.disconnect(); } + + void start() override + { + m_proc.setEnvironment(m_runParameters.debugger.environment); + m_proc.setCommand(m_cmd); + // Workaround to have output for Python + m_proc.setTerminalMode(TerminalMode::Run); + m_proc.start(); + + m_timer = new QTimer(this); + m_timer->setInterval(100); + + connect(m_timer, &QTimer::timeout, this, [this]() { + m_socket.connectToHost(m_hostName, m_port); + m_socket.waitForConnected(); + + if (m_socket.state() == QTcpSocket::ConnectedState) + m_timer->stop(); + + if (m_attempts >= m_maxAttempts) + kill(); + + m_attempts++; + }); + + m_timer->start(); + } + + bool isRunning() const override { return m_socket.isOpen(); } + void writeRaw(const QByteArray &data) override + { + if (m_socket.isOpen()) + m_socket.write(data); + } + void kill() override + { + m_timer->stop(); + + if (m_proc.state() == QProcess::Running) + m_proc.kill(); + + if (m_socket.isOpen()) + m_socket.disconnect(); + + m_socket.abort(); + emit done(); + } + QByteArray readAllStandardOutput() override { return m_socket.readAll(); } + QString readAllStandardError() override { return QString(); } + int exitCode() const override { return 0; } + QString executable() const override { return m_hostName + ":" + QString::number(m_port); } + + QProcess::ExitStatus exitStatus() const override { return QProcess::NormalExit; } + QProcess::ProcessError error() const override { return QProcess::UnknownError; } + Utils::ProcessResult result() const override { return ProcessResult::FinishedWithSuccess; } + QString exitMessage() const override { return QString(); } + +private: + Utils::Process m_proc; + const DebuggerRunParameters m_runParameters; + const CommandLine m_cmd; + + QTcpSocket m_socket; + const QString m_hostName; + const quint16 m_port; + + QTimer *m_timer; + const int m_maxAttempts = 10; + int m_attempts = 0; +}; + +class PythonDapClient : public DapClient +{ +public: + PythonDapClient(IDataProvider *provider, QObject *parent = nullptr) + : DapClient(provider, parent) + {} + + void sendInitialize() override + { + postRequest("initialize", + QJsonObject{{"clientID", "QtCreator"}, + {"clientName", "QtCreator"}, + {"adapterID", "python"}, + {"pathFormat", "path"}}); + } +private: + const QLoggingCategory &logCategory() override + { + static const QLoggingCategory dapEngineLog = QLoggingCategory("qtc.dbg.dapengine.python", + QtWarningMsg); + return dapEngineLog; + } +}; + +PyDapEngine::PyDapEngine() + : DapEngine() +{ + setObjectName("PythonDapEngine"); + setDebuggerName("PythonDAP"); + setDebuggerType("DAP"); +} + +void PyDapEngine::handleDapInitialize() +{ + QTC_ASSERT(state() == EngineRunRequested, qCDebug(logCategory()) << state()); + + m_dapClient->sendAttach(); + + qCDebug(logCategory()) << "handleDapAttach"; +} + +void PyDapEngine::quitDebugger() +{ + showMessage(QString("QUIT DEBUGGER REQUESTED IN STATE %1").arg(state())); + startDying(); + + // Temporary workaround for Python debugging instability, particularly + // in conjunction with PySide6, due to unreliable pause functionality. + if (state() == InferiorRunOk) { + setState(InferiorStopRequested); + notifyInferiorStopOk(); + return; + } + + DebuggerEngine::quitDebugger(); +} + +bool PyDapEngine::acceptsBreakpoint(const BreakpointParameters &bp) const +{ + return bp.fileName.endsWith(".py"); +} + +void PyDapEngine::insertBreakpoint(const Breakpoint &bp) +{ + DapEngine::insertBreakpoint(bp); + notifyBreakpointInsertOk(bp); // Needed for Python support issue:1386 +} + +void PyDapEngine::removeBreakpoint(const Breakpoint &bp) +{ + DapEngine::removeBreakpoint(bp); + notifyBreakpointRemoveOk(bp); // Needed for Python support issue:1386 +} + +void PyDapEngine::updateBreakpoint(const Breakpoint &bp) +{ + DapEngine::updateBreakpoint(bp); + + /* Needed for Python support issue:1386 */ + BreakpointParameters parameters = bp->requestedParameters(); + if (parameters.enabled != bp->isEnabled()) { + parameters.pending = false; + bp->setParameters(parameters); + } + notifyBreakpointChangeOk(bp); + /* Needed for Python support issue:1386 */ +} + +bool PyDapEngine::isLocalAttachEngine() const +{ + return runParameters().startMode == AttachToLocalProcess; +} + +void PyDapEngine::setupEngine() +{ + QTC_ASSERT(state() == EngineSetupRequested, qDebug() << state()); + + Utils::FilePath interpreter = runParameters().interpreter; + + const FilePath scriptFile = runParameters().mainScript; + if (!scriptFile.isReadableFile()) { + MessageManager::writeDisrupting( + "Python Error" + QString("Cannot open script file %1").arg(scriptFile.toUserOutput())); + notifyEngineSetupFailed(); + return; + } + + if (missingPySideInstallation(interpreter, "debugpy")) { + Utils::InfoBarEntry + info(installDebugPyInfoBarId, + Tr::tr( + "Python debugging support is not available. Install the debugpy package."), + Utils::InfoBarEntry::GlobalSuppression::Enabled); + info.addCustomButton(Tr::tr("Install debugpy"), [this] { + Core::ICore::infoBar()->removeInfo(installDebugPyInfoBarId); + Core::ICore::infoBar()->globallySuppressInfo(installDebugPyInfoBarId); + m_installProcess.reset(new Process); + m_installProcess->setCommand({runParameters().interpreter, + {"-m", "pip", "install", "debugpy"}}); + m_installProcess->setTerminalMode(TerminalMode::Run); + m_installProcess->start(); + }); + Core::ICore::infoBar()->addInfo(info); + + notifyEngineSetupFailed(); + return; + } + + CommandLine cmd{interpreter, + {"-Xfrozen_modules=off", + "-m", "debugpy", + "--listen", "127.0.0.1:5679"}}; + + if (isLocalAttachEngine()) { + cmd.addArgs({"--pid", QString::number(runParameters().attachPID.pid())}); + } else { + cmd.addArgs({"--wait-for-client", + scriptFile.path(), + runParameters().inferior.workingDirectory.path()}); + } + + IDataProvider *dataProvider + = new TcpSocketDataProvider(runParameters(), cmd, "127.0.0.1", 5679, this); + m_dapClient = new PythonDapClient(dataProvider, this); + + connectDataGeneratorSignals(); + m_dapClient->dataProvider()->start(); +} + +const QLoggingCategory &PyDapEngine::logCategory() +{ + static const QLoggingCategory logCategory = QLoggingCategory("qtc.dbg.dapengine.python", + QtWarningMsg); + return logCategory; +} + +} // namespace Debugger::Internal diff --git a/src/plugins/debugger/dap/pydapengine.h b/src/plugins/debugger/dap/pydapengine.h new file mode 100644 index 00000000000..2c5f53e63a9 --- /dev/null +++ b/src/plugins/debugger/dap/pydapengine.h @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "dapengine.h" + +namespace Debugger::Internal { + +class PyDapEngine : public DapEngine +{ +public: + PyDapEngine(); + +private: + void handleDapInitialize() override; + void quitDebugger() override; + + void setupEngine() override; + bool isLocalAttachEngine() const; + + bool acceptsBreakpoint(const BreakpointParameters &bp) const override; + /* Needed for Python support issue:1386 */ + void insertBreakpoint(const Breakpoint &bp) override; + void updateBreakpoint(const Breakpoint &bp) override; + void removeBreakpoint(const Breakpoint &bp) override; + /* Needed for Python support issue:1386 */ + + const QLoggingCategory &logCategory() override; + + std::unique_ptr<Utils::Process> m_installProcess; +}; + +} // Debugger::Internal diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index be2adfc04d9..7ff685a63c5 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -1,265 +1,261 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "Debugger" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "network"] } - Depends { name: "Aggregation" } + Depends { name: "Qt"; submodules: ["network", "widgets"] } + + Depends { name: "Aggregation" } + Depends { name: "CPlusPlus" } + Depends { name: "LanguageUtils" } + Depends { name: "QmlDebug" } + Depends { name: "QmlJS" } + Depends { name: "Utils" } + + Depends { name: "Core" } + Depends { name: "CppEditor" } + Depends { name: "ProjectExplorer" } + Depends { name: "QtSupport" } + Depends { name: "TextEditor" } + + pluginRecommends: ["BinEditor"] + pluginTestDepends: ["QmakeProjectManager"] + + cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"]) + cpp.enableExceptions: true + + Group { + name: "General" + files: [ + "breakhandler.cpp", "breakhandler.h", + "breakpoint.cpp", "breakpoint.h", + "commonoptionspage.cpp", "commonoptionspage.h", + "debugger.qrc", + "debugger_global.h", "debuggertr.h", + "debuggeractions.cpp", "debuggeractions.h", + "debuggerconstants.h", + "debuggericons.h", "debuggericons.cpp", + "debuggercore.h", + "debuggerdialogs.cpp", "debuggerdialogs.h", + "debuggerengine.cpp", "debuggerengine.h", + "debuggerinternalconstants.h", + "debuggeritem.cpp", "debuggeritem.h", + "debuggeritemmanager.cpp", "debuggeritemmanager.h", + "debuggerkitaspect.cpp", "debuggerkitaspect.h", + "debuggermainwindow.cpp", "debuggermainwindow.h", + "debuggerplugin.cpp", "debuggerplugin.h", + "debuggerprotocol.cpp", "debuggerprotocol.h", + "debuggerrunconfigurationaspect.cpp", "debuggerrunconfigurationaspect.h", + "debuggerruncontrol.cpp", "debuggerruncontrol.h", + "debuggersourcepathmappingwidget.cpp", "debuggersourcepathmappingwidget.h", + "debuggertooltipmanager.cpp", "debuggertooltipmanager.h", + "disassembleragent.cpp", "disassembleragent.h", + "disassemblerlines.cpp", "disassemblerlines.h", + "enginemanager.cpp", "enginemanager.h", + "imageviewer.cpp", "imageviewer.h", + "loadcoredialog.cpp", "loadcoredialog.h", + "localsandexpressionswindow.cpp", "localsandexpressionswindow.h", + "logwindow.cpp", "logwindow.h", + "memoryagent.cpp", "memoryagent.h", + "moduleshandler.cpp", "moduleshandler.h", + "outputcollector.cpp", "outputcollector.h", + "peripheralregisterhandler.cpp", "peripheralregisterhandler.h", + "procinterrupt.cpp", "procinterrupt.h", + "registerhandler.cpp", "registerhandler.h", + "sourceagent.cpp", "sourceagent.h", + "sourcefileshandler.cpp", "sourcefileshandler.h", + "sourceutils.cpp", "sourceutils.h", + "stackframe.cpp", "stackframe.h", + "stackhandler.cpp", "stackhandler.h", + "stackwindow.cpp", "stackwindow.h", + "terminal.cpp", "terminal.h", + "threaddata.h", + "threadshandler.cpp", "threadshandler.h", + "watchdata.cpp", "watchdata.h", + "watchdelegatewidgets.cpp", "watchdelegatewidgets.h", + "watchhandler.cpp", "watchhandler.h", + "watchutils.cpp", "watchutils.h", + "watchwindow.cpp", "watchwindow.h", + "simplifytype.cpp", "simplifytype.h", + "unstartedappwatcherdialog.cpp", "unstartedappwatcherdialog.h" + ] + } + + Group { + name: "cdb" + prefix: "cdb/" + files: [ + "cdbengine.cpp", "cdbengine.h", + "cdboptionspage.cpp", "cdboptionspage.h", + "cdbparsehelpers.cpp", "cdbparsehelpers.h", + "stringinputstream.cpp", "stringinputstream.h", + ] + } + + Group { + name: "gdb" + prefix: "gdb/" + files: [ + "gdbengine.cpp", "gdbengine.h", + "gdbsettings.cpp", "gdbsettings.h", + ] + } + + Group { + name: "lldb" + prefix: "lldb/" + files: [ + "lldbengine.cpp", "lldbengine.h" + ] + } + + Group { + name: "pdb" + prefix: "pdb/" + files: ["pdbengine.cpp", "pdbengine.h"] + } + + Group { + name: "dap" + prefix: "dap/" + files: [ + "cmakedapengine.cpp", "cmakedapengine.h", + "dapclient.cpp", "dapclient.h", + "dapengine.cpp", "dapengine.h", + "gdbdapengine.cpp", "gdbdapengine.h", + "pydapengine.cpp", "pydapengine.h", + ] + } + + Group { + name: "uvsc" + prefix: "uvsc/" + files: [ + "uvscclient.cpp", "uvscclient.h", + "uvscdatatypes.h", + "uvscengine.cpp", "uvscengine.h", + "uvscfunctions.h", + "uvscutils.cpp", "uvscutils.h", + ] + } + + Group { + name: "QML Debugger" + prefix: "qml/" + files: [ + "interactiveinterpreter.cpp", "interactiveinterpreter.h", + "qmlengine.cpp", "qmlengine.h", + "qmlengineutils.cpp", "qmlengineutils.h", + "qmlinspectoragent.cpp", "qmlinspectoragent.h", + "qmlv8debuggerclientconstants.h" + ] + } + + Group { + name: "Debugger Console" + prefix: "console/" + files: [ + "consoleitem.cpp", "consoleitem.h", + "consoleedit.cpp", "consoleedit.h", + "consoleitemdelegate.cpp", "consoleitemdelegate.h", + "consoleitemmodel.cpp", "consoleitemmodel.h", + "console.cpp", "console.h", + "consoleproxymodel.cpp", "consoleproxymodel.h", + "consoleview.cpp", "consoleview.h" + ] + } + + Group { + name: "shared" + prefix: "shared/" + files: [ + "cdbsymbolpathlisteditor.cpp", + "cdbsymbolpathlisteditor.h", + "hostutils.cpp", "hostutils.h", + "peutils.cpp", "peutils.h", + "symbolpathsdialog.cpp", "symbolpathsdialog.h" + ] + } + + Group { + name: "Images" + prefix: "images/" + files: ["*.png"] + } + + Group { + name: "Images/qml" + prefix: "images/qml/" + files: ["*.png"] + } + + Group { + name: "Images/analyzer" + prefix: "analyzer/images/" + files: ["*.png"] + } + + Group { + name: "RegistryAccess" + condition: qbs.targetOS.contains("windows") + prefix: project.sharedSourcesDir + "/registryaccess/" + files: [ + "registryaccess.cpp", + "registryaccess.h", + ] + } + + Group { + name: "RegisterPostMortem" + condition: qbs.targetOS.contains("windows") + files: [ + "registerpostmortemaction.cpp", + "registerpostmortemaction.h", + ] + } + + Properties { + condition: qbs.targetOS.contains("windows") + cpp.dynamicLibraries: [ + "advapi32", + "ole32", + "shell32" + ] + } + + Group { + name: "Analyzer" + prefix: "analyzer/" + files: [ + "analyzerbase.qrc", + "analyzerconstants.h", + "analyzericons.h", + "analyzermanager.h", + "analyzerrunconfigwidget.cpp", + "analyzerrunconfigwidget.h", + "analyzerutils.cpp", + "analyzerutils.h", + "detailederrorview.cpp", + "detailederrorview.h", + "diagnosticlocation.cpp", + "diagnosticlocation.h", + "startremotedialog.cpp", + "startremotedialog.h", + ] + } + + QtcTestFiles { + files: [ + "debuggerunittests.qrc", + ] + } + + Group { + name: "Unit test resources" + prefix: "unit-tests/" + fileTags: [] + files: ["**/*"] + } + + Export { Depends { name: "CPlusPlus" } - Depends { name: "QmlDebug" } - Depends { name: "LanguageUtils" } - Depends { name: "QmlJS" } - Depends { name: "Utils" } - - Depends { name: "Core" } - Depends { name: "CppEditor" } - Depends { name: "ProjectExplorer" } - Depends { name: "QtSupport" } - Depends { name: "TextEditor" } - Depends { name: "app_version_header" } - - pluginTestDepends: [ - "QmakeProjectManager" - ] - - cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"]) - cpp.enableExceptions: true - - pluginRecommends: [ - "CppEditor", - "BinEditor" - ] - - Group { - name: "General" - files: [ - "breakhandler.cpp", "breakhandler.h", - "breakpoint.cpp", "breakpoint.h", - "commonoptionspage.cpp", "commonoptionspage.h", - "debugger.qrc", - "debugger_global.h", "debuggertr.h", - "debuggeractions.cpp", "debuggeractions.h", - "debuggerconstants.h", - "debuggericons.h", "debuggericons.cpp", - "debuggercore.h", - "debuggerdialogs.cpp", "debuggerdialogs.h", - "debuggerengine.cpp", "debuggerengine.h", - "debuggerinternalconstants.h", - "debuggeritem.cpp", "debuggeritem.h", - "debuggeritemmanager.cpp", "debuggeritemmanager.h", - "debuggerkitinformation.cpp", "debuggerkitinformation.h", - "debuggermainwindow.cpp", "debuggermainwindow.h", - "debuggerplugin.cpp", "debuggerplugin.h", - "debuggerprotocol.cpp", "debuggerprotocol.h", - "debuggerrunconfigurationaspect.cpp", "debuggerrunconfigurationaspect.h", - "debuggerruncontrol.cpp", "debuggerruncontrol.h", - "debuggersourcepathmappingwidget.cpp", "debuggersourcepathmappingwidget.h", - "debuggertooltipmanager.cpp", "debuggertooltipmanager.h", - "disassembleragent.cpp", "disassembleragent.h", - "disassemblerlines.cpp", "disassemblerlines.h", - "enginemanager.cpp", "enginemanager.h", - "imageviewer.cpp", "imageviewer.h", - "loadcoredialog.cpp", "loadcoredialog.h", - "localsandexpressionswindow.cpp", "localsandexpressionswindow.h", - "logwindow.cpp", "logwindow.h", - "memoryagent.cpp", "memoryagent.h", - "moduleshandler.cpp", "moduleshandler.h", - "outputcollector.cpp", "outputcollector.h", - "peripheralregisterhandler.cpp", "peripheralregisterhandler.h", - "procinterrupt.cpp", "procinterrupt.h", - "registerhandler.cpp", "registerhandler.h", - "sourceagent.cpp", "sourceagent.h", - "sourcefileshandler.cpp", "sourcefileshandler.h", - "sourceutils.cpp", "sourceutils.h", - "stackframe.cpp", "stackframe.h", - "stackhandler.cpp", "stackhandler.h", - "stackwindow.cpp", "stackwindow.h", - "terminal.cpp", "terminal.h", - "threaddata.h", - "threadshandler.cpp", "threadshandler.h", - "watchdata.cpp", "watchdata.h", - "watchdelegatewidgets.cpp", "watchdelegatewidgets.h", - "watchhandler.cpp", "watchhandler.h", - "watchutils.cpp", "watchutils.h", - "watchwindow.cpp", "watchwindow.h", - "simplifytype.cpp", "simplifytype.h", - "unstartedappwatcherdialog.cpp", "unstartedappwatcherdialog.h" - ] - } - - Group { - name: "cdb" - prefix: "cdb/" - files: [ - "cdbengine.cpp", "cdbengine.h", - "cdboptionspage.cpp", "cdboptionspage.h", - "cdbparsehelpers.cpp", "cdbparsehelpers.h", - "stringinputstream.cpp", "stringinputstream.h", - ] - } - - Group { - name: "gdb" - prefix: "gdb/" - files: [ - "gdbengine.cpp", "gdbengine.h", - "gdboptionspage.cpp", - ] - } - - Group { - name: "lldb" - prefix: "lldb/" - files: [ - "lldbengine.cpp", "lldbengine.h" - ] - } - - Group { - name: "pdb" - prefix: "pdb/" - files: ["pdbengine.cpp", "pdbengine.h"] - } - - Group { - name: "dap" - prefix: "dap/" - files: ["dapengine.cpp", "dapengine.h"] - } - - Group { - name: "uvsc" - prefix: "uvsc/" - files: [ - "uvscclient.cpp", "uvscclient.h", - "uvscdatatypes.h", - "uvscengine.cpp", "uvscengine.h", - "uvscfunctions.h", - "uvscutils.cpp", "uvscutils.h", - ] - } - - Group { - name: "QML Debugger" - prefix: "qml/" - files: [ - "interactiveinterpreter.cpp", "interactiveinterpreter.h", - "qmlengine.cpp", "qmlengine.h", - "qmlengineutils.cpp", "qmlengineutils.h", - "qmlinspectoragent.cpp", "qmlinspectoragent.h", - "qmlv8debuggerclientconstants.h" - ] - } - - Group { - name: "Debugger Console" - prefix: "console/" - files: [ - "consoleitem.cpp", "consoleitem.h", - "consoleedit.cpp", "consoleedit.h", - "consoleitemdelegate.cpp", "consoleitemdelegate.h", - "consoleitemmodel.cpp", "consoleitemmodel.h", - "console.cpp", "console.h", - "consoleproxymodel.cpp", "consoleproxymodel.h", - "consoleview.cpp", "consoleview.h" - ] - } - - Group { - name: "shared" - prefix: "shared/" - files: [ - "cdbsymbolpathlisteditor.cpp", - "cdbsymbolpathlisteditor.h", - "hostutils.cpp", "hostutils.h", - "peutils.cpp", "peutils.h", - "symbolpathsdialog.cpp", "symbolpathsdialog.h" - ] - } - - Group { - name: "Images" - prefix: "images/" - files: ["*.png"] - } - - Group { - name: "Images/qml" - prefix: "images/qml/" - files: ["*.png"] - } - - Group { - name: "Images/analyzer" - prefix: "analyzer/images/" - files: ["*.png"] - } - - Group { - name: "RegistryAccess" - condition: qbs.targetOS.contains("windows") - prefix: project.sharedSourcesDir + "/registryaccess/" - files: [ - "registryaccess.cpp", - "registryaccess.h", - ] - } - - Group { - name: "RegisterPostMortem" - condition: qbs.targetOS.contains("windows") - files: [ - "registerpostmortemaction.cpp", - "registerpostmortemaction.h", - ] - } - - Properties { - condition: qbs.targetOS.contains("windows") - cpp.dynamicLibraries: [ - "advapi32", - "ole32", - "shell32" - ] - } - - Group { - name: "Analyzer" - prefix: "analyzer/" - files: [ - "analyzerbase.qrc", - "analyzerconstants.h", - "analyzericons.h", - "analyzermanager.h", - "analyzerrunconfigwidget.cpp", - "analyzerrunconfigwidget.h", - "analyzerutils.cpp", - "analyzerutils.h", - "detailederrorview.cpp", - "detailederrorview.h", - "diagnosticlocation.cpp", - "diagnosticlocation.h", - "startremotedialog.cpp", - "startremotedialog.h", - ] - } - - QtcTestFiles { - files: [ - "debuggerunittests.qrc", - ] - } - - Group { - name: "Unit test resources" - prefix: "unit-tests/" - fileTags: [] - files: ["**/*"] - } - - Export { - Depends { name: "CPlusPlus" } - } } } diff --git a/src/plugins/debugger/debuggeractions.cpp b/src/plugins/debugger/debuggeractions.cpp index 907e626f4dc..5c313d1be53 100644 --- a/src/plugins/debugger/debuggeractions.cpp +++ b/src/plugins/debugger/debuggeractions.cpp @@ -7,12 +7,7 @@ #include "debuggericons.h" #include "debuggerinternalconstants.h" #include "debuggertr.h" - -#ifdef Q_OS_WIN -#include "registerpostmortemaction.h" -#endif - -#include <app/app_version.h> +#include "gdb/gdbsettings.h" #include <coreplugin/coreconstants.h> #include <coreplugin/icore.h> @@ -21,34 +16,78 @@ #include <utils/qtcassert.h> #include <QDebug> +#include <QGuiApplication> using namespace Utils; namespace Debugger::Internal { -const char debugModeSettingsGroupC[] = "DebugMode"; -const char cdbSettingsGroupC[] = "CDB2"; - -////////////////////////////////////////////////////////////////////////// -// // DebuggerSettings -// -////////////////////////////////////////////////////////////////////////// -static DebuggerSettings *theDebuggerSettings_ = nullptr; - -DebuggerSettings *debuggerSettings() +DebuggerSettings &settings() { - QTC_CHECK(theDebuggerSettings_); - return theDebuggerSettings_; + static DebuggerSettings settings; + return settings; } -DebuggerSettings::DebuggerSettings() -{ - theDebuggerSettings_ = this; +DebuggerSettings::DebuggerSettings() : + useAlternatingRowColors{commonSettings().useAlternatingRowColors}, + useAnnotationsInMainEditor{commonSettings().useAnnotationsInMainEditor}, + useToolTipsInMainEditor{commonSettings().useToolTipsInMainEditor}, + closeSourceBuffersOnExit{commonSettings().closeSourceBuffersOnExit}, + closeMemoryBuffersOnExit{commonSettings().closeMemoryBuffersOnExit}, + raiseOnInterrupt{commonSettings().raiseOnInterrupt}, + breakpointsFullPathByDefault{commonSettings().breakpointsFullPathByDefault}, + warnOnReleaseBuilds{commonSettings().warnOnReleaseBuilds}, + maximalStackDepth{commonSettings().maximalStackDepth}, - const QString debugModeGroup(debugModeSettingsGroupC); - const QString cdbSettingsGroup(cdbSettingsGroupC); + fontSizeFollowsEditor{commonSettings().fontSizeFollowsEditor}, + switchModeOnExit{commonSettings().switchModeOnExit}, + showQmlObjectTree{commonSettings().showQmlObjectTree}, + stationaryEditorWhileStepping{commonSettings().stationaryEditorWhileStepping}, + forceLoggingToConsole{commonSettings().forceLoggingToConsole}, + + sourcePathMap{commonSettings().sourcePathMap}, + registerForPostMortem{*commonSettings().registerForPostMortem}, + + gdbWatchdogTimeout{gdbSettings().gdbWatchdogTimeout}, + skipKnownFrames{gdbSettings().skipKnownFrames}, + useMessageBoxForSignals{gdbSettings().useMessageBoxForSignals}, + adjustBreakpointLocations{gdbSettings().adjustBreakpointLocations}, + useDynamicType{gdbSettings().useDynamicType}, + loadGdbInit{gdbSettings().loadGdbInit}, + loadGdbDumpers{gdbSettings().loadGdbDumpers}, + intelFlavor{gdbSettings().intelFlavor}, + usePseudoTracepoints{gdbSettings().usePseudoTracepoints}, + useIndexCache{gdbSettings().useIndexCache}, + gdbStartupCommands{gdbSettings().gdbStartupCommands}, + gdbPostAttachCommands{gdbSettings().gdbPostAttachCommands}, + targetAsync{gdbSettings().targetAsync}, + autoEnrichParameters{gdbSettings().autoEnrichParameters}, + breakOnThrow{gdbSettings().breakOnThrow}, + breakOnCatch{gdbSettings().breakOnCatch}, + breakOnWarning{gdbSettings().breakOnWarning}, + breakOnFatal{gdbSettings().breakOnFatal}, + breakOnAbort{gdbSettings().breakOnAbort}, + enableReverseDebugging{gdbSettings().enableReverseDebugging}, + multiInferior{gdbSettings().multiInferior}, + + // Page 4 + useDebuggingHelpers{localsAndExpressionSettings().useDebuggingHelpers}, + useCodeModel{localsAndExpressionSettings().useCodeModel}, + showThreadNames{localsAndExpressionSettings().showThreadNames}, + extraDumperFile{localsAndExpressionSettings().extraDumperFile}, // For loading a file. Recommended. + extraDumperCommands{localsAndExpressionSettings().extraDumperCommands}, // To modify an existing setup. + + showStdNamespace{localsAndExpressionSettings().showStdNamespace}, + showQtNamespace{localsAndExpressionSettings().showQtNamespace}, + showQObjectNames{localsAndExpressionSettings().showQObjectNames}, + maximalStringLength{localsAndExpressionSettings().maximalStringLength}, + displayStringLimit{localsAndExpressionSettings().displayStringLimit}, + defaultArraySize{localsAndExpressionSettings().defaultArraySize} +{ + const Key debugModeGroup("DebugMode"); + const Key cdbSettingsGroup("CDB2"); settingsDialog.setLabelText(Tr::tr("Configure Debugger...")); @@ -85,37 +124,6 @@ DebuggerSettings::DebuggerSettings() alwaysAdjustColumnWidths.setSettingsKey(debugModeGroup, "AlwaysAdjustColumnWidths"); alwaysAdjustColumnWidths.setDefaultValue(true); - // Needed by QML Inspector - //useAlternatingRowColors.setLabelText(Tr::tr("Use Alternating Row Colors")); - useAlternatingRowColors.setSettingsKey(debugModeGroup, "UseAlternatingRowColours"); - useAlternatingRowColors.setLabelText(Tr::tr("Use alternating row colors in debug views")); - - stationaryEditorWhileStepping.setSettingsKey(debugModeGroup, "StationaryEditorWhileStepping"); - stationaryEditorWhileStepping.setLabelText(Tr::tr("Keep editor stationary when stepping")); - stationaryEditorWhileStepping.setToolTip(Tr::tr("Scrolls the editor only when it is necessary " - "to keep the current line in view, " - "instead of keeping the next statement centered at " - "all times.")); - - forceLoggingToConsole.setSettingsKey(debugModeGroup, "ForceLoggingToConsole"); - forceLoggingToConsole.setLabelText(Tr::tr("Force logging to console")); - forceLoggingToConsole.setToolTip(Tr::tr("Sets QT_LOGGING_TO_CONSOLE=1 in the environment " - "of the debugged program, preventing storing debug output " - "in system logs.")); - - fontSizeFollowsEditor.setSettingsKey(debugModeGroup, "FontSizeFollowsEditor"); - fontSizeFollowsEditor.setToolTip(Tr::tr("Changes the font size in the debugger views when " - "the font size in the main editor changes.")); - fontSizeFollowsEditor.setLabelText(Tr::tr("Debugger font size follows main editor")); - - useMessageBoxForSignals.setSettingsKey(debugModeGroup, "UseMessageBoxForSignals"); - useMessageBoxForSignals.setDefaultValue(true); - useMessageBoxForSignals.setLabelText(Tr::tr( - "Show a message box when receiving a signal")); - useMessageBoxForSignals.setToolTip(Tr::tr( - "Displays a message box as soon as your application\n" - "receives a signal like SIGSEGV during debugging.")); - logTimeStamps.setLabelText(Tr::tr("Log Time Stamps")); logTimeStamps.setSettingsKey(debugModeGroup, "LogTimeStamps"); @@ -142,11 +150,9 @@ DebuggerSettings::DebuggerSettings() cdbBreakEvents.setSettingsKey(cdbSettingsGroup, "BreakEvent"); cdbBreakOnCrtDbgReport.setSettingsKey(cdbSettingsGroup, "BreakOnCrtDbgReport"); - cdbBreakOnCrtDbgReport.setLabelText( - CommonOptionsPage::msgSetBreakpointAtFunction(Constants::CRT_DEBUG_REPORT)); - cdbBreakOnCrtDbgReport.setToolTip( - CommonOptionsPage::msgSetBreakpointAtFunctionToolTip(Constants::CRT_DEBUG_REPORT, - Tr::tr("Catches runtime error messages caused by assert(), for example."))); + cdbBreakOnCrtDbgReport.setLabelText(msgSetBreakpointAtFunction(Constants::CRT_DEBUG_REPORT)); + cdbBreakOnCrtDbgReport.setToolTip(msgSetBreakpointAtFunctionToolTip(Constants::CRT_DEBUG_REPORT, + Tr::tr("Catches runtime error messages caused by assert(), for example."))); useCdbConsole.setSettingsKey(cdbSettingsGroup, "CDB_Console"); useCdbConsole.setToolTip("<html><head/><body><p>" + Tr::tr( @@ -183,258 +189,19 @@ DebuggerSettings::DebuggerSettings() // // Locals & Watchers - // - showStdNamespace.setSettingsKey(debugModeGroup, "ShowStandardNamespace"); - showStdNamespace.setDefaultValue(true); - showStdNamespace.setDisplayName(Tr::tr("Show \"std::\" Namespace in Types")); - showStdNamespace.setLabelText(Tr::tr("Show \"std::\" namespace in types")); - showStdNamespace.setToolTip( - "<p>" + Tr::tr("Shows \"std::\" prefix for types from the standard library.")); - - showQtNamespace.setSettingsKey(debugModeGroup, "ShowQtNamespace"); - showQtNamespace.setDefaultValue(true); - showQtNamespace.setDisplayName(Tr::tr("Show Qt's Namespace in Types")); - showQtNamespace.setLabelText(Tr::tr("Show Qt's namespace in types")); - showQtNamespace.setToolTip("<p>" - + Tr::tr("Shows Qt namespace prefix for Qt types. This is only " - "relevant if Qt was configured with \"-qtnamespace\".")); - - showQObjectNames.setSettingsKey(debugModeGroup, "ShowQObjectNames2"); - showQObjectNames.setDefaultValue(true); - showQObjectNames.setDisplayName(Tr::tr("Show QObject names if available")); - showQObjectNames.setLabelText(Tr::tr("Show QObject names if available")); - showQObjectNames.setToolTip( - "<p>" - + Tr::tr("Displays the objectName property of QObject based items. " - "Note that this can negatively impact debugger performance " - "even if no QObjects are present.")); - sortStructMembers.setSettingsKey(debugModeGroup, "SortStructMembers"); sortStructMembers.setDisplayName(Tr::tr("Sort Members of Classes and Structs Alphabetically")); sortStructMembers.setLabelText(Tr::tr("Sort members of classes and structs alphabetically")); sortStructMembers.setDefaultValue(true); - // - // DebuggingHelper - // - useDebuggingHelpers.setSettingsKey(debugModeGroup, "UseDebuggingHelper"); - useDebuggingHelpers.setDefaultValue(true); - useDebuggingHelpers.setLabelText(Tr::tr("Use Debugging Helpers")); - - useCodeModel.setSettingsKey(debugModeGroup, "UseCodeModel"); - useCodeModel.setDefaultValue(true); - useCodeModel.setLabelText(Tr::tr("Use code model")); - useCodeModel.setToolTip( - "<p>" - + Tr::tr("Selecting this causes the C++ Code Model being asked " - "for variable scope information. This might result in slightly faster " - "debugger operation but may fail for optimized code.")); - - showThreadNames.setSettingsKey(debugModeGroup, "ShowThreadNames"); - showThreadNames.setLabelText(Tr::tr("Display thread names")); - showThreadNames.setToolTip("<p>" + Tr::tr("Displays names of QThread based threads.")); - // // Breakpoints // synchronizeBreakpoints.setLabelText(Tr::tr("Synchronize Breakpoints")); - adjustBreakpointLocations.setDisplayName(Tr::tr("Adjust Breakpoint Locations")); - adjustBreakpointLocations.setToolTip( - "<p>" - + Tr::tr("Not all source code lines generate " - "executable code. Putting a breakpoint on such a line acts as " - "if the breakpoint was set on the next line that generated code. " - "Selecting 'Adjust Breakpoint Locations' shifts the red " - "breakpoint markers in such cases to the location of the true " - "breakpoint.")); - adjustBreakpointLocations.setDefaultValue(true); - adjustBreakpointLocations.setSettingsKey(debugModeGroup, "AdjustBreakpointLocations"); - adjustBreakpointLocations.setLabelText(Tr::tr( - "Adjust breakpoint locations")); - adjustBreakpointLocations.setToolTip(Tr::tr( - "GDB allows setting breakpoints on source lines for which no code \n" - "was generated. In such situations the breakpoint is shifted to the\n" - "next source code line for which code was actually generated.\n" - "This option reflects such temporary change by moving the breakpoint\n" - "markers in the source code editor.")); - - - breakOnThrow.setLabelText(Tr::tr("Break on \"throw\"")); - breakOnThrow.setSettingsKey(debugModeGroup, "BreakOnThrow"); - - breakOnCatch.setLabelText(Tr::tr("Break on \"catch\"")); - breakOnCatch.setSettingsKey(debugModeGroup, "BreakOnCatch"); - - breakOnWarning.setLabelText(Tr::tr("Break on \"qWarning\"")); - breakOnWarning.setSettingsKey(debugModeGroup, "BreakOnWarning"); - // FIXME: Move to common settings page. - breakOnWarning.setLabelText(CommonOptionsPage::msgSetBreakpointAtFunction("qWarning")); - breakOnWarning.setToolTip(CommonOptionsPage::msgSetBreakpointAtFunctionToolTip("qWarning")); - - breakOnFatal.setLabelText(Tr::tr("Break on \"qFatal\"")); - breakOnFatal.setSettingsKey(debugModeGroup, "BreakOnFatal"); - breakOnFatal.setLabelText(CommonOptionsPage::msgSetBreakpointAtFunction("qFatal")); - breakOnFatal.setToolTip(CommonOptionsPage::msgSetBreakpointAtFunctionToolTip("qFatal")); - - breakOnAbort.setLabelText(Tr::tr("Break on \"abort\"")); - breakOnAbort.setSettingsKey(debugModeGroup, "BreakOnAbort"); - breakOnAbort.setLabelText(CommonOptionsPage::msgSetBreakpointAtFunction("abort")); - breakOnAbort.setToolTip(CommonOptionsPage::msgSetBreakpointAtFunctionToolTip("abort")); - - // - // Settings - // - - loadGdbInit.setSettingsKey(debugModeGroup, "LoadGdbInit"); - loadGdbInit.setDefaultValue(true); - loadGdbInit.setLabelText(Tr::tr("Load .gdbinit file on startup")); - loadGdbInit.setToolTip(Tr::tr( - "Allows or inhibits reading the user's default\n" - ".gdbinit file on debugger startup.")); - - loadGdbDumpers.setSettingsKey(debugModeGroup, "LoadGdbDumpers2"); - loadGdbDumpers.setLabelText(Tr::tr("Load system GDB pretty printers")); - loadGdbDumpers.setToolTip(Tr::tr( - "Uses the default GDB pretty printers installed in your " - "system or linked to the libraries your application uses.")); - - autoEnrichParameters.setSettingsKey(debugModeGroup, "AutoEnrichParameters"); - autoEnrichParameters.setDefaultValue(true); - autoEnrichParameters.setLabelText(Tr::tr( - "Use common locations for debug information")); - autoEnrichParameters.setToolTip(Tr::tr( - "<html><head/><body>Adds common paths to locations " - "of debug information such as <i>/usr/src/debug</i> " - "when starting GDB.</body></html>")); - - useDynamicType.setSettingsKey(debugModeGroup, "UseDynamicType"); - useDynamicType.setDefaultValue(true); - useDynamicType.setDisplayName(Tr::tr("Use Dynamic Object Type for Display")); - useDynamicType.setLabelText(Tr::tr( - "Use dynamic object type for display")); - useDynamicType.setToolTip(Tr::tr( - "Specifies whether the dynamic or the static type of objects will be " - "displayed. Choosing the dynamic type might be slower.")); - - targetAsync.setSettingsKey(debugModeGroup, "TargetAsync"); - targetAsync.setLabelText(Tr::tr( - "Use asynchronous mode to control the inferior")); - - warnOnReleaseBuilds.setSettingsKey(debugModeGroup, "WarnOnReleaseBuilds"); - warnOnReleaseBuilds.setDefaultValue(true); - warnOnReleaseBuilds.setLabelText(Tr::tr("Warn when debugging \"Release\" builds")); - warnOnReleaseBuilds.setToolTip(Tr::tr("Shows a warning when starting the debugger " - "on a binary with insufficient debug information.")); - - QString howToUsePython = Tr::tr( - "<p>To execute simple Python commands, prefix them with \"python\".</p>" - "<p>To execute sequences of Python commands spanning multiple lines " - "prepend the block with \"python\" on a separate line, and append " - "\"end\" on a separate line.</p>" - "<p>To execute arbitrary Python scripts, " - "use <i>python execfile('/path/to/script.py')</i>.</p>"); - - gdbStartupCommands.setSettingsKey(debugModeGroup, "GdbStartupCommands"); - gdbStartupCommands.setDisplayStyle(StringAspect::TextEditDisplay); - gdbStartupCommands.setUseGlobalMacroExpander(); - gdbStartupCommands.setToolTip("<html><head/><body><p>" + Tr::tr( - "GDB commands entered here will be executed after " - "GDB has been started, but before the debugged program is started or " - "attached, and before the debugging helpers are initialized.") + "</p>" - + howToUsePython + "</body></html>"); - - gdbPostAttachCommands.setSettingsKey(debugModeGroup, "GdbPostAttachCommands"); - gdbPostAttachCommands.setDisplayStyle(StringAspect::TextEditDisplay); - gdbPostAttachCommands.setUseGlobalMacroExpander(); - gdbPostAttachCommands.setToolTip("<html><head/><body><p>" + Tr::tr( - "GDB commands entered here will be executed after " - "GDB has successfully attached to remote targets.</p>" - "<p>You can add commands to further set up the target here, " - "such as \"monitor reset\" or \"load\".") + "</p>" - + howToUsePython + "</body></html>"); - - extraDumperCommands.setSettingsKey(debugModeGroup, "GdbCustomDumperCommands"); - extraDumperCommands.setDisplayStyle(StringAspect::TextEditDisplay); - extraDumperCommands.setUseGlobalMacroExpander(); - extraDumperCommands.setToolTip("<html><head/><body><p>" - + Tr::tr("Python commands entered here will be executed after built-in " - "debugging helpers have been loaded and fully initialized. You can " - "load additional debugging helpers or modify existing ones here.") - + "</p></body></html>"); - - extraDumperFile.setSettingsKey(debugModeGroup, "ExtraDumperFile"); - extraDumperFile.setDisplayName(Tr::tr("Extra Debugging Helpers")); - // Label text is intentional empty in the GUI. - extraDumperFile.setToolTip(Tr::tr("Path to a Python file containing additional data dumpers.")); - - const QString t = Tr::tr("Stopping and stepping in the debugger " - "will automatically open views associated with the current location.") + '\n'; - - closeSourceBuffersOnExit.setSettingsKey(debugModeGroup, "CloseBuffersOnExit"); - closeSourceBuffersOnExit.setLabelText(Tr::tr("Close temporary source views on debugger exit")); - closeSourceBuffersOnExit.setToolTip(t + Tr::tr("Closes automatically opened source views when the debugger exits.")); - - closeMemoryBuffersOnExit.setSettingsKey(debugModeGroup, "CloseMemoryBuffersOnExit"); - closeMemoryBuffersOnExit.setDefaultValue(true); - closeMemoryBuffersOnExit.setLabelText(Tr::tr("Close temporary memory views on debugger exit")); - closeMemoryBuffersOnExit.setToolTip(t + Tr::tr("Closes automatically opened memory views when the debugger exits.")); - - switchModeOnExit.setSettingsKey(debugModeGroup, "SwitchModeOnExit"); - switchModeOnExit.setLabelText(Tr::tr("Switch to previous mode on debugger exit")); - - breakpointsFullPathByDefault.setSettingsKey(debugModeGroup, "BreakpointsFullPath"); - breakpointsFullPathByDefault.setToolTip(Tr::tr("Enables a full file path in breakpoints by default also for GDB.")); - breakpointsFullPathByDefault.setLabelText(Tr::tr("Set breakpoints using a full absolute path")); - - raiseOnInterrupt.setSettingsKey(debugModeGroup, "RaiseOnInterrupt"); - raiseOnInterrupt.setDefaultValue(true); - raiseOnInterrupt.setLabelText(Tr::tr("Bring %1 to foreground when application interrupts") - .arg(Core::Constants::IDE_DISPLAY_NAME)); - autoQuit.setSettingsKey(debugModeGroup, "AutoQuit"); autoQuit.setLabelText(Tr::tr("Automatically Quit Debugger")); - multiInferior.setSettingsKey(debugModeGroup, "MultiInferior"); - multiInferior.setLabelText(Tr::tr("Debug all child processes")); - multiInferior.setToolTip(Tr::tr( - "<html><head/><body>Keeps debugging all children after a fork." - "</body></html>")); - - intelFlavor.setSettingsKey(debugModeGroup, "IntelFlavor"); - intelFlavor.setLabelText(Tr::tr("Use Intel style disassembly")); - intelFlavor.setToolTip(Tr::tr("GDB shows by default AT&&T style disassembly.")); - - useAnnotationsInMainEditor.setSettingsKey(debugModeGroup, "UseAnnotations"); - useAnnotationsInMainEditor.setLabelText(Tr::tr("Use annotations in main editor when debugging")); - useAnnotationsInMainEditor.setToolTip( - "<p>" - + Tr::tr("Shows simple variable values " - "as annotations in the main editor during debugging.")); - useAnnotationsInMainEditor.setDefaultValue(true); - - usePseudoTracepoints.setSettingsKey(debugModeGroup, "UsePseudoTracepoints"); - usePseudoTracepoints.setLabelText(Tr::tr("Use pseudo message tracepoints")); - usePseudoTracepoints.setToolTip(Tr::tr("Uses Python to extend the ordinary GDB breakpoint class.")); - usePseudoTracepoints.setDefaultValue(true); - - useIndexCache.setSettingsKey(debugModeGroup, "UseIndexCache"); - useIndexCache.setLabelText(Tr::tr("Use automatic symbol cache")); - useIndexCache.setToolTip(Tr::tr("It is possible for GDB to automatically save a copy of " - "its symbol index in a cache on disk and retrieve it from there when loading the same " - "binary in the future.")); - useIndexCache.setDefaultValue(true); - - useToolTipsInMainEditor.setSettingsKey(debugModeGroup, "UseToolTips"); - useToolTipsInMainEditor.setLabelText(Tr::tr("Use tooltips in main editor when debugging")); - useToolTipsInMainEditor.setToolTip( - "<p>" - + Tr::tr("Enables tooltips for variable " - "values during debugging. Since this can slow down debugging and " - "does not provide reliable information as it does not use scope " - "information, it is switched off by default.")); - useToolTipsInMainEditor.setDefaultValue(true); - useToolTipsInLocalsView.setSettingsKey(debugModeGroup, "UseToolTipsInLocalsView"); useToolTipsInLocalsView.setLabelText(Tr::tr("Use Tooltips in Locals View when Debugging")); useToolTipsInLocalsView.setToolTip("<p>" @@ -454,42 +221,6 @@ DebuggerSettings::DebuggerSettings() "view during debugging.")); useToolTipsInStackView.setDefaultValue(true); - skipKnownFrames.setSettingsKey(debugModeGroup, "SkipKnownFrames"); - skipKnownFrames.setDisplayName(Tr::tr("Skip Known Frames")); - skipKnownFrames.setLabelText(Tr::tr("Skip known frames when stepping")); - skipKnownFrames.setToolTip(Tr::tr( - "<html><head/><body><p>" - "Allows <i>Step Into</i> to compress several steps into one step\n" - "for less noisy debugging. For example, the atomic reference\n" - "counting code is skipped, and a single <i>Step Into</i> for a signal\n" - "emission ends up directly in the slot connected to it.")); - - enableReverseDebugging.setSettingsKey(debugModeGroup, "EnableReverseDebugging"); - enableReverseDebugging.setIcon(Icons::REVERSE_MODE.icon()); - enableReverseDebugging.setDisplayName(Tr::tr("Enable Reverse Debugging")); - enableReverseDebugging.setLabelText(Tr::tr("Enable reverse debugging")); - enableReverseDebugging.setToolTip(Tr::tr( - "<html><head/><body><p>Enables stepping backwards.</p><p>" - "<b>Note:</b> This feature is very slow and unstable on the GDB side. " - "It exhibits unpredictable behavior when going backwards over system " - "calls and is very likely to destroy your debugging session.</p></body></html>")); - - -#ifdef Q_OS_WIN - registerForPostMortem = new RegisterPostMortemAction; - registerForPostMortem->setSettingsKey(debugModeGroup, "RegisterForPostMortem"); - registerForPostMortem->setToolTip( - Tr::tr("Registers %1 for debugging crashed applications.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); - registerForPostMortem->setLabelText( - Tr::tr("Use %1 for post-mortem debugging") - .arg(Core::Constants::IDE_DISPLAY_NAME)); -#else - // Some dummy. - registerForPostMortem = new BoolAspect; - registerForPostMortem->setVisible(false); -#endif - allPluginBreakpoints.setSettingsKey(debugModeGroup, "AllPluginBreakpoints"); allPluginBreakpoints.setDefaultValue(true); @@ -500,125 +231,16 @@ DebuggerSettings::DebuggerSettings() selectedPluginBreakpointsPattern.setSettingsKey(debugModeGroup, "SelectedPluginBreakpointsPattern"); selectedPluginBreakpointsPattern.setDefaultValue(QString(".*")); - maximalStackDepth.setSettingsKey(debugModeGroup, "MaximalStackDepth"); - maximalStackDepth.setDefaultValue(20); - maximalStackDepth.setSpecialValueText(Tr::tr("<unlimited>")); - maximalStackDepth.setRange(0, 1000); - maximalStackDepth.setSingleStep(5); - maximalStackDepth.setLabelText(Tr::tr("Maximum stack depth:")); - - displayStringLimit.setSettingsKey(debugModeGroup, "DisplayStringLimit"); - displayStringLimit.setDefaultValue(300); - displayStringLimit.setSpecialValueText(Tr::tr("<unlimited>")); - displayStringLimit.setRange(20, 10000); - displayStringLimit.setSingleStep(10); - displayStringLimit.setLabelText(Tr::tr("Display string length:")); - displayStringLimit.setToolTip( - "<p>" - + Tr::tr("The maximum length of string entries in the " - "Locals and Expressions views. Longer than that are cut off " - "and displayed with an ellipsis attached.")); - - maximalStringLength.setSettingsKey(debugModeGroup, "MaximalStringLength"); - maximalStringLength.setDefaultValue(10000); - maximalStringLength.setSpecialValueText(Tr::tr("<unlimited>")); - maximalStringLength.setRange(20, 10000000); - maximalStringLength.setSingleStep(20); - maximalStringLength.setLabelText(Tr::tr("Maximum string length:")); - maximalStringLength.setToolTip( - "<p>" - + Tr::tr("The maximum length for strings in separated windows. " - "Longer strings are cut off and displayed with an ellipsis attached.")); - - defaultArraySize.setSettingsKey(debugModeGroup, "DefaultArraySize"); - defaultArraySize.setDefaultValue(100); - defaultArraySize.setRange(10, 1000000000); - defaultArraySize.setSingleStep(100); - defaultArraySize.setLabelText(Tr::tr("Default array size:")); - defaultArraySize.setToolTip("<p>" - + Tr::tr("The number of array elements requested when expanding " - "entries in the Locals and Expressions views.")); - expandStack.setLabelText(Tr::tr("Reload Full Stack")); createFullBacktrace.setLabelText(Tr::tr("Create Full Backtrace")); - gdbWatchdogTimeout.setSettingsKey(debugModeGroup, "WatchdogTimeout"); - gdbWatchdogTimeout.setDefaultValue(20); - gdbWatchdogTimeout.setSuffix(Tr::tr("sec")); - gdbWatchdogTimeout.setRange(20, 1000000); - gdbWatchdogTimeout.setLabelText(Tr::tr("GDB timeout:")); - gdbWatchdogTimeout.setToolTip(Tr::tr( - "The number of seconds before a non-responsive GDB process is terminated.\n" - "The default value of 20 seconds should be sufficient for most\n" - "applications, but there are situations when loading big libraries or\n" - "listing source files takes much longer than that on slow machines.\n" - "In this case, the value should be increased.")); - // // QML Tools // - showQmlObjectTree.setSettingsKey(debugModeGroup, "ShowQmlObjectTree"); - showQmlObjectTree.setDefaultValue(true); - showQmlObjectTree.setToolTip(Tr::tr("Shows QML object tree in Locals and Expressions " - "when connected and not stepping.")); - showQmlObjectTree.setLabelText(Tr::tr("Show QML object tree")); - - const QString qmlInspectorGroup = "QML.Inspector"; + const Key qmlInspectorGroup = "QML.Inspector"; showAppOnTop.setSettingsKey(qmlInspectorGroup, "QmlInspector.ShowAppOnTop"); - // Page 1 - page1.registerAspect(&useAlternatingRowColors); - page1.registerAspect(&useAnnotationsInMainEditor); - page1.registerAspect(&useToolTipsInMainEditor); - page1.registerAspect(&closeSourceBuffersOnExit); - page1.registerAspect(&closeMemoryBuffersOnExit); - page1.registerAspect(&raiseOnInterrupt); - page1.registerAspect(&breakpointsFullPathByDefault); - page1.registerAspect(&warnOnReleaseBuilds); - page1.registerAspect(&maximalStackDepth); - - page1.registerAspect(&fontSizeFollowsEditor); - page1.registerAspect(&switchModeOnExit); - page1.registerAspect(&showQmlObjectTree); - page1.registerAspect(&stationaryEditorWhileStepping); - page1.registerAspect(&forceLoggingToConsole); - - page1.registerAspect(&sourcePathMap); - - // Page 2 - page2.registerAspect(&gdbWatchdogTimeout); - page2.registerAspect(&skipKnownFrames); - page2.registerAspect(&useMessageBoxForSignals); - page2.registerAspect(&adjustBreakpointLocations); - page2.registerAspect(&useDynamicType); - page2.registerAspect(&loadGdbInit); - page2.registerAspect(&loadGdbDumpers); - page2.registerAspect(&intelFlavor); - page2.registerAspect(&usePseudoTracepoints); - page2.registerAspect(&useIndexCache); - page2.registerAspect(&gdbStartupCommands); - page2.registerAspect(&gdbPostAttachCommands); - page2.registerAspect(&targetAsync); - page2.registerAspect(&autoEnrichParameters); - page2.registerAspect(&breakOnWarning); - page2.registerAspect(&breakOnFatal); - page2.registerAspect(&breakOnAbort); - page2.registerAspect(&enableReverseDebugging); - page2.registerAspect(&multiInferior); - - // Page 4 - page4.registerAspect(&useDebuggingHelpers); - page4.registerAspect(&useCodeModel); - page4.registerAspect(&showThreadNames); - page4.registerAspect(&showStdNamespace); - page4.registerAspect(&showQtNamespace); - page4.registerAspect(&extraDumperFile); - page4.registerAspect(&extraDumperCommands); - page4.registerAspect(&showQObjectNames); - page4.registerAspect(&displayStringLimit); - page4.registerAspect(&maximalStringLength); - page4.registerAspect(&defaultArraySize); // Page 5 page5.registerAspect(&cdbAdditionalArguments); @@ -630,8 +252,6 @@ DebuggerSettings::DebuggerSettings() page5.registerAspect(&firstChanceExceptionTaskEntry); page5.registerAspect(&secondChanceExceptionTaskEntry); page5.registerAspect(&ignoreFirstChanceAccessViolation); - if (HostOsInfo::isWindowsHost()) - page5.registerAspect(registerForPostMortem); // Page 6 page6.registerAspect(&cdbSymbolPaths); @@ -645,13 +265,8 @@ DebuggerSettings::DebuggerSettings() all.registerAspect(&useToolTipsInStackView); all.registerAspect(&logTimeStamps); all.registerAspect(&sortStructMembers); - all.registerAspect(&breakOnThrow); // ?? - all.registerAspect(&breakOnCatch); // ?? // Collect all - all.registerAspects(page1); - all.registerAspects(page2); - all.registerAspects(page4); all.registerAspects(page5); all.registerAspects(page6); @@ -661,42 +276,33 @@ DebuggerSettings::DebuggerSettings() if (auto boolAspect = dynamic_cast<BoolAspect *>(aspect)) boolAspect->setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); }); -} -DebuggerSettings::~DebuggerSettings() -{ - delete registerForPostMortem; -} + all.readSettings(); -void DebuggerSettings::readSettings() -{ - all.readSettings(Core::ICore::settings()); -} + QObject::connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested, + &all, &AspectContainer::writeSettings); -void DebuggerSettings::writeSettings() const -{ - all.writeSettings(Core::ICore::settings()); } QString DebuggerSettings::dump() { - QStringList settings; - debuggerSettings()->all.forEachAspect([&settings](BaseAspect *aspect) { - QString key = aspect->settingsKey(); + QStringList msg; + settings().all.forEachAspect([&msg](BaseAspect *aspect) { + Key key = aspect->settingsKey(); if (!key.isEmpty()) { - const int pos = key.indexOf('/'); + const int pos = key.view().indexOf('/'); if (pos >= 0) - key = key.mid(pos); - const QString current = aspect->value().toString(); - const QString default_ = aspect->defaultValue().toString(); - QString setting = key + ": " + current + " (default: " + default_ + ')'; + key = key.toByteArray().mid(pos); + const QString current = aspect->variantValue().toString(); + const QString default_ = aspect->defaultVariantValue().toString(); + QString setting = stringFromKey(key) + ": " + current + " (default: " + default_ + ')'; if (current != default_) setting += " ***"; - settings << setting; + msg << setting; } }); - settings.sort(); - return "Debugger settings:\n" + settings.join('\n'); + msg.sort(); + return "Debugger settings:\n" + msg.join('\n'); } } // Debugger::Internal diff --git a/src/plugins/debugger/debuggeractions.h b/src/plugins/debugger/debuggeractions.h index a6fb497815f..f20d60839b0 100644 --- a/src/plugins/debugger/debuggeractions.h +++ b/src/plugins/debugger/debuggeractions.h @@ -3,115 +3,78 @@ #pragma once -#include <QCoreApplication> -#include <QHash> -#include <QMap> -#include <QVector> - #include <utils/aspects.h> +#include <QMap> + namespace Debugger::Internal { -class SourcePathMapAspectPrivate; - -// Entries starting with '(' are considered regular expressions in the ElfReader. -// This is useful when there are multiple build machines with different -// path, and the user would like to match anything up to some known -// directory to his local project. -// Syntax: (/home/.*)/KnownSubdir -> /home/my/project -using SourcePathMap = QMap<QString, QString>; - -class SourcePathMapAspect : public Utils::BaseAspect -{ -public: - SourcePathMapAspect(); - ~SourcePathMapAspect() override; - - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; - - void addToLayout(Layouting::LayoutItem &parent) override; - - QVariant volatileValue() const override; - void setVolatileValue(const QVariant &val) override; - - void readSettings(const QSettings *settings) override; - void writeSettings(QSettings *settings) const override; - - SourcePathMap value() const; - -private: - SourcePathMapAspectPrivate *d = nullptr; -}; - -class GeneralSettings -{ - GeneralSettings(); - ~GeneralSettings(); -}; - class DebuggerSettings { public: - explicit DebuggerSettings(); - ~DebuggerSettings(); + DebuggerSettings(); static QString dump(); // Page 1: General - Utils::BoolAspect useAlternatingRowColors; - Utils::BoolAspect useAnnotationsInMainEditor; - Utils::BoolAspect useToolTipsInMainEditor; - Utils::BoolAspect closeSourceBuffersOnExit; - Utils::BoolAspect closeMemoryBuffersOnExit; - Utils::BoolAspect raiseOnInterrupt; - Utils::BoolAspect breakpointsFullPathByDefault; - Utils::BoolAspect warnOnReleaseBuilds; - Utils::IntegerAspect maximalStackDepth; + Utils::BoolAspect &useAlternatingRowColors; + Utils::BoolAspect &useAnnotationsInMainEditor; + Utils::BoolAspect &useToolTipsInMainEditor; + Utils::BoolAspect &closeSourceBuffersOnExit; + Utils::BoolAspect &closeMemoryBuffersOnExit; + Utils::BoolAspect &raiseOnInterrupt; + Utils::BoolAspect &breakpointsFullPathByDefault; + Utils::BoolAspect &warnOnReleaseBuilds; + Utils::IntegerAspect &maximalStackDepth; - Utils::BoolAspect fontSizeFollowsEditor; - Utils::BoolAspect switchModeOnExit; - Utils::BoolAspect showQmlObjectTree; - Utils::BoolAspect stationaryEditorWhileStepping; - Utils::BoolAspect forceLoggingToConsole; + Utils::BoolAspect &fontSizeFollowsEditor; + Utils::BoolAspect &switchModeOnExit; + Utils::BoolAspect &showQmlObjectTree; + Utils::BoolAspect &stationaryEditorWhileStepping; + Utils::BoolAspect &forceLoggingToConsole; - SourcePathMapAspect sourcePathMap; + Utils::TypedAspect<QMap<QString, QString>> &sourcePathMap; - // Page 2: GDB - Utils::IntegerAspect gdbWatchdogTimeout; - Utils::BoolAspect skipKnownFrames; - Utils::BoolAspect useMessageBoxForSignals; - Utils::BoolAspect adjustBreakpointLocations; - Utils::BoolAspect useDynamicType; - Utils::BoolAspect loadGdbInit; - Utils::BoolAspect loadGdbDumpers; - Utils::BoolAspect intelFlavor; - Utils::BoolAspect usePseudoTracepoints; - Utils::BoolAspect useIndexCache; - Utils::StringAspect gdbStartupCommands; - Utils::StringAspect gdbPostAttachCommands; + Utils::BoolAspect ®isterForPostMortem; - // Page 3: GDB Extended - Utils::BoolAspect targetAsync; - Utils::BoolAspect autoEnrichParameters; - Utils::BoolAspect breakOnThrow; - Utils::BoolAspect breakOnCatch; - Utils::BoolAspect breakOnWarning; - Utils::BoolAspect breakOnFatal; - Utils::BoolAspect breakOnAbort; - Utils::BoolAspect enableReverseDebugging; - Utils::BoolAspect multiInferior; + // Page 2: Gdb + Utils::IntegerAspect &gdbWatchdogTimeout; + Utils::BoolAspect &skipKnownFrames; + Utils::BoolAspect &useMessageBoxForSignals; + Utils::BoolAspect &adjustBreakpointLocations; + Utils::BoolAspect &useDynamicType; + Utils::BoolAspect &loadGdbInit; + Utils::BoolAspect &loadGdbDumpers; + Utils::BoolAspect &intelFlavor; + Utils::BoolAspect &usePseudoTracepoints; + Utils::BoolAspect &useIndexCache; + Utils::StringAspect &gdbStartupCommands; + Utils::StringAspect &gdbPostAttachCommands; + + Utils::BoolAspect &targetAsync; + Utils::BoolAspect &autoEnrichParameters; + Utils::BoolAspect &breakOnThrow; + Utils::BoolAspect &breakOnCatch; + Utils::BoolAspect &breakOnWarning; + Utils::BoolAspect &breakOnFatal; + Utils::BoolAspect &breakOnAbort; + Utils::BoolAspect &enableReverseDebugging; + Utils::BoolAspect &multiInferior; // Page 4: Locals and expressions - Utils::BoolAspect useDebuggingHelpers; - Utils::BoolAspect useCodeModel; - Utils::BoolAspect showThreadNames; - Utils::FilePathAspect extraDumperFile; // For loading a file. Recommended. - Utils::StringAspect extraDumperCommands; // To modify an existing setup. + Utils::BoolAspect &useDebuggingHelpers; + Utils::BoolAspect &useCodeModel; + Utils::BoolAspect &showThreadNames; + Utils::FilePathAspect &extraDumperFile; // For loading a file. Recommended. + Utils::StringAspect &extraDumperCommands; // To modify an existing setup. - Utils::BoolAspect showStdNamespace; - Utils::BoolAspect showQtNamespace; - Utils::BoolAspect showQObjectNames; + Utils::BoolAspect &showStdNamespace; + Utils::BoolAspect &showQtNamespace; + Utils::BoolAspect &showQObjectNames; + + Utils::IntegerAspect &maximalStringLength; + Utils::IntegerAspect &displayStringLimit; + Utils::IntegerAspect &defaultArraySize; // Page 5: CDB Utils::StringAspect cdbAdditionalArguments; @@ -124,8 +87,6 @@ public: Utils::BoolAspect secondChanceExceptionTaskEntry; Utils::BoolAspect ignoreFirstChanceAccessViolation; - Utils::BoolAspect *registerForPostMortem = nullptr; - // Page 6: CDB Paths Utils::StringListAspect cdbSymbolPaths; Utils::StringListAspect cdbSourcePaths; @@ -144,9 +105,6 @@ public: // Watchers & Locals Utils::BoolAspect autoDerefPointers; - Utils::IntegerAspect maximalStringLength; - Utils::IntegerAspect displayStringLimit; - Utils::IntegerAspect defaultArraySize; Utils::BoolAspect sortStructMembers; Utils::BoolAspect useToolTipsInLocalsView; @@ -162,22 +120,17 @@ public: Utils::BoolAspect showAppOnTop; Utils::AspectContainer all; // All - Utils::AspectContainer page1; // General - Utils::AspectContainer page2; // GDB - Utils::AspectContainer page4; // Locals & Expressions Utils::AspectContainer page5; // CDB Utils::AspectContainer page6; // CDB Paths - void readSettings(); - void writeSettings() const; - private: DebuggerSettings(const DebuggerSettings &) = delete; DebuggerSettings &operator=(const DebuggerSettings &) = delete; }; -DebuggerSettings *debuggerSettings(); +DebuggerSettings &settings(); + +QString msgSetBreakpointAtFunction(const char *function); +QString msgSetBreakpointAtFunctionToolTip(const char *function, const QString &hint = {}); } // Debugger::Internal - -Q_DECLARE_METATYPE(Debugger::Internal::SourcePathMap) diff --git a/src/plugins/debugger/debuggerconstants.h b/src/plugins/debugger/debuggerconstants.h index 06ccb94f1b9..b071713a844 100644 --- a/src/plugins/debugger/debuggerconstants.h +++ b/src/plugins/debugger/debuggerconstants.h @@ -99,7 +99,6 @@ enum DebuggerEngineType CdbEngineType = 0x004, PdbEngineType = 0x008, LldbEngineType = 0x100, - DapEngineType = 0x200, UvscEngineType = 0x1000 }; diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index c382e91c175..2b8278b01e0 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -7,12 +7,10 @@ #include "debuggerruncontrol.h" #include "debuggertr.h" -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <projectexplorer/devicesupport/sshparameters.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/toolchain.h> @@ -30,6 +28,7 @@ #include <QDir> #include <QFormLayout> #include <QGroupBox> +#include <QGuiApplication> #include <QLabel> #include <QPlainTextEdit> #include <QPushButton> @@ -87,8 +86,8 @@ class StartApplicationParameters public: QString displayName() const; bool equals(const StartApplicationParameters &rhs) const; - void toSettings(QSettings *) const; - void fromSettings(const QSettings *settings); + void toSettings(QtcSettings *) const; + void fromSettings(const QtcSettings *settings); bool operator==(const StartApplicationParameters &p) const { return equals(p); } bool operator!=(const StartApplicationParameters &p) const { return !equals(p); } @@ -96,7 +95,7 @@ public: Id kitId; uint serverPort; QString serverAddress; - Runnable runnable; + ProcessRunData runnable; bool breakAtMain = false; bool runInTerminal = false; bool useTargetExtendedRemote = false; @@ -141,7 +140,7 @@ QString StartApplicationParameters::displayName() const return name; } -void StartApplicationParameters::toSettings(QSettings *settings) const +void StartApplicationParameters::toSettings(QtcSettings *settings) const { settings->setValue("LastKitId", kitId.toSetting()); settings->setValue("LastServerPort", serverPort); @@ -158,7 +157,7 @@ void StartApplicationParameters::toSettings(QSettings *settings) const settings->setValue("LastSysRoot", sysRoot.toSettings()); } -void StartApplicationParameters::fromSettings(const QSettings *settings) +void StartApplicationParameters::fromSettings(const QtcSettings *settings) { kitId = Id::fromSetting(settings->value("LastKitId")); serverPort = settings->value("LastServerPort").toUInt(); @@ -291,8 +290,11 @@ StartApplicationDialog::StartApplicationDialog(QWidget *parent) verticalLayout->addWidget(Layouting::createHr()); verticalLayout->addWidget(d->buttonBox); - connect(d->localExecutablePathChooser, &PathChooser::rawPathChanged, - this, &StartApplicationDialog::updateState); + connect(d->localExecutablePathChooser, + &PathChooser::validChanged, + this, + &StartApplicationDialog::updateState); + connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(d->historyComboBox, &QComboBox::currentIndexChanged, @@ -342,11 +344,11 @@ void StartApplicationDialog::updateState() void StartApplicationDialog::run(bool attachRemote) { - const QString settingsGroup = "DebugMode"; + const Key settingsGroup = "DebugMode"; const QString arrayName = "StartApplication"; QList<StartApplicationParameters> history; - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(settingsGroup); if (const int arraySize = settings->beginReadArray(arrayName)) { for (int i = 0; i < arraySize; ++i) { @@ -400,7 +402,7 @@ void StartApplicationDialog::run(bool attachRemote) } IDevice::ConstPtr dev = DeviceKitAspect::device(k); - Runnable inferior = newParameters.runnable; + ProcessRunData inferior = newParameters.runnable; const QString inputAddress = dialog.d->channelOverrideEdit->text(); if (!inputAddress.isEmpty()) debugger->setRemoteChannel(inputAddress); @@ -563,15 +565,18 @@ static QString cdbRemoteHelp() const QString ext32 = QDir::toNativeSeparators(CdbEngine::extensionLibraryName(false)); const QString ext64 = QDir::toNativeSeparators(CdbEngine::extensionLibraryName(true)); - return Tr::tr( - "<html><body><p>The remote CDB needs to load the matching %1 CDB extension " - "(<code>%2</code> or <code>%3</code>, respectively).</p><p>Copy it onto the remote machine and set the " - "environment variable <code>%4</code> to point to its folder.</p><p>" - "Launch the remote CDB as <code>%5 <executable></code> " - "to use TCP/IP as communication protocol.</p><p>Enter the connection parameters as:</p>" - "<pre>%6</pre></body></html>") - .arg(QString(Core::Constants::IDE_DISPLAY_NAME), - ext32, ext64, QString("_NT_DEBUGGER_EXTENSION_PATH"), + return Tr:: + tr("<html><body><p>The remote CDB needs to load the matching %1 CDB extension " + "(<code>%2</code> or <code>%3</code>, respectively).</p><p>Copy it onto the remote " + "machine and set the " + "environment variable <code>%4</code> to point to its folder.</p><p>" + "Launch the remote CDB as <code>%5 <executable></code> " + "to use TCP/IP as communication protocol.</p><p>Enter the connection parameters as:</p>" + "<pre>%6</pre></body></html>") + .arg(QGuiApplication::applicationDisplayName(), + ext32, + ext64, + QString("_NT_DEBUGGER_EXTENSION_PATH"), QString("cdb.exe -server tcp:port=1234"), QString(cdbConnectionSyntax)); } diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 0c80b0cf9f0..04befbf071c 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -265,7 +265,7 @@ public: m_logWindow = new LogWindow(m_engine); // Needed before start() m_logWindow->setObjectName("Debugger.Dock.Output"); - connect(&debuggerSettings()->enableReverseDebugging, &BaseAspect::changed, this, [this] { + connect(&settings().enableReverseDebugging, &BaseAspect::changed, this, [this] { updateState(); if (m_companionEngine) m_companionEngine->d->updateState(); @@ -406,7 +406,7 @@ public: m_watchHandler.cleanup(); m_engine->showMessage(Tr::tr("Debugger finished."), StatusBar); m_engine->setState(DebuggerFinished); // Also destroys views. - if (debuggerSettings()->switchModeOnExit.value()) + if (settings().switchModeOnExit()) EngineManager::deactivateDebugMode(); } @@ -440,6 +440,7 @@ public: DebuggerEngine *m_engine = nullptr; // Not owned. QString m_runId; QString m_debuggerName; + QString m_debuggerType; QPointer<Perspective> m_perspective; DebuggerRunParameters m_runParameters; IDevice::ConstPtr m_device; @@ -541,13 +542,15 @@ void DebuggerEnginePrivate::setupViews() QTC_CHECK(!m_perspective); + Perspective *currentPerspective = DebuggerMainWindow::instance()->currentPerspective(); + const QString perspectiveId = "Debugger.Perspective." + m_runId + '.' + m_debuggerName; const QString settingsId = "Debugger.Perspective." + m_debuggerName; + const QString parentPerspectiveId = currentPerspective ? currentPerspective->id() + : Constants::PRESET_PERSPECTIVE_ID; - m_perspective = new Perspective(perspectiveId, - m_engine->displayName(), - Debugger::Constants::PRESET_PERSPECTIVE_ID, - settingsId); + m_perspective + = new Perspective(perspectiveId, m_engine->displayName(), parentPerspectiveId, settingsId); m_progress.setProgressRange(0, 1000); FutureProgress *fp = ProgressManager::addTask(m_progress.future(), @@ -604,7 +607,7 @@ void DebuggerEnginePrivate::setupViews() connect(&m_locationTimer, &QTimer::timeout, this, &DebuggerEnginePrivate::resetLocation); - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); m_modulesView = new BaseTreeView; m_modulesView->setModel(m_modulesHandler.model()); @@ -724,7 +727,10 @@ void DebuggerEnginePrivate::setupViews() m_breakWindow->setObjectName("Debugger.Dock.Break." + engineId); m_breakWindow->setWindowTitle(Tr::tr("&Breakpoints")); - m_perspective->useSubPerspectiveSwitcher(EngineManager::engineChooser()); + if (currentPerspective && currentPerspective->id() == Constants::DAP_PERSPECTIVE_ID) + m_perspective->useSubPerspectiveSwitcher(EngineManager::dapEngineChooser()); + else + m_perspective->useSubPerspectiveSwitcher(EngineManager::engineChooser()); m_perspective->addToolBarAction(&m_continueAction); m_perspective->addToolBarAction(&m_interruptAction); @@ -805,10 +811,10 @@ void DebuggerEnginePrivate::setupViews() m_perspective->addToolBarWidget(m_threadsHandler.threadSwitcher()); connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged, - this, [this](const FontSettings &settings) { - if (!debuggerSettings()->fontSizeFollowsEditor.value()) + this, [this](const FontSettings &fs) { + if (!Internal::settings().fontSizeFollowsEditor()) return; - const qreal size = settings.fontZoom() * settings.fontSize() / 100.; + const qreal size = fs.fontZoom() * fs.fontSize() / 100.; QFont font = m_breakWindow->font(); font.setPointSizeF(size); m_breakWindow->setFont(font); @@ -869,6 +875,16 @@ QString DebuggerEngine::debuggerName() const return d->m_debuggerName; } +void DebuggerEngine::setDebuggerType(const QString &type) +{ + d->m_debuggerType = type; +} + +QString DebuggerEngine::debuggerType() const +{ + return d->m_debuggerType; +} + QString DebuggerEngine::stateName(int s) { # define SN(x) case x: return QLatin1String(#x); @@ -1085,7 +1101,7 @@ void DebuggerEngine::gotoLocation(const Location &loc) &newEditor); QTC_ASSERT(editor, return); // Unreadable file? - editor->gotoLine(line, 0, !debuggerSettings()->stationaryEditorWhileStepping.value()); + editor->gotoLine(line, 0, !settings().stationaryEditorWhileStepping()); if (newEditor) editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true); @@ -1348,7 +1364,7 @@ void DebuggerEngine::notifyInferiorSpontaneousStop() d->m_perspective->select(); showMessage(Tr::tr("Stopped."), StatusBar); setState(InferiorStopOk); - if (debuggerSettings()->raiseOnInterrupt.value()) + if (settings().raiseOnInterrupt()) ICore::raiseWindow(DebuggerMainWindow::instance()); } @@ -1408,8 +1424,8 @@ void DebuggerEnginePrivate::setInitialActionStates() m_jumpToLineAction.setVisible(false); m_stepOverAction.setEnabled(true); - debuggerSettings()->autoDerefPointers.setEnabled(true); - debuggerSettings()->expandStack.setEnabled(false); + settings().autoDerefPointers.setEnabled(true); + settings().expandStack.setEnabled(false); if (m_threadLabel) m_threadLabel->setEnabled(false); @@ -1549,9 +1565,9 @@ void DebuggerEnginePrivate::updateState() const bool actionsEnabled = m_engine->debuggerActionsEnabled(); const bool canDeref = actionsEnabled && m_engine->hasCapability(AutoDerefPointersCapability); - debuggerSettings()->autoDerefPointers.setEnabled(canDeref); - debuggerSettings()->autoDerefPointers.setEnabled(true); - debuggerSettings()->expandStack.setEnabled(actionsEnabled); + settings().autoDerefPointers.setEnabled(canDeref); + settings().autoDerefPointers.setEnabled(true); + settings().expandStack.setEnabled(actionsEnabled); const bool notbusy = state == InferiorStopOk || state == DebuggerNotReady @@ -1563,7 +1579,7 @@ void DebuggerEnginePrivate::updateState() void DebuggerEnginePrivate::updateReverseActions() { const bool stopped = m_state == InferiorStopOk; - const bool reverseEnabled = debuggerSettings()->enableReverseDebugging.value(); + const bool reverseEnabled = settings().enableReverseDebugging(); const bool canReverse = reverseEnabled && m_engine->hasCapability(ReverseSteppingCapability); const bool doesRecord = m_recordForReverseOperationAction.isChecked(); @@ -1581,8 +1597,8 @@ void DebuggerEnginePrivate::updateReverseActions() void DebuggerEnginePrivate::cleanupViews() { - const bool closeSource = debuggerSettings()->closeSourceBuffersOnExit.value(); - const bool closeMemory = debuggerSettings()->closeMemoryBuffersOnExit.value(); + const bool closeSource = settings().closeSourceBuffersOnExit(); + const bool closeMemory = settings().closeMemoryBuffersOnExit(); QList<IDocument *> toClose; const QList<IDocument *> documents = DocumentModel::openedDocuments(); @@ -1869,7 +1885,7 @@ QString DebuggerEngine::expand(const QString &string) const QString DebuggerEngine::nativeStartupCommands() const { - QStringList lines = debuggerSettings()->gdbStartupCommands.value().split('\n'); + QStringList lines = settings().gdbStartupCommands().split('\n'); lines += runParameters().additionalStartupCommands.split('\n'); lines = Utils::filtered(lines, [](const QString line) { @@ -2385,6 +2401,10 @@ void DebuggerEngine::updateItem(const QString &iname) doUpdateLocals(params); } +void DebuggerEngine::reexpandItems(const QSet<QString> &) +{ +} + void DebuggerEngine::updateWatchData(const QString &iname) { // This is used in cases where re-evaluation is ok for the same iname @@ -2587,7 +2607,6 @@ bool DebuggerRunParameters::isCppDebugging() const return cppEngineType == GdbEngineType || cppEngineType == LldbEngineType || cppEngineType == CdbEngineType - || cppEngineType == DapEngineType || cppEngineType == UvscEngineType; } @@ -2715,9 +2734,9 @@ Context CppDebuggerEngine::languageContext() const void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp) { - static const QString warnOnInappropriateDebuggerKey = "DebuggerWarnOnInappropriateDebugger"; + static const Key warnOnInappropriateDebuggerKey = "DebuggerWarnOnInappropriateDebugger"; - const bool warnOnRelease = debuggerSettings()->warnOnReleaseBuilds.value() + const bool warnOnRelease = settings().warnOnReleaseBuilds() && rp.toolChainAbi.osFlavor() != Abi::AndroidLinuxFlavor; bool warnOnInappropriateDebugger = false; QString detailedWarning; @@ -2818,7 +2837,7 @@ void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp) bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0; bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0; if (hasEmbeddedInfo) { - const SourcePathMap sourcePathMap = debuggerSettings()->sourcePathMap.value(); + const QMap<QString, QString> sourcePathMap = settings().sourcePathMap(); QList<QPair<QRegularExpression, QString>> globalRegExpSourceMap; globalRegExpSourceMap.reserve(sourcePathMap.size()); for (auto it = sourcePathMap.begin(), end = sourcePathMap.end(); it != end; ++it) { diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index cc6baf32d28..354ebb95fe0 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -13,11 +13,13 @@ #include <projectexplorer/abi.h> #include <projectexplorer/devicesupport/idevicefwd.h> -#include <projectexplorer/runcontrol.h> #include <texteditor/textmark.h> #include <utils/filepath.h> +#include <utils/outputformat.h> +#include <utils/processhandle.h> +#include <utils/processinterface.h> QT_BEGIN_NAMESPACE class QDebug; @@ -99,7 +101,7 @@ public: DebuggerStartMode startMode = NoStartMode; DebuggerCloseMode closeMode = KillAtClose; - ProjectExplorer::Runnable inferior; + Utils::ProcessRunData inferior; QString displayName; // Used in the Snapshots view. Utils::ProcessHandle attachPID; Utils::FilePaths solibSearchPath; @@ -144,6 +146,7 @@ public: QString additionalStartupCommands; DebuggerEngineType cppEngineType = NoEngineType; + QString version; bool isQmlDebugging = false; bool breakOnMain = false; @@ -151,7 +154,7 @@ public: bool useTerminal = false; bool runAsRoot = false; - ProjectExplorer::Runnable debugger; + Utils::ProcessRunData debugger; Utils::FilePath overrideStartScript; // Used in attach to core and remote debugging QString startMessage; // First status message shown. Utils::FilePath debugInfoLocation; // Gdb "set-debug-file-directory". @@ -287,6 +290,8 @@ public: virtual bool canHandleToolTip(const DebuggerToolTipContext &) const; virtual void expandItem(const QString &iname); // Called when item in tree gets expanded. + virtual void reexpandItems( + const QSet<QString> &inames); // Called when items in tree need to be reexpanded. virtual void updateItem(const QString &iname); // Called for fresh watch items. void updateWatchData(const QString &iname); // FIXME: Merge with above. virtual void selectWatchData(const QString &iname); @@ -449,6 +454,7 @@ public: void updateLocalsWindow(bool showReturn); void raiseWatchersWindow(); QString debuggerName() const; + QString debuggerType() const; bool isRegistersWindowVisible() const; bool isPeripheralRegistersWindowVisible() const; @@ -491,6 +497,7 @@ public: protected: void setDebuggerName(const QString &name); + void setDebuggerType(const QString &type); void notifyDebuggerProcessFinished(const Utils::ProcessResultData &resultData, const QString &backendName); diff --git a/src/plugins/debugger/debuggericons.cpp b/src/plugins/debugger/debuggericons.cpp index b59b9254364..2965934f291 100644 --- a/src/plugins/debugger/debuggericons.cpp +++ b/src/plugins/debugger/debuggericons.cpp @@ -34,13 +34,13 @@ const Icon CONTINUE_FLAT({ {":/debugger/images/debugger_continue_2_mask.png", Theme::IconsRunToolBarColor}, {":/projectexplorer/images/debugger_beetle_mask.png", Theme::IconsDebugColor}}); const Icon DEBUG_CONTINUE_SMALL({ - {":/projectexplorer/images/continue_1_small.png", Theme::IconsInterruptColor}, - {":/projectexplorer/images/continue_2_small.png", Theme::IconsRunColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); + {":/utils/images/continue_1_small.png", Theme::IconsInterruptColor}, + {":/utils/images/continue_2_small.png", Theme::IconsRunColor}, + {":/utils/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); const Icon DEBUG_CONTINUE_SMALL_TOOLBAR({ - {":/projectexplorer/images/continue_1_small.png", Theme::IconsInterruptToolBarColor}, - {":/projectexplorer/images/continue_2_small.png", Theme::IconsRunToolBarColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); + {":/utils/images/continue_1_small.png", Theme::IconsInterruptToolBarColor}, + {":/utils/images/continue_2_small.png", Theme::IconsRunToolBarColor}, + {":/utils/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); const Icon INTERRUPT( ":/debugger/images/debugger_interrupt.png"); const Icon INTERRUPT_FLAT({ @@ -53,16 +53,16 @@ const Icon STOP_FLAT({ {":/projectexplorer/images/debugger_beetle_mask.png", Theme::IconsDebugColor}}); const Icon DEBUG_INTERRUPT_SMALL({ {":/utils/images/interrupt_small.png", Theme::IconsInterruptColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); + {":/utils/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); const Icon DEBUG_INTERRUPT_SMALL_TOOLBAR({ {":/utils/images/interrupt_small.png", Theme::IconsInterruptToolBarColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); + {":/utils/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); const Icon DEBUG_EXIT_SMALL({ {":/utils/images/stop_small.png", Theme::IconsStopColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); + {":/utils/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); const Icon DEBUG_EXIT_SMALL_TOOLBAR({ {":/utils/images/stop_small.png", Theme::IconsStopToolBarColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); + {":/utils/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); const Icon LOCATION({ {":/debugger/images/location_background.png", Theme::IconsCodeModelOverlayForegroundColor}, {":/debugger/images/location.png", Theme::IconsWarningToolBarColor}}, Icon::Tint); diff --git a/src/plugins/debugger/debuggerinternalconstants.h b/src/plugins/debugger/debuggerinternalconstants.h index 5c4205b5a8f..dcb696d3d05 100644 --- a/src/plugins/debugger/debuggerinternalconstants.h +++ b/src/plugins/debugger/debuggerinternalconstants.h @@ -41,8 +41,8 @@ const char C_QMLDEBUGGER[] = "Qml/JavaScript Debugger"; const char C_DEBUGGER_NOTRUNNING[] = "Debugger.NotRunning"; const char PRESET_PERSPECTIVE_ID[] = "Debugger.Perspective.Preset"; +const char DAP_PERSPECTIVE_ID[] = "DAPDebugger"; -const char TASK_CATEGORY_DEBUGGER_DEBUGINFO[] = "Debuginfo"; const char TASK_CATEGORY_DEBUGGER_RUNTIME[] = "DebugRuntime"; const char TEXT_MARK_CATEGORY_BREAKPOINT[] = "Debugger.Mark.Breakpoint"; diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 241ac32ecdd..b95a40527ba 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -78,7 +78,7 @@ DebuggerItem::DebuggerItem(const QVariant &id) m_id = id; } -DebuggerItem::DebuggerItem(const QVariantMap &data) +DebuggerItem::DebuggerItem(const Store &data) { m_id = data.value(DEBUGGER_INFORMATION_ID).toString(); m_command = FilePath::fromSettings(data.value(DEBUGGER_INFORMATION_COMMAND)); @@ -104,7 +104,10 @@ DebuggerItem::DebuggerItem(const QVariantMap &data) && m_abis[0].osFlavor() == Abi::UnknownFlavor && m_abis[0].binaryFormat() == Abi::UnknownFormat; - if (m_version.isEmpty() || mightBeAPreQnxSeparateOSQnxDebugger) + bool needsReinitialization = m_version.isEmpty() && m_abis.isEmpty() + && m_engineType == NoEngineType; + + if (needsReinitialization || mightBeAPreQnxSeparateOSQnxDebugger) reinitializeFromFile(); } @@ -177,9 +180,6 @@ void DebuggerItem::reinitializeFromFile(QString *error, Utils::Environment *cust if (output.contains("gdb")) { m_engineType = GdbEngineType; - // FIXME: HACK while introducing DAP support - if (m_command.fileName().endsWith("-dap")) - m_engineType = DapEngineType; // Version bool isMacGdb, isQnxGdb; @@ -283,8 +283,6 @@ QString DebuggerItem::engineTypeName() const return QLatin1String("CDB"); case LldbEngineType: return QLatin1String("LLDB"); - case DapEngineType: - return QLatin1String("DAP"); case UvscEngineType: return QLatin1String("UVSC"); default: @@ -345,9 +343,9 @@ bool DebuggerItem::operator==(const DebuggerItem &other) const && m_workingDirectory == other.m_workingDirectory; } -QVariantMap DebuggerItem::toMap() const +Store DebuggerItem::toMap() const { - QVariantMap data; + Store data; data.insert(DEBUGGER_INFORMATION_DISPLAYNAME, m_unexpandedDisplayName); data.insert(DEBUGGER_INFORMATION_ID, m_id); data.insert(DEBUGGER_INFORMATION_COMMAND, m_command.toSettings()); diff --git a/src/plugins/debugger/debuggeritem.h b/src/plugins/debugger/debuggeritem.h index 95993665b67..dcdd80ad7ce 100644 --- a/src/plugins/debugger/debuggeritem.h +++ b/src/plugins/debugger/debuggeritem.h @@ -10,15 +10,12 @@ #include <utils/filepath.h> #include <utils/environment.h> +#include <utils/store.h> #include <QDateTime> -#include <QList> -#include <QVariant> namespace Debugger { -class DebuggerItemManager; - namespace Internal { class DebuggerConfigWidget; class DebuggerItemConfigWidget; @@ -33,14 +30,14 @@ class DEBUGGER_EXPORT DebuggerItem { public: DebuggerItem(); - DebuggerItem(const QVariantMap &data); + DebuggerItem(const Utils::Store &data); void createId(); bool canClone() const { return true; } bool isValid() const; QString engineTypeName() const; - QVariantMap toMap() const; + Utils::Store toMap() const; QVariant id() const { return m_id; } @@ -107,7 +104,6 @@ private: friend class Internal::DebuggerConfigWidget; friend class Internal::DebuggerItemConfigWidget; friend class Internal::DebuggerItemModel; - friend class DebuggerItemManager; }; } // namespace Debugger diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index 8f965d9c7c4..4abca97388a 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -10,14 +10,17 @@ #include <coreplugin/icore.h> #include <projectexplorer/devicesupport/devicemanager.h> +#include <projectexplorer/kitoptionspage.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorericons.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/detailswidget.h> #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> +#include <utils/layoutbuilder.h> #include <utils/pathchooser.h> #include <utils/persistentsettings.h> #include <utils/process.h> @@ -25,10 +28,13 @@ #include <utils/treemodel.h> #include <utils/winutils.h> +#include <nanotrace/nanotrace.h> + #include <QDebug> #include <QDir> #include <QFileInfo> #include <QFormLayout> +#include <QFutureWatcher> #include <QHeaderView> #include <QLabel> #include <QLineEdit> @@ -53,35 +59,10 @@ const char DEBUGGER_FILE_VERSION_KEY[] = "Version"; const char DEBUGGER_FILENAME[] = "debuggers.xml"; const char debuggingToolsWikiLinkC[] = "http://wiki.qt.io/Qt_Creator_Windows_Debugging"; -class DebuggerItemModel; - -class DebuggerItemManagerPrivate +static FilePath userSettingsFileName() { -public: - DebuggerItemManagerPrivate(); - ~DebuggerItemManagerPrivate(); - - void extensionsInitialized(); - - void restoreDebuggers(); - void saveDebuggers(); - - void addDebugger(const DebuggerItem &item); - QVariant registerDebugger(const DebuggerItem &item); - void readDebuggers(const FilePath &fileName, bool isSystem); - void autoDetectCdbDebuggers(); - void autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths, - const QString &detectionSource, - QString *logMessage = nullptr); - void autoDetectUvscDebuggers(); - QString uniqueDisplayName(const QString &base); - - PersistentSettingsWriter m_writer; - DebuggerItemModel *m_model = nullptr; - IOptionsPage *m_optionsPage = nullptr; -}; - -static DebuggerItemManagerPrivate *d = nullptr; + return ICore::userResourcePath(DEBUGGER_FILENAME); +} // ----------------------------------------------------------------------- // DebuggerItemConfigWidget @@ -101,7 +82,6 @@ private: void setAbis(const QStringList &abiNames); QLineEdit *m_displayNameLineEdit; - QLineEdit *m_typeLineEdit; QLabel *m_cdbLabel; PathChooser *m_binaryChooser; bool m_autodetected = false; @@ -109,12 +89,12 @@ private: DebuggerEngineType m_engineType = NoEngineType; QVariant m_id; - QLabel *m_abisLabel; - QLineEdit *m_abis; - QLabel *m_versionLabel; - QLineEdit *m_version; - QLabel *m_workingDirectoryLabel; + QLabel *m_abis; + QLabel *m_version; + QLabel *m_type; + PathChooser *m_workingDirectoryChooser; + QFutureWatcher<DebuggerItem> m_updateWatcher; }; // -------------------------------------------------------------------------- @@ -178,19 +158,39 @@ public: QModelIndex lastIndex() const; void setCurrentIndex(const QModelIndex &index); - DebuggerTreeItem *addDebugger(const DebuggerItem &item, bool changed = false); + DebuggerTreeItem *addDebuggerItem(const DebuggerItem &item, bool changed = false); void updateDebugger(const DebuggerItem &item); void apply(); void cancel(); DebuggerTreeItem *currentTreeItem(); + void restoreDebuggers(); + void saveDebuggers(); + + void addDebugger(const DebuggerItem &item); + QVariant registerDebugger(const DebuggerItem &item); + void readDebuggers(const FilePath &fileName, bool isSystem); + void autoDetectCdbDebuggers(); + void autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths, + const QString &detectionSource, + QString *logMessage = nullptr); + void autoDetectUvscDebuggers(); + QString uniqueDisplayName(const QString &base); + + PersistentSettingsWriter m_writer{userSettingsFileName(), "QtCreatorDebuggers"}; QPersistentModelIndex m_currentIndex; }; +static DebuggerItemModel &itemModel() +{ + static DebuggerItemModel theModel; + return theModel; +} + template <typename Predicate> void forAllDebuggers(const Predicate &pred) { - d->m_model->forItemsAtLevel<2>([pred](DebuggerTreeItem *titem) { + itemModel().forItemsAtLevel<2>([pred](DebuggerTreeItem *titem) { pred(titem->m_item); }); } @@ -198,17 +198,19 @@ void forAllDebuggers(const Predicate &pred) template <typename Predicate> const DebuggerItem *findDebugger(const Predicate &pred) { - DebuggerTreeItem *titem = d->m_model->findItemAtLevel<2>([pred](DebuggerTreeItem *titem) { + DebuggerTreeItem *titem = itemModel().findItemAtLevel<2>([pred](DebuggerTreeItem *titem) { return pred(titem->m_item); }); return titem ? &titem->m_item : nullptr; } +static QString genericCategoryDisplayName() { return Tr::tr("Generic"); } + DebuggerItemModel::DebuggerItemModel() { setHeader({Tr::tr("Name"), Tr::tr("Path"), Tr::tr("Type")}); - auto generic = new StaticTreeItem(Tr::tr("Generic")); + auto generic = new StaticTreeItem(genericCategoryDisplayName()); auto autoDetected = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()}, {ProjectExplorer::Constants::msgAutoDetectedToolTip()}); rootItem()->appendChild(generic); @@ -232,9 +234,12 @@ DebuggerItemModel::DebuggerItemModel() genericLldb.setCommand("lldb"); genericLldb.setUnexpandedDisplayName(Tr::tr("LLDB from PATH on Build Device")); generic->appendChild(new DebuggerTreeItem(genericLldb, false)); + + connect(ICore::instance(), &ICore::saveSettingsRequested, + this, &DebuggerItemModel::saveDebuggers); } -DebuggerTreeItem *DebuggerItemModel::addDebugger(const DebuggerItem &item, bool changed) +DebuggerTreeItem *DebuggerItemModel::addDebuggerItem(const DebuggerItem &item, bool changed) { QTC_ASSERT(item.id().isValid(), return {}); int group = item.isGeneric() ? Generic : (item.isAutoDetected() ? AutoDetected : Manual); @@ -311,52 +316,47 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget() { m_displayNameLineEdit = new QLineEdit(this); - m_typeLineEdit = new QLineEdit(this); - m_typeLineEdit->setEnabled(false); - m_binaryChooser = new PathChooser(this); m_binaryChooser->setExpectedKind(PathChooser::ExistingCommand); m_binaryChooser->setMinimumWidth(400); m_binaryChooser->setHistoryCompleter("DebuggerPaths"); - m_binaryChooser->setValidationFunction([this](FancyLineEdit *edit, QString *errorMessage) { - if (!m_binaryChooser->defaultValidationFunction()(edit, errorMessage)) - return false; - DebuggerItem item; - item.setCommand(m_binaryChooser->filePath()); - errorMessage->clear(); - item.reinitializeFromFile(errorMessage); - return errorMessage->isEmpty(); - }); + m_binaryChooser->setValidationFunction( + [this](const QString &text) -> FancyLineEdit::AsyncValidationFuture { + return m_binaryChooser->defaultValidationFunction()(text).then( + [](const FancyLineEdit::AsyncValidationResult &result) + -> FancyLineEdit::AsyncValidationResult { + if (!result) + return result; + + DebuggerItem item; + item.setCommand(FilePath::fromUserInput(result.value())); + QString errorMessage; + item.reinitializeFromFile(&errorMessage); + + if (!errorMessage.isEmpty()) + return make_unexpected(errorMessage); + + return result.value(); + }); + }); m_binaryChooser->setAllowPathFromDevice(true); - m_workingDirectoryLabel = new QLabel(Tr::tr("Working directory:")); m_workingDirectoryChooser = new PathChooser(this); m_workingDirectoryChooser->setExpectedKind(PathChooser::Directory); m_workingDirectoryChooser->setMinimumWidth(400); m_workingDirectoryChooser->setHistoryCompleter("DebuggerPaths"); - m_cdbLabel = new QLabel(this); - m_cdbLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_cdbLabel->setOpenExternalLinks(true); + auto makeInteractiveLabel = []() { + auto label = new QLabel; + label->setTextInteractionFlags(Qt::TextEditorInteraction | Qt::TextBrowserInteraction); + label->setOpenExternalLinks(true); + return label; + }; - m_versionLabel = new QLabel(Tr::tr("Version:")); - m_version = new QLineEdit(this); - m_version->setPlaceholderText(Tr::tr("Unknown")); - m_version->setEnabled(false); - - m_abisLabel = new QLabel(Tr::tr("ABIs:")); - m_abis = new QLineEdit(this); - m_abis->setEnabled(false); - - auto formLayout = new QFormLayout(this); - formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - formLayout->addRow(new QLabel(Tr::tr("Name:")), m_displayNameLineEdit); - formLayout->addRow(m_cdbLabel); - formLayout->addRow(new QLabel(Tr::tr("Path:")), m_binaryChooser); - formLayout->addRow(new QLabel(Tr::tr("Type:")), m_typeLineEdit); - formLayout->addRow(m_abisLabel, m_abis); - formLayout->addRow(m_versionLabel, m_version); - formLayout->addRow(m_workingDirectoryLabel, m_workingDirectoryChooser); + m_cdbLabel = makeInteractiveLabel(); + m_version = makeInteractiveLabel(); + m_abis = makeInteractiveLabel(); + m_type = makeInteractiveLabel(); connect(m_binaryChooser, &PathChooser::textChanged, this, &DebuggerItemConfigWidget::binaryPathHasChanged); @@ -364,6 +364,30 @@ DebuggerItemConfigWidget::DebuggerItemConfigWidget() this, &DebuggerItemConfigWidget::store); connect(m_displayNameLineEdit, &QLineEdit::textChanged, this, &DebuggerItemConfigWidget::store); + + connect(&m_updateWatcher, &QFutureWatcher<DebuggerItem>::finished, this, [this] { + if (m_updateWatcher.future().resultCount() > 0) { + DebuggerItem tmp = m_updateWatcher.result(); + setAbis(tmp.abiNames()); + m_version->setText(tmp.version()); + m_engineType = tmp.engineType(); + m_type->setText(tmp.engineTypeName()); + } + }); + + // clang-format off + using namespace Layouting; + Form { + fieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow), + Tr::tr("Name:"), m_displayNameLineEdit, br, + Tr::tr("Path:"), m_binaryChooser, br, + m_cdbLabel, br, + Tr::tr("Type:"), m_type, br, + Tr::tr("ABIs:"), m_abis, br, + Tr::tr("Version:"), m_version, br, + Tr::tr("Working directory:"), m_workingDirectoryChooser, br, + }.attachTo(this); + // clang-format on } DebuggerItem DebuggerItemConfigWidget::item() const @@ -392,7 +416,7 @@ DebuggerItem DebuggerItemConfigWidget::item() const void DebuggerItemConfigWidget::store() const { if (!m_id.isNull()) - d->m_model->updateDebugger(item()); + itemModel().updateDebugger(item()); } void DebuggerItemConfigWidget::setAbis(const QStringList &abiNames) @@ -413,20 +437,12 @@ void DebuggerItemConfigWidget::load(const DebuggerItem *item) m_displayNameLineEdit->setEnabled(!item->isAutoDetected()); m_displayNameLineEdit->setText(item->unexpandedDisplayName()); - m_typeLineEdit->setText(item->engineTypeName()); + m_type->setText(item->engineTypeName()); m_binaryChooser->setReadOnly(item->isAutoDetected()); m_binaryChooser->setFilePath(item->command()); m_binaryChooser->setExpectedKind(m_generic ? PathChooser::Any : PathChooser::ExistingCommand); - m_abisLabel->setVisible(!m_generic); - m_abis->setVisible(!m_generic); - m_versionLabel->setVisible(!m_generic); - m_version->setVisible(!m_generic); - m_workingDirectoryLabel->setVisible(!m_generic); - m_workingDirectoryChooser->setVisible(!m_generic); - - m_workingDirectoryChooser->setReadOnly(item->isAutoDetected()); m_workingDirectoryChooser->setFilePath(item->workingDirectory()); @@ -462,184 +478,27 @@ void DebuggerItemConfigWidget::binaryPathHasChanged() return; if (!m_generic) { + m_updateWatcher.cancel(); + DebuggerItem tmp; if (m_binaryChooser->filePath().isExecutableFile()) { tmp = item(); - tmp.reinitializeFromFile(); + m_updateWatcher.setFuture(Utils::asyncRun([tmp]() mutable { + tmp.reinitializeFromFile(); + return tmp; + })); + } else { + setAbis(tmp.abiNames()); + m_version->setText(tmp.version()); + m_engineType = tmp.engineType(); + m_type->setText(tmp.engineTypeName()); } - - setAbis(tmp.abiNames()); - m_version->setText(tmp.version()); - m_engineType = tmp.engineType(); - m_typeLineEdit->setText(tmp.engineTypeName()); } store(); } -// -------------------------------------------------------------------------- -// DebuggerConfigWidget -// -------------------------------------------------------------------------- - -class DebuggerConfigWidget : public IOptionsPageWidget -{ -public: - DebuggerConfigWidget() - { - m_addButton = new QPushButton(Tr::tr("Add"), this); - - m_cloneButton = new QPushButton(Tr::tr("Clone"), this); - m_cloneButton->setEnabled(false); - - m_delButton = new QPushButton(this); - m_delButton->setEnabled(false); - - m_container = new DetailsWidget(this); - m_container->setState(DetailsWidget::NoSummary); - m_container->setVisible(false); - - m_debuggerView = new QTreeView(this); - m_debuggerView->setModel(d->m_model); - m_debuggerView->setUniformRowHeights(true); - m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection); - m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_debuggerView->expandAll(); - - auto header = m_debuggerView->header(); - header->setStretchLastSection(false); - header->setSectionResizeMode(0, QHeaderView::ResizeToContents); - header->setSectionResizeMode(1, QHeaderView::ResizeToContents); - header->setSectionResizeMode(2, QHeaderView::Stretch); - - auto buttonLayout = new QVBoxLayout(); - buttonLayout->setSpacing(6); - buttonLayout->setContentsMargins(0, 0, 0, 0); - buttonLayout->addWidget(m_addButton); - buttonLayout->addWidget(m_cloneButton); - buttonLayout->addWidget(m_delButton); - buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); - - auto verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(m_debuggerView); - verticalLayout->addWidget(m_container); - - auto horizontalLayout = new QHBoxLayout(this); - horizontalLayout->addLayout(verticalLayout); - horizontalLayout->addLayout(buttonLayout); - - connect(m_debuggerView->selectionModel(), &QItemSelectionModel::currentChanged, - this, &DebuggerConfigWidget::currentDebuggerChanged, Qt::QueuedConnection); - - connect(m_addButton, &QAbstractButton::clicked, - this, &DebuggerConfigWidget::addDebugger, Qt::QueuedConnection); - connect(m_cloneButton, &QAbstractButton::clicked, - this, &DebuggerConfigWidget::cloneDebugger, Qt::QueuedConnection); - connect(m_delButton, &QAbstractButton::clicked, - this, &DebuggerConfigWidget::removeDebugger, Qt::QueuedConnection); - - m_itemConfigWidget = new DebuggerItemConfigWidget; - m_container->setWidget(m_itemConfigWidget); - updateButtons(); - } - - void apply() final - { - m_itemConfigWidget->store(); - d->m_model->apply(); - } - - void finish() final - { - d->m_model->cancel(); - } - - void cloneDebugger(); - void addDebugger(); - void removeDebugger(); - void currentDebuggerChanged(const QModelIndex &newCurrent); - void updateButtons(); - - QTreeView *m_debuggerView; - QPushButton *m_addButton; - QPushButton *m_cloneButton; - QPushButton *m_delButton; - DetailsWidget *m_container; - DebuggerItemConfigWidget *m_itemConfigWidget; -}; - -void DebuggerConfigWidget::cloneDebugger() -{ - DebuggerTreeItem *treeItem = d->m_model->currentTreeItem(); - if (!treeItem) - return; - - DebuggerItem *item = &treeItem->m_item; - DebuggerItem newItem; - newItem.createId(); - newItem.setCommand(item->command()); - newItem.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("Clone of %1").arg(item->displayName()))); - newItem.reinitializeFromFile(); - newItem.setAutoDetected(false); - newItem.setGeneric(item->isGeneric()); - newItem.setEngineType(item->engineType()); - auto addedItem = d->m_model->addDebugger(newItem, true); - m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem)); -} - -void DebuggerConfigWidget::addDebugger() -{ - DebuggerItem item; - item.createId(); - item.setEngineType(NoEngineType); - item.setUnexpandedDisplayName(d->uniqueDisplayName(Tr::tr("New Debugger"))); - item.setAutoDetected(false); - auto addedItem = d->m_model->addDebugger(item, true); - m_debuggerView->setCurrentIndex(d->m_model->indexForItem(addedItem)); -} - -void DebuggerConfigWidget::removeDebugger() -{ - DebuggerTreeItem *treeItem = d->m_model->currentTreeItem(); - QTC_ASSERT(treeItem, return); - treeItem->m_removed = !treeItem->m_removed; - treeItem->update(); - updateButtons(); -} - -void DebuggerConfigWidget::currentDebuggerChanged(const QModelIndex &newCurrent) -{ - d->m_model->setCurrentIndex(newCurrent); - updateButtons(); -} - -void DebuggerConfigWidget::updateButtons() -{ - DebuggerTreeItem *titem = d->m_model->currentTreeItem(); - DebuggerItem *item = titem ? &titem->m_item : nullptr; - - m_itemConfigWidget->load(item); - m_container->setVisible(item != nullptr); - m_cloneButton->setEnabled(item && item->isValid() && item->canClone()); - m_delButton->setEnabled(item && !item->isAutoDetected()); - m_delButton->setText(item && titem->m_removed ? Tr::tr("Restore") : Tr::tr("Remove")); -} - -// -------------------------------------------------------------------------- -// DebuggerOptionsPage -// -------------------------------------------------------------------------- - -class DebuggerOptionsPage : public Core::IOptionsPage -{ -public: - DebuggerOptionsPage() { - setId(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); - setDisplayName(Tr::tr("Debuggers")); - setCategory(ProjectExplorer::Constants::KITS_SETTINGS_CATEGORY); - setWidgetCreator([] { return new DebuggerConfigWidget; }); - } -}; - -void DebuggerItemManagerPrivate::autoDetectCdbDebuggers() +void DebuggerItemModel::autoDetectCdbDebuggers() { FilePaths cdbs; @@ -710,7 +569,7 @@ void DebuggerItemManagerPrivate::autoDetectCdbDebuggers() item.setEngineType(CdbEngineType); item.setUnexpandedDisplayName(uniqueDisplayName(Tr::tr("Auto-detected CDB at %1").arg(cdb.toUserOutput()))); item.reinitializeFromFile(); // collect version number - m_model->addDebugger(item); + addDebuggerItem(item); } } @@ -746,7 +605,7 @@ static Utils::FilePaths searchGdbPathsFromRegistry() return searchPaths; } -void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths, +void DebuggerItemModel::autoDetectGdbOrLldbDebuggers(const FilePaths &searchPaths, const QString &detectionSource, QString *logMessage) { @@ -793,7 +652,7 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s const auto commandMatches = [command](const DebuggerTreeItem *titem) { return titem->m_item.command() == command; }; - if (DebuggerTreeItem *existingItem = m_model->findItemAtLevel<2>(commandMatches)) { + if (DebuggerTreeItem *existingItem = findItemAtLevel<2>(commandMatches)) { if (command.lastModified() != existingItem->m_item.lastModified()) existingItem->m_item.reinitializeFromFile(); continue; @@ -809,22 +668,14 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers(const FilePaths &s //: %1: Debugger engine type (GDB, LLDB, CDB...), %2: Path const QString name = detectionSource.isEmpty() ? Tr::tr("System %1 at %2") : Tr::tr("Detected %1 at %2"); item.setUnexpandedDisplayName(name.arg(item.engineTypeName()).arg(command.toUserOutput())); - m_model->addDebugger(item); + addDebuggerItem(item); logMessages.append(Tr::tr("Found: \"%1\"").arg(command.toUserOutput())); - - if (item.engineType() == GdbEngineType) { - if (item.version().startsWith("GNU gdb (GDB) 14.0.50.2023")) { - // FIXME: Use something more robust - item.setEngineType(DapEngineType); - m_model->addDebugger(item); - } - } } if (logMessage) *logMessage = logMessages.join('\n'); } -void DebuggerItemManagerPrivate::autoDetectUvscDebuggers() +void DebuggerItemModel::autoDetectUvscDebuggers() { if (!HostOsInfo::isWindowsHost()) return; @@ -861,35 +712,11 @@ void DebuggerItemManagerPrivate::autoDetectUvscDebuggers() item.setUnexpandedDisplayName( uniqueDisplayName(Tr::tr("Auto-detected uVision at %1") .arg(uVision.toUserOutput()))); - m_model->addDebugger(item); + addDebuggerItem(item); } } -static FilePath userSettingsFileName() -{ - return ICore::userResourcePath(DEBUGGER_FILENAME); -} - -DebuggerItemManagerPrivate::DebuggerItemManagerPrivate() - : m_writer(userSettingsFileName(), "QtCreatorDebuggers") -{ - d = this; - m_model = new DebuggerItemModel; - m_optionsPage = new DebuggerOptionsPage; -} - -void DebuggerItemManagerPrivate::extensionsInitialized() -{ - restoreDebuggers(); -} - -DebuggerItemManagerPrivate::~DebuggerItemManagerPrivate() -{ - delete m_optionsPage; - delete m_model; -} - -QString DebuggerItemManagerPrivate::uniqueDisplayName(const QString &base) +QString DebuggerItemModel::uniqueDisplayName(const QString &base) { const DebuggerItem *item = findDebugger([base](const DebuggerItem &item) { return item.unexpandedDisplayName() == base; @@ -897,10 +724,10 @@ QString DebuggerItemManagerPrivate::uniqueDisplayName(const QString &base) return item ? uniqueDisplayName(base + " (1)") : base; } -QVariant DebuggerItemManagerPrivate::registerDebugger(const DebuggerItem &item) +QVariant DebuggerItemModel::registerDebugger(const DebuggerItem &item) { // Try re-using existing item first. - DebuggerTreeItem *titem = m_model->findItemAtLevel<2>([item](DebuggerTreeItem *titem) { + DebuggerTreeItem *titem = findItemAtLevel<2>([item](DebuggerTreeItem *titem) { const DebuggerItem &d = titem->m_item; return d.command() == item.command() && d.isAutoDetected() == item.isAutoDetected() @@ -916,16 +743,16 @@ QVariant DebuggerItemManagerPrivate::registerDebugger(const DebuggerItem &item) if (!di.id().isValid()) di.createId(); - m_model->addDebugger(di); + addDebuggerItem(di); return di.id(); } -void DebuggerItemManagerPrivate::readDebuggers(const FilePath &fileName, bool isSystem) +void DebuggerItemModel::readDebuggers(const FilePath &fileName, bool isSystem) { PersistentSettingsReader reader; if (!reader.load(fileName)) return; - QVariantMap data = reader.restoreValues(); + Store data = reader.restoreValues(); // Check version int version = data.value(DEBUGGER_FILE_VERSION_KEY, 0).toInt(); @@ -934,10 +761,10 @@ void DebuggerItemManagerPrivate::readDebuggers(const FilePath &fileName, bool is int count = data.value(DEBUGGER_COUNT_KEY, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = DEBUGGER_DATA_KEY + QString::number(i); + const Key key = numberedKey(DEBUGGER_DATA_KEY, i); if (!data.contains(key)) continue; - const QVariantMap dbMap = data.value(key).toMap(); + const Store dbMap = storeFromVariant(data.value(key)); DebuggerItem item(dbMap); if (isSystem) { item.setAutoDetected(true); @@ -963,7 +790,7 @@ void DebuggerItemManagerPrivate::readDebuggers(const FilePath &fileName, bool is } } -void DebuggerItemManagerPrivate::restoreDebuggers() +void DebuggerItemModel::restoreDebuggers() { // Read debuggers from SDK readDebuggers(ICore::installerResourcePath(DEBUGGER_FILENAME), true); @@ -979,9 +806,9 @@ void DebuggerItemManagerPrivate::restoreDebuggers() autoDetectUvscDebuggers(); } -void DebuggerItemManagerPrivate::saveDebuggers() +void DebuggerItemModel::saveDebuggers() { - QVariantMap data; + Store data; data.insert(DEBUGGER_FILE_VERSION_KEY, 1); int count = 0; @@ -989,9 +816,9 @@ void DebuggerItemManagerPrivate::saveDebuggers() if (item.isGeneric()) // do not store generic debuggers, these get added automatically return; if (item.isValid() && item.engineType() != NoEngineType) { - QVariantMap tmp = item.toMap(); + Store tmp = item.toMap(); if (!tmp.isEmpty()) { - data.insert(DEBUGGER_DATA_KEY + QString::number(count), tmp); + data.insert(numberedKey(DEBUGGER_DATA_KEY, count), variantFromStore(tmp)); ++count; } } @@ -1008,21 +835,10 @@ void DebuggerItemManagerPrivate::saveDebuggers() // DebuggerItemManager // -------------------------------------------------------------------------- -DebuggerItemManager::DebuggerItemManager() +void DebuggerItemManager::restoreDebuggers() { - new DebuggerItemManagerPrivate; - QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, - [] { d->saveDebuggers(); }); -} - -DebuggerItemManager::~DebuggerItemManager() -{ - delete d; -} - -void DebuggerItemManager::extensionsInitialized() -{ - d->extensionsInitialized(); + NANOTRACE_SCOPE("Debugger", "DebuggerItemManager::restoreDebuggers"); + itemModel().restoreDebuggers(); } const QList<DebuggerItem> DebuggerItemManager::debuggers() @@ -1055,14 +871,14 @@ const DebuggerItem *DebuggerItemManager::findByEngineType(DebuggerEngineType eng QVariant DebuggerItemManager::registerDebugger(const DebuggerItem &item) { - return d->registerDebugger(item); + return itemModel().registerDebugger(item); } void DebuggerItemManager::deregisterDebugger(const QVariant &id) { - d->m_model->forItemsAtLevel<2>([id](DebuggerTreeItem *titem) { + itemModel().forItemsAtLevel<2>([id](DebuggerTreeItem *titem) { if (titem->m_item.id() == id) - d->m_model->destroyItem(titem); + itemModel().destroyItem(titem); }); } @@ -1070,7 +886,7 @@ void DebuggerItemManager::autoDetectDebuggersForDevice(const FilePaths &searchPa const QString &detectionSource, QString *logMessage) { - d->autoDetectGdbOrLldbDebuggers(searchPaths, detectionSource, logMessage); + itemModel().autoDetectGdbOrLldbDebuggers(searchPaths, detectionSource, logMessage); } void DebuggerItemManager::removeDetectedDebuggers(const QString &detectionSource, @@ -1079,7 +895,7 @@ void DebuggerItemManager::removeDetectedDebuggers(const QString &detectionSource QStringList logMessages{Tr::tr("Removing debugger entries...")}; QList<DebuggerTreeItem *> toBeRemoved; - d->m_model->forItemsAtLevel<2>([detectionSource, &toBeRemoved](DebuggerTreeItem *titem) { + itemModel().forItemsAtLevel<2>([detectionSource, &toBeRemoved](DebuggerTreeItem *titem) { if (titem->m_item.detectionSource() == detectionSource) { toBeRemoved.append(titem); return; @@ -1091,7 +907,7 @@ void DebuggerItemManager::removeDetectedDebuggers(const QString &detectionSource }); for (DebuggerTreeItem *current : toBeRemoved) { logMessages.append(Tr::tr("Removed \"%1\"").arg(current->m_item.displayName())); - d->m_model->destroyItem(current); + itemModel().destroyItem(current); } if (logMessage) @@ -1102,11 +918,181 @@ void DebuggerItemManager::listDetectedDebuggers(const QString &detectionSource, { QTC_ASSERT(logMessage, return); QStringList logMessages{Tr::tr("Debuggers:")}; - d->m_model->forItemsAtLevel<2>([detectionSource, &logMessages](DebuggerTreeItem *titem) { + itemModel().forItemsAtLevel<2>([detectionSource, &logMessages](DebuggerTreeItem *titem) { if (titem->m_item.detectionSource() == detectionSource) logMessages.append(titem->m_item.displayName()); }); *logMessage = logMessages.join('\n'); } + +// DebuggerSettingsPageWidget + +class DebuggerSettingsPageWidget : public IOptionsPageWidget +{ +public: + DebuggerSettingsPageWidget() + { + m_addButton = new QPushButton(Tr::tr("Add"), this); + + m_cloneButton = new QPushButton(Tr::tr("Clone"), this); + m_cloneButton->setEnabled(false); + + m_delButton = new QPushButton(this); + m_delButton->setEnabled(false); + + m_container = new DetailsWidget(this); + m_container->setState(DetailsWidget::NoSummary); + m_container->setVisible(false); + + m_sortModel = new KitSettingsSortModel(this); + m_sortModel->setSourceModel(&itemModel()); + m_sortModel->setSortedCategories({genericCategoryDisplayName(), + ProjectExplorer::Constants::msgAutoDetected(), + ProjectExplorer::Constants::msgManual()}); + m_debuggerView = new QTreeView(this); + m_debuggerView->setModel(m_sortModel); + m_debuggerView->setUniformRowHeights(true); + m_debuggerView->setSelectionMode(QAbstractItemView::SingleSelection); + m_debuggerView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_debuggerView->expandAll(); + m_debuggerView->setSortingEnabled(true); + m_debuggerView->sortByColumn(0, Qt::AscendingOrder); + + auto header = m_debuggerView->header(); + header->setStretchLastSection(false); + header->setSectionResizeMode(0, QHeaderView::ResizeToContents); + header->setSectionResizeMode(1, QHeaderView::ResizeToContents); + header->setSectionResizeMode(2, QHeaderView::Stretch); + + auto buttonLayout = new QVBoxLayout(); + buttonLayout->setSpacing(6); + buttonLayout->setContentsMargins(0, 0, 0, 0); + buttonLayout->addWidget(m_addButton); + buttonLayout->addWidget(m_cloneButton); + buttonLayout->addWidget(m_delButton); + buttonLayout->addItem(new QSpacerItem(10, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + auto verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(m_debuggerView); + verticalLayout->addWidget(m_container); + + auto horizontalLayout = new QHBoxLayout(this); + horizontalLayout->addLayout(verticalLayout); + horizontalLayout->addLayout(buttonLayout); + + connect(m_debuggerView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &DebuggerSettingsPageWidget::currentDebuggerChanged, Qt::QueuedConnection); + + connect(m_addButton, &QAbstractButton::clicked, + this, &DebuggerSettingsPageWidget::addDebugger, Qt::QueuedConnection); + connect(m_cloneButton, &QAbstractButton::clicked, + this, &DebuggerSettingsPageWidget::cloneDebugger, Qt::QueuedConnection); + connect(m_delButton, &QAbstractButton::clicked, + this, &DebuggerSettingsPageWidget::removeDebugger, Qt::QueuedConnection); + + m_itemConfigWidget = new DebuggerItemConfigWidget; + m_container->setWidget(m_itemConfigWidget); + updateButtons(); + } + + void apply() final + { + m_itemConfigWidget->store(); + itemModel().apply(); + } + + void finish() final + { + itemModel().cancel(); + } + + void cloneDebugger(); + void addDebugger(); + void removeDebugger(); + void currentDebuggerChanged(const QModelIndex &newCurrent); + void updateButtons(); + + KitSettingsSortModel *m_sortModel; + QTreeView *m_debuggerView; + QPushButton *m_addButton; + QPushButton *m_cloneButton; + QPushButton *m_delButton; + DetailsWidget *m_container; + DebuggerItemConfigWidget *m_itemConfigWidget; +}; + +void DebuggerSettingsPageWidget::cloneDebugger() +{ + DebuggerTreeItem *treeItem = itemModel().currentTreeItem(); + if (!treeItem) + return; + + DebuggerItem *item = &treeItem->m_item; + DebuggerItem newItem; + newItem.createId(); + newItem.setCommand(item->command()); + newItem.setUnexpandedDisplayName(itemModel().uniqueDisplayName(Tr::tr("Clone of %1").arg(item->displayName()))); + newItem.reinitializeFromFile(); + newItem.setAutoDetected(false); + newItem.setGeneric(item->isGeneric()); + newItem.setEngineType(item->engineType()); + auto addedItem = itemModel().addDebuggerItem(newItem, true); + m_debuggerView->setCurrentIndex(m_sortModel->mapFromSource(itemModel().indexForItem(addedItem))); +} + +void DebuggerSettingsPageWidget::addDebugger() +{ + DebuggerItem item; + item.createId(); + item.setEngineType(NoEngineType); + item.setUnexpandedDisplayName(itemModel().uniqueDisplayName(Tr::tr("New Debugger"))); + item.setAutoDetected(false); + auto addedItem = itemModel().addDebuggerItem(item, true); + m_debuggerView->setCurrentIndex(m_sortModel->mapFromSource(itemModel().indexForItem(addedItem))); +} + +void DebuggerSettingsPageWidget::removeDebugger() +{ + DebuggerTreeItem *treeItem = itemModel().currentTreeItem(); + QTC_ASSERT(treeItem, return); + treeItem->m_removed = !treeItem->m_removed; + treeItem->update(); + updateButtons(); +} + +void DebuggerSettingsPageWidget::currentDebuggerChanged(const QModelIndex &newCurrent) +{ + itemModel().setCurrentIndex(m_sortModel->mapToSource(newCurrent)); + updateButtons(); +} + +void DebuggerSettingsPageWidget::updateButtons() +{ + DebuggerTreeItem *titem = itemModel().currentTreeItem(); + DebuggerItem *item = titem ? &titem->m_item : nullptr; + + m_itemConfigWidget->load(item); + m_container->setVisible(item != nullptr); + m_cloneButton->setEnabled(item && item->isValid() && item->canClone()); + m_delButton->setEnabled(item && !item->isAutoDetected()); + m_delButton->setText(item && titem->m_removed ? Tr::tr("Restore") : Tr::tr("Remove")); +} + + +// DebuggerSettingsPage + +class DebuggerSettingsPage : public Core::IOptionsPage +{ +public: + DebuggerSettingsPage() { + setId(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); + setDisplayName(Tr::tr("Debuggers")); + setCategory(ProjectExplorer::Constants::KITS_SETTINGS_CATEGORY); + setWidgetCreator([] { return new DebuggerSettingsPageWidget; }); + } +}; + +const DebuggerSettingsPage settingsPage; + } // namespace Debugger diff --git a/src/plugins/debugger/debuggeritemmanager.h b/src/plugins/debugger/debuggeritemmanager.h index 9be8c4faf6b..662306a3f31 100644 --- a/src/plugins/debugger/debuggeritemmanager.h +++ b/src/plugins/debugger/debuggeritemmanager.h @@ -14,30 +14,24 @@ namespace Debugger { class DebuggerItem; -class DEBUGGER_EXPORT DebuggerItemManager -{ - Q_DISABLE_COPY_MOVE(DebuggerItemManager) +namespace DebuggerItemManager { -public: - DebuggerItemManager(); - ~DebuggerItemManager(); +DEBUGGER_EXPORT void restoreDebuggers(); - void extensionsInitialized(); +DEBUGGER_EXPORT const QList<DebuggerItem> debuggers(); - static const QList<DebuggerItem> debuggers(); +DEBUGGER_EXPORT QVariant registerDebugger(const DebuggerItem &item); +DEBUGGER_EXPORT void deregisterDebugger(const QVariant &id); - static QVariant registerDebugger(const DebuggerItem &item); - static void deregisterDebugger(const QVariant &id); +DEBUGGER_EXPORT void autoDetectDebuggersForDevice(const Utils::FilePaths &searchPaths, + const QString &detectionSource, + QString *logMessage); +DEBUGGER_EXPORT void removeDetectedDebuggers(const QString &detectionSource, QString *logMessage); +DEBUGGER_EXPORT void listDetectedDebuggers(const QString &detectionSource, QString *logMessage); - static void autoDetectDebuggersForDevice(const Utils::FilePaths &searchPaths, - const QString &detectionSource, - QString *logMessage); - static void removeDetectedDebuggers(const QString &detectionSource, QString *logMessage); - static void listDetectedDebuggers(const QString &detectionSource, QString *logMessage); +DEBUGGER_EXPORT const DebuggerItem *findByCommand(const Utils::FilePath &command); +DEBUGGER_EXPORT const DebuggerItem *findById(const QVariant &id); +DEBUGGER_EXPORT const DebuggerItem *findByEngineType(DebuggerEngineType engineType); - static const DebuggerItem *findByCommand(const Utils::FilePath &command); - static const DebuggerItem *findById(const QVariant &id); - static const DebuggerItem *findByEngineType(DebuggerEngineType engineType); -}; - -} // namespace Debugger +} // DebuggerItemManager +} // Debugger diff --git a/src/plugins/debugger/debuggerkitaspect.cpp b/src/plugins/debugger/debuggerkitaspect.cpp new file mode 100644 index 00000000000..b881e90332d --- /dev/null +++ b/src/plugins/debugger/debuggerkitaspect.cpp @@ -0,0 +1,437 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "debuggerkitaspect.h" + +#include "debuggeritemmanager.h" +#include "debuggeritem.h" +#include "debuggertr.h" + +#include <projectexplorer/devicesupport/idevice.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/toolchain.h> + +#include <utils/environment.h> +#include <utils/guard.h> +#include <utils/filepath.h> +#include <utils/layoutbuilder.h> +#include <utils/macroexpander.h> +#include <utils/processinterface.h> +#include <utils/qtcassert.h> + +#include <QComboBox> + +#include <utility> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace Debugger { + +// -------------------------------------------------------------------------- +// DebuggerKitAspect +// -------------------------------------------------------------------------- + +namespace Internal { + +class DebuggerKitAspectImpl final : public KitAspect +{ +public: + DebuggerKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory) + { + m_comboBox = createSubWidget<QComboBox>(); + m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); + m_comboBox->setEnabled(true); + + refresh(); + m_comboBox->setToolTip(factory->description()); + connect(m_comboBox, &QComboBox::currentIndexChanged, this, [this] { + if (m_ignoreChanges.isLocked()) + return; + + int currentIndex = m_comboBox->currentIndex(); + QVariant id = m_comboBox->itemData(currentIndex); + m_kit->setValue(DebuggerKitAspect::id(), id); + }); + + m_manageButton = createManageButton(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); + } + + ~DebuggerKitAspectImpl() override + { + delete m_comboBox; + delete m_manageButton; + } + +private: + void addToLayoutImpl(Layouting::LayoutItem &parent) override + { + addMutableAction(m_comboBox); + parent.addItem(m_comboBox); + parent.addItem(m_manageButton); + } + + void makeReadOnly() override + { + m_manageButton->setEnabled(false); + m_comboBox->setEnabled(false); + } + + void refresh() override + { + const GuardLocker locker(m_ignoreChanges); + m_comboBox->clear(); + m_comboBox->addItem(Tr::tr("None"), QString()); + + IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); + const Utils::FilePath path = device->rootPath(); + const QList<DebuggerItem> list = DebuggerItemManager::debuggers(); + + const QList<DebuggerItem> same = Utils::filtered(list, [path](const DebuggerItem &item) { + return item.command().isSameDevice(path); + }); + const QList<DebuggerItem> other = Utils::filtered(list, [path](const DebuggerItem &item) { + return !item.command().isSameDevice(path); + }); + + for (const DebuggerItem &item : same) + m_comboBox->addItem(item.displayName(), item.id()); + + if (!same.isEmpty() && !other.isEmpty()) + m_comboBox->insertSeparator(m_comboBox->count()); + + for (const DebuggerItem &item : other) + m_comboBox->addItem(item.displayName(), item.id()); + + const DebuggerItem *item = DebuggerKitAspect::debugger(m_kit); + updateComboBox(item ? item->id() : QVariant()); + } + + QVariant currentId() const { return m_comboBox->itemData(m_comboBox->currentIndex()); } + + void updateComboBox(const QVariant &id) + { + for (int i = 0; i < m_comboBox->count(); ++i) { + if (id == m_comboBox->itemData(i)) { + m_comboBox->setCurrentIndex(i); + return; + } + } + m_comboBox->setCurrentIndex(0); + } + + Guard m_ignoreChanges; + QComboBox *m_comboBox; + QWidget *m_manageButton; +}; +} // namespace Internal + +// Check the configuration errors and return a flag mask. Provide a quick check and +// a verbose one with a list of errors. + +DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(const Kit *k) +{ + QTC_ASSERT(k, return NoDebugger); + + const DebuggerItem *item = DebuggerKitAspect::debugger(k); + if (!item) + return NoDebugger; + + const FilePath debugger = item->command(); + if (debugger.isEmpty()) + return NoDebugger; + + if (debugger.isRelativePath()) + return NoConfigurationError; + + ConfigurationErrors result = NoConfigurationError; + if (!debugger.isExecutableFile()) + result |= DebuggerNotExecutable; + + const Abi tcAbi = ToolChainKitAspect::targetAbi(k); + if (item->matchTarget(tcAbi) == DebuggerItem::DoesNotMatch) { + // currently restricting the check to desktop devices, may be extended to all device types + const IDevice::ConstPtr device = DeviceKitAspect::device(k); + if (device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) + result |= DebuggerDoesNotMatch; + } + + if (item->engineType() == NoEngineType) + return NoDebugger; + + // We need an absolute path to be able to locate Python on Windows. + if (item->engineType() == GdbEngineType) { + if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath()) + result |= DebuggerNeedsAbsolutePath; + } + + return result; +} + +const DebuggerItem *DebuggerKitAspect::debugger(const Kit *kit) +{ + QTC_ASSERT(kit, return nullptr); + const QVariant id = kit->value(DebuggerKitAspect::id()); + return DebuggerItemManager::findById(id); +} + +ProcessRunData DebuggerKitAspect::runnable(const Kit *kit) +{ + ProcessRunData runnable; + if (const DebuggerItem *item = debugger(kit)) { + FilePath cmd = item->command(); + if (cmd.isRelativePath()) { + if (const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit)) + cmd = buildDevice->searchExecutableInPath(cmd.path()); + } + runnable.command.setExecutable(cmd); + runnable.workingDirectory = item->workingDirectory(); + runnable.environment = cmd.deviceEnvironment(); + runnable.environment.set("LC_NUMERIC", "C"); + } + return runnable; +} + +Tasks DebuggerKitAspect::validateDebugger(const Kit *k) +{ + Tasks result; + + const ConfigurationErrors errors = configurationErrors(k); + if (errors == NoConfigurationError) + return result; + + QString path; + if (const DebuggerItem *item = debugger(k)) + path = item->command().toUserOutput(); + + if (errors & NoDebugger) + result << BuildSystemTask(Task::Warning, Tr::tr("No debugger set up.")); + + if (errors & DebuggerNotFound) + result << BuildSystemTask(Task::Error, Tr::tr("Debugger \"%1\" not found.").arg(path)); + + if (errors & DebuggerNotExecutable) + result << BuildSystemTask(Task::Error, Tr::tr("Debugger \"%1\" not executable.").arg(path)); + + if (errors & DebuggerNeedsAbsolutePath) { + const QString message = + Tr::tr("The debugger location must be given as an " + "absolute path (%1).").arg(path); + result << BuildSystemTask(Task::Error, message); + } + + if (errors & DebuggerDoesNotMatch) { + const QString message = Tr::tr("The ABI of the selected debugger does not " + "match the toolchain ABI."); + result << BuildSystemTask(Task::Warning, message); + } + return result; +} + + +DebuggerEngineType DebuggerKitAspect::engineType(const Kit *k) +{ + const DebuggerItem *item = debugger(k); + QTC_ASSERT(item, return NoEngineType); + return item->engineType(); +} + +QString DebuggerKitAspect::displayString(const Kit *k) +{ + const DebuggerItem *item = debugger(k); + if (!item) + return Tr::tr("No Debugger"); + QString binary = item->command().toUserOutput(); + QString name = Tr::tr("%1 Engine").arg(item->engineTypeName()); + return binary.isEmpty() ? Tr::tr("%1 <None>").arg(name) : Tr::tr("%1 using \"%2\"").arg(name, binary); +} + +QString DebuggerKitAspect::version(const Kit *k) +{ + const DebuggerItem *item = debugger(k); + QTC_ASSERT(item, return {}); + return item->version(); +} + +void DebuggerKitAspect::setDebugger(Kit *k, const QVariant &id) +{ + // Only register reasonably complete debuggers. + QTC_ASSERT(DebuggerItemManager::findById(id), return); + QTC_ASSERT(k, return); + k->setValue(DebuggerKitAspect::id(), id); +} + +Utils::Id DebuggerKitAspect::id() +{ + return "Debugger.Information"; +} + +// DebuggerKitAspectFactory + +class DebuggerKitAspectFactory : public KitAspectFactory +{ +public: + DebuggerKitAspectFactory() + { + setId(DebuggerKitAspect::id()); + setDisplayName(Tr::tr("Debugger")); + setDescription(Tr::tr("The debugger to use for this kit.")); + setPriority(28000); + } + + Tasks validate(const Kit *k) const override + { + return DebuggerKitAspect::validateDebugger(k); + } + + void setup(Kit *k) override + { + QTC_ASSERT(k, return); + + // This can be anything (Id, binary path, "auto") + // With 3.0 we have: + // <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value> + // Before we had: + // <valuemap type="QVariantMap" key="Debugger.Information"> + // <value type="QString" key="Binary">/data/dev/debugger/gdb-git/gdb/gdb</value> + // <value type="int" key="EngineType">1</value> + // </valuemap> + // Or for force auto-detected CDB + // <valuemap type="QVariantMap" key="Debugger.Information"> + // <value type="QString" key="Binary">auto</value> + // <value type="int" key="EngineType">4</value> + // </valuemap> + const QVariant rawId = k->value(DebuggerKitAspectFactory::id()); + + const Abi tcAbi = ToolChainKitAspect::targetAbi(k); + + // Get the best of the available debugger matching the kit's toolchain. + // The general idea is to find an item that exactly matches what + // is stored in the kit information, but also accept item based + // on toolchain matching as fallback with a lower priority. + + DebuggerItem bestItem; + DebuggerItem::MatchLevel bestLevel = DebuggerItem::DoesNotMatch; + const Environment systemEnvironment = Environment::systemEnvironment(); + for (const DebuggerItem &item : DebuggerItemManager::debuggers()) { + DebuggerItem::MatchLevel level = DebuggerItem::DoesNotMatch; + + if (rawId.isNull()) { + // Initial setup of a kit. + level = item.matchTarget(tcAbi); + // Hack to prefer a debugger from PATH (e.g. autodetected) over other matches. + // This improves the situation a bit if a cross-compilation tool chain has the + // same ABI as the host. + if (level == DebuggerItem::MatchesPerfectly + && !item.command().needsDevice() + && systemEnvironment.path().contains(item.command().parentDir())) { + level = DebuggerItem::MatchesPerfectlyInPath; + } + if (!item.detectionSource().isEmpty() && item.detectionSource() == k->autoDetectionSource()) + level = DebuggerItem::MatchLevel(level + 2); + } else if (rawId.type() == QVariant::String) { + // New structure. + if (item.id() == rawId) { + // Detected by ID. + level = DebuggerItem::MatchesPerfectly; + } else { + // This item does not match by ID, and is an unlikely candidate. + // However, consider using it as fallback if the tool chain fits. + level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); + } + } else { + // Old structure. + const QMap<QString, QVariant> map = rawId.toMap(); + QString binary = map.value("Binary").toString(); + if (binary == "auto") { + // This is close to the "new kit" case, except that we know + // an engine type. + DebuggerEngineType autoEngine = DebuggerEngineType(map.value("EngineType").toInt()); + if (item.engineType() == autoEngine) { + // Use item if host toolchain fits, but only as fallback. + level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); + } + } else { + // We have an executable path. + FilePath fileName = FilePath::fromUserInput(binary); + if (item.command() == fileName) { + // And it's is the path of this item. + level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); + } else { + // This item does not match by filename, and is an unlikely candidate. + // However, consider using it as fallback if the tool chain fits. + level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); + } + } + } + + if (level > bestLevel) { + bestLevel = level; + bestItem = item; + } else if (level == bestLevel) { + if (item.engineType() == bestItem.engineType()) { + const QStringList itemVersion = item.version().split('.'); + const QStringList bestItemVersion = bestItem.version().split('.'); + int end = qMax(item.version().size(), bestItemVersion.size()); + for (int i = 0; i < end; ++i) { + if (itemVersion.value(i) == bestItemVersion.value(i)) + continue; + if (itemVersion.value(i).toInt() > bestItemVersion.value(i).toInt()) + bestItem = item; + break; + } + } + } + } + + // Use the best id we found, or an invalid one. + k->setValue(DebuggerKitAspect::id(), bestLevel != DebuggerItem::DoesNotMatch ? bestItem.id() : QVariant()); + } + + KitAspect *createKitAspect(Kit *k) const override + { + return new Internal::DebuggerKitAspectImpl(k, this); + } + + void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override + { + QTC_ASSERT(kit, return); + expander->registerVariable("Debugger:Name", Tr::tr("Name of Debugger"), + [kit]() -> QString { + const DebuggerItem *item = DebuggerKitAspect::debugger(kit); + return item ? item->displayName() : Tr::tr("Unknown debugger"); + }); + + expander->registerVariable("Debugger:Type", Tr::tr("Type of Debugger Backend"), + [kit]() -> QString { + const DebuggerItem *item = DebuggerKitAspect::debugger(kit); + return item ? item->engineTypeName() : Tr::tr("Unknown debugger type"); + }); + + expander->registerVariable("Debugger:Version", Tr::tr("Debugger"), + [kit]() -> QString { + const DebuggerItem *item = DebuggerKitAspect::debugger(kit); + return item && !item->version().isEmpty() + ? item->version() : Tr::tr("Unknown debugger version"); + }); + + expander->registerVariable("Debugger:Abi", Tr::tr("Debugger"), + [kit]() -> QString { + const DebuggerItem *item = DebuggerKitAspect::debugger(kit); + return item && !item->abis().isEmpty() + ? item->abiNames().join(' ') + : Tr::tr("Unknown debugger ABI"); + }); + } + + ItemList toUserOutput(const Kit *k) const override + { + return {{Tr::tr("Debugger"), DebuggerKitAspect::displayString(k)}}; + } +}; + +const DebuggerKitAspectFactory debuggerKitAspectFactory; + +} // namespace Debugger diff --git a/src/plugins/debugger/debuggerkitinformation.h b/src/plugins/debugger/debuggerkitaspect.h similarity index 55% rename from src/plugins/debugger/debuggerkitinformation.h rename to src/plugins/debugger/debuggerkitaspect.h index 548f76793bb..fdfcdf2bd8e 100644 --- a/src/plugins/debugger/debuggerkitinformation.h +++ b/src/plugins/debugger/debuggerkitaspect.h @@ -6,25 +6,15 @@ #include "debugger_global.h" #include "debuggerconstants.h" -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/kitaspects.h> + +namespace Utils { class ProcessRunData; } namespace Debugger { -class DebuggerItem; -class DEBUGGER_EXPORT DebuggerKitAspect : public ProjectExplorer::KitAspect +class DEBUGGER_EXPORT DebuggerKitAspect { public: - DebuggerKitAspect(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const override - { return DebuggerKitAspect::validateDebugger(k); } - - void setup(ProjectExplorer::Kit *k) override; - - static const DebuggerItem *debugger(const ProjectExplorer::Kit *kit); - static ProjectExplorer::Runnable runnable(const ProjectExplorer::Kit *kit); - enum ConfigurationError { NoConfigurationError = 0x0, @@ -38,17 +28,13 @@ public: static ProjectExplorer::Tasks validateDebugger(const ProjectExplorer::Kit *k); static ConfigurationErrors configurationErrors(const ProjectExplorer::Kit *k); - - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const override; - void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; - - ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; - + static const class DebuggerItem *debugger(const ProjectExplorer::Kit *kit); + static Utils::ProcessRunData runnable(const ProjectExplorer::Kit *kit); static void setDebugger(ProjectExplorer::Kit *k, const QVariant &id); - - static Utils::Id id(); static DebuggerEngineType engineType(const ProjectExplorer::Kit *k); static QString displayString(const ProjectExplorer::Kit *k); + static QString version(const ProjectExplorer::Kit *k); + static Utils::Id id(); }; } // Debugger diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp deleted file mode 100644 index f2c149ad5a4..00000000000 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ /dev/null @@ -1,422 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "debuggerkitinformation.h" - -#include "debuggeritemmanager.h" -#include "debuggeritem.h" -#include "debuggertr.h" - -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runcontrol.h> -#include <projectexplorer/toolchain.h> - -#include <utils/environment.h> -#include <utils/guard.h> -#include <utils/filepath.h> -#include <utils/layoutbuilder.h> -#include <utils/macroexpander.h> -#include <utils/qtcassert.h> - -#include <QComboBox> -#include <QFileInfo> - -#include <utility> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Debugger { - -// -------------------------------------------------------------------------- -// DebuggerKitAspect -// -------------------------------------------------------------------------- - -namespace Internal { - -class DebuggerKitAspectWidget final : public KitAspectWidget -{ -public: - DebuggerKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki) - { - m_comboBox = createSubWidget<QComboBox>(); - m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); - m_comboBox->setEnabled(true); - - refresh(); - m_comboBox->setToolTip(ki->description()); - connect(m_comboBox, &QComboBox::currentIndexChanged, - this, &DebuggerKitAspectWidget::currentDebuggerChanged); - - m_manageButton = createManageButton(ProjectExplorer::Constants::DEBUGGER_SETTINGS_PAGE_ID); - } - - ~DebuggerKitAspectWidget() override - { - delete m_comboBox; - delete m_manageButton; - } - -private: - void addToLayout(Layouting::LayoutItem &parent) override - { - addMutableAction(m_comboBox); - parent.addItem(m_comboBox); - parent.addItem(m_manageButton); - } - - void makeReadOnly() override - { - m_manageButton->setEnabled(false); - m_comboBox->setEnabled(false); - } - - void refresh() override - { - const GuardLocker locker(m_ignoreChanges); - m_comboBox->clear(); - m_comboBox->addItem(Tr::tr("None"), QString()); - - IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); - const Utils::FilePath path = device->rootPath(); - const QList<DebuggerItem> list = DebuggerItemManager::debuggers(); - - const QList<DebuggerItem> same = Utils::filtered(list, [path](const DebuggerItem &item) { - return item.command().isSameDevice(path); - }); - const QList<DebuggerItem> other = Utils::filtered(list, [path](const DebuggerItem &item) { - return !item.command().isSameDevice(path); - }); - - for (const DebuggerItem &item : same) - m_comboBox->addItem(item.displayName(), item.id()); - - if (!same.isEmpty() && !other.isEmpty()) - m_comboBox->insertSeparator(m_comboBox->count()); - - for (const DebuggerItem &item : other) - m_comboBox->addItem(item.displayName(), item.id()); - - const DebuggerItem *item = DebuggerKitAspect::debugger(m_kit); - updateComboBox(item ? item->id() : QVariant()); - } - - void currentDebuggerChanged(int idx) - { - Q_UNUSED(idx) - if (m_ignoreChanges.isLocked()) - return; - - int currentIndex = m_comboBox->currentIndex(); - QVariant id = m_comboBox->itemData(currentIndex); - m_kit->setValue(DebuggerKitAspect::id(), id); - } - - QVariant currentId() const { return m_comboBox->itemData(m_comboBox->currentIndex()); } - - void updateComboBox(const QVariant &id) - { - for (int i = 0; i < m_comboBox->count(); ++i) { - if (id == m_comboBox->itemData(i)) { - m_comboBox->setCurrentIndex(i); - return; - } - } - m_comboBox->setCurrentIndex(0); - } - - Guard m_ignoreChanges; - QComboBox *m_comboBox; - QWidget *m_manageButton; -}; -} // namespace Internal - -DebuggerKitAspect::DebuggerKitAspect() -{ - setObjectName("DebuggerKitAspect"); - setId(DebuggerKitAspect::id()); - setDisplayName(Tr::tr("Debugger")); - setDescription(Tr::tr("The debugger to use for this kit.")); - setPriority(28000); -} - -void DebuggerKitAspect::setup(Kit *k) -{ - QTC_ASSERT(k, return); - - // This can be anything (Id, binary path, "auto") - // With 3.0 we have: - // <value type="QString" key="Debugger.Information">{75ecf347-f221-44c3-b613-ea1d29929cd4}</value> - // Before we had: - // <valuemap type="QVariantMap" key="Debugger.Information"> - // <value type="QString" key="Binary">/data/dev/debugger/gdb-git/gdb/gdb</value> - // <value type="int" key="EngineType">1</value> - // </valuemap> - // Or for force auto-detected CDB - // <valuemap type="QVariantMap" key="Debugger.Information"> - // <value type="QString" key="Binary">auto</value> - // <value type="int" key="EngineType">4</value> - // </valuemap> - const QVariant rawId = k->value(DebuggerKitAspect::id()); - - const Abi tcAbi = ToolChainKitAspect::targetAbi(k); - - // Get the best of the available debugger matching the kit's toolchain. - // The general idea is to find an item that exactly matches what - // is stored in the kit information, but also accept item based - // on toolchain matching as fallback with a lower priority. - - DebuggerItem bestItem; - DebuggerItem::MatchLevel bestLevel = DebuggerItem::DoesNotMatch; - const Environment systemEnvironment = Environment::systemEnvironment(); - for (const DebuggerItem &item : DebuggerItemManager::debuggers()) { - DebuggerItem::MatchLevel level = DebuggerItem::DoesNotMatch; - - if (rawId.isNull()) { - // Initial setup of a kit. - level = item.matchTarget(tcAbi); - // Hack to prefer a debugger from PATH (e.g. autodetected) over other matches. - // This improves the situation a bit if a cross-compilation tool chain has the - // same ABI as the host. - if (level == DebuggerItem::MatchesPerfectly - && !item.command().needsDevice() - && systemEnvironment.path().contains(item.command().parentDir())) { - level = DebuggerItem::MatchesPerfectlyInPath; - } - if (!item.detectionSource().isEmpty() && item.detectionSource() == k->autoDetectionSource()) - level = DebuggerItem::MatchLevel(level + 2); - } else if (rawId.type() == QVariant::String) { - // New structure. - if (item.id() == rawId) { - // Detected by ID. - level = DebuggerItem::MatchesPerfectly; - } else { - // This item does not match by ID, and is an unlikely candidate. - // However, consider using it as fallback if the tool chain fits. - level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); - } - } else { - // Old structure. - const QMap<QString, QVariant> map = rawId.toMap(); - QString binary = map.value("Binary").toString(); - if (binary == "auto") { - // This is close to the "new kit" case, except that we know - // an engine type. - DebuggerEngineType autoEngine = DebuggerEngineType(map.value("EngineType").toInt()); - if (item.engineType() == autoEngine) { - // Use item if host toolchain fits, but only as fallback. - level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); - } - } else { - // We have an executable path. - FilePath fileName = FilePath::fromUserInput(binary); - if (item.command() == fileName) { - // And it's is the path of this item. - level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); - } else { - // This item does not match by filename, and is an unlikely candidate. - // However, consider using it as fallback if the tool chain fits. - level = std::min(item.matchTarget(tcAbi), DebuggerItem::MatchesSomewhat); - } - } - } - - if (level > bestLevel) { - bestLevel = level; - bestItem = item; - } else if (level == bestLevel) { - if (item.engineType() == bestItem.engineType()) { - const QStringList itemVersion = item.version().split('.'); - const QStringList bestItemVersion = bestItem.version().split('.'); - int end = qMax(item.version().size(), bestItemVersion.size()); - for (int i = 0; i < end; ++i) { - if (itemVersion.value(i) == bestItemVersion.value(i)) - continue; - if (itemVersion.value(i).toInt() > bestItemVersion.value(i).toInt()) - bestItem = item; - break; - } - } - } - } - - // Use the best id we found, or an invalid one. - k->setValue(DebuggerKitAspect::id(), bestLevel != DebuggerItem::DoesNotMatch ? bestItem.id() : QVariant()); -} - -// Check the configuration errors and return a flag mask. Provide a quick check and -// a verbose one with a list of errors. - -DebuggerKitAspect::ConfigurationErrors DebuggerKitAspect::configurationErrors(const Kit *k) -{ - QTC_ASSERT(k, return NoDebugger); - - const DebuggerItem *item = DebuggerKitAspect::debugger(k); - if (!item) - return NoDebugger; - - const FilePath debugger = item->command(); - if (debugger.isEmpty()) - return NoDebugger; - - if (debugger.isRelativePath()) - return NoConfigurationError; - - ConfigurationErrors result = NoConfigurationError; - if (!debugger.isExecutableFile()) - result |= DebuggerNotExecutable; - - const Abi tcAbi = ToolChainKitAspect::targetAbi(k); - if (item->matchTarget(tcAbi) == DebuggerItem::DoesNotMatch) { - // currently restricting the check to desktop devices, may be extended to all device types - const IDevice::ConstPtr device = DeviceKitAspect::device(k); - if (device && device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) - result |= DebuggerDoesNotMatch; - } - - if (item->engineType() == NoEngineType) - return NoDebugger; - - // We need an absolute path to be able to locate Python on Windows. - if (item->engineType() == GdbEngineType) { - if (tcAbi.os() == Abi::WindowsOS && !debugger.isAbsolutePath()) - result |= DebuggerNeedsAbsolutePath; - } - - return result; -} - -const DebuggerItem *DebuggerKitAspect::debugger(const Kit *kit) -{ - QTC_ASSERT(kit, return nullptr); - const QVariant id = kit->value(DebuggerKitAspect::id()); - return DebuggerItemManager::findById(id); -} - -Runnable DebuggerKitAspect::runnable(const Kit *kit) -{ - Runnable runnable; - if (const DebuggerItem *item = debugger(kit)) { - FilePath cmd = item->command(); - if (cmd.isRelativePath()) { - if (const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit)) - cmd = buildDevice->searchExecutableInPath(cmd.path()); - } - runnable.command.setExecutable(cmd); - runnable.workingDirectory = item->workingDirectory(); - runnable.environment = kit->runEnvironment(); - runnable.environment.set("LC_NUMERIC", "C"); - } - return runnable; -} - -Tasks DebuggerKitAspect::validateDebugger(const Kit *k) -{ - Tasks result; - - const ConfigurationErrors errors = configurationErrors(k); - if (errors == NoConfigurationError) - return result; - - QString path; - if (const DebuggerItem *item = debugger(k)) - path = item->command().toUserOutput(); - - if (errors & NoDebugger) - result << BuildSystemTask(Task::Warning, Tr::tr("No debugger set up.")); - - if (errors & DebuggerNotFound) - result << BuildSystemTask(Task::Error, Tr::tr("Debugger \"%1\" not found.").arg(path)); - - if (errors & DebuggerNotExecutable) - result << BuildSystemTask(Task::Error, Tr::tr("Debugger \"%1\" not executable.").arg(path)); - - if (errors & DebuggerNeedsAbsolutePath) { - const QString message = - Tr::tr("The debugger location must be given as an " - "absolute path (%1).").arg(path); - result << BuildSystemTask(Task::Error, message); - } - - if (errors & DebuggerDoesNotMatch) { - const QString message = Tr::tr("The ABI of the selected debugger does not " - "match the toolchain ABI."); - result << BuildSystemTask(Task::Warning, message); - } - return result; -} - -KitAspectWidget *DebuggerKitAspect::createConfigWidget(Kit *k) const -{ - return new Internal::DebuggerKitAspectWidget(k, this); -} - -void DebuggerKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const -{ - QTC_ASSERT(kit, return); - expander->registerVariable("Debugger:Name", Tr::tr("Name of Debugger"), - [kit]() -> QString { - const DebuggerItem *item = debugger(kit); - return item ? item->displayName() : Tr::tr("Unknown debugger"); - }); - - expander->registerVariable("Debugger:Type", Tr::tr("Type of Debugger Backend"), - [kit]() -> QString { - const DebuggerItem *item = debugger(kit); - return item ? item->engineTypeName() : Tr::tr("Unknown debugger type"); - }); - - expander->registerVariable("Debugger:Version", Tr::tr("Debugger"), - [kit]() -> QString { - const DebuggerItem *item = debugger(kit); - return item && !item->version().isEmpty() - ? item->version() : Tr::tr("Unknown debugger version"); - }); - - expander->registerVariable("Debugger:Abi", Tr::tr("Debugger"), - [kit]() -> QString { - const DebuggerItem *item = debugger(kit); - return item && !item->abis().isEmpty() - ? item->abiNames().join(' ') - : Tr::tr("Unknown debugger ABI"); - }); -} - -KitAspect::ItemList DebuggerKitAspect::toUserOutput(const Kit *k) const -{ - return {{Tr::tr("Debugger"), displayString(k)}}; -} - -DebuggerEngineType DebuggerKitAspect::engineType(const Kit *k) -{ - const DebuggerItem *item = debugger(k); - QTC_ASSERT(item, return NoEngineType); - return item->engineType(); -} - -QString DebuggerKitAspect::displayString(const Kit *k) -{ - const DebuggerItem *item = debugger(k); - if (!item) - return Tr::tr("No Debugger"); - QString binary = item->command().toUserOutput(); - QString name = Tr::tr("%1 Engine").arg(item->engineTypeName()); - return binary.isEmpty() ? Tr::tr("%1 <None>").arg(name) : Tr::tr("%1 using \"%2\"").arg(name, binary); -} - -void DebuggerKitAspect::setDebugger(Kit *k, const QVariant &id) -{ - // Only register reasonably complete debuggers. - QTC_ASSERT(DebuggerItemManager::findById(id), return); - QTC_ASSERT(k, return); - k->setValue(DebuggerKitAspect::id(), id); -} - -Utils::Id DebuggerKitAspect::id() -{ - return "Debugger.Information"; -} - -} // namespace Debugger diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp index 2a5ec866edb..81e3a4435aa 100644 --- a/src/plugins/debugger/debuggermainwindow.cpp +++ b/src/plugins/debugger/debuggermainwindow.cpp @@ -162,6 +162,7 @@ DebuggerMainWindowPrivate::DebuggerMainWindowPrivate(DebuggerMainWindow *parent) { m_centralWidgetStack = new QStackedWidget; m_statusLabel = new Utils::StatusLabel; + m_statusLabel->setObjectName("DebuggerStatusLabel"); // used by Squish StyleHelper::setPanelWidget(m_statusLabel); m_statusLabel->setIndent(2 * QFontMetrics(q->font()).horizontalAdvance(QChar('x'))); m_editorPlaceHolder = new EditorManagerPlaceHolder; @@ -399,7 +400,7 @@ void DebuggerMainWindow::enterDebugMode() if (theMainWindow->d->needRestoreOnModeEnter) theMainWindow->restorePersistentSettings(); - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); const QString lastPerspectiveId = settings->value(LAST_PERSPECTIVE_KEY).toString(); Perspective *perspective = Perspective::findPerspective(lastPerspectiveId); // If we don't find a perspective with the stored name, pick any. @@ -440,7 +441,7 @@ void DebuggerMainWindow::leaveDebugMode() void DebuggerMainWindow::restorePersistentSettings() { qCDebug(perspectivesLog) << "RESTORE ALL PERSPECTIVES"; - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(MAINWINDOW_KEY); // state2 is current, state is kept for upgradeing from <=4.10 @@ -488,7 +489,7 @@ void DebuggerMainWindow::savePersistentSettings() const states.insert(type, QVariant::fromValue(state)); } - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(MAINWINDOW_KEY); settings->setValue(CHANGED_DOCK_KEY, QStringList(Utils::toList(d->m_persistentChangedDocks))); settings->setValue(STATE_KEY2, states); @@ -785,6 +786,11 @@ QString Perspective::id() const return d->m_id; } +QString Perspective::parentPerspectiveId() const +{ + return d->m_parentPerspectiveId; +} + QString Perspective::name() const { return d->m_name; diff --git a/src/plugins/debugger/debuggermainwindow.h b/src/plugins/debugger/debuggermainwindow.h index 051704d4d9d..fbe959f19fd 100644 --- a/src/plugins/debugger/debuggermainwindow.h +++ b/src/plugins/debugger/debuggermainwindow.h @@ -76,6 +76,7 @@ public: void setShouldPersistChecker(const ShouldPersistChecker &checker); QString id() const; // Currently used by GammaRay plugin. + QString parentPerspectiveId() const; QString name() const; QWidget *centralWidget() const; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index dffa503c456..bb90a260cc9 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -14,7 +14,7 @@ #include "debuggermainwindow.h" #include "debuggerrunconfigurationaspect.h" #include "debuggerruncontrol.h" -#include "debuggerkitinformation.h" +#include "debuggerkitaspect.h" #include "debuggertr.h" #include "breakhandler.h" #include "enginemanager.h" @@ -25,14 +25,11 @@ #include "sourceutils.h" #include "shared/hostutils.h" #include "console/console.h" - #include "commonoptionspage.h" #include "analyzer/analyzerconstants.h" #include "analyzer/analyzermanager.h" -#include <app/app_version.h> - #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -58,10 +55,12 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildmanager.h> +#include <projectexplorer/buildsystem.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/deviceprocessesdialog.h> #include <projectexplorer/devicesupport/sshparameters.h> #include <projectexplorer/itaskhandler.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorericons.h> @@ -106,20 +105,20 @@ #include <QFileDialog> #include <QHeaderView> #include <QInputDialog> +#include <QJsonDocument> +#include <QJsonObject> #include <QMenu> #include <QMessageBox> #include <QPointer> #include <QPushButton> #include <QScopeGuard> -#include <QSettings> +#include <QSortFilterProxyModel> #include <QStackedWidget> #include <QTextBlock> #include <QToolButton> #include <QTreeWidget> #include <QVBoxLayout> #include <QVariant> -#include <QJsonDocument> -#include <QJsonObject> #ifdef WITH_TESTS @@ -364,7 +363,6 @@ const char MENU_GROUP_SPECIAL[] = "Debugger.Group.Special"; const char MENU_GROUP_START_QML[] = "Debugger.Group.Start.Qml"; void addCdbOptionPages(QList<IOptionsPage*> *opts); -void addGdbOptionPages(QList<IOptionsPage*> *opts); static QIcon startIcon(bool toolBarStyle) { @@ -511,6 +509,7 @@ public: splitter->setObjectName("DebugModeWidget"); mainWindow->addSubPerspectiveSwitcher(EngineManager::engineChooser()); + mainWindow->addSubPerspectiveSwitcher(EngineManager::dapEngineChooser()); setWidget(splitter); @@ -586,12 +585,6 @@ public: RunControl *attachToRunningProcess(Kit *kit, const ProcessInfo &process, bool contAfterAttach); - void writeSettings() - { - m_debuggerSettings.writeSettings(); -// writeWindowSettings(); - } - void breakpointSetMarginActionTriggered(bool isMessageOnly, const ContextData &data) { QString message; @@ -618,6 +611,21 @@ public: BreakpointManager::setOrRemoveBreakpoint(data, message); } + void addFontSizeAdaptation(QWidget *widget); + BaseTreeView *createBreakpointManagerView(const QByteArray &settingsKey); + QWidget *createBreakpointManagerWindow(BaseTreeView *breakpointManagerView, + const QString &title, + const QString &objectName); + + BaseTreeView *createEngineManagerView(QAbstractItemModel *model, + const QString &title, + const QByteArray &settingsKey); + QWidget *createEngineManagerWindow(BaseTreeView *engineManagerView, + const QString &title, + const QString &objectName); + + void createDapDebuggerPerspective(QWidget *globalLogWindow); + void editorOpened(IEditor *editor); void updateBreakMenuItem(IEditor *editor); void requestMark(TextEditorWidget *widget, int lineNumber, @@ -663,6 +671,7 @@ public: ProxyAction m_hiddenStopAction; QAction m_undisturbableAction; OptionalAction m_startAction; + OptionalAction m_startDapAction; QAction m_debugWithoutDeployAction{Tr::tr("Start Debugging Without Deployment")}; QAction m_startAndDebugApplicationAction{Tr::tr("Start and Debug External Application...")}; QAction m_attachToRunningApplication{Tr::tr("Attach to Running Application...")}; @@ -687,18 +696,13 @@ public: QTimer m_shutdownTimer; Console m_console; // ensure Debugger Console is created before settings are taken into account - DebuggerSettings m_debuggerSettings; QStringList m_arguments; - DebuggerItemManager m_debuggerItemManager; - QList<IOptionsPage *> m_optionPages; IContext m_debugModeContext; Perspective m_perspective{Constants::PRESET_PERSPECTIVE_ID, Tr::tr("Debugger")}; - - DebuggerKitAspect debuggerKitAspect; - CommonOptionsPage commonOptionsPage; + Perspective m_perspectiveDap{Constants::DAP_PERSPECTIVE_ID, Tr::tr("DAP")}; DebuggerRunWorkerFactory debuggerWorkerFactory; @@ -708,6 +712,84 @@ public: // return isDebuggableScript; }; +static void addLabel(QWidget *widget, const QString &text) +{ + auto vbox = qobject_cast<QVBoxLayout *>(widget->layout()); + QTC_ASSERT(vbox, return); + auto label = new QLabel(widget); + label->setText(text); + label->setContentsMargins(6, 6, 6, 6); + vbox->insertWidget(0, label); +}; + +void DebuggerPluginPrivate::addFontSizeAdaptation(QWidget *widget) +{ + QObject::connect(TextEditorSettings::instance(), + &TextEditorSettings::fontSettingsChanged, + this, + [widget](const FontSettings &fs) { + if (!settings().fontSizeFollowsEditor()) + return; + qreal size = fs.fontZoom() * fs.fontSize() / 100.; + QFont font = widget->font(); + font.setPointSizeF(size); + widget->setFont(font); + }); +}; + +BaseTreeView *DebuggerPluginPrivate::createBreakpointManagerView(const QByteArray &settingsKey) +{ + auto breakpointManagerView = new BaseTreeView; + breakpointManagerView->setActivationMode(Utils::DoubleClickActivation); + breakpointManagerView->setIconSize(QSize(10, 10)); + breakpointManagerView->setWindowIcon(Icons::BREAKPOINTS.icon()); + breakpointManagerView->setSelectionMode(QAbstractItemView::ExtendedSelection); + breakpointManagerView->setSettings(ICore::settings(), settingsKey); + breakpointManagerView->setRootIsDecorated(true); + breakpointManagerView->setModel(BreakpointManager::model()); + breakpointManagerView->setSpanColumn(BreakpointFunctionColumn); + breakpointManagerView->enableColumnHiding(); + return breakpointManagerView; +} + +QWidget *DebuggerPluginPrivate::createBreakpointManagerWindow(BaseTreeView *breakpointManagerView, + const QString &title, + const QString &objectName) +{ + auto breakpointManagerWindow = addSearch(breakpointManagerView); + breakpointManagerWindow->setWindowTitle(title); + breakpointManagerWindow->setObjectName(objectName); + addLabel(breakpointManagerWindow, breakpointManagerWindow->windowTitle()); + addFontSizeAdaptation(breakpointManagerWindow); + return breakpointManagerWindow; +} + +BaseTreeView *DebuggerPluginPrivate::createEngineManagerView(QAbstractItemModel *model, + const QString &title, + const QByteArray &settingsKey) +{ + auto engineManagerView = new BaseTreeView; + engineManagerView->setWindowTitle(title); + engineManagerView->setSettings(ICore::settings(), settingsKey); + engineManagerView->setIconSize(QSize(10, 10)); + engineManagerView->setModel(model); + engineManagerView->setSelectionMode(QAbstractItemView::SingleSelection); + engineManagerView->enableColumnHiding(); + return engineManagerView; +} + +QWidget *DebuggerPluginPrivate::createEngineManagerWindow(BaseTreeView *engineManagerView, + const QString &title, + const QString &objectName) +{ + auto engineManagerWindow = addSearch(engineManagerView); + engineManagerWindow->setWindowTitle(title); + engineManagerWindow->setObjectName(objectName); + addLabel(engineManagerWindow, engineManagerWindow->windowTitle()); + addFontSizeAdaptation(engineManagerWindow); + return engineManagerWindow; +} + DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) { qRegisterMetaType<ContextData>("ContextData"); @@ -726,7 +808,9 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) // Task integration. //: Category under which Analyzer tasks are listed in Issues view - TaskHub::addCategory(ANALYZERTASK_ID, Tr::tr("Debugger")); + TaskHub::addCategory({ANALYZERTASK_ID, + Tr::tr("Valgrind"), + Tr::tr("Issues that the Valgrind tools found when analyzing the code.")}); const Context debuggerNotRunning(C_DEBUGGER_NOTRUNNING); ICore::addAdditionalContext(debuggerNotRunning); @@ -768,63 +852,23 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) act->setEnabled(false); Command *cmd = ActionManager::registerAction(act, Constants::OPEN_MEMORY_EDITOR); - TaskHub::addCategory(TASK_CATEGORY_DEBUGGER_DEBUGINFO, Tr::tr("Debug Information")); - TaskHub::addCategory(TASK_CATEGORY_DEBUGGER_RUNTIME, Tr::tr("Debugger Runtime")); + TaskHub::addCategory({TASK_CATEGORY_DEBUGGER_RUNTIME, + Tr::tr("Debugger Runtime"), + Tr::tr("Issues with starting the debugger.")}); - m_debuggerSettings.readSettings(); - - const auto addLabel = [](QWidget *widget, const QString &text) { - auto vbox = qobject_cast<QVBoxLayout *>(widget->layout()); - QTC_ASSERT(vbox, return); - auto label = new QLabel(widget); - label->setText(text); - label->setContentsMargins(6, 6, 6, 6); - vbox->insertWidget(0, label); - }; - - const auto addFontSizeAdaptation = [this](QWidget *widget) { - QObject::connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged, - this, [widget](const FontSettings &settings) { - if (!debuggerSettings()->fontSizeFollowsEditor.value()) - return; - qreal size = settings.fontZoom() * settings.fontSize() / 100.; - QFont font = widget->font(); - font.setPointSizeF(size); - widget->setFont(font); - }); - }; - - auto breakpointManagerView = new BaseTreeView; - breakpointManagerView->setActivationMode(Utils::DoubleClickActivation); - breakpointManagerView->setIconSize(QSize(10, 10)); - breakpointManagerView->setWindowIcon(Icons::BREAKPOINTS.icon()); - breakpointManagerView->setSelectionMode(QAbstractItemView::ExtendedSelection); - breakpointManagerView->setSettings(ICore::settings(), "Debugger.BreakWindow"); - breakpointManagerView->setRootIsDecorated(true); - breakpointManagerView->setModel(BreakpointManager::model()); - breakpointManagerView->setSpanColumn(BreakpointFunctionColumn); - breakpointManagerView->enableColumnHiding(); - - auto breakpointManagerWindow = addSearch(breakpointManagerView); - breakpointManagerWindow->setWindowTitle(Tr::tr("Breakpoint Preset")); - breakpointManagerWindow->setObjectName("Debugger.Docks.BreakpointManager"); - addLabel(breakpointManagerWindow, breakpointManagerWindow->windowTitle()); - addFontSizeAdaptation(breakpointManagerWindow); + auto breakpointManagerView = createBreakpointManagerView("Debugger.BreakWindow"); + auto breakpointManagerWindow + = createBreakpointManagerWindow(breakpointManagerView, + Tr::tr("Breakpoint Preset"), + "Debugger.Docks.BreakpointManager"); // Snapshot - auto engineManagerView = new BaseTreeView; - engineManagerView->setWindowTitle(Tr::tr("Running Debuggers")); - engineManagerView->setSettings(ICore::settings(), "Debugger.SnapshotView"); - engineManagerView->setIconSize(QSize(10, 10)); - engineManagerView->setModel(EngineManager::model()); - engineManagerView->setSelectionMode(QAbstractItemView::SingleSelection); - engineManagerView->enableColumnHiding(); - - auto engineManagerWindow = addSearch(engineManagerView); - engineManagerWindow->setWindowTitle(Tr::tr("Debugger Perspectives")); - engineManagerWindow->setObjectName("Debugger.Docks.Snapshots"); - addLabel(engineManagerWindow, engineManagerWindow->windowTitle()); - addFontSizeAdaptation(engineManagerWindow); + auto engineManagerView = createEngineManagerView(EngineManager::model(), + Tr::tr("Running Debuggers"), + "Debugger.SnapshotView"); + auto engineManagerWindow = createEngineManagerWindow(engineManagerView, + Tr::tr("Debugger Perspectives"), + "Debugger.Docks.Snapshots"); // Logging auto globalLogWindow = new GlobalLogWindow; @@ -1125,8 +1169,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) // QTC_CHECK(false); // }); - m_optionPages.append(new LocalsAndExpressionsOptionsPage); - addGdbOptionPages(&m_optionPages); addCdbOptionPages(&m_optionPages); connect(ModeManager::instance(), &ModeManager::currentModeAboutToChange, this, [] { @@ -1158,10 +1200,6 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) // Connections // - // Core - connect(ICore::instance(), &ICore::saveSettingsRequested, - this, &DebuggerPluginPrivate::writeSettings); - // ProjectExplorer connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::runActionsUpdated, this, &DebuggerPluginPrivate::updatePresetState); @@ -1174,9 +1212,13 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) // Application interaction // Use a queued connection so the dialog isn't triggered in the same event. - connect(debuggerSettings()->settingsDialog.action(), &QAction::triggered, this, + connect(settings().settingsDialog.action(), &QAction::triggered, this, [] { ICore::showOptionsDialog(DEBUGGER_COMMON_SETTINGS_ID); }, Qt::QueuedConnection); + EngineManager::registerDefaultPerspective(Tr::tr("Debugger Preset"), + {}, + Constants::PRESET_PERSPECTIVE_ID); + m_perspective.useSubPerspectiveSwitcher(EngineManager::engineChooser()); m_perspective.addToolBarAction(&m_startAction); @@ -1184,6 +1226,7 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) m_perspective.addWindow(breakpointManagerWindow, Perspective::SplitHorizontal, engineManagerWindow); m_perspective.addWindow(globalLogWindow, Perspective::AddToTab, nullptr, false, Qt::TopDockWidgetArea); + createDapDebuggerPerspective(globalLogWindow); setInitialState(); connect(ProjectManager::instance(), &ProjectManager::startupProjectChanged, @@ -1194,6 +1237,66 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) this, &DebuggerPluginPrivate::updatePresetState); } +void DebuggerPluginPrivate::createDapDebuggerPerspective(QWidget *globalLogWindow) +{ + struct DapPerspective + { + QString name; + char const *runMode; + }; + + const QList<DapPerspective> perspectiveList = { + DapPerspective{Tr::tr("CMake Preset"), ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE}, + DapPerspective{Tr::tr("GDB Preset"), ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE}, + DapPerspective{Tr::tr("Python Preset"), + ProjectExplorer::Constants::DAP_PY_DEBUG_RUN_MODE}, + }; + + for (const DapPerspective &dp : perspectiveList) + EngineManager::registerDefaultPerspective(dp.name, "DAP", Constants::DAP_PERSPECTIVE_ID); + + connect(&m_startDapAction, &QAction::triggered, this, [perspectiveList] { + QComboBox *combo = qobject_cast<QComboBox *>(EngineManager::dapEngineChooser()); + if (perspectiveList.size() > combo->currentIndex()) + ProjectExplorerPlugin::runStartupProject(perspectiveList.at(combo->currentIndex()) + .runMode, + false); + }); + + auto breakpointManagerView = createBreakpointManagerView("DAPDebugger.BreakWindow"); + auto breakpointManagerWindow + = createBreakpointManagerWindow(breakpointManagerView, + Tr::tr("DAP Breakpoint Preset"), + "DAPDebugger.Docks.BreakpointManager"); + + // Snapshot + auto engineManagerView = createEngineManagerView(EngineManager::dapModel(), + Tr::tr("Running Debuggers"), + "DAPDebugger.SnapshotView"); + auto engineManagerWindow = createEngineManagerWindow(engineManagerView, + Tr::tr("DAP Debugger Perspectives"), + "DAPDebugger.Docks.Snapshots"); + + m_perspectiveDap.addToolBarAction(&m_startDapAction); + m_startDapAction.setToolTip(Tr::tr("Start DAP Debugging")); + m_startDapAction.setText(Tr::tr("Start DAP Debugging")); + m_startDapAction.setEnabled(true); + m_startDapAction.setIcon(startIcon(true)); + m_startDapAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + m_startDapAction.setVisible(true); + + m_perspectiveDap.useSubPerspectiveSwitcher(EngineManager::dapEngineChooser()); + + m_perspectiveDap.addWindow(engineManagerWindow, Perspective::SplitVertical, nullptr); + m_perspectiveDap.addWindow(breakpointManagerWindow, + Perspective::SplitHorizontal, + engineManagerWindow); + m_perspectiveDap.addWindow(globalLogWindow, + Perspective::AddToTab, + nullptr, + false, + Qt::TopDockWidgetArea); +} DebuggerPluginPrivate::~DebuggerPluginPrivate() { @@ -1210,6 +1313,9 @@ static Kit *guessKitFromAbis(const Abis &abis) { Kit *kit = nullptr; + if (!KitManager::waitForLoaded()) + return kit; + // Try to find a kit via ABI. if (!abis.isEmpty()) { // Try exact abis. @@ -1270,9 +1376,11 @@ bool DebuggerPluginPrivate::parseArgument(QStringList::const_iterator &it, return false; } } else if (key == "kit") { - kit = KitManager::kit(Id::fromString(val)); - if (!kit) - kit = KitManager::kit(Utils::equal(&Kit::displayName, val)); + if (KitManager::waitForLoaded()) { + kit = KitManager::kit(Id::fromString(val)); + if (!kit) + kit = KitManager::kit(Utils::equal(&Kit::displayName, val)); + } } else if (key == "server") { startMode = AttachToRemoteServer; remoteChannel = val; @@ -1378,12 +1486,12 @@ void DebuggerPluginPrivate::parseCommandLineArguments() QTimer::singleShot(0, this, &DebuggerPluginPrivate::runScheduled); } -static void setConfigValue(const QString &name, const QVariant &value) +static void setConfigValue(const Key &name, const QVariant &value) { ICore::settings()->setValue("DebugMode/" + name, value); } -static QVariant configValue(const QString &name) +static QVariant configValue(const Key &name) { return ICore::settings()->value("DebugMode/" + name); } @@ -1397,9 +1505,8 @@ void DebuggerPluginPrivate::updatePresetState() RunConfiguration *startupRunConfig = ProjectManager::startupRunConfiguration(); DebuggerEngine *currentEngine = EngineManager::currentEngine(); - QString whyNot; - const bool canRun = - ProjectExplorerPlugin::canRunStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE, &whyNot); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject( + ProjectExplorer::Constants::DEBUG_RUN_MODE); QString startupRunConfigName; if (startupRunConfig) @@ -1408,8 +1515,8 @@ void DebuggerPluginPrivate::updatePresetState() startupRunConfigName = startupProject->displayName(); // Restrict width, otherwise Creator gets too wide, see QTCREATORBUG-21885 - const QString startToolTip = - canRun ? Tr::tr("Start debugging of startup project") : whyNot; + const QString startToolTip = canRun ? Tr::tr("Start debugging of startup project") + : canRun.error(); m_startAction.setToolTip(startToolTip); m_startAction.setText(Tr::tr("Start Debugging of Startup Project")); @@ -1417,11 +1524,11 @@ void DebuggerPluginPrivate::updatePresetState() if (!currentEngine) { // No engine running -- or -- we have a running engine but it does not // correspond to the current start up project. - m_startAction.setEnabled(canRun); + m_startAction.setEnabled(bool(canRun)); m_startAction.setIcon(startIcon(true)); m_startAction.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); m_startAction.setVisible(true); - m_debugWithoutDeployAction.setEnabled(canRun); + m_debugWithoutDeployAction.setEnabled(bool(canRun)); m_visibleStartAction.setAction(&m_startAction); m_hiddenStopAction.setAction(&m_undisturbableAction); return; @@ -1435,7 +1542,7 @@ void DebuggerPluginPrivate::updatePresetState() m_startAction.setEnabled(false); m_startAction.setVisible(false); - m_debugWithoutDeployAction.setEnabled(canRun); + m_debugWithoutDeployAction.setEnabled(bool(canRun)); const DebuggerState state = currentEngine->state(); @@ -1453,8 +1560,8 @@ void DebuggerPluginPrivate::updatePresetState() m_hiddenStopAction.setAction(ActionManager::command(Constants::INTERRUPT)->action()); } else if (state == DebuggerFinished) { // We don't want to do anything anymore. - m_startAction.setEnabled(canRun); - m_debugWithoutDeployAction.setEnabled(canRun); + m_startAction.setEnabled(bool(canRun)); + m_debugWithoutDeployAction.setEnabled(bool(canRun)); m_visibleStartAction.setAction(ActionManager::command(DEBUGGER_START)->action()); m_hiddenStopAction.setAction(&m_undisturbableAction); } else if (state == InferiorUnrunnable) { @@ -1477,10 +1584,10 @@ void DebuggerPluginPrivate::updatePresetState() // FIXME: Decentralize the actions below const bool actionsEnabled = currentEngine->debuggerActionsEnabled(); const bool canDeref = actionsEnabled && currentEngine->hasCapability(AutoDerefPointersCapability); - DebuggerSettings *s = debuggerSettings(); - s->autoDerefPointers.setEnabled(canDeref); - s->autoDerefPointers.setEnabled(true); - s->expandStack.setEnabled(actionsEnabled); + DebuggerSettings &s = settings(); + s.autoDerefPointers.setEnabled(canDeref); + s.autoDerefPointers.setEnabled(true); + s.expandStack.setEnabled(actionsEnabled); m_startAndDebugApplicationAction.setEnabled(true); m_attachToQmlPortAction.setEnabled(true); @@ -1560,7 +1667,7 @@ void DebuggerPluginPrivate::reloadDebuggingHelpers() void DebuggerPluginPrivate::startRemoteCdbSession() { - const QString connectionKey = "CdbRemoteConnection"; + const Key connectionKey = "CdbRemoteConnection"; Kit *kit = findUniversalCdbKit(); QTC_ASSERT(kit, return); @@ -1673,10 +1780,11 @@ RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit, const bool isWindows = (tcAbi.os() == Abi::WindowsOS); if (isWindows && isWinProcessBeingDebugged(processInfo.processId)) { AsynchronousMessageBox::warning( - Tr::tr("Process Already Under Debugger Control"), - Tr::tr("The process %1 is already under the control of a debugger.\n" - "%2 cannot attach to it.").arg(processInfo.processId) - .arg(Core::Constants::IDE_DISPLAY_NAME)); + Tr::tr("Process Already Under Debugger Control"), + Tr::tr("The process %1 is already under the control of a debugger.\n" + "%2 cannot attach to it.") + .arg(processInfo.processId) + .arg(QGuiApplication::applicationDisplayName())); return nullptr; } @@ -1702,6 +1810,20 @@ RunControl *DebuggerPluginPrivate::attachToRunningProcess(Kit *kit, return debugger->runControl(); } +void DebuggerPlugin::attachToProcess(const qint64 processId, const Utils::FilePath &executable) +{ + ProcessInfo processInfo; + processInfo.processId = processId; + processInfo.executable = executable.toString(); + + auto kitChooser = new KitChooser; + kitChooser->setShowIcons(true); + kitChooser->populate(); + Kit *kit = kitChooser->currentKit(); + + dd->attachToRunningProcess(kit, processInfo, false); +} + void DebuggerPlugin::attachExternalApplication(RunControl *rc) { ProcessHandle pid = rc->applicationProcessHandle(); @@ -1745,17 +1867,17 @@ void DebuggerPlugin::autoDetectDebuggersForDevice(const FilePaths &searchPaths, const QString &detectionSource, QString *logMessage) { - dd->m_debuggerItemManager.autoDetectDebuggersForDevice(searchPaths, detectionSource, logMessage); + DebuggerItemManager::autoDetectDebuggersForDevice(searchPaths, detectionSource, logMessage); } void DebuggerPlugin::removeDetectedDebuggers(const QString &detectionSource, QString *logMessage) { - dd->m_debuggerItemManager.removeDetectedDebuggers(detectionSource, logMessage); + DebuggerItemManager::removeDetectedDebuggers(detectionSource, logMessage); } void DebuggerPlugin::listDetectedDebuggers(const QString &detectionSource, QString *logMessage) { - dd->m_debuggerItemManager.listDetectedDebuggers(detectionSource, logMessage); + DebuggerItemManager::listDetectedDebuggers(detectionSource, logMessage); } void DebuggerPluginPrivate::attachToQmlPort() @@ -1959,8 +2081,8 @@ void DebuggerPluginPrivate::setInitialState() m_enableOrDisableBreakpointAction.setEnabled(false); //m_snapshotAction.setEnabled(false); - debuggerSettings()->autoDerefPointers.setEnabled(true); - debuggerSettings()->expandStack.setEnabled(false); + settings().autoDerefPointers.setEnabled(true); + settings().expandStack.setEnabled(false); } void DebuggerPluginPrivate::updateDebugWithoutDeployMenu() @@ -2032,9 +2154,7 @@ void DebuggerPluginPrivate::remoteCommand(const QStringList &options) void DebuggerPluginPrivate::extensionsInitialized() { - QTimer::singleShot(0, this, [this]{ - m_debuggerItemManager.extensionsInitialized(); - }); + QTimer::singleShot(0, this, &DebuggerItemManager::restoreDebuggers); // If the CppEditor or QmlJS editor plugin is there, we want to add something to // the editor context menu. @@ -2055,11 +2175,11 @@ void DebuggerPluginPrivate::extensionsInitialized() QWidget *DebuggerPluginPrivate::addSearch(BaseTreeView *treeView) { - BoolAspect &act = debuggerSettings()->useAlternatingRowColors; - treeView->setAlternatingRowColors(act.value()); + BoolAspect &act = settings().useAlternatingRowColors; + treeView->setAlternatingRowColors(act()); treeView->setProperty(PerspectiveState::savesHeaderKey(), true); connect(&act, &BaseAspect::changed, treeView, [treeView] { - treeView->setAlternatingRowColors(debuggerSettings()->useAlternatingRowColors.value()); + treeView->setAlternatingRowColors(settings().useAlternatingRowColors()); }); return ItemViewFind::createSearchableWrapper(treeView); @@ -2239,7 +2359,7 @@ bool wantRunTool(ToolMode toolMode, const QString &toolName) if (Utils::CheckableMessageBox::question(ICore::dialogParent(), title, message, - QString("AnalyzerCorrectModeWarning")) + Key("AnalyzerCorrectModeWarning")) != QMessageBox::Yes) return false; } diff --git a/src/plugins/debugger/debuggerplugin.h b/src/plugins/debugger/debuggerplugin.h index 209bd0454c4..fde9c2b462b 100644 --- a/src/plugins/debugger/debuggerplugin.h +++ b/src/plugins/debugger/debuggerplugin.h @@ -40,6 +40,8 @@ private: QString *logMessage); Q_SLOT void removeDetectedDebuggers(const QString &detectionId, QString *logMessage); Q_SLOT void listDetectedDebuggers(const QString &detectionId, QString *logMessage); + + Q_SLOT void attachToProcess(const qint64 processId, const Utils::FilePath &executable); }; } // Debugger::Internal diff --git a/src/plugins/debugger/debuggerprotocol.cpp b/src/plugins/debugger/debuggerprotocol.cpp index 8ab7a71b339..6df472afa52 100644 --- a/src/plugins/debugger/debuggerprotocol.cpp +++ b/src/plugins/debugger/debuggerprotocol.cpp @@ -655,13 +655,13 @@ QString decodeData(const QString &ba, const QString &encoding) } case DebuggerEncoding::HexEncodedUtf16: { const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8()); - result = QString::fromUtf16(reinterpret_cast<const ushort *> + result = QString::fromUtf16(reinterpret_cast<const char16_t *> (decodedBa.data()), decodedBa.size() / 2); break; } case DebuggerEncoding::HexEncodedUcs4: { const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8()); - result = QString::fromUcs4(reinterpret_cast<const uint *> + result = QString::fromUcs4(reinterpret_cast<const char32_t *> (decodedBa.data()), decodedBa.size() / 4); break; } @@ -706,7 +706,7 @@ QString decodeData(const QString &ba, const QString &encoding) const QByteArray scopeId = p == -1 ? QByteArray() : QByteArray::fromHex(ba.mid(p + 1).toUtf8()); if (!scopeId.isEmpty()) - ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const ushort *>(scopeId.constData()), + ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const char16_t *>(scopeId.constData()), scopeId.length() / 2)); return ip6.toString(); } @@ -737,9 +737,11 @@ QString decodeData(const QString &ba, const QString &encoding) if (spec == Qt::OffsetFromUTC) { dateTime = QDateTime(date, time, spec, offset); } else if (spec == Qt::TimeZone) { - if (!QTimeZone::isTimeZoneIdAvailable(timeZoneId)) + QTimeZone tz(timeZoneId); + if (!tz.isValid()) return QLatin1String("<unavailable>"); - dateTime = QDateTime(date, time, QTimeZone(timeZoneId)); + + dateTime = QDateTime(date, time, tz); } else { dateTime = QDateTime(date, time, spec); } diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index 8811934aee4..173b6b1a5e5 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -14,7 +14,7 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildstep.h> #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfiguration.h> @@ -154,7 +154,7 @@ bool DebuggerRunConfigurationAspect::useCppDebugger() const static bool projectHasQmlDefines(ProjectExplorer::Project *project) { - auto projectInfo = CppEditor::CppModelManager::instance()->projectInfo(project); + auto projectInfo = CppEditor::CppModelManager::projectInfo(project); if (!projectInfo) // we may have e.g. a Python project return false; return Utils::anyOf(projectInfo->projectParts(), @@ -211,7 +211,7 @@ int DebuggerRunConfigurationAspect::portsUsedByDebugger() const return ports; } -void DebuggerRunConfigurationAspect::toMap(QVariantMap &map) const +void DebuggerRunConfigurationAspect::toMap(Store &map) const { m_cppAspect->toMap(map); m_qmlAspect->toMap(map); @@ -223,7 +223,7 @@ void DebuggerRunConfigurationAspect::toMap(QVariantMap &map) const map.insert("RunConfiguration.UseQmlDebuggerAuto", m_qmlAspect->value() == TriState::Default); } -void DebuggerRunConfigurationAspect::fromMap(const QVariantMap &map) +void DebuggerRunConfigurationAspect::fromMap(const Store &map) { m_cppAspect->fromMap(map); m_qmlAspect->fromMap(map); diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.h b/src/plugins/debugger/debuggerrunconfigurationaspect.h index 53b9c1f54d2..c6357cf4a31 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.h +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.h @@ -17,8 +17,8 @@ public: DebuggerRunConfigurationAspect(ProjectExplorer::Target *target); ~DebuggerRunConfigurationAspect(); - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; bool useCppDebugger() const; bool useQmlDebugger() const; diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 1902bf86810..d797586d793 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -11,7 +11,7 @@ #include "debuggeractions.h" #include "debuggerengine.h" #include "debuggerinternalconstants.h" -#include "debuggerkitinformation.h" +#include "debuggerkitaspect.h" #include "debuggerrunconfigurationaspect.h" #include "breakhandler.h" #include "enginemanager.h" @@ -49,7 +49,7 @@ #include <qmldebug/qmldebugcommandlinearguments.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QTcpServer> #include <QTimer> @@ -70,7 +70,7 @@ DebuggerEngine *createPdbEngine(); DebuggerEngine *createQmlEngine(); DebuggerEngine *createLldbEngine(); DebuggerEngine *createUvscEngine(); -DebuggerEngine *createDapEngine(); +DebuggerEngine *createDapEngine(Utils::Id runMode = ProjectExplorer::Constants::NO_RUN_MODE); static QString noEngineMessage() { @@ -293,7 +293,7 @@ void DebuggerRunTool::setUseTerminal(bool on) bool useCdbConsole = m_runParameters.cppEngineType == CdbEngineType && (m_runParameters.startMode == StartInternal || m_runParameters.startMode == StartExternal) - && debuggerSettings()->useCdbConsole.value(); + && settings().useCdbConsole(); if (on && !d->terminalRunner && !useCdbConsole) { d->terminalRunner = @@ -305,11 +305,6 @@ void DebuggerRunTool::setUseTerminal(bool on) } } -void DebuggerRunTool::setRunAsRoot(bool on) -{ - m_runParameters.runAsRoot = on; -} - void DebuggerRunTool::setCommandsAfterConnect(const QString &commands) { m_runParameters.commandsAfterConnect = commands; @@ -359,7 +354,7 @@ void DebuggerRunTool::setAbi(const Abi &abi) m_runParameters.toolChainAbi = abi; } -void DebuggerRunTool::setInferior(const Runnable &runnable) +void DebuggerRunTool::setInferior(const ProcessRunData &runnable) { m_runParameters.inferior = runnable; } @@ -422,7 +417,6 @@ void DebuggerRunTool::addSearchDirectory(const Utils::FilePath &dir) void DebuggerRunTool::start() { - TaskHub::clearTasks(Constants::TASK_CATEGORY_DEBUGGER_DEBUGINFO); TaskHub::clearTasks(Constants::TASK_CATEGORY_DEBUGGER_RUNTIME); if (d->portsGatherer) { @@ -487,6 +481,8 @@ void DebuggerRunTool::start() runControl()->setDisplayName(m_runParameters.displayName); + m_engine = createDapEngine(runControl()->runMode()); + if (!m_engine) { if (m_runParameters.isCppDebugging()) { switch (m_runParameters.cppEngineType) { @@ -510,9 +506,6 @@ void DebuggerRunTool::start() case UvscEngineType: m_engine = createUvscEngine(); break; - case DapEngineType: - m_engine = createDapEngine(); - break; default: if (!m_runParameters.isQmlDebugging) { reportFailure(noEngineMessage() + '\n' + @@ -545,10 +538,12 @@ void DebuggerRunTool::start() m_engine->setRunId(d->runId); m_engine->setRunTool(this); m_engine->setCompanionEngine(m_engine2); - connect(m_engine, &DebuggerEngine::requestRunControlFinish, - runControl(), &RunControl::initiateFinish); - connect(m_engine, &DebuggerEngine::requestRunControlStop, - runControl(), &RunControl::initiateStop); + auto rc = runControl(); + connect(m_engine, &DebuggerEngine::requestRunControlFinish, rc, [rc] { + rc->setAutoDeleteOnStop(true); + rc->initiateStop(); + }, Qt::QueuedConnection); + connect(m_engine, &DebuggerEngine::requestRunControlStop, rc, &RunControl::initiateStop); connect(m_engine, &DebuggerEngine::engineStarted, this, [this] { handleEngineStarted(m_engine); }); connect(m_engine, &DebuggerEngine::engineFinished, @@ -575,10 +570,11 @@ void DebuggerRunTool::start() m_engine2->setRunTool(this); m_engine2->setCompanionEngine(m_engine); m_engine2->setSecondaryEngine(); - connect(m_engine2, &DebuggerEngine::requestRunControlFinish, - runControl(), &RunControl::initiateFinish); - connect(m_engine2, &DebuggerEngine::requestRunControlStop, - runControl(), &RunControl::initiateStop); + connect(m_engine2, &DebuggerEngine::requestRunControlFinish, rc, [rc] { + rc->setAutoDeleteOnStop(true); + rc->initiateStop(); + }, Qt::QueuedConnection); + connect(m_engine2, &DebuggerEngine::requestRunControlStop, rc, &RunControl::initiateStop); connect(m_engine2, &DebuggerEngine::engineStarted, this, [this] { handleEngineStarted(m_engine2); }); connect(m_engine2, &DebuggerEngine::engineFinished, @@ -690,11 +686,6 @@ bool DebuggerRunTool::isQmlDebugging() const return m_runParameters.isQmlDebugging; } -int DebuggerRunTool::portsUsedByDebugger() const -{ - return isCppDebugging() + isQmlDebugging(); -} - void DebuggerRunTool::setUsePortsGatherer(bool useCpp, bool useQml) { QTC_ASSERT(!d->portsGatherer, reportFailure(); return); @@ -749,7 +740,7 @@ bool DebuggerRunTool::fixupParameters() } } - if (debuggerSettings()->autoEnrichParameters.value()) { + if (settings().autoEnrichParameters()) { const FilePath sysroot = rp.sysRoot; if (rp.debugInfoLocation.isEmpty()) rp.debugInfoLocation = sysroot / "/usr/lib/debug"; @@ -806,7 +797,7 @@ bool DebuggerRunTool::fixupParameters() if (rp.isNativeMixedDebugging()) rp.inferior.environment.set("QV4_FORCE_INTERPRETER", "1"); - if (debuggerSettings()->forceLoggingToConsole.value()) + if (settings().forceLoggingToConsole()) rp.inferior.environment.set("QT_LOGGING_TO_CONSOLE", "1"); return true; @@ -862,6 +853,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm m_runParameters.macroExpander = runControl->macroExpander(); m_runParameters.debugger = DebuggerKitAspect::runnable(kit); m_runParameters.cppEngineType = DebuggerKitAspect::engineType(kit); + m_runParameters.version = DebuggerKitAspect::version(kit); if (QtSupport::QtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit)) { m_runParameters.qtPackageSourceLocation = qtVersion->qtPackageSourcePath().toString(); @@ -888,10 +880,7 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm } } - Runnable inferior = runControl->runnable(); - const FilePath &debuggerExecutable = m_runParameters.debugger.command.executable(); - inferior.command.setExecutable(debuggerExecutable.withNewMappedPath(inferior.command.executable())); - inferior.workingDirectory = debuggerExecutable.withNewMappedPath(inferior.workingDirectory); + ProcessRunData inferior = runControl->runnable(); // Normalize to work around QTBUG-17529 (QtDeclarative fails with 'File name case mismatch'...) inferior.workingDirectory = inferior.workingDirectory.normalizedPathName(); m_runParameters.inferior = inferior; @@ -927,7 +916,8 @@ DebuggerRunTool::DebuggerRunTool(RunControl *runControl, AllowTerminal allowTerm m_runParameters.interpreter = interpreter; if (auto args = runControl->aspect<ArgumentsAspect>()) m_runParameters.inferior.command.addArgs(args->arguments, CommandLine::Raw); - m_engine = createPdbEngine(); + if (runControl->runMode() == ProjectExplorer::Constants::DEBUG_RUN_MODE) + m_engine = createPdbEngine(); } } } @@ -1034,13 +1024,12 @@ DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGat CommandLine cmd; - QStringList args = ProcessArgs::splitArgs(commandLine().arguments(), OsTypeLinux); - if (isQmlDebugging) { - args.prepend(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices, - portsGatherer->qmlServer())); - } if (isQmlDebugging && !isCppDebugging) { - cmd.setExecutable(commandLine().executable()); // FIXME: Case should not happen? + // FIXME: Case should not happen? + cmd.setExecutable(commandLine().executable()); + cmd.addArg(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices, + portsGatherer->qmlServer())); + cmd.addArgs(commandLine().arguments(), CommandLine::Raw); } else { cmd.setExecutable(runControl->device()->debugServerPath()); @@ -1061,31 +1050,52 @@ DebugServerRunner::DebugServerRunner(RunControl *runControl, DebugServerPortsGat cmd.setExecutable(lldbserver); } } else { - cmd.setExecutable(runControl->device()->filePath("gdbserver")); + const FilePath gdbServerPath + = runControl->device()->filePath("gdbserver").searchInPath(); + FilePath lldbServerPath + = runControl->device()->filePath("lldb-server").searchInPath(); + + // TODO: Which one should we prefer? + if (gdbServerPath.isExecutableFile()) + cmd.setExecutable(gdbServerPath); + else if (lldbServerPath.isExecutableFile()) { + // lldb-server will fail if we start it through a link. + // see: https://github.com/llvm/llvm-project/issues/61955 + // + // So we first search for the real executable. + + // This is safe because we already checked that the file is executable. + while (lldbServerPath.isSymLink()) + lldbServerPath = lldbServerPath.symLinkTarget(); + + cmd.setExecutable(lldbServerPath); + } } } - args.clear(); if (cmd.executable().baseName().contains("lldb-server")) { - args.append("platform"); - args.append("--listen"); - args.append(QString("*:%1").arg(portsGatherer->gdbServer().port())); - args.append("--server"); + cmd.addArg("platform"); + cmd.addArg("--listen"); + cmd.addArg(QString("*:%1").arg(portsGatherer->gdbServer().port())); + cmd.addArg("--server"); } else if (cmd.executable().baseName() == "debugserver") { - args.append(QString("*:%1").arg(portsGatherer->gdbServer().port())); - args.append("--attach"); - args.append(QString::number(m_pid.pid())); + const QString ipAndPort("`echo $SSH_CLIENT | cut -d ' ' -f 1`:%1"); + cmd.addArgs(ipAndPort.arg(portsGatherer->gdbServer().port()), CommandLine::Raw); + + if (m_pid.isValid()) + cmd.addArgs({"--attach", QString::number(m_pid.pid())}); + else + cmd.addCommandLineAsArgs(runControl->runnable().command); } else { // Something resembling gdbserver if (m_useMulti) - args.append("--multi"); + cmd.addArg("--multi"); if (m_pid.isValid()) - args.append("--attach"); - args.append(QString(":%1").arg(portsGatherer->gdbServer().port())); + cmd.addArg("--attach"); + cmd.addArg(QString(":%1").arg(portsGatherer->gdbServer().port())); if (m_pid.isValid()) - args.append(QString::number(m_pid.pid())); + cmd.addArg(QString::number(m_pid.pid())); } } - cmd.setArguments(ProcessArgs::joinArgs(args, OsTypeLinux)); setCommandLine(cmd); }); @@ -1109,6 +1119,9 @@ DebuggerRunWorkerFactory::DebuggerRunWorkerFactory() { setProduct<DebuggerRunTool>(); addSupportedRunMode(ProjectExplorer::Constants::DEBUG_RUN_MODE); + addSupportedRunMode(ProjectExplorer::Constants::DAP_CMAKE_DEBUG_RUN_MODE); + addSupportedRunMode(ProjectExplorer::Constants::DAP_GDB_DEBUG_RUN_MODE); + addSupportedRunMode(ProjectExplorer::Constants::DAP_PY_DEBUG_RUN_MODE); addSupportedDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); addSupportedDeviceType("DockerDeviceType"); } diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index 2bd18508c6a..fdba73c83e2 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -32,34 +32,21 @@ public: void startRunControl(); - void showMessage(const QString &msg, int channel = LogDebug, int timeout = -1); - void start() override; void stop() override; - bool isCppDebugging() const; - bool isQmlDebugging() const; - int portsUsedByDebugger() const; - - void setUsePortsGatherer(bool useCpp, bool useQml); - DebugServerPortsGatherer *portsGatherer() const; - void setSolibSearchPath(const Utils::FilePaths &list); - void addSolibSearchDir(const QString &str); static void setBreakOnMainNextTime(); - void setInferior(const ProjectExplorer::Runnable &runnable); + void setInferior(const Utils::ProcessRunData &runnable); void setInferiorExecutable(const Utils::FilePath &executable); void setInferiorEnvironment(const Utils::Environment &env); // Used by GammaRay plugin void setRunControlName(const QString &name); void setStartMessage(const QString &msg); - void addQmlServerInferiorCommandLineArgumentIfNeeded(); - void modifyDebuggerEnvironment(const Utils::EnvironmentItems &item); void setCrashParameter(const QString &event); void addExpectedSignal(const QString &signal); - void addSearchDirectory(const Utils::FilePath &dir); void setStartMode(DebuggerStartMode startMode); void setCloseMode(DebuggerCloseMode closeMode); @@ -69,26 +56,19 @@ public: void setSysRoot(const Utils::FilePath &sysRoot); void setSymbolFile(const Utils::FilePath &symbolFile); - void setLldbPlatform(const QString &platform); void setRemoteChannel(const QString &channel); void setRemoteChannel(const QString &host, int port); - void setRemoteChannel(const QUrl &url); QString remoteChannel() const; void setUseExtendedRemote(bool on); void setUseContinueInsteadOfRun(bool on); - void setUseTargetAsync(bool on); void setContinueAfterAttach(bool on); - void setSkipExecutableValidation(bool on); - void setUseCtrlCStub(bool on); void setBreakOnMain(bool on); void setUseTerminal(bool on); - void setRunAsRoot(bool on); void setCommandsAfterConnect(const QString &commands); void setCommandsForReset(const QString &commands); - void setServerStartScript(const Utils::FilePath &serverStartScript); void setDebugInfoLocation(const Utils::FilePath &debugInfoLocation); void setQmlServer(const QUrl &qmlServer); @@ -96,20 +76,40 @@ public: void setCoreFilePath(const Utils::FilePath &core, bool isSnapshot = false); - void setIosPlatform(const QString &platform); - void setDeviceSymbolsRoot(const QString &deviceSymbolsRoot); - void setTestCase(int testCase); void setOverrideStartScript(const Utils::FilePath &script); - void setAbi(const ProjectExplorer::Abi &abi); - Internal::TerminalRunner *terminalRunner() const; - DebuggerEngineType cppEngineType() const; Internal::DebuggerRunParameters &runParameters() { return m_runParameters; } +protected: + bool isCppDebugging() const; + bool isQmlDebugging() const; + + void setUsePortsGatherer(bool useCpp, bool useQml); + DebugServerPortsGatherer *portsGatherer() const; + + void addSolibSearchDir(const QString &str); + void addQmlServerInferiorCommandLineArgumentIfNeeded(); + void modifyDebuggerEnvironment(const Utils::EnvironmentItems &item); + void addSearchDirectory(const Utils::FilePath &dir); + + void setLldbPlatform(const QString &platform); + void setRemoteChannel(const QUrl &url); + void setUseTargetAsync(bool on); + void setSkipExecutableValidation(bool on); + void setUseCtrlCStub(bool on); + + void setIosPlatform(const QString &platform); + void setDeviceSymbolsRoot(const QString &deviceSymbolsRoot); + void setAbi(const ProjectExplorer::Abi &abi); + + DebuggerEngineType cppEngineType() const; + private: + void showMessage(const QString &msg, int channel = LogDebug, int timeout = -1); + bool fixupParameters(); void handleEngineStarted(Internal::DebuggerEngine *engine); void handleEngineFinished(Internal::DebuggerEngine *engine); diff --git a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp index 0c98ba174b0..1cb7c6402cd 100644 --- a/src/plugins/debugger/debuggersourcepathmappingwidget.cpp +++ b/src/plugins/debugger/debuggersourcepathmappingwidget.cpp @@ -3,7 +3,7 @@ #include "debuggersourcepathmappingwidget.h" -#include "debuggeractions.h" +#include "commonoptionspage.h" #include "debuggerengine.h" #include "debuggertr.h" @@ -22,7 +22,6 @@ #include <QLabel> #include <QLineEdit> #include <QPushButton> -#include <QSettings> #include <QStandardItemModel> #include <QTreeView> @@ -442,8 +441,8 @@ public: }; -SourcePathMapAspect::SourcePathMapAspect() - : d(new SourcePathMapAspectPrivate) +SourcePathMapAspect::SourcePathMapAspect(AspectContainer *container) + : TypedAspect(container), d(new SourcePathMapAspectPrivate) { } @@ -452,16 +451,22 @@ SourcePathMapAspect::~SourcePathMapAspect() delete d; } -void SourcePathMapAspect::fromMap(const QVariantMap &) +void SourcePathMapAspect::fromMap(const Store &) { QTC_CHECK(false); // This is only used via read/writeSettings } -void SourcePathMapAspect::toMap(QVariantMap &) const +void SourcePathMapAspect::toMap(Store &) const { QTC_CHECK(false); } +bool SourcePathMapAspect::isDirty() +{ + guiToBuffer(); + return m_internal != m_buffer; +} + void SourcePathMapAspect::addToLayout(Layouting::LayoutItem &parent) { QTC_CHECK(!d->m_widget); @@ -470,36 +475,32 @@ void SourcePathMapAspect::addToLayout(Layouting::LayoutItem &parent) parent.addItem(d->m_widget.data()); } -QVariant SourcePathMapAspect::volatileValue() const +bool SourcePathMapAspect::guiToBuffer() { - QTC_CHECK(!isAutoApply()); - QTC_ASSERT(d->m_widget, return {}); - return QVariant::fromValue(d->m_widget->sourcePathMap()); + const SourcePathMap old = m_buffer; + if (d->m_widget) + m_buffer = d->m_widget->sourcePathMap(); + return m_buffer != old; } -void SourcePathMapAspect::setVolatileValue(const QVariant &val) +void SourcePathMapAspect::bufferToGui() { - QTC_CHECK(!isAutoApply()); if (d->m_widget) - d->m_widget->setSourcePathMap(val.value<SourcePathMap>()); + d->m_widget->setSourcePathMap(m_buffer); } const char sourcePathMappingArrayNameC[] = "SourcePathMappings"; const char sourcePathMappingSourceKeyC[] = "Source"; const char sourcePathMappingTargetKeyC[] = "Target"; -SourcePathMap SourcePathMapAspect::value() const -{ - return BaseAspect::value().value<SourcePathMap>(); -} - -void SourcePathMapAspect::writeSettings(QSettings *s) const +void SourcePathMapAspect::writeSettings() const { const SourcePathMap sourcePathMap = value(); + QtcSettings *s = qtcSettings(); s->beginWriteArray(sourcePathMappingArrayNameC); if (!sourcePathMap.isEmpty()) { - const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); - const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); + const Key sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); + const Key sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); int i = 0; for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; @@ -512,14 +513,13 @@ void SourcePathMapAspect::writeSettings(QSettings *s) const s->endArray(); } -void SourcePathMapAspect::readSettings(const QSettings *settings) +void SourcePathMapAspect::readSettings() { - // Eeks. But legitimate, this operates on ICore::settings(); - QSettings *s = const_cast<QSettings *>(settings); + QtcSettings *s = qtcSettings(); SourcePathMap sourcePathMap; if (const int count = s->beginReadArray(sourcePathMappingArrayNameC)) { - const QString sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); - const QString sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); + const Key sourcePathMappingSourceKey(sourcePathMappingSourceKeyC); + const Key sourcePathMappingTargetKey(sourcePathMappingTargetKeyC); for (int i = 0; i < count; ++i) { s->setArrayIndex(i); const QString key = s->value(sourcePathMappingSourceKey).toString(); @@ -528,7 +528,7 @@ void SourcePathMapAspect::readSettings(const QSettings *settings) } } s->endArray(); - setValue(QVariant::fromValue(sourcePathMap)); + setValue(sourcePathMap); } } // Debugger::Internal diff --git a/src/plugins/debugger/debuggertooltipmanager.cpp b/src/plugins/debugger/debuggertooltipmanager.cpp index 90b5f4edb15..d51dcd9bcba 100644 --- a/src/plugins/debugger/debuggertooltipmanager.cpp +++ b/src/plugins/debugger/debuggertooltipmanager.cpp @@ -1151,7 +1151,7 @@ void DebuggerToolTipManagerPrivate::slotTooltipOverrideRequested QTC_ASSERT(handled, return); QTC_ASSERT(editorWidget, return); - if (!debuggerSettings()->useToolTipsInMainEditor.value()) + if (!settings().useToolTipsInMainEditor()) return; const TextDocument *document = editorWidget->textDocument(); diff --git a/src/plugins/debugger/disassembleragent.cpp b/src/plugins/debugger/disassembleragent.cpp index d4e3caa6f1b..afe08e2d1fe 100644 --- a/src/plugins/debugger/disassembleragent.cpp +++ b/src/plugins/debugger/disassembleragent.cpp @@ -20,15 +20,14 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> -#include <utils/aspects.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> #include <QTextBlock> -#include <QDir> using namespace Core; using namespace TextEditor; +using namespace Utils; namespace Debugger::Internal { @@ -43,9 +42,7 @@ class DisassemblerBreakpointMarker : public TextMark { public: DisassemblerBreakpointMarker(const Breakpoint &bp, int lineNumber) - : TextMark(Utils::FilePath(), - lineNumber, - {Tr::tr("Breakpoint"), Constants::TEXT_MARK_CATEGORY_BREAKPOINT}) + : TextMark({}, lineNumber, {Tr::tr("Breakpoint"), Constants::TEXT_MARK_CATEGORY_BREAKPOINT}) , m_bp(bp) { setIcon(bp->icon()); @@ -161,7 +158,7 @@ int DisassemblerAgentPrivate::lineForAddress(quint64 address) const DisassemblerAgent::DisassemblerAgent(DebuggerEngine *engine) : d(new DisassemblerAgentPrivate(engine)) { - connect(&debuggerSettings()->intelFlavor, &Utils::BaseAspect::changed, + connect(&settings().intelFlavor, &Utils::BaseAspect::changed, this, &DisassemblerAgent::reload); } diff --git a/src/plugins/debugger/enginemanager.cpp b/src/plugins/debugger/enginemanager.cpp index dd86517c23f..a0f515897c4 100644 --- a/src/plugins/debugger/enginemanager.cpp +++ b/src/plugins/debugger/enginemanager.cpp @@ -17,9 +17,11 @@ #include <utils/treemodel.h> #include <utils/qtcassert.h> +#include <QAbstractProxyModel> #include <QComboBox> #include <QDebug> #include <QMenu> +#include <QSortFilterProxyModel> #include <QTimer> using namespace Core; @@ -28,6 +30,7 @@ using namespace Utils; namespace Debugger::Internal { const bool hideSwitcherUnlessNeeded = false; +const char INDEX_ID[] = "Debugger/Debugger.SelectedEngineIndex"; #if 0 SnapshotData::SnapshotData() @@ -93,6 +96,125 @@ QDebug operator<<(QDebug d, const SnapshotData &f) } #endif +class EngineTypeFilterProxyModel : public QSortFilterProxyModel +{ +public: + explicit EngineTypeFilterProxyModel(const QString &type, QObject *parent = nullptr) + : QSortFilterProxyModel(parent) + , m_type(type) + { + } + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override + { + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + if (index.isValid()) { + QVariant data = sourceModel()->data(index, Qt::UserRole); + if (data.isValid() && data.toString() == m_type) { + return true; // Display only DapEngines + } + } + return false; + } +private: + QString m_type; +}; + +class ModelChooser : public QObject +{ + Q_OBJECT +public: + ModelChooser(QAbstractItemModel *sourceModel, + const QString &engineType, + QObject *parent = nullptr) + : QObject(parent) + , m_engineChooser(new QComboBox()) + , m_proxyModel(new EngineTypeFilterProxyModel(engineType)) + , m_sourceModel(sourceModel) + , m_enginType(engineType) + , m_key(engineType.isEmpty() ? Utils::Key(INDEX_ID) + "." + engineType.toUtf8() + : Utils::Key(INDEX_ID)) + { + m_proxyModel->setSourceModel(sourceModel); + + m_engineChooser->setModel(m_proxyModel); + m_engineChooser->setIconSize(QSize(0, 0)); + if (hideSwitcherUnlessNeeded) + m_engineChooser->hide(); + + connect(m_engineChooser, &QComboBox::activated, this, [this](int index) { + QModelIndex sourceIndex = m_proxyModel->mapToSource(m_proxyModel->index(index, 0)); + emit activated(sourceIndex.row()); + m_lastActivatedIndex = sourceIndex.row(); + ICore::settings()->setValue(m_key, m_lastActivatedIndex); + }); + + connect(m_proxyModel, &QAbstractItemModel::rowsRemoved, this, [this] { + setCurrentIndex(m_lastActivatedIndex); + }); + } + + ~ModelChooser() + { + delete m_engineChooser; + delete m_proxyModel; + } + + QComboBox *comboBox() const { return m_engineChooser; } + QAbstractItemModel *model() const { return m_proxyModel; } + const QString &engineType() const { return m_enginType; } + + void restoreIndex() + { + m_lastActivatedIndex = ICore::settings()->value(m_key, 0).toInt(); + if (m_lastActivatedIndex <= m_engineChooser->count()) + setCurrentIndex(m_lastActivatedIndex); + } + + void setCurrentIndex(int index) + { + const QModelIndex sourceIndex = m_proxyModel->mapFromSource(m_sourceModel->index(index, 0)); + if (sourceIndex.isValid()) + m_engineChooser->setCurrentIndex(sourceIndex.row()); + else + m_engineChooser->setCurrentIndex(0); + } + + void adjustUiForEngine(int row) + { + setCurrentIndex(row); + + const int contentWidth = m_engineChooser->fontMetrics().horizontalAdvance( + m_engineChooser->currentText() + "xx"); + QStyleOptionComboBox option; + option.initFrom(m_engineChooser); + const QSize sz(contentWidth, 1); + const int width = m_engineChooser->style() + ->sizeFromContents(QStyle::CT_ComboBox, &option, sz) + .width(); + m_engineChooser->setFixedWidth(width); + } + +signals: + void activated(int index); + +private: + QPointer<QComboBox> m_engineChooser; + QPointer<EngineTypeFilterProxyModel> m_proxyModel; + QAbstractItemModel *m_sourceModel; + QString m_enginType; + const Utils::Key m_key; + int m_lastActivatedIndex = -1; +}; + +struct PerspectiveItem +{ + QString name; + QString type; + QString id; +}; + class EngineItem : public QObject, public TreeItem { public: @@ -101,6 +223,8 @@ public: const bool m_isPreset = false; QPointer<DebuggerEngine> m_engine; + + PerspectiveItem m_perspective; }; class EngineManagerPrivate : public QObject @@ -110,25 +234,19 @@ public: { m_engineModel.setHeader({Tr::tr("Perspective"), Tr::tr("Debugged Application")}); - // The preset case: - auto preset = new EngineItem; - m_engineModel.rootItem()->appendChild(preset); - m_currentItem = preset; + m_engineChooser = new ModelChooser(&m_engineModel, "", this); + m_engineDAPChooser = new ModelChooser(&m_engineModel, "DAP", this); - m_engineChooser = new QComboBox; - m_engineChooser->setModel(&m_engineModel); - m_engineChooser->setIconSize(QSize(0, 0)); - if (hideSwitcherUnlessNeeded) - m_engineChooser->hide(); + connect(m_engineChooser, &ModelChooser::activated, this, [this](int index) { + activateEngineByIndex(index); + }); - connect(m_engineChooser, &QComboBox::activated, - this, &EngineManagerPrivate::activateEngineByIndex); + connect(m_engineDAPChooser, &ModelChooser::activated, this, [this](int index) { + activateEngineByIndex(index); + }); } - ~EngineManagerPrivate() - { - delete m_engineChooser; - } + ~EngineManagerPrivate() = default; EngineItem *findEngineItem(DebuggerEngine *engine); void activateEngineItem(EngineItem *engineItem); @@ -140,7 +258,11 @@ public: TreeModel<TypedTreeItem<EngineItem>, EngineItem> m_engineModel; QPointer<EngineItem> m_currentItem; // The primary information is DebuggerMainWindow::d->m_currentPerspective Utils::Id m_previousMode; - QPointer<QComboBox> m_engineChooser; + + QPointer<ModelChooser> m_engineChooser; + QPointer<ModelChooser> m_engineDAPChooser; + + QList<PerspectiveItem> m_perspectives; bool m_shuttingDown = false; // This contains the contexts that need to be removed when switching @@ -173,7 +295,12 @@ EngineManager::EngineManager() QWidget *EngineManager::engineChooser() { - return d->m_engineChooser; + return d->m_engineChooser->comboBox(); +} + +QWidget *EngineManager::dapEngineChooser() +{ + return d->m_engineDAPChooser->comboBox(); } void EngineManager::updatePerspectives() @@ -194,7 +321,12 @@ EngineManager *EngineManager::instance() QAbstractItemModel *EngineManager::model() { - return &d->m_engineModel; + return d->m_engineChooser->model(); +} + +QAbstractItemModel *EngineManager::dapModel() +{ + return d->m_engineDAPChooser->model(); } QVariant EngineItem::data(int column, int role) const @@ -232,7 +364,9 @@ QVariant EngineItem::data(int column, int role) const // Return icon that indicates whether this is the active engine if (column == 0) return d->m_currentItem == this ? Icons::LOCATION.icon() : Icons::EMPTY.icon(); - + break; + case Qt::UserRole: + return QVariant::fromValue(m_engine->debuggerType()); default: break; } @@ -240,8 +374,10 @@ QVariant EngineItem::data(int column, int role) const switch (role) { case Qt::DisplayRole: if (column == 0) - return Tr::tr("Debugger Preset"); + return m_perspective.name; return QString("-"); + case Qt::UserRole: + return m_perspective.type; default: break; } @@ -302,10 +438,11 @@ void EngineManagerPrivate::activateEngineByIndex(int index) { // The actual activation is triggered indirectly via the perspective change. Perspective *perspective = nullptr; - if (index == 0) { - perspective = Perspective::findPerspective(Debugger::Constants::PRESET_PERSPECTIVE_ID); + EngineItem *engineItem = m_engineModel.rootItem()->childAt(index); + + if (engineItem && !engineItem->m_engine) { + perspective = Perspective::findPerspective(engineItem->m_perspective.id); } else { - EngineItem *engineItem = m_engineModel.rootItem()->childAt(index); QTC_ASSERT(engineItem, return); QTC_ASSERT(engineItem->m_engine, return); perspective = engineItem->m_engine->perspective(); @@ -338,7 +475,14 @@ void EngineManagerPrivate::activateEngineItem(EngineItem *engineItem) // In case this was triggered externally by some Perspective::select() call. const int idx = engineItem->indexInParent(); - m_engineChooser->setCurrentIndex(idx); + + if ((engineItem->m_engine + && engineItem->m_engine->debuggerType() == m_engineDAPChooser->engineType()) + || (engineItem->m_engine + && engineItem->m_perspective.type == m_engineDAPChooser->engineType())) + m_engineDAPChooser->setCurrentIndex(idx); + else + m_engineChooser->setCurrentIndex(idx); selectUiForCurrentEngine(); } @@ -352,15 +496,13 @@ void EngineManagerPrivate::selectUiForCurrentEngine() if (m_currentItem) row = m_engineModel.rootItem()->indexOf(m_currentItem); - m_engineChooser->setCurrentIndex(row); - const int contentWidth = - m_engineChooser->fontMetrics().horizontalAdvance(m_engineChooser->currentText() + "xx"); - QStyleOptionComboBox option; - option.initFrom(m_engineChooser); - const QSize sz(contentWidth, 1); - const int width = m_engineChooser->style()->sizeFromContents( - QStyle::CT_ComboBox, &option, sz).width(); - m_engineChooser->setFixedWidth(width); + if ((m_currentItem->m_engine + && m_currentItem->m_engine->debuggerType() == m_engineDAPChooser->engineType()) + || (m_currentItem->m_engine + && m_currentItem->m_perspective.type == m_engineDAPChooser->engineType())) + m_engineDAPChooser->adjustUiForEngine(row); + else + m_engineChooser->adjustUiForEngine(row); m_engineModel.rootItem()->forFirstLevelChildren([this](EngineItem *engineItem) { if (engineItem && engineItem->m_engine) @@ -382,7 +524,8 @@ void EngineManagerPrivate::updateEngineChooserVisibility() // Show it if there's more than one option (i.e. not the preset engine only) if (hideSwitcherUnlessNeeded) { const int count = m_engineModel.rootItem()->childCount(); - m_engineChooser->setVisible(count >= 2); + m_engineChooser->comboBox()->setVisible(count >= 2); + m_engineDAPChooser->comboBox()->setVisible(count >= 2); } } @@ -430,6 +573,20 @@ void EngineManager::unregisterEngine(DebuggerEngine *engine) d->updateEngineChooserVisibility(); } +QString EngineManager::registerDefaultPerspective(const QString &name, + const QString &type, + const QString &id) +{ + auto engineItem = new EngineItem; + engineItem->m_perspective.name = name; + engineItem->m_perspective.type = type; + engineItem->m_perspective.id = id; + d->m_engineModel.rootItem()->appendChild(engineItem); + d->m_engineDAPChooser->restoreIndex(); + d->m_engineChooser->restoreIndex(); + return QString::number(d->m_engineModel.rootItem()->childCount()); +} + void EngineManager::activateDebugMode() { if (ModeManager::currentModeId() != Constants::MODE_DEBUG) { @@ -438,6 +595,11 @@ void EngineManager::activateDebugMode() } } +void EngineManager::activateByIndex(int index) +{ + d->activateEngineByIndex(index); +} + void EngineManager::deactivateDebugMode() { if (ModeManager::currentModeId() == Constants::MODE_DEBUG && d->m_previousMode.isValid()) { @@ -479,3 +641,5 @@ bool EngineManager::shutDown() } } // Debugger::Internal + +#include "enginemanager.moc" diff --git a/src/plugins/debugger/enginemanager.h b/src/plugins/debugger/enginemanager.h index a8222835ad4..f9e757e033d 100644 --- a/src/plugins/debugger/enginemanager.h +++ b/src/plugins/debugger/enginemanager.h @@ -10,6 +10,7 @@ namespace Debugger::Internal { class DebuggerEngine; +struct PerspectiveItem; class EngineManager final : public QObject { @@ -21,17 +22,25 @@ public: static EngineManager *instance(); static QAbstractItemModel *model(); + static QAbstractItemModel *dapModel(); static QString registerEngine(DebuggerEngine *engine); static void unregisterEngine(DebuggerEngine *engine); + static QString registerDefaultPerspective(const QString &name, + const QString &type, + const QString &id); + static void activateDebugMode(); static void deactivateDebugMode(); + static void activateByIndex(int index); static QList<QPointer<DebuggerEngine> > engines(); static QPointer<DebuggerEngine> currentEngine(); static QWidget *engineChooser(); + static QWidget *dapEngineChooser(); + static void updatePerspectives(); static bool shutDown(); // Return true if some engine is being forced to shut down. diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 8b36ef539e6..09755f68e6d 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -34,7 +34,6 @@ #include <projectexplorer/projectexplorer.h> #include <projectexplorer/taskhub.h> -#include <app/app_version.h> #include <utils/algorithm.h> #include <utils/environment.h> #include <utils/hostosinfo.h> @@ -45,11 +44,12 @@ #include <utils/temporaryfile.h> #include <QDirIterator> +#include <QGuiApplication> +#include <QJsonArray> #include <QMessageBox> #include <QProcess> #include <QPushButton> #include <QRegularExpression> -#include <QJsonArray> using namespace Core; using namespace ProjectExplorer; @@ -136,7 +136,7 @@ GdbEngine::GdbEngine() connect(&m_commandTimer, &QTimer::timeout, this, &GdbEngine::commandTimeout); - DebuggerSettings &s = *debuggerSettings(); + DebuggerSettings &s = settings(); connect(&s.autoDerefPointers, &BaseAspect::changed, this, &GdbEngine::reloadLocals); connect(s.createFullBacktrace.action(), &QAction::triggered, @@ -417,7 +417,7 @@ void GdbEngine::handleResponse(const QString &buff) } } - if (debuggerSettings()->logTimeStamps.value()) + if (settings().logTimeStamps()) showMessage(QString("Output handled")); } @@ -801,7 +801,7 @@ void GdbEngine::runCommand(const DebuggerCommand &command) int GdbEngine::commandTimeoutTime() const { - const int time = debuggerSettings()->gdbWatchdogTimeout(); + const int time = settings().gdbWatchdogTimeout(); return 1000 * qMax(20, time); } @@ -942,7 +942,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) DebuggerCommand cmd = m_commandForToken.take(token); const int flags = m_flagsForToken.take(token); - if (debuggerSettings()->logTimeStamps.value()) { + if (settings().logTimeStamps()) { showMessage(QString("Response time: %1: %2 s") .arg(cmd.function) .arg(QTime::fromMSecsSinceStartOfDay(cmd.postTime).msecsTo(QTime::currentTime()) / 1000.), @@ -1016,7 +1016,7 @@ void GdbEngine::updateAll() { //PENDING_DEBUG("UPDATING ALL\n"); QTC_CHECK(state() == InferiorUnrunnable || state() == InferiorStopOk); - DebuggerCommand cmd(stackCommand(debuggerSettings()->maximalStackDepth())); + DebuggerCommand cmd(stackCommand(settings().maximalStackDepth())); cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); }; runCommand(cmd); stackHandler()->setCurrentIndex(0); @@ -1124,7 +1124,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) // Jump over well-known frames. //static int stepCounter = 0; - if (debuggerSettings()->skipKnownFrames.value()) { + if (settings().skipKnownFrames()) { if (reason == "end-stepping-range" || reason == "function-finished") { //showMessage(frame.toString()); QString funcName = frame["function"].data(); @@ -1310,7 +1310,7 @@ void GdbEngine::handleStop1(const GdbMi &data) if (!m_systemDumpersLoaded) { m_systemDumpersLoaded = true; - if (m_gdbVersion >= 70400 && debuggerSettings()->loadGdbDumpers.value()) + if (m_gdbVersion >= 70400 && settings().loadGdbDumpers()) runCommand({"importPlainDumpers on"}); else runCommand({"importPlainDumpers off"}); @@ -1427,7 +1427,7 @@ void GdbEngine::handleStop2(const GdbMi &data) m_expectTerminalTrap = false; } else { showMessage("HANDLING SIGNAL " + name); - if (debuggerSettings()->useMessageBoxForSignals.value() && !isStopperThread) + if (settings().useMessageBoxForSignals() && !isStopperThread) if (!showStoppedBySignalMessageBox(meaning, name)) { showMessage("SIGNAL RECEIVED WHILE SHOWING SIGNAL MESSAGE"); return; @@ -1501,11 +1501,12 @@ void GdbEngine::handlePythonSetup(const DebuggerResponse &response) int pythonMajor = m_pythonVersion / 10000; int pythonMinor = (m_pythonVersion / 100) % 100; QString out = "<p>" - + Tr::tr("The selected build of GDB supports Python scripting, " - "but the used version %1.%2 is not sufficient for " - "%3. Supported versions are Python 2.7 and 3.x.") - .arg(pythonMajor).arg(pythonMinor) - .arg(Core::Constants::IDE_DISPLAY_NAME); + + Tr::tr("The selected build of GDB supports Python scripting, " + "but the used version %1.%2 is not sufficient for " + "%3. Supported versions are Python 2.7 and 3.x.") + .arg(pythonMajor) + .arg(pythonMinor) + .arg(QGuiApplication::applicationDisplayName()); showStatusMessage(out); AsynchronousMessageBox::critical(Tr::tr("Execution Error"), out); } @@ -1518,7 +1519,7 @@ void GdbEngine::handlePythonSetup(const DebuggerResponse &response) if (msg.contains("Python scripting is not supported in this copy of GDB.")) { QString out1 = "The selected build of GDB does not support Python scripting."; QString out2 = QStringLiteral("It cannot be used in %1.") - .arg(Core::Constants::IDE_DISPLAY_NAME); + .arg(QGuiApplication::applicationDisplayName()); showStatusMessage(out1 + ' ' + out2); AsynchronousMessageBox::critical(Tr::tr("Execution Error"), out1 + "<br>" + out2); } @@ -1587,7 +1588,7 @@ FilePath GdbEngine::cleanupFullName(const QString &fileName) return {}; } - if (!debuggerSettings()->autoEnrichParameters.value()) + if (!settings().autoEnrichParameters()) return cleanFilePath; if (cleanFilePath.isReadableFile()) @@ -2043,7 +2044,7 @@ void GdbEngine::setTokenBarrier() QTC_ASSERT(good, return); PENDING_DEBUG("\n--- token barrier ---\n"); showMessage("--- token barrier ---", LogMiscInput); - if (debuggerSettings()->logTimeStamps.value()) + if (settings().logTimeStamps()) showMessage(LogWindow::logTimeStamp(), LogMiscInput); m_oldestAcceptableToken = currentToken(); m_stackNeeded = false; @@ -2165,7 +2166,7 @@ void GdbEngine::handleCatchInsert(const DebuggerResponse &response, const Breakp void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp) { QTC_ASSERT(bp, return); - const bool usePseudoTracepoints = debuggerSettings()->usePseudoTracepoints.value(); + const bool usePseudoTracepoints = settings().usePseudoTracepoints(); const QString nr = bkpt["number"].data(); if (nr.contains('.')) { // A sub-breakpoint. @@ -2580,7 +2581,7 @@ void GdbEngine::insertBreakpoint(const Breakpoint &bp) int spec = requested.threadSpec; if (requested.isTracepoint()) { - if (debuggerSettings()->usePseudoTracepoints.value()) { + if (settings().usePseudoTracepoints()) { cmd.function = "createTracepoint"; if (requested.oneShot) @@ -2616,7 +2617,7 @@ void GdbEngine::insertBreakpoint(const Breakpoint &bp) // for dumping of expressions const bool alwaysVerbose = qtcEnvironmentVariableIsSet( "QTC_DEBUGGER_PYTHON_VERBOSE"); - const DebuggerSettings &s = *debuggerSettings(); + const DebuggerSettings &s = settings(); cmd.arg("passexceptions", alwaysVerbose); cmd.arg("fancy", s.useDebuggingHelpers()); cmd.arg("autoderef", s.autoDerefPointers()); @@ -3117,7 +3118,7 @@ DebuggerCommand GdbEngine::stackCommand(int depth) void GdbEngine::reloadStack() { PENDING_DEBUG("RELOAD STACK"); - DebuggerCommand cmd = stackCommand(debuggerSettings()->maximalStackDepth.value()); + DebuggerCommand cmd = stackCommand(settings().maximalStackDepth()); cmd.callback = [this](const DebuggerResponse &r) { handleStackListFrames(r, false); }; cmd.flags = Discardable; runCommand(cmd); @@ -3181,8 +3182,8 @@ void GdbEngine::handleThreadInfo(const DebuggerResponse &response) ThreadsHandler *handler = threadsHandler(); handler->setThreads(response.data); updateState(); // Adjust Threads combobox. - if (debuggerSettings()->showThreadNames.value()) { - runCommand({QString("threadnames %1").arg(debuggerSettings()->maximalStackDepth.value()), + if (settings().showThreadNames()) { + runCommand({QString("threadnames %1").arg(settings().maximalStackDepth()), Discardable, CB(handleThreadNames)}); } reloadStack(); // Will trigger register reload. @@ -3650,7 +3651,7 @@ public: void GdbEngine::fetchDisassembler(DisassemblerAgent *agent) { - if (debuggerSettings()->intelFlavor.value()) + if (settings().intelFlavor()) runCommand({"set disassembly-flavor intel"}); else runCommand({"set disassembly-flavor att"}); @@ -3839,7 +3840,7 @@ void GdbEngine::setupEngine() } gdbCommand.addArgs({"-i", "mi"}); - if (!debuggerSettings()->loadGdbInit.value()) + if (!settings().loadGdbInit()) gdbCommand.addArg("-n"); // This is filled in DebuggerKitAspect::runnable @@ -3885,7 +3886,7 @@ void GdbEngine::handleGdbStarted() runCommand({"set breakpoint pending on"}); runCommand({"set print elements 10000"}); - if (debuggerSettings()->useIndexCache.value()) + if (settings().useIndexCache()) runCommand({"set index-cache on"}); // Produces a few messages during symtab loading @@ -3930,8 +3931,7 @@ void GdbEngine::handleGdbStarted() // Apply source path mappings from global options. //showMessage(_("Assuming Qt is installed at %1").arg(qtInstallPath)); - const SourcePathMap sourcePathMap = - mergePlatformQtPath(rp, debuggerSettings()->sourcePathMap.value()); + const SourcePathMap sourcePathMap = mergePlatformQtPath(rp, settings().sourcePathMap()); const SourcePathMap completeSourcePathMap = mergeStartParametersSourcePathMap(rp, sourcePathMap); for (auto it = completeSourcePathMap.constBegin(), cend = completeSourcePathMap.constEnd(); @@ -3959,7 +3959,7 @@ void GdbEngine::handleGdbStarted() //if (!ba.isEmpty()) // runCommand("set solib-search-path " + ba); - if (debuggerSettings()->multiInferior.value() || runParameters().multiProcess) { + if (settings().multiInferior() || runParameters().multiProcess) { //runCommand("set follow-exec-mode new"); runCommand({"set detach-on-fork off"}); } @@ -4029,14 +4029,14 @@ void GdbEngine::handleGdbStarted() runCommand({"python from gdbbridge import *"}); } - const FilePath path = debuggerSettings()->extraDumperFile(); + const FilePath path = settings().extraDumperFile(); if (!path.isEmpty() && path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); } - const QString commands = expand(debuggerSettings()->extraDumperCommands.value()); + const QString commands = expand(settings().extraDumperCommands()); if (!commands.isEmpty()) runCommand({commands}); @@ -4253,7 +4253,7 @@ bool GdbEngine::usesExecInterrupt() const bool GdbEngine::usesTargetAsync() const { - return runParameters().useTargetAsync || debuggerSettings()->targetAsync.value(); + return runParameters().useTargetAsync || settings().targetAsync(); } void GdbEngine::scheduleTestResponse(int testCase, const QString &response) @@ -4372,10 +4372,10 @@ void GdbEngine::claimInitialBreakpoints() showMessage(Tr::tr("Setting breakpoints...")); BreakpointManager::claimBreakpointsForEngine(this); - const DebuggerSettings &s = *debuggerSettings(); - const bool onAbort = s.breakOnAbort.value(); - const bool onWarning = s.breakOnWarning.value(); - const bool onFatal = s.breakOnFatal.value(); + const DebuggerSettings &s = settings(); + const bool onAbort = s.breakOnAbort(); + const bool onWarning = s.breakOnWarning(); + const bool onFatal = s.breakOnFatal(); if (onAbort || onWarning || onFatal) { DebuggerCommand cmd("createSpecialBreakpoints"); cmd.arg("breakonabort", onAbort); @@ -4613,7 +4613,7 @@ void GdbEngine::handleLocalAttach(const DebuggerResponse &response) { showMessage("INFERIOR ATTACHED"); - QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value()); + QString commands = expand(settings().gdbPostAttachCommands()); if (!commands.isEmpty()) runCommand({commands, NativeCommand}); @@ -4793,7 +4793,7 @@ void GdbEngine::handleExecRun(const DebuggerResponse &response) if (response.resultClass == ResultRunning) { if (isLocalRunEngine()) { - QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value()); + QString commands = expand(settings().gdbPostAttachCommands()); if (!commands.isEmpty()) runCommand({commands, NativeCommand}); } @@ -4847,7 +4847,7 @@ void GdbEngine::handleTargetRemote(const DebuggerResponse &response) // gdb server will stop the remote application itself. showMessage("INFERIOR STARTED"); showMessage(msgAttachedToStoppedInferior(), StatusBar); - QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value()); + QString commands = expand(settings().gdbPostAttachCommands()); if (!commands.isEmpty()) runCommand({commands, NativeCommand}); handleInferiorPrepared(); @@ -4863,7 +4863,7 @@ void GdbEngine::handleTargetExtendedRemote(const DebuggerResponse &response) if (response.resultClass == ResultDone) { showMessage("ATTACHED TO GDB SERVER STARTED"); showMessage(msgAttachedToStoppedInferior(), StatusBar); - QString commands = expand(debuggerSettings()->gdbPostAttachCommands.value()); + QString commands = expand(settings().gdbPostAttachCommands()); if (!commands.isEmpty()) runCommand({commands, NativeCommand}); if (runParameters().attachPID.isValid()) { // attach to pid if valid @@ -4993,8 +4993,7 @@ void GdbEngine::handleStubAttached(const DebuggerResponse &response, qint64 main break; case ResultError: if (response.data["msg"].data() == "ptrace: Operation not permitted.") { - showMessage(msgPtraceError(runParameters().startMode)); - notifyEngineRunFailed(); + notifyInferiorSetupFailedHelper(msgPtraceError(runParameters().startMode)); break; } showMessage(response.data["msg"].data()); @@ -5035,7 +5034,7 @@ static FilePath findExecutableFromName(const QString &fileNameFromCore, const Fi return {}; } -CoreInfo CoreInfo::readExecutableNameFromCore(const Runnable &debugger, const FilePath &coreFile) +CoreInfo CoreInfo::readExecutableNameFromCore(const ProcessRunData &debugger, const FilePath &coreFile) { CoreInfo cinfo; #if 0 @@ -5113,20 +5112,20 @@ void GdbEngine::doUpdateLocals(const UpdateParameters ¶ms) watchHandler()->appendWatchersAndTooltipRequests(&cmd); const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE"); - const DebuggerSettings &s = *debuggerSettings(); + const DebuggerSettings &s = settings(); cmd.arg("passexceptions", alwaysVerbose); - cmd.arg("fancy", s.useDebuggingHelpers.value()); - cmd.arg("autoderef", s.autoDerefPointers.value()); - cmd.arg("dyntype", s.useDynamicType.value()); - cmd.arg("qobjectnames", s.showQObjectNames.value()); - cmd.arg("timestamps", s.logTimeStamps.value()); + cmd.arg("fancy", s.useDebuggingHelpers()); + cmd.arg("autoderef", s.autoDerefPointers()); + cmd.arg("dyntype", s.useDynamicType()); + cmd.arg("qobjectnames", s.showQObjectNames()); + cmd.arg("timestamps", s.logTimeStamps()); StackFrame frame = stackHandler()->currentFrame(); cmd.arg("context", frame.context); cmd.arg("nativemixed", isNativeMixedActive()); - cmd.arg("stringcutoff", s.maximalStringLength.value()); - cmd.arg("displaystringlimit", s.displayStringLimit.value()); + cmd.arg("stringcutoff", s.maximalStringLength()); + cmd.arg("displaystringlimit", s.displayStringLimit()); cmd.arg("resultvarname", m_resultVarName); cmd.arg("partialvar", params.partialVariable); diff --git a/src/plugins/debugger/gdb/gdbengine.h b/src/plugins/debugger/gdb/gdbengine.h index 64a2eddd4eb..a60cdc59162 100644 --- a/src/plugins/debugger/gdb/gdbengine.h +++ b/src/plugins/debugger/gdb/gdbengine.h @@ -37,7 +37,7 @@ struct CoreInfo Utils::FilePath foundExecutableName; // empty if no corresponding exec could be found bool isCore = false; - static CoreInfo readExecutableNameFromCore(const ProjectExplorer::Runnable &debugger, + static CoreInfo readExecutableNameFromCore(const Utils::ProcessRunData &debugger, const Utils::FilePath &coreFile); }; diff --git a/src/plugins/debugger/gdb/gdboptionspage.cpp b/src/plugins/debugger/gdb/gdboptionspage.cpp deleted file mode 100644 index 37008cabcd1..00000000000 --- a/src/plugins/debugger/gdb/gdboptionspage.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <debugger/debuggeractions.h> -#include <debugger/debuggerconstants.h> -#include <debugger/debuggercore.h> -#include <debugger/debuggerinternalconstants.h> -#include <debugger/debuggertr.h> - -#include <coreplugin/dialogs/ioptionspage.h> - -#include <utils/layoutbuilder.h> - -using namespace Core; -using namespace Utils; - -namespace Debugger::Internal { - -///////////////////////////////////////////////////////////////////////// -// -// GdbOptionsPage - harmless options -// -///////////////////////////////////////////////////////////////////////// - -class GdbOptionsPage : public Core::IOptionsPage -{ -public: - GdbOptionsPage() - { - setId("M.Gdb"); - setDisplayName(Tr::tr("GDB")); - setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY); - setSettings(&debuggerSettings()->page2); - - setLayouter([] { - using namespace Layouting; - DebuggerSettings &s = *debuggerSettings(); - - auto labelDangerous = new QLabel("<html><head/><body><i>" + - Tr::tr("The options below give access to advanced<br>" - "or experimental functions of GDB.<p>" - "Enabling them may negatively impact<br>" - "your debugging experience.") + "</i></body></html>"); - - Group general { - title(Tr::tr("General")), - Column { - Row { s.gdbWatchdogTimeout, st }, - s.skipKnownFrames, - s.useMessageBoxForSignals, - s.adjustBreakpointLocations, - s.useDynamicType, - s.loadGdbInit, - s.loadGdbDumpers, - s.intelFlavor, - s.usePseudoTracepoints, - s.useIndexCache, - st - } - }; - - Group extended { - title(Tr::tr("Extended")), - Column { - labelDangerous, - s.targetAsync, - s.autoEnrichParameters, - s.breakOnWarning, - s.breakOnFatal, - s.breakOnAbort, - s.enableReverseDebugging, - s.multiInferior, - st - } - }; - - Group startup { - title(Tr::tr("Additional Startup Commands")), - Column { s.gdbStartupCommands } - }; - - Group attach { - title(Tr::tr("Additional Attach Commands")), - Column { s.gdbPostAttachCommands }, - }; - - return Grid { general, extended, br, startup, attach }; - }); - } -}; - -// Registration - -void addGdbOptionPages(QList<IOptionsPage *> *opts) -{ - opts->push_back(new GdbOptionsPage); -} - -} // Debugger::Internal diff --git a/src/plugins/debugger/gdb/gdbsettings.cpp b/src/plugins/debugger/gdb/gdbsettings.cpp new file mode 100644 index 00000000000..eef19df9719 --- /dev/null +++ b/src/plugins/debugger/gdb/gdbsettings.cpp @@ -0,0 +1,275 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "gdbsettings.h" + +#include <coreplugin/dialogs/ioptionspage.h> + +#include <debugger/commonoptionspage.h> +#include <debugger/debuggeractions.h> +#include <debugger/debuggerconstants.h> +#include <debugger/debuggercore.h> +#include <debugger/debuggericons.h> +#include <debugger/debuggerinternalconstants.h> +#include <debugger/debuggertr.h> + +#include <utils/layoutbuilder.h> + +using namespace Core; +using namespace Utils; + +namespace Debugger::Internal { + +GdbSettings &gdbSettings() +{ + static GdbSettings settings; + return settings; +} + +GdbSettings::GdbSettings() +{ + setAutoApply(false); + setSettingsGroup("DebugMode"); + + useMessageBoxForSignals.setSettingsKey("UseMessageBoxForSignals"); + useMessageBoxForSignals.setDefaultValue(true); + useMessageBoxForSignals.setLabelText(Tr::tr( + "Show a message box when receiving a signal")); + useMessageBoxForSignals.setToolTip(Tr::tr( + "Displays a message box as soon as your application\n" + "receives a signal like SIGSEGV during debugging.")); + + adjustBreakpointLocations.setDisplayName(Tr::tr("Adjust Breakpoint Locations")); + adjustBreakpointLocations.setToolTip( + "<p>" + + Tr::tr("Not all source code lines generate " + "executable code. Putting a breakpoint on such a line acts as " + "if the breakpoint was set on the next line that generated code. " + "Selecting 'Adjust Breakpoint Locations' shifts the red " + "breakpoint markers in such cases to the location of the true " + "breakpoint.")); + adjustBreakpointLocations.setDefaultValue(true); + adjustBreakpointLocations.setSettingsKey("AdjustBreakpointLocations"); + adjustBreakpointLocations.setLabelText(Tr::tr( + "Adjust breakpoint locations")); + adjustBreakpointLocations.setToolTip(Tr::tr( + "GDB allows setting breakpoints on source lines for which no code \n" + "was generated. In such situations the breakpoint is shifted to the\n" + "next source code line for which code was actually generated.\n" + "This option reflects such temporary change by moving the breakpoint\n" + "markers in the source code editor.")); + + + breakOnThrow.setLabelText(Tr::tr("Break on \"throw\"")); + breakOnThrow.setSettingsKey("BreakOnThrow"); + + breakOnCatch.setLabelText(Tr::tr("Break on \"catch\"")); + breakOnCatch.setSettingsKey("BreakOnCatch"); + + breakOnWarning.setLabelText(Tr::tr("Break on \"qWarning\"")); + breakOnWarning.setSettingsKey("BreakOnWarning"); + // FIXME: Move to common settings page. + breakOnWarning.setLabelText(msgSetBreakpointAtFunction("qWarning")); + breakOnWarning.setToolTip(msgSetBreakpointAtFunctionToolTip("qWarning")); + + breakOnFatal.setLabelText(Tr::tr("Break on \"qFatal\"")); + breakOnFatal.setSettingsKey("BreakOnFatal"); + breakOnFatal.setLabelText(msgSetBreakpointAtFunction("qFatal")); + breakOnFatal.setToolTip(msgSetBreakpointAtFunctionToolTip("qFatal")); + + breakOnAbort.setLabelText(Tr::tr("Break on \"abort\"")); + breakOnAbort.setSettingsKey("BreakOnAbort"); + breakOnAbort.setLabelText(msgSetBreakpointAtFunction("abort")); + breakOnAbort.setToolTip(msgSetBreakpointAtFunctionToolTip("abort")); + + loadGdbInit.setSettingsKey("LoadGdbInit"); + loadGdbInit.setDefaultValue(true); + loadGdbInit.setLabelText(Tr::tr("Load .gdbinit file on startup")); + loadGdbInit.setToolTip(Tr::tr( + "Allows or inhibits reading the user's default\n" + ".gdbinit file on debugger startup.")); + + loadGdbDumpers.setSettingsKey("LoadGdbDumpers2"); + loadGdbDumpers.setLabelText(Tr::tr("Load system GDB pretty printers")); + loadGdbDumpers.setToolTip(Tr::tr( + "Uses the default GDB pretty printers installed in your " + "system or linked to the libraries your application uses.")); + + autoEnrichParameters.setSettingsKey("AutoEnrichParameters"); + autoEnrichParameters.setDefaultValue(true); + autoEnrichParameters.setLabelText(Tr::tr( + "Use common locations for debug information")); + autoEnrichParameters.setToolTip(Tr::tr( + "<html><head/><body>Adds common paths to locations " + "of debug information such as <i>/usr/src/debug</i> " + "when starting GDB.</body></html>")); + + useDynamicType.setSettingsKey("UseDynamicType"); + useDynamicType.setDefaultValue(true); + useDynamicType.setDisplayName(Tr::tr("Use Dynamic Object Type for Display")); + useDynamicType.setLabelText(Tr::tr( + "Use dynamic object type for display")); + useDynamicType.setToolTip(Tr::tr( + "Specifies whether the dynamic or the static type of objects will be " + "displayed. Choosing the dynamic type might be slower.")); + + targetAsync.setSettingsKey("TargetAsync"); + targetAsync.setLabelText(Tr::tr( + "Use asynchronous mode to control the inferior")); + + QString howToUsePython = Tr::tr( + "<p>To execute simple Python commands, prefix them with \"python\".</p>" + "<p>To execute sequences of Python commands spanning multiple lines " + "prepend the block with \"python\" on a separate line, and append " + "\"end\" on a separate line.</p>" + "<p>To execute arbitrary Python scripts, " + "use <i>python execfile('/path/to/script.py')</i>.</p>"); + + gdbStartupCommands.setSettingsKey("GdbStartupCommands"); + gdbStartupCommands.setDisplayStyle(StringAspect::TextEditDisplay); + gdbStartupCommands.setUseGlobalMacroExpander(); + gdbStartupCommands.setToolTip("<html><head/><body><p>" + Tr::tr( + "GDB commands entered here will be executed after " + "GDB has been started, but before the debugged program is started or " + "attached, and before the debugging helpers are initialized.") + "</p>" + + howToUsePython + "</body></html>"); + + gdbPostAttachCommands.setSettingsKey("GdbPostAttachCommands"); + gdbPostAttachCommands.setDisplayStyle(StringAspect::TextEditDisplay); + gdbPostAttachCommands.setUseGlobalMacroExpander(); + gdbPostAttachCommands.setToolTip("<html><head/><body><p>" + Tr::tr( + "GDB commands entered here will be executed after " + "GDB has successfully attached to remote targets.</p>" + "<p>You can add commands to further set up the target here, " + "such as \"monitor reset\" or \"load\".") + "</p>" + + howToUsePython + "</body></html>"); + + multiInferior.setSettingsKey("MultiInferior"); + multiInferior.setLabelText(Tr::tr("Debug all child processes")); + multiInferior.setToolTip(Tr::tr( + "<html><head/><body>Keeps debugging all children after a fork." + "</body></html>")); + + intelFlavor.setSettingsKey("IntelFlavor"); + intelFlavor.setLabelText(Tr::tr("Use Intel style disassembly")); + intelFlavor.setToolTip(Tr::tr("GDB shows by default AT&&T style disassembly.")); + + usePseudoTracepoints.setSettingsKey("UsePseudoTracepoints"); + usePseudoTracepoints.setLabelText(Tr::tr("Use pseudo message tracepoints")); + usePseudoTracepoints.setToolTip(Tr::tr("Uses Python to extend the ordinary GDB breakpoint class.")); + usePseudoTracepoints.setDefaultValue(true); + + useIndexCache.setSettingsKey("UseIndexCache"); + useIndexCache.setLabelText(Tr::tr("Use automatic symbol cache")); + useIndexCache.setToolTip(Tr::tr("It is possible for GDB to automatically save a copy of " + "its symbol index in a cache on disk and retrieve it from there when loading the same " + "binary in the future.")); + useIndexCache.setDefaultValue(true); + + skipKnownFrames.setSettingsKey("SkipKnownFrames"); + skipKnownFrames.setDisplayName(Tr::tr("Skip Known Frames")); + skipKnownFrames.setLabelText(Tr::tr("Skip known frames when stepping")); + skipKnownFrames.setToolTip(Tr::tr( + "<html><head/><body><p>" + "Allows <i>Step Into</i> to compress several steps into one step\n" + "for less noisy debugging. For example, the atomic reference\n" + "counting code is skipped, and a single <i>Step Into</i> for a signal\n" + "emission ends up directly in the slot connected to it.")); + + enableReverseDebugging.setSettingsKey("EnableReverseDebugging"); + enableReverseDebugging.setIcon(Icons::REVERSE_MODE.icon()); + enableReverseDebugging.setDisplayName(Tr::tr("Enable Reverse Debugging")); + enableReverseDebugging.setLabelText(Tr::tr("Enable reverse debugging")); + enableReverseDebugging.setToolTip(Tr::tr( + "<html><head/><body><p>Enables stepping backwards.</p><p>" + "<b>Note:</b> This feature is very slow and unstable on the GDB side. " + "It exhibits unpredictable behavior when going backwards over system " + "calls and is very likely to destroy your debugging session.</p></body></html>")); + + gdbWatchdogTimeout.setSettingsKey("WatchdogTimeout"); + gdbWatchdogTimeout.setDefaultValue(20); + gdbWatchdogTimeout.setSuffix(Tr::tr("sec")); + gdbWatchdogTimeout.setRange(20, 1000000); + gdbWatchdogTimeout.setLabelText(Tr::tr("GDB timeout:")); + gdbWatchdogTimeout.setToolTip(Tr::tr( + "The number of seconds before a non-responsive GDB process is terminated.\n" + "The default value of 20 seconds should be sufficient for most\n" + "applications, but there are situations when loading big libraries or\n" + "listing source files takes much longer than that on slow machines.\n" + "In this case, the value should be increased.")); + + + setLayouter([this] { + using namespace Layouting; + + auto labelDangerous = new QLabel("<html><head/><body><i>" + + Tr::tr("The options below give access to advanced<br>" + "or experimental functions of GDB.<p>" + "Enabling them may negatively impact<br>" + "your debugging experience.") + "</i></body></html>"); + + Group general { + title(Tr::tr("General")), + Column { + Row { gdbWatchdogTimeout, st }, + skipKnownFrames, + useMessageBoxForSignals, + adjustBreakpointLocations, + useDynamicType, + loadGdbInit, + loadGdbDumpers, + intelFlavor, + usePseudoTracepoints, + useIndexCache, + st + } + }; + + Group extended { + title(Tr::tr("Extended")), + Column { + labelDangerous, + targetAsync, + autoEnrichParameters, + breakOnWarning, + breakOnFatal, + breakOnAbort, + enableReverseDebugging, + multiInferior, + st + } + }; + + Group startup { + title(Tr::tr("Additional Startup Commands")), + Column { gdbStartupCommands } + }; + + Group attach { + title(Tr::tr("Additional Attach Commands")), + Column { gdbPostAttachCommands }, + }; + + return Grid { general, extended, br, startup, attach }; + }); + + readSettings(); +} + +// GdbSettingsPage + +class GdbSettingsPage final : public Core::IOptionsPage +{ +public: + GdbSettingsPage() + { + setId("M.Gdb"); + setDisplayName(Tr::tr("GDB")); + setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY); + setSettingsProvider([] { return &gdbSettings(); }); + } +}; + +const GdbSettingsPage settingsPage; + +} // Debugger::Internal diff --git a/src/plugins/debugger/gdb/gdbsettings.h b/src/plugins/debugger/gdb/gdbsettings.h new file mode 100644 index 00000000000..73c2646e1ca --- /dev/null +++ b/src/plugins/debugger/gdb/gdbsettings.h @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <utils/aspects.h> + +namespace Debugger::Internal { + +class GdbSettings : public Utils::AspectContainer +{ +public: + GdbSettings(); + + Utils::IntegerAspect gdbWatchdogTimeout{this}; + Utils::BoolAspect skipKnownFrames{this}; + Utils::BoolAspect useMessageBoxForSignals{this}; + Utils::BoolAspect adjustBreakpointLocations{this}; + Utils::BoolAspect useDynamicType{this}; + Utils::BoolAspect loadGdbInit{this}; + Utils::BoolAspect loadGdbDumpers{this}; + Utils::BoolAspect intelFlavor{this}; + Utils::BoolAspect usePseudoTracepoints{this}; + Utils::BoolAspect useIndexCache{this}; + Utils::StringAspect gdbStartupCommands{this}; + Utils::StringAspect gdbPostAttachCommands{this}; + + Utils::BoolAspect targetAsync{this}; + Utils::BoolAspect autoEnrichParameters{this}; + Utils::BoolAspect breakOnThrow{this}; + Utils::BoolAspect breakOnCatch{this}; + Utils::BoolAspect breakOnWarning{this}; + Utils::BoolAspect breakOnFatal{this}; + Utils::BoolAspect breakOnAbort{this}; + Utils::BoolAspect enableReverseDebugging{this}; + Utils::BoolAspect multiInferior{this}; +}; + +GdbSettings &gdbSettings(); + +} // Debugger::Internal diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index f7667e40e29..12da7a86fc4 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -36,7 +36,6 @@ #include <QApplication> #include <QDateTime> #include <QDebug> -#include <QDir> #include <QFileInfo> #include <QTimer> #include <QToolTip> @@ -69,7 +68,7 @@ LldbEngine::LldbEngine() setObjectName("LldbEngine"); setDebuggerName("LLDB"); - DebuggerSettings &ds = *debuggerSettings(); + DebuggerSettings &ds = settings(); connect(&ds.autoDerefPointers, &BaseAspect::changed, this, &LldbEngine::updateLocals); connect(ds.createFullBacktrace.action(), &QAction::triggered, this, &LldbEngine::fetchFullBacktrace); @@ -180,6 +179,7 @@ void LldbEngine::setupEngine() showMessage("STARTING LLDB: " + lldbCmd.toUserOutput()); Environment environment = runParameters().debugger.environment; + environment.appendOrSet("QT_CREATOR_LLDB_PROCESS", "1"); environment.appendOrSet("PYTHONUNBUFFERED", "1"); // avoid flushing problem on macOS DebuggerItem::addAndroidLldbPythonEnv(lldbCmd, environment); @@ -228,14 +228,14 @@ void LldbEngine::handleLldbStarted() if (!commands.isEmpty()) executeCommand(commands); - const FilePath path = debuggerSettings()->extraDumperFile(); + const FilePath path = settings().extraDumperFile(); if (!path.isEmpty() && path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); } - commands = debuggerSettings()->extraDumperCommands.value(); + commands = settings().extraDumperCommands(); if (!commands.isEmpty()) { DebuggerCommand cmd("executeDebuggerCommand"); cmd.arg("command", commands); @@ -248,8 +248,7 @@ void LldbEngine::handleLldbStarted() }; runCommand(cmd1); - const SourcePathMap sourcePathMap = - mergePlatformQtPath(rp, debuggerSettings()->sourcePathMap.value()); + const SourcePathMap sourcePathMap = mergePlatformQtPath(rp, settings().sourcePathMap()); for (auto it = sourcePathMap.constBegin(), cend = sourcePathMap.constEnd(); it != cend; ++it) { @@ -295,7 +294,6 @@ void LldbEngine::handleLldbStarted() cmd2.arg("remotechannel", ((rp.startMode == AttachToRemoteProcess || rp.startMode == AttachToRemoteServer) ? rp.remoteChannel : QString())); - cmd2.arg("platform", rp.platform); QTC_CHECK(!rp.continueAfterAttach || (rp.startMode == AttachToRemoteProcess || rp.startMode == AttachToLocalProcess || rp.startMode == AttachToRemoteServer)); @@ -469,7 +467,7 @@ void LldbEngine::selectThread(const Thread &thread) DebuggerCommand cmd("selectThread"); cmd.arg("id", thread->id()); cmd.callback = [this](const DebuggerResponse &) { - fetchStack(debuggerSettings()->maximalStackDepth()); + fetchStack(settings().maximalStackDepth()); }; runCommand(cmd); } @@ -701,7 +699,7 @@ void LldbEngine::updateAll() DebuggerCommand cmd("fetchThreads"); cmd.callback = [this](const DebuggerResponse &response) { threadsHandler()->setThreads(response.data); - fetchStack(debuggerSettings()->maximalStackDepth()); + fetchStack(settings().maximalStackDepth()); reloadRegisters(); }; runCommand(cmd); @@ -734,21 +732,21 @@ void LldbEngine::doUpdateLocals(const UpdateParameters ¶ms) watchHandler()->appendWatchersAndTooltipRequests(&cmd); const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE"); - const DebuggerSettings &s = *debuggerSettings(); + const DebuggerSettings &s = settings(); cmd.arg("passexceptions", alwaysVerbose); - cmd.arg("fancy", s.useDebuggingHelpers.value()); - cmd.arg("autoderef", s.autoDerefPointers.value()); - cmd.arg("dyntype", s.useDynamicType.value()); + cmd.arg("fancy", s.useDebuggingHelpers()); + cmd.arg("autoderef", s.autoDerefPointers()); + cmd.arg("dyntype", s.useDynamicType()); cmd.arg("partialvar", params.partialVariable); - cmd.arg("qobjectnames", s.showQObjectNames.value()); - cmd.arg("timestamps", s.logTimeStamps.value()); + cmd.arg("qobjectnames", s.showQObjectNames()); + cmd.arg("timestamps", s.logTimeStamps()); StackFrame frame = stackHandler()->currentFrame(); cmd.arg("context", frame.context); cmd.arg("nativemixed", isNativeMixedActive()); - cmd.arg("stringcutoff", s.maximalStringLength.value()); - cmd.arg("displaystringlimit", s.displayStringLimit.value()); + cmd.arg("stringcutoff", s.maximalStringLength()); + cmd.arg("displaystringlimit", s.displayStringLimit()); //cmd.arg("resultvarname", m_resultVarName); cmd.arg("partialvar", params.partialVariable); @@ -998,7 +996,7 @@ void LldbEngine::fetchDisassembler(DisassemblerAgent *agent) DebuggerCommand cmd("fetchDisassembler"); cmd.arg("address", loc.address()); cmd.arg("function", loc.functionName()); - cmd.arg("flavor", debuggerSettings()->intelFlavor.value() ? "intel" : "att"); + cmd.arg("flavor", settings().intelFlavor() ? "intel" : "att"); cmd.callback = [this, id](const DebuggerResponse &response) { DisassemblerLines result; QPointer<DisassemblerAgent> agent = m_disassemblerAgents.key(id); @@ -1032,8 +1030,8 @@ void LldbEngine::fetchDisassembler(DisassemblerAgent *agent) void LldbEngine::fetchFullBacktrace() { DebuggerCommand cmd("fetchFullBacktrace"); - cmd.callback = [](const DebuggerResponse &response) { - Internal::openTextEditor("Backtrace $", fromHex(response.data.data())); + cmd.callback = [](const DebuggerResponse &response) { + Internal::openTextEditor("Backtrace $", fromHex(response.data["fulltrace"].data())); }; runCommand(cmd); } diff --git a/src/plugins/debugger/loadcoredialog.cpp b/src/plugins/debugger/loadcoredialog.cpp index 6c57b593e13..d52d52e1115 100644 --- a/src/plugins/debugger/loadcoredialog.cpp +++ b/src/plugins/debugger/loadcoredialog.cpp @@ -3,7 +3,7 @@ #include "loadcoredialog.h" -#include "debuggerkitinformation.h" +#include "debuggerkitaspect.h" #include "debuggertr.h" #include "gdb/gdbengine.h" @@ -275,7 +275,7 @@ void AttachCoreDialog::coreFileChanged(const FilePath &coreFile) if (coreFile.osType() != OsType::OsTypeWindows && coreFile.exists()) { Kit *k = d->kitChooser->currentKit(); QTC_ASSERT(k, return); - Runnable debugger = DebuggerKitAspect::runnable(k); + ProcessRunData debugger = DebuggerKitAspect::runnable(k); CoreInfo cinfo = CoreInfo::readExecutableNameFromCore(debugger, coreFile); if (!cinfo.foundExecutableName.isEmpty()) d->symbolFileName->setFilePath(cinfo.foundExecutableName); diff --git a/src/plugins/debugger/logwindow.cpp b/src/plugins/debugger/logwindow.cpp index a8b519aad05..ba3452f66d9 100644 --- a/src/plugins/debugger/logwindow.cpp +++ b/src/plugins/debugger/logwindow.cpp @@ -12,19 +12,18 @@ #include <QDebug> #include <QTime> +#include <QFileDialog> +#include <QGuiApplication> #include <QHBoxLayout> #include <QLabel> #include <QMenu> -#include <QSyntaxHighlighter> #include <QPlainTextEdit> #include <QPushButton> -#include <QFileDialog> +#include <QSyntaxHighlighter> #include <QToolButton> #include <aggregation/aggregate.h> -#include <app/app_version.h> - #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/findplaceholder.h> #include <coreplugin/minisplitter.h> @@ -194,10 +193,10 @@ public: QMenu *menu = createStandardContextMenu(); menu->addAction(m_clearContentsAction); menu->addAction(m_saveContentsAction); // X11 clipboard is unreliable for long texts - menu->addAction(debuggerSettings()->logTimeStamps.action()); + menu->addAction(settings().logTimeStamps.action()); menu->addAction(Core::ActionManager::command(Constants::RELOAD_DEBUGGING_HELPERS)->action()); menu->addSeparator(); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); menu->exec(ev->globalPos()); delete menu; } @@ -438,17 +437,19 @@ LogWindow::LogWindow(DebuggerEngine *engine) setMinimumHeight(60); - showOutput(LogWarning, - Tr::tr("Note: This log contains possibly confidential information about your machine, " - "environment variables, in-memory data of the processes you are debugging, and more. " - "It is never transferred over the internet by %1, and only stored " - "to disk if you manually use the respective option from the context menu, or through " - "mechanisms that are not under the control of %1's Debugger plugin, " - "for instance in swap files, or other plugins you might use.\n" - "You may be asked to share the contents of this log when reporting bugs related " - "to debugger operation. In this case, make sure your submission does not " - "contain data you do not want to or you are not allowed to share.\n\n") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + showOutput( + LogWarning, + Tr::tr( + "Note: This log contains possibly confidential information about your machine, " + "environment variables, in-memory data of the processes you are debugging, and more. " + "It is never transferred over the internet by %1, and only stored " + "to disk if you manually use the respective option from the context menu, or through " + "mechanisms that are not under the control of %1's Debugger plugin, " + "for instance in swap files, or other plugins you might use.\n" + "You may be asked to share the contents of this log when reporting bugs related " + "to debugger operation. In this case, make sure your submission does not " + "contain data you do not want to or you are not allowed to share.\n\n") + .arg(QGuiApplication::applicationDisplayName())); } LogWindow::~LogWindow() @@ -493,7 +494,7 @@ void LogWindow::showOutput(int channel, const QString &output) QString out; out.reserve(output.size() + 1000); - if (output.at(0) != '~' && debuggerSettings()->logTimeStamps.value()) { + if (output.at(0) != '~' && settings().logTimeStamps()) { out.append(charForChannel(LogTime)); out.append(logTimeStamp()); out.append(nchar); @@ -561,7 +562,7 @@ void LogWindow::showInput(int channel, const QString &input) m_inputText->setTextCursor(cursor); return; } - if (debuggerSettings()->logTimeStamps.value()) + if (settings().logTimeStamps()) m_inputText->append(logTimeStamp()); m_inputText->append(input); QTextCursor cursor = m_inputText->textCursor(); @@ -694,7 +695,7 @@ void GlobalLogWindow::doOutput(const QString &output) void GlobalLogWindow::doInput(const QString &input) { - if (debuggerSettings()->logTimeStamps.value()) + if (settings().logTimeStamps()) m_leftPane->append(LogWindow::logTimeStamp()); m_leftPane->append(input); QTextCursor cursor = m_leftPane->textCursor(); diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp index 4f063b57288..5d0d6d6a8b7 100644 --- a/src/plugins/debugger/moduleshandler.cpp +++ b/src/plugins/debugger/moduleshandler.cpp @@ -205,7 +205,7 @@ bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev) canShowSymbols && moduleNameValid, [this, modulePath] { engine->requestModuleSections(modulePath); }); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 8b548b9d3d0..9c7c9bcac35 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -543,7 +543,7 @@ void PdbEngine::updateLocals() const bool alwaysVerbose = qtcEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE"); cmd.arg("passexceptions", alwaysVerbose); - cmd.arg("fancy", debuggerSettings()->useDebuggingHelpers.value()); + cmd.arg("fancy", settings().useDebuggingHelpers()); //cmd.arg("resultvarname", m_resultVarName); //m_lastDebuggableCommand = cmd; diff --git a/src/plugins/debugger/peripheralregisterhandler.cpp b/src/plugins/debugger/peripheralregisterhandler.cpp index e2ae4a9b7a7..f94e11e6235 100644 --- a/src/plugins/debugger/peripheralregisterhandler.cpp +++ b/src/plugins/debugger/peripheralregisterhandler.cpp @@ -771,7 +771,7 @@ bool PeripheralRegisterHandler::contextMenuEvent(const ItemViewEvent &ev) menu->addMenu(fmtMenu); } - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); return true; diff --git a/src/plugins/debugger/procinterrupt.cpp b/src/plugins/debugger/procinterrupt.cpp index f8b010e2767..113fbeefc4f 100644 --- a/src/plugins/debugger/procinterrupt.cpp +++ b/src/plugins/debugger/procinterrupt.cpp @@ -4,10 +4,8 @@ #include "procinterrupt.h" #include "debuggerconstants.h" -#include <app/app_version.h> - -#include <QCoreApplication> #include <QDir> +#include <QGuiApplication> #include <QProcess> // makes kill visible on Windows. using namespace Debugger::Internal; @@ -122,13 +120,13 @@ GDB 32bit | Api | Api | NA | Win32 const QString executable = breakApi == UseWin32Interrupt ? QCoreApplication::applicationDirPath() + "/win32interrupt.exe" : QCoreApplication::applicationDirPath() + "/win64interrupt.exe"; - if (!QFile::exists(executable)) { + if (!QFileInfo::exists(executable)) { *errorMessage = QString::fromLatin1( "%1 does not exist. If you have built %2 " "on your own, checkout " "https://code.qt.io/cgit/qt-creator/binary-artifacts.git/.") .arg(QDir::toNativeSeparators(executable), - QString(Core::Constants::IDE_DISPLAY_NAME)); + QGuiApplication::applicationDisplayName()); break; } switch (QProcess::execute(executable, QStringList(QString::number(pID)))) { diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 6c7962088a7..d418d9cb4dc 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -36,8 +36,6 @@ #include <texteditor/textdocument.h> #include <texteditor/texteditor.h> -#include <app/app_version.h> - #include <utils/basetreeview.h> #include <utils/fileinprojectfinder.h> #include <utils/process.h> @@ -48,6 +46,7 @@ #include <QDir> #include <QDockWidget> #include <QFileInfo> +#include <QGuiApplication> #include <QHostAddress> #include <QJsonArray> #include <QJsonDocument> @@ -386,7 +385,7 @@ void QmlEngine::connectionStartupFailed() auto infoBox = new QMessageBox(ICore::dialogParent()); infoBox->setIcon(QMessageBox::Critical); - infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME); + infoBox->setWindowTitle(QGuiApplication::applicationDisplayName()); infoBox->setText(Tr::tr("Could not connect to the in-process QML debugger." "\nDo you want to retry?")); infoBox->setStandardButtons(QMessageBox::Retry | QMessageBox::Cancel | @@ -407,7 +406,7 @@ void QmlEngine::appStartupFailed(const QString &errorMessage) if (companionEngine()) { auto infoBox = new QMessageBox(ICore::dialogParent()); infoBox->setIcon(QMessageBox::Critical); - infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME); + infoBox->setWindowTitle(QGuiApplication::applicationDisplayName()); infoBox->setText(error); infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help); infoBox->setDefaultButton(QMessageBox::Ok); @@ -846,10 +845,9 @@ bool compareConsoleItems(const ConsoleItem *a, const ConsoleItem *b) return a->text() < b->text(); } -static ConsoleItem *constructLogItemTree(const QVariant &result, - const QString &key = QString()) +static ConsoleItem *constructLogItemTree(const QVariant &result, const QString &key = {}) { - bool sorted = debuggerSettings()->sortStructMembers.value(); + const bool sorted = settings().sortStructMembers(); if (!result.isValid()) return nullptr; @@ -2235,7 +2233,7 @@ void QmlEnginePrivate::constructChildLogItems(ConsoleItem *item, const QmlV8Obje for (const QVariant &property : objectData.properties) *(it++) = constructLogItemTree(extractData(property), seenHandles); - if (debuggerSettings()->sortStructMembers.value()) + if (settings().sortStructMembers()) std::sort(children.begin(), children.end(), compareConsoleItems); for (ConsoleItem *child : std::as_const(children)) @@ -2347,7 +2345,7 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro parent->appendChild(item.release()); } - if (debuggerSettings()->sortStructMembers.value()) { + if (settings().sortStructMembers()) { parent->sortChildren([](const WatchItem *item1, const WatchItem *item2) { return item1->name < item2->name; }); diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp index 712b66e5b35..c2e118b73cb 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.cpp +++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp @@ -46,12 +46,12 @@ QmlInspectorAgent::QmlInspectorAgent(QmlEngine *engine, QmlDebugConnection *conn : m_qmlEngine(engine) , m_inspectorToolsContext("Debugger.QmlInspector") , m_selectAction(new QAction(this)) - , m_showAppOnTopAction(debuggerSettings()->showAppOnTop.action()) + , m_showAppOnTopAction(settings().showAppOnTop.action()) { m_debugIdToIname.insert(WatchItem::InvalidId, "inspect"); - connect(&debuggerSettings()->showQmlObjectTree, &Utils::BaseAspect::changed, + connect(&settings().showQmlObjectTree, &Utils::BaseAspect::changed, this, &QmlInspectorAgent::updateState); - connect(&debuggerSettings()->sortStructMembers, &Utils::BaseAspect::changed, + connect(&settings().sortStructMembers, &Utils::BaseAspect::changed, this, &QmlInspectorAgent::updateState); m_delayQueryTimer.setSingleShot(true); m_delayQueryTimer.setInterval(100); @@ -171,7 +171,7 @@ void QmlInspectorAgent::addObjectWatch(int objectDebugId) if (objectDebugId == WatchItem::InvalidId) return; - if (!isConnected() || !debuggerSettings()->showQmlObjectTree.value()) + if (!isConnected() || !settings().showQmlObjectTree()) return; // already set @@ -190,8 +190,7 @@ void QmlInspectorAgent::updateState() m_qmlEngine->logServiceStateChange(m_engineClient->name(), m_engineClient->serviceVersion(), m_engineClient->state()); - if (m_engineClient->state() == QmlDebugClient::Enabled - && debuggerSettings()->showQmlObjectTree.value()) + if (m_engineClient->state() == QmlDebugClient::Enabled && settings().showQmlObjectTree()) reloadEngines(); else clearObjectTree(); @@ -280,7 +279,7 @@ void QmlInspectorAgent::newObject(int engineId, int /*objectId*/, int /*parentId static void sortChildrenIfNecessary(WatchItem *propertiesWatch) { - if (debuggerSettings()->sortStructMembers.value()) { + if (settings().sortStructMembers()) { propertiesWatch->sortChildren([](const WatchItem *item1, const WatchItem *item2) { return item1->name < item2->name; }); @@ -354,7 +353,7 @@ void QmlInspectorAgent::queryEngineContext() { qCDebug(qmlInspectorLog) << __FUNCTION__ << "pending queries:" << m_rootContextQueryIds; - if (!isConnected() || !debuggerSettings()->showQmlObjectTree.value()) + if (!isConnected() || !settings().showQmlObjectTree()) return; log(LogSend, "LIST_OBJECTS"); @@ -369,7 +368,7 @@ void QmlInspectorAgent::fetchObject(int debugId) { qCDebug(qmlInspectorLog) << __FUNCTION__ << '(' << debugId << ')'; - if (!isConnected() || !debuggerSettings()->showQmlObjectTree.value()) + if (!isConnected() || !settings().showQmlObjectTree()) return; log(LogSend, "FETCH_OBJECT " + QString::number(debugId)); @@ -383,7 +382,7 @@ void QmlInspectorAgent::updateObjectTree(const ContextReference &context, int en { qCDebug(qmlInspectorLog) << __FUNCTION__ << '(' << context << ')'; - if (!isConnected() || !debuggerSettings()->showQmlObjectTree.value()) + if (!isConnected() || !settings().showQmlObjectTree()) return; for (const ObjectReference &obj : context.objects()) diff --git a/src/plugins/debugger/registerhandler.cpp b/src/plugins/debugger/registerhandler.cpp index 09a04e815a2..64f82052bf6 100644 --- a/src/plugins/debugger/registerhandler.cpp +++ b/src/plugins/debugger/registerhandler.cpp @@ -811,7 +811,7 @@ bool RegisterHandler::contextMenuEvent(const ItemViewEvent &ev) addFormatAction(Tr::tr("Octal"), OctalFormat); addFormatAction(Tr::tr("Binary"), BinaryFormat); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); return true; diff --git a/src/plugins/debugger/registerpostmortemaction.cpp b/src/plugins/debugger/registerpostmortemaction.cpp index cc2f6fccf5d..4cfbdc02e94 100644 --- a/src/plugins/debugger/registerpostmortemaction.cpp +++ b/src/plugins/debugger/registerpostmortemaction.cpp @@ -46,10 +46,10 @@ void RegisterPostMortemAction::registerNow(bool value) RegisterPostMortemAction::RegisterPostMortemAction() { - connect(this, &BoolAspect::valueChanged, this, &RegisterPostMortemAction::registerNow); + connect(this, &BaseAspect::changed, this, [this] { registerNow(value()); }); } -void RegisterPostMortemAction::readSettings(const QSettings *) +void RegisterPostMortemAction::readSettings() { Q_UNUSED(debuggerRegistryValueNameC) // avoid warning from MinGW @@ -60,7 +60,7 @@ void RegisterPostMortemAction::readSettings(const QSettings *) registered = isRegistered(handle, debuggerCall(), &errorMessage); if (handle) RegCloseKey(handle); - setValueQuietly(registered); + setValue(registered, BaseAspect::BeQuiet); } } // namespace Internal diff --git a/src/plugins/debugger/registerpostmortemaction.h b/src/plugins/debugger/registerpostmortemaction.h index 99d104165b0..d981ea14ccb 100644 --- a/src/plugins/debugger/registerpostmortemaction.h +++ b/src/plugins/debugger/registerpostmortemaction.h @@ -12,8 +12,8 @@ class RegisterPostMortemAction : public Utils::BoolAspect { public: RegisterPostMortemAction(); - void readSettings(const QSettings *settings = nullptr) override; - void writeSettings(QSettings *) const override {} + void readSettings() override; + void writeSettings() const override {} private: void registerNow(bool value); diff --git a/src/plugins/debugger/sourcefileshandler.cpp b/src/plugins/debugger/sourcefileshandler.cpp index bec03054173..23ec2477023 100644 --- a/src/plugins/debugger/sourcefileshandler.cpp +++ b/src/plugins/debugger/sourcefileshandler.cpp @@ -114,7 +114,7 @@ bool SourceFilesHandler::setData(const QModelIndex &idx, const QVariant &data, i addAction(Tr::tr("Open File \"%1\"").arg(name), true, [this, name] { m_engine->gotoLocation(FilePath::fromString(name)); }); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); menu->popup(ev.globalPos()); return true; } diff --git a/src/plugins/debugger/sourceutils.cpp b/src/plugins/debugger/sourceutils.cpp index 82f1929bb89..26bb70b43fd 100644 --- a/src/plugins/debugger/sourceutils.cpp +++ b/src/plugins/debugger/sourceutils.cpp @@ -223,7 +223,7 @@ QStringList getUninitializedVariables(const Snapshot &snapshot, QString cppFunctionAt(const FilePath &filePath, int line, int column) { - const Snapshot snapshot = CppModelManager::instance()->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); if (const Document::Ptr document = snapshot.document(filePath)) return document->functionAt(line, column); @@ -240,7 +240,7 @@ QString cppExpressionAt(TextEditorWidget *editorWidget, int pos, function->clear(); const FilePath filePath = editorWidget->textDocument()->filePath(); - const Snapshot snapshot = CppModelManager::instance()->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); const Document::Ptr document = snapshot.document(filePath); QTextCursor tc = editorWidget->textCursor(); QString expr; @@ -376,7 +376,7 @@ static void setValueAnnotationsHelper(BaseTextEditor *textEditor, TextEditorWidget *widget = textEditor->editorWidget(); TextDocument *textDocument = widget->textDocument(); const FilePath filePath = loc.fileName(); - const Snapshot snapshot = CppModelManager::instance()->snapshot(); + const Snapshot snapshot = CppModelManager::snapshot(); const Document::Ptr cppDocument = snapshot.document(filePath); if (!cppDocument) // For non-C++ documents. return; diff --git a/src/plugins/debugger/stackframe.h b/src/plugins/debugger/stackframe.h index b377648614d..a6faca93f4a 100644 --- a/src/plugins/debugger/stackframe.h +++ b/src/plugins/debugger/stackframe.h @@ -37,6 +37,7 @@ public: quint64 address = 0; bool usable = false; QString context; // Opaque value produced and consumed by the native backends. + uint debuggerId = 0; }; using StackFrames = QList<StackFrame>; diff --git a/src/plugins/debugger/stackhandler.cpp b/src/plugins/debugger/stackhandler.cpp index 1fc6f04582f..d8ca3cad06b 100644 --- a/src/plugins/debugger/stackhandler.cpp +++ b/src/plugins/debugger/stackhandler.cpp @@ -47,9 +47,9 @@ StackHandler::StackHandler(DebuggerEngine *engine) setObjectName("StackModel"); setHeader({Tr::tr("Level"), Tr::tr("Function"), Tr::tr("File"), Tr::tr("Line"), Tr::tr("Address") }); - connect(debuggerSettings()->expandStack.action(), &QAction::triggered, + connect(settings().expandStack.action(), &QAction::triggered, this, &StackHandler::reloadFullStack); - connect(debuggerSettings()->maximalStackDepth.action(), &QAction::triggered, + connect(settings().maximalStackDepth.action(), &QAction::triggered, this, &StackHandler::reloadFullStack); // For now there's always only "the" current thread. @@ -66,7 +66,7 @@ QVariant SpecialStackItem::data(int column, int role) const return Tr::tr("<More>"); if (role == Qt::DecorationRole && column == StackLevelColumn) return Icons::EMPTY.icon(); - return QVariant(); + return {}; } QVariant StackFrameItem::data(int column, int role) const @@ -86,16 +86,16 @@ QVariant StackFrameItem::data(int column, int role) const return QString("0x%1").arg(frame.address, 0, 16); return QString(); } - return QVariant(); + return {}; } if (role == Qt::DecorationRole && column == StackLevelColumn) return handler->iconForRow(row); - if (role == Qt::ToolTipRole && debuggerSettings()->useToolTipsInStackView.value()) + if (role == Qt::ToolTipRole && settings().useToolTipsInStackView()) return frame.toToolTip(); - return QVariant(); + return {}; } Qt::ItemFlags StackFrameItem::flags(int column) const @@ -234,8 +234,8 @@ void StackHandler::setFramesAndCurrentIndex(const GdbMi &frames, bool isFull) targetFrame = i; } - bool canExpand = !isFull && (n >= debuggerSettings()->maximalStackDepth()); - debuggerSettings()->expandStack.setEnabled(canExpand); + bool canExpand = !isFull && n >= settings().maximalStackDepth(); + settings().expandStack.setEnabled(canExpand); setFrames(stackFrames, canExpand); // We can't jump to any file if we don't have any frames. @@ -424,7 +424,7 @@ bool StackHandler::contextMenuEvent(const ItemViewEvent &ev) frame = frameAt(row); const quint64 address = frame.address; - menu->addAction(debuggerSettings()->expandStack.action()); + menu->addAction(settings().expandStack.action()); addAction(this, menu, Tr::tr("Copy Contents to Clipboard"), true, [ev] { setClipboardAndSelection(selectedText(ev.view(), true)); @@ -437,7 +437,7 @@ bool StackHandler::contextMenuEvent(const ItemViewEvent &ev) addAction(this, menu, Tr::tr("Save as Task File..."), true, [this] { saveTaskFile(); }); if (m_engine->hasCapability(CreateFullBacktraceCapability)) - menu->addAction(debuggerSettings()->createFullBacktrace.action()); + menu->addAction(settings().createFullBacktrace.action()); if (m_engine->hasCapability(AdditionalQmlStackCapability)) addAction(this, menu, Tr::tr("Load QML Stack"), true, [this] { m_engine->loadAdditionalQmlStack(); }); @@ -485,8 +485,8 @@ bool StackHandler::contextMenuEvent(const ItemViewEvent &ev) } menu->addSeparator(); - menu->addAction(debuggerSettings()->useToolTipsInStackView.action()); - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().useToolTipsInStackView.action()); + menu->addAction(settings().settingsDialog.action()); connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); menu->popup(ev.globalPos()); return true; diff --git a/src/plugins/debugger/terminal.cpp b/src/plugins/debugger/terminal.cpp index 86531d0e0ad..18092f132fe 100644 --- a/src/plugins/debugger/terminal.cpp +++ b/src/plugins/debugger/terminal.cpp @@ -13,6 +13,7 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/process.h> +#include <utils/processinterface.h> #include <utils/qtcassert.h> #include <QDebug> @@ -149,7 +150,7 @@ void Terminal::onSlaveReaderActivated(int fd) } TerminalRunner::TerminalRunner(RunControl *runControl, - const std::function<Runnable()> &stubRunnable) + const std::function<ProcessRunData()> &stubRunnable) : RunWorker(runControl), m_stubRunnable(stubRunnable) { setId("TerminalRunner"); @@ -171,7 +172,7 @@ void TerminalRunner::start() { QTC_ASSERT(m_stubRunnable, reportFailure({}); return); QTC_ASSERT(!m_stubProc, reportFailure({}); return); - Runnable stub = m_stubRunnable(); + ProcessRunData stub = m_stubRunnable(); bool runAsRoot = false; if (auto runAsRootAspect = runControl()->aspect<RunAsRootAspect>()) diff --git a/src/plugins/debugger/terminal.h b/src/plugins/debugger/terminal.h index cd57e9eea21..07f334fbe8a 100644 --- a/src/plugins/debugger/terminal.h +++ b/src/plugins/debugger/terminal.h @@ -8,7 +8,10 @@ #include <projectexplorer/runcontrol.h> -namespace Utils { class Process; } +namespace Utils { +class Process; +class ProcessRunData; +} namespace Debugger { @@ -50,7 +53,7 @@ class TerminalRunner : public ProjectExplorer::RunWorker { public: TerminalRunner(ProjectExplorer::RunControl *runControl, - const std::function<ProjectExplorer::Runnable()> &stubRunnable); + const std::function<Utils::ProcessRunData()> &stubRunnable); qint64 applicationPid() const { return m_applicationPid; } qint64 applicationMainThreadId() const { return m_applicationMainThreadId; } @@ -66,7 +69,7 @@ private: void stubDone(); Utils::Process *m_stubProc = nullptr; - std::function<ProjectExplorer::Runnable()> m_stubRunnable; + std::function<Utils::ProcessRunData()> m_stubRunnable; qint64 m_applicationPid = 0; qint64 m_applicationMainThreadId = 0; }; diff --git a/src/plugins/debugger/threadshandler.cpp b/src/plugins/debugger/threadshandler.cpp index 063c12a85cf..f7adcf3288a 100644 --- a/src/plugins/debugger/threadshandler.cpp +++ b/src/plugins/debugger/threadshandler.cpp @@ -227,7 +227,7 @@ bool ThreadsHandler::setData(const QModelIndex &idx, const QVariant &data, int r if (ev.as<QContextMenuEvent>()) { auto menu = new QMenu; - menu->addAction(debuggerSettings()->settingsDialog.action()); + menu->addAction(settings().settingsDialog.action()); menu->popup(ev.globalPos()); return true; } diff --git a/src/plugins/debugger/unstartedappwatcherdialog.cpp b/src/plugins/debugger/unstartedappwatcherdialog.cpp index 84cdc73c16b..e120571839e 100644 --- a/src/plugins/debugger/unstartedappwatcherdialog.cpp +++ b/src/plugins/debugger/unstartedappwatcherdialog.cpp @@ -4,7 +4,7 @@ #include "unstartedappwatcherdialog.h" #include "debuggeritem.h" -#include "debuggerkitinformation.h" +#include "debuggerkitaspect.h" #include "debuggertr.h" #include <utils/pathchooser.h> @@ -12,14 +12,16 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/kit.h> #include <projectexplorer/kitchooser.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projecttree.h> #include <projectexplorer/runconfiguration.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> +#include <utils/processinterface.h> + #include <QCheckBox> #include <QDialogButtonBox> #include <QFileDialog> @@ -82,7 +84,7 @@ UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent) if (kit) m_kitChooser->setCurrentKitId(kit->id()); - else if (KitManager::defaultKit()) + else if (KitManager::waitForLoaded() && KitManager::defaultKit()) m_kitChooser->setCurrentKitId(KitManager::defaultKit()->id()); auto pathLayout = new QHBoxLayout; @@ -97,7 +99,7 @@ UnstartedAppWatcherDialog::UnstartedAppWatcherDialog(QWidget *parent) pathLayout->addWidget(resetExecutable); if (activeTarget) { if (RunConfiguration *runConfig = activeTarget->activeRunConfiguration()) { - const Runnable runnable = runConfig->runnable(); + const ProcessRunData runnable = runConfig->runnable(); if (isLocal(runConfig)) { resetExecutable->setEnabled(true); connect(resetExecutable, &QPushButton::clicked, this, [this, runnable] { @@ -178,7 +180,7 @@ void UnstartedAppWatcherDialog::selectExecutable() if (activeTarget) { if (RunConfiguration *runConfig = activeTarget->activeRunConfiguration()) { - const Runnable runnable = runConfig->runnable(); + const ProcessRunData runnable = runConfig->runnable(); if (isLocal(runConfig)) path = runnable.command.executable().parentDir(); } diff --git a/src/plugins/debugger/watchdata.h b/src/plugins/debugger/watchdata.h index 42d046f2a13..bb2e458bcd3 100644 --- a/src/plugins/debugger/watchdata.h +++ b/src/plugins/debugger/watchdata.h @@ -71,6 +71,7 @@ public: uint bitpos = 0; // Position within bit fields uint bitsize = 0; // Size in case of bit fields uint autoDerefCount = 0; // number of levels of automatic dereferencing that has taken place (for pointer types) + uint variablesReference = 0;// reference to the variable in the variables request DAP related int elided = 0; // Full size if value was cut off, -1 if cut on unknown size, 0 otherwise int arrayIndex = -1; // -1 if not an array member uchar sortGroup = 0; // 0 - ordinary member, 1 - vptr, 2 - base class diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index 45a3277f702..4a016c80527 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -28,7 +28,6 @@ #include <texteditor/syntaxhighlighter.h> -#include <app/app_version.h> #include <utils/algorithm.h> #include <utils/basetreeview.h> #include <utils/checkablemessagebox.h> @@ -269,7 +268,7 @@ public: this, &SeparatedView::tabBarContextMenuRequested); tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); setWindowFlags(windowFlags() | Qt::Window); - setWindowTitle(Tr::tr("Debugger - %1").arg(Core::Constants::IDE_DISPLAY_NAME)); + setWindowTitle(Tr::tr("Debugger - %1").arg(QGuiApplication::applicationDisplayName())); QVariant geometry = SessionManager::value("DebuggerSeparateWidgetGeometry"); if (geometry.isValid()) { @@ -526,7 +525,7 @@ WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine) m_engine->updateLocalsWindow(showReturn); }); - DebuggerSettings &s = *debuggerSettings(); + DebuggerSettings &s = settings(); connect(&s.sortStructMembers, &BaseAspect::changed, m_engine, &DebuggerEngine::updateLocals); connect(&s.showStdNamespace, &BaseAspect::changed, @@ -582,9 +581,9 @@ static QString niceTypeHelper(const QString &typeIn) QString WatchModel::removeNamespaces(QString str) const { - if (!debuggerSettings()->showStdNamespace.value()) + if (!settings().showStdNamespace()) str.remove("std::"); - if (!debuggerSettings()->showQtNamespace.value()) { + if (!settings().showQtNamespace()) { const QString qtNamespace = m_engine->qtNamespace(); if (!qtNamespace.isEmpty()) str.remove(qtNamespace); @@ -1114,8 +1113,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const } case Qt::ToolTipRole: - return debuggerSettings()->useToolTipsInLocalsView.value() - ? item->toToolTip() : QVariant(); + return settings().useToolTipsInLocalsView() ? item->toToolTip() : QVariant(); case Qt::ForegroundRole: return valueColor(item, column); @@ -1135,7 +1133,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const default: break; } - return QVariant(); + return {}; } bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role) @@ -1350,7 +1348,7 @@ void WatchModel::expand(WatchItem *item, bool requestEngineUpdate) if (item->isLoadMore()) { item = item->parent(); m_maxArrayCount[item->iname] - = m_maxArrayCount.value(item->iname, debuggerSettings()->defaultArraySize.value()) * 10; + = m_maxArrayCount.value(item->iname, settings().defaultArraySize()) * 10; if (requestEngineUpdate) m_engine->updateItem(item->iname); } else { @@ -1824,7 +1822,7 @@ bool WatchModel::contextMenuEvent(const ItemViewEvent &ev) menu->addSeparator(); - DebuggerSettings &s = *debuggerSettings(); + DebuggerSettings &s = settings(); menu->addAction(s.useDebuggingHelpers.action()); menu->addAction(s.useToolTipsInLocalsView.action()); menu->addAction(s.autoDerefPointers.action()); @@ -2191,7 +2189,7 @@ void WatchHandler::insertItems(const GdbMi &data) { QSet<WatchItem *> itemsToSort; - const bool sortStructMembers = debuggerSettings()->sortStructMembers.value(); + const bool sortStructMembers = settings().sortStructMembers(); for (const GdbMi &child : data) { auto item = new WatchItem; item->parse(child, sortStructMembers); @@ -2243,6 +2241,7 @@ bool WatchHandler::insertItem(WatchItem *item) void WatchModel::reexpandItems() { + m_engine->reexpandItems(m_expandedINames); for (const QString &iname: m_expandedINames) { if (WatchItem *item = findItem(iname)) { emit itemIsExpanded(indexForItem(item)); @@ -2333,7 +2332,7 @@ void WatchHandler::notifyUpdateFinished() }); QMap<QString, QString> values; - if (debuggerSettings()->useAnnotationsInMainEditor.value()) { + if (settings().useAnnotationsInMainEditor()) { m_model->forAllItems([&values](WatchItem *item) { const QString expr = item->sourceExpression(); if (!expr.isEmpty()) @@ -2575,7 +2574,7 @@ void WatchModel::clearWatches() ICore::dialogParent(), Tr::tr("Remove All Expression Evaluators"), Tr::tr("Are you sure you want to remove all expression evaluators?"), - QString("RemoveAllWatchers")); + Key("RemoveAllWatchers")); if (ret != QMessageBox::Yes) return; @@ -2849,7 +2848,7 @@ QSet<QString> WatchHandler::expandedINames() const int WatchHandler::maxArrayCount(const QString &iname) const { - return m_model->m_maxArrayCount.value(iname, debuggerSettings()->defaultArraySize()); + return m_model->m_maxArrayCount.value(iname, settings().defaultArraySize()); } void WatchHandler::recordTypeInfo(const GdbMi &typeInfo) diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index b7256464dfb..4e95b04b6f5 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -32,7 +32,7 @@ WatchTreeView::WatchTreeView(WatchType type) connect(this, &QTreeView::expanded, this, &WatchTreeView::expandNode); connect(this, &QTreeView::collapsed, this, &WatchTreeView::collapseNode); - connect(&debuggerSettings()->logTimeStamps, &Utils::BaseAspect::changed, + connect(&settings().logTimeStamps, &Utils::BaseAspect::changed, this, &WatchTreeView::updateTimeColumn); } @@ -84,8 +84,7 @@ void WatchTreeView::setModel(QAbstractItemModel *model) void WatchTreeView::updateTimeColumn() { if (header()) - header()->setSectionHidden(WatchModelBase::TimeColumn, - !debuggerSettings()->logTimeStamps.value()); + header()->setSectionHidden(WatchModelBase::TimeColumn, !settings().logTimeStamps()); } void WatchTreeView::handleItemIsExpanded(const QModelIndex &idx) diff --git a/src/plugins/designer/CMakeLists.txt b/src/plugins/designer/CMakeLists.txt index 18b1cf97692..581f7ec2a7e 100644 --- a/src/plugins/designer/CMakeLists.txt +++ b/src/plugins/designer/CMakeLists.txt @@ -1,3 +1,29 @@ +# used in the .json.in +if(Qt6_VERSION VERSION_GREATER_EQUAL 6.7.0) + set(DESIGNER_PLUGIN_ARGUMENTS + "\"Arguments\" : [ + { + \"Name\" : \"-designer-qt-pluginpath\", + \"Parameter\" : \"path\", + \"Description\" : \"Override the default search path for Qt Designer plugins\" + }, + { + \"Name\" : \"-designer-pluginpath\", + \"Parameter\" : \"path\", + \"Description\" : \"Add a custom search path for Qt Designer plugins\" + } + ],") +else() + set(DESIGNER_PLUGIN_ARGUMENTS + "\"Arguments\" : [ + { + \"Name\" : \"-designer-qt-pluginpath\", + \"Parameter\" : \"path\", + \"Description\" : \"Override the default search path for Qt Designer plugins\" + } + ],") +endif() + add_qtc_plugin(Designer PLUGIN_CLASS FormEditorPlugin CONDITION TARGET Qt::DesignerComponentsPrivate AND TARGET Qt::Designer diff --git a/src/plugins/designer/Designer.json.in b/src/plugins/designer/Designer.json.in index 91f90b06520..2d03527c30a 100644 --- a/src/plugins/designer/Designer.json.in +++ b/src/plugins/designer/Designer.json.in @@ -1,19 +1,20 @@ { - \"Name\" : \"Designer\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Designer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Creator\", - \"Description\" : \"Qt Designer integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Creator", + "Description" : "Qt Designer integration.", + "Url" : "http://www.qt.io", + ${DESIGNER_PLUGIN_ARGUMENTS} + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/designer/codemodelhelpers.cpp b/src/plugins/designer/codemodelhelpers.cpp index 8428fd98171..f58c3f65fb7 100644 --- a/src/plugins/designer/codemodelhelpers.cpp +++ b/src/plugins/designer/codemodelhelpers.cpp @@ -105,7 +105,7 @@ bool navigateToSlot(const QString &uiFileName, *errorMessage = Tr::tr("The generated header of the form \"%1\" could not be found.\nRebuilding the project might help.").arg(uiFileName); return false; } - const CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot(); + const CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot(); const DocumentPtr generatedHeaderDoc = snapshot.document(generatedHeaderFile); if (!generatedHeaderDoc) { *errorMessage = Tr::tr("The generated header \"%1\" could not be found in the code model.\nRebuilding the project might help.").arg(generatedHeaderFile.toUserOutput()); diff --git a/src/plugins/designer/cpp/formclasswizard.cpp b/src/plugins/designer/cpp/formclasswizard.cpp index 593c9db95f1..1582cd26de5 100644 --- a/src/plugins/designer/cpp/formclasswizard.cpp +++ b/src/plugins/designer/cpp/formclasswizard.cpp @@ -6,6 +6,8 @@ #include <designer/designerconstants.h> #include <designer/qtdesignerformclasscodegenerator.h> #include <cppeditor/cppeditorconstants.h> +#include <cppeditor/cpptoolsreuse.h> +#include <projectexplorer/projecttree.h> #include <qtsupport/qtsupportconstants.h> #include <QDebug> @@ -22,12 +24,12 @@ FormClassWizard::FormClassWizard() QString FormClassWizard::headerSuffix() const { - return preferredSuffix(CppEditor::Constants::CPP_HEADER_MIMETYPE); + return CppEditor::preferredCxxHeaderSuffix(ProjectExplorer::ProjectTree::currentProject()); } QString FormClassWizard::sourceSuffix() const { - return preferredSuffix(CppEditor::Constants::CPP_SOURCE_MIMETYPE); + return CppEditor::preferredCxxSourceSuffix(ProjectExplorer::ProjectTree::currentProject()); } QString FormClassWizard::formSuffix() const diff --git a/src/plugins/designer/cpp/formclasswizarddialog.cpp b/src/plugins/designer/cpp/formclasswizarddialog.cpp index 3d6d760332a..cc912683f69 100644 --- a/src/plugins/designer/cpp/formclasswizarddialog.cpp +++ b/src/plugins/designer/cpp/formclasswizarddialog.cpp @@ -9,6 +9,7 @@ #include <cppeditor/abstracteditorsupport.h> #include <designer/formtemplatewizardpage.h> +#include <projectexplorer/projecttree.h> #include <qtsupport/codegenerator.h> #include <utils/filepath.h> @@ -68,7 +69,8 @@ FormClassWizardParameters FormClassWizardDialog::parameters() const m_classPage->getParameters(&rc); // Name the ui class in the Ui namespace after the class specified rc.uiTemplate = QtSupport::CodeGenerator::changeUiClassName(m_rawFormTemplate, rc.className); - rc.usePragmaOnce = CppEditor::AbstractEditorSupport::usePragmaOnce(); + rc.usePragmaOnce = CppEditor::AbstractEditorSupport::usePragmaOnce( + ProjectExplorer::ProjectTree::currentProject()); return rc; } diff --git a/src/plugins/designer/cpp/formclasswizardpage.cpp b/src/plugins/designer/cpp/formclasswizardpage.cpp index f8113c65754..9ec4ebfa279 100644 --- a/src/plugins/designer/cpp/formclasswizardpage.cpp +++ b/src/plugins/designer/cpp/formclasswizardpage.cpp @@ -8,9 +8,9 @@ #include "../designertr.h" #include <coreplugin/icore.h> - #include <cppeditor/cppeditorconstants.h> - +#include <cppeditor/cpptoolsreuse.h> +#include <projectexplorer/projecttree.h> #include <utils/mimeutils.h> #include <utils/wizard.h> @@ -32,9 +32,9 @@ FormClassWizardPage::FormClassWizardPage() m_newClassWidget = new NewClassWidget(classGroupBox); m_newClassWidget->setHeaderExtension( - Utils::mimeTypeForName(CppEditor::Constants::CPP_HEADER_MIMETYPE).preferredSuffix()); + CppEditor::preferredCxxHeaderSuffix(ProjectExplorer::ProjectTree::currentProject())); m_newClassWidget->setSourceExtension( - Utils::mimeTypeForName(CppEditor::Constants::CPP_SOURCE_MIMETYPE).preferredSuffix()); + CppEditor::preferredCxxSourceSuffix(ProjectExplorer::ProjectTree::currentProject())); m_newClassWidget->setLowerCaseFiles(lowercaseHeaderFiles()); connect(m_newClassWidget, &NewClassWidget::validChanged, @@ -54,11 +54,7 @@ FormClassWizardPage::~FormClassWizardPage() = default; // Retrieve settings of CppEditor plugin. bool FormClassWizardPage::lowercaseHeaderFiles() { - QString lowerCaseSettingsKey = CppEditor::Constants::CPPEDITOR_SETTINGSGROUP; - lowerCaseSettingsKey += '/'; - lowerCaseSettingsKey += CppEditor::Constants::LOWERCASE_CPPFILES_KEY; - const bool lowerCaseDefault = CppEditor::Constants::LOWERCASE_CPPFILES_DEFAULT; - return Core::ICore::settings()->value(lowerCaseSettingsKey, QVariant(lowerCaseDefault)).toBool(); + return CppEditor::preferLowerCaseFileNames(ProjectExplorer::ProjectTree::currentProject()); } void FormClassWizardPage::setClassName(const QString &suggestedClassName) diff --git a/src/plugins/designer/cpp/newclasswidget.cpp b/src/plugins/designer/cpp/newclasswidget.cpp index 924700b59c0..8d678303405 100644 --- a/src/plugins/designer/cpp/newclasswidget.cpp +++ b/src/plugins/designer/cpp/newclasswidget.cpp @@ -36,7 +36,6 @@ struct NewClassWidgetPrivate { QString m_sourceExtension; QString m_formExtension; bool m_valid = false; - bool m_classEdited = false; ClassNameValidatingLineEdit *m_classLineEdit; FileNameValidatingLineEdit *m_headerFileLineEdit; @@ -78,8 +77,6 @@ NewClassWidget::NewClassWidget(QWidget *parent) : connect(d->m_classLineEdit, &ClassNameValidatingLineEdit::updateFileName, this, &NewClassWidget::slotUpdateFileNames); - connect(d->m_classLineEdit, &QLineEdit::textEdited, - this, &NewClassWidget::classNameEdited); connect(d->m_classLineEdit, &FancyLineEdit::validChanged, this, &NewClassWidget::slotValidChanged); connect(d->m_headerFileLineEdit, &FancyLineEdit::validChanged, @@ -110,13 +107,6 @@ NewClassWidget::~NewClassWidget() delete d; } -void NewClassWidget::classNameEdited() -{ - if (debugNewClassWidget) - qDebug() << Q_FUNC_INFO << d->m_headerExtension << d->m_sourceExtension; - d->m_classEdited = true; -} - void NewClassWidget::setClassName(const QString &suggestedName) { if (debugNewClassWidget) diff --git a/src/plugins/designer/cpp/newclasswidget.h b/src/plugins/designer/cpp/newclasswidget.h index bba1d9a82aa..e8fe4e4b125 100644 --- a/src/plugins/designer/cpp/newclasswidget.h +++ b/src/plugins/designer/cpp/newclasswidget.h @@ -53,7 +53,6 @@ private: void slotUpdateFileNames(const QString &t); void slotValidChanged(); void slotActivated(); - void classNameEdited(); QString fixSuffix(const QString &suffix); NewClassWidgetPrivate *d; diff --git a/src/plugins/designer/designer.qbs b/src/plugins/designer/designer.qbs index 8b97a494a7c..bde63fa3eea 100644 --- a/src/plugins/designer/designer.qbs +++ b/src/plugins/designer/designer.qbs @@ -27,6 +27,20 @@ QtcPlugin { sharedSources.prefix ]) + pluginjson.replacements: ({"DESIGNER_PLUGIN_ARGUMENTS": + "\"Arguments\" : [\n\ + {\n\ + \"Name\" : \"-designer-qt-pluginpath\",\n\ + \"Parameter\" : \"path\",\n\ + \"Description\" : \"Override the default search path for Qt Designer plugins\"\n\ + },\n\ + {\n\ + \"Name\" : \"-designer-pluginpath\",\n\ + \"Parameter\" : \"path\",\n\ + \"Description\" : \"Add a custom search path for Qt Designer plugins\"\n\ + }\n\ + ],"}) + Group { name: "General" files: [ diff --git a/src/plugins/designer/formeditor.cpp b/src/plugins/designer/formeditor.cpp index ab2dd3ddd1e..776e74f0f90 100644 --- a/src/plugins/designer/formeditor.cpp +++ b/src/plugins/designer/formeditor.cpp @@ -25,8 +25,12 @@ #include <coreplugin/minisplitter.h> #include <coreplugin/modemanager.h> #include <coreplugin/outputpane.h> + #include <utils/infobar.h> #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> +#include <utils/stringutils.h> +#include <utils/theme/theme.h> #include <QDesignerFormEditorPluginInterface> #include <QDesignerFormEditorInterface> @@ -41,23 +45,21 @@ #include <QActionGroup> #include <QApplication> #include <QCursor> +#include <QDebug> #include <QDockWidget> +#include <QElapsedTimer> +#include <QKeySequence> #include <QMenu> #include <QMessageBox> -#include <QKeySequence> +#include <QPainter> +#include <QPluginLoader> #include <QPrintDialog> #include <QPrinter> -#include <QPainter> #include <QStyle> +#include <QTime> #include <QToolBar> #include <QVBoxLayout> -#include <QDebug> -#include <QSettings> -#include <QPluginLoader> -#include <QTime> -#include <QElapsedTimer> - #include <algorithm> static const char settingsGroupC[] = "Designer"; @@ -78,6 +80,9 @@ static inline QIcon designerIcon(const QString &iconName) return icon; } +Q_GLOBAL_STATIC(QString, sQtPluginPath); +Q_GLOBAL_STATIC(QStringList, sAdditionalPluginPaths); + using namespace Core; using namespace Designer::Constants; using namespace Utils; @@ -139,7 +144,7 @@ public: void fullInit(); - void saveSettings(QSettings *s); + void saveSettings(QtcSettings *s); void initDesignerSubWindows(); @@ -203,9 +208,35 @@ public: static FormEditorData *d = nullptr; -FormEditorData::FormEditorData() : - m_formeditor(QDesignerComponents::createFormEditor(nullptr)) +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) +static QStringList designerPluginPaths() { + const QStringList qtPluginPath = sQtPluginPath->isEmpty() + ? QDesignerComponents::defaultPluginPaths() + : QStringList(*sQtPluginPath); + return qtPluginPath + *sAdditionalPluginPaths; +} +#endif + +FormEditorData::FormEditorData() +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + m_formeditor = QDesignerComponents::createFormEditorWithPluginPaths(designerPluginPaths(), + nullptr); +#else + // Qt < 6.7.0 doesn't have API for changing the plugin path yet. + // Work around it by temporarily changing the application's library paths, + // which are used for Designer's plugin paths. + // This must be done before creating the FormEditor, and with it QDesignerPluginManager. + const QStringList restoreLibraryPaths = sQtPluginPath->isEmpty() + ? QStringList() + : QCoreApplication::libraryPaths(); + if (!sQtPluginPath->isEmpty()) + QCoreApplication::setLibraryPaths(QStringList(*sQtPluginPath)); + m_formeditor = QDesignerComponents::createFormEditor(nullptr); + if (!sQtPluginPath->isEmpty()) + QCoreApplication::setLibraryPaths(restoreLibraryPaths); +#endif if (Designer::Constants::Internal::debug) qDebug() << Q_FUNC_INFO; QTC_ASSERT(!d, return); @@ -230,7 +261,7 @@ FormEditorData::FormEditorData() : m_settingsPages.append(settingsPage); } - QObject::connect(EditorManager::instance(), &EditorManager::currentEditorChanged, [this](IEditor *editor) { + QObject::connect(EditorManager::instance(), &EditorManager::currentEditorChanged, this, [this](IEditor *editor) { if (Designer::Constants::Internal::debug) qDebug() << Q_FUNC_INFO << editor << " of " << m_fwm->formWindowCount(); @@ -251,7 +282,7 @@ FormEditorData::FormEditorData() : FormEditorData::~FormEditorData() { if (m_initStage == FullyInitialized) { - QSettings *s = ICore::settings(); + QtcSettings *s = ICore::settings(); s->beginGroup(settingsGroupC); m_editorWidget->saveSettings(s); s->endGroup(); @@ -360,7 +391,7 @@ void FormEditorData::fullInit() delete initTime; } - QObject::connect(EditorManager::instance(), &EditorManager::editorsClosed, + QObject::connect(EditorManager::instance(), &EditorManager::editorsClosed, this, [this] (const QList<IEditor *> editors) { for (IEditor *editor : editors) m_editorWidget->removeFormWindowEditor(editor); @@ -368,7 +399,7 @@ void FormEditorData::fullInit() // Nest toolbar and editor widget m_editorWidget = new EditorWidget; - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginGroup(settingsGroupC); m_editorWidget->restoreSettings(settings); settings->endGroup(); @@ -487,7 +518,7 @@ void FormEditorData::setupActions() m_actionPrint = new QAction(this); bindShortcut(ActionManager::registerAction(m_actionPrint, Core::Constants::PRINT, m_contexts), m_actionPrint); - QObject::connect(m_actionPrint, &QAction::triggered, [this]() { print(); }); + connect(m_actionPrint, &QAction::triggered, this, &FormEditorData::print); //'delete' action. Do not set a shortcut as Designer handles // the 'Delete' key by event filter. Setting a shortcut triggers @@ -500,7 +531,7 @@ void FormEditorData::setupActions() m_actionGroupEditMode = new QActionGroup(this); m_actionGroupEditMode->setExclusive(true); - QObject::connect(m_actionGroupEditMode, &QActionGroup::triggered, + QObject::connect(m_actionGroupEditMode, &QActionGroup::triggered, this, [this](QAction *a) { activateEditMode(a->data().toInt()); }); medit->addSeparator(m_contexts, Core::Constants::G_EDIT_OTHER); @@ -605,7 +636,7 @@ void FormEditorData::setupActions() m_actionAboutPlugins->setEnabled(false); // FWM - QObject::connect(m_fwm, &QDesignerFormWindowManagerInterface::activeFormWindowChanged, + QObject::connect(m_fwm, &QDesignerFormWindowManagerInterface::activeFormWindowChanged, this, [this] (QDesignerFormWindowInterface *afw) { m_fwm->closeAllPreviews(); setPreviewMenuEnabled(afw != nullptr); @@ -670,7 +701,7 @@ void FormEditorData::setPreviewMenuEnabled(bool e) m_previewInStyleMenu->setEnabled(e); } -void FormEditorData::saveSettings(QSettings *s) +void FormEditorData::saveSettings(QtcSettings *s) { s->beginGroup(settingsGroupC); m_editorWidget->saveSettings(s); @@ -739,7 +770,8 @@ IEditor *FormEditorData::createEditor() QDesignerFormWindowInterface *form = m_fwm->createFormWindow(nullptr); QTC_ASSERT(form, return nullptr); form->setPalette(Theme::initialPalette()); - QObject::connect(form, &QDesignerFormWindowInterface::toolChanged, [this] (int i) { toolChanged(i); }); + connect(form, &QDesignerFormWindowInterface::toolChanged, + this, &FormEditorData::toolChanged); auto widgetHost = new SharedTools::WidgetHost( /* parent */ nullptr, form); FormWindowEditor *formWindowEditor = m_xmlEditorFactory->create(form); @@ -871,5 +903,33 @@ void FormEditorData::print() printer->setPageOrientation(oldOrientation); } +void setQtPluginPath(const QString &qtPluginPath) +{ + QTC_CHECK(!d); + *sQtPluginPath = QDir::fromNativeSeparators(qtPluginPath); +#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0) + // Cut a "/designer" postfix off, if present. + // For Qt < 6.7.0 we hack the plugin path by temporarily setting the application library paths + // and Designer adds "/designer" to these. + static const QString postfix = "/designer"; + *sQtPluginPath = Utils::trimBack(*sQtPluginPath, '/'); + if (sQtPluginPath->endsWith(postfix)) + sQtPluginPath->chop(postfix.size()); + if (!QFileInfo::exists(*sQtPluginPath + postfix)) { + qWarning() << qPrintable( + QLatin1String( + "Warning: The path \"%1\" passed to -designer-qt-pluginpath does not exist. " + "Note that \"%2\" at the end is enforced.") + .arg(*sQtPluginPath + postfix, postfix)); + } +#endif +} + +void addPluginPath(const QString &pluginPath) +{ + QTC_CHECK(!d); + sAdditionalPluginPaths->append(pluginPath); +} + } // namespace Internal } // namespace Designer diff --git a/src/plugins/designer/formeditor.h b/src/plugins/designer/formeditor.h index 0d069e2322c..875c8e50904 100644 --- a/src/plugins/designer/formeditor.h +++ b/src/plugins/designer/formeditor.h @@ -60,5 +60,8 @@ SharedTools::WidgetHost *activeWidgetHost(); FormWindowEditor *activeEditor(); QList<Core::IOptionsPage *> optionsPages(); +void setQtPluginPath(const QString &qtPluginPath); +void addPluginPath(const QString &pluginPath); + } // namespace Internal } // namespace Designer diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp index fd9a78187c5..e997b26b939 100644 --- a/src/plugins/designer/formeditorplugin.cpp +++ b/src/plugins/designer/formeditorplugin.cpp @@ -52,6 +52,7 @@ public: FormEditorFactory formEditorFactory; SettingsPageProvider settingsPageProvider; QtDesignerFormClassCodeGenerator formClassCodeGenerator; + FormPageFactory formPageFactory; }; FormEditorPlugin::~FormEditorPlugin() @@ -60,7 +61,27 @@ FormEditorPlugin::~FormEditorPlugin() delete d; } -void FormEditorPlugin::initialize() +static void parseArguments(const QStringList &arguments) +{ + const auto doWithNext = [arguments](auto it, const std::function<void(QString)> &fun) { + ++it; + if (it != arguments.cend()) + fun(*it); + }; + for (auto it = arguments.cbegin(); it != arguments.cend(); ++it) { + if (*it == "-designer-qt-pluginpath") + doWithNext(it, [](const QString &path) { setQtPluginPath(path); }); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + // -designer-pluginpath is only supported when building with Qt >= 6.7.0, which added the + // required API + else if (*it == "-designer-pluginpath") + doWithNext(it, [](const QString &path) { addPluginPath(path); }); +#endif + } +} + +bool FormEditorPlugin::initialize([[maybe_unused]] const QStringList &arguments, + [[maybe_unused]] QString *errorString) { d = new FormEditorPluginPrivate; @@ -79,8 +100,6 @@ void FormEditorPlugin::initialize() }); #endif - ProjectExplorer::JsonWizardFactory::registerPageFactory(new Internal::FormPageFactory); - // Ensure that loading designer translations is done before FormEditorW is instantiated const QString locale = ICore::userInterfaceLanguage(); if (!locale.isEmpty()) { @@ -91,6 +110,9 @@ void FormEditorPlugin::initialize() if (qtr->load(trFile, qtTrPath) || qtr->load(trFile, creatorTrPath)) QCoreApplication::installTranslator(qtr); } + + parseArguments(arguments); + return true; } void FormEditorPlugin::extensionsInitialized() diff --git a/src/plugins/designer/formeditorplugin.h b/src/plugins/designer/formeditorplugin.h index e24c76ec538..754aeecaee4 100644 --- a/src/plugins/designer/formeditorplugin.h +++ b/src/plugins/designer/formeditorplugin.h @@ -24,7 +24,7 @@ private slots: #endif private: - void initialize() override; + bool initialize(const QStringList &arguments, QString *errorString) override; void extensionsInitialized() override; void switchSourceForm(); diff --git a/src/plugins/designer/formwindowfile.cpp b/src/plugins/designer/formwindowfile.cpp index 7ab08540933..6923981e923 100644 --- a/src/plugins/designer/formwindowfile.cpp +++ b/src/plugins/designer/formwindowfile.cpp @@ -84,20 +84,15 @@ Core::IDocument::OpenResult FormWindowFile::open(QString *errorString, bool FormWindowFile::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { - const FilePath &actualName = filePath.isEmpty() ? this->filePath() : filePath; - - if (Designer::Constants::Internal::debug) - qDebug() << Q_FUNC_INFO << filePath << "->" << actualName; - QTC_ASSERT(m_formWindow, return false); - if (actualName.isEmpty()) + if (filePath.isEmpty()) return false; const QString oldFormName = m_formWindow->fileName(); if (!autoSave) - m_formWindow->setFileName(actualName.toString()); - const bool writeOK = writeFile(actualName, errorString); + m_formWindow->setFileName(filePath.toString()); + const bool writeOK = writeFile(filePath, errorString); m_shouldAutoSave = false; if (autoSave) return writeOK; @@ -108,7 +103,7 @@ bool FormWindowFile::saveImpl(QString *errorString, const FilePath &filePath, bo } m_formWindow->setDirty(false); - setFilePath(actualName); + setFilePath(filePath); updateIsModified(); return true; diff --git a/src/plugins/designer/qtcreatorintegration.cpp b/src/plugins/designer/qtcreatorintegration.cpp index 9c73ee755c9..31a5336342a 100644 --- a/src/plugins/designer/qtcreatorintegration.cpp +++ b/src/plugins/designer/qtcreatorintegration.cpp @@ -12,6 +12,7 @@ #include <designer/cpp/formclasswizardpage.h> #include <cppeditor/cppeditorconstants.h> +#include <cppeditor/cppeditorplugin.h> #include <cppeditor/cppeditorwidget.h> #include <cppeditor/cppmodelmanager.h> #include <cppeditor/cppsemanticinfo.h> @@ -133,7 +134,8 @@ QtCreatorIntegration::QtCreatorIntegration(QDesignerFormEditorInterface *core, Q connect(this, &QtCreatorIntegration::propertyChanged, this, [this](QDesignerFormWindowInterface *formWindow, const QString &name, const QVariant &) { - if (name == "objectName") { + qCDebug(log) << "got propertyChanged() signal" << name; + if (name.endsWith("Name")) { if (const auto extraCompiler = d->extraCompilers.find(formWindow); extraCompiler != d->extraCompilers.end()) { (*extraCompiler)->unblock(); @@ -498,7 +500,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, const QString uicedName = "ui_" + fi.completeBaseName() + ".h"; // Retrieve code model snapshot restricted to project of ui file or the working copy. - Snapshot docTable = CppEditor::CppModelManager::instance()->snapshot(); + Snapshot docTable = CppEditor::CppModelManager::snapshot(); Snapshot newDocTable; const Project *uiProject = ProjectManager::projectForFile(currentUiFile); if (uiProject) { @@ -510,7 +512,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, } else { const FilePath configFileName = CppEditor::CppModelManager::configurationFileName(); const CppEditor::WorkingCopy::Table elements = - CppEditor::CppModelManager::instance()->workingCopy().elements(); + CppEditor::CppModelManager::workingCopy().elements(); for (auto it = elements.cbegin(), end = elements.cend(); it != end; ++it) { const Utils::FilePath &fileName = it.key(); if (fileName != configFileName) @@ -577,7 +579,7 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, FilePath declFilePath; if (!fun) { // add function declaration to cl - CppEditor::WorkingCopy workingCopy = CppEditor::CppModelManager::instance()->workingCopy(); + CppEditor::WorkingCopy workingCopy = CppEditor::CppModelManager::workingCopy(); declFilePath = declDoc->filePath(); getParsedDocument(declFilePath, workingCopy, docTable); addDeclaration(docTable, declFilePath, cl, functionNameWithParameterNames); @@ -586,8 +588,8 @@ bool QtCreatorIntegration::navigateToSlot(const QString &objectName, QList<Utils::FilePath> filePaths; for (auto it = docTable.begin(); it != docTable.end(); ++it) filePaths << it.key(); - workingCopy = CppEditor::CppModelManager::instance()->workingCopy(); - docTable = CppEditor::CppModelManager::instance()->snapshot(); + workingCopy = CppEditor::CppModelManager::workingCopy(); + docTable = CppEditor::CppModelManager::snapshot(); newDocTable = {}; for (const auto &file : std::as_const(filePaths)) { const Document::Ptr doc = docTable.document(file); @@ -741,7 +743,7 @@ void QtCreatorIntegration::handleSymbolRenameStage2( if (usesClangd) editorWidget->textDocument()->setFilePath(uiHeader); editorWidget->textDocument()->setPlainText(QString::fromUtf8(virtualContent)); - Snapshot snapshot = CppEditor::CppModelManager::instance()->snapshot(); + Snapshot snapshot = CppEditor::CppModelManager::snapshot(); snapshot.remove(uiHeader); snapshot.remove(editor->textDocument()->filePath()); const Document::Ptr cppDoc = snapshot.preprocessedDocument(virtualContent, uiHeader); @@ -782,8 +784,8 @@ void QtCreatorIntegration::handleSymbolRenameStage2( qCDebug(log) << "renaming with built-in code model"; snapshot.insert(cppDoc); snapshot.updateDependencyTable(); - CppEditor::CppModelManager::instance()->renameUsages(cppDoc, cursor, snapshot, - newName, callback); + CppEditor::CppModelManager::renameUsages(cppDoc, cursor, snapshot, + newName, callback); } return; } @@ -795,6 +797,6 @@ void QtCreatorIntegration::handleSymbolRenameStage2( void QtCreatorIntegration::slotSyncSettingsToDesigner() { // Set promotion-relevant parameters on integration. - setHeaderSuffix(Utils::mimeTypeForName(CppEditor::Constants::CPP_HEADER_MIMETYPE).preferredSuffix()); + setHeaderSuffix(CppEditor::preferredCxxHeaderSuffix(ProjectTree::currentProject())); setHeaderLowercase(FormClassWizardPage::lowercaseHeaderFiles()); } diff --git a/src/plugins/designer/qtcreatorintegration.h b/src/plugins/designer/qtcreatorintegration.h index c27d296ab0b..31e3cdcb32c 100644 --- a/src/plugins/designer/qtcreatorintegration.h +++ b/src/plugins/designer/qtcreatorintegration.h @@ -6,7 +6,9 @@ #include <QtGlobal> #include <QDesignerIntegration> -QT_FORWARD_DECLARE_CLASS(QUrl) +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE namespace Designer { namespace Internal { diff --git a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp index d4d5e02a3c2..d935079cf43 100644 --- a/src/plugins/designer/qtdesignerformclasscodegenerator.cpp +++ b/src/plugins/designer/qtdesignerformclasscodegenerator.cpp @@ -5,15 +5,15 @@ #include "formtemplatewizardpage.h" #include <designer/cpp/formclasswizardparameters.h> -#include <utils/codegeneration.h> +#include <extensionsystem/pluginmanager.h> #include <coreplugin/icore.h> #include <cppeditor/abstracteditorsupport.h> +#include <projectexplorer/projecttree.h> #include <qtsupport/codegenerator.h> #include <qtsupport/codegensettings.h> -#include <extensionsystem/pluginmanager.h> +#include <utils/codegeneration.h> #include <QTextStream> -#include <QSettings> #include <QFileInfo> #include <QDebug> @@ -29,7 +29,7 @@ namespace Designer { // Write out how to access the Ui class in the source code. static void writeUiMemberAccess(const QtSupport::CodeGenSettings &fp, QTextStream &str) { - switch (fp.embedding) { + switch (fp.embedding()) { case QtSupport::CodeGenSettings::PointerAggregatedUiClass: str << uiMemberC << "->"; break; @@ -44,8 +44,7 @@ static void writeUiMemberAccess(const QtSupport::CodeGenSettings &fp, QTextStrea bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParameters ¶meters, QString *header, QString *source, int indentation) { - QtSupport::CodeGenSettings generationParameters; - generationParameters.fromSettings(Core::ICore::settings()); + const QtSupport::CodeGenSettings &generationParameters = QtSupport::codeGenSettings(); const QString indent = QString(indentation, ' '); QString formBaseClass; @@ -70,12 +69,11 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete const QString unqualifiedClassName = namespaceList.takeLast(); - const QString headerLicense = - CppEditor::AbstractEditorSupport::licenseTemplate( - FilePath::fromString(parameters.headerFile), parameters.className); - const QString sourceLicense = - CppEditor::AbstractEditorSupport::licenseTemplate( - FilePath::fromString(parameters.sourceFile), parameters.className); + ProjectExplorer::Project * const project = ProjectExplorer::ProjectTree::currentProject(); + const QString headerLicense = CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(parameters.headerFile), parameters.className); + const QString sourceLicense = CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(parameters.sourceFile), parameters.className); // Include guards const QString guard = Utils::headerGuard(parameters.headerFile, namespaceList); @@ -91,14 +89,14 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete headerStr << "#ifndef " << guard << "\n#define " << guard << "\n\n"; // Include 'ui_' - if (generationParameters.embedding != QtSupport::CodeGenSettings::PointerAggregatedUiClass) { + if (generationParameters.embedding() != QtSupport::CodeGenSettings::PointerAggregatedUiClass) { Utils::writeIncludeFileDirective(uiInclude, false, headerStr); } else { // Todo: Can we obtain the header from the code model for custom widgets? // Alternatively, from Designer. if (formBaseClass.startsWith('Q')) { - if (generationParameters.includeQtModule) { - if (generationParameters.addQtVersionCheck) { + if (generationParameters.includeQtModule()) { + if (generationParameters.addQtVersionCheck()) { Utils::writeBeginQtVersionCheck(headerStr); Utils::writeIncludeFileDirective("QtWidgets/" + formBaseClass, true, headerStr); headerStr << "#else\n"; @@ -117,7 +115,7 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete headerStr); // Forward-declare the UI class - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) { + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) { headerStr << '\n' << namespaceIndent << "namespace " << uiNamespaceC << " {\n" << namespaceIndent << indent << "class " << Internal::FormTemplateWizardPage::stripNamespaces(uiClassName) << ";\n" @@ -127,22 +125,22 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete // Class declaration headerStr << '\n' << namespaceIndent << "class " << unqualifiedClassName << " : public " << formBaseClass; - if (generationParameters.embedding == QtSupport::CodeGenSettings::InheritedUiClass) + if (generationParameters.embedding() == QtSupport::CodeGenSettings::InheritedUiClass) headerStr << ", private " << uiClassName; headerStr << "\n{\n" << namespaceIndent << indent << "Q_OBJECT\n\n" << namespaceIndent << "public:\n" << namespaceIndent << indent << "explicit " << unqualifiedClassName << "(QWidget *parent = nullptr);\n"; - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) headerStr << namespaceIndent << indent << "~" << unqualifiedClassName << "();\n"; // retranslation - if (generationParameters.retranslationSupport) + if (generationParameters.retranslationSupport()) headerStr << '\n' << namespaceIndent << "protected:\n" << namespaceIndent << indent << "void changeEvent(QEvent *e);\n"; // Member variable - if (generationParameters.embedding != QtSupport::CodeGenSettings::InheritedUiClass) { + if (generationParameters.embedding() != QtSupport::CodeGenSettings::InheritedUiClass) { headerStr << '\n' << namespaceIndent << "private:\n" << namespaceIndent << indent << uiClassName << ' '; - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) headerStr << '*'; headerStr << uiMemberC << ";\n"; } @@ -156,27 +154,27 @@ bool QtDesignerFormClassCodeGenerator::generateCpp(const FormClassWizardParamete QTextStream sourceStr(source); sourceStr << sourceLicense; Utils::writeIncludeFileDirective(parameters.headerFile, false, sourceStr); - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) Utils::writeIncludeFileDirective(uiInclude, false, sourceStr); // NameSpaces( Utils::writeOpeningNameSpaces(namespaceList, QString(), sourceStr); // Constructor with setupUi sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::" << unqualifiedClassName << "(QWidget *parent) :\n" << namespaceIndent << indent << formBaseClass << "(parent)"; - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) sourceStr << ",\n" << namespaceIndent << indent << uiMemberC << "(new " << uiClassName << ")"; sourceStr << '\n' << namespaceIndent << "{\n" << namespaceIndent << indent; writeUiMemberAccess(generationParameters, sourceStr); sourceStr << "setupUi(this);\n" << namespaceIndent << "}\n"; // Deleting destructor for ptr - if (generationParameters.embedding == QtSupport::CodeGenSettings::PointerAggregatedUiClass) { + if (generationParameters.embedding() == QtSupport::CodeGenSettings::PointerAggregatedUiClass) { sourceStr << '\n' << namespaceIndent << unqualifiedClassName << "::~" << unqualifiedClassName << "()\n" << namespaceIndent << "{\n" << namespaceIndent << indent << "delete " << uiMemberC << ";\n" << namespaceIndent << "}\n"; } // retranslation - if (generationParameters.retranslationSupport) { + if (generationParameters.retranslationSupport()) { sourceStr << '\n' << namespaceIndent << "void " << unqualifiedClassName << "::" << "changeEvent(QEvent *e)\n" << namespaceIndent << "{\n" << namespaceIndent << indent << formBaseClass << "::changeEvent(e);\n" diff --git a/src/plugins/designer/settingsmanager.cpp b/src/plugins/designer/settingsmanager.cpp index f533d401072..59a032595d4 100644 --- a/src/plugins/designer/settingsmanager.cpp +++ b/src/plugins/designer/settingsmanager.cpp @@ -4,12 +4,20 @@ #include "settingsmanager.h" #include <coreplugin/icore.h> + #include <utils/qtcassert.h> -#include <QSettings> -#include <QDebug> +using namespace Utils; -using namespace Designer::Internal; +namespace Designer::Internal { + +static Key addPrefix(const QString &name) +{ + Key result; + if (Core::ICore::settings()->group().isEmpty()) + result = "Designer"; + return Key(result + name.toUtf8()); +} void SettingsManager::beginGroup(const QString &prefix) { @@ -41,10 +49,4 @@ void SettingsManager::remove(const QString &key) Core::ICore::settings()->remove(addPrefix(key)); } -QString SettingsManager::addPrefix(const QString &name) const -{ - QString result = name; - if (Core::ICore::settings()->group().isEmpty()) - result.prepend("Designer"); - return result; -} +} // Designer::Internal diff --git a/src/plugins/designer/settingsmanager.h b/src/plugins/designer/settingsmanager.h index 633e58d117d..06ce37a98cb 100644 --- a/src/plugins/designer/settingsmanager.h +++ b/src/plugins/designer/settingsmanager.h @@ -21,9 +21,6 @@ public: void setValue(const QString &key, const QVariant &value) override; QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const override; void remove(const QString &key) override; - -private: - QString addPrefix(const QString &name) const; }; } // namespace Internal diff --git a/src/plugins/designer/settingspage.cpp b/src/plugins/designer/settingspage.cpp index e1998ffd5e3..4fdee045a78 100644 --- a/src/plugins/designer/settingspage.cpp +++ b/src/plugins/designer/settingspage.cpp @@ -47,8 +47,7 @@ SettingsPageProvider::SettingsPageProvider() { setCategory(Designer::Constants::SETTINGS_CATEGORY); setDisplayCategory(Tr::tr(Designer::Constants::SETTINGS_TR_CATEGORY)); - setCategoryIcon(Utils::Icon({{":/core/images/settingscategory_design.png", - Utils::Theme::PanelTextColorDark}}, Utils::Icon::Tint)); + setCategoryIconPath(":/core/images/settingscategory_design.png"); } QList<Core::IOptionsPage *> SettingsPageProvider::pages() const diff --git a/src/plugins/diffeditor/DiffEditor.json.in b/src/plugins/diffeditor/DiffEditor.json.in index 0dbbfbd6106..8fc93667da0 100644 --- a/src/plugins/diffeditor/DiffEditor.json.in +++ b/src/plugins/diffeditor/DiffEditor.json.in @@ -1,18 +1,18 @@ { - \"Name\" : \"DiffEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "DiffEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Diff editor component.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Diff editor component.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp index ddf91b4e1ec..d202fb4338a 100644 --- a/src/plugins/diffeditor/diffeditor.cpp +++ b/src/plugins/diffeditor/diffeditor.cpp @@ -518,7 +518,7 @@ void DiffEditor::toggleSync() IDiffView *DiffEditor::loadSettings() { QTC_ASSERT(currentView(), return nullptr); - QSettings *s = ICore::settings(); + QtcSettings *s = ICore::settings(); // Read current settings: s->beginGroup(settingsGroupC); @@ -537,9 +537,9 @@ IDiffView *DiffEditor::loadSettings() return view; } -void DiffEditor::saveSetting(const QString &key, const QVariant &value) const +void DiffEditor::saveSetting(const Key &key, const QVariant &value) const { - QSettings *s = ICore::settings(); + QtcSettings *s = ICore::settings(); s->beginGroup(settingsGroupC); s->setValue(key, value); s->endGroup(); diff --git a/src/plugins/diffeditor/diffeditor.h b/src/plugins/diffeditor/diffeditor.h index 24f89d55030..3bf270b5248 100644 --- a/src/plugins/diffeditor/diffeditor.h +++ b/src/plugins/diffeditor/diffeditor.h @@ -59,7 +59,7 @@ private: void toggleSync(); IDiffView *loadSettings(); - void saveSetting(const QString &key, const QVariant &value) const; + void saveSetting(const Utils::Key &key, const QVariant &value) const; void updateEntryToolTip(); void showDiffView(IDiffView *view); void updateDiffEditorSwitcher(); @@ -74,7 +74,7 @@ private: UnifiedView *m_unifiedView = nullptr; SideBySideView *m_sideBySideView = nullptr; QStackedWidget *m_stackedWidget = nullptr; - QVector<IDiffView *> m_views; + QList<IDiffView *> m_views; QToolBar *m_toolBar = nullptr; QComboBox *m_entriesComboBox = nullptr; QSpinBox *m_contextSpinBox = nullptr; diff --git a/src/plugins/diffeditor/diffeditordocument.h b/src/plugins/diffeditor/diffeditordocument.h index 6a7e72ae373..287e53de592 100644 --- a/src/plugins/diffeditor/diffeditordocument.h +++ b/src/plugins/diffeditor/diffeditordocument.h @@ -8,8 +8,6 @@ #include <coreplugin/patchtool.h> #include <coreplugin/textdocument.h> -QT_FORWARD_DECLARE_CLASS(QMenu) - namespace DiffEditor { class DiffEditorController; diff --git a/src/plugins/diffeditor/diffeditorwidgetcontroller.h b/src/plugins/diffeditor/diffeditorwidgetcontroller.h index 81baef980a5..7364cf1a637 100644 --- a/src/plugins/diffeditor/diffeditorwidgetcontroller.h +++ b/src/plugins/diffeditor/diffeditorwidgetcontroller.h @@ -12,7 +12,9 @@ #include <QTextCharFormat> #include <QTimer> -QT_FORWARD_DECLARE_CLASS(QMenu) +QT_BEGIN_NAMESPACE +class QMenu; +QT_END_NAMESPACE namespace Core { class IDocument; } namespace TextEditor { class FontSettings; } diff --git a/src/plugins/diffeditor/diffview.h b/src/plugins/diffeditor/diffview.h index 9028f83c636..07e4c1d1863 100644 --- a/src/plugins/diffeditor/diffview.h +++ b/src/plugins/diffeditor/diffview.h @@ -11,7 +11,9 @@ #include <QString> #include <QObject> -QT_FORWARD_DECLARE_CLASS(QWidget) +QT_BEGIN_NAMESPACE +class QWidget; +QT_END_NAMESPACE namespace TextEditor { class TextEditorWidget; } diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.cpp b/src/plugins/diffeditor/selectabletexteditorwidget.cpp index e0c95f6abd3..bcb3ee0381d 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.cpp +++ b/src/plugins/diffeditor/selectabletexteditorwidget.cpp @@ -114,13 +114,13 @@ void SelectableTextEditorWidget::setFoldingIndent(const QTextBlock &block, int i void SelectableTextEditorWidget::paintBlock(QPainter *painter, const QTextBlock &block, const QPointF &offset, - const QVector<QTextLayout::FormatRange> &selections, + const QList<QTextLayout::FormatRange> &selections, const QRect &clipRect) const { const int blockNumber = block.blockNumber(); - QList<DiffSelection> diffs = m_diffSelections.value(blockNumber); + const QList<DiffSelection> diffs = m_diffSelections.value(blockNumber); - QVector<QTextLayout::FormatRange> newSelections; + QList<QTextLayout::FormatRange> newSelections; for (const DiffSelection &diffSelection : diffs) { if (diffSelection.format) { QTextLayout::FormatRange formatRange; diff --git a/src/plugins/diffeditor/selectabletexteditorwidget.h b/src/plugins/diffeditor/selectabletexteditorwidget.h index 4b0d465204f..4e82f83b304 100644 --- a/src/plugins/diffeditor/selectabletexteditorwidget.h +++ b/src/plugins/diffeditor/selectabletexteditorwidget.h @@ -39,7 +39,7 @@ private: void paintBlock(QPainter *painter, const QTextBlock &block, const QPointF &offset, - const QVector<QTextLayout::FormatRange> &selections, + const QList<QTextLayout::FormatRange> &selections, const QRect &clipRect) const override; DiffSelections m_diffSelections; diff --git a/src/plugins/docker/CMakeLists.txt b/src/plugins/docker/CMakeLists.txt index b2773c25056..827c78732dc 100644 --- a/src/plugins/docker/CMakeLists.txt +++ b/src/plugins/docker/CMakeLists.txt @@ -8,7 +8,7 @@ add_qtc_plugin(Docker dockerconstants.h dockerdevice.cpp dockerdevice.h dockerdevicewidget.cpp dockerdevicewidget.h - dockerplugin.cpp dockerplugin.h + dockerplugin.cpp dockersettings.cpp dockersettings.h kitdetector.cpp kitdetector.h ) diff --git a/src/plugins/docker/Docker.json.in b/src/plugins/docker/Docker.json.in index c2854973d48..6fc896f8032 100644 --- a/src/plugins/docker/Docker.json.in +++ b/src/plugins/docker/Docker.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Docker\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Docker", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Experimental\" : true, - \"Description\" : \"Basic support for Docker\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Experimental" : true, + "Description" : "Basic support for Docker", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/docker/docker.qbs b/src/plugins/docker/docker.qbs index d2e0dd416fd..a2cb29ae9e9 100644 --- a/src/plugins/docker/docker.qbs +++ b/src/plugins/docker/docker.qbs @@ -20,7 +20,6 @@ QtcPlugin { "dockerdevicewidget.cpp", "dockerdevicewidget.h", "dockerplugin.cpp", - "dockerplugin.h", "dockersettings.cpp", "dockersettings.h", "kitdetector.cpp", diff --git a/src/plugins/docker/dockerapi.cpp b/src/plugins/docker/dockerapi.cpp index baa9ed6be9f..900e5bb18b2 100644 --- a/src/plugins/docker/dockerapi.cpp +++ b/src/plugins/docker/dockerapi.cpp @@ -22,8 +22,7 @@ namespace Docker::Internal { DockerApi *s_instance{nullptr}; -DockerApi::DockerApi(DockerSettings *settings) - : m_settings(settings) +DockerApi::DockerApi() { s_instance = this; } @@ -103,7 +102,79 @@ std::optional<bool> DockerApi::isDockerDaemonAvailable(bool async) FilePath DockerApi::dockerClient() { - return m_settings->dockerBinaryPath(); + return settings().dockerBinaryPath(); +} + +QFuture<Utils::expected_str<QList<Network>>> DockerApi::networks() +{ + return Utils::asyncRun([this]() -> Utils::expected_str<QList<Network>> { + QList<Network> result; + + Process process; + FilePath dockerExe = dockerClient(); + if (dockerExe.isEmpty() || !dockerExe.isExecutableFile()) + return make_unexpected(Tr::tr("Docker executable not found")); + + process.setCommand( + CommandLine(dockerExe, QStringList{"network", "ls", "--format", "{{json .}}"})); + process.runBlocking(); + + if (process.result() != ProcessResult::FinishedWithSuccess) { + return make_unexpected( + Tr::tr("Failed to retrieve docker networks. Exit code: %1. Error: %2") + .arg(process.exitCode()) + .arg(process.allOutput())); + } + + for (const auto &line : process.readAllStandardOutput().split('\n')) { + if (line.isEmpty()) + continue; + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(line.toUtf8(), &error); + + if (error.error != QJsonParseError::NoError) { + qCWarning(dockerApiLog) + << "Failed to parse docker network info:" << error.errorString(); + continue; + } + + Network network; + network.id = doc["ID"].toString(); + network.name = doc["Name"].toString(); + network.driver = doc["Driver"].toString(); + network.scope = doc["Scope"].toString(); + network.internal = doc["Internal"].toString() == "true"; + network.ipv6 = doc["IPv6"].toString() == "true"; + network.createdAt = QDateTime::fromString(doc["CreatedAt"].toString(), Qt::ISODate); + network.labels = doc["Labels"].toString(); + + result.append(network); + } + + return result; + }); +} + +QString Network::toString() const +{ + return QString(R"(ID: "%1" +Name: "%2" +Driver: "%3" +Scope: "%4" +Internal: "%5" +IPv6: "%6" +CreatedAt: "%7" +Labels: "%8" + )") + .arg(id) + .arg(name) + .arg(driver) + .arg(scope) + .arg(internal) + .arg(ipv6) + .arg(createdAt.toString(Qt::ISODate)) + .arg(labels); } } // Docker::Internal diff --git a/src/plugins/docker/dockerapi.h b/src/plugins/docker/dockerapi.h index f422e542eb7..3e697654a3a 100644 --- a/src/plugins/docker/dockerapi.h +++ b/src/plugins/docker/dockerapi.h @@ -5,6 +5,7 @@ #include "dockersettings.h" +#include <utils/expected.h> #include <utils/filepath.h> #include <utils/guard.h> @@ -15,18 +16,33 @@ namespace Docker::Internal { +struct Network +{ + QString id; + QString name; + QString driver; + QString scope; + bool internal; + bool ipv6; + QDateTime createdAt; + QString labels; + + QString toString() const; +}; + class DockerApi : public QObject { Q_OBJECT public: - DockerApi(DockerSettings *settings); + DockerApi(); static DockerApi *instance(); bool canConnect(); void checkCanConnect(bool async = true); static void recheckDockerDaemon(); + QFuture<Utils::expected_str<QList<Network>>> networks(); signals: void dockerDaemonAvailableChanged(); @@ -40,7 +56,6 @@ private: std::optional<bool> m_dockerDaemonAvailable; QMutex m_daemonCheckGuard; - DockerSettings *m_settings; }; } // Docker::Internal diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 6c3c71d5f7e..7dc014eadaf 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -18,7 +18,7 @@ #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/devicesupport/idevicewidget.h> #include <projectexplorer/devicesupport/processlist.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorertr.h> @@ -27,12 +27,14 @@ #include <projectexplorer/toolchainmanager.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionfactory.h> #include <qtsupport/qtversionmanager.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/basetreeview.h> +#include <utils/clangutils.h> #include <utils/devicefileaccess.h> #include <utils/deviceshell.h> #include <utils/environment.h> @@ -63,10 +65,12 @@ #include <QHeaderView> #include <QHostAddress> #include <QLoggingCategory> +#include <QMessageBox> #include <QNetworkInterface> #include <QPushButton> #include <QRandomGenerator> #include <QRegularExpression> +#include <QStandardItem> #include <QTextBrowser> #include <QThread> #include <QToolButton> @@ -87,20 +91,29 @@ Q_LOGGING_CATEGORY(dockerDeviceLog, "qtc.docker.device", QtWarningMsg); namespace Docker::Internal { -class ContainerShell : public Utils::DeviceShell +const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId"; +const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo"; +const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag"; +const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid"; +const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths"; +const char DockerDeviceKeepEntryPoint[] = "DockerDeviceKeepEntryPoint"; +const char DockerDeviceEnableLldbFlags[] = "DockerDeviceEnableLldbFlags"; +const char DockerDeviceClangDExecutable[] = "DockerDeviceClangDExecutable"; +const char DockerDeviceExtraArgs[] = "DockerDeviceExtraCreateArguments"; + +class ContainerShell : public DeviceShell { public: - ContainerShell(DockerSettings *settings, const QString &containerId, const FilePath &devicePath) - : m_settings(settings) - , m_containerId(containerId) + ContainerShell(const QString &containerId, const FilePath &devicePath) + : m_containerId(containerId) , m_devicePath(devicePath) {} private: void setupShellProcess(Process *shellProcess) final { - shellProcess->setCommand({m_settings->dockerBinaryPath(), - {"container", "start", "-i", "-a", m_containerId}}); + shellProcess->setCommand( + {settings().dockerBinaryPath(), {"container", "start", "-i", "-a", m_containerId}}); } CommandLine createFallbackCommand(const CommandLine &cmdLine) @@ -111,7 +124,6 @@ private: } private: - DockerSettings *m_settings; QString m_containerId; FilePath m_devicePath; }; @@ -123,42 +135,187 @@ public: : m_dev(dev) {} - RunResult runInShell(const CommandLine &cmdLine, - const QByteArray &stdInData) const override; + RunResult runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const override; QString mapToDevicePath(const QString &hostPath) const override; DockerDevicePrivate *m_dev = nullptr; }; +void DockerDeviceSettings::fromMap(const Store &map) +{ + DeviceSettings::fromMap(map); + + // This is the only place where we can correctly set the default name. + // Only here do we know the image id and the repo reliably, no matter + // where or how we were created. + if (displayName.value() == displayName.defaultValue()) { + displayName.setDefaultValue( + Tr::tr("Docker Image \"%1\" (%2)").arg(repoAndTag()).arg(imageId.value())); + } +} + +DockerDeviceSettings::DockerDeviceSettings() +{ + imageId.setSettingsKey(DockerDeviceDataImageIdKey); + imageId.setLabelText(Tr::tr("Image ID:")); + imageId.setReadOnly(true); + + repo.setSettingsKey(DockerDeviceDataRepoKey); + repo.setLabelText(Tr::tr("Repository:")); + repo.setReadOnly(true); + + tag.setSettingsKey(DockerDeviceDataTagKey); + tag.setLabelText(Tr::tr("Tag:")); + tag.setReadOnly(true); + + useLocalUidGid.setSettingsKey(DockerDeviceUseOutsideUser); + useLocalUidGid.setLabelText(Tr::tr("Run as outside user:")); + useLocalUidGid.setDefaultValue(true); + useLocalUidGid.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); + + keepEntryPoint.setSettingsKey(DockerDeviceKeepEntryPoint); + keepEntryPoint.setLabelText(Tr::tr("Do not modify entry point:")); + keepEntryPoint.setDefaultValue(false); + keepEntryPoint.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); + + enableLldbFlags.setSettingsKey(DockerDeviceEnableLldbFlags); + enableLldbFlags.setLabelText(Tr::tr("Enable flags needed for LLDB:")); + enableLldbFlags.setDefaultValue(false); + enableLldbFlags.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); + + mounts.setSettingsKey(DockerDeviceMappedPaths); + mounts.setLabelText(Tr::tr("Paths to mount:")); + mounts.setDefaultValue({Core::DocumentManager::projectsDirectory().toString()}); + mounts.setToolTip(Tr::tr("Maps paths in this list one-to-one to the docker container.")); + mounts.setPlaceHolderText(Tr::tr("Host directories to mount into the container.")); + + extraArgs.setSettingsKey(DockerDeviceExtraArgs); + extraArgs.setLabelText(Tr::tr("Extra arguments:")); + extraArgs.setToolTip(Tr::tr("Extra arguments to pass to docker create.")); + extraArgs.setDisplayStyle(StringAspect::LineEditDisplay); + + clangdExecutable.setSettingsKey(DockerDeviceClangDExecutable); + clangdExecutable.setLabelText(Tr::tr("Clangd Executable:")); + clangdExecutable.setAllowPathFromDevice(true); + + network.setSettingsKey("Network"); + network.setLabelText(Tr::tr("Network:")); + network.setDefaultValue("bridge"); + network.setFillCallback([this](const StringSelectionAspect::ResultCallback &cb) { + auto future = DockerApi::instance()->networks(); + + auto watcher = new QFutureWatcher<expected_str<QList<Network>>>(this); + watcher->setFuture(future); + QObject::connect(watcher, + &QFutureWatcher<expected_str<QList<Network>>>::finished, + this, + [watcher, cb]() { + expected_str<QList<Network>> result = watcher->result(); + if (result) { + auto items = transform(*result, [](const Network &network) { + QStandardItem *item = new QStandardItem(network.name); + item->setData(network.name); + item->setToolTip(network.toString()); + return item; + }); + cb(items); + } else { + QStandardItem *errorItem = new QStandardItem(Tr::tr("Error")); + errorItem->setToolTip(result.error()); + cb({errorItem}); + } + }); + }); + + connect(DockerApi::instance(), + &DockerApi::dockerDaemonAvailableChanged, + &network, + &StringSelectionAspect::refill); + + clangdExecutable.setValidationFunction( + [this](const QString &newValue) -> FancyLineEdit::AsyncValidationFuture { + const FilePath rootPath = FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, + repoAndTagEncoded(), + u"/"); + return asyncRun([rootPath, newValue]() -> expected_str<QString> { + QString changedValue = newValue; + FilePath path = FilePath::fromUserInput(newValue); + if (!path.needsDevice()) { + const FilePath onDevicePath = rootPath.withNewMappedPath(path); + if (onDevicePath.exists()) { + changedValue = onDevicePath.toUserOutput(); + path = onDevicePath; + } else { + return make_unexpected( + Tr::tr("The path \"%1\" does not exist.").arg(onDevicePath.toUserOutput())); + } + } + QString error; + bool result = checkClangdVersion(path, &error); + if (!result) + return make_unexpected(error); + return changedValue; + }); + }); + + containerStatus.setText(Tr::tr("stopped")); +} + +// Used for "docker run" +QString DockerDeviceSettings::repoAndTag() const +{ + if (repo() == "<none>") + return imageId(); + + if (tag() == "<none>") + return repo(); + + return repo() + ':' + tag(); +} + +QString DockerDeviceSettings::repoAndTagEncoded() const +{ + return repoAndTag().replace(':', '.'); +} + +FilePath DockerDeviceSettings::rootPath() const +{ + return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, repoAndTagEncoded(), u"/"); +} + class DockerDevicePrivate : public QObject { public: - DockerDevicePrivate(DockerDevice *parent, DockerSettings *settings, DockerDeviceData data) + DockerDevicePrivate(DockerDevice *parent) : q(parent) - , m_data(std::move(data)) - , m_settings(settings) - {} + , deviceSettings(static_cast<DockerDeviceSettings *>(q->settings())) + { + QObject::connect(deviceSettings, &DockerDeviceSettings::applied, this, [this] { + if (!m_container.isEmpty()) { + stopCurrentContainer(); + } + }); + } ~DockerDevicePrivate() { stopCurrentContainer(); } + CommandLine createCommandLine(); + RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool updateContainerAccess(); + expected_str<void> updateContainerAccess(); void changeMounts(QStringList newMounts); bool ensureReachable(const FilePath &other); void shutdown(); expected_str<FilePath> localSource(const FilePath &other) const; QString containerId() { return m_container; } - DockerDeviceData data() { return m_data; } - void setData(const DockerDeviceData &data); - DockerSettings *settings() { return m_settings; } - QString repoAndTag() const { return m_data.repoAndTag(); } - QString repoAndTagEncoded() const { return m_data.repoAndTagEncoded(); } - QString dockerImageId() const { return m_data.imageId; } + QString repoAndTag() const { return deviceSettings->repoAndTag(); } + QString repoAndTagEncoded() const { return deviceSettings->repoAndTagEncoded(); } + QString dockerImageId() const { return deviceSettings->imageId(); } - Environment environment(); + expected_str<Environment> environment(); CommandLine withDockerExecCmd(const CommandLine &cmd, const std::optional<Environment> &env = std::nullopt, @@ -170,16 +327,18 @@ public: bool prepareForBuild(const Target *target); Tasks validateMounts() const; - bool createContainer(); - bool startContainer(); + expected_str<QString> createContainer(); + expected_str<void> startContainer(); void stopCurrentContainer(); - void fetchSystemEnviroment(); + expected_str<void> fetchSystemEnviroment(); std::optional<FilePath> clangdExecutable() const { - if (m_data.clangdExecutable.isEmpty()) + if (deviceSettings->clangdExecutable().isEmpty()) return std::nullopt; - return m_data.clangdExecutable; + if (!deviceSettings->clangdExecutable().needsDevice()) + return deviceSettings->rootPath().withNewMappedPath(deviceSettings->clangdExecutable()); + return deviceSettings->clangdExecutable(); } bool addTemporaryMount(const FilePath &path, const FilePath &containerPath); @@ -189,8 +348,7 @@ public: bool isImageAvailable() const; DockerDevice *const q; - DockerDeviceData m_data; - DockerSettings *m_settings; + DockerDeviceSettings *deviceSettings; struct TemporaryMountInfo { @@ -200,6 +358,7 @@ public: QList<TemporaryMountInfo> m_temporaryMounts; + QMutex m_shellMutex; std::unique_ptr<ContainerShell> m_shell; QString m_container; @@ -209,7 +368,7 @@ public: DockerDeviceFileAccess m_fileAccess{this}; }; -class DockerProcessImpl : public Utils::ProcessInterface +class DockerProcessImpl : public ProcessInterface { public: DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePrivate *devicePrivate); @@ -282,7 +441,7 @@ DockerProcessImpl::DockerProcessImpl(IDevice::ConstPtr device, DockerDevicePriva qCDebug(dockerDeviceLog) << "Process exited:" << m_process.commandLine() << "with code:" << m_process.resultData().m_exitCode; - Utils::ProcessResultData resultData = m_process.resultData(); + ProcessResultData resultData = m_process.resultData(); if (m_remotePID == 0 && !m_hasReceivedFirstOutput) { resultData.m_error = QProcess::FailedToStart; @@ -368,6 +527,11 @@ void DockerProcessImpl::sendControlSignal(ControlSignal controlSignal) } } +CommandLine DockerDevice::createCommandLine() const +{ + return d->createCommandLine(); +} + IDeviceWidget *DockerDevice::createWidget() { return new DockerDeviceWidget(sharedFromThis()); @@ -382,11 +546,10 @@ Tasks DockerDevicePrivate::validateMounts() const { Tasks result; - for (const QString &mount : m_data.mounts) { - const FilePath path = FilePath::fromUserInput(mount); - if (!path.isDir()) { + for (const FilePath &mount : deviceSettings->mounts()) { + if (!mount.isDir()) { const QString message = Tr::tr("Path \"%1\" is not a directory or does not exist.") - .arg(mount); + .arg(mount.toUserOutput()); result.append(Task(Task::Error, message, {}, -1, {})); } @@ -414,45 +577,55 @@ QString DockerDeviceFileAccess::mapToDevicePath(const QString &hostPath) const return newPath; } -DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data) - : d(new DockerDevicePrivate(this, settings, data)) +DockerDevice::DockerDevice(std::unique_ptr<DockerDeviceSettings> deviceSettings) + : ProjectExplorer::IDevice(std::move(deviceSettings)) + , d(new DockerDevicePrivate(this)) { setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Docker")); setOsType(OsTypeLinux); - setDefaultDisplayName(Tr::tr("Docker Image")); setupId(IDevice::ManuallyAdded); setType(Constants::DOCKER_DEVICE_TYPE); setMachineType(IDevice::Hardware); - setDisplayName(Tr::tr("Docker Image \"%1\" (%2)").arg(data.repoAndTag()).arg(data.imageId)); setAllowEmptyCommand(true); - setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { + setOpenTerminal([this](const Environment &env, + const FilePath &workingDir) -> expected_str<void> { Q_UNUSED(env); // TODO: That's the runnable's environment in general. Use it via -e below. - if (!updateContainerAccess()) - return; - if (d->containerId().isEmpty()) { - MessageManager::writeDisrupting(Tr::tr("Error starting remote shell. No container.")); - return; - } + expected_str<void> result = d->updateContainerAccess(); + + if (!result) + return result; + + if (d->containerId().isEmpty()) + return make_unexpected(Tr::tr("Error starting remote shell. No container.")); + + expected_str<FilePath> shell = Terminal::defaultShellForDevice(rootPath()); + if (!shell) + return make_unexpected(shell.error()); Process proc; proc.setTerminalMode(TerminalMode::Detached); proc.setEnvironment(env); proc.setWorkingDirectory(workingDir); - proc.setCommand({Terminal::defaultShellForDevice(rootPath()), {}}); + proc.setCommand({*shell, {}}); proc.start(); - if (proc.error() != QProcess::UnknownError && MessageManager::instance()) { - MessageManager::writeDisrupting( - Tr::tr("Error starting remote shell: %1").arg(proc.errorString())); - } + return {}; }); - addDeviceAction({Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) { - device->openTerminal(device->systemEnvironment(), FilePath()); - }}); + addDeviceAction( + {Tr::tr("Open Shell in Container"), [](const IDevice::Ptr &device, QWidget *) { + expected_str<Environment> env = device->systemEnvironmentWithError(); + if (!env) { + QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error"), env.error()); + return; + } + expected_str<void> result = device->openTerminal(*env, FilePath()); + if (!result) + QMessageBox::warning(ICore::dialogParent(), Tr::tr("Error"), result.error()); + }}); } DockerDevice::~DockerDevice() @@ -465,22 +638,7 @@ void DockerDevice::shutdown() d->shutdown(); } -const DockerDeviceData DockerDevice::data() const -{ - return d->data(); -} - -DockerDeviceData DockerDevice::data() -{ - return d->data(); -} - -void DockerDevice::setData(const DockerDeviceData &data) -{ - d->setData(data); -} - -bool DockerDevice::updateContainerAccess() const +expected_str<void> DockerDevice::updateContainerAccess() const { return d->updateContainerAccess(); } @@ -492,13 +650,10 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, bool withPty, bool withMarker) { - if (!m_settings) - return {}; - if (!updateContainerAccess()) return {}; - CommandLine dockerCmd{m_settings->dockerBinaryPath(), {"exec"}}; + CommandLine dockerCmd{settings().dockerBinaryPath(), {"exec"}}; if (interactive) dockerCmd.addArg("-i"); @@ -538,13 +693,13 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, void DockerDevicePrivate::stopCurrentContainer() { - if (!m_settings) - return; if (m_container.isEmpty()) return; + if (!DockerApi::isDockerDaemonAvailable(false).value_or(false)) return; + QMutexLocker lk(&m_shellMutex); if (m_shell) { // We have to disconnect the shell from the device, otherwise it will try to // tell us about the container being stopped. Since that signal is emitted in a different @@ -555,7 +710,7 @@ void DockerDevicePrivate::stopCurrentContainer() } Process proc; - proc.setCommand({m_settings->dockerBinaryPath(), {"container", "stop", m_container}}); + proc.setCommand({settings().dockerBinaryPath(), {"container", "stop", m_container}}); m_container.clear(); @@ -611,32 +766,32 @@ QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi) expected_str<void> isValidMountInfo(const DockerDevicePrivate::TemporaryMountInfo &mi) { if (mi.path.needsDevice()) - return make_unexpected(QString("Path \"%1\" is not local").arg(mi.path.toUserOutput())); + return make_unexpected(QString("The path \"%1\" is not local.").arg(mi.path.toUserOutput())); if (mi.path.isEmpty() && mi.containerPath.isEmpty()) - return make_unexpected(QString("Both paths are empty")); + return make_unexpected(QString("Both paths are empty.")); if (mi.path.isEmpty()) { - return make_unexpected(QString("Local path is empty, container path is \"%1\"") + return make_unexpected(QString("The local path is empty, the container path is \"%1\".") .arg(mi.containerPath.toUserOutput())); } if (mi.containerPath.isEmpty()) { return make_unexpected( - QString("Container path is empty, local path is \"%1\"").arg(mi.path.toUserOutput())); + QString("The container path is empty, the local path is \"%1\".").arg(mi.path.toUserOutput())); } if (!mi.path.isAbsolutePath() || !mi.containerPath.isAbsolutePath()) { - return make_unexpected(QString("Path \"%1\" or \"%2\" is not absolute") + return make_unexpected(QString("The path \"%1\" or \"%2\" is not absolute.") .arg(mi.path.toUserOutput()) .arg(mi.containerPath.toUserOutput())); } if (mi.containerPath.isRootPath()) - return make_unexpected(QString("Path \"%1\" is root").arg(mi.containerPath.toUserOutput())); + return make_unexpected(QString("The path \"%1\" is root.").arg(mi.containerPath.toUserOutput())); if (!mi.path.exists()) - return make_unexpected(QString("Path \"%1\" does not exist").arg(mi.path.toUserOutput())); + return make_unexpected(QString("The path \"%1\" does not exist.").arg(mi.path.toUserOutput())); return {}; } @@ -645,8 +800,8 @@ QStringList DockerDevicePrivate::createMountArgs() const { QStringList cmds; QList<TemporaryMountInfo> mounts = m_temporaryMounts; - for (const QString &m : m_data.mounts) - mounts.append({FilePath::fromUserInput(m), FilePath::fromUserInput(m)}); + for (const FilePath &m : deviceSettings->mounts()) + mounts.append({m, m}); for (const TemporaryMountInfo &mi : mounts) { if (isValidMountInfo(mi)) @@ -660,81 +815,92 @@ bool DockerDevicePrivate::isImageAvailable() const { Process proc; proc.setCommand( - {m_settings->dockerBinaryPath(), - {"image", "list", m_data.repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}}); + {settings().dockerBinaryPath(), + {"image", "list", deviceSettings->repoAndTag(), "--format", "{{.Repository}}:{{.Tag}}"}}); proc.runBlocking(); if (proc.result() != ProcessResult::FinishedWithSuccess) return false; - if (proc.stdOut().trimmed() == m_data.repoAndTag()) + if (proc.stdOut().trimmed() == deviceSettings->repoAndTag()) return true; return false; } -bool DockerDevicePrivate::createContainer() +CommandLine DockerDevicePrivate::createCommandLine() { - if (!m_settings) - return false; - - if (!isImageAvailable()) - return false; - const QString display = HostOsInfo::isLinuxHost() ? QString(":0") : QString("host.docker.internal:0"); - CommandLine dockerCreate{m_settings->dockerBinaryPath(), + CommandLine dockerCreate{settings().dockerBinaryPath(), {"create", "-i", "--rm", "-e", QString("DISPLAY=%1").arg(display), "-e", - "XAUTHORITY=/.Xauthority", - "--net", - "host"}}; + "XAUTHORITY=/.Xauthority"}}; #ifdef Q_OS_UNIX // no getuid() and getgid() on Windows. - if (m_data.useLocalUidGid) + if (deviceSettings->useLocalUidGid()) dockerCreate.addArgs({"-u", QString("%1:%2").arg(getuid()).arg(getgid())}); #endif + if (!deviceSettings->network().isEmpty()) { + dockerCreate.addArg("--network"); + dockerCreate.addArg(deviceSettings->network()); + } + dockerCreate.addArgs(createMountArgs()); - if (!m_data.keepEntryPoint) + if (!deviceSettings->keepEntryPoint()) dockerCreate.addArgs({"--entrypoint", "/bin/sh"}); - if (m_data.enableLldbFlags) + if (deviceSettings->enableLldbFlags()) dockerCreate.addArgs({"--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"}); - dockerCreate.addArg(m_data.repoAndTag()); + dockerCreate.addArgs(deviceSettings->extraArgs(), CommandLine::Raw); - qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << dockerCreate.toUserOutput(); + dockerCreate.addArg(deviceSettings->repoAndTag()); + + return dockerCreate; +} + +expected_str<QString> DockerDevicePrivate::createContainer() +{ + if (!isImageAvailable()) + return make_unexpected(Tr::tr("Image \"%1\" is not available.").arg(repoAndTag())); + + const CommandLine cmdLine = createCommandLine(); + + qCDebug(dockerDeviceLog).noquote() << "RUNNING: " << cmdLine.toUserOutput(); Process createProcess; - createProcess.setCommand(dockerCreate); + createProcess.setCommand(cmdLine); createProcess.runBlocking(); if (createProcess.result() != ProcessResult::FinishedWithSuccess) { - qCWarning(dockerDeviceLog) << "Failed creating docker container:"; - qCWarning(dockerDeviceLog) << "Exit Code:" << createProcess.exitCode(); - qCWarning(dockerDeviceLog) << createProcess.allOutput(); - return false; + return make_unexpected(Tr::tr("Failed creating Docker container. Exit code: %1, output: %2") + .arg(createProcess.exitCode()) + .arg(createProcess.allOutput())); } m_container = createProcess.cleanedStdOut().trimmed(); if (m_container.isEmpty()) - return false; + return make_unexpected( + Tr::tr("Failed creating Docker container. No container ID received.")); qCDebug(dockerDeviceLog) << "ContainerId:" << m_container; - return true; + return m_container; } -bool DockerDevicePrivate::startContainer() +expected_str<void> DockerDevicePrivate::startContainer() { - if (!createContainer()) - return false; + auto createResult = createContainer(); + if (!createResult) + return make_unexpected(createResult.error()); - m_shell = std::make_unique<ContainerShell>(m_settings, m_container, q->rootPath()); + QMutexLocker lk(&m_shellMutex); + m_shell = std::make_unique<ContainerShell>(m_container, q->rootPath()); connect(m_shell.get(), &DeviceShell::done, this, [this](const ProcessResultData &resultData) { if (m_shell) @@ -747,33 +913,48 @@ bool DockerDevicePrivate::startContainer() qCWarning(dockerDeviceLog) << "Container shell encountered error:" << resultData.m_error; DockerApi::recheckDockerDaemon(); + //: %1 is the application name (Qt Creator) MessageManager::writeFlashing(Tr::tr("Docker daemon appears to be not running. " "Verify daemon is up and running and reset the " "Docker daemon in Docker device preferences " - "or restart Qt Creator.")); + "or restart %1.") + .arg(QGuiApplication::applicationDisplayName())); }); - QTC_ASSERT(m_shell, return false); + QTC_ASSERT(m_shell, + return make_unexpected(Tr::tr("Failed to create container shell (Out of memory)."))); - if (m_shell->start()) - return true; - - qCWarning(dockerDeviceLog) << "Container shell failed to start"; - return false; + return m_shell->start(); } -bool DockerDevicePrivate::updateContainerAccess() +expected_str<void> DockerDevicePrivate::updateContainerAccess() { + { + QMutexLocker lk(&m_shellMutex); + if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) + return {}; + } + + if (QThread::currentThread() != thread()) { + expected_str<void> result; + return make_unexpected(Tr::tr("Cannot start docker device from non-main thread")); + } + if (m_isShutdown) - return false; + return make_unexpected(Tr::tr("Device is shut down")); if (DockerApi::isDockerDaemonAvailable(false).value_or(false) == false) - return false; + return make_unexpected(Tr::tr("Docker system is not reachable")); - if (m_shell) - return true; + expected_str<void> result = startContainer(); + if (result) { + deviceSettings->containerStatus.setText(Tr::tr("Running")); + return result; + } - return startContainer(); + const QString error = QString("Failed to start container: %1").arg(result.error()); + deviceSettings->containerStatus.setText(result.error().trimmed()); + return make_unexpected(error); } void DockerDevice::setMounts(const QStringList &mounts) const @@ -781,47 +962,16 @@ void DockerDevice::setMounts(const QStringList &mounts) const d->changeMounts(mounts); } -const char DockerDeviceDataImageIdKey[] = "DockerDeviceDataImageId"; -const char DockerDeviceDataRepoKey[] = "DockerDeviceDataRepo"; -const char DockerDeviceDataTagKey[] = "DockerDeviceDataTag"; -const char DockerDeviceDataSizeKey[] = "DockerDeviceDataSize"; -const char DockerDeviceUseOutsideUser[] = "DockerDeviceUseUidGid"; -const char DockerDeviceMappedPaths[] = "DockerDeviceMappedPaths"; -const char DockerDeviceKeepEntryPoint[] = "DockerDeviceKeepEntryPoint"; -const char DockerDeviceEnableLldbFlags[] = "DockerDeviceEnableLldbFlags"; -const char DockerDeviceClangDExecutable[] = "DockerDeviceClangDExecutable"; - -void DockerDevice::fromMap(const QVariantMap &map) +void DockerDevice::fromMap(const Store &map) { ProjectExplorer::IDevice::fromMap(map); - DockerDeviceData data; - - data.repo = map.value(DockerDeviceDataRepoKey).toString(); - data.tag = map.value(DockerDeviceDataTagKey).toString(); - data.imageId = map.value(DockerDeviceDataImageIdKey).toString(); - data.size = map.value(DockerDeviceDataSizeKey).toString(); - data.useLocalUidGid = map.value(DockerDeviceUseOutsideUser, HostOsInfo::isLinuxHost()).toBool(); - data.mounts = map.value(DockerDeviceMappedPaths).toStringList(); - data.keepEntryPoint = map.value(DockerDeviceKeepEntryPoint).toBool(); - data.enableLldbFlags = map.value(DockerDeviceEnableLldbFlags).toBool(); - data.clangdExecutable = FilePath::fromSettings(map.value(DockerDeviceClangDExecutable)); - d->setData(data); + d->deviceSettings->fromMap(map); } -QVariantMap DockerDevice::toMap() const +Store DockerDevice::toMap() const { - QVariantMap map = ProjectExplorer::IDevice::toMap(); - DockerDeviceData data = d->data(); - - map.insert(DockerDeviceDataRepoKey, data.repo); - map.insert(DockerDeviceDataTagKey, data.tag); - map.insert(DockerDeviceDataImageIdKey, data.imageId); - map.insert(DockerDeviceDataSizeKey, data.size); - map.insert(DockerDeviceUseOutsideUser, data.useLocalUidGid); - map.insert(DockerDeviceMappedPaths, data.mounts); - map.insert(DockerDeviceKeepEntryPoint, data.keepEntryPoint); - map.insert(DockerDeviceEnableLldbFlags, data.enableLldbFlags); - map.insert(DockerDeviceClangDExecutable, data.clangdExecutable.toSettings()); + Store map = ProjectExplorer::IDevice::toMap(); + d->deviceSettings->toMap(map); return map; } @@ -830,11 +980,6 @@ ProcessInterface *DockerDevice::createProcessInterface() const return new DockerProcessImpl(this->sharedFromThis(), d); } -DeviceProcessList *DockerDevice::createProcessListModel(QObject *parent) const -{ - return new ProcessList(sharedFromThis(), parent); -} - DeviceTester *DockerDevice::createDeviceTester() const { return nullptr; @@ -850,19 +995,11 @@ FilePath DockerDevice::filePath(const QString &pathOnDevice) const return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, d->repoAndTagEncoded(), pathOnDevice); - -// The following would work, but gives no hint on repo and tag -// result.setScheme("docker"); -// result.setHost(d->m_data.imageId); - -// The following would work, but gives no hint on repo, tag and imageid -// result.setScheme("device"); -// result.setHost(id().toString()); } -Utils::FilePath DockerDevice::rootPath() const +FilePath DockerDevice::rootPath() const { - return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, d->repoAndTagEncoded(), u"/"); + return d->deviceSettings->rootPath(); } bool DockerDevice::handlesFile(const FilePath &filePath) const @@ -900,12 +1037,12 @@ bool DockerDevice::ensureReachable(const FilePath &other) const return d->ensureReachable(other.parentDir()); } -expected_str<FilePath> DockerDevice::localSource(const Utils::FilePath &other) const +expected_str<FilePath> DockerDevice::localSource(const FilePath &other) const { return d->localSource(other); } -Environment DockerDevice::systemEnvironment() const +expected_str<Environment> DockerDevice::systemEnvironmentWithError() const { return d->environment(); } @@ -916,29 +1053,34 @@ void DockerDevice::aboutToBeRemoved() const detector.undoAutoDetect(id().toString()); } -void DockerDevicePrivate::fetchSystemEnviroment() +expected_str<void> DockerDevicePrivate::fetchSystemEnviroment() { - if (!updateContainerAccess()) - return; + expected_str<void> result = updateContainerAccess(); + if (!result) + return result; + + QString stdErr; if (m_shell && m_shell->state() == DeviceShell::State::Succeeded) { const RunResult result = runInShell({"env", {}}); const QString out = QString::fromUtf8(result.stdOut); m_cachedEnviroment = Environment(out.split('\n', Qt::SkipEmptyParts), q->osType()); - return; + stdErr = QString::fromUtf8(result.stdErr); + } else { + Process proc; + proc.setCommand(withDockerExecCmd({"env", {}})); + proc.start(); + proc.waitForFinished(); + const QString remoteOutput = proc.cleanedStdOut(); + + m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); + stdErr = proc.cleanedStdErr(); } - Process proc; - proc.setCommand(withDockerExecCmd({"env", {}})); - proc.start(); - proc.waitForFinished(); - const QString remoteOutput = proc.cleanedStdOut(); + if (stdErr.isEmpty()) + return {}; - m_cachedEnviroment = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); - - const QString remoteError = proc.cleanedStdErr(); - if (!remoteError.isEmpty()) - qCWarning(dockerDeviceLog) << "Cannot read container environment:", qPrintable(remoteError); + return make_unexpected("Could not read container environment: " + stdErr); } RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArray &stdInData) @@ -951,7 +1093,7 @@ RunResult DockerDevicePrivate::runInShell(const CommandLine &cmd, const QByteArr // Factory -class DockerImageItem final : public TreeItem, public DockerDeviceData +class DockerImageItem final : public TreeItem { public: DockerImageItem() {} @@ -979,14 +1121,18 @@ public: return QVariant(); } + + QString repo; + QString tag; + QString imageId; + QString size; }; class DockerDeviceSetupWizard final : public QDialog { public: - DockerDeviceSetupWizard(DockerSettings *settings) + DockerDeviceSetupWizard() : QDialog(ICore::dialogParent()) - , m_settings(settings) { setWindowTitle(Tr::tr("Docker Image Selection")); resize(800, 600); @@ -1033,7 +1179,7 @@ public: const QString fail = QString{"Docker: "} + ::ProjectExplorer::Tr::tr("The process failed to start."); - auto errorLabel = new Utils::InfoLabel(fail, Utils::InfoLabel::Error, this); + auto errorLabel = new InfoLabel(fail, InfoLabel::Error, this); errorLabel->setVisible(false); m_buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -1055,9 +1201,9 @@ public: connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); m_buttons->button(QDialogButtonBox::Ok)->setEnabled(false); - CommandLine cmd{m_settings->dockerBinaryPath(), + CommandLine cmd{settings().dockerBinaryPath(), {"images", "--format", "{{.ID}}\\t{{.Repository}}\\t{{.Tag}}\\t{{.Size}}"}}; - m_log->append(Tr::tr("Running \"%1\"\n").arg(cmd.toUserOutput())); + m_log->append(Tr::tr("Running \"%1\"").arg(cmd.toUserOutput()) + "\n"); m_process = new Process(this); m_process->setCommand(cmd); @@ -1081,7 +1227,7 @@ public: m_log->append(Tr::tr("Done.")); }); - connect(m_process, &Utils::Process::readyReadStandardError, this, [this] { + connect(m_process, &Process::readyReadStandardError, this, [this] { const QString out = Tr::tr("Error: %1").arg(m_process->cleanedStdErr()); m_log->append(Tr::tr("Error: %1").arg(out)); }); @@ -1111,7 +1257,12 @@ public: m_proxyModel->mapToSource(selectedRows.front())); QTC_ASSERT(item, return {}); - auto device = DockerDevice::create(m_settings, *item); + auto deviceSettings = std::make_unique<DockerDeviceSettings>(); + deviceSettings->repo.setValue(item->repo); + deviceSettings->tag.setValue(item->tag); + deviceSettings->imageId.setValue(item->imageId); + + auto device = DockerDevice::create(std::move(deviceSettings)); return device; } @@ -1122,7 +1273,6 @@ public: SortFilterModel *m_proxyModel = nullptr; QTextBrowser *m_log = nullptr; QDialogButtonBox *m_buttons; - DockerSettings *m_settings; Process *m_process = nullptr; QString m_selectedId; @@ -1130,19 +1280,19 @@ public: // Factory -DockerDeviceFactory::DockerDeviceFactory(DockerSettings *settings) +DockerDeviceFactory::DockerDeviceFactory() : IDeviceFactory(Constants::DOCKER_DEVICE_TYPE) { setDisplayName(Tr::tr("Docker Device")); setIcon(QIcon()); - setCreator([settings] { - DockerDeviceSetupWizard wizard(settings); + setCreator([] { + DockerDeviceSetupWizard wizard; if (wizard.exec() != QDialog::Accepted) return IDevice::Ptr(); return wizard.device(); }); - setConstructionFunction([settings, this] { - auto device = DockerDevice::create(settings, {}); + setConstructionFunction([this] { + auto device = DockerDevice::create(std::make_unique<DockerDeviceSettings>()); QMutexLocker lk(&m_deviceListMutex); m_existingDevices.push_back(device); return device; @@ -1168,9 +1318,8 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath if (alreadyAdded) return false; - const bool alreadyManuallyAdded = anyOf(m_data.mounts, [path](const QString &mount) { - return mount == path.path(); - }); + const bool alreadyManuallyAdded = anyOf(deviceSettings->mounts(), + [path](const FilePath &mount) { return mount == path; }); if (alreadyManuallyAdded) return false; @@ -1186,10 +1335,13 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath return true; } -Environment DockerDevicePrivate::environment() +expected_str<Environment> DockerDevicePrivate::environment() { - if (!m_cachedEnviroment) - fetchSystemEnviroment(); + if (!m_cachedEnviroment) { + expected_str<void> result = fetchSystemEnviroment(); + if (!result) + return make_unexpected(result.error()); + } QTC_ASSERT(m_cachedEnviroment, return {}); return m_cachedEnviroment.value(); @@ -1198,15 +1350,14 @@ Environment DockerDevicePrivate::environment() void DockerDevicePrivate::shutdown() { m_isShutdown = true; - m_settings = nullptr; stopCurrentContainer(); } void DockerDevicePrivate::changeMounts(QStringList newMounts) { newMounts.removeDuplicates(); - if (m_data.mounts != newMounts) { - m_data.mounts = newMounts; + if (deviceSettings->mounts.value() != newMounts) { + deviceSettings->mounts.value() = newMounts; stopCurrentContainer(); // Force re-start with new mounts. } } @@ -1221,8 +1372,8 @@ expected_str<FilePath> DockerDevicePrivate::localSource(const FilePath &other) c } } - for (const QString &mount : m_data.mounts) { - const FilePath mountPoint = FilePath::fromString(mount); + for (const FilePath &mount : deviceSettings->mounts()) { + const FilePath mountPoint = mount; if (devicePath.isChildOf(mountPoint)) { const FilePath relativePath = devicePath.relativeChildPath(mountPoint); return mountPoint.pathAppended(relativePath.path()); @@ -1234,12 +1385,14 @@ expected_str<FilePath> DockerDevicePrivate::localSource(const FilePath &other) c bool DockerDevicePrivate::ensureReachable(const FilePath &other) { - for (const QString &mount : m_data.mounts) { - const FilePath fMount = FilePath::fromString(mount); - if (other.isChildOf(fMount)) + if (other.isSameDevice(q->rootPath())) + return true; + + for (const FilePath &mount : deviceSettings->mounts()) { + if (other.isChildOf(mount)) return true; - if (fMount == other) + if (mount == other) return true; } @@ -1261,18 +1414,6 @@ bool DockerDevicePrivate::ensureReachable(const FilePath &other) return true; } -void DockerDevicePrivate::setData(const DockerDeviceData &data) -{ - if (m_data != data) { - m_data = data; - - // Force restart if the container is already running - if (!m_container.isEmpty()) { - stopCurrentContainer(); - } - } -} - bool DockerDevice::prepareForBuild(const Target *target) { return d->prepareForBuild(target); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index bd0ce29d9d4..ab88d0b9d00 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -14,42 +14,29 @@ namespace Docker::Internal { -class DockerDeviceData +class DockerDeviceSettings : public ProjectExplorer::DeviceSettings { public: - bool operator==(const DockerDeviceData &other) const - { - return imageId == other.imageId && repo == other.repo && tag == other.tag - && useLocalUidGid == other.useLocalUidGid && mounts == other.mounts - && keepEntryPoint == other.keepEntryPoint && enableLldbFlags == other.enableLldbFlags - && clangdExecutable == other.clangdExecutable; - } + DockerDeviceSettings(); - bool operator!=(const DockerDeviceData &other) const { return !(*this == other); } + void fromMap(const Utils::Store &map) override; - // Used for "docker run" - QString repoAndTag() const - { - if (repo == "<none>") - return imageId; + QString repoAndTag() const; + QString repoAndTagEncoded() const; + Utils::FilePath rootPath() const; - if (tag == "<none>") - return repo; + Utils::StringAspect imageId{this}; + Utils::StringAspect repo{this}; + Utils::StringAspect tag{this}; + Utils::BoolAspect useLocalUidGid{this}; + Utils::FilePathListAspect mounts{this}; + Utils::BoolAspect keepEntryPoint{this}; + Utils::BoolAspect enableLldbFlags{this}; + Utils::FilePathAspect clangdExecutable{this}; + Utils::StringSelectionAspect network{this}; + Utils::StringAspect extraArgs{this}; - return repo + ':' + tag; - } - - QString repoAndTagEncoded() const { return repoAndTag().replace(':', '.'); } - - QString imageId; - QString repo; - QString tag; - QString size; - bool useLocalUidGid = true; - QStringList mounts = {Core::DocumentManager::projectsDirectory().toString()}; - bool keepEntryPoint = false; - bool enableLldbFlags = false; - Utils::FilePath clangdExecutable; + Utils::TextDisplay containerStatus{this}; }; class DockerDevice : public ProjectExplorer::IDevice @@ -58,23 +45,24 @@ public: using Ptr = QSharedPointer<DockerDevice>; using ConstPtr = QSharedPointer<const DockerDevice>; - explicit DockerDevice(DockerSettings *settings, const DockerDeviceData &data); + explicit DockerDevice(std::unique_ptr<DockerDeviceSettings> settings); ~DockerDevice(); void shutdown(); - static Ptr create(DockerSettings *settings, const DockerDeviceData &data) + static Ptr create(std::unique_ptr<DockerDeviceSettings> settings) { - return Ptr(new DockerDevice(settings, data)); + return Ptr(new DockerDevice(std::move(settings))); } + Utils::CommandLine createCommandLine() const; + ProjectExplorer::IDeviceWidget *createWidget() override; QList<ProjectExplorer::Task> validate() const override; Utils::ProcessInterface *createProcessInterface() const override; bool canCreateProcessModel() const override { return true; } - ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override; bool hasDeviceTester() const override { return false; } ProjectExplorer::DeviceTester *createDeviceTester() const override; bool usableAsBuildDevice() const override; @@ -86,22 +74,17 @@ public: bool ensureReachable(const Utils::FilePath &other) const override; Utils::expected_str<Utils::FilePath> localSource(const Utils::FilePath &other) const override; - Utils::Environment systemEnvironment() const override; + Utils::expected_str<Utils::Environment> systemEnvironmentWithError() const override; - const DockerDeviceData data() const; - DockerDeviceData data(); - - void setData(const DockerDeviceData &data); - - bool updateContainerAccess() const; + Utils::expected_str<void> updateContainerAccess() const; void setMounts(const QStringList &mounts) const; bool prepareForBuild(const ProjectExplorer::Target *target) override; std::optional<Utils::FilePath> clangdExecutable() const override; protected: - void fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Utils::Store &map) final; + Utils::Store toMap() const final; private: void aboutToBeRemoved() const final; @@ -115,7 +98,7 @@ private: class DockerDeviceFactory final : public ProjectExplorer::IDeviceFactory { public: - DockerDeviceFactory(DockerSettings *settings); + DockerDeviceFactory(); void shutdownExistingDevices(); @@ -125,5 +108,3 @@ private: }; } // namespace Docker::Internal - -Q_DECLARE_METATYPE(Docker::Internal::DockerDeviceData) diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index 259325274c0..0e269ee3972 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -9,6 +9,7 @@ #include <utils/algorithm.h> #include <utils/clangutils.h> +#include <utils/commandline.h> #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> @@ -33,22 +34,9 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) auto dockerDevice = device.dynamicCast<DockerDevice>(); QTC_ASSERT(dockerDevice, return); - m_data = dockerDevice->data(); + DockerDeviceSettings *deviceSettings = static_cast<DockerDeviceSettings *>(device->settings()); - auto repoLabel = new QLabel(Tr::tr("Repository:")); - m_repoLineEdit = new QLineEdit; - m_repoLineEdit->setText(m_data.repo); - m_repoLineEdit->setEnabled(false); - - auto tagLabel = new QLabel(Tr::tr("Tag:")); - m_tagLineEdit = new QLineEdit; - m_tagLineEdit->setText(m_data.tag); - m_tagLineEdit->setEnabled(false); - - auto idLabel = new QLabel(Tr::tr("Image ID:")); - m_idLineEdit = new QLineEdit; - m_idLineEdit->setText(m_data.imageId); - m_idLineEdit->setEnabled(false); + using namespace Layouting; auto daemonStateLabel = new QLabel(Tr::tr("Daemon state:")); m_daemonReset = new QToolButton; @@ -67,80 +55,16 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) DockerApi::recheckDockerDaemon(); }); - m_keepEntryPoint = new QCheckBox(Tr::tr("Do not modify entry point")); - m_keepEntryPoint->setToolTip( - Tr::tr("Prevents modifying the entry point of the image. Enable only if " - "the image starts into a shell.")); - m_keepEntryPoint->setChecked(m_data.keepEntryPoint); - m_keepEntryPoint->setEnabled(true); - - connect(m_keepEntryPoint, &QCheckBox::toggled, this, [this, dockerDevice](bool on) { - m_data.keepEntryPoint = on; - dockerDevice->setData(m_data); - }); - - m_enableLldbFlags = new QCheckBox(Tr::tr("Enable flags needed for LLDB")); - m_enableLldbFlags->setToolTip(Tr::tr("Adds the following flags to the container " - "to allow LLDB to run: " - "--cap-add=SYS_PTRACE --security-opt seccomp=unconfined")); - m_enableLldbFlags->setChecked(m_data.enableLldbFlags); - m_enableLldbFlags->setEnabled(true); - - connect(m_enableLldbFlags, &QCheckBox::toggled, this, [this, dockerDevice](bool on) { - m_data.enableLldbFlags = on; - dockerDevice->setData(m_data); - }); - - m_runAsOutsideUser = new QCheckBox(Tr::tr("Run as outside user")); - m_runAsOutsideUser->setToolTip(Tr::tr("Uses user ID and group ID of the user running Qt Creator " - "in the docker container.")); - m_runAsOutsideUser->setChecked(m_data.useLocalUidGid); - m_runAsOutsideUser->setEnabled(HostOsInfo::isAnyUnixHost()); - - connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [this, dockerDevice](bool on) { - m_data.useLocalUidGid = on; - dockerDevice->setData(m_data); - }); - - auto clangDLabel = new QLabel(Tr::tr("Clangd Executable:")); - - m_clangdExecutable = new PathChooser(); - m_clangdExecutable->setExpectedKind(PathChooser::ExistingCommand); - m_clangdExecutable->setHistoryCompleter("Docker.ClangdExecutable.History"); - m_clangdExecutable->setAllowPathFromDevice(true); - m_clangdExecutable->setFilePath(m_data.clangdExecutable); - m_clangdExecutable->setValidationFunction( - [chooser = m_clangdExecutable](FancyLineEdit *, QString *error) { - return Utils::checkClangdVersion(chooser->filePath(), error); - }); - - connect(m_clangdExecutable, &PathChooser::rawPathChanged, this, [this, dockerDevice] { - m_data.clangdExecutable = m_clangdExecutable->filePath(); - dockerDevice->setData(m_data); - }); - auto pathListLabel = new InfoLabel(Tr::tr("Paths to mount:")); pathListLabel->setAdditionalToolTip(Tr::tr("Source directory list should not be empty.")); - m_pathsListEdit = new PathListEditor; - m_pathsListEdit->setPlaceholderText(Tr::tr("Host directories to mount into the container")); - m_pathsListEdit->setToolTip(Tr::tr("Maps paths in this list one-to-one to the " - "docker container.")); - m_pathsListEdit->setPathList(m_data.mounts); - m_pathsListEdit->setMaximumHeight(100); - m_pathsListEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - - auto markupMounts = [this, pathListLabel] { - const bool isEmpty = m_pathsListEdit->pathList().isEmpty(); + auto markupMounts = [deviceSettings, pathListLabel] { + const bool isEmpty = deviceSettings->mounts.volatileValue().isEmpty(); pathListLabel->setType(isEmpty ? InfoLabel::Warning : InfoLabel::None); }; markupMounts(); - connect(m_pathsListEdit, &PathListEditor::changed, this, [this, dockerDevice, markupMounts] { - m_data.mounts = m_pathsListEdit->pathList(); - dockerDevice->setData(m_data); - markupMounts(); - }); + connect(&deviceSettings->mounts, &FilePathListAspect::volatileValueChanged, this, markupMounts); auto logView = new QTextBrowser; connect(&m_kitItemDetector, &KitDetector::logOutput, @@ -177,31 +101,38 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) return paths; }; - connect(autoDetectButton, &QPushButton::clicked, this, - [this, logView, dockerDevice, searchPaths] { - logView->clear(); - dockerDevice->updateContainerAccess(); + connect(autoDetectButton, + &QPushButton::clicked, + this, + [this, logView, dockerDevice, searchPaths, deviceSettings] { + logView->clear(); + expected_str<void> startResult = dockerDevice->updateContainerAccess(); - const FilePath clangdPath = dockerDevice->filePath("clangd") - .searchInPath({}, - FilePath::AppendToPath, - [](const FilePath &clangd) { - return Utils::checkClangdVersion(clangd); - }); + if (!startResult) { + logView->append(Tr::tr("Failed to start container.")); + logView->append(startResult.error()); + return; + } - if (!clangdPath.isEmpty()) - m_clangdExecutable->setFilePath(clangdPath); + const FilePath clangdPath + = dockerDevice->filePath("clangd") + .searchInPath({}, FilePath::AppendToPath, [](const FilePath &clangd) { + return Utils::checkClangdVersion(clangd); + }); - m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths()); + if (!clangdPath.isEmpty()) + deviceSettings->clangdExecutable.setValue(clangdPath); - if (DockerApi::instance()->dockerDaemonAvailable().value_or(false) == false) - logView->append(Tr::tr("Docker daemon appears to be stopped.")); - else - logView->append(Tr::tr("Docker daemon appears to be running.")); + m_kitItemDetector.autoDetect(dockerDevice->id().toString(), searchPaths()); - logView->append(Tr::tr("Detection complete.")); - updateDaemonStateTexts(); - }); + if (DockerApi::instance()->dockerDaemonAvailable().value_or(false) == false) + logView->append(Tr::tr("Docker daemon appears to be stopped.")); + else + logView->append(Tr::tr("Docker daemon appears to be running.")); + + logView->append(Tr::tr("Detection complete.")); + updateDaemonStateTexts(); + }); connect(undoAutoDetectButton, &QPushButton::clicked, this, [this, logView, device] { logView->clear(); @@ -213,6 +144,9 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) m_kitItemDetector.listAutoDetected(device->id().toString()); }); + auto createLineLabel = new QLabel(dockerDevice->createCommandLine().toUserOutput()); + createLineLabel->setWordWrap(true); + using namespace Layouting; // clang-format off @@ -232,22 +166,28 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) Tr::tr("Detection log:"), logView }; - - Form { - repoLabel, m_repoLineEdit, br, - tagLabel, m_tagLineEdit, br, - idLabel, m_idLineEdit, br, - daemonStateLabel, m_daemonReset, m_daemonState, br, - m_runAsOutsideUser, br, - m_keepEntryPoint, br, - m_enableLldbFlags, br, - clangDLabel, m_clangdExecutable, br, - Column { - pathListLabel, - m_pathsListEdit, - }, br, - (dockerDevice->isAutoDetected() ? Column {} : std::move(detectionControls)), + Column { noMargin, + Form { + deviceSettings->repo, br, + deviceSettings->tag, br, + deviceSettings->imageId, br, + daemonStateLabel, m_daemonReset, m_daemonState, br, + Tr::tr("Container state:"), deviceSettings->containerStatus, br, + deviceSettings->useLocalUidGid, br, + deviceSettings->keepEntryPoint, br, + deviceSettings->enableLldbFlags, br, + deviceSettings->clangdExecutable, br, + deviceSettings->network, br, + deviceSettings->extraArgs, br, + Column { + pathListLabel, + deviceSettings->mounts, + }, br, + If { dockerDevice->isAutoDetected(), {}, {detectionControls} }, + noMargin, + },br, + Tr::tr("Command line:"), createLineLabel, br, }.attachTo(this); // clang-format on @@ -258,6 +198,10 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) searchDirsLineEdit->setFocus(); }; QObject::connect(searchDirsComboBox, &QComboBox::activated, this, updateDirectoriesLineEdit); + + connect(deviceSettings, &AspectContainer::applied, this, [createLineLabel, dockerDevice] { + createLineLabel->setText(dockerDevice->createCommandLine().toUserOutput()); + }); } void DockerDeviceWidget::updateDaemonStateTexts() diff --git a/src/plugins/docker/dockerdevicewidget.h b/src/plugins/docker/dockerdevicewidget.h index d92ac5ed218..edc76062c40 100644 --- a/src/plugins/docker/dockerdevicewidget.h +++ b/src/plugins/docker/dockerdevicewidget.h @@ -31,20 +31,10 @@ public: void updateDaemonStateTexts(); private: - QLineEdit *m_repoLineEdit; - QLineEdit *m_tagLineEdit; - QLineEdit *m_idLineEdit; - QToolButton *m_daemonReset; QLabel *m_daemonState; - QCheckBox *m_runAsOutsideUser; - QCheckBox *m_keepEntryPoint; - QCheckBox *m_enableLldbFlags; - Utils::PathChooser *m_clangdExecutable; + QToolButton *m_daemonReset; - Utils::PathListEditor *m_pathsListEdit; KitDetector m_kitItemDetector; - - DockerDeviceData m_data; }; } // Docker::Internal diff --git a/src/plugins/docker/dockerplugin.cpp b/src/plugins/docker/dockerplugin.cpp index 7e47abd7b1f..eb4ce5a7468 100644 --- a/src/plugins/docker/dockerplugin.cpp +++ b/src/plugins/docker/dockerplugin.cpp @@ -1,17 +1,15 @@ // Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "dockerplugin.h" - #include "dockerapi.h" #include "dockerconstants.h" #include "dockerdevice.h" -#include "dockersettings.h" + +#include <extensionsystem/iplugin.h> #include <projectexplorer/projectexplorerconstants.h> #include <utils/fsengine/fsengine.h> -#include <utils/qtcassert.h> using namespace Core; using namespace ProjectExplorer; @@ -19,32 +17,34 @@ using namespace Utils; namespace Docker::Internal { -class DockerPluginPrivate +class DockerPlugin final : public ExtensionSystem::IPlugin { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Docker.json") + public: - ~DockerPluginPrivate() { - m_deviceFactory.shutdownExistingDevices(); + DockerPlugin() + { + FSEngine::registerDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); } - DockerSettings m_settings; - DockerDeviceFactory m_deviceFactory{&m_settings}; - DockerApi m_dockerApi{&m_settings}; +private: + ~DockerPlugin() final + { + FSEngine::unregisterDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); + m_deviceFactory->shutdownExistingDevices(); + } + + void initialize() final + { + m_deviceFactory = std::make_unique<DockerDeviceFactory>(); + m_dockerApi = std::make_unique<DockerApi>(); + } + + std::unique_ptr<DockerDeviceFactory> m_deviceFactory; + std::unique_ptr<DockerApi> m_dockerApi; }; -DockerPlugin::DockerPlugin() -{ - FSEngine::registerDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); -} +} // Docker::Internal -DockerPlugin::~DockerPlugin() -{ - FSEngine::unregisterDeviceScheme(Constants::DOCKER_DEVICE_SCHEME); - delete d; -} - -void DockerPlugin::initialize() -{ - d = new DockerPluginPrivate; -} - -} // Docker::Interanl +#include "dockerplugin.moc" diff --git a/src/plugins/docker/dockerplugin.h b/src/plugins/docker/dockerplugin.h deleted file mode 100644 index 267244709d8..00000000000 --- a/src/plugins/docker/dockerplugin.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace Docker::Internal { - -class DockerPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Docker.json") - -public: - DockerPlugin(); - -private: - ~DockerPlugin() final; - - void initialize() final; - - class DockerPluginPrivate *d = nullptr; -}; - -} // Docker::Internal diff --git a/src/plugins/docker/dockersettings.cpp b/src/plugins/docker/dockersettings.cpp index 0643cb3a31a..925ed6559b8 100644 --- a/src/plugins/docker/dockersettings.cpp +++ b/src/plugins/docker/dockersettings.cpp @@ -6,6 +6,8 @@ #include "dockerconstants.h" #include "dockertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <projectexplorer/projectexplorerconstants.h> #include <utils/hostosinfo.h> @@ -15,12 +17,16 @@ using namespace Utils; namespace Docker::Internal { +DockerSettings &settings() +{ + static DockerSettings theSettings; + return theSettings; +} + DockerSettings::DockerSettings() { + setAutoApply(false); setSettingsGroup(Constants::DOCKER); - setId(Docker::Constants::DOCKER_SETTINGS_ID); - setDisplayName(Tr::tr("Docker")); - setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); setLayouter([this] { using namespace Layouting; @@ -42,8 +48,8 @@ DockerSettings::DockerSettings() additionalPaths.append("/usr/local/bin"); dockerBinaryPath.setExpectedKind(PathChooser::ExistingCommand); - dockerBinaryPath.setDefaultFilePath( - FilePath::fromString("docker").searchInPath(additionalPaths)); + dockerBinaryPath.setDefaultValue( + FilePath::fromString("docker").searchInPath(additionalPaths).toUserOutput()); dockerBinaryPath.setDisplayName(Tr::tr("Docker CLI")); dockerBinaryPath.setHistoryCompleter("Docker.Command.History"); dockerBinaryPath.setLabelText(Tr::tr("Command:")); @@ -52,4 +58,18 @@ DockerSettings::DockerSettings() readSettings(); } +class DockerSettingsPage final : public Core::IOptionsPage +{ +public: + DockerSettingsPage() + { + setId(Docker::Constants::DOCKER_SETTINGS_ID); + setDisplayName(Tr::tr("Docker")); + setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const DockerSettingsPage settingsPage; + } // Docker::Internal diff --git a/src/plugins/docker/dockersettings.h b/src/plugins/docker/dockersettings.h index 076acf6fa8c..77481a8eaee 100644 --- a/src/plugins/docker/dockersettings.h +++ b/src/plugins/docker/dockersettings.h @@ -3,11 +3,11 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace Docker::Internal { -class DockerSettings final : public Core::PagedSettings +class DockerSettings final : public Utils::AspectContainer { public: DockerSettings(); @@ -15,4 +15,6 @@ public: Utils::FilePathAspect dockerBinaryPath{this}; }; +DockerSettings &settings(); + } // Docker::Internal diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 0b9e05b5892..2408d366dc1 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -13,7 +13,7 @@ #include <projectexplorer/toolchainmanager.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionfactory.h> #include <qtsupport/qtversionmanager.h> diff --git a/src/plugins/emacskeys/EmacsKeys.json.in b/src/plugins/emacskeys/EmacsKeys.json.in index 9935c0cd72a..c0e5bdde59a 100644 --- a/src/plugins/emacskeys/EmacsKeys.json.in +++ b/src/plugins/emacskeys/EmacsKeys.json.in @@ -1,32 +1,32 @@ { - \"Name\" : \"EmacsKeys\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"nsf\", - \"Copyright\" : \"(C) 2016 nsf <no.smile.face@gmail.com>, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "EmacsKeys", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "nsf", + "Copyright" : "(C) 2016 nsf <no.smile.face@gmail.com>, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Provides additional actions that typical Emacs users would expect.\", - \"LongDescription\" : [ - \"The main idea behind this plugin is to provide additional actions a typical emacs user would expect. It doesn\'t claim to provide full emacs emulation. The following actions are available:\", - \" - Movement [C-f, C-b, C-n, C-p, M-f, M-b, C-a, C-e, M-<, M->]\", - \" - Mark-based selection [C-SPC, C-x C-x]\", - \" - Cut/copy/yank (doesn\'t provide kill ring feature) [M-w, C-w, C-y]\", - \" - Kill actions, which interact properly with clipboard [C-k, M-d, C-d]\", - \" - Scrolling (half of the screen, keeps cursor visible) [C-v, M-v]\", - \" - Insert new line and indent [C-j]\", - \"\", - \"IMPORTANT: Actions are not bound to any key combinations by default. You can find them under \'EmacsKeys\' section in keyboard shortcuts settings.\", - \"\", - \"Also it\'s worth mentioning that EmacsKeys plugin forces disabling of menu mnemonics by calling Qt\'s qt_set_sequence_auto_mnemonic function with false argument. Many of the english menu mnemonics get into the way of typical emacs keys, this includes: Alt+F (File), Alt+B (Build), Alt+W (Window). It\'s a temporary solution, it remains until there is a better one.\" + "Description" : "Provides additional actions that typical Emacs users would expect.", + "LongDescription" : [ + "The main idea behind this plugin is to provide additional actions a typical emacs user would expect. It doesn't claim to provide full emacs emulation. The following actions are available:", + " - Movement [C-f, C-b, C-n, C-p, M-f, M-b, C-a, C-e, M-<, M->]", + " - Mark-based selection [C-SPC, C-x C-x]", + " - Cut/copy/yank (doesn't provide kill ring feature) [M-w, C-w, C-y]", + " - Kill actions, which interact properly with clipboard [C-k, M-d, C-d]", + " - Scrolling (half of the screen, keeps cursor visible) [C-v, M-v]", + " - Insert new line and indent [C-j]", + "", + "IMPORTANT: Actions are not bound to any key combinations by default. You can find them under 'EmacsKeys' section in keyboard shortcuts settings.", + "", + "Also it's worth mentioning that EmacsKeys plugin forces disabling of menu mnemonics by calling Qt's qt_set_sequence_auto_mnemonic function with false argument. Many of the english menu mnemonics get into the way of typical emacs keys, this includes: Alt+F (File), Alt+B (Build), Alt+W (Window). It's a temporary solution, it remains until there is a better one." ], - \"Url\" : \"http://nosmileface.ru\", - $$dependencyList + "Url" : "http://nosmileface.ru", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/emacskeys/emacskeysplugin.h b/src/plugins/emacskeys/emacskeysplugin.h index ab124b47ce3..1e5300ff1be 100644 --- a/src/plugins/emacskeys/emacskeysplugin.h +++ b/src/plugins/emacskeys/emacskeysplugin.h @@ -30,9 +30,10 @@ #include <QTextCursor> -// forward declarations -QT_FORWARD_DECLARE_CLASS(QAction) -QT_FORWARD_DECLARE_CLASS(QPlainTextEdit) +QT_BEGIN_NAMESPACE +class QAction; +class QPlainTextEdit; +QT_END_NAMESPACE namespace Core { class IEditor; diff --git a/src/plugins/emacskeys/emacskeysstate.h b/src/plugins/emacskeys/emacskeysstate.h index a0de7ceffd6..43b65876674 100644 --- a/src/plugins/emacskeys/emacskeysstate.h +++ b/src/plugins/emacskeys/emacskeysstate.h @@ -26,7 +26,9 @@ #include <QObject> -QT_FORWARD_DECLARE_CLASS(QPlainTextEdit) +QT_BEGIN_NAMESPACE +class QPlainTextEdit; +QT_END_NAMESPACE namespace EmacsKeys { namespace Internal { diff --git a/src/plugins/fakevim/FakeVim.json.in b/src/plugins/fakevim/FakeVim.json.in index b9b72636495..915b50331b3 100644 --- a/src/plugins/fakevim/FakeVim.json.in +++ b/src/plugins/fakevim/FakeVim.json.in @@ -1,18 +1,18 @@ { - \"Name\" : \"FakeVim\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "FakeVim", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"VI-style keyboard navigation.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "VI-style keyboard navigation.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp index 916fea16097..b4d568c7f07 100644 --- a/src/plugins/fakevim/fakevimactions.cpp +++ b/src/plugins/fakevim/fakevimactions.cpp @@ -28,38 +28,14 @@ using namespace Utils; namespace FakeVim::Internal { #ifdef FAKEVIM_STANDALONE -FvBaseAspect::FvBaseAspect() -{ -} -void FvBaseAspect::setValue(const QVariant &value) -{ - m_value = value; -} - -QVariant FvBaseAspect::value() const -{ - return m_value; -} - -void FvBaseAspect::setDefaultValue(const QVariant &value) -{ - m_defaultValue = value; - m_value = value; -} - -QVariant FvBaseAspect::defaultValue() const -{ - return m_defaultValue; -} - -void FvBaseAspect::setSettingsKey(const QString &group, const QString &key) +void FvBaseAspect::setSettingsKey(const Key &group, const Key &key) { m_settingsGroup = group; m_settingsKey = key; } -QString FvBaseAspect::settingsKey() const +Key FvBaseAspect::settingsKey() const { return m_settingsKey; } @@ -69,29 +45,17 @@ void setAutoApply(bool ) {} #endif -static FakeVimSettings *s_settings; - FakeVimSettings &settings() { - return *s_settings; + static FakeVimSettings theSettings; + return theSettings; } FakeVimSettings::FakeVimSettings() { - s_settings = this; - -#ifndef FAKEVIM_STANDALONE - const char SETTINGS_CATEGORY[] = "D.FakeVim"; - const char SETTINGS_ID[] = "A.FakeVim.General"; - - setId(SETTINGS_ID); - setDisplayName(Tr::tr("General")); - setCategory(SETTINGS_CATEGORY); - setDisplayCategory(Tr::tr("FakeVim")); - setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png"); + setAutoApply(false); setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim")); -#endif // Specific FakeVim settings setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:")); @@ -118,6 +82,7 @@ FakeVimSettings::FakeVimSettings() setup(&showCmd, true, "ShowCmd", "sc", Tr::tr("Show partial command")); setup(&relativeNumber, false, "RelativeNumber", "rnu", Tr::tr("Show line numbers relative to cursor")); setup(&blinkingCursor, false, "BlinkingCursor", "bc", Tr::tr("Blinking cursor")); + setup(&systemEncoding, false, "SystemEncoding", {}, Tr::tr("Use system encoding for :source")); setup(&scrollOff, 0, "ScrollOff", "so", Tr::tr("Scroll offset:")); setup(&backspace, "indent,eol,start", "Backspace", "bs", Tr::tr("Backspace:")); @@ -158,7 +123,6 @@ FakeVimSettings::FakeVimSettings() vimRcPath.setToolTip(Tr::tr("Keep empty to use the default path, i.e. " "%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise.")); vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault)); - vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay); setLayouter([this] { using namespace Layouting; @@ -174,7 +138,8 @@ FakeVimSettings::FakeVimSettings() showCmd, startOfLine, passKeys, - blinkingCursor + blinkingCursor, + HostOsInfo::isWindowsHost() ? LayoutItem(systemEncoding) : empty }, Column { incSearch, @@ -279,14 +244,14 @@ FakeVimSettings::FakeVimSettings() FakeVimSettings::~FakeVimSettings() = default; -FvBaseAspect *FakeVimSettings::item(const QString &name) +FvBaseAspect *FakeVimSettings::item(const Utils::Key &name) { return m_nameToAspect.value(name, nullptr); } QString FakeVimSettings::trySetValue(const QString &name, const QString &value) { - FvBaseAspect *aspect = m_nameToAspect.value(name, nullptr); + FvBaseAspect *aspect = m_nameToAspect.value(keyFromString(name), nullptr); if (!aspect) return Tr::tr("Unknown option: %1").arg(name); if (aspect == &tabStop || aspect == &shiftWidth) { @@ -294,18 +259,18 @@ QString FakeVimSettings::trySetValue(const QString &name, const QString &value) return Tr::tr("Argument must be positive: %1=%2") .arg(name).arg(value); } - aspect->setValue(value); + aspect->setVariantValue(value); return QString(); } void FakeVimSettings::setup(FvBaseAspect *aspect, const QVariant &value, - const QString &settingsKey, - const QString &shortName, + const Utils::Key &settingsKey, + const Utils::Key &shortName, const QString &labelText) { aspect->setSettingsKey("FakeVim", settingsKey); - aspect->setDefaultValue(value); + aspect->setDefaultVariantValue(value); #ifndef FAKEVIM_STANDALONE aspect->setLabelText(labelText); aspect->setAutoApply(false); @@ -317,7 +282,7 @@ void FakeVimSettings::setup(FvBaseAspect *aspect, Q_UNUSED(labelText) #endif - const QString longName = settingsKey.toLower(); + const Key longName = settingsKey.toByteArray().toLower(); if (!longName.isEmpty()) { m_nameToAspect[longName] = aspect; m_aspectToName[aspect] = longName; @@ -326,4 +291,27 @@ void FakeVimSettings::setup(FvBaseAspect *aspect, m_nameToAspect[shortName] = aspect; } +#ifndef FAKEVIM_STANDALONE + +class FakeVimSettingsPage final : public Core::IOptionsPage +{ +public: + FakeVimSettingsPage() + { + const char SETTINGS_CATEGORY[] = "D.FakeVim"; + const char SETTINGS_ID[] = "A.FakeVim.General"; + + setId(SETTINGS_ID); + setDisplayName(Tr::tr("General")); + setCategory(SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("FakeVim")); + setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const FakeVimSettingsPage settingsPage; + +#endif + } // FakeVim::Internal diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h index e3b14b0d47a..1ad91bb968c 100644 --- a/src/plugins/fakevim/fakevimactions.h +++ b/src/plugins/fakevim/fakevimactions.h @@ -3,8 +3,16 @@ #pragma once -#ifndef FAKEVIM_STANDALONE -# include <coreplugin/dialogs/ioptionspage.h> +#ifdef FAKEVIM_STANDALONE + +#include <utils/store.h> + +namespace Utils { class FilePath {}; } + +#else + +#include <coreplugin/dialogs/ioptionspage.h> + #endif #include <QCoreApplication> @@ -16,49 +24,60 @@ namespace FakeVim::Internal { #ifdef FAKEVIM_STANDALONE + class FvBaseAspect { public: - FvBaseAspect(); - virtual ~FvBaseAspect() {} + FvBaseAspect() = default; + virtual ~FvBaseAspect() = default; - void setValue(const QVariant &value); - QVariant value() const; - void setDefaultValue(const QVariant &value); - QVariant defaultValue() const; - void setSettingsKey(const QString &group, const QString &key); - QString settingsKey() const; + virtual void setVariantValue(const QVariant &) {} + virtual void setDefaultVariantValue(const QVariant &) {} + virtual QVariant variantValue() const { return {}; } + virtual QVariant defaultVariantValue() const { return {}; } + void setSettingsKey(const Utils::Key &group, const Utils::Key &key); + Utils::Key settingsKey() const; void setCheckable(bool) {} void setDisplayName(const QString &) {} void setToolTip(const QString &) {} private: - QVariant m_value; - QVariant m_defaultValue; - QString m_settingsGroup; - QString m_settingsKey; + Utils::Key m_settingsGroup; + Utils::Key m_settingsKey; }; -class FvBoolAspect : public FvBaseAspect +template <class ValueType> +class FvTypedAspect : public FvBaseAspect { public: - bool value() const { return FvBaseAspect::value().toBool(); } - bool operator()() const { return value(); } + void setVariantValue(const QVariant &value) override + { + m_value = value.value<ValueType>(); + } + void setDefaultVariantValue(const QVariant &value) override + { + m_defaultValue = value.value<ValueType>(); + } + QVariant variantValue() const override + { + return QVariant::fromValue<ValueType>(m_value); + } + QVariant defaultVariantValue() const override + { + return QVariant::fromValue<ValueType>(m_defaultValue); + } + + ValueType value() const { return m_value; } + ValueType operator()() const { return m_value; } + + ValueType m_value; + ValueType m_defaultValue; }; -class FvIntegerAspect : public FvBaseAspect -{ -public: - qint64 value() const { return FvBaseAspect::value().toLongLong(); } - qint64 operator()() const { return value(); } -}; - -class FvStringAspect : public FvBaseAspect -{ -public: - QString value() const { return FvBaseAspect::value().toString(); } - QString operator()() const { return value(); } -}; +using FvBoolAspect = FvTypedAspect<bool>; +using FvIntegerAspect = FvTypedAspect<qint64>; +using FvStringAspect = FvTypedAspect<QString>; +using FvFilePathAspect = FvTypedAspect<Utils::FilePath>; class FvAspectContainer : public FvBaseAspect { @@ -67,11 +86,12 @@ public: #else -using FvAspectContainer = Core::PagedSettings; +using FvAspectContainer = Utils::AspectContainer; using FvBaseAspect = Utils::BaseAspect; using FvBoolAspect = Utils::BoolAspect; using FvIntegerAspect = Utils::IntegerAspect; using FvStringAspect = Utils::StringAspect; +using FvFilePathAspect = Utils::FilePathAspect; #endif @@ -81,12 +101,12 @@ public: FakeVimSettings(); ~FakeVimSettings(); - FvBaseAspect *item(const QString &name); + FvBaseAspect *item(const Utils::Key &name); QString trySetValue(const QString &name, const QString &value); FvBoolAspect useFakeVim; FvBoolAspect readVimRc; - FvStringAspect vimRcPath; + FvFilePathAspect vimRcPath; FvBoolAspect startOfLine; FvIntegerAspect tabStop; @@ -133,15 +153,16 @@ public: FvBoolAspect emulateSurround; FvBoolAspect blinkingCursor; + FvBoolAspect systemEncoding; private: void setup(FvBaseAspect *aspect, const QVariant &value, - const QString &settingsKey, - const QString &shortName, - const QString &label); + const Utils::Key &settingsKey, + const Utils::Key &shortName, + const QString &label); - QHash<QString, FvBaseAspect *> m_nameToAspect; - QHash<FvBaseAspect *, QString> m_aspectToName; + QHash<Utils::Key, FvBaseAspect *> m_nameToAspect; + QHash<FvBaseAspect *, Utils::Key> m_aspectToName; }; FakeVimSettings &settings(); diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index edfa315af98..06ad9703c48 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -4538,7 +4538,8 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) } else if (input.isControl('c')) { if (isNoVisualMode()) { #if defined(Q_OS_MACOS) - showMessage(MessageInfo, Tr::tr("Type Meta-Shift-Y, Meta-Shift-Y to quit FakeVim mode.")); + showMessage(MessageInfo, + Tr::tr("Type Control-Shift-Y, Control-Shift-Y to quit FakeVim mode.")); #else showMessage(MessageInfo, Tr::tr("Type Alt-Y, Alt-Y to quit FakeVim mode.")); #endif @@ -6136,24 +6137,24 @@ bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd) if (negateOption) optionName.remove(0, 2); - FvBaseAspect *act = s.item(optionName); + FvBaseAspect *act = s.item(Utils::keyFromString(optionName)); if (!act) { showMessage(MessageError, Tr::tr("Unknown option:") + ' ' + cmd.args); - } else if (act->defaultValue().type() == QVariant::Bool) { - bool oldValue = act->value().toBool(); + } else if (act->defaultVariantValue().type() == QVariant::Bool) { + bool oldValue = act->variantValue().toBool(); if (printOption) { showMessage(MessageInfo, QLatin1String(oldValue ? "" : "no") - + act->settingsKey().toLower()); + + act->settingsKey().toByteArray().toLower()); } else if (toggleOption || negateOption == oldValue) { - act->setValue(!oldValue); + act->setVariantValue(!oldValue); } } else if (negateOption && !printOption) { showMessage(MessageError, Tr::tr("Invalid argument:") + ' ' + cmd.args); } else if (toggleOption) { showMessage(MessageError, Tr::tr("Trailing characters:") + ' ' + cmd.args); } else { - showMessage(MessageInfo, act->settingsKey().toLower() + "=" - + act->value().toString()); + showMessage(MessageInfo, act->settingsKey().toByteArray().toLower() + "=" + + act->variantValue().toString()); } } updateEditor(); @@ -6625,7 +6626,8 @@ bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd) } else if (!line.isEmpty() && !inFunction) { //qDebug() << "EXECUTING: " << line; ExCommand cmd; - QString commandLine = QString::fromLocal8Bit(line); + QString commandLine = s.systemEncoding() ? QString::fromLocal8Bit(line) + : QString::fromUtf8(line); while (parseExCommand(&commandLine, &cmd)) { if (!handleExCommandHelper(cmd)) break; diff --git a/src/plugins/fakevim/fakevimhandler.h b/src/plugins/fakevim/fakevimhandler.h index 0b615e1657e..4532fa374cf 100644 --- a/src/plugins/fakevim/fakevimhandler.h +++ b/src/plugins/fakevim/fakevimhandler.h @@ -38,8 +38,7 @@ struct Range struct ExCommand { ExCommand() = default; - ExCommand(const QString &cmd, const QString &args = QString(), - const Range &range = Range()); + ExCommand(const QString &cmd, const QString &args = {}, const Range &range = {}); bool matches(const QString &min, const QString &full) const; diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 54ae4ca93f8..f14cbbb2027 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -45,6 +45,7 @@ #include <texteditor/textmark.h> #include <texteditor/typingsettings.h> +#include <utils/algorithm.h> #include <utils/aspects.h> #include <utils/fancylineedit.h> #include <utils/hostosinfo.h> @@ -72,7 +73,6 @@ #include <QPushButton> #include <QRegularExpression> #include <QScrollBar> -#include <QSettings> #include <QStackedWidget> #include <QStandardPaths> #include <QStyleHints> @@ -501,7 +501,7 @@ FakeVimExCommandsMappings::FakeVimExCommandsMappings() auto infoLabel = new InfoLabel(Tr::tr("Invalid regular expression."), InfoLabel::Error); infoLabel->setVisible(false); - connect(m_commandEdit, &FancyLineEdit::validChanged, [infoLabel](bool valid){ + connect(m_commandEdit, &FancyLineEdit::validChanged, this, [infoLabel](bool valid){ infoLabel->setVisible(!valid); }); commandBoxLayout->addWidget(infoLabel); @@ -635,7 +635,7 @@ void FakeVimExCommandsMappings::apply() if (newMapping != globalCommandMapping) { const ExCommandMap &defaultMap = dd->m_defaultExCommandMap; - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginWriteArray(exCommandMapGroup); int count = 0; using Iterator = ExCommandMap::const_iterator; @@ -682,6 +682,8 @@ public: } }; +const FakeVimExCommandsPage exCommandPage; + /////////////////////////////////////////////////////////////////////// // // FakeVimUserCommandsPage @@ -751,11 +753,10 @@ public: class FakeVimUserCommandsPageWidget : public IOptionsPageWidget { public: - FakeVimUserCommandsPageWidget(FakeVimUserCommandsModel *model) - : m_model(model) + FakeVimUserCommandsPageWidget() { auto widget = new QTreeView; - widget->setModel(m_model); + widget->setModel(&m_model); widget->resizeColumnToContents(0); auto delegate = new FakeVimUserCommandsDelegate(widget); @@ -770,11 +771,11 @@ private: void apply() final { // now save the mappings if necessary - const UserCommandMap ¤t = m_model->commandMap(); + const UserCommandMap ¤t = m_model.commandMap(); UserCommandMap &userMap = dd->m_userCommandMap; if (current != userMap) { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); settings->beginWriteArray(userCommandMapGroup); int count = 0; using Iterator = UserCommandMap::const_iterator; @@ -799,7 +800,7 @@ private: } } - FakeVimUserCommandsModel *m_model; + FakeVimUserCommandsModel m_model; }; class FakeVimUserCommandsPage : public IOptionsPage @@ -810,13 +811,11 @@ public: setId(SETTINGS_USER_CMDS_ID); setDisplayName(Tr::tr("User Command Mapping")); setCategory(SETTINGS_CATEGORY); - setWidgetCreator([this] { return new FakeVimUserCommandsPageWidget(&m_model); }); + setWidgetCreator([] { return new FakeVimUserCommandsPageWidget; }); } - -private: - FakeVimUserCommandsModel m_model; }; +const FakeVimUserCommandsPage userCommandsPage; /////////////////////////////////////////////////////////////////////// // @@ -944,9 +943,8 @@ public: QString found = sel.selectedText(); // Only add "real" completions. if (found.startsWith(needle) - && !seen.contains(found) - && sel.anchor() != basePosition) { - seen.insert(found); + && sel.anchor() != basePosition + && Utils::insert(seen, found)) { auto item = new FakeVimAssistProposalItem(m_provider); item->setText(found); items.append(item); @@ -978,10 +976,6 @@ IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor(const AssistI class FakeVimPluginRunData { public: - FakeVimSettings settings; - FakeVimExCommandsPage exCommandsPage; - FakeVimUserCommandsPage userCommandsPage; - FakeVimCompletionAssistProvider wordProvider; }; @@ -1089,16 +1083,16 @@ void FakeVimPluginPrivate::initialize() this, &FakeVimPluginPrivate::documentRenamed); FakeVimSettings &s = settings(); - connect(&s.useFakeVim, &FvBoolAspect::valueChanged, - this, &FakeVimPluginPrivate::setUseFakeVim); + connect(&s.useFakeVim, &FvBoolAspect::changed, + this, [this, &s] { setUseFakeVim(s.useFakeVim()); }); connect(&s.readVimRc, &FvBaseAspect::changed, this, &FakeVimPluginPrivate::maybeReadVimRc); connect(&s.vimRcPath, &FvBaseAspect::changed, this, &FakeVimPluginPrivate::maybeReadVimRc); - connect(&s.relativeNumber, &FvBoolAspect::valueChanged, - this, &FakeVimPluginPrivate::setShowRelativeLineNumbers); - connect(&s.blinkingCursor, &FvBoolAspect::valueChanged, - this, &FakeVimPluginPrivate::setCursorBlinking); + connect(&s.relativeNumber, &FvBoolAspect::changed, + this, [this, &s] { setShowRelativeLineNumbers(s.relativeNumber()); }); + connect(&s.blinkingCursor, &FvBoolAspect::changed, + this, [this, &s] { setCursorBlinking(s.blinkingCursor()); }); // Delayed operations. connect(this, &FakeVimPluginPrivate::delayedQuitRequested, @@ -1151,7 +1145,7 @@ void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor) void FakeVimPluginPrivate::readSettings() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); m_exCommandMap = m_defaultExCommandMap; int size = settings->beginReadArray(exCommandMapGroup); @@ -1183,7 +1177,7 @@ void FakeVimPluginPrivate::maybeReadVimRc() //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value(); if (!settings().readVimRc()) return; - QString fileName = settings().vimRcPath(); + QString fileName = settings().vimRcPath().path(); if (fileName.isEmpty()) { fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc"); diff --git a/src/plugins/fossil/Fossil.json.in b/src/plugins/fossil/Fossil.json.in index eb4719af8f9..61204143ba6 100644 --- a/src/plugins/fossil/Fossil.json.in +++ b/src/plugins/fossil/Fossil.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"Fossil\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"Artur Shepilko\", - \"Copyright\" : \"(C) 2018 Artur Shepilko\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Fossil", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "Artur Shepilko", + "Copyright" : "(C) 2018 Artur Shepilko", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Fossil SCM integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Version Control", + "Description" : "Fossil SCM integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp index f7bb29f2aea..3c3de5e523c 100644 --- a/src/plugins/fossil/fossilclient.cpp +++ b/src/plugins/fossil/fossilclient.cpp @@ -433,7 +433,7 @@ RepositorySettings FossilClient::synchronousSettingsQuery(const FilePath &workin RepositorySettings repoSettings; repoSettings.user = synchronousUserDefaultQuery(workingDirectory); if (repoSettings.user.isEmpty()) - repoSettings.user = settings().userName.value(); + repoSettings.user = settings().userName(); for (const QString &line : output.split('\n', Qt::SkipEmptyParts)) { // parse settings line: @@ -587,7 +587,7 @@ bool FossilClient::synchronousCreateRepository(const FilePath &workingDirectory, const QString repoName = workingDirectory.fileName().simplified(); const FilePath repoPath = settings().defaultRepoPath(); - const QString adminUser = settings().userName.value(); + const QString adminUser = settings().userName(); if (repoName.isEmpty() || repoPath.isEmpty()) return false; @@ -764,9 +764,9 @@ bool FossilClient::managesFile(const FilePath &workingDirectory, const QString & unsigned int FossilClient::binaryVersion() const { static unsigned int cachedBinaryVersion = 0; - static QString cachedBinaryPath; + static FilePath cachedBinaryPath; - const QString currentBinaryPath = settings().binaryPath.value(); + const FilePath currentBinaryPath = settings().binaryPath(); if (currentBinaryPath.isEmpty()) return 0; @@ -884,7 +884,7 @@ void FossilLogHighlighter::highlightBlock(const QString &text) void FossilClient::log(const FilePath &workingDir, const QStringList &files, const QStringList &extraOptions, bool enableAnnotationContextMenu, - const std::function<void(Utils::CommandLine &)> &addAuthOptions) + const std::function<void(CommandLine &)> &addAuthOptions) { // Show timeline for both repository and a file or path (--path <file-or-path>) // When used for log repository, the files list is empty @@ -915,8 +915,10 @@ void FossilClient::log(const FilePath &workingDir, const QStringList &files, if (VcsBaseEditorConfig *editorConfig = createLogEditor(fossilEditor)) { editorConfig->setBaseArguments(extraOptions); // editor has been just created, createVcsEditor() didn't set a configuration widget yet - connect(editorConfig, &VcsBaseEditorConfig::commandExecutionRequested, - [=]() { this->log(workingDir, files, editorConfig->arguments(), enableAnnotationContextMenu, addAuthOptions); } ); + connect(editorConfig, &VcsBaseEditorConfig::commandExecutionRequested, this, [=] { + log(workingDir, files, editorConfig->arguments(), enableAnnotationContextMenu, + addAuthOptions); + }); fossilEditor->setEditorConfig(editorConfig); } } @@ -938,7 +940,7 @@ void FossilClient::log(const FilePath &workingDir, const QStringList &files, void FossilClient::logCurrentFile(const FilePath &workingDir, const QStringList &files, const QStringList &extraOptions, bool enableAnnotationContextMenu, - const std::function<void(Utils::CommandLine &)> &addAuthOptions) + const std::function<void(CommandLine &)> &addAuthOptions) { // Show commit history for the given file/file-revision // NOTE: 'fossil finfo' shows full history from all branches. @@ -968,8 +970,10 @@ void FossilClient::logCurrentFile(const FilePath &workingDir, const QStringList if (VcsBaseEditorConfig *editorConfig = createLogCurrentFileEditor(fossilEditor)) { editorConfig->setBaseArguments(extraOptions); // editor has been just created, createVcsEditor() didn't set a configuration widget yet - connect(editorConfig, &VcsBaseEditorConfig::commandExecutionRequested, - [=]() { this->logCurrentFile(workingDir, files, editorConfig->arguments(), enableAnnotationContextMenu, addAuthOptions); } ); + connect(editorConfig, &VcsBaseEditorConfig::commandExecutionRequested, this, [=] { + logCurrentFile(workingDir, files, editorConfig->arguments(), + enableAnnotationContextMenu, addAuthOptions); + }); fossilEditor->setEditorConfig(editorConfig); } } diff --git a/src/plugins/fossil/fossilcommitwidget.cpp b/src/plugins/fossil/fossilcommitwidget.cpp index 72fba5ef86f..c8cec3d9585 100644 --- a/src/plugins/fossil/fossilcommitwidget.cpp +++ b/src/plugins/fossil/fossilcommitwidget.cpp @@ -30,7 +30,7 @@ namespace Internal { // Retrieve the comment char format from the text editor. static QTextCharFormat commentFormat() { - const TextEditor::FontSettings settings = TextEditor::TextEditorSettings::instance()->fontSettings(); + const TextEditor::FontSettings &settings = TextEditor::TextEditorSettings::instance()->fontSettings(); return settings.toTextCharFormat(TextEditor::C_COMMENT); } diff --git a/src/plugins/fossil/fossilplugin.cpp b/src/plugins/fossil/fossilplugin.cpp index bc854a218d3..2e4d4f09611 100644 --- a/src/plugins/fossil/fossilplugin.cpp +++ b/src/plugins/fossil/fossilplugin.cpp @@ -16,15 +16,14 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/vcsmanager.h> #include <coreplugin/coreconstants.h> -#include <coreplugin/helpmanager.h> -#include <coreplugin/icore.h> -#include <coreplugin/idocument.h> #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/locator/commandlocator.h> +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> #include <coreplugin/jsexpander.h> +#include <coreplugin/locator/commandlocator.h> +#include <coreplugin/vcsmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projecttree.h> @@ -39,7 +38,6 @@ #include <vcsbase/basevcseditorfactory.h> #include <vcsbase/basevcssubmiteditorfactory.h> #include <vcsbase/vcsbaseclient.h> -#include <vcsbase/vcsbaseconstants.h> #include <vcsbase/vcsbaseeditor.h> #include <vcsbase/vcsbasesubmiteditor.h> #include <vcsbase/vcscommand.h> @@ -49,12 +47,10 @@ #include <QDialog> #include <QDialogButtonBox> #include <QDir> -#include <QFileDialog> #include <QGroupBox> #include <QMenu> #include <QMessageBox> #include <QRegularExpression> -#include <QtPlugin> using namespace Core; using namespace Utils; @@ -64,7 +60,7 @@ using namespace std::placeholders; namespace Fossil { namespace Internal { -class FossilTopicCache : public Core::IVersionControl::TopicCache +class FossilTopicCache : public IVersionControl::TopicCache { public: FossilTopicCache(FossilClient *client) : @@ -115,7 +111,7 @@ const VcsBaseSubmitEditorParameters submitEditorParameters { }; -class FossilPluginPrivate final : public VcsBase::VcsBasePluginPrivate +class FossilPluginPrivate final : public VcsBasePluginPrivate { public: enum SyncMode { @@ -146,11 +142,11 @@ public: void vcsDescribe(const FilePath &source, const QString &id) final; VcsCommand *createInitialCheckoutCommand(const QString &url, - const Utils::FilePath &baseDirectory, + const FilePath &baseDirectory, const QString &localName, const QStringList &extraArgs) final; - void updateActions(VcsBase::VcsBasePluginPrivate::ActionState) override; + void updateActions(VcsBasePluginPrivate::ActionState) override; bool activateCommit() override; // File menu action slots @@ -174,20 +170,19 @@ public: void update(); void configureRepository(); void commit(); - void showCommitWidget(const QList<VcsBase::VcsBaseClient::StatusItem> &status); + void showCommitWidget(const QList<VcsBaseClient::StatusItem> &status); void diffFromEditorSelected(const QStringList &files); void createRepository(); // Methods - void createMenu(const Core::Context &context); - void createFileActions(const Core::Context &context); - void createDirectoryActions(const Core::Context &context); - void createRepositoryActions(const Core::Context &context); + void createMenu(const Context &context); + void createFileActions(const Context &context); + void createDirectoryActions(const Context &context); + void createRepositoryActions(const Context &context); bool pullOrPush(SyncMode mode); // Variables - FossilSettings m_settings; FossilClient m_client; VcsSubmitEditorFactory submitEditorFactory { @@ -214,26 +209,26 @@ public: std::bind(&FossilPluginPrivate::vcsDescribe, this, _1, _2) }; - Core::CommandLocator *m_commandLocator = nullptr; - Core::ActionContainer *m_fossilContainer = nullptr; + CommandLocator *m_commandLocator = nullptr; + ActionContainer *m_fossilContainer = nullptr; QList<QAction *> m_repositoryActionList; // Menu Items (file actions) - Utils::ParameterAction *m_addAction = nullptr; - Utils::ParameterAction *m_deleteAction = nullptr; - Utils::ParameterAction *m_annotateFile = nullptr; - Utils::ParameterAction *m_diffFile = nullptr; - Utils::ParameterAction *m_logFile = nullptr; - Utils::ParameterAction *m_revertFile = nullptr; - Utils::ParameterAction *m_statusFile = nullptr; + ParameterAction *m_addAction = nullptr; + ParameterAction *m_deleteAction = nullptr; + ParameterAction *m_annotateFile = nullptr; + ParameterAction *m_diffFile = nullptr; + ParameterAction *m_logFile = nullptr; + ParameterAction *m_revertFile = nullptr; + ParameterAction *m_statusFile = nullptr; QAction *m_createRepositoryAction = nullptr; // Submit editor actions QAction *m_menuAction = nullptr; - Utils::FilePath m_submitRepository; + FilePath m_submitRepository; // To be connected to the VcsTask's success signal to emit the repository/ // files changed signals according to the variant's type: @@ -280,18 +275,18 @@ FossilClient *FossilPlugin::client() } FossilPluginPrivate::FossilPluginPrivate() - : VcsBase::VcsBasePluginPrivate(Core::Context(Constants::FOSSIL_CONTEXT)) + : VcsBasePluginPrivate(Context(Constants::FOSSIL_CONTEXT)) { - Core::Context context(Constants::FOSSIL_CONTEXT); + Context context(Constants::FOSSIL_CONTEXT); setTopicCache(new FossilTopicCache(&m_client)); - connect(&m_client, &VcsBase::VcsBaseClient::changed, this, &FossilPluginPrivate::changed); + connect(&m_client, &VcsBaseClient::changed, this, &FossilPluginPrivate::changed); - m_commandLocator = new Core::CommandLocator("Fossil", "fossil", "fossil", this); + m_commandLocator = new CommandLocator("Fossil", "fossil", "fossil", this); m_commandLocator->setDescription(Tr::tr("Triggers a Fossil version control operation.")); - ProjectExplorer::JsonWizardFactory::addWizardPath(Utils::FilePath::fromString(Constants::WIZARD_PATH)); - Core::JsExpander::registerGlobalObject("Fossil", [] { return new FossilJsExtension; }); + ProjectExplorer::JsonWizardFactory::addWizardPath(FilePath::fromString(Constants::WIZARD_PATH)); + JsExpander::registerGlobalObject("Fossil", [] { return new FossilJsExtension; }); connect(&settings(), &AspectContainer::changed, this, &IVersionControl::configurationChanged); @@ -299,10 +294,10 @@ FossilPluginPrivate::FossilPluginPrivate() createMenu(context); } -void FossilPluginPrivate::createMenu(const Core::Context &context) +void FossilPluginPrivate::createMenu(const Context &context) { // Create menu item for Fossil - m_fossilContainer = Core::ActionManager::createMenu("Fossil.FossilMenu"); + m_fossilContainer = ActionManager::createMenu("Fossil.FossilMenu"); QMenu *menu = m_fossilContainer->menu(); menu->setTitle(Tr::tr("&Fossil")); @@ -314,43 +309,47 @@ void FossilPluginPrivate::createMenu(const Core::Context &context) m_fossilContainer->addSeparator(context); // Request the Tools menu and add the Fossil menu to it - Core::ActionContainer *toolsMenu = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS); + ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS); toolsMenu->addMenu(m_fossilContainer); m_menuAction = m_fossilContainer->menu()->menuAction(); } -void FossilPluginPrivate::createFileActions(const Core::Context &context) +void FossilPluginPrivate::createFileActions(const Context &context) { - Core::Command *command; + Command *command; - m_annotateFile = new Utils::ParameterAction(Tr::tr("Annotate Current File"), Tr::tr("Annotate \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_annotateFile, Constants::ANNOTATE, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_annotateFile = new ParameterAction(Tr::tr("Annotate Current File"), Tr::tr("Annotate \"%1\""), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_annotateFile, Constants::ANNOTATE, context); + command->setAttribute(Command::CA_UpdateText); connect(m_annotateFile, &QAction::triggered, this, &FossilPluginPrivate::annotateCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); - m_diffFile = new Utils::ParameterAction(Tr::tr("Diff Current File"), Tr::tr("Diff \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_diffFile, Constants::DIFF, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_diffFile = new ParameterAction(Tr::tr("Diff Current File"), Tr::tr("Diff \"%1\""), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_diffFile, Constants::DIFF, context); + command->setAttribute(Command::CA_UpdateText); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+D") : Tr::tr("Alt+I,Alt+D"))); connect(m_diffFile, &QAction::triggered, this, &FossilPluginPrivate::diffCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); - m_logFile = new Utils::ParameterAction(Tr::tr("Timeline Current File"), Tr::tr("Timeline \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_logFile, Constants::LOG, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_logFile = new ParameterAction(Tr::tr("Timeline Current File"), Tr::tr("Timeline \"%1\""), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_logFile, Constants::LOG, context); + command->setAttribute(Command::CA_UpdateText); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+L") : Tr::tr("Alt+I,Alt+L"))); connect(m_logFile, &QAction::triggered, this, &FossilPluginPrivate::logCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); - m_statusFile = new Utils::ParameterAction(Tr::tr("Status Current File"), Tr::tr("Status \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_statusFile, Constants::STATUS, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_statusFile = new ParameterAction(Tr::tr("Status Current File"), Tr::tr("Status \"%1\""), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_statusFile, Constants::STATUS, context); + command->setAttribute(Command::CA_UpdateText); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+S") : Tr::tr("Alt+I,Alt+S"))); connect(m_statusFile, &QAction::triggered, this, &FossilPluginPrivate::statusCurrentFile); @@ -359,23 +358,26 @@ void FossilPluginPrivate::createFileActions(const Core::Context &context) m_fossilContainer->addSeparator(context); - m_addAction = new Utils::ParameterAction(Tr::tr("Add Current File"), Tr::tr("Add \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_addAction, Constants::ADD, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_addAction = new ParameterAction(Tr::tr("Add Current File"), Tr::tr("Add \"%1\""), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_addAction, Constants::ADD, context); + command->setAttribute(Command::CA_UpdateText); connect(m_addAction, &QAction::triggered, this, &FossilPluginPrivate::addCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); - m_deleteAction = new Utils::ParameterAction(Tr::tr("Delete Current File..."), Tr::tr("Delete \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_deleteAction, Constants::DELETE, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_deleteAction = new ParameterAction(Tr::tr("Delete Current File..."), Tr::tr("Delete \"%1\"..."), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_deleteAction, Constants::DELETE, context); + command->setAttribute(Command::CA_UpdateText); connect(m_deleteAction, &QAction::triggered, this, &FossilPluginPrivate::deleteCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); - m_revertFile = new Utils::ParameterAction(Tr::tr("Revert Current File..."), Tr::tr("Revert \"%1\"..."), Utils::ParameterAction::EnabledWithParameter, this); - command = Core::ActionManager::registerAction(m_revertFile, Constants::REVERT, context); - command->setAttribute(Core::Command::CA_UpdateText); + m_revertFile = new ParameterAction(Tr::tr("Revert Current File..."), Tr::tr("Revert \"%1\"..."), + ParameterAction::EnabledWithParameter, this); + command = ActionManager::registerAction(m_revertFile, Constants::REVERT, context); + command->setAttribute(Command::CA_UpdateText); connect(m_revertFile, &QAction::triggered, this, &FossilPluginPrivate::revertCurrentFile); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); @@ -383,7 +385,7 @@ void FossilPluginPrivate::createFileActions(const Core::Context &context) void FossilPluginPrivate::addCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); m_client.synchronousAdd(state.currentFileTopLevel(), state.relativeCurrentFile()); } @@ -395,22 +397,22 @@ void FossilPluginPrivate::deleteCurrentFile() void FossilPluginPrivate::annotateCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - const int lineNumber = VcsBase::VcsBaseEditor::lineNumberOfCurrentEditor(state.currentFile()); + const int lineNumber = VcsBaseEditor::lineNumberOfCurrentEditor(state.currentFile()); m_client.annotate(state.currentFileTopLevel(), state.relativeCurrentFile(), lineNumber); } void FossilPluginPrivate::diffCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); m_client.diff(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); } void FossilPluginPrivate::logCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); FossilClient::SupportedFeatures features = m_client.supportedFeatures(); QStringList extraOptions; @@ -428,10 +430,10 @@ void FossilPluginPrivate::logCurrentFile() void FossilPluginPrivate::revertCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - RevertDialog dialog(Tr::tr("Revert"), Core::ICore::dialogParent()); + RevertDialog dialog(Tr::tr("Revert"), ICore::dialogParent()); if (dialog.exec() == QDialog::Accepted) { m_client.revertFile(state.currentFileTopLevel(), state.relativeCurrentFile(), dialog.revision()); @@ -440,26 +442,26 @@ void FossilPluginPrivate::revertCurrentFile() void FossilPluginPrivate::statusCurrentFile() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); m_client.status(state.currentFileTopLevel(), state.relativeCurrentFile()); } -void FossilPluginPrivate::createDirectoryActions(const Core::Context &context) +void FossilPluginPrivate::createDirectoryActions(const Context &context) { QAction *action; - Core::Command *command; + Command *command; action = new QAction(Tr::tr("Diff"), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::DIFFMULTI, context); + command = ActionManager::registerAction(action, Constants::DIFFMULTI, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::diffRepository); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); action = new QAction(Tr::tr("Timeline"), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::LOGMULTI, context); + command = ActionManager::registerAction(action, Constants::LOGMULTI, context); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+T") : Tr::tr("Alt+I,Alt+T"))); connect(action, &QAction::triggered, this, &FossilPluginPrivate::logRepository); @@ -468,14 +470,14 @@ void FossilPluginPrivate::createDirectoryActions(const Core::Context &context) action = new QAction(Tr::tr("Revert..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::REVERTMULTI, context); + command = ActionManager::registerAction(action, Constants::REVERTMULTI, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::revertAll); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); action = new QAction(Tr::tr("Status"), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::STATUSMULTI, context); + command = ActionManager::registerAction(action, Constants::STATUSMULTI, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::statusMulti); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); @@ -484,14 +486,14 @@ void FossilPluginPrivate::createDirectoryActions(const Core::Context &context) void FossilPluginPrivate::diffRepository() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); m_client.diff(state.topLevel()); } void FossilPluginPrivate::logRepository() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); FossilClient::SupportedFeatures features = m_client.supportedFeatures(); QStringList extraOptions; @@ -505,43 +507,43 @@ void FossilPluginPrivate::logRepository() void FossilPluginPrivate::revertAll() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - RevertDialog dialog(Tr::tr("Revert"), Core::ICore::dialogParent()); + RevertDialog dialog(Tr::tr("Revert"), ICore::dialogParent()); if (dialog.exec() == QDialog::Accepted) m_client.revertAll(state.topLevel(), dialog.revision()); } void FossilPluginPrivate::statusMulti() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); m_client.status(state.topLevel()); } -void FossilPluginPrivate::createRepositoryActions(const Core::Context &context) +void FossilPluginPrivate::createRepositoryActions(const Context &context) { QAction *action = 0; - Core::Command *command = 0; + Command *command = 0; action = new QAction(Tr::tr("Pull..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::PULL, context); + command = ActionManager::registerAction(action, Constants::PULL, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::pull); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); action = new QAction(Tr::tr("Push..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::PUSH, context); + command = ActionManager::registerAction(action, Constants::PUSH, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::push); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); action = new QAction(Tr::tr("Update..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::UPDATE, context); + command = ActionManager::registerAction(action, Constants::UPDATE, context); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+U") : Tr::tr("Alt+I,Alt+U"))); connect(action, &QAction::triggered, this, &FossilPluginPrivate::update); @@ -550,7 +552,7 @@ void FossilPluginPrivate::createRepositoryActions(const Core::Context &context) action = new QAction(Tr::tr("Commit..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::COMMIT, context); + command = ActionManager::registerAction(action, Constants::COMMIT, context); command->setDefaultKeySequence(QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+I,Meta+C") : Tr::tr("Alt+I,Alt+C"))); connect(action, &QAction::triggered, this, &FossilPluginPrivate::commit); @@ -559,7 +561,7 @@ void FossilPluginPrivate::createRepositoryActions(const Core::Context &context) action = new QAction(Tr::tr("Settings..."), this); m_repositoryActionList.append(action); - command = Core::ActionManager::registerAction(action, Constants::CONFIGURE_REPOSITORY, context); + command = ActionManager::registerAction(action, Constants::CONFIGURE_REPOSITORY, context); connect(action, &QAction::triggered, this, &FossilPluginPrivate::configureRepository); m_fossilContainer->addAction(command); m_commandLocator->appendCommand(command); @@ -567,7 +569,7 @@ void FossilPluginPrivate::createRepositoryActions(const Core::Context &context) // Register "Create Repository..." action in global context, so that it's visible // without active repository to allow creating a new one. m_createRepositoryAction = new QAction(Tr::tr("Create Repository..."), this); - command = Core::ActionManager::registerAction(m_createRepositoryAction, Constants::CREATE_REPOSITORY); + command = ActionManager::registerAction(m_createRepositoryAction, Constants::CREATE_REPOSITORY); connect(m_createRepositoryAction, &QAction::triggered, this, &FossilPluginPrivate::createRepository); m_fossilContainer->addAction(command); } @@ -586,10 +588,10 @@ bool FossilPluginPrivate::pullOrPush(FossilPluginPrivate::SyncMode mode) return false; } - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return false); - PullOrPushDialog dialog(pullOrPushMode, Core::ICore::dialogParent()); + PullOrPushDialog dialog(pullOrPushMode, ICore::dialogParent()); dialog.setLocalBaseDirectory(m_client.settings().defaultRepoPath()); const QString defaultURL(m_client.synchronousGetRepositoryURL(state.topLevel())); dialog.setDefaultRemoteLocation(defaultURL); @@ -598,7 +600,7 @@ bool FossilPluginPrivate::pullOrPush(FossilPluginPrivate::SyncMode mode) QString remoteLocation(dialog.remoteLocation()); if (remoteLocation.isEmpty() && defaultURL.isEmpty()) { - VcsBase::VcsOutputWindow::appendError(Tr::tr("Remote repository is not defined.")); + VcsOutputWindow::appendError(Tr::tr("Remote repository is not defined.")); return false; } else if (remoteLocation == defaultURL) { remoteLocation.clear(); @@ -621,17 +623,17 @@ bool FossilPluginPrivate::pullOrPush(FossilPluginPrivate::SyncMode mode) void FossilPluginPrivate::update() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - RevertDialog dialog(Tr::tr("Update"), Core::ICore::dialogParent()); + RevertDialog dialog(Tr::tr("Update"), ICore::dialogParent()); if (dialog.exec() == QDialog::Accepted) m_client.update(state.topLevel(), dialog.revision()); } void FossilPluginPrivate::configureRepository() { - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); ConfigureDialog dialog; @@ -655,7 +657,7 @@ void FossilPluginPrivate::commit() if (raiseSubmitEditor()) return; - const VcsBase::VcsBasePluginState state = currentState(); + const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); m_submitRepository = state.topLevel(); @@ -663,36 +665,36 @@ void FossilPluginPrivate::commit() m_client.emitParsedStatus(m_submitRepository, {}); } -void FossilPluginPrivate::showCommitWidget(const QList<VcsBase::VcsBaseClient::StatusItem> &status) +void FossilPluginPrivate::showCommitWidget(const QList<VcsBaseClient::StatusItem> &status) { //Once we receive our data release the connection so it can be reused elsewhere disconnect(&m_client, &VcsBaseClient::parsedStatus, this, &FossilPluginPrivate::showCommitWidget); if (status.isEmpty()) { - VcsBase::VcsOutputWindow::appendError(Tr::tr("There are no changes to commit.")); + VcsOutputWindow::appendError(Tr::tr("There are no changes to commit.")); return; } // Start new temp file for commit message - Utils::TempFileSaver saver; + TempFileSaver saver; // Keep the file alive, else it removes self and forgets its name saver.setAutoRemove(false); if (!saver.finalize()) { - VcsBase::VcsOutputWindow::appendError(saver.errorString()); + VcsOutputWindow::appendError(saver.errorString()); return; } - Core::IEditor *editor = Core::EditorManager::openEditor(saver.filePath(), Constants::COMMIT_ID); + IEditor *editor = EditorManager::openEditor(saver.filePath(), Constants::COMMIT_ID); if (!editor) { - VcsBase::VcsOutputWindow::appendError(Tr::tr("Unable to create an editor for the commit.")); + VcsOutputWindow::appendError(Tr::tr("Unable to create an editor for the commit.")); return; } CommitEditor *commitEditor = qobject_cast<CommitEditor *>(editor); if (!commitEditor) { - VcsBase::VcsOutputWindow::appendError(Tr::tr("Unable to create a commit editor.")); + VcsOutputWindow::appendError(Tr::tr("Unable to create a commit editor.")); return; } setSubmitEditor(commitEditor); @@ -708,7 +710,7 @@ void FossilPluginPrivate::showCommitWidget(const QList<VcsBase::VcsBaseClient::S tags.removeAll(currentBranch.name); commitEditor->setFields(m_submitRepository, currentBranch, tags, currentUser, status); - connect(commitEditor, &VcsBase::VcsBaseSubmitEditor::diffSelectedFiles, + connect(commitEditor, &VcsBaseSubmitEditor::diffSelectedFiles, this, &FossilPluginPrivate::diffFromEditorSelected); commitEditor->setCheckScriptWorkingDirectory(m_submitRepository); } @@ -730,16 +732,16 @@ void FossilPluginPrivate::createRepository() // re-implemented from void VcsBasePlugin::createRepository() // Find current starting directory - Utils::FilePath directory; + FilePath directory; if (const ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectTree::currentProject()) directory = currentProject->projectDirectory(); // Prompt for a directory that is not under version control yet - QWidget *mw = Core::ICore::dialogParent(); + QWidget *mw = ICore::dialogParent(); do { directory = FileUtils::getExistingDirectory(nullptr, Tr::tr("Choose Checkout Directory"), directory); if (directory.isEmpty()) return; - const Core::IVersionControl *managingControl = Core::VcsManager::findVersionControlForDirectory(directory); + const IVersionControl *managingControl = VcsManager::findVersionControlForDirectory(directory); if (managingControl == 0) break; const QString question = Tr::tr("The directory \"%1\" is already managed by a version control system (%2)." @@ -766,13 +768,13 @@ bool FossilPluginPrivate::activateCommit() { CommitEditor *commitEditor = qobject_cast<CommitEditor *>(submitEditor()); QTC_ASSERT(commitEditor, return true); - Core::IDocument *editorDocument = commitEditor->document(); + IDocument *editorDocument = commitEditor->document(); QTC_ASSERT(editorDocument, return true); QStringList files = commitEditor->checkedFiles(); if (!files.empty()) { //save the commit message - if (!Core::DocumentManager::saveDocument(editorDocument)) + if (!DocumentManager::saveDocument(editorDocument)) return false; //rewrite entries of the form 'file => newfile' to 'newfile' because @@ -812,7 +814,7 @@ bool FossilPluginPrivate::activateCommit() } -void FossilPluginPrivate::updateActions(VcsBase::VcsBasePluginPrivate::ActionState as) +void FossilPluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as) { m_createRepositoryAction->setEnabled(true); @@ -886,14 +888,14 @@ bool FossilPluginPrivate::supportsOperation(Operation operation) const bool supported = isConfigured(); switch (operation) { - case Core::IVersionControl::AddOperation: - case Core::IVersionControl::DeleteOperation: - case Core::IVersionControl::MoveOperation: - case Core::IVersionControl::CreateRepositoryOperation: - case Core::IVersionControl::AnnotateOperation: - case Core::IVersionControl::InitialCheckoutOperation: + case IVersionControl::AddOperation: + case IVersionControl::DeleteOperation: + case IVersionControl::MoveOperation: + case IVersionControl::CreateRepositoryOperation: + case IVersionControl::AnnotateOperation: + case IVersionControl::InitialCheckoutOperation: break; - case Core::IVersionControl::SnapshotOperations: + case IVersionControl::SnapshotOperations: supported = false; break; } @@ -957,9 +959,9 @@ VcsCommand *FossilPluginPrivate::createInitialCheckoutCommand(const QString &sou // -- open/checkout an existing local fossil // Clone URL is an absolute local path and is the same as the local fossil. - const Utils::FilePath checkoutPath = baseDirectory.pathAppended(localName); + const FilePath checkoutPath = baseDirectory.pathAppended(localName); const QString fossilFile = options.value("fossil-file"); - const Utils::FilePath fossilFilePath = Utils::FilePath::fromUserInput(QDir::fromNativeSeparators(fossilFile)); + const FilePath fossilFilePath = FilePath::fromUserInput(QDir::fromNativeSeparators(fossilFile)); const QString fossilFileNative = fossilFilePath.toUserOutput(); const QFileInfo cloneRepository(fossilFilePath.toString()); @@ -991,7 +993,7 @@ VcsCommand *FossilPluginPrivate::createInitialCheckoutCommand(const QString &sou && !cloneRepository.exists()) { const QString sslIdentityFile = options.value("ssl-identity"); - const Utils::FilePath sslIdentityFilePath = Utils::FilePath::fromUserInput(QDir::fromNativeSeparators(sslIdentityFile)); + const FilePath sslIdentityFilePath = FilePath::fromUserInput(QDir::fromNativeSeparators(sslIdentityFile)); const bool includePrivate = (options.value("include-private") == "true"); QStringList extraOptions; @@ -1053,7 +1055,7 @@ void FossilPluginPrivate::changed(const QVariant &v) { switch (v.type()) { case QVariant::String: - emit repositoryChanged(Utils::FilePath::fromVariant(v)); + emit repositoryChanged(FilePath::fromVariant(v)); break; case QVariant::StringList: emit filesChanged(v.toStringList()); @@ -1133,7 +1135,7 @@ void Fossil::Internal::FossilPlugin::testDiffFileResolving_data() void Fossil::Internal::FossilPlugin::testDiffFileResolving() { - VcsBase::VcsBaseEditorWidget::testDiffFileResolving(dd->diffFactory); + VcsBaseEditorWidget::testDiffFileResolving(dd->diffFactory); } void Fossil::Internal::FossilPlugin::testLogResolving() @@ -1146,6 +1148,6 @@ void Fossil::Internal::FossilPlugin::testLogResolving() " EDITED src/core/scaler.cpp\n" " EDITED src/core/scaler.h\n" ); - VcsBase::VcsBaseEditorWidget::testLogResolving(dd->fileLogFactory, data, "ac6d1129b8", "56d6917c3b"); + VcsBaseEditorWidget::testLogResolving(dd->fileLogFactory, data, "ac6d1129b8", "56d6917c3b"); } #endif diff --git a/src/plugins/fossil/fossilplugin.h b/src/plugins/fossil/fossilplugin.h index 7297cd05ae9..033e22822c0 100644 --- a/src/plugins/fossil/fossilplugin.h +++ b/src/plugins/fossil/fossilplugin.h @@ -3,9 +3,7 @@ #pragma once -#include <vcsbase/vcsbaseclient.h> #include <vcsbase/vcsbaseplugin.h> -#include <coreplugin/icontext.h> namespace Fossil { namespace Internal { diff --git a/src/plugins/fossil/fossilsettings.cpp b/src/plugins/fossil/fossilsettings.cpp index 0662445b131..2443e61c8f0 100644 --- a/src/plugins/fossil/fossilsettings.cpp +++ b/src/plugins/fossil/fossilsettings.cpp @@ -6,6 +6,7 @@ #include "constants.h" #include "fossiltr.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <utils/layoutbuilder.h> @@ -17,21 +18,16 @@ using namespace Utils; namespace Fossil::Internal { -static FossilSettings *theSettings; - FossilSettings &settings() { - return *theSettings; + static FossilSettings theSettings; + return theSettings; } FossilSettings::FossilSettings() { - theSettings = this; - + setAutoApply(false); setSettingsGroup(Constants::FOSSIL); - setId(Constants::VCS_ID_FOSSIL); - setDisplayName(Tr::tr("Fossil")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); binaryPath.setExpectedKind(PathChooser::ExistingCommand); binaryPath.setDefaultValue(Constants::FOSSILDEFAULT); @@ -119,6 +115,24 @@ FossilSettings::FossilSettings() st }; }); + + readSettings(); } +// FossilSettingsPage + +class FossilSettingsPage final : public Core::IOptionsPage +{ +public: + FossilSettingsPage() + { + setId(Constants::VCS_ID_FOSSIL); + setDisplayName(Tr::tr("Fossil")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const FossilSettingsPage settingsPage; + } // Fossil::Internal diff --git a/src/plugins/fossil/fossilsettings.h b/src/plugins/fossil/fossilsettings.h index 8e56b1985cc..f1b9b3f52b8 100644 --- a/src/plugins/fossil/fossilsettings.h +++ b/src/plugins/fossil/fossilsettings.h @@ -7,7 +7,7 @@ namespace Fossil::Internal { -class FossilSettings : public VcsBase::VcsBaseSettings +class FossilSettings final : public VcsBase::VcsBaseSettings { public: FossilSettings(); diff --git a/src/plugins/fossil/pullorpushdialog.cpp b/src/plugins/fossil/pullorpushdialog.cpp index b6473a68b41..8c0df1dd522 100644 --- a/src/plugins/fossil/pullorpushdialog.cpp +++ b/src/plugins/fossil/pullorpushdialog.cpp @@ -82,7 +82,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent) QString PullOrPushDialog::remoteLocation() const { if (m_defaultButton->isChecked()) - return QString(); + return {}; if (m_localButton->isChecked()) return m_localPathChooser->filePath().toString(); return m_urlLineEdit->text(); diff --git a/src/plugins/fossil/wizard/fossiljsextension.cpp b/src/plugins/fossil/wizard/fossiljsextension.cpp index 4c013b503a8..aaf5d172b67 100644 --- a/src/plugins/fossil/wizard/fossiljsextension.cpp +++ b/src/plugins/fossil/wizard/fossiljsextension.cpp @@ -47,26 +47,17 @@ QString FossilJsExtension::displayName() const QString FossilJsExtension::defaultAdminUser() const { - if (!isConfigured()) - return QString(); - - return settings().userName.value(); + return isConfigured() ? settings().userName() : QString(); } QString FossilJsExtension::defaultSslIdentityFile() const { - if (!isConfigured()) - return QString(); - - return settings().sslIdentityFile.value(); + return isConfigured() ? settings().sslIdentityFile().toFSPathString() : QString(); } QString FossilJsExtension::defaultLocalRepoPath() const { - if (!isConfigured()) - return QString(); - - return settings().defaultRepoPath.value(); + return isConfigured() ? settings().defaultRepoPath().toFSPathString() : QString(); } bool FossilJsExtension::defaultDisableAutosync() const @@ -74,7 +65,7 @@ bool FossilJsExtension::defaultDisableAutosync() const if (!isConfigured()) return false; - return settings().disableAutosync.value(); + return settings().disableAutosync(); } } // namespace Internal diff --git a/src/plugins/fossil/wizard/projects/vcs/wizard.json b/src/plugins/fossil/wizard/projects/vcs/wizard.json index 7f5fb174520..e1d92573574 100644 --- a/src/plugins/fossil/wizard/projects/vcs/wizard.json +++ b/src/plugins/fossil/wizard/projects/vcs/wizard.json @@ -80,7 +80,7 @@ "trIncompleteMessage": "The clone fossil already exists in local repositories path.", "data": { - "trText": "%{JS: (%{isCloneRepo} && '%{Repo}' !== '' && '%{FossilName}' !== '') || (%{isLocalRepo} && '%{LocalRepo}' !== '') ? 'true' : '' }" + "text": "%{JS: (%{isCloneRepo} && '%{Repo}' !== '' && '%{FossilName}' !== '') || (%{isLocalRepo} && '%{LocalRepo}' !== '') ? 'true' : '' }" } }, { @@ -100,7 +100,7 @@ "mandatory": false, "data": { - "trText": "%{defaultFossilName}" + "text": "%{defaultFossilName}" } }, { @@ -139,7 +139,7 @@ "trIncompleteMessage": "The checkout directory already exists in the filesystem.", "data": { - "trText": "%{defaultDir}" + "text": "%{defaultDir}" } }, { @@ -167,7 +167,7 @@ "enabled": "%{isCloneRepo}", "data": { - "trText": "%{JS: Fossil.defaultAdminUser()}" + "text": "%{JS: Fossil.defaultAdminUser()}" } }, { diff --git a/src/plugins/genericprojectmanager/GenericProjectManager.json.in b/src/plugins/genericprojectmanager/GenericProjectManager.json.in index c52201914a8..ec4eae552e8 100644 --- a/src/plugins/genericprojectmanager/GenericProjectManager.json.in +++ b/src/plugins/genericprojectmanager/GenericProjectManager.json.in @@ -1,62 +1,62 @@ { - \"Name\" : \"GenericProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "GenericProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Generic support.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Build Systems", + "Description" : "Generic support.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", + "Mimetypes" : [ + "<?xml version='1.0'?>", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-generic-project\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Qt Creator Project file</comment>\", - \" <glob pattern=\'*.creator\'/>\", - \" </mime-type>\", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-generic-project'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Qt Creator Project file</comment>", + " <glob pattern='*.creator'/>", + " </mime-type>", - \" <mime-type type=\'application/vnd.qtcreator.generic.files\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Project Files</comment>\", - \" <glob pattern=\'*.files\'/>\", - \" </mime-type>\", + " <mime-type type='application/vnd.qtcreator.generic.files'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Project Files</comment>", + " <glob pattern='*.files'/>", + " </mime-type>", - \" <mime-type type=\'application/vnd.qtcreator.generic.includes\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Project Include Paths</comment>\", - \" <glob pattern=\'*.includes\'/>\", - \" </mime-type>\", + " <mime-type type='application/vnd.qtcreator.generic.includes'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Project Include Paths</comment>", + " <glob pattern='*.includes'/>", + " </mime-type>", - \" <mime-type type=\'application/vnd.qtcreator.generic.config\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Project Configuration File</comment>\", - \" <glob pattern=\'*.config\'/>\", - \" </mime-type>\", + " <mime-type type='application/vnd.qtcreator.generic.config'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Project Configuration File</comment>", + " <glob pattern='*.config'/>", + " </mime-type>", - \" <mime-type type=\'application/vnd.qtcreator.generic.cxxflags\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Project CXXFLAGS File</comment>\", - \" <glob pattern=\'*.cxxflags\'/>\", - \" </mime-type>\", + " <mime-type type='application/vnd.qtcreator.generic.cxxflags'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Project CXXFLAGS File</comment>", + " <glob pattern='*.cxxflags'/>", + " </mime-type>", - \" <mime-type type=\'application/vnd.qtcreator.generic.cflags\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Generic Project CFLAGS File</comment>\", - \" <glob pattern=\'*.cflags\'/>\", - \" </mime-type>\", + " <mime-type type='application/vnd.qtcreator.generic.cflags'>", + " <sub-class-of type='text/plain'/>", + " <comment>Generic Project CFLAGS File</comment>", + " <glob pattern='*.cflags'/>", + " </mime-type>", - \"</mime-info>\" + "</mime-info>" ] } diff --git a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp index 607a200dc90..5d1c4562762 100644 --- a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp +++ b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp @@ -12,7 +12,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorertr.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/aspects.h> #include <utils/qtcassert.h> diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index ef83f906a95..20afb9f99c1 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -24,7 +24,7 @@ #include <projectexplorer/customexecutablerunconfiguration.h> #include <projectexplorer/deploymentdata.h> #include <projectexplorer/headerpath.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/selectablefilesmodel.h> @@ -33,7 +33,7 @@ #include <qtsupport/baseqtversion.h> #include <qtsupport/qtcppkitinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/filesystemwatcher.h> @@ -398,7 +398,7 @@ static QStringList readFlags(const QString &filePath) { const QStringList lines = readLines(filePath); if (lines.isEmpty()) - return QStringList(); + return {}; QStringList flags; for (const auto &line : lines) flags.append(ProcessArgs::splitArgs(line, HostOsInfo::hostOs())); @@ -619,7 +619,7 @@ void GenericBuildSystem::removeFiles(const FilePaths &filesToRemove) } } -Project::RestoreResult GenericProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult GenericProject::fromMap(const Store &map, QString *errorMessage) { const RestoreResult result = Project::fromMap(map, errorMessage); if (result != RestoreResult::Ok) @@ -663,7 +663,7 @@ void GenericProject::configureAsExampleProject(ProjectExplorer::Kit *kit) BuildInfo buildInfo; buildInfo.displayName = Tr::tr("Build %1").arg(i + 1); buildInfo.factory = factory; - buildInfo.kitId = kit->id(); + buildInfo.kitId = k->id(); buildInfo.buildDirectory = projectFilePath(); infoList << buildInfo; } diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index a3848402244..db72c2b872f 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -19,7 +19,7 @@ public: void removeFilesTriggered(const Utils::FilePaths &filesToRemove); private: - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final; + RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) final; ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const final; void configureAsExampleProject(ProjectExplorer::Kit *kit) override; }; diff --git a/src/plugins/genericprojectmanager/genericprojectmanager.qbs b/src/plugins/genericprojectmanager/genericprojectmanager.qbs index db678bd9dcd..5ce959e9a64 100644 --- a/src/plugins/genericprojectmanager/genericprojectmanager.qbs +++ b/src/plugins/genericprojectmanager/genericprojectmanager.qbs @@ -11,7 +11,6 @@ QtcPlugin { Depends { name: "TextEditor" } Depends { name: "ProjectExplorer" } Depends { name: "QtSupport" } - Depends { name: "app_version_header" } pluginRecommends: [ "CppEditor" diff --git a/src/plugins/genericprojectmanager/genericprojectwizard.cpp b/src/plugins/genericprojectmanager/genericprojectwizard.cpp index 1f125ff321c..314c850b0d2 100644 --- a/src/plugins/genericprojectmanager/genericprojectwizard.cpp +++ b/src/plugins/genericprojectmanager/genericprojectwizard.cpp @@ -12,7 +12,6 @@ #include <projectexplorer/projectexplorericons.h> #include <projectexplorer/customwizard/customwizard.h> -#include <app/app_version.h> #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/filewizardpage.h> @@ -97,9 +96,10 @@ GenericProjectWizard::GenericProjectWizard() setIcon(ProjectExplorer::Icons::WIZARD_IMPORT_AS_PROJECT.icon()); setDisplayName(Tr::tr("Import Existing Project")); setId("Z.Makefile"); - setDescription(Tr::tr("Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools. " - "This allows you to use %1 as a code editor.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + setDescription( + Tr::tr("Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools. " + "This allows you to use %1 as a code editor.") + .arg(QGuiApplication::applicationDisplayName())); setCategory(QLatin1String(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY)); setDisplayCategory(QLatin1String(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY_DISPLAY)); setFlags(Core::IWizardFactory::PlatformIndependent); diff --git a/src/plugins/git/Git.json.in b/src/plugins/git/Git.json.in index 244ed2f8435..db0663a8765 100644 --- a/src/plugins/git/Git.json.in +++ b/src/plugins/git/Git.json.in @@ -1,43 +1,43 @@ { - \"Name\" : \"Git\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Git", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Git integration.\", - \"Url\" : \"http://www.qt.io\", - \"Arguments\" : [ + "Category" : "Version Control", + "Description" : "Git integration.", + "Url" : "http://www.qt.io", + "Arguments" : [ { - \"Name\" : \"-git-show\", - \"Parameter\" : \"git commit hash\", - \"Description\" : \"Show given commit hash\" + "Name" : "-git-show", + "Parameter" : "git commit hash", + "Description" : "Show given commit hash" } ], - $$dependencyList, + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/vnd.qtcreator.git.commit\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Git Commit File</comment>\", - \" <glob pattern=\'COMMIT_MSG\'/>\", - \" <glob pattern=\'COMMIT_EDITMSG\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/vnd.qtcreator.git.rebase\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Git Commit File</comment>\", - \" <glob pattern=\'git-rebase-todo\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/vnd.qtcreator.git.commit'>", + " <sub-class-of type='text/plain'/>", + " <comment>Git Commit File</comment>", + " <glob pattern='COMMIT_MSG'/>", + " <glob pattern='COMMIT_EDITMSG'/>", + " </mime-type>", + " <mime-type type='text/vnd.qtcreator.git.rebase'>", + " <sub-class-of type='text/plain'/>", + " <comment>Git Commit File</comment>", + " <glob pattern='git-rebase-todo'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/git/branchmodel.cpp b/src/plugins/git/branchmodel.cpp index 3bf65cf5b38..f01d2903d61 100644 --- a/src/plugins/git/branchmodel.cpp +++ b/src/plugins/git/branchmodel.cpp @@ -201,9 +201,8 @@ public: class BranchModel::Private { public: - explicit Private(BranchModel *q, GitClient *client) : + explicit Private(BranchModel *q) : q(q), - client(client), rootNode(new BranchNode) { } @@ -222,7 +221,6 @@ public: void updateAllUpstreamStatus(BranchNode *node); BranchModel *q; - GitClient *client; FilePath workingDirectory; BranchNode *rootNode; BranchNode *currentBranch = nullptr; @@ -249,12 +247,10 @@ public: // BranchModel: // -------------------------------------------------------------------------- -BranchModel::BranchModel(GitClient *client, QObject *parent) : +BranchModel::BranchModel(QObject *parent) : QAbstractItemModel(parent), - d(new Private(this, client)) + d(new Private(this)) { - QTC_CHECK(d->client); - // Abuse the sha field for ref prefix d->rootNode->append(new BranchNode(Tr::tr("Local Branches"), "refs/heads")); d->rootNode->append(new BranchNode(Tr::tr("Remote Branches"), "refs/remotes")); @@ -268,22 +264,22 @@ BranchModel::~BranchModel() QModelIndex BranchModel::index(int row, int column, const QModelIndex &parentIdx) const { if (column > 1) - return QModelIndex(); + return {}; BranchNode *parentNode = indexToNode(parentIdx); if (row >= parentNode->count()) - return QModelIndex(); + return {}; return nodeToIndex(parentNode->children.at(row), column); } QModelIndex BranchModel::parent(const QModelIndex &index) const { if (!index.isValid()) - return QModelIndex(); + return {}; BranchNode *node = indexToNode(index); if (node->parent == d->rootNode) - return QModelIndex(); + return {}; return nodeToIndex(node->parent, ColumnBranch); } @@ -308,7 +304,7 @@ QVariant BranchModel::data(const QModelIndex &index, int role) const BranchNode *node = indexToNode(index); if (!node) - return QVariant(); + return {}; switch (role) { case Qt::DisplayRole: { @@ -334,7 +330,7 @@ QVariant BranchModel::data(const QModelIndex &index, int role) const return index.column() == 0 ? node->fullRef() : QVariant(); case Qt::ToolTipRole: if (!node->isLeaf()) - return QVariant(); + return {}; if (node->toolTip.isEmpty()) node->toolTip = toolTip(node->sha); return node->toolTip; @@ -350,7 +346,7 @@ QVariant BranchModel::data(const QModelIndex &index, int role) const return font; } default: - return QVariant(); + return {}; } } @@ -415,7 +411,7 @@ void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError) } const ProcessTask topRevisionProc = - d->client->topRevision(workingDirectory, + gitClient().topRevision(workingDirectory, [=](const QString &ref, const QDateTime &dateTime) { d->currentSha = ref; d->currentDateTime = dateTime; @@ -430,7 +426,7 @@ void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError) "refs/remotes/**"}; if (settings().showTags()) args << "refs/tags/**"; - d->client->setupCommand(process, workingDirectory, args); + gitClient().setupCommand(process, workingDirectory, args); }; const auto forEachRefDone = [=](const Process &process) { @@ -481,7 +477,7 @@ void BranchModel::refresh(const FilePath &workingDirectory, ShowError showError) void BranchModel::setCurrentBranch() { - const QString currentBranch = d->client->synchronousCurrentLocalBranch(d->workingDirectory); + const QString currentBranch = gitClient().synchronousCurrentLocalBranch(d->workingDirectory); if (currentBranch.isEmpty()) return; @@ -499,8 +495,8 @@ void BranchModel::renameBranch(const QString &oldName, const QString &newName) { QString errorMessage; QString output; - if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-m", oldName, newName}, - &output, &errorMessage)) + if (!gitClient().synchronousBranchCmd(d->workingDirectory, {"-m", oldName, newName}, + &output, &errorMessage)) VcsOutputWindow::appendError(errorMessage); else refresh(d->workingDirectory); @@ -510,10 +506,10 @@ void BranchModel::renameTag(const QString &oldName, const QString &newName) { QString errorMessage; QString output; - if (!d->client->synchronousTagCmd(d->workingDirectory, {newName, oldName}, - &output, &errorMessage) - || !d->client->synchronousTagCmd(d->workingDirectory, {"-d", oldName}, - &output, &errorMessage)) { + if (!gitClient().synchronousTagCmd(d->workingDirectory, {newName, oldName}, + &output, &errorMessage) + || !gitClient().synchronousTagCmd(d->workingDirectory, {"-d", oldName}, + &output, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); } else { refresh(d->workingDirectory); @@ -528,17 +524,17 @@ FilePath BranchModel::workingDirectory() const QModelIndex BranchModel::currentBranch() const { if (!d->currentBranch) - return QModelIndex(); + return {}; return nodeToIndex(d->currentBranch, ColumnBranch); } QString BranchModel::fullName(const QModelIndex &idx, bool includePrefix) const { if (!idx.isValid()) - return QString(); + return {}; BranchNode *node = indexToNode(idx); if (!node || !node->isLeaf()) - return QString(); + return {}; if (node == d->headNode) return QString("HEAD"); return node->fullRef(includePrefix); @@ -547,15 +543,14 @@ QString BranchModel::fullName(const QModelIndex &idx, bool includePrefix) const QStringList BranchModel::localBranchNames() const { if (!d->rootNode || !d->rootNode->count()) - return QStringList(); - + return {}; return d->rootNode->children.at(LocalBranches)->childrenNames() + d->obsoleteLocalBranches; } QString BranchModel::sha(const QModelIndex &idx) const { if (!idx.isValid()) - return QString(); + return {}; BranchNode *node = indexToNode(idx); return node->sha; } @@ -563,13 +558,11 @@ QString BranchModel::sha(const QModelIndex &idx) const QDateTime BranchModel::dateTime(const QModelIndex &idx) const { if (!idx.isValid()) - return QDateTime(); + return {}; BranchNode *node = indexToNode(idx); return node->dateTime; } - - bool BranchModel::isHead(const QModelIndex &idx) const { if (!idx.isValid()) @@ -610,7 +603,7 @@ void BranchModel::removeBranch(const QModelIndex &idx) QString errorMessage; QString output; - if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-D", branch}, &output, &errorMessage)) { + if (!gitClient().synchronousBranchCmd(d->workingDirectory, {"-D", branch}, &output, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); return; } @@ -626,7 +619,7 @@ void BranchModel::removeTag(const QModelIndex &idx) QString errorMessage; QString output; - if (!d->client->synchronousTagCmd(d->workingDirectory, {"-d", tag}, &output, &errorMessage)) { + if (!gitClient().synchronousTagCmd(d->workingDirectory, {"-d", tag}, &output, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); return; } @@ -642,8 +635,8 @@ void BranchModel::checkoutBranch(const QModelIndex &idx, const QObject *context, // No StashGuard since this function for now is only used with clean working dir. // If it is ever used from another place, please add StashGuard here - d->client->checkout(d->workingDirectory, branch, GitClient::StashMode::NoStash, - context, handler); + gitClient().checkout(d->workingDirectory, branch, GitClient::StashMode::NoStash, + context, handler); } bool BranchModel::branchIsMerged(const QModelIndex &idx) @@ -655,8 +648,8 @@ bool BranchModel::branchIsMerged(const QModelIndex &idx) QString errorMessage; QString output; - if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-a", "--contains", sha(idx)}, - &output, &errorMessage)) { + if (!gitClient().synchronousBranchCmd(d->workingDirectory, {"-a", "--contains", sha(idx)}, + &output, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); } @@ -684,7 +677,7 @@ static int positionForName(BranchNode *node, const QString &name) QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModelIndex &startPoint) { if (!d->rootNode || !d->rootNode->count()) - return QModelIndex(); + return {}; const QString trackedBranch = fullName(startPoint); const QString fullTrackedBranch = fullName(startPoint, true); @@ -700,17 +693,17 @@ QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModel branchDateTime = dateTime(startPoint); } else { const QStringList arguments({"-n1", "--format=%H %ct"}); - if (d->client->synchronousLog(d->workingDirectory, arguments, &output, &errorMessage, - RunFlags::SuppressCommandLogging)) { + if (gitClient().synchronousLog(d->workingDirectory, arguments, &output, &errorMessage, + RunFlags::SuppressCommandLogging)) { const QStringList values = output.split(' '); startSha = values[0]; branchDateTime = QDateTime::fromSecsSinceEpoch(values[1].toLongLong()); } } - if (!d->client->synchronousBranchCmd(d->workingDirectory, args, &output, &errorMessage)) { + if (!gitClient().synchronousBranchCmd(d->workingDirectory, args, &output, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); - return QModelIndex(); + return {}; } BranchNode *local = d->rootNode->children.at(LocalBranches); @@ -748,7 +741,7 @@ void BranchModel::setRemoteTracking(const QModelIndex &trackingIndex) const QString currentName = fullName(current); const QString shortTracking = fullName(trackingIndex); const QString tracking = fullName(trackingIndex, true); - d->client->synchronousSetTrackingBranch(d->workingDirectory, currentName, tracking); + gitClient().synchronousSetTrackingBranch(d->workingDirectory, currentName, tracking); d->currentBranch->tracking = shortTracking; updateUpstreamStatus(d->currentBranch); emit dataChanged(current, current); @@ -766,7 +759,7 @@ std::optional<QString> BranchModel::remoteName(const QModelIndex &idx) const if (!node) return std::nullopt; if (node == remotesNode) - return QString(); + return {}; if (node->parent == remotesNode) return node->name; return std::nullopt; @@ -893,7 +886,7 @@ BranchNode *BranchModel::indexToNode(const QModelIndex &index) const QModelIndex BranchModel::nodeToIndex(BranchNode *node, int column) const { if (node == d->rootNode) - return QModelIndex(); + return {}; return createIndex(node->parent->rowOf(node), column, static_cast<void *>(node)); } @@ -920,8 +913,8 @@ void BranchModel::updateUpstreamStatus(BranchNode *node) return; Process *process = new Process(node); - process->setEnvironment(d->client->processEnvironment()); - process->setCommand({d->client->vcsBinary(), {"rev-list", "--no-color", "--left-right", + process->setEnvironment(gitClient().processEnvironment()); + process->setCommand({gitClient().vcsBinary(), {"rev-list", "--no-color", "--left-right", "--count", node->fullRef() + "..." + node->tracking}}); process->setWorkingDirectory(d->workingDirectory); connect(process, &Process::done, this, [this, process, node] { @@ -958,8 +951,8 @@ QString BranchModel::toolTip(const QString &sha) const // Show the sha description excluding diff as toolTip QString output; QString errorMessage; - if (!d->client->synchronousLog(d->workingDirectory, {"-n1", sha}, &output, &errorMessage, - RunFlags::SuppressCommandLogging)) { + if (!gitClient().synchronousLog(d->workingDirectory, {"-n1", sha}, &output, &errorMessage, + RunFlags::SuppressCommandLogging)) { return errorMessage; } return output; diff --git a/src/plugins/git/branchmodel.h b/src/plugins/git/branchmodel.h index de126e9a5eb..e1f0cabee8d 100644 --- a/src/plugins/git/branchmodel.h +++ b/src/plugins/git/branchmodel.h @@ -16,12 +16,11 @@ class CommandResult; namespace Git::Internal { class BranchNode; -class GitClient; class BranchModel : public QAbstractItemModel { public: - explicit BranchModel(GitClient *client, QObject *parent = nullptr); + explicit BranchModel(QObject *parent = nullptr); ~BranchModel() override; // QAbstractItemModel diff --git a/src/plugins/git/branchview.cpp b/src/plugins/git/branchview.cpp index c3daf9d5a88..77d700147ae 100644 --- a/src/plugins/git/branchview.cpp +++ b/src/plugins/git/branchview.cpp @@ -78,7 +78,7 @@ BranchView::BranchView() , m_refreshAction(new QAction(this)) , m_repositoryLabel(new ElidingLabel(this)) , m_branchView(new NavigationTreeView(this)) - , m_model(new BranchModel(GitClient::instance(), this)) + , m_model(new BranchModel(this)) , m_filterModel(new BranchFilterModel(this)) { m_addAction->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); @@ -115,7 +115,7 @@ BranchView::BranchView() connect(m_includeOldEntriesAction, &QAction::toggled, this, &BranchView::setIncludeOldEntries); m_includeTagsAction->setCheckable(true); - m_includeTagsAction->setChecked(settings().showTags.value()); + m_includeTagsAction->setChecked(settings().showTags()); connect(m_includeTagsAction, &QAction::toggled, this, &BranchView::setIncludeTags); @@ -132,12 +132,15 @@ BranchView::BranchView() m_branchView->selectionModel()->clear(); m_repository = GitPlugin::currentState().topLevel(); - refreshCurrentRepository(); } void BranchView::refreshIfSame(const FilePath &repository) { - if (m_repository == repository) + if (m_repository != repository) + return; + if (m_blockRefresh) + m_postponedRefresh = true; + else refreshCurrentRepository(); } @@ -228,17 +231,18 @@ void BranchView::slotCustomContextMenu(const QPoint &point) std::unique_ptr<TaskTree> taskTree; QAction *mergeAction = nullptr; + SetInContext block(m_blockRefresh); QMenu contextMenu; contextMenu.addAction(Tr::tr("&Add..."), this, &BranchView::add); const std::optional<QString> remote = m_model->remoteName(index); if (remote.has_value()) { contextMenu.addAction(Tr::tr("&Fetch"), this, [this, &remote] { - GitClient::instance()->fetch(m_repository, *remote); + gitClient().fetch(m_repository, *remote); }); contextMenu.addSeparator(); if (!remote->isEmpty()) { contextMenu.addAction(Tr::tr("Remove &Stale Branches"), this, [this, &remote] { - GitClient::instance()->removeStaleRemoteBranches(m_repository, *remote); + gitClient().removeStaleRemoteBranches(m_repository, *remote); }); contextMenu.addSeparator(); } @@ -256,10 +260,8 @@ void BranchView::slotCustomContextMenu(const QPoint &point) contextMenu.addSeparator(); contextMenu.addAction(Tr::tr("&Diff"), this, [this] { const QString fullName = m_model->fullName(selectedIndex(), true); - if (!fullName.isEmpty()) { - SetInContext block(m_blockRefresh); - GitClient::instance()->diffBranch(m_repository, fullName); - } + if (!fullName.isEmpty()) + gitClient().diffBranch(m_repository, fullName); }); contextMenu.addAction(Tr::tr("&Log"), this, [this] { log(selectedIndex()); }); contextMenu.addAction(Tr::tr("Reflo&g"), this, [this] { reflog(selectedIndex()); }); @@ -303,6 +305,10 @@ void BranchView::slotCustomContextMenu(const QPoint &point) } } contextMenu.exec(m_branchView->viewport()->mapToGlobal(point)); + if (m_postponedRefresh) { + refreshCurrentRepository(); + m_postponedRefresh = false; + } } void BranchView::expandAndResize() @@ -327,7 +333,7 @@ QModelIndex BranchView::selectedIndex() { QModelIndexList selected = m_branchView->selectionModel()->selectedIndexes(); if (selected.isEmpty()) - return QModelIndex(); + return {}; return m_filterModel->mapToSource(selected.at(0)); } @@ -388,13 +394,12 @@ bool BranchView::checkout() ' ' + nextBranch + "-AutoStash "; BranchCheckoutDialog branchCheckoutDialog(this, currentBranch, nextBranch); - GitClient *client = GitClient::instance(); - if (client->gitStatus(m_repository, StatusMode(NoUntracked | NoSubmodules)) != GitClient::StatusChanged) + if (gitClient().gitStatus(m_repository, StatusMode(NoUntracked | NoSubmodules)) != GitClient::StatusChanged) branchCheckoutDialog.foundNoLocalChanges(); QList<Stash> stashes; - client->synchronousStashList(m_repository, &stashes); + gitClient().synchronousStashList(m_repository, &stashes); for (const Stash &stash : std::as_const(stashes)) { if (stash.message.startsWith(popMessageStart)) { branchCheckoutDialog.foundStashForNextBranch(); @@ -409,13 +414,13 @@ bool BranchView::checkout() } else if (branchCheckoutDialog.exec() == QDialog::Accepted) { if (branchCheckoutDialog.makeStashOfCurrentBranch()) { - if (client->synchronousStash(m_repository, currentBranch + "-AutoStash").isEmpty()) + if (gitClient().synchronousStash(m_repository, currentBranch + "-AutoStash").isEmpty()) return false; } else if (branchCheckoutDialog.moveLocalChangesToNextBranch()) { - if (!client->beginStashScope(m_repository, "Checkout", NoPrompt)) + if (!gitClient().beginStashScope(m_repository, "Checkout", NoPrompt)) return false; } else if (branchCheckoutDialog.discardLocalChanges()) { - if (!client->synchronousReset(m_repository)) + if (!gitClient().synchronousReset(m_repository)) return false; } @@ -423,18 +428,18 @@ bool BranchView::checkout() const bool popStash = branchCheckoutDialog.popStashOfNextBranch(); const auto commandHandler = [=](const CommandResult &) { if (moveChanges) { - client->endStashScope(m_repository); + gitClient().endStashScope(m_repository); } else if (popStash) { QList<Stash> stashes; QString stashName; - client->synchronousStashList(m_repository, &stashes); + gitClient().synchronousStashList(m_repository, &stashes); for (const Stash &stash : std::as_const(stashes)) { if (stash.message.startsWith(popMessageStart)) { stashName = stash.name; break; } } - client->synchronousStashRestore(m_repository, stashName, true); + gitClient().synchronousStashRestore(m_repository, stashName, true); } }; m_model->checkoutBranch(selected, this, commandHandler); @@ -520,7 +525,7 @@ bool BranchView::reset(const QByteArray &resetType) if (QMessageBox::question(this, Tr::tr("Git Reset"), Tr::tr("Reset branch \"%1\" to \"%2\"?") .arg(currentName, branchName), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - GitClient::instance()->reset(m_repository, QLatin1String("--" + resetType), branchName); + gitClient().reset(m_repository, QLatin1String("--" + resetType), branchName); return true; } return false; @@ -541,22 +546,21 @@ TaskTree *BranchView::onFastForwardMerge(const std::function<void()> &callback) const TreeStorage<FastForwardStorage> storage; - GitClient *client = GitClient::instance(); const auto setupMergeBase = [=](Process &process) { - client->setupCommand(process, m_repository, {"merge-base", "HEAD", branch}); + gitClient().setupCommand(process, m_repository, {"merge-base", "HEAD", branch}); }; const auto onMergeBaseDone = [storage](const Process &process) { storage->mergeBase = process.cleanedStdOut().trimmed(); }; - const ProcessTask topRevisionProc = client->topRevision( + const ProcessTask topRevisionProc = gitClient().topRevision( m_repository, [storage](const QString &revision, const QDateTime &) { storage->topRevision = revision; }); const Group root { - Storage(storage), + Tasking::Storage(storage), parallel, ProcessTask(setupMergeBase, onMergeBaseDone), topRevisionProc, @@ -578,9 +582,8 @@ bool BranchView::merge(bool allowFastForward) QTC_CHECK(selected != m_model->currentBranch()); const QString branch = m_model->fullName(selected, true); - GitClient *client = GitClient::instance(); - if (client->beginStashScope(m_repository, "merge", AllowUnstashed)) - return client->synchronousMerge(m_repository, branch, allowFastForward); + if (gitClient().beginStashScope(m_repository, "merge", AllowUnstashed)) + return gitClient().synchronousMerge(m_repository, branch, allowFastForward); return false; } @@ -593,9 +596,8 @@ void BranchView::rebase() QTC_CHECK(selected != m_model->currentBranch()); const QString baseBranch = m_model->fullName(selected, true); - GitClient *client = GitClient::instance(); - if (client->beginStashScope(m_repository, "rebase")) - client->rebase(m_repository, baseBranch); + if (gitClient().beginStashScope(m_repository, "rebase")) + gitClient().rebase(m_repository, baseBranch); } bool BranchView::cherryPick() @@ -606,7 +608,7 @@ bool BranchView::cherryPick() QTC_CHECK(selected != m_model->currentBranch()); const QString branch = m_model->fullName(selected, true); - return GitClient::instance()->synchronousCherryPick(m_repository, branch); + return gitClient().synchronousCherryPick(m_repository, branch); } void BranchView::log(const QModelIndex &idx) @@ -615,7 +617,7 @@ void BranchView::log(const QModelIndex &idx) if (branchName.isEmpty()) return; SetInContext block(m_blockRefresh); - GitClient::instance()->log(m_repository, QString(), false, {branchName}); + gitClient().log(m_repository, QString(), false, {branchName}); } void BranchView::reflog(const QModelIndex &idx) @@ -624,7 +626,7 @@ void BranchView::reflog(const QModelIndex &idx) if (branchName.isEmpty()) return; SetInContext block(m_blockRefresh); - GitClient::instance()->reflog(m_repository, branchName); + gitClient().reflog(m_repository, branchName); } void BranchView::push() @@ -640,7 +642,7 @@ void BranchView::push() const QString remoteBranch = fullTargetName.mid(pos + 1); const QStringList pushArgs = {remoteName, localBranch + ':' + remoteBranch}; - GitClient::instance()->push(m_repository, pushArgs); + gitClient().push(m_repository, pushArgs); } BranchViewFactory::BranchViewFactory() diff --git a/src/plugins/git/branchview.h b/src/plugins/git/branchview.h index d272a5e2194..5c16af16d5b 100644 --- a/src/plugins/git/branchview.h +++ b/src/plugins/git/branchview.h @@ -75,6 +75,7 @@ private: BranchFilterModel *m_filterModel = nullptr; Utils::FilePath m_repository; bool m_blockRefresh = false; + bool m_postponedRefresh = false; }; class BranchViewFactory : public Core::INavigationWidgetFactory diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp index 8e4a6bdc997..cf991e6e938 100644 --- a/src/plugins/git/changeselectiondialog.cpp +++ b/src/plugins/git/changeselectiondialog.cpp @@ -33,8 +33,8 @@ ChangeSelectionDialog::ChangeSelectionDialog(const FilePath &workingDirectory, I QWidget *parent) : QDialog(parent) { - m_gitExecutable = GitClient::instance()->vcsBinary(); - m_gitEnvironment = GitClient::instance()->processEnvironment(); + m_gitExecutable = gitClient().vcsBinary(); + m_gitEnvironment = gitClient().processEnvironment(); resize(550, 350); setWindowTitle(Tr::tr("Select a Git Commit")); @@ -208,10 +208,9 @@ void ChangeSelectionDialog::recalculateCompletion() if (workingDir.isEmpty()) return; - GitClient *client = GitClient::instance(); Process *process = new Process(this); - process->setEnvironment(client->processEnvironment()); - process->setCommand({client->vcsBinary(), {"for-each-ref", "--format=%(refname:short)"}}); + process->setEnvironment(gitClient().processEnvironment()); + process->setCommand({gitClient().vcsBinary(), {"for-each-ref", "--format=%(refname:short)"}}); process->setWorkingDirectory(workingDir); process->setUseCtrlCStub(true); connect(process, &Process::done, this, [this, process] { diff --git a/src/plugins/git/gerrit/authenticationdialog.cpp b/src/plugins/git/gerrit/authenticationdialog.cpp index 2fc855411d9..fd1eac05d61 100644 --- a/src/plugins/git/gerrit/authenticationdialog.cpp +++ b/src/plugins/git/gerrit/authenticationdialog.cpp @@ -39,7 +39,7 @@ static QString findEntry(const QString &line, const QString &type) const QRegularExpressionMatch match = entryMatch(line, type); if (match.hasMatch()) return match.captured(1); - return QString(); + return {}; } static bool replaceEntry(QString &line, const QString &type, const QString &value) diff --git a/src/plugins/git/gerrit/branchcombobox.cpp b/src/plugins/git/gerrit/branchcombobox.cpp index 6337b529de2..01a861ec427 100644 --- a/src/plugins/git/gerrit/branchcombobox.cpp +++ b/src/plugins/git/gerrit/branchcombobox.cpp @@ -14,7 +14,7 @@ BranchComboBox::BranchComboBox(QWidget *parent) : QComboBox(parent) void BranchComboBox::init(const FilePath &repository) { m_repository = repository; - QString currentBranch = GitClient::instance()->synchronousCurrentLocalBranch(repository); + QString currentBranch = gitClient().synchronousCurrentLocalBranch(repository); if (currentBranch.isEmpty()) { m_detached = true; currentBranch = "HEAD"; @@ -22,7 +22,7 @@ void BranchComboBox::init(const FilePath &repository) } QString output; const QString branchPrefix("refs/heads/"); - if (!GitClient::instance()->synchronousForEachRefCmd( + if (!gitClient().synchronousForEachRefCmd( m_repository, {"--format=%(refname)", branchPrefix}, &output)) { return; } diff --git a/src/plugins/git/gerrit/gerritdialog.cpp b/src/plugins/git/gerrit/gerritdialog.cpp index 990c792e41a..9a78739d192 100644 --- a/src/plugins/git/gerrit/gerritdialog.cpp +++ b/src/plugins/git/gerrit/gerritdialog.cpp @@ -82,7 +82,6 @@ GerritDialog::GerritDialog(const QSharedPointer<GerritParameters> &p, m_treeView = new TreeView(changesGroup); m_treeView->setMinimumSize(QSize(600, 0)); m_treeView->setRootIsDecorated(false); - m_treeView->setUniformRowHeights(true); m_treeView->setSortingEnabled(true); auto detailsGroup = new QGroupBox(Git::Tr::tr("Details")); diff --git a/src/plugins/git/gerrit/gerritmodel.cpp b/src/plugins/git/gerrit/gerritmodel.cpp index 6457117b322..a4f31f3393d 100644 --- a/src/plugins/git/gerrit/gerritmodel.cpp +++ b/src/plugins/git/gerrit/gerritmodel.cpp @@ -81,7 +81,7 @@ static inline QString defaultUrl(const QSharedPointer<GerritParameters> &p, QString GerritPatchSet::approvalsToHtml() const { if (approvals.isEmpty()) - return QString(); + return {}; QString result; QTextStream str(&result); @@ -268,7 +268,7 @@ QueryContext::QueryContext(const QString &query, m_output.append(m_process.readAllRawStandardOutput()); }); connect(&m_process, &Process::done, this, &QueryContext::processDone); - m_process.setEnvironment(Git::Internal::GitClient::instance()->processEnvironment()); + m_process.setEnvironment(Git::Internal::gitClient().processEnvironment()); m_timer.setInterval(timeOutMS); m_timer.setSingleShot(true); @@ -411,7 +411,7 @@ QString GerritModel::toHtml(const QModelIndex& index) const static const QString neededByHeader = Git::Tr::tr("Needed by"); if (!index.isValid()) - return QString(); + return {}; const GerritChangePtr c = change(index); const QString serverPrefix = c->url.left(c->url.lastIndexOf('/') + 1); QString result; diff --git a/src/plugins/git/gerrit/gerritparameters.cpp b/src/plugins/git/gerrit/gerritparameters.cpp index ace85a81c47..781597e5e78 100644 --- a/src/plugins/git/gerrit/gerritparameters.cpp +++ b/src/plugins/git/gerrit/gerritparameters.cpp @@ -8,9 +8,9 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/pathchooser.h> +#include <utils/qtcsettings.h> #include <QDir> -#include <QSettings> #include <QStandardPaths> using namespace Utils; @@ -86,7 +86,7 @@ bool GerritParameters::equals(const GerritParameters &rhs) const return server == rhs.server && ssh == rhs.ssh && curl == rhs.curl && https == rhs.https; } -void GerritParameters::toSettings(QSettings *s) const +void GerritParameters::toSettings(QtcSettings *s) const { s->beginGroup(settingsGroupC); s->setValue(hostKeyC, server.host); @@ -99,16 +99,16 @@ void GerritParameters::toSettings(QSettings *s) const s->endGroup(); } -void GerritParameters::saveQueries(QSettings *s) const +void GerritParameters::saveQueries(QtcSettings *s) const { s->beginGroup(settingsGroupC); s->setValue(savedQueriesKeyC, savedQueries.join(',')); s->endGroup(); } -void GerritParameters::fromSettings(const QSettings *s) +void GerritParameters::fromSettings(const QtcSettings *s) { - const QString rootKey = QLatin1String(settingsGroupC) + '/'; + const Key rootKey = Key(settingsGroupC) + '/'; server.host = s->value(rootKey + hostKeyC, GerritServer::defaultHost()).toString(); server.user.userName = s->value(rootKey + userKeyC, QString()).toString(); ssh = FilePath::fromSettings(s->value(rootKey + sshKeyC, QString())); diff --git a/src/plugins/git/gerrit/gerritparameters.h b/src/plugins/git/gerrit/gerritparameters.h index 75806aa4f57..81644dcfa39 100644 --- a/src/plugins/git/gerrit/gerritparameters.h +++ b/src/plugins/git/gerrit/gerritparameters.h @@ -7,7 +7,7 @@ #include <utils/filepath.h> -QT_FORWARD_DECLARE_CLASS(QSettings) +namespace Utils { class QtcSettings; } namespace Gerrit { namespace Internal { @@ -19,9 +19,9 @@ public: bool isValid() const; bool equals(const GerritParameters &rhs) const; - void toSettings(QSettings *) const; - void saveQueries(QSettings *) const; - void fromSettings(const QSettings *); + void toSettings(Utils::QtcSettings *) const; + void saveQueries(Utils::QtcSettings *) const; + void fromSettings(const Utils::QtcSettings *); void setPortFlagBySshType(); friend bool operator==(const GerritParameters &p1, const GerritParameters &p2) diff --git a/src/plugins/git/gerrit/gerritplugin.cpp b/src/plugins/git/gerrit/gerritplugin.cpp index 87ce5bfdaca..f39cc538609 100644 --- a/src/plugins/git/gerrit/gerritplugin.cpp +++ b/src/plugins/git/gerrit/gerritplugin.cpp @@ -101,7 +101,7 @@ FetchContext::FetchContext(const QSharedPointer<GerritChange> &change, VcsBase::VcsOutputWindow::append(QString::fromLocal8Bit(m_process.readAllRawStandardOutput())); }); m_process.setWorkingDirectory(repository); - m_process.setEnvironment(GitClient::instance()->processEnvironment()); + m_process.setEnvironment(gitClient().processEnvironment()); } void FetchContext::start() @@ -136,7 +136,7 @@ void FetchContext::show() { const QString title = QString::number(m_change->number) + '/' + QString::number(m_change->currentPatchSet.patchSetNumber); - GitClient::instance()->show(m_repository, "FETCH_HEAD", title); + gitClient().show(m_repository, "FETCH_HEAD", title); } void FetchContext::cherryPick() @@ -144,12 +144,12 @@ void FetchContext::cherryPick() // Point user to errors. VcsBase::VcsOutputWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus); - GitClient::instance()->synchronousCherryPick(m_repository, "FETCH_HEAD"); + gitClient().synchronousCherryPick(m_repository, "FETCH_HEAD"); } void FetchContext::checkout() { - GitClient::instance()->checkout(m_repository, "FETCH_HEAD"); + gitClient().checkout(m_repository, "FETCH_HEAD"); } GerritPlugin::GerritPlugin() @@ -219,7 +219,7 @@ void GerritPlugin::push(const FilePath &topLevel) dialog.storeTopic(); m_reviewers = dialog.reviewers(); - GitClient::instance()->push(topLevel, {dialog.selectedRemoteName(), dialog.pushTarget()}); + gitClient().push(topLevel, {dialog.selectedRemoteName(), dialog.pushTarget()}); } static FilePath currentRepository() @@ -267,19 +267,19 @@ void GerritPlugin::push() Utils::FilePath GerritPlugin::gitBinDirectory() { - return GitClient::instance()->gitBinDirectory(); + return gitClient().gitBinDirectory(); } // Find the branch of a repository. QString GerritPlugin::branch(const FilePath &repository) { - return GitClient::instance()->synchronousCurrentLocalBranch(repository); + return gitClient().synchronousCurrentLocalBranch(repository); } void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode) { // Locate git. - const Utils::FilePath git = GitClient::instance()->vcsBinary(); + const Utils::FilePath git = gitClient().vcsBinary(); if (git.isEmpty()) { VcsBase::VcsOutputWindow::appendError(Git::Tr::tr("Git is not available.")); return; @@ -292,7 +292,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode) if (!repository.isEmpty()) { // Check if remote from a working dir is the same as remote from patch - QMap<QString, QString> remotesList = GitClient::instance()->synchronousRemotesList(repository); + QMap<QString, QString> remotesList = gitClient().synchronousRemotesList(repository); if (!remotesList.isEmpty()) { const QStringList remotes = remotesList.values(); for (QString remote : remotes) { @@ -305,7 +305,7 @@ void GerritPlugin::fetch(const QSharedPointer<GerritChange> &change, int mode) } if (!verifiedRepository) { - const SubmoduleDataMap submodules = GitClient::instance()->submoduleList(repository); + const SubmoduleDataMap submodules = gitClient().submoduleList(repository); for (const SubmoduleData &submoduleData : submodules) { QString remote = submoduleData.url; if (remote.endsWith(".git")) diff --git a/src/plugins/git/gerrit/gerritpushdialog.cpp b/src/plugins/git/gerrit/gerritpushdialog.cpp index 87961c5945a..a7d28060faf 100644 --- a/src/plugins/git/gerrit/gerritpushdialog.cpp +++ b/src/plugins/git/gerrit/gerritpushdialog.cpp @@ -54,16 +54,16 @@ QString GerritPushDialog::determineRemoteBranch(const QString &localBranch) QString output; QString error; - if (!GitClient::instance()->synchronousBranchCmd( + if (!gitClient().synchronousBranchCmd( m_workingDir, {"-r", "--contains", earliestCommit + '^'}, &output, &error)) { - return QString(); + return {}; } const QString head = "/HEAD"; const QStringList refs = output.split('\n'); QString remoteTrackingBranch; if (localBranch != "HEAD") - remoteTrackingBranch = GitClient::instance()->synchronousTrackingBranch(m_workingDir, localBranch); + remoteTrackingBranch = gitClient().synchronousTrackingBranch(m_workingDir, localBranch); QString remoteBranch; for (const QString &reference : refs) { @@ -87,7 +87,7 @@ void GerritPushDialog::initRemoteBranches() const QString head = "/HEAD"; const QString remotesPrefix("refs/remotes/"); - if (!GitClient::instance()->synchronousForEachRefCmd( + if (!gitClient().synchronousForEachRefCmd( m_workingDir, {"--format=%(refname)\t%(committerdate:raw)", remotesPrefix}, &output)) { return; } @@ -198,7 +198,7 @@ QString GerritPushDialog::calculateChangeRange(const QString &branch) const QString remote = selectedRemoteName() + '/' + selectedRemoteBranchName(); QString number; QString error; - GitClient::instance()->synchronousRevListCmd( + gitClient().synchronousRevListCmd( m_workingDir, { remote + ".." + branch, "--count" }, &number, &error); number.chop(1); return number; @@ -322,7 +322,7 @@ QString GerritPushDialog::pushTarget() const void GerritPushDialog::storeTopic() { const QString branch = m_localBranchComboBox->currentText(); - GitClient::instance()->setConfigValue( + gitClient().setConfigValue( m_workingDir, QString("branch.%1.topic").arg(branch), selectedTopic()); } @@ -335,7 +335,7 @@ void GerritPushDialog::setRemoteBranches(bool includeOld) const QString remoteName = selectedRemoteName(); if (!m_remoteBranches.contains(remoteName)) { const QStringList remoteBranches = - GitClient::instance()->synchronousRepositoryBranches(remoteName, m_workingDir); + gitClient().synchronousRepositoryBranches(remoteName, m_workingDir); for (const QString &branch : remoteBranches) m_remoteBranches.insertMulti(remoteName, {branch, {}}); if (remoteBranches.isEmpty()) { @@ -373,7 +373,7 @@ void GerritPushDialog::updateCommits(int index) { const QString branch = m_localBranchComboBox->itemText(index); m_hasLocalCommits = m_commitView->init(m_workingDir, branch, LogChangeWidget::Silent); - const QString topic = GitClient::instance()->readConfigValue( + const QString topic = gitClient().readConfigValue( m_workingDir, QString("branch.%1.topic").arg(branch)); if (!topic.isEmpty()) m_topicLineEdit->setText(topic); diff --git a/src/plugins/git/gerrit/gerritremotechooser.cpp b/src/plugins/git/gerrit/gerritremotechooser.cpp index 2804d75333d..d3363051857 100644 --- a/src/plugins/git/gerrit/gerritremotechooser.cpp +++ b/src/plugins/git/gerrit/gerritremotechooser.cpp @@ -85,7 +85,7 @@ bool GerritRemoteChooser::updateRemotes(bool forceReload) m_remotes.clear(); QString errorMessage; // Mute errors. We'll just fallback to the defaults const QMap<QString, QString> remotesList = - Git::Internal::GitClient::instance()->synchronousRemotesList(m_repository, &errorMessage); + Git::Internal::gitClient().synchronousRemotesList(m_repository, &errorMessage); for (auto mapIt = remotesList.cbegin(), end = remotesList.cend(); mapIt != end; ++mapIt) { GerritServer server; if (!server.fillFromRemote(mapIt.value(), *m_parameters, forceReload)) diff --git a/src/plugins/git/gerrit/gerritserver.cpp b/src/plugins/git/gerrit/gerritserver.cpp index a02174b2053..114e8b020c2 100644 --- a/src/plugins/git/gerrit/gerritserver.cpp +++ b/src/plugins/git/gerrit/gerritserver.cpp @@ -1,9 +1,10 @@ // Copyright (C) 2017 Orgad Shaneh <orgads@gmail.com>. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "gerritserver.h" + #include "authenticationdialog.h" #include "gerritparameters.h" -#include "gerritserver.h" #include "../gitclient.h" #include "../gittr.h" @@ -19,7 +20,6 @@ #include <QJsonObject> #include <QMessageBox> #include <QRegularExpression> -#include <QSettings> using namespace Git::Internal; using namespace Utils; @@ -169,8 +169,8 @@ bool GerritServer::fillFromRemote(const QString &remote, GerritServer::StoredHostValidity GerritServer::loadSettings() { StoredHostValidity validity = Invalid; - QSettings *settings = Core::ICore::settings(); - settings->beginGroup("Gerrit/" + host); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup("Gerrit/" + keyFromString(host)); if (!settings->value(isGerritKey, true).toBool()) { validity = NotGerrit; } else if (settings->contains(isAuthenticatedKey)) { @@ -187,8 +187,8 @@ GerritServer::StoredHostValidity GerritServer::loadSettings() void GerritServer::saveSettings(StoredHostValidity validity) const { - QSettings *settings = Core::ICore::settings(); - settings->beginGroup("Gerrit/" + host); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup("Gerrit/" + keyFromString(host)); switch (validity) { case NotGerrit: settings->setValue(isGerritKey, false); @@ -222,9 +222,8 @@ QStringList GerritServer::curlArguments() const int GerritServer::testConnection() { - static GitClient *const client = GitClient::instance(); const QStringList arguments = curlArguments() << (url(RestUrl) + accountUrlC); - const CommandResult result = client->vcsSynchronousExec({}, {curlBinary, arguments}); + const CommandResult result = gitClient().vcsSynchronousExec({}, {curlBinary, arguments}); if (result.result() == ProcessResult::FinishedWithSuccess) { QString output = result.cleanedStdOut(); // Gerrit returns an empty response for /p/qt-creator/a/accounts/self @@ -310,9 +309,8 @@ bool GerritServer::resolveRoot() bool GerritServer::resolveVersion(const GerritParameters &p, bool forceReload) { - static GitClient *const client = GitClient::instance(); - QSettings *settings = Core::ICore::settings(); - const QString fullVersionKey = "Gerrit/" + host + '/' + versionKey; + QtcSettings *settings = Core::ICore::settings(); + const Key fullVersionKey = "Gerrit/" + keyFromString(host) + '/' + versionKey; version = settings->value(fullVersionKey).toString(); if (!version.isEmpty() && !forceReload) return true; @@ -321,8 +319,8 @@ bool GerritServer::resolveVersion(const GerritParameters &p, bool forceReload) if (port) arguments << p.portFlag << QString::number(port); arguments << hostArgument() << "gerrit" << "version"; - const CommandResult result = client->vcsSynchronousExec({}, {p.ssh, arguments}, - RunFlags::NoOutput); + const CommandResult result = gitClient().vcsSynchronousExec({}, {p.ssh, arguments}, + RunFlags::NoOutput); QString stdOut = result.cleanedStdOut().trimmed(); stdOut.remove("gerrit version "); version = stdOut; @@ -330,8 +328,8 @@ bool GerritServer::resolveVersion(const GerritParameters &p, bool forceReload) return false; } else { const QStringList arguments = curlArguments() << (url(RestUrl) + versionUrlC); - const CommandResult result = client->vcsSynchronousExec({}, {curlBinary, arguments}, - RunFlags::NoOutput); + const CommandResult result = gitClient().vcsSynchronousExec({}, {curlBinary, arguments}, + RunFlags::NoOutput); // REST endpoint for version is only available from 2.8 and up. Do not consider invalid // if it fails. if (result.result() == ProcessResult::FinishedWithSuccess) { diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 70954ff4214..3b561368f0e 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -82,8 +82,6 @@ using namespace VcsBase; namespace Git::Internal { -static GitClient *m_instance = nullptr; - static QString branchesDisplay(const QString &prefix, QStringList *branches, bool *first) { const int limit = 12; @@ -128,7 +126,7 @@ static void stage(DiffEditorController *diffController, const QString &patch, bo if (revert) args << "--reverse"; QString errorMessage; - if (GitClient::instance()->synchronousApplyPatch(baseDir, patchFile.fileName(), + if (gitClient().synchronousApplyPatch(baseDir, patchFile.fileName(), &errorMessage, args)) { if (errorMessage.isEmpty()) { if (revert) @@ -219,7 +217,7 @@ private: auto fixRightCommit = [this](const QString &commit) { if (!commit.isEmpty()) return commit; - if (m_instance->checkCommandInProgress(workingDirectory()) == GitClient::NoCommand) + if (gitClient().checkCommandInProgress(workingDirectory()) == GitClient::NoCommand) return QString(); return QString(HEAD); }; @@ -238,9 +236,7 @@ GitDiffEditorController::GitDiffEditorController(IDocument *document, const QStringList &extraArgs) : GitBaseDiffEditorController(document) { - using namespace Tasking; - - const TreeStorage<QString> diffInputStorage = inputStorage(); + const TreeStorage<QString> diffInputStorage; const auto setupDiff = [=](Process &process) { process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), {})); @@ -252,9 +248,9 @@ GitDiffEditorController::GitDiffEditorController(IDocument *document, }; const Group root { - Storage(diffInputStorage), + Tasking::Storage(diffInputStorage), ProcessTask(setupDiff, onDiffDone), - postProcessTask() + postProcessTask(diffInputStorage) }; setReloadRecipe(root); } @@ -298,15 +294,13 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin const QStringList &unstagedFiles) : GitBaseDiffEditorController(document) { - using namespace Tasking; - struct DiffStorage { QString m_stagedOutput; QString m_unstagedOutput; }; const TreeStorage<DiffStorage> storage; - const TreeStorage<QString> diffInputStorage = inputStorage(); + const TreeStorage<QString> diffInputStorage; const auto setupStaged = [this, stagedFiles](Process &process) { if (stagedFiles.isEmpty()) @@ -339,8 +333,8 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin }; const Group root { - Storage(storage), - Storage(diffInputStorage), + Tasking::Storage(storage), + Tasking::Storage(diffInputStorage), Group { parallel, continueOnDone, @@ -348,7 +342,7 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin ProcessTask(setupUnstaged, onUnstagedDone), onGroupDone(onStagingDone) }, - postProcessTask() + postProcessTask(diffInputStorage) }; setReloadRecipe(root); } @@ -365,7 +359,6 @@ ShowController::ShowController(IDocument *document, const QString &id) { setDisplayName("Git Show"); static const QString busyMessage = Tr::tr("<resolving>"); - using namespace Tasking; struct ReloadStorage { bool m_postProcessDescription = false; @@ -379,7 +372,7 @@ ShowController::ShowController(IDocument *document, const QString &id) }; const TreeStorage<ReloadStorage> storage; - const TreeStorage<QString> diffInputStorage = inputStorage(); + const TreeStorage<QString> diffInputStorage; const auto updateDescription = [this](const ReloadStorage &storage) { QString desc = storage.m_header; @@ -399,7 +392,7 @@ ShowController::ShowController(IDocument *document, const QString &id) }; const auto setupDescription = [this, id](Process &process) { - process.setCodec(m_instance->encoding(GitClient::EncodingCommit, workingDirectory())); + process.setCodec(gitClient().encoding(GitClient::EncodingCommit, workingDirectory())); setupCommand(process, {"show", "-s", noColorOption, showFormatC, id}); VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine()); setDescription(Tr::tr("Waiting for data...")); @@ -499,7 +492,7 @@ ShowController::ShowController(IDocument *document, const QString &id) QStringList parents; QString errorMessage; // TODO: it's trivial now to call below asynchonously, too - m_instance->synchronousParentRevisions(workingDirectory(), data->m_commit, + gitClient().synchronousParentRevisions(workingDirectory(), data->m_commit, &parents, &errorMessage); data->m_follows = {busyMessage}; data->m_follows.resize(parents.size()); @@ -536,8 +529,8 @@ ShowController::ShowController(IDocument *document, const QString &id) }; const Group root { - Storage(storage), - Storage(diffInputStorage), + Tasking::Storage(storage), + Tasking::Storage(diffInputStorage), parallel, onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }), Group { @@ -554,7 +547,7 @@ ShowController::ShowController(IDocument *document, const QString &id) }, Group { ProcessTask(setupDiff, onDiffDone), - postProcessTask() + postProcessTask(diffInputStorage) } }; setReloadRecipe(root); @@ -638,7 +631,7 @@ public: static bool gitHasRgbColors() { - const unsigned gitVersion = GitClient::instance()->gitVersion().result(); + const unsigned gitVersion = gitClient().gitVersion().result(); return gitVersion >= 0x020300U; } @@ -754,10 +747,10 @@ static void handleConflictResponse(const VcsBase::CommandResult &result, commit = errMatch.captured(1); if (commit.isEmpty() && files.isEmpty()) { - if (m_instance->checkCommandInProgress(workingDirectory) == GitClient::NoCommand) - m_instance->endStashScope(workingDirectory); + if (gitClient().checkCommandInProgress(workingDirectory) == GitClient::NoCommand) + gitClient().endStashScope(workingDirectory); } else { - m_instance->handleMergeConflicts(workingDirectory, commit, files, abortCommand); + gitClient().handleMergeConflicts(workingDirectory, commit, files, abortCommand); } } @@ -812,19 +805,21 @@ static inline void msgCannotRun(const QStringList &args, const FilePath &working // ---------------- GitClient +GitClient &gitClient() +{ + static GitClient client; + return client; +} + GitClient::GitClient() : VcsBase::VcsBaseClientImpl(&Internal::settings()) { - m_instance = this; m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2") .arg(QCoreApplication::applicationFilePath()) .arg(QCoreApplication::applicationPid()); } -GitClient *GitClient::instance() -{ - return m_instance; -} +GitClient::~GitClient() = default; GitSettings &GitClient::settings() { @@ -1285,7 +1280,7 @@ QStringList GitClient::setupCheckoutArguments(const FilePath &workingDirectory, ICore::dialogParent() /*parent*/, Tr::tr("Create Local Branch") /*title*/, Tr::tr("Would you like to create a local branch?") /*message*/, - QString("Git.CreateLocalBranchOnCheckout"), /* decider */ + Key("Git.CreateLocalBranchOnCheckout"), /* decider */ QMessageBox::Yes | QMessageBox::No /*buttons*/, QMessageBox::No /*default button*/, QMessageBox::No /*button to save*/) @@ -1677,7 +1672,7 @@ QString GitClient::synchronousTopic(const FilePath &workingDirectory) const // Detached HEAD, try a tag or remote branch QStringList references; if (!synchronousHeadRefs(workingDirectory, &references)) - return QString(); + return {}; const QString tagStart("refs/tags/"); const QString remoteStart("refs/remotes/"); @@ -2108,9 +2103,8 @@ bool GitClient::synchronousApplyPatch(const FilePath &workingDirectory, Environment GitClient::processEnvironment() const { Environment environment = VcsBaseClientImpl::processEnvironment(); - const QString gitPath = settings().path.value(); - environment.prependOrSetPath(FilePath::fromUserInput(gitPath)); - if (HostOsInfo::isWindowsHost() && settings().winSetHomeEnvironment.value()) { + environment.prependOrSetPath(settings().path()); + if (HostOsInfo::isWindowsHost() && settings().winSetHomeEnvironment()) { QString homePath; if (qtcEnvironmentVariableIsEmpty("HOMESHARE")) { homePath = QDir::toNativeSeparators(QDir::homePath()); @@ -2263,7 +2257,7 @@ QString GitClient::commandInProgressDescription(const FilePath &workingDirectory case Merge: return Tr::tr("MERGING"); } - return QString(); + return {}; } GitClient::CommandInProgress GitClient::checkCommandInProgress(const FilePath &workingDirectory) const @@ -2429,7 +2423,7 @@ static FilePath gitBinDir(const GitClient::GitKLaunchTrial trial, const FilePath if (trial == GitClient::SystemPath) return Environment::systemEnvironment().searchInPath("gitk").parentDir(); QTC_CHECK(false); - return FilePath(); + return {}; } void GitClient::tryLaunchingGitK(const Environment &env, @@ -2448,7 +2442,7 @@ void GitClient::tryLaunchingGitK(const Environment &env, binary = wish; } } - const QString gitkOpts = settings().gitkOptions.value(); + const QString gitkOpts = settings().gitkOptions(); if (!gitkOpts.isEmpty()) arguments.append(ProcessArgs::splitArgs(gitkOpts, HostOsInfo::hostOs())); if (!fileName.isEmpty()) @@ -2457,7 +2451,7 @@ void GitClient::tryLaunchingGitK(const Environment &env, // This should always use Process::startDetached (as not to kill // the child), but that does not have an environment parameter. - if (!settings().path.value().isEmpty()) { + if (!settings().path().isEmpty()) { auto process = new Process(const_cast<GitClient*>(this)); process->setWorkingDirectory(workingDirectory); process->setEnvironment(env); @@ -2519,7 +2513,7 @@ FilePath GitClient::gitBinDirectory() const { const QString git = vcsBinary().toString(); if (git.isEmpty()) - return FilePath(); + return {}; // Is 'git\cmd' in the path (folder containing .bats)? QString path = QFileInfo(git).absolutePath(); @@ -2535,7 +2529,7 @@ FilePath GitClient::gitBinDirectory() const // Git for Windows uses Git/usr/bin. Prefer that if it exists. QString usrBinPath = path; usrBinPath.replace(usrBinPath.size() - 3, 3, "usr/bin"); - if (QFile::exists(usrBinPath)) + if (QFileInfo::exists(usrBinPath)) path = usrBinPath; } } @@ -2565,7 +2559,7 @@ FilePath GitClient::vcsBinary() const bool ok; Utils::FilePath binary = settings().gitExecutable(&ok); if (!ok) - return Utils::FilePath(); + return {}; return binary; } @@ -3099,11 +3093,6 @@ void GitClient::handleMergeConflicts(const FilePath &workingDir, const QString & } } -void GitClient::addFuture(const QFuture<void> &future) -{ - m_synchronizer.addFuture(future); -} - // Subversion: git svn void GitClient::synchronousSubversionFetch(const FilePath &workingDirectory) const { @@ -3502,7 +3491,7 @@ bool GitClient::StashInfo::init(const FilePath &workingDirectory, const QString m_pushAction = pushAction; QString errorMessage; QString statusOutput; - switch (m_instance->gitStatus(m_workingDir, StatusMode(NoUntracked | NoSubmodules), + switch (gitClient().gitStatus(m_workingDir, StatusMode(NoUntracked | NoSubmodules), &statusOutput, &errorMessage)) { case GitClient::StatusChanged: if (m_flags & NoPrompt) @@ -3555,14 +3544,14 @@ void GitClient::StashInfo::stashPrompt(const QString &command, const QString &st msgBox.exec(); if (msgBox.clickedButton() == discardButton) { - m_stashResult = m_instance->synchronousReset(m_workingDir, QStringList(), errorMessage) ? + m_stashResult = gitClient().synchronousReset(m_workingDir, QStringList(), errorMessage) ? StashUnchanged : StashFailed; } else if (msgBox.clickedButton() == ignoreButton) { // At your own risk, so. m_stashResult = NotStashed; } else if (msgBox.clickedButton() == cancelButton) { m_stashResult = StashCanceled; } else if (msgBox.clickedButton() == stashButton) { - const bool result = m_instance->executeSynchronousStash( + const bool result = gitClient().executeSynchronousStash( m_workingDir, creatorStashMessage(command), false, errorMessage); m_stashResult = result ? StashUnchanged : StashFailed; } else if (msgBox.clickedButton() == stashAndPopButton) { @@ -3573,7 +3562,7 @@ void GitClient::StashInfo::stashPrompt(const QString &command, const QString &st void GitClient::StashInfo::executeStash(const QString &command, QString *errorMessage) { m_message = creatorStashMessage(command); - if (!m_instance->executeSynchronousStash(m_workingDir, m_message, false, errorMessage)) + if (!gitClient().executeSynchronousStash(m_workingDir, m_message, false, errorMessage)) m_stashResult = StashFailed; else m_stashResult = Stashed; @@ -3596,12 +3585,12 @@ void GitClient::StashInfo::end() { if (m_stashResult == Stashed) { QString stashName; - if (m_instance->stashNameFromMessage(m_workingDir, m_message, &stashName)) - m_instance->stashPop(m_workingDir, stashName); + if (gitClient().stashNameFromMessage(m_workingDir, m_message, &stashName)) + gitClient().stashPop(m_workingDir, stashName); } if (m_pushAction == NormalPush) - m_instance->push(m_workingDir); + gitClient().push(m_workingDir); else if (m_pushAction == PushToGerrit) GitPlugin::gerritPush(m_workingDir); @@ -3626,7 +3615,7 @@ QString GitClient::suggestedLocalBranchName( initialName = target.mid(target.lastIndexOf('/') + 1); } else { QString subject; - instance()->synchronousLog(workingDirectory, {"-n", "1", "--format=%s", target}, + gitClient().synchronousLog(workingDirectory, {"-n", "1", "--format=%s", target}, &subject, nullptr, RunFlags::NoOutput); initialName = subject.trimmed(); } @@ -3646,14 +3635,14 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr const FilePath &workingDir = fileWorkingDirectory(source); const bool isRange = change.contains(".."); menu->addAction(Tr::tr("Cherr&y-Pick %1").arg(change), [workingDir, change] { - m_instance->synchronousCherryPick(workingDir, change); + gitClient().synchronousCherryPick(workingDir, change); }); menu->addAction(Tr::tr("Re&vert %1").arg(change), [workingDir, change] { - m_instance->synchronousRevert(workingDir, change); + gitClient().synchronousRevert(workingDir, change); }); if (!isRange) { menu->addAction(Tr::tr("C&heckout %1").arg(change), [workingDir, change] { - m_instance->checkout(workingDir, change); + gitClient().checkout(workingDir, change); }); connect(menu->addAction(Tr::tr("&Interactive Rebase from %1...").arg(change)), &QAction::triggered, [workingDir, change] { @@ -3661,7 +3650,7 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr }); } QAction *logAction = menu->addAction(Tr::tr("&Log for %1").arg(change), [workingDir, change] { - m_instance->log(workingDir, QString(), false, {change}); + gitClient().log(workingDir, QString(), false, {change}); }); if (isRange) { menu->setDefaultAction(logAction); @@ -3670,13 +3659,13 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr if (!filePath.isDir()) { menu->addAction(Tr::tr("Sh&ow file \"%1\" on revision %2").arg(filePath.fileName(), change), [workingDir, change, source] { - m_instance->openShowEditor(workingDir, change, source); + gitClient().openShowEditor(workingDir, change, source); }); } menu->addAction(Tr::tr("Add &Tag for %1...").arg(change), [workingDir, change] { QString output; QString errorMessage; - m_instance->synchronousTagCmd(workingDir, QStringList(), &output, &errorMessage); + gitClient().synchronousTagCmd(workingDir, QStringList(), &output, &errorMessage); const QStringList tags = output.split('\n'); BranchAddDialog dialog(tags, BranchAddDialog::Type::AddTag, Core::ICore::dialogParent()); @@ -3684,7 +3673,7 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr if (dialog.exec() == QDialog::Rejected) return; - m_instance->synchronousTagCmd(workingDir, {dialog.branchName(), change}, + gitClient().synchronousTagCmd(workingDir, {dialog.branchName(), change}, &output, &errorMessage); VcsOutputWindow::append(output); if (!errorMessage.isEmpty()) @@ -3692,7 +3681,7 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr }); auto resetChange = [workingDir, change](const QByteArray &resetType) { - m_instance->reset(workingDir, QLatin1String("--" + resetType), change); + gitClient().reset(workingDir, QLatin1String("--" + resetType), change); }; auto resetMenu = new QMenu(Tr::tr("&Reset to Change %1").arg(change), menu); resetMenu->addAction(Tr::tr("&Hard"), std::bind(resetChange, "hard")); @@ -3703,18 +3692,18 @@ void GitClient::addChangeActions(QMenu *menu, const FilePath &source, const QStr menu->addAction((isRange ? Tr::tr("Di&ff %1") : Tr::tr("Di&ff Against %1")).arg(change), [workingDir, change] { - m_instance->diffRepository(workingDir, change, {}); + gitClient().diffRepository(workingDir, change, {}); }); if (!isRange) { - if (!m_instance->m_diffCommit.isEmpty()) { - menu->addAction(Tr::tr("Diff &Against Saved %1").arg(m_instance->m_diffCommit), + if (!gitClient().m_diffCommit.isEmpty()) { + menu->addAction(Tr::tr("Diff &Against Saved %1").arg(gitClient().m_diffCommit), [workingDir, change] { - m_instance->diffRepository(workingDir, m_instance->m_diffCommit, change); - m_instance->m_diffCommit.clear(); + gitClient().diffRepository(workingDir, gitClient().m_diffCommit, change); + gitClient().m_diffCommit.clear(); }); } menu->addAction(Tr::tr("&Save for Diff"), [change] { - m_instance->m_diffCommit = change; + gitClient().m_diffCommit = change; }); } } diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 3604eb9f3cf..03649f0f4e1 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -12,7 +12,6 @@ #include <vcsbase/vcsbaseclient.h> #include <utils/fileutils.h> -#include <utils/futuresynchronizer.h> #include <utils/process.h> #include <QObject> @@ -121,7 +120,7 @@ public: }; GitClient(); - static GitClient *instance(); + ~GitClient(); Utils::FilePath vcsBinary() const override; QFuture<unsigned> gitVersion() const; @@ -246,7 +245,7 @@ public: QString synchronousTopic(const Utils::FilePath &workingDirectory) const; bool synchronousRevParseCmd(const Utils::FilePath &workingDirectory, const QString &ref, QString *output, QString *errorMessage = nullptr) const; - Tasking::ProcessTask topRevision(const Utils::FilePath &workingDirectory, + Utils::ProcessTask topRevision(const Utils::FilePath &workingDirectory, const std::function<void(const QString &, const QDateTime &)> &callback); bool isRemoteCommit(const Utils::FilePath &workingDirectory, const QString &commit); @@ -331,7 +330,6 @@ public: bool isValidRevision(const QString &revision) const; void handleMergeConflicts(const Utils::FilePath &workingDir, const QString &commit, const QStringList &files, const QString &abortCommand); - void addFuture(const QFuture<void> &future); static QString msgNoChangedFiles(); static QString msgNoCommits(bool includeRemote); @@ -406,10 +404,10 @@ private: QString m_diffCommit; Utils::FilePaths m_updatedSubmodules; bool m_disableEditor = false; - // The synchronizer has cancelOnWait set to true by default. - Utils::FutureSynchronizer m_synchronizer; // for commit updates }; +GITSHARED_EXPORT GitClient &gitClient(); + class GitRemote : public Core::IVersionControl::RepoUrl { public: diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp index c7078dc2ba1..a8613688ea8 100644 --- a/src/plugins/git/giteditor.cpp +++ b/src/plugins/git/giteditor.cpp @@ -110,11 +110,11 @@ QString GitEditorWidget::changeUnderCursor(const QTextCursor &c) const // Any number is regarded as change number. cursor.select(QTextCursor::WordUnderCursor); if (!cursor.hasSelection()) - return QString(); + return {}; const QString change = cursor.selectedText(); if (m_changeNumberPattern.match(change).hasMatch()) return change; - return QString(); + return {}; } BaseAnnotationHighlighter *GitEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const @@ -132,7 +132,7 @@ static QString sanitizeBlameOutput(const QString &b) if (b.isEmpty()) return b; - const bool omitDate = settings().omitAnnotationDate.value(); + const bool omitDate = settings().omitAnnotationDate(); const QChar space(' '); const int parenPos = b.indexOf(')'); if (parenPos == -1) @@ -224,7 +224,7 @@ void GitEditorWidget::applyDiffChunk(const DiffChunk& chunk, PatchAction patchAc if (patchAction == PatchAction::Revert) args << "--reverse"; QString errorMessage; - if (GitClient::instance()->synchronousApplyPatch(baseDir, patchFile.fileName(), &errorMessage, args)) { + if (gitClient().synchronousApplyPatch(baseDir, patchFile.fileName(), &errorMessage, args)) { if (errorMessage.isEmpty()) VcsOutputWindow::append(Tr::tr("Chunk successfully staged")); else @@ -244,7 +244,7 @@ void GitEditorWidget::init() const bool isRebaseEditor = editorId == Git::Constants::GIT_REBASE_EDITOR_ID; if (!isCommitEditor && !isRebaseEditor) return; - const QChar commentChar = GitClient::instance()->commentChar(source()); + const QChar commentChar = gitClient().commentChar(source()); if (isCommitEditor) textDocument()->setSyntaxHighlighter(new GitSubmitHighlighter(commentChar)); else if (isRebaseEditor) @@ -274,14 +274,14 @@ void GitEditorWidget::aboutToOpen(const FilePath &filePath, const FilePath &real || editorId == Git::Constants::GIT_REBASE_EDITOR_ID) { const FilePath gitPath = filePath.absolutePath(); setSource(gitPath); - textDocument()->setCodec(GitClient::instance()->encoding(GitClient::EncodingCommit, gitPath)); + textDocument()->setCodec(gitClient().encoding(GitClient::EncodingCommit, gitPath)); } } QString GitEditorWidget::decorateVersion(const QString &revision) const { // Format verbose, SHA1 being first token - return GitClient::instance()->synchronousShortDescription(sourceWorkingDirectory(), revision); + return gitClient().synchronousShortDescription(sourceWorkingDirectory(), revision); } QStringList GitEditorWidget::annotationPreviousVersions(const QString &revision) const @@ -289,17 +289,17 @@ QStringList GitEditorWidget::annotationPreviousVersions(const QString &revision) QStringList revisions; QString errorMessage; // Get the SHA1's of the file. - if (!GitClient::instance()->synchronousParentRevisions( + if (!gitClient().synchronousParentRevisions( sourceWorkingDirectory(), revision, &revisions, &errorMessage)) { VcsOutputWindow::appendSilently(errorMessage); - return QStringList(); + return {}; } return revisions; } bool GitEditorWidget::isValidRevision(const QString &revision) const { - return GitClient::instance()->isValidRevision(revision); + return gitClient().isValidRevision(revision); } void GitEditorWidget::addChangeActions(QMenu *menu, const QString &change) @@ -317,7 +317,7 @@ QString GitEditorWidget::revisionSubject(const QTextBlock &inBlock) const return block.text().trimmed(); } } - return QString(); + return {}; } bool GitEditorWidget::supportChangeLinks() const @@ -363,21 +363,21 @@ QWidget *GitEditorWidget::addFilterWidget() QString GitEditorWidget::grepValue() const { if (!m_logFilterWidget) - return QString(); + return {}; return m_logFilterWidget->grepLineEdit->text(); } QString GitEditorWidget::pickaxeValue() const { if (!m_logFilterWidget) - return QString(); + return {}; return m_logFilterWidget->pickaxeLineEdit->text(); } QString GitEditorWidget::authorValue() const { if (!m_logFilterWidget) - return QString(); + return {}; return m_logFilterWidget->authorLineEdit->text(); } diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index f6324f152de..f4a2c4f452b 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -23,7 +23,6 @@ #include <QCheckBox> #include <QHBoxLayout> #include <QRegularExpressionValidator> -#include <QSettings> using namespace Core; using namespace TextEditor; @@ -131,16 +130,12 @@ static SearchResultItems parse(const QFuture<void> &future, const QString &input return items; } -static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParameters ¶meters) +static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParameters ¶meters, + const GitGrepParameters &gitParameters) { - const FilePath directory = FilePath::fromString(parameters.additionalParameters.toString()); - const GitGrepParameters gitParameters - = parameters.searchEngineParameters.value<GitGrepParameters>(); - const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':'; - - const auto setupProcess = [&](Process &process) { - const FilePath vcsBinary = GitClient::instance()->vcsBinary(); - const Environment environment = GitClient::instance()->processEnvironment(); + const auto setupProcess = [¶meters, gitParameters](Process &process) { + const FilePath vcsBinary = gitClient().vcsBinary(); + const Environment environment = gitClient().processEnvironment(); QStringList arguments = { "-c", "color.grep.match=bold red", @@ -174,12 +169,13 @@ static void runGitGrep(QPromise<SearchResultItems> &promise, const FileFindParam process.setEnvironment(environment); process.setCommand({vcsBinary, arguments}); - process.setWorkingDirectory(directory); + process.setWorkingDirectory(parameters.searchDir); }; - const auto outputParser = [&ref, &directory](const QFuture<void> &future, const QString &input, + const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':'; + const auto outputParser = [&ref, ¶meters](const QFuture<void> &future, const QString &input, const std::optional<QRegularExpression> ®Exp) { - return parse(future, input, regExp, ref, directory); + return parse(future, input, regExp, ref, parameters.searchDir); }; TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser); @@ -192,8 +188,7 @@ static bool isGitDirectory(const FilePath &path) return gitVc == VcsManager::findVersionControlForDirectory(path); } -GitGrep::GitGrep(GitClient *client) - : m_client(client) +GitGrep::GitGrep() { m_widget = new QWidget; auto layout = new QHBoxLayout(m_widget); @@ -206,7 +201,7 @@ GitGrep::GitGrep(GitClient *client) m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this)); layout->addWidget(m_treeLineEdit); // asynchronously check git version, add "recurse submodules" option if available - Utils::onResultReady(client->gitVersion(), this, + Utils::onResultReady(gitClient().gitVersion(), this, [this, pLayout = QPointer<QHBoxLayout>(layout)](unsigned version) { if (version >= 0x021300 && pLayout) { m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules")); @@ -215,7 +210,7 @@ GitGrep::GitGrep(GitClient *client) }); FindInFiles *findInFiles = FindInFiles::instance(); QTC_ASSERT(findInFiles, return); - connect(findInFiles, &FindInFiles::pathChanged, m_widget, [this](const FilePath &path) { + connect(findInFiles, &FindInFiles::searchDirChanged, m_widget, [this](const FilePath &path) { setEnabled(isGitDirectory(path)); }); connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled); @@ -245,47 +240,42 @@ QWidget *GitGrep::widget() const return m_widget; } -QVariant GitGrep::parameters() const +GitGrepParameters GitGrep::gitParameters() const { - GitGrepParameters params; - params.ref = m_treeLineEdit->text(); - if (m_recurseSubmodules) - params.recurseSubmodules = m_recurseSubmodules->isChecked(); - return QVariant::fromValue(params); + return {m_treeLineEdit->text(), m_recurseSubmodules && m_recurseSubmodules->isChecked()}; } -void GitGrep::readSettings(QSettings *settings) +void GitGrep::readSettings(QtcSettings *settings) { m_treeLineEdit->setText(settings->value(GitGrepRef).toString()); } -void GitGrep::writeSettings(QSettings *settings) const +void GitGrep::writeSettings(QtcSettings *settings) const { settings->setValue(GitGrepRef, m_treeLineEdit->text()); } -QFuture<SearchResultItems> GitGrep::executeSearch(const FileFindParameters ¶meters, - BaseFileFind *) +SearchExecutor GitGrep::searchExecutor() const { - return Utils::asyncRun(runGitGrep, parameters); + return [gitParameters = gitParameters()](const FileFindParameters ¶meters) { + return Utils::asyncRun(runGitGrep, parameters, gitParameters); + }; } -IEditor *GitGrep::openEditor(const SearchResultItem &item, - const FileFindParameters ¶meters) +EditorOpener GitGrep::editorOpener() const { - const GitGrepParameters params = parameters.searchEngineParameters.value<GitGrepParameters>(); - const QStringList &itemPath = item.path(); - if (params.ref.isEmpty() || itemPath.isEmpty()) - return nullptr; - const FilePath path = FilePath::fromUserInput(itemPath.first()); - const FilePath topLevel = FilePath::fromString(parameters.additionalParameters.toString()); - IEditor *editor = m_client->openShowEditor(topLevel, params.ref, path, - GitClient::ShowEditor::OnlyIfDifferent); - if (editor) - editor->gotoLine(item.mainRange().begin.line, item.mainRange().begin.column); - return editor; + return [params = gitParameters()](const SearchResultItem &item, + const FileFindParameters ¶meters) -> IEditor * { + const QStringList &itemPath = item.path(); + if (params.ref.isEmpty() || itemPath.isEmpty()) + return nullptr; + const FilePath path = FilePath::fromUserInput(itemPath.first()); + IEditor *editor = gitClient().openShowEditor( + parameters.searchDir, params.ref, path, GitClient::ShowEditor::OnlyIfDifferent); + if (editor) + editor->gotoLine(item.mainRange().begin.line, item.mainRange().begin.column); + return editor; + }; } } // Git::Internal - -Q_DECLARE_METATYPE(Git::Internal::GitGrepParameters) diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h index fda6fe56ebd..bb6967eb675 100644 --- a/src/plugins/git/gitgrep.h +++ b/src/plugins/git/gitgrep.h @@ -13,28 +13,24 @@ namespace Utils { class FancyLineEdit; } namespace Git::Internal { -class GitClient; +class GitGrepParameters; class GitGrep : public TextEditor::SearchEngine { public: - explicit GitGrep(GitClient *client); + GitGrep(); ~GitGrep() override; QString title() const override; QString toolTip() const override; QWidget *widget() const override; - QVariant parameters() const override; - void readSettings(QSettings *settings) override; - void writeSettings(QSettings *settings) const override; - QFuture<Utils::SearchResultItems> executeSearch( - const TextEditor::FileFindParameters ¶meters, - TextEditor::BaseFileFind *baseFileFind) override; - Core::IEditor *openEditor(const Utils::SearchResultItem &item, - const TextEditor::FileFindParameters ¶meters) override; + void readSettings(Utils::QtcSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) const override; + TextEditor::SearchExecutor searchExecutor() const override; + TextEditor::EditorOpener editorOpener() const override; private: - GitClient *m_client; + GitGrepParameters gitParameters() const; QWidget *m_widget; Utils::FancyLineEdit *m_treeLineEdit; QCheckBox *m_recurseSubmodules = nullptr; diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 3c1affca33f..3f8c55b18d4 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -10,6 +10,7 @@ #include "gitconstants.h" #include "giteditor.h" #include "gitgrep.h" +#include "gitsettings.h" #include "gitsubmiteditor.h" #include "gittr.h" #include "gitutils.h" @@ -61,6 +62,8 @@ #include <vcsbase/vcscommand.h> #include <vcsbase/vcsoutputwindow.h> +#include <nanotrace/nanotrace.h> + #include <QAction> #include <QApplication> #include <QDebug> @@ -193,11 +196,13 @@ public: class BlameMark : public TextEditor::TextMark { + const CommitInfo m_info; public: BlameMark(const FilePath &fileName, int lineNumber, const CommitInfo &info) : TextEditor::TextMark(fileName, lineNumber, {Tr::tr("Git Blame"), Constants::TEXT_MARK_CATEGORY_BLAME}) + , m_info(info) { const QString text = info.shortAuthor + " " + info.authorTime.toString("yyyy-MM-dd"); @@ -212,21 +217,27 @@ public: QObject::connect(copyToClipboardAction, &QAction::triggered, [info] { Utils::setClipboardAndSelection(info.sha1); }); - QAction *showAction = new QAction; - showAction->setIcon(Utils::Icons::ZOOM.icon()); - showAction->setToolTip(TextEditor::Tr::tr("Show Commit %1").arg(info.sha1.left(8))); - QObject::connect(showAction, &QAction::triggered, [info] { - GitClient::instance()->show(info.filePath, info.sha1); - }); - return QList<QAction *>{copyToClipboardAction, showAction}; + return QList<QAction *>{copyToClipboardAction}; }); } + bool addToolTipContent(QLayout *target) const final + { + auto textLabel = new QLabel; + textLabel->setText(toolTip()); + target->addWidget(textLabel); + QObject::connect(textLabel, &QLabel::linkActivated, textLabel, [this] { + gitClient().show(m_info.filePath, m_info.sha1); + }); + + return true; + } + QString toolTipText(const CommitInfo &info) const { - const QString result = QString( + QString result = QString( "<table>" - " <tr><td>commit</td><td>%1</td></tr>" + " <tr><td>commit</td><td><a href>%1</a></td></tr>" " <tr><td>Author:</td><td>%2 <%3></td></tr>" " <tr><td>Date:</td><td>%4</td></tr>" " <tr></tr>" @@ -234,6 +245,18 @@ public: "</table>") .arg(info.sha1, info.author, info.authorMail, info.authorTime.toString("yyyy-MM-dd hh:mm:ss"), info.summary); + + if (settings().instantBlameIgnoreSpaceChanges() + || settings().instantBlameIgnoreLineMoves()) { + result.append( + "<p>" + //: %1 and %2 are the "ignore whitespace changes" and "ignore line moves" options + + Tr::tr("<b>Note:</b> \"%1\" or \"%2\"" + " is enabled in the instant blame settings.") + .arg(GitSettings::trIgnoreWhitespaceChanges(), + GitSettings::trIgnoreLineMoves()) + + "</p>"); + } return result; } }; @@ -267,7 +290,7 @@ public: bool vcsCreateRepository(const FilePath &directory) final; void vcsAnnotate(const FilePath &filePath, int line) final; - void vcsDescribe(const FilePath &source, const QString &id) final { m_gitClient.show(source, id); }; + void vcsDescribe(const FilePath &source, const QString &id) final { gitClient().show(source, id); } QString vcsTopic(const FilePath &directory) final; VcsCommand *createInitialCheckoutCommand(const QString &url, @@ -290,9 +313,9 @@ public: bool handleLink(const FilePath &workingDirectory, const QString &reference) final { if (reference.contains("..")) - GitClient::instance()->log(workingDirectory, {}, false, {reference}); + gitClient().log(workingDirectory, {}, false, {reference}); else - GitClient::instance()->show(workingDirectory, reference); + gitClient().show(workingDirectory, reference); return true; } @@ -399,7 +422,6 @@ public: void onApplySettings(); - GitSettings setting; CommandLocator *m_commandLocator = nullptr; QAction *m_menuAction = nullptr; @@ -417,12 +439,11 @@ public: QAction *m_fixupCommitAction = nullptr; QAction *m_interactiveRebaseAction = nullptr; - QVector<ParameterAction *> m_fileActions; - QVector<ParameterAction *> m_projectActions; - QVector<QAction *> m_repositoryActions; + QList<ParameterAction *> m_fileActions; + QList<ParameterAction *> m_projectActions; + QList<QAction *> m_repositoryActions; ParameterAction *m_applyCurrentFilePatchAction = nullptr; - GitClient m_gitClient; Gerrit::Internal::GerritPlugin m_gerritPlugin; QPointer<StashDialog> m_stashDialog; @@ -438,8 +459,9 @@ public: QTimer *m_cursorPositionChangedTimer = nullptr; std::unique_ptr<BlameMark> m_blameMark; QMetaObject::Connection m_blameCursorPosConn; + QMetaObject::Connection m_documentChangedConn; - GitGrep gitGrep{&m_gitClient}; + GitGrep gitGrep; VcsEditorFactory svnLogEditorFactory { &svnLogEditorParameters, @@ -489,25 +511,20 @@ static GitPluginPrivate *dd = nullptr; class GitTopicCache : public IVersionControl::TopicCache { public: - GitTopicCache(GitClient *client) : - m_client(client) - { } + GitTopicCache() {} protected: FilePath trackFile(const FilePath &repository) override { - const FilePath gitDir = m_client->findGitDirForRepository(repository); + const FilePath gitDir = gitClient().findGitDirForRepository(repository); return gitDir.isEmpty() ? FilePath() : gitDir / "HEAD"; } QString refreshTopic(const FilePath &repository) override { emit dd->repositoryChanged(repository); - return m_client->synchronousTopic(repository); + return gitClient().synchronousTopic(repository); } - -private: - GitClient *m_client; }; GitPluginPrivate::~GitPluginPrivate() @@ -548,11 +565,6 @@ bool GitPluginPrivate::isCommitEditorOpen() const return !m_commitMessageFileName.isEmpty(); } -GitClient *GitPlugin::client() -{ - return &dd->m_gitClient; -} - IVersionControl *GitPlugin::versionControl() { return dd; @@ -671,7 +683,7 @@ QAction *GitPluginPrivate::createRepositoryAction(ActionContainer *ac, const QSt { auto cb = [this, func] { QTC_ASSERT(currentState().hasTopLevel(), return); - (m_gitClient.*func)(currentState().topLevel()); + (gitClient().*func)(currentState().topLevel()); }; // Set the member func as data and connect to GitClient method return createRepositoryAction(ac, text, id, context, addToLocator, cb, keys); @@ -685,6 +697,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) auto cmdContext = new QObject(this); connect(ICore::instance(), &ICore::coreOpened, cmdContext, [this, cmdContext, arguments] { + NANOTRACE_SCOPE("Git", "GitPlugin::initialize::coreOpened"); remoteCommand(arguments, QDir::currentPath(), {}); cmdContext->deleteLater(); }); @@ -702,12 +715,12 @@ GitPluginPrivate::GitPluginPrivate() { dd = this; - setTopicCache(new GitTopicCache(&m_gitClient)); + setTopicCache(new GitTopicCache); m_fileActions.reserve(10); m_projectActions.reserve(10); m_repositoryActions.reserve(50); - m_codec = GitClient::instance()->defaultCommitEncoding(); + m_codec = gitClient().defaultCommitEncoding(); Context context(Constants::GIT_CONTEXT); @@ -862,28 +875,28 @@ GitPluginPrivate::GitPluginPrivate() }; m_abortMergeAction = createAction(Tr::tr("Abort Merge", "Avoid translating \"Merge\""), "Git.MergeAbort", - std::bind(&GitClient::synchronousMerge, &m_gitClient, _1, QString("--abort"), true)); + std::bind(&GitClient::synchronousMerge, &gitClient(), _1, QString("--abort"), true)); m_abortRebaseAction = createAction(Tr::tr("Abort Rebase", "Avoid translating \"Rebase\""), "Git.RebaseAbort", - std::bind(&GitClient::rebase, &m_gitClient, _1, QString("--abort"))); + std::bind(&GitClient::rebase, &gitClient(), _1, QString("--abort"))); m_continueRebaseAction = createAction(Tr::tr("Continue Rebase"), "Git.RebaseContinue", - std::bind(&GitClient::rebase, &m_gitClient, _1, QString("--continue"))); + std::bind(&GitClient::rebase, &gitClient(), _1, QString("--continue"))); m_skipRebaseAction = createAction(Tr::tr("Skip Rebase"), "Git.RebaseSkip", - std::bind(&GitClient::rebase, &m_gitClient, _1, QString("--skip"))); + std::bind(&GitClient::rebase, &gitClient(), _1, QString("--skip"))); m_abortCherryPickAction = createAction(Tr::tr("Abort Cherry Pick", "Avoid translating \"Cherry Pick\""), "Git.CherryPickAbort", - std::bind(&GitClient::synchronousCherryPick, &m_gitClient, _1, QString("--abort"))); + std::bind(&GitClient::synchronousCherryPick, &gitClient(), _1, QString("--abort"))); m_continueCherryPickAction = createAction(Tr::tr("Continue Cherry Pick"), "Git.CherryPickContinue", - std::bind(&GitClient::cherryPick, &m_gitClient, _1, QString("--continue"))); + std::bind(&GitClient::cherryPick, &gitClient(), _1, QString("--continue"))); m_abortRevertAction = createAction(Tr::tr("Abort Revert", "Avoid translating \"Revert\""), "Git.RevertAbort", - std::bind(&GitClient::synchronousRevert, &m_gitClient, _1, QString("--abort"))); + std::bind(&GitClient::synchronousRevert, &gitClient(), _1, QString("--abort"))); m_continueRevertAction = createAction(Tr::tr("Continue Revert"), "Git.RevertContinue", - std::bind(&GitClient::revert, &m_gitClient, _1, QString("--continue"))); + std::bind(&GitClient::revert, &gitClient(), _1, QString("--continue"))); // -------------- localRepositoryMenu->addSeparator(context); @@ -1077,7 +1090,7 @@ void GitPluginPrivate::diffCurrentFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - m_gitClient.diffFile(state.currentFileTopLevel(), state.relativeCurrentFile()); + gitClient().diffFile(state.currentFileTopLevel(), state.relativeCurrentFile()); } void GitPluginPrivate::diffCurrentProject() @@ -1086,16 +1099,16 @@ void GitPluginPrivate::diffCurrentProject() QTC_ASSERT(state.hasProject(), return); const QString relativeProject = state.relativeCurrentProject(); if (relativeProject.isEmpty()) - m_gitClient.diffRepository(state.currentProjectTopLevel()); + gitClient().diffRepository(state.currentProjectTopLevel()); else - m_gitClient.diffProject(state.currentProjectTopLevel(), relativeProject); + gitClient().diffProject(state.currentProjectTopLevel(), relativeProject); } void GitPluginPrivate::logFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - m_gitClient.log(state.currentFileTopLevel(), state.relativeCurrentFile(), true); + gitClient().log(state.currentFileTopLevel(), state.relativeCurrentFile(), true); } void GitPluginPrivate::blameFile() @@ -1133,7 +1146,7 @@ void GitPluginPrivate::blameFile() const FilePath fileName = state.currentFile().canonicalPath(); FilePath topLevel; VcsManager::findVersionControlForDirectory(fileName.parentDir(), &topLevel); - m_gitClient.annotate(topLevel, fileName.relativeChildPath(topLevel).toString(), + gitClient().annotate(topLevel, fileName.relativeChildPath(topLevel).toString(), lineNumber, {}, extraOptions, firstLine); } @@ -1141,21 +1154,21 @@ void GitPluginPrivate::logProject() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasProject(), return); - m_gitClient.log(state.currentProjectTopLevel(), state.relativeCurrentProject()); + gitClient().log(state.currentProjectTopLevel(), state.relativeCurrentProject()); } void GitPluginPrivate::logRepository() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.log(state.topLevel()); + gitClient().log(state.topLevel()); } void GitPluginPrivate::reflogRepository() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.reflog(state.topLevel()); + gitClient().reflog(state.topLevel()); } void GitPluginPrivate::undoFileChanges(bool revertStaging) @@ -1167,7 +1180,7 @@ void GitPluginPrivate::undoFileChanges(bool revertStaging) const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); FileChangeBlocker fcb(state.currentFile()); - m_gitClient.revertFiles({state.currentFile().toString()}, revertStaging); + gitClient().revertFiles({state.currentFile().toString()}, revertStaging); } class ResetItemDelegate : public LogItemDelegate @@ -1209,7 +1222,7 @@ void GitPluginPrivate::resetRepository() ResetItemDelegate delegate(dialog.widget()); dialog.setWindowTitle(Tr::tr("Undo Changes to %1").arg(topLevel.toUserOutput())); if (dialog.runDialog(topLevel, {}, LogChangeWidget::IncludeRemotes)) - m_gitClient.reset(topLevel, dialog.resetFlag(), dialog.commit()); + gitClient().reset(topLevel, dialog.resetFlag(), dialog.commit()); } void GitPluginPrivate::recoverDeletedFiles() @@ -1218,7 +1231,7 @@ void GitPluginPrivate::recoverDeletedFiles() return; const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.recoverDeletedFiles(state.topLevel()); + gitClient().recoverDeletedFiles(state.topLevel()); } void GitPluginPrivate::startRebase() @@ -1234,7 +1247,7 @@ void GitPluginPrivate::startRebaseFromCommit(const FilePath &workingDirectory, Q { if (!DocumentManager::saveAllModifiedDocuments()) return; - if (workingDirectory.isEmpty() || !m_gitClient.canRebase(workingDirectory)) + if (workingDirectory.isEmpty() || !gitClient().canRebase(workingDirectory)) return; if (commit.isEmpty()) { @@ -1246,8 +1259,8 @@ void GitPluginPrivate::startRebaseFromCommit(const FilePath &workingDirectory, Q commit = dialog.commit(); } - if (m_gitClient.beginStashScope(workingDirectory, "Rebase-i")) - m_gitClient.interactiveRebase(workingDirectory, commit, false); + if (gitClient().beginStashScope(workingDirectory, "Rebase-i")) + gitClient().interactiveRebase(workingDirectory, commit, false); } void GitPluginPrivate::startChangeRelatedAction(const Id &id) @@ -1272,15 +1285,15 @@ void GitPluginPrivate::startChangeRelatedAction(const Id &id) const int colon = change.indexOf(':'); if (colon > 0) { const FilePath path = workingDirectory.resolvePath(change.mid(colon + 1)); - m_gitClient.openShowEditor(workingDirectory, change.left(colon), path); + gitClient().openShowEditor(workingDirectory, change.left(colon), path); } else { - m_gitClient.show(workingDirectory, change); + gitClient().show(workingDirectory, change); } return; } if (dialog.command() == Archive) { - m_gitClient.archive(workingDirectory, change); + gitClient().archive(workingDirectory, change); return; } @@ -1289,13 +1302,13 @@ void GitPluginPrivate::startChangeRelatedAction(const Id &id) switch (dialog.command()) { case CherryPick: - m_gitClient.synchronousCherryPick(workingDirectory, change); + gitClient().synchronousCherryPick(workingDirectory, change); break; case Revert: - m_gitClient.synchronousRevert(workingDirectory, change); + gitClient().synchronousRevert(workingDirectory, change); break; case Checkout: - m_gitClient.checkout(workingDirectory, change); + gitClient().checkout(workingDirectory, change); break; default: return; @@ -1306,21 +1319,21 @@ void GitPluginPrivate::stageFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - m_gitClient.addFile(state.currentFileTopLevel(), state.relativeCurrentFile()); + gitClient().addFile(state.currentFileTopLevel(), state.relativeCurrentFile()); } void GitPluginPrivate::unstageFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - m_gitClient.synchronousReset(state.currentFileTopLevel(), {state.relativeCurrentFile()}); + gitClient().synchronousReset(state.currentFileTopLevel(), {state.relativeCurrentFile()}); } void GitPluginPrivate::gitkForCurrentFile() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasFile(), return); - m_gitClient.launchGitK(state.currentFileTopLevel(), state.relativeCurrentFile()); + gitClient().launchGitK(state.currentFileTopLevel(), state.relativeCurrentFile()); } void GitPluginPrivate::gitkForCurrentFolder() @@ -1331,7 +1344,7 @@ void GitPluginPrivate::gitkForCurrentFolder() /* * entire lower part of the code can be easily replaced with one line: * - * m_gitClient.launchGitK(dir.currentFileDirectory(), "."); + * gitClient().launchGitK(dir.currentFileDirectory(), "."); * * However, there is a bug in gitk in version 1.7.9.5, and if you run above * command, there will be no documents listed in lower right section. @@ -1344,12 +1357,12 @@ void GitPluginPrivate::gitkForCurrentFolder() */ QDir dir(state.currentFileDirectory().toString()); if (QFileInfo(dir,".git").exists() || dir.cd(".git")) { - m_gitClient.launchGitK(state.currentFileDirectory()); + gitClient().launchGitK(state.currentFileDirectory()); } else { QString folderName = dir.absolutePath(); dir.cdUp(); folderName = folderName.remove(0, dir.absolutePath().length() + 1); - m_gitClient.launchGitK(FilePath::fromString(dir.absolutePath()), folderName); + gitClient().launchGitK(FilePath::fromString(dir.absolutePath()), folderName); } } @@ -1357,14 +1370,14 @@ void GitPluginPrivate::gitGui() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.launchGitGui(state.topLevel()); + gitClient().launchGitGui(state.topLevel()); } void GitPluginPrivate::gitBash() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.launchGitBash(state.topLevel()); + gitClient().launchGitBash(state.topLevel()); } void GitPluginPrivate::startCommit(CommitType commitType) @@ -1384,7 +1397,7 @@ void GitPluginPrivate::startCommit(CommitType commitType) QString errorMessage, commitTemplate; CommitData data(commitType); - if (!m_gitClient.getCommitData(state.topLevel(), &commitTemplate, data, &errorMessage)) { + if (!gitClient().getCommitData(state.topLevel(), &commitTemplate, data, &errorMessage)) { VcsOutputWindow::appendError(errorMessage); return; } @@ -1411,7 +1424,7 @@ void GitPluginPrivate::updateVersionWarning() QPointer<IDocument> curDocument = EditorManager::currentDocument(); if (!curDocument) return; - Utils::onResultReady(m_gitClient.gitVersion(), this, [curDocument](unsigned version) { + Utils::onResultReady(gitClient().gitVersion(), this, [curDocument](unsigned version) { if (!curDocument || !version || version >= minimumRequiredVersion) return; InfoBar *infoBar = curDocument->infoBar(); @@ -1438,7 +1451,7 @@ void GitPluginPrivate::setupInstantBlame() return; } - if (!settings().instantBlame.value()) { + if (!settings().instantBlame()) { m_lastVisitedEditorLine = -1; stopInstantBlame(); return; @@ -1457,19 +1470,23 @@ void GitPluginPrivate::setupInstantBlame() m_blameCursorPosConn = connect(widget, &QPlainTextEdit::cursorPositionChanged, this, [this] { - if (!settings().instantBlame.value()) { + if (!settings().instantBlame()) { disconnect(m_blameCursorPosConn); return; } m_cursorPositionChangedTimer->start(500); }); + IDocument *document = editor->document(); + m_documentChangedConn = connect(document, &IDocument::changed, this, [this, document] { + if (!document->isModified()) + forceInstantBlame(); + }); forceInstantBlame(); }; - connect(&settings().instantBlame, &BoolAspect::valueChanged, this, - [this, setupBlameForEditor](bool enabled) { - if (enabled) + connect(&settings().instantBlame, &BaseAspect::changed, this, [this, setupBlameForEditor] { + if (settings().instantBlame()) setupBlameForEditor(EditorManager::currentEditor()); else stopInstantBlame(); @@ -1517,7 +1534,7 @@ CommitInfo parseBlameOutput(const QStringList &blame, const Utils::FilePath &fil void GitPluginPrivate::instantBlameOnce() { - if (!settings().instantBlame.value()) { + if (!settings().instantBlame()) { const TextEditorWidget *widget = TextEditorWidget::currentTextEditorWidget(); if (!widget) return; @@ -1582,9 +1599,14 @@ void GitPluginPrivate::instantBlame() const CommitInfo info = parseBlameOutput(output.split('\n'), filePath, m_author); m_blameMark.reset(new BlameMark(filePath, line, info)); }; - GitClient::instance()->vcsExecWithHandler(workingDirectory, - {"blame", "-p", "-L", lineString, "--", filePath.toString()}, - this, commandHandler, RunFlags::NoOutput, m_codec); + QStringList options = {"blame", "-p"}; + if (settings().instantBlameIgnoreSpaceChanges()) + options.append("-w"); + if (settings().instantBlameIgnoreLineMoves()) + options.append("-M"); + options.append({"-L", lineString, "--", filePath.toString()}); + gitClient().vcsExecWithHandler(workingDirectory, options, this, + commandHandler, RunFlags::NoOutput, m_codec); } void GitPluginPrivate::stopInstantBlame() @@ -1592,6 +1614,7 @@ void GitPluginPrivate::stopInstantBlame() m_blameMark.reset(); m_cursorPositionChangedTimer->stop(); disconnect(m_blameCursorPosConn); + disconnect(m_documentChangedConn); } bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory) @@ -1611,7 +1634,7 @@ bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory) const QString codecName = result.cleanedStdOut().trimmed(); codec = QTextCodec::codecForName(codecName.toUtf8()); } else { - codec = GitClient::instance()->defaultCommitEncoding(); + codec = gitClient().defaultCommitEncoding(); } if (m_codec != codec) { @@ -1619,13 +1642,13 @@ bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory) forceInstantBlame(); } }; - GitClient::instance()->readConfigAsync(workingDirectory, {"config", "i18n.commitEncoding"}, + gitClient().readConfigAsync(workingDirectory, {"config", "i18n.commitEncoding"}, commitCodecHandler); const auto authorHandler = [this, workingDirectory](const CommandResult &result) { if (result.result() == ProcessResult::FinishedWithSuccess) { const QString authorInfo = result.cleanedStdOut().trimmed(); - const Author author = GitClient::instance()->parseAuthor(authorInfo); + const Author author = gitClient().parseAuthor(authorInfo); if (m_author != author) { m_author = author; @@ -1633,7 +1656,7 @@ bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory) } } }; - GitClient::instance()->readConfigAsync(workingDirectory, {"var", "GIT_AUTHOR_IDENT"}, + gitClient().readConfigAsync(workingDirectory, {"var", "GIT_AUTHOR_IDENT"}, authorHandler); return true; @@ -1689,7 +1712,7 @@ bool GitPluginPrivate::activateCommit() if (!DocumentManager::saveDocument(editorDocument)) return false; - if (!m_gitClient.addAndCommit(m_submitRepository, editor->panelData(), commitType, + if (!gitClient().addAndCommit(m_submitRepository, editor->panelData(), commitType, amendSHA1, m_commitMessageFileName, model)) { editor->updateFileModel(); return false; @@ -1697,15 +1720,15 @@ bool GitPluginPrivate::activateCommit() } cleanCommitMessageFile(); if (commitType == FixupCommit) { - if (!m_gitClient.beginStashScope(m_submitRepository, "Rebase-fixup", + if (!gitClient().beginStashScope(m_submitRepository, "Rebase-fixup", NoPrompt, editor->panelData().pushAction)) { return false; } - m_gitClient.interactiveRebase(m_submitRepository, amendSHA1, true); + gitClient().interactiveRebase(m_submitRepository, amendSHA1, true); } else { - m_gitClient.continueCommandIfNeeded(m_submitRepository); + gitClient().continueCommandIfNeeded(m_submitRepository); if (editor->panelData().pushAction == NormalPush) { - m_gitClient.push(m_submitRepository); + gitClient().push(m_submitRepository); } else if (editor->panelData().pushAction == PushToGerrit) { connect(editor, &QObject::destroyed, this, &GitPluginPrivate::delayedPushToGerrit, Qt::QueuedConnection); @@ -1717,7 +1740,7 @@ bool GitPluginPrivate::activateCommit() void GitPluginPrivate::fetch() { - m_gitClient.fetch(currentState().topLevel(), {}); + gitClient().fetch(currentState().topLevel(), {}); } void GitPluginPrivate::pull() @@ -1727,34 +1750,34 @@ void GitPluginPrivate::pull() const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); FilePath topLevel = state.topLevel(); - bool rebase = settings().pullRebase.value(); + bool rebase = settings().pullRebase(); if (!rebase) { - QString currentBranch = m_gitClient.synchronousCurrentLocalBranch(topLevel); + QString currentBranch = gitClient().synchronousCurrentLocalBranch(topLevel); if (!currentBranch.isEmpty()) { currentBranch.prepend("branch."); currentBranch.append(".rebase"); - rebase = (m_gitClient.readConfigValue(topLevel, currentBranch) == "true"); + rebase = (gitClient().readConfigValue(topLevel, currentBranch) == "true"); } } - if (!m_gitClient.beginStashScope(topLevel, "Pull", rebase ? Default : AllowUnstashed)) + if (!gitClient().beginStashScope(topLevel, "Pull", rebase ? Default : AllowUnstashed)) return; - m_gitClient.pull(topLevel, rebase); + gitClient().pull(topLevel, rebase); } void GitPluginPrivate::push() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.push(state.topLevel()); + gitClient().push(state.topLevel()); } void GitPluginPrivate::startMergeTool() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.merge(state.topLevel()); + gitClient().merge(state.topLevel()); } void GitPluginPrivate::cleanProject() @@ -1778,7 +1801,7 @@ void GitPluginPrivate::cleanRepository(const FilePath &directory) QStringList files; QStringList ignoredFiles; QApplication::setOverrideCursor(Qt::WaitCursor); - const bool gotFiles = m_gitClient.synchronousCleanList(directory, {}, &files, &ignoredFiles, + const bool gotFiles = gitClient().synchronousCleanList(directory, {}, &files, &ignoredFiles, &errorMessage); QApplication::restoreOverrideCursor(); @@ -1802,7 +1825,7 @@ void GitPluginPrivate::updateSubmodules() { const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - m_gitClient.updateSubmodulesIfNeeded(state.topLevel(), false); + gitClient().updateSubmodulesIfNeeded(state.topLevel(), false); } // If the file is modified in an editor, make sure it is saved. @@ -1832,20 +1855,20 @@ void GitPluginPrivate::promptApplyPatch() void GitPluginPrivate::applyPatch(const FilePath &workingDirectory, QString file) { // Ensure user has been notified about pending changes - if (!m_gitClient.beginStashScope(workingDirectory, "Apply-Patch", AllowUnstashed)) + if (!gitClient().beginStashScope(workingDirectory, "Apply-Patch", AllowUnstashed)) return; // Prompt for file if (file.isEmpty()) { const QString filter = Tr::tr("Patches (*.patch *.diff)"); file = QFileDialog::getOpenFileName(ICore::dialogParent(), Tr::tr("Choose Patch"), {}, filter); if (file.isEmpty()) { - m_gitClient.endStashScope(workingDirectory); + gitClient().endStashScope(workingDirectory); return; } } // Run! QString errorMessage; - if (m_gitClient.synchronousApplyPatch(workingDirectory, file, &errorMessage)) { + if (gitClient().synchronousApplyPatch(workingDirectory, file, &errorMessage)) { if (errorMessage.isEmpty()) VcsOutputWindow::appendMessage(Tr::tr("Patch %1 successfully applied to %2") .arg(file, workingDirectory.toUserOutput())); @@ -1854,7 +1877,7 @@ void GitPluginPrivate::applyPatch(const FilePath &workingDirectory, QString file } else { VcsOutputWindow::appendError(errorMessage); } - m_gitClient.endStashScope(workingDirectory); + gitClient().endStashScope(workingDirectory); } void GitPluginPrivate::stash(bool unstagedOnly) @@ -1866,7 +1889,7 @@ void GitPluginPrivate::stash(bool unstagedOnly) QTC_ASSERT(state.hasTopLevel(), return); const FilePath topLevel = state.topLevel(); - m_gitClient.executeSynchronousStash(topLevel, {}, unstagedOnly); + gitClient().executeSynchronousStash(topLevel, {}, unstagedOnly); if (m_stashDialog) m_stashDialog->refresh(topLevel, true); } @@ -1881,7 +1904,7 @@ void GitPluginPrivate::stashSnapshot() // Prompt for description, restore immediately and keep on working. const VcsBasePluginState state = currentState(); QTC_ASSERT(state.hasTopLevel(), return); - const QString id = m_gitClient.synchronousStash(state.topLevel(), {}, + const QString id = gitClient().synchronousStash(state.topLevel(), {}, GitClient::StashImmediateRestore | GitClient::StashPromptDescription); if (!id.isEmpty() && m_stashDialog) m_stashDialog->refresh(state.topLevel(), true); @@ -1892,7 +1915,7 @@ void GitPluginPrivate::stashPop() if (!DocumentManager::saveAllModifiedDocuments()) return; const FilePath repository = currentState().topLevel(); - m_gitClient.stashPop(repository); + gitClient().stashPop(repository); if (m_stashDialog) m_stashDialog->refresh(repository, true); } @@ -1966,7 +1989,7 @@ void GitPluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as) repositoryAction->setEnabled(repositoryEnabled); m_submoduleUpdateAction->setVisible(repositoryEnabled - && !m_gitClient.submoduleList(state.topLevel()).isEmpty()); + && !gitClient().submoduleList(state.topLevel()).isEmpty()); updateContinueAndAbortCommands(); updateRepositoryBrowserAction(); @@ -1978,7 +2001,7 @@ void GitPluginPrivate::updateContinueAndAbortCommands() { if (currentState().hasTopLevel()) { GitClient::CommandInProgress gitCommandInProgress = - m_gitClient.checkCommandInProgress(currentState().topLevel()); + gitClient().checkCommandInProgress(currentState().topLevel()); m_mergeToolAction->setVisible(gitCommandInProgress != GitClient::NoCommand); m_abortMergeAction->setVisible(gitCommandInProgress == GitClient::Merge); @@ -2031,7 +2054,7 @@ QObject *GitPlugin::remoteCommand(const QStringList &options, const QString &wor return nullptr; if (options.first() == "-git-show") - dd->m_gitClient.show(FilePath::fromUserInput(workingDirectory), options.at(1)); + gitClient().show(FilePath::fromUserInput(workingDirectory), options.at(1)); return nullptr; } @@ -2066,7 +2089,7 @@ bool GitPluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) const bool GitPluginPrivate::isConfigured() const { - return !m_gitClient.vcsBinary().isEmpty(); + return !gitClient().vcsBinary().isEmpty(); } bool GitPluginPrivate::supportsOperation(Operation operation) const @@ -2094,30 +2117,30 @@ bool GitPluginPrivate::vcsOpen(const FilePath & /*filePath*/) bool GitPluginPrivate::vcsAdd(const FilePath &filePath) { - return m_gitClient.synchronousAdd(filePath.parentDir(), {filePath.fileName()}, {"--intent-to-add"}); + return gitClient().synchronousAdd(filePath.parentDir(), {filePath.fileName()}, {"--intent-to-add"}); } bool GitPluginPrivate::vcsDelete(const FilePath &filePath) { - return m_gitClient.synchronousDelete(filePath.absolutePath(), true, {filePath.fileName()}); + return gitClient().synchronousDelete(filePath.absolutePath(), true, {filePath.fileName()}); } bool GitPluginPrivate::vcsMove(const FilePath &from, const FilePath &to) { const QFileInfo fromInfo = from.toFileInfo(); const QFileInfo toInfo = to.toFileInfo(); - return m_gitClient.synchronousMove(from.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath()); + return gitClient().synchronousMove(from.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath()); } bool GitPluginPrivate::vcsCreateRepository(const FilePath &directory) { - return m_gitClient.synchronousInit(directory); + return gitClient().synchronousInit(directory); } QString GitPluginPrivate::vcsTopic(const FilePath &directory) { QString topic = IVersionControl::vcsTopic(directory); - const QString commandInProgress = m_gitClient.commandInProgressDescription(directory); + const QString commandInProgress = gitClient().commandInProgressDescription(directory); if (!commandInProgress.isEmpty()) topic += " (" + commandInProgress + ')'; return topic; @@ -2131,9 +2154,9 @@ VcsCommand *GitPluginPrivate::createInitialCheckoutCommand(const QString &url, QStringList args = {"clone", "--progress"}; args << extraArgs << url << localName; - auto command = VcsBaseClient::createVcsCommand(baseDirectory, m_gitClient.processEnvironment()); + auto command = VcsBaseClient::createVcsCommand(baseDirectory, gitClient().processEnvironment()); command->addFlags(RunFlags::SuppressStdErr); - command->addJob({m_gitClient.vcsBinary(), args}, -1); + command->addJob({gitClient().vcsBinary(), args}, -1); return command; } @@ -2145,7 +2168,7 @@ GitPluginPrivate::RepoUrl GitPluginPrivate::getRepoUrl(const QString &location) FilePaths GitPluginPrivate::additionalToolsPath() const { FilePaths res = settings().searchPathList(); - const FilePath binaryPath = m_gitClient.gitBinDirectory(); + const FilePath binaryPath = gitClient().gitBinDirectory(); if (!binaryPath.isEmpty() && !res.contains(binaryPath)) res << binaryPath; return res; @@ -2153,7 +2176,7 @@ FilePaths GitPluginPrivate::additionalToolsPath() const bool GitPluginPrivate::managesDirectory(const FilePath &directory, FilePath *topLevel) const { - const FilePath topLevelFound = m_gitClient.findRepositoryForDirectory(directory); + const FilePath topLevelFound = gitClient().findRepositoryForDirectory(directory); if (topLevel) *topLevel = topLevelFound; return !topLevelFound.isEmpty(); @@ -2161,17 +2184,17 @@ bool GitPluginPrivate::managesDirectory(const FilePath &directory, FilePath *top bool GitPluginPrivate::managesFile(const FilePath &workingDirectory, const QString &fileName) const { - return m_gitClient.managesFile(workingDirectory, fileName); + return gitClient().managesFile(workingDirectory, fileName); } FilePaths GitPluginPrivate::unmanagedFiles(const FilePaths &filePaths) const { - return m_gitClient.unmanagedFiles(filePaths); + return gitClient().unmanagedFiles(filePaths); } void GitPluginPrivate::vcsAnnotate(const FilePath &filePath, int line) { - m_gitClient.annotate(filePath.absolutePath(), filePath.fileName(), line); + gitClient().annotate(filePath.absolutePath(), filePath.fileName(), line); } void GitPlugin::emitFilesChanged(const QStringList &l) diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 59d9a87fdec..00005d855e5 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -17,8 +17,6 @@ namespace VcsBase { class VcsBasePluginState; } namespace Git::Internal { -class GitClient; - class GITSHARED_EXPORT GitPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT @@ -33,7 +31,6 @@ public: QObject *remoteCommand(const QStringList &options, const QString &workingDirectory, const QStringList &args) final; - static GitClient *client(); static Core::IVersionControl *versionControl(); static const VcsBase::VcsBasePluginState ¤tState(); diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 4ebf7debeed..cb23f55c9a8 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -5,6 +5,8 @@ #include "gittr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/environment.h> #include <utils/layoutbuilder.h> @@ -17,20 +19,15 @@ using namespace VcsBase; namespace Git::Internal { -static GitSettings *theSettings; - GitSettings &settings() { - return *theSettings; + static GitSettings theSettings; + return theSettings; } GitSettings::GitSettings() { - theSettings = this; - - setId(VcsBase::Constants::VCS_ID_GIT); - setDisplayName(Tr::tr("Git")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setAutoApply(false); setSettingsGroup("Git"); path.setDisplayStyle(StringAspect::LineEditDisplay); @@ -92,6 +89,16 @@ GitSettings::GitSettings() instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor")); instantBlame.setToolTip( Tr::tr("Annotate the current line in the editor with Git \"blame\" output.")); + instantBlameIgnoreSpaceChanges.setSettingsKey("GitInstantIgnoreSpaceChanges"); + instantBlameIgnoreSpaceChanges.setDefaultValue(false); + instantBlameIgnoreSpaceChanges.setLabelText(trIgnoreWhitespaceChanges()); + instantBlameIgnoreSpaceChanges.setToolTip( + Tr::tr("Finds the commit that introduced the last real code changes to the line.")); + instantBlameIgnoreLineMoves.setSettingsKey("GitInstantIgnoreLineMoves"); + instantBlameIgnoreLineMoves.setDefaultValue(false); + instantBlameIgnoreLineMoves.setLabelText(trIgnoreLineMoves()); + instantBlameIgnoreLineMoves.setToolTip( + Tr::tr("Finds the commit that introduced the line before it was moved.")); graphLog.setSettingsKey("GraphLog"); @@ -140,14 +147,17 @@ GitSettings::GitSettings() Group { title(Tr::tr("Instant Blame")), - Row { instantBlame } + instantBlame.groupChecker(), + Row { instantBlameIgnoreSpaceChanges, instantBlameIgnoreLineMoves, st }, }, st }; }); - connect(&binaryPath, &StringAspect::valueChanged, this, [this] { tryResolve = true; }); - connect(&path, &StringAspect::valueChanged, this, [this] { tryResolve = true; }); + connect(&binaryPath, &BaseAspect::changed, this, [this] { tryResolve = true; }); + connect(&path, &BaseAspect::changed, this, [this] { tryResolve = true; }); + + readSettings(); } FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const @@ -161,7 +171,7 @@ FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const if (tryResolve) { resolvedBinPath = binaryPath(); if (!resolvedBinPath.isAbsolutePath()) - resolvedBinPath = resolvedBinPath.searchInPath({path.filePath()}, FilePath::PrependToPath); + resolvedBinPath = resolvedBinPath.searchInPath(searchPathList(), FilePath::PrependToPath); tryResolve = false; } @@ -170,9 +180,35 @@ FilePath GitSettings::gitExecutable(bool *ok, QString *errorMessage) const *ok = false; if (errorMessage) *errorMessage = Tr::tr("The binary \"%1\" could not be located in the path \"%2\"") - .arg(binaryPath.value(), path.value()); + .arg(binaryPath().toUserOutput(), path()); } return resolvedBinPath; } +QString GitSettings::trIgnoreWhitespaceChanges() +{ + return Tr::tr("Ignore whitespace changes"); +} + +QString GitSettings::trIgnoreLineMoves() +{ + return Tr::tr("Ignore line moves"); +} + +// GitSettingsPage + +class GitSettingsPage final : public Core::IOptionsPage +{ +public: + GitSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_GIT); + setDisplayName(Tr::tr("Git")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const GitSettingsPage settingsPage; + } // Git::Internal diff --git a/src/plugins/git/gitsettings.h b/src/plugins/git/gitsettings.h index df6cf56e4e8..f6ec650c2e8 100644 --- a/src/plugins/git/gitsettings.h +++ b/src/plugins/git/gitsettings.h @@ -15,7 +15,7 @@ enum CommitType }; // Todo: Add user name and password? -class GitSettings : public VcsBase::VcsBaseSettings +class GitSettings final : public VcsBase::VcsBaseSettings { public: GitSettings(); @@ -38,11 +38,16 @@ public: Utils::IntegerAspect lastResetIndex{this}; Utils::BoolAspect refLogShowDate{this}; Utils::BoolAspect instantBlame{this}; + Utils::BoolAspect instantBlameIgnoreSpaceChanges{this}; + Utils::BoolAspect instantBlameIgnoreLineMoves{this}; mutable Utils::FilePath resolvedBinPath; mutable bool tryResolve = true; Utils::FilePath gitExecutable(bool *ok = nullptr, QString *errorMessage = nullptr) const; + + static QString trIgnoreWhitespaceChanges(); + static QString trIgnoreLineMoves(); }; GitSettings &settings(); diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp index 9ff83c8c124..ba663635750 100644 --- a/src/plugins/git/gitsubmiteditor.cpp +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -11,6 +11,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/iversioncontrol.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <extensionsystem/pluginmanager.h> #include <utils/async.h> #include <utils/qtcassert.h> #include <vcsbase/submitfilemodel.h> @@ -68,7 +69,7 @@ CommitDataFetchResult CommitDataFetchResult::fetch(CommitType commitType, const CommitDataFetchResult result; result.commitData.commitType = commitType; QString commitTemplate; - result.success = GitClient::instance()->getCommitData( + result.success = gitClient().getCommitData( workingDirectory, &commitTemplate, result.commitData, &result.errorMessage); return result; } @@ -180,15 +181,15 @@ void GitSubmitEditor::slotDiffSelected(const QList<int> &rows) } } if (!unstagedFiles.empty() || !stagedFiles.empty()) - GitClient::instance()->diffFiles(m_workingDirectory, unstagedFiles, stagedFiles); + gitClient().diffFiles(m_workingDirectory, unstagedFiles, stagedFiles); if (!unmergedFiles.empty()) - GitClient::instance()->merge(m_workingDirectory, unmergedFiles); + gitClient().merge(m_workingDirectory, unmergedFiles); } void GitSubmitEditor::showCommit(const QString &commit) { if (!m_workingDirectory.isEmpty()) - GitClient::instance()->show(m_workingDirectory, commit); + gitClient().show(m_workingDirectory, commit); } void GitSubmitEditor::updateFileModel() @@ -209,7 +210,7 @@ void GitSubmitEditor::updateFileModel() Core::ProgressManager::addTask(m_fetchWatcher.future(), Tr::tr("Refreshing Commit Data"), TASK_UPDATE_COMMIT); - GitClient::instance()->addFuture(QFuture<void>(m_fetchWatcher.future())); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_fetchWatcher.future()); } void GitSubmitEditor::forceUpdateFileModel() diff --git a/src/plugins/git/logchangedialog.cpp b/src/plugins/git/logchangedialog.cpp index 75660c11897..a1091683311 100644 --- a/src/plugins/git/logchangedialog.cpp +++ b/src/plugins/git/logchangedialog.cpp @@ -47,7 +47,7 @@ public: const auto it = m_descriptions.constFind(revision); if (it != m_descriptions.constEnd()) return *it; - const QString desc = QString::fromUtf8(GitClient::instance()->synchronousShow( + const QString desc = QString::fromUtf8(gitClient().synchronousShow( m_workingDirectory, revision, RunFlags::NoOutput)); m_descriptions[revision] = desc; return desc; @@ -71,7 +71,6 @@ LogChangeWidget::LogChangeWidget(QWidget *parent) m_model->setHorizontalHeaderLabels(headers); setModel(m_model); setMinimumWidth(300); - setUniformRowHeights(true); setRootIsDecorated(false); setSelectionBehavior(QAbstractItemView::SelectRows); setActivationMode(Utils::DoubleClickActivation); @@ -95,7 +94,7 @@ QString LogChangeWidget::commit() const { if (const QStandardItem *sha1Item = currentItem(Sha1Column)) return sha1Item->text(); - return QString(); + return {}; } int LogChangeWidget::commitIndex() const @@ -113,7 +112,7 @@ QString LogChangeWidget::earliestCommit() const if (const QStandardItem *item = m_model->item(rows - 1, Sha1Column)) return item->text(); } - return QString(); + return {}; } void LogChangeWidget::setItemDelegate(QAbstractItemDelegate *delegate) @@ -170,7 +169,7 @@ bool LogChangeWidget::populateLog(const FilePath &repository, const QString &com } arguments << "--"; QString output; - if (!GitClient::instance()->synchronousLog( + if (!gitClient().synchronousLog( repository, arguments, &output, nullptr, RunFlags::NoOutput)) { return false; } @@ -269,7 +268,7 @@ int LogChangeDialog::commitIndex() const QString LogChangeDialog::resetFlag() const { if (!m_resetTypeComboBox) - return QString(); + return {}; return m_resetTypeComboBox->itemData(m_resetTypeComboBox->currentIndex()).toString(); } diff --git a/src/plugins/git/mergetool.cpp b/src/plugins/git/mergetool.cpp index 6fbc9d5ea83..775eb1ac213 100644 --- a/src/plugins/git/mergetool.cpp +++ b/src/plugins/git/mergetool.cpp @@ -37,7 +37,7 @@ void MergeTool::start(const FilePath &workingDirectory, const QStringList &files { QStringList arguments; arguments << "mergetool" << "-y" << files; - const CommandLine cmd = {GitClient::instance()->vcsBinary(), arguments}; + const CommandLine cmd = {gitClient().vcsBinary(), arguments}; VcsOutputWindow::appendCommand(workingDirectory, cmd); m_process.setCommand(cmd); m_process.setWorkingDirectory(workingDirectory); @@ -94,7 +94,7 @@ QString MergeTool::mergeTypeName() case DeletedMerge: return Tr::tr("Deleted"); case SymbolicLinkMerge: return Tr::tr("Symbolic link"); } - return QString(); + return {}; } QString MergeTool::stateName(MergeTool::FileState state, const QString &extraInfo) @@ -107,7 +107,7 @@ QString MergeTool::stateName(MergeTool::FileState state, const QString &extraInf case SymbolicLinkState: return Tr::tr("Symbolic link -> %1").arg(extraInfo); default: break; } - return QString(); + return {}; } void MergeTool::chooseAction() @@ -224,7 +224,7 @@ void MergeTool::done() VcsOutputWindow::appendError(m_process.exitMessage()); const FilePath workingDirectory = m_process.workingDirectory(); - GitClient::instance()->continueCommandIfNeeded(workingDirectory, success); + gitClient().continueCommandIfNeeded(workingDirectory, success); GitPlugin::emitRepositoryChanged(workingDirectory); deleteLater(); } diff --git a/src/plugins/git/remotedialog.cpp b/src/plugins/git/remotedialog.cpp index 25d0f76af25..7c1f55a62ad 100644 --- a/src/plugins/git/remotedialog.cpp +++ b/src/plugins/git/remotedialog.cpp @@ -248,7 +248,7 @@ void RemoteDialog::pushToRemote() const int row = indexList.at(0).row(); const QString remoteName = m_remoteModel->remoteName(row); - GitClient::instance()->push(m_remoteModel->workingDirectory(), {remoteName}); + gitClient().push(m_remoteModel->workingDirectory(), {remoteName}); } void RemoteDialog::fetchFromRemote() @@ -259,7 +259,7 @@ void RemoteDialog::fetchFromRemote() int row = indexList.at(0).row(); const QString remoteName = m_remoteModel->remoteName(row); - GitClient::instance()->fetch(m_remoteModel->workingDirectory(), remoteName); + gitClient().fetch(m_remoteModel->workingDirectory(), remoteName); } void RemoteDialog::updateButtonState() diff --git a/src/plugins/git/remotemodel.cpp b/src/plugins/git/remotemodel.cpp index 908f0cff134..cbfa77afd93 100644 --- a/src/plugins/git/remotemodel.cpp +++ b/src/plugins/git/remotemodel.cpp @@ -34,7 +34,7 @@ bool RemoteModel::removeRemote(int row) { QString output; QString error; - bool success = GitClient::instance()->synchronousRemoteCmd( + bool success = gitClient().synchronousRemoteCmd( m_workingDirectory, {"rm", remoteName(row)}, &output, &error); if (success) success = refresh(m_workingDirectory, &error); @@ -48,7 +48,7 @@ bool RemoteModel::addRemote(const QString &name, const QString &url) if (name.isEmpty() || url.isEmpty()) return false; - bool success = GitClient::instance()->synchronousRemoteCmd( + bool success = gitClient().synchronousRemoteCmd( m_workingDirectory, {"add", name, url}, &output, &error); if (success) success = refresh(m_workingDirectory, &error); @@ -59,7 +59,7 @@ bool RemoteModel::renameRemote(const QString &oldName, const QString &newName) { QString output; QString error; - bool success = GitClient::instance()->synchronousRemoteCmd( + bool success = gitClient().synchronousRemoteCmd( m_workingDirectory, {"rename", oldName, newName}, &output, &error); if (success) success = refresh(m_workingDirectory, &error); @@ -70,7 +70,7 @@ bool RemoteModel::updateUrl(const QString &name, const QString &newUrl) { QString output; QString error; - bool success = GitClient::instance()->synchronousRemoteCmd( + bool success = gitClient().synchronousRemoteCmd( m_workingDirectory, {"set-url", name, newUrl}, &output, &error); if (success) success = refresh(m_workingDirectory, &error); @@ -112,14 +112,13 @@ QVariant RemoteModel::data(const QModelIndex &index, int role) const default: break; } - return QVariant(); + return {}; } QVariant RemoteModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - + return {}; return (section == 0) ? Tr::tr("Name") : Tr::tr("URL"); } @@ -165,7 +164,7 @@ bool RemoteModel::refresh(const FilePath &workingDirectory, QString *errorMessag // get list of remotes. QMap<QString,QString> remotesList - = GitClient::instance()->synchronousRemotesList(workingDirectory, errorMessage); + = gitClient().synchronousRemotesList(workingDirectory, errorMessage); beginResetModel(); m_remotes.clear(); diff --git a/src/plugins/git/stashdialog.cpp b/src/plugins/git/stashdialog.cpp index 195d6622d9a..cfd0b92e777 100644 --- a/src/plugins/git/stashdialog.cpp +++ b/src/plugins/git/stashdialog.cpp @@ -132,7 +132,6 @@ StashDialog::StashDialog(QWidget *parent) : QDialog(parent), m_stashView->setModel(m_proxyModel); m_stashView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_stashView->setAllColumnsShowFocus(true); - m_stashView->setUniformRowHeights(true); m_stashView->setFocus(); using namespace Layouting; @@ -171,7 +170,7 @@ void StashDialog::refresh(const FilePath &repository, bool force) m_model->setStashes(QList<Stash>()); } else { QList<Stash> stashes; - GitClient::instance()->synchronousStashList(m_repository, &stashes); + gitClient().synchronousStashList(m_repository, &stashes); m_model->setStashes(stashes); if (!stashes.isEmpty()) { for (int c = 0; c < ColumnCount; c++) @@ -187,7 +186,7 @@ void StashDialog::deleteAll() if (!ask(title, Tr::tr("Do you want to delete all stashes?"))) return; QString errorMessage; - if (GitClient::instance()->synchronousStashRemove(m_repository, QString(), &errorMessage)) + if (gitClient().synchronousStashRemove(m_repository, QString(), &errorMessage)) refresh(m_repository, true); else warning(title, errorMessage); @@ -204,7 +203,7 @@ void StashDialog::deleteSelection() QStringList errors; // Delete in reverse order as stashes rotate for (int r = rows.size() - 1; r >= 0; r--) - if (!GitClient::instance()->synchronousStashRemove(m_repository, m_model->at(rows.at(r)).name, &errorMessage)) + if (!gitClient().synchronousStashRemove(m_repository, m_model->at(rows.at(r)).name, &errorMessage)) errors.push_back(errorMessage); refresh(m_repository, true); if (!errors.isEmpty()) @@ -215,7 +214,7 @@ void StashDialog::showCurrent() { const int index = currentRow(); QTC_ASSERT(index >= 0, return); - GitClient::instance()->show(m_repository, QString(m_model->at(index).name)); + gitClient().show(m_repository, QString(m_model->at(index).name)); } // Suggest Branch name to restore 'stash@{0}' -> 'stash0-date' @@ -234,14 +233,14 @@ static inline QString nextStash(const QString &stash) { const int openingBracePos = stash.indexOf('{'); if (openingBracePos == -1) - return QString(); + return {}; const int closingBracePos = stash.indexOf('}', openingBracePos + 2); if (closingBracePos == -1) - return QString(); + return {}; bool ok; const int n = stash.mid(openingBracePos + 1, closingBracePos - openingBracePos - 1).toInt(&ok); if (!ok) - return QString(); + return {}; QString rc = stash.left(openingBracePos + 1); rc += QString::number(n + 1); rc += '}'; @@ -276,7 +275,7 @@ bool StashDialog::promptForRestore(QString *stash, { const QString stashIn = *stash; bool modifiedPromptShown = false; - switch (GitClient::instance()->gitStatus( + switch (gitClient().gitStatus( m_repository, StatusMode(NoUntracked | NoSubmodules), nullptr, errorMessage)) { case GitClient::StatusFailed: return false; @@ -285,7 +284,7 @@ bool StashDialog::promptForRestore(QString *stash, case ModifiedRepositoryCancel: return false; case ModifiedRepositoryStash: - if (GitClient::instance()->synchronousStash( + if (gitClient().synchronousStash( m_repository, QString(), GitClient::StashPromptDescription).isEmpty()) { return false; } @@ -293,7 +292,7 @@ bool StashDialog::promptForRestore(QString *stash, QTC_ASSERT(!stash->isEmpty(), return false); break; case ModifiedRepositoryDiscard: - if (!GitClient::instance()->synchronousReset(m_repository)) + if (!gitClient().synchronousReset(m_repository)) return false; break; } @@ -330,7 +329,7 @@ void StashDialog::restoreCurrent() // Make sure repository is not modified, restore. The command will // output to window on success. if (promptForRestore(&name, nullptr, &errorMessage) - && GitClient::instance()->synchronousStashRestore(m_repository, name)) { + && gitClient().synchronousStashRestore(m_repository, name)) { refresh(m_repository, true); // Might have stashed away local changes. } else if (!errorMessage.isEmpty()) { warning(msgRestoreFailedTitle(name), errorMessage); @@ -345,7 +344,7 @@ void StashDialog::restoreCurrentInBranch() QString branch; QString name = m_model->at(index).name; if (promptForRestore(&name, &branch, &errorMessage) - && GitClient::instance()->synchronousStashRestore(m_repository, name, false, branch)) { + && gitClient().synchronousStashRestore(m_repository, name, false, branch)) { refresh(m_repository, true); // git deletes the stash, unfortunately. } else if (!errorMessage.isEmpty()) { warning(msgRestoreFailedTitle(name), errorMessage); diff --git a/src/plugins/gitlab/GitLab.json.in b/src/plugins/gitlab/GitLab.json.in index cd65b9ee3ee..ad55a21d7c5 100644 --- a/src/plugins/gitlab/GitLab.json.in +++ b/src/plugins/gitlab/GitLab.json.in @@ -1,19 +1,19 @@ { -\"Name\" : \"GitLab\", -\"Version\" : \"$$QTCREATOR_VERSION\", -\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", -\"Experimental\" : true, -\"Vendor\" : \"The Qt Company Ltd\", -\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", -\"License\" : [ \"Commercial Usage\", -\"\", -\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", -\"\", -\"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.\" +"Name" : "GitLab", +"Version" : "${IDE_VERSION}", +"CompatVersion" : "${IDE_VERSION_COMPAT}", +"Experimental" : true, +"Vendor" : "The Qt Company Ltd", +"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", +"License" : [ "Commercial Usage", +"", +"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", +"", +"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." ], -\"Description\" : \"GitLab plugin.\", -\"Url\" : \"http://www.qt.io\", -$$dependencyList +"Description" : "GitLab plugin.", +"Url" : "http://www.qt.io", +${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/gitlab/gitlabclonedialog.cpp b/src/plugins/gitlab/gitlabclonedialog.cpp index 3a061b9cc57..0a884f5249a 100644 --- a/src/plugins/gitlab/gitlabclonedialog.cpp +++ b/src/plugins/gitlab/gitlabclonedialog.cpp @@ -95,9 +95,11 @@ GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent) connect(m_pathChooser, &PathChooser::textChanged, this, [this] { m_directoryLE->validate(); - GitLabCloneDialog::updateUi(); + updateUi(); }); + connect(m_pathChooser, &PathChooser::validChanged, this, &GitLabCloneDialog::updateUi); connect(m_directoryLE, &FancyLineEdit::textChanged, this, &GitLabCloneDialog::updateUi); + connect(m_directoryLE, &FancyLineEdit::validChanged, this, &GitLabCloneDialog::updateUi); connect(m_cloneButton, &QPushButton::clicked, this, &GitLabCloneDialog::cloneProject); connect(m_cancelButton, &QPushButton::clicked, this, &GitLabCloneDialog::cancel); diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp index 4660cc92bb5..bb471c5a414 100644 --- a/src/plugins/gitlab/gitlaboptionspage.cpp +++ b/src/plugins/gitlab/gitlaboptionspage.cpp @@ -69,19 +69,19 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent) , m_mode(m) { m_host.setLabelText(Tr::tr("Host:")); - m_host.setDisplayStyle(m == Display ? Utils::StringAspect::LabelDisplay - : Utils::StringAspect::LineEditDisplay); - m_host.setValidationFunction([](Utils::FancyLineEdit *l, QString *) { + m_host.setDisplayStyle(m == Display ? StringAspect::LabelDisplay + : StringAspect::LineEditDisplay); + m_host.setValidationFunction([](FancyLineEdit *l, QString *) { return hostValid(l->text()); }); m_description.setLabelText(Tr::tr("Description:")); - m_description.setDisplayStyle(m == Display ? Utils::StringAspect::LabelDisplay - : Utils::StringAspect::LineEditDisplay); + m_description.setDisplayStyle(m == Display ? StringAspect::LabelDisplay + : StringAspect::LineEditDisplay); m_token.setLabelText(Tr::tr("Access token:")); - m_token.setDisplayStyle(m == Display ? Utils::StringAspect::LabelDisplay - : Utils::StringAspect::LineEditDisplay); + m_token.setDisplayStyle(m == Display ? StringAspect::LabelDisplay + : StringAspect::LineEditDisplay); m_token.setVisible(m == Edit); m_port.setLabelText(Tr::tr("Port:")); @@ -89,7 +89,7 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent) m_port.setValue(GitLabServer::defaultPort); m_port.setEnabled(m == Edit); m_secure.setLabelText(Tr::tr("HTTPS:")); - m_secure.setLabelPlacement(Utils::BoolAspect::LabelPlacement::InExtraLabel); + m_secure.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); m_secure.setDefaultValue(true); m_secure.setEnabled(m == Edit); @@ -110,12 +110,12 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent) GitLabServer GitLabServerWidget::gitLabServer() const { GitLabServer result; - result.id = m_mode == Edit ? Utils::Id::fromName(QUuid::createUuid().toByteArray()) : m_id; - result.host = m_host.value(); - result.description = m_description.value(); - result.token = m_token.value(); - result.port = m_port.value(); - result.secure = m_secure.value(); + result.id = m_mode == Edit ? Id::fromName(QUuid::createUuid().toByteArray()) : m_id; + result.host = m_host(); + result.description = m_description(); + result.token = m_token(); + result.port = m_port(); + result.secure = m_secure(); return result; } @@ -178,7 +178,7 @@ GitLabOptionsWidget::GitLabOptionsWidget(GitLabParameters *params) }, Column { m_add, m_edit, m_remove, st }, }.attachTo(this); - m_curl.setFilePath(params->curl); + m_curl.setValue(params->curl); for (const auto &gitLabServer : params->gitLabServers) { m_defaultGitLabServer->addItem(gitLabServer.displayString(), diff --git a/src/plugins/gitlab/gitlabparameters.cpp b/src/plugins/gitlab/gitlabparameters.cpp index 79a6ce7da67..e226d3a523f 100644 --- a/src/plugins/gitlab/gitlabparameters.cpp +++ b/src/plugins/gitlab/gitlabparameters.cpp @@ -5,14 +5,16 @@ #include <utils/algorithm.h> #include <utils/hostosinfo.h> +#include <utils/qtcsettings.h> #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> -#include <QSettings> #include <QStandardPaths> +using namespace Utils; + namespace GitLab { const char settingsGroupC[] = "GitLab"; @@ -153,13 +155,13 @@ static QList<GitLabServer> readTokensFile(const Utils::FilePath &filePath) return result; } -static Utils::FilePath tokensFilePath(const QSettings *s) +static FilePath tokensFilePath(const QtcSettings *s) { - return Utils::FilePath::fromString(s->fileName()).parentDir() + return FilePath::fromString(s->fileName()).parentDir() .pathAppended("/qtcreator/gitlabtokens.json"); } -void GitLabParameters::toSettings(QSettings *s) const +void GitLabParameters::toSettings(QtcSettings *s) const { writeTokensFile(tokensFilePath(s), gitLabServers); @@ -169,11 +171,11 @@ void GitLabParameters::toSettings(QSettings *s) const s->endGroup(); } -void GitLabParameters::fromSettings(const QSettings *s) +void GitLabParameters::fromSettings(const QtcSettings *s) { - const QString rootKey = QLatin1String(settingsGroupC) + '/'; - curl = Utils::FilePath::fromSettings(s->value(rootKey + curlKeyC)); - defaultGitLabServer = Utils::Id::fromSetting(s->value(rootKey + defaultUuidKeyC)); + const Key rootKey = Key(settingsGroupC) + '/'; + curl = FilePath::fromSettings(s->value(rootKey + curlKeyC)); + defaultGitLabServer = Id::fromSetting(s->value(rootKey + defaultUuidKeyC)); gitLabServers = readTokensFile(tokensFilePath(s)); diff --git a/src/plugins/gitlab/gitlabparameters.h b/src/plugins/gitlab/gitlabparameters.h index 7a21976dc24..7dcd6d9dafb 100644 --- a/src/plugins/gitlab/gitlabparameters.h +++ b/src/plugins/gitlab/gitlabparameters.h @@ -7,10 +7,11 @@ #include <utils/id.h> QT_BEGIN_NAMESPACE -class QSettings; class QJsonObject; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace GitLab { class GitLabServer @@ -49,8 +50,8 @@ public: bool equals(const GitLabParameters &other) const; bool isValid() const; - void toSettings(QSettings *s) const; - void fromSettings(const QSettings *s); + void toSettings(Utils::QtcSettings *s) const; + void fromSettings(const Utils::QtcSettings *s); GitLabServer currentDefaultServer() const; GitLabServer serverForId(const Utils::Id &id) const; diff --git a/src/plugins/gitlab/gitlabprojectsettings.cpp b/src/plugins/gitlab/gitlabprojectsettings.cpp index 240f5292e4f..b5bb12942b4 100644 --- a/src/plugins/gitlab/gitlabprojectsettings.cpp +++ b/src/plugins/gitlab/gitlabprojectsettings.cpp @@ -246,13 +246,13 @@ void GitLabProjectSettingsWidget::updateUi() } const Utils::FilePath projectDirectory = m_projectSettings->project()->projectDirectory(); - const auto *gitClient = Git::Internal::GitClient::instance(); - const Utils::FilePath repository = gitClient - ? gitClient->findRepositoryForDirectory(projectDirectory) : Utils::FilePath(); + const Utils::FilePath repository = + Git::Internal::gitClient().findRepositoryForDirectory(projectDirectory); m_hostCB->clear(); if (!repository.isEmpty()) { - const QMap<QString, QString> remotes = gitClient->synchronousRemotesList(repository); + const QMap<QString, QString> remotes = + Git::Internal::gitClient().synchronousRemotesList(repository); for (auto it = remotes.begin(), end = remotes.end(); it != end; ++it) { const QString display = it.key() + " (" + it.value() + ')'; m_hostCB->addItem(display, QVariant::fromValue(it.value())); @@ -291,9 +291,8 @@ void GitLabProjectSettingsWidget::updateEnabledStates() m_checkConnection->setEnabled(isGitRepository && hasGitLabServers); if (!isGitRepository) { const Utils::FilePath projectDirectory = m_projectSettings->project()->projectDirectory(); - const auto *gitClient = Git::Internal::GitClient::instance(); - const Utils::FilePath repository = gitClient - ? gitClient->findRepositoryForDirectory(projectDirectory) : Utils::FilePath(); + const Utils::FilePath repository = + Git::Internal::gitClient().findRepositoryForDirectory(projectDirectory); if (repository.isEmpty()) m_infoLabel->setText(Tr::tr("Not a git repository.")); else diff --git a/src/plugins/gitlab/queryrunner.cpp b/src/plugins/gitlab/queryrunner.cpp index fd56c68c29d..8c48855f0b7 100644 --- a/src/plugins/gitlab/queryrunner.cpp +++ b/src/plugins/gitlab/queryrunner.cpp @@ -51,7 +51,7 @@ QString Query::toString() const QString query = API_PREFIX; switch (m_type) { case Query::NoQuery: - return QString(); + return {}; case Query::Project: QTC_ASSERT(!m_parameter.isEmpty(), return {}); query += QLatin1String(QUERY_PROJECT).arg(QLatin1String( diff --git a/src/plugins/glsleditor/GLSLEditor.json.in b/src/plugins/glsleditor/GLSLEditor.json.in index aa67b7bfc9e..54ef8d548bb 100644 --- a/src/plugins/glsleditor/GLSLEditor.json.in +++ b/src/plugins/glsleditor/GLSLEditor.json.in @@ -1,64 +1,64 @@ { - \"Name\" : \"GLSLEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "GLSLEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Other Languages\", - \"Description\" : \"Editor for GLSL.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Other Languages", + "Description" : "Editor for GLSL.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", - \" <mime-type type=\'application/x-glsl\'>\", - \" <alias type=\'text/x-glsl\'/>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>GLSL Shader file</comment>\", - \" <glob pattern=\'*.glsl\'/>\", - \" <glob pattern=\'*.shader\'/>\", - \" </mime-type>\", + " <mime-type type='application/x-glsl'>", + " <alias type='text/x-glsl'/>", + " <sub-class-of type='text/plain'/>", + " <comment>GLSL Shader file</comment>", + " <glob pattern='*.glsl'/>", + " <glob pattern='*.shader'/>", + " </mime-type>", - \" <mime-type type=\'text/x-glsl-frag\'>\", - \" <sub-class-of type=\'text/x-glsl\'/>\", - \" <comment>GLSL Fragment Shader file</comment>\", - \" <glob pattern=\'*.frag\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-glsl-frag'>", + " <sub-class-of type='text/x-glsl'/>", + " <comment>GLSL Fragment Shader file</comment>", + " <glob pattern='*.frag'/>", + " </mime-type>", - \" <mime-type type=\'text/x-glsl-es-frag\'>\", - \" <sub-class-of type=\'text/x-glsl\'/>\", - \" <comment>GLSL/ES Fragment Shader file</comment>\", - \" <glob pattern=\'*.fsh\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-glsl-es-frag'>", + " <sub-class-of type='text/x-glsl'/>", + " <comment>GLSL/ES Fragment Shader file</comment>", + " <glob pattern='*.fsh'/>", + " </mime-type>", - \" <mime-type type=\'text/x-glsl-vert\'>\", - \" <sub-class-of type=\'text/x-glsl\'/>\", - \" <comment>GLSL Vertex Shader file</comment>\", - \" <glob pattern=\'*.vert\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-glsl-vert'>", + " <sub-class-of type='text/x-glsl'/>", + " <comment>GLSL Vertex Shader file</comment>", + " <glob pattern='*.vert'/>", + " </mime-type>", - \" <mime-type type=\'text/x-glsl-es-vert\'>\", - \" <sub-class-of type=\'text/x-glsl\'/>\", - \" <comment>GLSL/ES Vertex Shader file</comment>\", - \" <glob pattern=\'*.vsh\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-glsl-es-vert'>", + " <sub-class-of type='text/x-glsl'/>", + " <comment>GLSL/ES Vertex Shader file</comment>", + " <glob pattern='*.vsh'/>", + " </mime-type>", - \" <mime-type type=\'text/x-glsl-es-geometry\'>\", - \" <sub-class-of type=\'text/x-glsl\'/>\", - \" <comment>GLSL/ES Geometry Shader file</comment>\", - \" <glob pattern=\'*.gsh\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-glsl-es-geometry'>", + " <sub-class-of type='text/x-glsl'/>", + " <comment>GLSL/ES Geometry Shader file</comment>", + " <glob pattern='*.gsh'/>", + " </mime-type>", - \"</mime-info>\" + "</mime-info>" ] } diff --git a/src/plugins/glsleditor/glsleditor.cpp b/src/plugins/glsleditor/glsleditor.cpp index aecb7cf6545..893aaf47d36 100644 --- a/src/plugins/glsleditor/glsleditor.cpp +++ b/src/plugins/glsleditor/glsleditor.cpp @@ -42,7 +42,6 @@ #include <utils/uncommentselection.h> #include <QCoreApplication> -#include <QSettings> #include <QComboBox> #include <QFileInfo> #include <QHeaderView> @@ -269,11 +268,9 @@ void GlslEditorWidget::updateDocumentNow() for (const DiagnosticMessage &m : messages) { if (! m.line()) continue; - else if (errors.contains(m.line())) + if (!Utils::insert(errors, m.line())) continue; - errors.insert(m.line()); - QTextCursor cursor(document()->findBlockByNumber(m.line() - 1)); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); diff --git a/src/plugins/glsleditor/glslindenter.cpp b/src/plugins/glsleditor/glslindenter.cpp index c914dbc36c4..7a8dcbbb289 100644 --- a/src/plugins/glsleditor/glslindenter.cpp +++ b/src/plugins/glsleditor/glslindenter.cpp @@ -34,8 +34,7 @@ void GlslIndenter::indentBlock(const QTextBlock &block, { // TODO: do something with it CppEditor::QtStyleCodeFormatter - codeFormatter(tabSettings, - CppEditor::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings()); + codeFormatter(tabSettings, CppEditor::CppToolsSettings::cppCodeStyle()->codeStyleSettings()); codeFormatter.updateStateUntil(block); int indent; @@ -66,8 +65,7 @@ void GlslIndenter::indent(const QTextCursor &cursor, // TODO: do something with it CppEditor::QtStyleCodeFormatter codeFormatter(tabSettings, - CppEditor::CppToolsSettings::instance() - ->cppCodeStyle() + CppEditor::CppToolsSettings::cppCodeStyle() ->codeStyleSettings()); codeFormatter.updateStateUntil(block); @@ -92,8 +90,7 @@ int GlslIndenter::indentFor(const QTextBlock &block, int /*cursorPositionInEditor*/) { CppEditor::QtStyleCodeFormatter - codeFormatter(tabSettings, - CppEditor::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings()); + codeFormatter(tabSettings, CppEditor::CppToolsSettings::cppCodeStyle()->codeStyleSettings()); codeFormatter.updateStateUntil(block); int indent; @@ -109,8 +106,7 @@ TextEditor::IndentationForBlock GlslIndenter::indentationForBlocks( int /*cursorPositionInEditor*/) { CppEditor::QtStyleCodeFormatter - codeFormatter(tabSettings, - CppEditor::CppToolsSettings::instance()->cppCodeStyle()->codeStyleSettings()); + codeFormatter(tabSettings, CppEditor::CppToolsSettings::cppCodeStyle()->codeStyleSettings()); codeFormatter.updateStateUntil(blocks.last()); diff --git a/src/plugins/haskell/Haskell.json.in b/src/plugins/haskell/Haskell.json.in index f9062c88b27..9cdb34694a5 100644 --- a/src/plugins/haskell/Haskell.json.in +++ b/src/plugins/haskell/Haskell.json.in @@ -1,23 +1,23 @@ { - \"Name\" : \"Haskell\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"Eike Ziller\", - \"Copyright\" : \"(C) Eike Ziller\", - \"License\" : \"MIT\", - \"Description\" : \"Haskell support\", - \"Url\" : \"https://haskell.org\", - $$dependencyList, + "Name" : "Haskell", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "Eike Ziller", + "Copyright" : "(C) Eike Ziller", + "License" : "MIT", + "Description" : "Haskell support", + "Url" : "https://haskell.org", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-haskell-project\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Haskell Cabal project file</comment>\", - \" <glob pattern=\'*.cabal\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-haskell-project'>", + " <sub-class-of type='text/plain'/>", + " <comment>Haskell Cabal project file</comment>", + " <glob pattern='*.cabal'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/haskell/haskelleditorfactory.cpp b/src/plugins/haskell/haskelleditorfactory.cpp index ba87f88ef70..f3e65ffcd8e 100644 --- a/src/plugins/haskell/haskelleditorfactory.cpp +++ b/src/plugins/haskell/haskelleditorfactory.cpp @@ -16,12 +16,12 @@ namespace Haskell::Internal { -static QWidget *createEditorWidget(QObject *guard) +static QWidget *createEditorWidget() { auto widget = new TextEditor::TextEditorWidget; auto ghciButton = new Core::CommandButton(Constants::A_RUN_GHCI, widget); ghciButton->setText(Tr::tr("GHCi")); - QObject::connect(ghciButton, &QToolButton::clicked, guard, [widget] { + QObject::connect(ghciButton, &QToolButton::clicked, widget, [widget] { HaskellManager::openGhci(widget->textDocument()->filePath()); }); widget->insertExtraToolBarWidget(TextEditor::TextEditorWidget::Left, ghciButton); @@ -37,7 +37,7 @@ HaskellEditorFactory::HaskellEditorFactory() | TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor); setDocumentCreator([] { return new TextEditor::TextDocument(Constants::C_HASKELLEDITOR_ID); }); setIndenterCreator([](QTextDocument *doc) { return new TextEditor::TextIndenter(doc); }); - setEditorWidgetCreator([this] { return createEditorWidget(this); }); + setEditorWidgetCreator(&createEditorWidget); setCommentDefinition(Utils::CommentDefinition("--", "{-", "-}")); setParenthesesMatchingEnabled(true); setMarksVisible(true); diff --git a/src/plugins/haskell/haskellplugin.cpp b/src/plugins/haskell/haskellplugin.cpp index 84a86f7db48..9c082c67a9e 100644 --- a/src/plugins/haskell/haskellplugin.cpp +++ b/src/plugins/haskell/haskellplugin.cpp @@ -9,15 +9,17 @@ #include "haskellmanager.h" #include "haskellproject.h" #include "haskellrunconfiguration.h" -#include "haskellsettings.h" #include "haskelltr.h" #include "stackbuildstep.h" #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> -#include <projectexplorer/projectmanager.h> + #include <projectexplorer/jsonwizard/jsonwizardfactory.h> +#include <projectexplorer/projectmanager.h> +#include <projectexplorer/runcontrol.h> + #include <texteditor/snippets/snippetprovider.h> #include <QAction> @@ -28,7 +30,6 @@ namespace Internal { class HaskellPluginPrivate { public: - HaskellSettings settings; HaskellEditorFactory editorFactory; HaskellBuildConfigurationFactory buildConfigFactory; StackBuildStepFactory stackBuildStepFactory; diff --git a/src/plugins/haskell/haskellrunconfiguration.cpp b/src/plugins/haskell/haskellrunconfiguration.cpp index 76d25e557c3..0381f86784a 100644 --- a/src/plugins/haskell/haskellrunconfiguration.cpp +++ b/src/plugins/haskell/haskellrunconfiguration.cpp @@ -4,19 +4,76 @@ #include "haskellrunconfiguration.h" #include "haskellconstants.h" -#include "haskellproject.h" #include "haskelltr.h" #include "haskellsettings.h" #include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> -using namespace ProjectExplorer; +#include <utils/processinterface.h> -namespace Haskell { -namespace Internal { +using namespace ProjectExplorer; +using namespace Utils; + +namespace Haskell::Internal { + +class HaskellRunConfiguration : public RunConfiguration +{ +public: + HaskellRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + environment.setSupportForBuildEnvironment(target); + + executable.setSettingsKey("Haskell.Executable"); + executable.setLabelText(Tr::tr("Executable")); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + workingDir.setDefaultWorkingDirectory(project()->projectDirectory()); + workingDir.setVisible(false); + + setUpdater([this] { executable.setValue(buildTargetInfo().buildKey); }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + update(); + } + +private: + Utils::ProcessRunData runnable() const final + { + const FilePath projectDirectory = project()->projectDirectory(); + ProcessRunData r; + QStringList args; + if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration()) { + args << "--work-dir" + << QDir(projectDirectory.toString()).relativeFilePath( + buildConfiguration->buildDirectory().toString()); + } + args << "exec" << executable(); + if (!arguments.arguments().isEmpty()) + args << "--" << arguments.arguments(); + + r.workingDirectory = projectDirectory; + r.environment = environment.environment(); + r.command = {r.environment.searchInPath(settings().stackPath().path()), args}; + return r; + } + + EnvironmentAspect environment{this}; + StringAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; +}; + +// Factory HaskellRunConfigurationFactory::HaskellRunConfigurationFactory() { @@ -25,52 +82,4 @@ HaskellRunConfigurationFactory::HaskellRunConfigurationFactory() addSupportedTargetDeviceType(ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE); } -HaskellExecutableAspect::HaskellExecutableAspect() -{ - setSettingsKey("Haskell.Executable"); - setLabelText(Tr::tr("Executable")); -} - -HaskellRunConfiguration::HaskellRunConfiguration(Target *target, Utils::Id id) - : RunConfiguration(target, id) -{ - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); - - addAspect<HaskellExecutableAspect>(); - addAspect<ArgumentsAspect>(macroExpander()); - - auto workingDirAspect = addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - workingDirAspect->setDefaultWorkingDirectory(target->project()->projectDirectory()); - workingDirAspect->setVisible(false); - - addAspect<TerminalAspect>(); - - setUpdater([this] { aspect<HaskellExecutableAspect>()->setValue(buildTargetInfo().buildKey); }); - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - update(); -} - -Runnable HaskellRunConfiguration::runnable() const -{ - const Utils::FilePath projectDirectory = target()->project()->projectDirectory(); - Runnable r; - QStringList args; - if (BuildConfiguration *buildConfiguration = target()->activeBuildConfiguration()) { - args << "--work-dir" - << QDir(projectDirectory.toString()).relativeFilePath( - buildConfiguration->buildDirectory().toString()); - } - args << "exec" << aspect<HaskellExecutableAspect>()->value(); - const QString arguments = aspect<ArgumentsAspect>()->arguments(); - if (!arguments.isEmpty()) - args << "--" << arguments; - - r.workingDirectory = projectDirectory; - r.environment = aspect<EnvironmentAspect>()->environment(); - r.command = {r.environment.searchInPath(settings().stackPath().path()), args}; - return r; -} - -} // namespace Internal -} // namespace Haskell +} // Haskell::Internal diff --git a/src/plugins/haskell/haskellrunconfiguration.h b/src/plugins/haskell/haskellrunconfiguration.h index c0c0423d2a0..579f8110a75 100644 --- a/src/plugins/haskell/haskellrunconfiguration.h +++ b/src/plugins/haskell/haskellrunconfiguration.h @@ -3,24 +3,9 @@ #pragma once -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/runcontrol.h> -#include <utils/aspects.h> +#include <projectexplorer/runconfiguration.h> -namespace Haskell { -namespace Internal { - -class HaskellRunConfiguration : public ProjectExplorer::RunConfiguration -{ - Q_OBJECT - -public: - HaskellRunConfiguration(ProjectExplorer::Target *target, Utils::Id id); - -private: - ProjectExplorer::Runnable runnable() const final; -}; +namespace Haskell::Internal { class HaskellRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory { @@ -28,13 +13,4 @@ public: HaskellRunConfigurationFactory(); }; -class HaskellExecutableAspect : public Utils::StringAspect -{ - Q_OBJECT - -public: - HaskellExecutableAspect(); -}; - -} // namespace Internal -} // namespace Haskell +} // Haskell::Internal diff --git a/src/plugins/haskell/haskellsettings.cpp b/src/plugins/haskell/haskellsettings.cpp index 7b2bbc7fd38..ffc6fa066dc 100644 --- a/src/plugins/haskell/haskellsettings.cpp +++ b/src/plugins/haskell/haskellsettings.cpp @@ -6,6 +6,8 @@ #include "haskellconstants.h" #include "haskelltr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> @@ -13,22 +15,15 @@ using namespace Utils; namespace Haskell::Internal { -static HaskellSettings *theSettings; - HaskellSettings &settings() { - return *theSettings; + static HaskellSettings theSettings; + return theSettings; } HaskellSettings::HaskellSettings() { - theSettings = this; - - setId(Constants::OPTIONS_GENERAL); - setDisplayName(Tr::tr("General")); - setCategory("J.Z.Haskell"); - setDisplayCategory(Tr::tr("Haskell")); - setCategoryIconPath(":/haskell/images/settingscategory_haskell.png"); + setAutoApply(false); stackPath.setSettingsKey("Haskell/StackExecutable"); stackPath.setExpectedKind(PathChooser::ExistingCommand); @@ -37,9 +32,9 @@ HaskellSettings::HaskellSettings() // stack from brew or the installer script from https://docs.haskellstack.org // install to /usr/local/bin. - stackPath.setDefaultFilePath(HostOsInfo::isAnyUnixHost() - ? FilePath::fromString("/usr/local/bin/stack") - : FilePath::fromString("stack")); + stackPath.setDefaultValue(HostOsInfo::isAnyUnixHost() + ? QLatin1String("/usr/local/bin/stack") + : QLatin1String("stack")); setLayouter([this] { using namespace Layouting; @@ -55,4 +50,22 @@ HaskellSettings::HaskellSettings() readSettings(); } +// HaskellSettingsPage + +class HaskellSettingsPage final : public Core::IOptionsPage +{ +public: + HaskellSettingsPage() + { + setId(Constants::OPTIONS_GENERAL); + setDisplayName(Tr::tr("General")); + setCategory("J.Z.Haskell"); + setDisplayCategory(Tr::tr("Haskell")); + setCategoryIconPath(":/haskell/images/settingscategory_haskell.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const HaskellSettingsPage settingsPage; + } // Haskell::Internal diff --git a/src/plugins/haskell/haskellsettings.h b/src/plugins/haskell/haskellsettings.h index 1331e5aa89a..dbe7c6b40b9 100644 --- a/src/plugins/haskell/haskellsettings.h +++ b/src/plugins/haskell/haskellsettings.h @@ -3,11 +3,11 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace Haskell::Internal { -class HaskellSettings : public Core::PagedSettings +class HaskellSettings final : public Utils::AspectContainer { public: HaskellSettings(); diff --git a/src/plugins/helloworld/HelloWorld.json.in b/src/plugins/helloworld/HelloWorld.json.in index d471fd0ba98..bf6fa1587be 100644 --- a/src/plugins/helloworld/HelloWorld.json.in +++ b/src/plugins/helloworld/HelloWorld.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"HelloWorld\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "HelloWorld", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Hello World sample plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Hello World sample plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/helloworld/helloworld.qbs b/src/plugins/helloworld/helloworld.qbs index 65fc74e3b3e..41f6571426e 100644 --- a/src/plugins/helloworld/helloworld.qbs +++ b/src/plugins/helloworld/helloworld.qbs @@ -4,13 +4,14 @@ QtcPlugin { name: "HelloWorld" Depends { name: "Core" } - Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] } + Depends { name: "Qt"; submodules: ["widgets"] } files: [ "helloworldplugin.cpp", "helloworldplugin.h", "helloworldwindow.cpp", "helloworldwindow.h", + "helloworldtr.h" ] } diff --git a/src/plugins/helloworld/helloworldplugin.cpp b/src/plugins/helloworld/helloworldplugin.cpp index 5a7c22f2163..9269910ce55 100644 --- a/src/plugins/helloworld/helloworldplugin.cpp +++ b/src/plugins/helloworld/helloworldplugin.cpp @@ -16,8 +16,7 @@ #include <QMessageBox> #include <QPushButton> -namespace HelloWorld { -namespace Internal { +namespace HelloWorld::Internal { /*! A mode with a push button based on BaseMode. */ @@ -53,17 +52,12 @@ HelloWorldPlugin::~HelloWorldPlugin() delete m_helloMode; } -/*! Initializes the plugin. Returns true on success. +/*! Initializes the plugin. Plugins want to register objects with the plugin manager here. - \a errorMessage can be used to pass an error message to the plugin system, - if there was any. */ -bool HelloWorldPlugin::initialize(const QStringList &arguments, QString *errorMessage) +void HelloWorldPlugin::initialize() { - Q_UNUSED(arguments) - Q_UNUSED(errorMessage) - // Create a unique context for our own view, that will be used for the // menu entry later. Core::Context context("HelloWorld.MainView"); @@ -94,8 +88,6 @@ bool HelloWorldPlugin::initialize(const QStringList &arguments, QString *errorMe // Add a mode with a push button based on BaseMode. m_helloMode = new HelloMode; - - return true; } /*! Notification that all extensions that this plugin depends on have been @@ -121,5 +113,4 @@ void HelloWorldPlugin::sayHelloWorld() nullptr, tr("Hello World!"), tr("Hello World! Beautiful day today, isn't it?")); } -} // namespace Internal -} // namespace HelloWorld +} // namespace HelloWorld::Internal diff --git a/src/plugins/helloworld/helloworldplugin.h b/src/plugins/helloworld/helloworldplugin.h index 4c37aed60c6..773308b2eb7 100644 --- a/src/plugins/helloworld/helloworldplugin.h +++ b/src/plugins/helloworld/helloworldplugin.h @@ -5,8 +5,7 @@ #include <extensionsystem/iplugin.h> -namespace HelloWorld { -namespace Internal { +namespace HelloWorld::Internal { class HelloMode; @@ -20,8 +19,7 @@ public: HelloWorldPlugin(); ~HelloWorldPlugin() override; - bool initialize(const QStringList &arguments, QString *errorMessage) override; - + void initialize() override; void extensionsInitialized() override; private: @@ -30,5 +28,4 @@ private: HelloMode *m_helloMode = nullptr; }; -} // namespace Internal -} // namespace HelloWorld +} // namespace HelloWorld::Internal diff --git a/src/plugins/helloworld/helloworldwindow.h b/src/plugins/helloworld/helloworldwindow.h index 8afb694b71e..d9e57615a32 100644 --- a/src/plugins/helloworld/helloworldwindow.h +++ b/src/plugins/helloworld/helloworldwindow.h @@ -5,16 +5,13 @@ #include <QWidget> -namespace HelloWorld { -namespace Internal { +namespace HelloWorld::Internal { class HelloWorldWindow : public QWidget { Q_OBJECT - public: explicit HelloWorldWindow(QWidget *parent = nullptr); }; -} // namespace Internal -} // namespace HelloWorld +} // namespace HelloWorld::Internal diff --git a/src/plugins/help/Help.json.in b/src/plugins/help/Help.json.in index 98f79b0b3f0..e6ecc9eeb37 100644 --- a/src/plugins/help/Help.json.in +++ b/src/plugins/help/Help.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Help\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Help", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Core\", - \"Description\" : \"Help system.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Core", + "Description" : "Help system.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/help/docsettingspage.cpp b/src/plugins/help/docsettingspage.cpp index 354971a1200..2d0cc9de2b2 100644 --- a/src/plugins/help/docsettingspage.cpp +++ b/src/plugins/help/docsettingspage.cpp @@ -289,7 +289,7 @@ void DocSettingsPageWidget::addDocumentation() void DocSettingsPageWidget::apply() { - HelpManager::unregisterNamespaces(m_filesToUnregister.keys()); + HelpManager::instance()->unregisterDocumentation(m_filesToUnregister.values()); QStringList files; auto it = m_filesToRegisterUserManaged.constBegin(); while (it != m_filesToRegisterUserManaged.constEnd()) { diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp index d5f5756ed69..ff1e7e82182 100644 --- a/src/plugins/help/generalsettingspage.cpp +++ b/src/plugins/help/generalsettingspage.cpp @@ -18,6 +18,7 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> +#include <utils/layoutbuilder.h> #include <QCheckBox> #include <QComboBox> @@ -31,7 +32,6 @@ #include <QLineEdit> #include <QLabel> #include <QPushButton> -#include <QSettings> #include <QSpinBox> #include <QTextStream> #include <QVBoxLayout> @@ -76,6 +76,7 @@ private: QFontComboBox *familyComboBox; QComboBox *styleComboBox; QComboBox *sizeComboBox; + QCheckBox *antialiasCheckBox; QLineEdit *homePageLineEdit; QComboBox *helpStartComboBox; QComboBox *contextHelpComboBox; @@ -92,49 +93,31 @@ private: GeneralSettingsPageWidget::GeneralSettingsPageWidget() { + using namespace Layouting; + // font group box - auto fontGroupBox = new QGroupBox(Tr::tr("Font")); - auto familyLabel = new QLabel(Tr::tr("Family:")); - familyComboBox = new QFontComboBox; - auto styleLabel = new QLabel(Tr::tr("Style:")); styleComboBox = new QComboBox; - auto sizeLabel = new QLabel(Tr::tr("Size:")); sizeComboBox = new QComboBox; - - auto fontLayout = new QHBoxLayout(); - fontLayout->addWidget(familyComboBox); - fontLayout->addSpacing(20); - fontLayout->addWidget(styleLabel); - fontLayout->addWidget(styleComboBox); - fontLayout->addSpacing(20); - fontLayout->addWidget(sizeLabel); - fontLayout->addWidget(sizeComboBox); - fontLayout->addStretch(); - - auto noteLabel = new QLabel(Tr::tr( - "Note: The above setting takes effect only if the HTML file does not use a style sheet.")); - noteLabel->setWordWrap(true); - auto zoomLabel = new QLabel(Tr::tr("Zoom:")); - zoomSpinBox = new QSpinBox; zoomSpinBox->setMinimum(10); zoomSpinBox->setMaximum(3000); zoomSpinBox->setSingleStep(10); zoomSpinBox->setValue(100); zoomSpinBox->setSuffix(Tr::tr("%")); + antialiasCheckBox = new QCheckBox(Tr::tr("Antialias")); - auto zoomLayout = new QHBoxLayout(); - zoomLayout->addWidget(zoomSpinBox); - zoomLayout->addStretch(); - - auto fontGroupBoxLayout = new QGridLayout; - fontGroupBox->setLayout(fontGroupBoxLayout); - fontGroupBoxLayout->addWidget(familyLabel, 0, 0); - fontGroupBoxLayout->addLayout(fontLayout, 0, 1); - fontGroupBoxLayout->addWidget(noteLabel, 1, 0, 1, 2); - fontGroupBoxLayout->addWidget(zoomLabel, 2, 0); - fontGroupBoxLayout->addLayout(zoomLayout, 2, 1); + auto fontGroupBox = new QGroupBox(Tr::tr("Font")); + // clang-format off + Column { + Row { Tr::tr("Family:"), familyComboBox, + Tr::tr("Style:"), styleComboBox, + Tr::tr("Size:"), sizeComboBox, st }, + Row { Tr::tr("Note: The above setting takes effect only if the " + "HTML file does not use a style sheet.") }, + Row { Tr::tr("Zoom:"), zoomSpinBox, antialiasCheckBox, st } + }.attachTo(fontGroupBox); + // clang-format on // startup group box auto startupGroupBox = new QGroupBox(Tr::tr("Startup")); @@ -231,6 +214,7 @@ GeneralSettingsPageWidget::GeneralSettingsPageWidget() m_font = LocalHelpManager::fallbackFont(); m_fontZoom = LocalHelpManager::fontZoom(); zoomSpinBox->setValue(m_fontZoom); + antialiasCheckBox->setChecked(LocalHelpManager::antialias()); updateFontSizeSelector(); updateFontStyleSelector(); @@ -309,6 +293,8 @@ void GeneralSettingsPageWidget::apply() if (m_fontZoom != LocalHelpManager::fontZoom()) LocalHelpManager::setFontZoom(m_fontZoom); + LocalHelpManager::setAntialias(antialiasCheckBox->isChecked()); + QString homePage = QUrl::fromUserInput(homePageLineEdit->text()).toString(); if (homePage.isEmpty()) homePage = Help::Constants::AboutBlank; diff --git a/src/plugins/help/help.qbs b/src/plugins/help/help.qbs index d96cfa154e7..df960310e5b 100644 --- a/src/plugins/help/help.qbs +++ b/src/plugins/help/help.qbs @@ -1,92 +1,86 @@ import qbs.Utilities -Project { +QtcPlugin { name: "Help" - QtcPlugin { - name: "Help" + Depends { name: "Qt"; submodules: ["help", "network", "sql"] } + Depends { name: "Qt.printsupport" } + Depends { name: "Qt.webenginewidgets"; required: false } - Depends { name: "Qt"; submodules: ["help", "network", "sql"]; } - Depends { name: "Qt.printsupport" } - Depends { name: "Qt.webenginewidgets"; required: false } + Depends { name: "Aggregation" } + Depends { name: "Utils" } - Depends { name: "Aggregation" } - Depends { name: "Utils" } + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } - Depends { name: "Core" } - Depends { name: "ProjectExplorer" } + Depends { name: "qlitehtml"; required: false } - Depends { name: "app_version_header" } + cpp.defines: { + var defines = base.concat(["QT_CLUCENE_SUPPORT"]); + if (Qt.webenginewidgets.present) + defines.push("QTC_WEBENGINE_HELPVIEWER"); + if (qlitehtml.present) + defines.push("QTC_LITEHTML_HELPVIEWER") + return defines; + } - Depends { name: "qlitehtml"; required: false } + // We include headers from src/shared/help, and their sources include headers from here... + cpp.includePaths: base.concat([sharedSources.prefix, path]) - cpp.defines: { - var defines = base.concat(["QT_CLUCENE_SUPPORT"]); - if (Qt.webenginewidgets.present) - defines.push("QTC_WEBENGINE_HELPVIEWER"); - if (qlitehtml.present) - defines.push("QTC_LITEHTML_HELPVIEWER") - return defines; - } + Group { + name: "Sources" + files: [ + "docsettingspage.cpp", "docsettingspage.h", + "filtersettingspage.cpp", "filtersettingspage.h", + "generalsettingspage.cpp", "generalsettingspage.h", + "help.qrc", + "helpconstants.h", + "helpfindsupport.cpp", "helpfindsupport.h", + "helpindexfilter.cpp", "helpindexfilter.h", + "helpmanager.cpp", "helpmanager.h", + "helpplugin.cpp", "helpplugin.h", + "helpviewer.cpp", "helpviewer.h", + "helpwidget.cpp", "helpwidget.h", + "helptr.h", + "localhelpmanager.cpp", "localhelpmanager.h", + "openpagesmanager.cpp", "openpagesmanager.h", + "openpagesswitcher.cpp", "openpagesswitcher.h", + "openpageswidget.cpp", "openpageswidget.h", + "searchtaskhandler.cpp", "searchtaskhandler.h", + "searchwidget.cpp", "searchwidget.h", + "textbrowserhelpviewer.cpp", "textbrowserhelpviewer.h", + "xbelsupport.cpp", "xbelsupport.h", + ] + } - // We include headers from src/shared/help, and their sources include headers from here... - cpp.includePaths: base.concat([sharedSources.prefix, path]) + Group { + name: "WebEngine Sources" + condition: Qt.webenginewidgets.present + files: [ + "webenginehelpviewer.cpp", "webenginehelpviewer.h" + ] + } - Group { - name: "Sources" - files: [ - "docsettingspage.cpp", "docsettingspage.h", - "filtersettingspage.cpp", "filtersettingspage.h", - "generalsettingspage.cpp", "generalsettingspage.h", - "help.qrc", - "helpconstants.h", - "helpfindsupport.cpp", "helpfindsupport.h", - "helpindexfilter.cpp", "helpindexfilter.h", - "helpmanager.cpp", "helpmanager.h", - "helpplugin.cpp", "helpplugin.h", - "helpviewer.cpp", "helpviewer.h", - "helpwidget.cpp", "helpwidget.h", - "helptr.h", - "localhelpmanager.cpp", "localhelpmanager.h", - "openpagesmanager.cpp", "openpagesmanager.h", - "openpagesswitcher.cpp", "openpagesswitcher.h", - "openpageswidget.cpp", "openpageswidget.h", - "searchtaskhandler.cpp", "searchtaskhandler.h", - "searchwidget.cpp", "searchwidget.h", - "textbrowserhelpviewer.cpp", "textbrowserhelpviewer.h", - "xbelsupport.cpp", "xbelsupport.h", - ] - } + Group { + name: "litehtml-specific sources" + condition: qlitehtml.present + cpp.warningLevel: "none" + files: [ + "litehtmlhelpviewer.cpp", + "litehtmlhelpviewer.h", + ] + } - Group { - name: "WebEngine Sources" - condition: Qt.webenginewidgets.present - files: [ - "webenginehelpviewer.cpp", "webenginehelpviewer.h" - ] - } - - Group { - name: "litehtml-specific sources" - condition: qlitehtml.present - cpp.warningLevel: "none" - files: [ - "litehtmlhelpviewer.cpp", - "litehtmlhelpviewer.h", - ] - } - - Group { - id: sharedSources - name: "Shared Sources" - prefix: project.sharedSourcesDir + "/help/" - files: [ - "bookmarkmanager.cpp", "bookmarkmanager.h", - "contentwindow.cpp", "contentwindow.h", - "helpicons.h", - "indexwindow.cpp", "indexwindow.h", - "topicchooser.cpp", "topicchooser.h", - ] - } + Group { + id: sharedSources + name: "Shared Sources" + prefix: project.sharedSourcesDir + "/help/" + files: [ + "bookmarkmanager.cpp", "bookmarkmanager.h", + "contentwindow.cpp", "contentwindow.h", + "helpicons.h", + "indexwindow.cpp", "indexwindow.h", + "topicchooser.cpp", "topicchooser.h", + ] } } diff --git a/src/plugins/help/helpconstants.h b/src/plugins/help/helpconstants.h index f47d325313f..5acb6281056 100644 --- a/src/plugins/help/helpconstants.h +++ b/src/plugins/help/helpconstants.h @@ -6,13 +6,10 @@ #include <QtGlobal> #include <QLatin1String> -namespace Help { -namespace Constants { +namespace Help::Constants { -static const QLatin1String ListSeparator("|"); -static const QLatin1String AboutBlank("about:blank"); -static const QLatin1String WeAddedFilterKey("UnfilteredFilterInserted"); -static const QLatin1String PreviousFilterNameKey("UnfilteredFilterName"); +const QLatin1String ListSeparator("|"); +const QLatin1String AboutBlank("about:blank"); const int P_MODE_HELP = 70; const char ID_MODE_HELP [] = "Help"; @@ -34,14 +31,13 @@ const char HELP_SEARCH[] = "Help.Search"; const char HELP_BOOKMARKS[] = "Help.Bookmarks"; const char HELP_OPENPAGES[] = "Help.OpenPages"; -static const char SB_INDEX[] = QT_TRANSLATE_NOOP("QtC::Help", "Index"); -static const char SB_CONTENTS[] = QT_TRANSLATE_NOOP("QtC::Help", "Contents"); -static const char SB_BOOKMARKS[] = QT_TRANSLATE_NOOP("QtC::Help", "Bookmarks"); -static const char SB_OPENPAGES[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Pages"); -static const char SB_SEARCH[] = QT_TRANSLATE_NOOP("QtC::Help", "Search"); +const char SB_INDEX[] = QT_TRANSLATE_NOOP("QtC::Help", "Index"); +const char SB_CONTENTS[] = QT_TRANSLATE_NOOP("QtC::Help", "Contents"); +const char SB_BOOKMARKS[] = QT_TRANSLATE_NOOP("QtC::Help", "Bookmarks"); +const char SB_OPENPAGES[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Pages"); +const char SB_SEARCH[] = QT_TRANSLATE_NOOP("QtC::Help", "Search"); -static const char TR_OPEN_LINK_AS_NEW_PAGE[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Link as New Page"); -static const char TR_OPEN_LINK_IN_WINDOW[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Link in Window"); +const char TR_OPEN_LINK_AS_NEW_PAGE[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Link as New Page"); +const char TR_OPEN_LINK_IN_WINDOW[] = QT_TRANSLATE_NOOP("QtC::Help", "Open Link in Window"); -} // Constants -} // Help +} // Help::Constants diff --git a/src/plugins/help/helpfindsupport.cpp b/src/plugins/help/helpfindsupport.cpp index cc2c1b1381f..799fb4c5e87 100644 --- a/src/plugins/help/helpfindsupport.cpp +++ b/src/plugins/help/helpfindsupport.cpp @@ -8,6 +8,7 @@ using namespace Core; using namespace Help::Internal; +using namespace Utils; HelpViewerFindSupport::HelpViewerFindSupport(HelpViewer *viewer) : m_viewer(viewer) diff --git a/src/plugins/help/helpfindsupport.h b/src/plugins/help/helpfindsupport.h index cdb0aa10a37..8a334083bde 100644 --- a/src/plugins/help/helpfindsupport.h +++ b/src/plugins/help/helpfindsupport.h @@ -17,17 +17,17 @@ public: HelpViewerFindSupport(HelpViewer *viewer); bool supportsReplace() const override { return false; } - Core::FindFlags supportedFindFlags() const override; + Utils::FindFlags supportedFindFlags() const override; void resetIncrementalSearch() override {} void clearHighlights() override {} QString currentFindString() const override; QString completedFindString() const override { return QString(); } - Result findIncremental(const QString &txt, Core::FindFlags findFlags) override; - Result findStep(const QString &txt, Core::FindFlags findFlags) override; + Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Result findStep(const QString &txt, Utils::FindFlags findFlags) override; private: - bool find(const QString &ttf, Core::FindFlags findFlags, bool incremental); + bool find(const QString &ttf, Utils::FindFlags findFlags, bool incremental); HelpViewer *m_viewer; }; diff --git a/src/plugins/help/helpmanager.cpp b/src/plugins/help/helpmanager.cpp index 3c053662d54..6a232083bd1 100644 --- a/src/plugins/help/helpmanager.cpp +++ b/src/plugins/help/helpmanager.cpp @@ -29,13 +29,15 @@ #include <QtHelp/QHelpLink> using namespace Core; - -static const char kUserDocumentationKey[] = "Help/UserDocumentation"; -static const char kUpdateDocumentationTask[] = "UpdateDocumentationTask"; +using namespace Utils; namespace Help { namespace Internal { +const char kUserDocumentationKey[] = "Help/UserDocumentation"; +const char kUpdateDocumentationTask[] = "UpdateDocumentationTask"; +const char kPurgeDocumentationTask[] = "PurgeDocumentationTask"; + struct HelpManagerPrivate { HelpManagerPrivate() = default; @@ -52,7 +54,7 @@ struct HelpManagerPrivate // data for delayed initialization QSet<QString> m_filesToRegister; - QSet<QString> m_nameSpacesToUnregister; + QSet<QString> m_filesToUnregister; QHash<QString, QVariant> m_customValues; QSet<QString> m_userRegisteredFiles; @@ -93,45 +95,15 @@ QString HelpManager::collectionFilePath() return ICore::userResourcePath("helpcollection.qhc").toString(); } -void HelpManager::registerDocumentation(const QStringList &files) -{ - if (d->m_needsSetup) { - for (const QString &filePath : files) - d->m_filesToRegister.insert(filePath); - return; - } - - QFuture<bool> future = Utils::asyncRun(&HelpManager::registerDocumentationNow, files); - Utils::onResultReady(future, this, [](bool docsChanged){ - if (docsChanged) { - d->m_helpEngine->setupData(); - emit Core::HelpManager::Signals::instance()->documentationChanged(); - } - }); - ProgressManager::addTask(future, Tr::tr("Update Documentation"), kUpdateDocumentationTask); -} - -void HelpManager::unregisterDocumentation(const QStringList &fileNames) -{ - if (fileNames.isEmpty()) - return; - const auto getNamespaces = [](const QStringList &fileNames) { - QMutexLocker locker(&d->m_helpengineMutex); - return Utils::transform(fileNames, [](const QString &filePath) { - return QHelpEngineCore::namespaceName(filePath); - }); - }; - unregisterNamespaces(getNamespaces(fileNames)); -} - -void HelpManager::registerDocumentationNow(QPromise<bool> &promise, const QStringList &files) +static void registerDocumentationNow(QPromise<bool> &promise, const QString &collectionFilePath, + const QStringList &files) { QMutexLocker locker(&d->m_helpengineMutex); promise.setProgressRange(0, files.count()); promise.setProgressValue(0); - QHelpEngineCore helpEngine(collectionFilePath()); + QHelpEngineCore helpEngine(collectionFilePath); helpEngine.setReadOnly(false); helpEngine.setupData(); bool docsChanged = false; @@ -149,39 +121,87 @@ void HelpManager::registerDocumentationNow(QPromise<bool> &promise, const QStrin docsChanged = true; } else { qWarning() << "Error registering namespace '" << nameSpace - << "' from file '" << file << "':" << helpEngine.error(); + << "' from file '" << file << "':" << helpEngine.error(); } } } promise.addResult(docsChanged); } -void HelpManager::unregisterNamespaces(const QStringList &nameSpaces) +void HelpManager::registerDocumentation(const QStringList &files) { if (d->m_needsSetup) { - for (const QString &name : nameSpaces) - d->m_nameSpacesToUnregister.insert(name); + for (const QString &filePath : files) + d->m_filesToRegister.insert(filePath); return; } + QFuture<bool> future = Utils::asyncRun(®isterDocumentationNow, collectionFilePath(), files); + Utils::onResultReady(future, this, [](bool docsChanged){ + if (docsChanged) { + d->m_helpEngine->setupData(); + emit Core::HelpManager::Signals::instance()->documentationChanged(); + } + }); + ProgressManager::addTask(future, Tr::tr("Update Documentation"), kUpdateDocumentationTask); +} + +static void unregisterDocumentationNow(QPromise<bool> &promise, + const QString collectionFilePath, + const QStringList &files) +{ QMutexLocker locker(&d->m_helpengineMutex); + + promise.setProgressRange(0, files.count()); + promise.setProgressValue(0); + bool docsChanged = false; - for (const QString &nameSpace : nameSpaces) { - const QString filePath = d->m_helpEngine->documentationFileName(nameSpace); + + QHelpEngineCore helpEngine(collectionFilePath); + helpEngine.setReadOnly(false); + helpEngine.setupData(); + for (const QString &file : files) { + if (promise.isCanceled()) + break; + promise.setProgressValue(promise.future().progressValue() + 1); + const QString nameSpace = QHelpEngineCore::namespaceName(file); + const QString filePath = helpEngine.documentationFileName(nameSpace); if (filePath.isEmpty()) // wasn't registered anyhow, ignore continue; - if (d->m_helpEngine->unregisterDocumentation(nameSpace)) { + if (helpEngine.unregisterDocumentation(nameSpace)) { docsChanged = true; - d->m_userRegisteredFiles.remove(filePath); + } else { qWarning() << "Error unregistering namespace '" << nameSpace - << "' from file '" << filePath - << "': " << d->m_helpEngine->error(); + << "' from file '" << filePath + << "': " << helpEngine.error(); } } - locker.unlock(); - if (docsChanged) - emit Core::HelpManager::Signals::instance()->documentationChanged(); + promise.addResult(docsChanged); +} + +void HelpManager::unregisterDocumentation(const QStringList &files) +{ + if (d->m_needsSetup) { + for (const QString &file : files) + d->m_filesToUnregister.insert(file); + return; + } + + if (files.isEmpty()) + return; + + d->m_userRegisteredFiles.subtract(Utils::toSet(files)); + QFuture<bool> future = Utils::asyncRun(&unregisterDocumentationNow, collectionFilePath(), files); + Utils::onResultReady(future, this, [](bool docsChanged){ + if (docsChanged) { + d->m_helpEngine->setupData(); + emit Core::HelpManager::Signals::instance()->documentationChanged(); + } + }); + ProgressManager::addTask(future, + Tr::tr("Purge Outdated Documentation"), + kPurgeDocumentationTask); } void HelpManager::registerUserDocumentation(const QStringList &filePaths) @@ -316,9 +336,9 @@ void HelpManager::setupHelpManager() d->cleanUpDocumentation(); - if (!d->m_nameSpacesToUnregister.isEmpty()) { - m_instance->unregisterNamespaces(Utils::toList(d->m_nameSpacesToUnregister)); - d->m_nameSpacesToUnregister.clear(); + if (!d->m_filesToUnregister.isEmpty()) { + m_instance->unregisterDocumentation(Utils::toList(d->m_filesToUnregister)); + d->m_filesToUnregister.clear(); } if (!d->m_filesToRegister.isEmpty()) { @@ -343,7 +363,7 @@ void HelpManagerPrivate::cleanUpDocumentation() if (!QFileInfo::exists(filePath) || (!m_filesToRegister.contains(filePath) && !m_userRegisteredFiles.contains(filePath))) { - m_nameSpacesToUnregister.insert(nameSpace); + m_filesToUnregister.insert(filePath); } } } @@ -357,8 +377,8 @@ HelpManagerPrivate::~HelpManagerPrivate() const QStringList HelpManagerPrivate::documentationFromInstaller() { - QSettings *installSettings = ICore::settings(); - const QStringList documentationPaths = installSettings->value(QLatin1String("Help/InstalledDocumentation")) + QtcSettings *installSettings = ICore::settings(); + const QStringList documentationPaths = installSettings->value("Help/InstalledDocumentation") .toStringList(); QStringList documentationFiles; for (const QString &path : documentationPaths) { @@ -377,8 +397,7 @@ const QStringList HelpManagerPrivate::documentationFromInstaller() void HelpManagerPrivate::readSettings() { - m_userRegisteredFiles = Utils::toSet(ICore::settings()->value(QLatin1String(kUserDocumentationKey)) - .toStringList()); + m_userRegisteredFiles = Utils::toSet(ICore::settings()->value(kUserDocumentationKey).toStringList()); } void HelpManagerPrivate::writeSettings() diff --git a/src/plugins/help/helpmanager.h b/src/plugins/help/helpmanager.h index 739d5a68767..6a5874e7df2 100644 --- a/src/plugins/help/helpmanager.h +++ b/src/plugins/help/helpmanager.h @@ -31,8 +31,6 @@ public: void registerDocumentation(const QStringList &fileNames) override; void unregisterDocumentation(const QStringList &fileNames) override; - static void unregisterNamespaces(const QStringList &nameSpaces); - static void registerUserDocumentation(const QStringList &filePaths); static QSet<QString> userDocumentationPaths(); @@ -59,7 +57,6 @@ public: Core::HelpManager::HelpViewerLocation location = Core::HelpManager::HelpModeAlways) override; static void setupHelpManager(); - static void registerDocumentationNow(QPromise<bool> &promise, const QStringList &fileNames); signals: void collectionFileChanged(); diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index 9ad90528676..d693c9973e8 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -20,8 +20,6 @@ #include "searchtaskhandler.h" #include "topicchooser.h" -#include <app/app_version.h> - #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -40,6 +38,8 @@ #include <extensionsystem/pluginmanager.h> +#include <projectexplorer/kitmanager.h> + #include <texteditor/texteditorconstants.h> #include <utils/algorithm.h> @@ -47,6 +47,7 @@ #include <utils/qtcassert.h> #include <utils/styledbar.h> #include <utils/stringutils.h> +#include <utils/qtcsettings.h> #include <utils/theme/theme.h> #include <utils/tooltip/tooltip.h> @@ -251,7 +252,7 @@ HelpPluginPrivate::HelpPluginPrivate() Core::HelpManager::HelpModeAlways); }); - const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode"; //entry from designer settings + const Key qdsStandaloneEntry = "QML/Designer/StandAloneMode"; //entry from designer settings const bool isDesigner = Core::ICore::settings()->value(qdsStandaloneEntry, false).toBool(); action = new QAction(Tr::tr("Report Bug..."), this); @@ -285,7 +286,12 @@ void HelpPlugin::extensionsInitialized() bool HelpPlugin::delayedInitialize() { - HelpManager::setupHelpManager(); + if (ProjectExplorer::KitManager::isLoaded()) { + HelpManager::setupHelpManager(); + } else { + connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsLoaded, + this, &HelpManager::setupHelpManager); + } return true; } @@ -349,7 +355,7 @@ HelpViewer *HelpPluginPrivate::externalHelpViewer() m_externalWindow = createHelpWidget(Context(Constants::C_HELP_EXTERNAL), HelpWidget::ExternalWindow); if (m_externalWindowState.isNull()) { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); m_externalWindowState = settings->value(kExternalWindowStateKey).toRect(); } if (m_externalWindowState.isNull()) @@ -376,6 +382,13 @@ HelpViewer *HelpPlugin::createHelpViewer() connect(LocalHelpManager::instance(), &LocalHelpManager::fontZoomChanged, viewer, &HelpViewer::setFontZoom); + // initialize antialias + viewer->setAntialias(LocalHelpManager::antialias()); + connect(LocalHelpManager::instance(), + &LocalHelpManager::antialiasChanged, + viewer, + &HelpViewer::setAntialias); + viewer->setScrollWheelZoomingEnabled(LocalHelpManager::isScrollWheelZoomingEnabled()); connect(LocalHelpManager::instance(), &LocalHelpManager::scrollWheelZoomingEnabledChanged, viewer, &HelpViewer::setScrollWheelZoomingEnabled); @@ -568,7 +581,7 @@ HelpViewer *HelpPluginPrivate::showHelpUrl(const QUrl &url, Core::HelpManager::H // QtHelp doesn't know about versions, add the version number and use that QUrl versioned = url; versioned.setHost(qtcreatorUnversionedID + "." - + QString::fromLatin1(Core::Constants::IDE_VERSION_LONG).remove('.')); + + QCoreApplication::applicationVersion().remove('.')); return showHelpUrl(versioned, location); } diff --git a/src/plugins/help/helpviewer.cpp b/src/plugins/help/helpviewer.cpp index e10c65a019b..323d9d3f854 100644 --- a/src/plugins/help/helpviewer.cpp +++ b/src/plugins/help/helpviewer.cpp @@ -71,6 +71,8 @@ HelpViewer::~HelpViewer() restoreOverrideCursor(); } +void HelpViewer::setAntialias(bool) {} + void HelpViewer::setFontZoom(int percentage) { setScale(percentage / 100.0); diff --git a/src/plugins/help/helpviewer.h b/src/plugins/help/helpviewer.h index 5368c09a84d..503e06db35a 100644 --- a/src/plugins/help/helpviewer.h +++ b/src/plugins/help/helpviewer.h @@ -5,6 +5,8 @@ #include <coreplugin/find/textfindconstants.h> +#include <utils/filesearch.h> + #include <QFont> #include <QMenu> #include <QPrinter> @@ -30,6 +32,7 @@ public: ~HelpViewer() override; virtual void setViewerFont(const QFont &font) = 0; + virtual void setAntialias(bool on); virtual void setScale(qreal scale) = 0; @@ -52,7 +55,7 @@ public: void setActionVisible(Action action, bool visible); bool isActionVisible(Action action); - virtual bool findText(const QString &text, Core::FindFlags flags, + virtual bool findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped = nullptr) = 0; bool handleForwardBackwardMouseButtons(QMouseEvent *e); diff --git a/src/plugins/help/helpwidget.cpp b/src/plugins/help/helpwidget.cpp index 67b42232c90..c79bb052eb5 100644 --- a/src/plugins/help/helpwidget.cpp +++ b/src/plugins/help/helpwidget.cpp @@ -229,11 +229,14 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget m_toggleSideBarAction->setChecked(false); cmd = Core::ActionManager::registerAction(m_toggleSideBarAction, Core::Constants::TOGGLE_LEFT_SIDEBAR, context); - connect(m_toggleSideBarAction, &QAction::toggled, m_toggleSideBarAction, [this](bool checked) { - m_toggleSideBarAction->setText(::Core::Tr::tr( - checked ? Core::Constants::TR_HIDE_LEFT_SIDEBAR - : Core::Constants::TR_SHOW_LEFT_SIDEBAR)); - }); + connect(m_toggleSideBarAction, + &QAction::toggled, + m_toggleSideBarAction, + [this](bool checked) { + m_toggleSideBarAction->setToolTip( + ::Core::Tr::tr(checked ? Core::Constants::TR_HIDE_LEFT_SIDEBAR + : Core::Constants::TR_SHOW_LEFT_SIDEBAR)); + }); addSideBar(); m_toggleSideBarAction->setChecked(m_sideBar->isVisibleTo(this)); connect(m_toggleSideBarAction, &QAction::triggered, m_sideBar, &Core::SideBar::setVisible); @@ -323,14 +326,11 @@ HelpWidget::HelpWidget(const Core::Context &context, WidgetStyle style, QWidget helpTargetButton->setProperty(Utils::StyleHelper::C_NO_ARROW, true); helpTargetButton->setPopupMode(QToolButton::DelayedPopup); helpTargetButton->setMenu(createHelpTargetMenu(helpTargetButton)); - connect(LocalHelpManager::instance(), - &LocalHelpManager::contextHelpOptionChanged, + connect(LocalHelpManager::instance(), &LocalHelpManager::contextHelpOptionChanged, this, [this, helpTargetAction] { helpTargetAction->setChecked(isTargetOfContextHelp(m_style)); }); - connect(helpTargetAction, - &QAction::triggered, - this, + connect(helpTargetAction, &QAction::triggered, this, [this, helpTargetAction, helpTargetButton](bool checked) { if (checked) { LocalHelpManager::setContextHelpOption(optionForStyle(m_style)); @@ -695,6 +695,8 @@ HelpViewer *HelpWidget::insertViewer(int index, const QUrl &url) if (currentViewer() == viewer) { m_addBookmarkAction->setEnabled(isBookmarkable(url)); m_openOnlineDocumentationAction->setEnabled(LocalHelpManager::canOpenOnlineHelp(url)); + if (m_switchToHelp) + m_switchToHelp->setEnabled(url != QUrl("about:blank")); } }); connect(viewer, &HelpViewer::forwardAvailable, this, [viewer, this](bool available) { diff --git a/src/plugins/help/litehtmlhelpviewer.cpp b/src/plugins/help/litehtmlhelpviewer.cpp index e965ae60ca6..af4b84c5fcb 100644 --- a/src/plugins/help/litehtmlhelpviewer.cpp +++ b/src/plugins/help/litehtmlhelpviewer.cpp @@ -134,6 +134,11 @@ void LiteHtmlHelpViewer::setScale(qreal scale) m_viewer->setZoomFactor(scale == 0 ? qreal(1) : scale); } +void LiteHtmlHelpViewer::setAntialias(bool on) +{ + m_viewer->setAntialias(on); +} + QString LiteHtmlHelpViewer::title() const { return m_viewer->title(); @@ -209,11 +214,11 @@ void LiteHtmlHelpViewer::addForwardHistoryItems(QMenu *forwardMenu) } bool LiteHtmlHelpViewer::findText( - const QString &text, Core::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) + const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) { Q_UNUSED(fromSearch) return m_viewer->findText(text, - Core::textDocumentFlagsForFindFlags(flags), + Utils::textDocumentFlagsForFindFlags(flags), incremental, wrapped); } diff --git a/src/plugins/help/litehtmlhelpviewer.h b/src/plugins/help/litehtmlhelpviewer.h index be2182a8880..0978bca9b20 100644 --- a/src/plugins/help/litehtmlhelpviewer.h +++ b/src/plugins/help/litehtmlhelpviewer.h @@ -24,8 +24,8 @@ public: ~LiteHtmlHelpViewer() override; void setViewerFont(const QFont &font) override; - void setScale(qreal scale) override; + void setAntialias(bool on) final; QString title() const override; @@ -40,7 +40,7 @@ public: void addBackHistoryItems(QMenu *backMenu) override; void addForwardHistoryItems(QMenu *forwardMenu) override; - bool findText(const QString &text, Core::FindFlags flags, + bool findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped = nullptr) override; void copy() override; diff --git a/src/plugins/help/localhelpmanager.cpp b/src/plugins/help/localhelpmanager.cpp index f397a5db121..bfa827b3319 100644 --- a/src/plugins/help/localhelpmanager.cpp +++ b/src/plugins/help/localhelpmanager.cpp @@ -21,10 +21,10 @@ #include "macwebkithelpviewer.h" #endif -#include <app/app_version.h> #include <coreplugin/icore.h> #include <utils/algorithm.h> +#include <utils/appinfo.h> #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> @@ -35,6 +35,7 @@ #include <QHelpEngine> #include <QHelpLink> #include <QMutexLocker> +#include <QVersionNumber> #include <optional> @@ -51,21 +52,23 @@ QHelpEngine* LocalHelpManager::m_guiEngine = nullptr; QMutex LocalHelpManager::m_bkmarkMutex; BookmarkManager* LocalHelpManager::m_bookmarkManager = nullptr; -static const char kHelpHomePageKey[] = "Help/HomePage"; -static const char kFontFamilyKey[] = "Help/FallbackFontFamily"; -static const char kFontStyleNameKey[] = "Help/FallbackFontStyleName"; -static const char kFontSizeKey[] = "Help/FallbackFontSize"; -static const char kFontZoomKey[] = "Help/FontZoom"; -static const char kStartOptionKey[] = "Help/StartOption"; -static const char kContextHelpOptionKey[] = "Help/ContextHelpOption"; -static const char kReturnOnCloseKey[] = "Help/ReturnOnClose"; -static const char kUseScrollWheelZooming[] = "Help/UseScrollWheelZooming"; -static const char kLastShownPagesKey[] = "Help/LastShownPages"; -static const char kLastSelectedTabKey[] = "Help/LastSelectedTab"; -static const char kViewerBackend[] = "Help/ViewerBackend"; +const char kHelpHomePageKey[] = "Help/HomePage"; +const char kFontFamilyKey[] = "Help/FallbackFontFamily"; +const char kFontStyleNameKey[] = "Help/FallbackFontStyleName"; +const char kFontSizeKey[] = "Help/FallbackFontSize"; +const char kFontZoomKey[] = "Help/FontZoom"; +const char kAntialiasKey[] = "Help/FontAntialias"; +const char kStartOptionKey[] = "Help/StartOption"; +const char kContextHelpOptionKey[] = "Help/ContextHelpOption"; +const char kReturnOnCloseKey[] = "Help/ReturnOnClose"; +const char kUseScrollWheelZooming[] = "Help/UseScrollWheelZooming"; +const char kLastShownPagesKey[] = "Help/LastShownPages"; +const char kLastSelectedTabKey[] = "Help/LastSelectedTab"; +const char kViewerBackend[] = "Help/ViewerBackend"; -static const int kDefaultFallbackFontSize = 14; -static const int kDefaultFontZoom = 100; +const int kDefaultFallbackFontSize = 14; +const int kDefaultFontZoom = 100; +const bool kDefaultAntialias = true; const int kDefaultStartOption = LocalHelpManager::ShowLastPages; const int kDefaultContextHelpOption = Core::HelpManager::SideBySideIfPossible; const bool kDefaultReturnOnClose = false; @@ -113,9 +116,12 @@ LocalHelpManager *LocalHelpManager::instance() QString LocalHelpManager::defaultHomePage() { + const auto version = QVersionNumber::fromString(QCoreApplication::applicationVersion()); static const QString url = QString::fromLatin1("qthelp://org.qt-project.qtcreator." - "%1%2%3/doc/index.html").arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR) - .arg(IDE_VERSION_RELEASE); + "%1%2%3/doc/index.html") + .arg(version.majorVersion()) + .arg(version.minorVersion()) + .arg(version.microVersion()); return url; } @@ -131,7 +137,7 @@ void LocalHelpManager::setHomePage(const QString &page) QFont LocalHelpManager::fallbackFont() { - QSettings *settings = Core::ICore::settings(); + Utils::QtcSettings *settings = Core::ICore::settings(); const QString family = settings->value(kFontFamilyKey, defaultFallbackFontFamily()).toString(); const int size = settings->value(kFontSizeKey, kDefaultFallbackFontSize).toInt(); QFont font(family, size); @@ -171,6 +177,19 @@ int LocalHelpManager::setFontZoom(int percentage) return newZoom; } +bool LocalHelpManager::antialias() +{ + return Core::ICore::settings()->value(kAntialiasKey, kDefaultAntialias).toBool(); +} + +void LocalHelpManager::setAntialias(bool on) +{ + if (on != antialias()) { + Core::ICore::settings()->setValueWithDefault(kAntialiasKey, on, kDefaultAntialias); + emit m_instance->antialiasChanged(on); + } +} + LocalHelpManager::StartOption LocalHelpManager::startOption() { const QVariant value = Core::ICore::settings()->value(kStartOptionKey, kDefaultStartOption); @@ -496,12 +515,13 @@ bool LocalHelpManager::canOpenOnlineHelp(const QUrl &url) bool LocalHelpManager::openOnlineHelp(const QUrl &url) { - static const QString unversionedLocalDomainName = QString("org.qt-project.%1").arg(Core::Constants::IDE_ID); + static const QString unversionedLocalDomainName + = QString("org.qt-project.%1").arg(Utils::appInfo().id); if (canOpenOnlineHelp(url)) { QString urlPrefix = "http://doc.qt.io/"; if (url.authority().startsWith(unversionedLocalDomainName)) { - urlPrefix.append(Core::Constants::IDE_ID); + urlPrefix.append(Utils::appInfo().id); } else { const auto host = url.host(); const auto dot = host.lastIndexOf('.'); diff --git a/src/plugins/help/localhelpmanager.h b/src/plugins/help/localhelpmanager.h index 433d1ce7663..5195e7f1b70 100644 --- a/src/plugins/help/localhelpmanager.h +++ b/src/plugins/help/localhelpmanager.h @@ -61,6 +61,9 @@ public: static int fontZoom(); static int setFontZoom(int percentage); + static bool antialias(); + static void setAntialias(bool on); + static StartOption startOption(); static void setStartOption(StartOption option); @@ -104,6 +107,7 @@ public: signals: void fallbackFontChanged(const QFont &font); void fontZoomChanged(int percentage); + void antialiasChanged(bool on); void returnOnCloseChanged(); void scrollWheelZoomingEnabledChanged(bool enabled); void contextHelpOptionChanged(Core::HelpManager::HelpViewerLocation option); diff --git a/src/plugins/help/macwebkithelpviewer.h b/src/plugins/help/macwebkithelpviewer.h index 31bd3bd716e..2eb1ff2c048 100644 --- a/src/plugins/help/macwebkithelpviewer.h +++ b/src/plugins/help/macwebkithelpviewer.h @@ -65,7 +65,7 @@ public: void addForwardHistoryItems(QMenu *forwardMenu) override; void setActionVisible(bool visible); - bool findText(const QString &text, Core::FindFlags flags, + bool findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped = nullptr) override; MacWebKitHelpWidget *widget() const { return m_widget; } diff --git a/src/plugins/help/macwebkithelpviewer.mm b/src/plugins/help/macwebkithelpviewer.mm index d5781103158..68cfa56debf 100644 --- a/src/plugins/help/macwebkithelpviewer.mm +++ b/src/plugins/help/macwebkithelpviewer.mm @@ -700,7 +700,7 @@ DOMRange *MacWebKitHelpViewer::findText(NSString *text, bool forward, bool caseS return nil; } -bool MacWebKitHelpViewer::findText(const QString &text, Core::FindFlags flags, bool incremental, +bool MacWebKitHelpViewer::findText(const QString &text, FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) { Q_UNUSED(incremental); @@ -708,8 +708,8 @@ bool MacWebKitHelpViewer::findText(const QString &text, Core::FindFlags flags, b @autoreleasepool { if (wrapped) *wrapped = false; - bool forward = !(flags & Core::FindBackward); - bool caseSensitive = (flags & Core::FindCaseSensitively); + bool forward = !(flags & FindBackward); + bool caseSensitive = (flags & FindCaseSensitively); WebView *webView = m_widget->webView(); // WebView searchFor:.... grabs first responder, and when losing first responder afterwards, diff --git a/src/plugins/help/searchtaskhandler.h b/src/plugins/help/searchtaskhandler.h index 09e03f1e06a..e813afb9028 100644 --- a/src/plugins/help/searchtaskhandler.h +++ b/src/plugins/help/searchtaskhandler.h @@ -5,7 +5,9 @@ #include <projectexplorer/itaskhandler.h> -QT_FORWARD_DECLARE_CLASS(QUrl) +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE namespace Help { namespace Internal { diff --git a/src/plugins/help/textbrowserhelpviewer.cpp b/src/plugins/help/textbrowserhelpviewer.cpp index 3f32fe29a47..419dfb93595 100644 --- a/src/plugins/help/textbrowserhelpviewer.cpp +++ b/src/plugins/help/textbrowserhelpviewer.cpp @@ -55,22 +55,28 @@ TextBrowserHelpViewer::~TextBrowserHelpViewer() = default; void TextBrowserHelpViewer::setViewerFont(const QFont &newFont) { - setFontAndScale(newFont, LocalHelpManager::fontZoom() / 100.0); + setFontAndScale(newFont, LocalHelpManager::fontZoom() / 100.0, LocalHelpManager::antialias()); } -void TextBrowserHelpViewer::setFontAndScale(const QFont &font, qreal scale) +void TextBrowserHelpViewer::setFontAndScale(const QFont &font, qreal scale, bool antialias) { - m_textBrowser->withFixedTopPosition([this, &font, scale] { + m_textBrowser->withFixedTopPosition([this, &font, scale, antialias] { QFont newFont = font; const float newSize = font.pointSizeF() * scale; newFont.setPointSizeF(newSize); + newFont.setStyleStrategy(antialias ? QFont::PreferAntialias : QFont::NoAntialias); m_textBrowser->setFont(newFont); }); } void TextBrowserHelpViewer::setScale(qreal scale) { - setFontAndScale(LocalHelpManager::fallbackFont(), scale); + setFontAndScale(LocalHelpManager::fallbackFont(), scale, LocalHelpManager::antialias()); +} + +void TextBrowserHelpViewer::setAntialias(bool on) +{ + setFontAndScale(LocalHelpManager::fallbackFont(), LocalHelpManager::fontZoom() / 100.0, on); } QString TextBrowserHelpViewer::title() const @@ -143,7 +149,7 @@ void TextBrowserHelpViewer::addForwardHistoryItems(QMenu *forwardMenu) } } -bool TextBrowserHelpViewer::findText(const QString &text, Core::FindFlags flags, +bool TextBrowserHelpViewer::findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) { if (wrapped) @@ -157,10 +163,10 @@ bool TextBrowserHelpViewer::findText(const QString &text, Core::FindFlags flags, if (incremental) cursor.setPosition(position); - QTextDocument::FindFlags f = Core::textDocumentFlagsForFindFlags(flags); + QTextDocument::FindFlags f = Utils::textDocumentFlagsForFindFlags(flags); QTextCursor found = doc->find(text, cursor, f); if (found.isNull()) { - if ((flags & Core::FindBackward) == 0) + if ((flags & Utils::FindBackward) == 0) cursor.movePosition(QTextCursor::Start); else cursor.movePosition(QTextCursor::End); diff --git a/src/plugins/help/textbrowserhelpviewer.h b/src/plugins/help/textbrowserhelpviewer.h index 56fc20ff6c1..2177cf55c0c 100644 --- a/src/plugins/help/textbrowserhelpviewer.h +++ b/src/plugins/help/textbrowserhelpviewer.h @@ -22,8 +22,8 @@ public: ~TextBrowserHelpViewer() override; void setViewerFont(const QFont &font) override; - void setScale(qreal scale) override; + void setAntialias(bool on) final; QString title() const override; @@ -38,7 +38,7 @@ public: void addBackHistoryItems(QMenu *backMenu) override; void addForwardHistoryItems(QMenu *forwardMenu) override; - bool findText(const QString &text, Core::FindFlags flags, + bool findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped = nullptr) override; void copy() override; @@ -48,7 +48,7 @@ public: void print(QPrinter *printer) override; private: - void setFontAndScale(const QFont &font, qreal scale); + void setFontAndScale(const QFont &font, qreal scale, bool antialias); TextBrowserHelpWidget *m_textBrowser; }; diff --git a/src/plugins/help/webenginehelpviewer.cpp b/src/plugins/help/webenginehelpviewer.cpp index 15a9eb9fafb..ec8929bff77 100644 --- a/src/plugins/help/webenginehelpviewer.cpp +++ b/src/plugins/help/webenginehelpviewer.cpp @@ -219,7 +219,7 @@ void WebEngineHelpViewer::addForwardHistoryItems(QMenu *forwardMenu) } } -bool WebEngineHelpViewer::findText(const QString &text, Core::FindFlags flags, bool incremental, +bool WebEngineHelpViewer::findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) { Q_UNUSED(incremental) @@ -227,9 +227,9 @@ bool WebEngineHelpViewer::findText(const QString &text, Core::FindFlags flags, b if (wrapped) *wrapped = false; // missing feature in QWebEngine QWebEnginePage::FindFlags webEngineFlags; - if (flags & Core::FindBackward) + if (flags & Utils::FindBackward) webEngineFlags |= QWebEnginePage::FindBackward; - if (flags & Core::FindCaseSensitively) + if (flags & Utils::FindCaseSensitively) webEngineFlags |= QWebEnginePage::FindCaseSensitively; // QWebEngineView's findText is asynchronous, and the variant taking a callback runs the // callback on the main thread, so blocking here becomes ugly too diff --git a/src/plugins/help/webenginehelpviewer.h b/src/plugins/help/webenginehelpviewer.h index 0ecfc58a4d9..6f810f8769b 100644 --- a/src/plugins/help/webenginehelpviewer.h +++ b/src/plugins/help/webenginehelpviewer.h @@ -71,7 +71,7 @@ public: bool isBackwardAvailable() const override; void addBackHistoryItems(QMenu *backMenu) override; void addForwardHistoryItems(QMenu *forwardMenu) override; - bool findText(const QString &text, Core::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) override; + bool findText(const QString &text, Utils::FindFlags flags, bool incremental, bool fromSearch, bool *wrapped) override; WebEngineHelpPage *page() const; diff --git a/src/plugins/imageviewer/ImageViewer.json.in b/src/plugins/imageviewer/ImageViewer.json.in index 239ed0b0fdf..90ffbcc0c62 100644 --- a/src/plugins/imageviewer/ImageViewer.json.in +++ b/src/plugins/imageviewer/ImageViewer.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"ImageViewer\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ImageViewer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Image Viewer component.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Description" : "Image Viewer component.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", - \" <mime-type type=\'image/webp\'>\", - \" <comment>WebP Image file</comment>\", - \" <glob pattern=\'*.webp\'/>\", - \" </mime-type>\", + " <mime-type type='image/webp'>", + " <comment>WebP Image file</comment>", + " <glob pattern='*.webp'/>", + " </mime-type>", - \"</mime-info>\" + "</mime-info>" ] } diff --git a/src/plugins/imageviewer/exportdialog.h b/src/plugins/imageviewer/exportdialog.h index 5868cc7ef7c..f9e7cf939bc 100644 --- a/src/plugins/imageviewer/exportdialog.h +++ b/src/plugins/imageviewer/exportdialog.h @@ -5,7 +5,9 @@ #include <QDialog> -QT_FORWARD_DECLARE_CLASS(QSpinBox) +QT_BEGIN_NAMESPACE +class QSpinBox; +QT_END_NAMESPACE namespace Utils { class FilePath; diff --git a/src/plugins/imageviewer/imageview.h b/src/plugins/imageviewer/imageview.h index 6bee8e3243e..e5cf20ea808 100644 --- a/src/plugins/imageviewer/imageview.h +++ b/src/plugins/imageviewer/imageview.h @@ -8,7 +8,9 @@ #include <QGraphicsView> -QT_FORWARD_DECLARE_CLASS(QImage) +QT_BEGIN_NAMESPACE +class QImage; +QT_END_NAMESPACE namespace Utils { class QtcSettings; diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp index 5d4a068e717..d1893b703ae 100644 --- a/src/plugins/imageviewer/imageviewer.cpp +++ b/src/plugins/imageviewer/imageviewer.cpp @@ -234,7 +234,7 @@ void ImageViewer::ctor() d->imageView, &ImageView::reset); connect(d->file.data(), &ImageViewerFile::reloadFinished, d->imageView, &ImageView::createScene); - connect(d->file.data(), &ImageViewerFile::isPausedChanged, + connect(d->file.data(), &ImageViewerFile::movieStateChanged, this, &ImageViewer::updatePauseAction); connect(d->imageView, &ImageView::scaleFactorChanged, this, &ImageViewer::scaleFactorUpdate); @@ -349,19 +349,42 @@ void ImageViewer::togglePlay() void ImageViewer::playToggled() { - d->file->setPaused(!d->file->isPaused()); + QMovie *m = d->file->movie(); + if (!m) + return; + const QMovie::MovieState state = d->file->movie()->state(); + switch (state) { + case QMovie::NotRunning: + m->start(); + break; + case QMovie::Paused: + m->setPaused(false); + break; + case QMovie::Running: + m->setPaused(true); + break; + } } void ImageViewer::updatePauseAction() { - bool isMovie = d->file->type() == ImageViewerFile::TypeMovie; - if (isMovie && !d->file->isPaused()) { - d->actionPlayPause->setToolTipBase(Tr::tr("Pause Animation")); - d->actionPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon()); - } else { - d->actionPlayPause->setToolTipBase(Tr::tr("Play Animation")); - d->actionPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon()); - d->actionPlayPause->setEnabled(isMovie); + const bool isMovie = d->file->type() == ImageViewerFile::TypeMovie; + const QMovie::MovieState state = isMovie ? d->file->movie()->state() : QMovie::NotRunning; + CommandAction *a = d->actionPlayPause; + switch (state) { + case QMovie::NotRunning: + a->setToolTipBase(Tr::tr("Play Animation")); + a->setIcon(Icons::RUN_SMALL_TOOLBAR.icon()); + a->setEnabled(isMovie); + break; + case QMovie::Paused: + a->setToolTipBase(Tr::tr("Resume Paused Animation")); + a->setIcon(Icons::CONTINUE_SMALL_TOOLBAR.icon()); + break; + case QMovie::Running: + a->setToolTipBase(Tr::tr("Pause Animation")); + a->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon()); + break; } } diff --git a/src/plugins/imageviewer/imageviewerfile.cpp b/src/plugins/imageviewer/imageviewerfile.cpp index afa5c5cfc3d..15a3d8d77b3 100644 --- a/src/plugins/imageviewer/imageviewerfile.cpp +++ b/src/plugins/imageviewer/imageviewerfile.cpp @@ -6,6 +6,7 @@ #include "imageviewerconstants.h" #include "imageviewertr.h" +#include "utils/algorithm.h" #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/ieditor.h> @@ -115,20 +116,8 @@ Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString, return OpenResult::CannotHandle; } m_type = TypeMovie; - m_movie->setCacheMode(QMovie::CacheAll); - connect( - m_movie, - &QMovie::finished, - m_movie, - [this] { - if (m_movie->isValid()) - m_movie->start(); - }, - Qt::QueuedConnection); connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged); - m_movie->start(); - m_isPaused = false; // force update - setPaused(true); + connect(m_movie, &QMovie::stateChanged, this, &ImageViewerFile::movieStateChanged); } else { m_pixmap = new QPixmap(fileName); if (m_pixmap->isNull()) { @@ -169,18 +158,9 @@ bool ImageViewerFile::reload(QString *errorString, return success; } -bool ImageViewerFile::isPaused() const +QMovie *ImageViewerFile::movie() const { - return m_isPaused; -} - -void ImageViewerFile::setPaused(bool paused) -{ - if (!m_movie || m_isPaused == paused) - return; - m_isPaused = paused; - m_movie->setPaused(paused); - emit isPausedChanged(m_isPaused); + return m_movie; } QGraphicsItem *ImageViewerFile::createGraphicsItem() const @@ -221,16 +201,13 @@ ImageViewerFile::ImageType ImageViewerFile::type() const void ImageViewerFile::updateVisibility() { - if (!m_movie || m_isPaused) + if (!m_movie || m_movie->state() != QMovie::Running) return; - bool visible = false; - for (Core::IEditor *editor : Core::DocumentModel::editorsForDocument(this)) { - if (editor->widget()->isVisible()) { - visible = true; - break; - } - } - m_movie->setPaused(!visible); + const bool anyVisible = Utils::anyOf(Core::DocumentModel::editorsForDocument(this), + [] (Core::IEditor *editor) + { return editor->widget()->isVisible(); }); + if (!anyVisible) + m_movie->setPaused(true); } void ImageViewerFile::cleanUp() diff --git a/src/plugins/imageviewer/imageviewerfile.h b/src/plugins/imageviewer/imageviewerfile.h index fc1c73f8de8..26ed01a94fe 100644 --- a/src/plugins/imageviewer/imageviewerfile.h +++ b/src/plugins/imageviewer/imageviewerfile.h @@ -6,9 +6,10 @@ #include <coreplugin/idocument.h> +#include <QMovie> + QT_BEGIN_NAMESPACE class QGraphicsItem; -class QMovie; class QPixmap; #ifndef QT_NO_SVG @@ -40,8 +41,7 @@ public: ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override; bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override; - bool isPaused() const; - void setPaused(bool paused); + QMovie *movie() const; QGraphicsItem *createGraphicsItem() const; ImageType type() const; @@ -51,7 +51,7 @@ public: signals: void openFinished(bool success); void imageSizeChanged(const QSize &size); - void isPausedChanged(bool paused); + void movieStateChanged(); private: void cleanUp(); @@ -63,7 +63,6 @@ private: #endif QMovie *m_movie = nullptr; QPixmap *m_pixmap = nullptr; - bool m_isPaused = false; }; } // ImageViewer::Internal diff --git a/src/plugins/imageviewer/multiexportdialog.cpp b/src/plugins/imageviewer/multiexportdialog.cpp index 1b526c9b11f..c7e18b8fe1b 100644 --- a/src/plugins/imageviewer/multiexportdialog.cpp +++ b/src/plugins/imageviewer/multiexportdialog.cpp @@ -21,7 +21,6 @@ #include <QMenu> #include <QMessageBox> #include <QScreen> -#include <QSettings> #include <QToolButton> #include <QWidgetAction> @@ -104,27 +103,27 @@ static FilePath fileNameForSize(QString pattern, const QSize &s) // Helpers for writing/reading the user-specified size specifications // from/to the settings. -static inline QString settingsGroup() { return QStringLiteral("ExportSvgSizes"); } +static Key settingsGroup() { return "ExportSvgSizes"; } static QVector<QSize> readSettings(const QSize &size) { QVector<QSize> result; - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(settingsGroup()); const QStringList keys = settings->allKeys(); const int idx = keys.indexOf(sizeToString(size)); if (idx >= 0) - result = stringToSizes(settings->value(keys.at(idx)).toString()); + result = stringToSizes(settings->value(keyFromString(keys.at(idx))).toString()); settings->endGroup(); return result; } static void writeSettings(const QSize &size, const QString &sizeSpec) { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(settingsGroup()); const QString spec = sizeToString(size); - settings->setValue(spec, QVariant(sizeSpec)); + settings->setValue(keyFromString(spec), QVariant(sizeSpec)); // Limit the number of sizes to 10. Remove the // first element unless it is the newly added spec. @@ -132,7 +131,7 @@ static void writeSettings(const QSize &size, const QString &sizeSpec) while (keys.size() > 10) { const int existingIndex = keys.indexOf(spec); const int removeIndex = existingIndex == 0 ? 1 : 0; - settings->remove(keys.takeAt(removeIndex)); + settings->remove(keyFromString(keys.takeAt(removeIndex))); } settings->endGroup(); } diff --git a/src/plugins/imageviewer/multiexportdialog.h b/src/plugins/imageviewer/multiexportdialog.h index bd8ed32b1a2..74eb33f4ca0 100644 --- a/src/plugins/imageviewer/multiexportdialog.h +++ b/src/plugins/imageviewer/multiexportdialog.h @@ -8,7 +8,9 @@ #include <QSize> #include <QVector> -QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_BEGIN_NAMESPACE +class QLineEdit; +QT_END_NAMESPACE namespace Utils { class FilePath; diff --git a/src/plugins/incredibuild/IncrediBuild.json.in b/src/plugins/incredibuild/IncrediBuild.json.in index ff8d1718728..d7fb5f242b6 100644 --- a/src/plugins/incredibuild/IncrediBuild.json.in +++ b/src/plugins/incredibuild/IncrediBuild.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"IncrediBuild\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Platform\" : \"^(Linux|Windows)\", - \"Vendor\" : \"IncrediBuild\", - \"Copyright\" : \"(C) IncrediBuild\", - \"Category\" : \"Build Systems\", - \"Url\" : \"http://www.IncrediBuild.com\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "IncrediBuild", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Platform" : "^(Linux|Windows)", + "Vendor" : "IncrediBuild", + "Copyright" : "(C) IncrediBuild", + "Category" : "Build Systems", + "Url" : "http://www.IncrediBuild.com", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Support for Incredibuild.\", - $$dependencyList + "Description" : "Support for Incredibuild.", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/incredibuild/buildconsolebuildstep.cpp b/src/plugins/incredibuild/buildconsolebuildstep.cpp index 19ac73dd0cf..61b40a8b2f9 100644 --- a/src/plugins/incredibuild/buildconsolebuildstep.cpp +++ b/src/plugins/incredibuild/buildconsolebuildstep.cpp @@ -155,7 +155,7 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id monFile.setLabelText(Tr::tr("Save IncrediBuild monitor file:")); monFile.setExpectedKind(PathChooser::Kind::Any); monFile.setBaseFileName(PathChooser::homePath()); - monFile.setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.MonFile.History")); + monFile.setHistoryCompleter("IncrediBuild.BuildConsole.MonFile.History"); monFile.setToolTip(Tr::tr("Writes a copy of the build progress file (.ib_mon) to the specified " "location. If only a folder name is given, a generated GUID will serve " "as the file name. The full path of the saved Build Monitor will be " @@ -169,7 +169,7 @@ BuildConsoleBuildStep::BuildConsoleBuildStep(BuildStepList *buildStepList, Id id logFile.setLabelText(Tr::tr("Output Log file:")); logFile.setExpectedKind(PathChooser::Kind::SaveFile); logFile.setBaseFileName(PathChooser::homePath()); - logFile.setHistoryCompleter(QLatin1String("IncrediBuild.BuildConsole.LogFile.History")); + logFile.setHistoryCompleter("IncrediBuild.BuildConsole.LogFile.History"); logFile.setToolTip(Tr::tr("Writes build output to a file.")); showCmd.setSettingsKey("IncrediBuild.BuildConsole.ShowCmd"); diff --git a/src/plugins/incredibuild/commandbuilder.cpp b/src/plugins/incredibuild/commandbuilder.cpp index 9febc343df0..5d99ed2e8ab 100644 --- a/src/plugins/incredibuild/commandbuilder.cpp +++ b/src/plugins/incredibuild/commandbuilder.cpp @@ -12,21 +12,26 @@ namespace IncrediBuild::Internal { const char CUSTOMCOMMANDBUILDER_COMMAND[] = "IncrediBuild.BuildConsole.%1.Command"; const char CUSTOMCOMMANDBUILDER_ARGS[] = "IncrediBuild.BuildConsole.%1.Arguments"; +static Key key(const QString &pattern, const QString &id) +{ + return keyFromString(pattern.arg(id)); +} + QString CommandBuilder::displayName() const { return Tr::tr("Custom Command"); } -void CommandBuilder::fromMap(const QVariantMap &map) +void CommandBuilder::fromMap(const Store &map) { - m_command = FilePath::fromSettings(map.value(QString(CUSTOMCOMMANDBUILDER_COMMAND).arg(id()))); - m_args = map.value(QString(CUSTOMCOMMANDBUILDER_ARGS).arg(id())).toString(); + m_command = FilePath::fromSettings(map.value(key(CUSTOMCOMMANDBUILDER_COMMAND, id()))); + m_args = map.value(key(CUSTOMCOMMANDBUILDER_ARGS, id())).toString(); } -void CommandBuilder::toMap(QVariantMap *map) const +void CommandBuilder::toMap(Store *map) const { - (*map)[QString(CUSTOMCOMMANDBUILDER_COMMAND).arg(id())] = m_command.toSettings(); - (*map)[QString(CUSTOMCOMMANDBUILDER_ARGS).arg(id())] = QVariant(m_args); + map->insert(key(CUSTOMCOMMANDBUILDER_COMMAND, id()), m_command.toSettings()); + map->insert(key(CUSTOMCOMMANDBUILDER_ARGS, id()), QVariant(m_args)); } void CommandBuilder::setCommand(const FilePath &command) diff --git a/src/plugins/incredibuild/commandbuilder.h b/src/plugins/incredibuild/commandbuilder.h index 7d800e91b6a..bebc0934f12 100644 --- a/src/plugins/incredibuild/commandbuilder.h +++ b/src/plugins/incredibuild/commandbuilder.h @@ -20,8 +20,8 @@ public: virtual QString id() const { return "CustomCommandBuilder"; } virtual QString displayName() const; - virtual void fromMap(const QVariantMap &map); - virtual void toMap(QVariantMap *map) const; + virtual void fromMap(const Utils::Store &map); + virtual void toMap(Utils::Store *map) const; virtual Utils::FilePath defaultCommand() const { return {}; } virtual QString defaultArguments() const { return QString(); } diff --git a/src/plugins/incredibuild/commandbuilderaspect.cpp b/src/plugins/incredibuild/commandbuilderaspect.cpp index 3eced7c144f..ce499f28038 100644 --- a/src/plugins/incredibuild/commandbuilderaspect.cpp +++ b/src/plugins/incredibuild/commandbuilderaspect.cpp @@ -159,7 +159,7 @@ void CommandBuilderAspect::addToLayout(Layouting::LayoutItem &parent) updateGui(); } -void CommandBuilderAspect::fromMap(const QVariantMap &map) +void CommandBuilderAspect::fromMap(const Store &map) { d->m_loadedFromMap = true; @@ -171,7 +171,7 @@ void CommandBuilderAspect::fromMap(const QVariantMap &map) updateGui(); } -void CommandBuilderAspect::toMap(QVariantMap &map) const +void CommandBuilderAspect::toMap(Store &map) const { map[IncrediBuild::Constants::INCREDIBUILD_BUILDSTEP_TYPE] = QVariant(IncrediBuild::Constants::BUILDCONSOLE_BUILDSTEP_ID); diff --git a/src/plugins/incredibuild/commandbuilderaspect.h b/src/plugins/incredibuild/commandbuilderaspect.h index 6033e9036e5..3282a18415f 100644 --- a/src/plugins/incredibuild/commandbuilderaspect.h +++ b/src/plugins/incredibuild/commandbuilderaspect.h @@ -24,8 +24,8 @@ public: private: void addToLayout(Layouting::LayoutItem &parent) final; - void fromMap(const QVariantMap &map) final; - void toMap(QVariantMap &map) const final; + void fromMap(const Utils::Store &map) final; + void toMap(Utils::Store &map) const final; void updateGui(); diff --git a/src/plugins/incredibuild/incredibuildplugin.cpp b/src/plugins/incredibuild/incredibuildplugin.cpp index 4e8f572812f..8bff118b66e 100644 --- a/src/plugins/incredibuild/incredibuildplugin.cpp +++ b/src/plugins/incredibuild/incredibuildplugin.cpp @@ -8,17 +8,25 @@ namespace IncrediBuild::Internal { +class IncrediBuildPluginPrivate +{ +public: + BuildConsoleStepFactory buildConsoleStepFactory; + IBConsoleStepFactory ibConsolStepFactory; +}; + class IncrediBuildPlugin final : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "IncrediBuild.json") public: - IncrediBuildPlugin() + void initialize() { - addManaged<BuildConsoleStepFactory>(); - addManaged<IBConsoleStepFactory>(); + d = std::make_unique<IncrediBuildPluginPrivate>(); } + + std::unique_ptr<IncrediBuildPluginPrivate> d; }; } // IncrediBuild::Internal diff --git a/src/plugins/incredibuild/makecommandbuilder.cpp b/src/plugins/incredibuild/makecommandbuilder.cpp index f5baa68a46a..0d5caa548ec 100644 --- a/src/plugins/incredibuild/makecommandbuilder.cpp +++ b/src/plugins/incredibuild/makecommandbuilder.cpp @@ -7,11 +7,11 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildstep.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/project.h> #include <qmakeprojectmanager/qmakeprojectmanagerconstants.h> // Compile-time only diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in index 0ac25523677..096cc0569f5 100644 --- a/src/plugins/insight/Insight.json.in +++ b/src/plugins/insight/Insight.json.in @@ -1,21 +1,21 @@ { - \"Name\" : \"Insight\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Revision\" : \"$$QTC_PLUGIN_REVISION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Insight", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Revision" : "${QTC_PLUGIN_REVISION}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Qt Insight Support for Design Studio.\", - \"DisabledByDefault\" : true, - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Quick", + "Description" : "Qt Insight Support for Design Studio.", + "DisabledByDefault" : true, + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/insight/insightmodel.cpp b/src/plugins/insight/insightmodel.cpp index 75ac061838c..282020349e0 100644 --- a/src/plugins/insight/insightmodel.cpp +++ b/src/plugins/insight/insightmodel.cpp @@ -16,7 +16,7 @@ #include <projectexplorer/projecttree.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/filepath.h> #include <utils/qtcassert.h> diff --git a/src/plugins/ios/CMakeLists.txt b/src/plugins/ios/CMakeLists.txt index ebfaa6fc69b..d26bcdf1034 100644 --- a/src/plugins/ios/CMakeLists.txt +++ b/src/plugins/ios/CMakeLists.txt @@ -17,7 +17,6 @@ add_qtc_plugin(Ios iosrunconfiguration.cpp iosrunconfiguration.h iosrunner.cpp iosrunner.h iossettingspage.cpp iossettingspage.h - iossettingswidget.cpp iossettingswidget.h iossimulator.cpp iossimulator.h iostoolhandler.cpp iostoolhandler.h iostr.h diff --git a/src/plugins/ios/Ios.json.in b/src/plugins/ios/Ios.json.in index 102f18caa89..2b2e4bae777 100644 --- a/src/plugins/ios/Ios.json.in +++ b/src/plugins/ios/Ios.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"Ios\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Platform\" : \"OS X.*\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Ios", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Platform" : "OS X.*", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Support for deployment to and execution on iOS Devices.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Support for deployment to and execution on iOS Devices.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/ios/ios.qbs b/src/plugins/ios/ios.qbs index ac4575c4e3d..e60edb7315f 100644 --- a/src/plugins/ios/ios.qbs +++ b/src/plugins/ios/ios.qbs @@ -43,8 +43,6 @@ QtcPlugin { "iosrunner.h", "iossettingspage.cpp", "iossettingspage.h", - "iossettingswidget.cpp", - "iossettingswidget.h", "iossimulator.cpp", "iossimulator.h", "iostoolhandler.cpp", diff --git a/src/plugins/ios/iosbuildconfiguration.cpp b/src/plugins/ios/iosbuildconfiguration.cpp index 84b58f53022..92c41179658 100644 --- a/src/plugins/ios/iosbuildconfiguration.cpp +++ b/src/plugins/ios/iosbuildconfiguration.cpp @@ -7,7 +7,7 @@ #include "iosconstants.h" #include "iostr.h" -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/namedwidget.h> #include <projectexplorer/target.h> @@ -367,25 +367,36 @@ void IosSigningSettingsWidget::updateWarningText() m_warningLabel->setText(warningText); } +// IosQmakeBuildConfiguration -// IosBuildConfiguration +class IosQmakeBuildConfiguration : public QmakeProjectManager::QmakeBuildConfiguration +{ +public: + IosQmakeBuildConfiguration(Target *target, Id id); -IosQmakeBuildConfiguration::IosQmakeBuildConfiguration(Target *target, Utils::Id id) +private: + QList<NamedWidget *> createSubConfigWidgets() override; + void fromMap(const Store &map) override; + + void updateQmakeCommand(); + + StringAspect m_signingIdentifier{this}; + BoolAspect m_autoManagedSigning{this}; +}; + +IosQmakeBuildConfiguration::IosQmakeBuildConfiguration(Target *target, Id id) : QmakeBuildConfiguration(target, id) { - m_signingIdentifier = addAspect<StringAspect>(); - m_signingIdentifier->setSettingsKey(signingIdentifierKey); + m_signingIdentifier.setSettingsKey(signingIdentifierKey); - m_autoManagedSigning = addAspect<BoolAspect>(); - m_autoManagedSigning->setDefaultValue(true); - m_autoManagedSigning->setValue(true); - m_autoManagedSigning->setSettingsKey(autoManagedSigningKey); + m_autoManagedSigning.setDefaultValue(true); + m_autoManagedSigning.setSettingsKey(autoManagedSigningKey); - connect(m_signingIdentifier, + connect(&m_signingIdentifier, &BaseAspect::changed, this, &IosQmakeBuildConfiguration::updateQmakeCommand); - connect(m_autoManagedSigning, + connect(&m_autoManagedSigning, &BaseAspect::changed, this, &IosQmakeBuildConfiguration::updateQmakeCommand); @@ -397,18 +408,17 @@ QList<NamedWidget *> IosQmakeBuildConfiguration::createSubConfigWidgets() // Ownership of this widget is with BuildSettingsWidget auto buildSettingsWidget = new IosSigningSettingsWidget(this, - m_autoManagedSigning, - m_signingIdentifier); + &m_autoManagedSigning, + &m_signingIdentifier); subConfigWidgets.prepend(buildSettingsWidget); return subConfigWidgets; } -bool IosQmakeBuildConfiguration::fromMap(const QVariantMap &map) +void IosQmakeBuildConfiguration::fromMap(const Store &map) { - if (!QmakeBuildConfiguration::fromMap(map)) - return false; - updateQmakeCommand(); - return true; + QmakeBuildConfiguration::fromMap(map); + if (!hasError()) + updateQmakeCommand(); } static QString teamIdForProvisioningProfile(const QString &id) @@ -440,13 +450,13 @@ void IosQmakeBuildConfiguration::updateQmakeCommand() }); // Set force ovveride qmake switch - const QString signingIdentifier = m_signingIdentifier->value(); + const QString signingIdentifier = m_signingIdentifier(); if (signingIdentifier.isEmpty() ) extraArgs << forceOverrideArg; Utils::Id devType = DeviceTypeKitAspect::deviceTypeId(kit()); if (devType == Constants::IOS_DEVICE_TYPE && !signingIdentifier.isEmpty()) { - if (m_autoManagedSigning->value()) { + if (m_autoManagedSigning()) { extraArgs << qmakeIosTeamSettings + signingIdentifier; } else { const QString teamId = teamIdForProvisioningProfile(signingIdentifier); @@ -469,22 +479,35 @@ IosQmakeBuildConfigurationFactory::IosQmakeBuildConfigurationFactory() addSupportedTargetDeviceType(Constants::IOS_SIMULATOR_TYPE); } +// IosCMakeBuildConfiguration + +class IosCMakeBuildConfiguration : public CMakeProjectManager::CMakeBuildConfiguration +{ +public: + IosCMakeBuildConfiguration(Target *target, Id id); + +private: + QList<NamedWidget *> createSubConfigWidgets() override; + + CMakeProjectManager::CMakeConfig signingFlags() const final; + + StringAspect m_signingIdentifier{this}; + BoolAspect m_autoManagedSigning{this}; +}; + IosCMakeBuildConfiguration::IosCMakeBuildConfiguration(Target *target, Id id) : CMakeBuildConfiguration(target, id) { - m_signingIdentifier = addAspect<StringAspect>(); - m_signingIdentifier->setSettingsKey(signingIdentifierKey); + m_signingIdentifier.setSettingsKey(signingIdentifierKey); - m_autoManagedSigning = addAspect<BoolAspect>(); - m_autoManagedSigning->setDefaultValue(true); - m_autoManagedSigning->setValue(true); - m_autoManagedSigning->setSettingsKey(autoManagedSigningKey); + m_autoManagedSigning.setDefaultValue(true); + m_autoManagedSigning.setSettingsKey(autoManagedSigningKey); - connect(m_signingIdentifier, + connect(&m_signingIdentifier, &BaseAspect::changed, this, &IosCMakeBuildConfiguration::signingFlagsChanged); - connect(m_autoManagedSigning, + connect(&m_autoManagedSigning, &BaseAspect::changed, this, &IosCMakeBuildConfiguration::signingFlagsChanged); @@ -496,25 +519,18 @@ QList<NamedWidget *> IosCMakeBuildConfiguration::createSubConfigWidgets() // Ownership of this widget is with BuildSettingsWidget auto buildSettingsWidget = new IosSigningSettingsWidget(this, - m_autoManagedSigning, - m_signingIdentifier); + &m_autoManagedSigning, + &m_signingIdentifier); subConfigWidgets.prepend(buildSettingsWidget); return subConfigWidgets; } -bool IosCMakeBuildConfiguration::fromMap(const QVariantMap &map) -{ - if (!CMakeBuildConfiguration::fromMap(map)) - return false; - return true; -} - CMakeConfig IosCMakeBuildConfiguration::signingFlags() const { if (DeviceTypeKitAspect::deviceTypeId(kit()) != Constants::IOS_DEVICE_TYPE) return {}; - const QString signingIdentifier = m_signingIdentifier->value(); - if (m_autoManagedSigning->value()) { + const QString signingIdentifier = m_signingIdentifier(); + if (m_autoManagedSigning()) { const DevelopmentTeams teams = IosConfigurations::developmentTeams(); const QString teamId = signingIdentifier.isEmpty() && !teams.isEmpty() ? teams.first()->identifier() diff --git a/src/plugins/ios/iosbuildconfiguration.h b/src/plugins/ios/iosbuildconfiguration.h index 5549dfef87a..31ed50bdff9 100644 --- a/src/plugins/ios/iosbuildconfiguration.h +++ b/src/plugins/ios/iosbuildconfiguration.h @@ -1,5 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + #pragma once #include <qmakeprojectmanager/qmakebuildconfiguration.h> @@ -7,42 +8,12 @@ namespace Ios::Internal { -class IosQmakeBuildConfiguration : public QmakeProjectManager::QmakeBuildConfiguration -{ -public: - IosQmakeBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); - -private: - QList<ProjectExplorer::NamedWidget *> createSubConfigWidgets() override; - bool fromMap(const QVariantMap &map) override; - - void updateQmakeCommand(); - - Utils::StringAspect *m_signingIdentifier = nullptr; - Utils::BoolAspect *m_autoManagedSigning = nullptr; -}; - class IosQmakeBuildConfigurationFactory : public QmakeProjectManager::QmakeBuildConfigurationFactory { public: IosQmakeBuildConfigurationFactory(); }; -class IosCMakeBuildConfiguration : public CMakeProjectManager::CMakeBuildConfiguration -{ -public: - IosCMakeBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); - -private: - QList<ProjectExplorer::NamedWidget *> createSubConfigWidgets() override; - bool fromMap(const QVariantMap &map) override; - - CMakeProjectManager::CMakeConfig signingFlags() const final; - - Utils::StringAspect *m_signingIdentifier = nullptr; - Utils::BoolAspect *m_autoManagedSigning = nullptr; -}; - class IosCMakeBuildConfigurationFactory : public CMakeProjectManager::CMakeBuildConfigurationFactory { public: diff --git a/src/plugins/ios/iosbuildstep.cpp b/src/plugins/ios/iosbuildstep.cpp index 7831d84e239..b6612336058 100644 --- a/src/plugins/ios/iosbuildstep.cpp +++ b/src/plugins/ios/iosbuildstep.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/buildsteplist.h> #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/gnumakeparser.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -56,8 +56,8 @@ private: bool init() final; void setupOutputFormatter(Utils::OutputFormatter *formatter) final; - bool fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Store &map) final; + void toMap(Store &map) const final; QStringList m_baseBuildArguments; QStringList m_extraArguments; @@ -166,26 +166,24 @@ void IosBuildStep::setupOutputFormatter(OutputFormatter *formatter) AbstractProcessStep::setupOutputFormatter(formatter); } -QVariantMap IosBuildStep::toMap() const +void IosBuildStep::toMap(Store &map) const { - QVariantMap map(AbstractProcessStep::toMap()); + AbstractProcessStep::toMap(map); map.insert(BUILD_ARGUMENTS_KEY, m_baseBuildArguments); map.insert(BUILD_USE_DEFAULT_ARGS_KEY, m_useDefaultArguments); // Not used anymore since 4.14. But make sure older versions of Creator can read this. map.insert(CLEAN_KEY, stepList()->id() == ProjectExplorer::Constants::BUILDSTEPS_CLEAN); - - return map; } -bool IosBuildStep::fromMap(const QVariantMap &map) +void IosBuildStep::fromMap(const Store &map) { QVariant bArgs = map.value(BUILD_ARGUMENTS_KEY); m_baseBuildArguments = bArgs.toStringList(); m_useDefaultArguments = map.value(BUILD_USE_DEFAULT_ARGS_KEY).toBool(); - return BuildStep::fromMap(map); + BuildStep::fromMap(map); } QStringList IosBuildStep::allArguments() const diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index 04e4363a2a1..105ba236eaa 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -12,8 +12,10 @@ #include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> -#include <projectexplorer/kitinformation.h> #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/toolchainmanager.h> @@ -23,14 +25,15 @@ #include <debugger/debuggeritemmanager.h> #include <debugger/debuggeritem.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <qtsupport/qtversionfactory.h> #include <utils/algorithm.h> +#include <utils/futuresynchronizer.h> #include <utils/process.h> #include <utils/qtcassert.h> @@ -58,7 +61,7 @@ static Q_LOGGING_CATEGORY(kitSetupLog, "qtc.ios.kitSetup", QtWarningMsg) static Q_LOGGING_CATEGORY(iosCommonLog, "qtc.ios.common", QtWarningMsg) } -using ToolChainPair = std::pair<ClangToolChain *, ClangToolChain *>; +using ToolChainPair = std::pair<GccToolChain *, GccToolChain *>; namespace Ios { namespace Internal { @@ -95,19 +98,19 @@ static bool isSimulatorDeviceId(const Id &id) return id == Constants::IOS_SIMULATOR_TYPE; } -static QList<ClangToolChain *> clangToolChains(const Toolchains &toolChains) +static QList<GccToolChain *> clangToolChains(const Toolchains &toolChains) { - QList<ClangToolChain *> clangToolChains; + QList<GccToolChain *> clangToolChains; for (ToolChain *toolChain : toolChains) if (toolChain->typeId() == ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID) - clangToolChains.append(static_cast<ClangToolChain *>(toolChain)); + clangToolChains.append(static_cast<GccToolChain *>(toolChain)); return clangToolChains; } -static QList<ClangToolChain *> autoDetectedIosToolChains() +static QList<GccToolChain *> autoDetectedIosToolChains() { - const QList<ClangToolChain *> toolChains = clangToolChains(ToolChainManager::toolchains()); - return filtered(toolChains, [](ClangToolChain *toolChain) { + const QList<GccToolChain *> toolChains = clangToolChains(ToolChainManager::toolchains()); + return filtered(toolChains, [](GccToolChain *toolChain) { return toolChain->isAutoDetected() && (toolChain->displayName().startsWith("iphone") || toolChain->displayName().startsWith("Apple Clang")); // TODO tool chains should be marked directly @@ -116,10 +119,10 @@ static QList<ClangToolChain *> autoDetectedIosToolChains() static ToolChainPair findToolChainForPlatform(const XcodePlatform &platform, const XcodePlatform::ToolchainTarget &target, - const QList<ClangToolChain *> &toolChains) + const QList<GccToolChain *> &toolChains) { ToolChainPair platformToolChains; - auto toolchainMatch = [](ClangToolChain *toolChain, const FilePath &compilerPath, const QStringList &flags) { + auto toolchainMatch = [](GccToolChain *toolChain, const FilePath &compilerPath, const QStringList &flags) { return compilerPath == toolChain->compilerCommand() && flags == toolChain->platformCodeGenFlags() && flags == toolChain->platformLinkerFlags(); @@ -136,7 +139,7 @@ static ToolChainPair findToolChainForPlatform(const XcodePlatform &platform, static QHash<XcodePlatform::ToolchainTarget, ToolChainPair> findToolChains(const QList<XcodePlatform> &platforms) { QHash<XcodePlatform::ToolchainTarget, ToolChainPair> platformToolChainHash; - const QList<ClangToolChain *> toolChains = autoDetectedIosToolChains(); + const QList<GccToolChain *> toolChains = autoDetectedIosToolChains(); for (const XcodePlatform &platform : platforms) { for (const XcodePlatform::ToolchainTarget &target : platform.targets) { ToolChainPair platformToolchains = findToolChainForPlatform(platform, target, @@ -365,7 +368,7 @@ QVersionNumber IosConfigurations::xcodeVersion() void IosConfigurations::save() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(SettingsGroup); settings->setValue(ignoreAllDevicesKey, m_ignoreAllDevices); settings->setValue(screenshotDirPathKey, m_screenshotDir.toString()); @@ -382,7 +385,7 @@ IosConfigurations::IosConfigurations(QObject *parent) void IosConfigurations::load() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(SettingsGroup); m_ignoreAllDevices = settings->value(ignoreAllDevicesKey, false).toBool(); m_screenshotDir = FilePath::fromString(settings->value(screenshotDirPathKey).toString()); @@ -405,7 +408,8 @@ void IosConfigurations::updateSimulators() dev = IDevice::ConstPtr(new IosSimulator(devId)); devManager->addDevice(dev); } - SimulatorControl::updateAvailableSimulators(this); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture( + SimulatorControl::updateAvailableSimulators(this)); } void IosConfigurations::setDeveloperPath(const FilePath &devPath) @@ -577,7 +581,7 @@ Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) co if (detector.device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) return {}; - QList<ClangToolChain *> existingClangToolChains = clangToolChains(detector.alreadyKnown); + QList<GccToolChain *> existingClangToolChains = clangToolChains(detector.alreadyKnown); const QList<XcodePlatform> platforms = XcodeProbe::detectPlatforms().values(); Toolchains toolChains; toolChains.reserve(platforms.size()); @@ -585,9 +589,10 @@ Toolchains IosToolChainFactory::autoDetect(const ToolchainDetector &detector) co for (const XcodePlatform::ToolchainTarget &target : platform.targets) { ToolChainPair platformToolchains = findToolChainForPlatform(platform, target, existingClangToolChains); - auto createOrAdd = [&](ClangToolChain *toolChain, Id l) { + auto createOrAdd = [&](GccToolChain *toolChain, Id l) { if (!toolChain) { - toolChain = new ClangToolChain; + toolChain = new GccToolChain(ProjectExplorer::Constants::CLANG_TOOLCHAIN_TYPEID, + GccToolChain::Clang); toolChain->setPriority(ToolChain::PriorityHigh); toolChain->setDetection(ToolChain::AutoDetection); toolChain->setLanguage(l); diff --git a/src/plugins/ios/iosdeploystep.cpp b/src/plugins/ios/iosdeploystep.cpp index 2995f290ffe..3f80d9d931a 100644 --- a/src/plugins/ios/iosdeploystep.cpp +++ b/src/plugins/ios/iosdeploystep.cpp @@ -14,62 +14,114 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> -#include <projectexplorer/kitinformation.h> #include <projectexplorer/devicesupport/devicemanager.h> +#include <solutions/tasking/tasktree.h> + #include <utils/temporaryfile.h> #include <QFile> #include <QSettings> using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; namespace Ios::Internal { +class IosTransfer : public QObject +{ + Q_OBJECT + +public: + void setDeviceType(const IosDeviceType &deviceType) { m_deviceType = deviceType; } + void setBundlePath(const FilePath &bundlePath) { m_bundlePath = bundlePath; } + void setExpectSuccess(bool success) { m_expectSuccess = success; } + void start() + { + QTC_ASSERT(m_deviceType, emit done(false); return); + QTC_ASSERT(!m_toolHandler, return); + + m_toolHandler.reset(new IosToolHandler(*m_deviceType)); + connect(m_toolHandler.get(), &IosToolHandler::isTransferringApp, this, + [this](IosToolHandler *, const FilePath &, const QString &, + int progress, int maxProgress, const QString &info) { + emit progressValueChanged(progress * 100 / maxProgress, info); + }); + connect(m_toolHandler.get(), &IosToolHandler::errorMsg, this, + [this](IosToolHandler *, const QString &message) { + if (message.contains(QLatin1String("AMDeviceInstallApplication returned -402653103"))) + TaskHub::addTask(DeploymentTask(Task::Warning, Tr::tr("The Info.plist might be incorrect."))); + emit errorMessage(message); + }); + connect(m_toolHandler.get(), &IosToolHandler::didTransferApp, this, + [this](IosToolHandler *, const FilePath &, const QString &, + IosToolHandler::OpStatus status) { + disconnect(m_toolHandler.get(), nullptr, this, nullptr); + m_toolHandler.release()->deleteLater(); + if (status != IosToolHandler::Success && m_expectSuccess) { + TaskHub::addTask(DeploymentTask(Task::Error, Tr::tr("Deployment failed. " + "The settings in the Devices window of Xcode might be incorrect."))); + } + emit done(status == IosToolHandler::Success); + }); + connect(m_toolHandler.get(), &IosToolHandler::finished, this, [this] { + disconnect(m_toolHandler.get(), nullptr, this, nullptr); + m_toolHandler.release()->deleteLater(); + TaskHub::addTask(DeploymentTask(Task::Error, Tr::tr("Deployment failed."))); + emit done(false); + }); + m_toolHandler->requestTransferApp(m_bundlePath, m_deviceType->identifier); + } + +signals: + void done(bool success); + void progressValueChanged(int progress, const QString &info); // progress in % + void errorMessage(const QString &message); + +private: + std::optional<IosDeviceType> m_deviceType; + FilePath m_bundlePath; + bool m_expectSuccess = true; + std::unique_ptr<IosToolHandler> m_toolHandler; +}; + +class IosTransferTaskAdapter : public TaskAdapter<IosTransfer> +{ +public: + IosTransferTaskAdapter() { connect(task(), &IosTransfer::done, this, &TaskInterface::done); } + +private: + void start() final { task()->start(); } +}; + +using IosTransferTask = CustomTask<IosTransferTaskAdapter>; + class IosDeployStep final : public BuildStep { public: - enum TransferStatus { - NoTransfer, - TransferInProgress, - TransferOk, - TransferFailed - }; - IosDeployStep(BuildStepList *bc, Utils::Id id); private: void cleanup(); - void doRun() final; - void doCancel() final; + Tasking::GroupItem runRecipe() final; - void handleIsTransferringApp(IosToolHandler *handler, const FilePath &bundlePath, - const QString &deviceId, int progress, int maxProgress, - const QString &info); - void handleDidTransferApp(IosToolHandler *handler, const FilePath &bundlePath, - const QString &deviceId, IosToolHandler::OpStatus status); - void handleFinished(IosToolHandler *handler); - void handleErrorMsg(IosToolHandler *handler, const QString &msg); void updateDisplayNames(); bool init() final; QWidget *createConfigWidget() final; - IDevice::ConstPtr device() const; IosDevice::ConstPtr iosdevice() const; IosSimulator::ConstPtr iossimulator() const; QString deviceId() const; - void checkProvisioningProfile(); + bool checkProvisioningProfile(); - TransferStatus m_transferStatus = NoTransfer; - IosToolHandler *m_toolHandler = nullptr; IDevice::ConstPtr m_device; FilePath m_bundlePath; IosDeviceType m_deviceType; - bool m_expectFail = false; }; IosDeployStep::IosDeployStep(BuildStepList *parent, Utils::Id id) @@ -92,7 +144,6 @@ void IosDeployStep::updateDisplayNames() bool IosDeployStep::init() { - QTC_ASSERT(m_transferStatus == NoTransfer, return false); m_device = DeviceKitAspect::device(kit()); auto runConfig = qobject_cast<const IosRunConfiguration *>( this->target()->activeRunConfiguration()); @@ -111,96 +162,25 @@ bool IosDeployStep::init() return true; } -void IosDeployStep::doRun() +GroupItem IosDeployStep::runRecipe() { - QTC_CHECK(m_transferStatus == NoTransfer); - if (m_device.isNull()) { - TaskHub::addTask( - DeploymentTask(Task::Error, Tr::tr("Deployment failed. No iOS device found."))); - emit finished(!iossimulator().isNull()); - cleanup(); - return; - } - m_toolHandler = new IosToolHandler(m_deviceType, this); - m_transferStatus = TransferInProgress; - emit progress(0, Tr::tr("Transferring application")); - connect(m_toolHandler, &IosToolHandler::isTransferringApp, - this, &IosDeployStep::handleIsTransferringApp); - connect(m_toolHandler, &IosToolHandler::didTransferApp, - this, &IosDeployStep::handleDidTransferApp); - connect(m_toolHandler, &IosToolHandler::finished, - this, &IosDeployStep::handleFinished); - connect(m_toolHandler, &IosToolHandler::errorMsg, - this, &IosDeployStep::handleErrorMsg); - checkProvisioningProfile(); - m_toolHandler->requestTransferApp(m_bundlePath, m_deviceType.identifier); -} - -void IosDeployStep::doCancel() -{ - if (m_toolHandler) - m_toolHandler->stop(); -} - -void IosDeployStep::cleanup() -{ - QTC_CHECK(m_transferStatus != TransferInProgress); - m_transferStatus = NoTransfer; - m_device.clear(); - m_toolHandler = nullptr; - m_expectFail = false; -} - -void IosDeployStep::handleIsTransferringApp(IosToolHandler *handler, const FilePath &bundlePath, - const QString &deviceId, int progress, int maxProgress, - const QString &info) -{ - Q_UNUSED(handler); Q_UNUSED(bundlePath); Q_UNUSED(deviceId) - QTC_CHECK(m_transferStatus == TransferInProgress); - emit this->progress(progress * 100 / maxProgress, info); -} - -void IosDeployStep::handleDidTransferApp(IosToolHandler *handler, const FilePath &bundlePath, - const QString &deviceId, IosToolHandler::OpStatus status) -{ - Q_UNUSED(handler); Q_UNUSED(bundlePath); Q_UNUSED(deviceId) - QTC_CHECK(m_transferStatus == TransferInProgress); - if (status == IosToolHandler::Success) { - m_transferStatus = TransferOk; - } else { - m_transferStatus = TransferFailed; - if (!m_expectFail) - TaskHub::addTask(DeploymentTask(Task::Error, - Tr::tr("Deployment failed. The settings in the Devices window of Xcode might be incorrect."))); - } - emit finished(status == IosToolHandler::Success); -} - -void IosDeployStep::handleFinished(IosToolHandler *handler) -{ - switch (m_transferStatus) { - case TransferInProgress: - m_transferStatus = TransferFailed; - TaskHub::addTask(DeploymentTask(Task::Error, Tr::tr("Deployment failed."))); - emit finished(false); - break; - case NoTransfer: - case TransferOk: - case TransferFailed: - break; - } - cleanup(); - handler->deleteLater(); - // move it when result is reported? (would need care to avoid problems with concurrent runs) -} - -void IosDeployStep::handleErrorMsg(IosToolHandler *handler, const QString &msg) -{ - Q_UNUSED(handler) - if (msg.contains(QLatin1String("AMDeviceInstallApplication returned -402653103"))) - TaskHub::addTask(DeploymentTask(Task::Warning, Tr::tr("The Info.plist might be incorrect."))); - - emit addOutput(msg, OutputFormat::ErrorMessage); + const auto onSetup = [this](IosTransfer &transfer) { + if (m_device.isNull()) { + TaskHub::addTask( + DeploymentTask(Task::Error, Tr::tr("Deployment failed. No iOS device found."))); + return SetupResult::StopWithError; + } + transfer.setDeviceType(m_deviceType); + transfer.setBundlePath(m_bundlePath); + transfer.setExpectSuccess(checkProvisioningProfile()); + emit progress(0, Tr::tr("Transferring application")); + connect(&transfer, &IosTransfer::progressValueChanged, this, &IosDeployStep::progress); + connect(&transfer, &IosTransfer::errorMessage, this, [this](const QString &message) { + emit addOutput(message, OutputFormat::ErrorMessage); + }); + return SetupResult::Continue; + }; + return IosTransferTask(onSetup); } QWidget *IosDeployStep::createConfigWidget() @@ -222,53 +202,54 @@ QString IosDeployStep::deviceId() const return iosdevice()->uniqueDeviceID(); } -void IosDeployStep::checkProvisioningProfile() +bool IosDeployStep::checkProvisioningProfile() { IosDevice::ConstPtr device = iosdevice(); if (device.isNull()) - return; + return true; const FilePath provisioningFilePath = m_bundlePath.pathAppended("embedded.mobileprovision"); - // the file is a signed plist stored in DER format // we simply search for start and end of the plist instead of decoding the DER payload if (!provisioningFilePath.exists()) - return; + return true; + QFile provisionFile(provisioningFilePath.toString()); if (!provisionFile.open(QIODevice::ReadOnly)) - return; - QByteArray provisionData = provisionFile.readAll(); - int start = provisionData.indexOf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + return true; + + const QByteArray provisionData = provisionFile.readAll(); + const int start = provisionData.indexOf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); int end = provisionData.indexOf("</plist>"); if (start == -1 || end == -1) - return; - end += 8; + return true; + end += 8; TemporaryFile f("iosdeploy"); if (!f.open()) - return; + return true; + f.write(provisionData.mid(start, end - start)); f.flush(); - QSettings provisionPlist(f.fileName(), QSettings::NativeFormat); - + const QSettings provisionPlist(f.fileName(), QSettings::NativeFormat); if (!provisionPlist.contains(QLatin1String("ProvisionedDevices"))) - return; + return true; + const QStringList deviceIds = provisionPlist.value("ProvisionedDevices").toStringList(); const QString targetId = device->uniqueDeviceID(); for (const QString &deviceId : deviceIds) { if (deviceId == targetId) - return; + return true; } - m_expectFail = true; - QString provisioningProfile = provisionPlist.value(QLatin1String("Name")).toString(); - QString provisioningUid = provisionPlist.value(QLatin1String("UUID")).toString(); - CompileTask task(Task::Warning, + const QString provisioningProfile = provisionPlist.value(QLatin1String("Name")).toString(); + const QString provisioningUid = provisionPlist.value(QLatin1String("UUID")).toString(); + const CompileTask task(Task::Warning, Tr::tr("The provisioning profile \"%1\" (%2) used to sign the application " - "does not cover the device %3 (%4). Deployment to it will fail.") - .arg(provisioningProfile, provisioningUid, device->displayName(), - targetId)); + "does not cover the device %3 (%4). Deployment to it will fail.") + .arg(provisioningProfile, provisioningUid, device->displayName(), targetId)); emit addTask(task); + return false; } IosDevice::ConstPtr IosDeployStep::iosdevice() const @@ -293,3 +274,5 @@ IosDeployStepFactory::IosDeployStepFactory() } } // Ios::Internal + +#include "iosdeploystep.moc" diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp index 326092fccc3..f76f41db976 100644 --- a/src/plugins/ios/iosdevice.cpp +++ b/src/plugins/ios/iosdevice.cpp @@ -13,15 +13,13 @@ #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/devicesupport/idevicewidget.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <utils/portlist.h> #include <QFormLayout> #include <QLabel> #include <QMessageBox> -#include <QVariant> -#include <QVariantMap> #ifdef Q_OS_MAC #include <IOKit/IOKitLib.h> @@ -42,6 +40,7 @@ #include <exception> using namespace ProjectExplorer; +using namespace Utils; namespace { static Q_LOGGING_CATEGORY(detectLog, "qtc.ios.deviceDetect", QtWarningMsg) @@ -87,7 +86,7 @@ IosDevice::IosDevice(CtorHelper) : m_lastPort(Constants::IOS_DEVICE_PORT_START) { setType(Constants::IOS_DEVICE_TYPE); - setDefaultDisplayName(IosDevice::name()); + settings()->displayName.setDefaultValue(IosDevice::name()); setDisplayType(Tr::tr("iOS")); setMachineType(IDevice::Hardware); setOsType(Utils::OsTypeMac); @@ -127,23 +126,23 @@ IDeviceWidget *IosDevice::createWidget() return new IosDeviceInfoWidget(sharedFromThis()); } -void IosDevice::fromMap(const QVariantMap &map) +void IosDevice::fromMap(const Store &map) { IDevice::fromMap(map); m_extraInfo.clear(); - const QVariantMap vMap = map.value(QLatin1String(Constants::EXTRA_INFO_KEY)).toMap(); + const Store vMap = storeFromVariant(map.value(Constants::EXTRA_INFO_KEY)); for (auto i = vMap.cbegin(), end = vMap.cend(); i != end; ++i) - m_extraInfo.insert(i.key(), i.value().toString()); + m_extraInfo.insert(stringFromKey(i.key()), i.value().toString()); } -QVariantMap IosDevice::toMap() const +Store IosDevice::toMap() const { - QVariantMap res = IDevice::toMap(); - QVariantMap vMap; + Store res = IDevice::toMap(); + Store vMap; for (auto i = m_extraInfo.cbegin(), end = m_extraInfo.cend(); i != end; ++i) - vMap.insert(i.key(), i.value()); - res.insert(QLatin1String(Constants::EXTRA_INFO_KEY), vMap); + vMap.insert(keyFromString(i.key()), i.value()); + res.insert(Constants::EXTRA_INFO_KEY, variantFromStore(vMap)); return res; } @@ -154,7 +153,7 @@ QString IosDevice::deviceName() const QString IosDevice::uniqueDeviceID() const { - return id().suffixAfter(Utils::Id(Constants::IOS_DEVICE_ID)); + return id().suffixAfter(Id(Constants::IOS_DEVICE_ID)); } QString IosDevice::uniqueInternalDeviceId() const @@ -216,7 +215,7 @@ void IosDeviceManager::deviceConnected(const QString &uid, const QString &name) if (dev.isNull()) { auto newDev = new IosDevice(uid); if (!name.isNull()) - newDev->setDisplayName(name); + newDev->settings()->displayName.setValue(name); qCDebug(detectLog) << "adding ios device " << uid; devManager->addDevice(IDevice::ConstPtr(newDev)); } else if (dev->deviceState() != IDevice::DeviceConnected && @@ -286,7 +285,7 @@ void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid, } if (!skipUpdate) { if (info.contains(kDeviceName)) - newDev->setDisplayName(info.value(kDeviceName)); + newDev->settings()->displayName.setValue(info.value(kDeviceName)); newDev->m_extraInfo = info; qCDebug(detectLog) << "updated info of ios device " << uid; dev = IDevice::ConstPtr(newDev); @@ -544,9 +543,9 @@ IosDeviceFactory::IosDeviceFactory() setConstructionFunction([] { return IDevice::Ptr(new IosDevice); }); } -bool IosDeviceFactory::canRestore(const QVariantMap &map) const +bool IosDeviceFactory::canRestore(const Store &map) const { - QVariantMap vMap = map.value(QLatin1String(Constants::EXTRA_INFO_KEY)).toMap(); + Store vMap = map.value(Constants::EXTRA_INFO_KEY).value<Store>(); if (vMap.isEmpty() || vMap.value(kDeviceName).toString() == QLatin1String("*unknown*")) return false; // transient device (probably generated during an activation) return true; diff --git a/src/plugins/ios/iosdevice.h b/src/plugins/ios/iosdevice.h index ada952037d9..24bee023de7 100644 --- a/src/plugins/ios/iosdevice.h +++ b/src/plugins/ios/iosdevice.h @@ -8,10 +8,6 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/idevicefactory.h> -#include <QVariantMap> -#include <QMap> -#include <QString> -#include <QStringList> #include <QTimer> namespace Ios { @@ -40,8 +36,8 @@ public: static QString name(); protected: - void fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Utils::Store &map) final; + Utils::Store toMap() const final; friend class IosDeviceFactory; friend class Ios::Internal::IosDeviceManager; @@ -61,7 +57,7 @@ class IosDeviceFactory final : public ProjectExplorer::IDeviceFactory public: IosDeviceFactory(); - bool canRestore(const QVariantMap &map) const override; + bool canRestore(const Utils::Store &map) const override; }; class IosDeviceManager : public QObject diff --git a/src/plugins/ios/iosdsymbuildstep.cpp b/src/plugins/ios/iosdsymbuildstep.cpp index 2d0e2b841f0..0c07ff4182d 100644 --- a/src/plugins/ios/iosdsymbuildstep.cpp +++ b/src/plugins/ios/iosdsymbuildstep.cpp @@ -12,14 +12,14 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtparser.h> #include <utils/process.h> @@ -59,8 +59,8 @@ public: private: void setupOutputFormatter(OutputFormatter *formatter) override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &map) override; + void toMap(Store &map) const override; + void fromMap(const Store &map) override; QStringList defaultCleanCmdList() const; QStringList defaultCmdList() const; @@ -83,33 +83,29 @@ IosDsymBuildStep::IosDsymBuildStep(BuildStepList *parent, Id id) : setIgnoreReturnValue(m_clean); } -QVariantMap IosDsymBuildStep::toMap() const +void IosDsymBuildStep::toMap(Store &map) const { - QVariantMap map(AbstractProcessStep::toMap()); + AbstractProcessStep::toMap(map); - map.insert(id().withSuffix(ARGUMENTS_PARTIAL_KEY).toString(), - arguments()); - map.insert(id().withSuffix(USE_DEFAULT_ARGS_PARTIAL_KEY).toString(), - isDefault()); - map.insert(id().withSuffix(CLEAN_PARTIAL_KEY).toString(), m_clean); - map.insert(id().withSuffix(COMMAND_PARTIAL_KEY).toString(), command().toSettings()); - return map; + map.insert(id().toKey() + ARGUMENTS_PARTIAL_KEY, arguments()); + map.insert(id().toKey() + USE_DEFAULT_ARGS_PARTIAL_KEY, isDefault()); + map.insert(id().toKey() + CLEAN_PARTIAL_KEY, m_clean); + map.insert(id().toKey() + COMMAND_PARTIAL_KEY, command().toSettings()); } -bool IosDsymBuildStep::fromMap(const QVariantMap &map) +void IosDsymBuildStep::fromMap(const Store &map) { - QVariant bArgs = map.value(id().withSuffix(ARGUMENTS_PARTIAL_KEY).toString()); + QVariant bArgs = map.value(id().toKey() + ARGUMENTS_PARTIAL_KEY); m_arguments = bArgs.toStringList(); - bool useDefaultArguments = map.value( - id().withSuffix(USE_DEFAULT_ARGS_PARTIAL_KEY).toString()).toBool(); - m_clean = map.value(id().withSuffix(CLEAN_PARTIAL_KEY).toString(), m_clean).toBool(); - m_command = FilePath::fromSettings(map.value(id().withSuffix(COMMAND_PARTIAL_KEY).toString())); + bool useDefaultArguments = map.value(id().toKey() + USE_DEFAULT_ARGS_PARTIAL_KEY).toBool(); + m_clean = map.value(id().toKey() + CLEAN_PARTIAL_KEY, m_clean).toBool(); + m_command = FilePath::fromSettings(map.value(id().toKey() + COMMAND_PARTIAL_KEY)); if (useDefaultArguments) { m_command = defaultCommand(); m_arguments = defaultArguments(); } - return BuildStep::fromMap(map); + BuildStep::fromMap(map); } QStringList IosDsymBuildStep::defaultArguments() const @@ -130,11 +126,11 @@ FilePath IosDsymBuildStep::defaultCommand() const QStringList IosDsymBuildStep::defaultCleanCmdList() const { auto runConf = qobject_cast<IosRunConfiguration *>(target()->activeRunConfiguration()); - QTC_ASSERT(runConf, return QStringList("echo")); + QTC_ASSERT(runConf, return {"echo"}); QString dsymPath = runConf->bundleDirectory().toUserOutput(); dsymPath.chop(4); dsymPath.append(".dSYM"); - return QStringList({"rm", "-rf", dsymPath}); + return {"rm", "-rf", dsymPath}; } QStringList IosDsymBuildStep::defaultCmdList() const @@ -145,11 +141,11 @@ QStringList IosDsymBuildStep::defaultCmdList() const if (dsymUtilPath.exists()) dsymutilCmd = dsymUtilPath.toUserOutput(); auto runConf = qobject_cast<const IosRunConfiguration *>(target()->activeRunConfiguration()); - QTC_ASSERT(runConf, return QStringList("echo")); + QTC_ASSERT(runConf, return {"echo"}); QString dsymPath = runConf->bundleDirectory().toUserOutput(); dsymPath.chop(4); dsymPath.append(".dSYM"); - return QStringList({dsymutilCmd, "-o", dsymPath, runConf->localExecutable().toUserOutput()}); + return {dsymutilCmd, "-o", dsymPath, runConf->localExecutable().toUserOutput()}; } FilePath IosDsymBuildStep::command() const diff --git a/src/plugins/ios/iosprobe.h b/src/plugins/ios/iosprobe.h index f0bd4bf6609..9d2896162f9 100644 --- a/src/plugins/ios/iosprobe.h +++ b/src/plugins/ios/iosprobe.h @@ -5,7 +5,6 @@ #include <utils/filepath.h> -#include <QSettings> #include <QSharedPointer> #include <QString> #include <QStringList> diff --git a/src/plugins/ios/iosqtversion.cpp b/src/plugins/ios/iosqtversion.cpp index fc772ce72fa..ab275590844 100644 --- a/src/plugins/ios/iosqtversion.cpp +++ b/src/plugins/ios/iosqtversion.cpp @@ -10,7 +10,7 @@ #include <utils/hostosinfo.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionmanager.h> diff --git a/src/plugins/ios/iosrunconfiguration.cpp b/src/plugins/ios/iosrunconfiguration.cpp index 8a0a3e62dcf..961a7c1c501 100644 --- a/src/plugins/ios/iosrunconfiguration.cpp +++ b/src/plugins/ios/iosrunconfiguration.cpp @@ -13,7 +13,7 @@ #include <projectexplorer/buildsteplist.h> #include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectnodes.h> #include <projectexplorer/runconfigurationaspects.h> @@ -40,7 +40,7 @@ using namespace Utils; namespace Ios::Internal { -const QLatin1String deviceTypeKey("Ios.device_type"); +const char deviceTypeKey[] = "Ios.device_type"; static QString displayName(const SimulatorInfo &device) { @@ -56,24 +56,20 @@ static IosDeviceType toIosDeviceType(const SimulatorInfo &device) } IosRunConfiguration::IosRunConfiguration(Target *target, Id id) - : RunConfiguration(target, id) + : RunConfiguration(target, id), iosDeviceType(this, this) { - auto executableAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - executableAspect->setDisplayStyle(StringAspect::LabelDisplay); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); - addAspect<ArgumentsAspect>(macroExpander()); + arguments.setMacroExpander(macroExpander()); - m_deviceTypeAspect = addAspect<IosDeviceTypeAspect>(this); - - setUpdater([this, target, executableAspect] { + setUpdater([this, target] { IDevice::ConstPtr dev = DeviceKitAspect::device(target->kit()); const QString devName = dev.isNull() ? IosDevice::name() : dev->displayName(); setDefaultDisplayName(Tr::tr("Run on %1").arg(devName)); setDisplayName(Tr::tr("Run %1 on %2").arg(applicationName()).arg(devName)); - executableAspect->setExecutable(localExecutable()); - - m_deviceTypeAspect->updateDeviceType(); + executable.setExecutable(localExecutable()); + iosDeviceType.updateDeviceType(); }); } @@ -210,19 +206,19 @@ FilePath IosRunConfiguration::localExecutable() const return bundleDirectory().pathAppended(applicationName()); } -void IosDeviceTypeAspect::fromMap(const QVariantMap &map) +void IosDeviceTypeAspect::fromMap(const Store &map) { bool deviceTypeIsInt; map.value(deviceTypeKey).toInt(&deviceTypeIsInt); - if (deviceTypeIsInt || !m_deviceType.fromMap(map.value(deviceTypeKey).toMap())) + if (deviceTypeIsInt || !m_deviceType.fromMap(storeFromVariant(map.value(deviceTypeKey)))) updateDeviceType(); m_runConfiguration->update(); } -void IosDeviceTypeAspect::toMap(QVariantMap &map) const +void IosDeviceTypeAspect::toMap(Store &map) const { - map.insert(deviceTypeKey, deviceType().toMap()); + map.insert(deviceTypeKey, QVariant::fromValue(deviceType().toMap())); } QString IosRunConfiguration::disabledReason() const @@ -278,7 +274,7 @@ QString IosRunConfiguration::disabledReason() const IosDeviceType IosRunConfiguration::deviceType() const { - return m_deviceTypeAspect->deviceType(); + return iosDeviceType.deviceType(); } IosDeviceType IosDeviceTypeAspect::deviceType() const @@ -310,8 +306,8 @@ void IosDeviceTypeAspect::setDeviceType(const IosDeviceType &deviceType) m_deviceType = deviceType; } -IosDeviceTypeAspect::IosDeviceTypeAspect(IosRunConfiguration *runConfiguration) - : m_runConfiguration(runConfiguration) +IosDeviceTypeAspect::IosDeviceTypeAspect(AspectContainer *container, IosRunConfiguration *rc) + : BaseAspect(container), m_runConfiguration(rc) { addDataExtractor(this, &IosDeviceTypeAspect::deviceType, &Data::deviceType); addDataExtractor(this, &IosDeviceTypeAspect::bundleDirectory, &Data::bundleDirectory); diff --git a/src/plugins/ios/iosrunconfiguration.h b/src/plugins/ios/iosrunconfiguration.h index 1ebe79efa56..da890ae3e5e 100644 --- a/src/plugins/ios/iosrunconfiguration.h +++ b/src/plugins/ios/iosrunconfiguration.h @@ -8,6 +8,7 @@ #include "iossimulator.h" #include <projectexplorer/runconfiguration.h> +#include <projectexplorer/runconfigurationaspects.h> #include <utils/fileutils.h> @@ -23,10 +24,11 @@ class IosDeviceTypeAspect : public Utils::BaseAspect Q_OBJECT public: - explicit IosDeviceTypeAspect(IosRunConfiguration *runConfiguration); + explicit IosDeviceTypeAspect(Utils::AspectContainer *container, + IosRunConfiguration *runConfiguration); - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; void addToLayout(Layouting::LayoutItem &parent) override; IosDeviceType deviceType() const; @@ -74,7 +76,9 @@ public: private: bool isEnabled() const final; - IosDeviceTypeAspect *m_deviceTypeAspect = nullptr; + ProjectExplorer::ExecutableAspect executable{this}; + ProjectExplorer::ArgumentsAspect arguments{this}; + IosDeviceTypeAspect iosDeviceType; }; class IosRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory diff --git a/src/plugins/ios/iosrunner.cpp b/src/plugins/ios/iosrunner.cpp index 9b1f2d04869..979ada34746 100644 --- a/src/plugins/ios/iosrunner.cpp +++ b/src/plugins/ios/iosrunner.cpp @@ -12,11 +12,11 @@ #include "iostr.h" #include <debugger/debuggerconstants.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerplugin.h> #include <debugger/debuggerruncontrol.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> @@ -81,26 +81,21 @@ public: void setCppDebugging(bool cppDebug); void setQmlDebugging(QmlDebug::QmlDebugServicesPreset qmlDebugServices); - Utils::FilePath bundlePath() const; - QString deviceId(); - IosToolHandler::RunKind runType(); - bool cppDebug() const; - bool qmlDebug() const; - QmlDebug::QmlDebugServicesPreset qmlDebugServices() const; - void start() override; void stop() final; - virtual void appOutput(const QString &/*output*/) {} - virtual void errorMsg(const QString &/*msg*/) {} - virtual void onStart() { reportStarted(); } - Port qmlServerPort() const; Port gdbServerPort() const; qint64 pid() const; bool isAppRunning() const; private: + Utils::FilePath bundlePath() const; + QString deviceId() const; + IosToolHandler::RunKind runType() const; + bool cppDebug() const; + bool qmlDebug() const; + void handleGotServerPorts(Ios::IosToolHandler *handler, const FilePath &bundlePath, const QString &deviceId, Port gdbPort, Port qmlPort); void handleGotInferiorPid(Ios::IosToolHandler *handler, const FilePath &bundlePath, @@ -155,7 +150,7 @@ FilePath IosRunner::bundlePath() const return m_bundleDir; } -QString IosRunner::deviceId() +QString IosRunner::deviceId() const { IosDevice::ConstPtr dev = m_device.dynamicCast<const IosDevice>(); if (!dev) @@ -163,7 +158,7 @@ QString IosRunner::deviceId() return dev->uniqueDeviceID(); } -IosToolHandler::RunKind IosRunner::runType() +IosToolHandler::RunKind IosRunner::runType() const { if (m_cppDebug) return IosToolHandler::DebugRun; @@ -180,11 +175,6 @@ bool IosRunner::qmlDebug() const return m_qmlDebugServices != QmlDebug::NoQmlDebugServices; } -QmlDebug::QmlDebugServicesPreset IosRunner::qmlDebugServices() const -{ - return m_qmlDebugServices; -} - void IosRunner::start() { if (m_toolHandler && isAppRunning()) @@ -315,7 +305,6 @@ void IosRunner::handleAppOutput(IosToolHandler *handler, const QString &output) if (match.hasMatch() && m_qmlServerPort.isValid()) res.replace(match.captured(1), QString::number(m_qmlServerPort.number())); appendMessage(output, StdOutFormat); - appOutput(res); } void IosRunner::handleErrorMsg(IosToolHandler *handler, const QString &msg) @@ -337,7 +326,6 @@ void IosRunner::handleErrorMsg(IosToolHandler *handler, const QString &msg) res.replace(match.captured(1), QString::number(m_qmlServerPort.number())); appendMessage(res, StdErrFormat); - errorMsg(res); } void IosRunner::handleToolExited(IosToolHandler *handler, int code) @@ -389,8 +377,6 @@ public: explicit IosRunSupport(RunControl *runControl); ~IosRunSupport() override; - void didStartApp(IosToolHandler::OpStatus status); - private: void start() override; }; @@ -400,8 +386,8 @@ IosRunSupport::IosRunSupport(RunControl *runControl) { setId("IosRunSupport"); runControl->setIcon(Icons::RUN_SMALL_TOOLBAR); - QString displayName = QString("Run on %1").arg(device().isNull() ? QString() : device()->displayName()); - runControl->setDisplayName(displayName); + runControl->setDisplayName(QString("Run on %1") + .arg(device().isNull() ? QString() : device()->displayName())); } IosRunSupport::~IosRunSupport() diff --git a/src/plugins/ios/iossettingspage.cpp b/src/plugins/ios/iossettingspage.cpp index 7a4f306f659..3292fefc079 100644 --- a/src/plugins/ios/iossettingspage.cpp +++ b/src/plugins/ios/iossettingspage.cpp @@ -1,16 +1,396 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "iossettingspage.h" +#include "createsimulatordialog.h" +#include "iosconfigurations.h" #include "iosconstants.h" -#include "iossettingswidget.h" #include "iostr.h" +#include "simulatorcontrol.h" +#include "simulatorinfomodel.h" +#include "simulatoroperationdialog.h" #include <projectexplorer/projectexplorerconstants.h> +#include <utils/algorithm.h> +#include <utils/async.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> + +#include <QCheckBox> +#include <QDateTime> +#include <QGroupBox> +#include <QHeaderView> +#include <QInputDialog> +#include <QLabel> +#include <QMessageBox> +#include <QPointer> +#include <QPushButton> +#include <QSortFilterProxyModel> +#include <QTreeView> + +using namespace std::placeholders; + namespace Ios::Internal { +class IosSettingsWidget final : public Core::IOptionsPageWidget +{ +public: + IosSettingsWidget(); + ~IosSettingsWidget() final; + +private: + void apply() final; + + void saveSettings(); + + void onStart(); + void onCreate(); + void onReset(); + void onRename(); + void onDelete(); + void onScreenshot(); + void onSelectionChanged(); + +private: + Utils::PathChooser *m_pathWidget; + QPushButton *m_startButton; + QPushButton *m_renameButton; + QPushButton *m_deleteButton; + QPushButton *m_resetButton; + QTreeView *m_deviceView; + QCheckBox *m_deviceAskCheckBox; +}; + +const int simStartWarnCount = 4; + +static SimulatorInfoList selectedSimulators(const QTreeView *deviceTreeView) +{ + SimulatorInfoList list; + QItemSelectionModel *selectionModel = deviceTreeView->selectionModel(); + for (QModelIndex index: selectionModel->selectedRows()) + list << deviceTreeView->model()->data(index, Qt::UserRole).value<SimulatorInfo>(); + return list; +} + +static void onSimOperation(const SimulatorInfo &simInfo, SimulatorOperationDialog* dlg, + const QString &contextStr, const SimulatorControl::ResponseData &response) +{ + dlg->addMessage(simInfo, response, contextStr); +} + +IosSettingsWidget::IosSettingsWidget() +{ + setWindowTitle(Tr::tr("iOS Configuration")); + + m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode")); + m_deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices()); + + m_renameButton = new QPushButton(Tr::tr("Rename")); + m_renameButton->setEnabled(false); + m_renameButton->setToolTip(Tr::tr("Rename a simulator device.")); + + m_deleteButton = new QPushButton(Tr::tr("Delete")); + m_deleteButton->setEnabled(false); + m_deleteButton->setToolTip(Tr::tr("Delete simulator devices.")); + + m_resetButton = new QPushButton(Tr::tr("Reset")); + m_resetButton->setEnabled(false); + m_resetButton->setToolTip(Tr::tr("Reset contents and settings of simulator devices.")); + + auto createButton = new QPushButton(Tr::tr("Create")); + createButton->setToolTip(Tr::tr("Create a new simulator device.")); + + m_startButton = new QPushButton(Tr::tr("Start")); + m_startButton->setEnabled(false); + m_startButton->setToolTip(Tr::tr("Start simulator devices.")); + + auto proxyModel = new QSortFilterProxyModel(this); + proxyModel->setSourceModel(new SimulatorInfoModel(this)); + + m_deviceView = new QTreeView; + m_deviceView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); + m_deviceView->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_deviceView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_deviceView->setSortingEnabled(true); + m_deviceView->setModel(proxyModel); + m_deviceView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + + m_pathWidget = new Utils::PathChooser; + m_pathWidget->setExpectedKind(Utils::PathChooser::ExistingDirectory); + m_pathWidget->lineEdit()->setReadOnly(true); + m_pathWidget->setFilePath(IosConfigurations::screenshotDir()); + m_pathWidget->addButton(Tr::tr("Screenshot"), this, + std::bind(&IosSettingsWidget::onScreenshot, this)); + + using namespace Layouting; + Column { + Group { + title(Tr::tr("Devices")), + Row { m_deviceAskCheckBox } + }, + Group { + title(Tr::tr("Simulator")), + Column { + Row { + m_deviceView, + Column { + createButton, + st, // FIXME: Better some fixed space? + m_startButton, + m_renameButton, + m_resetButton, + m_deleteButton, + st + }, + }, + hr, + Row { Tr::tr("Screenshot directory:"), m_pathWidget } + } + } + }.attachTo(this); + + connect(m_startButton, &QPushButton::clicked, this, &IosSettingsWidget::onStart); + connect(createButton, &QPushButton::clicked, this, &IosSettingsWidget::onCreate); + connect(m_renameButton, &QPushButton::clicked, this, &IosSettingsWidget::onRename); + connect(m_resetButton, &QPushButton::clicked, this, &IosSettingsWidget::onReset); + connect(m_deleteButton, &QPushButton::clicked, this, &IosSettingsWidget::onDelete); + + connect(m_deviceView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &IosSettingsWidget::onSelectionChanged); +} + +IosSettingsWidget::~IosSettingsWidget() = default; + +void IosSettingsWidget::apply() +{ + saveSettings(); + IosConfigurations::updateAutomaticKitList(); +} + +/*! + Called on start button click. Selected simulator devices are started. Multiple devices can be + started simultaneously provided they in shutdown state. + */ +void IosSettingsWidget::onStart() +{ + const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); + if (simulatorInfoList.isEmpty()) + return; + + if (simulatorInfoList.count() > simStartWarnCount) { + const QString message = + Tr::tr("You are trying to launch %n simulators simultaneously. This " + "will take significant system resources. Do you really want to " + "continue?", "", simulatorInfoList.count()); + const int buttonCode = QMessageBox::warning(this, Tr::tr("Simulator Start"), message, + QMessageBox::Ok | QMessageBox::Abort, + QMessageBox::Abort); + + if (buttonCode == QMessageBox::Abort) + return; + } + + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Starting %n simulator device(s)...", "", simulatorInfoList.count()), + Utils::NormalMessageFormat); + + QList<QFuture<void>> futureList; + for (const SimulatorInfo &info : simulatorInfoList) { + if (!info.isShutdown()) { + statusDialog->addMessage(Tr::tr("Cannot start simulator (%1, %2) in current state: %3") + .arg(info.name).arg(info.runtimeName).arg(info.state), + Utils::StdErrFormat); + } else { + futureList << QFuture<void>(Utils::onResultReady( + SimulatorControl::startSimulator(info.identifier), this, + std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator start"), _1))); + } + } + + statusDialog->addFutures(futureList); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. +} + +/*! + Called on create button click. User is presented with the create simulator dialog and with the + selected options a new device is created. + */ +void IosSettingsWidget::onCreate() +{ + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Creating simulator device..."), Utils::NormalMessageFormat); + const auto onSimulatorCreate = [statusDialog](const QString &name, + const SimulatorControl::ResponseData &response) { + if (response.success) { + statusDialog->addMessage(Tr::tr("Simulator device (%1) created.\nUDID: %2") + .arg(name).arg(response.simUdid), Utils::StdOutFormat); + } else { + statusDialog->addMessage(Tr::tr("Simulator device (%1) creation failed.\nError: %2"). + arg(name).arg(response.commandOutput), + Utils::StdErrFormat); + } + }; + + CreateSimulatorDialog createDialog(this); + if (createDialog.exec() == QDialog::Accepted) { + QFuture<void> f = QFuture<void>(Utils::onResultReady(SimulatorControl::createSimulator( + createDialog.name(), createDialog.deviceType(), createDialog.runtime()), + this, std::bind(onSimulatorCreate, createDialog.name(), _1))); + statusDialog->addFutures({ f }); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. + } +} + +/*! + Called on reset button click. Contents and settings of the selected devices are erased. Multiple + devices can be erased simultaneously provided they in shutdown state. + */ +void IosSettingsWidget::onReset() +{ + const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); + if (simulatorInfoList.isEmpty()) + return; + + const int userInput = QMessageBox::question(this, Tr::tr("Reset"), + Tr::tr("Do you really want to reset the contents and settings" + " of the %n selected device(s)?", "", + simulatorInfoList.count())); + if (userInput == QMessageBox::No) + return; + + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Resetting contents and settings..."), + Utils::NormalMessageFormat); + + QList<QFuture<void>> futureList; + for (const SimulatorInfo &info : simulatorInfoList) { + futureList << QFuture<void>(Utils::onResultReady( + SimulatorControl::resetSimulator(info.identifier), this, + std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator reset"), _1))); + } + + statusDialog->addFutures(futureList); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. +} + +/*! + Called on rename button click. Selected device is renamed. Only one device can be renamed at a + time. Rename button is disabled on multi-selection. + */ +void IosSettingsWidget::onRename() +{ + const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); + if (simulatorInfoList.isEmpty() || simulatorInfoList.count() > 1) + return; + + const SimulatorInfo &simInfo = simulatorInfoList.at(0); + const QString newName = QInputDialog::getText(this, Tr::tr("Rename %1").arg(simInfo.name), + Tr::tr("Enter new name:")); + if (newName.isEmpty()) + return; + + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Renaming simulator device..."), Utils::NormalMessageFormat); + QFuture<void> f = QFuture<void>(Utils::onResultReady( + SimulatorControl::renameSimulator(simInfo.identifier, newName), this, + std::bind(onSimOperation, simInfo, statusDialog, Tr::tr("simulator rename"), _1))); + statusDialog->addFutures({f}); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. +} + +/*! + Called on delete button click. Selected devices are deleted. Multiple devices can be deleted + simultaneously provided they in shutdown state. + */ +void IosSettingsWidget::onDelete() +{ + const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); + if (simulatorInfoList.isEmpty()) + return; + + const int userInput = + QMessageBox::question(this, Tr::tr("Delete Device"), + Tr::tr("Do you really want to delete the %n selected " + "device(s)?", "", simulatorInfoList.count())); + if (userInput == QMessageBox::No) + return; + + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Deleting %n simulator device(s)...", "", simulatorInfoList.count()), + Utils::NormalMessageFormat); + QList<QFuture<void>> futureList; + for (const SimulatorInfo &info : simulatorInfoList) { + futureList << QFuture<void>(Utils::onResultReady( + SimulatorControl::deleteSimulator(info.identifier), this, + std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator delete"), _1))); + } + + statusDialog->addFutures(futureList); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. +} + +/*! + Called on screenshot button click. Screenshot of the selected devices are saved to the selected + path. Screenshot from multiple devices can be taken simultaneously provided they in booted state. + */ +void IosSettingsWidget::onScreenshot() +{ + const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); + if (simulatorInfoList.isEmpty()) + return; + + const auto generatePath = [this](const SimulatorInfo &info) { + const QString fileName = QString("%1_%2_%3.png").arg(info.name).arg(info.runtimeName) + .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss-z")).replace(' ', '_'); + return m_pathWidget->filePath().pathAppended(fileName).toString(); + }; + + QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); + statusDialog->setAttribute(Qt::WA_DeleteOnClose); + statusDialog->addMessage(Tr::tr("Capturing screenshots from %n device(s)...", "", + simulatorInfoList.count()), Utils::NormalMessageFormat); + QList<QFuture<void>> futureList; + for (const SimulatorInfo &info : simulatorInfoList) { + futureList << QFuture<void>(Utils::onResultReady( + SimulatorControl::takeSceenshot(info.identifier, generatePath(info)), this, + std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator screenshot"), _1))); + } + + statusDialog->addFutures(futureList); + statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. +} + +void IosSettingsWidget::onSelectionChanged() +{ + const SimulatorInfoList infoList = selectedSimulators(m_deviceView); + const bool hasRunning = Utils::anyOf(infoList, [](const SimulatorInfo &info) { + return info.isBooted(); + }); + const bool hasShutdown = Utils::anyOf(infoList, [](const SimulatorInfo &info) { + return info.isShutdown(); + }); + m_startButton->setEnabled(hasShutdown); + m_deleteButton->setEnabled(hasShutdown); + m_resetButton->setEnabled(hasShutdown); + m_renameButton->setEnabled(infoList.count() == 1 && hasShutdown); + m_pathWidget->buttonAtIndex(1)->setEnabled(hasRunning); // Screenshot button +} + +void IosSettingsWidget::saveSettings() +{ + IosConfigurations::setIgnoreAllDevices(!m_deviceAskCheckBox->isChecked()); + IosConfigurations::setScreenshotDir(m_pathWidget->filePath()); +} + +// IosSettingsPage + IosSettingsPage::IosSettingsPage() { setId(Constants::IOS_SETTINGS_ID); @@ -20,3 +400,4 @@ IosSettingsPage::IosSettingsPage() } } // Ios::Internal + diff --git a/src/plugins/ios/iossettingspage.h b/src/plugins/ios/iossettingspage.h index b58d8bfb8c6..7617f08207e 100644 --- a/src/plugins/ios/iossettingspage.h +++ b/src/plugins/ios/iossettingspage.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once diff --git a/src/plugins/ios/iossettingswidget.cpp b/src/plugins/ios/iossettingswidget.cpp deleted file mode 100644 index bb0bc36d61a..00000000000 --- a/src/plugins/ios/iossettingswidget.cpp +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "iossettingswidget.h" - -#include "createsimulatordialog.h" -#include "iosconfigurations.h" -#include "iosconfigurations.h" -#include "iostr.h" -#include "simulatorcontrol.h" -#include "simulatorinfomodel.h" -#include "simulatoroperationdialog.h" - -#include <utils/algorithm.h> -#include <utils/async.h> -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> - -#include <QCheckBox> -#include <QDateTime> -#include <QGroupBox> -#include <QHeaderView> -#include <QInputDialog> -#include <QLabel> -#include <QMessageBox> -#include <QPointer> -#include <QPushButton> -#include <QSortFilterProxyModel> -#include <QTreeView> - -using namespace std::placeholders; - -namespace Ios::Internal { - -const int simStartWarnCount = 4; - -static SimulatorInfoList selectedSimulators(const QTreeView *deviceTreeView) -{ - SimulatorInfoList list; - QItemSelectionModel *selectionModel = deviceTreeView->selectionModel(); - for (QModelIndex index: selectionModel->selectedRows()) - list << deviceTreeView->model()->data(index, Qt::UserRole).value<SimulatorInfo>(); - return list; -} - -static void onSimOperation(const SimulatorInfo &simInfo, SimulatorOperationDialog* dlg, - const QString &contextStr, const SimulatorControl::ResponseData &response) -{ - dlg->addMessage(simInfo, response, contextStr); -} - -IosSettingsWidget::IosSettingsWidget() -{ - setWindowTitle(Tr::tr("iOS Configuration")); - - m_deviceAskCheckBox = new QCheckBox(Tr::tr("Ask about devices not in developer mode")); - m_deviceAskCheckBox->setChecked(!IosConfigurations::ignoreAllDevices()); - - m_renameButton = new QPushButton(Tr::tr("Rename")); - m_renameButton->setEnabled(false); - m_renameButton->setToolTip(Tr::tr("Rename a simulator device.")); - - m_deleteButton = new QPushButton(Tr::tr("Delete")); - m_deleteButton->setEnabled(false); - m_deleteButton->setToolTip(Tr::tr("Delete simulator devices.")); - - m_resetButton = new QPushButton(Tr::tr("Reset")); - m_resetButton->setEnabled(false); - m_resetButton->setToolTip(Tr::tr("Reset contents and settings of simulator devices.")); - - auto createButton = new QPushButton(Tr::tr("Create")); - createButton->setToolTip(Tr::tr("Create a new simulator device.")); - - m_startButton = new QPushButton(Tr::tr("Start")); - m_startButton->setEnabled(false); - m_startButton->setToolTip(Tr::tr("Start simulator devices.")); - - auto proxyModel = new QSortFilterProxyModel(this); - proxyModel->setSourceModel(new SimulatorInfoModel(this)); - - m_deviceView = new QTreeView; - m_deviceView->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); - m_deviceView->setSelectionMode(QAbstractItemView::ExtendedSelection); - m_deviceView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_deviceView->setSortingEnabled(true); - m_deviceView->setModel(proxyModel); - m_deviceView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - - m_pathWidget = new Utils::PathChooser; - m_pathWidget->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_pathWidget->lineEdit()->setReadOnly(true); - m_pathWidget->setFilePath(IosConfigurations::screenshotDir()); - m_pathWidget->addButton(Tr::tr("Screenshot"), this, - std::bind(&IosSettingsWidget::onScreenshot, this)); - - using namespace Layouting; - Column { - Group { - title(Tr::tr("Devices")), - Row { m_deviceAskCheckBox } - }, - Group { - title(Tr::tr("Simulator")), - Column { - Row { - m_deviceView, - Column { - createButton, - st, // FIXME: Better some fixed space? - m_startButton, - m_renameButton, - m_resetButton, - m_deleteButton, - st - }, - }, - hr, - Row { Tr::tr("Screenshot directory:"), m_pathWidget } - } - } - }.attachTo(this); - - connect(m_startButton, &QPushButton::clicked, this, &IosSettingsWidget::onStart); - connect(createButton, &QPushButton::clicked, this, &IosSettingsWidget::onCreate); - connect(m_renameButton, &QPushButton::clicked, this, &IosSettingsWidget::onRename); - connect(m_resetButton, &QPushButton::clicked, this, &IosSettingsWidget::onReset); - connect(m_deleteButton, &QPushButton::clicked, this, &IosSettingsWidget::onDelete); - - connect(m_deviceView->selectionModel(), &QItemSelectionModel::selectionChanged, - this, &IosSettingsWidget::onSelectionChanged); -} - -IosSettingsWidget::~IosSettingsWidget() = default; - -void IosSettingsWidget::apply() -{ - saveSettings(); - IosConfigurations::updateAutomaticKitList(); -} - -/*! - Called on start button click. Selected simulator devices are started. Multiple devices can be - started simultaneously provided they in shutdown state. - */ -void IosSettingsWidget::onStart() -{ - const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); - if (simulatorInfoList.isEmpty()) - return; - - if (simulatorInfoList.count() > simStartWarnCount) { - const QString message = - Tr::tr("You are trying to launch %n simulators simultaneously. This " - "will take significant system resources. Do you really want to " - "continue?", "", simulatorInfoList.count()); - const int buttonCode = QMessageBox::warning(this, Tr::tr("Simulator Start"), message, - QMessageBox::Ok | QMessageBox::Abort, - QMessageBox::Abort); - - if (buttonCode == QMessageBox::Abort) - return; - } - - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Starting %n simulator device(s)...", "", simulatorInfoList.count()), - Utils::NormalMessageFormat); - - QList<QFuture<void>> futureList; - for (const SimulatorInfo &info : simulatorInfoList) { - if (!info.isShutdown()) { - statusDialog->addMessage(Tr::tr("Cannot start simulator (%1, %2) in current state: %3") - .arg(info.name).arg(info.runtimeName).arg(info.state), - Utils::StdErrFormat); - } else { - futureList << QFuture<void>(Utils::onResultReady( - SimulatorControl::startSimulator(info.identifier), this, - std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator start"), _1))); - } - } - - statusDialog->addFutures(futureList); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. -} - -/*! - Called on create button click. User is presented with the create simulator dialog and with the - selected options a new device is created. - */ -void IosSettingsWidget::onCreate() -{ - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Creating simulator device..."), Utils::NormalMessageFormat); - const auto onSimulatorCreate = [statusDialog](const QString &name, - const SimulatorControl::ResponseData &response) { - if (response.success) { - statusDialog->addMessage(Tr::tr("Simulator device (%1) created.\nUDID: %2") - .arg(name).arg(response.simUdid), Utils::StdOutFormat); - } else { - statusDialog->addMessage(Tr::tr("Simulator device (%1) creation failed.\nError: %2"). - arg(name).arg(response.commandOutput), - Utils::StdErrFormat); - } - }; - - CreateSimulatorDialog createDialog(this); - if (createDialog.exec() == QDialog::Accepted) { - QFuture<void> f = QFuture<void>(Utils::onResultReady(SimulatorControl::createSimulator( - createDialog.name(), createDialog.deviceType(), createDialog.runtime()), - this, std::bind(onSimulatorCreate, createDialog.name(), _1))); - statusDialog->addFutures({ f }); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. - } -} - -/*! - Called on reset button click. Contents and settings of the selected devices are erased. Multiple - devices can be erased simultaneously provided they in shutdown state. - */ -void IosSettingsWidget::onReset() -{ - const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); - if (simulatorInfoList.isEmpty()) - return; - - const int userInput = QMessageBox::question(this, Tr::tr("Reset"), - Tr::tr("Do you really want to reset the contents and settings" - " of the %n selected device(s)?", "", - simulatorInfoList.count())); - if (userInput == QMessageBox::No) - return; - - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Resetting contents and settings..."), - Utils::NormalMessageFormat); - - QList<QFuture<void>> futureList; - for (const SimulatorInfo &info : simulatorInfoList) { - futureList << QFuture<void>(Utils::onResultReady( - SimulatorControl::resetSimulator(info.identifier), this, - std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator reset"), _1))); - } - - statusDialog->addFutures(futureList); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. -} - -/*! - Called on rename button click. Selected device is renamed. Only one device can be renamed at a - time. Rename button is disabled on multi-selection. - */ -void IosSettingsWidget::onRename() -{ - const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); - if (simulatorInfoList.isEmpty() || simulatorInfoList.count() > 1) - return; - - const SimulatorInfo &simInfo = simulatorInfoList.at(0); - const QString newName = QInputDialog::getText(this, Tr::tr("Rename %1").arg(simInfo.name), - Tr::tr("Enter new name:")); - if (newName.isEmpty()) - return; - - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Renaming simulator device..."), Utils::NormalMessageFormat); - QFuture<void> f = QFuture<void>(Utils::onResultReady( - SimulatorControl::renameSimulator(simInfo.identifier, newName), this, - std::bind(onSimOperation, simInfo, statusDialog, Tr::tr("simulator rename"), _1))); - statusDialog->addFutures({f}); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. -} - -/*! - Called on delete button click. Selected devices are deleted. Multiple devices can be deleted - simultaneously provided they in shutdown state. - */ -void IosSettingsWidget::onDelete() -{ - const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); - if (simulatorInfoList.isEmpty()) - return; - - const int userInput = - QMessageBox::question(this, Tr::tr("Delete Device"), - Tr::tr("Do you really want to delete the %n selected " - "device(s)?", "", simulatorInfoList.count())); - if (userInput == QMessageBox::No) - return; - - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Deleting %n simulator device(s)...", "", simulatorInfoList.count()), - Utils::NormalMessageFormat); - QList<QFuture<void>> futureList; - for (const SimulatorInfo &info : simulatorInfoList) { - futureList << QFuture<void>(Utils::onResultReady( - SimulatorControl::deleteSimulator(info.identifier), this, - std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator delete"), _1))); - } - - statusDialog->addFutures(futureList); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. -} - -/*! - Called on screenshot button click. Screenshot of the selected devices are saved to the selected - path. Screenshot from multiple devices can be taken simultaneously provided they in booted state. - */ -void IosSettingsWidget::onScreenshot() -{ - const SimulatorInfoList simulatorInfoList = selectedSimulators(m_deviceView); - if (simulatorInfoList.isEmpty()) - return; - - const auto generatePath = [this](const SimulatorInfo &info) { - const QString fileName = QString("%1_%2_%3.png").arg(info.name).arg(info.runtimeName) - .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss-z")).replace(' ', '_'); - return m_pathWidget->filePath().pathAppended(fileName).toString(); - }; - - QPointer<SimulatorOperationDialog> statusDialog = new SimulatorOperationDialog(this); - statusDialog->setAttribute(Qt::WA_DeleteOnClose); - statusDialog->addMessage(Tr::tr("Capturing screenshots from %n device(s)...", "", - simulatorInfoList.count()), Utils::NormalMessageFormat); - QList<QFuture<void>> futureList; - for (const SimulatorInfo &info : simulatorInfoList) { - futureList << QFuture<void>(Utils::onResultReady( - SimulatorControl::takeSceenshot(info.identifier, generatePath(info)), this, - std::bind(onSimOperation, info, statusDialog, Tr::tr("simulator screenshot"), _1))); - } - - statusDialog->addFutures(futureList); - statusDialog->exec(); // Modal dialog returns only when all the operations are done or cancelled. -} - -void IosSettingsWidget::onSelectionChanged() -{ - const SimulatorInfoList infoList = selectedSimulators(m_deviceView); - const bool hasRunning = Utils::anyOf(infoList, [](const SimulatorInfo &info) { - return info.isBooted(); - }); - const bool hasShutdown = Utils::anyOf(infoList, [](const SimulatorInfo &info) { - return info.isShutdown(); - }); - m_startButton->setEnabled(hasShutdown); - m_deleteButton->setEnabled(hasShutdown); - m_resetButton->setEnabled(hasShutdown); - m_renameButton->setEnabled(infoList.count() == 1 && hasShutdown); - m_pathWidget->buttonAtIndex(1)->setEnabled(hasRunning); // Screenshot button -} - -void IosSettingsWidget::saveSettings() -{ - IosConfigurations::setIgnoreAllDevices(!m_deviceAskCheckBox->isChecked()); - IosConfigurations::setScreenshotDir(m_pathWidget->filePath()); -} - -} // Ios::Internal - diff --git a/src/plugins/ios/iossettingswidget.h b/src/plugins/ios/iossettingswidget.h deleted file mode 100644 index 6d315db6906..00000000000 --- a/src/plugins/ios/iossettingswidget.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -QT_BEGIN_NAMESPACE -class QCheckBox; -class QPushButton; -class QTreeView; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - -namespace Ios::Internal { - -class IosSettingsWidget final : public Core::IOptionsPageWidget -{ -public: - IosSettingsWidget(); - ~IosSettingsWidget() final; - -private: - void apply() final; - - void saveSettings(); - - void onStart(); - void onCreate(); - void onReset(); - void onRename(); - void onDelete(); - void onScreenshot(); - void onSelectionChanged(); - -private: - Utils::PathChooser *m_pathWidget; - QPushButton *m_startButton; - QPushButton *m_renameButton; - QPushButton *m_deleteButton; - QPushButton *m_resetButton; - QTreeView *m_deviceView; - QCheckBox *m_deviceAskCheckBox; -}; - -} // Ios::Internal diff --git a/src/plugins/ios/iossimulator.cpp b/src/plugins/ios/iossimulator.cpp index fa68c4031e3..185db8f429d 100644 --- a/src/plugins/ios/iossimulator.cpp +++ b/src/plugins/ios/iossimulator.cpp @@ -5,7 +5,7 @@ #include "iosconstants.h" #include "iostr.h" -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <utils/port.h> #include <utils/process.h> @@ -13,21 +13,22 @@ #include <QMapIterator> using namespace ProjectExplorer; +using namespace Utils; namespace Ios::Internal { -const QLatin1String iosDeviceTypeDisplayNameKey("displayName"); -const QLatin1String iosDeviceTypeTypeKey("type"); -const QLatin1String iosDeviceTypeIdentifierKey("identifier"); +const char iosDeviceTypeDisplayNameKey[] = "displayName"; +const char iosDeviceTypeTypeKey[] = "type"; +const char iosDeviceTypeIdentifierKey[] = "identifier"; -IosSimulator::IosSimulator(Utils::Id id) +IosSimulator::IosSimulator(Id id) : m_lastPort(Constants::IOS_SIMULATOR_PORT_START) { setupId(IDevice::AutoDetected, id); setType(Constants::IOS_SIMULATOR_TYPE); setMachineType(IDevice::Emulator); setOsType(Utils::OsTypeMac); - setDefaultDisplayName(Tr::tr("iOS Simulator")); + settings()->displayName.setDefaultValue(Tr::tr("iOS Simulator")); setDisplayType(Tr::tr("iOS Simulator")); setDeviceState(DeviceReadyToUse); } @@ -72,7 +73,7 @@ IosDeviceType::IosDeviceType(IosDeviceType::Type type, const QString &identifier type(type), identifier(identifier), displayName(displayName) { } -bool IosDeviceType::fromMap(const QVariantMap &map) +bool IosDeviceType::fromMap(const Store &map) { bool validType; displayName = map.value(iosDeviceTypeDisplayNameKey, QVariant()).toString(); @@ -82,9 +83,9 @@ bool IosDeviceType::fromMap(const QVariantMap &map) && (type != IosDeviceType::SimulatedDevice || !identifier.isEmpty()); } -QVariantMap IosDeviceType::toMap() const +Store IosDeviceType::toMap() const { - QVariantMap res; + Store res; res[iosDeviceTypeDisplayNameKey] = displayName; res[iosDeviceTypeTypeKey] = type; res[iosDeviceTypeIdentifierKey] = identifier; diff --git a/src/plugins/ios/iossimulator.h b/src/plugins/ios/iossimulator.h index 988776e5086..db0c81623da 100644 --- a/src/plugins/ios/iossimulator.h +++ b/src/plugins/ios/iossimulator.h @@ -25,8 +25,8 @@ public: IosDeviceType(Type type = IosDevice, const QString &identifier = QString(), const QString &displayName = QString()); - bool fromMap(const QVariantMap &map); - QVariantMap toMap() const; + bool fromMap(const Utils::Store &map); + Utils::Store toMap() const; bool operator ==(const IosDeviceType &o) const; bool operator !=(const IosDeviceType &o) const { return !(*this == o); } diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index 18cc42d44e2..43cc6fab142 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -13,26 +13,14 @@ #include <debugger/debuggerconstants.h> #include <utils/async.h> -#include <utils/filepath.h> #include <utils/futuresynchronizer.h> #include <utils/process.h> #include <utils/qtcassert.h> #include <utils/temporarydirectory.h> #include <QDir> -#include <QFutureWatcher> -#include <QJsonArray> -#include <QJsonDocument> -#include <QJsonObject> -#include <QList> #include <QLoggingCategory> -#include <QPointer> -#include <QProcess> -#include <QProcessEnvironment> -#include <QScopedArrayPointer> -#include <QSocketNotifier> #include <QTemporaryFile> -#include <QTimer> #include <QXmlStreamReader> #include <signal.h> @@ -62,8 +50,8 @@ static QString CONSOLE_PATH_TEMPLATE = QDir::homePath() + class LogTailFiles : public QObject { Q_OBJECT -public: +public: void exec(QPromise<void> &promise, std::shared_ptr<QTemporaryFile> stdoutFile, std::shared_ptr<QTemporaryFile> stderrFile) { @@ -77,27 +65,20 @@ public: watcher.setFuture(promise.future()); // Process to print the console output while app is running. - auto logProcess = [&](QProcess *tailProcess, std::shared_ptr<QTemporaryFile> file) { - QObject::connect(tailProcess, &QProcess::readyReadStandardOutput, &loop, [&, tailProcess] { + auto logProcess = [&](Process *tailProcess, std::shared_ptr<QTemporaryFile> file) { + QObject::connect(tailProcess, &Process::readyReadStandardOutput, &loop, [&, tailProcess] { if (!promise.isCanceled()) - emit logMessage(QString::fromLocal8Bit(tailProcess->readAll())); + emit logMessage(QString::fromLocal8Bit(tailProcess->readAllRawStandardOutput())); }); - tailProcess->start(QStringLiteral("tail"), {"-f", file->fileName()}); + tailProcess->setCommand({FilePath::fromString("tail"), {"-f", file->fileName()}}); + tailProcess->start(); }; - auto processDeleter = [](QProcess *process) { - if (process->state() != QProcess::NotRunning) { - process->terminate(); - process->waitForFinished(); - } - delete process; - }; - - std::unique_ptr<QProcess, void(*)(QProcess *)> tailStdout(new QProcess, processDeleter); + std::unique_ptr<Process> tailStdout(new Process); if (stdoutFile) logProcess(tailStdout.get(), stdoutFile); - std::unique_ptr<QProcess, void(*)(QProcess *)> tailStderr(new QProcess, processDeleter); + std::unique_ptr<Process> tailStderr(new Process); if (stderrFile) logProcess(tailStderr.get(), stderrFile); @@ -106,7 +87,7 @@ public: } signals: - void logMessage(QString message); + void logMessage(const QString &message); }; struct ParserState { @@ -216,7 +197,6 @@ class IosDeviceToolHandlerPrivate final : public IosToolHandlerPrivate }; public: explicit IosDeviceToolHandlerPrivate(const IosDeviceType &devType, IosToolHandler *q); - ~IosDeviceToolHandlerPrivate() override; // IosToolHandlerPrivate overrides public: @@ -231,14 +211,20 @@ public: void stop(int errorCode) override; private: - void subprocessError(QProcess::ProcessError error); - void subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus); void subprocessHasData(); void processXml(); - void killProcess(); - QTimer killTimer; - std::shared_ptr<QProcess> process; + struct Deleter { + void operator()(Process *process) const + { + if (process->state() != QProcess::NotRunning) { + process->write("k\n\r"); + process->closeWriteChannel(); + } + delete process; + }; + }; + std::unique_ptr<Process, Deleter> process; State state = NonStarted; Op op = OpNone; QXmlStreamReader outputParser; @@ -305,7 +291,7 @@ private: private: qint64 m_pid = -1; LogTailFiles outputLogger; - Utils::FutureSynchronizer futureSynchronizer; + FutureSynchronizer futureSynchronizer; }; IosToolHandlerPrivate::IosToolHandlerPrivate(const IosDeviceType &devType, @@ -369,25 +355,6 @@ void IosToolHandlerPrivate::toolExited(int code) emit q->toolExited(q, code); } -void IosDeviceToolHandlerPrivate::subprocessError(QProcess::ProcessError error) -{ - if (state != Stopped) - errorMsg(Tr::tr("iOS tool error %1").arg(error)); - stop(-1); - if (error == QProcess::FailedToStart) { - qCDebug(toolHandlerLog) << "IosToolHandler::finished(" << this << ")"; - emit q->finished(q); - } -} - -void IosDeviceToolHandlerPrivate::subprocessFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - stop((exitStatus == QProcess::NormalExit) ? exitCode : -1 ); - qCDebug(toolHandlerLog) << "IosToolHandler::finished(" << this << ")"; - killTimer.stop(); - emit q->finished(q); -} - void IosDeviceToolHandlerPrivate::processXml() { while (!outputParser.atEnd()) { @@ -475,10 +442,10 @@ void IosDeviceToolHandlerPrivate::processXml() } else if (elName == QLatin1String("server_ports")) { stack.append(ParserState(ParserState::ServerPorts)); QXmlStreamAttributes attributes = outputParser.attributes(); - Utils::Port gdbServerPort( - attributes.value(QLatin1String("gdb_server")).toString().toInt()); - Utils::Port qmlServerPort( - attributes.value(QLatin1String("qml_server")).toString().toInt()); + Port gdbServerPort( + attributes.value(QLatin1String("gdb_server")).toString().toInt()); + Port qmlServerPort( + attributes.value(QLatin1String("qml_server")).toString().toInt()); gotServerPorts(m_bundlePath, m_deviceId, gdbServerPort, qmlServerPort); } else { qCWarning(toolHandlerLog) << "unexpected element " << elName; @@ -570,12 +537,6 @@ void IosDeviceToolHandlerPrivate::processXml() } } -void IosDeviceToolHandlerPrivate::killProcess() -{ - if (isRunning()) - process->kill(); -} - void IosDeviceToolHandlerPrivate::subprocessHasData() { qCDebug(toolHandlerLog) << "subprocessHasData, state:" << state; @@ -588,17 +549,12 @@ void IosDeviceToolHandlerPrivate::subprocessHasData() case StartedInferior: // read some data { - char buf[200]; while (isRunning()) { - qint64 rRead = process->read(buf, sizeof(buf)); - if (rRead == -1) { - stop(-1); + const QByteArray buffer = process->readAllRawStandardOutput(); + if (buffer.isEmpty()) return; - } - if (rRead == 0) - return; - qCDebug(toolHandlerLog) << "subprocessHasData read " << QByteArray(buf, rRead); - outputParser.addData(QByteArray(buf, rRead)); + qCDebug(toolHandlerLog) << "subprocessHasData read " << buffer; + outputParser.addData(buffer); processXml(); } break; @@ -617,27 +573,18 @@ void IosDeviceToolHandlerPrivate::subprocessHasData() IosDeviceToolHandlerPrivate::IosDeviceToolHandlerPrivate(const IosDeviceType &devType, IosToolHandler *q) : IosToolHandlerPrivate(devType, q) + , process(new Process, Deleter()) { - killTimer.setSingleShot(true); - - auto deleter = [](QProcess *p) { - if (p->state() != QProcess::NotRunning) { - p->kill(); - if (!p->waitForFinished(2000)) - p->terminate(); - } - delete p; - }; - process = std::shared_ptr<QProcess>(new QProcess, deleter); - // Prepare & set process Environment. - QProcessEnvironment env(QProcessEnvironment::systemEnvironment()); - const QStringList keys = env.keys(); - for (const QString &k : keys) - if (k.startsWith(QLatin1String("DYLD_"))) - env.remove(k); + const Environment systemEnv = Environment::systemEnvironment(); + Environment env(systemEnv); + systemEnv.forEachEntry([&env](const QString &key, const QString &, bool enabled) { + if (enabled && key.startsWith(QLatin1String("DYLD_"))) + env.unset(key); + }); + QStringList frameworkPaths; - const Utils::FilePath libPath = IosConfigurations::developerPath().pathAppended("Platforms/iPhoneSimulator.platform/Developer/Library"); + const FilePath libPath = IosConfigurations::developerPath().pathAppended("Platforms/iPhoneSimulator.platform/Developer/Library"); for (const auto framework : {"PrivateFrameworks", "OtherFrameworks", "SharedFrameworks"}) { const QString frameworkPath = libPath.pathAppended(QLatin1String(framework)).toFileInfo().canonicalFilePath(); @@ -645,34 +592,27 @@ IosDeviceToolHandlerPrivate::IosDeviceToolHandlerPrivate(const IosDeviceType &de frameworkPaths << frameworkPath; } frameworkPaths << "/System/Library/Frameworks" << "/System/Library/PrivateFrameworks"; - env.insert(QLatin1String("DYLD_FALLBACK_FRAMEWORK_PATH"), frameworkPaths.join(QLatin1Char(':'))); + env.set(QLatin1String("DYLD_FALLBACK_FRAMEWORK_PATH"), frameworkPaths.join(QLatin1Char(':'))); qCDebug(toolHandlerLog) << "IosToolHandler runEnv:" << env.toStringList(); - process->setProcessEnvironment(env); + process->setEnvironment(env); + process->setProcessMode(ProcessMode::Writer); + process->setReaperTimeout(1500); - QObject::connect(process.get(), &QProcess::readyReadStandardOutput, - std::bind(&IosDeviceToolHandlerPrivate::subprocessHasData,this)); - - QObject::connect(process.get(), &QProcess::finished, - std::bind(&IosDeviceToolHandlerPrivate::subprocessFinished,this, _1,_2)); - - QObject::connect(process.get(), &QProcess::errorOccurred, - std::bind(&IosDeviceToolHandlerPrivate::subprocessError, this, _1)); - - QObject::connect(&killTimer, &QTimer::timeout, std::bind(&IosDeviceToolHandlerPrivate::killProcess, this)); -} - -IosDeviceToolHandlerPrivate::~IosDeviceToolHandlerPrivate() -{ - if (isRunning()) { - // Disconnect the signals to avoid notifications while destructing. - // QTCREATORBUG-18147 - process->disconnect(); - // Quit ios-tool gracefully before kill is executed. - process->write("k\n\r"); - process->closeWriteChannel(); - // Give some time to ios-tool to finish. - process->waitForFinished(2000); - } + QObject::connect(process.get(), &Process::readyReadStandardOutput, + q, [this] { subprocessHasData(); }); + QObject::connect(process.get(), &Process::done, q, [this] { + if (process->result() == ProcessResult::FinishedWithSuccess) { + stop((process->exitStatus() == QProcess::NormalExit) ? process->exitCode() : -1); + qCDebug(toolHandlerLog) << "IosToolHandler::finished(" << this << ")"; + } else { + if (state != Stopped) + errorMsg(Tr::tr("iOS tool error %1").arg(process->error())); + stop(-1); + if (process->result() == ProcessResult::StartFailed) + qCDebug(toolHandlerLog) << "IosToolHandler::finished(" << this << ")"; + } + emit IosToolHandlerPrivate::q->finished(IosToolHandlerPrivate::q); + }); } void IosDeviceToolHandlerPrivate::requestTransferApp(const FilePath &bundlePath, @@ -680,7 +620,7 @@ void IosDeviceToolHandlerPrivate::requestTransferApp(const FilePath &bundlePath, { m_bundlePath = bundlePath; m_deviceId = deviceId; - QString tmpDeltaPath = Utils::TemporaryDirectory::masterDirectoryFilePath().pathAppended("ios").toString(); + QString tmpDeltaPath = TemporaryDirectory::masterDirectoryFilePath().pathAppended("ios").toString(); QStringList args; args << QLatin1String("--id") << deviceId << QLatin1String("--bundle") << bundlePath.path() << QLatin1String("--timeout") << QString::number(timeout) @@ -736,11 +676,11 @@ void IosDeviceToolHandlerPrivate::start(const QString &exe, const QStringList &a QTC_CHECK(state == NonStarted); state = Starting; qCDebug(toolHandlerLog) << "running " << exe << args; - process->start(exe, args); + process->setCommand({FilePath::fromString(exe), args}); + process->start(); state = StartedInferior; } - void IosDeviceToolHandlerPrivate::stop(int errorCode) { qCDebug(toolHandlerLog) << "IosToolHandlerPrivate::stop"; @@ -775,7 +715,7 @@ void IosDeviceToolHandlerPrivate::stop(int errorCode) if (isRunning()) { process->write("k\n\r"); process->closeWriteChannel(); - killTimer.start(1500); + process->stop(); } } @@ -787,7 +727,7 @@ IosSimulatorToolHandlerPrivate::IosSimulatorToolHandlerPrivate(const IosDeviceTy : IosToolHandlerPrivate(devType, q) { QObject::connect(&outputLogger, &LogTailFiles::logMessage, - std::bind(&IosToolHandlerPrivate::appOutput, this, _1)); + q, [q](const QString &message) { q->appOutput(q, message); }); } void IosSimulatorToolHandlerPrivate::requestTransferApp(const FilePath &appBundlePath, @@ -806,6 +746,8 @@ void IosSimulatorToolHandlerPrivate::requestTransferApp(const FilePath &appBundl installAppOnSimulator(); } else { errorMsg(Tr::tr("Application install on simulator failed. Simulator not running.")); + if (!response.commandOutput.isEmpty()) + errorMsg(response.commandOutput); didTransferApp(m_bundlePath, m_deviceId, IosToolHandler::Failure); emit q->finished(q); } diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp index 0832a5faf37..900309fc31c 100644 --- a/src/plugins/ios/simulatorcontrol.cpp +++ b/src/plugins/ios/simulatorcontrol.cpp @@ -3,6 +3,7 @@ #include "simulatorcontrol.h" #include "iosconfigurations.h" +#include "iostr.h" #include <utils/algorithm.h> #include <utils/async.h> @@ -399,10 +400,10 @@ void startSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QSt SimulatorInfo simInfo = deviceInfo(simUdid); if (!simInfo.available) { - qCDebug(simulatorLog) << "Simulator device is not available." << simUdid; + promise.addResult( + response.withError(Tr::tr("Simulator device is not available. (%1)").arg(simUdid))); return; } - // Shutting down state checks are for the case when simulator start is called within a short // interval of closing the previous interval of the simulator. We wait untill the shutdown // process is complete. @@ -414,8 +415,14 @@ void startSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QSt } if (simInfo.isShuttingDown()) { - qCDebug(simulatorLog) << "Cannot start Simulator device. " - << "Previous instance taking too long to shutdown." << simInfo; + promise.addResult(response.withError( + Tr::tr("Cannot start Simulator device. Previous instance taking " + "too long to shut down. (name=%1, udid=%2, available=%3, state=%4, runtime=%5)") + .arg(simInfo.name) + .arg(simInfo.identifier) + .arg(simInfo.available) + .arg(simInfo.state) + .arg(simInfo.runtimeName))); return; } @@ -436,11 +443,19 @@ void startSimulator(QPromise<SimulatorControl::ResponseData> &promise, const QSt if (info.isBooted()) response.success = true; } else { - qCDebug(simulatorLog) << "Error starting simulator."; + promise.addResult(response.withError(Tr::tr("Error starting simulator."))); + return; } } else { - qCDebug(simulatorLog) << "Cannot start Simulator device. Simulator not in shutdown state." - << simInfo; + promise.addResult(response.withError( + Tr::tr("Cannot start Simulator device. Simulator not in shutdown state.(name=%1, " + "udid=%2, available=%3, state=%4, runtime=%5)") + .arg(simInfo.name) + .arg(simInfo.identifier) + .arg(simInfo.available) + .arg(simInfo.state) + .arg(simInfo.runtimeName))); + return; } if (!promise.isCanceled()) diff --git a/src/plugins/ios/simulatorcontrol.h b/src/plugins/ios/simulatorcontrol.h index 402100fbeac..c1519b39b32 100644 --- a/src/plugins/ios/simulatorcontrol.h +++ b/src/plugins/ios/simulatorcontrol.h @@ -61,6 +61,14 @@ public: bool success = false; qint64 pID = -1; QString commandOutput; + + ResponseData withError(const QString errorMsg) + { + ResponseData result = *this; + result.commandOutput = errorMsg; + result.success = false; + return result; + } }; public: diff --git a/src/plugins/languageclient/CMakeLists.txt b/src/plugins/languageclient/CMakeLists.txt index 5af221f7f86..3bde89f27e3 100644 --- a/src/plugins/languageclient/CMakeLists.txt +++ b/src/plugins/languageclient/CMakeLists.txt @@ -5,12 +5,12 @@ elseif (MINGW) endif() add_qtc_plugin(LanguageClient - PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version + PUBLIC_DEPENDS LanguageServerProtocol Qt::Core PLUGIN_DEPENDS ProjectExplorer Core TextEditor SOURCES callhierarchy.cpp callhierarchy.h client.cpp client.h - clientrequesttask.cpp clientrequesttask.h + clientrequest.cpp clientrequest.h currentdocumentsymbolsrequest.cpp currentdocumentsymbolsrequest.h diagnosticmanager.cpp diagnosticmanager.h documentsymbolcache.cpp documentsymbolcache.h diff --git a/src/plugins/languageclient/LanguageClient.json.in b/src/plugins/languageclient/LanguageClient.json.in index 8eea5c88cc6..b72f23b154d 100644 --- a/src/plugins/languageclient/LanguageClient.json.in +++ b/src/plugins/languageclient/LanguageClient.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"LanguageClient\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "LanguageClient", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Other Languages\", - \"Description\" : \"Language Server Protocol client component. See https://microsoft.github.io/language-server-protocol/overview for an overview on Language Servers.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Other Languages", + "Description" : "Language Server Protocol client component. See https://microsoft.github.io/language-server-protocol/overview for an overview on Language Servers.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/languageclient/callhierarchy.cpp b/src/plugins/languageclient/callhierarchy.cpp index a05922cbe5e..029ca64d939 100644 --- a/src/plugins/languageclient/callhierarchy.cpp +++ b/src/plugins/languageclient/callhierarchy.cpp @@ -292,10 +292,8 @@ Core::NavigationView CallHierarchyFactory::createWidget() auto button = new QToolButton; button->setIcon(Icons::RELOAD_TOOLBAR.icon()); button->setToolTip(Tr::tr("Reloads the call hierarchy for the symbol under cursor position.")); - connect(button, &QToolButton::clicked, [h](){ - h->updateHierarchyAtCursorPosition(); - }); - return {h,{button}}; + connect(button, &QToolButton::clicked, this, [h] { h->updateHierarchyAtCursorPosition(); }); + return {h, {button}}; } } // namespace LanguageClient diff --git a/src/plugins/languageclient/callhierarchy.h b/src/plugins/languageclient/callhierarchy.h index bbc15b09712..79c92fd3600 100644 --- a/src/plugins/languageclient/callhierarchy.h +++ b/src/plugins/languageclient/callhierarchy.h @@ -1,10 +1,10 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0 -#include <coreplugin/inavigationwidgetfactory.h> - #pragma once +#include <coreplugin/inavigationwidgetfactory.h> + namespace Core { class IDocument; } namespace LanguageClient { @@ -13,8 +13,6 @@ class Client; class CallHierarchyFactory : public Core::INavigationWidgetFactory { - Q_OBJECT - public: CallHierarchyFactory(); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index da8a0cf19db..7583b065988 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -20,8 +20,6 @@ #include "progressmanager.h" #include "semantichighlightsupport.h" -#include <app/app_version.h> - #include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/icore.h> #include <coreplugin/idocument.h> @@ -54,10 +52,12 @@ #include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorsettings.h> +#include <utils/appinfo.h> #include <utils/mimeutils.h> #include <utils/process.h> #include <QDebug> +#include <QGuiApplication> #include <QJsonDocument> #include <QLoggingCategory> #include <QMessageBox> @@ -148,8 +148,8 @@ public: { using namespace ProjectExplorer; - m_clientInfo.setName(Core::Constants::IDE_DISPLAY_NAME); - m_clientInfo.setVersion(Core::Constants::IDE_VERSION_DISPLAY); + m_clientInfo.setName(QGuiApplication::applicationDisplayName()); + m_clientInfo.setVersion(Utils::appInfo().displayVersion); m_clientProviders.completionAssistProvider = new LanguageClientCompletionAssistProvider(q); m_clientProviders.functionHintProvider = new FunctionHintAssistProvider(q); @@ -328,6 +328,7 @@ public: ProjectExplorer::Project *m_project = nullptr; QSet<TextEditor::IAssistProcessor *> m_runningAssistProcessors; SymbolSupport m_symbolSupport; + MessageId m_runningFindLinkRequest; ProgressManager m_progressManager; bool m_activateDocAutomatically = false; SemanticTokenSupport m_tokenSupport; @@ -374,6 +375,14 @@ static ClientCapabilities generateClientCapabilities() { ClientCapabilities capabilities; WorkspaceClientCapabilities workspaceCapabilities; + WorkspaceClientCapabilities::WorkspaceEditCapabilities workspaceEditCapabilities; + workspaceEditCapabilities.setDocumentChanges(true); + using ResourceOperationKind + = WorkspaceClientCapabilities::WorkspaceEditCapabilities::ResourceOperationKind; + workspaceEditCapabilities.setResourceOperations({ResourceOperationKind::Create, + ResourceOperationKind::Rename, + ResourceOperationKind::Delete}); + workspaceCapabilities.setWorkspaceEdit(workspaceEditCapabilities); workspaceCapabilities.setWorkspaceFolders(true); workspaceCapabilities.setApplyEdit(true); DynamicRegistrationCapabilities allowDynamicRegistration; @@ -528,7 +537,7 @@ void Client::initialize() else params.setWorkSpaceFolders(workspaces); InitializeRequest initRequest(params); - initRequest.setResponseCallback([this](const InitializeRequest::Response &initResponse){ + initRequest.setResponseCallback([this](const InitializeRequest::Response &initResponse) { d->initializeCallback(initResponse); }); if (std::optional<ResponseHandler> responseHandler = initRequest.responseHandler()) @@ -565,6 +574,8 @@ QString Client::stateString() const //: language client state case InitializeRequested: return Tr::tr("initialize requested"); //: language client state + case FailedToInitialize: return Tr::tr("failed to initialize"); + //: language client state case Initialized: return Tr::tr("initialized"); //: language client state case ShutdownRequested: return Tr::tr("shutdown requested"); @@ -687,8 +698,30 @@ void Client::openDocument(TextEditor::TextDocument *document) void Client::sendMessage(const JsonRpcMessage &message, SendDocUpdates sendUpdates, Schedule semanticTokensSchedule) { + QScopeGuard guard([responseHandler = message.responseHandler()](){ + if (responseHandler) { + static ResponseError<std::nullptr_t> error; + if (!error.isValid()) { + error.setCode(-32803); // RequestFailed + error.setMessage("The server is currently in an unreachable state."); + } + QJsonObject response; + response[idKey] = responseHandler->id; + response[errorKey] = QJsonObject(error); + responseHandler->callback(JsonRpcMessage(response)); + } + }); + QTC_ASSERT(d->m_clientInterface, return); + if (d->m_state == Shutdown || d->m_state == ShutdownRequested) { + auto key = message.toJsonObject().contains(methodKey) ? QString(methodKey) : QString(idKey); + const QString method = message.toJsonObject()[key].toString(); + qCDebug(LOGLSPCLIENT) << "Ignoring message " << method << " because client is shutting down"; + return; + } QTC_ASSERT(d->m_state == Initialized, return); + guard.dismiss(); + if (sendUpdates == SendDocUpdates::Send) d->sendPostponedDocumentUpdates(semanticTokensSchedule); if (std::optional<ResponseHandler> responseHandler = message.responseHandler()) @@ -847,7 +880,7 @@ void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *w q->cancelRequest(m_highlightRequests.take(widget)); }); request.setResponseCallback( - [widget, this, uri, connection] + [widget, this, uri, connection, adjustedCursor] (const DocumentHighlightsRequest::Response &response) { m_highlightRequests.remove(widget); @@ -873,6 +906,30 @@ void ClientPrivate::requestDocumentHighlightsNow(TextEditor::TextEditorWidget *w selection.cursor.setPosition(end, QTextCursor::KeepAnchor); selections << selection; } + if (!selections.isEmpty()) { + const QList<Text::Range> extraRanges = q->additionalDocumentHighlights( + widget, adjustedCursor); + for (const Text::Range &range : extraRanges) { + QTextEdit::ExtraSelection selection{widget->textCursor(), format}; + const Text::Position &startPos = range.begin; + const Text::Position &endPos = range.end; + const int start = Text::positionInText(document, startPos.line, + startPos.column + 1); + const int end = Text::positionInText(document, endPos.line, + endPos.column + 1); + if (start < 0 || end < 0 || start >= end) + continue; + selection.cursor.setPosition(start); + selection.cursor.setPosition(end, QTextCursor::KeepAnchor); + static const auto cmp = [](const QTextEdit::ExtraSelection &s1, + const QTextEdit::ExtraSelection &s2) { + return s1.cursor.position() < s2.cursor.position(); + }; + const auto it = std::lower_bound(selections.begin(), selections.end(), + selection, cmp); + selections.insert(it, selection); + } + } widget->setExtraSelections(id, selections); }); m_highlightRequests[widget] = request.id(); @@ -911,6 +968,10 @@ void Client::activateEditor(Core::IEditor *editor) optionalActions |= TextEditor::TextEditorActionHandler::FindUsage; if (symbolSupport().supportsRename(widget->textDocument())) optionalActions |= TextEditor::TextEditorActionHandler::RenameSymbol; + if (symbolSupport().supportsFindLink(widget->textDocument(), LinkTarget::SymbolDef)) + optionalActions |= TextEditor::TextEditorActionHandler::FollowSymbolUnderCursor; + if (symbolSupport().supportsFindLink(widget->textDocument(), LinkTarget::SymbolTypeDef)) + optionalActions |= TextEditor::TextEditorActionHandler::FollowTypeUnderCursor; if (CallHierarchyFactory::supportsCallHierarchy(this, textEditor->document())) optionalActions |= TextEditor::TextEditorActionHandler::CallHierarchy; widget->setOptionalActions(optionalActions); @@ -1112,6 +1173,8 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document, { if (!d->m_openedDocument.contains(document) || !reachable()) return; + if (d->m_runningFindLinkRequest.isValid()) + cancelRequest(d->m_runningFindLinkRequest); if (d->m_diagnosticManager) d->m_diagnosticManager->disableDiagnostics(document); const QString method(DidChangeTextDocumentNotification::methodName); @@ -1252,6 +1315,8 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget) { + if (d->m_runningFindLinkRequest.isValid()) + cancelRequest(d->m_runningFindLinkRequest); TextEditor::TextDocument *document = widget->textDocument(); if (d->m_documentsToUpdate.find(document) != d->m_documentsToUpdate.end()) return; // we are currently changing this document so postpone the DocumentHighlightsRequest @@ -1274,6 +1339,25 @@ SymbolSupport &Client::symbolSupport() return d->m_symbolSupport; } +void Client::findLinkAt(TextEditor::TextDocument *document, + const QTextCursor &cursor, + Utils::LinkHandler callback, + const bool resolveTarget, + LinkTarget target) +{ + if (d->m_runningFindLinkRequest.isValid()) + cancelRequest(d->m_runningFindLinkRequest); + d->m_runningFindLinkRequest = symbolSupport().findLinkAt( + document, + cursor, + [this, callback](const Link &link) { + d->m_runningFindLinkRequest = {}; + callback(link); + }, + resolveTarget, + target); +} + void Client::requestCodeActions(const LanguageServerProtocol::DocumentUri &uri, const LanguageServerProtocol::Diagnostic &diagnostic) { @@ -1614,7 +1698,7 @@ bool ClientPrivate::reset() void Client::setError(const QString &message) { log(message); - d->m_state = Error; + d->m_state = d->m_state < Initialized ? FailedToInitialize : Error; } ProgressManager *Client::progressManager() @@ -1941,6 +2025,23 @@ void ClientPrivate::handleMethod(const QString &method, const MessageId &id, con if (ProgressManager::isProgressEndMessage(*params)) emit q->workDone(params->token()); } + } else if (method == ConfigurationRequest::methodName) { + ConfigurationRequest::Response response; + QJsonArray result; + if (QTC_GUARD(id.isValid())) + response.setId(id); + ConfigurationRequest configurationRequest(message.toJsonObject()); + if (std::optional<ConfigurationParams> params = configurationRequest.params()) { + const QList<ConfigurationParams::ConfigurationItem> items = params->items(); + for (const ConfigurationParams::ConfigurationItem &item : items) { + if (const std::optional<QString> section = item.section()) + result.append(m_configuration[*section]); + else + result.append({}); + } + } + response.setResult(result); + sendResponse(response); } else if (isRequest) { Response<JsonObject, JsonObject> response(id); ResponseError<JsonObject> error; @@ -2006,7 +2107,11 @@ void Client::setDocumentChangeUpdateThreshold(int msecs) void ClientPrivate::initializeCallback(const InitializeRequest::Response &initResponse) { - QTC_ASSERT(m_state == Client::InitializeRequested, return); + if (m_state != Client::InitializeRequested) { + qCWarning(LOGLSPCLIENT) << "Dropping initialize response in unexpected state " << m_state; + qCDebug(LOGLSPCLIENT) << initResponse.toJsonObject(); + return; + } if (std::optional<ResponseError<InitializeError>> error = initResponse.error()) { if (std::optional<InitializeError> data = error->data()) { if (data->retry()) { diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 006559760c5..aba2b996754 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -4,6 +4,7 @@ #pragma once #include "languageclient_global.h" +#include "languageclientsymbolsupport.h" #include "languageclientutils.h" #include "semantichighlightsupport.h" @@ -16,6 +17,8 @@ class TextDocument; class TextEditorWidget; } +namespace Utils { namespace Text { class Range; } } + QT_BEGIN_NAMESPACE class QWidget; QT_END_NAMESPACE @@ -43,7 +46,6 @@ class LanguageClientOutlineItem; class LanguageClientQuickFixProvider; class LanguageFilter; class ProgressManager; -class SymbolSupport; class LANGUAGECLIENT_EXPORT Client : public QObject { @@ -75,6 +77,7 @@ public: enum State { Uninitialized, InitializeRequested, + FailedToInitialize, Initialized, ShutdownRequested, Shutdown, @@ -149,6 +152,14 @@ public: void addAssistProcessor(TextEditor::IAssistProcessor *processor); void removeAssistProcessor(TextEditor::IAssistProcessor *processor); SymbolSupport &symbolSupport(); + // In contrast to the findLinkAt of symbol support this find link makes sure that there is only + // one request running at a time and cancels the running request if the document changes, cursor + // moves or another link is requested + void findLinkAt(TextEditor::TextDocument *document, + const QTextCursor &cursor, + Utils::LinkHandler callback, + const bool resolveTarget, + LinkTarget target); DocumentSymbolCache *documentSymbolCache(); HoverHandler *hoverHandler(); QList<LanguageServerProtocol::Diagnostic> diagnosticsAt(const Utils::FilePath &filePath, @@ -219,6 +230,8 @@ private: TextEditor::TextDocument *doc); virtual bool referencesShadowFile(const TextEditor::TextDocument *doc, const Utils::FilePath &candidate); + virtual QList<Utils::Text::Range> additionalDocumentHighlights( + TextEditor::TextEditorWidget *, const QTextCursor &) { return {}; } }; } // namespace LanguageClient diff --git a/src/plugins/languageclient/clientrequesttask.cpp b/src/plugins/languageclient/clientrequest.cpp similarity index 74% rename from src/plugins/languageclient/clientrequesttask.cpp rename to src/plugins/languageclient/clientrequest.cpp index 6da5be5704c..630a1b01940 100644 --- a/src/plugins/languageclient/clientrequesttask.cpp +++ b/src/plugins/languageclient/clientrequest.cpp @@ -1,27 +1,27 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "clientrequesttask.h" +#include "clientrequest.h" using namespace LanguageServerProtocol; namespace LanguageClient { -WorkspaceSymbolRequestTaskAdapter::WorkspaceSymbolRequestTaskAdapter() +ClientWorkspaceSymbolRequestTaskAdapter::ClientWorkspaceSymbolRequestTaskAdapter() { task()->setResponseCallback([this](const WorkspaceSymbolRequest::Response &response){ emit done(response.result().has_value()); }); } -void WorkspaceSymbolRequestTaskAdapter::start() +void ClientWorkspaceSymbolRequestTaskAdapter::start() { task()->start(); } -bool WorkspaceSymbolRequestTask::preStartCheck() +bool ClientWorkspaceSymbolRequest::preStartCheck() { - if (!ClientRequestTask::preStartCheck()) + if (!ClientRequest::preStartCheck()) return false; const std::optional<std::variant<bool, WorkDoneProgressOptions>> capability diff --git a/src/plugins/languageclient/clientrequesttask.h b/src/plugins/languageclient/clientrequest.h similarity index 79% rename from src/plugins/languageclient/clientrequesttask.h rename to src/plugins/languageclient/clientrequest.h index 3e08e1a287f..d1ed3161741 100644 --- a/src/plugins/languageclient/clientrequesttask.h +++ b/src/plugins/languageclient/clientrequest.h @@ -16,10 +16,10 @@ namespace LanguageClient { template <typename Request> -class LANGUAGECLIENT_EXPORT ClientRequestTask +class LANGUAGECLIENT_EXPORT ClientRequest { public: - virtual ~ClientRequestTask() + virtual ~ClientRequest() { if (m_id) m_client->cancelRequest(*m_id); // In order to not to invoke a response callback anymore @@ -60,21 +60,22 @@ private: typename Request::Response m_response; }; -class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTask - : public ClientRequestTask<LanguageServerProtocol::WorkspaceSymbolRequest> +class LANGUAGECLIENT_EXPORT ClientWorkspaceSymbolRequest + : public ClientRequest<LanguageServerProtocol::WorkspaceSymbolRequest> { public: bool preStartCheck() override; }; -class LANGUAGECLIENT_EXPORT WorkspaceSymbolRequestTaskAdapter - : public Tasking::TaskAdapter<WorkspaceSymbolRequestTask> +class LANGUAGECLIENT_EXPORT ClientWorkspaceSymbolRequestTaskAdapter + : public Tasking::TaskAdapter<ClientWorkspaceSymbolRequest> { public: - WorkspaceSymbolRequestTaskAdapter(); + ClientWorkspaceSymbolRequestTaskAdapter(); void start() final; }; -} // namespace LanguageClient +using ClientWorkspaceSymbolRequestTask + = Tasking::CustomTask<ClientWorkspaceSymbolRequestTaskAdapter>; -TASKING_DECLARE_TASK(SymbolRequest, LanguageClient::WorkspaceSymbolRequestTaskAdapter); +} // namespace LanguageClient diff --git a/src/plugins/languageclient/currentdocumentsymbolsrequest.h b/src/plugins/languageclient/currentdocumentsymbolsrequest.h index 0b9c11a2215..ef124154dfd 100644 --- a/src/plugins/languageclient/currentdocumentsymbolsrequest.h +++ b/src/plugins/languageclient/currentdocumentsymbolsrequest.h @@ -47,7 +47,7 @@ public: void start() final; }; -} // namespace LanguageClient +using CurrentDocumentSymbolsRequestTask + = Tasking::CustomTask<CurrentDocumentSymbolsRequestTaskAdapter>; -TASKING_DECLARE_TASK(CurrentDocumentSymbolsRequestTask, - LanguageClient::CurrentDocumentSymbolsRequestTaskAdapter); +} // namespace LanguageClient diff --git a/src/plugins/languageclient/diagnosticmanager.cpp b/src/plugins/languageclient/diagnosticmanager.cpp index 2e8e2d87396..dd58407f9e0 100644 --- a/src/plugins/languageclient/diagnosticmanager.cpp +++ b/src/plugins/languageclient/diagnosticmanager.cpp @@ -120,7 +120,7 @@ TextEditor::TextMark *DiagnosticManager::createTextMark(TextDocument *doc, const Diagnostic &diagnostic, bool /*isProjectFile*/) const { - static const auto icon = QIcon::fromTheme("edit-copy", Utils::Icons::COPY.icon()); + static const QIcon icon = Icon::fromTheme("edit-copy"); static const QString tooltip = Tr::tr("Copy to Clipboard"); auto mark = new TextMark(doc, diagnostic, m_client); mark->setActionsProvider([text = diagnostic.message()] { diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs index 286f319a3cc..5bb5e1ac11e 100644 --- a/src/plugins/languageclient/languageclient.qbs +++ b/src/plugins/languageclient/languageclient.qbs @@ -5,10 +5,7 @@ QtcPlugin { name: "LanguageClient" Depends { name: "Qt.core" } - Depends { - name: "Qt.testlib" - condition: qtc.testsEnabled - } + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } Depends { name: "Utils" } Depends { name: "ProjectExplorer" } @@ -17,15 +14,13 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "TextEditor" } - Depends { name: "app_version_header" } - files: [ "callhierarchy.cpp", "callhierarchy.h", "client.cpp", "client.h", - "clientrequesttask.cpp", - "clientrequesttask.h", + "clientrequest.cpp", + "clientrequest.h", "currentdocumentsymbolsrequest.cpp", "currentdocumentsymbolsrequest.h", "diagnosticmanager.cpp", @@ -72,5 +67,10 @@ QtcPlugin { "snippet.h", ] + Properties { + condition: qbs.toolchain.contains("mingw") + cpp.cxxFlags: "-Wa,-mbig-obj" + } + Export { Depends { name: "LanguageServerProtocol" } } } diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp index 62daa1457c4..96aebde7850 100644 --- a/src/plugins/languageclient/languageclientcompletionassist.cpp +++ b/src/plugins/languageclient/languageclientcompletionassist.cpp @@ -225,13 +225,6 @@ public: bool supportsPrefixExpansion() const override { return false; } QList<AssistProposalItemInterface *> items() const { return m_currentItems; } - - bool isComplete(const QString prefix) - { return m_completePrefix && prefix.startsWith(*m_completePrefix); } - void setCompletePrefix(const QString &completePrefix) { m_completePrefix = completePrefix; } - -private: - std::optional<QString> m_completePrefix; }; bool LanguageClientCompletionModel::isSortable(const QString &) const @@ -278,13 +271,6 @@ public: } } - bool isComplete(const AssistInterface *interface) - { - const QString prefix = interface->textAt(basePosition(), - interface->position() - basePosition()); - return static_cast<LanguageClientCompletionModel *>(model().data())->isComplete(prefix); - } - void setProposal(IAssistProposal *proposal, const QString &prefix) { if (!proposal) { @@ -306,7 +292,7 @@ public: void updateProposal(std::unique_ptr<AssistInterface> &&interface) override { deleteCurrentProcessor(); - if (!m_provider || isComplete(interface.get())) { + if (!m_provider) { GenericProposalWidget::updateProposal(std::move(interface)); return; } @@ -513,12 +499,9 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( m_pos - m_basePos); QList<CompletionItem> items; - bool isComplete = true; if (std::holds_alternative<CompletionList>(*result)) { const auto &list = std::get<CompletionList>(*result); items = list.items().value_or(QList<CompletionItem>()); - if (list.isIncomplete()) - isComplete = false; } else if (std::holds_alternative<QList<CompletionItem>>(*result)) { items = std::get<QList<CompletionItem>>(*result); } @@ -531,8 +514,6 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( } auto model = new LanguageClientCompletionModel(); model->loadContent(proposalItems); - if (isComplete) - model->setCompletePrefix(prefix); LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_provider, m_basePos, model); diff --git a/src/plugins/languageclient/languageclientformatter.cpp b/src/plugins/languageclient/languageclientformatter.cpp index e0dc144dc00..c3790e42cb5 100644 --- a/src/plugins/languageclient/languageclientformatter.cpp +++ b/src/plugins/languageclient/languageclientformatter.cpp @@ -93,7 +93,7 @@ QFutureWatcher<ChangeSet> *LanguageClientFormatter::format( m_ignoreCancel = true; m_progress.reportStarted(); auto watcher = new QFutureWatcher<ChangeSet>(); - QObject::connect(watcher, &QFutureWatcher<Text::Replacements>::canceled, [this]() { + QObject::connect(watcher, &QFutureWatcher<ChangeSet>::canceled, [this]() { cancelCurrentRequest(); }); watcher->setFuture(m_progress.future()); diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 3129bccf30c..bb655e2d8f8 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -116,6 +116,13 @@ void LanguageClient::LanguageClientManager::addClient(Client *client) for (QList<Client *> &clients : managerInstance->m_clientsForSetting) QTC_CHECK(clients.removeAll(client) == 0); }); + + ProjectExplorer::Project *project = client->project(); + if (!project) + project = ProjectExplorer::ProjectManager::startupProject(); + if (project) + client->updateConfiguration(ProjectSettings(project).workspaceConfiguration()); + emit managerInstance->clientAdded(client); } @@ -161,11 +168,12 @@ void LanguageClientManager::clientFinished(Client *client) const bool unexpectedFinish = client->state() != Client::Shutdown && client->state() != Client::ShutdownRequested; + const QList<TextEditor::TextDocument *> &clientDocs + = managerInstance->m_clientForDocument.keys(client); if (unexpectedFinish) { if (!PluginManager::isShuttingDown()) { - const QList<TextEditor::TextDocument *> &clientDocs - = managerInstance->m_clientForDocument.keys(client); - if (client->reset()) { + const bool shouldRestart = client->state() > Client::FailedToInitialize; + if (shouldRestart && client->reset()) { qCDebug(Log) << "restart unexpectedly finished client: " << client->name() << client; client->log( Tr::tr("Unexpectedly finished. Restarting in %1 seconds.").arg(restartTimeoutS)); @@ -179,10 +187,14 @@ void LanguageClientManager::clientFinished(Client *client) } qCDebug(Log) << "client finished unexpectedly: " << client->name() << client; client->log(Tr::tr("Unexpectedly finished.")); - for (TextEditor::TextDocument *document : clientDocs) - managerInstance->m_clientForDocument.remove(document); } } + + if (unexpectedFinish || !QTC_GUARD(clientDocs.isEmpty())) { + for (TextEditor::TextDocument *document : clientDocs) + openDocumentWithClient(document, nullptr); + } + deleteClient(client); if (isShutdownFinished()) emit managerInstance->shutdownFinished(); @@ -390,6 +402,16 @@ const BaseSettings *LanguageClientManager::settingForClient(Client *client) return nullptr; } +void LanguageClientManager::updateWorkspaceConfiguration(const ProjectExplorer::Project *project, + const QJsonValue &json) +{ + for (Client *client : managerInstance->m_clients) { + ProjectExplorer::Project *clientProject = client->project(); + if (!clientProject || clientProject == project) + client->updateConfiguration(json); + } +} + Client *LanguageClientManager::clientForDocument(TextEditor::TextDocument *document) { QTC_ASSERT(managerInstance, return nullptr); @@ -462,8 +484,24 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor) connect(widget, &TextEditorWidget::requestLinkAt, this, [document = textEditor->textDocument()] (const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) { - if (auto client = clientForDocument(document)) - client->symbolSupport().findLinkAt(document, cursor, callback, resolveTarget); + if (auto client = clientForDocument(document)) { + client->findLinkAt(document, + cursor, + callback, + resolveTarget, + LinkTarget::SymbolDef); + } + }); + connect(widget, &TextEditorWidget::requestTypeAt, this, + [document = textEditor->textDocument()] + (const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget) { + if (auto client = clientForDocument(document)) { + client->findLinkAt(document, + cursor, + callback, + resolveTarget, + LinkTarget::SymbolTypeDef); + } }); connect(widget, &TextEditorWidget::requestUsages, this, [document = textEditor->textDocument()](const QTextCursor &cursor) { @@ -590,7 +628,7 @@ void LanguageClientManager::trackClientDeletion(Client *client) { QTC_ASSERT(!m_scheduledForDeletion.contains(client->id()), return); m_scheduledForDeletion.insert(client->id()); - connect(client, &QObject::destroyed, [this, id = client->id()](){ + connect(client, &QObject::destroyed, this, [this, id = client->id()] { m_scheduledForDeletion.remove(id); if (isShutdownFinished()) emit shutdownFinished(); diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 301cb4c5264..e47548b8741 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -60,6 +60,9 @@ public: static void enableClientSettings(const QString &settingsId, bool enable = true); static QList<Client *> clientsForSetting(const BaseSettings *setting); static const BaseSettings *settingForClient(Client *setting); + static void updateWorkspaceConfiguration(const ProjectExplorer::Project *project, + const QJsonValue &json); + static Client *clientForDocument(TextEditor::TextDocument *document); static Client *clientForFilePath(const Utils::FilePath &filePath); static const QList<Client *> clientsForProject(const ProjectExplorer::Project *project); diff --git a/src/plugins/languageclient/languageclientplugin.cpp b/src/plugins/languageclient/languageclientplugin.cpp index b6b411f8cb4..235ea9b5593 100644 --- a/src/plugins/languageclient/languageclientplugin.cpp +++ b/src/plugins/languageclient/languageclientplugin.cpp @@ -5,11 +5,14 @@ #include "client.h" #include "languageclientmanager.h" +#include "languageclientsettings.h" #include "languageclienttr.h" #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> +#include <projectexplorer/projectpanelfactory.h> + #include <QAction> #include <QMenu> @@ -37,6 +40,13 @@ void LanguageClientPlugin::initialize() { using namespace Core; + auto panelFactory = new ProjectExplorer::ProjectPanelFactory; + panelFactory->setPriority(35); + panelFactory->setDisplayName(Tr::tr("Language Server")); + panelFactory->setCreateWidgetFunction( + [](ProjectExplorer::Project *project) { return new ProjectSettingsWidget(project); }); + ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + LanguageClientManager::init(); LanguageClientSettings::registerClientType({Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID, Tr::tr("Generic StdIO Language Server"), diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 349a84f7b88..7b6c4a41bfe 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -36,6 +36,7 @@ #include <QDialogButtonBox> #include <QDir> #include <QFileInfo> +#include <QGroupBox> #include <QHeaderView> #include <QJsonDocument> #include <QLabel> @@ -43,7 +44,6 @@ #include <QMenu> #include <QMimeData> #include <QPushButton> -#include <QSettings> #include <QSortFilterProxyModel> #include <QStringListModel> #include <QToolButton> @@ -66,6 +66,8 @@ constexpr char typedClientsKey[] = "typedClients"; constexpr char outlineSortedKey[] = "outlineSorted"; constexpr char mimeType[] = "application/language.client.setting"; +using namespace Utils; + namespace LanguageClient { class LanguageClientSettingsModel : public QAbstractListModel @@ -561,9 +563,9 @@ Client *BaseSettings::createClient(BaseClientInterface *interface) const return new Client(interface); } -QVariantMap BaseSettings::toMap() const +Store BaseSettings::toMap() const { - QVariantMap map; + Store map; map.insert(typeIdKey, m_settingsTypeId.toSetting()); map.insert(nameKey, m_name); map.insert(idKey, m_id); @@ -576,7 +578,7 @@ QVariantMap BaseSettings::toMap() const return map; } -void BaseSettings::fromMap(const QVariantMap &map) +void BaseSettings::fromMap(const Store &map) { m_name = map[nameKey].toString(); m_id = map.value(idKey, QUuid::createUuid().toString()).toString(); @@ -602,16 +604,16 @@ void LanguageClientSettings::init() LanguageClientManager::applySettings(); } -QList<BaseSettings *> LanguageClientSettings::fromSettings(QSettings *settingsIn) +QList<BaseSettings *> LanguageClientSettings::fromSettings(QtcSettings *settingsIn) { settingsIn->beginGroup(settingsGroupKey); QList<BaseSettings *> result; - for (auto varList : + for (const QVariantList &varList : {settingsIn->value(clientsKey).toList(), settingsIn->value(typedClientsKey).toList()}) { for (const QVariant &var : varList) { - const QMap<QString, QVariant> &map = var.toMap(); - Utils::Id typeId = Utils::Id::fromSetting(map.value(typeIdKey)); + const Store map = storeFromVariant(var); + Id typeId = Id::fromSetting(map.value(typeIdKey)); if (!typeId.isValid()) typeId = Constants::LANGUAGECLIENT_STDIO_SETTINGS_ID; if (BaseSettings *settings = generateSettings(typeId)) { @@ -651,13 +653,13 @@ void LanguageClientSettings::enableSettings(const QString &id, bool enable) settingsPage().enableSettings(id, enable); } -void LanguageClientSettings::toSettings(QSettings *settings, +void LanguageClientSettings::toSettings(QtcSettings *settings, const QList<BaseSettings *> &languageClientSettings) { settings->beginGroup(settingsGroupKey); auto transform = [](const QList<BaseSettings *> &settings) { return Utils::transform(settings, [](const BaseSettings *setting) { - return QVariant(setting->toMap()); + return variantFromStore(setting->toMap()); }); }; auto isStdioSetting = Utils::equal(&BaseSettings::m_settingsTypeId, @@ -712,15 +714,15 @@ bool StdIOSettings::isValid() const return BaseSettings::isValid() && !m_executable.isEmpty(); } -QVariantMap StdIOSettings::toMap() const +Store StdIOSettings::toMap() const { - QVariantMap map = BaseSettings::toMap(); + Store map = BaseSettings::toMap(); map.insert(executableKey, m_executable.toSettings()); map.insert(argumentsKey, m_arguments); return map; } -void StdIOSettings::fromMap(const QVariantMap &map) +void StdIOSettings::fromMap(const Store &map) { BaseSettings::fromMap(map); m_executable = Utils::FilePath::fromSettings(map[executableKey]); @@ -1028,12 +1030,18 @@ TextEditor::BaseTextEditor *jsonEditor() { using namespace TextEditor; using namespace Utils::Text; - BaseTextEditor *editor = PlainTextEditorFactory::createPlainTextEditor(); - TextDocument *document = editor->textDocument(); - TextEditorWidget *widget = editor->editorWidget(); + BaseTextEditor *textEditor = nullptr; + for (Core::IEditorFactory *factory : Core::IEditorFactory::preferredEditorFactories("foo.json")) { + Core::IEditor *editor = factory->createEditor(); + if (textEditor = qobject_cast<BaseTextEditor *>(editor); textEditor) + break; + delete editor; + } + QTC_ASSERT(textEditor, textEditor = PlainTextEditorFactory::createPlainTextEditor()); + TextDocument *document = textEditor->textDocument(); + TextEditorWidget *widget = textEditor->editorWidget(); widget->configureGenericHighlighter(Utils::mimeTypeForName("application/json")); widget->setLineNumbersVisible(false); - widget->setMarksVisible(false); widget->setRevisionsVisible(false); widget->setCodeFoldingSupported(false); QObject::connect(document, &TextDocument::contentsChanged, widget, [document](){ @@ -1061,7 +1069,66 @@ TextEditor::BaseTextEditor *jsonEditor() mark->setIcon(Utils::Icons::CODEMODEL_ERROR.icon()); document->addMark(mark); }); - return editor; + return textEditor; +} + +constexpr const char projectSettingsId[] = "LanguageClient.ProjectSettings"; + +ProjectSettings::ProjectSettings(ProjectExplorer::Project *project) + : m_project(project) +{ + m_json = m_project->namedSettings(projectSettingsId).toByteArray(); +} + +QJsonValue ProjectSettings::workspaceConfiguration() const +{ + const auto doc = QJsonDocument::fromJson(m_json); + if (doc.isObject()) + return doc.object(); + if (doc.isArray()) + return doc.array(); + return {}; +} + +QByteArray ProjectSettings::json() const +{ + return m_json; +} + +void ProjectSettings::setJson(const QByteArray &json) +{ + const QJsonValue oldConfig = workspaceConfiguration(); + m_json = json; + m_project->setNamedSettings(projectSettingsId, m_json); + const QJsonValue newConfig = workspaceConfiguration(); + if (oldConfig != newConfig) + LanguageClientManager::updateWorkspaceConfiguration(m_project, newConfig); +} + +ProjectSettingsWidget::ProjectSettingsWidget(ProjectExplorer::Project *project) + : m_settings(project) +{ + setUseGlobalSettingsCheckBoxVisible(false); + setGlobalSettingsId(Constants::LANGUAGECLIENT_SETTINGS_PAGE); + setExpanding(true); + + TextEditor::BaseTextEditor *editor = jsonEditor(); + editor->document()->setContents(m_settings.json()); + + auto layout = new QVBoxLayout; + setLayout(layout); + auto group = new QGroupBox(Tr::tr("Workspace Configuration")); + group->setLayout(new QVBoxLayout); + group->layout()->addWidget(new QLabel(Tr::tr( + "Additional JSON configuration sent to all running language servers for this project.\n" + "See the documentation of the specific language server for valid settings."))); + group->layout()->addWidget(editor->widget()); + layout->addWidget(group); + + connect(editor->editorWidget()->textDocument(), + &TextEditor::TextDocument::contentsChanged, + this, + [=]() { m_settings.setJson(editor->document()->contents()); }); } } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h index bb8ebe631f9..7c33fedad1a 100644 --- a/src/plugins/languageclient/languageclientsettings.h +++ b/src/plugins/languageclient/languageclientsettings.h @@ -7,6 +7,8 @@ #include <coreplugin/dialogs/ioptionspage.h> +#include <projectexplorer/projectsettingswidget.h> + #include <QAbstractItemModel> #include <QCoreApplication> #include <QJsonObject> @@ -79,8 +81,8 @@ public: virtual bool isValid() const; Client *createClient() const; Client *createClient(ProjectExplorer::Project *project) const; - virtual QVariantMap toMap() const; - virtual void fromMap(const QVariantMap &map); + virtual Utils::Store toMap() const; + virtual void fromMap(const Utils::Store &map); protected: virtual BaseClientInterface *createInterface(ProjectExplorer::Project *) const; @@ -108,8 +110,8 @@ public: QWidget *createSettingsWidget(QWidget *parent = nullptr) const override; BaseSettings *copy() const override { return new StdIOSettings(*this); } bool isValid() const override; - QVariantMap toMap() const override; - void fromMap(const QVariantMap &map) override; + Utils::Store toMap() const override; + void fromMap(const Utils::Store &map) override; QString arguments() const; Utils::CommandLine command() const; @@ -133,7 +135,7 @@ class LANGUAGECLIENT_EXPORT LanguageClientSettings { public: static void init(); - static QList<BaseSettings *> fromSettings(QSettings *settings); + static QList<BaseSettings *> fromSettings(Utils::QtcSettings *settings); static QList<BaseSettings *> pageSettings(); static QList<BaseSettings *> changedSettings(); @@ -144,7 +146,7 @@ public: static void registerClientType(const ClientType &type); static void addSettings(BaseSettings *settings); static void enableSettings(const QString &id, bool enable = true); - static void toSettings(QSettings *settings, const QList<BaseSettings *> &languageClientSettings); + static void toSettings(Utils::QtcSettings *settings, const QList<BaseSettings *> &languageClientSettings); static bool outlineComboBoxIsSorted(); static void setOutlineComboBoxSorted(bool sorted); @@ -191,6 +193,30 @@ private: QLineEdit *m_arguments = nullptr; }; +class ProjectSettings +{ +public: + explicit ProjectSettings(ProjectExplorer::Project *project); + + QJsonValue workspaceConfiguration() const; + + QByteArray json() const; + void setJson(const QByteArray &json); + +private: + ProjectExplorer::Project *m_project = nullptr; + QByteArray m_json; +}; + +class ProjectSettingsWidget : public ProjectExplorer::ProjectSettingsWidget +{ +public: + explicit ProjectSettingsWidget(ProjectExplorer::Project *project); + +private: + ProjectSettings m_settings; +}; + LANGUAGECLIENT_EXPORT TextEditor::BaseTextEditor *jsonEditor(); } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientsymbolsupport.cpp b/src/plugins/languageclient/languageclientsymbolsupport.cpp index 89cc9f44e2b..2631a853996 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.cpp +++ b/src/plugins/languageclient/languageclientsymbolsupport.cpp @@ -14,6 +14,8 @@ #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <projectexplorer/projecttree.h> #include <utils/algorithm.h> #include <utils/mimeutils.h> @@ -76,16 +78,17 @@ SymbolSupport::SymbolSupport(Client *client) : m_client(client) {} -template<typename Request> -static void sendTextDocumentPositionParamsRequest(Client *client, - const Request &request, - const DynamicCapabilities &dynamicCapabilities, - const ServerCapabilities &serverCapability) +template<typename Request, typename R> +static MessageId sendTextDocumentPositionParamsRequest(Client *client, + const Request &request, + R ServerCapabilities::*member) { if (!request.isValid(nullptr)) - return; + return {}; const DocumentUri uri = request.params().value().textDocument().uri(); const bool supportedFile = client->isSupportedUri(uri); + const DynamicCapabilities dynamicCapabilities = client->dynamicCapabilities(); + const ServerCapabilities serverCapability = client->capabilities(); bool sendMessage = dynamicCapabilities.isRegistered(Request::methodName).value_or(false); if (sendMessage) { const TextDocumentRegistrationOptions option( @@ -96,20 +99,23 @@ static void sendTextDocumentPositionParamsRequest(Client *client, else sendMessage = supportedFile; } else { - const std::optional<std::variant<bool, WorkDoneProgressOptions>> &provider - = serverCapability.referencesProvider(); + const auto provider = std::mem_fn(member)(serverCapability); sendMessage = provider.has_value(); if (sendMessage && std::holds_alternative<bool>(*provider)) sendMessage = std::get<bool>(*provider); } - if (sendMessage) + if (sendMessage) { client->sendMessage(request); + return request.id(); + } + return {}; } -static void handleGotoDefinitionResponse(const GotoDefinitionRequest::Response &response, - Utils::LinkHandler callback, - std::optional<Utils::Link> linkUnderCursor, - const Client *client) +template<typename Request> +static void handleGotoResponse(const typename Request::Response &response, + Utils::LinkHandler callback, + std::optional<Utils::Link> linkUnderCursor, + const Client *client) { if (std::optional<GotoResult> result = response.result()) { if (std::holds_alternative<std::nullptr_t>(*result)) { @@ -137,14 +143,70 @@ static TextDocumentPositionParams generateDocPosParams(TextEditor::TextDocument return TextDocumentPositionParams(documentId, pos); } -void SymbolSupport::findLinkAt(TextEditor::TextDocument *document, - const QTextCursor &cursor, - Utils::LinkHandler callback, - const bool resolveTarget) +template<typename Request, typename R> +static MessageId sendGotoRequest(TextEditor::TextDocument *document, + const QTextCursor &cursor, + Utils::LinkHandler callback, + Client *client, + std::optional<Utils::Link> linkUnderCursor, + R ServerCapabilities::*member) +{ + Request request(generateDocPosParams(document, cursor, client)); + request.setResponseCallback([callback, linkUnderCursor, client]( + const typename Request::Response &response) { + handleGotoResponse<Request>(response, callback, linkUnderCursor, client); + }); + return sendTextDocumentPositionParamsRequest(client, request, member); + return request.id(); +} + +bool SymbolSupport::supportsFindLink(TextEditor::TextDocument *document, LinkTarget target) const +{ + const DocumentUri uri = m_client->hostPathToServerUri(document->filePath()); + const DynamicCapabilities dynamicCapabilities = m_client->dynamicCapabilities(); + const ServerCapabilities serverCapability = m_client->capabilities(); + QString methodName; + std::optional<std::variant<bool, ServerCapabilities::RegistrationOptions>> provider; + switch (target) { + case LinkTarget::SymbolDef: + methodName = GotoDefinitionRequest::methodName; + provider = serverCapability.definitionProvider(); + break; + case LinkTarget::SymbolTypeDef: + methodName = GotoTypeDefinitionRequest::methodName; + provider = serverCapability.typeDefinitionProvider(); + break; + case LinkTarget::SymbolImplementation: + methodName = GotoImplementationRequest::methodName; + provider = serverCapability.implementationProvider(); + break; + } + if (methodName.isEmpty()) + return false; + bool supported = dynamicCapabilities.isRegistered(methodName).value_or(false); + if (supported) { + const TextDocumentRegistrationOptions option(dynamicCapabilities.option(methodName)); + if (option.isValid()) + supported = option.filterApplies( + Utils::FilePath::fromString(QUrl(uri).adjusted(QUrl::PreferLocalFile).toString())); + else + supported = m_client->isSupportedUri(uri); + } else { + supported = provider.has_value(); + if (supported && std::holds_alternative<bool>(*provider)) + supported = std::get<bool>(*provider); + } + return supported; +} + +MessageId SymbolSupport::findLinkAt(TextEditor::TextDocument *document, + const QTextCursor &cursor, + Utils::LinkHandler callback, + const bool resolveTarget, + const LinkTarget target) { if (!m_client->reachable()) - return; - GotoDefinitionRequest request(generateDocPosParams(document, cursor, m_client)); + return {}; std::optional<Utils::Link> linkUnderCursor; if (!resolveTarget) { QTextCursor linkCursor = cursor; @@ -156,15 +218,32 @@ void SymbolSupport::findLinkAt(TextEditor::TextDocument *document, link.linkTextEnd = linkCursor.selectionEnd(); linkUnderCursor = link; } - request.setResponseCallback([callback, linkUnderCursor, client = m_client]( - const GotoDefinitionRequest::Response &response) { - handleGotoDefinitionResponse(response, callback, linkUnderCursor, client); - }); - sendTextDocumentPositionParamsRequest(m_client, - request, - m_client->dynamicCapabilities(), - m_client->capabilities()); + const TextDocumentPositionParams params = generateDocPosParams(document, cursor, m_client); + switch (target) { + case LinkTarget::SymbolDef: + return sendGotoRequest<GotoDefinitionRequest>(document, + cursor, + callback, + m_client, + linkUnderCursor, + &ServerCapabilities::definitionProvider); + case LinkTarget::SymbolTypeDef: + return sendGotoRequest<GotoTypeDefinitionRequest>(document, + cursor, + callback, + m_client, + linkUnderCursor, + &ServerCapabilities::typeDefinitionProvider); + case LinkTarget::SymbolImplementation: + return sendGotoRequest<GotoImplementationRequest>(document, + cursor, + callback, + m_client, + linkUnderCursor, + &ServerCapabilities::implementationProvider); + } + return {}; } bool SymbolSupport::supportsFindUsages(TextEditor::TextDocument *document) const @@ -237,10 +316,10 @@ Utils::SearchResultItems generateSearchResultItems( item.setFilePath(filePath); item.setUseTextEditorFont(true); if (renaming && limitToProjects) { - const bool fileBelongsToProject = ProjectExplorer::ProjectManager::projectForFile( - filePath); - item.setSelectForReplacement(fileBelongsToProject); - if (fileBelongsToProject + const ProjectExplorer::Node * const node + = ProjectExplorer::ProjectTree::nodeForFile(filePath); + item.setSelectForReplacement(node && !node->isGenerated()); + if (node && filePath.baseName().compare(oldSymbolName, Qt::CaseInsensitive) == 0) { fileRenameCandidates << filePath; } @@ -316,10 +395,7 @@ std::optional<MessageId> SymbolSupport::findUsages(TextEditor::TextDocument *doc handleFindReferencesResponse(response, wordUnderCursor, handler); }); - sendTextDocumentPositionParamsRequest(m_client, - request, - m_client->dynamicCapabilities(), - m_client->capabilities()); + sendTextDocumentPositionParamsRequest(m_client, request, &ServerCapabilities::referencesProvider); return request.id(); } @@ -452,40 +528,69 @@ void SymbolSupport::requestPrepareRename(TextEditor::TextDocument *document, void SymbolSupport::requestRename(const TextDocumentPositionParams &positionParams, Core::SearchResult *search) { + if (m_renameRequestIds[search].isValid()) + m_client->cancelRequest(m_renameRequestIds[search]); RenameParams params(positionParams); params.setNewName(search->textToReplace()); RenameRequest request(params); request.setResponseCallback([this, search](const RenameRequest::Response &response) { handleRenameResponse(search, response); }); + m_renameRequestIds[search] = request.id(); m_client->sendMessage(request); if (search->isInteractive()) search->popup(); } Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits, - Core::SearchResult *search, - bool limitToProjects, - const DocumentUri::PathMapper &pathMapper) + Core::SearchResult *search, + bool limitToProjects, + const DocumentUri::PathMapper &pathMapper) { + Utils::SearchResultItems items; auto convertEdits = [](const QList<TextEdit> &edits) { return Utils::transform(edits, [](const TextEdit &edit) { return ItemData{SymbolSupport::convertRange(edit.range()), QVariant(edit)}; }); }; QMap<Utils::FilePath, QList<ItemData>> rangesInDocument; - auto documentChanges = edits.documentChanges().value_or(QList<TextDocumentEdit>()); + auto documentChanges = edits.documentChanges().value_or(QList<DocumentChange>()); if (!documentChanges.isEmpty()) { - for (const TextDocumentEdit &documentChange : std::as_const(documentChanges)) { - rangesInDocument[documentChange.textDocument().uri().toFilePath(pathMapper)] - = convertEdits(documentChange.edits()); + for (const DocumentChange &documentChange : std::as_const(documentChanges)) { + if (std::holds_alternative<TextDocumentEdit>(documentChange)) { + const TextDocumentEdit edit = std::get<TextDocumentEdit>(documentChange); + rangesInDocument[edit.textDocument().uri().toFilePath(pathMapper)] = convertEdits( + edit.edits()); + } else { + Utils::SearchResultItem item; + + if (std::holds_alternative<CreateFileOperation>(documentChange)) { + auto op = std::get<CreateFileOperation>(documentChange); + item.setLineText(op.message(pathMapper)); + item.setFilePath(op.uri().toFilePath(pathMapper)); + item.setUserData(QVariant(op)); + } else if (std::holds_alternative<RenameFileOperation>(documentChange)) { + auto op = std::get<RenameFileOperation>(documentChange); + item.setLineText(op.message(pathMapper)); + item.setFilePath(op.oldUri().toFilePath(pathMapper)); + item.setUserData(QVariant(op)); + } else if (std::holds_alternative<DeleteFileOperation>(documentChange)) { + auto op = std::get<DeleteFileOperation>(documentChange); + item.setLineText(op.message(pathMapper)); + item.setFilePath(op.uri().toFilePath(pathMapper)); + item.setUserData(QVariant(op)); + } + + items << item; + } } } else { auto changes = edits.changes().value_or(WorkspaceEdit::Changes()); for (auto it = changes.begin(), end = changes.end(); it != end; ++it) rangesInDocument[it.key().toFilePath(pathMapper)] = convertEdits(it.value()); } - return generateSearchResultItems(rangesInDocument, search, limitToProjects); + items += generateSearchResultItems(rangesInDocument, search, limitToProjects); + return items; } Core::SearchResult *SymbolSupport::createSearch(const TextDocumentPositionParams &positionParams, @@ -546,6 +651,7 @@ void SymbolSupport::startRenameSymbol(const TextDocumentPositionParams &position void SymbolSupport::handleRenameResponse(Core::SearchResult *search, const RenameRequest::Response &response) { + m_renameRequestIds.remove(search); const std::optional<PrepareRenameRequest::Response::Error> &error = response.error(); QString errorMessage; if (error.has_value()) { @@ -558,11 +664,22 @@ void SymbolSupport::handleRenameResponse(Core::SearchResult *search, const std::optional<WorkspaceEdit> &edits = response.result(); if (edits.has_value()) { - search->addResults(generateReplaceItems(*edits, - search, - m_limitRenamingToProjects, - m_client->hostPathMapper()), - Core::SearchResult::AddOrdered); + const Utils::SearchResultItems items = generateReplaceItems( + *edits, search, m_limitRenamingToProjects, m_client->hostPathMapper()); + search->addResults(items, Core::SearchResult::AddOrdered); + if (m_renameResultsEnhancer) { + Utils::SearchResultItems additionalItems = m_renameResultsEnhancer(items); + for (Utils::SearchResultItem &item : additionalItems) { + TextEdit edit; + const Utils::Text::Position startPos = item.mainRange().begin; + const Utils::Text::Position endPos = item.mainRange().end; + edit.setRange({{startPos.line - 1, startPos.column}, + {endPos.line - 1, endPos.column}}); + edit.setNewText(search->textToReplace()); + item.setUserData(QVariant(edit)); + } + search->addResults(additionalItems, Core::SearchResult::AddSortedByPosition); + } qobject_cast<ReplaceWidget *>(search->additionalReplaceWidget())->showLabel(false); search->setReplaceEnabled(true); search->finishSearch(false); @@ -576,15 +693,25 @@ void SymbolSupport::applyRename(const Utils::SearchResultItems &checkedItems, { QSet<Utils::FilePath> affectedNonOpenFilePaths; QMap<Utils::FilePath, QList<TextEdit>> editsForDocuments; + QList<DocumentChange> changes; for (const Utils::SearchResultItem &item : checkedItems) { - const auto filePath = Utils::FilePath::fromString(item.path().value(0)); + const auto filePath = Utils::FilePath::fromUserInput(item.path().value(0)); if (!m_client->documentForFilePath(filePath)) affectedNonOpenFilePaths << filePath; - TextEdit edit(item.userData().toJsonObject()); - if (edit.isValid()) + const QJsonObject jsonObject = item.userData().toJsonObject(); + if (const TextEdit edit(jsonObject); edit.isValid()) editsForDocuments[filePath] << edit; + else if (const CreateFileOperation createFile(jsonObject); createFile.isValid()) + changes << createFile; + else if (const RenameFileOperation renameFile(jsonObject); renameFile.isValid()) + changes << renameFile; + else if (const DeleteFileOperation deleteFile(jsonObject); deleteFile.isValid()) + changes << deleteFile; } + for (const DocumentChange &change : changes) + applyDocumentChange(m_client, change); + for (auto it = editsForDocuments.begin(), end = editsForDocuments.end(); it != end; ++it) applyTextEdits(m_client, it.key(), it.value()); @@ -629,6 +756,11 @@ void SymbolSupport::setDefaultRenamingSymbolMapper(const SymbolMapper &mapper) m_defaultSymbolMapper = mapper; } +void SymbolSupport::setRenameResultsEnhancer(const RenameResultsEnhancer &enhancer) +{ + m_renameResultsEnhancer = enhancer; +} + } // namespace LanguageClient #include <languageclientsymbolsupport.moc> diff --git a/src/plugins/languageclient/languageclientsymbolsupport.h b/src/plugins/languageclient/languageclientsymbolsupport.h index 3dcc7b0ddc3..b3c7f4edb50 100644 --- a/src/plugins/languageclient/languageclientsymbolsupport.h +++ b/src/plugins/languageclient/languageclientsymbolsupport.h @@ -19,16 +19,19 @@ namespace LanguageServerProtocol { class MessageId; } namespace LanguageClient { class Client; +enum class LinkTarget { SymbolDef, SymbolTypeDef, SymbolImplementation }; class LANGUAGECLIENT_EXPORT SymbolSupport : public QObject { public: explicit SymbolSupport(Client *client); - void findLinkAt(TextEditor::TextDocument *document, - const QTextCursor &cursor, - Utils::LinkHandler callback, - const bool resolveTarget); + bool supportsFindLink(TextEditor::TextDocument *document, LinkTarget target) const; + LanguageServerProtocol::MessageId findLinkAt(TextEditor::TextDocument *document, + const QTextCursor &cursor, + Utils::LinkHandler callback, + const bool resolveTarget, + const LinkTarget target); bool supportsFindUsages(TextEditor::TextDocument *document) const; using ResultHandler = std::function<void(const QList<LanguageServerProtocol::Location> &)>; @@ -51,6 +54,9 @@ public: void setLimitRenamingToProjects(bool limit) { m_limitRenamingToProjects = limit; } + using RenameResultsEnhancer = std::function<Utils::SearchResultItems(const Utils::SearchResultItems &)>; + void setRenameResultsEnhancer(const RenameResultsEnhancer &enhancer); + private: void handleFindReferencesResponse( const LanguageServerProtocol::FindReferencesRequest::Response &response, @@ -78,6 +84,8 @@ private: Client *m_client = nullptr; SymbolMapper m_defaultSymbolMapper; + RenameResultsEnhancer m_renameResultsEnhancer; + QHash<Core::SearchResult *, LanguageServerProtocol::MessageId> m_renameRequestIds; bool m_limitRenamingToProjects = false; }; diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index 06ed8dcae65..8fc032f06d6 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -44,9 +44,17 @@ QTextCursor rangeToTextCursor(const Range &range, QTextDocument *doc) ChangeSet::Range convertRange(const QTextDocument *doc, const Range &range) { - return ChangeSet::Range( - Text::positionInText(doc, range.start().line() + 1, range.start().character() + 1), - Text::positionInText(doc, range.end().line() + 1, range.end().character()) + 1); + int start = range.start().toPositionInDocument(doc); + int end = range.end().toPositionInDocument(doc); + // This addesses an issue from the python language server where the reported end line + // was behind the actual end of the document. As a workaround treat every position after + // the end of the document as the end of the document. + if (end < 0 && range.end().line() >= doc->blockCount()) { + QTextCursor tc(doc->firstBlock()); + tc.movePosition(QTextCursor::End); + end = tc.position(); + } + return ChangeSet::Range(start, end); } ChangeSet editsToChangeSet(const QList<TextEdit> &edits, const QTextDocument *doc) @@ -112,11 +120,10 @@ void applyTextEdit(TextDocumentManipulatorInterface &manipulator, bool applyWorkspaceEdit(const Client *client, const WorkspaceEdit &edit) { bool result = true; - const QList<TextDocumentEdit> &documentChanges - = edit.documentChanges().value_or(QList<TextDocumentEdit>()); + const auto documentChanges = edit.documentChanges().value_or(QList<DocumentChange>()); if (!documentChanges.isEmpty()) { - for (const TextDocumentEdit &documentChange : documentChanges) - result |= applyTextDocumentEdit(client, documentChange); + for (const DocumentChange &documentChange : documentChanges) + result |= applyDocumentChange(client, documentChange); } else { const WorkspaceEdit::Changes &changes = edit.changes().value_or(WorkspaceEdit::Changes()); for (auto it = changes.cbegin(); it != changes.cend(); ++it) @@ -180,13 +187,13 @@ void updateCodeActionRefactoringMarker(Client *client, if (std::optional<WorkspaceEdit> edit = action.edit()) { if (diagnostics.isEmpty()) { QList<TextEdit> edits; - if (std::optional<QList<TextDocumentEdit>> documentChanges = edit->documentChanges()) { - QList<TextDocumentEdit> changesForUri = Utils::filtered( - *documentChanges, [uri](const TextDocumentEdit &edit) { - return edit.textDocument().uri() == uri; - }); - for (const TextDocumentEdit &edit : changesForUri) - edits << edit.edits(); + if (std::optional<QList<DocumentChange>> documentChanges = edit->documentChanges()) { + for (const DocumentChange &change : *documentChanges) { + if (auto edit = std::get_if<TextDocumentEdit>(&change)) { + if (edit->textDocument().uri() == uri) + edits << edit->edits(); + } + } } else if (std::optional<WorkspaceEdit::Changes> localChanges = edit->changes()) { edits = (*localChanges)[uri]; } @@ -337,4 +344,58 @@ const QIcon symbolIcon(int type) return icons[kind]; } +bool applyDocumentChange(const Client *client, const DocumentChange &change) +{ + if (!client) + return false; + + if (std::holds_alternative<TextDocumentEdit>(change)) { + return applyTextDocumentEdit(client, std::get<TextDocumentEdit>(change)); + } else if (std::holds_alternative<CreateFileOperation>(change)) { + const auto createOperation = std::get<CreateFileOperation>(change); + const FilePath filePath = createOperation.uri().toFilePath(client->hostPathMapper()); + if (filePath.exists()) { + if (const std::optional<CreateFileOptions> options = createOperation.options()) { + if (options->overwrite().value_or(false)) { + if (!filePath.removeFile()) + return false; + } else if (options->ignoreIfExists().value_or(false)) { + return true; + } + } + } + return filePath.ensureExistingFile(); + } else if (std::holds_alternative<RenameFileOperation>(change)) { + const RenameFileOperation renameOperation = std::get<RenameFileOperation>(change); + const FilePath oldPath = renameOperation.oldUri().toFilePath(client->hostPathMapper()); + if (!oldPath.exists()) + return false; + const FilePath newPath = renameOperation.newUri().toFilePath(client->hostPathMapper()); + if (oldPath == newPath) + return true; + if (newPath.exists()) { + if (const std::optional<CreateFileOptions> options = renameOperation.options()) { + if (options->overwrite().value_or(false)) { + if (!newPath.removeFile()) + return false; + } else if (options->ignoreIfExists().value_or(false)) { + return true; + } + } + } + return oldPath.renameFile(newPath); + } else if (std::holds_alternative<DeleteFileOperation>(change)) { + const auto deleteOperation = std::get<DeleteFileOperation>(change); + const FilePath filePath = deleteOperation.uri().toFilePath(client->hostPathMapper()); + if (const std::optional<DeleteFileOptions> options = deleteOperation.options()) { + if (!filePath.exists()) + return options->ignoreIfNotExists().value_or(false); + if (filePath.isDir() && options->recursive().value_or(false)) + return filePath.removeRecursively(); + } + return filePath.removeFile(); + } + return false; +} + } // namespace LanguageClient diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h index f24b8e45e74..13795f52d85 100644 --- a/src/plugins/languageclient/languageclientutils.h +++ b/src/plugins/languageclient/languageclientutils.h @@ -35,6 +35,8 @@ bool LANGUAGECLIENT_EXPORT applyTextEdits(const Client *client, bool LANGUAGECLIENT_EXPORT applyTextEdits(const Client *client, const Utils::FilePath &filePath, const QList<LanguageServerProtocol::TextEdit> &edits); +bool LANGUAGECLIENT_EXPORT applyDocumentChange(const Client *client, + const LanguageServerProtocol::DocumentChange &change); void LANGUAGECLIENT_EXPORT applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator, const LanguageServerProtocol::TextEdit &edit, bool newTextIsSnippet = false); diff --git a/src/plugins/languageclient/locatorfilter.cpp b/src/plugins/languageclient/locatorfilter.cpp index 61a6d187daa..7f78130bdb0 100644 --- a/src/plugins/languageclient/locatorfilter.cpp +++ b/src/plugins/languageclient/locatorfilter.cpp @@ -3,7 +3,7 @@ #include "locatorfilter.h" -#include "clientrequesttask.h" +#include "clientrequest.h" #include "currentdocumentsymbolsrequest.h" #include "languageclientmanager.h" #include "languageclienttr.h" @@ -51,7 +51,7 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount, TreeStorage<LocatorStorage> storage; TreeStorage<QList<SymbolInformation>> resultStorage; - const auto onQuerySetup = [storage, client, maxResultCount](WorkspaceSymbolRequestTask &request) { + const auto onQuerySetup = [storage, client, maxResultCount](ClientWorkspaceSymbolRequest &request) { request.setClient(client); WorkspaceSymbolParams params; params.setQuery(storage->input()); @@ -59,7 +59,7 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount, params.setLimit(maxResultCount); request.setParams(params); }; - const auto onQueryDone = [resultStorage](const WorkspaceSymbolRequestTask &request) { + const auto onQueryDone = [resultStorage](const ClientWorkspaceSymbolRequest &request) { const std::optional<LanguageClientArray<SymbolInformation>> result = request.response().result(); if (result.has_value()) @@ -76,8 +76,8 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount, }; const Group root { - Storage(resultStorage), - SymbolRequest(onQuerySetup, onQueryDone), + Tasking::Storage(resultStorage), + ClientWorkspaceSymbolRequestTask(onQuerySetup, onQueryDone), AsyncTask<void>(onFilterSetup) }; return {root, storage}; @@ -135,7 +135,7 @@ LocatorMatcherTask currentDocumentMatcher() }; const Group root { - Storage(resultStorage), + Tasking::Storage(resultStorage), CurrentDocumentSymbolsRequestTask(onQuerySetup, onQueryDone), AsyncTask<void>(onFilterSetup) }; diff --git a/src/plugins/languageclient/lspinspector.cpp b/src/plugins/languageclient/lspinspector.cpp index 31a353c2787..c01b5061cea 100644 --- a/src/plugins/languageclient/lspinspector.cpp +++ b/src/plugins/languageclient/lspinspector.cpp @@ -341,14 +341,15 @@ private: void LspInspector::show(const QString &defaultClient) { if (!m_currentWidget) { - m_currentWidget = new LspInspectorWidget(this); - m_currentWidget->setAttribute(Qt::WA_DeleteOnClose); - Core::ICore::registerWindow(m_currentWidget, Core::Context("LanguageClient.Inspector")); + auto widget = new LspInspectorWidget(this); + connect(widget, &LspInspectorWidget::finished, this, &LspInspector::onInspectorClosed); + Core::ICore::registerWindow(widget, Core::Context("LanguageClient.Inspector")); + m_currentWidget = widget; } else { QApplication::setActiveWindow(m_currentWidget); } if (!defaultClient.isEmpty()) - static_cast<LspInspectorWidget *>(m_currentWidget.data())->selectClient(defaultClient); + static_cast<LspInspectorWidget *>(m_currentWidget)->selectClient(defaultClient); m_currentWidget->show(); } @@ -392,6 +393,12 @@ QList<QString> LspInspector::clients() const return m_logs.keys(); } +void LspInspector::onInspectorClosed() +{ + m_currentWidget->deleteLater(); + m_currentWidget = nullptr; +} + LspInspectorWidget::LspInspectorWidget(LspInspector *inspector) : m_inspector(inspector), m_tabWidget(new QTabWidget(this)) { diff --git a/src/plugins/languageclient/lspinspector.h b/src/plugins/languageclient/lspinspector.h index de37b9f713f..aa3620fb2ba 100644 --- a/src/plugins/languageclient/lspinspector.h +++ b/src/plugins/languageclient/lspinspector.h @@ -68,9 +68,11 @@ signals: void capabilitiesUpdated(const QString &clientName); private: + void onInspectorClosed(); + QMap<QString, std::list<LspLogMessage>> m_logs; QMap<QString, Capabilities> m_capabilities; - QPointer<QWidget> m_currentWidget; + QWidget *m_currentWidget = nullptr; int m_logSize = 100; // default log size if no widget is currently visible }; diff --git a/src/plugins/languageclient/semantichighlightsupport.cpp b/src/plugins/languageclient/semantichighlightsupport.cpp index 919b04c20bc..472f55b4976 100644 --- a/src/plugins/languageclient/semantichighlightsupport.cpp +++ b/src/plugins/languageclient/semantichighlightsupport.cpp @@ -10,6 +10,7 @@ #include <texteditor/syntaxhighlighter.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorsettings.h> +#include <utils/algorithm.h> #include <utils/mimeutils.h> #include <QTextDocument> @@ -165,9 +166,8 @@ void SemanticTokenSupport::updateSemanticTokensImpl(TextDocument *textDocument, void SemanticTokenSupport::queueDocumentReload(TextEditor::TextDocument *doc) { - if (m_docReloadQueue.contains(doc)) + if (!Utils::insert(m_docReloadQueue, doc)) return; - m_docReloadQueue << doc; connect( m_client, &Client::initialized, diff --git a/src/plugins/macros/Macros.json.in b/src/plugins/macros/Macros.json.in index cac1bdf1871..1c5d949c1c2 100644 --- a/src/plugins/macros/Macros.json.in +++ b/src/plugins/macros/Macros.json.in @@ -1,18 +1,18 @@ { - \"Name\" : \"Macros\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Macros", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Macros in text editors.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Macros in text editors.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/macros/actionmacrohandler.cpp b/src/plugins/macros/actionmacrohandler.cpp index 37274f68a65..0dc4fd95773 100644 --- a/src/plugins/macros/actionmacrohandler.cpp +++ b/src/plugins/macros/actionmacrohandler.cpp @@ -13,6 +13,8 @@ #include <texteditor/texteditorconstants.h> +#include <utils/algorithm.h> + #include <QAction> #include <QEvent> @@ -55,8 +57,7 @@ bool ActionMacroHandler::executeEvent(const MacroEvent ¯oEvent) void ActionMacroHandler::registerCommand(Id id) { - if (!m_commandIds.contains(id)) { - m_commandIds.insert(id); + if (Utils::insert(m_commandIds, id)) { const Command *command = ActionManager::command(id); if (QAction *action = command->action()) { connect(action, &QAction::triggered, this, [this, id, command]() { diff --git a/src/plugins/macros/findmacrohandler.cpp b/src/plugins/macros/findmacrohandler.cpp index ba853adc1a5..878854e7b17 100644 --- a/src/plugins/macros/findmacrohandler.cpp +++ b/src/plugins/macros/findmacrohandler.cpp @@ -15,6 +15,7 @@ using namespace Macros; using namespace Macros::Internal; +using namespace Utils; static const char EVENTNAME[] = "Find"; static const quint8 TYPE = 0; @@ -59,26 +60,26 @@ bool FindMacroHandler::executeEvent(const MacroEvent ¯oEvent) switch (macroEvent.value(TYPE).toInt()) { case FINDINCREMENTAL: currentFind->findIncremental(macroEvent.value(BEFORE).toString(), - (Core::FindFlags)macroEvent.value(FLAGS).toInt()); + FindFlags(macroEvent.value(FLAGS).toInt())); break; case FINDSTEP: currentFind->findStep(macroEvent.value(BEFORE).toString(), - (Core::FindFlags)macroEvent.value(FLAGS).toInt()); + FindFlags(macroEvent.value(FLAGS).toInt())); break; case REPLACE: currentFind->replace(macroEvent.value(BEFORE).toString(), macroEvent.value(AFTER).toString(), - (Core::FindFlags)macroEvent.value(FLAGS).toInt()); + FindFlags(macroEvent.value(FLAGS).toInt())); break; case REPLACESTEP: currentFind->replaceStep(macroEvent.value(BEFORE).toString(), - macroEvent.value(AFTER).toString(), - (Core::FindFlags)macroEvent.value(FLAGS).toInt()); + macroEvent.value(AFTER).toString(), + FindFlags(macroEvent.value(FLAGS).toInt())); break; case REPLACEALL: currentFind->replaceAll(macroEvent.value(BEFORE).toString(), - macroEvent.value(AFTER).toString(), - (Core::FindFlags)macroEvent.value(FLAGS).toInt()); + macroEvent.value(AFTER).toString(), + FindFlags(macroEvent.value(FLAGS).toInt())); break; case RESET: currentFind->resetIncrementalSearch(); @@ -87,31 +88,31 @@ bool FindMacroHandler::executeEvent(const MacroEvent ¯oEvent) return true; } -void FindMacroHandler::findIncremental(const QString &txt, Core::FindFlags findFlags) +void FindMacroHandler::findIncremental(const QString &txt, FindFlags findFlags) { if (!isRecording()) return; MacroEvent e; e.setId(EVENTNAME); e.setValue(BEFORE, txt); - e.setValue(FLAGS, (int)findFlags); + e.setValue(FLAGS, int(findFlags)); e.setValue(TYPE, FINDINCREMENTAL); addMacroEvent(e); } -void FindMacroHandler::findStep(const QString &txt, Core::FindFlags findFlags) +void FindMacroHandler::findStep(const QString &txt, FindFlags findFlags) { if (!isRecording()) return; MacroEvent e; e.setId(EVENTNAME); e.setValue(BEFORE, txt); - e.setValue(FLAGS, (int)findFlags); + e.setValue(FLAGS, int(findFlags)); e.setValue(TYPE, FINDSTEP); addMacroEvent(e); } -void FindMacroHandler::replace(const QString &before, const QString &after, Core::FindFlags findFlags) +void FindMacroHandler::replace(const QString &before, const QString &after, FindFlags findFlags) { if (!isRecording()) return; @@ -119,12 +120,12 @@ void FindMacroHandler::replace(const QString &before, const QString &after, Core e.setId(EVENTNAME); e.setValue(BEFORE, before); e.setValue(AFTER, after); - e.setValue(FLAGS, (int)findFlags); + e.setValue(FLAGS, int(findFlags)); e.setValue(TYPE, REPLACE); addMacroEvent(e); } -void FindMacroHandler::replaceStep(const QString &before, const QString &after, Core::FindFlags findFlags) +void FindMacroHandler::replaceStep(const QString &before, const QString &after, FindFlags findFlags) { if (!isRecording()) return; @@ -132,12 +133,12 @@ void FindMacroHandler::replaceStep(const QString &before, const QString &after, e.setId(EVENTNAME); e.setValue(BEFORE, before); e.setValue(AFTER, after); - e.setValue(FLAGS, (int)findFlags); + e.setValue(FLAGS, int(findFlags)); e.setValue(TYPE, REPLACESTEP); addMacroEvent(e); } -void FindMacroHandler::replaceAll(const QString &before, const QString &after, Core::FindFlags findFlags) +void FindMacroHandler::replaceAll(const QString &before, const QString &after, FindFlags findFlags) { if (!isRecording()) return; @@ -145,7 +146,7 @@ void FindMacroHandler::replaceAll(const QString &before, const QString &after, C e.setId(EVENTNAME); e.setValue(BEFORE, before); e.setValue(AFTER, after); - e.setValue(FLAGS, (int)findFlags); + e.setValue(FLAGS, int(findFlags)); e.setValue(TYPE, REPLACEALL); addMacroEvent(e); } diff --git a/src/plugins/macros/findmacrohandler.h b/src/plugins/macros/findmacrohandler.h index 4c876e33cb4..ef157051c75 100644 --- a/src/plugins/macros/findmacrohandler.h +++ b/src/plugins/macros/findmacrohandler.h @@ -6,6 +6,7 @@ #include "imacrohandler.h" #include <coreplugin/find/textfindconstants.h> +#include <utils/filesearch.h> namespace Core { class IEditor; } @@ -24,11 +25,11 @@ public: bool canExecuteEvent(const MacroEvent ¯oEvent) override; bool executeEvent(const MacroEvent ¯oEvent) override; - void findIncremental(const QString &txt, Core::FindFlags findFlags); - void findStep(const QString &txt, Core::FindFlags findFlags); - void replace(const QString &before, const QString &after, Core::FindFlags findFlags); - void replaceStep(const QString &before, const QString &after, Core::FindFlags findFlags); - void replaceAll(const QString &before, const QString &after, Core::FindFlags findFlags); + void findIncremental(const QString &txt, Utils::FindFlags findFlags); + void findStep(const QString &txt, Utils::FindFlags findFlags); + void replace(const QString &before, const QString &after, Utils::FindFlags findFlags); + void replaceStep(const QString &before, const QString &after, Utils::FindFlags findFlags); + void replaceAll(const QString &before, const QString &after, Utils::FindFlags findFlags); void resetIncrementalSearch(); private: diff --git a/src/plugins/macros/macro.cpp b/src/plugins/macros/macro.cpp index 46eaa7a8456..829cae5b72a 100644 --- a/src/plugins/macros/macro.cpp +++ b/src/plugins/macros/macro.cpp @@ -4,7 +4,6 @@ #include "macro.h" #include "macroevent.h" -#include <app/app_version.h> #include <utils/fileutils.h> #include <QFileInfo> @@ -35,8 +34,8 @@ public: QList<MacroEvent> events; }; -Macro::MacroPrivate::MacroPrivate() : - version(QLatin1String(Core::Constants::IDE_VERSION_LONG)) +Macro::MacroPrivate::MacroPrivate() + : version(QCoreApplication::applicationVersion()) { } diff --git a/src/plugins/macros/macromanager.cpp b/src/plugins/macros/macromanager.cpp index 507ed92179d..b5151b7ee25 100644 --- a/src/plugins/macros/macromanager.cpp +++ b/src/plugins/macros/macromanager.cpp @@ -28,7 +28,6 @@ #include <QDir> #include <QFile> #include <QFileInfo> -#include <QSettings> #include <QList> #include <QAction> @@ -372,7 +371,7 @@ void MacroManager::saveLastMacro() QString MacroManager::macrosDirectory() { const QString path = Core::ICore::userResourcePath("macros").toString(); - if (QFile::exists(path) || QDir().mkpath(path)) + if (QFileInfo::exists(path) || QDir().mkpath(path)) return path; return QString(); } diff --git a/src/plugins/macros/macros.qbs b/src/plugins/macros/macros.qbs index c975f756d7d..9aa7441b771 100644 --- a/src/plugins/macros/macros.qbs +++ b/src/plugins/macros/macros.qbs @@ -10,8 +10,6 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "TextEditor" } - Depends { name: "app_version_header" } - files: [ "actionmacrohandler.cpp", "actionmacrohandler.h", diff --git a/src/plugins/macros/macrosplugin.cpp b/src/plugins/macros/macrosplugin.cpp index 74fffddeb43..c87686fea3c 100644 --- a/src/plugins/macros/macrosplugin.cpp +++ b/src/plugins/macros/macrosplugin.cpp @@ -18,7 +18,6 @@ #include <coreplugin/icore.h> #include <coreplugin/icontext.h> -#include <QSettings> #include <QAction> #include <QKeySequence> #include <QMenu> diff --git a/src/plugins/macros/macrotextfind.cpp b/src/plugins/macros/macrotextfind.cpp index 4dca44ee9b6..0f949426a03 100644 --- a/src/plugins/macros/macrotextfind.cpp +++ b/src/plugins/macros/macrotextfind.cpp @@ -7,6 +7,7 @@ using namespace Macros; using namespace Macros::Internal; +using namespace Utils; MacroTextFind::MacroTextFind(Core::IFindSupport *currentFind): Core::IFindSupport(), @@ -20,7 +21,7 @@ bool MacroTextFind::supportsReplace() const return m_currentFind->supportsReplace(); } -Core::FindFlags MacroTextFind::supportedFindFlags() const +FindFlags MacroTextFind::supportedFindFlags() const { QTC_ASSERT(m_currentFind, return {}); return m_currentFind->supportedFindFlags(); @@ -51,13 +52,13 @@ QString MacroTextFind::completedFindString() const return m_currentFind->completedFindString(); } -void MacroTextFind::highlightAll(const QString &txt, Core::FindFlags findFlags) +void MacroTextFind::highlightAll(const QString &txt, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return); m_currentFind->highlightAll(txt, findFlags); } -Core::IFindSupport::Result MacroTextFind::findIncremental(const QString &txt, Core::FindFlags findFlags) +Core::IFindSupport::Result MacroTextFind::findIncremental(const QString &txt, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return IFindSupport::NotFound); Core::IFindSupport::Result result = m_currentFind->findIncremental(txt, findFlags); @@ -66,7 +67,7 @@ Core::IFindSupport::Result MacroTextFind::findIncremental(const QString &txt, Co return result; } -Core::IFindSupport::Result MacroTextFind::findStep(const QString &txt, Core::FindFlags findFlags) +Core::IFindSupport::Result MacroTextFind::findStep(const QString &txt, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return IFindSupport::NotFound); Core::IFindSupport::Result result = m_currentFind->findStep(txt, findFlags); @@ -75,14 +76,14 @@ Core::IFindSupport::Result MacroTextFind::findStep(const QString &txt, Core::Fin return result; } -void MacroTextFind::replace(const QString &before, const QString &after, Core::FindFlags findFlags) +void MacroTextFind::replace(const QString &before, const QString &after, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return); m_currentFind->replace(before, after, findFlags); emit replaced(before, after, findFlags); } -bool MacroTextFind::replaceStep(const QString &before, const QString &after, Core::FindFlags findFlags) +bool MacroTextFind::replaceStep(const QString &before, const QString &after, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return false); bool result = m_currentFind->replaceStep(before, after, findFlags); @@ -90,7 +91,7 @@ bool MacroTextFind::replaceStep(const QString &before, const QString &after, Cor return result; } -int MacroTextFind::replaceAll(const QString &before, const QString &after, Core::FindFlags findFlags) +int MacroTextFind::replaceAll(const QString &before, const QString &after, FindFlags findFlags) { QTC_ASSERT(m_currentFind, return 0); int result = m_currentFind->replaceAll(before, after, findFlags); diff --git a/src/plugins/macros/macrotextfind.h b/src/plugins/macros/macrotextfind.h index b5270662f50..e59b8086c05 100644 --- a/src/plugins/macros/macrotextfind.h +++ b/src/plugins/macros/macrotextfind.h @@ -18,32 +18,29 @@ public: MacroTextFind(Core::IFindSupport *currentFind); bool supportsReplace() const override; - Core::FindFlags supportedFindFlags() const override; + Utils::FindFlags supportedFindFlags() const override; void resetIncrementalSearch() override; void clearHighlights() override; QString currentFindString() const override; QString completedFindString() const override; - void highlightAll(const QString &txt, Core::FindFlags findFlags) override; - Core::IFindSupport::Result findIncremental(const QString &txt, Core::FindFlags findFlags) override; - Core::IFindSupport::Result findStep(const QString &txt, Core::FindFlags findFlags) override; - void replace(const QString &before, const QString &after, Core::FindFlags findFlags) override; - bool replaceStep(const QString &before, const QString &after, Core::FindFlags findFlags) override; - int replaceAll(const QString &before, const QString &after, Core::FindFlags findFlags) override; + void highlightAll(const QString &txt, Utils::FindFlags findFlags) override; + Core::IFindSupport::Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Core::IFindSupport::Result findStep(const QString &txt, Utils::FindFlags findFlags) override; + void replace(const QString &before, const QString &after, Utils::FindFlags findFlags) override; + bool replaceStep(const QString &before, const QString &after, Utils::FindFlags findFlags) override; + int replaceAll(const QString &before, const QString &after, Utils::FindFlags findFlags) override; void defineFindScope() override; void clearFindScope() override; signals: void incrementalSearchReseted(); - void incrementalFound(const QString &txt, Core::FindFlags findFlags); - void stepFound(const QString &txt, Core::FindFlags findFlags); - void replaced(const QString &before, const QString &after, - Core::FindFlags findFlags); - void stepReplaced(const QString &before, const QString &after, - Core::FindFlags findFlags); - void allReplaced(const QString &before, const QString &after, - Core::FindFlags findFlags); + void incrementalFound(const QString &txt, Utils::FindFlags findFlags); + void stepFound(const QString &txt, Utils::FindFlags findFlags); + void replaced(const QString &before, const QString &after, Utils::FindFlags findFlags); + void stepReplaced(const QString &before, const QString &after, Utils::FindFlags findFlags); + void allReplaced(const QString &before, const QString &after, Utils::FindFlags findFlags); private: QPointer<Core::IFindSupport> m_currentFind; diff --git a/src/plugins/marketplace/Marketplace.json.in b/src/plugins/marketplace/Marketplace.json.in index fd7e6b592cb..1122b40a67a 100644 --- a/src/plugins/marketplace/Marketplace.json.in +++ b/src/plugins/marketplace/Marketplace.json.in @@ -1,18 +1,18 @@ { -\"Name\" : \"Marketplace\", -\"Version\" : \"$$QTCREATOR_VERSION\", -\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", -\"Vendor\" : \"The Qt Company Ltd\", -\"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", -\"License\" : [ \"Commercial Usage\", -\"\", -\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", -\"\", -\"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.\" +"Name" : "Marketplace", +"Version" : "${IDE_VERSION}", +"CompatVersion" : "${IDE_VERSION_COMPAT}", +"Vendor" : "The Qt Company Ltd", +"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", +"License" : [ "Commercial Usage", +"", +"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", +"", +"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." ], -\"Description\" : \"Qt Marketplace plugin.\", -\"Url\" : \"http://www.qt.io\", -$$dependencyList +"Description" : "Qt Marketplace plugin.", +"Url" : "http://www.qt.io", +${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp index 44272858c5e..a81f252b03c 100644 --- a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp +++ b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp @@ -74,7 +74,7 @@ public: connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator, progressIndicator, &Utils::ProgressIndicator::setVisible); - connect(m_sectionedProducts, &SectionedProducts::errorOccurred, + connect(m_sectionedProducts, &SectionedProducts::errorOccurred, this, [this, progressIndicator, searchBox](int, const QString &message) { progressIndicator->hide(); progressIndicator->deleteLater(); @@ -94,8 +94,10 @@ public: this, []() { QDesktopServices::openUrl(QUrl("https://marketplace.qt.io")); }); }); - connect(m_searcher, &QLineEdit::textChanged, - m_sectionedProducts, &SectionedProducts::setSearchString); + connect(m_searcher, + &QLineEdit::textChanged, + m_sectionedProducts, + &SectionedProducts::setSearchStringDelayed); connect(m_sectionedProducts, &SectionedProducts::tagClicked, this, &QtMarketplacePageWidget::onTagClicked); } diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index cebf2e1a038..aa8b4f2bf1b 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -1,8 +1,8 @@ add_qtc_plugin(McuSupport DEPENDS Qt::Core QmlJS - PLUGIN_DEPENDS Core BareMetal ProjectExplorer Debugger CMakeProjectManager QtSupport + PLUGIN_DEPENDS Core BareMetal ProjectExplorer Debugger CMakeProjectManager QmlJSTools QtSupport SOURCES - mcukitinformation.cpp mcukitinformation.h + mcukitaspect.cpp mcukitaspect.h mcusupport.qrc mcusupport_global.h mcusupporttr.h @@ -25,6 +25,7 @@ add_qtc_plugin(McuSupport mcuqmlprojectnode.cpp mcuqmlprojectnode.h mcubuildstep.cpp mcubuildstep.h dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h + mcusupportimportprovider.cpp mcusupportimportprovider.h ) add_subdirectory(test) diff --git a/src/plugins/mcusupport/McuSupport.json.in b/src/plugins/mcusupport/McuSupport.json.in index 2616ea29dcd..94586c932e8 100644 --- a/src/plugins/mcusupport/McuSupport.json.in +++ b/src/plugins/mcusupport/McuSupport.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"McuSupport\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "McuSupport", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Helper for MCU related projects.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Helper for MCU related projects.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp index 9702fb77bfb..897fcd0aa14 100644 --- a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp +++ b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp @@ -89,9 +89,9 @@ McuKitCreationDialog::McuKitCreationDialog(const MessagesList &messages, if (qtMCUPackage->isValidStatus()) m_qtMCUsPathLabel->setText( Tr::tr("Qt for MCUs path %1").arg(qtMCUPackage->path().toUserOutput())); - connect(m_nextButton, &QPushButton::clicked, [=] { updateMessage(1); }); - connect(m_previousButton, &QPushButton::clicked, [=] { updateMessage(-1); }); - connect(fixButton, &QPushButton::clicked, [=] { + connect(m_nextButton, &QPushButton::clicked, this, [this] { updateMessage(1); }); + connect(m_previousButton, &QPushButton::clicked, this, [this] { updateMessage(-1); }); + connect(fixButton, &QPushButton::clicked, this, [this, settingsHandler] { // Open the MCU Options widget on the current platform settingsHandler->setInitialPlatformName(m_messages[m_currentIndex].platform); Core::ICore::showOptionsDialog(Constants::SETTINGS_ID); diff --git a/src/plugins/mcusupport/mcuabstractpackage.h b/src/plugins/mcusupport/mcuabstractpackage.h index c319c32f03c..c6131919e54 100644 --- a/src/plugins/mcusupport/mcuabstractpackage.h +++ b/src/plugins/mcusupport/mcuabstractpackage.h @@ -3,11 +3,11 @@ #pragma once +#include <utils/store.h> + #include <QObject> -namespace Utils { -class FilePath; -} // namespace Utils +namespace Utils { class FilePath; } namespace McuSupport::Internal { @@ -39,7 +39,7 @@ public: virtual void setPath(const Utils::FilePath &) = 0; virtual Utils::FilePath defaultPath() const = 0; virtual Utils::FilePath detectionPath() const = 0; - virtual QString settingsKey() const = 0; + virtual Utils::Key settingsKey() const = 0; virtual void updateStatus() = 0; virtual Status status() const = 0; @@ -56,6 +56,6 @@ signals: void changed(); void statusChanged(); void reset(); +}; -}; // class McuAbstractPackage } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcubuildstep.cpp b/src/plugins/mcusupport/mcubuildstep.cpp index 0250eb82f30..ceef09d078e 100644 --- a/src/plugins/mcusupport/mcubuildstep.cpp +++ b/src/plugins/mcusupport/mcubuildstep.cpp @@ -7,7 +7,7 @@ #include "mculegacyconstants.h" #include "mcusupportconstants.h" -#include <cmakeprojectmanager/cmakekitinformation.h> +#include <cmakeprojectmanager/cmakekitaspect.h> #include <projectexplorer/abstractprocessstep.h> #include <projectexplorer/buildsteplist.h> @@ -30,22 +30,28 @@ #include <QTemporaryDir> #include <QVersionNumber> +using namespace Utils; + namespace McuSupport::Internal { class DeployMcuProcessStep : public ProjectExplorer::AbstractProcessStep { public: - static const Utils::Id id; + static const Id id; static void showError(const QString &text); - DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id); + DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Id id); private: QString findKitInformation(ProjectExplorer::Kit *kit, const QString &key); QTemporaryDir m_tmpDir; + + FilePathAspect cmd{this}; + StringAspect args{this}; + FilePathAspect outDir{this}; }; -const Utils::Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; +const Id DeployMcuProcessStep::id = "QmlProject.Mcu.DeployStep"; void DeployMcuProcessStep::showError(const QString &text) { @@ -53,7 +59,7 @@ void DeployMcuProcessStep::showError(const QString &text) ProjectExplorer::TaskHub::addTask(task); } -DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Utils::Id id) +DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, Id id) : AbstractProcessStep(bc, id) , m_tmpDir() { @@ -71,50 +77,46 @@ DeployMcuProcessStep::DeployMcuProcessStep(ProjectExplorer::BuildStepList *bc, U if (!kit) return; - QString root = findKitInformation(kit, Internal::Legacy::Constants::QUL_CMAKE_VAR); - auto rootPath = Utils::FilePath::fromString(root); + const QString root = findKitInformation(kit, Internal::Legacy::Constants::QUL_CMAKE_VAR); + const FilePath rootPath = FilePath::fromString(root); - auto cmd = addAspect<Utils::FilePathAspect>(); - cmd->setSettingsKey("QmlProject.Mcu.ProcessStep.Command"); - cmd->setExpectedKind(Utils::PathChooser::Command); - cmd->setLabelText(QmlProjectManager::Tr::tr("Command:")); - cmd->setFilePath(rootPath.pathAppended("/bin/qmlprojectexporter")); + cmd.setSettingsKey("QmlProject.Mcu.ProcessStep.Command"); + cmd.setExpectedKind(PathChooser::Command); + cmd.setLabelText(QmlProjectManager::Tr::tr("Command:")); + cmd.setValue(rootPath.pathAppended("/bin/qmlprojectexporter")); const char *importPathConstant = QtSupport::Constants::KIT_QML_IMPORT_PATH; - Utils::FilePath projectDir = buildSystem()->projectDirectory(); - Utils::FilePath qulIncludeDir = Utils::FilePath::fromVariant(kit->value(importPathConstant)); + const FilePath qulIncludeDir = FilePath::fromVariant(kit->value(importPathConstant)); QStringList includeDirs { - Utils::ProcessArgs::quoteArg(qulIncludeDir.toString()), - Utils::ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()) + ProcessArgs::quoteArg(qulIncludeDir.toString()), + ProcessArgs::quoteArg(qulIncludeDir.pathAppended("Timeline").toString()) }; const char *toolChainConstant = Internal::Constants::KIT_MCUTARGET_TOOLCHAIN_KEY; QStringList arguments = { - Utils::ProcessArgs::quoteArg(buildSystem()->projectFilePath().toString()), + ProcessArgs::quoteArg(buildSystem()->projectFilePath().toString()), "--platform", findKitInformation(kit, "QUL_PLATFORM"), "--toolchain", kit->value(toolChainConstant).toString(), "--include-dirs", includeDirs.join(","), }; - auto args = addAspect<Utils::StringAspect>(); - args->setSettingsKey("QmlProject.Mcu.ProcessStep.Arguments"); - args->setDisplayStyle(Utils::StringAspect::LineEditDisplay); - args->setLabelText(QmlProjectManager::Tr::tr("Arguments:")); - args->setValue(Utils::ProcessArgs::joinArgs(arguments)); + args.setSettingsKey("QmlProject.Mcu.ProcessStep.Arguments"); + args.setDisplayStyle(StringAspect::LineEditDisplay); + args.setLabelText(QmlProjectManager::Tr::tr("Arguments:")); + args.setValue(ProcessArgs::joinArgs(arguments)); - auto outDir = addAspect<Utils::FilePathAspect>(); - outDir->setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory"); - outDir->setExpectedKind(Utils::PathChooser::Directory); - outDir->setLabelText(QmlProjectManager::Tr::tr("Build directory:")); - outDir->setPlaceHolderText(m_tmpDir.path()); + outDir.setSettingsKey("QmlProject.Mcu.ProcessStep.BuildDirectory"); + outDir.setExpectedKind(PathChooser::Directory); + outDir.setLabelText(QmlProjectManager::Tr::tr("Build directory:")); + outDir.setPlaceHolderText(m_tmpDir.path()); - setCommandLineProvider([this, cmd, args, outDir]() -> Utils::CommandLine { - auto directory = outDir->value(); + setCommandLineProvider([this] { + QString directory = outDir().path(); if (directory.isEmpty()) directory = m_tmpDir.path(); - Utils::CommandLine cmdLine(cmd->filePath()); - cmdLine.addArgs(args->value(), Utils::CommandLine::Raw); + CommandLine cmdLine(cmd()); + cmdLine.addArgs(args(), CommandLine::Raw); cmdLine.addArg("--outdir"); cmdLine.addArg(directory); return cmdLine; diff --git a/src/plugins/mcusupport/mcukitaspect.cpp b/src/plugins/mcusupport/mcukitaspect.cpp new file mode 100644 index 00000000000..f53dcacfd04 --- /dev/null +++ b/src/plugins/mcusupport/mcukitaspect.cpp @@ -0,0 +1,126 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mcukitaspect.h" +#include "mcusupporttr.h" + +#include <cmakeprojectmanager/cmakekitaspect.h> + +#include <utils/algorithm.h> +#include <utils/filepath.h> +#include <utils/qtcassert.h> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace McuSupport::Internal { + +class McuDependenciesKitAspectImpl final : public KitAspect +{ +public: + McuDependenciesKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory) + {} + + void makeReadOnly() override {} + void refresh() override {} + void addToLayoutImpl(Layouting::LayoutItem &) override {} +}; + +Utils::Id McuDependenciesKitAspect::id() +{ + return "PE.Profile.McuCMakeDependencies"; +} + +Utils::NameValueItems McuDependenciesKitAspect::dependencies(const Kit *kit) +{ + if (kit) + return Utils::NameValueItem::fromStringList( + kit->value(McuDependenciesKitAspect::id()).toStringList()); + return Utils::NameValueItems(); +} + +void McuDependenciesKitAspect::setDependencies(Kit *k, const Utils::NameValueItems &dependencies) +{ + if (k) + k->setValue(McuDependenciesKitAspect::id(), + Utils::NameValueItem::toStringList(dependencies)); +} + +Utils::NameValuePairs McuDependenciesKitAspect::configuration(const Kit *kit) +{ + using namespace CMakeProjectManager; + const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); + return Utils::transform<Utils::NameValuePairs>(config, [](const CMakeConfigItem &it) { + return Utils::NameValuePair(QString::fromUtf8(it.key), QString::fromUtf8(it.value)); + }); +} + +// McuDependenciesKitAspectFactory + +class McuDependenciesKitAspectFactory final : public KitAspectFactory +{ +public: + McuDependenciesKitAspectFactory() + { + setId(McuDependenciesKitAspect::id()); + setDisplayName(Tr::tr("MCU Dependencies")); + setDescription(Tr::tr("Paths to 3rd party dependencies")); + setPriority(28500); + } + + Tasks validate(const Kit *kit) const final + { + Tasks result; + QTC_ASSERT(kit, return result); + + // check dependencies are defined properly for this kit + const QVariant checkFormat = kit->value(McuDependenciesKitAspect::id()); + if (!checkFormat.isValid() || checkFormat.isNull()) + return result; + if (!checkFormat.canConvert(QVariant::List)) + return {BuildSystemTask(Task::Error, Tr::tr("The MCU dependencies setting value is invalid."))}; + + // check paths defined in cmake variables for given dependencies exist + const auto cMakeEntries = Utils::NameValueDictionary(McuDependenciesKitAspect::configuration(kit)); + for (const auto &dependency : McuDependenciesKitAspect::dependencies(kit)) { + auto givenPath = Utils::FilePath::fromUserInput(cMakeEntries.value(dependency.name)); + if (givenPath.isEmpty()) { + result << BuildSystemTask(Task::Warning, + Tr::tr("CMake variable %1 not defined.").arg(dependency.name)); + } else { + const auto detectionPath = givenPath.resolvePath(dependency.value); + if (!detectionPath.exists()) { + result << BuildSystemTask(Task::Warning, + Tr::tr("CMake variable %1: path %2 does not exist.") + .arg(dependency.name, detectionPath.toUserOutput())); + } + } + } + + return result; + } + void fix(Kit *kit) final + { + QTC_ASSERT(kit, return ); + + const QVariant variant = kit->value(McuDependenciesKitAspect::id()); + if (!variant.isNull() && !variant.canConvert(QVariant::List)) { + qWarning("Kit \"%s\" has a wrong mcu dependencies value set.", + qPrintable(kit->displayName())); + McuDependenciesKitAspect::setDependencies(kit, Utils::NameValueItems()); + } + } + + KitAspect *createKitAspect(Kit *kit) const final + { + QTC_ASSERT(kit, return nullptr); + return new McuDependenciesKitAspectImpl(kit, this); + } + + ItemList toUserOutput(const Kit *) const final { return {}; } +}; + +const McuDependenciesKitAspectFactory theMcuDependenciesKitAspectFactory; + +} // McuSupport::Internal diff --git a/src/plugins/mcusupport/mcukitaspect.h b/src/plugins/mcusupport/mcukitaspect.h new file mode 100644 index 00000000000..a86841a72aa --- /dev/null +++ b/src/plugins/mcusupport/mcukitaspect.h @@ -0,0 +1,19 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <projectexplorer/kitaspects.h> + +namespace McuSupport::Internal { + +class McuDependenciesKitAspect final +{ +public: + static Utils::Id id(); + static Utils::NameValueItems dependencies(const ProjectExplorer::Kit *kit); + static void setDependencies(ProjectExplorer::Kit *kit, const Utils::NameValueItems &dependencies); + static Utils::NameValuePairs configuration(const ProjectExplorer::Kit *kit); +}; + +} // McuSupport::Internal diff --git a/src/plugins/mcusupport/mcukitinformation.cpp b/src/plugins/mcusupport/mcukitinformation.cpp deleted file mode 100644 index 2fd6f846e1e..00000000000 --- a/src/plugins/mcusupport/mcukitinformation.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "mcukitinformation.h" -#include "mcusupporttr.h" - -#include <cmakeprojectmanager/cmakekitinformation.h> -#include <utils/algorithm.h> -#include <utils/filepath.h> -#include <utils/qtcassert.h> - -using namespace ProjectExplorer; - -namespace { - -class McuDependenciesKitAspectWidget final : public KitAspectWidget -{ -public: - McuDependenciesKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki) - {} - - void makeReadOnly() override {} - void refresh() override {} - void addToLayout(Layouting::LayoutItem &) override {} -}; - -} // anonymous namespace - -namespace McuSupport { -namespace Internal { - -McuDependenciesKitAspect::McuDependenciesKitAspect() -{ - setObjectName(QLatin1String("McuDependenciesKitAspect")); - setId(McuDependenciesKitAspect::id()); - setDisplayName(Tr::tr("MCU Dependencies")); - setDescription(Tr::tr("Paths to 3rd party dependencies")); - setPriority(28500); -} - -Tasks McuDependenciesKitAspect::validate(const Kit *kit) const -{ - Tasks result; - QTC_ASSERT(kit, return result); - - // check dependencies are defined properly for this kit - const QVariant checkFormat = kit->value(McuDependenciesKitAspect::id()); - if (!checkFormat.isValid() || checkFormat.isNull()) - return result; - if (!checkFormat.canConvert(QVariant::List)) - return {BuildSystemTask(Task::Error, Tr::tr("The MCU dependencies setting value is invalid."))}; - - // check paths defined in cmake variables for given dependencies exist - const auto cMakeEntries = Utils::NameValueDictionary(configuration(kit)); - for (const auto &dependency : dependencies(kit)) { - auto givenPath = Utils::FilePath::fromUserInput(cMakeEntries.value(dependency.name)); - if (givenPath.isEmpty()) { - result << BuildSystemTask(Task::Warning, - Tr::tr("CMake variable %1 not defined.").arg(dependency.name)); - } else { - const auto detectionPath = givenPath.resolvePath(dependency.value); - if (!detectionPath.exists()) { - result << BuildSystemTask(Task::Warning, - Tr::tr("CMake variable %1: path %2 does not exist.") - .arg(dependency.name, detectionPath.toUserOutput())); - } - } - } - - return result; -} - -void McuDependenciesKitAspect::fix(Kit *kit) -{ - QTC_ASSERT(kit, return ); - - const QVariant variant = kit->value(McuDependenciesKitAspect::id()); - if (!variant.isNull() && !variant.canConvert(QVariant::List)) { - qWarning("Kit \"%s\" has a wrong mcu dependencies value set.", - qPrintable(kit->displayName())); - setDependencies(kit, Utils::NameValueItems()); - } -} - -KitAspectWidget *McuDependenciesKitAspect::createConfigWidget(Kit *kit) const -{ - QTC_ASSERT(kit, return nullptr); - return new McuDependenciesKitAspectWidget(kit, this); -} - -KitAspect::ItemList McuDependenciesKitAspect::toUserOutput(const Kit *kit) const -{ - Q_UNUSED(kit) - - return {}; -} - -Utils::Id McuDependenciesKitAspect::id() -{ - return "PE.Profile.McuCMakeDependencies"; -} - -Utils::NameValueItems McuDependenciesKitAspect::dependencies(const Kit *kit) -{ - if (kit) - return Utils::NameValueItem::fromStringList( - kit->value(McuDependenciesKitAspect::id()).toStringList()); - return Utils::NameValueItems(); -} - -void McuDependenciesKitAspect::setDependencies(Kit *k, const Utils::NameValueItems &dependencies) -{ - if (k) - k->setValue(McuDependenciesKitAspect::id(), - Utils::NameValueItem::toStringList(dependencies)); -} - -Utils::NameValuePairs McuDependenciesKitAspect::configuration(const Kit *kit) -{ - using namespace CMakeProjectManager; - const auto config = CMakeConfigurationKitAspect::configuration(kit).toList(); - return Utils::transform<Utils::NameValuePairs>(config, [](const CMakeConfigItem &it) { - return Utils::NameValuePair(QString::fromUtf8(it.key), QString::fromUtf8(it.value)); - }); -} - -} // namespace Internal -} // namespace McuSupport diff --git a/src/plugins/mcusupport/mcukitinformation.h b/src/plugins/mcusupport/mcukitinformation.h deleted file mode 100644 index f0bafd34c1b..00000000000 --- a/src/plugins/mcusupport/mcukitinformation.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/kitinformation.h> - -namespace McuSupport { -namespace Internal { - -class McuDependenciesKitAspect final : public ProjectExplorer::KitAspect -{ - Q_OBJECT - -public: - McuDependenciesKitAspect(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *kit) const override; - void fix(ProjectExplorer::Kit *kit) override; - - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *kit) const override; - - ItemList toUserOutput(const ProjectExplorer::Kit *kit) const override; - - static Utils::Id id(); - static Utils::NameValueItems dependencies(const ProjectExplorer::Kit *kit); - static void setDependencies(ProjectExplorer::Kit *kit, const Utils::NameValueItems &dependencies); - static Utils::NameValuePairs configuration(const ProjectExplorer::Kit *kit); -}; - -} // namespace Internal -} // namespace McuSupport diff --git a/src/plugins/mcusupport/mcukitmanager.cpp b/src/plugins/mcusupport/mcukitmanager.cpp index 00709b126f5..39e13f80892 100644 --- a/src/plugins/mcusupport/mcukitmanager.cpp +++ b/src/plugins/mcusupport/mcukitmanager.cpp @@ -2,30 +2,30 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "mcukitmanager.h" -#include "mculegacyconstants.h" -#include "mcusupport_global.h" -#include "mcusupportoptions.h" -#include "mcukitinformation.h" +#include "mcukitaspect.h" +#include "mculegacyconstants.h" #include "mcupackage.h" +#include "mcusupport_global.h" #include "mcusupportconstants.h" +#include "mcusupportoptions.h" #include "mcusupportplugin.h" #include "mcusupportsdk.h" #include "mcusupporttr.h" #include "mcutarget.h" -#include <cmakeprojectmanager/cmakekitinformation.h> +#include <cmakeprojectmanager/cmakekitaspect.h> #include <cmakeprojectmanager/cmaketoolmanager.h> -#include <coreplugin/icore.h> #include <debugger/debuggeritem.h> #include <debugger/debuggeritemmanager.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/toolchain.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> +#include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionmanager.h> #include <utils/algorithm.h> @@ -115,15 +115,15 @@ public: k->makeSticky(); if (mcuTarget->toolChainPackage()->isDesktopToolchain()) k->setDeviceTypeForIcon(DEVICE_TYPE); - k->setValue(QtSupport::SuppliesQtQuickImportPath::id(), true); + k->setValue(QtSupport::Constants::FLAGS_SUPPLIES_QTQUICK_IMPORT_PATH, true); // FIXME: This is treated as a pathlist in CMakeBuildSystem::updateQmlJSCodeModel - k->setValue(QtSupport::KitQmlImportPath::id(), (sdkPath / "include/qul").toString()); - k->setValue(QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), true); + k->setValue(QtSupport::Constants::KIT_QML_IMPORT_PATH, (sdkPath / "include/qul").toString()); + k->setValue(QtSupport::Constants::KIT_HAS_MERGED_HEADER_PATHS_WITH_QML_IMPORT_PATHS, true); QSet<Id> irrelevant = { SysRootKitAspect::id(), - QtSupport::SuppliesQtQuickImportPath::id(), - QtSupport::KitQmlImportPath::id(), - QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), + QtSupport::Constants::FLAGS_SUPPLIES_QTQUICK_IMPORT_PATH, + QtSupport::Constants::KIT_QML_IMPORT_PATH, + QtSupport::Constants::KIT_HAS_MERGED_HEADER_PATHS_WITH_QML_IMPORT_PATHS, }; if (!McuSupportOptions::kitsNeedQtVersion()) irrelevant.insert(QtSupport::QtKitAspect::id()); @@ -413,7 +413,7 @@ static FilePath kitDependencyPath(const Kit *kit, const QString &cmakeVariableNa if (configItem.key == keyName) return FilePath::fromUserInput(QString::fromUtf8(configItem.value)); } - return FilePath(); + return {}; } // Kit Information @@ -668,7 +668,7 @@ void fixExistingKits(const SettingsHandler::Ptr &settingsHandler) // Check if the MCU kits are flagged as supplying a QtQuick import path, in order // to tell the QMLJS code-model that it won't need to add a fall-back import // path. - const auto bringsQtQuickImportPath = QtSupport::SuppliesQtQuickImportPath::id(); + const Id bringsQtQuickImportPath = QtSupport::Constants::FLAGS_SUPPLIES_QTQUICK_IMPORT_PATH; auto irrelevantAspects = kit->irrelevantAspects(); if (!irrelevantAspects.contains(bringsQtQuickImportPath)) { irrelevantAspects.insert(bringsQtQuickImportPath); @@ -679,7 +679,7 @@ void fixExistingKits(const SettingsHandler::Ptr &settingsHandler) } // Check if the MCU kit supplies its import path. - const auto kitQmlImportPath = QtSupport::KitQmlImportPath::id(); + const Id kitQmlImportPath = QtSupport::Constants::KIT_QML_IMPORT_PATH; if (!irrelevantAspects.contains(kitQmlImportPath)) { irrelevantAspects.insert(kitQmlImportPath); kit->setIrrelevantAspects(irrelevantAspects); @@ -698,7 +698,7 @@ void fixExistingKits(const SettingsHandler::Ptr &settingsHandler) } // Check if the MCU kit has the flag for merged header/qml-import paths set. - const auto mergedPaths = QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(); + const Id mergedPaths = QtSupport::Constants::KIT_HAS_MERGED_HEADER_PATHS_WITH_QML_IMPORT_PATHS; if (!irrelevantAspects.contains(mergedPaths)) { irrelevantAspects.insert(mergedPaths); kit->setIrrelevantAspects(irrelevantAspects); diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 1c079eebdfb..84c9d7d2d5c 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -33,7 +33,7 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, const QString &label, const FilePath &defaultPath, const FilePath &detectionPath, - const QString &settingsKey, + const Key &settingsKey, const QString &cmakeVarName, const QString &envVarName, const QStringList &versions, @@ -65,7 +65,7 @@ QString McuPackage::label() const return m_label; } -QString McuPackage::settingsKey() const +Key McuPackage::settingsKey() const { return m_settingsKey; } @@ -334,7 +334,7 @@ McuToolChainPackage::McuToolChainPackage(const SettingsHandler::Ptr &settingsHan const QString &label, const FilePath &defaultPath, const FilePath &detectionPath, - const QString &settingsKey, + const Key &settingsKey, McuToolChainPackage::ToolChainType type, const QStringList &versions, const QString &cmakeVarName, diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index 1bd3b26fb4d..7008b2c499d 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -12,8 +12,6 @@ #include <QObject> -QT_FORWARD_DECLARE_CLASS(QWidget) - namespace ProjectExplorer { class ToolChain; } @@ -30,12 +28,11 @@ class McuPackage : public McuAbstractPackage Q_OBJECT public: - McuPackage( - const SettingsHandler::Ptr &settingsHandler, + McuPackage(const SettingsHandler::Ptr &settingsHandler, const QString &label, const Utils::FilePath &defaultPath, const Utils::FilePath &detectionPath, - const QString &settingsKey, + const Utils::Key &settingsKey, const QString &cmakeVarName, const QString &envVarName, const QStringList &versions = {}, @@ -58,7 +55,7 @@ public: Utils::FilePath path() const override; Utils::FilePath defaultPath() const override; Utils::FilePath detectionPath() const override; - QString settingsKey() const final; + Utils::Key settingsKey() const final; void updateStatus() override; Status status() const override; @@ -85,7 +82,7 @@ private: const QString m_label; Utils::FilePath m_defaultPath; const Utils::FilePath m_detectionPath; - const QString m_settingsKey; + const Utils::Key m_settingsKey; QScopedPointer<const McuPackageVersionDetector> m_versionDetector; Utils::FilePath m_path; @@ -110,7 +107,7 @@ public: const QString &label, const Utils::FilePath &defaultPath, const Utils::FilePath &detectionPath, - const QString &settingsKey, + const Utils::Key &settingsKey, ToolChainType toolchainType, const QStringList &versions, const QString &cmakeVarName, diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index 36c8bdbd47b..e944ba5ce97 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -5,9 +5,8 @@ QtcPlugin { Depends { name: "Qt.core" } Depends { name: "Qt.widgets" } - Depends { name: "Qt.testlib"; condition: qtc.testsEnabled } + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } Depends { name: "Utils" } - Depends { name: "app_version_header" } Depends { name: "Core" } Depends { name: "BareMetal" } @@ -15,9 +14,10 @@ QtcPlugin { Depends { name: "Debugger" } Depends { name: "CMakeProjectManager" } Depends { name: "QmlJS" } + Depends { name: "QmlJSTools" } Depends { name: "QtSupport" } - Depends { name: "qtc_gtest_gmock"; condition: qtc.testsEnabled; required: false } + Depends { name: "qtc_gtest_gmock"; condition: qtc.withPluginTests; required: false } files: [ "mcuabstractpackage.h", @@ -53,18 +53,20 @@ QtcPlugin { "mcusupportversiondetection.cpp", "mcusupportversiondetection.h", "mcutargetdescription.h", - "mcukitinformation.cpp", - "mcukitinformation.h", + "mcukitaspect.cpp", + "mcukitaspect.h", "mcuhelpers.cpp", "mcuhelpers.h", "settingshandler.h", "settingshandler.cpp", "dialogs/mcukitcreationdialog.h", "dialogs/mcukitcreationdialog.cpp", + "mcusupportimportprovider.h", + "mcusupportimportprovider.cpp", ] QtcTestFiles { - condition: qtc.testsEnabled && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent) + condition: qtc.withPluginTests && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent) prefix: "test/" files: [ "packagemock.h", @@ -74,7 +76,7 @@ QtcPlugin { } Properties { - condition: qtc.testsEnabled && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent) + condition: qtc.withPluginTests && (qtc_gtest_gmock.hasRepo || qtc_gtest_gmock.externalLibsPresent) cpp.defines: base.concat(["GOOGLE_TEST_IS_FOUND"]) cpp.includePaths: base.concat([ "." ]) } diff --git a/src/plugins/mcusupport/mcusupport.qrc b/src/plugins/mcusupport/mcusupport.qrc index dc64ac1ca2f..f6e5c151332 100644 --- a/src/plugins/mcusupport/mcusupport.qrc +++ b/src/plugins/mcusupport/mcusupport.qrc @@ -13,8 +13,8 @@ <file>wizards/qmlproject/DejaVuSansMono.ttf</file> <file>wizards/qmlproject/LICENSE</file> <file>wizards/qmlproject/translation.nb_NO.ts</file> + <file>wizards/qmlproject/translation.en_US.ts</file> <file>wizards/qmlproject/BackendObject.h</file> - <file>wizards/qmlproject/main_big.qml.tpl</file> <file>wizards/qmlproject/main.qml.tpl</file> <file>wizards/qmlproject/project.qmlproject.tpl</file> <file>wizards/qmlproject/module.qmlproject.tpl</file> diff --git a/src/plugins/mcusupport/mcusupportdevice.cpp b/src/plugins/mcusupport/mcusupportdevice.cpp index 9e3118cab6a..ad611f265fe 100644 --- a/src/plugins/mcusupport/mcusupportdevice.cpp +++ b/src/plugins/mcusupport/mcusupportdevice.cpp @@ -16,7 +16,7 @@ McuSupportDevice::McuSupportDevice() setupId(IDevice::AutoDetected, Constants::DEVICE_ID); setType(Constants::DEVICE_TYPE); const QString displayNameAndType = Tr::tr("MCU Device"); - setDefaultDisplayName(displayNameAndType); + settings()->displayName.setDefaultValue(displayNameAndType); setDisplayType(displayNameAndType); setDeviceState(IDevice::DeviceStateUnknown); setMachineType(IDevice::Hardware); diff --git a/src/plugins/mcusupport/mcusupportimportprovider.cpp b/src/plugins/mcusupport/mcusupportimportprovider.cpp new file mode 100644 index 00000000000..b78baf8bd94 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportimportprovider.cpp @@ -0,0 +1,185 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mcusupportimportprovider.h" + +#include <cmakeprojectmanager/cmakeprojectconstants.h> +#include <languageutils/componentversion.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> +#include <qmljs/qmljsbind.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/qmljsinterpreter.h> +#include <qmljs/qmljsvalueowner.h> + +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonValue> +#include <QRegularExpression> + +#include <optional> + +static const char uriPattern[]{R"(uri\s*:\s*[\'\"](\w+)[\'\"])"}; + +namespace McuSupport::Internal { +using namespace ProjectExplorer; + +// Get the MCU target build folder for a file +static std::optional<FilePath> getTargetBuildFolder(const FilePath &path) +{ + const Project *project = ProjectExplorer::ProjectManager::projectForFile(path); + if (!project) + return std::nullopt; + auto node = project->nodeForFilePath(path); + if (!node) + return std::nullopt; + + // Get the cmake target node (CMakeTargetNode is internal) + // Go up in hierarchy until the first CMake target node + const ProjectNode *targetNode = nullptr; + FilePath projectBuildFolder; + while (node) { + targetNode = node->asProjectNode(); + if (!targetNode) { + node = node->parentProjectNode(); + continue; + } + projectBuildFolder = FilePath::fromVariant( + targetNode->data(CMakeProjectManager::Constants::BUILD_FOLDER_ROLE)); + if (!projectBuildFolder.isDir()) { + node = node->parentProjectNode(); + targetNode = nullptr; + continue; + } + break; + } + + if (!targetNode) + return std::nullopt; + + return projectBuildFolder / "CMakeFiles" / (project->displayName() + ".dir"); +}; + +QList<Import> McuSupportImportProvider::imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot) const +{ + QList<Import> ret; + + const FilePath path = context->fileName(); + const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path); + if (!cmakeFilesPathOpt) + return {}; + + const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json"; + + const QJsonDocument doc = QJsonDocument::fromJson(inputFilePath.fileContents().value_or("")); + if (!doc.isObject()) + return {}; + const QJsonObject mainProjectObj = doc.object(); + for (const QJsonValue &moduleValue : mainProjectObj["modulesDependencies"].toArray()) { + if (!moduleValue.isObject()) + continue; + const FilePath modulePath = FilePath::fromUserInput( + moduleValue.toObject()["qmlProjectFile"].toString()); + const QString fileContent = QString::fromLatin1(modulePath.fileContents().value_or("")); + QRegularExpressionMatch uriMatch = QRegularExpression(uriPattern).match(fileContent); + if (!uriMatch.hasMatch()) + continue; + QString moduleUri = uriMatch.captured(1); + const FilePath moduleFolder = *cmakeFilesPathOpt / moduleUri; + + Import import; + import.valid = true; + import.object = new ObjectValue(valueOwner, moduleUri); + import.info = ImportInfo::moduleImport(moduleUri, {1, 0}, QString()); + for (const auto &qmlFilePath : moduleFolder.dirEntries(FileFilter({"*.qml"}, QDir::Files))) { + Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml); + doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or(""))); + doc->parseQml(); + snapshot->insert(doc, true); + import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue()); + } + ret << import; + } + return ret; +}; + +void McuSupportImportProvider::loadBuiltins(ImportsPerDocument *importsPerDocument, + Imports *imports, + const Document *context, + ValueOwner *valueOwner, + Snapshot *snapshot) +{ + Import import; + import.valid = true; + import.object = new ObjectValue(valueOwner, "<qul>"); + import.info = ImportInfo::moduleImport("qul", {1, 0}, QString()); + getInterfacesImport(context->fileName(), importsPerDocument, import, valueOwner, snapshot); + imports->append(import); +}; + +void McuSupportImportProvider::getInterfacesImport(const FilePath &path, + ImportsPerDocument *importsPerDocument, + Import &import, + ValueOwner *valueOwner, + Snapshot *snapshot) const +{ + const std::optional<FilePath> cmakeFilesPathOpt = getTargetBuildFolder(path); + if (!cmakeFilesPathOpt) + return; + + const FilePath inputFilePath = *cmakeFilesPathOpt / "config/input.json"; + + std::optional<FilePath> fileModule = getFileModule(path, inputFilePath); + FilePath lookupDir = *cmakeFilesPathOpt + / (fileModule && !fileModule->isEmpty() + ? QRegularExpression(uriPattern) + .match(QString::fromLatin1( + fileModule->fileContents().value_or(""))) + .captured(1) + : ""); + + for (const auto &qmlFilePath : lookupDir.dirEntries(FileFilter({"*.qml"}, QDir::Files))) { + Document::MutablePtr doc = Document::create(qmlFilePath, Dialect::Qml); + doc->setSource(QString::fromLatin1(qmlFilePath.fileContents().value_or(""))); + doc->parseQml(); + snapshot->insert(doc, true); + import.object->setMember(doc->componentName(), doc->bind()->rootObjectValue()); + importsPerDocument->insert(doc.data(), QSharedPointer<Imports>(new Imports(valueOwner))); + } +} + +std::optional<FilePath> McuSupportImportProvider::getFileModule(const FilePath &file, + const FilePath &inputFile) const +{ + const auto doc = QJsonDocument::fromJson(inputFile.fileContents().value_or("")); + if (!doc.isObject()) + return {}; + + const QJsonObject mainProjectObject = doc.object(); + + // Mapping module objects starting with mainProject + const QJsonArray mainProjectQmlFiles = mainProjectObject["QmlFiles"].toArray(); + if (std::any_of(mainProjectQmlFiles.constBegin(), + mainProjectQmlFiles.constEnd(), + [&file](const QJsonValue &value) { + return FilePath::fromUserInput(value.toString()) == file; + })) + return std::nullopt; + + for (const QJsonValue &module : mainProjectObject["modulesDependencies"].toArray()) { + const QJsonObject moduleObject = module.toObject(); + const QJsonArray moduleQmlFiles = moduleObject["QmlFiles"].toArray(); + if (std::any_of(moduleQmlFiles.constBegin(), + moduleQmlFiles.constEnd(), + [&file](const QJsonValue &value) { + return FilePath::fromUserInput(value.toString()) == file; + })) + return FilePath::fromUserInput(moduleObject["qmlProjectFile"].toString()); + } + return std::nullopt; +} +}; // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportimportprovider.h b/src/plugins/mcusupport/mcusupportimportprovider.h new file mode 100644 index 00000000000..829fa711505 --- /dev/null +++ b/src/plugins/mcusupport/mcusupportimportprovider.h @@ -0,0 +1,48 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "mcusupport_global.h" +#include "mcusupportplugin.h" + +#include <utils/filepath.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/qmljsinterpreter.h> + +namespace McuSupport::Internal { +using namespace QmlJS; +using namespace Utils; +class McuSupportImportProvider : public CustomImportsProvider +{ +public: + McuSupportImportProvider() {} + ~McuSupportImportProvider() {} + + // Overridden functions + virtual QList<Import> imports(ValueOwner *valueOwner, + const Document *context, + Snapshot *snapshot) const override; + virtual void loadBuiltins(ImportsPerDocument *importsPerDocument, + Imports *imports, + const Document *context, + ValueOwner *valueOwner, + Snapshot *snapshot) override; + + // Add to the interfaces needed for a document + // path: opened qml document + // importsPerDocument: imports available in the document (considered imported) + // import: qul import containing builtin types (interfaces) + // valueOwner: imports members owner + // snapshot: where qul documenents live + void getInterfacesImport(const FilePath &path, + ImportsPerDocument *importsPerDocument, + Import &import, + ValueOwner *valueOwner, + Snapshot *snapshot) const; + + // Get the qmlproject module which a qmlfile is part of + // nullopt means the file is part of the main project + std::optional<FilePath> getFileModule(const FilePath &file, const FilePath &inputFile) const; +}; +}; // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/mcusupportoptions.cpp b/src/plugins/mcusupport/mcusupportoptions.cpp index 51cb1eacd81..187591298cf 100644 --- a/src/plugins/mcusupport/mcusupportoptions.cpp +++ b/src/plugins/mcusupport/mcusupportoptions.cpp @@ -14,15 +14,15 @@ #include "mcutarget.h" #include "settingshandler.h" -#include <cmakeprojectmanager/cmakekitinformation.h> +#include <cmakeprojectmanager/cmakekitaspect.h> #include <cmakeprojectmanager/cmaketoolmanager.h> #include <coreplugin/helpmanager.h> #include <coreplugin/icore.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <utils/algorithm.h> #include <utils/filepath.h> #include <utils/infobar.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <QMessageBox> diff --git a/src/plugins/mcusupport/mcusupportoptions.h b/src/plugins/mcusupport/mcusupportoptions.h index 1090f871004..5823bed4529 100644 --- a/src/plugins/mcusupport/mcusupportoptions.h +++ b/src/plugins/mcusupport/mcusupportoptions.h @@ -13,8 +13,6 @@ #include <QObject> #include <QVersionNumber> -QT_FORWARD_DECLARE_CLASS(QWidget) - namespace Utils { class FilePath; class PathChooser; diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index 90bc2af509c..b9cd52a0e06 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -4,11 +4,11 @@ #include "mcusupportplugin.h" #include "mcubuildstep.h" -#include "mcukitinformation.h" #include "mcukitmanager.h" #include "mcuqmlprojectnode.h" #include "mcusupportconstants.h" #include "mcusupportdevice.h" +#include "mcusupportimportprovider.h" #include "mcusupportoptions.h" #include "mcusupportoptionspage.h" #include "mcusupportrunconfiguration.h" @@ -104,8 +104,8 @@ public: SettingsHandler::Ptr m_settingsHandler{new SettingsHandler}; McuSupportOptions m_options{m_settingsHandler}; McuSupportOptionsPage optionsPage{m_options, m_settingsHandler}; - McuDependenciesKitAspect environmentPathsKitAspect; MCUBuildStepFactory mcuBuildStepFactory; + McuSupportImportProvider mcuImportProvider; }; // class McuSupportPluginPrivate static McuSupportPluginPrivate *dd{nullptr}; diff --git a/src/plugins/mcusupport/mcusupportrunconfiguration.cpp b/src/plugins/mcusupport/mcusupportrunconfiguration.cpp index 8b80f09fccc..5b397801c8f 100644 --- a/src/plugins/mcusupport/mcusupportrunconfiguration.cpp +++ b/src/plugins/mcusupport/mcusupportrunconfiguration.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> -#include <cmakeprojectmanager/cmakekitinformation.h> +#include <cmakeprojectmanager/cmakekitaspect.h> #include <cmakeprojectmanager/cmaketool.h> #include <utils/aspects.h> @@ -42,22 +42,23 @@ static QStringList flashAndRunArgs(const RunConfiguration *rc, const Target *tar class FlashAndRunConfiguration final : public RunConfiguration { public: - FlashAndRunConfiguration(Target *target, Utils::Id id) + FlashAndRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto flashAndRunParameters = addAspect<StringAspect>(); - flashAndRunParameters->setLabelText(Tr::tr("Flash and run CMake parameters:")); - flashAndRunParameters->setDisplayStyle(StringAspect::TextEditDisplay); - flashAndRunParameters->setSettingsKey("FlashAndRunConfiguration.Parameters"); + flashAndRunParameters.setLabelText(Tr::tr("Flash and run CMake parameters:")); + flashAndRunParameters.setDisplayStyle(StringAspect::TextEditDisplay); + flashAndRunParameters.setSettingsKey("FlashAndRunConfiguration.Parameters"); - setUpdater([target, flashAndRunParameters, this] { - flashAndRunParameters->setValue(flashAndRunArgs(this, target).join(' ')); + setUpdater([target, this] { + flashAndRunParameters.setValue(flashAndRunArgs(this, target).join(' ')); }); update(); connect(target->project(), &Project::displayNameChanged, this, &RunConfiguration::update); } + + StringAspect flashAndRunParameters{this}; }; class FlashAndRunWorker : public SimpleTargetRunner diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index d29aea4a54f..a3eb731ea2a 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -120,7 +120,7 @@ McuPackagePtr createBoardSdkPackage(const SettingsHandler::Ptr &settingsHandler, sdkName, defaultPath, {}, // detection path - desc.boardSdk.envVar, // settings key + keyFromString(desc.boardSdk.envVar), // settings key Constants::BOARD_SDK_CMAKE_VAR, // cmake var desc.boardSdk.envVar, // env var desc.boardSdk.versions, @@ -145,7 +145,7 @@ McuPackagePtr createFreeRTOSSourcesPackage(const SettingsHandler::Ptr &settingsH QString::fromLatin1("FreeRTOS Sources (%1)").arg(envVarPrefix), defaultPath, "tasks.c", // detection path - QString{Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(envVarPrefix), + Constants::SETTINGS_KEY_FREERTOS_PREFIX + keyFromString(envVarPrefix), "FREERTOS_DIR", // cmake var envVar, // env var {}, // versions @@ -687,7 +687,7 @@ static PackageDescription parsePackage(const QJsonObject &cmakeEntry) cmakeEntry["envVar"].toString(), cmakeEntry["cmakeVar"].toString(), cmakeEntry["description"].toString(), - cmakeEntry["setting"].toString(), + keyFromString(cmakeEntry["setting"].toString()), FilePath::fromUserInput(defaultPathString), FilePath::fromUserInput(detectionPathString), versions, diff --git a/src/plugins/mcusupport/mcutargetdescription.h b/src/plugins/mcusupport/mcutargetdescription.h index ebf7e8c7ef6..e495b261922 100644 --- a/src/plugins/mcusupport/mcutargetdescription.h +++ b/src/plugins/mcusupport/mcutargetdescription.h @@ -28,7 +28,7 @@ struct PackageDescription QString envVar; QString cmakeVar; QString description; - QString setting; + Utils::Key setting; Utils::FilePath defaultPath; Utils::FilePath detectionPath; QStringList versions; diff --git a/src/plugins/mcusupport/settingshandler.cpp b/src/plugins/mcusupport/settingshandler.cpp index 67fbd27f978..2bae687cfc8 100644 --- a/src/plugins/mcusupport/settingshandler.cpp +++ b/src/plugins/mcusupport/settingshandler.cpp @@ -6,29 +6,28 @@ #include "mcusupportconstants.h" #include <coreplugin/icore.h> + #include <utils/filepath.h> +#include <utils/store.h> + +using namespace Utils; namespace McuSupport::Internal { -using Utils::FilePath; +const Key automaticKitCreationSettingsKey = Key(Constants::SETTINGS_GROUP) + '/' + + Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION; -namespace { -const QString automaticKitCreationSettingsKey = QLatin1String(Constants::SETTINGS_GROUP) + '/' - + QLatin1String( - Constants::SETTINGS_KEY_AUTOMATIC_KIT_CREATION); -} - -static FilePath packagePathFromSettings(const QString &settingsKey, - QSettings &settings, +static FilePath packagePathFromSettings(const Key &settingsKey, + QtcSettings &settings, const FilePath &defaultPath) { - const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' - + QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + settingsKey; + const Key key = Key(Constants::SETTINGS_GROUP) + '/' + + Constants::SETTINGS_KEY_PACKAGE_PREFIX + settingsKey; const QString path = settings.value(key, defaultPath.toUserOutput()).toString(); return FilePath::fromUserInput(path); } -FilePath SettingsHandler::getPath(const QString &settingsKey, +FilePath SettingsHandler::getPath(const Key &settingsKey, QSettings::Scope scope, const FilePath &defaultPath) const { @@ -39,15 +38,14 @@ FilePath SettingsHandler::getPath(const QString &settingsKey, return packagePathFromSettings(settingsKey, *Core::ICore::settings(scope), defaultPath); } -bool SettingsHandler::write(const QString &settingsKey, +bool SettingsHandler::write(const Key &settingsKey, const FilePath &path, const FilePath &defaultPath) const { const FilePath savedPath = packagePathFromSettings(settingsKey, *Core::ICore::settings(QSettings::UserScope), defaultPath); - const QString key = QLatin1String(Constants::SETTINGS_GROUP) + '/' - + QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX) + settingsKey; + const Key key = Key(Constants::SETTINGS_GROUP) + '/' + Constants::SETTINGS_KEY_PACKAGE_PREFIX + settingsKey; Core::ICore::settings()->setValueWithDefault(key, path.toUserOutput(), defaultPath.toUserOutput()); @@ -57,26 +55,26 @@ bool SettingsHandler::write(const QString &settingsKey, bool SettingsHandler::isAutomaticKitCreationEnabled() const { - QSettings *settings = Core::ICore::settings(QSettings::UserScope); + QtcSettings *settings = Core::ICore::settings(QSettings::UserScope); const bool automaticKitCreation = settings->value(automaticKitCreationSettingsKey, true).toBool(); return automaticKitCreation; } void SettingsHandler::setAutomaticKitCreation(bool isEnabled) { - QSettings *settings = Core::ICore::settings(QSettings::UserScope); + QtcSettings *settings = Core::ICore::settings(QSettings::UserScope); settings->setValue(automaticKitCreationSettingsKey, isEnabled); } void SettingsHandler::setInitialPlatformName(const QString &platform) { - QSettings *settings = Core::ICore::settings(QSettings::UserScope); + QtcSettings *settings = Core::ICore::settings(QSettings::UserScope); settings->setValue(Constants::SETTINGS_KEY_INITIAL_PLATFORM_KEY, platform); } QString SettingsHandler::initialPlatformName() const { - QSettings *settings = Core::ICore::settings(QSettings::UserScope); + QtcSettings *settings = Core::ICore::settings(QSettings::UserScope); const QString name = settings->value(Constants::SETTINGS_KEY_INITIAL_PLATFORM_KEY, "").toString(); return name; diff --git a/src/plugins/mcusupport/settingshandler.h b/src/plugins/mcusupport/settingshandler.h index 145049e31db..477f04da42a 100644 --- a/src/plugins/mcusupport/settingshandler.h +++ b/src/plugins/mcusupport/settingshandler.h @@ -2,13 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once +#include <utils/storekey.h> + #include <QSettings> #include <QSharedPointer> -#include <QString> -namespace Utils { -class FilePath; -} //namespace Utils +namespace Utils { class FilePath; } namespace McuSupport::Internal { @@ -17,11 +16,11 @@ class SettingsHandler public: using Ptr = QSharedPointer<SettingsHandler>; virtual ~SettingsHandler() = default; - virtual Utils::FilePath getPath(const QString &settingsKey, + virtual Utils::FilePath getPath(const Utils::Key &settingsKey, QSettings::Scope scope, const Utils::FilePath &m_defaultPath) const; - virtual bool write(const QString &settingsKey, + virtual bool write(const Utils::Key &settingsKey, const Utils::FilePath &path, const Utils::FilePath &defaultPath) const; @@ -29,6 +28,6 @@ public: void setAutomaticKitCreation(bool isEnabled); void setInitialPlatformName(const QString &platform); QString initialPlatformName() const; +}; -}; //class SettingsHandler } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/test/packagemock.h b/src/plugins/mcusupport/test/packagemock.h index 1e62fb77646..06d3be62305 100644 --- a/src/plugins/mcusupport/test/packagemock.h +++ b/src/plugins/mcusupport/test/packagemock.h @@ -6,7 +6,9 @@ #include "mcuabstractpackage.h" #include <gmock/gmock.h> + #include <utils/filepath.h> +#include <utils/storekey.h> namespace McuSupport::Internal { @@ -21,7 +23,7 @@ public: MOCK_METHOD(Utils::FilePath, detectionPath, (), (const)); MOCK_METHOD(QString, statusText, (), (const)); MOCK_METHOD(void, updateStatus, ()); - MOCK_METHOD(QString, settingsKey, (), (const)); + MOCK_METHOD(Utils::Key, settingsKey, (), (const)); MOCK_METHOD(Status, status, (), (const)); MOCK_METHOD(bool, isValidStatus, (), (const)); @@ -34,5 +36,6 @@ public: MOCK_METHOD(QWidget *, widget, ()); MOCK_METHOD(const McuPackageVersionDetector *, getVersionDetector, (), (const)); -}; // class PackageMock +}; + } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/test/settingshandlermock.h b/src/plugins/mcusupport/test/settingshandlermock.h index cecf1415c2e..1cb3faf4730 100644 --- a/src/plugins/mcusupport/test/settingshandlermock.h +++ b/src/plugins/mcusupport/test/settingshandlermock.h @@ -5,7 +5,9 @@ #include "settingshandler.h" #include <gmock/gmock.h> + #include <utils/filepath.h> +#include <utils/storekey.h> namespace McuSupport::Internal { @@ -14,13 +16,15 @@ class SettingsHandlerMock : public SettingsHandler public: SettingsHandlerMock() = default; ~SettingsHandlerMock() override = default; + MOCK_METHOD(Utils::FilePath, getPath, - (const QString &, QSettings::Scope, const Utils::FilePath &), + (const Utils::Key &, QSettings::Scope, const Utils::FilePath &), (const, override)); MOCK_METHOD(bool, write, - (const QString &, const Utils::FilePath &, const Utils::FilePath &), + (const Utils::Key &, const Utils::FilePath &, const Utils::FilePath &), (const, override)); -}; //class SettingsHandler +}; + } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 697472c23fe..13ed66f8035 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -33,12 +33,12 @@ #include <baremetal/baremetalconstants.h> #include <cmakeprojectmanager/cmakeconfigitem.h> -#include <cmakeprojectmanager/cmakekitinformation.h> +#include <cmakeprojectmanager/cmakekitaspect.h> #include <gmock/gmock-actions.h> #include <gmock/gmock.h> #include <projectexplorer/customtoolchain.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/toolchain.h> @@ -293,7 +293,7 @@ void verifyTargetToolchains(const Targets &targets, const QString &toolchainFilePath, const QString &toolchainFileDefaultPath, const QString &compilerPath, - const QString &compilerSetting, + const Key &compilerSetting, const QStringList &versions) { QCOMPARE(targets.size(), 1); @@ -322,7 +322,7 @@ void verifyBoardSdk(const McuPackagePtr &boardSdk, QVERIFY(boardSdk); QCOMPARE(boardSdk->cmakeVariableName(), cmakeVariable); QCOMPARE(boardSdk->environmentVariableName(), environmentVariable); - QCOMPARE(boardSdk->settingsKey(), environmentVariable); + QCOMPARE(boardSdk->settingsKey(), keyFromString(environmentVariable)); QCOMPARE(boardSdk->detectionPath().toString(), empty); QCOMPARE(boardSdk->versions(), versions); } @@ -332,7 +332,7 @@ void verifyFreeRtosPackage(const McuPackagePtr &freeRtos, const FilePath &boardSdkDir, const QString &freeRtosPath, const QString &freeRtosDetectionPath, - const QString &expectedSettingsKey) + const Key &expectedSettingsKey) { QVERIFY(freeRtos); QCOMPARE(freeRtos->environmentVariableName(), envVar); @@ -346,7 +346,7 @@ void verifyFreeRtosPackage(const McuPackagePtr &freeRtos, void verifyPackage(const McuPackagePtr &package, const QString &path, const QString &defaultPath, - const QString &setting, + const Key &setting, const QString &cmakeVar, const QString &envVar, const QString &label, @@ -468,7 +468,7 @@ void McuSupportTest::initTestCase() ON_CALL(*sdkPackage, label()).WillByDefault(Return(QString{QUL_LABEL})); ON_CALL(*sdkPackage, settingsKey()) - .WillByDefault(Return(QString{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK})); + .WillByDefault(Return(Key{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK})); ON_CALL(*sdkPackage, environmentVariableName()).WillByDefault(Return(QString{QUL_ENV_VAR})); ON_CALL(*sdkPackage, cmakeVariableName()).WillByDefault(Return(QString{QUL_CMAKE_VAR})); ON_CALL(*sdkPackage, isValidStatus()).WillByDefault(Return(true)); @@ -487,7 +487,7 @@ void McuSupportTest::initTestCase() EXPECT_CALL(*armGccToolchainFilePackage, detectionPath()).WillRepeatedly(Return(FilePath{})); ON_CALL(*settingsMockPtr, getPath) - .WillByDefault([](const QString &, QSettings::Scope, const FilePath &m_defaultPath) { + .WillByDefault([](const Key &, QSettings::Scope, const FilePath &m_defaultPath) { return m_defaultPath; }); } @@ -685,41 +685,39 @@ void McuSupportTest::test_mapParsedToolchainIdToCorrespondingType() void McuSupportTest::test_legacy_createPackagesWithCorrespondingSettings_data() { QTest::addColumn<QString>("json"); - QTest::addColumn<QSet<QString>>("expectedSettings"); + QTest::addColumn<QSet<Key>>("expectedSettings"); - QSet<QString> commonSettings{{"CypressAutoFlashUtil"}, - {"MCUXpressoIDE"}, - {"RenesasFlashProgrammer"}, - {"Stm32CubeProgrammer"}}; + QSet<Key> commonSettings{{"CypressAutoFlashUtil"}, + {"MCUXpressoIDE"}, + {"RenesasFlashProgrammer"}, + {"Stm32CubeProgrammer"}}; QTest::newRow("iar_mimxrt1064_evk_freertos_json") << iar_mimxrt1064_evk_freertos_json - << QSet<QString>{{"EVK_MIMXRT1064_SDK_PATH"}, - {QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append( - "IMXRT1064")}, - "IARToolchain"} + << QSet<Key>{"EVK_MIMXRT1064_SDK_PATH", + Key{QByteArray(Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX).append("IMXRT1064")}, + "IARToolchain"} .unite(commonSettings); QTest::newRow("stm32f469i") << iar_stm32f469i_discovery_baremetal_json - << QSet<QString>{{"STM32Cube_FW_F4_SDK_PATH"}, "IARToolchain"}.unite( + << QSet<Key>{{"STM32Cube_FW_F4_SDK_PATH"}, "IARToolchain"}.unite( commonSettings); - QTest::newRow("nxp1050") << armgcc_mimxrt1050_evk_freertos_json - << QSet<QString>{{"EVKB_IMXRT1050_SDK_PATH"}, - {QString{ - Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX} - .append("IMXRT1050")}, - "GNUArmEmbeddedToolchain"} - .unite(commonSettings); + QTest::newRow("nxp1050") + << armgcc_mimxrt1050_evk_freertos_json + << QSet<Key>{"EVKB_IMXRT1050_SDK_PATH", + Key{QByteArray(Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX).append("IMXRT1050")}, + "GNUArmEmbeddedToolchain"} + .unite(commonSettings); QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json") << armgcc_stm32h750b_discovery_baremetal_json - << QSet<QString>{{"STM32Cube_FW_H7_SDK_PATH"}, "GNUArmEmbeddedToolchain"}.unite( + << QSet<Key>{{"STM32Cube_FW_H7_SDK_PATH"}, "GNUArmEmbeddedToolchain"}.unite( commonSettings); QTest::newRow("armgcc_stm32f769i_discovery_freertos_json") << armgcc_stm32f769i_discovery_freertos_json - << QSet<QString>{{"STM32Cube_FW_F7_SDK_PATH"}, "GNUArmEmbeddedToolchain"}.unite( + << QSet<Key>{{"STM32Cube_FW_F7_SDK_PATH"}, "GNUArmEmbeddedToolchain"}.unite( commonSettings); QTest::newRow("ghs_rh850_d1m1a_baremetal_json") - << ghs_rh850_d1m1a_baremetal_json << QSet<QString>{"GHSToolchain"}.unite(commonSettings); + << ghs_rh850_d1m1a_baremetal_json << QSet<Key>{"GHSToolchain"}.unite(commonSettings); } void McuSupportTest::test_legacy_createPackagesWithCorrespondingSettings() @@ -730,10 +728,10 @@ void McuSupportTest::test_legacy_createPackagesWithCorrespondingSettings() targetsFromDescriptions({description}, settingsMockPtr, sdkPackagePtr, runLegacy)}; Q_UNUSED(targets); - QSet<QString> settings = transform<QSet<QString>>(packages, [](const auto &package) { + QSet<Key> settings = transform<QSet<Key>>(packages, [](const auto &package) { return package->settingsKey(); }); - QFETCH(QSet<QString>, expectedSettings); + QFETCH(QSet<Key>, expectedSettings); QVERIFY(settings.contains(expectedSettings)); } @@ -851,7 +849,7 @@ void McuSupportTest::test_useFallbackPathForToolchainWhenPathFromSettingsIsNotAv Utils::PathChooser::Kind::ExistingDirectory}; McuTargetDescription::Toolchain toolchainDescription{armGcc, {}, compilerDescription, {}}; - EXPECT_CALL(*settingsMockPtr, getPath(QString{armGccDirectorySetting}, _, FilePath{fallbackDir})) + EXPECT_CALL(*settingsMockPtr, getPath(Key{armGccDirectorySetting}, _, FilePath{fallbackDir})) .Times(2) .WillRepeatedly(Return(FilePath{fallbackDir})); @@ -875,7 +873,7 @@ void McuSupportTest::test_usePathFromSettingsForToolchainPath() Utils::PathChooser::Kind::ExistingDirectory}; McuTargetDescription::Toolchain toolchainDescription{armGcc, {}, compilerDescription, {}}; - EXPECT_CALL(*settingsMockPtr, getPath(QString{armGccDirectorySetting}, _, FilePath{empty})) + EXPECT_CALL(*settingsMockPtr, getPath(Key{armGccDirectorySetting}, _, FilePath{empty})) .Times(2) .WillOnce(Return(FilePath{empty})) // system scope settings .WillOnce(Return(FilePath{armGccDir})); // user scope settings @@ -951,39 +949,39 @@ void McuSupportTest::test_legacy_createTargetWithToolchainPackages_data() QTest::addColumn<QString>("toolchainFilePath"); QTest::addColumn<QString>("toolchainFileDefaultPath"); QTest::addColumn<QString>("compilerPath"); - QTest::addColumn<QString>("compilerSetting"); + QTest::addColumn<Key>("compilerSetting"); QTest::addColumn<QStringList>("versions"); QTest::newRow("armgcc_mimxrt1050_evk_freertos_json") << armgcc_mimxrt1050_evk_freertos_json << armGccToolchainFilePath - << armGccToolchainFileUnexpandedPath << armGccDir << armGccDirectorySetting + << armGccToolchainFileUnexpandedPath << armGccDir << keyFromString(armGccDirectorySetting) << QStringList{armGccVersion}; QTest::newRow("armgcc_mimxrt1064_evk_freertos_json") << armgcc_mimxrt1064_evk_freertos_json << armGccToolchainFilePath - << armGccToolchainFileUnexpandedPath << armGccDir << armGccDirectorySetting + << armGccToolchainFileUnexpandedPath << armGccDir << keyFromString(armGccDirectorySetting) << QStringList{armGccVersion}; QTest::newRow("armgcc_mimxrt1170_evk_freertos_json") << armgcc_mimxrt1170_evk_freertos_json << armGccToolchainFilePath - << armGccToolchainFileUnexpandedPath << armGccDir << armGccDirectorySetting + << armGccToolchainFileUnexpandedPath << armGccDir << keyFromString(armGccDirectorySetting) << QStringList{armGccVersion}; QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json") << armgcc_stm32h750b_discovery_baremetal_json << armGccToolchainFilePath - << armGccToolchainFileUnexpandedPath << armGccDir << armGccDirectorySetting + << armGccToolchainFileUnexpandedPath << armGccDir << keyFromString(armGccDirectorySetting) << QStringList{armGccVersion}; QTest::newRow("armgcc_stm32f769i_discovery_freertos_json") << armgcc_stm32f769i_discovery_freertos_json << armGccToolchainFilePath - << armGccToolchainFileUnexpandedPath << armGccDir << armGccDirectorySetting + << armGccToolchainFileUnexpandedPath << armGccDir << keyFromString(armGccDirectorySetting) << QStringList{armGccVersion}; QTest::newRow("iar_stm32f469i_discovery_baremetal_json") << iar_stm32f469i_discovery_baremetal_json << iarToolchainFilePath - << iarToolchainFileUnexpandedPath << iarDir << iarSetting << iarVersions; + << iarToolchainFileUnexpandedPath << iarDir << keyFromString(iarSetting) << iarVersions; QTest::newRow("iar_mimxrt1064_evk_freertos_json") << iar_mimxrt1064_evk_freertos_json << iarToolchainFilePath - << iarToolchainFileUnexpandedPath << iarDir << iarSetting << iarVersions; + << iarToolchainFileUnexpandedPath << iarDir << keyFromString(iarSetting) << iarVersions; QTest::newRow("ghs_rh850_d1m1a_baremetal_json") << ghs_rh850_d1m1a_baremetal_json << greenhillToolchainFilePath - << greenhillToolchainFileUnexpandedPath << greenhillCompilerDir << greenhillSetting - << greenhillVersions; + << greenhillToolchainFileUnexpandedPath << greenhillCompilerDir + << keyFromString(greenhillSetting) << greenhillVersions; } void McuSupportTest::test_legacy_createTargetWithToolchainPackages() @@ -992,13 +990,13 @@ void McuSupportTest::test_legacy_createTargetWithToolchainPackages() QFETCH(QString, toolchainFilePath); QFETCH(QString, toolchainFileDefaultPath); QFETCH(QString, compilerPath); - QFETCH(QString, compilerSetting); + QFETCH(Key, compilerSetting); QFETCH(QStringList, versions); const McuTargetDescription description = parseDescriptionJson(json.toLocal8Bit()); EXPECT_CALL(*settingsMockPtr, - getPath(QString{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) + getPath(Key{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) .WillRepeatedly(Return(FilePath::fromUserInput(qtForMcuSdkPath))); EXPECT_CALL(*settingsMockPtr, getPath(compilerSetting, _, _)) .WillRepeatedly(Return(FilePath::fromUserInput(compilerPath))); @@ -1026,14 +1024,14 @@ void McuSupportTest::test_createTargetWithToolchainPackages() QFETCH(QString, toolchainFilePath); QFETCH(QString, toolchainFileDefaultPath); QFETCH(QString, compilerPath); - QFETCH(QString, compilerSetting); + QFETCH(Key, compilerSetting); QFETCH(QStringList, versions); EXPECT_CALL(*settingsMockPtr, getPath(compilerSetting, _, _)) .WillRepeatedly(Return(FilePath::fromUserInput(compilerPath))); EXPECT_CALL(*settingsMockPtr, - getPath(QString{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) + getPath(Key{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) .WillRepeatedly(Return(FilePath::fromUserInput(qtForMcuSdkPath))); const McuTargetDescription description = parseDescriptionJson(json.toLocal8Bit()); @@ -1141,28 +1139,28 @@ void McuSupportTest::test_legacy_createFreeRtosPackage_data() { QTest::addColumn<QString>("json"); QTest::addColumn<QStringList>("versions"); - QTest::addColumn<QString>("expectedSettingsKey"); + QTest::addColumn<Key>("expectedSettingsKey"); QTest::addColumn<FilePath>("expectedPath"); QTest::addColumn<FilePath>("expectedDetectionPath"); QTest::newRow("armgcc_mimxrt1050_evk_freertos_json") << armgcc_mimxrt1050_evk_freertos_json << QStringList{boardSdkVersion} - << QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1050) + << keyFromString(QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1050)) << FilePath::fromUserInput(boardSdkDir) / freeRtosNxpPathSuffix << FilePath::fromUserInput(freeRtosDetectionPath); QTest::newRow("armgcc_mimxrt1064_evk_freertos_json") << armgcc_mimxrt1064_evk_freertos_json << QStringList{boardSdkVersion} - << QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1064) + << keyFromString(QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1064)) << FilePath::fromUserInput(boardSdkDir) / freeRtosNxpPathSuffix << FilePath::fromUserInput(freeRtosDetectionPath); QTest::newRow("iar_mimxrt1064_evk_freertos_json") << iar_mimxrt1064_evk_freertos_json << QStringList{boardSdkVersion} - << QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1064) + << keyFromString(QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(nxp1064)) << FilePath::fromUserInput(boardSdkDir) / freeRtosNxpPathSuffix << FilePath::fromUserInput(freeRtosDetectionPath); QTest::newRow("armgcc_stm32f769i_discovery_freertos_json") << armgcc_stm32f769i_discovery_freertos_json << QStringList{"1.16.0"} - << QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(stm32f7) + << keyFromString(QString{Legacy::Constants::SETTINGS_KEY_FREERTOS_PREFIX}.append(stm32f7)) << FilePath::fromUserInput(boardSdkDir) / freeRtosStmPathSuffix << FilePath::fromUserInput(freeRtosDetectionPath); } @@ -1171,7 +1169,7 @@ void McuSupportTest::test_legacy_createFreeRtosPackage() { QFETCH(QString, json); QFETCH(QStringList, versions); - QFETCH(QString, expectedSettingsKey); + QFETCH(Key, expectedSettingsKey); QFETCH(FilePath, expectedPath); QFETCH(FilePath, expectedDetectionPath); @@ -1200,13 +1198,13 @@ void McuSupportTest::test_createFreeRtosPackage() { QFETCH(QString, json); QFETCH(QStringList, versions); - QFETCH(QString, expectedSettingsKey); + QFETCH(Key, expectedSettingsKey); QFETCH(FilePath, expectedPath); QFETCH(FilePath, expectedDetectionPath); McuTargetDescription targetDescription{parseDescriptionJson(json.toLocal8Bit())}; - EXPECT_CALL(*settingsMockPtr, getPath(targetDescription.boardSdk.envVar, _, _)) + EXPECT_CALL(*settingsMockPtr, getPath(keyFromString(targetDescription.boardSdk.envVar), _, _)) .WillRepeatedly(Return(FilePath::fromString(boardSdkDir))); auto [targets, packages]{targetFactory.createTargets(targetDescription, sdkPackagePtr)}; @@ -1254,7 +1252,7 @@ void McuSupportTest::test_legacy_doNOTcreateFreeRtosPackageForMetalVariants() void McuSupportTest::test_legacy_createQtMCUsPackage() { EXPECT_CALL(*settingsMockPtr, - getPath(QString{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) + getPath(Key{Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK}, _, _)) .WillRepeatedly(Return(FilePath::fromUserInput(qtForMcuSdkPath))); McuPackagePtr qtForMCUsSDK = createQtForMCUsPackage(settingsMockPtr); @@ -1497,7 +1495,7 @@ void McuSupportTest::test_legacy_createThirdPartyPackage_data() QTest::addColumn<QString>("json"); QTest::addColumn<QString>("path"); QTest::addColumn<QString>("defaultPath"); - QTest::addColumn<QString>("setting"); + QTest::addColumn<Key>("setting"); QTest::addColumn<QString>("cmakeVar"); QTest::addColumn<QString>("envVar"); QTest::addColumn<QString>("label"); @@ -1506,42 +1504,42 @@ void McuSupportTest::test_legacy_createThirdPartyPackage_data() QTest::newRow("armgcc_mimxrt1050_evk_freertos_json mcuXpresso") << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} << armgcc_mimxrt1050_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_mimxrt1064_evk_freertos_json mcuXpresso") << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} << armgcc_mimxrt1064_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel - << xpressoIdeDetectionPath; + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar + << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_mimxrt1170_evk_freertos_json mcuXpresso") << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} << armgcc_mimxrt1170_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel - << xpressoIdeDetectionPath; + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar + << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json stmCubeProgrammer") << PackageCreator{[this]() { return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr); }} << armgcc_stm32h750b_discovery_baremetal_json << stmCubeProgrammerPath - << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerPath << keyFromString(stmCubeProgrammerSetting) << empty << empty << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; QTest::newRow("armgcc_stm32f769i_discovery_freertos_json stmCubeProgrammer") << PackageCreator{[this]() { return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr); }} << armgcc_stm32f769i_discovery_freertos_json << stmCubeProgrammerPath - << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerPath << keyFromString(stmCubeProgrammerSetting) << empty << empty << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; QTest::newRow("ghs_rh850_d1m1a_baremetal_json renesasProgrammer") << PackageCreator{[this]() { return Legacy::createRenesasProgrammerPackage(settingsMockPtr); }} - << ghs_rh850_d1m1a_baremetal_json << empty << empty << renesasProgrammerSetting - << renesasProgrammerCmakeVar << renesasProgrammerEnvVar << renesasProgrammerLabel - << renesasProgrammerDetectionPath; + << ghs_rh850_d1m1a_baremetal_json << empty << empty + << keyFromString(renesasProgrammerSetting) << renesasProgrammerCmakeVar + << renesasProgrammerEnvVar << renesasProgrammerLabel << renesasProgrammerDetectionPath; } void McuSupportTest::test_legacy_createThirdPartyPackage() @@ -1550,13 +1548,13 @@ void McuSupportTest::test_legacy_createThirdPartyPackage() QFETCH(QString, json); QFETCH(QString, path); QFETCH(QString, defaultPath); - QFETCH(QString, setting); + QFETCH(Key, setting); QFETCH(QString, cmakeVar); QFETCH(QString, envVar); QFETCH(QString, label); QFETCH(QString, detectionPath); - EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, _, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{setting}, _, _)) .Times(2) .WillRepeatedly(Return(FilePath::fromUserInput(defaultPath))); @@ -1577,7 +1575,7 @@ void McuSupportTest::test_createThirdPartyPackage_data() QTest::addColumn<QString>("json"); QTest::addColumn<QString>("path"); QTest::addColumn<QString>("defaultPath"); - QTest::addColumn<QString>("setting"); + QTest::addColumn<Key>("setting"); QTest::addColumn<QString>("cmakeVar"); QTest::addColumn<QString>("envVar"); QTest::addColumn<QString>("label"); @@ -1594,33 +1592,34 @@ void McuSupportTest::test_createThirdPartyPackage_data() QTest::newRow("armgcc_mimxrt1050_evk_freertos_json mcuXpresso") << armgcc_mimxrt1050_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel - << xpressoIdeDetectionPath; + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar + << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_mimxrt1064_evk_freertos_json mcuXpresso") << armgcc_mimxrt1064_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel - << xpressoIdeDetectionPath; + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar + << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_mimxrt1170_evk_freertos_json mcuXpresso") << armgcc_mimxrt1170_evk_freertos_json << xpressoIdePath << xpressoIdePath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel + << keyFromString(xpressoIdeSetting) << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel << xpressoIdeDetectionPath; QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json stmCubeProgrammer") << armgcc_stm32h750b_discovery_baremetal_json << stmCubeProgrammerPath - << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerPath << keyFromString(stmCubeProgrammerSetting) << empty << empty << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; QTest::newRow("armgcc_stm32f769i_discovery_freertos_json stmCubeProgrammer") << armgcc_stm32f769i_discovery_freertos_json << stmCubeProgrammerPath - << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerPath << keyFromString(stmCubeProgrammerSetting) << empty << empty << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; QTest::newRow("ghs_rh850_d1m1a_baremetal_json renesasProgrammer") << ghs_rh850_d1m1a_baremetal_json << renesasProgrammerDefaultPath << empty - << "FlashProgrammerPath" << renesasProgrammerCmakeVar << "RenesasFlashProgrammer_PATH" - << renesasProgrammerLabel << renesasProgrammerDetectionPath; + << keyFromString("FlashProgrammerPath") << renesasProgrammerCmakeVar + << "RenesasFlashProgrammer_PATH" << renesasProgrammerLabel + << renesasProgrammerDetectionPath; } void McuSupportTest::test_createThirdPartyPackage() @@ -1628,7 +1627,7 @@ void McuSupportTest::test_createThirdPartyPackage() QFETCH(QString, json); QFETCH(QString, path); QFETCH(QString, defaultPath); - QFETCH(QString, setting); + QFETCH(Key, setting); QFETCH(QString, cmakeVar); QFETCH(QString, envVar); QFETCH(QString, label); @@ -1636,11 +1635,11 @@ void McuSupportTest::test_createThirdPartyPackage() McuTargetDescription targetDescription{parseDescriptionJson(json.toLocal8Bit())}; - EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::SystemScope, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{setting}, QSettings::SystemScope, _)) .Times(testing::AtMost(1)) .WillOnce(Return(FilePath::fromUserInput(defaultPath))); - EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::UserScope, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{setting}, QSettings::UserScope, _)) .Times(testing::AtMost(1)) .WillOnce(Return(FilePath::fromUserInput(path))); @@ -1664,7 +1663,7 @@ void McuSupportTest::test_createThirdPartyPackage() void McuSupportTest::test_legacy_createCypressProgrammer3rdPartyPackage() { - EXPECT_CALL(*settingsMockPtr, getPath(QString{cypressProgrammerSetting}, _, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{cypressProgrammerSetting}, _, _)) .Times(2) .WillRepeatedly(Return(FilePath::fromUserInput(defaultToolPath))); @@ -1684,11 +1683,11 @@ void McuSupportTest::test_createJLink3rdPartyPackage() { McuTargetDescription targetDescription{parseDescriptionJson(armgcc_ek_ra6m3g_baremetal_json)}; - EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::SystemScope, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{jlinkSetting}, QSettings::SystemScope, _)) .Times(testing::AtMost(1)) .WillOnce(Return(FilePath::fromUserInput(jlinkPath))); - EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::UserScope, _)) + EXPECT_CALL(*settingsMockPtr, getPath(Key{jlinkSetting}, QSettings::UserScope, _)) .Times(testing::AtMost(1)) .WillOnce(Return(FilePath::fromUserInput(jlinkPath))); diff --git a/src/plugins/mcusupport/wizards/qmlproject-empty/project.qmlproject.tpl b/src/plugins/mcusupport/wizards/qmlproject-empty/project.qmlproject.tpl index ab85861a5ec..ead4e315a36 100644 --- a/src/plugins/mcusupport/wizards/qmlproject-empty/project.qmlproject.tpl +++ b/src/plugins/mcusupport/wizards/qmlproject-empty/project.qmlproject.tpl @@ -5,6 +5,7 @@ Project { // importPaths: [] // Alternative API to ModuleFiles for importing modules. // projectRootPath: "." // Optional root path relative to qmlproject file path. mainFile: "%{MainQmlFile}" // The application's entrypoint + idBasedTranslations: true // Use qsTrId() instead of qsTr() MCU.Config { controlsStyle: "QtQuick.Controls.StyleDefault" diff --git a/src/plugins/mcusupport/wizards/qmlproject/main.qml.tpl b/src/plugins/mcusupport/wizards/qmlproject/main.qml.tpl index 44f4788740c..5097435e099 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/main.qml.tpl +++ b/src/plugins/mcusupport/wizards/qmlproject/main.qml.tpl @@ -16,9 +16,10 @@ Rectangle { CustomButton { anchors.centerIn: parent - text: qsTr("Hello world!") + text: qsTrId("hello-world") onClicked: BackendObject.toggle() } BackendObject.onCustomPropertyChanged: Qt.uiLanguage = BackendObject.customProperty ? "en_US" : "nb_NO" + Component.onCompleted: Qt.uiLanguage = "en_US" } diff --git a/src/plugins/mcusupport/wizards/qmlproject/main_big.qml.tpl b/src/plugins/mcusupport/wizards/qmlproject/main_big.qml.tpl deleted file mode 100644 index 60f77144499..00000000000 --- a/src/plugins/mcusupport/wizards/qmlproject/main_big.qml.tpl +++ /dev/null @@ -1,30 +0,0 @@ -import QtQuick 2.0 -import CustomModule - -Rectangle { - // Component.onCompleted: Qt.uiLanguage = "nb_NO" // Uncomment to change the UI language //TODO: Is this the "official" method of setting ui language? - - Row { - visible: CustomObject.customProperty - Image { - anchors.centerIn: parent - id: icon - source: "assets/icon.png" - } - Text { - anchors.centerIn: parent - font.pixelSize: 28 - id: title - text: " for MCUs" - } - } - - CustomComponent { - anchors.centerIn: parent - } - - MouseArea { - anchors.fill: parent - onClicked: CustomObject.toggle() - } -} diff --git a/src/plugins/mcusupport/wizards/qmlproject/project.qmlproject.tpl b/src/plugins/mcusupport/wizards/qmlproject/project.qmlproject.tpl index 0c879ccfb0d..113cd1f07a7 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/project.qmlproject.tpl +++ b/src/plugins/mcusupport/wizards/qmlproject/project.qmlproject.tpl @@ -5,6 +5,7 @@ Project { // importPaths: ["imports/CustomModule"] // Alternative API for importing modules. // projectRootPath: "." // Optional root path relative to qmlproject file path. mainFile: "%{MainQmlFile}" // The application's entrypoint + idBasedTranslations: true // Use qsTrId() instead of qsTr() /* Global configuration */ MCU.Config { @@ -88,8 +89,11 @@ Project { /* Translations */ TranslationFiles { - files: ["translations/%{TsFile}"] - MCU.omitSourceLanguage: false + files: [ + "translations/%{TsFileEn}", + "translations/%{TsFileNo}" + ] + MCU.omitSourceLanguage: true } FontFiles { diff --git a/src/plugins/mcusupport/wizards/qmlproject/translation.en_US.ts b/src/plugins/mcusupport/wizards/qmlproject/translation.en_US.ts new file mode 100644 index 00000000000..f4f94ed311c --- /dev/null +++ b/src/plugins/mcusupport/wizards/qmlproject/translation.en_US.ts @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="en_US"> +<context> + <name>%{CorrectedProjectName}</name> + <message id="hello-world"> + <translation>Hello world!</translation> + </message> +</context> +</TS> diff --git a/src/plugins/mcusupport/wizards/qmlproject/translation.nb_NO.ts b/src/plugins/mcusupport/wizards/qmlproject/translation.nb_NO.ts index 2cd70a44edd..10bdd92d377 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/translation.nb_NO.ts +++ b/src/plugins/mcusupport/wizards/qmlproject/translation.nb_NO.ts @@ -3,9 +3,7 @@ <TS version="2.1" language="nb_NO"> <context> <name>%{CorrectedProjectName}</name> - <message> - <location filename="../%{MainQmlFile}" line="37"/> - <source>Hello world!</source> + <message id="hello-world"> <translation>Hallo verden!</translation> </message> </context> diff --git a/src/plugins/mcusupport/wizards/qmlproject/wizard.json b/src/plugins/mcusupport/wizards/qmlproject/wizard.json index 4a5f582d432..8375cfb212b 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/wizard.json +++ b/src/plugins/mcusupport/wizards/qmlproject/wizard.json @@ -21,7 +21,8 @@ { "key": "ModuleFile", "value": "CustomModule.qmlproject"}, { "key": "QmlComponent", "value": "CustomButton.qml"}, { "key": "InterfaceFile", "value": "BackendObject.h"}, - { "key": "TsFile", "value": "%{CorrectedProjectName}.nb_NO.ts"} + { "key": "TsFileEn", "value": "%{CorrectedProjectName}.en_US.ts"}, + { "key": "TsFileNo", "value": "%{CorrectedProjectName}.nb_NO.ts"} ], "pages": @@ -103,7 +104,12 @@ }, { "source": "translation.nb_NO.ts", - "target": "%{QmlProjectDirectory}/translations/%{TsFile}", + "target": "%{QmlProjectDirectory}/translations/%{TsFileNo}", + "openInEditor": false + }, + { + "source": "translation.en_US.ts", + "target": "%{QmlProjectDirectory}/translations/%{TsFileEn}", "openInEditor": false }, { diff --git a/src/plugins/mercurial/Mercurial.json.in b/src/plugins/mercurial/Mercurial.json.in index 80a349365d7..5c897570e5f 100644 --- a/src/plugins/mercurial/Mercurial.json.in +++ b/src/plugins/mercurial/Mercurial.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Mercurial\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Brian McGillion\", - \"Copyright\" : \"(C) 2016 Brian McGillion, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Mercurial", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Brian McGillion", + "Copyright" : "(C) 2016 Brian McGillion, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Mercurial integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Version Control", + "Description" : "Mercurial integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/mercurial/annotationhighlighter.cpp b/src/plugins/mercurial/annotationhighlighter.cpp index be2259182f0..ec75322a71e 100644 --- a/src/plugins/mercurial/annotationhighlighter.cpp +++ b/src/plugins/mercurial/annotationhighlighter.cpp @@ -18,7 +18,7 @@ QString MercurialAnnotationHighlighter::changeNumber(const QString &block) const const QRegularExpressionMatch match = changeset.match(block); if (match.hasMatch()) return match.captured(1); - return QString(); + return {}; } } // Mercurial::Internal diff --git a/src/plugins/mercurial/mercurialclient.cpp b/src/plugins/mercurial/mercurialclient.cpp index 6a0c2574d43..bd5e45f4e96 100644 --- a/src/plugins/mercurial/mercurialclient.cpp +++ b/src/plugins/mercurial/mercurialclient.cpp @@ -53,7 +53,7 @@ MercurialDiffEditorController::MercurialDiffEditorController(IDocument *document using namespace Tasking; - const TreeStorage<QString> diffInputStorage = inputStorage(); + const TreeStorage<QString> diffInputStorage; const auto setupDiff = [=](Process &process) { setupCommand(process, {addConfigurationArguments(args)}); @@ -64,9 +64,9 @@ MercurialDiffEditorController::MercurialDiffEditorController(IDocument *document }; const Group root { - Storage(diffInputStorage), + Tasking::Storage(diffInputStorage), ProcessTask(setupDiff, onDiffDone), - postProcessTask() + postProcessTask(diffInputStorage) }; setReloadRecipe(root); } diff --git a/src/plugins/mercurial/mercurialcommitwidget.cpp b/src/plugins/mercurial/mercurialcommitwidget.cpp index c6b35a67d08..d1d1a7b37b9 100644 --- a/src/plugins/mercurial/mercurialcommitwidget.cpp +++ b/src/plugins/mercurial/mercurialcommitwidget.cpp @@ -151,7 +151,7 @@ QString MercurialCommitWidget::committer() const const QString author = mercurialCommitPanel->m_authorLineEdit->text(); const QString email = mercurialCommitPanel->m_emailLineEdit->text(); if (author.isEmpty()) - return QString(); + return {}; QString user = author; if (!email.isEmpty()) { diff --git a/src/plugins/mercurial/mercurialeditor.cpp b/src/plugins/mercurial/mercurialeditor.cpp index 19e1921e02c..b08802b8d66 100644 --- a/src/plugins/mercurial/mercurialeditor.cpp +++ b/src/plugins/mercurial/mercurialeditor.cpp @@ -45,7 +45,7 @@ QString MercurialEditorWidget::changeUnderCursor(const QTextCursor &cursorIn) co if (exactIdentifier40.match(change).hasMatch()) return change; } - return QString(); + return {}; } VcsBase::BaseAnnotationHighlighter *MercurialEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index cc294cad57a..75fbc5eb75f 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -169,7 +169,6 @@ private: void createRepositoryActions(const Core::Context &context); // Variables - MercurialSettings m_settings; MercurialClient m_client; Core::CommandLocator *m_commandLocator = nullptr; diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp index 0b493e277bf..05c96672f90 100644 --- a/src/plugins/mercurial/mercurialsettings.cpp +++ b/src/plugins/mercurial/mercurialsettings.cpp @@ -6,6 +6,8 @@ #include "constants.h" #include "mercurialtr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/layoutbuilder.h> #include <vcsbase/vcsbaseconstants.h> @@ -14,20 +16,15 @@ using namespace Utils; namespace Mercurial::Internal { -static MercurialSettings *theSettings; - MercurialSettings &settings() { - return *theSettings; + static MercurialSettings theSettings; + return theSettings; } MercurialSettings::MercurialSettings() { - theSettings = this; - - setId(VcsBase::Constants::VCS_ID_MERCURIAL); - setDisplayName(Tr::tr("Mercurial")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setAutoApply(false); setSettingsGroup("Mercurial"); binaryPath.setExpectedKind(PathChooser::ExistingCommand); @@ -73,6 +70,24 @@ MercurialSettings::MercurialSettings() st }; }); + + readSettings(); } +// MercurialSettingsPage + +class MercurialSettingsPage final : public Core::IOptionsPage +{ +public: + MercurialSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_MERCURIAL); + setDisplayName(Tr::tr("Mercurial")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const MercurialSettingsPage settingsPage; + } // Mercurial::Internal diff --git a/src/plugins/mercurial/mercurialsettings.h b/src/plugins/mercurial/mercurialsettings.h index 2102d7037d6..6dc245307bf 100644 --- a/src/plugins/mercurial/mercurialsettings.h +++ b/src/plugins/mercurial/mercurialsettings.h @@ -7,7 +7,7 @@ namespace Mercurial::Internal { -class MercurialSettings : public VcsBase::VcsBaseSettings +class MercurialSettings final : public VcsBase::VcsBaseSettings { public: MercurialSettings(); diff --git a/src/plugins/mercurial/srcdestdialog.cpp b/src/plugins/mercurial/srcdestdialog.cpp index 607507b2a6f..1e4f0b76b9b 100644 --- a/src/plugins/mercurial/srcdestdialog.cpp +++ b/src/plugins/mercurial/srcdestdialog.cpp @@ -33,7 +33,7 @@ SrcDestDialog::SrcDestDialog(const VcsBasePluginState &state, Direction dir, QWi m_localButton = new QRadioButton(Tr::tr("Local filesystem:")); auto urlButton = new QRadioButton(Tr::tr("Specify URL:")); - urlButton->setToolTip(Tr::tr("For example: 'https://[user[:pass]@]host[:port]/[path]'.")); + urlButton->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\".")); m_localPathChooser = new Utils::PathChooser; m_localPathChooser->setEnabled(false); @@ -41,7 +41,8 @@ SrcDestDialog::SrcDestDialog(const VcsBasePluginState &state, Direction dir, QWi m_localPathChooser->setHistoryCompleter("Hg.SourceDir.History"); m_urlLineEdit = new QLineEdit; - m_urlLineEdit->setToolTip(Tr::tr("For example: 'https://[user[:pass]@]host[:port]/[path]'.", nullptr)); + m_urlLineEdit->setToolTip( + Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\".", nullptr)); m_urlLineEdit->setEnabled(false); QUrl repoUrl = getRepoUrl(); diff --git a/src/plugins/mesonprojectmanager/CMakeLists.txt b/src/plugins/mesonprojectmanager/CMakeLists.txt index 3c541a1219e..da8f32ef701 100644 --- a/src/plugins/mesonprojectmanager/CMakeLists.txt +++ b/src/plugins/mesonprojectmanager/CMakeLists.txt @@ -13,23 +13,16 @@ add_qtc_plugin(MesonProjectManager common.h infoparser.h kitdata.h - kithelper.h - machinefilemanager.cpp - machinefilemanager.h mesonactionsmanager.cpp mesonactionsmanager.h mesonbuildconfiguration.cpp mesonbuildconfiguration.h - mesonbuildsettingswidget.cpp - mesonbuildsettingswidget.h mesonbuildsystem.cpp mesonbuildsystem.h mesoninfo.h mesoninfoparser.h mesonoutputparser.cpp mesonoutputparser.h - mesonprocess.cpp - mesonprocess.h mesonproject.cpp mesonproject.h mesonprojectimporter.cpp @@ -40,7 +33,6 @@ add_qtc_plugin(MesonProjectManager mesonprojectparser.cpp mesonprojectparser.h mesonprojectplugin.cpp - mesonprojectplugin.h mesonrunconfiguration.cpp mesonrunconfiguration.h mesontoolkitaspect.cpp @@ -49,8 +41,6 @@ add_qtc_plugin(MesonProjectManager mesontools.h mesonwrapper.cpp mesonwrapper.h - nativefilegenerator.cpp - nativefilegenerator.h ninjabuildstep.cpp ninjabuildstep.h ninjaparser.cpp diff --git a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in index 27a0bfda7d0..169ac45e628 100644 --- a/src/plugins/mesonprojectmanager/MesonProjectManager.json.in +++ b/src/plugins/mesonprojectmanager/MesonProjectManager.json.in @@ -1,21 +1,21 @@ { - \"Name\" : \"MesonProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Laboratory of Plasma Physics\", - \"Experimental\" : true, - \"DisabledByDefault\" : true, - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR Laboratory of Plasma Physics\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "MesonProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Laboratory of Plasma Physics", + "Experimental" : true, + "DisabledByDefault" : true, + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Laboratory of Plasma Physics", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Meson support.\", - \"Url\" : \"http://www.mesonbuild.com\", - $$dependencyList + "Category" : "Build Systems", + "Description" : "Meson support.", + "Url" : "http://www.mesonbuild.com", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/mesonprojectmanager/buildoptionsparser.h b/src/plugins/mesonprojectmanager/buildoptionsparser.h index ce2c927118f..4564269f12f 100644 --- a/src/plugins/mesonprojectmanager/buildoptionsparser.h +++ b/src/plugins/mesonprojectmanager/buildoptionsparser.h @@ -7,6 +7,8 @@ #include "common.h" #include "mesonpluginconstants.h" +#include <utils/filepath.h> + #include <QFile> #include <QJsonArray> #include <QJsonDocument> @@ -72,15 +74,14 @@ class BuildOptionsParser std::vector<std::unique_ptr<BuildOption>> m_buildOptions; public: - BuildOptionsParser(const QString &buildDir) + BuildOptionsParser(const Utils::FilePath &buildDir) { - auto arr = load<QJsonArray>(QString("%1/%2/%3") - .arg(buildDir) - .arg(Constants::MESON_INFO_DIR) - .arg(Constants::MESON_INTRO_BUIDOPTIONS)); + Utils::FilePath path = buildDir / Constants::MESON_INFO_DIR / Constants::MESON_INTRO_BUIDOPTIONS; + auto arr = load<QJsonArray>(path.toFSPathString()); if (arr) m_buildOptions = load_options(*arr); } + BuildOptionsParser(const QJsonDocument &js) { auto obj = get<QJsonArray>(js.object(), "buildoptions"); diff --git a/src/plugins/mesonprojectmanager/buildsystemfilesparser.h b/src/plugins/mesonprojectmanager/buildsystemfilesparser.h index 3bd8249c3ab..a64dd0c63c7 100644 --- a/src/plugins/mesonprojectmanager/buildsystemfilesparser.h +++ b/src/plugins/mesonprojectmanager/buildsystemfilesparser.h @@ -32,12 +32,10 @@ class BuildSystemFilesParser } public: - BuildSystemFilesParser(const QString &buildDir) + BuildSystemFilesParser(const Utils::FilePath &buildDir) { - auto arr = load<QJsonArray>(QString("%1/%2/%3") - .arg(buildDir) - .arg(Constants::MESON_INFO_DIR) - .arg(Constants::MESON_INTRO_BUILDSYSTEM_FILES)); + Utils::FilePath path = buildDir / Constants::MESON_INFO_DIR / Constants::MESON_INTRO_BUILDSYSTEM_FILES; + auto arr = load<QJsonArray>(path.toFSPathString()); appendFiles(arr, m_files); } diff --git a/src/plugins/mesonprojectmanager/infoparser.h b/src/plugins/mesonprojectmanager/infoparser.h index 27845a51490..6aca6211ea4 100644 --- a/src/plugins/mesonprojectmanager/infoparser.h +++ b/src/plugins/mesonprojectmanager/infoparser.h @@ -30,20 +30,14 @@ class InfoParser MesonInfo m_info; public: - InfoParser(const QString &buildDir) - /*: AbstractParser(jsonFile(buildDir))*/ + InfoParser(const Utils::FilePath &buildDir) { - auto obj = load<QJsonObject>(jsonFile(buildDir)); + Utils::FilePath jsonFile = buildDir / Constants::MESON_INFO_DIR / Constants::MESON_INFO; + auto obj = load<QJsonObject>(jsonFile.toFSPathString()); if (obj) m_info = load_info(*obj); } - static inline QString jsonFile(const QString &buildDir) - { - return QString("%1/%2/%3") - .arg(buildDir) - .arg(Constants::MESON_INFO_DIR) - .arg(Constants::MESON_INFO); - } + MesonInfo info() { return m_info; } }; diff --git a/src/plugins/mesonprojectmanager/kithelper.h b/src/plugins/mesonprojectmanager/kithelper.h deleted file mode 100644 index 965dad6563e..00000000000 --- a/src/plugins/mesonprojectmanager/kithelper.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "kitdata.h" -#include "versionhelper.h" - -#include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> - -#include <utils/macroexpander.h> -#include <utils/qtcassert.h> - -namespace MesonProjectManager { -namespace Internal { - -namespace KitHelper { -namespace details { - -inline QString expand(const ProjectExplorer::Kit *kit, const QString ¯o) -{ - return kit->macroExpander()->expand(macro); -} - -} // namespace details - -inline QString cCompilerPath(const ProjectExplorer::Kit *kit) -{ - QTC_ASSERT(kit, return {}); - return details::expand(kit, "%{Compiler:Executable:C}"); -} - -inline QString cxxCompilerPath(const ProjectExplorer::Kit *kit) -{ - QTC_ASSERT(kit, return {}); - return details::expand(kit, "%{Compiler:Executable:Cxx}"); -} - -inline QString qmakePath(const ProjectExplorer::Kit *kit) -{ - return details::expand(kit, "%{Qt:qmakeExecutable}"); -} - -inline QString cmakePath(const ProjectExplorer::Kit *kit) -{ - return details::expand(kit, "%{CMake:Executable:FilePath}"); -} - -inline QString qtVersion(const ProjectExplorer::Kit *kit) -{ - QTC_ASSERT(kit, return {}); - return details::expand(kit, "%{Qt:Version}"); -} - -inline KitData kitData(const ProjectExplorer::Kit *kit) -{ - QTC_ASSERT(kit, return {}); - KitData data; - data.cCompilerPath = cCompilerPath(kit); - data.cxxCompilerPath = cxxCompilerPath(kit); - data.cmakePath = cmakePath(kit); - data.qmakePath = qmakePath(kit); - data.qtVersionStr = qtVersion(kit); - data.qtVersion = Utils::QtMajorVersion::None; - auto version = Version::fromString(data.qtVersionStr); - if (version.isValid) { - switch (version.major) { - case 4: - data.qtVersion = Utils::QtMajorVersion::Qt4; - break; - case 5: - data.qtVersion = Utils::QtMajorVersion::Qt5; - break; - case 6: - data.qtVersion = Utils::QtMajorVersion::Qt6; - break; - default: - data.qtVersion = Utils::QtMajorVersion::Unknown; - } - } - return data; -} - -} // namespace KitHelper - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/machinefilemanager.cpp b/src/plugins/mesonprojectmanager/machinefilemanager.cpp deleted file mode 100644 index 2ecf7b70e3a..00000000000 --- a/src/plugins/mesonprojectmanager/machinefilemanager.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "machinefilemanager.h" - -#include "kitdata.h" -#include "kithelper.h" -#include "nativefilegenerator.h" - -#include <coreplugin/icore.h> - -#include <utils/qtcassert.h> - -#include <QDir> -#include <QFile> -#include <QRegularExpression> - -#include <optional> - -namespace MesonProjectManager { -namespace Internal { - -const char MACHINE_FILE_PREFIX[] = "Meson-MachineFile-"; -const char MACHINE_FILE_EXT[] = ".ini"; - -template<typename F> -bool withFile(const Utils::FilePath &path, const F &f) -{ - QFile file(path.toString()); - if (file.open(QIODevice::WriteOnly)) { - f(&file); - return file.flush(); - } - return false; -} - -Utils::FilePath MachineFilesDir() -{ - return Core::ICore::userResourcePath("Meson-machine-files"); -} - -MachineFileManager::MachineFileManager() -{ - using namespace ProjectExplorer; - connect(KitManager::instance(), - &KitManager::kitAdded, - this, - &MachineFileManager::addMachineFile); - connect(KitManager::instance(), - &KitManager::kitUpdated, - this, - &MachineFileManager::updateMachineFile); - connect(KitManager::instance(), - &KitManager::kitRemoved, - this, - &MachineFileManager::removeMachineFile); - connect(KitManager::instance(), - &KitManager::kitsLoaded, - this, - &MachineFileManager::cleanupMachineFiles); -} - -Utils::FilePath MachineFileManager::machineFile(const ProjectExplorer::Kit *kit) -{ - QTC_ASSERT(kit, return {}); - auto baseName - = QString("%1%2%3").arg(MACHINE_FILE_PREFIX).arg(kit->id().toString()).arg(MACHINE_FILE_EXT); - baseName = baseName.remove('{').remove('}'); - return MachineFilesDir().pathAppended(baseName); -} - -void MachineFileManager::addMachineFile(const ProjectExplorer::Kit *kit) -{ - auto filePath = machineFile(kit); - QTC_ASSERT(!filePath.isEmpty(), return ); - auto data = KitHelper::kitData(kit); - QTC_ASSERT(withFile(filePath, - [&data](QFile *file) { NativeFileGenerator::makeNativeFile(file, data); }), - return ); -} - -void MachineFileManager::removeMachineFile(const ProjectExplorer::Kit *kit) -{ - auto filePath = machineFile(kit); - if (filePath.exists()) - QFile::remove(filePath.toString()); -} - -void MachineFileManager::updateMachineFile(const ProjectExplorer::Kit *kit) -{ - addMachineFile(kit); -} - -void MachineFileManager::cleanupMachineFiles() -{ - const auto kits = ProjectExplorer::KitManager::kits(); - auto machineFilesDir = QDir(MachineFilesDir().toString()); - if (!machineFilesDir.exists()) { - machineFilesDir.mkdir(machineFilesDir.path()); - } - auto machineFiles = QDir(MachineFilesDir().toString()) - .entryList( - {QString("%1*%2").arg(MACHINE_FILE_PREFIX).arg(MACHINE_FILE_EXT)}); - QStringList expected; - for (auto const *kit : kits) { - QString fname = machineFile(kit).toString(); - expected.push_back(fname); - if (!machineFiles.contains(fname)) - addMachineFile(kit); - } - - for (const auto &file : machineFiles) { - if (!expected.contains(file)) - QFile::remove(file); - } -} - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/machinefilemanager.h b/src/plugins/mesonprojectmanager/machinefilemanager.h deleted file mode 100644 index e6cab231bbf..00000000000 --- a/src/plugins/mesonprojectmanager/machinefilemanager.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/kit.h> -#include <projectexplorer/kitmanager.h> - -#include <utils/fileutils.h> - -namespace MesonProjectManager { -namespace Internal { - -class MachineFileManager final : public QObject -{ - Q_OBJECT -public: - MachineFileManager(); - - static Utils::FilePath machineFile(const ProjectExplorer::Kit *kit); - -private: - void addMachineFile(const ProjectExplorer::Kit *kit); - void removeMachineFile(const ProjectExplorer::Kit *kit); - void updateMachineFile(const ProjectExplorer::Kit *kit); - void cleanupMachineFiles(); -}; - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp index 514c529212a..45543a5f261 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp +++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.cpp @@ -3,34 +3,73 @@ #include "mesonbuildconfiguration.h" -#include "mesonbuildsettingswidget.h" +#include "buildoptionsmodel.h" #include "mesonbuildsystem.h" #include "mesonpluginconstants.h" -#include "mesonpluginconstants.h" +#include "mesonprojectmanagertr.h" #include "mesonwrapper.h" #include "ninjabuildstep.h" +#include <coreplugin/find/itemviewfind.h> + +#include <projectexplorer/buildaspects.h> #include <projectexplorer/buildinfo.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/buildstep.h> #include <projectexplorer/buildsteplist.h> #include <projectexplorer/kit.h> +#include <projectexplorer/namedwidget.h> #include <projectexplorer/project.h> +#include <projectexplorer/projectconfiguration.h> #include <projectexplorer/projectexplorer.h> -#include <utils/fileutils.h> +#include <utils/categorysortfiltermodel.h> +#include <utils/detailswidget.h> +#include <utils/headerviewstretcher.h> +#include <utils/itemviews.h> +#include <utils/layoutbuilder.h> #include <utils/process.h> +#include <utils/progressindicator.h> +#include <utils/utilsicons.h> -#include <QDir> +#include <QLayout> +#include <QPushButton> using namespace ProjectExplorer; using namespace Utils; -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { -MesonBuildConfiguration::MesonBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id) - : ProjectExplorer::BuildConfiguration{target, id} +const QHash<QString, MesonBuildType> buildTypesByName = { + {"plain", MesonBuildType::plain}, + {"debug", MesonBuildType::debug}, + {"debugoptimized", MesonBuildType::debugoptimized}, + {"release", MesonBuildType::release}, + {"minsize", MesonBuildType::minsize}, + {"custom", MesonBuildType::custom} + }; + +static MesonBuildType mesonBuildType(const QString &typeName) +{ + return buildTypesByName.value(typeName, MesonBuildType::custom); +} + +static FilePath shadowBuildDirectory(const FilePath &projectFilePath, + const Kit *k, + const QString &bcName, + BuildConfiguration::BuildType buildType) +{ + if (projectFilePath.isEmpty()) + return {}; + + const QString projectName = projectFilePath.parentDir().fileName(); + return MesonBuildConfiguration::buildDirectoryFromTemplate( + Project::projectDirectory(projectFilePath), projectFilePath, + projectName, k, bcName, buildType, "meson"); +} + +MesonBuildConfiguration::MesonBuildConfiguration(ProjectExplorer::Target *target, Id id) + : BuildConfiguration(target, id) { appendInitialBuildStep(Constants::MESON_BUILD_STEP_ID); appendInitialCleanStep(Constants::MESON_BUILD_STEP_ID); @@ -52,19 +91,6 @@ MesonBuildConfiguration::~MesonBuildConfiguration() delete m_buildSystem; } -FilePath MesonBuildConfiguration::shadowBuildDirectory(const FilePath &projectFilePath, - const Kit *k, - const QString &bcName, - BuildConfiguration::BuildType buildType) -{ - if (projectFilePath.isEmpty()) - return {}; - - const QString projectName = projectFilePath.parentDir().fileName(); - return buildDirectoryFromTemplate(Project::projectDirectory(projectFilePath), projectFilePath, - projectName, k, bcName, buildType, "meson"); -} - ProjectExplorer::BuildSystem *MesonBuildConfiguration::buildSystem() const { return m_buildSystem; @@ -89,6 +115,11 @@ void MesonBuildConfiguration::build(const QString &target) mesonBuildStep->setBuildTarget(originalBuildTarget); } +static QString mesonBuildTypeName(MesonBuildType type) +{ + return buildTypesByName.key(type, "custom"); +} + QStringList MesonBuildConfiguration::mesonConfigArgs() { return Utils::ProcessArgs::splitArgs(m_parameters, HostOsInfo::hostOs()) @@ -106,32 +137,214 @@ void MesonBuildConfiguration::setParameters(const QString ¶ms) emit parametersChanged(); } -QVariantMap MesonBuildConfiguration::toMap() const +void MesonBuildConfiguration::toMap(Store &map) const { - auto data = ProjectExplorer::BuildConfiguration::toMap(); - data[Constants::BuildConfiguration::BUILD_TYPE_KEY] = mesonBuildTypeName(m_buildType); - data[Constants::BuildConfiguration::PARAMETERS_KEY] = m_parameters; - return data; + ProjectExplorer::BuildConfiguration::toMap(map); + map[Constants::BuildConfiguration::BUILD_TYPE_KEY] = mesonBuildTypeName(m_buildType); + map[Constants::BuildConfiguration::PARAMETERS_KEY] = m_parameters; } -bool MesonBuildConfiguration::fromMap(const QVariantMap &map) +void MesonBuildConfiguration::fromMap(const Store &map) { - auto res = ProjectExplorer::BuildConfiguration::fromMap(map); + ProjectExplorer::BuildConfiguration::fromMap(map); m_buildSystem = new MesonBuildSystem{this}; m_buildType = mesonBuildType( map.value(Constants::BuildConfiguration::BUILD_TYPE_KEY).toString()); m_parameters = map.value(Constants::BuildConfiguration::PARAMETERS_KEY).toString(); - return res; } -ProjectExplorer::NamedWidget *MesonBuildConfiguration::createConfigWidget() +class MesonBuildSettingsWidget : public NamedWidget +{ +public: + explicit MesonBuildSettingsWidget(MesonBuildConfiguration *buildCfg) + : NamedWidget(Tr::tr("Meson")), m_progressIndicator(ProgressIndicatorSize::Large) + { + auto configureButton = new QPushButton(Tr::tr("Apply Configuration Changes")); + configureButton->setEnabled(false); + configureButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + + auto wipeButton = new QPushButton(Tr::tr("Wipe Project")); + wipeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + wipeButton->setIcon(Utils::Icons::WARNING.icon()); + wipeButton->setToolTip(Tr::tr("Wipes build directory and reconfigures using previous command " + "line options.\nUseful if build directory is corrupted or when " + "rebuilding with a newer version of Meson.")); + + auto container = new DetailsWidget; + + auto details = new QWidget; + + container->setState(DetailsWidget::NoSummary); + container->setWidget(details); + + auto parametersLineEdit = new QLineEdit; + + auto optionsFilterLineEdit = new FancyLineEdit; + + auto optionsTreeView = new TreeView; + optionsTreeView->setMinimumHeight(300); + optionsTreeView->setFrameShape(QFrame::NoFrame); + optionsTreeView->setSelectionBehavior(QAbstractItemView::SelectItems); + optionsTreeView->setSortingEnabled(true); + + using namespace Layouting; + Column { + noMargin, + Form { + Tr::tr("Parameters:"), parametersLineEdit, br, + buildCfg->buildDirectoryAspect(), br + }, + optionsFilterLineEdit, + optionsTreeView, + }.attachTo(details); + + Column { + noMargin, + container, + Row { configureButton, wipeButton, noMargin } + }.attachTo(this); + + parametersLineEdit->setText(buildCfg->parameters()); + optionsFilterLineEdit->setFiltering(true); + + optionsTreeView->sortByColumn(0, Qt::AscendingOrder); + + QFrame *findWrapper + = Core::ItemViewFind::createSearchableWrapper(optionsTreeView, + Core::ItemViewFind::LightColored); + findWrapper->setFrameStyle(QFrame::StyledPanel); + m_progressIndicator.attachToWidget(findWrapper); + m_progressIndicator.raise(); + m_progressIndicator.hide(); + details->layout()->addWidget(findWrapper); + + m_showProgressTimer.setSingleShot(true); + m_showProgressTimer.setInterval(50); // don't show progress for < 50ms tasks + connect(&m_showProgressTimer, &QTimer::timeout, + this, [this] { m_progressIndicator.show(); }); + connect(&m_optionsModel, &BuidOptionsModel::configurationChanged, this, [configureButton] { + configureButton->setEnabled(true); + }); + m_optionsFilter.setSourceModel(&m_optionsModel); + m_optionsFilter.setSortRole(Qt::DisplayRole); + m_optionsFilter.setFilterKeyColumn(-1); + + optionsTreeView->setModel(&m_optionsFilter); + optionsTreeView->setItemDelegate(new BuildOptionDelegate{optionsTreeView}); + + MesonBuildSystem *bs = static_cast<MesonBuildSystem *>(buildCfg->buildSystem()); + connect(buildCfg->target(), &ProjectExplorer::Target::parsingFinished, + this, [this, bs, optionsTreeView](bool success) { + if (success) { + m_optionsModel.setConfiguration(bs->buildOptions()); + } else { + m_optionsModel.clear(); + } + optionsTreeView->expandAll(); + optionsTreeView->resizeColumnToContents(0); + optionsTreeView->setEnabled(true); + m_showProgressTimer.stop(); + m_progressIndicator.hide(); + }); + + connect(bs, &MesonBuildSystem::parsingStarted, this, [this, optionsTreeView] { + if (!m_showProgressTimer.isActive()) { + optionsTreeView->setEnabled(false); + m_showProgressTimer.start(); + } + }); + + connect(&m_optionsModel, &BuidOptionsModel::dataChanged, this, [bs, this] { + bs->setMesonConfigArgs(this->m_optionsModel.changesAsMesonArgs()); + }); + + connect(&m_optionsFilter, &QAbstractItemModel::modelReset, this, [optionsTreeView] { + optionsTreeView->expandAll(); + optionsTreeView->resizeColumnToContents(0); + }); + + connect(optionsFilterLineEdit, &QLineEdit::textChanged, &m_optionsFilter, [this](const QString &txt) { + m_optionsFilter.setFilterRegularExpression( + QRegularExpression(QRegularExpression::escape(txt), + QRegularExpression::CaseInsensitiveOption)); + }); + + connect(optionsTreeView, + &Utils::TreeView::activated, + optionsTreeView, + [tree = optionsTreeView](const QModelIndex &idx) { tree->edit(idx); }); + + connect(configureButton, &QPushButton::clicked, + this, [this, bs, configureButton, optionsTreeView] { + optionsTreeView->setEnabled(false); + configureButton->setEnabled(false); + m_showProgressTimer.start(); + bs->configure(); + }); + connect(wipeButton, &QPushButton::clicked, + this, [this, bs, configureButton, optionsTreeView] { + optionsTreeView->setEnabled(false); + configureButton->setEnabled(false); + m_showProgressTimer.start(); + bs->wipe(); + }); + connect(parametersLineEdit, &QLineEdit::editingFinished, this, [ buildCfg, parametersLineEdit] { + buildCfg->setParameters(parametersLineEdit->text()); + }); + bs->triggerParsing(); + } + +private: + BuidOptionsModel m_optionsModel; + CategorySortFilterModel m_optionsFilter; + ProgressIndicator m_progressIndicator; + QTimer m_showProgressTimer; +}; + +NamedWidget *MesonBuildConfiguration::createConfigWidget() { return new MesonBuildSettingsWidget{this}; } -ProjectExplorer::BuildInfo createBuildInfo(MesonBuildType type) +static BuildConfiguration::BuildType buildType(MesonBuildType type) { - ProjectExplorer::BuildInfo bInfo; + switch (type) { + case MesonBuildType::plain: + return BuildConfiguration::Unknown; + case MesonBuildType::debug: + return BuildConfiguration::Debug; + case MesonBuildType::debugoptimized: + return BuildConfiguration::Profile; + case MesonBuildType::release: + return BuildConfiguration::Release; + case MesonBuildType::minsize: + return BuildConfiguration::Release; + default: + return BuildConfiguration::Unknown; + } +} + +static QString mesonBuildTypeDisplayName(MesonBuildType type) +{ + switch (type) { + case MesonBuildType::plain: + return {"Plain"}; + case MesonBuildType::debug: + return {"Debug"}; + case MesonBuildType::debugoptimized: + return {"Debug With Optimizations"}; + case MesonBuildType::release: + return {"Release"}; + case MesonBuildType::minsize: + return {"Minimum Size"}; + default: + return {"Custom"}; + } +} + +BuildInfo createBuildInfo(MesonBuildType type) +{ + BuildInfo bInfo; bInfo.typeName = mesonBuildTypeName(type); bInfo.displayName = mesonBuildTypeDisplayName(type); bInfo.buildType = buildType(type); @@ -148,7 +361,7 @@ MesonBuildConfigurationFactory::MesonBuildConfigurationFactory() QList<ProjectExplorer::BuildInfo> result; Utils::FilePath path = forSetup - ? ProjectExplorer::Project::projectDirectory(projectPath) + ? Project::projectDirectory(projectPath) : projectPath; for (const auto &bType : {MesonBuildType::debug, MesonBuildType::release, @@ -156,16 +369,14 @@ MesonBuildConfigurationFactory::MesonBuildConfigurationFactory() MesonBuildType::minsize}) { auto bInfo = createBuildInfo(bType); if (forSetup) - bInfo.buildDirectory - = MesonBuildConfiguration::shadowBuildDirectory(projectPath, - k, - bInfo.typeName, - bInfo.buildType); + bInfo.buildDirectory = shadowBuildDirectory(projectPath, + k, + bInfo.typeName, + bInfo.buildType); result << bInfo; } return result; }); } -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h index a4059091892..278722fea73 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h +++ b/src/plugins/mesonprojectmanager/mesonbuildconfiguration.h @@ -1,71 +1,15 @@ // Copyright (C) 2020 Alexis Jeandet. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + #pragma once #include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/target.h> -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { enum class MesonBuildType { plain, debug, debugoptimized, release, minsize, custom }; -const QHash<QString, MesonBuildType> buildTypesByName = {{"plain", MesonBuildType::plain}, - {"debug", MesonBuildType::debug}, - {"debugoptimized", - MesonBuildType::debugoptimized}, - {"release", MesonBuildType::release}, - {"minsize", MesonBuildType::minsize}, - {"custom", MesonBuildType::custom}}; - -inline QString mesonBuildTypeName(MesonBuildType type) -{ - return buildTypesByName.key(type, "custom"); -} - -inline QString mesonBuildTypeDisplayName(MesonBuildType type) -{ - switch (type) { - case MesonBuildType::plain: - return {"Plain"}; - case MesonBuildType::debug: - return {"Debug"}; - case MesonBuildType::debugoptimized: - return {"Debug With Optimizations"}; - case MesonBuildType::release: - return {"Release"}; - case MesonBuildType::minsize: - return {"Minimum Size"}; - default: - return {"Custom"}; - } -} - -inline MesonBuildType mesonBuildType(const QString &typeName) -{ - return buildTypesByName.value(typeName, MesonBuildType::custom); -} - -inline ProjectExplorer::BuildConfiguration::BuildType buildType(MesonBuildType type) -{ - switch (type) { - case MesonBuildType::plain: - return ProjectExplorer::BuildConfiguration::Unknown; - case MesonBuildType::debug: - return ProjectExplorer::BuildConfiguration::Debug; - case MesonBuildType::debugoptimized: - return ProjectExplorer::BuildConfiguration::Profile; - case MesonBuildType::release: - return ProjectExplorer::BuildConfiguration::Release; - case MesonBuildType::minsize: - return ProjectExplorer::BuildConfiguration::Release; - default: - return ProjectExplorer::BuildConfiguration::Unknown; - } -} - class MesonBuildSystem; -class MesonTools; class MesonBuildConfiguration final : public ProjectExplorer::BuildConfiguration { @@ -74,12 +18,6 @@ public: MesonBuildConfiguration(ProjectExplorer::Target *target, Utils::Id id); ~MesonBuildConfiguration() final; - static Utils::FilePath shadowBuildDirectory( - const Utils::FilePath &projectFilePath, - const ProjectExplorer::Kit *k, - const QString &bcName, - ProjectExplorer::BuildConfiguration::BuildType buildType); - ProjectExplorer::BuildSystem *buildSystem() const final; void build(const QString &target); @@ -92,8 +30,9 @@ signals: void parametersChanged(); private: - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &map) override; + void toMap(Utils::Store &map) const override; + void fromMap(const Utils::Store &map) override; + MesonBuildType m_buildType; ProjectExplorer::NamedWidget *createConfigWidget() final; MesonBuildSystem *m_buildSystem = nullptr; @@ -106,5 +45,4 @@ public: MesonBuildConfigurationFactory(); }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp deleted file mode 100644 index 26389d38883..00000000000 --- a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "mesonbuildsettingswidget.h" - -#include "mesonbuildconfiguration.h" -#include "mesonbuildsystem.h" -#include "mesonprojectmanagertr.h" - -#include <coreplugin/find/itemviewfind.h> - -#include <projectexplorer/buildaspects.h> -#include <projectexplorer/projectconfiguration.h> - -#include <utils/detailswidget.h> -#include <utils/headerviewstretcher.h> -#include <utils/itemviews.h> -#include <utils/layoutbuilder.h> -#include <utils/utilsicons.h> - -#include <QLayout> -#include <QPushButton> - -using namespace Utils; - -namespace MesonProjectManager::Internal { - -MesonBuildSettingsWidget::MesonBuildSettingsWidget(MesonBuildConfiguration *buildCfg) - : ProjectExplorer::NamedWidget(Tr::tr("Meson")) - , m_progressIndicator(ProgressIndicatorSize::Large) -{ - auto configureButton = new QPushButton(Tr::tr("Apply Configuration Changes")); - configureButton->setEnabled(false); - configureButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - - auto wipeButton = new QPushButton(Tr::tr("Wipe Project")); - wipeButton->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); - wipeButton->setIcon(Utils::Icons::WARNING.icon()); - wipeButton->setToolTip(Tr::tr("Wipes build directory and reconfigures using previous command " - "line options.\nUseful if build directory is corrupted or when " - "rebuilding with a newer version of Meson.")); - - auto container = new DetailsWidget; - - auto details = new QWidget; - - container->setState(DetailsWidget::NoSummary); - container->setWidget(details); - - auto parametersLineEdit = new QLineEdit; - - auto optionsFilterLineEdit = new FancyLineEdit; - - auto optionsTreeView = new TreeView; - optionsTreeView->setMinimumHeight(300); - optionsTreeView->setFrameShape(QFrame::NoFrame); - optionsTreeView->setSelectionBehavior(QAbstractItemView::SelectItems); - optionsTreeView->setUniformRowHeights(true); - optionsTreeView->setSortingEnabled(true); - - using namespace Layouting; - Column { - noMargin, - Form { - Tr::tr("Parameters:"), parametersLineEdit, br, - buildCfg->buildDirectoryAspect(), br - }, - optionsFilterLineEdit, - optionsTreeView, - }.attachTo(details); - - Column { - noMargin, - container, - Row { configureButton, wipeButton, noMargin } - }.attachTo(this); - - parametersLineEdit->setText(buildCfg->parameters()); - optionsFilterLineEdit->setFiltering(true); - - optionsTreeView->sortByColumn(0, Qt::AscendingOrder); - - QFrame *findWrapper - = Core::ItemViewFind::createSearchableWrapper(optionsTreeView, - Core::ItemViewFind::LightColored); - findWrapper->setFrameStyle(QFrame::StyledPanel); - m_progressIndicator.attachToWidget(findWrapper); - m_progressIndicator.raise(); - m_progressIndicator.hide(); - details->layout()->addWidget(findWrapper); - - m_showProgressTimer.setSingleShot(true); - m_showProgressTimer.setInterval(50); // don't show progress for < 50ms tasks - connect(&m_showProgressTimer, &QTimer::timeout, [this]() { m_progressIndicator.show(); }); - connect(&m_optionsModel, &BuidOptionsModel::configurationChanged, this, [configureButton] { - configureButton->setEnabled(true); - }); - m_optionsFilter.setSourceModel(&m_optionsModel); - m_optionsFilter.setSortRole(Qt::DisplayRole); - m_optionsFilter.setFilterKeyColumn(-1); - - optionsTreeView->setModel(&m_optionsFilter); - optionsTreeView->setItemDelegate(new BuildOptionDelegate{optionsTreeView}); - - MesonBuildSystem *bs = static_cast<MesonBuildSystem *>(buildCfg->buildSystem()); - connect(buildCfg->target(), &ProjectExplorer::Target::parsingFinished, - this, [this, bs, optionsTreeView](bool success) { - if (success) { - m_optionsModel.setConfiguration(bs->buildOptions()); - } else { - m_optionsModel.clear(); - } - optionsTreeView->expandAll(); - optionsTreeView->resizeColumnToContents(0); - optionsTreeView->setEnabled(true); - m_showProgressTimer.stop(); - m_progressIndicator.hide(); - }); - - connect(bs, &MesonBuildSystem::parsingStarted, this, [this, optionsTreeView] { - if (!m_showProgressTimer.isActive()) { - optionsTreeView->setEnabled(false); - m_showProgressTimer.start(); - } - }); - - connect(&m_optionsModel, &BuidOptionsModel::dataChanged, this, [bs, this] { - bs->setMesonConfigArgs(this->m_optionsModel.changesAsMesonArgs()); - }); - - connect(&m_optionsFilter, &QAbstractItemModel::modelReset, this, [optionsTreeView] { - optionsTreeView->expandAll(); - optionsTreeView->resizeColumnToContents(0); - }); - - connect(optionsFilterLineEdit, &QLineEdit::textChanged, &m_optionsFilter, [this](const QString &txt) { - m_optionsFilter.setFilterRegularExpression( - QRegularExpression(QRegularExpression::escape(txt), - QRegularExpression::CaseInsensitiveOption)); - }); - - connect(optionsTreeView, - &Utils::TreeView::activated, - optionsTreeView, - [tree = optionsTreeView](const QModelIndex &idx) { tree->edit(idx); }); - - connect(configureButton, &QPushButton::clicked, [this, bs, configureButton, optionsTreeView] { - optionsTreeView->setEnabled(false); - configureButton->setEnabled(false); - m_showProgressTimer.start(); - bs->configure(); - }); - connect(wipeButton, &QPushButton::clicked, [this, bs, configureButton, optionsTreeView] { - optionsTreeView->setEnabled(false); - configureButton->setEnabled(false); - m_showProgressTimer.start(); - bs->wipe(); - }); - connect(parametersLineEdit, &QLineEdit::editingFinished, this, [ buildCfg, parametersLineEdit] { - buildCfg->setParameters(parametersLineEdit->text()); - }); - bs->triggerParsing(); -} - -MesonBuildSettingsWidget::~MesonBuildSettingsWidget() = default; - -} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.h b/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.h deleted file mode 100644 index 8c3f89d9518..00000000000 --- a/src/plugins/mesonprojectmanager/mesonbuildsettingswidget.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "buildoptionsmodel.h" - -#include <projectexplorer/namedwidget.h> - -#include <utils/categorysortfiltermodel.h> -#include <utils/progressindicator.h> - -#include <QTimer> - -namespace MesonProjectManager::Internal { - -class MesonBuildConfiguration; - -class MesonBuildSettingsWidget : public ProjectExplorer::NamedWidget -{ - Q_OBJECT - -public: - explicit MesonBuildSettingsWidget(MesonBuildConfiguration *buildCfg); - ~MesonBuildSettingsWidget(); - -private: - BuidOptionsModel m_optionsModel; - Utils::CategorySortFilterModel m_optionsFilter; - Utils::ProgressIndicator m_progressIndicator; - QTimer m_showProgressTimer; -}; - -} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp index d140a468e34..8aacff10c92 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp +++ b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp @@ -3,20 +3,29 @@ #include "mesonbuildsystem.h" -#include "kithelper.h" -#include "machinefilemanager.h" +#include "kitdata.h" #include "mesonbuildconfiguration.h" #include "mesonprojectmanagertr.h" #include "mesontoolkitaspect.h" #include "settings.h" +#include <coreplugin/icore.h> + #include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/kitmanager.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/taskhub.h> +#include <projectexplorer/toolchain.h> #include <qtsupport/qtcppkitinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> + +#include <utils/macroexpander.h> +#include <utils/qtcassert.h> + +#include <optional> -#include <QDir> #include <QLoggingCategory> #define LEAVE_IF_BUSY() \ @@ -37,16 +46,174 @@ }; using namespace ProjectExplorer; +using namespace Utils; + +namespace MesonProjectManager::Internal { -namespace MesonProjectManager { -namespace Internal { static Q_LOGGING_CATEGORY(mesonBuildSystemLog, "qtc.meson.buildsystem", QtWarningMsg); -MesonBuildSystem::MesonBuildSystem(MesonBuildConfiguration *bc) - : ProjectExplorer::BuildSystem{bc} - , m_parser{MesonToolKitAspect::mesonToolId(bc->kit()), bc->environment(), project()} +const char MACHINE_FILE_PREFIX[] = "Meson-MachineFile-"; +const char MACHINE_FILE_EXT[] = ".ini"; + +static KitData createKitData(const Kit *kit) { - init(); + QTC_ASSERT(kit, return {}); + + MacroExpander *expander = kit->macroExpander(); + + KitData data; + data.cCompilerPath = expander->expand(QString("%{Compiler:Executable:C}")); + data.cxxCompilerPath = expander->expand(QString("%{Compiler:Executable:Cxx}")); + data.cmakePath = expander->expand(QString("%{CMake:Executable:FilePath}")); + data.qmakePath = expander->expand(QString("%{Qt:qmakeExecutable}")); + data.qtVersionStr = expander->expand(QString("%{Qt:Version}")); + data.qtVersion = Utils::QtMajorVersion::None; + auto version = Version::fromString(data.qtVersionStr); + if (version.isValid) { + switch (version.major) { + case 4: + data.qtVersion = Utils::QtMajorVersion::Qt4; + break; + case 5: + data.qtVersion = Utils::QtMajorVersion::Qt5; + break; + case 6: + data.qtVersion = Utils::QtMajorVersion::Qt6; + break; + default: + data.qtVersion = Utils::QtMajorVersion::Unknown; + } + } + return data; +} + +static FilePath machineFilesDir() +{ + return Core::ICore::userResourcePath("Meson-machine-files"); +} + +FilePath MachineFileManager::machineFile(const Kit *kit) +{ + QTC_ASSERT(kit, return {}); + auto baseName + = QString("%1%2%3").arg(MACHINE_FILE_PREFIX).arg(kit->id().toString()).arg(MACHINE_FILE_EXT); + baseName = baseName.remove('{').remove('}'); + return machineFilesDir().pathAppended(baseName); +} + +MachineFileManager::MachineFileManager() +{ + connect(KitManager::instance(), &KitManager::kitAdded, + this, &MachineFileManager::addMachineFile); + connect(KitManager::instance(), &KitManager::kitUpdated, + this, &MachineFileManager::updateMachineFile); + connect(KitManager::instance(), &KitManager::kitRemoved, + this, &MachineFileManager::removeMachineFile); + connect(KitManager::instance(), &KitManager::kitsLoaded, + this, &MachineFileManager::cleanupMachineFiles); +} + +void MachineFileManager::addMachineFile(const Kit *kit) +{ + FilePath filePath = machineFile(kit); + QTC_ASSERT(!filePath.isEmpty(), return ); + auto kitData = createKitData(kit); + + auto entry = [](const QString &key, const QString &value) { + return QString("%1 = '%2'\n").arg(key).arg(value).toUtf8(); + }; + + QByteArray ba = "[binaries]\n"; + ba += entry("c", kitData.cCompilerPath); + ba += entry("cpp", kitData.cxxCompilerPath); + ba += entry("qmake", kitData.qmakePath); + if (kitData.qtVersion == QtMajorVersion::Qt4) + ba += entry("qmake-qt4", kitData.qmakePath); + else if (kitData.qtVersion == QtMajorVersion::Qt5) + ba += entry("qmake-qt5", kitData.qmakePath); + else if (kitData.qtVersion == QtMajorVersion::Qt6) + ba += entry("qmake-qt6", kitData.qmakePath); + ba += entry("cmake", kitData.cmakePath); + + filePath.writeFileContents(ba); +} + +void MachineFileManager::removeMachineFile(const Kit *kit) +{ + FilePath filePath = machineFile(kit); + if (filePath.exists()) + filePath.removeFile(); +} + +void MachineFileManager::updateMachineFile(const Kit *kit) +{ + addMachineFile(kit); +} + +void MachineFileManager::cleanupMachineFiles() +{ + FilePath dir = machineFilesDir(); + dir.ensureWritableDir(); + + const FileFilter filter = {{QString("%1*%2").arg(MACHINE_FILE_PREFIX).arg(MACHINE_FILE_EXT)}}; + const FilePaths machineFiles = dir.dirEntries(filter); + + FilePaths expected; + for (Kit const *kit : KitManager::kits()) { + const FilePath fname = machineFile(kit); + expected.push_back(fname); + if (!machineFiles.contains(fname)) + addMachineFile(kit); + } + + for (const FilePath &file : machineFiles) { + if (!expected.contains(file)) + file.removeFile(); + } +} + +// MesonBuildSystem + +MesonBuildSystem::MesonBuildSystem(MesonBuildConfiguration *bc) + : BuildSystem(bc) + , m_parser(MesonToolKitAspect::mesonToolId(bc->kit()), bc->environment(), project()) +{ + qCDebug(mesonBuildSystemLog) << "Init"; + connect(bc->target(), &ProjectExplorer::Target::kitChanged, this, [this] { + updateKit(kit()); + }); + connect(bc, &MesonBuildConfiguration::buildDirectoryChanged, this, [this] { + updateKit(kit()); + this->triggerParsing(); + }); + connect(bc, &MesonBuildConfiguration::parametersChanged, this, [this] { + updateKit(kit()); + wipe(); + }); + connect(bc, &MesonBuildConfiguration::environmentChanged, this, [this] { + m_parser.setEnvironment(buildConfiguration()->environment()); + }); + + connect(project(), &ProjectExplorer::Project::projectFileIsDirty, this, [this] { + if (buildConfiguration()->isActive()) + parseProject(); + }); + connect(&m_parser, &MesonProjectParser::parsingCompleted, this, &MesonBuildSystem::parsingCompleted); + + connect(&m_IntroWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this] { + if (buildConfiguration()->isActive()) + parseProject(); + }); + + updateKit(kit()); + // as specified here https://mesonbuild.com/IDE-integration.html#ide-integration + // meson-info.json is the last written file, which ensure that all others introspection + // files are ready when a modification is detected on this one. + m_IntroWatcher.addFile(buildConfiguration() + ->buildDirectory() + .pathAppended(Constants::MESON_INFO_DIR) + .pathAppended(Constants::MESON_INFO), + Utils::FileSystemWatcher::WatchModifiedDate); } MesonBuildSystem::~MesonBuildSystem() @@ -92,21 +259,17 @@ void MesonBuildSystem::parsingCompleted(bool success) emit buildConfiguration()->enabledChanged(); // HACK. Should not be needed. } -ProjectExplorer::Kit *MesonBuildSystem::MesonBuildSystem::kit() -{ - return buildConfiguration()->kit(); -} - QStringList MesonBuildSystem::configArgs(bool isSetup) { - const QString ¶ms = mesonBuildConfiguration()->parameters(); + MesonBuildConfiguration *bc = static_cast<MesonBuildConfiguration *>(buildConfiguration()); + + const QString ¶ms = bc->parameters(); if (!isSetup || params.contains("--cross-file") || params.contains("--native-file")) - return m_pendingConfigArgs + mesonBuildConfiguration()->mesonConfigArgs(); - else { - return QStringList{ - QString("--native-file=%1").arg(MachineFileManager::machineFile(kit()).toString())} - + m_pendingConfigArgs + mesonBuildConfiguration()->mesonConfigArgs(); - } + return m_pendingConfigArgs + bc->mesonConfigArgs(); + + return QStringList{ + QString("--native-file=%1").arg(MachineFileManager::machineFile(kit()).toString())} + + m_pendingConfigArgs + bc->mesonConfigArgs(); } bool MesonBuildSystem::configure() @@ -147,51 +310,6 @@ bool MesonBuildSystem::wipe() return false; } -MesonBuildConfiguration *MesonBuildSystem::mesonBuildConfiguration() -{ - return static_cast<MesonBuildConfiguration *>(buildConfiguration()); -} - -void MesonBuildSystem::init() -{ - qCDebug(mesonBuildSystemLog) << "Init"; - connect(buildConfiguration()->target(), &ProjectExplorer::Target::kitChanged, this, [this] { - updateKit(kit()); - }); - connect(mesonBuildConfiguration(), &MesonBuildConfiguration::buildDirectoryChanged, this, [this]() { - updateKit(kit()); - this->triggerParsing(); - }); - connect(mesonBuildConfiguration(), &MesonBuildConfiguration::parametersChanged, this, [this]() { - updateKit(kit()); - wipe(); - }); - connect(mesonBuildConfiguration(), &MesonBuildConfiguration::environmentChanged, this, [this]() { - m_parser.setEnvironment(buildConfiguration()->environment()); - }); - - connect(project(), &ProjectExplorer::Project::projectFileIsDirty, this, [this]() { - if (buildConfiguration()->isActive()) - parseProject(); - }); - connect(&m_parser, &MesonProjectParser::parsingCompleted, this, &MesonBuildSystem::parsingCompleted); - - connect(&m_IntroWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this]() { - if (buildConfiguration()->isActive()) - parseProject(); - }); - - updateKit(kit()); - // as specified here https://mesonbuild.com/IDE-integration.html#ide-integration - // meson-info.json is the last written file, which ensure that all others introspection - // files are ready when a modification is detected on this one. - m_IntroWatcher.addFile(buildConfiguration() - ->buildDirectory() - .pathAppended(Constants::MESON_INFO_DIR) - .pathAppended(Constants::MESON_INFO), - Utils::FileSystemWatcher::WatchModifiedDate); -} - bool MesonBuildSystem::parseProject() { QTC_ASSERT(buildConfiguration(), return false); @@ -209,9 +327,8 @@ bool MesonBuildSystem::parseProject() void MesonBuildSystem::updateKit(ProjectExplorer::Kit *kit) { QTC_ASSERT(kit, return ); - m_kitData = KitHelper::kitData(kit); + m_kitData = createKitData(kit); m_parser.setQtVersion(m_kitData.qtVersion); } -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonbuildsystem.h b/src/plugins/mesonprojectmanager/mesonbuildsystem.h index 72e2cc9a961..b07d5aba4ce 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildsystem.h +++ b/src/plugins/mesonprojectmanager/mesonbuildsystem.h @@ -13,13 +13,28 @@ #include <utils/filesystemwatcher.h> -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { class MesonBuildConfiguration; + +class MachineFileManager final : public QObject +{ +public: + MachineFileManager(); + + static Utils::FilePath machineFile(const ProjectExplorer::Kit *kit); + +private: + void addMachineFile(const ProjectExplorer::Kit *kit); + void removeMachineFile(const ProjectExplorer::Kit *kit); + void updateMachineFile(const ProjectExplorer::Kit *kit); + void cleanupMachineFiles(); +}; + class MesonBuildSystem final : public ProjectExplorer::BuildSystem { Q_OBJECT + public: MesonBuildSystem(MesonBuildConfiguration *bc); ~MesonBuildSystem() final; @@ -34,20 +49,17 @@ public: bool setup(); bool wipe(); - MesonBuildConfiguration *mesonBuildConfiguration(); - const QStringList &targetList() const { return m_parser.targetsNames(); } void setMesonConfigArgs(const QStringList &args) { m_pendingConfigArgs = args; } private: - void init(); bool parseProject(); void updateKit(ProjectExplorer::Kit *kit); bool needsSetup(); void parsingCompleted(bool success); - ProjectExplorer::Kit *kit(); QStringList configArgs(bool isSetup); + ProjectExplorer::BuildSystem::ParseGuard m_parseGuard; MesonProjectParser m_parser; CppEditor::CppProjectUpdater m_cppCodeModelUpdater; @@ -56,5 +68,4 @@ private: KitData m_kitData; }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesoninfoparser.h b/src/plugins/mesonprojectmanager/mesoninfoparser.h index f32220f48b5..768ed13cf2e 100644 --- a/src/plugins/mesonprojectmanager/mesoninfoparser.h +++ b/src/plugins/mesonprojectmanager/mesoninfoparser.h @@ -11,7 +11,7 @@ #include "target.h" #include "targetparser.h" -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <optional> @@ -30,7 +30,7 @@ struct Result std::optional<MesonInfo> mesonInfo; }; -inline Result parse(const QString &buildDir) +inline Result parse(const Utils::FilePath &buildDir) { return {TargetParser{buildDir}.targetList(), BuildOptionsParser{buildDir}.takeBuildOptions(), @@ -58,7 +58,8 @@ inline Result parse(QIODevice *introFile) } return {}; } -inline std::optional<MesonInfo> mesonInfo(const QString &buildDir) + +inline std::optional<MesonInfo> mesonInfo(const Utils::FilePath &buildDir) { return InfoParser{buildDir}.info(); } diff --git a/src/plugins/mesonprojectmanager/mesonprocess.cpp b/src/plugins/mesonprojectmanager/mesonprocess.cpp deleted file mode 100644 index 2e4ba024559..00000000000 --- a/src/plugins/mesonprojectmanager/mesonprocess.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "mesonprocess.h" - -#include "mesonprojectmanagertr.h" -#include "toolwrapper.h" - -#include <coreplugin/messagemanager.h> -#include <coreplugin/progressmanager/processprogress.h> - -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/taskhub.h> - -#include <utils/environment.h> -#include <utils/process.h> -#include <utils/stringutils.h> - -#include <QLoggingCategory> - -using namespace Core; -using namespace Utils; - -namespace MesonProjectManager { -namespace Internal { - -static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg); - -MesonProcess::MesonProcess() = default; -MesonProcess::~MesonProcess() = default; - -bool MesonProcess::run(const Command &command, - const Environment &env, - const QString &projectName, - bool captureStdo) -{ - if (!sanityCheck(command)) - return false; - m_stdo.clear(); - ProjectExplorer::TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); - setupProcess(command, env, projectName, captureStdo); - m_elapsed.start(); - m_process->start(); - qCDebug(mesonProcessLog()) << "Starting:" << command.toUserOutput(); - return true; -} - -void MesonProcess::handleProcessDone() -{ - if (m_process->result() != ProcessResult::FinishedWithSuccess) { - ProjectExplorer::TaskHub::addTask(ProjectExplorer::BuildSystemTask{ - ProjectExplorer::Task::TaskType::Error, m_process->exitMessage()}); - } - m_stdo = m_process->readAllRawStandardOutput(); - m_stderr = m_process->readAllRawStandardError(); - const QString elapsedTime = formatElapsedTime(m_elapsed.elapsed()); - MessageManager::writeSilently(elapsedTime); - emit finished(m_process->exitCode(), m_process->exitStatus()); -} - -void MesonProcess::setupProcess(const Command &command, const Environment &env, - const QString &projectName, bool captureStdo) -{ - if (m_process) - m_process.release()->deleteLater(); - m_process.reset(new Process); - connect(m_process.get(), &Process::done, this, &MesonProcess::handleProcessDone); - if (!captureStdo) { - connect(m_process.get(), &Process::readyReadStandardOutput, - this, &MesonProcess::processStandardOutput); - connect(m_process.get(), &Process::readyReadStandardError, - this, &MesonProcess::processStandardError); - } - - m_process->setWorkingDirectory(command.workDir()); - m_process->setEnvironment(env); - MessageManager::writeFlashing(Tr::tr("Running %1 in %2.") - .arg(command.toUserOutput(), command.workDir().toUserOutput())); - m_process->setCommand(command.cmdLine()); - m_process->setTimeoutS(10); - ProcessProgress *progress = new ProcessProgress(m_process.get()); - progress->setDisplayName(Tr::tr("Configuring \"%1\".").arg(projectName)); -} - -bool MesonProcess::sanityCheck(const Command &command) const -{ - const auto &exe = command.cmdLine().executable(); - if (!exe.exists()) { - //Should only reach this point if Meson exe is removed while a Meson project is opened - ProjectExplorer::TaskHub::addTask( - ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::TaskType::Error, - Tr::tr("Executable does not exist: %1") - .arg(exe.toUserOutput())}); - return false; - } - if (!exe.toFileInfo().isExecutable()) { - ProjectExplorer::TaskHub::addTask( - ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::TaskType::Error, - Tr::tr("Command is not executable: %1") - .arg(exe.toUserOutput())}); - return false; - } - return true; -} - -void MesonProcess::processStandardOutput() -{ - const auto data = m_process->readAllRawStandardOutput(); - MessageManager::writeSilently(QString::fromLocal8Bit(data)); - emit readyReadStandardOutput(data); -} - -void MesonProcess::processStandardError() -{ - MessageManager::writeSilently(QString::fromLocal8Bit(m_process->readAllRawStandardError())); -} - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/mesonprocess.h b/src/plugins/mesonprojectmanager/mesonprocess.h deleted file mode 100644 index c01427c8fb9..00000000000 --- a/src/plugins/mesonprojectmanager/mesonprocess.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QByteArray> -#include <QElapsedTimer> -#include <QObject> -#include <QProcess> - -#include <memory> - -namespace Utils { -class Environment; -class Process; -} - -namespace MesonProjectManager { -namespace Internal { - -class Command; - -class MesonProcess final : public QObject -{ - Q_OBJECT -public: - MesonProcess(); - ~MesonProcess(); - bool run(const Command &command, const Utils::Environment &env, - const QString &projectName, bool captureStdo = false); - - const QByteArray &stdOut() const { return m_stdo; } - const QByteArray &stdErr() const { return m_stderr; } -signals: - void finished(int exitCode, QProcess::ExitStatus exitStatus); - void readyReadStandardOutput(const QByteArray &data); - -private: - void handleProcessDone(); - void setupProcess(const Command &command, const Utils::Environment &env, - const QString &projectName, bool captureStdo); - bool sanityCheck(const Command &command) const; - - void processStandardOutput(); - void processStandardError(); - - std::unique_ptr<Utils::Process> m_process; - QElapsedTimer m_elapsed; - QByteArray m_stdo; - QByteArray m_stderr; -}; - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/mesonproject.cpp b/src/plugins/mesonprojectmanager/mesonproject.cpp index 546347d459f..1566aa25533 100644 --- a/src/plugins/mesonprojectmanager/mesonproject.cpp +++ b/src/plugins/mesonprojectmanager/mesonproject.cpp @@ -10,7 +10,7 @@ #include <coreplugin/icontext.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> diff --git a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs index c721ab2d207..b0c84579132 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs +++ b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs @@ -15,7 +15,6 @@ Project { Depends { name: "CppEditor" } Depends { name: "ProjectExplorer" } Depends { name: "QtSupport" } - Depends { name: "app_version_header" } cpp.includePaths: "." @@ -28,11 +27,6 @@ Project { "toolwrapper.cpp", "toolwrapper.h", "kitdata.h", - "kithelper.h", - "machinefilemanager.cpp", - "machinefilemanager.h", - "nativefilegenerator.cpp", - "nativefilegenerator.h", "mesonactionsmanager.cpp", "mesonactionsmanager.h", "buildoptions.h", @@ -46,9 +40,6 @@ Project { "target.h", "mesonpluginconstants.h", "mesonprojectplugin.cpp", - "mesonprojectplugin.h", - "mesonbuildsettingswidget.cpp", - "mesonbuildsettingswidget.h", "arrayoptionlineedit.cpp", "arrayoptionlineedit.h", "buildoptionsmodel.cpp", @@ -57,8 +48,6 @@ Project { "mesonbuildconfiguration.h", "mesonbuildsystem.cpp", "mesonbuildsystem.h", - "mesonprocess.cpp", - "mesonprocess.h", "mesonproject.cpp", "mesonproject.h", "mesonprojectimporter.cpp", @@ -103,7 +92,6 @@ Project { QtcAutotest { name: "mesonwrapper" - condition: project.withAutotests Depends { name: "Core" } Depends { name: "Utils" } @@ -124,7 +112,6 @@ Project { QtcAutotest { name: "mesoninfoparser" - condition: project.withAutotests Depends { name: "Core" } Depends { name: "Utils" } @@ -146,7 +133,6 @@ Project { QtcAutotest { name: "ninjaparser" - condition: project.withAutotests Depends { name: "Core" } Depends { name: "ProjectExplorer" } @@ -164,7 +150,6 @@ Project { QtcAutotest { name: "mesonparser" - condition: project.withAutotests Depends { name: "Core" } Depends { name: "ProjectExplorer" } diff --git a/src/plugins/mesonprojectmanager/mesonprojectnodes.cpp b/src/plugins/mesonprojectmanager/mesonprojectnodes.cpp index ad213ade0a3..4e842d30376 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectnodes.cpp +++ b/src/plugins/mesonprojectmanager/mesonprojectnodes.cpp @@ -10,41 +10,43 @@ #include <projectexplorer/project.h> #include <projectexplorer/target.h> -namespace MesonProjectManager { -namespace Internal { +using namespace ProjectExplorer; +using namespace Utils; -MesonProjectNode::MesonProjectNode(const Utils::FilePath &directory) - : ProjectExplorer::ProjectNode{directory} +namespace MesonProjectManager::Internal { + +MesonProjectNode::MesonProjectNode(const FilePath &directory) + : ProjectNode(directory) { setPriority(Node::DefaultProjectPriority + 1000); setIcon(Constants::Icons::MESON); setListInProject(false); } -MesonFileNode::MesonFileNode(const Utils::FilePath &file) - : ProjectExplorer::ProjectNode{file} +MesonFileNode::MesonFileNode(const FilePath &file) + : ProjectNode(file) { - setIcon(ProjectExplorer::DirectoryIcon(Constants::Icons::MESON)); + setIcon(DirectoryIcon(Constants::Icons::MESON)); setListInProject(true); } -MesonTargetNode::MesonTargetNode(const Utils::FilePath &directory, const QString &name) - : ProjectExplorer::ProjectNode{directory} - , m_name{name} +MesonTargetNode::MesonTargetNode(const FilePath &directory, const QString &name) + : ProjectNode(directory) + , m_name(name) { setPriority(Node::DefaultProjectPriority + 900); setIcon(":/projectexplorer/images/build.png"); setListInProject(false); setShowWhenEmpty(true); - setProductType(ProjectExplorer::ProductType::Other); + setProductType(ProductType::Other); } void MesonTargetNode::build() { - ProjectExplorer::Project *p = getProject(); + Project *p = getProject(); ProjectExplorer::Target *t = p ? p->activeTarget() : nullptr; if (t) - static_cast<MesonBuildSystem *>(t->buildSystem())->mesonBuildConfiguration()->build(m_name); + static_cast<MesonBuildConfiguration *>(t->buildSystem()->buildConfiguration())->build(m_name); } QString MesonTargetNode::tooltip() const @@ -57,5 +59,4 @@ QString MesonTargetNode::buildKey() const return m_name; } -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonprojectnodes.h b/src/plugins/mesonprojectmanager/mesonprojectnodes.h index 0a5cb919738..504a77b8dc4 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectnodes.h +++ b/src/plugins/mesonprojectmanager/mesonprojectnodes.h @@ -5,11 +5,7 @@ #include <projectexplorer/projectnodes.h> -#include <utils/fileutils.h> -#include <utils/fsengine/fileiconprovider.h> - -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { class MesonProjectNode : public ProjectExplorer::ProjectNode { @@ -21,6 +17,7 @@ class MesonTargetNode : public ProjectExplorer::ProjectNode { public: MesonTargetNode(const Utils::FilePath &directory, const QString &name); + void build() override; QString tooltip() const final; QString buildKey() const final; @@ -33,6 +30,7 @@ class MesonFileNode : public ProjectExplorer::ProjectNode { public: MesonFileNode(const Utils::FilePath &file); + bool showInSimpleTree() const final { return false; } std::optional<Utils::FilePath> visibleAfterAddFileAction() const override { @@ -40,5 +38,4 @@ public: } }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager:Internal diff --git a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp index 528865f3d37..5fb39a5f845 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectparser.cpp +++ b/src/plugins/mesonprojectmanager/mesonprojectparser.cpp @@ -4,34 +4,45 @@ #include "mesonprojectparser.h" #include "mesoninfoparser.h" +#include "mesonprojectmanagertr.h" #include "mesonprojectnodes.h" #include "mesontools.h" #include "projecttree.h" +#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/taskhub.h> #include <utils/async.h> +#include <utils/environment.h> #include <utils/fileinprojectfinder.h> - -#include <QStringList> -#include <QTextStream> +#include <utils/stringutils.h> #include <optional> +#include <coreplugin/progressmanager/processprogress.h> + +using namespace Core; +using namespace ProjectExplorer; +using namespace Utils; + namespace MesonProjectManager { namespace Internal { +static Q_LOGGING_CATEGORY(mesonProcessLog, "qtc.meson.buildsystem", QtWarningMsg); + struct CompilerArgs { QStringList args; QStringList includePaths; - ProjectExplorer::Macros macros; + Macros macros; }; -inline std::optional<QString> extractValueIfMatches(const QString &arg, - const QStringList &candidates) +static std::optional<QString> extractValueIfMatches(const QString &arg, + const QStringList &candidates) { for (const auto &flag : candidates) { if (arg.startsWith(flag)) @@ -40,22 +51,23 @@ inline std::optional<QString> extractValueIfMatches(const QString &arg, return std::nullopt; } -inline std::optional<QString> extractInclude(const QString &arg) +static std::optional<QString> extractInclude(const QString &arg) { return extractValueIfMatches(arg, {"-I", "/I", "-isystem", "-imsvc", "/imsvc"}); } -inline std::optional<ProjectExplorer::Macro> extractMacro(const QString &arg) + +static std::optional<Macro> extractMacro(const QString &arg) { auto define = extractValueIfMatches(arg, {"-D", "/D"}); if (define) - return ProjectExplorer::Macro::fromKeyValue(define->toLatin1()); + return Macro::fromKeyValue(define->toLatin1()); auto undef = extractValueIfMatches(arg, {"-U", "/U"}); if (undef) - return ProjectExplorer::Macro(undef->toLatin1(), ProjectExplorer::MacroType::Undefine); + return Macro(undef->toLatin1(), MacroType::Undefine); return std::nullopt; } -CompilerArgs splitArgs(const QStringList &args) +static CompilerArgs splitArgs(const QStringList &args) { CompilerArgs splited; for (const QString &arg : args) { @@ -74,7 +86,7 @@ CompilerArgs splitArgs(const QStringList &args) return splited; } -QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList) +static QStringList toAbsolutePath(const FilePath &refPath, QStringList &pathList) { QStringList allAbs; std::transform(std::cbegin(pathList), @@ -86,35 +98,22 @@ QStringList toAbsolutePath(const Utils::FilePath &refPath, QStringList &pathList return allAbs; } -MesonProjectParser::MesonProjectParser(const Utils::Id &meson, - Utils::Environment env, - ProjectExplorer::Project *project) +MesonProjectParser::MesonProjectParser(const Id &meson, const Environment &env, Project *project) : m_env{env} , m_meson{meson} , m_projectName{project->displayName()} { - connect(&m_process, &MesonProcess::finished, this, &MesonProjectParser::processFinished); - connect(&m_process, - &MesonProcess::readyReadStandardOutput, - &m_outputParser, - &MesonOutputParser::readStdo); - // TODO re-think the way all BuildSystem/ProjectParser are tied // I take project info here, I also take build and src dir later from // functions args. - auto fileFinder = new Utils::FileInProjectFinder; + auto fileFinder = new FileInProjectFinder; fileFinder->setProjectDirectory(project->projectDirectory()); - fileFinder->setProjectFiles(project->files(ProjectExplorer::Project::AllFiles)); + fileFinder->setProjectFiles(project->files(Project::AllFiles)); m_outputParser.setFileFinder(fileFinder); } -void MesonProjectParser::setMesonTool(const Utils::Id &meson) -{ - m_meson = meson; -} - -bool MesonProjectParser::configure(const Utils::FilePath &sourcePath, - const Utils::FilePath &buildPath, +bool MesonProjectParser::configure(const FilePath &sourcePath, + const FilePath &buildPath, const QStringList &args) { m_introType = IntroDataType::file; @@ -126,18 +125,18 @@ bool MesonProjectParser::configure(const Utils::FilePath &sourcePath, m_pendingCommands.enqueue( std::make_tuple(MesonTools::mesonWrapper(m_meson)->regenerate(sourcePath, buildPath), false)); - return m_process.run(cmd, m_env, m_projectName); + return run(cmd, m_env, m_projectName); } -bool MesonProjectParser::wipe(const Utils::FilePath &sourcePath, - const Utils::FilePath &buildPath, +bool MesonProjectParser::wipe(const FilePath &sourcePath, + const FilePath &buildPath, const QStringList &args) { return setup(sourcePath, buildPath, args, true); } -bool MesonProjectParser::setup(const Utils::FilePath &sourcePath, - const Utils::FilePath &buildPath, +bool MesonProjectParser::setup(const FilePath &sourcePath, + const FilePath &buildPath, const QStringList &args, bool forceWipe) { @@ -149,10 +148,10 @@ bool MesonProjectParser::setup(const Utils::FilePath &sourcePath, if (forceWipe || isSetup(buildPath)) cmdArgs << "--wipe"; auto cmd = MesonTools::mesonWrapper(m_meson)->setup(sourcePath, buildPath, cmdArgs); - return m_process.run(cmd, m_env, m_projectName); + return run(cmd, m_env, m_projectName); } -bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath) +bool MesonProjectParser::parse(const FilePath &sourcePath, const FilePath &buildPath) { m_srcDir = sourcePath; m_buildDir = buildPath; @@ -165,29 +164,29 @@ bool MesonProjectParser::parse(const Utils::FilePath &sourcePath, const Utils::F } } -bool MesonProjectParser::parse(const Utils::FilePath &sourcePath) +bool MesonProjectParser::parse(const FilePath &sourcePath) { m_srcDir = sourcePath; m_introType = IntroDataType::stdo; m_outputParser.setSourceDirectory(sourcePath); - return m_process.run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath), - m_env, - m_projectName, - true); + return run(MesonTools::mesonWrapper(m_meson)->introspect(sourcePath), + m_env, + m_projectName, + true); } -QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const +QList<BuildTargetInfo> MesonProjectParser::appsTargets() const { - QList<ProjectExplorer::BuildTargetInfo> apps; + QList<BuildTargetInfo> apps; for (const Target &target : m_parserResult.targets) { if (target.type == Target::Type::executable) { - ProjectExplorer::BuildTargetInfo bti; + BuildTargetInfo bti; bti.displayName = target.name; bti.buildKey = Target::fullName(m_buildDir, target); bti.displayNameUniquifier = bti.buildKey; - bti.targetFilePath = Utils::FilePath::fromString(target.fileName.first()); - bti.workingDirectory = Utils::FilePath::fromString(target.fileName.first()).absolutePath(); - bti.projectFilePath = Utils::FilePath::fromString(target.definedIn); + bti.targetFilePath = FilePath::fromString(target.fileName.first()); + bti.workingDirectory = FilePath::fromString(target.fileName.first()).absolutePath(); + bti.projectFilePath = FilePath::fromString(target.definedIn); bti.usesTerminal = true; apps.append(bti); } @@ -198,9 +197,9 @@ QList<ProjectExplorer::BuildTargetInfo> MesonProjectParser::appsTargets() const bool MesonProjectParser::startParser() { m_parserFutureResult = Utils::asyncRun( - ProjectExplorer::ProjectExplorerPlugin::sharedThreadPool(), - [processOutput = m_process.stdOut(), introType = m_introType, - buildDir = m_buildDir.toString(), srcDir = m_srcDir] { + ProjectExplorerPlugin::sharedThreadPool(), + [processOutput = m_stdo, introType = m_introType, + buildDir = m_buildDir, srcDir = m_srcDir] { if (introType == IntroDataType::file) return extractParserResults(srcDir, MesonInfoParser::parse(buildDir)); else @@ -212,7 +211,7 @@ bool MesonProjectParser::startParser() } MesonProjectParser::ParserData *MesonProjectParser::extractParserResults( - const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult) + const FilePath &srcDir, MesonInfoParser::Result &&parserResult) { auto rootNode = ProjectTree::buildTree(srcDir, parserResult.targets, @@ -220,13 +219,20 @@ MesonProjectParser::ParserData *MesonProjectParser::extractParserResults( return new ParserData{std::move(parserResult), std::move(rootNode)}; } -void MesonProjectParser::addMissingTargets(QStringList &targetList) +static void addMissingTargets(QStringList &targetList) { // Not all targets are listed in introspection data - for (const auto &target : additionalTargets()) { - if (!targetList.contains(target)) { + static const QString additionalTargets[] { + Constants::Targets::all, + Constants::Targets::clean, + Constants::Targets::install, + Constants::Targets::benchmark, + Constants::Targets::scan_build + }; + + for (const QString &target : additionalTargets) { + if (!targetList.contains(target)) targetList.append(target); - } } } @@ -245,17 +251,17 @@ void MesonProjectParser::update(const QFuture<MesonProjectParser::ParserData *> emit parsingCompleted(true); } -ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart( +RawProjectPart MesonProjectParser::buildRawPart( const Target &target, const Target::SourceGroup &sources, - const ProjectExplorer::ToolChain *cxxToolChain, - const ProjectExplorer::ToolChain *cToolChain) + const ToolChain *cxxToolChain, + const ToolChain *cToolChain) { - ProjectExplorer::RawProjectPart part; + RawProjectPart part; part.setDisplayName(target.name); part.setBuildSystemTarget(Target::fullName(m_buildDir, target)); part.setFiles(sources.sources + sources.generatedSources); - auto flags = splitArgs(sources.parameters); + CompilerArgs flags = splitArgs(sources.parameters); part.setMacros(flags.macros); part.setIncludePaths(toAbsolutePath(m_buildDir, flags.includePaths)); part.setProjectFileLocation(target.definedIn); @@ -267,30 +273,10 @@ ProjectExplorer::RawProjectPart MesonProjectParser::buildRawPart( return part; } -void MesonProjectParser::processFinished(int exitCode, QProcess::ExitStatus exitStatus) +RawProjectParts MesonProjectParser::buildProjectParts( + const ToolChain *cxxToolChain, const ToolChain *cToolChain) { - if (exitCode == 0 && exitStatus == QProcess::NormalExit) { - if (m_pendingCommands.isEmpty()) - startParser(); - else { - // see comment near m_pendingCommands declaration - std::tuple<Command, bool> args = m_pendingCommands.dequeue(); - m_process.run(std::get<0>(args), m_env, m_projectName, std::get<1>(args)); - } - } else { - if (m_introType == IntroDataType::stdo) { - auto data = m_process.stdErr(); - Core::MessageManager::writeSilently(QString::fromLocal8Bit(data)); - m_outputParser.readStdo(data); - } - emit parsingCompleted(false); - } -} - -ProjectExplorer::RawProjectParts MesonProjectParser::buildProjectParts( - const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain) -{ - ProjectExplorer::RawProjectParts parts; + RawProjectParts parts; for_each_source_group(m_parserResult.targets, [&parts, &cxxToolChain, @@ -321,11 +307,111 @@ bool MesonProjectParser::matchesKit(const KitData &kit) return matches; } -bool MesonProjectParser::usesSameMesonVersion(const Utils::FilePath &buildPath) +bool MesonProjectParser::usesSameMesonVersion(const FilePath &buildPath) { - auto info = MesonInfoParser::mesonInfo(buildPath.toString()); + auto info = MesonInfoParser::mesonInfo(buildPath); auto meson = MesonTools::mesonWrapper(m_meson); return info && meson && info->mesonVersion == meson->version(); } + + +bool MesonProjectParser::run(const Command &command, + const Environment &env, + const QString &projectName, + bool captureStdo) +{ + if (!sanityCheck(command)) + return false; + m_stdo.clear(); + TaskHub::clearTasks(ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + setupProcess(command, env, projectName, captureStdo); + m_elapsed.start(); + m_process->start(); + qCDebug(mesonProcessLog()) << "Starting:" << command.toUserOutput(); + return true; +} + +void MesonProjectParser::handleProcessDone() +{ + if (m_process->result() != ProcessResult::FinishedWithSuccess) + TaskHub::addTask(BuildSystemTask{Task::TaskType::Error, m_process->exitMessage()}); + + m_stdo = m_process->readAllRawStandardOutput(); + m_stderr = m_process->readAllRawStandardError(); + const QString elapsedTime = formatElapsedTime(m_elapsed.elapsed()); + MessageManager::writeSilently(elapsedTime); + + if (m_process->exitCode() == 0 && m_process->exitStatus() == QProcess::NormalExit) { + if (m_pendingCommands.isEmpty()) + startParser(); + else { + // see comment near m_pendingCommands declaration + std::tuple<Command, bool> args = m_pendingCommands.dequeue(); + run(std::get<0>(args), m_env, m_projectName, std::get<1>(args)); + } + } else { + if (m_introType == IntroDataType::stdo) { + MessageManager::writeSilently(QString::fromLocal8Bit(m_stderr)); + m_outputParser.readStdo(m_stderr); + } + emit parsingCompleted(false); + } +} + +void MesonProjectParser::setupProcess(const Command &command, const Environment &env, + const QString &projectName, bool captureStdo) +{ + if (m_process) + m_process.release()->deleteLater(); + m_process.reset(new Process); + connect(m_process.get(), &Process::done, this, &MesonProjectParser::handleProcessDone); + if (!captureStdo) { + connect(m_process.get(), &Process::readyReadStandardOutput, + this, &MesonProjectParser::processStandardOutput); + connect(m_process.get(), &Process::readyReadStandardError, + this, &MesonProjectParser::processStandardError); + } + + m_process->setWorkingDirectory(command.workDir()); + m_process->setEnvironment(env); + MessageManager::writeFlashing(Tr::tr("Running %1 in %2.") + .arg(command.toUserOutput(), command.workDir().toUserOutput())); + m_process->setCommand(command.cmdLine()); + m_process->setTimeoutS(10); + ProcessProgress *progress = new ProcessProgress(m_process.get()); + progress->setDisplayName(Tr::tr("Configuring \"%1\".").arg(projectName)); +} + +bool MesonProjectParser::sanityCheck(const Command &command) const +{ + const auto &exe = command.cmdLine().executable(); + if (!exe.exists()) { + //Should only reach this point if Meson exe is removed while a Meson project is opened + TaskHub::addTask( + BuildSystemTask{Task::TaskType::Error, + Tr::tr("Executable does not exist: %1").arg(exe.toUserOutput())}); + return false; + } + if (!exe.toFileInfo().isExecutable()) { + TaskHub::addTask( + BuildSystemTask{Task::TaskType::Error, + Tr::tr("Command is not executable: %1").arg(exe.toUserOutput())}); + return false; + } + return true; +} + +void MesonProjectParser::processStandardOutput() +{ + const auto data = m_process->readAllRawStandardOutput(); + MessageManager::writeSilently(QString::fromLocal8Bit(data)); + m_outputParser.readStdo(data); +} + +void MesonProjectParser::processStandardError() +{ + MessageManager::writeSilently(QString::fromLocal8Bit(m_process->readAllRawStandardError())); +} + } // namespace Internal } // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/mesonprojectparser.h b/src/plugins/mesonprojectmanager/mesonprojectparser.h index 168b4f1448d..e21fe5ec476 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectparser.h +++ b/src/plugins/mesonprojectmanager/mesonprojectparser.h @@ -6,7 +6,6 @@ #include "kitdata.h" #include "mesoninfoparser.h" #include "mesonoutputparser.h" -#include "mesonprocess.h" #include "mesonprojectnodes.h" #include "mesonwrapper.h" @@ -14,9 +13,6 @@ #include <projectexplorer/kit.h> #include <projectexplorer/rawprojectpart.h> -#include <utils/environment.h> -#include <utils/fileutils.h> - #include <QFuture> #include <QQueue> @@ -26,6 +22,7 @@ namespace Internal { class MesonProjectParser : public QObject { Q_OBJECT + enum class IntroDataType { file, stdo }; struct ParserData { @@ -34,8 +31,10 @@ class MesonProjectParser : public QObject }; public: - MesonProjectParser(const Utils::Id &meson, Utils::Environment env, ProjectExplorer::Project* project); - void setMesonTool(const Utils::Id &meson); + MesonProjectParser(const Utils::Id &meson, + const Utils::Environment &env, + ProjectExplorer::Project *project); + bool configure(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath, const QStringList &args); @@ -49,22 +48,11 @@ public: bool parse(const Utils::FilePath &sourcePath, const Utils::FilePath &buildPath); bool parse(const Utils::FilePath &sourcePath); - Q_SIGNAL void parsingCompleted(bool success); - std::unique_ptr<MesonProjectNode> takeProjectNode() { return std::move(m_rootNode); } - inline const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; }; - inline const TargetsList &targets() const { return m_parserResult.targets; } - inline const QStringList &targetsNames() const { return m_targetsNames; } - - static inline QStringList additionalTargets() - { - return QStringList{Constants::Targets::all, - Constants::Targets::clean, - Constants::Targets::install, - Constants::Targets::benchmark, - Constants::Targets::scan_build}; - } + const BuildOptionsList &buildOptions() const { return m_parserResult.buildOptions; }; + const TargetsList &targets() const { return m_parserResult.targets; } + const QStringList &targetsNames() const { return m_targetsNames; } QList<ProjectExplorer::BuildTargetInfo> appsTargets() const; @@ -72,26 +60,27 @@ public: const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain); - inline void setEnvironment(const Utils::Environment &environment) { m_env = environment; } + void setEnvironment(const Utils::Environment &environment) { m_env = environment; } - inline void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; } + void setQtVersion(Utils::QtMajorVersion v) { m_qtVersion = v; } bool matchesKit(const KitData &kit); bool usesSameMesonVersion(const Utils::FilePath &buildPath); +signals: + void parsingCompleted(bool success); + private: bool startParser(); static ParserData *extractParserResults(const Utils::FilePath &srcDir, MesonInfoParser::Result &&parserResult); - static void addMissingTargets(QStringList &targetList); void update(const QFuture<ParserData *> &data); ProjectExplorer::RawProjectPart buildRawPart(const Target &target, const Target::SourceGroup &sources, const ProjectExplorer::ToolChain *cxxToolChain, const ProjectExplorer::ToolChain *cToolChain); - void processFinished(int exitCode, QProcess::ExitStatus exitStatus); - MesonProcess m_process; + MesonOutputParser m_outputParser; Utils::Environment m_env; Utils::Id m_meson; @@ -99,7 +88,7 @@ private: Utils::FilePath m_srcDir; QFuture<ParserData *> m_parserFutureResult; bool m_configuring = false; - IntroDataType m_introType; + IntroDataType m_introType = IntroDataType::file; MesonInfoParser::Result m_parserResult; QStringList m_targetsNames; Utils::QtMajorVersion m_qtVersion = Utils::QtMajorVersion::Unknown; @@ -108,6 +97,22 @@ private: // maybe moving meson to build step could make this class simpler // also this should ease command dependencies QQueue<std::tuple<Command, bool>> m_pendingCommands; + + bool run(const Command &command, const Utils::Environment &env, + const QString &projectName, bool captureStdo = false); + + void handleProcessDone(); + void setupProcess(const Command &command, const Utils::Environment &env, + const QString &projectName, bool captureStdo); + bool sanityCheck(const Command &command) const; + + void processStandardOutput(); + void processStandardError(); + + std::unique_ptr<Utils::Process> m_process; + QElapsedTimer m_elapsed; + QByteArray m_stdo; + QByteArray m_stderr; }; } // namespace Internal diff --git a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp index 87beb589bbd..3b3a9e6b862 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp +++ b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp @@ -1,21 +1,16 @@ // Copyright (C) 2020 Alexis Jeandet. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "mesonprojectplugin.h" - -#include "machinefilemanager.h" #include "mesonactionsmanager.h" #include "mesonbuildconfiguration.h" +#include "mesonbuildsystem.h" #include "mesonproject.h" #include "mesonrunconfiguration.h" -#include "mesontoolkitaspect.h" #include "ninjabuildstep.h" -#include "ninjatoolkitaspect.h" -#include "settings.h" #include "toolssettingsaccessor.h" #include "toolssettingspage.h" -#include <coreplugin/icore.h> +#include <extensionsystem/iplugin.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> @@ -23,59 +18,47 @@ #include <utils/fsengine/fileiconprovider.h> -using namespace Core; using namespace ProjectExplorer; using namespace Utils; namespace MesonProjectManager::Internal { -class MesonProjectPluginPrivate : public QObject +class MesonProjectPluginPrivate { - Q_OBJECT public: - MesonProjectPluginPrivate() - { - MesonTools::setTools(m_toolsSettings.loadMesonTools(ICore::dialogParent())); - connect(ICore::instance(), - &ICore::saveSettingsRequested, - this, - &MesonProjectPluginPrivate::saveAll); - } - - ~MesonProjectPluginPrivate() {} - -private: - Settings m_settings; ToolsSettingsPage m_toolslSettingsPage; ToolsSettingsAccessor m_toolsSettings; - MesonToolKitAspect m_mesonKitAspect; - NinjaToolKitAspect m_ninjaKitAspect; MesonBuildStepFactory m_buildStepFactory; MesonBuildConfigurationFactory m_buildConfigurationFactory; MesonRunConfigurationFactory m_runConfigurationFactory; MesonActionsManager m_actions; MachineFileManager m_machineFilesManager; SimpleTargetRunnerFactory m_mesonRunWorkerFactory{{m_runConfigurationFactory.runConfigurationId()}}; - - void saveAll() - { - m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent()); - } }; -MesonProjectPlugin::~MesonProjectPlugin() +class MesonProjectPlugin final : public ExtensionSystem::IPlugin { - delete d; -} + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "MesonProjectManager.json") -void MesonProjectPlugin::initialize() -{ - d = new MesonProjectPluginPrivate; +public: + ~MesonProjectPlugin() final + { + delete d; + } - ProjectManager::registerProjectType<MesonProject>(Constants::Project::MIMETYPE); - FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson.build"); - FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson_options.txt"); -} +private: + void initialize() final + { + d = new MesonProjectPluginPrivate; + + ProjectManager::registerProjectType<MesonProject>(Constants::Project::MIMETYPE); + FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson.build"); + FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson_options.txt"); + } + + class MesonProjectPluginPrivate *d = nullptr; +}; } // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonprojectplugin.h b/src/plugins/mesonprojectmanager/mesonprojectplugin.h deleted file mode 100644 index 6cae4586dc5..00000000000 --- a/src/plugins/mesonprojectmanager/mesonprojectplugin.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace MesonProjectManager { -namespace Internal { - -class MesonProjectPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "MesonProjectManager.json") - -public: - ~MesonProjectPlugin() final; - -private: - void initialize() final; - - class MesonProjectPluginPrivate *d = nullptr; -}; - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp b/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp index 74919a49696..64d9cb3eeb0 100644 --- a/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp +++ b/src/plugins/mesonprojectmanager/mesonrunconfiguration.cpp @@ -23,32 +23,33 @@ public: MesonRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); + environment.setSupportForBuildEnvironment(target); - addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); - auto libAspect = addAspect<UseLibraryPathsAspect>(); - connect(libAspect, &UseLibraryPathsAspect::changed, - envAspect, &EnvironmentAspect::environmentChanged); + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + connect(&useLibraryPaths, &BaseAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); if (HostOsInfo::isMacHost()) { - auto dyldAspect = addAspect<UseDyldSuffixAspect>(); - connect(dyldAspect, &UseLibraryPathsAspect::changed, - envAspect, &EnvironmentAspect::environmentChanged); - envAspect->addModifier([dyldAspect](Utils::Environment &env) { - if (dyldAspect->value()) + connect(&useDyldSuffix, &BaseAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); + environment.addModifier([this](Environment &env) { + if (useDyldSuffix()) env.set(QLatin1String("DYLD_IMAGE_SUFFIX"), QLatin1String("_debug")); }); + } else { + useDyldSuffix.setVisible(false); } - envAspect->addModifier([this, libAspect](Environment &env) { + environment.addModifier([this](Environment &env) { BuildTargetInfo bti = buildTargetInfo(); if (bti.runEnvModifier) - bti.runEnvModifier(env, libAspect->value()); + bti.runEnvModifier(env, useLibraryPaths()); }); setUpdater([this] { @@ -56,14 +57,22 @@ public: return; BuildTargetInfo bti = buildTargetInfo(); - aspect<TerminalAspect>()->setUseTerminalHint(bti.usesTerminal); - aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath); - aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory); - emit aspect<EnvironmentAspect>()->environmentChanged(); + terminal.setUseTerminalHint(bti.usesTerminal); + executable.setExecutable(bti.targetFilePath); + workingDir.setDefaultWorkingDirectory(bti.workingDirectory); + emit environment.environmentChanged(); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); } + + EnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + UseLibraryPathsAspect useLibraryPaths{this}; + UseDyldSuffixAspect useDyldSuffix{this}; }; MesonRunConfigurationFactory::MesonRunConfigurationFactory() diff --git a/src/plugins/mesonprojectmanager/mesontoolkitaspect.cpp b/src/plugins/mesonprojectmanager/mesontoolkitaspect.cpp index 3c3298bc642..59fc7ffb4a6 100644 --- a/src/plugins/mesonprojectmanager/mesontoolkitaspect.cpp +++ b/src/plugins/mesonprojectmanager/mesontoolkitaspect.cpp @@ -8,71 +8,75 @@ #include <utils/qtcassert.h> -namespace MesonProjectManager { -namespace Internal { +using namespace ProjectExplorer; -static const char TOOL_ID[] = "MesonProjectManager.MesonKitInformation.Meson"; +namespace MesonProjectManager::Internal { -MesonToolKitAspect::MesonToolKitAspect() -{ - setObjectName(QLatin1String("MesonKitAspect")); - setId(TOOL_ID); - setDisplayName(Tr::tr("Meson Tool")); - setDescription(Tr::tr("The Meson tool to use when building a project with Meson.<br>" - "This setting is ignored when using other build systems.")); - setPriority(9000); -} +const char TOOL_ID[] = "MesonProjectManager.MesonKitInformation.Meson"; -ProjectExplorer::Tasks MesonToolKitAspect::validate(const ProjectExplorer::Kit *k) const -{ - ProjectExplorer::Tasks tasks; - const auto tool = mesonTool(k); - if (tool && !tool->isValid()) - tasks << ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::Warning, - Tr::tr("Cannot validate this meson executable.")}; - return tasks; -} - -void MesonToolKitAspect::setup(ProjectExplorer::Kit *k) -{ - const auto tool = mesonTool(k); - if (!tool) { - const auto autoDetected = MesonTools::mesonWrapper(); - if (autoDetected) - setMesonTool(k, autoDetected->id()); - } -} - -void MesonToolKitAspect::fix(ProjectExplorer::Kit *k) -{ - setup(k); -} - -ProjectExplorer::KitAspect::ItemList MesonToolKitAspect::toUserOutput( - const ProjectExplorer::Kit *k) const -{ - const auto tool = mesonTool(k); - if (tool) - return {{Tr::tr("Meson"), tool->name()}}; - return {{Tr::tr("Meson"), Tr::tr("Unconfigured")}}; -} - -ProjectExplorer::KitAspectWidget *MesonToolKitAspect::createConfigWidget(ProjectExplorer::Kit *k) const -{ - QTC_ASSERT(k, return nullptr); - return new ToolKitAspectWidget{k, this, ToolKitAspectWidget::ToolType::Meson}; -} - -void MesonToolKitAspect::setMesonTool(ProjectExplorer::Kit *kit, Utils::Id id) +void MesonToolKitAspect::setMesonTool(Kit *kit, Utils::Id id) { QTC_ASSERT(kit && id.isValid(), return ); kit->setValue(TOOL_ID, id.toSetting()); } -Utils::Id MesonToolKitAspect::mesonToolId(const ProjectExplorer::Kit *kit) +Utils::Id MesonToolKitAspect::mesonToolId(const Kit *kit) { QTC_ASSERT(kit, return {}); return Utils::Id::fromSetting(kit->value(TOOL_ID)); } -} // namespace Internal -} // namespace MesonProjectManager + +// MesonToolKitAspectFactory + +class MesonToolKitAspectFactory final : public KitAspectFactory +{ +public: + MesonToolKitAspectFactory() + { + setId(TOOL_ID); + setDisplayName(Tr::tr("Meson Tool")); + setDescription(Tr::tr("The Meson tool to use when building a project with Meson.<br>" + "This setting is ignored when using other build systems.")); + setPriority(9000); + } + + Tasks validate(const Kit *k) const final + { + Tasks tasks; + const auto tool = MesonToolKitAspect::mesonTool(k); + if (tool && !tool->isValid()) + tasks << BuildSystemTask{Task::Warning, Tr::tr("Cannot validate this meson executable.")}; + return tasks; + } + + void setup(Kit *k) final + { + const auto tool = MesonToolKitAspect::mesonTool(k); + if (!tool) { + const auto autoDetected = MesonTools::mesonWrapper(); + if (autoDetected) + MesonToolKitAspect::setMesonTool(k, autoDetected->id()); + } + } + void fix(Kit *k) final + { + setup(k); + } + + KitAspect *createKitAspect(Kit *k) const + { + return new ToolKitAspectWidget{k, this, ToolKitAspectWidget::ToolType::Meson}; + } + + ItemList toUserOutput( const Kit *k) const + { + const auto tool = MesonToolKitAspect::mesonTool(k); + if (tool) + return {{Tr::tr("Meson"), tool->name()}}; + return {{Tr::tr("Meson"), Tr::tr("Unconfigured")}}; + } +}; + +const MesonToolKitAspectFactory theMesonKitAspectFactory; + +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesontoolkitaspect.h b/src/plugins/mesonprojectmanager/mesontoolkitaspect.h index 7fb45be5873..3924d455f47 100644 --- a/src/plugins/mesonprojectmanager/mesontoolkitaspect.h +++ b/src/plugins/mesonprojectmanager/mesontoolkitaspect.h @@ -8,20 +8,11 @@ #include <projectexplorer/kit.h> #include <projectexplorer/kitmanager.h> -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { -class MesonToolKitAspect final : public ProjectExplorer::KitAspect +class MesonToolKitAspect final { public: - MesonToolKitAspect(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; - void setup(ProjectExplorer::Kit *k) final; - void fix(ProjectExplorer::Kit *k) final; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const final; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *) const final; - static void setMesonTool(ProjectExplorer::Kit *kit, Utils::Id id); static Utils::Id mesonToolId(const ProjectExplorer::Kit *kit); @@ -37,5 +28,4 @@ public: } }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/mesonwrapper.h b/src/plugins/mesonprojectmanager/mesonwrapper.h index 6ec1e57a0f1..7cf194a2736 100644 --- a/src/plugins/mesonprojectmanager/mesonwrapper.h +++ b/src/plugins/mesonprojectmanager/mesonwrapper.h @@ -24,7 +24,7 @@ namespace Internal { template<typename File_t> bool containsFiles(const QString &path, const File_t &file) { - return QFile::exists(QString("%1/%2").arg(path).arg(file)); + return QFileInfo::exists(QString("%1/%2").arg(path).arg(file)); } template<typename File_t, typename... T> @@ -78,18 +78,18 @@ public: Command introspect(const Utils::FilePath &sourceDirectory) const; - static inline std::optional<Utils::FilePath> find() + static std::optional<Utils::FilePath> find() { return ToolWrapper::findTool({"meson.py", "meson"}); } - static inline QString toolName() { return {"Meson"}; }; + static QString toolName() { return {"Meson"}; } }; template<> -inline QVariantMap toVariantMap<MesonWrapper>(const MesonWrapper &meson) +inline Utils::Store toVariantMap<MesonWrapper>(const MesonWrapper &meson) { - QVariantMap data; + Utils::Store data; data.insert(Constants::ToolsSettings::NAME_KEY, meson.m_name); data.insert(Constants::ToolsSettings::EXE_KEY, meson.m_exe.toSettings()); data.insert(Constants::ToolsSettings::AUTO_DETECTED_KEY, meson.m_autoDetected); @@ -98,7 +98,7 @@ inline QVariantMap toVariantMap<MesonWrapper>(const MesonWrapper &meson) return data; } template<> -inline MesonWrapper *fromVariantMap<MesonWrapper *>(const QVariantMap &data) +inline MesonWrapper *fromVariantMap<MesonWrapper *>(const Utils::Store &data) { return new MesonWrapper(data[Constants::ToolsSettings::NAME_KEY].toString(), Utils::FilePath::fromSettings(data[Constants::ToolsSettings::EXE_KEY]), diff --git a/src/plugins/mesonprojectmanager/nativefilegenerator.cpp b/src/plugins/mesonprojectmanager/nativefilegenerator.cpp deleted file mode 100644 index 9f920aa404d..00000000000 --- a/src/plugins/mesonprojectmanager/nativefilegenerator.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nativefilegenerator.h" - -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/kitmanager.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/toolchain.h> - -#include <utils/macroexpander.h> -#include <utils/qtcassert.h> - -namespace MesonProjectManager { -namespace Internal { - -NativeFileGenerator::NativeFileGenerator() {} - -inline void addEntry(QIODevice *nativeFile, const QString &key, const QString &value) -{ - nativeFile->write(QString("%1 = '%2'\n").arg(key).arg(value).toUtf8()); -} - -void writeBinariesSection(QIODevice *nativeFile, const KitData &kitData) -{ - nativeFile->write("[binaries]\n"); - addEntry(nativeFile, "c", kitData.cCompilerPath); - addEntry(nativeFile, "cpp", kitData.cxxCompilerPath); - addEntry(nativeFile, "qmake", kitData.qmakePath); - if (kitData.qtVersion == Utils::QtMajorVersion::Qt4) - addEntry(nativeFile, QString{"qmake-qt4"}, kitData.qmakePath); - else if (kitData.qtVersion == Utils::QtMajorVersion::Qt5) - addEntry(nativeFile, QString{"qmake-qt5"}, kitData.qmakePath); - else if (kitData.qtVersion == Utils::QtMajorVersion::Qt6) - addEntry(nativeFile, QString{"qmake-qt6"}, kitData.qmakePath); - addEntry(nativeFile, "cmake", kitData.cmakePath); -} - -void NativeFileGenerator::makeNativeFile(QIODevice *nativeFile, const KitData &kitData) -{ - QTC_ASSERT(nativeFile, return ); - writeBinariesSection(nativeFile, kitData); -} - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/nativefilegenerator.h b/src/plugins/mesonprojectmanager/nativefilegenerator.h deleted file mode 100644 index 21dba07ba19..00000000000 --- a/src/plugins/mesonprojectmanager/nativefilegenerator.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "kitdata.h" - -#include <projectexplorer/kit.h> - -#include <QIODevice> - -namespace MesonProjectManager { -namespace Internal { - -class NativeFileGenerator -{ - NativeFileGenerator(); - -public: - static void makeNativeFile(QIODevice *nativeFile, const KitData &kitData); -}; - -} // namespace Internal -} // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp index 0cb71186bdd..59d10acd2ba 100644 --- a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp +++ b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp @@ -189,7 +189,7 @@ MesonBuildStepFactory::MesonBuildStepFactory() setDisplayName(Tr::tr("Meson Build")); } -void MesonProjectManager::Internal::NinjaBuildStep::setBuildTarget(const QString &targetName) +void NinjaBuildStep::setBuildTarget(const QString &targetName) { m_targetName = targetName; } @@ -199,15 +199,14 @@ void NinjaBuildStep::setCommandArgs(const QString &args) m_commandArgs = args.trimmed(); } -QVariantMap NinjaBuildStep::toMap() const +void NinjaBuildStep::toMap(Store &map) const { - QVariantMap map(AbstractProcessStep::toMap()); + AbstractProcessStep::toMap(map); map.insert(TARGETS_KEY, m_targetName); map.insert(TOOL_ARGUMENTS_KEY, m_commandArgs); - return map; } -bool NinjaBuildStep::fromMap(const QVariantMap &map) +void NinjaBuildStep::fromMap(const Store &map) { m_targetName = map.value(TARGETS_KEY).toString(); m_commandArgs = map.value(TOOL_ARGUMENTS_KEY).toString(); diff --git a/src/plugins/mesonprojectmanager/ninjabuildstep.h b/src/plugins/mesonprojectmanager/ninjabuildstep.h index 1feecadb5f1..fd8b21334ae 100644 --- a/src/plugins/mesonprojectmanager/ninjabuildstep.h +++ b/src/plugins/mesonprojectmanager/ninjabuildstep.h @@ -25,8 +25,9 @@ public: const QString &targetName() const { return m_targetName; } Q_SIGNAL void targetListChanged(); Q_SIGNAL void commandChanged(); - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &map) override; + + void toMap(Utils::Store &map) const override; + void fromMap(const Utils::Store &map) override; private: void update(bool parsingSuccessful); diff --git a/src/plugins/mesonprojectmanager/ninjatoolkitaspect.cpp b/src/plugins/mesonprojectmanager/ninjatoolkitaspect.cpp index 409765e8441..4dae32a7b0d 100644 --- a/src/plugins/mesonprojectmanager/ninjatoolkitaspect.cpp +++ b/src/plugins/mesonprojectmanager/ninjatoolkitaspect.cpp @@ -8,60 +8,11 @@ #include <utils/qtcassert.h> -namespace MesonProjectManager { -namespace Internal { +using namespace ProjectExplorer; -static const char TOOL_ID[] = "MesonProjectManager.MesonKitInformation.Ninja"; +namespace MesonProjectManager::Internal { -NinjaToolKitAspect::NinjaToolKitAspect() -{ - setObjectName(QLatin1String("NinjaKitAspect")); - setId(TOOL_ID); - setDisplayName(Tr::tr("Ninja Tool")); - setDescription(Tr::tr("The Ninja tool to use when building a project with Meson.<br>" - "This setting is ignored when using other build systems.")); - setPriority(9000); -} - -ProjectExplorer::Tasks NinjaToolKitAspect::validate(const ProjectExplorer::Kit *k) const -{ - ProjectExplorer::Tasks tasks; - const auto tool = ninjaTool(k); - if (tool && !tool->isValid()) - tasks << ProjectExplorer::BuildSystemTask{ProjectExplorer::Task::Warning, - Tr::tr("Cannot validate this Ninja executable.")}; - return tasks; -} - -void NinjaToolKitAspect::setup(ProjectExplorer::Kit *k) -{ - const auto tool = ninjaTool(k); - if (!tool) { - const auto autoDetected = MesonTools::ninjaWrapper(); - if (autoDetected) - setNinjaTool(k, autoDetected->id()); - } -} - -void NinjaToolKitAspect::fix(ProjectExplorer::Kit *k) -{ - setup(k); -} - -ProjectExplorer::KitAspect::ItemList NinjaToolKitAspect::toUserOutput( - const ProjectExplorer::Kit *k) const -{ - const auto tool = ninjaTool(k); - if (tool) - return {{Tr::tr("Ninja"), tool->name()}}; - return {{Tr::tr("Ninja"), Tr::tr("Unconfigured")}}; -} - -ProjectExplorer::KitAspectWidget *NinjaToolKitAspect::createConfigWidget(ProjectExplorer::Kit *k) const -{ - QTC_ASSERT(k, return nullptr); - return new ToolKitAspectWidget{k, this, ToolKitAspectWidget::ToolType::Ninja}; -} +const char TOOL_ID[] = "MesonProjectManager.MesonKitInformation.Ninja"; void NinjaToolKitAspect::setNinjaTool(ProjectExplorer::Kit *kit, Utils::Id id) { @@ -74,5 +25,59 @@ Utils::Id NinjaToolKitAspect::ninjaToolId(const ProjectExplorer::Kit *kit) QTC_ASSERT(kit, return {}); return Utils::Id::fromSetting(kit->value(TOOL_ID)); } -} // namespace Internal -} // namespace MesonProjectManager + +// NinjaToolKitAspectFactory + +class NinjaToolKitAspectFactory final : public ProjectExplorer::KitAspectFactory +{ +public: + NinjaToolKitAspectFactory() + { + setId(TOOL_ID); + setDisplayName(Tr::tr("Ninja Tool")); + setDescription(Tr::tr("The Ninja tool to use when building a project with Meson.<br>" + "This setting is ignored when using other build systems.")); + setPriority(9000); + } + + Tasks validate(const Kit *k) const final + { + Tasks tasks; + const auto tool = NinjaToolKitAspect::ninjaTool(k); + if (tool && !tool->isValid()) + tasks << BuildSystemTask{Task::Warning, + Tr::tr("Cannot validate this Ninja executable.")}; + return tasks; + } + + void setup(Kit *k) final + { + const auto tool = NinjaToolKitAspect::ninjaTool(k); + if (!tool) { + const auto autoDetected = MesonTools::ninjaWrapper(); + if (autoDetected) + NinjaToolKitAspect::setNinjaTool(k, autoDetected->id()); + } + } + void fix(Kit *k) final + { + setup(k); + } + + ItemList toUserOutput( const Kit *k) const + { + const auto tool = NinjaToolKitAspect::ninjaTool(k); + if (tool) + return {{Tr::tr("Ninja"), tool->name()}}; + return {{Tr::tr("Ninja"), Tr::tr("Unconfigured")}}; + } + + KitAspect *createKitAspect(Kit *k) const final + { + return new ToolKitAspectWidget(k, this, ToolKitAspectWidget::ToolType::Ninja); + } +}; + +const NinjaToolKitAspectFactory theNinjaToolKitAspectFactory; + +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/ninjatoolkitaspect.h b/src/plugins/mesonprojectmanager/ninjatoolkitaspect.h index 36b0af57f68..d1ee50c7a38 100644 --- a/src/plugins/mesonprojectmanager/ninjatoolkitaspect.h +++ b/src/plugins/mesonprojectmanager/ninjatoolkitaspect.h @@ -8,20 +8,11 @@ #include <projectexplorer/kit.h> #include <projectexplorer/kitmanager.h> -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { -class NinjaToolKitAspect final : public ProjectExplorer::KitAspect +class NinjaToolKitAspect final { public: - NinjaToolKitAspect(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const final; - void setup(ProjectExplorer::Kit *k) final; - void fix(ProjectExplorer::Kit *k) final; - ItemList toUserOutput(const ProjectExplorer::Kit *k) const final; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *) const final; - static void setNinjaTool(ProjectExplorer::Kit *kit, Utils::Id id); static Utils::Id ninjaToolId(const ProjectExplorer::Kit *kit); @@ -36,5 +27,4 @@ public: } }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/ninjawrapper.h b/src/plugins/mesonprojectmanager/ninjawrapper.h index 4b99fdf4020..93339d37f9e 100644 --- a/src/plugins/mesonprojectmanager/ninjawrapper.h +++ b/src/plugins/mesonprojectmanager/ninjawrapper.h @@ -14,17 +14,17 @@ class NinjaWrapper final : public ToolWrapper public: using ToolWrapper::ToolWrapper; - static inline std::optional<Utils::FilePath> find() + static std::optional<Utils::FilePath> find() { return ToolWrapper::findTool({"ninja", "ninja-build"}); } - static inline QString toolName() { return {"Ninja"}; }; + static QString toolName() { return {"Ninja"}; } }; template<> -inline QVariantMap toVariantMap<NinjaWrapper>(const NinjaWrapper &meson) +inline Utils::Store toVariantMap<NinjaWrapper>(const NinjaWrapper &meson) { - QVariantMap data; + Utils::Store data; data.insert(Constants::ToolsSettings::NAME_KEY, meson.m_name); data.insert(Constants::ToolsSettings::EXE_KEY, meson.m_exe.toSettings()); data.insert(Constants::ToolsSettings::AUTO_DETECTED_KEY, meson.m_autoDetected); @@ -33,7 +33,7 @@ inline QVariantMap toVariantMap<NinjaWrapper>(const NinjaWrapper &meson) return data; } template<> -inline NinjaWrapper *fromVariantMap<NinjaWrapper *>(const QVariantMap &data) +inline NinjaWrapper *fromVariantMap<NinjaWrapper *>(const Utils::Store &data) { return new NinjaWrapper(data[Constants::ToolsSettings::NAME_KEY].toString(), Utils::FilePath::fromSettings(data[Constants::ToolsSettings::EXE_KEY]), diff --git a/src/plugins/mesonprojectmanager/settings.cpp b/src/plugins/mesonprojectmanager/settings.cpp index e1fe290bc5c..b233ae3c48b 100644 --- a/src/plugins/mesonprojectmanager/settings.cpp +++ b/src/plugins/mesonprojectmanager/settings.cpp @@ -6,29 +6,23 @@ #include "mesonpluginconstants.h" #include "mesonprojectmanagertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/layoutbuilder.h> namespace MesonProjectManager::Internal { -static Settings *s_instance; - -Settings &settings() +MesonSettings &settings() { - return *s_instance; + static MesonSettings theSettings; + return theSettings; } -Settings::Settings() +MesonSettings::MesonSettings() { - s_instance = this; - + setAutoApply(false); setSettingsGroup("MesonProjectManager"); - setId("A.MesonProjectManager.SettingsPage.General"); - setDisplayName(Tr::tr("General")); - setDisplayCategory("Meson"); - setCategory(Constants::SettingsPage::CATEGORY); - setCategoryIconPath(Constants::Icons::MESON_BW); - autorunMeson.setSettingsKey("meson.autorun"); autorunMeson.setLabelText(Tr::tr("Autorun Meson")); autorunMeson.setToolTip(Tr::tr("Automatically run Meson when needed.")); @@ -49,4 +43,20 @@ Settings::Settings() readSettings(); } +class MesonSettingsPage final : public Core::IOptionsPage +{ +public: + MesonSettingsPage() + { + setId("A.MesonProjectManager.SettingsPage.General"); + setDisplayName(Tr::tr("General")); + setDisplayCategory("Meson"); + setCategory(Constants::SettingsPage::CATEGORY); + setCategoryIconPath(Constants::Icons::MESON_BW); + setSettingsProvider([] { return &settings(); }); + } +}; + +const MesonSettingsPage settingsPage; + } // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/settings.h b/src/plugins/mesonprojectmanager/settings.h index 34c2e9e97d9..8ddc5f53ece 100644 --- a/src/plugins/mesonprojectmanager/settings.h +++ b/src/plugins/mesonprojectmanager/settings.h @@ -3,21 +3,19 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> - #include <utils/aspects.h> namespace MesonProjectManager::Internal { -class Settings : public Core::PagedSettings +class MesonSettings final : public Utils::AspectContainer { public: - Settings(); + MesonSettings(); Utils::BoolAspect autorunMeson{this}; Utils::BoolAspect verboseNinja{this}; }; -Settings &settings(); +MesonSettings &settings(); } // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/targetparser.h b/src/plugins/mesonprojectmanager/targetparser.h index 2b31083ba26..9c88b12b267 100644 --- a/src/plugins/mesonprojectmanager/targetparser.h +++ b/src/plugins/mesonprojectmanager/targetparser.h @@ -63,12 +63,10 @@ class TargetParser } public: - TargetParser(const QString &buildDir) + TargetParser(const Utils::FilePath &buildDir) { - auto arr = load<QJsonArray>(QString("%1/%2/%3") - .arg(buildDir) - .arg(Constants::MESON_INFO_DIR) - .arg(Constants::MESON_INTRO_TARGETS)); + Utils::FilePath path = buildDir / Constants::MESON_INFO_DIR / Constants::MESON_INTRO_TARGETS; + auto arr = load<QJsonArray>(path.toFSPathString()); if (arr) m_targets = load_targets(*arr); } diff --git a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp index f45b59c86f3..37051b7ecdc 100644 --- a/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp +++ b/src/plugins/mesonprojectmanager/tests/testmesoninfoparser.cpp @@ -19,6 +19,7 @@ #include <iostream> using namespace MesonProjectManager::Internal; +using namespace Utils; struct projectData { @@ -27,22 +28,9 @@ struct projectData QStringList targets; }; -namespace { static const QList<projectData> projectList{ {"Simple C Project", "simplecproject", {"SimpleCProject"}}}; -} // namespace -#define WITH_CONFIGURED_PROJECT(_source_dir, _build_dir, ...) \ - { \ - QTemporaryDir _build_dir{"test-meson"}; \ - const auto tool = MesonWrapper::find(); \ - QVERIFY(tool.has_value()); \ - const auto _meson = MesonWrapper("name", *tool); \ - run_meson(_meson.setup(Utils::FilePath::fromString(_source_dir), \ - Utils::FilePath::fromString(_build_dir.path()))); \ - QVERIFY(isSetup(Utils::FilePath::fromString(_build_dir.path()))); \ - __VA_ARGS__ \ - } #define WITH_UNCONFIGURED_PROJECT(_source_dir, _intro_file, ...) \ { \ @@ -86,15 +74,24 @@ private slots: { QFETCH(QString, src_dir); QFETCH(QStringList, expectedTargets); - WITH_CONFIGURED_PROJECT(src_dir, build_dir, { - auto result = MesonInfoParser::parse(build_dir.path()); + + { + QTemporaryDir build_dir{"test-meson"}; + FilePath buildDir = FilePath::fromString(build_dir.path()); + const auto tool = MesonWrapper::find(); + QVERIFY(tool.has_value()); + MesonWrapper meson("name", *tool); + run_meson(meson.setup(FilePath::fromString(src_dir), buildDir)); + QVERIFY(isSetup(buildDir)); + + auto result = MesonInfoParser::parse(buildDir); QStringList targetsNames; std::transform(std::cbegin(result.targets), std::cend(result.targets), std::back_inserter(targetsNames), [](const auto &target) { return target.name; }); QVERIFY(targetsNames == expectedTargets); - }) + } WITH_UNCONFIGURED_PROJECT(src_dir, introFile, { auto result = MesonInfoParser::parse(&introFile); diff --git a/src/plugins/mesonprojectmanager/toolitemsettings.cpp b/src/plugins/mesonprojectmanager/toolitemsettings.cpp index a0591b905c5..1b66cc2d03d 100644 --- a/src/plugins/mesonprojectmanager/toolitemsettings.cpp +++ b/src/plugins/mesonprojectmanager/toolitemsettings.cpp @@ -22,7 +22,7 @@ ToolItemSettings::ToolItemSettings(QWidget *parent) m_mesonPathChooser = new PathChooser; m_mesonPathChooser->setExpectedKind(PathChooser::ExistingCommand); - m_mesonPathChooser->setHistoryCompleter(QLatin1String("Meson.Command.History")); + m_mesonPathChooser->setHistoryCompleter("Meson.Command.History"); using namespace Layouting; diff --git a/src/plugins/mesonprojectmanager/toolkitaspectwidget.cpp b/src/plugins/mesonprojectmanager/toolkitaspectwidget.cpp index d556ab2ebbc..e3398aa9f79 100644 --- a/src/plugins/mesonprojectmanager/toolkitaspectwidget.cpp +++ b/src/plugins/mesonprojectmanager/toolkitaspectwidget.cpp @@ -13,9 +13,9 @@ namespace MesonProjectManager { namespace Internal { ToolKitAspectWidget::ToolKitAspectWidget(ProjectExplorer::Kit *kit, - const ProjectExplorer::KitAspect *ki, + const ProjectExplorer::KitAspectFactory *factory, ToolType type) - : ProjectExplorer::KitAspectWidget(kit, ki) + : ProjectExplorer::KitAspect(kit, factory) , m_toolsComboBox(createSubWidget<QComboBox>()) , m_manageButton(createManageButton(Constants::SettingsPage::TOOLS_ID)) , m_type{type} @@ -23,7 +23,7 @@ ToolKitAspectWidget::ToolKitAspectWidget(ProjectExplorer::Kit *kit, m_toolsComboBox->setSizePolicy(QSizePolicy::Ignored, m_toolsComboBox->sizePolicy().verticalPolicy()); m_toolsComboBox->setEnabled(false); - m_toolsComboBox->setToolTip(ki->description()); + m_toolsComboBox->setToolTip(factory->description()); loadTools(); connect(MesonTools::instance(), &MesonTools::toolAdded, diff --git a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h index c327fb4e2b1..9a354dc1e75 100644 --- a/src/plugins/mesonprojectmanager/toolkitaspectwidget.h +++ b/src/plugins/mesonprojectmanager/toolkitaspectwidget.h @@ -15,13 +15,13 @@ namespace MesonProjectManager { namespace Internal { -class ToolKitAspectWidget final : public ProjectExplorer::KitAspectWidget +class ToolKitAspectWidget final : public ProjectExplorer::KitAspect { public: enum class ToolType { Meson, Ninja }; ToolKitAspectWidget(ProjectExplorer::Kit *kit, - const ProjectExplorer::KitAspect *ki, + const ProjectExplorer::KitAspectFactory *factory, ToolType type); ~ToolKitAspectWidget(); @@ -36,7 +36,7 @@ private: void makeReadOnly() override { m_toolsComboBox->setEnabled(false); } - void addToLayout(Layouting::LayoutItem &parent) override + void addToLayoutImpl(Layouting::LayoutItem &parent) override { addMutableAction(m_toolsComboBox); parent.addItem(m_toolsComboBox); diff --git a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp index a67175742bb..80e5274c481 100644 --- a/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp +++ b/src/plugins/mesonprojectmanager/toolssettingsaccessor.cpp @@ -6,67 +6,76 @@ #include "mesonpluginconstants.h" #include "mesonprojectmanagertr.h" -#include <app/app_version.h> #include <coreplugin/icore.h> -#include <utils/fileutils.h> +#include <coreplugin/icore.h> -#include <QCoreApplication> -#include <QVariantMap> +#include <utils/filepath.h> +#include <utils/store.h> + +#include <QGuiApplication> #include <iterator> #include <vector> +using namespace Core; +using namespace Utils; + namespace MesonProjectManager { namespace Internal { -static QString entryName(int index) +static Key entryName(int index) { - return QString("%1%2").arg(Constants::ToolsSettings::ENTRY_KEY).arg(index); + return numberedKey(Constants::ToolsSettings::ENTRY_KEY, index); } ToolsSettingsAccessor::ToolsSettingsAccessor() { setDocType("QtCreatorMesonTools"); - setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); - setBaseFilePath(Core::ICore::userResourcePath(Constants::ToolsSettings::FILENAME)); + setApplicationDisplayName(QGuiApplication::applicationDisplayName()); + setBaseFilePath(ICore::userResourcePath(Constants::ToolsSettings::FILENAME)); + + MesonTools::setTools(loadMesonTools()); + + QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, [this] { + saveMesonTools(MesonTools::tools()); + }); } -void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools, - QWidget *parent) +void ToolsSettingsAccessor::saveMesonTools(const std::vector<MesonTools::Tool_t> &tools) { using namespace Constants; - QVariantMap data; + Store data; int entry_count = 0; for (const MesonTools::Tool_t &tool : tools) { auto asMeson = std::dynamic_pointer_cast<MesonWrapper>(tool); if (asMeson) - data.insert(entryName(entry_count), toVariantMap<MesonWrapper>(*asMeson)); + data.insert(entryName(entry_count), variantFromStore(toVariantMap<MesonWrapper>(*asMeson))); else { auto asNinja = std::dynamic_pointer_cast<NinjaWrapper>(tool); if (asNinja) - data.insert(entryName(entry_count), toVariantMap<NinjaWrapper>(*asNinja)); + data.insert(entryName(entry_count), variantFromStore(toVariantMap<NinjaWrapper>(*asNinja))); } entry_count++; } data.insert(ToolsSettings::ENTRY_COUNT, entry_count); - saveSettings(data, parent); + saveSettings(data, ICore::dialogParent()); } -std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools(QWidget *parent) +std::vector<MesonTools::Tool_t> ToolsSettingsAccessor::loadMesonTools() { using namespace Constants; - auto data = restoreSettings(parent); + auto data = restoreSettings(ICore::dialogParent()); auto entry_count = data.value(ToolsSettings::ENTRY_COUNT, 0).toInt(); std::vector<MesonTools::Tool_t> result; for (auto toolIndex = 0; toolIndex < entry_count; toolIndex++) { - auto name = entryName(toolIndex); + Key name = entryName(toolIndex); if (data.contains(name)) { const auto map = data[name].toMap(); auto type = map.value(ToolsSettings::TOOL_TYPE_KEY, ToolsSettings::TOOL_TYPE_MESON); if (type == ToolsSettings::TOOL_TYPE_NINJA) - result.emplace_back(fromVariantMap<NinjaWrapper *>(data[name].toMap())); + result.emplace_back(fromVariantMap<NinjaWrapper *>(storeFromVariant(data[name]))); else - result.emplace_back(fromVariantMap<MesonWrapper *>(data[name].toMap())); + result.emplace_back(fromVariantMap<MesonWrapper *>(storeFromVariant(data[name]))); } } return result; diff --git a/src/plugins/mesonprojectmanager/toolssettingsaccessor.h b/src/plugins/mesonprojectmanager/toolssettingsaccessor.h index 61cc505d3a7..10fce88099d 100644 --- a/src/plugins/mesonprojectmanager/toolssettingsaccessor.h +++ b/src/plugins/mesonprojectmanager/toolssettingsaccessor.h @@ -14,8 +14,9 @@ class ToolsSettingsAccessor final : public Utils::UpgradingSettingsAccessor { public: ToolsSettingsAccessor(); - void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools, QWidget *parent); - std::vector<MesonTools::Tool_t> loadMesonTools(QWidget *parent); + + void saveMesonTools(const std::vector<MesonTools::Tool_t> &tools); + std::vector<MesonTools::Tool_t> loadMesonTools(); }; } // namespace Internal diff --git a/src/plugins/mesonprojectmanager/toolwrapper.cpp b/src/plugins/mesonprojectmanager/toolwrapper.cpp index 83ed27f495d..632465c219e 100644 --- a/src/plugins/mesonprojectmanager/toolwrapper.cpp +++ b/src/plugins/mesonprojectmanager/toolwrapper.cpp @@ -5,6 +5,8 @@ #include <utils/process.h> +#include <QUuid> + namespace MesonProjectManager { namespace Internal { diff --git a/src/plugins/mesonprojectmanager/toolwrapper.h b/src/plugins/mesonprojectmanager/toolwrapper.h index be7bbb5a11d..47fe583eff3 100644 --- a/src/plugins/mesonprojectmanager/toolwrapper.h +++ b/src/plugins/mesonprojectmanager/toolwrapper.h @@ -7,14 +7,9 @@ #include <utils/commandline.h> #include <utils/environment.h> -#include <utils/fileutils.h> #include <utils/id.h> #include <utils/qtcassert.h> - -#include <QFileInfo> -#include <QUuid> -#include <QVariant> -#include <QVariantMap> +#include <utils/store.h> #include <optional> @@ -32,17 +27,17 @@ public: : m_cmd{exe, args} , m_workDir{workDir} {} - inline const Utils::CommandLine &cmdLine() const { return m_cmd; } - inline const Utils::FilePath &workDir() const { return m_workDir; } - inline Utils::FilePath executable() const { return m_cmd.executable(); } - inline QStringList arguments() const { return m_cmd.splitArguments(); } - inline QString toUserOutput() const { return m_cmd.toUserOutput(); }; + const Utils::CommandLine &cmdLine() const { return m_cmd; } + const Utils::FilePath &workDir() const { return m_workDir; } + Utils::FilePath executable() const { return m_cmd.executable(); } + QStringList arguments() const { return m_cmd.splitArguments(); } + QString toUserOutput() const { return m_cmd.toUserOutput(); } }; class ToolWrapper { public: - virtual ~ToolWrapper(){}; + virtual ~ToolWrapper() {} ToolWrapper() = delete; ToolWrapper(const QString &name, const Utils::FilePath &path, bool autoDetected = false); ToolWrapper(const QString &name, @@ -54,12 +49,12 @@ public: ToolWrapper &operator=(const ToolWrapper &other) = default; ToolWrapper &operator=(ToolWrapper &&other) = default; - inline const Version &version() const noexcept { return m_version; }; - inline bool isValid() const noexcept { return m_isValid; }; - inline bool autoDetected() const noexcept { return m_autoDetected; }; - inline Utils::Id id() const noexcept { return m_id; }; - inline Utils::FilePath exe() const noexcept { return m_exe; }; - inline QString name() const noexcept { return m_name; }; + const Version &version() const noexcept { return m_version; } + bool isValid() const noexcept { return m_isValid; } + bool autoDetected() const noexcept { return m_autoDetected; } + Utils::Id id() const noexcept { return m_id; } + Utils::FilePath exe() const noexcept { return m_exe; } + QString name() const noexcept { return m_name; } inline void setName(const QString &newName) { m_name = newName; } virtual void setExe(const Utils::FilePath &newExe); @@ -69,9 +64,9 @@ public: static std::optional<Utils::FilePath> findTool(const QStringList &exeNames); template<typename T> - friend QVariantMap toVariantMap(const T &); + friend Utils::Store toVariantMap(const T &); template<typename T> - friend T fromVariantMap(const QVariantMap &); + friend T fromVariantMap(const Utils::Store &); protected: Version m_version; @@ -83,9 +78,9 @@ protected: }; template<typename T> -QVariantMap toVariantMap(const T &); +Utils::Store toVariantMap(const T &); template<typename T> -T fromVariantMap(const QVariantMap &); +T fromVariantMap(const Utils::Store &); } // namespace Internal } // namespace MesonProjectManager diff --git a/src/plugins/modeleditor/CMakeLists.txt b/src/plugins/modeleditor/CMakeLists.txt index bb21d68692f..aeefa8c47c6 100644 --- a/src/plugins/modeleditor/CMakeLists.txt +++ b/src/plugins/modeleditor/CMakeLists.txt @@ -27,7 +27,6 @@ add_qtc_plugin(ModelEditor pxnodecontroller.cpp pxnodecontroller.h pxnodeutilities.cpp pxnodeutilities.h resources/modeleditor.qrc - settingscontroller.cpp settingscontroller.h uicontroller.cpp uicontroller.h EXPLICIT_MOC actionhandler.h diff --git a/src/plugins/modeleditor/ModelEditor.json.in b/src/plugins/modeleditor/ModelEditor.json.in index ea4aa9d92f6..6237bce22f1 100644 --- a/src/plugins/modeleditor/ModelEditor.json.in +++ b/src/plugins/modeleditor/ModelEditor.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"ModelEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Jochen Becher\", - \"Copyright\" : \"(C) 2017 Jochen Becher, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ModelEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Jochen Becher", + "Copyright" : "(C) 2017 Jochen Becher, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Modeling\", - \"Description\" : \"Graphical modeling with structured diagrams.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Modeling", + "Description" : "Graphical modeling with structured diagrams.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/vnd.qtcreator.model\'>\", - \" <sub-class-of type=\'text/xml\'/>\", - \" <comment>Qt Creator Model File</comment>\", - \" <glob pattern=\'*.qmodel\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0' encoding='UTF-8'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/vnd.qtcreator.model'>", + " <sub-class-of type='text/xml'/>", + " <comment>Qt Creator Model File</comment>", + " <glob pattern='*.qmodel'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/modeleditor/classviewcontroller.cpp b/src/plugins/modeleditor/classviewcontroller.cpp index 68f5a66ae2a..16dcdb92d45 100644 --- a/src/plugins/modeleditor/classviewcontroller.cpp +++ b/src/plugins/modeleditor/classviewcontroller.cpp @@ -21,8 +21,7 @@ QSet<QString> ClassViewController::findClassDeclarations(const FilePath &filePat { QSet<QString> classNames; - CppEditor::CppModelManager *cppModelManager = CppEditor::CppModelManager::instance(); - CPlusPlus::Snapshot snapshot = cppModelManager->snapshot(); + CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot(); // scan original file CPlusPlus::Document::Ptr document = snapshot.document(filePath); diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp index 4071c5b0159..684d104fd9f 100644 --- a/src/plugins/modeleditor/componentviewcontroller.cpp +++ b/src/plugins/modeleditor/componentviewcontroller.cpp @@ -146,8 +146,7 @@ void UpdateIncludeDependenciesVisitor::updateFilePaths() void UpdateIncludeDependenciesVisitor::visitMComponent(qmt::MComponent *component) { - CppEditor::CppModelManager *cppModelManager = CppEditor::CppModelManager::instance(); - CPlusPlus::Snapshot snapshot = cppModelManager->snapshot(); + CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot(); const QStringList filePaths = findFilePathOfComponent(component); for (const QString &filePath : filePaths) { diff --git a/src/plugins/modeleditor/elementtasks.cpp b/src/plugins/modeleditor/elementtasks.cpp index 6413686f61d..3cc271214fe 100644 --- a/src/plugins/modeleditor/elementtasks.cpp +++ b/src/plugins/modeleditor/elementtasks.cpp @@ -87,7 +87,7 @@ bool ElementTasks::hasClassDefinition(const qmt::MElement *element) const if (auto klass = dynamic_cast<const qmt::MClass *>(element)) { const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name() : klass->umlNamespace() + "::" + klass->name(); - auto *locatorData = CppModelManager::instance()->locatorData(); + auto *locatorData = CppModelManager::locatorData(); if (!locatorData) return false; const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class, @@ -118,7 +118,7 @@ void ElementTasks::openClassDefinition(const qmt::MElement *element) const QString qualifiedClassName = klass->umlNamespace().isEmpty() ? klass->name() : klass->umlNamespace() + "::" + klass->name(); - auto *locatorData = CppModelManager::instance()->locatorData(); + auto *locatorData = CppModelManager::locatorData(); if (!locatorData) return; const QList<IndexItem::Ptr> matches = locatorData->findSymbols(IndexItem::Class, diff --git a/src/plugins/modeleditor/extpropertiesmview.cpp b/src/plugins/modeleditor/extpropertiesmview.cpp index 4977d816b18..b57d1bc2a96 100644 --- a/src/plugins/modeleditor/extpropertiesmview.cpp +++ b/src/plugins/modeleditor/extpropertiesmview.cpp @@ -41,9 +41,6 @@ void ExtPropertiesMView::visitMPackage(const qmt::MPackage *package) m_configPath = new Utils::PathChooser(m_topWidget); m_configPath->setPromptDialogTitle(Tr::tr("Select Custom Configuration Folder")); m_configPath->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_configPath->setValidationFunction([=](Utils::FancyLineEdit *edit, QString *errorMessage) { - return edit->text().isEmpty() || m_configPath->defaultValidationFunction()(edit, errorMessage); - }); m_configPath->setInitialBrowsePathBackup( Utils::FilePath::fromString(project->fileName()).absolutePath()); addRow(Tr::tr("Config path:"), m_configPath, "configpath"); diff --git a/src/plugins/modeleditor/modeldocument.cpp b/src/plugins/modeleditor/modeldocument.cpp index dc34acacf70..a1c271539c6 100644 --- a/src/plugins/modeleditor/modeldocument.cpp +++ b/src/plugins/modeleditor/modeldocument.cpp @@ -62,8 +62,7 @@ bool ModelDocument::saveImpl(QString *errorString, const Utils::FilePath &filePa return false; } - const Utils::FilePath actualName = filePath.isEmpty() ? this->filePath() : filePath; - d->documentController->projectController()->setFileName(actualName.toString()); + d->documentController->projectController()->setFileName(filePath.toString()); try { d->documentController->projectController()->save(); } catch (const qmt::Exception &ex) { diff --git a/src/plugins/modeleditor/modeleditor.qbs b/src/plugins/modeleditor/modeleditor.qbs index a2ac96adc49..b9ad90bd3e8 100644 --- a/src/plugins/modeleditor/modeleditor.qbs +++ b/src/plugins/modeleditor/modeleditor.qbs @@ -70,8 +70,6 @@ QtcPlugin { "pxnodeutilities.cpp", "pxnodeutilities.h", "resources/modeleditor.qrc", - "settingscontroller.cpp", - "settingscontroller.h", "uicontroller.cpp", "uicontroller.h", ] diff --git a/src/plugins/modeleditor/modeleditor_constants.h b/src/plugins/modeleditor/modeleditor_constants.h index e600993fedf..c7731810613 100644 --- a/src/plugins/modeleditor/modeleditor_constants.h +++ b/src/plugins/modeleditor/modeleditor_constants.h @@ -3,18 +3,13 @@ #pragma once -#include <QtGlobal> - -namespace ModelEditor { -namespace Constants { +namespace ModelEditor::Constants { const char MODEL_EDITOR_ID[] = "Editors.ModelEditor"; -const char MODEL_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Model Editor"); const char REMOVE_SELECTED_ELEMENTS[] = "ModelEditor.RemoveSelectedElements"; const char DELETE_SELECTED_ELEMENTS[] = "ModelEditor.DeleteSelectedElements"; const char OPEN_PARENT_DIAGRAM[] = "ModelEditor.OpenParentDiagram"; -const char MENU_ID[] = "ModelEditor.Menu"; const char EXPORT_DIAGRAM[] = "ModelEditor.ExportDiagram"; const char EXPORT_SELECTED_ELEMENTS[] = "ModelEditor.ExportSelectedElements"; const char ACTION_ADD_PACKAGE[] = "ModelEditor.Action.AddPackage"; @@ -31,16 +26,6 @@ const char SHORTCUT_MODEL_EDITOR_EDIT_PROPERTIES[] = const char SHORTCUT_MODEL_EDITOR_EDIT_ITEM[] = "ModelEditor.ModelEditor.Shortcut.EditItem"; -const char WIZARD_CATEGORY[] = "O.Model"; -const char WIZARD_TR_CATEGORY[] = QT_TRANSLATE_NOOP("Modeling", "Modeling"); -const char WIZARD_MODEL_ID[] = "SA.Model"; - const char MIME_TYPE_MODEL[] = "text/vnd.qtcreator.model"; -// Settings entries -const char SETTINGS_GROUP[] = "ModelEditorPlugin"; -const char SETTINGS_RIGHT_SPLITTER[] = "RightSplitter"; -const char SETTINGS_RIGHT_HORIZ_SPLITTER[] = "RightHorizSplitter"; - -} // namespace Constants -} // namespace ModelEditor +} // ModelEditor::Constants diff --git a/src/plugins/modeleditor/modeleditor_plugin.cpp b/src/plugins/modeleditor/modeleditor_plugin.cpp index 4cdbf85a749..9e89a6ad452 100644 --- a/src/plugins/modeleditor/modeleditor_plugin.cpp +++ b/src/plugins/modeleditor/modeleditor_plugin.cpp @@ -4,11 +4,8 @@ #include "modeleditor_plugin.h" #include "jsextension.h" -#include "modeleditor_constants.h" #include "modeleditorfactory.h" -#include "modeleditor_global.h" #include "modelsmanager.h" -#include "settingscontroller.h" #include "uicontroller.h" #include "actionhandler.h" @@ -41,7 +38,6 @@ public: UiController uiController; ActionHandler actionHandler; ModelEditorFactory modelFactory{&uiController, &actionHandler}; - SettingsController settingsController; }; ModelEditorPlugin::ModelEditorPlugin() @@ -63,22 +59,17 @@ void ModelEditorPlugin::initialize() d = new ModelEditorPluginPrivate; Core::JsExpander::registerGlobalObject<JsExtension>("Modeling"); - - connect(&d->settingsController, &SettingsController::saveSettings, - &d->uiController, &UiController::saveSettings); - connect(&d->settingsController, &SettingsController::loadSettings, - &d->uiController, &UiController::loadSettings); } void ModelEditorPlugin::extensionsInitialized() { d->actionHandler.createActions(); - d->settingsController.load(Core::ICore::settings()); + d->uiController.loadSettings(); } ExtensionSystem::IPlugin::ShutdownFlag ModelEditorPlugin::aboutToShutdown() { - d->settingsController.save(Core::ICore::settings()); + d->uiController.saveSettings(); return SynchronousShutdown; } diff --git a/src/plugins/modeleditor/modeleditorfactory.cpp b/src/plugins/modeleditor/modeleditorfactory.cpp index 394ba071f0c..f6670c66074 100644 --- a/src/plugins/modeleditor/modeleditorfactory.cpp +++ b/src/plugins/modeleditor/modeleditorfactory.cpp @@ -17,7 +17,7 @@ namespace Internal { ModelEditorFactory::ModelEditorFactory(UiController *uiController, ActionHandler *actionHandler) { setId(Constants::MODEL_EDITOR_ID); - setDisplayName(::Core::Tr::tr(Constants::MODEL_EDITOR_DISPLAY_NAME)); + setDisplayName(::Core::Tr::tr("Model Editor")); addMimeType(Constants::MIME_TYPE_MODEL); setEditorCreator([uiController, actionHandler] { return new ModelEditor(uiController, actionHandler); }); } diff --git a/src/plugins/modeleditor/modelindexer.cpp b/src/plugins/modeleditor/modelindexer.cpp index d4471fe0ec6..163d0d60b53 100644 --- a/src/plugins/modeleditor/modelindexer.cpp +++ b/src/plugins/modeleditor/modelindexer.cpp @@ -21,6 +21,7 @@ #include <projectexplorer/projectmanager.h> #include <projectexplorer/projectnodes.h> +#include <utils/algorithm.h> #include <utils/mimeutils.h> #include <utils/qtcassert.h> @@ -288,8 +289,7 @@ void ModelIndexer::IndexerThread::onFilesQueued() // collect all diagrams of model DiagramsCollectorVisitor visitor(indexedModel); project.rootPackage()->accept(&visitor); - if (m_indexer->d->defaultModelFiles.contains(queuedFile)) { - m_indexer->d->defaultModelFiles.remove(queuedFile); + if (m_indexer->d->defaultModelFiles.remove(queuedFile)) { // check if model has a diagram which could be opened qmt::FindRootDiagramVisitor diagramVisitor; project.rootPackage()->accept(&diagramVisitor); @@ -426,10 +426,9 @@ void ModelIndexer::scanProject(ProjectExplorer::Project *project) // queue files while (!filesQueue.isEmpty()) { QueuedFile queuedFile = filesQueue.takeFirst(); - if (!d->queuedFilesSet.contains(queuedFile)) { + if (Utils::insert(d->queuedFilesSet, queuedFile)) { QMT_CHECK(!d->filesQueue.contains(queuedFile)); d->filesQueue.append(queuedFile); - d->queuedFilesSet.insert(queuedFile); filesAreQueued = true; } } @@ -474,11 +473,10 @@ void ModelIndexer::forgetProject(ProjectExplorer::Project *project) const QString fileString = file.toString(); // remove file from queue QueuedFile queuedFile(fileString, project); - if (d->queuedFilesSet.contains(queuedFile)) { + if (d->queuedFilesSet.remove(queuedFile)) { QMT_CHECK(d->filesQueue.contains(queuedFile)); d->filesQueue.removeOne(queuedFile); QMT_CHECK(!d->filesQueue.contains(queuedFile)); - d->queuedFilesSet.remove(queuedFile); } removeModelFile(fileString, project); removeDiagramReferenceFile(fileString, project); diff --git a/src/plugins/modeleditor/pxnodeutilities.cpp b/src/plugins/modeleditor/pxnodeutilities.cpp index f9e8e3b034c..c9e58d5052e 100644 --- a/src/plugins/modeleditor/pxnodeutilities.cpp +++ b/src/plugins/modeleditor/pxnodeutilities.cpp @@ -216,8 +216,7 @@ qmt::MObject *PxNodeUtilities::findSameObject(const QStringList &relativeElement bool PxNodeUtilities::isProxyHeader(const QString &file) const { - CppEditor::CppModelManager *cppModelManager = CppEditor::CppModelManager::instance(); - CPlusPlus::Snapshot snapshot = cppModelManager->snapshot(); + CPlusPlus::Snapshot snapshot = CppEditor::CppModelManager::snapshot(); CPlusPlus::Document::Ptr document = snapshot.document(Utils::FilePath::fromString(file)); if (document) { diff --git a/src/plugins/modeleditor/settingscontroller.cpp b/src/plugins/modeleditor/settingscontroller.cpp deleted file mode 100644 index d4f9424af9f..00000000000 --- a/src/plugins/modeleditor/settingscontroller.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2016 Jochen Becher -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "settingscontroller.h" - -#include "modeleditor_constants.h" - -#include <QSettings> - -namespace ModelEditor { -namespace Internal { - -SettingsController::SettingsController() -{ -} - -void SettingsController::reset() -{ - emit resetSettings(); -} - -void SettingsController::save(QSettings *settings) -{ - settings->beginGroup(QLatin1String(Constants::SETTINGS_GROUP)); - emit saveSettings(settings); - settings->endGroup(); - settings->sync(); -} - -void SettingsController::load(QSettings *settings) -{ - settings->beginGroup(QLatin1String(Constants::SETTINGS_GROUP)); - emit loadSettings(settings); - settings->endGroup(); -} - -} // namespace Internal -} // namespace ModelEditor diff --git a/src/plugins/modeleditor/settingscontroller.h b/src/plugins/modeleditor/settingscontroller.h deleted file mode 100644 index 6efc7063b37..00000000000 --- a/src/plugins/modeleditor/settingscontroller.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2016 Jochen Becher -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QObject> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - -namespace ModelEditor { -namespace Internal { - -class SettingsController : - public QObject -{ - Q_OBJECT - -public: - SettingsController(); - -signals: - void resetSettings(); - void saveSettings(QSettings *settings); - void loadSettings(QSettings *settings); - -public: - void reset(); - void save(QSettings *settings); - void load(QSettings *settings); -}; - -} // namespace Internal -} // namespace ModelEditor diff --git a/src/plugins/modeleditor/uicontroller.cpp b/src/plugins/modeleditor/uicontroller.cpp index 8584b09b0f0..138b3a028b1 100644 --- a/src/plugins/modeleditor/uicontroller.cpp +++ b/src/plugins/modeleditor/uicontroller.cpp @@ -3,85 +3,67 @@ #include "uicontroller.h" -#include "modeleditor_constants.h" +#include <coreplugin/icore.h> -#include <QSettings> +#include <utils/qtcsettings.h> + +using namespace Utils; namespace ModelEditor { namespace Internal { -class UiController::UiControllerPrivate -{ -public: - QByteArray rightSplitterState; - QByteArray rightHorizSplitterState; -}; - -UiController::UiController() - : d(new UiControllerPrivate) -{ -} - -UiController::~UiController() -{ - delete d; -} +// Settings entries +const char SETTINGS_RIGHT_SPLITTER[] = "ModelEditorPlugin/RightSplitter"; +const char SETTINGS_RIGHT_HORIZ_SPLITTER[] = "ModelEditorPlugin/RightHorizSplitter"; bool UiController::hasRightSplitterState() const { - return !d->rightSplitterState.isEmpty(); + return !m_rightSplitterState.isEmpty(); } QByteArray UiController::rightSplitterState() const { - return d->rightSplitterState; + return m_rightSplitterState; } bool UiController::hasRightHorizSplitterState() const { - return !d->rightHorizSplitterState.isEmpty(); + return !m_rightHorizSplitterState.isEmpty(); } QByteArray UiController::rightHorizSplitterState() const { - return d->rightHorizSplitterState; + return m_rightHorizSplitterState; } void UiController::onRightSplitterChanged(const QByteArray &state) { - d->rightSplitterState = state; + m_rightSplitterState = state; emit rightSplitterChanged(state); } void UiController::onRightHorizSplitterChanged(const QByteArray &state) { - d->rightHorizSplitterState = state; + m_rightHorizSplitterState = state; emit rightHorizSplitterChanged(state); } -void UiController::saveSettings(QSettings *settings) +void UiController::saveSettings() { - if (hasRightSplitterState()) { - settings->setValue( - QLatin1String(Constants::SETTINGS_RIGHT_SPLITTER), d->rightSplitterState); - } - if (hasRightHorizSplitterState()) { - settings->setValue( - QLatin1String(Constants::SETTINGS_RIGHT_HORIZ_SPLITTER), - d->rightHorizSplitterState); - } + QtcSettings *settings = Core::ICore::settings(); + if (hasRightSplitterState()) + settings->setValue(SETTINGS_RIGHT_SPLITTER, m_rightSplitterState); + if (hasRightHorizSplitterState()) + settings->setValue(SETTINGS_RIGHT_HORIZ_SPLITTER, m_rightHorizSplitterState); } -void UiController::loadSettings(QSettings *settings) +void UiController::loadSettings() { - if (settings->contains(QLatin1String(Constants::SETTINGS_RIGHT_SPLITTER))) { - d->rightSplitterState = settings->value( - QLatin1String(Constants::SETTINGS_RIGHT_SPLITTER)).toByteArray(); - } - if (settings->contains(QLatin1String(Constants::SETTINGS_RIGHT_HORIZ_SPLITTER))) { - d->rightHorizSplitterState = settings->value( - QLatin1String(Constants::SETTINGS_RIGHT_HORIZ_SPLITTER)).toByteArray(); - } + QtcSettings *settings = Core::ICore::settings(); + if (settings->contains(SETTINGS_RIGHT_SPLITTER)) + m_rightSplitterState = settings->value(SETTINGS_RIGHT_SPLITTER).toByteArray(); + if (settings->contains(SETTINGS_RIGHT_HORIZ_SPLITTER)) + m_rightHorizSplitterState = settings->value(SETTINGS_RIGHT_HORIZ_SPLITTER).toByteArray(); } } // namespace Internal diff --git a/src/plugins/modeleditor/uicontroller.h b/src/plugins/modeleditor/uicontroller.h index ce68f65322a..0709d26be63 100644 --- a/src/plugins/modeleditor/uicontroller.h +++ b/src/plugins/modeleditor/uicontroller.h @@ -5,22 +5,16 @@ #include <QObject> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace ModelEditor { namespace Internal { -class UiController : - public QObject +class UiController : public QObject { Q_OBJECT class UiControllerPrivate; public: - UiController(); - ~UiController(); + UiController() {} signals: void rightSplitterChanged(const QByteArray &state); @@ -34,11 +28,12 @@ public: void onRightSplitterChanged(const QByteArray &state); void onRightHorizSplitterChanged(const QByteArray &state); - void saveSettings(QSettings *settings); - void loadSettings(QSettings *settings); + void saveSettings(); + void loadSettings(); private: - UiControllerPrivate *d; + QByteArray m_rightSplitterState; + QByteArray m_rightHorizSplitterState; }; } // namespace Internal diff --git a/src/plugins/nim/CMakeLists.txt b/src/plugins/nim/CMakeLists.txt index 02cf6356f4f..85a3a204ed0 100644 --- a/src/plugins/nim/CMakeLists.txt +++ b/src/plugins/nim/CMakeLists.txt @@ -24,7 +24,6 @@ add_qtc_plugin(Nim project/nimproject.cpp project/nimproject.h project/nimrunconfiguration.cpp project/nimrunconfiguration.h project/nimtoolchain.cpp project/nimtoolchain.h - project/nimtoolchainfactory.cpp project/nimtoolchainfactory.h settings/nimcodestylepreferencesfactory.cpp settings/nimcodestylepreferencesfactory.h settings/nimcodestylepreferenceswidget.cpp settings/nimcodestylepreferenceswidget.h settings/nimcodestylesettingspage.cpp settings/nimcodestylesettingspage.h diff --git a/src/plugins/nim/Nim.json.in b/src/plugins/nim/Nim.json.in index ae37bfb085d..622e460a0b8 100644 --- a/src/plugins/nim/Nim.json.in +++ b/src/plugins/nim/Nim.json.in @@ -1,50 +1,50 @@ { - \"Name\" : \"Nim\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Filippo Cucchetto\", - \"Copyright\" : \"(C) 2017 Filippo Cucchetto, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Nim", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Filippo Cucchetto", + "Copyright" : "(C) 2017 Filippo Cucchetto, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Other Languages\", - \"Description\" : \"Plugin for supporting the Nim programming language.\", - \"Url\" : \"http://www.qt.io\", - \"Experimental\" : true, - $$dependencyList, + "Category" : "Other Languages", + "Description" : "Plugin for supporting the Nim programming language.", + "Url" : "http://www.qt.io", + "Experimental" : true, + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", - \" <mime-type type=\'text/x-nim-project\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Nim project file</comment>\", - \" <glob pattern=\'*.nimproject\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-nim-project'>", + " <sub-class-of type='text/plain'/>", + " <comment>Nim project file</comment>", + " <glob pattern='*.nimproject'/>", + " </mime-type>", - \" <mime-type type=\'text/x-nim\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Nim source file</comment>\", - \" <glob pattern=\'*.nim\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-nim'>", + " <sub-class-of type='text/plain'/>", + " <comment>Nim source file</comment>", + " <glob pattern='*.nim'/>", + " </mime-type>", - \" <mime-type type=\'text/x-nimble\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Nimble project file</comment>\", - \" <glob pattern=\'*.nimble\'/>\", - \" </mime-type>\", + " <mime-type type='text/x-nimble'>", + " <sub-class-of type='text/plain'/>", + " <comment>Nimble project file</comment>", + " <glob pattern='*.nimble'/>", + " </mime-type>", - \" <mime-type type=\'text/x-nim-script\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Nim script file</comment>\", - \" <glob pattern=\'*.nims\'/>\", - \" </mime-type>\", - \"</mime-info>\" + " <mime-type type='text/x-nim-script'>", + " <sub-class-of type='text/plain'/>", + " <comment>Nim script file</comment>", + " <glob pattern='*.nims'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/nim/editor/nimcompletionassistprovider.cpp b/src/plugins/nim/editor/nimcompletionassistprovider.cpp index f732c45d173..e290c8fb84d 100644 --- a/src/plugins/nim/editor/nimcompletionassistprovider.cpp +++ b/src/plugins/nim/editor/nimcompletionassistprovider.cpp @@ -40,8 +40,6 @@ bool isActivationChar(QChar c) class NimCompletionAssistProcessor : public QObject, public TextEditor::IAssistProcessor { - Q_OBJECT - public: TextEditor::IAssistProposal *perform() final { @@ -132,7 +130,7 @@ private: static Suggest::NimSuggest *nimSuggestInstance(const AssistInterface *interface) { - return Nim::Suggest::NimSuggestCache::instance().get(interface->filePath()); + return Suggest::getFromCache(interface->filePath()); } static std::shared_ptr<Suggest::NimSuggestClientRequest> sendRequest(const AssistInterface *interface, @@ -156,7 +154,7 @@ private: return result; } - static AssistProposalItemInterface *createProposal(const Nim::Suggest::Line &line) + static AssistProposalItemInterface *createProposal(const Suggest::Line &line) { auto item = new AssistProposalItem(); item->setIcon(Utils::CodeModelIcon::iconForType(symbolIcon(line.symbol_kind))); @@ -249,5 +247,3 @@ bool NimCompletionAssistProvider::isActivationCharSequence(const QString &sequen } } - -#include "nimcompletionassistprovider.moc" diff --git a/src/plugins/nim/editor/nimtexteditorwidget.cpp b/src/plugins/nim/editor/nimtexteditorwidget.cpp index b297607dced..f7ca080dace 100644 --- a/src/plugins/nim/editor/nimtexteditorwidget.cpp +++ b/src/plugins/nim/editor/nimtexteditorwidget.cpp @@ -39,7 +39,7 @@ void NimTextEditorWidget::findLinkAt(const QTextCursor &c, const Utils::LinkHand { const Utils::FilePath &path = textDocument()->filePath(); - NimSuggest *suggest = NimSuggestCache::instance().get(path); + NimSuggest *suggest = Nim::Suggest::getFromCache(path); if (!suggest) return processLinkCallback(Utils::Link()); diff --git a/src/plugins/nim/nim.qbs b/src/plugins/nim/nim.qbs index 34a20473b8f..d1ceb5007c8 100644 --- a/src/plugins/nim/nim.qbs +++ b/src/plugins/nim/nim.qbs @@ -46,7 +46,6 @@ QtcPlugin { "nimproject.h", "nimproject.cpp", "nimrunconfiguration.h", "nimrunconfiguration.cpp", "nimtoolchain.h", "nimtoolchain.cpp", - "nimtoolchainfactory.h", "nimtoolchainfactory.cpp", "nimblebuildstep.h", "nimblebuildstep.cpp", "nimbleproject.h", "nimbleproject.cpp", "nimblerunconfiguration.h", "nimblerunconfiguration.cpp", diff --git a/src/plugins/nim/nimconstants.h b/src/plugins/nim/nimconstants.h index 5c66be9f295..31dfd8c469e 100644 --- a/src/plugins/nim/nimconstants.h +++ b/src/plugins/nim/nimconstants.h @@ -30,21 +30,18 @@ const char C_NIMBLEBUILDSTEP_ARGUMENTS[] = "Nim.NimbleBuildStep.Arguments"; // NimbleTaskStep const char C_NIMBLETASKSTEP_ID[] = "Nim.NimbleTaskStep"; -const QString C_NIMBLETASKSTEP_TASKNAME = QStringLiteral("Nim.NimbleTaskStep.TaskName"); -const QString C_NIMBLETASKSTEP_TASKARGS = QStringLiteral("Nim.NimbleTaskStep.TaskArgs"); +const char C_NIMBLETASKSTEP_TASKNAME[] = "Nim.NimbleTaskStep.TaskName"; +const char C_NIMBLETASKSTEP_TASKARGS[] = "Nim.NimbleTaskStep.TaskArgs"; // NimCompilerBuildStep const char C_NIMCOMPILERBUILDSTEP_ID[] = "Nim.NimCompilerBuildStep"; -const QString C_NIMCOMPILERBUILDSTEP_USERCOMPILEROPTIONS = QStringLiteral("Nim.NimCompilerBuildStep.UserCompilerOptions"); -const QString C_NIMCOMPILERBUILDSTEP_DEFAULTBUILDOPTIONS = QStringLiteral("Nim.NimCompilerBuildStep.DefaultBuildOptions"); -const QString C_NIMCOMPILERBUILDSTEP_TARGETNIMFILE = QStringLiteral("Nim.NimCompilerBuildStep.TargetNimFile"); +const char C_NIMCOMPILERBUILDSTEP_USERCOMPILEROPTIONS[] = "Nim.NimCompilerBuildStep.UserCompilerOptions"; +const char C_NIMCOMPILERBUILDSTEP_DEFAULTBUILDOPTIONS[] = "Nim.NimCompilerBuildStep.DefaultBuildOptions"; +const char C_NIMCOMPILERBUILDSTEP_TARGETNIMFILE[] = "Nim.NimCompilerBuildStep.TargetNimFile"; // NimCompilerCleanStep const char C_NIMCOMPILERCLEANSTEP_ID[] = "Nim.NimCompilerCleanStep"; -// Nim task category (Issues pane) -const char C_NIMPARSE_ID[] = "Nim.NimParse"; - const char C_NIMLANGUAGE_ID[] = "Nim"; const char C_NIMCODESTYLESETTINGSPAGE_ID[] = "Nim.NimCodeStyleSettings"; const char C_NIMCODESTYLESETTINGSPAGE_CATEGORY[] = "Z.Nim"; diff --git a/src/plugins/nim/nimplugin.cpp b/src/plugins/nim/nimplugin.cpp index b3ef6fc49f0..1b435ae0ebd 100644 --- a/src/plugins/nim/nimplugin.cpp +++ b/src/plugins/nim/nimplugin.cpp @@ -14,13 +14,11 @@ #include "project/nimproject.h" #include "project/nimbleproject.h" #include "project/nimrunconfiguration.h" -#include "project/nimtoolchainfactory.h" +#include "project/nimtoolchain.h" #include "project/nimblebuildstep.h" #include "project/nimbletaskstep.h" #include "settings/nimcodestylepreferencesfactory.h" #include "settings/nimcodestylesettingspage.h" -#include "settings/nimsettings.h" -#include "suggest/nimsuggestcache.h" #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runcontrol.h> @@ -30,6 +28,8 @@ #include <texteditor/snippets/snippetprovider.h> #include <utils/fsengine/fileiconprovider.h> +#include <utils/icon.h> +#include <utils/theme/theme.h> using namespace Utils; using namespace ProjectExplorer; @@ -39,15 +39,6 @@ namespace Nim { class NimPluginPrivate { public: - NimPluginPrivate() - { - Suggest::NimSuggestCache::instance().setExecutablePath(settings.nimSuggestPath.value()); - QObject::connect(&settings.nimSuggestPath, &StringAspect::valueChanged, - &Suggest::NimSuggestCache::instance(), - &Suggest::NimSuggestCache::setExecutablePath); - } - - NimSettings settings; NimEditorFactory editorFactory; NimBuildConfigurationFactory buildConfigFactory; NimbleBuildConfigurationFactory nimbleBuildConfigFactory; @@ -96,7 +87,6 @@ void NimPlugin::extensionsInitialized() FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIM_SCRIPT_MIMETYPE); FileIconProvider::registerIconOverlayForMimeType(icon, Constants::C_NIMBLE_MIMETYPE); } - TaskHub::addCategory(Constants::C_NIMPARSE_ID, "Nim"); } } // namespace Nim diff --git a/src/plugins/nim/project/nimblebuildconfiguration.cpp b/src/plugins/nim/project/nimblebuildconfiguration.cpp index b0fc7cefb88..a0efe78fa7b 100644 --- a/src/plugins/nim/project/nimblebuildconfiguration.cpp +++ b/src/plugins/nim/project/nimblebuildconfiguration.cpp @@ -37,17 +37,16 @@ BuildConfiguration::BuildType NimbleBuildConfiguration::buildType() const return m_buildType; } -bool NimbleBuildConfiguration::fromMap(const QVariantMap &map) +void NimbleBuildConfiguration::fromMap(const Store &map) { m_buildType = static_cast<BuildType>(map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE].toInt()); - return BuildConfiguration::fromMap(map); + BuildConfiguration::fromMap(map); } -QVariantMap NimbleBuildConfiguration::toMap() const +void NimbleBuildConfiguration::toMap(Store &map) const { - auto map = BuildConfiguration::toMap(); + BuildConfiguration::toMap(map); map[Constants::C_NIMBLEBUILDCONFIGURATION_BUILDTYPE] = buildType(); - return map; } void NimbleBuildConfiguration::setBuildType(BuildConfiguration::BuildType buildType) diff --git a/src/plugins/nim/project/nimblebuildconfiguration.h b/src/plugins/nim/project/nimblebuildconfiguration.h index fa572bdc78d..4b1e9593097 100644 --- a/src/plugins/nim/project/nimblebuildconfiguration.h +++ b/src/plugins/nim/project/nimblebuildconfiguration.h @@ -18,9 +18,8 @@ class NimbleBuildConfiguration : public ProjectExplorer::BuildConfiguration BuildType buildType() const override; - bool fromMap(const QVariantMap &map) override; - - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; private: void setBuildType(BuildType buildType); diff --git a/src/plugins/nim/project/nimblebuildstep.cpp b/src/plugins/nim/project/nimblebuildstep.cpp index d432db5154e..0177ce2d1b8 100644 --- a/src/plugins/nim/project/nimblebuildstep.cpp +++ b/src/plugins/nim/project/nimblebuildstep.cpp @@ -18,46 +18,51 @@ using namespace Utils; namespace Nim { -class NimbleBuildStep : public AbstractProcessStep +class NimbleBuildStep final : public AbstractProcessStep { public: - NimbleBuildStep(BuildStepList *parentList, Id id); + NimbleBuildStep(BuildStepList *parentList, Id id) + : AbstractProcessStep(parentList, id) + { + arguments.setMacroExpander(macroExpander()); + arguments.setSettingsKey(Constants::C_NIMBLEBUILDSTEP_ARGUMENTS); + arguments.setResetter([this] { return defaultArguments(); }); + arguments.setArguments(defaultArguments()); + + setCommandLineProvider([this] { + return CommandLine(Nim::nimblePathFromKit(kit()), {"build", arguments.arguments()}); + }); + setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); + setEnvironmentModifier([this](Environment &env) { + env.appendOrSetPath(Nim::nimPathFromKit(kit())); + }); + + setSummaryUpdater([this] { + ProcessParameters param; + setupProcessParameters(¶m); + return param.summary(displayName()); + }); + + QTC_ASSERT(buildConfiguration(), return); + QObject::connect(buildConfiguration(), &BuildConfiguration::buildTypeChanged, + &arguments, &ArgumentsAspect::resetArguments); + QObject::connect(&arguments, &BaseAspect::changed, + this, &AbstractProcessStep::updateSummary); + } void setupOutputFormatter(OutputFormatter *formatter) final; private: - QString defaultArguments() const; + QString defaultArguments() const + { + if (buildType() == BuildConfiguration::Debug) + return {"--debugger:native"}; + return {}; + } + + ArgumentsAspect arguments{this}; }; -NimbleBuildStep::NimbleBuildStep(BuildStepList *parentList, Id id) - : AbstractProcessStep(parentList, id) -{ - auto arguments = addAspect<ArgumentsAspect>(macroExpander()); - arguments->setSettingsKey(Constants::C_NIMBLEBUILDSTEP_ARGUMENTS); - arguments->setResetter([this] { return defaultArguments(); }); - arguments->setArguments(defaultArguments()); - - setCommandLineProvider([this, arguments] { - return CommandLine(Nim::nimblePathFromKit(kit()), {"build", arguments->arguments()}); - }); - setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); - setEnvironmentModifier([this](Environment &env) { - env.appendOrSetPath(Nim::nimPathFromKit(kit())); - }); - - setSummaryUpdater([this] { - ProcessParameters param; - setupProcessParameters(¶m); - return param.summary(displayName()); - }); - - QTC_ASSERT(buildConfiguration(), return); - QObject::connect(buildConfiguration(), &BuildConfiguration::buildTypeChanged, - arguments, &ArgumentsAspect::resetArguments); - QObject::connect(arguments, &ArgumentsAspect::changed, - this, &AbstractProcessStep::updateSummary); -} - void NimbleBuildStep::setupOutputFormatter(OutputFormatter *formatter) { const auto parser = new NimParser(); @@ -66,12 +71,6 @@ void NimbleBuildStep::setupOutputFormatter(OutputFormatter *formatter) AbstractProcessStep::setupOutputFormatter(formatter); } -QString NimbleBuildStep::defaultArguments() const -{ - if (buildType() == BuildConfiguration::Debug) - return {"--debugger:native"}; - return {}; -} NimbleBuildStepFactory::NimbleBuildStepFactory() { diff --git a/src/plugins/nim/project/nimblebuildsystem.cpp b/src/plugins/nim/project/nimblebuildsystem.cpp index 3a8ccb08d62..baed02b5254 100644 --- a/src/plugins/nim/project/nimblebuildsystem.cpp +++ b/src/plugins/nim/project/nimblebuildsystem.cpp @@ -39,7 +39,7 @@ static std::vector<NimbleTask> parseTasks(const FilePath &nimblePath, const File std::vector<NimbleTask> result; if (process.exitCode() != 0) { - TaskHub::addTask(Task(Task::Error, process.cleanedStdOut(), {}, -1, Constants::C_NIMPARSE_ID)); + TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); return result; } @@ -67,7 +67,7 @@ static NimbleMetadata parseMetadata(const FilePath &nimblePath, const FilePath & NimbleMetadata result = {}; if (process.exitCode() != 0) { - TaskHub::addTask(Task(Task::Error, process.cleanedStdOut(), {}, -1, Constants::C_NIMPARSE_ID)); + TaskHub::addTask(ProjectExplorer::BuildSystemTask(Task::Error, process.cleanedStdOut())); return result; } const QList<QByteArray> &lines = linesFromProcessOutput(&process); @@ -141,7 +141,6 @@ void NimbleBuildSystem::triggerParsing() void NimbleBuildSystem::updateProject() { - TaskHub::clearTasks(Constants::C_NIMPARSE_ID); const FilePath projectDir = projectDirectory(); const FilePath nimble = Nim::nimblePathFromKit(kit()); diff --git a/src/plugins/nim/project/nimbleproject.cpp b/src/plugins/nim/project/nimbleproject.cpp index 5777724afbd..0a354832826 100644 --- a/src/plugins/nim/project/nimbleproject.cpp +++ b/src/plugins/nim/project/nimbleproject.cpp @@ -13,6 +13,7 @@ #include <utils/qtcassert.h> using namespace ProjectExplorer; +using namespace Utils; namespace Nim { @@ -26,14 +27,13 @@ NimbleProject::NimbleProject(const Utils::FilePath &fileName) setBuildSystemCreator([] (Target *t) { return new NimbleBuildSystem(t); }); } -QVariantMap NimbleProject::toMap() const +void NimbleProject::toMap(Store &map) const { - QVariantMap result = Project::toMap(); - result[Constants::C_NIMPROJECT_EXCLUDEDFILES] = m_excludedFiles; - return result; + Project::toMap(map); + map[Constants::C_NIMPROJECT_EXCLUDEDFILES] = m_excludedFiles; } -Project::RestoreResult NimbleProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult NimbleProject::fromMap(const Store &map, QString *errorMessage) { auto result = Project::fromMap(map, errorMessage); m_excludedFiles = map.value(Constants::C_NIMPROJECT_EXCLUDEDFILES).toStringList(); diff --git a/src/plugins/nim/project/nimbleproject.h b/src/plugins/nim/project/nimbleproject.h index 9abbc90dcc4..7664152c3f4 100644 --- a/src/plugins/nim/project/nimbleproject.h +++ b/src/plugins/nim/project/nimbleproject.h @@ -16,14 +16,14 @@ public: NimbleProject(const Utils::FilePath &filename); // Keep for compatibility with Qt Creator 4.10 - QVariantMap toMap() const final; + void toMap(Utils::Store &map) const final; QStringList excludedFiles() const; void setExcludedFiles(const QStringList &excludedFiles); protected: // Keep for compatibility with Qt Creator 4.10 - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final; + RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) final; QStringList m_excludedFiles; }; diff --git a/src/plugins/nim/project/nimblerunconfiguration.cpp b/src/plugins/nim/project/nimblerunconfiguration.cpp index 8fa24ee33e4..e23942f8c4f 100644 --- a/src/plugins/nim/project/nimblerunconfiguration.cpp +++ b/src/plugins/nim/project/nimblerunconfiguration.cpp @@ -11,10 +11,8 @@ #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> -#include <utils/algorithm.h> -#include <utils/environment.h> - using namespace ProjectExplorer; +using namespace Utils; namespace Nim { @@ -23,28 +21,34 @@ namespace Nim { class NimbleRunConfiguration : public RunConfiguration { public: - NimbleRunConfiguration(Target *target, Utils::Id id) + NimbleRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); + environment.setSupportForBuildEnvironment(target); - addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); setUpdater([this] { BuildTargetInfo bti = buildTargetInfo(); setDisplayName(bti.displayName); setDefaultDisplayName(bti.displayName); - aspect<ExecutableAspect>()->setExecutable(bti.targetFilePath); - aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.workingDirectory); + executable.setExecutable(bti.targetFilePath); + workingDir.setDefaultWorkingDirectory(bti.workingDirectory); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); update(); } + + EnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; }; NimbleRunConfigurationFactory::NimbleRunConfigurationFactory() @@ -61,19 +65,26 @@ NimbleRunConfigurationFactory::NimbleRunConfigurationFactory() class NimbleTestConfiguration : public RunConfiguration { public: - NimbleTestConfiguration(ProjectExplorer::Target *target, Utils::Id id) + NimbleTestConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - addAspect<ExecutableAspect>(target, ExecutableAspect::BuildDevice) - ->setExecutable(Nim::nimblePathFromKit(target->kit())); - addAspect<ArgumentsAspect>(macroExpander())->setArguments("test"); - addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr) - ->setDefaultWorkingDirectory(project()->projectDirectory()); - addAspect<TerminalAspect>(); - setDisplayName(Tr::tr("Nimble Test")); setDefaultDisplayName(Tr::tr("Nimble Test")); + + executable.setDeviceSelector(target, ExecutableAspect::BuildDevice); + executable.setExecutable(Nim::nimblePathFromKit(kit())); + + arguments.setMacroExpander(macroExpander()); + arguments.setArguments("test"); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setDefaultWorkingDirectory(project()->projectDirectory()); } + + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; }; NimbleTestConfigurationFactory::NimbleTestConfigurationFactory() diff --git a/src/plugins/nim/project/nimbuildsystem.cpp b/src/plugins/nim/project/nimbuildsystem.cpp index 00d68034eb2..f07d8f607f3 100644 --- a/src/plugins/nim/project/nimbuildsystem.cpp +++ b/src/plugins/nim/project/nimbuildsystem.cpp @@ -6,9 +6,9 @@ #include "nimconstants.h" #include "nimbleproject.h" +#include <projectexplorer/kitaspects.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> -#include <projectexplorer/kitinformation.h> #include <utils/algorithm.h> #include <utils/fileutils.h> diff --git a/src/plugins/nim/project/nimcompilerbuildstep.cpp b/src/plugins/nim/project/nimcompilerbuildstep.cpp index 76802a85df8..eecd7d442c0 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.cpp +++ b/src/plugins/nim/project/nimcompilerbuildstep.cpp @@ -10,7 +10,7 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/ioutputparser.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> @@ -121,22 +121,20 @@ QWidget *NimCompilerBuildStep::createConfigWidget() return widget; } -bool NimCompilerBuildStep::fromMap(const QVariantMap &map) +void NimCompilerBuildStep::fromMap(const Store &map) { AbstractProcessStep::fromMap(map); m_userCompilerOptions = map[Constants::C_NIMCOMPILERBUILDSTEP_USERCOMPILEROPTIONS].toString().split('|'); m_defaultOptions = static_cast<DefaultBuildOptions>(map[Constants::C_NIMCOMPILERBUILDSTEP_DEFAULTBUILDOPTIONS].toInt()); m_targetNimFile = FilePath::fromString(map[Constants::C_NIMCOMPILERBUILDSTEP_TARGETNIMFILE].toString()); - return true; } -QVariantMap NimCompilerBuildStep::toMap() const +void NimCompilerBuildStep::toMap(Store &map) const { - QVariantMap result = AbstractProcessStep::toMap(); - result[Constants::C_NIMCOMPILERBUILDSTEP_USERCOMPILEROPTIONS] = m_userCompilerOptions.join('|'); - result[Constants::C_NIMCOMPILERBUILDSTEP_DEFAULTBUILDOPTIONS] = m_defaultOptions; - result[Constants::C_NIMCOMPILERBUILDSTEP_TARGETNIMFILE] = m_targetNimFile.toString(); - return result; + AbstractProcessStep::toMap(map); + map[Constants::C_NIMCOMPILERBUILDSTEP_USERCOMPILEROPTIONS] = m_userCompilerOptions.join('|'); + map[Constants::C_NIMCOMPILERBUILDSTEP_DEFAULTBUILDOPTIONS] = m_defaultOptions; + map[Constants::C_NIMCOMPILERBUILDSTEP_TARGETNIMFILE] = m_targetNimFile.toString(); } void NimCompilerBuildStep::setBuildType(BuildConfiguration::BuildType buildType) diff --git a/src/plugins/nim/project/nimcompilerbuildstep.h b/src/plugins/nim/project/nimcompilerbuildstep.h index 90fa7bdb718..7de03659f18 100644 --- a/src/plugins/nim/project/nimcompilerbuildstep.h +++ b/src/plugins/nim/project/nimcompilerbuildstep.h @@ -26,8 +26,8 @@ private: void setupOutputFormatter(Utils::OutputFormatter *formatter) override; QWidget *createConfigWidget() override; - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; void updateTargetNimFile(); Utils::CommandLine commandLine(); diff --git a/src/plugins/nim/project/nimcompilercleanstep.cpp b/src/plugins/nim/project/nimcompilercleanstep.cpp index 8e13c068131..edfe1b370cb 100644 --- a/src/plugins/nim/project/nimcompilercleanstep.cpp +++ b/src/plugins/nim/project/nimcompilercleanstep.cpp @@ -9,6 +9,8 @@ #include <projectexplorer/projectexplorerconstants.h> +#include <solutions/tasking/tasktree.h> + #include <utils/aspects.h> #include <utils/qtcassert.h> @@ -16,6 +18,7 @@ #include <QDateTime> using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; namespace Nim { @@ -23,63 +26,58 @@ namespace Nim { class NimCompilerCleanStep final : public BuildStep { public: - NimCompilerCleanStep(BuildStepList *parentList, Id id); + NimCompilerCleanStep(BuildStepList *parentList, Id id) + : BuildStep(parentList, id) + { + workingDir.setLabelText(Tr::tr("Working directory:")); + + setSummaryUpdater([this] { + workingDir.setValue(buildDirectory()); + return displayName(); + }); + } private: bool init() final; - void doRun() final; - void doCancel() final {} // Can be left empty. The run() function hardly does anything. + GroupItem runRecipe() final; bool removeCacheDirectory(); bool removeOutFilePath(); FilePath m_buildDir; + FilePathAspect workingDir{this}; }; -NimCompilerCleanStep::NimCompilerCleanStep(BuildStepList *parentList, Id id) - : BuildStep(parentList, id) -{ - auto workingDirectory = addAspect<StringAspect>(); - workingDirectory->setLabelText(Tr::tr("Working directory:")); - workingDirectory->setDisplayStyle(StringAspect::LineEditDisplay); - - setSummaryUpdater([this, workingDirectory] { - workingDirectory->setFilePath(buildDirectory()); - return displayName(); - }); -} - bool NimCompilerCleanStep::init() { - FilePath buildDir = buildDirectory(); - bool result = buildDir.exists(); - if (result) + const FilePath buildDir = buildDirectory(); + const bool exists = buildDir.exists(); + if (exists) m_buildDir = buildDir; - return result; + return exists; } -void NimCompilerCleanStep::doRun() +GroupItem NimCompilerCleanStep::runRecipe() { - if (!m_buildDir.exists()) { - emit addOutput(Tr::tr("Build directory \"%1\" does not exist.").arg(m_buildDir.toUserOutput()), OutputFormat::ErrorMessage); - emit finished(false); - return; - } - - if (!removeCacheDirectory()) { - emit addOutput(Tr::tr("Failed to delete the cache directory."), OutputFormat::ErrorMessage); - emit finished(false); - return; - } - - if (!removeOutFilePath()) { - emit addOutput(Tr::tr("Failed to delete the out file."), OutputFormat::ErrorMessage); - emit finished(false); - return; - } - - emit addOutput(Tr::tr("Clean step completed successfully."), OutputFormat::NormalMessage); - emit finished(true); + const auto onSetup = [this] { + if (!m_buildDir.exists()) { + emit addOutput(Tr::tr("Build directory \"%1\" does not exist.") + .arg(m_buildDir.toUserOutput()), OutputFormat::ErrorMessage); + return false; + } + if (!removeCacheDirectory()) { + emit addOutput(Tr::tr("Failed to delete the cache directory."), + OutputFormat::ErrorMessage); + return false; + } + if (!removeOutFilePath()) { + emit addOutput(Tr::tr("Failed to delete the out file."), OutputFormat::ErrorMessage); + return false; + } + emit addOutput(Tr::tr("Clean step completed successfully."), OutputFormat::NormalMessage); + return true; + }; + return Sync(onSetup); } bool NimCompilerCleanStep::removeCacheDirectory() diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index 4a2bc060260..215820818b2 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -6,13 +6,13 @@ #include "../nimconstants.h" #include "../nimtr.h" #include "nimbuildsystem.h" -#include "nimtoolchain.h" #include <coreplugin/icontext.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/toolchain.h> using namespace ProjectExplorer; using namespace Utils; @@ -27,14 +27,14 @@ public: Tasks projectIssues(const Kit *k) const final; // Keep for compatibility with Qt Creator 4.10 - QVariantMap toMap() const final; + void toMap(Store &map) const final; QStringList excludedFiles() const; void setExcludedFiles(const QStringList &excludedFiles); protected: // Keep for compatibility with Qt Creator 4.10 - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final; + RestoreResult fromMap(const Store &map, QString *errorMessage) final; QStringList m_excludedFiles; }; @@ -52,7 +52,7 @@ NimProject::NimProject(const FilePath &filePath) : Project(Constants::C_NIM_MIME Tasks NimProject::projectIssues(const Kit *k) const { Tasks result = Project::projectIssues(k); - auto tc = dynamic_cast<NimToolChain *>(ToolChainKitAspect::toolChain(k, Constants::C_NIMLANGUAGE_ID)); + auto tc = ToolChainKitAspect::toolChain(k, Constants::C_NIMLANGUAGE_ID); if (!tc) { result.append(createProjectTask(Task::TaskType::Error, Tr::tr("No Nim compiler set."))); return result; @@ -63,14 +63,13 @@ Tasks NimProject::projectIssues(const Kit *k) const return result; } -QVariantMap NimProject::toMap() const +void NimProject::toMap(Store &map) const { - QVariantMap result = Project::toMap(); - result[Constants::C_NIMPROJECT_EXCLUDEDFILES] = m_excludedFiles; - return result; + Project::toMap(map); + map[Constants::C_NIMPROJECT_EXCLUDEDFILES] = m_excludedFiles; } -Project::RestoreResult NimProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult NimProject::fromMap(const Store &map, QString *errorMessage) { auto result = Project::fromMap(map, errorMessage); m_excludedFiles = map.value(Constants::C_NIMPROJECT_EXCLUDEDFILES).toStringList(); diff --git a/src/plugins/nim/project/nimrunconfiguration.cpp b/src/plugins/nim/project/nimrunconfiguration.cpp index 79eeeb898dd..30ed5d64d3b 100644 --- a/src/plugins/nim/project/nimrunconfiguration.cpp +++ b/src/plugins/nim/project/nimrunconfiguration.cpp @@ -26,13 +26,13 @@ public: NimRunConfiguration(Target *target, Utils::Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); + environment.setSupportForBuildEnvironment(target); - addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); setDisplayName(Tr::tr("Current Build Target")); setDefaultDisplayName(Tr::tr("Current Build Target")); @@ -41,15 +41,21 @@ public: auto buildConfiguration = qobject_cast<NimBuildConfiguration *>(target->activeBuildConfiguration()); QTC_ASSERT(buildConfiguration, return); const QFileInfo outFileInfo = buildConfiguration->outFilePath().toFileInfo(); - aspect<ExecutableAspect>()->setExecutable(FilePath::fromString(outFileInfo.absoluteFilePath())); + executable.setExecutable(FilePath::fromString(outFileInfo.absoluteFilePath())); const QString workingDirectory = outFileInfo.absoluteDir().absolutePath(); - aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(FilePath::fromString(workingDirectory)); + workingDir.setDefaultWorkingDirectory(FilePath::fromString(workingDirectory)); }); // Connect target signals connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); update(); } + + EnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; }; // NimRunConfigurationFactory diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 21b441fa6ce..9d264922e98 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -4,13 +4,22 @@ #include "nimtoolchain.h" #include "nimconstants.h" -#include "nimtoolchainfactory.h" +#include "nimtoolchain.h" #include "nimtr.h" #include <projectexplorer/abi.h> -#include <utils/environment.h> -#include <utils/process.h> +#include <projectexplorer/devicesupport/devicemanager.h> +#include <projectexplorer/toolchainconfigwidget.h> +#include <utils/algorithm.h> +#include <utils/environment.h> +#include <utils/environment.h> +#include <utils/fileutils.h> +#include <utils/pathchooser.h> +#include <utils/process.h> +#include <utils/qtcassert.h> + +#include <QFormLayout> #include <QRegularExpression> using namespace ProjectExplorer; @@ -70,11 +79,6 @@ QList<Utils::OutputLineParser *> NimToolChain::createOutputParsers() const return {}; } -std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> NimToolChain::createConfigurationWidget() -{ - return std::make_unique<NimToolChainConfigWidget>(this); -} - QString NimToolChain::compilerVersion() const { return compilerCommand().isEmpty() || m_version == std::make_tuple(-1,-1,-1) @@ -85,12 +89,12 @@ QString NimToolChain::compilerVersion() const std::get<2>(m_version)); } -bool NimToolChain::fromMap(const QVariantMap &data) +void NimToolChain::fromMap(const Store &data) { - if (!ToolChain::fromMap(data)) - return false; + ToolChain::fromMap(data); + if (hasError()) + return; parseVersion(compilerCommand(), m_version); - return true; } bool NimToolChain::parseVersion(const FilePath &path, std::tuple<int, int, int> &result) @@ -114,4 +118,133 @@ bool NimToolChain::parseVersion(const FilePath &path, std::tuple<int, int, int> return true; } +// NimToolChainConfigWidget + +class NimToolChainConfigWidget : public ToolChainConfigWidget +{ +public: + explicit NimToolChainConfigWidget(NimToolChain *tc) + : ToolChainConfigWidget(tc) + , m_compilerCommand(new PathChooser) + , m_compilerVersion(new QLineEdit) + { + // Create ui + const auto gnuVersionArgs = QStringList("--version"); + m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); + m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); + m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); + m_compilerVersion->setReadOnly(true); + m_mainLayout->addRow(Tr::tr("&Compiler version:"), m_compilerVersion); + + // Fill + fillUI(); + + // Connect + connect(m_compilerCommand, &PathChooser::validChanged, this, [this] { + const FilePath path = m_compilerCommand->rawFilePath(); + auto tc = static_cast<NimToolChain *>(toolChain()); + QTC_ASSERT(tc, return); + tc->setCompilerCommand(path); + fillUI(); + }); + } + +protected: + void applyImpl() final; + void discardImpl() final; + bool isDirtyImpl() const final; + void makeReadOnlyImpl() final; + +private: + void fillUI(); + + Utils::PathChooser *m_compilerCommand; + QLineEdit *m_compilerVersion; +}; + +void NimToolChainConfigWidget::applyImpl() +{ + auto tc = static_cast<NimToolChain *>(toolChain()); + Q_ASSERT(tc); + if (tc->isAutoDetected()) + return; + tc->setCompilerCommand(m_compilerCommand->filePath()); +} + +void NimToolChainConfigWidget::discardImpl() +{ + fillUI(); +} + +bool NimToolChainConfigWidget::isDirtyImpl() const +{ + auto tc = static_cast<NimToolChain *>(toolChain()); + Q_ASSERT(tc); + return tc->compilerCommand() != m_compilerCommand->filePath(); +} + +void NimToolChainConfigWidget::makeReadOnlyImpl() +{ + m_compilerCommand->setReadOnly(true); +} + +void NimToolChainConfigWidget::fillUI() +{ + auto tc = static_cast<NimToolChain *>(toolChain()); + Q_ASSERT(tc); + m_compilerCommand->setFilePath(tc->compilerCommand()); + m_compilerVersion->setText(tc->compilerVersion()); +} + +std::unique_ptr<ToolChainConfigWidget> NimToolChain::createConfigurationWidget() +{ + return std::make_unique<NimToolChainConfigWidget>(this); +} + +// NimToolChainFactory + +NimToolChainFactory::NimToolChainFactory() +{ + setDisplayName(Tr::tr("Nim")); + setSupportedToolChainType(Constants::C_NIMTOOLCHAIN_TYPEID); + setSupportedLanguages({Constants::C_NIMLANGUAGE_ID}); + setToolchainConstructor([] { return new NimToolChain; }); + setUserCreatable(true); +} + +Toolchains NimToolChainFactory::autoDetect(const ToolchainDetector &detector) const +{ + Toolchains result; + + const FilePath compilerPath = detector.device->searchExecutableInPath("nim"); + if (compilerPath.isEmpty()) + return result; + + result = Utils::filtered(detector.alreadyKnown, [compilerPath](ToolChain *tc) { + return tc->typeId() == Constants::C_NIMTOOLCHAIN_TYPEID + && tc->compilerCommand() == compilerPath; + }); + + if (!result.empty()) + return result; + + auto tc = new NimToolChain; + tc->setDetection(ToolChain::AutoDetection); + tc->setCompilerCommand(compilerPath); + result.append(tc); + return result; +} + +Toolchains NimToolChainFactory::detectForImport(const ToolChainDescription &tcd) const +{ + Toolchains result; + if (tcd.language == Constants::C_NIMLANGUAGE_ID) { + auto tc = new NimToolChain; + tc->setDetection(ToolChain::ManualDetection); // FIXME: sure? + tc->setCompilerCommand(tcd.compilerPath); + result.append(tc); + } + return result; +} + } // Nim diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index a2f816a7f97..f71edf44bbe 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -26,7 +26,7 @@ public: QList<Utils::OutputLineParser *> createOutputParsers() const final; std::unique_ptr<ProjectExplorer::ToolChainConfigWidget> createConfigurationWidget() final; - bool fromMap(const QVariantMap &data) final; + void fromMap(const Utils::Store &data) final; static bool parseVersion(const Utils::FilePath &path, std::tuple<int, int, int> &version); @@ -34,4 +34,13 @@ private: std::tuple<int, int, int> m_version; }; +class NimToolChainFactory : public ProjectExplorer::ToolChainFactory +{ +public: + NimToolChainFactory(); + + ProjectExplorer::Toolchains autoDetect(const ProjectExplorer::ToolchainDetector &detector) const final; + ProjectExplorer::Toolchains detectForImport(const ProjectExplorer::ToolChainDescription &tcd) const final; +}; + } // Nim diff --git a/src/plugins/nim/project/nimtoolchainfactory.cpp b/src/plugins/nim/project/nimtoolchainfactory.cpp deleted file mode 100644 index 415e7e76962..00000000000 --- a/src/plugins/nim/project/nimtoolchainfactory.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "nimtoolchainfactory.h" - -#include "nimconstants.h" -#include "nimtoolchain.h" -#include "nimtr.h" - -#include <projectexplorer/devicesupport/devicemanager.h> - -#include <utils/algorithm.h> -#include <utils/environment.h> -#include <utils/fileutils.h> -#include <utils/pathchooser.h> -#include <utils/qtcassert.h> - -#include <QFormLayout> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Nim { - -NimToolChainFactory::NimToolChainFactory() -{ - setDisplayName(Tr::tr("Nim")); - setSupportedToolChainType(Constants::C_NIMTOOLCHAIN_TYPEID); - setSupportedLanguages({Constants::C_NIMLANGUAGE_ID}); - setToolchainConstructor([] { return new NimToolChain; }); - setUserCreatable(true); -} - -Toolchains NimToolChainFactory::autoDetect(const ToolchainDetector &detector) const -{ - Toolchains result; - - const FilePath compilerPath = detector.device->searchExecutableInPath("nim"); - if (compilerPath.isEmpty()) - return result; - - result = Utils::filtered(detector.alreadyKnown, [compilerPath](ToolChain *tc) { - return tc->typeId() == Constants::C_NIMTOOLCHAIN_TYPEID - && tc->compilerCommand() == compilerPath; - }); - - if (!result.empty()) - return result; - - auto tc = new NimToolChain; - tc->setDetection(ToolChain::AutoDetection); - tc->setCompilerCommand(compilerPath); - result.append(tc); - return result; -} - -Toolchains NimToolChainFactory::detectForImport(const ToolChainDescription &tcd) const -{ - Toolchains result; - if (tcd.language == Constants::C_NIMLANGUAGE_ID) { - auto tc = new NimToolChain; - tc->setDetection(ToolChain::ManualDetection); // FIXME: sure? - tc->setCompilerCommand(tcd.compilerPath); - result.append(tc); - } - return result; -} - -NimToolChainConfigWidget::NimToolChainConfigWidget(NimToolChain *tc) - : ToolChainConfigWidget(tc) - , m_compilerCommand(new PathChooser) - , m_compilerVersion(new QLineEdit) -{ - // Create ui - const auto gnuVersionArgs = QStringList("--version"); - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); - m_compilerVersion->setReadOnly(true); - m_mainLayout->addRow(Tr::tr("&Compiler version:"), m_compilerVersion); - - // Fill - fillUI(); - - // Connect - connect(m_compilerCommand, &PathChooser::validChanged, this, [this] { - const FilePath path = m_compilerCommand->rawFilePath(); - auto tc = static_cast<NimToolChain *>(toolChain()); - QTC_ASSERT(tc, return); - tc->setCompilerCommand(path); - fillUI(); - }); -} - -void NimToolChainConfigWidget::applyImpl() -{ - auto tc = static_cast<NimToolChain *>(toolChain()); - Q_ASSERT(tc); - if (tc->isAutoDetected()) - return; - tc->setCompilerCommand(m_compilerCommand->filePath()); -} - -void NimToolChainConfigWidget::discardImpl() -{ - fillUI(); -} - -bool NimToolChainConfigWidget::isDirtyImpl() const -{ - auto tc = static_cast<NimToolChain *>(toolChain()); - Q_ASSERT(tc); - return tc->compilerCommand() != m_compilerCommand->filePath(); -} - -void NimToolChainConfigWidget::makeReadOnlyImpl() -{ - m_compilerCommand->setReadOnly(true); -} - -void NimToolChainConfigWidget::fillUI() -{ - auto tc = static_cast<NimToolChain *>(toolChain()); - Q_ASSERT(tc); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_compilerVersion->setText(tc->compilerVersion()); -} - -} diff --git a/src/plugins/nim/project/nimtoolchainfactory.h b/src/plugins/nim/project/nimtoolchainfactory.h deleted file mode 100644 index 96fc55b8e56..00000000000 --- a/src/plugins/nim/project/nimtoolchainfactory.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) Filippo Cucchetto <filippocucchetto@gmail.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/toolchain.h> -#include <projectexplorer/toolchainconfigwidget.h> - -namespace Utils { class PathChooser; } - -namespace Nim { - -class NimToolChain; - -class NimToolChainFactory : public ProjectExplorer::ToolChainFactory -{ -public: - NimToolChainFactory(); - - ProjectExplorer::Toolchains autoDetect(const ProjectExplorer::ToolchainDetector &detector) const final; - ProjectExplorer::Toolchains detectForImport(const ProjectExplorer::ToolChainDescription &tcd) const final; -}; - -class NimToolChainConfigWidget : public ProjectExplorer::ToolChainConfigWidget -{ - Q_OBJECT - -public: - explicit NimToolChainConfigWidget(NimToolChain *tc); - -protected: - void applyImpl() final; - void discardImpl() final; - bool isDirtyImpl() const final; - void makeReadOnlyImpl() final; - -private: - void fillUI(); - - Utils::PathChooser *m_compilerCommand; - QLineEdit *m_compilerVersion; -}; - -} // Nim diff --git a/src/plugins/nim/settings/nimcodestylesettingspage.cpp b/src/plugins/nim/settings/nimcodestylesettingspage.cpp index 56919a6c156..270097508f8 100644 --- a/src/plugins/nim/settings/nimcodestylesettingspage.cpp +++ b/src/plugins/nim/settings/nimcodestylesettingspage.cpp @@ -3,14 +3,19 @@ #include "nimcodestylesettingspage.h" +#include "nimcodestylepreferencesfactory.h" +#include "nimsettings.h" #include "../nimconstants.h" #include "../nimtr.h" -#include "nimsettings.h" -#include <texteditor/simplecodestylepreferences.h> +#include <coreplugin/icore.h> + #include <texteditor/codestyleeditor.h> -#include <texteditor/texteditorsettings.h> +#include <texteditor/codestylepool.h> +#include <texteditor/icodestylepreferencesfactory.h> +#include <texteditor/simplecodestylepreferences.h> #include <texteditor/tabsettings.h> +#include <texteditor/texteditorsettings.h> #include <QVBoxLayout> @@ -18,12 +23,77 @@ using namespace TextEditor; namespace Nim { +static SimpleCodeStylePreferences *m_globalCodeStyle = nullptr; +static CodeStylePool *pool = nullptr; + +SimpleCodeStylePreferences *globalCodeStyle() +{ + QTC_CHECK(m_globalCodeStyle); + return m_globalCodeStyle; +} + +static void createGlobalCodeStyle() +{ + auto factory = new NimCodeStylePreferencesFactory(); + TextEditorSettings::registerCodeStyleFactory(factory); + + // code style pool + pool = new CodeStylePool(factory); + TextEditorSettings::registerCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID, pool); + + m_globalCodeStyle = new SimpleCodeStylePreferences(); + m_globalCodeStyle->setDelegatingPool(pool); + m_globalCodeStyle->setDisplayName(Tr::tr("Global", "Settings")); + m_globalCodeStyle->setId(Nim::Constants::C_NIMGLOBALCODESTYLE_ID); + pool->addCodeStyle(m_globalCodeStyle); + TextEditorSettings::registerCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID, m_globalCodeStyle); + + auto nimCodeStyle = new SimpleCodeStylePreferences(); + nimCodeStyle->setId("nim"); + nimCodeStyle->setDisplayName(Tr::tr("Nim")); + nimCodeStyle->setReadOnly(true); + + TabSettings nimTabSettings; + nimTabSettings.m_tabPolicy = TabSettings::SpacesOnlyTabPolicy; + nimTabSettings.m_tabSize = 2; + nimTabSettings.m_indentSize = 2; + nimTabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithIndent; + nimCodeStyle->setTabSettings(nimTabSettings); + + pool->addCodeStyle(nimCodeStyle); + + m_globalCodeStyle->setCurrentDelegate(nimCodeStyle); + + pool->loadCustomCodeStyles(); + + // load global settings (after built-in settings are added to the pool) + m_globalCodeStyle->fromSettings(Nim::Constants::C_NIMLANGUAGE_ID); + + TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_MIMETYPE, + Nim::Constants::C_NIMLANGUAGE_ID); + TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE, + Nim::Constants::C_NIMLANGUAGE_ID); +} + +static void destroyGlobalCodeStyle() +{ + TextEditorSettings::unregisterCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID); + TextEditorSettings::unregisterCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID); + TextEditorSettings::unregisterCodeStyleFactory(Nim::Constants::C_NIMLANGUAGE_ID); + + delete m_globalCodeStyle; + m_globalCodeStyle = nullptr; + + delete pool; + pool = nullptr; +} + class NimCodeStyleSettingsWidget : public Core::IOptionsPageWidget { public: NimCodeStyleSettingsWidget() { - auto originalTabPreferences = NimSettings::globalCodeStyle(); + auto originalTabPreferences = globalCodeStyle(); m_nimCodeStylePreferences = new SimpleCodeStylePreferences(this); m_nimCodeStylePreferences->setDelegatingPool(originalTabPreferences->delegatingPool()); m_nimCodeStylePreferences->setTabSettings(originalTabPreferences->tabSettings()); @@ -38,10 +108,18 @@ public: layout->addWidget(editor); } + void apply() final + { + QTC_ASSERT(m_globalCodeStyle, return); + m_globalCodeStyle->toSettings(Nim::Constants::C_NIMLANGUAGE_ID); + } + private: TextEditor::SimpleCodeStylePreferences *m_nimCodeStylePreferences; }; +// NimCodeStyleSettingsPage + NimCodeStyleSettingsPage::NimCodeStyleSettingsPage() { setId(Nim::Constants::C_NIMCODESTYLESETTINGSPAGE_ID); @@ -50,6 +128,13 @@ NimCodeStyleSettingsPage::NimCodeStyleSettingsPage() setDisplayCategory(Tr::tr("Nim")); setCategoryIconPath(":/nim/images/settingscategory_nim.png"); setWidgetCreator([] { return new NimCodeStyleSettingsWidget; }); + + createGlobalCodeStyle(); +} + +NimCodeStyleSettingsPage::~NimCodeStyleSettingsPage() +{ + destroyGlobalCodeStyle(); } } // Nim diff --git a/src/plugins/nim/settings/nimcodestylesettingspage.h b/src/plugins/nim/settings/nimcodestylesettingspage.h index 173b1eeb7db..f0acc402e98 100644 --- a/src/plugins/nim/settings/nimcodestylesettingspage.h +++ b/src/plugins/nim/settings/nimcodestylesettingspage.h @@ -11,6 +11,7 @@ class NimCodeStyleSettingsPage final : public Core::IOptionsPage { public: NimCodeStyleSettingsPage(); + ~NimCodeStyleSettingsPage(); }; } // Nim diff --git a/src/plugins/nim/settings/nimsettings.cpp b/src/plugins/nim/settings/nimsettings.cpp index fac77109fde..9df9acd73bf 100644 --- a/src/plugins/nim/settings/nimsettings.cpp +++ b/src/plugins/nim/settings/nimsettings.cpp @@ -5,33 +5,26 @@ #include "../nimconstants.h" #include "../nimtr.h" -#include "nimcodestylepreferencesfactory.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> -#include <texteditor/codestylepool.h> -#include <texteditor/icodestylepreferencesfactory.h> -#include <texteditor/simplecodestylepreferences.h> -#include <texteditor/tabsettings.h> -#include <texteditor/texteditorsettings.h> - #include <utils/layoutbuilder.h> -using namespace TextEditor; using namespace Utils; namespace Nim { -static SimpleCodeStylePreferences *m_globalCodeStyle = nullptr; +NimSettings &settings() +{ + static NimSettings theSettings; + return theSettings; +} NimSettings::NimSettings() { setSettingsGroups("Nim", "NimSuggest"); - setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID); - setDisplayName(Tr::tr("Tools")); - setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY); - setDisplayCategory(Tr::tr("Nim")); - setCategoryIconPath(":/nim/images/settingscategory_nim.png"); + setAutoApply(false); setLayouter([this] { using namespace Layouting; @@ -44,48 +37,6 @@ NimSettings::NimSettings() }; }); - // code style factory - auto factory = new NimCodeStylePreferencesFactory(); - TextEditorSettings::registerCodeStyleFactory(factory); - - // code style pool - auto pool = new CodeStylePool(factory, this); - TextEditorSettings::registerCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID, pool); - - m_globalCodeStyle = new SimpleCodeStylePreferences(); - m_globalCodeStyle->setDelegatingPool(pool); - m_globalCodeStyle->setDisplayName(Tr::tr("Global", "Settings")); - m_globalCodeStyle->setId(Nim::Constants::C_NIMGLOBALCODESTYLE_ID); - pool->addCodeStyle(m_globalCodeStyle); - TextEditorSettings::registerCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID, m_globalCodeStyle); - - auto nimCodeStyle = new SimpleCodeStylePreferences(); - nimCodeStyle->setId("nim"); - nimCodeStyle->setDisplayName(Tr::tr("Nim")); - nimCodeStyle->setReadOnly(true); - - TabSettings nimTabSettings; - nimTabSettings.m_tabPolicy = TabSettings::SpacesOnlyTabPolicy; - nimTabSettings.m_tabSize = 2; - nimTabSettings.m_indentSize = 2; - nimTabSettings.m_continuationAlignBehavior = TabSettings::ContinuationAlignWithIndent; - nimCodeStyle->setTabSettings(nimTabSettings); - - pool->addCodeStyle(nimCodeStyle); - - m_globalCodeStyle->setCurrentDelegate(nimCodeStyle); - - pool->loadCustomCodeStyles(); - - // load global settings (after built-in settings are added to the pool) - QSettings *s = Core::ICore::settings(); - m_globalCodeStyle->fromSettings(QLatin1String(Nim::Constants::C_NIMLANGUAGE_ID), s); - - TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_MIMETYPE, - Nim::Constants::C_NIMLANGUAGE_ID); - TextEditorSettings::registerMimeTypeForLanguageId(Nim::Constants::C_NIM_SCRIPT_MIMETYPE, - Nim::Constants::C_NIMLANGUAGE_ID); - nimSuggestPath.setSettingsKey("Command"); nimSuggestPath.setExpectedKind(PathChooser::ExistingCommand); nimSuggestPath.setLabelText(Tr::tr("Path:")); @@ -93,19 +44,22 @@ NimSettings::NimSettings() readSettings(); } -NimSettings::~NimSettings() -{ - TextEditorSettings::unregisterCodeStyle(Nim::Constants::C_NIMLANGUAGE_ID); - TextEditorSettings::unregisterCodeStylePool(Nim::Constants::C_NIMLANGUAGE_ID); - TextEditorSettings::unregisterCodeStyleFactory(Nim::Constants::C_NIMLANGUAGE_ID); +// NimSettingsPage - delete m_globalCodeStyle; - m_globalCodeStyle = nullptr; -} - -SimpleCodeStylePreferences *NimSettings::globalCodeStyle() +class NimSettingsPage final : public Core::IOptionsPage { - return m_globalCodeStyle; -} +public: + NimSettingsPage() + { + setId(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_ID); + setDisplayName(Tr::tr("Tools")); + setCategory(Nim::Constants::C_NIMTOOLSSETTINGSPAGE_CATEGORY); + setDisplayCategory(Tr::tr("Nim")); + setCategoryIconPath(":/nim/images/settingscategory_nim.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const NimSettingsPage settingsPage; } // namespace Nim diff --git a/src/plugins/nim/settings/nimsettings.h b/src/plugins/nim/settings/nimsettings.h index a7c6628b65b..4a105bf628c 100644 --- a/src/plugins/nim/settings/nimsettings.h +++ b/src/plugins/nim/settings/nimsettings.h @@ -3,22 +3,19 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> - -namespace TextEditor { class SimpleCodeStylePreferences; } +#include <utils/aspects.h> namespace Nim { -class NimSettings : public Core::PagedSettings +class NimSettings final : public Utils::AspectContainer { public: NimSettings(); - ~NimSettings(); Utils::FilePathAspect nimSuggestPath{this}; - - static TextEditor::SimpleCodeStylePreferences *globalCodeStyle(); }; +NimSettings &settings(); + } // Nim diff --git a/src/plugins/nim/suggest/nimsuggest.cpp b/src/plugins/nim/suggest/nimsuggest.cpp index 1e59233a2d0..260598713ca 100644 --- a/src/plugins/nim/suggest/nimsuggest.cpp +++ b/src/plugins/nim/suggest/nimsuggest.cpp @@ -3,8 +3,9 @@ #include "nimsuggest.h" -namespace Nim { -namespace Suggest { +using namespace Utils; + +namespace Nim::Suggest { NimSuggest::NimSuggest(QObject *parent) : QObject(parent) @@ -16,12 +17,12 @@ NimSuggest::NimSuggest(QObject *parent) connect(&m_client, &NimSuggestClient::connected, this, &NimSuggest::onClientConnected); } -QString NimSuggest::projectFile() const +FilePath NimSuggest::projectFile() const { return m_projectFile; } -void NimSuggest::setProjectFile(const QString &file) +void NimSuggest::setProjectFile(const Utils::FilePath &file) { if (m_projectFile == file) return; @@ -32,12 +33,12 @@ void NimSuggest::setProjectFile(const QString &file) restart(); } -QString NimSuggest::executablePath() const +FilePath NimSuggest::executablePath() const { return m_executablePath; } -void NimSuggest::setExecutablePath(const QString &path) +void NimSuggest::setExecutablePath(const FilePath &path) { if (m_executablePath == path) return; @@ -145,5 +146,4 @@ void NimSuggest::onClientDisconnected() connectClient(); } -} // namespace Suggest -} // namespace Nim +} // Nim::Suggest diff --git a/src/plugins/nim/suggest/nimsuggest.h b/src/plugins/nim/suggest/nimsuggest.h index 403b70abf69..14ea812336d 100644 --- a/src/plugins/nim/suggest/nimsuggest.h +++ b/src/plugins/nim/suggest/nimsuggest.h @@ -6,8 +6,10 @@ #include "client.h" #include "server.h" -namespace Nim { -namespace Suggest { +#include <utils/filepath.h> + + +namespace Nim::Suggest { class NimSuggest : public QObject { @@ -16,11 +18,11 @@ class NimSuggest : public QObject public: NimSuggest(QObject *parent = nullptr); - QString projectFile() const; - void setProjectFile(const QString &file); + Utils::FilePath projectFile() const; + void setProjectFile(const Utils::FilePath &file); - QString executablePath() const; - void setExecutablePath(const QString &path); + Utils::FilePath executablePath() const; + void setExecutablePath(const Utils::FilePath &path); bool isReady() const; @@ -32,8 +34,8 @@ public: signals: void readyChanged(bool ready); - void projectFileChanged(const QString &projectFile); - void executablePathChanged(const QString &executablePath); + void projectFileChanged(const Utils::FilePath &projectFile); + void executablePathChanged(const Utils::FilePath &executablePath); private: void restart(); @@ -61,11 +63,10 @@ private: bool m_ready = false; bool m_clientReady = false; bool m_serverReady = false; - QString m_projectFile; - QString m_executablePath; + Utils::FilePath m_projectFile; + Utils::FilePath m_executablePath; NimSuggestServer m_server; NimSuggestClient m_client; }; -} // namespace Suggest -} // namespace Nim +} // Nim::Suggest diff --git a/src/plugins/nim/suggest/nimsuggestcache.cpp b/src/plugins/nim/suggest/nimsuggestcache.cpp index 127c55904b3..fa4751cb5c1 100644 --- a/src/plugins/nim/suggest/nimsuggestcache.cpp +++ b/src/plugins/nim/suggest/nimsuggestcache.cpp @@ -5,72 +5,84 @@ #include "nimconstants.h" #include "nimsuggest.h" +#include "settings/nimsettings.h" #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> -namespace Nim { -namespace Suggest { +#include <unordered_map> -NimSuggestCache &NimSuggestCache::instance() +using namespace Utils; + +namespace Nim::Suggest { + +class NimSuggestCache final : public QObject { - static NimSuggestCache instance; - return instance; -} +public: + NimSuggestCache() + { + setExecutablePath(settings().nimSuggestPath()); + QObject::connect(&settings().nimSuggestPath, &StringAspect::changed, this, [this] { + setExecutablePath(settings().nimSuggestPath()); + }); -NimSuggestCache::~NimSuggestCache() = default; - -NimSuggest *NimSuggestCache::get(const Utils::FilePath &filename) -{ - auto it = m_nimSuggestInstances.find(filename); - if (it == m_nimSuggestInstances.end()) { - auto instance = std::make_unique<Suggest::NimSuggest>(this); - instance->setProjectFile(filename.toString()); - instance->setExecutablePath(m_executablePath); - it = m_nimSuggestInstances.emplace(filename, std::move(instance)).first; + Core::EditorManager *editorManager = Core::EditorManager::instance(); + connect(editorManager, &Core::EditorManager::editorOpened, + this, &NimSuggestCache::onEditorOpened); + connect(editorManager, &Core::EditorManager::editorAboutToClose, + this, &NimSuggestCache::onEditorClosed); } - return it->second.get(); -} -NimSuggestCache::NimSuggestCache() -{ - Core::EditorManager *editorManager = Core::EditorManager::instance(); - connect(editorManager, &Core::EditorManager::editorOpened, - this, &NimSuggestCache::onEditorOpened); - connect(editorManager, &Core::EditorManager::editorAboutToClose, - this, &NimSuggestCache::onEditorClosed); -} + void setExecutablePath(const FilePath &path) + { + if (m_executablePath == path) + return; -QString NimSuggestCache::executablePath() const -{ - return m_executablePath; -} + m_executablePath = path; -void NimSuggestCache::setExecutablePath(const QString &path) -{ - if (m_executablePath == path) - return; - - m_executablePath = path; - - for (const auto &pair : m_nimSuggestInstances) { - pair.second->setExecutablePath(path); + for (const auto &pair : m_nimSuggestInstances) + pair.second->setExecutablePath(path); } -} -void Nim::Suggest::NimSuggestCache::onEditorOpened(Core::IEditor *editor) -{ - if (editor->document()->mimeType() == Constants::C_NIM_MIMETYPE) { - get(editor->document()->filePath()); + void onEditorOpened(Core::IEditor *editor) + { + if (editor->document()->mimeType() == Constants::C_NIM_MIMETYPE) + getFromCache(editor->document()->filePath()); } -} -void Nim::Suggest::NimSuggestCache::onEditorClosed(Core::IEditor *editor) + void onEditorClosed(Core::IEditor *editor) + { + auto it = m_nimSuggestInstances.find(editor->document()->filePath()); + if (it != m_nimSuggestInstances.end()) + m_nimSuggestInstances.erase(it); + } + + NimSuggest *get(const FilePath &filePath) + { + auto it = m_nimSuggestInstances.find(filePath); + if (it == m_nimSuggestInstances.end()) { + auto instance = std::make_unique<NimSuggest>(this); + instance->setProjectFile(filePath); + instance->setExecutablePath(m_executablePath); + it = m_nimSuggestInstances.emplace(filePath, std::move(instance)).first; + } + return it->second.get(); + } + + std::unordered_map<FilePath, std::unique_ptr<Suggest::NimSuggest>> m_nimSuggestInstances; + + FilePath m_executablePath; +}; + +static NimSuggestCache &cache() { - auto it = m_nimSuggestInstances.find(editor->document()->filePath()); - if (it != m_nimSuggestInstances.end()) - m_nimSuggestInstances.erase(it); + static NimSuggestCache theCache; + return theCache; } +NimSuggest *getFromCache(const FilePath &filePath) +{ + return cache().get(filePath); } -} + +} // Nim::Suggest diff --git a/src/plugins/nim/suggest/nimsuggestcache.h b/src/plugins/nim/suggest/nimsuggestcache.h index df06c5b33fd..764828cf0a7 100644 --- a/src/plugins/nim/suggest/nimsuggestcache.h +++ b/src/plugins/nim/suggest/nimsuggestcache.h @@ -3,42 +3,12 @@ #pragma once -#include <utils/fileutils.h> +namespace Utils { class FilePath; } -#include <QObject> - -#include <unordered_map> - -namespace Core { class IEditor; } - -namespace Nim { -namespace Suggest { +namespace Nim::Suggest { class NimSuggest; -class NimSuggestCache : public QObject -{ - Q_OBJECT +NimSuggest *getFromCache(const Utils::FilePath &filePath); -public: - static NimSuggestCache &instance(); - - NimSuggest *get(const Utils::FilePath &filename); - - QString executablePath() const; - void setExecutablePath(const QString &path); - -private: - NimSuggestCache(); - ~NimSuggestCache(); - - void onEditorOpened(Core::IEditor *editor); - void onEditorClosed(Core::IEditor *editor); - - std::unordered_map<Utils::FilePath, std::unique_ptr<Suggest::NimSuggest>> m_nimSuggestInstances; - - QString m_executablePath; -}; - -} // namespace Suggest -} // namespace Nim +} // Nim::Suggest diff --git a/src/plugins/nim/suggest/server.cpp b/src/plugins/nim/suggest/server.cpp index 7573505f727..d3f8d086ceb 100644 --- a/src/plugins/nim/suggest/server.cpp +++ b/src/plugins/nim/suggest/server.cpp @@ -5,8 +5,7 @@ using namespace Utils; -namespace Nim { -namespace Suggest { +namespace Nim::Suggest { NimSuggestServer::NimSuggestServer(QObject *parent) : QObject(parent) { @@ -15,20 +14,14 @@ NimSuggestServer::NimSuggestServer(QObject *parent) : QObject(parent) &NimSuggestServer::onStandardOutputAvailable); } -QString NimSuggestServer::executablePath() const +bool NimSuggestServer::start(const FilePath &executablePath, const FilePath &projectFilePath) { - return m_executablePath; -} - -bool NimSuggestServer::start(const QString &executablePath, - const QString &projectFilePath) -{ - if (!QFile::exists(executablePath)) { + if (!executablePath.exists()) { qWarning() << "NimSuggest executable path" << executablePath << "does not exist"; return false; } - if (!QFile::exists(projectFilePath)) { + if (!projectFilePath.exists()) { qWarning() << "Project file" << projectFilePath << "doesn't exist"; return false; } @@ -36,7 +29,7 @@ bool NimSuggestServer::start(const QString &executablePath, stop(); m_executablePath = executablePath; m_projectFilePath = projectFilePath; - m_process.setCommand({FilePath::fromString(executablePath), {"--epc", m_projectFilePath}}); + m_process.setCommand({executablePath, {"--epc", m_projectFilePath.path()}}); m_process.start(); return true; } @@ -52,11 +45,6 @@ quint16 NimSuggestServer::port() const return m_port; } -QString NimSuggestServer::projectFilePath() const -{ - return m_projectFilePath; -} - void NimSuggestServer::onStandardOutputAvailable() { if (!m_portAvailable) { @@ -81,5 +69,4 @@ void NimSuggestServer::clearState() m_port = 0; } -} // namespace Suggest -} // namespace Nim +} // namespace Nim::Suggest diff --git a/src/plugins/nim/suggest/server.h b/src/plugins/nim/suggest/server.h index 9eb2bac141d..73e28096e29 100644 --- a/src/plugins/nim/suggest/server.h +++ b/src/plugins/nim/suggest/server.h @@ -3,14 +3,9 @@ #pragma once -#include <QDebug> -#include <QFile> -#include <QObject> - #include <utils/process.h> -namespace Nim { -namespace Suggest { +namespace Nim::Suggest { class NimSuggestServer : public QObject { @@ -19,12 +14,10 @@ class NimSuggestServer : public QObject public: NimSuggestServer(QObject *parent = nullptr); - bool start(const QString &executablePath, const QString &projectFilePath); + bool start(const Utils::FilePath &executablePath, const Utils::FilePath &projectFilePath); void stop(); quint16 port() const; - QString executablePath() const; - QString projectFilePath() const; signals: void started(); @@ -38,9 +31,8 @@ private: bool m_portAvailable = false; Utils::Process m_process; quint16 m_port = 0; - QString m_projectFilePath; - QString m_executablePath; + Utils::FilePath m_projectFilePath; + Utils::FilePath m_executablePath; }; -} // namespace Suggest -} // namespace Nim +} // Nim::Suggest diff --git a/src/plugins/perforce/Perforce.json.in b/src/plugins/perforce/Perforce.json.in index fc8e4b3679c..8a8e1e5099a 100644 --- a/src/plugins/perforce/Perforce.json.in +++ b/src/plugins/perforce/Perforce.json.in @@ -1,33 +1,33 @@ { - \"Name\" : \"Perforce\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Perforce", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Perforce integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Version Control", + "Description" : "Perforce integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/vnd.qtcreator.p4.submit\'>\", - \" <comment>Perforce submit template</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <magic priority=\'50\'>\", - \" <match value=\'# A Perforce Change Specification.\' type=\'string\' offset=\'0\'/>\", - \" </magic>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/vnd.qtcreator.p4.submit'>", + " <comment>Perforce submit template</comment>", + " <sub-class-of type='text/plain'/>", + " <magic priority='50'>", + " <match value='# A Perforce Change Specification.' type='string' offset='0'/>", + " </magic>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/perforce/perforceeditor.cpp b/src/plugins/perforce/perforceeditor.cpp index 2483f0dc522..623ac1ed8fe 100644 --- a/src/plugins/perforce/perforceeditor.cpp +++ b/src/plugins/perforce/perforceeditor.cpp @@ -46,7 +46,7 @@ QString PerforceEditorWidget::changeUnderCursor(const QTextCursor &c) const // Any number is regarded as change number. cursor.select(QTextCursor::WordUnderCursor); if (!cursor.hasSelection()) - return QString(); + return {}; const QString change = cursor.selectedText(); return m_changeNumberPattern.match(change).hasMatch() ? change : QString(); } @@ -70,7 +70,7 @@ QStringList PerforceEditorWidget::annotationPreviousVersions(const QString &v) c bool ok; const int changeList = v.toInt(&ok); if (!ok || changeList < 2) - return QStringList(); + return {}; return QStringList(QString::number(changeList - 1)); } diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index ae61d12d342..a94a12adb30 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -49,7 +49,6 @@ #include <QMenu> #include <QMessageBox> #include <QRegularExpression> -#include <QSettings> #include <QTextCodec> using namespace Core; @@ -312,8 +311,6 @@ public: bool revertProject(const FilePath &workingDir, const QStringList &args, bool unchangedOnly); bool managesDirectoryFstat(const FilePath &directory); - void applySettings(); - CommandLocator *m_commandLocator = nullptr; ParameterAction *m_editAction = nullptr; ParameterAction *m_addAction = nullptr; @@ -340,9 +337,6 @@ public: mutable QString m_tempFilePattern; QAction *m_menuAction = nullptr; - PerforceSettings m_settings; - PerforceSettingsPage m_settingsPage{&m_settings}; - ManagedDirectoryCache m_managedDirectoryCache; VcsSubmitEditorFactory submitEditorFactory { @@ -379,8 +373,6 @@ PerforcePluginPrivate::PerforcePluginPrivate() dd = this; - m_settings.readSettings(ICore::settings()); - const QString prefix = QLatin1String("p4"); m_commandLocator = new CommandLocator("Perforce", prefix, prefix, this); m_commandLocator->setDescription(Tr::tr("Triggers a Perforce version control operation.")); @@ -556,9 +548,12 @@ PerforcePluginPrivate::PerforcePluginPrivate() connect(m_filelogAction, &QAction::triggered, this, &PerforcePluginPrivate::filelogFile); perforceContainer->addAction(command); - QObject::connect(&m_settings, &AspectContainer::applied, this, [this] { - m_settings.clearTopLevel(); - applySettings(); + QObject::connect(&settings(), &AspectContainer::applied, this, [this] { + settings().clearTopLevel(); + settings().writeSettings(); + m_managedDirectoryCache.clear(); + getTopLevel(); + emit configurationChanged(); }); } @@ -633,7 +628,7 @@ void PerforcePluginPrivate::diffCurrentProject() void PerforcePluginPrivate::diffAllOpened() { - p4Diff(m_settings.topLevel(), QStringList()); + p4Diff(settings().topLevel(), QStringList()); } void PerforcePluginPrivate::updateCurrentProject() @@ -645,7 +640,7 @@ void PerforcePluginPrivate::updateCurrentProject() void PerforcePluginPrivate::updateAll() { - updateCheckout(m_settings.topLevel()); + updateCheckout(settings().topLevel()); } void PerforcePluginPrivate::revertCurrentProject() @@ -695,7 +690,7 @@ void PerforcePluginPrivate::updateCheckout(const FilePath &workingDir, const QSt void PerforcePluginPrivate::printOpenedFileList() { - const PerforceResponse perforceResponse = runP4Cmd(m_settings.topLevel(), {"opened"}, + const PerforceResponse perforceResponse = runP4Cmd(settings().topLevel(), {"opened"}, CommandToWindow|StdErrToWindow|ErrorToWindow); if (perforceResponse.error || perforceResponse.stdOut.isEmpty()) return; @@ -791,7 +786,7 @@ IEditor *PerforcePluginPrivate::openPerforceSubmitEditor(const QString &fileName submitEditor->restrictToProjectFiles(depotFileNames); connect(submitEditor, &VcsBaseSubmitEditor::diffSelectedFiles, this, &PerforcePluginPrivate::slotSubmitDiff); - submitEditor->setCheckScriptWorkingDirectory(m_settings.topLevel()); + submitEditor->setCheckScriptWorkingDirectory(settings().topLevel()); return editor; } @@ -804,7 +799,7 @@ void PerforcePluginPrivate::printPendingChanges() const int i = dia.changeNumber(); QStringList args(QLatin1String("submit")); args << QLatin1String("-c") << QString::number(i); - runP4Cmd(m_settings.topLevel(), args, + runP4Cmd(settings().topLevel(), args, CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow); } } @@ -893,8 +888,8 @@ void PerforcePluginPrivate::filelog(const FilePath &workingDir, const QString &f QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName)); QStringList args; args << QLatin1String("filelog") << QLatin1String("-li"); - if (m_settings.logCount() > 0) - args << "-m" << QString::number(m_settings.logCount()); + if (settings().logCount() > 0) + args << "-m" << QString::number(settings().logCount()); if (!fileName.isEmpty()) args.append(fileName); const PerforceResponse result = runP4Cmd(workingDir, args, @@ -915,8 +910,8 @@ void PerforcePluginPrivate::changelists(const FilePath &workingDir, const QStrin QTextCodec *codec = VcsBaseEditor::getCodec(workingDir, QStringList(fileName)); QStringList args; args << QLatin1String("changelists") << QLatin1String("-lit"); - if (m_settings.logCount() > 0) - args << "-m" << QString::number(m_settings.logCount()); + if (settings().logCount() > 0) + args << "-m" << QString::number(settings().logCount()); if (!fileName.isEmpty()) args.append(fileName); const PerforceResponse result = runP4Cmd(workingDir, args, @@ -961,7 +956,7 @@ bool PerforcePluginPrivate::managesDirectory(const FilePath &directory, FilePath const bool rc = const_cast<PerforcePluginPrivate *>(this)->managesDirectoryFstat(directory); if (topLevel) { if (rc) - *topLevel = m_settings.topLevelSymLinkTarget(); + *topLevel = settings().topLevelSymLinkTarget(); else topLevel->clear(); } @@ -985,20 +980,20 @@ bool PerforcePluginPrivate::managesDirectoryFstat(const FilePath &directory) setTopLevel(entry.m_topLevel); return entry.m_isManaged; } - if (!m_settings.isValid()) { - if (m_settings.topLevel().isEmpty()) + if (!settings().isValid()) { + if (settings().topLevel().isEmpty()) getTopLevel(directory, true); - if (!m_settings.isValid()) + if (!settings().isValid()) return false; } // Determine value and insert into cache bool managed = false; do { // Quick check: Must be at or below top level and not "../../other_path" - const QString relativeDirArgs = m_settings.relativeToTopLevelArguments(directory.toString()); + const QString relativeDirArgs = settings().relativeToTopLevelArguments(directory.toString()); if (!relativeDirArgs.isEmpty() && relativeDirArgs.startsWith(QLatin1String(".."))) { - if (!m_settings.defaultEnv()) + if (!settings().defaultEnv()) break; else getTopLevel(directory, true); @@ -1006,14 +1001,14 @@ bool PerforcePluginPrivate::managesDirectoryFstat(const FilePath &directory) // Is it actually managed by perforce? QStringList args; args << QLatin1String("fstat") << QLatin1String("-m1") << perforceRelativeFileArguments(relativeDirArgs); - const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args, + const PerforceResponse result = runP4Cmd(settings().topLevel(), args, RunFullySynchronous); managed = result.stdOut.contains(QLatin1String("depotFile")) || result.stdErr.contains(QLatin1String("... - no such file(s)")); } while (false); - m_managedDirectoryCache.insert(directory, DirectoryCacheEntry(managed, m_settings.topLevel())); + m_managedDirectoryCache.insert(directory, DirectoryCacheEntry(managed, settings().topLevel())); return managed; } @@ -1117,11 +1112,8 @@ bool PerforcePluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) const bool PerforcePluginPrivate::isConfigured() const { - const QString binary = m_settings.p4BinaryPath.value(); - if (binary.isEmpty()) - return false; - QFileInfo fi(binary); - return fi.exists() && fi.isFile() && fi.isExecutable(); + const FilePath binary = settings().p4BinaryPath(); + return !binary.isEmpty() && binary.isExecutableFile(); } bool PerforcePluginPrivate::supportsOperation(Operation operation) const @@ -1155,7 +1147,7 @@ bool PerforcePluginPrivate::vcsOpen(const FilePath &filePath) IVersionControl::SettingsFlags PerforcePluginPrivate::settingsFlags() const { SettingsFlags rc; - if (m_settings.autoOpen.value()) + if (settings().autoOpen()) rc |= AutoOpen; return rc; } @@ -1199,9 +1191,10 @@ QString PerforcePluginPrivate::vcsMakeWritableText() const // Run messages -static QString msgNotStarted(const QString &cmd) +static QString msgNotStarted(const FilePath &cmd) { - return Tr::tr("Could not start perforce \"%1\". Please check your settings in the preferences.").arg(cmd); + return Tr::tr("Could not start perforce \"%1\". Please check your settings in the preferences.") + .arg(cmd.toUserOutput()); } static QString msgTimeout(int timeOutS) @@ -1230,7 +1223,7 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki // Run, connect stderr to the output window Process process; - const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS(); + const int timeOutS = (flags & LongTimeOut) ? settings().longTimeOutS() : settings().timeOutS(); process.setTimeoutS(timeOutS); if (outputCodec) process.setCodec(outputCodec); @@ -1251,7 +1244,7 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki process.setStdOutCallback([](const QString &lines) { VcsOutputWindow::append(lines); }); } process.setTimeOutMessageBoxEnabled(true); - process.setCommand({m_settings.p4BinaryPath.filePath(), args}); + process.setCommand({settings().p4BinaryPath(), args}); process.runBlocking(EventLoopMode::On); PerforceResponse response; @@ -1271,7 +1264,7 @@ PerforceResponse PerforcePluginPrivate::synchronousProcess(const FilePath &worki response.message = msgCrash(); break; case ProcessResult::StartFailed: - response.message = msgNotStarted(m_settings.p4BinaryPath.value()); + response.message = msgNotStarted(settings().p4BinaryPath()); break; case ProcessResult::Hang: response.message = msgCrash(); @@ -1295,19 +1288,19 @@ PerforceResponse PerforcePluginPrivate::fullySynchronousProcess(const FilePath & process.setWorkingDirectory(workingDir); PerforceResponse response; - process.setCommand({m_settings.p4BinaryPath.filePath(), args}); + process.setCommand({settings().p4BinaryPath(), args}); process.setWriteData(stdInput); process.start(); if (!process.waitForStarted(3000)) { response.error = true; - response.message = msgNotStarted(m_settings.p4BinaryPath.value()); + response.message = msgNotStarted(settings().p4BinaryPath()); return response; } QByteArray stdOut; QByteArray stdErr; - const int timeOutS = (flags & LongTimeOut) ? m_settings.longTimeOutS() : m_settings.timeOutS(); + const int timeOutS = (flags & LongTimeOut) ? settings().longTimeOutS() : settings().timeOutS(); if (!process.readDataFromProcess(&stdOut, &stdErr, timeOutS)) { process.stop(); process.waitForFinished(); @@ -1343,14 +1336,14 @@ PerforceResponse PerforcePluginPrivate::runP4Cmd(const FilePath &workingDir, const QByteArray &stdInput, QTextCodec *outputCodec) const { - if (!m_settings.isValid()) { + if (!settings().isValid()) { PerforceResponse invalidConfigResponse; invalidConfigResponse.error = true; invalidConfigResponse.message = Tr::tr("Perforce is not correctly configured."); VcsOutputWindow::appendError(invalidConfigResponse.message); return invalidConfigResponse; } - QStringList actualArgs = m_settings.commonP4Arguments(workingDir.toString()); + QStringList actualArgs = settings().commonP4Arguments(workingDir.toString()); QString errorMessage; QSharedPointer<TempFileSaver> tempFile = createTemporaryArgumentFile(extraArgs, &errorMessage); if (!tempFile.isNull()) { @@ -1364,7 +1357,7 @@ PerforceResponse PerforcePluginPrivate::runP4Cmd(const FilePath &workingDir, actualArgs.append(args); if (flags & CommandToWindow) - VcsOutputWindow::appendCommand(workingDir, {m_settings.p4BinaryPath.filePath(), actualArgs}); + VcsOutputWindow::appendCommand(workingDir, {settings().p4BinaryPath(), actualArgs}); if (flags & ShowBusyCursor) QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); @@ -1412,7 +1405,7 @@ IEditor *PerforcePluginPrivate::showOutputInEditor(const QString &title, void PerforcePluginPrivate::slotSubmitDiff(const QStringList &files) { - p4Diff(m_settings.topLevel(), files); + p4Diff(settings().topLevel(), files); } // Parameter widget controlling whitespace diff mode, associated with a parameter @@ -1527,7 +1520,7 @@ void PerforcePluginPrivate::vcsDescribe(const FilePath &source, const QString &n : VcsBaseEditor::getCodec(source); QStringList args; args << QLatin1String("describe") << QLatin1String("-du") << n; - const PerforceResponse result = runP4Cmd(m_settings.topLevel(), args, CommandToWindow|StdErrToWindow|ErrorToWindow, + const PerforceResponse result = runP4Cmd(settings().topLevel(), args, CommandToWindow|StdErrToWindow|ErrorToWindow, {}, {}, codec); if (!result.error) showOutputInEditor(Tr::tr("p4 describe %1").arg(n), result.stdOut, diffEditorParameters.id, source, codec); @@ -1567,7 +1560,7 @@ bool PerforcePluginPrivate::activateCommit() QStringList submitArgs; submitArgs << QLatin1String("submit") << QLatin1String("-i"); - const PerforceResponse submitResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), submitArgs, + const PerforceResponse submitResponse = runP4Cmd(settings().topLevelSymLinkTarget(), submitArgs, LongTimeOut|RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow|ShowBusyCursor, {}, reader.data()); if (submitResponse.error) { @@ -1584,14 +1577,14 @@ bool PerforcePluginPrivate::activateCommit() QString PerforcePluginPrivate::clientFilePath(const QString &serverFilePath) { - QTC_ASSERT(m_settings.isValid(), return QString()); + QTC_ASSERT(settings().isValid(), return QString()); QStringList args; args << QLatin1String("fstat") << serverFilePath; - const PerforceResponse response = runP4Cmd(m_settings.topLevelSymLinkTarget(), args, + const PerforceResponse response = runP4Cmd(settings().topLevelSymLinkTarget(), args, ShowBusyCursor|RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow); if (response.error) - return QString(); + return {}; const QRegularExpression r("\\.\\.\\.\\sclientFile\\s(.+?)\n"); const QRegularExpressionMatch match = r.match(response.stdOut); @@ -1600,23 +1593,23 @@ QString PerforcePluginPrivate::clientFilePath(const QString &serverFilePath) QString PerforcePluginPrivate::pendingChangesData() { - QTC_ASSERT(m_settings.isValid(), return QString()); + QTC_ASSERT(settings().isValid(), return QString()); QStringList args = QStringList(QLatin1String("info")); - const PerforceResponse userResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), args, + const PerforceResponse userResponse = runP4Cmd(settings().topLevelSymLinkTarget(), args, RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow); if (userResponse.error) - return QString(); + return {}; const QRegularExpression r("User\\sname:\\s(\\S+?)\\s*?\n"); QTC_ASSERT(r.isValid(), return QString()); const QRegularExpressionMatch match = r.match(userResponse.stdOut); const QString user = match.hasMatch() ? match.captured(1).trimmed() : QString(); if (user.isEmpty()) - return QString(); + return {}; args.clear(); args << QLatin1String("changes") << QLatin1String("-s") << QLatin1String("pending") << QLatin1String("-u") << user; - const PerforceResponse dataResponse = runP4Cmd(m_settings.topLevelSymLinkTarget(), args, + const PerforceResponse dataResponse = runP4Cmd(settings().topLevelSymLinkTarget(), args, RunFullySynchronous|CommandToWindow|StdErrToWindow|ErrorToWindow); return dataResponse.error ? QString() : dataResponse.stdOut; } @@ -1643,10 +1636,10 @@ QString PerforcePlugin::fileNameFromPerforceName(const QString& perforceName, unsigned flags = RunFullySynchronous; if (!quiet) flags |= CommandToWindow|StdErrToWindow|ErrorToWindow; - const PerforceResponse response = dd->runP4Cmd(dd->m_settings.topLevelSymLinkTarget(), args, flags); + const PerforceResponse response = dd->runP4Cmd(settings().topLevelSymLinkTarget(), args, flags); if (response.error) { *errorMessage = msgWhereFailed(perforceName, response.message); - return QString(); + return {}; } QString output = response.stdOut; @@ -1658,31 +1651,23 @@ QString PerforcePlugin::fileNameFromPerforceName(const QString& perforceName, if (output.isEmpty()) { //: File is not managed by Perforce *errorMessage = msgWhereFailed(perforceName, Tr::tr("The file is not mapped")); - return QString(); + return {}; } const QString p4fileSpec = output.mid(output.lastIndexOf(QLatin1Char(' ')) + 1); - return dd->m_settings.mapToFileSystem(p4fileSpec); + return settings().mapToFileSystem(p4fileSpec); } void PerforcePluginPrivate::setTopLevel(const FilePath &topLevel) { - if (m_settings.topLevel() == topLevel) + if (settings().topLevel() == topLevel) return; - m_settings.setTopLevel(topLevel.toString()); + settings().setTopLevel(topLevel.toString()); const QString msg = Tr::tr("Perforce repository: %1").arg(topLevel.toUserOutput()); VcsOutputWindow::appendSilently(msg); } -void PerforcePluginPrivate::applySettings() -{ - m_settings.writeSettings(ICore::settings()); - m_managedDirectoryCache.clear(); - getTopLevel(); - emit configurationChanged(); -} - void PerforcePluginPrivate::slotTopLevelFailed(const QString &errorMessage) { VcsOutputWindow::appendSilently(Tr::tr("Perforce: Unable to determine the repository: %1").arg(errorMessage)); @@ -1691,7 +1676,7 @@ void PerforcePluginPrivate::slotTopLevelFailed(const QString &errorMessage) void PerforcePluginPrivate::getTopLevel(const FilePath &workingDirectory, bool isSync) { // Run a new checker - if (m_settings.p4BinaryPath.value().isEmpty()) + if (settings().p4BinaryPath().isEmpty()) return; auto checker = new PerforceChecker(dd); connect(checker, &PerforceChecker::failed, dd, &PerforcePluginPrivate::slotTopLevelFailed); @@ -1699,8 +1684,8 @@ void PerforcePluginPrivate::getTopLevel(const FilePath &workingDirectory, bool i connect(checker, &PerforceChecker::succeeded, dd, &PerforcePluginPrivate::setTopLevel); connect(checker, &PerforceChecker::succeeded,checker, &QObject::deleteLater); - checker->start(m_settings.p4BinaryPath.filePath(), workingDirectory, - m_settings.commonP4Arguments(QString()), 30000); + checker->start(settings().p4BinaryPath(), workingDirectory, + settings().commonP4Arguments(QString()), 30000); if (isSync) checker->waitForFinished(); diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp index 47ef5a03b35..6f7691c054f 100644 --- a/src/plugins/perforce/perforcesettings.cpp +++ b/src/plugins/perforce/perforcesettings.cpp @@ -30,15 +30,20 @@ static QString defaultCommand() return QLatin1String("p4" QTC_HOST_EXE_SUFFIX); } +PerforceSettings &settings() +{ + static PerforceSettings theSettings; + return theSettings; +} + PerforceSettings::PerforceSettings() { setSettingsGroup("Perforce"); setAutoApply(false); - p4BinaryPath.setDisplayStyle(StringAspect::PathChooserDisplay); p4BinaryPath.setSettingsKey("Command"); p4BinaryPath.setDefaultValue( - Environment::systemEnvironment().searchInPath(defaultCommand()).toString()); + Environment::systemEnvironment().searchInPath(defaultCommand()).toUserOutput()); p4BinaryPath.setHistoryCompleter("Perforce.Command.History"); p4BinaryPath.setExpectedKind(PathChooser::Command); p4BinaryPath.setDisplayName(Tr::tr("Perforce Command")); @@ -77,6 +82,71 @@ PerforceSettings::PerforceSettings() autoOpen.setSettingsKey("PromptToOpen"); autoOpen.setDefaultValue(true); autoOpen.setLabelText(Tr::tr("Automatically open files when editing")); + + setLayouter([this] { + using namespace Layouting; + + auto errorLabel = new InfoLabel({}, InfoLabel::None); + errorLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + errorLabel->setFilled(true); + auto testButton = new QPushButton(Tr::tr("Test")); + QObject::connect(testButton, &QPushButton::clicked, errorLabel, + [this, errorLabel, testButton] { + testButton->setEnabled(false); + auto checker = new PerforceChecker(errorLabel); + checker->setUseOverideCursor(true); + QObject::connect(checker, &PerforceChecker::failed, errorLabel, + [errorLabel, testButton, checker](const QString &t) { + errorLabel->setType(InfoLabel::Error); + errorLabel->setText(t); + testButton->setEnabled(true); + checker->deleteLater(); + }); + QObject::connect(checker, &PerforceChecker::succeeded, errorLabel, + [errorLabel, testButton, checker](const FilePath &repo) { + errorLabel->setType(InfoLabel::Ok); + errorLabel->setText(Tr::tr("Test succeeded (%1).") + .arg(repo.toUserOutput())); + testButton->setEnabled(true); + checker->deleteLater(); + }); + + errorLabel->setType(InfoLabel::Information); + errorLabel->setText(Tr::tr("Testing...")); + + const FilePath p4Bin = FilePath::fromUserInput(p4BinaryPath.volatileValue()); + checker->start(p4Bin, {}, commonP4Arguments_volatile(), 10000); + }); + + Group config { + title(Tr::tr("Configuration")), + Row { p4BinaryPath } + }; + + Group environment { + title(Tr::tr("Environment Variables")), + customEnv.groupChecker(), + Row { p4Port, p4Client, p4User } + }; + + Group misc { + title(Tr::tr("Miscellaneous")), + Column { + Row { logCount, timeOutS, st }, + autoOpen + } + }; + + return Column { + config, + environment, + misc, + Row { errorLabel, st, testButton }, + st + }; + }); + + readSettings(); } // --------------------PerforceSettings @@ -88,13 +158,13 @@ PerforceSettings::~PerforceSettings() QStringList PerforceSettings::commonP4Arguments() const { QStringList lst; - if (customEnv.value()) { - if (!p4Client.value().isEmpty()) - lst << "-c" << p4Client.value(); - if (!p4Port.value().isEmpty()) - lst << "-p" << p4Port.value(); - if (!p4User.value().isEmpty()) - lst << "-u" << p4User.value(); + if (customEnv()) { + if (!p4Client().isEmpty()) + lst << "-c" << p4Client(); + if (!p4Port().isEmpty()) + lst << "-p" << p4Port(); + if (!p4User().isEmpty()) + lst << "-u" << p4User(); } return lst; } @@ -102,14 +172,14 @@ QStringList PerforceSettings::commonP4Arguments() const QStringList PerforceSettings::commonP4Arguments_volatile() const { QStringList lst; - if (customEnv.volatileValue().toBool()) { - auto p4C = p4Client.volatileValue().toString(); + if (customEnv.volatileValue()) { + const QString p4C = p4Client.volatileValue(); if (!p4C.isEmpty()) lst << "-c" << p4C; - auto p4P = p4Port.volatileValue().toString(); + const QString p4P = p4Port.volatileValue(); if (!p4P.isEmpty()) lst << "-p" << p4P; - auto p4U = p4User.volatileValue().toString(); + const QString p4U = p4User.volatileValue(); if (!p4U.isEmpty()) lst << "-u" << p4U; } @@ -118,7 +188,7 @@ QStringList PerforceSettings::commonP4Arguments_volatile() const bool PerforceSettings::isValid() const { - return !m_topLevel.isEmpty() && !p4BinaryPath.value().isEmpty(); + return !m_topLevel.isEmpty() && !p4BinaryPath().isEmpty(); } bool PerforceSettings::defaultEnv() const @@ -209,77 +279,19 @@ QString PerforceSettings::mapToFileSystem(const QString &perforceFilePath) const // SettingsPage -PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings) +class PerforceSettingsPage final : public Core::IOptionsPage { - setId(VcsBase::Constants::VCS_ID_PERFORCE); - setDisplayName(Tr::tr("Perforce")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); - setSettings(settings); +public: + explicit PerforceSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_PERFORCE); + setDisplayName(Tr::tr("Perforce")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); - setLayouter([settings] { - PerforceSettings &s = *settings; - using namespace Layouting; + } +}; - auto errorLabel = new InfoLabel({}, InfoLabel::None); - errorLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - errorLabel->setFilled(true); - auto testButton = new QPushButton(Tr::tr("Test")); - QObject::connect(testButton, &QPushButton::clicked, errorLabel, - [settings, errorLabel, testButton] { - testButton->setEnabled(false); - auto checker = new PerforceChecker(errorLabel); - checker->setUseOverideCursor(true); - QObject::connect(checker, &PerforceChecker::failed, errorLabel, - [errorLabel, testButton, checker](const QString &t) { - errorLabel->setType(InfoLabel::Error); - errorLabel->setText(t); - testButton->setEnabled(true); - checker->deleteLater(); - }); - QObject::connect(checker, &PerforceChecker::succeeded, errorLabel, - [errorLabel, testButton, checker](const FilePath &repo) { - errorLabel->setType(InfoLabel::Ok); - errorLabel->setText(Tr::tr("Test succeeded (%1).") - .arg(repo.toUserOutput())); - testButton->setEnabled(true); - checker->deleteLater(); - }); - - errorLabel->setType(InfoLabel::Information); - errorLabel->setText(Tr::tr("Testing...")); - - const FilePath p4Bin = FilePath::fromUserInput( - settings->p4BinaryPath.volatileValue().toString()); - checker->start(p4Bin, {}, settings->commonP4Arguments_volatile(), 10000); - }); - - Group config { - title(Tr::tr("Configuration")), - Row { s.p4BinaryPath } - }; - - Group environment { - title(Tr::tr("Environment Variables")), - s.customEnv.groupChecker(), - Row { s.p4Port, s.p4Client, s.p4User } - }; - - Group misc { - title(Tr::tr("Miscellaneous")), - Column { - Row { s.logCount, s.timeOutS, st }, - s.autoOpen - } - }; - - return Column { - config, - environment, - misc, - Row { errorLabel, st, testButton }, - st - }; - }); -} +const PerforceSettingsPage settingsPage; } // Perforce::Internal diff --git a/src/plugins/perforce/perforcesettings.h b/src/plugins/perforce/perforcesettings.h index 0801c310907..d1d8265157f 100644 --- a/src/plugins/perforce/perforcesettings.h +++ b/src/plugins/perforce/perforcesettings.h @@ -68,7 +68,7 @@ public: void clearTopLevel(); - Utils::StringAspect p4BinaryPath{this}; + Utils::FilePathAspect p4BinaryPath{this}; Utils::StringAspect p4Port{this}; Utils::StringAspect p4Client{this}; Utils::StringAspect p4User{this}; @@ -84,10 +84,6 @@ private: QDir *m_topLevelDir = nullptr; }; -class PerforceSettingsPage final : public Core::IOptionsPage -{ -public: - explicit PerforceSettingsPage(PerforceSettings *settings); -}; +PerforceSettings &settings(); } // Perforce::Internal diff --git a/src/plugins/perfprofiler/CMakeLists.txt b/src/plugins/perfprofiler/CMakeLists.txt index b7ae8e6c9cf..360ee8a7ee9 100644 --- a/src/plugins/perfprofiler/CMakeLists.txt +++ b/src/plugins/perfprofiler/CMakeLists.txt @@ -10,12 +10,10 @@ endif() set(PERFPROFILER_CPP_SOURCES perfconfigeventsmodel.cpp perfconfigeventsmodel.h - perfconfigwidget.cpp perfconfigwidget.h perfdatareader.cpp perfdatareader.h perfevent.h perfeventtype.h perfloaddialog.cpp perfloaddialog.h - perfoptionspage.cpp perfoptionspage.h perfprofiler.qrc perfprofilerconstants.h perfprofilerflamegraphmodel.cpp perfprofilerflamegraphmodel.h @@ -29,7 +27,7 @@ set(PERFPROFILER_CPP_SOURCES perfprofilertracemanager.cpp perfprofilertracemanager.h perfprofilertraceview.cpp perfprofilertraceview.h perfprofiler_global.h - perfprofilertr.h + perfprofilertr.h perfresourcecounter.cpp perfresourcecounter.h perfrunconfigurationaspect.cpp perfrunconfigurationaspect.h perfsettings.cpp perfsettings.h diff --git a/src/plugins/perfprofiler/PerfProfiler.json.in b/src/plugins/perfprofiler/PerfProfiler.json.in index 7af2536d01e..c3a439b1a56 100644 --- a/src/plugins/perfprofiler/PerfProfiler.json.in +++ b/src/plugins/perfprofiler/PerfProfiler.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"PerfProfiler\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "PerfProfiler", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"Perf Profiler Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "Perf Profiler Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/perfprofiler/perfconfigeventsmodel.cpp b/src/plugins/perfprofiler/perfconfigeventsmodel.cpp index a235a6122db..296feb88790 100644 --- a/src/plugins/perfprofiler/perfconfigeventsmodel.cpp +++ b/src/plugins/perfprofiler/perfconfigeventsmodel.cpp @@ -22,7 +22,7 @@ PerfConfigEventsModel::PerfConfigEventsModel(PerfSettings *settings, QObject *pa int PerfConfigEventsModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : m_settings->events.value().length(); + return parent.isValid() ? 0 : m_settings->events().length(); } int PerfConfigEventsModel::columnCount(const QModelIndex &parent) const @@ -40,7 +40,7 @@ QVariant PerfConfigEventsModel::data(const QModelIndex &index, int role) const return QVariant(); // ignore } - QString event = m_settings->events.value().value(index.row()); + QString event = m_settings->events().value(index.row()); const EventDescription description = parseEvent(event); switch (index.column()) { case ColumnEventType: { @@ -124,7 +124,7 @@ bool PerfConfigEventsModel::setData(const QModelIndex &dataIndex, const QVariant const int row = dataIndex.row(); const int column = dataIndex.column(); - QStringList events = m_settings->events.value(); + QStringList events = m_settings->events(); EventDescription description = parseEvent(events[row]); switch (column) { case ColumnEventType: @@ -183,7 +183,7 @@ bool PerfConfigEventsModel::insertRows(int row, int count, const QModelIndex &pa if (parent.isValid()) return false; - QStringList events = m_settings->events.value(); + QStringList events = m_settings->events(); for (int i = 0; i < count; ++i) events.insert(row, "dummy"); beginInsertRows(parent, row, row + count - 1); @@ -197,7 +197,7 @@ bool PerfConfigEventsModel::removeRows(int row, int count, const QModelIndex &pa if (parent.isValid()) return false; - QStringList events = m_settings->events.value(); + QStringList events = m_settings->events(); for (int i = 0; i < count; ++i) events.removeAt(row); beginRemoveRows(parent, row, row + count - 1); diff --git a/src/plugins/perfprofiler/perfconfigwidget.cpp b/src/plugins/perfprofiler/perfconfigwidget.cpp deleted file mode 100644 index e5e3fd37256..00000000000 --- a/src/plugins/perfprofiler/perfconfigwidget.cpp +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "perfconfigeventsmodel.h" -#include "perfconfigwidget.h" -#include "perfprofilertr.h" - -#include <coreplugin/messagebox.h> - -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/target.h> - -#include <utils/aspects.h> -#include <utils/layoutbuilder.h> -#include <utils/process.h> -#include <utils/qtcassert.h> - -#include <QComboBox> -#include <QHeaderView> -#include <QMessageBox> -#include <QMetaEnum> -#include <QPushButton> -#include <QStyledItemDelegate> -#include <QTableView> - -using namespace Utils; - -namespace PerfProfiler { -namespace Internal { - -class SettingsDelegate : public QStyledItemDelegate -{ - Q_OBJECT -public: - SettingsDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} - - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; - - void setEditorData(QWidget *editor, const QModelIndex &index) const override; - void setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const override; - - void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, - const QModelIndex &index) const override; -}; - -PerfConfigWidget::PerfConfigWidget(PerfSettings *settings, QWidget *parent) - : m_settings(settings) -{ - setParent(parent); - - eventsView = new QTableView(this); - eventsView->setMinimumSize(QSize(0, 300)); - eventsView->setEditTriggers(QAbstractItemView::AllEditTriggers); - eventsView->setSelectionMode(QAbstractItemView::SingleSelection); - eventsView->setSelectionBehavior(QAbstractItemView::SelectRows); - eventsView->setModel(new PerfConfigEventsModel(m_settings, this)); - eventsView->setItemDelegate(new SettingsDelegate(this)); - eventsView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - - useTracePointsButton = new QPushButton(this); - useTracePointsButton->setText(Tr::tr("Use Trace Points")); - useTracePointsButton->setVisible(false); - connect(useTracePointsButton, &QPushButton::pressed, - this, &PerfConfigWidget::readTracePoints); - - addEventButton = new QPushButton(this); - addEventButton->setText(Tr::tr("Add Event")); - connect(addEventButton, &QPushButton::pressed, this, [this]() { - auto model = eventsView->model(); - model->insertRow(model->rowCount()); - }); - - removeEventButton = new QPushButton(this); - removeEventButton->setText(Tr::tr("Remove Event")); - connect(removeEventButton, &QPushButton::pressed, this, [this]() { - QModelIndex index = eventsView->currentIndex(); - if (index.isValid()) - eventsView->model()->removeRow(index.row()); - }); - - resetButton = new QPushButton(this); - resetButton->setText(Tr::tr("Reset")); - connect(resetButton, &QPushButton::pressed, m_settings, &PerfSettings::resetToDefault); - - using namespace Layouting; - Column { - Row { st, useTracePointsButton, addEventButton, removeEventButton, resetButton }, - - eventsView, - - Grid { - m_settings->callgraphMode, m_settings->stackSize, br, - m_settings->sampleMode, m_settings->period, br, - m_settings->extraArguments, - }, - - st - }.attachTo(this); -} - -PerfConfigWidget::~PerfConfigWidget() = default; - -void PerfConfigWidget::setTarget(ProjectExplorer::Target *target) -{ - ProjectExplorer::IDevice::ConstPtr device; - if (target) { - if (ProjectExplorer::Kit *kit = target->kit()) - device = ProjectExplorer::DeviceKitAspect::device(kit); - } - - if (device.isNull()) { - useTracePointsButton->setEnabled(false); - return; - } - - QTC_ASSERT(device, return); - QTC_CHECK(!m_process || m_process->state() == QProcess::NotRunning); - - m_process.reset(new Process); - m_process->setCommand({device->filePath("perf"), {"probe", "-l"}}); - connect(m_process.get(), &Process::done, - this, &PerfConfigWidget::handleProcessDone); - - useTracePointsButton->setEnabled(true); -} - -void PerfConfigWidget::setTracePointsButtonVisible(bool visible) -{ - useTracePointsButton->setVisible(visible); -} - -void PerfConfigWidget::apply() -{ - m_settings->writeGlobalSettings(); -} - -void PerfConfigWidget::readTracePoints() -{ - QMessageBox messageBox; - messageBox.setWindowTitle(Tr::tr("Use Trace Points")); - messageBox.setIcon(QMessageBox::Question); - messageBox.setText(Tr::tr("Replace events with trace points read from the device?")); - messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - if (messageBox.exec() == QMessageBox::Yes) { - m_process->start(); - useTracePointsButton->setEnabled(false); - } -} - -void PerfConfigWidget::handleProcessDone() -{ - if (m_process->error() == QProcess::FailedToStart) { - Core::AsynchronousMessageBox::warning( - Tr::tr("Cannot List Trace Points"), - Tr::tr("\"perf probe -l\" failed to start. Is perf installed?")); - useTracePointsButton->setEnabled(true); - return; - } - const QList<QByteArray> lines = - m_process->readAllRawStandardOutput().append(m_process->readAllRawStandardError()) - .split('\n'); - auto model = eventsView->model(); - const int previousRows = model->rowCount(); - QHash<QByteArray, QByteArray> tracePoints; - for (const QByteArray &line : lines) { - const QByteArray trimmed = line.trimmed(); - const int space = trimmed.indexOf(' '); - if (space < 0) - continue; - - // If the whole "on ..." string is the same, the trace points are redundant - tracePoints[trimmed.mid(space + 1)] = trimmed.left(space); - } - - if (tracePoints.isEmpty()) { - Core::AsynchronousMessageBox::warning( - Tr::tr("No Trace Points Found"), - Tr::tr("Trace points can be defined with \"perf probe -a\".")); - } else { - for (const QByteArray &event : std::as_const(tracePoints)) { - int row = model->rowCount(); - model->insertRow(row); - model->setData(model->index(row, PerfConfigEventsModel::ColumnEventType), - PerfConfigEventsModel::EventTypeCustom); - model->setData(model->index(row, PerfConfigEventsModel::ColumnSubType), - QString::fromUtf8(event)); - } - model->removeRows(0, previousRows); - m_settings->sampleMode.setVolatileValue(1); - m_settings->period.setVolatileValue(1); - } - useTracePointsButton->setEnabled(true); -} - -QWidget *SettingsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - Q_UNUSED(option) - const int row = index.row(); - const int column = index.column(); - const PerfConfigEventsModel *model = qobject_cast<const PerfConfigEventsModel *>(index.model()); - - auto getRowEventType = [&]() { - return qvariant_cast<PerfConfigEventsModel::EventType>( - model->data(model->index(row, PerfConfigEventsModel::ColumnEventType), - Qt::EditRole)); - }; - - switch (column) { - case PerfConfigEventsModel::ColumnEventType: { - QComboBox *editor = new QComboBox(parent); - QMetaEnum meta = QMetaEnum::fromType<PerfConfigEventsModel::EventType>(); - for (int i = 0; i < PerfConfigEventsModel::EventTypeInvalid; ++i) { - editor->addItem(QString::fromLatin1(meta.valueToKey(i)).mid( - static_cast<int>(strlen("EventType"))).toLower(), i); - } - return editor; - } - case PerfConfigEventsModel::ColumnSubType: { - PerfConfigEventsModel::EventType eventType = getRowEventType(); - switch (eventType) { - case PerfConfigEventsModel::EventTypeHardware: { - QComboBox *editor = new QComboBox(parent); - for (int i = PerfConfigEventsModel::SubTypeEventTypeHardware; - i < PerfConfigEventsModel::SubTypeEventTypeSoftware; ++i) { - editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeHardware, - PerfConfigEventsModel::SubType(i)), i); - } - return editor; - } - case PerfConfigEventsModel::EventTypeSoftware: { - QComboBox *editor = new QComboBox(parent); - for (int i = PerfConfigEventsModel::SubTypeEventTypeSoftware; - i < PerfConfigEventsModel::SubTypeEventTypeCache; ++i) { - editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeSoftware, - PerfConfigEventsModel::SubType(i)), i); - } - return editor; - } - case PerfConfigEventsModel::EventTypeCache: { - QComboBox *editor = new QComboBox(parent); - for (int i = PerfConfigEventsModel::SubTypeEventTypeCache; - i < PerfConfigEventsModel::SubTypeInvalid; ++i) { - editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeCache, - PerfConfigEventsModel::SubType(i)), i); - } - return editor; - } - case PerfConfigEventsModel::EventTypeBreakpoint: { - QLineEdit *editor = new QLineEdit(parent); - editor->setText("0x0000000000000000"); - editor->setValidator(new QRegularExpressionValidator( - QRegularExpression("0x[0-9a-f]{16}"), parent)); - return editor; - } - case PerfConfigEventsModel::EventTypeCustom: { - QLineEdit *editor = new QLineEdit(parent); - return editor; - } - case PerfConfigEventsModel::EventTypeRaw: { - QLineEdit *editor = new QLineEdit(parent); - editor->setText("r000"); - editor->setValidator(new QRegularExpressionValidator( - QRegularExpression("r[0-9a-f]{3}"), parent)); - return editor; - } - case PerfConfigEventsModel::EventTypeInvalid: - return nullptr; - } - return nullptr; // Will never be reached, but GCC cannot figure this out. - } - case PerfConfigEventsModel::ColumnOperation: { - QComboBox *editor = new QComboBox(parent); - PerfConfigEventsModel::EventType eventType = getRowEventType(); - if (eventType == PerfConfigEventsModel::EventTypeCache) { - editor->addItem("load", PerfConfigEventsModel::OperationLoad); - editor->addItem("store", PerfConfigEventsModel::OperationStore); - editor->addItem("prefetch", PerfConfigEventsModel::OperationPrefetch); - } else if (eventType == PerfConfigEventsModel::EventTypeBreakpoint) { - editor->addItem("r", PerfConfigEventsModel::OperationLoad); - editor->addItem("rw", PerfConfigEventsModel::OperationLoad - | PerfConfigEventsModel::OperationStore); - editor->addItem("rwx", PerfConfigEventsModel::OperationLoad - | PerfConfigEventsModel::OperationStore - | PerfConfigEventsModel::OperationExecute); - editor->addItem("rx", PerfConfigEventsModel::OperationLoad - | PerfConfigEventsModel::OperationExecute); - editor->addItem("w", PerfConfigEventsModel::OperationStore); - editor->addItem("wx", PerfConfigEventsModel::OperationStore - | PerfConfigEventsModel::OperationExecute); - editor->addItem("x", PerfConfigEventsModel::OperationExecute); - } else { - editor->setEnabled(false); - } - return editor; - } - case PerfConfigEventsModel::ColumnResult: { - QComboBox *editor = new QComboBox(parent); - PerfConfigEventsModel::EventType eventType = getRowEventType(); - if (eventType != PerfConfigEventsModel::EventTypeCache) { - editor->setEnabled(false); - } else { - editor->addItem("refs", PerfConfigEventsModel::ResultRefs); - editor->addItem("misses", PerfConfigEventsModel::ResultMisses); - } - return editor; - } - default: - return nullptr; - } -} - -void SettingsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - if (QComboBox *combo = qobject_cast<QComboBox *>(editor)) { - QVariant data = index.model()->data(index, Qt::EditRole); - for (int i = 0, end = combo->count(); i != end; ++i) { - if (combo->itemData(i) == data) { - combo->setCurrentIndex(i); - return; - } - } - } else if (QLineEdit *lineedit = qobject_cast<QLineEdit *>(editor)) { - lineedit->setText(index.model()->data(index, Qt::DisplayRole).toString()); - } -} - -void SettingsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - if (QComboBox *combo = qobject_cast<QComboBox *>(editor)) { - model->setData(index, combo->currentData()); - } else if (QLineEdit *lineedit = qobject_cast<QLineEdit *>(editor)) { - QString text = lineedit->text(); - QVariant type = model->data(model->index(index.row(), - PerfConfigEventsModel::ColumnEventType), - Qt::EditRole); - switch (qvariant_cast<PerfConfigEventsModel::EventType>(type)) { - case PerfConfigEventsModel::EventTypeRaw: - model->setData(index, text.mid(static_cast<int>(strlen("r"))).toULongLong(nullptr, 16)); - break; - case PerfConfigEventsModel::EventTypeBreakpoint: - model->setData(index, - text.mid(static_cast<int>(strlen("0x"))).toULongLong(nullptr, 16)); - break; - case PerfConfigEventsModel::EventTypeCustom: - model->setData(index, text); - break; - default: - break; - } - } -} - -void SettingsDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - Q_UNUSED(index) - editor->setGeometry(option.rect); -} - -} // namespace Internal -} // namespace PerfProfiler - -#include "perfconfigwidget.moc" diff --git a/src/plugins/perfprofiler/perfconfigwidget.h b/src/plugins/perfprofiler/perfconfigwidget.h deleted file mode 100644 index 5372647064e..00000000000 --- a/src/plugins/perfprofiler/perfconfigwidget.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "perfsettings.h" - -#include <coreplugin/dialogs/ioptionspage.h> - -#include <QProcess> - -QT_BEGIN_NAMESPACE -class QPushButton; -class QTableView; -QT_END_NAMESPACE - -namespace Utils { class Process; } - -namespace PerfProfiler { -namespace Internal { - -class PerfConfigWidget : public Core::IOptionsPageWidget -{ - Q_OBJECT -public: - explicit PerfConfigWidget(PerfSettings *settings, QWidget *parent = nullptr); - ~PerfConfigWidget(); - - void updateUi(); - void setTarget(ProjectExplorer::Target *target); - void setTracePointsButtonVisible(bool visible); - -private: - void apply() final; - - void readTracePoints(); - void handleProcessDone(); - - PerfSettings *m_settings; - std::unique_ptr<Utils::Process> m_process; - - QTableView *eventsView; - QPushButton *useTracePointsButton; - QPushButton *addEventButton; - QPushButton *removeEventButton; - QPushButton *resetButton; -}; - -} // namespace Internal -} // namespace PerfProfiler diff --git a/src/plugins/perfprofiler/perfdatareader.cpp b/src/plugins/perfprofiler/perfdatareader.cpp index a545dff913e..222e1c9788e 100644 --- a/src/plugins/perfprofiler/perfdatareader.cpp +++ b/src/plugins/perfprofiler/perfdatareader.cpp @@ -3,11 +3,7 @@ #include "perfdatareader.h" #include "perfprofilerconstants.h" -#include "perfprofilerplugin.h" #include "perfprofilertr.h" -#include "perfrunconfigurationaspect.h" -#include "perfsettings.h" -#include "perftimelinemodel.h" #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> @@ -15,16 +11,16 @@ #include <coreplugin/progressmanager/progressmanager.h> #include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> #include <utils/environment.h> #include <utils/qtcassert.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QDateTime> #include <QDebug> diff --git a/src/plugins/perfprofiler/perfoptionspage.cpp b/src/plugins/perfprofiler/perfoptionspage.cpp deleted file mode 100644 index e295a5be489..00000000000 --- a/src/plugins/perfprofiler/perfoptionspage.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "perfconfigwidget.h" -#include "perfoptionspage.h" -#include "perfprofilerconstants.h" -#include "perfprofilertr.h" - -#include <debugger/analyzer/analyzericons.h> -#include <debugger/debuggertr.h> - -namespace PerfProfiler { -namespace Internal { - -PerfOptionsPage::PerfOptionsPage(PerfSettings *settings) -{ - setId(Constants::PerfSettingsId); - setDisplayName(Tr::tr("CPU Usage")); - setCategory("T.Analyzer"); - setDisplayCategory(::Debugger::Tr::tr("Analyzer")); - setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); - setWidgetCreator([settings] { return new PerfConfigWidget(settings); }); -} - -} // namespace Internal -} // namespace PerfProfiler diff --git a/src/plugins/perfprofiler/perfoptionspage.h b/src/plugins/perfprofiler/perfoptionspage.h deleted file mode 100644 index ae87f031ed0..00000000000 --- a/src/plugins/perfprofiler/perfoptionspage.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace PerfProfiler { - -class PerfSettings; - -namespace Internal { - -class PerfOptionsPage final : public Core::IOptionsPage -{ -public: - explicit PerfOptionsPage(PerfSettings *settings); -}; - -} // namespace Internal -} // namespace PerfProfiler diff --git a/src/plugins/perfprofiler/perfprofiler.qbs b/src/plugins/perfprofiler/perfprofiler.qbs index 7a037e7fa47..93235770250 100644 --- a/src/plugins/perfprofiler/perfprofiler.qbs +++ b/src/plugins/perfprofiler/perfprofiler.qbs @@ -9,7 +9,6 @@ QtcPlugin { Depends { name: "QtSupport" } Depends { name: "Tracing" } Depends { name: "Utils" } - Depends { name: "app_version_header" } Depends { name: "Qt" @@ -19,16 +18,12 @@ QtcPlugin { files: [ "perfconfigeventsmodel.cpp", "perfconfigeventsmodel.h", - "perfconfigwidget.cpp", - "perfconfigwidget.h", "perfdatareader.cpp", "perfdatareader.h", "perfevent.h", "perfeventtype.h", "perfloaddialog.cpp", "perfloaddialog.h", - "perfoptionspage.cpp", - "perfoptionspage.h", "perfprofiler_global.h", "perfprofilertr.h", "perfprofilerconstants.h", "perfprofilerplugin.cpp", diff --git a/src/plugins/perfprofiler/perfprofilerplugin.cpp b/src/plugins/perfprofiler/perfprofilerplugin.cpp index 1293d84f436..f7ac90eaa9a 100644 --- a/src/plugins/perfprofiler/perfprofilerplugin.cpp +++ b/src/plugins/perfprofiler/perfprofilerplugin.cpp @@ -1,8 +1,8 @@ // Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "perfoptionspage.h" #include "perfprofilerplugin.h" + #include "perfprofilerruncontrol.h" #include "perfprofilertool.h" #include "perfrunconfigurationaspect.h" @@ -17,8 +17,6 @@ using namespace ProjectExplorer; namespace PerfProfiler::Internal { -Q_GLOBAL_STATIC(PerfSettings, perfGlobalSettings) - class PerfProfilerPluginPrivate { public: @@ -28,7 +26,6 @@ public: } PerfProfilerRunWorkerFactory profilerWorkerFactory; - PerfOptionsPage optionsPage{perfGlobalSettings()}; PerfProfilerTool profilerTool; }; @@ -47,9 +44,4 @@ void PerfProfilerPlugin::initialize() #endif // WITH_TESTS } -PerfSettings *PerfProfilerPlugin::globalSettings() -{ - return perfGlobalSettings(); -} - } // PerfProfiler::Internal diff --git a/src/plugins/perfprofiler/perfprofilerplugin.h b/src/plugins/perfprofiler/perfprofilerplugin.h index afb6ea0936d..8421884f0fe 100644 --- a/src/plugins/perfprofiler/perfprofilerplugin.h +++ b/src/plugins/perfprofiler/perfprofilerplugin.h @@ -3,8 +3,6 @@ #pragma once -#include "perfsettings.h" - #include <extensionsystem/iplugin.h> namespace PerfProfiler::Internal { @@ -19,8 +17,6 @@ public: void initialize() final; - static PerfSettings *globalSettings(); - class PerfProfilerPluginPrivate *d = nullptr; }; diff --git a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp index 3d633160133..1919547383d 100644 --- a/src/plugins/perfprofiler/perfprofilerruncontrol.cpp +++ b/src/plugins/perfprofiler/perfprofilerruncontrol.cpp @@ -13,7 +13,7 @@ #include <coreplugin/messagemanager.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -185,8 +185,6 @@ void PerfProfilerRunner::start() &PerfProfilerTool::onRunControlStarted); connect(runControl(), &RunControl::stopped, PerfProfilerTool::instance(), &PerfProfilerTool::onRunControlFinished); - connect(runControl(), &RunControl::finished, PerfProfilerTool::instance(), - &PerfProfilerTool::onRunControlFinished); PerfDataReader *reader = m_perfParserWorker->reader(); if (auto prw = qobject_cast<LocalPerfRecordWorker *>(m_perfRecordWorker)) { diff --git a/src/plugins/perfprofiler/perfprofilertool.cpp b/src/plugins/perfprofiler/perfprofilertool.cpp index 60bbcc3ae31..6951622e52b 100644 --- a/src/plugins/perfprofiler/perfprofilertool.cpp +++ b/src/plugins/perfprofiler/perfprofilertool.cpp @@ -3,9 +3,7 @@ #include "perfprofilertool.h" -#include "perfconfigwidget.h" #include "perfloaddialog.h" -#include "perfprofilerplugin.h" #include "perfprofilertr.h" #include "perfsettings.h" #include "perftracepointdialog.h" @@ -22,15 +20,15 @@ #include <debugger/analyzer/analyzermanager.h> #include <debugger/debuggericons.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/fancymainwindow.h> @@ -215,12 +213,11 @@ void PerfProfilerTool::createViews() m_flameGraphView, &PerfProfilerFlameGraphView::selectByTypeId); // Clear settings if the statistics or flamegraph view isn't there yet. - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String("AnalyzerViewSettings_") + - QLatin1String(Constants::PerfProfilerPerspectiveId)); - if (!settings->contains(m_statisticsView->objectName()) - || !settings->contains(m_flameGraphView->objectName())) { - settings->remove(QString()); + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(Key("AnalyzerViewSettings_") + Constants::PerfProfilerPerspectiveId); + if (!settings->contains(keyFromString(m_statisticsView->objectName())) + || !settings->contains(keyFromString(m_flameGraphView->objectName()))) { + settings->remove(Key()); } settings->endGroup(); @@ -236,11 +233,8 @@ void PerfProfilerTool::createViews() settings = runConfig->currentSettings<PerfSettings>(Constants::PerfSettingsId); } - PerfConfigWidget *widget = new PerfConfigWidget( - settings ? settings : PerfProfilerPlugin::globalSettings(), - Core::ICore::dialogParent()); - widget->setTracePointsButtonVisible(true); - widget->setTarget(target); + QWidget *widget = settings ? settings->createPerfConfigWidget(target) + : globalSettings().createPerfConfigWidget(target); widget->setWindowFlags(Qt::Dialog); widget->setAttribute(Qt::WA_DeleteOnClose); widget->show(); @@ -449,11 +443,10 @@ void PerfProfilerTool::updateRunActions() m_loadPerfData->setEnabled(false); m_loadTrace->setEnabled(false); } else { - QString whyNot = Tr::tr("Start a performance analysis."); - bool canRun = ProjectExplorerPlugin::canRunStartupProject( - ProjectExplorer::Constants::PERFPROFILER_RUN_MODE, &whyNot); - m_startAction->setToolTip(whyNot); - m_startAction->setEnabled(canRun); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject( + ProjectExplorer::Constants::PERFPROFILER_RUN_MODE); + m_startAction->setToolTip(canRun ? Tr::tr("Start a performance analysis.") : canRun.error()); + m_startAction->setEnabled(bool(canRun)); m_loadPerfData->setEnabled(true); m_loadTrace->setEnabled(true); } diff --git a/src/plugins/perfprofiler/perfprofilertracefile.cpp b/src/plugins/perfprofiler/perfprofilertracefile.cpp index 50c519e7af5..9cc7befc4c9 100644 --- a/src/plugins/perfprofiler/perfprofilertracefile.cpp +++ b/src/plugins/perfprofiler/perfprofilertracefile.cpp @@ -6,9 +6,8 @@ #include "perfprofilertr.h" #include "perfprofilertracefile.h" -#include <app/app_version.h> - #include <QFile> +#include <QGuiApplication> #include <QtEndian> namespace PerfProfiler { @@ -250,9 +249,9 @@ void PerfProfilerTraceFile::readFromDevice() fail(Tr::tr("Invalid data format. The trace file's identification string is \"%1\". " "An acceptable trace file should have \"%2\". You cannot read trace files " "generated with older versions of %3.") - .arg(QString::fromLatin1(magic)) - .arg(QString::fromLatin1(Constants::PerfZqfileMagic)) - .arg(Core::Constants::IDE_DISPLAY_NAME)); + .arg(QString::fromLatin1(magic)) + .arg(QString::fromLatin1(Constants::PerfZqfileMagic)) + .arg(QGuiApplication::applicationDisplayName())); return; } diff --git a/src/plugins/perfprofiler/perfrunconfigurationaspect.cpp b/src/plugins/perfprofiler/perfrunconfigurationaspect.cpp index 2196d8be2a0..d801af2104c 100644 --- a/src/plugins/perfprofiler/perfrunconfigurationaspect.cpp +++ b/src/plugins/perfprofiler/perfrunconfigurationaspect.cpp @@ -14,7 +14,7 @@ namespace PerfProfiler { PerfRunConfigurationAspect::PerfRunConfigurationAspect(ProjectExplorer::Target *target) { setProjectSettings(new PerfSettings(target)); - setGlobalSettings(Internal::PerfProfilerPlugin::globalSettings()); + setGlobalSettings(&PerfProfiler::globalSettings()); setId(Constants::PerfSettingsId); setDisplayName(Tr::tr("Performance Analyzer Settings")); setUsingGlobalSettings(true); diff --git a/src/plugins/perfprofiler/perfsettings.cpp b/src/plugins/perfprofiler/perfsettings.cpp index 59a753e5dde..456f8909a99 100644 --- a/src/plugins/perfprofiler/perfsettings.cpp +++ b/src/plugins/perfprofiler/perfsettings.cpp @@ -1,30 +1,377 @@ // Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "perfconfigwidget.h" -#include "perfprofilerconstants.h" -#include "perfprofilertr.h" #include "perfsettings.h" +#include "perfconfigeventsmodel.h" +#include "perfprofilerconstants.h" +#include "perfprofilertr.h" + +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> +#include <coreplugin/messagebox.h> -#include <QSettings> +#include <debugger/analyzer/analyzericons.h> +#include <debugger/debuggertr.h> +#include <projectexplorer/devicesupport/idevice.h> +#include <projectexplorer/kit.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/target.h> + +#include <utils/aspects.h> +#include <utils/layoutbuilder.h> #include <utils/process.h> +#include <utils/qtcassert.h> +#include <QComboBox> +#include <QHeaderView> +#include <QMessageBox> +#include <QMetaEnum> +#include <QPushButton> +#include <QStyledItemDelegate> +#include <QTableView> + +using namespace ProjectExplorer; using namespace Utils; +using namespace PerfProfiler::Internal; + namespace PerfProfiler { -PerfSettings::PerfSettings(ProjectExplorer::Target *target) +class PerfConfigWidget : public QWidget { - setConfigWidgetCreator([this, target] { - auto widget = new Internal::PerfConfigWidget(this); - widget->setTracePointsButtonVisible(target != nullptr); - widget->setTarget(target); - return widget; +public: + PerfConfigWidget(PerfSettings *settings, Target *target); + +private: + void readTracePoints(); + void handleProcessDone(); + + PerfSettings *m_settings; + std::unique_ptr<Utils::Process> m_process; + + QTableView *eventsView; + QPushButton *useTracePointsButton; +}; + +class SettingsDelegate : public QStyledItemDelegate +{ +public: + SettingsDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {} + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const override; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &) const override + { + editor->setGeometry(option.rect); + } +}; + +PerfConfigWidget::PerfConfigWidget(PerfSettings *settings, Target *target) + : m_settings(settings) +{ + eventsView = new QTableView(this); + eventsView->setMinimumSize(QSize(0, 300)); + eventsView->setEditTriggers(QAbstractItemView::AllEditTriggers); + eventsView->setSelectionMode(QAbstractItemView::SingleSelection); + eventsView->setSelectionBehavior(QAbstractItemView::SelectRows); + eventsView->setModel(new PerfConfigEventsModel(m_settings, this)); + eventsView->setItemDelegate(new SettingsDelegate(this)); + eventsView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + + useTracePointsButton = new QPushButton(this); + useTracePointsButton->setText(Tr::tr("Use Trace Points")); + useTracePointsButton->setVisible(target != nullptr); + connect(useTracePointsButton, &QPushButton::pressed, + this, &PerfConfigWidget::readTracePoints); + + auto addEventButton = new QPushButton(Tr::tr("Add Event"), this); + connect(addEventButton, &QPushButton::pressed, this, [this]() { + auto model = eventsView->model(); + model->insertRow(model->rowCount()); }); + auto removeEventButton = new QPushButton(Tr::tr("Remove Event"), this); + connect(removeEventButton, &QPushButton::pressed, this, [this] { + QModelIndex index = eventsView->currentIndex(); + if (index.isValid()) + eventsView->model()->removeRow(index.row()); + }); + + auto resetButton = new QPushButton(Tr::tr("Reset"), this); + connect(resetButton, &QPushButton::pressed, m_settings, &PerfSettings::resetToDefault); + + using namespace Layouting; + Column { + Row { st, useTracePointsButton, addEventButton, removeEventButton, resetButton }, + + eventsView, + + Grid { + m_settings->callgraphMode, m_settings->stackSize, br, + m_settings->sampleMode, m_settings->period, br, + m_settings->extraArguments, + }, + + st + }.attachTo(this); + + IDevice::ConstPtr device; + if (target) + device = DeviceKitAspect::device(target->kit()); + + if (device.isNull()) { + useTracePointsButton->setEnabled(false); + return; + } + + QTC_ASSERT(device, return); + QTC_CHECK(!m_process || m_process->state() == QProcess::NotRunning); + + m_process.reset(new Process); + m_process->setCommand({device->filePath("perf"), {"probe", "-l"}}); + connect(m_process.get(), &Process::done, + this, &PerfConfigWidget::handleProcessDone); + + useTracePointsButton->setEnabled(true); +} + +void PerfConfigWidget::readTracePoints() +{ + QMessageBox messageBox; + messageBox.setWindowTitle(Tr::tr("Use Trace Points")); + messageBox.setIcon(QMessageBox::Question); + messageBox.setText(Tr::tr("Replace events with trace points read from the device?")); + messageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + if (messageBox.exec() == QMessageBox::Yes) { + m_process->start(); + useTracePointsButton->setEnabled(false); + } +} + +void PerfConfigWidget::handleProcessDone() +{ + if (m_process->error() == QProcess::FailedToStart) { + Core::AsynchronousMessageBox::warning( + Tr::tr("Cannot List Trace Points"), + Tr::tr("\"perf probe -l\" failed to start. Is perf installed?")); + useTracePointsButton->setEnabled(true); + return; + } + const QList<QByteArray> lines = + m_process->readAllRawStandardOutput().append(m_process->readAllRawStandardError()) + .split('\n'); + auto model = eventsView->model(); + const int previousRows = model->rowCount(); + QHash<QByteArray, QByteArray> tracePoints; + for (const QByteArray &line : lines) { + const QByteArray trimmed = line.trimmed(); + const int space = trimmed.indexOf(' '); + if (space < 0) + continue; + + // If the whole "on ..." string is the same, the trace points are redundant + tracePoints[trimmed.mid(space + 1)] = trimmed.left(space); + } + + if (tracePoints.isEmpty()) { + Core::AsynchronousMessageBox::warning( + Tr::tr("No Trace Points Found"), + Tr::tr("Trace points can be defined with \"perf probe -a\".")); + } else { + for (const QByteArray &event : std::as_const(tracePoints)) { + int row = model->rowCount(); + model->insertRow(row); + model->setData(model->index(row, PerfConfigEventsModel::ColumnEventType), + PerfConfigEventsModel::EventTypeCustom); + model->setData(model->index(row, PerfConfigEventsModel::ColumnSubType), + QString::fromUtf8(event)); + } + model->removeRows(0, previousRows); + m_settings->sampleMode.setVolatileValue(1); + m_settings->period.setVolatileValue(1); + } + useTracePointsButton->setEnabled(true); +} + +QWidget *SettingsDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + Q_UNUSED(option) + const int row = index.row(); + const int column = index.column(); + const PerfConfigEventsModel *model = qobject_cast<const PerfConfigEventsModel *>(index.model()); + + auto getRowEventType = [&]() { + return qvariant_cast<PerfConfigEventsModel::EventType>( + model->data(model->index(row, PerfConfigEventsModel::ColumnEventType), + Qt::EditRole)); + }; + + switch (column) { + case PerfConfigEventsModel::ColumnEventType: { + QComboBox *editor = new QComboBox(parent); + QMetaEnum meta = QMetaEnum::fromType<PerfConfigEventsModel::EventType>(); + for (int i = 0; i < PerfConfigEventsModel::EventTypeInvalid; ++i) { + editor->addItem(QString::fromLatin1(meta.valueToKey(i)).mid( + static_cast<int>(strlen("EventType"))).toLower(), i); + } + return editor; + } + case PerfConfigEventsModel::ColumnSubType: { + PerfConfigEventsModel::EventType eventType = getRowEventType(); + switch (eventType) { + case PerfConfigEventsModel::EventTypeHardware: { + QComboBox *editor = new QComboBox(parent); + for (int i = PerfConfigEventsModel::SubTypeEventTypeHardware; + i < PerfConfigEventsModel::SubTypeEventTypeSoftware; ++i) { + editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeHardware, + PerfConfigEventsModel::SubType(i)), i); + } + return editor; + } + case PerfConfigEventsModel::EventTypeSoftware: { + QComboBox *editor = new QComboBox(parent); + for (int i = PerfConfigEventsModel::SubTypeEventTypeSoftware; + i < PerfConfigEventsModel::SubTypeEventTypeCache; ++i) { + editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeSoftware, + PerfConfigEventsModel::SubType(i)), i); + } + return editor; + } + case PerfConfigEventsModel::EventTypeCache: { + QComboBox *editor = new QComboBox(parent); + for (int i = PerfConfigEventsModel::SubTypeEventTypeCache; + i < PerfConfigEventsModel::SubTypeInvalid; ++i) { + editor->addItem(PerfConfigEventsModel::subTypeString(PerfConfigEventsModel::EventTypeCache, + PerfConfigEventsModel::SubType(i)), i); + } + return editor; + } + case PerfConfigEventsModel::EventTypeBreakpoint: { + QLineEdit *editor = new QLineEdit(parent); + editor->setText("0x0000000000000000"); + editor->setValidator(new QRegularExpressionValidator( + QRegularExpression("0x[0-9a-f]{16}"), parent)); + return editor; + } + case PerfConfigEventsModel::EventTypeCustom: { + QLineEdit *editor = new QLineEdit(parent); + return editor; + } + case PerfConfigEventsModel::EventTypeRaw: { + QLineEdit *editor = new QLineEdit(parent); + editor->setText("r000"); + editor->setValidator(new QRegularExpressionValidator( + QRegularExpression("r[0-9a-f]{3}"), parent)); + return editor; + } + case PerfConfigEventsModel::EventTypeInvalid: + return nullptr; + } + return nullptr; // Will never be reached, but GCC cannot figure this out. + } + case PerfConfigEventsModel::ColumnOperation: { + QComboBox *editor = new QComboBox(parent); + PerfConfigEventsModel::EventType eventType = getRowEventType(); + if (eventType == PerfConfigEventsModel::EventTypeCache) { + editor->addItem("load", PerfConfigEventsModel::OperationLoad); + editor->addItem("store", PerfConfigEventsModel::OperationStore); + editor->addItem("prefetch", PerfConfigEventsModel::OperationPrefetch); + } else if (eventType == PerfConfigEventsModel::EventTypeBreakpoint) { + editor->addItem("r", PerfConfigEventsModel::OperationLoad); + editor->addItem("rw", PerfConfigEventsModel::OperationLoad + | PerfConfigEventsModel::OperationStore); + editor->addItem("rwx", PerfConfigEventsModel::OperationLoad + | PerfConfigEventsModel::OperationStore + | PerfConfigEventsModel::OperationExecute); + editor->addItem("rx", PerfConfigEventsModel::OperationLoad + | PerfConfigEventsModel::OperationExecute); + editor->addItem("w", PerfConfigEventsModel::OperationStore); + editor->addItem("wx", PerfConfigEventsModel::OperationStore + | PerfConfigEventsModel::OperationExecute); + editor->addItem("x", PerfConfigEventsModel::OperationExecute); + } else { + editor->setEnabled(false); + } + return editor; + } + case PerfConfigEventsModel::ColumnResult: { + QComboBox *editor = new QComboBox(parent); + PerfConfigEventsModel::EventType eventType = getRowEventType(); + if (eventType != PerfConfigEventsModel::EventTypeCache) { + editor->setEnabled(false); + } else { + editor->addItem("refs", PerfConfigEventsModel::ResultRefs); + editor->addItem("misses", PerfConfigEventsModel::ResultMisses); + } + return editor; + } + default: + return nullptr; + } +} + +void SettingsDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + if (QComboBox *combo = qobject_cast<QComboBox *>(editor)) { + QVariant data = index.model()->data(index, Qt::EditRole); + for (int i = 0, end = combo->count(); i != end; ++i) { + if (combo->itemData(i) == data) { + combo->setCurrentIndex(i); + return; + } + } + } else if (QLineEdit *lineedit = qobject_cast<QLineEdit *>(editor)) { + lineedit->setText(index.model()->data(index, Qt::DisplayRole).toString()); + } +} + +void SettingsDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (QComboBox *combo = qobject_cast<QComboBox *>(editor)) { + model->setData(index, combo->currentData()); + } else if (QLineEdit *lineedit = qobject_cast<QLineEdit *>(editor)) { + QString text = lineedit->text(); + QVariant type = model->data(model->index(index.row(), + PerfConfigEventsModel::ColumnEventType), + Qt::EditRole); + switch (qvariant_cast<PerfConfigEventsModel::EventType>(type)) { + case PerfConfigEventsModel::EventTypeRaw: + model->setData(index, text.mid(static_cast<int>(strlen("r"))).toULongLong(nullptr, 16)); + break; + case PerfConfigEventsModel::EventTypeBreakpoint: + model->setData(index, + text.mid(static_cast<int>(strlen("0x"))).toULongLong(nullptr, 16)); + break; + case PerfConfigEventsModel::EventTypeCustom: + model->setData(index, text); + break; + default: + break; + } + } +} + +// PerfSettingsPage + +PerfSettings &globalSettings() +{ + static PerfSettings theSettings(nullptr); + return theSettings; +} + +PerfSettings::PerfSettings(ProjectExplorer::Target *target) +{ + setAutoApply(false); period.setSettingsKey("Analyzer.Perf.Frequency"); period.setRange(250, 2147483647); period.setDefaultValue(250); @@ -58,11 +405,18 @@ PerfSettings::PerfSettings(ProjectExplorer::Target *target) extraArguments.setLabelText(Tr::tr("Additional arguments:")); extraArguments.setSpan(4); - connect(&callgraphMode, &SelectionAspect::volatileValueChanged, this, [this](int index) { - stackSize.setEnabled(index == 0); + connect(&callgraphMode, &SelectionAspect::volatileValueChanged, this, [this] { + stackSize.setEnabled(callgraphMode.volatileValue() == 0); + }); + + setLayouter([this, target] { + using namespace Layouting; + auto widget = new PerfConfigWidget(this, target); + return Column { widget }; }); readGlobalSettings(); + readSettings(); } PerfSettings::~PerfSettings() @@ -71,13 +425,13 @@ PerfSettings::~PerfSettings() void PerfSettings::readGlobalSettings() { - QVariantMap defaults; + Store defaults; // Read stored values - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String(Constants::AnalyzerSettingsGroupId)); - QVariantMap map = defaults; - for (QVariantMap::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it) + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(Constants::AnalyzerSettingsGroupId); + Store map = defaults; + for (Store::ConstIterator it = defaults.constBegin(); it != defaults.constEnd(); ++it) map.insert(it.key(), settings->value(it.key(), it.value())); settings->endGroup(); @@ -86,11 +440,11 @@ void PerfSettings::readGlobalSettings() void PerfSettings::writeGlobalSettings() const { - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(QLatin1String(Constants::AnalyzerSettingsGroupId)); - QVariantMap map; + QtcSettings *settings = Core::ICore::settings(); + settings->beginGroup(Constants::AnalyzerSettingsGroupId); + Store map; toMap(map); - for (QVariantMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) + for (Store::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) settings->setValue(it.key(), it.value()); settings->endGroup(); } @@ -99,10 +453,10 @@ void PerfSettings::addPerfRecordArguments(CommandLine *cmd) const { QString callgraphArg = callgraphMode.itemValue().toString(); if (callgraphArg == Constants::PerfCallgraphDwarf) - callgraphArg += "," + QString::number(stackSize.value()); + callgraphArg += "," + QString::number(stackSize()); QString events; - for (const QString &event : this->events.value()) { + for (const QString &event : this->events()) { if (!event.isEmpty()) { if (!events.isEmpty()) events.append(','); @@ -113,16 +467,39 @@ void PerfSettings::addPerfRecordArguments(CommandLine *cmd) const cmd->addArgs({"-e", events, "--call-graph", callgraphArg, sampleMode.itemValue().toString(), - QString::number(period.value())}); + QString::number(period())}); cmd->addArgs(extraArguments(), CommandLine::Raw); } void PerfSettings::resetToDefault() { PerfSettings defaults; - QVariantMap map; + Store map; defaults.toMap(map); fromMap(map); } +QWidget *PerfSettings::createPerfConfigWidget(Target *target) +{ + return new PerfConfigWidget(this, target); +} + +// PerfSettingsPage + +class PerfSettingsPage final : public Core::IOptionsPage +{ +public: + PerfSettingsPage() + { + setId(Constants::PerfSettingsId); + setDisplayName(Tr::tr("CPU Usage")); + setCategory("T.Analyzer"); + setDisplayCategory(::Debugger::Tr::tr("Analyzer")); + setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); + setSettingsProvider([] { return &globalSettings(); }); + } +}; + +const PerfSettingsPage settingsPage; + } // namespace PerfProfiler diff --git a/src/plugins/perfprofiler/perfsettings.h b/src/plugins/perfprofiler/perfsettings.h index 7c10706098b..a53557b9c47 100644 --- a/src/plugins/perfprofiler/perfsettings.h +++ b/src/plugins/perfprofiler/perfsettings.h @@ -7,11 +7,9 @@ #include <projectexplorer/runconfiguration.h> -#include <QObject> - namespace PerfProfiler { -class PERFPROFILER_EXPORT PerfSettings final : public ProjectExplorer::ISettingsAspect +class PERFPROFILER_EXPORT PerfSettings final : public Utils::AspectContainer { Q_OBJECT @@ -26,6 +24,8 @@ public: void resetToDefault(); + QWidget *createPerfConfigWidget(ProjectExplorer::Target *target); + Utils::IntegerAspect period{this}; Utils::IntegerAspect stackSize{this}; Utils::SelectionAspect sampleMode{this}; @@ -34,4 +34,6 @@ public: Utils::StringAspect extraArguments{this}; }; +PerfSettings &globalSettings(); + } // namespace PerfProfiler diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp index 3920b526b89..d5cb2e16e3d 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.cpp +++ b/src/plugins/perfprofiler/perftracepointdialog.cpp @@ -5,7 +5,7 @@ #include "perftracepointdialog.h" #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs index 6deee972959..6adaa25ae7e 100644 --- a/src/plugins/plugins.qbs +++ b/src/plugins/plugins.qbs @@ -12,7 +12,6 @@ Project { "bazaar/bazaar.qbs", "beautifier/beautifier.qbs", "bineditor/bineditor.qbs", - "bookmarks/bookmarks.qbs", "boot2qt/boot2qt.qbs", "clangcodemodel/clangcodemodel.qbs", "clangformat/clangformat.qbs", @@ -20,13 +19,13 @@ Project { "classview/classview.qbs", "clearcase/clearcase.qbs", "cmakeprojectmanager/cmakeprojectmanager.qbs", + "compilerexplorer/compilerexplorer.qbs", "mesonprojectmanager/mesonprojectmanager.qbs", "coco/coco.qbs", "compilationdatabaseprojectmanager/compilationdatabaseprojectmanager.qbs", "conan/conan.qbs", "copilot/copilot.qbs", "coreplugin/coreplugin.qbs", - "coreplugin/images/logo/logo.qbs", "cpaster/cpaster.qbs", "cpaster/frontend/frontend.qbs", "cppcheck/cppcheck.qbs", @@ -76,6 +75,7 @@ Project { "remotelinux/remotelinux.qbs", "resourceeditor/resourceeditor.qbs", "saferenderer/saferenderer.qbs", + "screenrecorder/screenrecorder.qbs", "scxmleditor/scxmleditor.qbs", "serialterminal/serialterminal.qbs", "silversearcher/silversearcher.qbs", diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index d16e868a5cc..26c7d39d70a 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -55,9 +55,7 @@ add_qtc_plugin(ProjectExplorer devicesupport/devicemanager.cpp devicesupport/devicemanager.h devicesupport/devicemanagermodel.cpp devicesupport/devicemanagermodel.h devicesupport/deviceprocessesdialog.cpp devicesupport/deviceprocessesdialog.h - devicesupport/deviceprocesslist.cpp devicesupport/deviceprocesslist.h devicesupport/devicesettingspage.cpp devicesupport/devicesettingspage.h - devicesupport/devicesettingswidget.cpp devicesupport/devicesettingswidget.h devicesupport/devicetestdialog.cpp devicesupport/devicetestdialog.h devicesupport/deviceusedportsgatherer.cpp devicesupport/deviceusedportsgatherer.h devicesupport/filetransfer.cpp devicesupport/filetransfer.h @@ -105,12 +103,11 @@ add_qtc_plugin(ProjectExplorer jsonwizard/wizarddebug.h jsonwizard/jsonwizardscannergenerator.cpp jsonwizard/jsonwizardscannergenerator.h kit.cpp kit.h + kitaspects.cpp kitaspects.h kitchooser.cpp kitchooser.h kitfeatureprovider.h - kitinformation.cpp kitinformation.h kitmanager.cpp kitmanager.h kitmanagerconfigwidget.cpp kitmanagerconfigwidget.h - kitmodel.cpp kitmodel.h kitoptionspage.cpp kitoptionspage.h ldparser.cpp ldparser.h lldparser.cpp lldparser.h @@ -126,6 +123,7 @@ add_qtc_plugin(ProjectExplorer processparameters.cpp processparameters.h processstep.cpp processstep.h project.cpp project.h + projectcommentssettings.cpp projectcommentssettings.h projectconfiguration.cpp projectconfiguration.h projectconfigurationmodel.cpp projectconfigurationmodel.h projectexplorer.cpp projectexplorer.h @@ -183,11 +181,6 @@ add_qtc_plugin(ProjectExplorer xcodebuildparser.cpp xcodebuildparser.h ) -extend_qtc_plugin(ProjectExplorer - CONDITION PROJECT_USER_FILE_EXTENSION - DEFINES "PROJECT_USER_FILE_EXTENSION=${PROJECT_USER_FILE_EXTENSION}" -) - if (TARGET clangd) set(CLANG_BINDIR "$<TARGET_FILE_DIR:clangd>") endif() diff --git a/src/plugins/projectexplorer/ProjectExplorer.json.in b/src/plugins/projectexplorer/ProjectExplorer.json.in index c9b54ad441c..b5f30d4242c 100644 --- a/src/plugins/projectexplorer/ProjectExplorer.json.in +++ b/src/plugins/projectexplorer/ProjectExplorer.json.in @@ -1,42 +1,42 @@ { - \"Name\" : \"ProjectExplorer\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ProjectExplorer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Core\", - \"Description\" : \"ProjectExplorer framework that can be extended with different kind of project types.\", - \"Url\" : \"http://www.qt.io\", - \"Arguments\" : [ + "Category" : "Core", + "Description" : "ProjectExplorer framework that can be extended with different kind of project types.", + "Url" : "http://www.qt.io", + "Arguments" : [ { - \"Name\" : \"-customwizard-verbose\", - \"Description\" : \"Verbose loading of custom wizards\" + "Name" : "-customwizard-verbose", + "Description" : "Verbose loading of custom wizards" }, { - \"Name\" : \"-ensure-kit-for-binary\", - \"Parameter\" : \"file path\", - \"Description\" : \"Create kit with architecture matching a given application or library\" + "Name" : "-ensure-kit-for-binary", + "Parameter" : "file path", + "Description" : "Create kit with architecture matching a given application or library" } ], - $$dependencyList, + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-tasklist\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Creator task list file</comment>\", - \" <glob pattern=\'*.tasks\'/>\", - \" <glob pattern=\'*.tasks.txt\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0' encoding='UTF-8'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-tasklist'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Creator task list file</comment>", + " <glob pattern='*.tasks'/>", + " <glob pattern='*.tasks.txt'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index 88efb966599..07365fc5dd0 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -100,6 +100,7 @@ static void setupPreregisteredOsFlavors() { Abi::OS::UnixOS, Abi::OS::QnxOS, Abi::OS::BareMetalOS}); + registerOsFlavor(Abi::PokyFlavor, "poky", {Abi::OS::LinuxOS}); registerOsFlavor(Abi::UnknownFlavor, "unknown", {Abi::OS::BsdOS, Abi::OS::LinuxOS, Abi::OS::DarwinOS, @@ -196,7 +197,7 @@ static Abi macAbiForCpu(quint32 type) { case 0x01000000 + 12: // CPU_TYPE_ARM64 return Abi(Abi::ArmArchitecture, Abi::DarwinOS, Abi::GenericFlavor, Abi::MachOFormat, 64); default: - return Abi(); + return {}; } } @@ -451,7 +452,7 @@ Abi Abi::abiFromTargetTriplet(const QString &triple) { const QString machine = triple.toLower(); if (machine.isEmpty()) - return Abi(); + return {}; const QStringList parts = machine.split(QRegularExpression("[ /-]")); @@ -618,6 +619,9 @@ Abi Abi::abiFromTargetTriplet(const QString &triple) width = 32; } else if (p.startsWith("asmjs")) { arch = AsmJsArchitecture; + } else if (p == "poky") { + os = LinuxOS; + flavor = PokyFlavor; } else if (p == "none") { os = BareMetalOS; flavor = GenericFlavor; @@ -873,7 +877,7 @@ Abi Abi::fromString(const QString &abiString) if (!abiParts.isEmpty()) { architecture = architectureFromString(abiParts.at(0)); if (abiParts.at(0) != toString(architecture)) - return Abi(); + return {}; } Abi::OS os = UnknownOS; diff --git a/src/plugins/projectexplorer/abi.h b/src/plugins/projectexplorer/abi.h index 13fc0191282..7bf808071cc 100644 --- a/src/plugins/projectexplorer/abi.h +++ b/src/plugins/projectexplorer/abi.h @@ -102,6 +102,9 @@ public: RtosFlavor, GenericFlavor, + // Yocto + PokyFlavor, + UnknownFlavor // keep last in this enum! }; diff --git a/src/plugins/projectexplorer/abstractprocessstep.cpp b/src/plugins/projectexplorer/abstractprocessstep.cpp index 806d9159f95..c5516d5dc5b 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.cpp +++ b/src/plugins/projectexplorer/abstractprocessstep.cpp @@ -3,14 +3,11 @@ #include "abstractprocessstep.h" -#include "buildconfiguration.h" -#include "buildstep.h" #include "processparameters.h" #include "projectexplorer.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" -#include <utils/fileutils.h> #include <utils/outputformatter.h> #include <utils/process.h> #include <utils/qtcassert.h> @@ -74,11 +71,7 @@ class AbstractProcessStep::Private public: Private(AbstractProcessStep *q) : q(q) {} - void cleanUp(int exitCode, QProcess::ExitStatus status); - AbstractProcessStep *q; - std::unique_ptr<Process> m_process; - std::unique_ptr<TaskTree> m_taskTree; ProcessParameters m_param; ProcessParameters *m_displayedParams = &m_param; std::function<CommandLine()> m_commandLineProvider; @@ -86,8 +79,8 @@ public: std::function<void(Environment &)> m_environmentModifier; bool m_ignoreReturnValue = false; bool m_lowPriority = false; - std::unique_ptr<QTextDecoder> stdoutStream; - std::unique_ptr<QTextDecoder> stderrStream; + std::unique_ptr<QTextDecoder> stdOutDecoder; + std::unique_ptr<QTextDecoder> stdErrDecoder; OutputFormatter *outputFormatter = nullptr; }; @@ -150,12 +143,12 @@ void AbstractProcessStep::setWorkingDirectoryProvider(const std::function<FilePa bool AbstractProcessStep::init() { - if (d->m_process || d->m_taskTree) - return false; - if (!setupProcessParameters(processParameters())) return false; + d->stdOutDecoder = std::make_unique<QTextDecoder>(buildEnvironment().hasKey("VSLANG") + ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale()); + d->stdErrDecoder = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale()); return true; } @@ -166,98 +159,81 @@ void AbstractProcessStep::setupOutputFormatter(OutputFormatter *formatter) BuildStep::setupOutputFormatter(formatter); } -/*! - Reimplemented from BuildStep::init(). You need to call this from - YourBuildStep::run(). -*/ - -void AbstractProcessStep::doRun() +GroupItem AbstractProcessStep::defaultProcessTask() { - if (!checkWorkingDirectory()) - return; + const auto onSetup = [this](Process &process) { + return setupProcess(process) ? SetupResult::Continue : SetupResult::StopWithError; + }; + const auto onEnd = [this](const Process &process) { handleProcessDone(process); }; + return ProcessTask(onSetup, onEnd, onEnd); +} +bool AbstractProcessStep::setupProcess(Process &process) +{ + const FilePath workingDir = d->m_param.effectiveWorkingDirectory(); + if (!workingDir.exists() && !workingDir.createDir()) { + emit addOutput(Tr::tr("Could not create directory \"%1\"").arg(workingDir.toUserOutput()), + OutputFormat::ErrorMessage); + return false; + } if (!d->m_param.effectiveCommand().isExecutableFile()) { - processStartupFailed(); - return; + emit addOutput(Tr::tr("The program \"%1\" does not exist or is not executable.") + .arg(d->m_displayedParams->effectiveCommand().toUserOutput()), + OutputFormat::ErrorMessage); + return false; } - setupStreams(); - - d->m_process.reset(new Process); - setupProcess(d->m_process.get()); - connect(d->m_process.get(), &Process::done, this, &AbstractProcessStep::handleProcessDone); - d->m_process->start(); -} - -bool AbstractProcessStep::checkWorkingDirectory() -{ - const FilePath wd = d->m_param.effectiveWorkingDirectory(); - if (!wd.exists()) { - if (!wd.createDir()) { - emit addOutput(Tr::tr("Could not create directory \"%1\"").arg(wd.toUserOutput()), - OutputFormat::ErrorMessage); - finish(ProcessResult::StartFailed); - return false; - } - } - return true; -} - -void AbstractProcessStep::setupStreams() -{ - d->stdoutStream = std::make_unique<QTextDecoder>(buildEnvironment().hasKey("VSLANG") - ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale()); - d->stderrStream = std::make_unique<QTextDecoder>(QTextCodec::codecForLocale()); -} - -void AbstractProcessStep::setupProcess(Process *process) -{ - process->setUseCtrlCStub(HostOsInfo::isWindowsHost()); - process->setWorkingDirectory(d->m_param.effectiveWorkingDirectory()); + process.setUseCtrlCStub(HostOsInfo::isWindowsHost()); + process.setWorkingDirectory(workingDir); // Enforce PWD in the environment because some build tools use that. // PWD can be different from getcwd in case of symbolic links (getcwd resolves symlinks). // For example Clang uses PWD for paths in debug info, see QTCREATORBUG-23788 Environment envWithPwd = d->m_param.environment(); - envWithPwd.set("PWD", process->workingDirectory().path()); - process->setEnvironment(envWithPwd); - process->setCommand({d->m_param.effectiveCommand(), d->m_param.effectiveArguments(), + envWithPwd.set("PWD", workingDir.path()); + process.setEnvironment(envWithPwd); + process.setCommand({d->m_param.effectiveCommand(), d->m_param.effectiveArguments(), CommandLine::Raw}); if (d->m_lowPriority && ProjectExplorerPlugin::projectExplorerSettings().lowBuildPriority) - process->setLowPriority(); + process.setLowPriority(); - connect(process, &Process::readyReadStandardOutput, this, [this, process] { - emit addOutput(d->stdoutStream->toUnicode(process->readAllRawStandardOutput()), + connect(&process, &Process::readyReadStandardOutput, this, [this, &process] { + emit addOutput(d->stdOutDecoder->toUnicode(process.readAllRawStandardOutput()), OutputFormat::Stdout, DontAppendNewline); }); - connect(process, &Process::readyReadStandardError, this, [this, process] { - emit addOutput(d->stderrStream->toUnicode(process->readAllRawStandardError()), + connect(&process, &Process::readyReadStandardError, this, [this, &process] { + emit addOutput(d->stdErrDecoder->toUnicode(process.readAllRawStandardError()), OutputFormat::Stderr, DontAppendNewline); }); - connect(process, &Process::started, this, [this] { - ProcessParameters *params = displayedParameters(); + connect(&process, &Process::started, this, [this] { + ProcessParameters *params = d->m_displayedParams; emit addOutput(Tr::tr("Starting: \"%1\" %2") .arg(params->effectiveCommand().toUserOutput(), params->prettyArguments()), OutputFormat::NormalMessage); }); + return true; } -void AbstractProcessStep::runTaskTree(const Group &recipe) +void AbstractProcessStep::handleProcessDone(const Process &process) { - setupStreams(); - - d->m_taskTree.reset(new TaskTree(recipe)); - connect(d->m_taskTree.get(), &TaskTree::progressValueChanged, this, [this](int value) { - emit progress(qRound(double(value) * 100 / std::max(d->m_taskTree->progressMaximum(), 1)), {}); - }); - connect(d->m_taskTree.get(), &TaskTree::done, this, [this] { - emit finished(true); - d->m_taskTree.release()->deleteLater(); - }); - connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, [this] { - emit finished(false); - d->m_taskTree.release()->deleteLater(); - }); - d->m_taskTree->start(); + const QString command = d->m_displayedParams->effectiveCommand().toUserOutput(); + if (process.result() == ProcessResult::FinishedWithSuccess) { + emit addOutput(Tr::tr("The process \"%1\" exited normally.").arg(command), + OutputFormat::NormalMessage); + } else if (process.result() == ProcessResult::FinishedWithError) { + emit addOutput(Tr::tr("The process \"%1\" exited with code %2.") + .arg(command, QString::number(process.exitCode())), + OutputFormat::ErrorMessage); + } else if (process.result() == ProcessResult::StartFailed) { + emit addOutput(Tr::tr("Could not start process \"%1\" %2.") + .arg(command, d->m_displayedParams->prettyArguments()), + OutputFormat::ErrorMessage); + const QString errorString = process.errorString(); + if (!errorString.isEmpty()) + emit addOutput(errorString, OutputFormat::ErrorMessage); + } else { + emit addOutput(Tr::tr("The process \"%1\" crashed.").arg(command), + OutputFormat::ErrorMessage); + } } void AbstractProcessStep::setLowPriority() @@ -265,18 +241,6 @@ void AbstractProcessStep::setLowPriority() d->m_lowPriority = true; } -void AbstractProcessStep::doCancel() -{ - if (d->m_process) { - d->cleanUp(-1, QProcess::CrashExit); - } - if (d->m_taskTree) { - d->m_taskTree.reset(); - emit addOutput(Tr::tr("The build step was ended forcefully."), OutputFormat::ErrorMessage); - emit finished(false); - } -} - ProcessParameters *AbstractProcessStep::processParameters() { return &d->m_param; @@ -312,78 +276,14 @@ bool AbstractProcessStep::setupProcessParameters(ProcessParameters *params) cons return true; } -ProcessParameters *AbstractProcessStep::displayedParameters() const -{ - return d->m_displayedParams; -} - void AbstractProcessStep::setDisplayedParameters(ProcessParameters *params) { d->m_displayedParams = params; } -void AbstractProcessStep::Private::cleanUp(int exitCode, QProcess::ExitStatus status) +GroupItem AbstractProcessStep::runRecipe() { - const QString command = q->displayedParameters()->effectiveCommand().toUserOutput(); - if (status == QProcess::NormalExit && exitCode == 0) { - emit q->addOutput(Tr::tr("The process \"%1\" exited normally.").arg(command), - OutputFormat::NormalMessage); - } else if (status == QProcess::NormalExit) { - emit q->addOutput(Tr::tr("The process \"%1\" exited with code %2.") - .arg(command, QString::number(exitCode)), - OutputFormat::ErrorMessage); - } else { - emit q->addOutput(Tr::tr("The process \"%1\" crashed.").arg(command), - OutputFormat::ErrorMessage); - } - - if (m_process) - m_process.release()->deleteLater(); - const ProcessResult result = (status == QProcess::NormalExit && exitCode == 0 - && !outputFormatter->hasFatalErrors()) - ? ProcessResult::FinishedWithSuccess : ProcessResult::FinishedWithError; - q->finish(result); -} - -/*! - Called if the process could not be started. - - By default, adds a message to the output window. -*/ - -void AbstractProcessStep::processStartupFailed() -{ - ProcessParameters *params = displayedParameters(); - emit addOutput(Tr::tr("Could not start process \"%1\" %2.") - .arg(params->effectiveCommand().toUserOutput(), params->prettyArguments()), - OutputFormat::ErrorMessage); - - const QString err = d->m_process ? d->m_process->errorString() : QString(); - if (!err.isEmpty()) - emit addOutput(err, OutputFormat::ErrorMessage); - finish(ProcessResult::StartFailed); -} - -bool AbstractProcessStep::isSuccess(ProcessResult result) const -{ - return result == ProcessResult::FinishedWithSuccess - || (result == ProcessResult::FinishedWithError && d->m_ignoreReturnValue); -} - -void AbstractProcessStep::finish(ProcessResult result) -{ - emit finished(isSuccess(result)); -} - -void AbstractProcessStep::handleProcessDone() -{ - QTC_ASSERT(d->m_process.get(), return); - if (d->m_process->error() == QProcess::FailedToStart) { - processStartupFailed(); - d->m_process.release()->deleteLater(); - return; - } - d->cleanUp(d->m_process->exitCode(), d->m_process->exitStatus()); + return Group { ignoreReturnValue() ? finishAllAndDone : stopOnError, defaultProcessTask() }; } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/abstractprocessstep.h b/src/plugins/projectexplorer/abstractprocessstep.h index fc1ed131357..ef52e648c00 100644 --- a/src/plugins/projectexplorer/abstractprocessstep.h +++ b/src/plugins/projectexplorer/abstractprocessstep.h @@ -5,16 +5,11 @@ #include "buildstep.h" -#include <QProcess> - namespace Utils { class CommandLine; -enum class ProcessResult; class Process; } -namespace Tasking { class Group; } - namespace ProjectExplorer { class ProcessParameters; @@ -25,11 +20,14 @@ class PROJECTEXPLORER_EXPORT AbstractProcessStep : public BuildStep public: ProcessParameters *processParameters(); - bool setupProcessParameters(ProcessParameters *params) const; +protected: + AbstractProcessStep(BuildStepList *bsl, Utils::Id id); + ~AbstractProcessStep() override; + + bool setupProcessParameters(ProcessParameters *params) const; bool ignoreReturnValue() const; void setIgnoreReturnValue(bool b); - void setCommandLineProvider(const std::function<Utils::CommandLine()> &provider); void setWorkingDirectoryProvider(const std::function<Utils::FilePath()> &provider); void setEnvironmentModifier(const std::function<void(Utils::Environment &)> &modifier); @@ -37,29 +35,17 @@ public: void emitFaultyConfigurationMessage(); -protected: - AbstractProcessStep(BuildStepList *bsl, Utils::Id id); - ~AbstractProcessStep() override; - bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; - void doRun() override; - void doCancel() override; void setLowPriority(); void setDisplayedParameters(ProcessParameters *params); - bool isSuccess(Utils::ProcessResult result) const; - virtual void finish(Utils::ProcessResult result); - - bool checkWorkingDirectory(); - void setupProcess(Utils::Process *process); - void runTaskTree(const Tasking::Group &recipe); - ProcessParameters *displayedParameters() const; + Tasking::GroupItem defaultProcessTask(); + bool setupProcess(Utils::Process &process); + void handleProcessDone(const Utils::Process &process); private: - void setupStreams(); - void processStartupFailed(); - void handleProcessDone(); + Tasking::GroupItem runRecipe() override; class Private; Private *d; diff --git a/src/plugins/projectexplorer/addrunconfigdialog.cpp b/src/plugins/projectexplorer/addrunconfigdialog.cpp index b0b502f9ede..4457bf07003 100644 --- a/src/plugins/projectexplorer/addrunconfigdialog.cpp +++ b/src/plugins/projectexplorer/addrunconfigdialog.cpp @@ -52,7 +52,7 @@ private: } return displayPath.isEmpty() ? Tr::tr("[none]") : displayPath.toUserOutput(); } - return QVariant(); + return {}; } const RunConfigurationCreationInfo m_creationInfo; @@ -95,10 +95,7 @@ private: class CandidatesTreeView : public TreeView { public: - CandidatesTreeView(QWidget *parent) : TreeView(parent) - { - setUniformRowHeights(true); - } + CandidatesTreeView(QWidget *parent) : TreeView(parent) {} private: QSize sizeHint() const override diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index d61b1560523..c2cee55e309 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -11,15 +11,12 @@ #include <coreplugin/editormanager/editormanager.h> -#include <texteditor/texteditor.h> #include <texteditor/textdocument.h> -#include <utils/filesearch.h> #include <utils/algorithm.h> +#include <utils/qtcsettings.h> #include <QGridLayout> -#include <QLabel> -#include <QSettings> using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; @@ -47,17 +44,16 @@ bool AllProjectsFind::isEnabled() const return BaseFileFind::isEnabled() && ProjectManager::hasProjects(); } -FileIterator *AllProjectsFind::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider AllProjectsFind::fileContainerProvider() const { - Q_UNUSED(additionalParameters) - return filesForProjects(nameFilters, exclusionFilters, ProjectManager::projects()); + return [nameFilters = fileNameFilters(), exclusionFilters = fileExclusionFilters()] { + return filesForProjects(nameFilters, exclusionFilters, ProjectManager::projects()); + }; } -FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFilters, +FileContainer AllProjectsFind::filesForProjects(const QStringList &nameFilters, const QStringList &exclusionFilters, - const QList<Project *> &projects) const + const QList<Project *> &projects) { std::function<FilePaths(const FilePaths &)> filterFiles = Utils::filterFilesFunction(nameFilters, exclusionFilters); @@ -77,12 +73,7 @@ FileIterator *AllProjectsFind::filesForProjects(const QStringList &nameFilters, encodings.insert(fileName, codec); } } - return new FileListIterator(encodings.keys(), encodings.values()); -} - -QVariant AllProjectsFind::additionalParameters() const -{ - return QVariant(); + return FileListContainer(encodings.keys(), encodings.values()); } QString AllProjectsFind::label() const @@ -122,16 +113,16 @@ QWidget *AllProjectsFind::createConfigWidget() return m_configWidget; } -void AllProjectsFind::writeSettings(QSettings *settings) +void AllProjectsFind::writeSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("AllProjectsFind")); + settings->beginGroup("AllProjectsFind"); writeCommonSettings(settings); settings->endGroup(); } -void AllProjectsFind::readSettings(QSettings *settings) +void AllProjectsFind::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("AllProjectsFind")); + settings->beginGroup("AllProjectsFind"); readCommonSettings(settings, "*", ""); settings->endGroup(); } diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h index a66ab8d96a9..02dfc2a27ae 100644 --- a/src/plugins/projectexplorer/allprojectsfind.h +++ b/src/plugins/projectexplorer/allprojectsfind.h @@ -26,22 +26,18 @@ public: bool isEnabled() const override; QWidget *createConfigWidget() override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; - Utils::FileIterator *filesForProjects(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QList<Project *> &projects) const; - - QVariant additionalParameters() const override; + static Utils::FileContainer filesForProjects(const QStringList &nameFilters, + const QStringList &exclusionFilters, + const QList<Project *> &projects); QString label() const override; QString toolTip() const override; private: + TextEditor::FileContainerProvider fileContainerProvider() const override; void handleFileListChanged(); QPointer<QWidget> m_configWidget; diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index b8ce9417e70..9bea0e0d842 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -152,6 +152,10 @@ AppOutputPane::AppOutputPane() : Tr::tr("Show the output that generated this issue in Application Output."), Tr::tr("A"))) { + setId("ApplicationOutput"); + setDisplayName(Tr::tr("Application Output")); + setPriorityInStatusBar(60); + ExtensionSystem::PluginManager::addObject(m_handler); setObjectName("AppOutputPane"); // Used in valgrind engine @@ -299,22 +303,12 @@ QWidget *AppOutputPane::outputWidget(QWidget *) return m_tabWidget; } -QList<QWidget*> AppOutputPane::toolBarWidgets() const +QList<QWidget *> AppOutputPane::toolBarWidgets() const { return QList<QWidget *>{m_reRunButton, m_stopButton, m_attachButton, m_settingsButton, m_formatterWidget} + IOutputPane::toolBarWidgets(); } -QString AppOutputPane::displayName() const -{ - return Tr::tr("Application Output"); -} - -int AppOutputPane::priorityInStatusBar() const -{ - return 60; -} - void AppOutputPane::clearContents() { auto *currentWindow = qobject_cast<Core::OutputWindow *>(m_tabWidget->currentWidget()); @@ -380,7 +374,8 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) QTimer::singleShot(0, this, [this, rc] { runControlFinished(rc); }); for (const RunControlTab &t : std::as_const(m_runControlTabs)) { if (t.runControl == rc) { - t.window->flush(); + if (t.window) + t.window->flush(); break; } } @@ -407,7 +402,8 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc) if (tab != m_runControlTabs.end()) { // Reuse this tab if (tab->runControl) - tab->runControl->initiateFinish(); + delete tab->runControl; + tab->runControl = rc; tab->window->reset(); rc->setupFormatter(tab->window->outputFormatter()); @@ -540,8 +536,8 @@ void AppOutputPane::storeSettings() const void AppOutputPane::loadSettings() { - QSettings * const s = Core::ICore::settings(); - const auto modeFromSettings = [s](const QString key, AppOutputPaneMode defaultValue) { + QtcSettings * const s = Core::ICore::settings(); + const auto modeFromSettings = [s](const Key key, AppOutputPaneMode defaultValue) { return static_cast<AppOutputPaneMode>(s->value(key, int(defaultValue)).toInt()); }; m_settings.runOutputMode = modeFromSettings(POP_UP_FOR_RUN_OUTPUT_KEY, kRunOutputModeDefault); @@ -642,10 +638,18 @@ void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode) m_tabWidget->removeTab(tabIndex); delete window; - if (runControl) - runControl->initiateFinish(); // Will self-destruct. - Utils::erase(m_runControlTabs, [tab](const RunControlTab &t) { - return t.runControl == tab->runControl; }); + Utils::erase(m_runControlTabs, [runControl](const RunControlTab &t) { + return t.runControl == runControl; }); + if (runControl) { + if (runControl->isRunning()) { + QMetaObject::invokeMethod(runControl, [runControl] { + runControl->setAutoDeleteOnStop(true); + runControl->initiateStop(); + }, Qt::QueuedConnection); + } else { + delete runControl; + } + } updateCloseActions(); setFilteringEnabled(m_tabWidget->count() > 0); diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index f0f4b56eb90..f7cbb653ff3 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -46,8 +46,6 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; bool canFocus() const override; bool hasFocus() const override; @@ -71,13 +69,12 @@ public: // ApplicationOutput specifics void projectRemoved(); - void appendMessage(ProjectExplorer::RunControl *rc, const QString &out, - Utils::OutputFormat format); - const AppOutputSettings &settings() const { return m_settings; } void setSettings(const AppOutputSettings &settings); private: + void appendMessage(ProjectExplorer::RunControl *rc, const QString &out, + Utils::OutputFormat format); void reRunRunControl(); void stopRunControl(); void attachToRunControl(); diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp index 7cfdb8fa7a5..a108558f360 100644 --- a/src/plugins/projectexplorer/buildaspects.cpp +++ b/src/plugins/projectexplorer/buildaspects.cpp @@ -6,9 +6,8 @@ #include "buildconfiguration.h" #include "buildpropertiessettings.h" #include "devicesupport/idevice.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "projectexplorerconstants.h" -#include "projectexplorer.h" #include "projectexplorertr.h" #include "target.h" @@ -36,30 +35,32 @@ public: QPointer<InfoLabel> problemLabel; }; -BuildDirectoryAspect::BuildDirectoryAspect(const BuildConfiguration *bc) - : d(new Private(bc->target())) +BuildDirectoryAspect::BuildDirectoryAspect(AspectContainer *container, const BuildConfiguration *bc) + : FilePathAspect(container), + d(new Private(bc->target())) { setSettingsKey("ProjectExplorer.BuildConfiguration.BuildDirectory"); setLabelText(Tr::tr("Build directory:")); setExpectedKind(Utils::PathChooser::Directory); - setValidationFunction([this](FancyLineEdit *edit, QString *error) { - const FilePath fixedDir = fixupDir(FilePath::fromUserInput(edit->text())); - if (!fixedDir.isEmpty()) - edit->setText(fixedDir.toUserOutput()); - const FilePath newPath = FilePath::fromUserInput(edit->text()); + setValidationFunction([this](QString text) -> FancyLineEdit::AsyncValidationFuture { + const FilePath fixedDir = fixupDir(FilePath::fromUserInput(text)); + if (!fixedDir.isEmpty()) + text = fixedDir.toUserOutput(); + + const FilePath newPath = FilePath::fromUserInput(text); const auto buildDevice = BuildDeviceKitAspect::device(d->target->kit()); if (buildDevice && buildDevice->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && !buildDevice->rootPath().ensureReachable(newPath)) { - *error = Tr::tr("The build directory is not reachable from the build device."); - return false; + return QtFuture::makeReadyFuture((Utils::expected_str<QString>(make_unexpected( + Tr::tr("The build directory is not reachable from the build device."))))); } - - return pathChooser() ? pathChooser()->defaultValidationFunction()(edit, error) : true; + return pathChooser()->defaultValidationFunction()(text); }); + setOpenTerminalHandler([this, bc] { - Core::FileUtils::openTerminal(FilePath::fromString(value()), bc->environment()); + Core::FileUtils::openTerminal(expandedValue(), bc->environment()); }); } @@ -71,13 +72,13 @@ BuildDirectoryAspect::~BuildDirectoryAspect() void BuildDirectoryAspect::allowInSourceBuilds(const FilePath &sourceDir) { d->sourceDir = sourceDir; - makeCheckable(CheckBoxPlacement::Top, Tr::tr("Shadow build:"), QString()); - setChecked(d->sourceDir != filePath()); + makeCheckable(CheckBoxPlacement::Top, Tr::tr("Shadow build:"), Key()); + setChecked(d->sourceDir != expandedValue()); } bool BuildDirectoryAspect::isShadowBuild() const { - return !d->sourceDir.isEmpty() && d->sourceDir != filePath(); + return !d->sourceDir.isEmpty() && d->sourceDir != expandedValue(); } void BuildDirectoryAspect::setProblem(const QString &description) @@ -86,29 +87,29 @@ void BuildDirectoryAspect::setProblem(const QString &description) updateProblemLabel(); } -void BuildDirectoryAspect::toMap(QVariantMap &map) const +void BuildDirectoryAspect::toMap(Store &map) const { - StringAspect::toMap(map); + FilePathAspect::toMap(map); if (!d->sourceDir.isEmpty()) { - const FilePath shadowDir = isChecked() ? filePath() : d->savedShadowBuildDir; + const FilePath shadowDir = isChecked() ? expandedValue() : d->savedShadowBuildDir; saveToMap(map, shadowDir.toSettings(), QString(), settingsKey() + ".shadowDir"); } } -void BuildDirectoryAspect::fromMap(const QVariantMap &map) +void BuildDirectoryAspect::fromMap(const Store &map) { - StringAspect::fromMap(map); + FilePathAspect::fromMap(map); if (!d->sourceDir.isEmpty()) { d->savedShadowBuildDir = FilePath::fromSettings(map.value(settingsKey() + ".shadowDir")); if (d->savedShadowBuildDir.isEmpty()) - setFilePath(d->sourceDir); - setChecked(d->sourceDir != filePath()); + setValue(d->sourceDir); + setChecked(d->sourceDir != expandedValue()); // FIXME: Check. } } void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent) { - StringAspect::addToLayout(parent); + FilePathAspect::addToLayout(parent); d->problemLabel = new InfoLabel({}, InfoLabel::Warning); d->problemLabel->setElideMode(Qt::ElideNone); parent.addItems({Layouting::br, Layouting::empty, d->problemLabel.data()}); @@ -116,11 +117,11 @@ void BuildDirectoryAspect::addToLayout(Layouting::LayoutItem &parent) if (!d->sourceDir.isEmpty()) { connect(this, &StringAspect::checkedChanged, this, [this] { if (isChecked()) { - setFilePath(d->savedShadowBuildDir.isEmpty() + setValue(d->savedShadowBuildDir.isEmpty() ? d->sourceDir : d->savedShadowBuildDir); } else { - d->savedShadowBuildDir = filePath(); - setFilePath(d->sourceDir); + d->savedShadowBuildDir = expandedValue(); // FIXME: Check. + setValue(d->sourceDir); } }); } @@ -161,11 +162,12 @@ void BuildDirectoryAspect::updateProblemLabel() d->problemLabel->setVisible(!d->problem.isEmpty()); } -SeparateDebugInfoAspect::SeparateDebugInfoAspect() +SeparateDebugInfoAspect::SeparateDebugInfoAspect(AspectContainer *container) + : TriStateAspect(container) { setDisplayName(Tr::tr("Separate debug info:")); setSettingsKey("SeparateDebugInfo"); - setValue(ProjectExplorerPlugin::buildPropertiesSettings().separateDebugInfo.value()); + setValue(buildPropertiesSettings().separateDebugInfo()); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildaspects.h b/src/plugins/projectexplorer/buildaspects.h index bcc8987cd0c..f159cd9d37b 100644 --- a/src/plugins/projectexplorer/buildaspects.h +++ b/src/plugins/projectexplorer/buildaspects.h @@ -14,8 +14,9 @@ class BuildConfiguration; class PROJECTEXPLORER_EXPORT BuildDirectoryAspect : public Utils::FilePathAspect { Q_OBJECT + public: - explicit BuildDirectoryAspect(const BuildConfiguration *bc); + explicit BuildDirectoryAspect(Utils::AspectContainer *container, const BuildConfiguration *bc); ~BuildDirectoryAspect() override; void allowInSourceBuilds(const Utils::FilePath &sourceDir); @@ -27,8 +28,8 @@ public: static Utils::FilePath fixupDir(const Utils::FilePath &dir); private: - void toMap(QVariantMap &map) const override; - void fromMap(const QVariantMap &map) override; + void toMap(Utils::Store &map) const override; + void fromMap(const Utils::Store &map) override; void updateProblemLabel(); @@ -40,7 +41,7 @@ class PROJECTEXPLORER_EXPORT SeparateDebugInfoAspect : public Utils::TriStateAsp { Q_OBJECT public: - SeparateDebugInfoAspect(); + SeparateDebugInfoAspect(Utils::AspectContainer *container = nullptr); }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 70d5a348c1c..f189cf0a9b9 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -5,13 +5,14 @@ #include "buildaspects.h" #include "buildinfo.h" +#include "buildpropertiessettings.h" #include "buildsteplist.h" #include "buildstepspage.h" #include "buildsystem.h" #include "customparser.h" #include "environmentwidget.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "namedwidget.h" #include "projectexplorerconstants.h" #include "projectexplorer.h" @@ -114,7 +115,7 @@ public: const auto selectionWidget = new CustomParsersSelectionWidget(this); layout->addWidget(selectionWidget); - connect(selectionWidget, &CustomParsersSelectionWidget::selectionChanged, + connect(selectionWidget, &CustomParsersSelectionWidget::selectionChanged, this, [selectionWidget, bc] { bc->setCustomParsers(selectionWidget->selectedParsers()); }); @@ -127,16 +128,18 @@ class BuildConfigurationPrivate { public: BuildConfigurationPrivate(BuildConfiguration *bc) - : m_buildSteps(bc, Constants::BUILDSTEPS_BUILD), - m_cleanSteps(bc, Constants::BUILDSTEPS_CLEAN) + : m_buildSteps(bc, Constants::BUILDSTEPS_BUILD) + , m_cleanSteps(bc, Constants::BUILDSTEPS_CLEAN) + , m_buildDirectoryAspect(bc, bc) + , m_tooltipAspect(bc) {} bool m_clearSystemEnvironment = false; EnvironmentItems m_userEnvironmentChanges; BuildStepList m_buildSteps; BuildStepList m_cleanSteps; - BuildDirectoryAspect *m_buildDirectoryAspect = nullptr; - StringAspect *m_tooltipAspect = nullptr; + BuildDirectoryAspect m_buildDirectoryAspect; + StringAspect m_tooltipAspect; FilePath m_lastEmittedBuildDirectory; mutable Environment m_cachedEnvironment; QString m_configWidgetDisplayName; @@ -155,10 +158,9 @@ public: } // Internal BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id) - : ProjectConfiguration(target, id), d(new Internal::BuildConfigurationPrivate(this)) + : ProjectConfiguration(target, id) + , d(new Internal::BuildConfigurationPrivate(this)) { - QTC_CHECK(target && target == this->target()); - MacroExpander *expander = macroExpander(); expander->setDisplayName(Tr::tr("Build Settings")); expander->setAccumulating(true); @@ -190,24 +192,22 @@ BuildConfiguration::BuildConfiguration(Target *target, Utils::Id id) connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &BuildConfiguration::updateCacheAndEmitEnvironmentChanged); - d->m_buildDirectoryAspect = addAspect<BuildDirectoryAspect>(this); - d->m_buildDirectoryAspect->setBaseFileName(target->project()->projectDirectory()); - d->m_buildDirectoryAspect->setEnvironment(environment()); - d->m_buildDirectoryAspect->setMacroExpanderProvider([this] { return macroExpander(); }); - connect(d->m_buildDirectoryAspect, &StringAspect::changed, + d->m_buildDirectoryAspect.setBaseFileName(target->project()->projectDirectory()); + d->m_buildDirectoryAspect.setEnvironment(environment()); + d->m_buildDirectoryAspect.setMacroExpanderProvider([this] { return macroExpander(); }); + connect(&d->m_buildDirectoryAspect, &StringAspect::changed, this, &BuildConfiguration::emitBuildDirectoryChanged); connect(this, &BuildConfiguration::environmentChanged, this, [this] { - d->m_buildDirectoryAspect->setEnvironment(environment()); + d->m_buildDirectoryAspect.setEnvironment(environment()); emit this->target()->buildEnvironmentChanged(this); }); - d->m_tooltipAspect = addAspect<StringAspect>(); - d->m_tooltipAspect->setLabelText(Tr::tr("Tooltip in target selector:")); - d->m_tooltipAspect->setToolTip(Tr::tr("Appears as a tooltip when hovering the build configuration")); - d->m_tooltipAspect->setDisplayStyle(StringAspect::LineEditDisplay); - d->m_tooltipAspect->setSettingsKey("ProjectExplorer.BuildConfiguration.Tooltip"); - connect(d->m_tooltipAspect, &StringAspect::changed, this, [this] { - setToolTip(d->m_tooltipAspect->value()); + d->m_tooltipAspect.setLabelText(Tr::tr("Tooltip in target selector:")); + d->m_tooltipAspect.setToolTip(Tr::tr("Appears as a tooltip when hovering the build configuration")); + d->m_tooltipAspect.setDisplayStyle(StringAspect::LineEditDisplay); + d->m_tooltipAspect.setSettingsKey("ProjectExplorer.BuildConfiguration.Tooltip"); + connect(&d->m_tooltipAspect, &StringAspect::changed, this, [this] { + setToolTip(d->m_tooltipAspect()); }); connect(target, &Target::parsingStarted, this, &BuildConfiguration::enabledChanged); @@ -228,7 +228,10 @@ BuildConfiguration::~BuildConfiguration() FilePath BuildConfiguration::buildDirectory() const { FilePath path = FilePath::fromUserInput( - environment().expandVariables(d->m_buildDirectoryAspect->value().trimmed())); + environment().expandVariables(d->m_buildDirectoryAspect.value().trimmed())); + // FIXME: If the macro expander is expected to be able to do some + // structual changes, the fromUserInput() above might already have + // mis-parsed. Should this here be encapsulated in the FilePathAspect? path = macroExpander()->expand(path); path = path.cleanPath(); @@ -239,17 +242,17 @@ FilePath BuildConfiguration::buildDirectory() const FilePath BuildConfiguration::rawBuildDirectory() const { - return d->m_buildDirectoryAspect->filePath(); + return d->m_buildDirectoryAspect(); } void BuildConfiguration::setBuildDirectory(const FilePath &dir) { - if (dir == d->m_buildDirectoryAspect->filePath()) + if (dir == d->m_buildDirectoryAspect()) return; - d->m_buildDirectoryAspect->setFilePath(dir); + d->m_buildDirectoryAspect.setValue(dir); const FilePath fixedDir = BuildDirectoryAspect::fixupDir(buildDirectory()); if (!fixedDir.isEmpty()) - d->m_buildDirectoryAspect->setFilePath(fixedDir); + d->m_buildDirectoryAspect.setValue(fixedDir); emitBuildDirectoryChanged(); } @@ -293,7 +296,7 @@ MacroExpander *BuildConfiguration::macroExpander() const bool BuildConfiguration::createBuildDirectory() { - const bool result = buildDirectory().ensureWritableDir(); + const bool result = buildDirectory().ensureWritableDir().has_value(); buildDirectoryAspect()->validateInput(); return result; } @@ -369,44 +372,41 @@ void BuildConfiguration::appendInitialCleanStep(Utils::Id id) d->m_initialCleanSteps.append(id); } -QVariantMap BuildConfiguration::toMap() const +void BuildConfiguration::toMap(Store &map) const { - QVariantMap map = ProjectConfiguration::toMap(); + ProjectConfiguration::toMap(map); - map.insert(QLatin1String(Constants::CLEAR_SYSTEM_ENVIRONMENT_KEY), d->m_clearSystemEnvironment); - map.insert(QLatin1String(Constants::USER_ENVIRONMENT_CHANGES_KEY), + map.insert(Constants::CLEAR_SYSTEM_ENVIRONMENT_KEY, d->m_clearSystemEnvironment); + map.insert(Constants::USER_ENVIRONMENT_CHANGES_KEY, EnvironmentItem::toStringList(d->m_userEnvironmentChanges)); - map.insert(QLatin1String(BUILD_STEP_LIST_COUNT), 2); - map.insert(QLatin1String(BUILD_STEP_LIST_PREFIX) + QString::number(0), d->m_buildSteps.toMap()); - map.insert(QLatin1String(BUILD_STEP_LIST_PREFIX) + QString::number(1), d->m_cleanSteps.toMap()); + map.insert(BUILD_STEP_LIST_COUNT, 2); + map.insert(numberedKey(BUILD_STEP_LIST_PREFIX, 0), variantFromStore(d->m_buildSteps.toMap())); + map.insert(numberedKey(BUILD_STEP_LIST_PREFIX, 1), variantFromStore(d->m_cleanSteps.toMap())); map.insert(PARSE_STD_OUT_KEY, d->m_parseStdOut); - map.insert(CUSTOM_PARSERS_KEY, transform(d->m_customParsers,&Utils::Id::toSetting)); - - return map; + map.insert(CUSTOM_PARSERS_KEY, transform(d->m_customParsers, &Id::toSetting)); } -bool BuildConfiguration::fromMap(const QVariantMap &map) +void BuildConfiguration::fromMap(const Store &map) { - d->m_clearSystemEnvironment = map.value(QLatin1String(Constants::CLEAR_SYSTEM_ENVIRONMENT_KEY)) - .toBool(); + d->m_clearSystemEnvironment = map.value(Constants::CLEAR_SYSTEM_ENVIRONMENT_KEY).toBool(); d->m_userEnvironmentChanges = EnvironmentItem::fromStringList( - map.value(QLatin1String(Constants::USER_ENVIRONMENT_CHANGES_KEY)).toStringList()); + map.value(Constants::USER_ENVIRONMENT_CHANGES_KEY).toStringList()); updateCacheAndEmitEnvironmentChanged(); d->m_buildSteps.clear(); d->m_cleanSteps.clear(); - int maxI = map.value(QLatin1String(BUILD_STEP_LIST_COUNT), 0).toInt(); + int maxI = map.value(BUILD_STEP_LIST_COUNT, 0).toInt(); for (int i = 0; i < maxI; ++i) { - QVariantMap data = map.value(QLatin1String(BUILD_STEP_LIST_PREFIX) + QString::number(i)).toMap(); + Store data = storeFromVariant(map.value(numberedKey(BUILD_STEP_LIST_PREFIX, i))); if (data.isEmpty()) { qWarning() << "No data for build step list" << i << "found!"; continue; } - Utils::Id id = idFromMap(data); + Id id = idFromMap(data); if (id == Constants::BUILDSTEPS_BUILD) { if (!d->m_buildSteps.fromMap(data)) qWarning() << "Failed to restore build step list"; @@ -419,11 +419,10 @@ bool BuildConfiguration::fromMap(const QVariantMap &map) } d->m_parseStdOut = map.value(PARSE_STD_OUT_KEY).toBool(); - d->m_customParsers = transform(map.value(CUSTOM_PARSERS_KEY).toList(), &Utils::Id::fromSetting); + d->m_customParsers = transform(map.value(CUSTOM_PARSERS_KEY).toList(), &Id::fromSetting); - const bool res = ProjectConfiguration::fromMap(map); - setToolTip(d->m_tooltipAspect->value()); - return res; + ProjectConfiguration::fromMap(map); + setToolTip(d->m_tooltipAspect()); } void BuildConfiguration::updateCacheAndEmitEnvironmentChanged() @@ -446,7 +445,7 @@ void BuildConfiguration::emitBuildDirectoryChanged() ProjectExplorer::BuildDirectoryAspect *BuildConfiguration::buildDirectoryAspect() const { - return d->m_buildDirectoryAspect; + return &d->m_buildDirectoryAspect; } void BuildConfiguration::setConfigWidgetDisplayName(const QString &display) @@ -454,9 +453,9 @@ void BuildConfiguration::setConfigWidgetDisplayName(const QString &display) d->m_configWidgetDisplayName = display; } -void BuildConfiguration::setBuildDirectoryHistoryCompleter(const QString &history) +void BuildConfiguration::setBuildDirectoryHistoryCompleter(const Key &history) { - d->m_buildDirectoryAspect->setHistoryCompleter(history); + d->m_buildDirectoryAspect.setHistoryCompleter(history); } void BuildConfiguration::setConfigWidgetHasFrame(bool configWidgetHasFrame) @@ -464,9 +463,9 @@ void BuildConfiguration::setConfigWidgetHasFrame(bool configWidgetHasFrame) d->m_configWidgetHasFrame = configWidgetHasFrame; } -void BuildConfiguration::setBuildDirectorySettingsKey(const QString &key) +void BuildConfiguration::setBuildDirectorySettingsKey(const Key &key) { - d->m_buildDirectoryAspect->setSettingsKey(key); + d->m_buildDirectoryAspect.setSettingsKey(key); } Environment BuildConfiguration::baseEnvironment() const @@ -546,9 +545,8 @@ bool BuildConfiguration::isEnabled() const QString BuildConfiguration::disabledReason() const { - if (!buildSystem()->hasParsingData()) - return (Tr::tr("The project was not parsed successfully.")); - return QString(); + return buildSystem()->hasParsingData() ? QString() + : Tr::tr("The project was not parsed successfully."); } bool BuildConfiguration::regenerateBuildFiles(Node *node) @@ -619,7 +617,7 @@ FilePath BuildConfiguration::buildDirectoryFromTemplate(const FilePath &projectD [buildType] { return buildTypeName(buildType); }); exp.registerSubProvider([kit] { return kit->macroExpander(); }); - FilePath buildDir = FilePath::fromUserInput(ProjectExplorerPlugin::buildDirectoryTemplate()); + FilePath buildDir = FilePath::fromUserInput(buildPropertiesSettings().buildDirectoryTemplate()); qCDebug(bcLog) << "build dir template:" << buildDir.toUserOutput(); buildDir = exp.expand(buildDir); qCDebug(bcLog) << "expanded build:" << buildDir.toUserOutput(); @@ -771,7 +769,7 @@ BuildConfiguration *BuildConfigurationFactory::create(Target *parent, const Buil return bc; } -BuildConfiguration *BuildConfigurationFactory::restore(Target *parent, const QVariantMap &map) +BuildConfiguration *BuildConfigurationFactory::restore(Target *parent, const Store &map) { const Utils::Id id = idFromMap(map); for (BuildConfigurationFactory *factory : std::as_const(g_buildConfigurationFactories)) { @@ -782,7 +780,8 @@ BuildConfiguration *BuildConfigurationFactory::restore(Target *parent, const QVa continue; BuildConfiguration *bc = factory->m_creator(parent); QTC_ASSERT(bc, return nullptr); - if (!bc->fromMap(map)) { + bc->fromMap(map); + if (bc->hasError()) { delete bc; bc = nullptr; } @@ -792,9 +791,11 @@ BuildConfiguration *BuildConfigurationFactory::restore(Target *parent, const QVa } BuildConfiguration *BuildConfigurationFactory::clone(Target *parent, - const BuildConfiguration *source) + const BuildConfiguration *source) { - return restore(parent, source->toMap()); + Store map; + source->toMap(map); + return restore(parent, map); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index 7d2a779bbda..0b38b3b3a82 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -68,8 +68,8 @@ public: void appendInitialBuildStep(Utils::Id id); void appendInitialCleanStep(Utils::Id id); - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; bool isEnabled() const; QString disabledReason() const; @@ -102,9 +102,9 @@ public: ProjectExplorer::BuildDirectoryAspect *buildDirectoryAspect() const; void setConfigWidgetDisplayName(const QString &display); - void setBuildDirectoryHistoryCompleter(const QString &history); + void setBuildDirectoryHistoryCompleter(const Utils::Key &history); void setConfigWidgetHasFrame(bool configWidgetHasFrame); - void setBuildDirectorySettingsKey(const QString &key); + void setBuildDirectorySettingsKey(const Utils::Key &key); void addConfigWidgets(const std::function<void (NamedWidget *)> &adder); @@ -149,7 +149,7 @@ public: BuildConfiguration *create(Target *parent, const BuildInfo &info) const; - static BuildConfiguration *restore(Target *parent, const QVariantMap &map); + static BuildConfiguration *restore(Target *parent, const Utils::Store &map); static BuildConfiguration *clone(Target *parent, const BuildConfiguration *source); static BuildConfigurationFactory *find(const Kit *k, const Utils::FilePath &projectPath); diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 888cc7f14df..6c74b5cf828 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -10,7 +10,7 @@ #include "deployconfiguration.h" #include "devicesupport/devicemanager.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -30,6 +30,8 @@ #include <extensionsystem/pluginmanager.h> +#include <solutions/tasking/tasktree.h> + #include <utils/algorithm.h> #include <utils/outputformatter.h> #include <utils/stringutils.h> @@ -46,11 +48,38 @@ #include <QTimer> using namespace Core; +using namespace Tasking; using namespace Utils; namespace ProjectExplorer { using namespace Internal; +class ParserAwaiterTaskAdapter : public TaskAdapter<QSet<BuildSystem *>> +{ +private: + void start() final { checkParsing(); } + void checkParsing() { + const QSet<BuildSystem *> buildSystems = *task(); + for (BuildSystem *buildSystem : buildSystems) { + if (!buildSystem || !buildSystem->isParsing()) + continue; + connect(buildSystem, &BuildSystem::parsingFinished, + this, [this, buildSystem](bool success) { + disconnect(buildSystem, &BuildSystem::parsingFinished, this, nullptr); + if (!success) { + emit done(false); + return; + } + checkParsing(); + }); + return; + } + emit done(true); + } +}; + +using ParserAwaiterTask = CustomTask<ParserAwaiterTaskAdapter>; + static QString msgProgress(int progress, int total) { return Tr::tr("Finished %1 of %n steps", nullptr, total).arg(progress); @@ -77,7 +106,8 @@ static const QList<BuildConfiguration *> buildConfigsForSelection(const Target * } static int queue(const QList<Project *> &projects, const QList<Id> &stepIds, - ConfigSelection configSelection, const RunConfiguration *forRunConfig = nullptr) + ConfigSelection configSelection, const RunConfiguration *forRunConfig = nullptr, + RunControl *starter = nullptr) { if (!ProjectExplorerPlugin::saveModifiedFiles()) return -1; @@ -88,7 +118,10 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds, StopBeforeBuild stopCondition = settings.stopBeforeBuild; if (stopCondition == StopBeforeBuild::SameApp && !forRunConfig) stopCondition = StopBeforeBuild::SameBuildDir; - const auto isStoppableRc = [&projects, stopCondition, configSelection, forRunConfig](RunControl *rc) { + const auto isStoppableRc = [&projects, stopCondition, configSelection, forRunConfig, + starter](RunControl *rc) { + if (rc == starter) + return false; if (!rc->isRunning()) return false; @@ -213,6 +246,13 @@ static int queue(const QList<Project *> &projects, const QList<Id> &stepIds, return stepLists.count(); } +class BuildItem +{ +public: + BuildStep *buildStep = nullptr; + bool enabled = true; + QString name; +}; class BuildManagerPrivate { @@ -220,33 +260,23 @@ public: Internal::CompileOutputWindow *m_outputWindow = nullptr; Internal::TaskWindow *m_taskWindow = nullptr; - QMetaObject::Connection m_scheduledBuild; - QList<BuildStep *> m_buildQueue; - QList<bool> m_enabledState; - QStringList m_stepNames; + QList<BuildItem> m_pendingQueue; + QList<BuildItem> m_buildQueue; int m_progress = 0; int m_maxProgress = 0; bool m_poppedUpTaskWindow = false; - bool m_running = false; bool m_isDeploying = false; - // is set to true while canceling, so that nextBuildStep knows that the BuildStep finished because of canceling - bool m_skipDisabled = false; - bool m_canceling = false; - bool m_lastStepSucceeded = true; - bool m_allStepsSucceeded = true; - BuildStep *m_currentBuildStep = nullptr; - QString m_currentConfiguration; // used to decide if we are building a project to decide when to emit buildStateChanged(Project *) QHash<Project *, int> m_activeBuildSteps; QHash<Target *, int> m_activeBuildStepsPerTarget; QHash<ProjectConfiguration *, int> m_activeBuildStepsPerProjectConfiguration; - Project *m_previousBuildStepProject = nullptr; // Progress reporting to the progress manager QFutureInterface<void> *m_progressFutureInterface = nullptr; QFutureWatcher<void> m_progressWatcher; QPointer<FutureProgress> m_futureProgress; + std::unique_ptr<TaskTree> m_taskTree; QElapsedTimer m_elapsed; }; @@ -288,18 +318,28 @@ BuildManager *BuildManager::instance() void BuildManager::extensionsInitialized() { - TaskHub::addCategory(Constants::TASK_CATEGORY_COMPILE, - Tr::tr("Compile", "Category for compiler issues listed under 'Issues'"), - true, 100); - TaskHub::addCategory(Constants::TASK_CATEGORY_BUILDSYSTEM, - Tr::tr("Build System", "Category for build system issues listed under 'Issues'"), - true, 100); - TaskHub::addCategory(Constants::TASK_CATEGORY_DEPLOYMENT, - Tr::tr("Deployment", "Category for deployment issues listed under 'Issues'"), - true, 100); - TaskHub::addCategory(Constants::TASK_CATEGORY_AUTOTEST, - Tr::tr("Autotests", "Category for autotest issues listed under 'Issues'"), - true, 100); + TaskHub::addCategory({Constants::TASK_CATEGORY_COMPILE, + Tr::tr("Compile", "Category for compiler issues listed under 'Issues'"), + Tr::tr("Issues parsed from the compile output."), + true, + 100}); + TaskHub::addCategory( + {Constants::TASK_CATEGORY_BUILDSYSTEM, + Tr::tr("Build System", "Category for build system issues listed under 'Issues'"), + Tr::tr("Issues from the build system, such as CMake or qmake."), + true, + 100}); + TaskHub::addCategory( + {Constants::TASK_CATEGORY_DEPLOYMENT, + Tr::tr("Deployment", "Category for deployment issues listed under 'Issues'"), + Tr::tr("Issues found when deploying applications to devices."), + true, + 100}); + TaskHub::addCategory({Constants::TASK_CATEGORY_AUTOTEST, + Tr::tr("Autotests", "Category for autotest issues listed under 'Issues'"), + Tr::tr("Issues found when running tests."), + true, + 100}); } void BuildManager::buildProjectWithoutDependencies(Project *project) @@ -318,10 +358,11 @@ void BuildManager::rebuildProjectWithoutDependencies(Project *project) ConfigSelection::Active); } -void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection) +void BuildManager::buildProjectWithDependencies(Project *project, ConfigSelection configSelection, + RunControl *starter) { queue(ProjectManager::projectOrder(project), {Id(Constants::BUILDSTEPS_BUILD)}, - configSelection); + configSelection, nullptr, starter); } void BuildManager::cleanProjectWithDependencies(Project *project, ConfigSelection configSelection) @@ -427,7 +468,7 @@ void BuildManager::aboutToRemoveProject(Project *p) bool BuildManager::isBuilding() { // we are building even if we are not running yet - return !d->m_buildQueue.isEmpty() || d->m_running; + return !d->m_pendingQueue.isEmpty() || !d->m_buildQueue.isEmpty(); } bool BuildManager::isDeploying() @@ -458,20 +499,51 @@ QString BuildManager::displayNameForStepId(Id stepId) return Tr::tr("Build"); } +void BuildManager::cleanupBuild() +{ + const QList<BuildItem> buildQueue = d->m_buildQueue; + d->m_buildQueue.clear(); + for (const BuildItem &item : buildQueue) { + decrementActiveBuildSteps(item.buildStep); + disconnect(item.buildStep, nullptr, m_instance, nullptr); + } + if (d->m_progressFutureInterface) { + d->m_progressFutureInterface->reportFinished(); + d->m_progressWatcher.setFuture(QFuture<void>()); + delete d->m_progressFutureInterface; + d->m_progressFutureInterface = nullptr; + } + d->m_progress = 0; + d->m_maxProgress = 0; + d->m_futureProgress = nullptr; +} + void BuildManager::cancel() { - if (d->m_scheduledBuild) { - disconnect(d->m_scheduledBuild); - d->m_scheduledBuild = {}; - clearBuildQueue(); + if (!d->m_taskTree) return; + + d->m_taskTree.reset(); + + const QList<BuildItem> pendingQueue = d->m_pendingQueue; + d->m_pendingQueue.clear(); + for (const BuildItem &item : pendingQueue) { + decrementActiveBuildSteps(item.buildStep); + disconnect(item.buildStep, nullptr, m_instance, nullptr); } - if (d->m_running) { - if (d->m_canceling) - return; - d->m_canceling = true; - d->m_currentBuildStep->cancel(); + + d->m_poppedUpTaskWindow = false; + d->m_isDeploying = false; + + if (d->m_progressFutureInterface) { + d->m_progressFutureInterface->setProgressValueAndText(100 * d->m_progress, + Tr::tr("Build/Deployment canceled")); + d->m_progressFutureInterface->reportCanceled(); } + cleanupBuild(); + + addToOutputWindow(Tr::tr("Canceled build/deployment."), BuildStep::OutputFormat::ErrorMessage); + emit m_instance->buildQueueFinished(false); } void BuildManager::updateTaskCount() @@ -493,41 +565,6 @@ void BuildManager::finish() QApplication::alert(ICore::dialogParent(), 3000); } -void BuildManager::emitCancelMessage() -{ - addToOutputWindow(Tr::tr("Canceled build/deployment."), BuildStep::OutputFormat::ErrorMessage); -} - -void BuildManager::clearBuildQueue() -{ - for (BuildStep *bs : std::as_const(d->m_buildQueue)) { - decrementActiveBuildSteps(bs); - disconnectOutput(bs); - } - - d->m_stepNames.clear(); - d->m_buildQueue.clear(); - d->m_enabledState.clear(); - d->m_running = false; - d->m_poppedUpTaskWindow = false; - d->m_isDeploying = false; - d->m_previousBuildStepProject = nullptr; - d->m_currentBuildStep = nullptr; - - if (d->m_progressFutureInterface) { - d->m_progressFutureInterface->reportCanceled(); - d->m_progressFutureInterface->reportFinished(); - d->m_progressWatcher.setFuture(QFuture<void>()); - delete d->m_progressFutureInterface; - d->m_progressFutureInterface = nullptr; - } - d->m_futureProgress = nullptr; - d->m_maxProgress = 0; - - emit m_instance->buildQueueFinished(false); -} - - void BuildManager::toggleOutputWindow() { d->m_outputWindow->toggle(IOutputPane::ModeSwitch | IOutputPane::WithFocus); @@ -554,58 +591,139 @@ bool BuildManager::tasksAvailable() void BuildManager::startBuildQueue() { - if (d->m_buildQueue.isEmpty()) { - emit m_instance->buildQueueFinished(true); - return; - } + if (compileOutputSettings().popUp()) + d->m_outputWindow->popup(IOutputPane::NoModeSwitch); - // Delay if any of the involved build systems are currently parsing. - const auto buildSystems = transform<QSet<BuildSystem *>>(d->m_buildQueue, - [](const BuildStep *bs) { return bs->buildSystem(); }); - for (const BuildSystem * const bs : buildSystems) { - if (!bs || !bs->isParsing()) + const auto onAwaiterSetup = [](QSet<BuildSystem *> &buildSystems) { + // Delay if any of the involved build systems are currently parsing. + buildSystems = transform<QSet<BuildSystem *>>( + d->m_buildQueue, [](const BuildItem &item) { return item.buildStep->buildSystem(); }); + if (d->m_futureProgress && !d->m_buildQueue.isEmpty()) + d->m_futureProgress.data()->setTitle(d->m_buildQueue.first().name); + }; + + const GroupItem abortPolicy + = ProjectExplorerPlugin::projectExplorerSettings().abortBuildAllOnError + ? stopOnError : continueOnError; + + QList<GroupItem> topLevel { abortPolicy, ParserAwaiterTask(onAwaiterSetup) }; + Project *lastProject = nullptr; + Target *lastTarget = nullptr; + QList<GroupItem> targetTasks; + d->m_progress = 0; + d->m_maxProgress = 0; + + for (const BuildItem &item : std::as_const(d->m_buildQueue)) { + BuildStep *buildStep = item.buildStep; + Target *target = buildStep->target(); + if (lastTarget != target) { + if (!targetTasks.isEmpty()) + topLevel.append(Group(targetTasks)); + targetTasks.clear(); + lastTarget = target; + } + + Project *project = buildStep->project(); + if (lastProject != project) { + targetTasks.append(Sync([projectName = buildStep->project()->displayName()] { + addToOutputWindow(Tr::tr("Running steps for project %1...") + .arg(projectName), BuildStep::OutputFormat::NormalMessage); + })); + lastProject = project; + } + + if (!item.enabled) { + targetTasks.append(Sync([name = buildStep->displayName()] { + addToOutputWindow(Tr::tr("Skipping disabled step %1.") + .arg(name), BuildStep::OutputFormat::NormalMessage); + })); continue; - d->m_scheduledBuild = QObject::connect(bs, &BuildSystem::parsingFinished, - BuildManager::instance(), - [](bool parsingSuccess) { - if (!d->m_scheduledBuild) - return; - QObject::disconnect(d->m_scheduledBuild); - d->m_scheduledBuild = {}; - if (parsingSuccess) - startBuildQueue(); - else - clearBuildQueue(); - }, Qt::QueuedConnection); - return; - } + } + ++d->m_maxProgress; - if (!d->m_running) { - d->m_elapsed.start(); - // Progress Reporting - d->m_progressFutureInterface = new QFutureInterface<void>; - d->m_progressWatcher.setFuture(d->m_progressFutureInterface->future()); - ProgressManager::setApplicationLabel(QString()); - d->m_futureProgress = ProgressManager::addTask(d->m_progressFutureInterface->future(), - QString(), "ProjectExplorer.Task.Build", - ProgressManager::KeepOnFinish | ProgressManager::ShowInApplicationIcon); - connect(d->m_futureProgress.data(), &FutureProgress::clicked, - m_instance, &BuildManager::showBuildResults); - d->m_futureProgress.data()->setWidget(new Internal::BuildProgress(d->m_taskWindow)); - d->m_futureProgress.data()->setStatusBarWidget(new Internal::BuildProgress(d->m_taskWindow, - Qt::Horizontal)); - d->m_progress = 0; - d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100); - - d->m_running = true; - d->m_allStepsSucceeded = true; - d->m_progressFutureInterface->reportStarted(); - nextStep(); - } else { - // Already running - d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100); - d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress)); + const auto onRecipeSetup = [buildStep, name = item.name] { + d->m_outputWindow->reset(); + buildStep->setupOutputFormatter(d->m_outputWindow->outputFormatter()); + connect(buildStep, &BuildStep::progress, instance(), &BuildManager::progressChanged); + if (d->m_futureProgress) + d->m_futureProgress.data()->setTitle(name); + }; + const auto onRecipeDone = [buildStep] { + disconnect(buildStep, &BuildStep::progress, instance(), nullptr); + d->m_outputWindow->flush(); + ++d->m_progress; + d->m_progressFutureInterface->setProgressValueAndText( + 100 * d->m_progress, msgProgress(d->m_progress, d->m_maxProgress)); + }; + const auto onRecipeError = [buildStep, target, onRecipeDone] { + onRecipeDone(); + const QString projectName = buildStep->project()->displayName(); + const QString targetName = target->displayName(); + addToOutputWindow(Tr::tr("Error while building/deploying project %1 (kit: %2)") + .arg(projectName, targetName), BuildStep::OutputFormat::Stderr); + const Tasks kitTasks = target->kit()->validate(); + if (!kitTasks.isEmpty()) { + addToOutputWindow(Tr::tr("The kit %1 has configuration issues which might " + "be the root cause for this problem.") + .arg(targetName), BuildStep::OutputFormat::Stderr); + } + addToOutputWindow(Tr::tr("When executing step \"%1\"") + .arg(buildStep->displayName()), BuildStep::OutputFormat::Stderr); + }; + const Group recipeGroup { + onGroupSetup(onRecipeSetup), + buildStep->runRecipe(), + onGroupDone(onRecipeDone), + onGroupError(onRecipeError), + }; + targetTasks.append(recipeGroup); } + if (!targetTasks.isEmpty()) + topLevel.append(Group(targetTasks)); + + d->m_taskTree.reset(new TaskTree(Group{topLevel})); + const auto endHandler = [](bool success) { + d->m_taskTree.release()->deleteLater(); + + if (!success && d->m_progressFutureInterface) + d->m_progressFutureInterface->reportCanceled(); + + cleanupBuild(); + + if (d->m_pendingQueue.isEmpty()) { + d->m_poppedUpTaskWindow = false; + d->m_isDeploying = false; + } + + emit m_instance->buildQueueFinished(success); + + if (!d->m_pendingQueue.isEmpty()) { + d->m_buildQueue = d->m_pendingQueue; + d->m_pendingQueue.clear(); + startBuildQueue(); + } + }; + connect(d->m_taskTree.get(), &TaskTree::done, instance(), [endHandler] { endHandler(true); }); + connect(d->m_taskTree.get(), &TaskTree::errorOccurred, instance(), + [endHandler] { endHandler(false); }); + + // Progress Reporting + d->m_progressFutureInterface = new QFutureInterface<void>; + d->m_progressWatcher.setFuture(d->m_progressFutureInterface->future()); + ProgressManager::setApplicationLabel({}); + d->m_futureProgress = ProgressManager::addTask(d->m_progressFutureInterface->future(), + {}, "ProjectExplorer.Task.Build", + ProgressManager::KeepOnFinish | ProgressManager::ShowInApplicationIcon); + connect(d->m_futureProgress.data(), &FutureProgress::clicked, + m_instance, &BuildManager::showBuildResults); + d->m_futureProgress.data()->setWidget(new BuildProgress(d->m_taskWindow)); + d->m_futureProgress.data()->setStatusBarWidget(new BuildProgress(d->m_taskWindow, + Qt::Horizontal)); + d->m_progressFutureInterface->setProgressRange(0, d->m_maxProgress * 100); + d->m_progressFutureInterface->reportStarted(); + + d->m_elapsed.start(); + d->m_taskTree->start(); } void BuildManager::showBuildResults() @@ -638,130 +756,15 @@ void BuildManager::addToOutputWindow(const QString &string, BuildStep::OutputFor d->m_outputWindow->appendText(stringToWrite, format); } -void BuildManager::nextBuildQueue() -{ - d->m_outputWindow->flush(); - if (d->m_canceling) { - d->m_canceling = false; - QTimer::singleShot(0, m_instance, &BuildManager::emitCancelMessage); - - disconnectOutput(d->m_currentBuildStep); - decrementActiveBuildSteps(d->m_currentBuildStep); - - //TODO NBS fix in qtconcurrent - d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, - Tr::tr("Build/Deployment canceled")); - clearBuildQueue(); - return; - } - - disconnectOutput(d->m_currentBuildStep); - if (!d->m_skipDisabled) - ++d->m_progress; - d->m_progressFutureInterface->setProgressValueAndText(d->m_progress*100, msgProgress(d->m_progress, d->m_maxProgress)); - decrementActiveBuildSteps(d->m_currentBuildStep); - - const bool success = d->m_skipDisabled || d->m_lastStepSucceeded; - if (success) { - nextStep(); - } else { - // Build Failure - d->m_allStepsSucceeded = false; - Target *t = d->m_currentBuildStep->target(); - const QString projectName = d->m_currentBuildStep->project()->displayName(); - const QString targetName = t->displayName(); - addToOutputWindow(Tr::tr("Error while building/deploying project %1 (kit: %2)").arg(projectName, targetName), BuildStep::OutputFormat::Stderr); - const Tasks kitTasks = t->kit()->validate(); - if (!kitTasks.isEmpty()) { - addToOutputWindow(Tr::tr("The kit %1 has configuration issues which might be the root cause for this problem.") - .arg(targetName), BuildStep::OutputFormat::Stderr); - } - addToOutputWindow(Tr::tr("When executing step \"%1\"").arg(d->m_currentBuildStep->displayName()), BuildStep::OutputFormat::Stderr); - - bool abort = ProjectExplorerPlugin::projectExplorerSettings().abortBuildAllOnError; - if (!abort) { - while (!d->m_buildQueue.isEmpty() - && d->m_buildQueue.front()->target() == t) { - BuildStep * const nextStepForFailedTarget = d->m_buildQueue.takeFirst(); - disconnectOutput(nextStepForFailedTarget); - decrementActiveBuildSteps(nextStepForFailedTarget); - } - if (d->m_buildQueue.isEmpty()) - abort = true; - } - - if (abort) { - // NBS TODO fix in qtconcurrent - d->m_progressFutureInterface->setProgressValueAndText(d->m_progress * 100, - Tr::tr("Error while building/deploying project %1 (kit: %2)") - .arg(projectName, targetName)); - clearBuildQueue(); - } else { - nextStep(); - } - } -} - void BuildManager::progressChanged(int percent, const QString &text) { if (d->m_progressFutureInterface) d->m_progressFutureInterface->setProgressValueAndText(percent + 100 * d->m_progress, text); } -void BuildManager::nextStep() +bool BuildManager::buildQueueAppend(const QList<BuildItem> &items, const QStringList &preambleMessage) { - if (!d->m_buildQueue.empty()) { - d->m_currentBuildStep = d->m_buildQueue.front(); - d->m_buildQueue.pop_front(); - QString name = d->m_stepNames.takeFirst(); - d->m_skipDisabled = !d->m_enabledState.takeFirst(); - if (d->m_futureProgress) - d->m_futureProgress.data()->setTitle(name); - - if (d->m_currentBuildStep->project() != d->m_previousBuildStepProject) { - const QString projectName = d->m_currentBuildStep->project()->displayName(); - addToOutputWindow(Tr::tr("Running steps for project %1...") - .arg(projectName), BuildStep::OutputFormat::NormalMessage); - d->m_previousBuildStepProject = d->m_currentBuildStep->project(); - } - - if (d->m_skipDisabled) { - addToOutputWindow(Tr::tr("Skipping disabled step %1.") - .arg(d->m_currentBuildStep->displayName()), BuildStep::OutputFormat::NormalMessage); - nextBuildQueue(); - return; - } - - static const auto finishedHandler = [](bool success) { - d->m_outputWindow->flush(); - d->m_lastStepSucceeded = success; - disconnect(d->m_currentBuildStep, nullptr, instance(), nullptr); - BuildManager::nextBuildQueue(); - }; - connect(d->m_currentBuildStep, &BuildStep::finished, instance(), finishedHandler); - connect(d->m_currentBuildStep, &BuildStep::progress, - instance(), &BuildManager::progressChanged); - d->m_outputWindow->reset(); - d->m_currentBuildStep->setupOutputFormatter(d->m_outputWindow->outputFormatter()); - d->m_currentBuildStep->run(); - } else { - d->m_running = false; - d->m_poppedUpTaskWindow = false; - d->m_isDeploying = false; - d->m_previousBuildStepProject = nullptr; - d->m_progressFutureInterface->reportFinished(); - d->m_progressWatcher.setFuture(QFuture<void>()); - d->m_currentBuildStep = nullptr; - delete d->m_progressFutureInterface; - d->m_progressFutureInterface = nullptr; - d->m_maxProgress = 0; - emit m_instance->buildQueueFinished(d->m_allStepsSucceeded); - } -} - -bool BuildManager::buildQueueAppend(const QList<BuildStep *> &steps, QStringList names, const QStringList &preambleMessage) -{ - if (!d->m_running) { + if (!d->m_taskTree) { d->m_outputWindow->clearContents(); if (ProjectExplorerPlugin::projectExplorerSettings().clearIssuesOnRebuild) { TaskHub::clearTasks(Constants::TASK_CATEGORY_COMPILE); @@ -769,50 +772,51 @@ bool BuildManager::buildQueueAppend(const QList<BuildStep *> &steps, QStringList TaskHub::clearTasks(Constants::TASK_CATEGORY_DEPLOYMENT); TaskHub::clearTasks(Constants::TASK_CATEGORY_AUTOTEST); } - for (const QString &str : preambleMessage) addToOutputWindow(str, BuildStep::OutputFormat::NormalMessage, BuildStep::DontAppendNewline); } - int count = steps.size(); - bool init = true; - int i = 0; - for (; i < count; ++i) { - BuildStep *bs = steps.at(i); - connect(bs, &BuildStep::addTask, m_instance, &BuildManager::addToTaskWindow); - connect(bs, &BuildStep::addOutput, m_instance, &BuildManager::addToOutputWindow); - if (bs->enabled()) { - init = bs->init(); - if (!init) - break; - } - } - if (!init) { - BuildStep *bs = steps.at(i); + QList<BuildStep *> connectedSteps; + for (const BuildItem &item : items) { + BuildStep *buildStep = item.buildStep; + connect(buildStep, &BuildStep::addTask, m_instance, &BuildManager::addToTaskWindow); + connect(buildStep, &BuildStep::addOutput, m_instance, &BuildManager::addToOutputWindow); + connectedSteps.append(buildStep); + if (!item.enabled) + continue; + if (!isBuilding(buildStep) && buildStep->init()) + continue; - // cleaning up - // print something for the user - const QString projectName = bs->project()->displayName(); - const QString targetName = bs->target()->displayName(); - addToOutputWindow(Tr::tr("Error while building/deploying project %1 (kit: %2)").arg(projectName, targetName), BuildStep::OutputFormat::Stderr); - addToOutputWindow(Tr::tr("When executing step \"%1\"").arg(bs->displayName()), BuildStep::OutputFormat::Stderr); - - // disconnect the buildsteps again - for (int j = 0; j <= i; ++j) - disconnectOutput(steps.at(j)); + // init() failed, print something for the user... + const QString projectName = buildStep->project()->displayName(); + const QString targetName = buildStep->target()->displayName(); + addToOutputWindow(Tr::tr("Error while building/deploying project %1 (kit: %2)") + .arg(projectName, targetName), BuildStep::OutputFormat::Stderr); + addToOutputWindow(Tr::tr("When executing step \"%1\"") + .arg(buildStep->displayName()), BuildStep::OutputFormat::Stderr); + for (BuildStep *buildStep : std::as_const(connectedSteps)) + disconnect(buildStep, nullptr, m_instance, nullptr); + d->m_outputWindow->popup(IOutputPane::NoModeSwitch); return false; } - // Everthing init() well - for (i = 0; i < count; ++i) { - d->m_buildQueue.append(steps.at(i)); - d->m_stepNames.append(names.at(i)); - bool enabled = steps.at(i)->enabled(); - d->m_enabledState.append(enabled); - if (enabled) - ++d->m_maxProgress; - incrementActiveBuildSteps(steps.at(i)); + if (d->m_taskTree) + d->m_pendingQueue << items; + else + d->m_buildQueue = items; + + if (d->m_buildQueue.isEmpty() && d->m_pendingQueue.isEmpty()) { + if (compileOutputSettings().popUp()) + d->m_outputWindow->popup(IOutputPane::NoModeSwitch); + emit m_instance->buildQueueFinished(true); + return true; } + + for (const BuildItem &item : items) + incrementActiveBuildSteps(item.buildStep); + + if (!d->m_taskTree) + startBuildQueue(); return true; } @@ -821,46 +825,28 @@ bool BuildManager::buildList(BuildStepList *bsl) return buildLists({bsl}); } -bool BuildManager::buildLists(const QList<BuildStepList *> bsls, const QStringList &preambelMessage) +bool BuildManager::buildLists(const QList<BuildStepList *> &bsls, const QStringList &preambleMessage) { - QList<BuildStep *> steps; - QStringList stepListNames; + const bool wasDeploying = d->m_isDeploying; + QList<BuildItem> buildItems; for (BuildStepList *list : bsls) { - steps.append(list->steps()); - stepListNames.append(displayNameForStepId(list->id())); + const QString name = displayNameForStepId(list->id()); + const QList<BuildStep *> steps = list->steps(); + for (BuildStep *step : steps) + buildItems.append({step, step->enabled(), name}); d->m_isDeploying = d->m_isDeploying || list->id() == Constants::BUILDSTEPS_DEPLOY; } - QStringList names; - names.reserve(steps.size()); - for (int i = 0; i < bsls.size(); ++i) { - for (int j = 0; j < bsls.at(i)->count(); ++j) - names.append(stepListNames.at(i)); - } + if (buildQueueAppend(buildItems, preambleMessage)) + return true; - bool success = buildQueueAppend(steps, names, preambelMessage); - if (!success) { - d->m_outputWindow->popup(IOutputPane::NoModeSwitch); - d->m_isDeploying = false; - return false; - } - - if (CompileOutputSettings::instance().popUp()) - d->m_outputWindow->popup(IOutputPane::NoModeSwitch); - startBuildQueue(); - return true; + d->m_isDeploying = wasDeploying; + return false; } void BuildManager::appendStep(BuildStep *step, const QString &name) { - bool success = buildQueueAppend({step}, {name}); - if (!success) { - d->m_outputWindow->popup(IOutputPane::NoModeSwitch); - return; - } - if (CompileOutputSettings::instance().popUp()) - d->m_outputWindow->popup(IOutputPane::NoModeSwitch); - startBuildQueue(); + buildQueueAppend({{step, step->enabled(), name}}); } template <class T> @@ -890,7 +876,8 @@ bool BuildManager::isBuilding(const ProjectConfiguration *p) bool BuildManager::isBuilding(BuildStep *step) { - return (d->m_currentBuildStep == step) || d->m_buildQueue.contains(step); + const auto checker = [step](const BuildItem &item) { return item.buildStep == step; }; + return Utils::anyOf(d->m_buildQueue, checker) || Utils::anyOf(d->m_pendingQueue, checker); } template <class T> bool increment(QHash<T *, int> &hash, T *key) @@ -940,10 +927,4 @@ void BuildManager::decrementActiveBuildSteps(BuildStep *bs) emit m_instance->buildStateChanged(bs->project()); } -void BuildManager::disconnectOutput(BuildStep *bs) -{ - disconnect(bs, &BuildStep::addTask, m_instance, nullptr); - disconnect(bs, &BuildStep::addOutput, m_instance, nullptr); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h index d9478db4093..ec068615ab5 100644 --- a/src/plugins/projectexplorer/buildmanager.h +++ b/src/plugins/projectexplorer/buildmanager.h @@ -11,8 +11,10 @@ namespace ProjectExplorer { +class BuildItem; class Project; class RunConfiguration; +class RunControl; class Task; enum class BuildForRunConfigStatus { Building, NotBuilding, BuildFailed }; @@ -32,10 +34,8 @@ public: static void buildProjectWithoutDependencies(Project *project); static void cleanProjectWithoutDependencies(Project *project); static void rebuildProjectWithoutDependencies(Project *project); - static void buildProjectWithDependencies( - Project *project, - ConfigSelection configSelection = ConfigSelection::Active - ); + static void buildProjectWithDependencies(Project *project, + ConfigSelection configSelection = ConfigSelection::Active, RunControl *starter = nullptr); static void cleanProjectWithDependencies(Project *project, ConfigSelection configSelection); static void rebuildProjectWithDependencies(Project *project, ConfigSelection configSelection); static void buildProjects(const QList<Project *> &projects, ConfigSelection configSelection); @@ -50,8 +50,8 @@ public: static bool isDeploying(); static bool tasksAvailable(); - static bool buildLists(const QList<BuildStepList *> bsls, - const QStringList &preambelMessage = QStringList()); + static bool buildLists(const QList<BuildStepList *> &bsls, + const QStringList &preambelMessage = {}); static bool buildList(BuildStepList *bsl); static bool isBuilding(const Project *p); @@ -79,24 +79,21 @@ signals: void buildQueueFinished(bool success); private: + static void cleanupBuild(); static void addToTaskWindow(const ProjectExplorer::Task &task, int linkedOutputLines, int skipLines); static void addToOutputWindow(const QString &string, BuildStep::OutputFormat format, BuildStep::OutputNewlineSetting newlineSettings = BuildStep::DoAppendNewline); - static void nextBuildQueue(); static void progressChanged(int percent, const QString &text); - static void emitCancelMessage(); static void showBuildResults(); static void updateTaskCount(); static void finish(); static void startBuildQueue(); - static void nextStep(); - static void clearBuildQueue(); - static bool buildQueueAppend(const QList<BuildStep *> &steps, QStringList names, const QStringList &preambleMessage = QStringList()); + static bool buildQueueAppend(const QList<BuildItem> &items, + const QStringList &preambleMessage = {}); static void incrementActiveBuildSteps(BuildStep *bs); static void decrementActiveBuildSteps(BuildStep *bs); - static void disconnectOutput(BuildStep *bs); }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildpropertiessettings.cpp b/src/plugins/projectexplorer/buildpropertiessettings.cpp index 6df8fdd328e..09c373befb2 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.cpp +++ b/src/plugins/projectexplorer/buildpropertiessettings.cpp @@ -6,15 +6,24 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/layoutbuilder.h> using namespace Utils; namespace ProjectExplorer { -// Default directory: -const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[] - = "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}"; +static QString defaultBuildDirectoryTemplate() +{ + return "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}"; +} + +BuildPropertiesSettings &buildPropertiesSettings() +{ + static BuildPropertiesSettings theSettings; + return theSettings; +} BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect(AspectContainer *container) : TriStateAspect(container, Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")) @@ -24,11 +33,6 @@ BuildPropertiesSettings::BuildPropertiesSettings() { setAutoApply(false); - setId("AB.ProjectExplorer.BuildPropertiesSettingsPage"); - setDisplayName(Tr::tr("Default Build Properties")); - setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); - setSettings(this); - setLayouter([this] { using namespace Layouting; @@ -45,7 +49,7 @@ BuildPropertiesSettings::BuildPropertiesSettings() buildDirectoryTemplate.setDisplayStyle(StringAspect::LineEditDisplay); buildDirectoryTemplate.setSettingsKey("Directories/BuildDirectory.TemplateV2"); - buildDirectoryTemplate.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE); + buildDirectoryTemplate.setDefaultValue(defaultBuildDirectoryTemplate()); buildDirectoryTemplate.setLabelText(Tr::tr("Default build directory:")); buildDirectoryTemplate.setUseGlobalMacroExpander(); buildDirectoryTemplate.setUseResetButton(); @@ -55,19 +59,35 @@ BuildPropertiesSettings::BuildPropertiesSettings() qmlDebugging.setSettingsKey("ProjectExplorer/Settings/QmlDebugging"); qmlDebugging.setLabelText(Tr::tr("QML debugging:")); + qmlDebugging.setVisible(false); qtQuickCompiler.setSettingsKey("ProjectExplorer/Settings/QtQuickCompiler"); qtQuickCompiler.setLabelText(Tr::tr("Use qmlcachegen:")); + qtQuickCompiler.setVisible(false); - QObject::connect(&showQtSettings, &BoolAspect::valueChanged, - &qmlDebugging, &BaseAspect::setVisible); - QObject::connect(&showQtSettings, &BoolAspect::valueChanged, - &qtQuickCompiler, &BaseAspect::setVisible); + readSettings(); } -QString BuildPropertiesSettings::defaultBuildDirectoryTemplate() +void BuildPropertiesSettings::showQtSettings() { - return QString(DEFAULT_BUILD_DIRECTORY_TEMPLATE); + buildPropertiesSettings().qmlDebugging.setVisible(true); + buildPropertiesSettings().qtQuickCompiler.setVisible(true); } +// BuildPropertiesSettingsPage + +class BuildPropertiesSettingsPage final : public Core::IOptionsPage +{ +public: + BuildPropertiesSettingsPage() + { + setId("AB.ProjectExplorer.BuildPropertiesSettingsPage"); + setDisplayName(Tr::tr("Default Build Properties")); + setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setSettingsProvider([] { return &buildPropertiesSettings(); }); + } +}; + +const BuildPropertiesSettingsPage settingsPage; + } // ProjectExplorer diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h index 3b1b2b7c698..75061f3e28c 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.h +++ b/src/plugins/projectexplorer/buildpropertiessettings.h @@ -5,11 +5,11 @@ #include "projectexplorer_export.h" -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Core::PagedSettings +class PROJECTEXPLORER_EXPORT BuildPropertiesSettings : public Utils::AspectContainer { public: BuildPropertiesSettings(); @@ -24,9 +24,10 @@ public: BuildTriStateAspect separateDebugInfo{this}; BuildTriStateAspect qmlDebugging{this}; BuildTriStateAspect qtQuickCompiler{this}; - Utils::BoolAspect showQtSettings; - QString defaultBuildDirectoryTemplate(); + static void showQtSettings(); // Called by the Qt support plugin }; +PROJECTEXPLORER_EXPORT BuildPropertiesSettings &buildPropertiesSettings(); + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index d896836a500..d575d9b7cc5 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -7,17 +7,15 @@ #include "buildsteplist.h" #include "customparser.h" #include "deployconfiguration.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "projectexplorerconstants.h" #include "sanitizerparser.h" #include "target.h" -#include <utils/algorithm.h> #include <utils/fileinprojectfinder.h> #include <utils/layoutbuilder.h> #include <utils/outputformatter.h> -#include <utils/qtcassert.h> #include <utils/variablechooser.h> /*! @@ -38,9 +36,6 @@ \c init() is called in the GUI thread and can be used to query the project for any information you need. - - \c run() is run via Utils::runAsync in a separate thread. If you need an - event loop, you need to create it yourself. */ /*! @@ -50,26 +45,6 @@ that you need in the run() function. */ -/*! - \fn void ProjectExplorer::BuildStep::run(QFutureInterface<bool> &fi) - - Reimplement this function. It is called when the target is built. - By default, this function is NOT run in the GUI thread, but runs in its - own thread. If you need an event loop, you need to create one. - This function should block until the task is done - - The absolute minimal implementation is: - \code - fi.reportResult(true); - \endcode - - By returning \c true from runInGuiThread(), this function is called in - the GUI thread. Then the function should not block and instead the - finished() signal should be emitted. - - \sa runInGuiThread() -*/ - /*! \fn BuildStepConfigWidget *ProjectExplorer::BuildStep::createConfigWidget() @@ -90,11 +65,6 @@ It should be in plain text, with the format in the parameter. */ -/*! - \fn void ProjectExplorer::BuildStep::finished() - This signal needs to be emitted if the build step runs in the GUI thread. -*/ - /*! \fn bool ProjectExplorer::BuildStep::isImmutable() @@ -111,31 +81,11 @@ namespace ProjectExplorer { static QList<BuildStepFactory *> g_buildStepFactories; -BuildStep::BuildStep(BuildStepList *bsl, Id id) : - ProjectConfiguration(bsl, id) +BuildStep::BuildStep(BuildStepList *bsl, Id id) + : ProjectConfiguration(bsl->target(), id) + , m_stepList(bsl) { - QTC_CHECK(bsl->target() && bsl->target() == this->target()); - connect(this, &ProjectConfiguration::displayNameChanged, - this, &BuildStep::updateSummary); -// m_displayName = step->displayName(); -// m_summaryText = "<b>" + m_displayName + "</b>"; -} - -BuildStep::~BuildStep() -{ - emit finished(false); -} - -void BuildStep::run() -{ - m_cancelFlag = false; - doRun(); -} - -void BuildStep::cancel() -{ - m_cancelFlag = true; - doCancel(); + connect(this, &ProjectConfiguration::displayNameChanged, this, &BuildStep::updateSummary); } QWidget *BuildStep::doCreateConfigWidget() @@ -176,22 +126,21 @@ QWidget *BuildStep::createConfigWidget() return widget; } -bool BuildStep::fromMap(const QVariantMap &map) +void BuildStep::fromMap(const Store &map) { m_enabled = map.value(buildStepEnabledKey, true).toBool(); - return ProjectConfiguration::fromMap(map); + ProjectConfiguration::fromMap(map); } -QVariantMap BuildStep::toMap() const +void BuildStep::toMap(Store &map) const { - QVariantMap map = ProjectConfiguration::toMap(); + ProjectConfiguration::toMap(map); map.insert(buildStepEnabledKey, m_enabled); - return map; } BuildConfiguration *BuildStep::buildConfiguration() const { - auto config = qobject_cast<BuildConfiguration *>(parent()->parent()); + auto config = qobject_cast<BuildConfiguration *>(projectConfiguration()); if (config) return config; @@ -201,7 +150,7 @@ BuildConfiguration *BuildStep::buildConfiguration() const DeployConfiguration *BuildStep::deployConfiguration() const { - auto config = qobject_cast<DeployConfiguration *>(parent()->parent()); + auto config = qobject_cast<DeployConfiguration *>(projectConfiguration()); if (config) return config; // See comment in buildConfiguration() @@ -212,7 +161,7 @@ DeployConfiguration *BuildStep::deployConfiguration() const ProjectConfiguration *BuildStep::projectConfiguration() const { - return static_cast<ProjectConfiguration *>(parent()->parent()); + return stepList()->projectConfiguration(); } BuildSystem *BuildStep::buildSystem() const @@ -224,7 +173,7 @@ BuildSystem *BuildStep::buildSystem() const Environment BuildStep::buildEnvironment() const { - if (const auto bc = qobject_cast<BuildConfiguration *>(parent()->parent())) + if (const auto bc = qobject_cast<BuildConfiguration *>(projectConfiguration())) return bc->environment(); if (const auto bc = target()->activeBuildConfiguration()) return bc->environment(); @@ -261,8 +210,8 @@ QString BuildStep::fallbackWorkingDirectory() const void BuildStep::setupOutputFormatter(OutputFormatter *formatter) { - if (qobject_cast<BuildConfiguration *>(parent()->parent())) { - for (const Id id : buildConfiguration()->customParsers()) { + if (auto bc = qobject_cast<BuildConfiguration *>(projectConfiguration())) { + for (const Id id : bc->customParsers()) { if (Internal::CustomParser * const parser = Internal::CustomParser::createFromId(id)) formatter->addLineParser(parser); } @@ -292,22 +241,6 @@ QVariant BuildStep::data(Id id) const return {}; } -bool BuildStep::isCanceled() const -{ - return m_cancelFlag; -} - -void BuildStep::doCancel() -{ - QTC_ASSERT(false, qWarning() << "Build step" << displayName() - << "neeeds to implement the doCancel() function"); -} - -void BuildStep::addMacroExpander() -{ - m_addMacroExpander = true; -} - void BuildStep::setEnabled(bool b) { if (m_enabled == b) @@ -318,7 +251,7 @@ void BuildStep::setEnabled(bool b) BuildStepList *BuildStep::stepList() const { - return qobject_cast<BuildStepList *>(parent()); + return m_stepList; } bool BuildStep::enabled() const @@ -346,7 +279,7 @@ bool BuildStepFactory::canHandle(BuildStepList *bsl) const if (!m_supportedStepLists.isEmpty() && !m_supportedStepLists.contains(bsl->id())) return false; - auto config = qobject_cast<ProjectConfiguration *>(bsl->parent()); + ProjectConfiguration *config = bsl->projectConfiguration(); if (!m_supportedDeviceTypes.isEmpty()) { Target *target = bsl->target(); @@ -464,12 +397,13 @@ BuildStep *BuildStepFactory::create(BuildStepList *parent) return step; } -BuildStep *BuildStepFactory::restore(BuildStepList *parent, const QVariantMap &map) +BuildStep *BuildStepFactory::restore(BuildStepList *parent, const Store &map) { BuildStep *bs = create(parent); if (!bs) return nullptr; - if (!bs->fromMap(map)) { + bs->fromMap(map); + if (bs->hasError()) { QTC_CHECK(false); delete bs; return nullptr; diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h index 46be75f5633..0f8b762f313 100644 --- a/src/plugins/projectexplorer/buildstep.h +++ b/src/plugins/projectexplorer/buildstep.h @@ -3,30 +3,25 @@ #pragma once -#include "projectconfiguration.h" +#include "projectexplorer_export.h" #include "buildconfiguration.h" -#include "projectexplorer_export.h" +#include "projectconfiguration.h" #include <utils/qtcassert.h> -#include <QWidget> - -#include <atomic> #include <functional> -#include <memory> #include <optional> namespace Utils { -class Environment; -class FilePath; class MacroExpander; class OutputFormatter; } // Utils +namespace Tasking { class GroupItem; } + namespace ProjectExplorer { -class BuildConfiguration; class BuildStepFactory; class BuildStepList; class BuildSystem; @@ -43,13 +38,10 @@ protected: explicit BuildStep(BuildStepList *bsl, Utils::Id id); public: - ~BuildStep() override; virtual bool init() = 0; - void run(); - void cancel(); - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; bool enabled() const; void setEnabled(bool b); @@ -57,17 +49,10 @@ public: BuildStepList *stepList() const; BuildConfiguration *buildConfiguration() const; - DeployConfiguration *deployConfiguration() const; - ProjectConfiguration *projectConfiguration() const; BuildSystem *buildSystem() const; - Utils::Environment buildEnvironment() const; - Utils::FilePath buildDirectory() const; BuildConfiguration::BuildType buildType() const; Utils::MacroExpander *macroExpander() const; - QString fallbackWorkingDirectory() const; - - virtual void setupOutputFormatter(Utils::OutputFormatter *formatter); enum class OutputFormat { Stdout, Stderr, // These are for forwarded output from external tools @@ -83,23 +68,12 @@ public: }; bool widgetExpandedByDefault() const; - void setWidgetExpandedByDefault(bool widgetExpandedByDefault); - bool hasUserExpansionState() const { return m_wasExpanded.has_value(); } bool wasUserExpanded() const { return m_wasExpanded.value_or(false); } void setUserExpanded(bool expanded) { m_wasExpanded = expanded; } - bool isImmutable() const { return m_immutable; } - void setImmutable(bool immutable) { m_immutable = immutable; } - virtual QVariant data(Utils::Id id) const; - void setSummaryUpdater(const std::function<QString ()> &summaryUpdater); - - void addMacroExpander(); - QString summaryText() const; - void setSummaryText(const QString &summaryText); - QWidget *doCreateConfigWidget(); signals: @@ -109,29 +83,37 @@ signals: /// Do note that for linking compile output with tasks, you should first emit the output /// and then emit the task. \p linkedOutput lines will be linked. And the last \p skipLines will /// be skipped. - void addTask(const ProjectExplorer::Task &task, int linkedOutputLines = 0, int skipLines = 0); + void addTask(const Task &task, int linkedOutputLines = 0, int skipLines = 0); /// Adds \p string to the compile output view, formatted in \p format - void addOutput(const QString &string, ProjectExplorer::BuildStep::OutputFormat format, - ProjectExplorer::BuildStep::OutputNewlineSetting newlineSetting = DoAppendNewline); + void addOutput(const QString &string, OutputFormat format, + OutputNewlineSetting newlineSetting = DoAppendNewline); void enabledChanged(); void progress(int percentage, const QString &message); - void finished(bool result); protected: - virtual QWidget *createConfigWidget(); + void setWidgetExpandedByDefault(bool widgetExpandedByDefault); + void setImmutable(bool immutable) { m_immutable = immutable; } + void setSummaryUpdater(const std::function<QString()> &summaryUpdater); + void addMacroExpander() { m_addMacroExpander = true; } + void setSummaryText(const QString &summaryText); - bool isCanceled() const; + DeployConfiguration *deployConfiguration() const; + Utils::Environment buildEnvironment() const; + Utils::FilePath buildDirectory() const; + QString fallbackWorkingDirectory() const; + + virtual QWidget *createConfigWidget(); + virtual void setupOutputFormatter(Utils::OutputFormatter *formatter); private: - using ProjectConfiguration::parent; + friend class BuildManager; + virtual Tasking::GroupItem runRecipe() = 0; + ProjectConfiguration *projectConfiguration() const; - virtual void doRun() = 0; - virtual void doCancel(); - - std::atomic_bool m_cancelFlag; + BuildStepList * const m_stepList; bool m_enabled = true; bool m_immutable = false; bool m_widgetExpandedByDefault = true; @@ -155,7 +137,7 @@ public: BuildStep::Flags stepFlags() const; Utils::Id stepId() const; BuildStep *create(BuildStepList *parent); - BuildStep *restore(BuildStepList *parent, const QVariantMap &map); + BuildStep *restore(BuildStepList *parent, const Utils::Store &map); bool canHandle(BuildStepList *bsl) const; diff --git a/src/plugins/projectexplorer/buildsteplist.cpp b/src/plugins/projectexplorer/buildsteplist.cpp index 8d6159541ef..d0badc4b5c4 100644 --- a/src/plugins/projectexplorer/buildsteplist.cpp +++ b/src/plugins/projectexplorer/buildsteplist.cpp @@ -14,18 +14,17 @@ #include <QDebug> +using namespace Utils; + namespace ProjectExplorer { const char STEPS_COUNT_KEY[] = "ProjectExplorer.BuildStepList.StepsCount"; const char STEPS_PREFIX[] = "ProjectExplorer.BuildStepList.Step."; -BuildStepList::BuildStepList(QObject *parent, Utils::Id id) - : QObject(parent), m_id(id) +BuildStepList::BuildStepList(ProjectConfiguration *config, Utils::Id id) + : m_projectConfiguration(config), m_id(id) { - QTC_ASSERT(parent, return); - QTC_ASSERT(parent->parent(), return); - m_target = qobject_cast<Target *>(parent->parent()); - QTC_ASSERT(m_target, return); + QTC_CHECK(config); } BuildStepList::~BuildStepList() @@ -39,24 +38,32 @@ void BuildStepList::clear() m_steps.clear(); } -QVariantMap BuildStepList::toMap() const +Target *BuildStepList::target() const { - QVariantMap map; + return m_projectConfiguration->target(); +} + +Store BuildStepList::toMap() const +{ + Store map; { // Only written for compatibility reasons within the 4.11 cycle const char CONFIGURATION_ID_KEY[] = "ProjectExplorer.ProjectConfiguration.Id"; const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayName"; const char DEFAULT_DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; - map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting()); - map.insert(QLatin1String(DISPLAY_NAME_KEY), displayName()); - map.insert(QLatin1String(DEFAULT_DISPLAY_NAME_KEY), displayName()); + map.insert(CONFIGURATION_ID_KEY, m_id.toSetting()); + map.insert(DISPLAY_NAME_KEY, displayName()); + map.insert(DEFAULT_DISPLAY_NAME_KEY, displayName()); } // Save build steps - map.insert(QString::fromLatin1(STEPS_COUNT_KEY), m_steps.count()); - for (int i = 0; i < m_steps.count(); ++i) - map.insert(QString::fromLatin1(STEPS_PREFIX) + QString::number(i), m_steps.at(i)->toMap()); + map.insert(STEPS_COUNT_KEY, m_steps.count()); + for (int i = 0; i < m_steps.count(); ++i) { + Store data; + m_steps.at(i)->toMap(data); + map.insert(numberedKey(STEPS_PREFIX, i), variantFromStore(data)); + } return map; } @@ -96,15 +103,15 @@ QString BuildStepList::displayName() const return {}; } -bool BuildStepList::fromMap(const QVariantMap &map) +bool BuildStepList::fromMap(const Store &map) { clear(); const QList<BuildStepFactory *> factories = BuildStepFactory::allBuildStepFactories(); - int maxSteps = map.value(QString::fromLatin1(STEPS_COUNT_KEY), 0).toInt(); + int maxSteps = map.value(STEPS_COUNT_KEY, 0).toInt(); for (int i = 0; i < maxSteps; ++i) { - QVariantMap bsData(map.value(QString::fromLatin1(STEPS_PREFIX) + QString::number(i)).toMap()); + Store bsData = storeFromVariant(map.value(numberedKey(STEPS_PREFIX, i))); if (bsData.isEmpty()) { qWarning() << "No step data found for" << i << "(continuing)."; continue; diff --git a/src/plugins/projectexplorer/buildsteplist.h b/src/plugins/projectexplorer/buildsteplist.h index 909c3be3e8c..0e1f68e4203 100644 --- a/src/plugins/projectexplorer/buildsteplist.h +++ b/src/plugins/projectexplorer/buildsteplist.h @@ -6,13 +6,14 @@ #include "projectexplorer_export.h" #include <utils/id.h> +#include <utils/store.h> #include <QObject> -#include <QVariantMap> namespace ProjectExplorer { class BuildStep; +class ProjectConfiguration; class Target; class PROJECTEXPLORER_EXPORT BuildStepList : public QObject @@ -20,7 +21,7 @@ class PROJECTEXPLORER_EXPORT BuildStepList : public QObject Q_OBJECT public: - explicit BuildStepList(QObject *parent, Utils::Id id); + explicit BuildStepList(ProjectConfiguration *config, Utils::Id id); ~BuildStepList() override; void clear(); @@ -56,10 +57,11 @@ public: void moveStepUp(int position); BuildStep *at(int position) const; - Target *target() { return m_target; } + ProjectConfiguration *projectConfiguration() const { return m_projectConfiguration; } + Target *target() const; - QVariantMap toMap() const; - bool fromMap(const QVariantMap &map); + Utils::Store toMap() const; + bool fromMap(const Utils::Store &map); Utils::Id id() const { return m_id; } QString displayName() const; @@ -71,7 +73,7 @@ signals: void stepMoved(int from, int to); private: - Target *m_target; + ProjectConfiguration *m_projectConfiguration; Utils::Id m_id; QList<BuildStep *> m_steps; }; diff --git a/src/plugins/projectexplorer/buildsystem.cpp b/src/plugins/projectexplorer/buildsystem.cpp index 9a120a9863f..405de77e178 100644 --- a/src/plugins/projectexplorer/buildsystem.cpp +++ b/src/plugins/projectexplorer/buildsystem.cpp @@ -9,7 +9,6 @@ #include "projectexplorertr.h" #include "projectmanager.h" #include "runconfiguration.h" -#include "runcontrol.h" #include "target.h" #include <coreplugin/messagemanager.h> @@ -20,6 +19,7 @@ #include <projectexplorer/makestep.h> #include <utils/algorithm.h> +#include <utils/processinterface.h> #include <utils/qtcassert.h> #include <QTimer> @@ -62,8 +62,7 @@ BuildSystem::BuildSystem(Target *target) // Timer: d->m_delayedParsingTimer.setSingleShot(true); - connect(&d->m_delayedParsingTimer, &QTimer::timeout, this, - [this] { + connect(&d->m_delayedParsingTimer, &QTimer::timeout, this, [this] { if (ProjectManager::hasProject(project())) triggerParsing(); else @@ -324,7 +323,6 @@ void BuildSystem::setDeploymentData(const DeploymentData &deploymentData) { if (d->m_deploymentData != deploymentData) { d->m_deploymentData = deploymentData; - emit deploymentDataChanged(); emit target()->deploymentDataChanged(); } } diff --git a/src/plugins/projectexplorer/buildsystem.h b/src/plugins/projectexplorer/buildsystem.h index 1cd1e41a060..276427a7f91 100644 --- a/src/plugins/projectexplorer/buildsystem.h +++ b/src/plugins/projectexplorer/buildsystem.h @@ -66,6 +66,7 @@ public: Utils::Environment activeParseEnvironment() const; + virtual void requestDebugging() {} virtual bool addFiles(Node *context, const Utils::FilePaths &filePaths, Utils::FilePaths *notAdded = nullptr); @@ -151,8 +152,8 @@ public: signals: void parsingStarted(); void parsingFinished(bool success); - void deploymentDataChanged(); void testInformationUpdated(); + void debuggingStarted(); protected: // Helper methods to manage parsing state and signalling diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index bafc31fc14a..a8692b8c5e0 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -11,9 +11,12 @@ #include "showoutputtaskhandler.h" #include <coreplugin/outputwindow.h> +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> + #include <extensionsystem/pluginmanager.h> + #include <texteditor/texteditorsettings.h> #include <texteditor/fontsettings.h> #include <texteditor/behaviorsettings.h> @@ -37,11 +40,7 @@ #include <QToolButton> #include <QVBoxLayout> -namespace ProjectExplorer { - -class Task; - -namespace Internal { +namespace ProjectExplorer::Internal { const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom"; const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput"; @@ -51,6 +50,10 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_cancelBuildButton(new QToolButton), m_settingsButton(new QToolButton) { + setId("CompileOutput"); + setDisplayName(QCoreApplication::translate("QtC::ProjectExplorer", "Compile Output")); + setPriorityInStatusBar(40); + Core::Context context(C_COMPILE_OUTPUT); m_outputWindow = new Core::OutputWindow(context, SETTINGS_KEY); m_outputWindow->setWindowTitle(displayName()); @@ -102,14 +105,15 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : setupContext(C_COMPILE_OUTPUT, m_outputWindow); updateFromSettings(); - m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); - m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + CompileOutputSettings &s = compileOutputSettings(); + m_outputWindow->setWordWrapEnabled(s.wrapOutput()); + m_outputWindow->setMaxCharCount(s.maxCharCount()); - connect(&m_settings.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { - m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); + connect(&s.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setWordWrapEnabled(compileOutputSettings().wrapOutput()); }); - connect(&m_settings.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] { - m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + connect(&s.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setMaxCharCount(compileOutputSettings().maxCharCount()); }); } @@ -176,11 +180,6 @@ void CompileOutputWindow::clearContents() m_outputWindow->clear(); } -int CompileOutputWindow::priorityInStatusBar() const -{ - return 50; -} - bool CompileOutputWindow::canNext() const { return false; @@ -231,20 +230,15 @@ void CompileOutputWindow::updateFilter() // CompileOutputSettings -static CompileOutputSettings *s_compileOutputSettings; - -CompileOutputSettings &CompileOutputSettings::instance() +CompileOutputSettings &compileOutputSettings() { - return *s_compileOutputSettings; + static CompileOutputSettings theSettings; + return theSettings; } CompileOutputSettings::CompileOutputSettings() { - s_compileOutputSettings = this; - - setId(OPTIONS_PAGE_ID); - setDisplayName(Tr::tr("Compile Output")); - setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setAutoApply(false); wrapOutput.setSettingsKey("ProjectExplorer/Settings/WrapBuildOutput"); wrapOutput.setDefaultValue(true); @@ -274,5 +268,20 @@ CompileOutputSettings::CompileOutputSettings() readSettings(); } -} // Internal -} // ProjectExplorer +// CompileOutputSettingsPage + +class CompileOutputSettingsPage final : public Core::IOptionsPage +{ +public: + CompileOutputSettingsPage() + { + setId(OPTIONS_PAGE_ID); + setDisplayName(Tr::tr("Compile Output")); + setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setSettingsProvider([] { return &compileOutputSettings(); }); + } +}; + +const CompileOutputSettingsPage settingsPage; + +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index 89c7b749f2e..21cebcb2dba 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -26,18 +26,18 @@ namespace Internal { class ShowOutputTaskHandler; class CompileOutputTextEdit; -class CompileOutputSettings final : public Core::PagedSettings +class CompileOutputSettings final : public Utils::AspectContainer { public: CompileOutputSettings(); - static CompileOutputSettings &instance(); - Utils::BoolAspect popUp{this}; Utils::BoolAspect wrapOutput{this}; Utils::IntegerAspect maxCharCount{this}; }; +CompileOutputSettings &compileOutputSettings(); + class CompileOutputWindow final : public Core::IOutputPane { Q_OBJECT @@ -48,9 +48,6 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override { - return QCoreApplication::translate("QtC::ProjectExplorer","Compile Output"); } - int priorityInStatusBar() const override; void clearContents() override; bool canFocus() const override; bool hasFocus() const override; @@ -80,7 +77,6 @@ private: ShowOutputTaskHandler *m_handler; QToolButton *m_cancelBuildButton; QToolButton * const m_settingsButton; - CompileOutputSettings m_settings; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/copystep.cpp b/src/plugins/projectexplorer/copystep.cpp index 6d5aef64db0..98cd5df3f17 100644 --- a/src/plugins/projectexplorer/copystep.cpp +++ b/src/plugins/projectexplorer/copystep.cpp @@ -7,7 +7,9 @@ #include "projectexplorertr.h" #include <utils/aspects.h> +#include <utils/filestreamer.h> +using namespace Tasking; using namespace Utils; namespace ProjectExplorer::Internal { @@ -38,26 +40,27 @@ protected: return m_source.exists(); } - void doRun() final - { - // FIXME: asyncCopy does not handle directories yet. - QTC_ASSERT(m_source.isFile(), emit finished(false)); - m_source.asyncCopy(m_target, this, [this](const expected_str<void> &cont) { - if (!cont) { - addOutput(cont.error(), OutputFormat::ErrorMessage); - addOutput(Tr::tr("Copying failed."), OutputFormat::ErrorMessage); - emit finished(false); - } else { - addOutput(Tr::tr("Copying finished."), OutputFormat::NormalMessage); - emit finished(true); - } - }); - } - FilePathAspect m_sourceAspect{this}; FilePathAspect m_targetAspect{this}; private: + GroupItem runRecipe() final + { + const auto onSetup = [this](FileStreamer &streamer) { + QTC_ASSERT(m_source.isFile(), return SetupResult::StopWithError); + streamer.setSource(m_source); + streamer.setDestination(m_target); + return SetupResult::Continue; + }; + const auto onDone = [this](const FileStreamer &) { + addOutput(Tr::tr("Copying finished."), OutputFormat::NormalMessage); + }; + const auto onError = [this](const FileStreamer &) { + addOutput(Tr::tr("Copying failed."), OutputFormat::ErrorMessage); + }; + return FileStreamerTask(onSetup, onDone, onError); + } + FilePath m_source; FilePath m_target; }; diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 3bde2bb62f2..fe95d318313 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -9,10 +9,7 @@ #include "projecttree.h" #include <utils/qtcassert.h> -#include <utils/filesearch.h> - -#include <QDebug> -#include <QSettings> +#include <utils/qtcsettings.h> using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; @@ -24,7 +21,7 @@ CurrentProjectFind::CurrentProjectFind() connect(ProjectTree::instance(), &ProjectTree::currentProjectChanged, this, &CurrentProjectFind::handleProjectChanged); connect(ProjectManager::instance(), &ProjectManager::projectDisplayNameChanged, - this, [this](ProjectExplorer::Project *p) { + this, [this](Project *p) { if (p == ProjectTree::currentProject()) emit displayNameChanged(); }); @@ -49,25 +46,22 @@ bool CurrentProjectFind::isEnabled() const return ProjectTree::currentProject() != nullptr && BaseFileFind::isEnabled(); } -QVariant CurrentProjectFind::additionalParameters() const +static FilePath currentProjectFilePath() { Project *project = ProjectTree::currentProject(); - if (project) - return project->projectFilePath().toVariant(); - return QVariant(); + return project ? project->projectFilePath() : FilePath(); } -FileIterator *CurrentProjectFind::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider CurrentProjectFind::fileContainerProvider() const { - QTC_ASSERT(additionalParameters.isValid(), return new FileListIterator); - const FilePath projectFile = FilePath::fromVariant(additionalParameters); - for (Project *project : ProjectManager::projects()) { - if (project && projectFile == project->projectFilePath()) - return filesForProjects(nameFilters, exclusionFilters, {project}); - } - return new FileListIterator; + return [nameFilters = fileNameFilters(), exclusionFilters = fileExclusionFilters(), + projectFile = currentProjectFilePath()] { + for (Project *project : ProjectManager::projects()) { + if (project && projectFile == project->projectFilePath()) + return filesForProjects(nameFilters, exclusionFilters, {project}); + } + return FileContainer(); + }; } QString CurrentProjectFind::label() const @@ -83,28 +77,31 @@ void CurrentProjectFind::handleProjectChanged() emit displayNameChanged(); } -void CurrentProjectFind::recheckEnabled(Core::SearchResult *search) +void CurrentProjectFind::setupSearch(Core::SearchResult *search) { - const FilePath projectFile = FilePath::fromVariant(getAdditionalParameters(search)); - for (Project *project : ProjectManager::projects()) { - if (projectFile == project->projectFilePath()) { - search->setSearchAgainEnabled(true); - return; + const FilePath projectFile = currentProjectFilePath(); + connect(this, &IFindFilter::enabledChanged, search, [search, projectFile] { + const QList<Project *> projects = ProjectManager::projects(); + for (Project *project : projects) { + if (projectFile == project->projectFilePath()) { + search->setSearchAgainEnabled(true); + return; + } } - } - search->setSearchAgainEnabled(false); + search->setSearchAgainEnabled(false); + }); } -void CurrentProjectFind::writeSettings(QSettings *settings) +void CurrentProjectFind::writeSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("CurrentProjectFind")); + settings->beginGroup("CurrentProjectFind"); writeCommonSettings(settings); settings->endGroup(); } -void CurrentProjectFind::readSettings(QSettings *settings) +void CurrentProjectFind::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("CurrentProjectFind")); + settings->beginGroup("CurrentProjectFind"); readCommonSettings(settings, "*", ""); settings->endGroup(); } diff --git a/src/plugins/projectexplorer/currentprojectfind.h b/src/plugins/projectexplorer/currentprojectfind.h index 586d0b85b10..c9081bcd9f9 100644 --- a/src/plugins/projectexplorer/currentprojectfind.h +++ b/src/plugins/projectexplorer/currentprojectfind.h @@ -23,19 +23,16 @@ public: bool isEnabled() const override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; - QVariant additionalParameters() const override; QString label() const override; private: + TextEditor::FileContainerProvider fileContainerProvider() const override; void handleProjectChanged(); - void recheckEnabled(Core::SearchResult *search) override; + void setupSearch(Core::SearchResult *search) override; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 00f7225d0df..4341e14cf77 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -8,6 +8,8 @@ #include "runconfigurationaspects.h" #include "target.h" +#include <utils/processinterface.h> + using namespace Utils; namespace ProjectExplorer { @@ -23,43 +25,38 @@ CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *targe CustomExecutableRunConfiguration::CustomExecutableRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); + environment.setSupportForBuildEnvironment(target); - auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::HostDevice); - exeAspect->setSettingsKey("ProjectExplorer.CustomExecutableRunConfiguration.Executable"); - exeAspect->setDisplayStyle(StringAspect::PathChooserDisplay); - exeAspect->setHistoryCompleter("Qt.CustomExecutable.History"); - exeAspect->setExpectedKind(PathChooser::ExistingCommand); - exeAspect->setEnvironment(envAspect->environment()); + executable.setDeviceSelector(target, ExecutableAspect::HostDevice); + executable.setSettingsKey("ProjectExplorer.CustomExecutableRunConfiguration.Executable"); + executable.setReadOnly(false); + executable.setHistoryCompleter("Qt.CustomExecutable.History"); + executable.setExpectedKind(PathChooser::ExistingCommand); + executable.setEnvironment(environment.environment()); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); + arguments.setMacroExpander(macroExpander()); - connect(envAspect, &EnvironmentAspect::environmentChanged, this, [exeAspect, envAspect] { - exeAspect->setEnvironment(envAspect->environment()); + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + connect(&environment, &EnvironmentAspect::environmentChanged, this, [this] { + executable.setEnvironment(environment.environment()); }); setDefaultDisplayName(defaultDisplayName()); } -FilePath CustomExecutableRunConfiguration::executable() const -{ - return aspect<ExecutableAspect>()->executable(); -} - bool CustomExecutableRunConfiguration::isEnabled() const { return true; } -Runnable CustomExecutableRunConfiguration::runnable() const +ProcessRunData CustomExecutableRunConfiguration::runnable() const { - Runnable r; + ProcessRunData r; r.command = commandLine(); - r.environment = aspect<EnvironmentAspect>()->environment(); - r.workingDirectory = aspect<WorkingDirectoryAspect>()->workingDirectory(); + r.environment = environment.environment(); + r.workingDirectory = workingDir(); if (!r.command.isEmpty()) { const FilePath expanded = macroExpander()->expand(r.command.executable()); diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.h b/src/plugins/projectexplorer/customexecutablerunconfiguration.h index 101516bb2de..39199f4b4a1 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.h +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.h @@ -3,6 +3,8 @@ #pragma once +#include "environmentaspect.h" +#include "runconfigurationaspects.h" #include "runcontrol.h" namespace ProjectExplorer { @@ -18,12 +20,17 @@ public: QString defaultDisplayName() const; private: - Runnable runnable() const override; + Utils::ProcessRunData runnable() const override; bool isEnabled() const override; Tasks checkForIssues() const override; void configurationDialogFinished(); - Utils::FilePath executable() const; + + EnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; }; class CustomExecutableRunConfigurationFactory : public FixedRunConfigurationFactory diff --git a/src/plugins/projectexplorer/customparser.cpp b/src/plugins/projectexplorer/customparser.cpp index 327e6ac8441..e1dc3c7577a 100644 --- a/src/plugins/projectexplorer/customparser.cpp +++ b/src/plugins/projectexplorer/customparser.cpp @@ -91,9 +91,9 @@ void CustomParserExpression::setMessageCap(int messageCap) m_messageCap = messageCap; } -QVariantMap CustomParserExpression::toMap() const +Store CustomParserExpression::toMap() const { - QVariantMap map; + Store map; map.insert(patternKey, pattern()); map.insert(messageCapKey, messageCap()); map.insert(fileNameCapKey, fileNameCap()); @@ -103,7 +103,7 @@ QVariantMap CustomParserExpression::toMap() const return map; } -void CustomParserExpression::fromMap(const QVariantMap &map) +void CustomParserExpression::fromMap(const Store &map) { setPattern(map.value(patternKey).toString()); setMessageCap(map.value(messageCapKey).toInt()); @@ -139,22 +139,22 @@ bool CustomParserSettings::operator ==(const CustomParserSettings &other) const && error == other.error && warning == other.warning; } -QVariantMap CustomParserSettings::toMap() const +Store CustomParserSettings::toMap() const { - QVariantMap map; + Store map; map.insert(idKey, id.toSetting()); map.insert(nameKey, displayName); - map.insert(errorKey, error.toMap()); - map.insert(warningKey, warning.toMap()); + map.insert(errorKey, variantFromStore(error.toMap())); + map.insert(warningKey, variantFromStore(warning.toMap())); return map; } -void CustomParserSettings::fromMap(const QVariantMap &map) +void CustomParserSettings::fromMap(const Store &map) { - id = Utils::Id::fromSetting(map.value(idKey)); + id = Id::fromSetting(map.value(idKey)); displayName = map.value(nameKey).toString(); - error.fromMap(map.value(errorKey).toMap()); - warning.fromMap(map.value(warningKey).toMap()); + error.fromMap(storeFromVariant(map.value(errorKey))); + warning.fromMap(storeFromVariant(map.value(warningKey))); } CustomParsersAspect::CustomParsersAspect(Target *target) @@ -173,14 +173,14 @@ CustomParsersAspect::CustomParsersAspect(Target *target) }); } -void CustomParsersAspect::fromMap(const QVariantMap &map) +void CustomParsersAspect::fromMap(const Store &map) { - m_parsers = transform(map.value(settingsKey()).toList(), &Utils::Id::fromSetting); + m_parsers = transform(map.value(settingsKey()).toList(), &Id::fromSetting); } -void CustomParsersAspect::toMap(QVariantMap &map) const +void CustomParsersAspect::toMap(Store &map) const { - map.insert(settingsKey(), transform(m_parsers, &Utils::Id::toSetting)); + map.insert(settingsKey(), transform(m_parsers, &Id::toSetting)); } namespace Internal { @@ -322,7 +322,7 @@ private: CustomParsersSelectionWidget::CustomParsersSelectionWidget(QWidget *parent) : DetailsWidget(parent) { const auto widget = new SelectionWidget(this); - connect(widget, &SelectionWidget::selectionChanged, [this] { + connect(widget, &SelectionWidget::selectionChanged, this, [this] { updateSummary(); emit selectionChanged(); }); diff --git a/src/plugins/projectexplorer/customparser.h b/src/plugins/projectexplorer/customparser.h index d11979bcc21..2416a6bed1f 100644 --- a/src/plugins/projectexplorer/customparser.h +++ b/src/plugins/projectexplorer/customparser.h @@ -44,8 +44,8 @@ public: int messageCap() const; void setMessageCap(int messageCap); - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); private: QRegularExpression m_regExp; @@ -62,8 +62,8 @@ public: bool operator ==(const CustomParserSettings &other) const; bool operator !=(const CustomParserSettings &other) const { return !operator==(other); } - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); Utils::Id id; QString displayName; @@ -86,8 +86,8 @@ public: }; private: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; QList<Utils::Id> m_parsers; }; diff --git a/src/plugins/projectexplorer/customparserssettingspage.cpp b/src/plugins/projectexplorer/customparserssettingspage.cpp index fd16d41ff6f..b1238f684ac 100644 --- a/src/plugins/projectexplorer/customparserssettingspage.cpp +++ b/src/plugins/projectexplorer/customparserssettingspage.cpp @@ -49,7 +49,7 @@ public: buttonLayout->addWidget(editButton); buttonLayout->addStretch(1); - connect(addButton, &QPushButton::clicked, [this] { + connect(addButton, &QPushButton::clicked, this, [this] { CustomParserConfigDialog dlg(this); dlg.setSettings(CustomParserSettings()); if (dlg.exec() != QDialog::Accepted) @@ -60,13 +60,13 @@ public: m_customParsers << newParser; resetListView(); }); - connect(removeButton, &QPushButton::clicked, [this] { + connect(removeButton, &QPushButton::clicked, this, [this] { const QList<QListWidgetItem *> sel = m_parserListView.selectedItems(); QTC_ASSERT(sel.size() == 1, return); m_customParsers.removeAt(m_parserListView.row(sel.first())); delete sel.first(); }); - connect(editButton, &QPushButton::clicked, [this] { + connect(editButton, &QPushButton::clicked, this, [this] { const QList<QListWidgetItem *> sel = m_parserListView.selectedItems(); QTC_ASSERT(sel.size() == 1, return); CustomParserSettings &s = m_customParsers[m_parserListView.row(sel.first())]; @@ -78,7 +78,7 @@ public: s.warning = dlg.settings().warning; }); - connect(&m_parserListView, &QListWidget::itemChanged, [this](QListWidgetItem *item) { + connect(&m_parserListView, &QListWidget::itemChanged, this, [this](QListWidgetItem *item) { m_customParsers[m_parserListView.row(item)].displayName = item->text(); resetListView(); }); diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 9c7ab0a4088..f2ec50ed54a 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -13,6 +13,7 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "projectmacro.h" +#include "toolchainconfigwidget.h" #include <utils/algorithm.h> #include <utils/detailswidget.h> @@ -30,31 +31,84 @@ using namespace Utils; -namespace ProjectExplorer { +namespace ProjectExplorer::Internal { -// -------------------------------------------------------------------------- -// Helpers: -// -------------------------------------------------------------------------- - -static const char makeCommandKeyC[] = "ProjectExplorer.CustomToolChain.MakePath"; -static const char predefinedMacrosKeyC[] = "ProjectExplorer.CustomToolChain.PredefinedMacros"; -static const char headerPathsKeyC[] = "ProjectExplorer.CustomToolChain.HeaderPaths"; -static const char cxx11FlagsKeyC[] = "ProjectExplorer.CustomToolChain.Cxx11Flags"; -static const char mkspecsKeyC[] = "ProjectExplorer.CustomToolChain.Mkspecs"; -static const char outputParserKeyC[] = "ProjectExplorer.CustomToolChain.OutputParser"; +const char makeCommandKeyC[] = "ProjectExplorer.CustomToolChain.MakePath"; +const char predefinedMacrosKeyC[] = "ProjectExplorer.CustomToolChain.PredefinedMacros"; +const char headerPathsKeyC[] = "ProjectExplorer.CustomToolChain.HeaderPaths"; +const char cxx11FlagsKeyC[] = "ProjectExplorer.CustomToolChain.Cxx11Flags"; +const char mkspecsKeyC[] = "ProjectExplorer.CustomToolChain.Mkspecs"; +const char outputParserKeyC[] = "ProjectExplorer.CustomToolChain.OutputParser"; // -------------------------------------------------------------------------- // CustomToolChain // -------------------------------------------------------------------------- -CustomToolChain::CustomToolChain() : - ToolChain(Constants::CUSTOM_TOOLCHAIN_TYPEID), - m_outputParserId(GccParser::id()) +class CustomToolChain : public ToolChain { - setTypeDisplayName(Tr::tr("Custom")); - setTargetAbiKey("ProjectExplorer.CustomToolChain.TargetAbi"); - setCompilerCommandKey("ProjectExplorer.CustomToolChain.CompilerPath"); -} +public: + CustomToolChain() + : ToolChain(Constants::CUSTOM_TOOLCHAIN_TYPEID) + , m_outputParserId(GccParser::id()) + { + setTypeDisplayName(Tr::tr("Custom")); + setTargetAbiKey("ProjectExplorer.CustomToolChain.TargetAbi"); + setCompilerCommandKey("ProjectExplorer.CustomToolChain.CompilerPath"); + } + + class Parser { + public: + Id parserId; ///< A unique id identifying a parser + QString displayName; ///< A translateable name to show in the user interface + }; + + bool isValid() const override; + + MacroInspectionRunner createMacroInspectionRunner() const override; + LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; + WarningFlags warningFlags(const QStringList &cxxflags) const override; + const Macros &rawPredefinedMacros() const; + void setPredefinedMacros(const Macros ¯os); + + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Environment &) const override; + void addToEnvironment(Environment &env) const override; + QStringList suggestedMkspecList() const override; + QList<OutputLineParser *> createOutputParsers() const override; + QStringList headerPathsList() const; + void setHeaderPaths(const QStringList &list); + + void toMap(Store &data) const override; + void fromMap(const Store &data) override; + + std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; + + bool operator ==(const ToolChain &) const override; + + void setMakeCommand(const FilePath &); + FilePath makeCommand(const Environment &environment) const override; + + void setCxx11Flags(const QStringList &); + const QStringList &cxx11Flags() const; + + void setMkspecs(const QString &); + QString mkspecs() const; + + Id outputParserId() const; + void setOutputParserId(Id parserId); + static QList<CustomToolChain::Parser> parsers(); + + CustomParserSettings customParserSettings() const; + +private: + FilePath m_makeCommand; + + Macros m_predefinedMacros; + HeaderPaths m_builtInHeaderPaths; + QStringList m_cxx11Flags; + QStringList m_mkspecs; + + Id m_outputParserId; +}; CustomParserSettings CustomToolChain::customParserSettings() const { @@ -72,7 +126,7 @@ bool CustomToolChain::isValid() const ToolChain::MacroInspectionRunner CustomToolChain::createMacroInspectionRunner() const { const Macros theMacros = m_predefinedMacros; - const Utils::Id lang = language(); + const Id lang = language(); // This runner must be thread-safe! return [theMacros, lang](const QStringList &cxxflags){ @@ -88,7 +142,7 @@ ToolChain::MacroInspectionRunner CustomToolChain::createMacroInspectionRunner() }; } -Utils::LanguageExtensions CustomToolChain::languageExtensions(const QStringList &) const +LanguageExtensions CustomToolChain::languageExtensions(const QStringList &) const { return LanguageExtension::None; } @@ -147,7 +201,7 @@ QStringList CustomToolChain::suggestedMkspecList() const return m_mkspecs; } -QList<Utils::OutputLineParser *> CustomToolChain::createOutputParsers() const +QList<OutputLineParser *> CustomToolChain::createOutputParsers() const { if (m_outputParserId == GccParser::id()) return GccParser::gccParserSuite(); @@ -157,7 +211,7 @@ QList<Utils::OutputLineParser *> CustomToolChain::createOutputParsers() const return LinuxIccParser::iccParserSuite(); if (m_outputParserId == MsvcParser::id()) return {new MsvcParser}; - return {new Internal::CustomParser(customParserSettings())}; + return {new CustomParser(customParserSettings())}; } QStringList CustomToolChain::headerPathsList() const @@ -217,34 +271,31 @@ QString CustomToolChain::mkspecs() const return m_mkspecs.join(','); } -QVariantMap CustomToolChain::toMap() const +void CustomToolChain::toMap(Store &data) const { - QVariantMap data = ToolChain::toMap(); - data.insert(QLatin1String(makeCommandKeyC), m_makeCommand.toString()); + ToolChain::toMap(data); + data.insert(makeCommandKeyC, m_makeCommand.toString()); QStringList macros = Utils::transform<QList>(m_predefinedMacros, [](const Macro &m) { return QString::fromUtf8(m.toByteArray()); }); - data.insert(QLatin1String(predefinedMacrosKeyC), macros); - data.insert(QLatin1String(headerPathsKeyC), headerPathsList()); - data.insert(QLatin1String(cxx11FlagsKeyC), m_cxx11Flags); - data.insert(QLatin1String(mkspecsKeyC), mkspecs()); - data.insert(QLatin1String(outputParserKeyC), m_outputParserId.toSetting()); - - return data; + data.insert(predefinedMacrosKeyC, macros); + data.insert(headerPathsKeyC, headerPathsList()); + data.insert(cxx11FlagsKeyC, m_cxx11Flags); + data.insert(mkspecsKeyC, mkspecs()); + data.insert(outputParserKeyC, m_outputParserId.toSetting()); } -bool CustomToolChain::fromMap(const QVariantMap &data) +void CustomToolChain::fromMap(const Store &data) { - if (!ToolChain::fromMap(data)) - return false; + ToolChain::fromMap(data); + if (hasError()) + return; - m_makeCommand = FilePath::fromString(data.value(QLatin1String(makeCommandKeyC)).toString()); - const QStringList macros = data.value(QLatin1String(predefinedMacrosKeyC)).toStringList(); + m_makeCommand = FilePath::fromString(data.value(makeCommandKeyC).toString()); + const QStringList macros = data.value(predefinedMacrosKeyC).toStringList(); m_predefinedMacros = Macro::toMacros(macros.join('\n').toUtf8()); - setHeaderPaths(data.value(QLatin1String(headerPathsKeyC)).toStringList()); - m_cxx11Flags = data.value(QLatin1String(cxx11FlagsKeyC)).toStringList(); - setMkspecs(data.value(QLatin1String(mkspecsKeyC)).toString()); - setOutputParserId(Utils::Id::fromSetting(data.value(QLatin1String(outputParserKeyC)))); - - return true; + setHeaderPaths(data.value(headerPathsKeyC).toStringList()); + m_cxx11Flags = data.value(cxx11FlagsKeyC).toStringList(); + setMkspecs(data.value(mkspecsKeyC).toString()); + setOutputParserId(Id::fromSetting(data.value(outputParserKeyC))); } bool CustomToolChain::operator ==(const ToolChain &other) const @@ -259,12 +310,12 @@ bool CustomToolChain::operator ==(const ToolChain &other) const && m_builtInHeaderPaths == customTc->m_builtInHeaderPaths; } -Utils::Id CustomToolChain::outputParserId() const +Id CustomToolChain::outputParserId() const { return m_outputParserId; } -void CustomToolChain::setOutputParserId(Utils::Id parserId) +void CustomToolChain::setOutputParserId(Id parserId) { if (m_outputParserId == parserId) return; @@ -282,26 +333,6 @@ QList<CustomToolChain::Parser> CustomToolChain::parsers() return result; } -std::unique_ptr<ToolChainConfigWidget> CustomToolChain::createConfigurationWidget() -{ - return std::make_unique<Internal::CustomToolChainConfigWidget>(this); -} - -namespace Internal { - -// -------------------------------------------------------------------------- -// CustomToolChainFactory -// -------------------------------------------------------------------------- - -CustomToolChainFactory::CustomToolChainFactory() -{ - setDisplayName(Tr::tr("Custom")); - setSupportedToolChainType(Constants::CUSTOM_TOOLCHAIN_TYPEID); - setSupportsAllLanguages(true); - setToolchainConstructor([] { return new CustomToolChain; }); - setUserCreatable(true); -} - // -------------------------------------------------------------------------- // Helper for ConfigWidget // -------------------------------------------------------------------------- @@ -314,7 +345,7 @@ public: setWidget(textEdit); } - inline QPlainTextEdit *textEditWidget() const + QPlainTextEdit *textEditWidget() const { return static_cast<QPlainTextEdit *>(widget()); } @@ -350,6 +381,34 @@ public: // CustomToolChainConfigWidget // -------------------------------------------------------------------------- +class CustomToolChainConfigWidget final : public ToolChainConfigWidget +{ +public: + explicit CustomToolChainConfigWidget(CustomToolChain *); + +private: + void updateSummaries(TextEditDetailsWidget *detailsWidget); + void errorParserChanged(int index = -1); + + void applyImpl() override; + void discardImpl() override { setFromToolchain(); } + bool isDirtyImpl() const override; + void makeReadOnlyImpl() override; + + void setFromToolchain(); + + PathChooser *m_compilerCommand; + PathChooser *m_makeCommand; + AbiWidget *m_abiWidget; + QPlainTextEdit *m_predefinedMacros; + QPlainTextEdit *m_headerPaths; + TextEditDetailsWidget *m_predefinedDetails; + TextEditDetailsWidget *m_headerDetails; + QLineEdit *m_cxx11Flags; + QLineEdit *m_mkspecs; + QComboBox *m_errorParserComboBox; +}; + CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) : ToolChainConfigWidget(tc), m_compilerCommand(new PathChooser), @@ -382,9 +441,9 @@ CustomToolChainConfigWidget::CustomToolChainConfigWidget(CustomToolChain *tc) : m_cxx11Flags->setToolTip(Tr::tr("Comma-separated list of flags that turn on C++11 support.")); m_mkspecs->setToolTip(Tr::tr("Comma-separated list of mkspecs.")); m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter(QLatin1String("PE.ToolChainCommand.History")); + m_compilerCommand->setHistoryCompleter("PE.ToolChainCommand.History"); m_makeCommand->setExpectedKind(PathChooser::ExistingCommand); - m_makeCommand->setHistoryCompleter(QLatin1String("PE.MakeCommand.History")); + m_makeCommand->setHistoryCompleter("PE.MakeCommand.History"); m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); m_mainLayout->addRow(Tr::tr("&Make path:"), m_makeCommand); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); @@ -446,7 +505,7 @@ void CustomToolChainConfigWidget::applyImpl() tc->setCxx11Flags(m_cxx11Flags->text().split(QLatin1Char(','))); tc->setMkspecs(m_mkspecs->text()); tc->setDisplayName(displayName); // reset display name - tc->setOutputParserId(Utils::Id::fromSetting(m_errorParserComboBox->currentData())); + tc->setOutputParserId(Id::fromSetting(m_errorParserComboBox->currentData())); setFromToolchain(); // Refresh with actual data from the toolchain. This shows what e.g. the // macro parser did with the input. @@ -482,7 +541,7 @@ bool CustomToolChainConfigWidget::isDirtyImpl() const || m_headerDetails->entries() != tc->headerPathsList() || m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags() || m_mkspecs->text() != tc->mkspecs() - || Utils::Id::fromSetting(m_errorParserComboBox->currentData()) == tc->outputParserId(); + || Id::fromSetting(m_errorParserComboBox->currentData()) == tc->outputParserId(); } void CustomToolChainConfigWidget::makeReadOnlyImpl() @@ -490,5 +549,22 @@ void CustomToolChainConfigWidget::makeReadOnlyImpl() m_mainLayout->setEnabled(false); } -} // namespace Internal -} // namespace ProjectExplorer +std::unique_ptr<ToolChainConfigWidget> CustomToolChain::createConfigurationWidget() +{ + return std::make_unique<CustomToolChainConfigWidget>(this); +} + +// -------------------------------------------------------------------------- +// CustomToolChainFactory +// -------------------------------------------------------------------------- + +CustomToolChainFactory::CustomToolChainFactory() +{ + setDisplayName(Tr::tr("Custom")); + setSupportedToolChainType(Constants::CUSTOM_TOOLCHAIN_TYPEID); + setSupportsAllLanguages(true); + setToolchainConstructor([] { return new CustomToolChain; }); + setUserCreatable(true); +} + +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index 3da4dfd9160..a8d8616efb9 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -3,98 +3,9 @@ #pragma once -#include "projectexplorer_export.h" - -#include "abi.h" -#include "customparser.h" -#include "headerpath.h" #include "toolchain.h" -#include "toolchainconfigwidget.h" -#include <utils/fileutils.h> - -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -class QTextEdit; -class QComboBox; -QT_END_NAMESPACE - -namespace Utils { class PathChooser; } - -namespace ProjectExplorer { - -class AbiWidget; - -namespace Internal { class CustomToolChainFactory; } - -// -------------------------------------------------------------------------- -// CustomToolChain -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT CustomToolChain : public ToolChain -{ -public: - class Parser { - public: - Utils::Id parserId; ///< A unique id identifying a parser - QString displayName; ///< A translateable name to show in the user interface - }; - - bool isValid() const override; - - MacroInspectionRunner createMacroInspectionRunner() const override; - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; - Utils::WarningFlags warningFlags(const QStringList &cxxflags) const override; - const Macros &rawPredefinedMacros() const; - void setPredefinedMacros(const Macros ¯os); - - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( - const Utils::Environment &) const override; - void addToEnvironment(Utils::Environment &env) const override; - QStringList suggestedMkspecList() const override; - QList<Utils::OutputLineParser *> createOutputParsers() const override; - QStringList headerPathsList() const; - void setHeaderPaths(const QStringList &list); - - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; - - std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; - - bool operator ==(const ToolChain &) const override; - - void setMakeCommand(const Utils::FilePath &); - Utils::FilePath makeCommand(const Utils::Environment &environment) const override; - - void setCxx11Flags(const QStringList &); - const QStringList &cxx11Flags() const; - - void setMkspecs(const QString &); - QString mkspecs() const; - - Utils::Id outputParserId() const; - void setOutputParserId(Utils::Id parserId); - static QList<CustomToolChain::Parser> parsers(); - -private: - CustomToolChain(); - - CustomParserSettings customParserSettings() const; - - Utils::FilePath m_makeCommand; - - Macros m_predefinedMacros; - HeaderPaths m_builtInHeaderPaths; - QStringList m_cxx11Flags; - QStringList m_mkspecs; - - Utils::Id m_outputParserId; - - friend class Internal::CustomToolChainFactory; - friend class ToolChainFactory; -}; - -namespace Internal { +namespace ProjectExplorer::Internal { class CustomToolChainFactory : public ToolChainFactory { @@ -102,42 +13,4 @@ public: CustomToolChainFactory(); }; -// -------------------------------------------------------------------------- -// CustomToolChainConfigWidget -// -------------------------------------------------------------------------- - -class TextEditDetailsWidget; - -class CustomToolChainConfigWidget : public ToolChainConfigWidget -{ - Q_OBJECT - -public: - CustomToolChainConfigWidget(CustomToolChain *); - -private: - void updateSummaries(TextEditDetailsWidget *detailsWidget); - void errorParserChanged(int index = -1); - -protected: - void applyImpl() override; - void discardImpl() override { setFromToolchain(); } - bool isDirtyImpl() const override; - void makeReadOnlyImpl() override; - - void setFromToolchain(); - - Utils::PathChooser *m_compilerCommand; - Utils::PathChooser *m_makeCommand; - AbiWidget *m_abiWidget; - QPlainTextEdit *m_predefinedMacros; - QPlainTextEdit *m_headerPaths; - TextEditDetailsWidget *m_predefinedDetails; - TextEditDetailsWidget *m_headerDetails; - QLineEdit *m_cxx11Flags; - QLineEdit *m_mkspecs; - QComboBox *m_errorParserComboBox; -}; - -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Interna; diff --git a/src/plugins/projectexplorer/customwizard/customwizard.h b/src/plugins/projectexplorer/customwizard/customwizard.h index ca98a2bffb3..c0a50a2fab7 100644 --- a/src/plugins/projectexplorer/customwizard/customwizard.h +++ b/src/plugins/projectexplorer/customwizard/customwizard.h @@ -28,13 +28,11 @@ class CustomWizardParameters; } // Documentation inside. -class PROJECTEXPLORER_EXPORT ICustomWizardMetaFactory : public QObject +class PROJECTEXPLORER_EXPORT ICustomWizardMetaFactory { - Q_OBJECT - public: ICustomWizardMetaFactory(const QString &klass, Core::IWizardFactory::WizardKind kind); - ~ICustomWizardMetaFactory() override; + virtual ~ICustomWizardMetaFactory(); virtual CustomWizard *create() const = 0; QString klass() const { return m_klass; } diff --git a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp index d25d59947e9..1337e2183f6 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardpage.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardpage.cpp @@ -225,7 +225,8 @@ QWidget *CustomWizardFieldPage::registerPathChooser(const QString &fieldName, pathChooser->setExpectedKind(PathChooser::Command); else if (expectedKind == QLatin1String("any")) pathChooser->setExpectedKind(PathChooser::Any); - pathChooser->setHistoryCompleter(QString::fromLatin1("PE.Custom.") + m_parameters->id.toString() + QLatin1Char('.') + field.name); + pathChooser->setHistoryCompleter(keyFromString("PE.Custom." + m_parameters->id.name() + + '.' + field.name)); registerField(fieldName, pathChooser, "path", SIGNAL(rawPathChanged(QString))); // Connect to completeChanged() for derived classes that reimplement isComplete() @@ -407,7 +408,7 @@ CustomWizardPage::CustomWizardPage(const QSharedPointer<CustomWizardContext> &ct CustomWizardFieldPage(ctx, parameters, parent), m_pathChooser(new PathChooser) { - m_pathChooser->setHistoryCompleter(QLatin1String("PE.ProjectDir.History")); + m_pathChooser->setHistoryCompleter("PE.ProjectDir.History"); addRow(Tr::tr("Path:"), m_pathChooser); connect(m_pathChooser, &PathChooser::validChanged, this, &QWizardPage::completeChanged); } diff --git a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp index c63181bc6e6..516bab9f574 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardparameters.cpp @@ -199,13 +199,13 @@ static inline QIcon wizardIcon(const QString &configFileFullPath, if (fi.isFile() && fi.isAbsolute()) return QIcon(fi.absoluteFilePath()); if (!fi.isRelative()) - return QIcon(); + return {}; // Expand by config path const QFileInfo absFi(QFileInfo(configFileFullPath).absolutePath() + QLatin1Char('/') + xmlIconFileName); if (absFi.isFile()) return QIcon(absFi.absoluteFilePath()); - return QIcon(); + return {}; } // Forward a reader over element text @@ -929,13 +929,13 @@ QString CustomWizardContext::processFile(const FieldReplacementMap &fm, QString if (!errorMessage.isEmpty()) { qWarning("Error processing custom widget file: %s\nFile:\n%s", qPrintable(errorMessage), qPrintable(in)); - return QString(); + return {}; } if (!Utils::TemplateEngine::preprocessText(in, &out, &errorMessage)) { qWarning("Error preprocessing custom widget file: %s\nFile:\n%s", qPrintable(errorMessage), qPrintable(in)); - return QString(); + return {}; } return out; } diff --git a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp index 7d1174839dd..bfc8f46ae9d 100644 --- a/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp +++ b/src/plugins/projectexplorer/customwizard/customwizardscriptgenerator.cpp @@ -27,7 +27,7 @@ namespace Internal { QStringList fixGeneratorScript(const QString &configFile, QString binary) { if (binary.isEmpty()) - return QStringList(); + return {}; // Expand to full path if it is relative and in the wizard // directory, else assume it can be found in path. QFileInfo binaryInfo(binary); diff --git a/src/plugins/projectexplorer/dependenciespanel.cpp b/src/plugins/projectexplorer/dependenciespanel.cpp index daac52b8d75..5c6990b67d8 100644 --- a/src/plugins/projectexplorer/dependenciespanel.cpp +++ b/src/plugins/projectexplorer/dependenciespanel.cpp @@ -81,7 +81,7 @@ QVariant DependenciesModel::data(const QModelIndex &index, int role) const case Qt::DecorationRole: return Utils::FileIconProvider::icon(p->projectFilePath()); default: - return QVariant(); + return {}; } } diff --git a/src/plugins/projectexplorer/deployconfiguration.cpp b/src/plugins/projectexplorer/deployconfiguration.cpp index d6a105682d3..418873ca024 100644 --- a/src/plugins/projectexplorer/deployconfiguration.cpp +++ b/src/plugins/projectexplorer/deployconfiguration.cpp @@ -5,7 +5,7 @@ #include "buildsteplist.h" #include "deploymentdataview.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" @@ -26,10 +26,9 @@ const char USES_DEPLOYMENT_DATA[] = "ProjectExplorer.DeployConfiguration.CustomD const char DEPLOYMENT_DATA[] = "ProjectExplorer.DeployConfiguration.CustomData"; DeployConfiguration::DeployConfiguration(Target *target, Id id) - : ProjectConfiguration(target, id), - m_stepList(this, Constants::BUILDSTEPS_DEPLOY) + : ProjectConfiguration(target, id) + , m_stepList(this, Constants::BUILDSTEPS_DEPLOY) { - QTC_CHECK(target && target == this->target()); //: Default DeployConfiguration display name setDefaultDisplayName(Tr::tr("Deploy locally")); } @@ -51,47 +50,50 @@ QWidget *DeployConfiguration::createConfigWidget() return m_configWidgetCreator(this); } -QVariantMap DeployConfiguration::toMap() const +void DeployConfiguration::toMap(Store &map) const { - QVariantMap map(ProjectConfiguration::toMap()); - map.insert(QLatin1String(BUILD_STEP_LIST_COUNT), 1); - map.insert(QLatin1String(BUILD_STEP_LIST_PREFIX) + QLatin1Char('0'), m_stepList.toMap()); + ProjectConfiguration::toMap(map); + map.insert(BUILD_STEP_LIST_COUNT, 1); + map.insert(Key(BUILD_STEP_LIST_PREFIX) + '0', variantFromStore(m_stepList.toMap())); map.insert(USES_DEPLOYMENT_DATA, usesCustomDeploymentData()); - QVariantMap deployData; + Store deployData; for (int i = 0; i < m_customDeploymentData.fileCount(); ++i) { const DeployableFile &f = m_customDeploymentData.fileAt(i); - deployData.insert(f.localFilePath().toString(), f.remoteDirectory()); + deployData.insert(keyFromString(f.localFilePath().toString()), f.remoteDirectory()); } - map.insert(DEPLOYMENT_DATA, deployData); - return map; + map.insert(DEPLOYMENT_DATA, variantFromStore(deployData)); } -bool DeployConfiguration::fromMap(const QVariantMap &map) +void DeployConfiguration::fromMap(const Store &map) { - if (!ProjectConfiguration::fromMap(map)) - return false; + ProjectConfiguration::fromMap(map); + if (hasError()) + return; - int maxI = map.value(QLatin1String(BUILD_STEP_LIST_COUNT), 0).toInt(); - if (maxI != 1) - return false; - QVariantMap data = map.value(QLatin1String(BUILD_STEP_LIST_PREFIX) + QLatin1Char('0')).toMap(); + int maxI = map.value(BUILD_STEP_LIST_COUNT, 0).toInt(); + if (maxI != 1) { + reportError(); + return; + } + Store data = storeFromVariant(map.value(Key(BUILD_STEP_LIST_PREFIX) + '0')); if (!data.isEmpty()) { m_stepList.clear(); if (!m_stepList.fromMap(data)) { qWarning() << "Failed to restore deploy step list"; m_stepList.clear(); - return false; + reportError(); + return; } } else { qWarning() << "No data for deploy step list found!"; - return false; + reportError(); + return; } m_usesCustomDeploymentData = map.value(USES_DEPLOYMENT_DATA, false).toBool(); - const QVariantMap deployData = map.value(DEPLOYMENT_DATA).toMap(); + const Store deployData = storeFromVariant(map.value(DEPLOYMENT_DATA)); for (auto it = deployData.begin(); it != deployData.end(); ++it) - m_customDeploymentData.addFile(FilePath::fromString(it.key()), it.value().toString()); - return true; + m_customDeploymentData.addFile(FilePath::fromString(stringFromKey(it.key())), it.value().toString()); } bool DeployConfiguration::isActive() const @@ -186,10 +188,12 @@ DeployConfiguration *DeployConfigurationFactory::create(Target *parent) DeployConfiguration *DeployConfigurationFactory::clone(Target *parent, const DeployConfiguration *source) { - return restore(parent, source->toMap()); + Store map; + source->toMap(map); + return restore(parent, map); } -DeployConfiguration *DeployConfigurationFactory::restore(Target *parent, const QVariantMap &map) +DeployConfiguration *DeployConfigurationFactory::restore(Target *parent, const Store &map) { const Id id = idFromMap(map); DeployConfigurationFactory *factory = Utils::findOrDefault(g_deployConfigurationFactories, @@ -202,7 +206,8 @@ DeployConfiguration *DeployConfigurationFactory::restore(Target *parent, const Q return nullptr; DeployConfiguration *dc = factory->createDeployConfiguration(parent); QTC_ASSERT(dc, return nullptr); - if (!dc->fromMap(map)) { + dc->fromMap(map); + if (dc->hasError()) { delete dc; dc = nullptr; } else if (factory->postRestore()) { @@ -240,16 +245,4 @@ void DeployConfigurationFactory::addInitialStep(Utils::Id stepId, const std::fun m_initialSteps.append({stepId, condition}); } -/// -// DefaultDeployConfigurationFactory -/// - -DefaultDeployConfigurationFactory::DefaultDeployConfigurationFactory() -{ - setConfigBaseId("ProjectExplorer.DefaultDeployConfiguration"); - addSupportedTargetDeviceType(Constants::DESKTOP_DEVICE_TYPE); - //: Display name of the default deploy configuration - setDefaultDisplayName(Tr::tr("Deploy Configuration")); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/deployconfiguration.h b/src/plugins/projectexplorer/deployconfiguration.h index e5a0f6d164a..a3ebac85b89 100644 --- a/src/plugins/projectexplorer/deployconfiguration.h +++ b/src/plugins/projectexplorer/deployconfiguration.h @@ -31,8 +31,8 @@ public: QWidget *createConfigWidget(); - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; bool isActive() const; @@ -66,7 +66,7 @@ public: DeployConfiguration *create(Target *parent); static const QList<DeployConfigurationFactory *> find(Target *parent); - static DeployConfiguration *restore(Target *parent, const QVariantMap &map); + static DeployConfiguration *restore(Target *parent, const Utils::Store &map); static DeployConfiguration *clone(Target *parent, const DeployConfiguration *dc); void addSupportedTargetDeviceType(Utils::Id id); @@ -81,7 +81,7 @@ public: void setConfigWidgetCreator(const DeployConfiguration::WidgetCreator &configWidgetCreator); void setUseDeploymentDataView(); - using PostRestore = std::function<void(DeployConfiguration *dc, const QVariantMap &)>; + using PostRestore = std::function<void(DeployConfiguration *dc, const Utils::Store &)>; void setPostRestore(const PostRestore &postRestore) { m_postRestore = postRestore; } PostRestore postRestore() const { return m_postRestore; } @@ -100,10 +100,4 @@ private: PostRestore m_postRestore; }; -class DefaultDeployConfigurationFactory : public DeployConfigurationFactory -{ -public: - DefaultDeployConfigurationFactory(); -}; - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/deploymentdataview.cpp b/src/plugins/projectexplorer/deploymentdataview.cpp index 5c13cb3acfb..c513c9cd19b 100644 --- a/src/plugins/projectexplorer/deploymentdataview.cpp +++ b/src/plugins/projectexplorer/deploymentdataview.cpp @@ -45,7 +45,7 @@ public: { if (role == Qt::DisplayRole || role == Qt::EditRole) return column == 0 ? file.localFilePath().toUserOutput() : file.remoteDirectory(); - return QVariant(); + return {}; } bool setData(int column, const QVariant &data, int role) override diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.cpp b/src/plugins/projectexplorer/desktoprunconfiguration.cpp index 8b8cdf2b793..afcfe3607c5 100644 --- a/src/plugins/projectexplorer/desktoprunconfiguration.cpp +++ b/src/plugins/projectexplorer/desktoprunconfiguration.cpp @@ -14,22 +14,53 @@ #include <qbsprojectmanager/qbsprojectmanagerconstants.h> #include <qmakeprojectmanager/qmakeprojectmanagerconstants.h> -#include <utils/fileutils.h> -#include <utils/pathchooser.h> -#include <utils/qtcassert.h> -#include <utils/stringutils.h> - using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { class DesktopRunConfiguration : public RunConfiguration { protected: enum Kind { Qmake, Qbs, CMake }; // FIXME: Remove - DesktopRunConfiguration(Target *target, Id id, Kind kind); + DesktopRunConfiguration(Target *target, Id id, Kind kind) + : RunConfiguration(target, id), m_kind(kind) + { + environment.setSupportForBuildEnvironment(target); + + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + connect(&useLibraryPaths, &UseLibraryPathsAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); + + if (HostOsInfo::isMacHost()) { + connect(&useDyldSuffix, &UseLibraryPathsAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); + environment.addModifier([this](Environment &env) { + if (useDyldSuffix()) + env.set(QLatin1String("DYLD_IMAGE_SUFFIX"), QLatin1String("_debug")); + }); + } else { + useDyldSuffix.setVisible(false); + } + + runAsRoot.setVisible(HostOsInfo::isAnyUnixHost()); + + environment.addModifier([this](Environment &env) { + BuildTargetInfo bti = buildTargetInfo(); + if (bti.runEnvModifier) + bti.runEnvModifier(env, useLibraryPaths()); + }); + + setUpdater([this] { updateTargetInformation(); }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + } private: void updateTargetInformation(); @@ -37,48 +68,16 @@ private: FilePath executableToRun(const BuildTargetInfo &targetInfo) const; const Kind m_kind; + EnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + UseDyldSuffixAspect useDyldSuffix{this}; + UseLibraryPathsAspect useLibraryPaths{this}; + RunAsRootAspect runAsRoot{this}; }; -DesktopRunConfiguration::DesktopRunConfiguration(Target *target, Id id, Kind kind) - : RunConfiguration(target, id), m_kind(kind) -{ - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); - - addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); - - auto libAspect = addAspect<UseLibraryPathsAspect>(); - connect(libAspect, &UseLibraryPathsAspect::changed, - envAspect, &EnvironmentAspect::environmentChanged); - - if (HostOsInfo::isMacHost()) { - auto dyldAspect = addAspect<UseDyldSuffixAspect>(); - connect(dyldAspect, &UseLibraryPathsAspect::changed, - envAspect, &EnvironmentAspect::environmentChanged); - envAspect->addModifier([dyldAspect](Environment &env) { - if (dyldAspect->value()) - env.set(QLatin1String("DYLD_IMAGE_SUFFIX"), QLatin1String("_debug")); - }); - } - - if (HostOsInfo::isAnyUnixHost()) - addAspect<RunAsRootAspect>(); - - envAspect->addModifier([this, libAspect](Environment &env) { - BuildTargetInfo bti = buildTargetInfo(); - if (bti.runEnvModifier) - bti.runEnvModifier(env, libAspect->value()); - }); - - - setUpdater([this] { updateTargetInformation(); }); - - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); -} - void DesktopRunConfiguration::updateTargetInformation() { if (!activeBuildSystem()) @@ -197,5 +196,4 @@ DesktopQmakeRunConfigurationFactory::DesktopQmakeRunConfigurationFactory() addSupportedTargetDeviceType(Docker::Constants::DOCKER_DEVICE_TYPE); } -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/desktoprunconfiguration.h b/src/plugins/projectexplorer/desktoprunconfiguration.h index 2a0d00f7a29..6a2203cace4 100644 --- a/src/plugins/projectexplorer/desktoprunconfiguration.h +++ b/src/plugins/projectexplorer/desktoprunconfiguration.h @@ -5,8 +5,7 @@ #include "runconfiguration.h" -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { class DesktopQmakeRunConfigurationFactory final : public RunConfigurationFactory { @@ -26,5 +25,4 @@ public: CMakeRunConfigurationFactory(); }; -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 099040d6b24..08880aff3b7 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -6,7 +6,6 @@ #include "../projectexplorerconstants.h" #include "../projectexplorertr.h" #include "desktopprocesssignaloperation.h" -#include "deviceprocesslist.h" #include "processlist.h" #include <coreplugin/fileutils.h> @@ -45,7 +44,7 @@ DesktopDevice::DesktopDevice() setupId(IDevice::AutoDetected, DESKTOP_DEVICE_ID); setType(DESKTOP_DEVICE_TYPE); - setDefaultDisplayName(Tr::tr("Local PC")); + settings()->displayName.setDefaultValue(Tr::tr("Local PC")); setDisplayType(Tr::tr("Desktop")); setDeviceState(IDevice::DeviceStateUnknown); @@ -56,17 +55,21 @@ DesktopDevice::DesktopDevice() = QString::fromLatin1("%1-%2").arg(DESKTOP_PORT_START).arg(DESKTOP_PORT_END); setFreePorts(Utils::PortList::fromString(portRange)); - setOpenTerminal([](const Environment &env, const FilePath &path) { + setOpenTerminal([](const Environment &env, const FilePath &path) -> expected_str<void> { const Environment realEnv = env.hasChanges() ? env : Environment::systemEnvironment(); - const FilePath shell = Terminal::defaultShellForDevice(path); + const expected_str<FilePath> shell = Terminal::defaultShellForDevice(path); + if (!shell) + return make_unexpected(shell.error()); Process process; process.setTerminalMode(TerminalMode::Detached); process.setEnvironment(realEnv); - process.setCommand({shell, {}}); + process.setCommand({*shell, {}}); process.setWorkingDirectory(path); process.start(); + + return {}; }); } @@ -74,7 +77,7 @@ DesktopDevice::~DesktopDevice() = default; IDevice::DeviceInfo DesktopDevice::deviceInformation() const { - return DeviceInfo(); + return {}; } IDeviceWidget *DesktopDevice::createWidget() @@ -90,11 +93,6 @@ bool DesktopDevice::canCreateProcessModel() const return true; } -DeviceProcessList *DesktopDevice::createProcessListModel(QObject *parent) const -{ - return new ProcessList(sharedFromThis(), parent); -} - DeviceProcessSignalOperation::Ptr DesktopDevice::signalOperation() const { return DeviceProcessSignalOperation::Ptr(new DesktopProcessSignalOperation()); @@ -123,7 +121,7 @@ FilePath DesktopDevice::filePath(const QString &pathOnDevice) const return FilePath::fromParts({}, {}, pathOnDevice); } -Environment DesktopDevice::systemEnvironment() const +expected_str<Environment> DesktopDevice::systemEnvironmentWithError() const { return Environment::systemEnvironment(); } @@ -131,8 +129,18 @@ Environment DesktopDevice::systemEnvironment() const FilePath DesktopDevice::rootPath() const { if (id() == DESKTOP_DEVICE_ID) - return FilePath::fromParts({}, {}, QDir::rootPath()); + return HostOsInfo::root(); return IDevice::rootPath(); } +void DesktopDevice::fromMap(const Store &map) +{ + IDevice::fromMap(map); + + const FilePath rsync = FilePath::fromString("rsync").searchInPath(); + const FilePath sftp = FilePath::fromString("sftp").searchInPath(); + setExtraData(Constants::SUPPORTS_RSYNC, rsync.isExecutableFile()); + setExtraData(Constants::SUPPORTS_SFTP, sftp.isExecutableFile()); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index d9cafbfda9a..437a4f8c074 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -26,17 +26,18 @@ public: IDeviceWidget *createWidget() override; bool canCreateProcessModel() const override; - DeviceProcessList *createProcessListModel(QObject *parent) const override; DeviceProcessSignalOperation::Ptr signalOperation() const override; QUrl toolControlChannel(const ControlChannelHint &) const override; bool usableAsBuildDevice() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - Utils::Environment systemEnvironment() const override; + Utils::expected_str<Utils::Environment> systemEnvironmentWithError() const override; Utils::FilePath rootPath() const override; Utils::FilePath filePath(const QString &pathOnDevice) const override; + void fromMap(const Utils::Store &map) override; + protected: DesktopDevice(); diff --git a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp index 36b502c5ad8..70412124462 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopprocesssignaloperation.cpp @@ -5,14 +5,12 @@ #include "../projectexplorertr.h" -#include <app/app_version.h> - #include <utils/winutils.h> #include <utils/fileutils.h> #include <utils/processinfo.h> -#include <QCoreApplication> #include <QDir> +#include <QGuiApplication> #include <QProcess> #ifdef Q_OS_WIN @@ -165,13 +163,13 @@ GDB 32bit | Api | Api | N/A | Win32 executable += si == Win32Interrupt ? QLatin1String("/win32interrupt.exe") : QLatin1String("/win64interrupt.exe"); - if (!QFile::exists(executable)) { + if (!QFileInfo::exists(executable)) { appendMsgCannotInterrupt(pid, Tr::tr("%1 does not exist. If you built %2 " - "yourself, check out https://code.qt.io/cgit/" - "qt-creator/binary-artifacts.git/.") + "yourself, check out https://code.qt.io/cgit/" + "qt-creator/binary-artifacts.git/.") .arg(QDir::toNativeSeparators(executable), - QString(Core::Constants::IDE_DISPLAY_NAME))); + QGuiApplication::applicationDisplayName())); } switch (QProcess::execute(executable, QStringList(QString::number(pid)))) { case -2: diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp index 84e3b274084..b01ecb47bc7 100644 --- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.cpp @@ -3,14 +3,15 @@ #include "devicecheckbuildstep.h" -#include "../kitinformation.h" +#include "../kitaspects.h" #include "../projectexplorerconstants.h" #include "../projectexplorertr.h" #include "devicemanager.h" -#include "idevice.h" #include "idevicefactory.h" +#include <solutions/tasking/tasktree.h> + #include <QMessageBox> namespace ProjectExplorer { @@ -27,39 +28,39 @@ public: bool init() override { IDevice::ConstPtr device = DeviceKitAspect::device(kit()); - if (!device) { - Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit()); - IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId); - if (!factory || !factory->canCreate()) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } + if (device) + return true; - QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"), - Tr::tr("There is no device set up for this kit. Do you want to add a device?"), - QMessageBox::Yes|QMessageBox::No); - msgBox.setDefaultButton(QMessageBox::Yes); - if (msgBox.exec() == QMessageBox::No) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } - - IDevice::Ptr newDevice = factory->create(); - if (newDevice.isNull()) { - emit addOutput(Tr::tr("No device configured."), BuildStep::OutputFormat::ErrorMessage); - return false; - } - - DeviceManager *dm = DeviceManager::instance(); - dm->addDevice(newDevice); - - DeviceKitAspect::setDevice(kit(), newDevice); + Utils::Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(kit()); + IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId); + if (!factory || !factory->canCreate()) { + emit addOutput(Tr::tr("No device configured."), OutputFormat::ErrorMessage); + return false; } + QMessageBox msgBox(QMessageBox::Question, Tr::tr("Set Up Device"), + Tr::tr("There is no device set up for this kit. Do you want to add a device?"), + QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + if (msgBox.exec() == QMessageBox::No) { + emit addOutput(Tr::tr("No device configured."), OutputFormat::ErrorMessage); + return false; + } + + IDevice::Ptr newDevice = factory->create(); + if (newDevice.isNull()) { + emit addOutput(Tr::tr("No device configured."), OutputFormat::ErrorMessage); + return false; + } + + DeviceManager *dm = DeviceManager::instance(); + dm->addDevice(newDevice); + DeviceKitAspect::setDevice(kit(), newDevice); return true; } - void doRun() override { emit finished(true); } +private: + Tasking::GroupItem runRecipe() final { return Tasking::Group{}; } }; // Factory diff --git a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h index 6b7a8edb11b..d93227e264a 100644 --- a/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h +++ b/src/plugins/projectexplorer/devicesupport/devicecheckbuildstep.h @@ -4,11 +4,10 @@ #pragma once #include "../buildstep.h" -#include "../projectexplorer_export.h" namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT DeviceCheckBuildStepFactory : public BuildStepFactory +class DeviceCheckBuildStepFactory : public BuildStepFactory { public: DeviceCheckBuildStepFactory(); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index aa017df46b0..2a934c5b9f6 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -115,6 +115,11 @@ DeviceManager *DeviceManager::cloneInstance() return DeviceManagerPrivate::clonedInstance; } +DeviceManager *DeviceManager::clonedInstance() +{ + return DeviceManagerPrivate::clonedInstance; +} + void DeviceManager::copy(const DeviceManager *source, DeviceManager *target, bool deep) { if (deep) { @@ -130,8 +135,8 @@ void DeviceManager::save() { if (d->clonedInstance == this || !d->writer) return; - QVariantMap data; - data.insert(QLatin1String(DeviceManagerKey), toMap()); + Store data; + data.insert(DeviceManagerKey, variantFromStore(toMap())); d->writer->save(data, Core::ICore::dialogParent()); } @@ -157,11 +162,11 @@ void DeviceManager::load() QHash<Id, Id> defaultDevices; QList<IDevice::Ptr> sdkDevices; if (reader.load(systemSettingsFilePath("devices.xml"))) - sdkDevices = fromMap(reader.restoreValues().value(DeviceManagerKey).toMap(), &defaultDevices); + sdkDevices = fromMap(storeFromVariant(reader.restoreValues().value(DeviceManagerKey)), &defaultDevices); // read devices file from user settings path QList<IDevice::Ptr> userDevices; if (reader.load(settingsFilePath("devices.xml"))) - userDevices = fromMap(reader.restoreValues().value(DeviceManagerKey).toMap(), &defaultDevices); + userDevices = fromMap(storeFromVariant(reader.restoreValues().value(DeviceManagerKey)), &defaultDevices); // Insert devices into the model. Prefer the higher device version when there are multiple // devices with the same id. for (IDevice::ConstPtr device : std::as_const(userDevices)) { @@ -189,7 +194,7 @@ void DeviceManager::load() emit devicesLoaded(); } -static const IDeviceFactory *restoreFactory(const QVariantMap &map) +static const IDeviceFactory *restoreFactory(const Store &map) { const Id deviceType = IDevice::typeFromMap(map); IDeviceFactory *factory = Utils::findOrDefault(IDeviceFactory::allDeviceFactories(), @@ -204,18 +209,18 @@ static const IDeviceFactory *restoreFactory(const QVariantMap &map) return factory; } -QList<IDevice::Ptr> DeviceManager::fromMap(const QVariantMap &map, QHash<Id, Id> *defaultDevices) +QList<IDevice::Ptr> DeviceManager::fromMap(const Store &map, QHash<Id, Id> *defaultDevices) { QList<IDevice::Ptr> devices; if (defaultDevices) { - const QVariantMap defaultDevsMap = map.value(DefaultDevicesKey).toMap(); + const Store defaultDevsMap = storeFromVariant(map.value(DefaultDevicesKey)); for (auto it = defaultDevsMap.constBegin(); it != defaultDevsMap.constEnd(); ++it) - defaultDevices->insert(Id::fromString(it.key()), Id::fromSetting(it.value())); + defaultDevices->insert(Id::fromString(stringFromKey(it.key())), Id::fromSetting(it.value())); } - const QVariantList deviceList = map.value(QLatin1String(DeviceListKey)).toList(); + const QVariantList deviceList = map.value(DeviceListKey).toList(); for (const QVariant &v : deviceList) { - const QVariantMap map = v.toMap(); + const Store map = storeFromVariant(v); const IDeviceFactory * const factory = restoreFactory(map); if (!factory) continue; @@ -227,18 +232,18 @@ QList<IDevice::Ptr> DeviceManager::fromMap(const QVariantMap &map, QHash<Id, Id> return devices; } -QVariantMap DeviceManager::toMap() const +Store DeviceManager::toMap() const { - QVariantMap map; - QVariantMap defaultDeviceMap; + Store map; + Store defaultDeviceMap; for (auto it = d->defaultDevices.constBegin(); it != d->defaultDevices.constEnd(); ++it) - defaultDeviceMap.insert(it.key().toString(), it.value().toSetting()); + defaultDeviceMap.insert(keyFromString(it.key().toString()), it.value().toSetting()); - map.insert(QLatin1String(DefaultDevicesKey), defaultDeviceMap); + map.insert(DefaultDevicesKey, variantFromStore(defaultDeviceMap)); QVariantList deviceList; for (const IDevice::Ptr &device : std::as_const(d->devices)) - deviceList << device->toMap(); - map.insert(QLatin1String(DeviceListKey), deviceList); + deviceList << variantFromStore(device->toMap()); + map.insert(DeviceListKey, deviceList); return map; } @@ -253,7 +258,8 @@ void DeviceManager::addDevice(const IDevice::ConstPtr &_device) } // TODO: make it thread safe? - device->setDisplayName(Utils::makeUniquelyNumbered(device->displayName(), names)); + device->settings()->displayName.setValue( + Utils::makeUniquelyNumbered(device->displayName(), names)); const int pos = d->indexForId(device->id()); @@ -411,24 +417,33 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager deviceHooks.fileAccess = [](const FilePath &filePath) -> expected_str<DeviceFileAccess *> { if (!filePath.needsDevice()) return DesktopDeviceFileAccess::instance(); + IDevice::ConstPtr device = DeviceManager::deviceForPath(filePath); + if (!device) { + return make_unexpected( + Tr::tr("No device found for path \"%1\"").arg(filePath.toUserOutput())); + } + DeviceFileAccess *fileAccess = device->fileAccess(); + if (!fileAccess) { + return make_unexpected( + Tr::tr("No file access for device \"%1\"").arg(device->displayName())); + } + return fileAccess; + }; + + deviceHooks.environment = [](const FilePath &filePath) -> expected_str<Environment> { auto device = DeviceManager::deviceForPath(filePath); if (!device) { return make_unexpected( - QString("No device found for path \"%1\"").arg(filePath.toUserOutput())); + Tr::tr("No device found for path \"%1\"").arg(filePath.toUserOutput())); } - return device->fileAccess(); - }; - - deviceHooks.environment = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, qDebug() << filePath.toString(); return Environment{}); - return device->systemEnvironment(); + return device->systemEnvironmentWithError(); }; deviceHooks.deviceDisplayName = [](const FilePath &filePath) { auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return filePath.toUserOutput()); - return device->displayName(); + if (device) + return device->displayName(); + return filePath.host().toString(); }; deviceHooks.ensureReachable = [](const FilePath &filePath, const FilePath &other) { @@ -458,29 +473,7 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager return device->createProcessInterface(); }; - processHooks.systemEnvironmentForBinary = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return Environment()); - return device->systemEnvironment(); - }; - Process::setRemoteProcessHooks(processHooks); - - Terminal::Hooks::instance().getTerminalCommandsForDevicesHook().set( - [this]() -> QList<Terminal::NameAndCommandLine> { - QList<Terminal::NameAndCommandLine> result; - for (const IDevice::ConstPtr device : d->devices) { - if (device->type() == Constants::DESKTOP_DEVICE_TYPE) - continue; - - const FilePath shell = Terminal::defaultShellForDevice(device->rootPath()); - - if (!shell.isEmpty()) - result << Terminal::NameAndCommandLine{device->displayName(), - CommandLine{shell, {}}}; - } - return result; - }); } DeviceManager::~DeviceManager() @@ -497,6 +490,14 @@ IDevice::ConstPtr DeviceManager::deviceAt(int idx) const return d->devices.at(idx); } +void DeviceManager::forEachDevice(const std::function<void(const IDeviceConstPtr &)> &func) const +{ + const QList<IDevice::Ptr> devices = d->deviceList(); + + for (const IDevice::Ptr &device : devices) + func(device); +} + IDevice::Ptr DeviceManager::mutableDevice(Id id) const { const int index = d->indexForId(id); @@ -565,7 +566,7 @@ void ProjectExplorerPlugin::testDeviceManager() TestDeviceFactory factory; TestDevice::Ptr dev = IDevice::Ptr(new TestDevice); - dev->setDisplayName(QLatin1String("blubbdiblubbfurz!")); + dev->settings()->displayName.setValue(QLatin1String("blubbdiblubbfurz!")); QVERIFY(dev->isAutoDetected()); QCOMPARE(dev->deviceState(), IDevice::DeviceStateUnknown); QCOMPARE(dev->type(), TestDevice::testTypeId()); @@ -626,7 +627,7 @@ void ProjectExplorerPlugin::testDeviceManager() TestDevice::Ptr dev3 = IDevice::Ptr(new TestDevice); QVERIFY(dev->id() != dev3->id()); - dev3->setDisplayName(dev->displayName()); + dev3->settings()->displayName.setValue(dev->displayName()); mgr->addDevice(dev3); QCOMPARE(mgr->deviceAt(mgr->deviceCount() - 1)->displayName(), QString(dev3->displayName() + QLatin1Char('2'))); diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.h b/src/plugins/projectexplorer/devicesupport/devicemanager.h index 213624971b8..6d7eeae1ee0 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.h +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.h @@ -31,10 +31,13 @@ public: ~DeviceManager() override; static DeviceManager *instance(); + static DeviceManager *clonedInstance(); int deviceCount() const; IDevice::ConstPtr deviceAt(int index) const; + void forEachDevice(const std::function<void(const IDeviceConstPtr &)> &) const; + IDevice::ConstPtr find(Utils::Id id) const; IDevice::ConstPtr defaultDevice(Utils::Id deviceType) const; bool hasDevice(const QString &name) const; @@ -63,8 +66,8 @@ private: DeviceManager(bool isInstance = true); void load(); - QList<IDevice::Ptr> fromMap(const QVariantMap &map, QHash<Utils::Id, Utils::Id> *defaultDevices); - QVariantMap toMap() const; + QList<IDevice::Ptr> fromMap(const Utils::Store &map, QHash<Utils::Id, Utils::Id> *defaultDevices); + Utils::Store toMap() const; // For SettingsWidget. IDevice::Ptr mutableDevice(Utils::Id id) const; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp index 30e12354202..edb9999d6a0 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanagermodel.cpp @@ -141,9 +141,9 @@ int DeviceManagerModel::rowCount(const QModelIndex &parent) const QVariant DeviceManagerModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= rowCount()) - return QVariant(); + return {}; if (role != Qt::DisplayRole && role != Qt::UserRole) - return QVariant(); + return {}; const IDevice::ConstPtr dev = device(index.row()); if (role == Qt::UserRole) return dev->id().toSetting(); diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp index 4c75ea39899..0a7d7942400 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp +++ b/src/plugins/projectexplorer/devicesupport/deviceprocessesdialog.cpp @@ -3,10 +3,10 @@ #include "deviceprocessesdialog.h" -#include "deviceprocesslist.h" #include "idevice.h" +#include "processlist.h" +#include "../kitaspects.h" #include "../kitchooser.h" -#include "../kitinformation.h" #include "../projectexplorertr.h" #include <utils/fancylineedit.h> @@ -84,7 +84,7 @@ public: ProcessInfo selectedProcess() const; QDialog *q; - std::unique_ptr<DeviceProcessList> processList; + std::unique_ptr<ProcessList> processList; ProcessListFilterModel proxyModel; QLabel *kitLabel; KitChooser *kitChooser; @@ -111,7 +111,7 @@ DeviceProcessesDialogPrivate::DeviceProcessesDialogPrivate(KitChooser *chooser, processFilterLineEdit = new FancyLineEdit(q); processFilterLineEdit->setPlaceholderText(Tr::tr("Filter")); processFilterLineEdit->setFocus(Qt::TabFocusReason); - processFilterLineEdit->setHistoryCompleter(QLatin1String("DeviceProcessDialogFilter"), + processFilterLineEdit->setHistoryCompleter("DeviceProcessDialogFilter", true /*restoreLastItemFromHistory*/); processFilterLineEdit->setFiltering(true); @@ -121,7 +121,6 @@ DeviceProcessesDialogPrivate::DeviceProcessesDialogPrivate(KitChooser *chooser, procView->setModel(&proxyModel); procView->setSelectionBehavior(QAbstractItemView::SelectRows); procView->setSelectionMode(QAbstractItemView::SingleSelection); - procView->setUniformRowHeights(true); procView->setRootIsDecorated(false); procView->setAlternatingRowColors(true); procView->setSortingEnabled(true); @@ -187,15 +186,16 @@ void DeviceProcessesDialogPrivate::setDevice(const IDevice::ConstPtr &device) if (!device) return; - processList.reset(device->createProcessListModel()); + processList.reset(new ProcessList(device->sharedFromThis(), this)); + QTC_ASSERT(processList, return); proxyModel.setSourceModel(processList->model()); - connect(processList.get(), &DeviceProcessList::error, + connect(processList.get(), &ProcessList::error, this, &DeviceProcessesDialogPrivate::handleRemoteError); - connect(processList.get(), &DeviceProcessList::processListUpdated, + connect(processList.get(), &ProcessList::processListUpdated, this, &DeviceProcessesDialogPrivate::handleProcessListUpdated); - connect(processList.get(), &DeviceProcessList::processKilled, + connect(processList.get(), &ProcessList::processKilled, this, &DeviceProcessesDialogPrivate::handleProcessKilled, Qt::QueuedConnection); updateButtons(); @@ -258,7 +258,7 @@ ProcessInfo DeviceProcessesDialogPrivate::selectedProcess() const { const QModelIndexList indexes = procView->selectionModel()->selectedIndexes(); if (indexes.empty() || !processList) - return ProcessInfo(); + return {}; return processList->at(proxyModel.mapToSource(indexes.first()).row()); } diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp deleted file mode 100644 index 6645a478ecb..00000000000 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "deviceprocesslist.h" - -#include "idevice.h" -#include "../projectexplorertr.h" - -#include <utils/processinfo.h> -#include <utils/qtcassert.h> -#include <utils/treemodel.h> - -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -enum State { Inactive, Listing, Killing }; - -class DeviceProcessTreeItem : public TreeItem -{ -public: - DeviceProcessTreeItem(const ProcessInfo &p, Qt::ItemFlags f) : process(p), fl(f) {} - - QVariant data(int column, int role) const final; - Qt::ItemFlags flags(int) const final { return fl; } - - ProcessInfo process; - Qt::ItemFlags fl; -}; - -class DeviceProcessListPrivate -{ -public: - DeviceProcessListPrivate(const IDevice::ConstPtr &device) - : device(device) - { } - - qint64 ownPid = -1; - const IDevice::ConstPtr device; - State state = Inactive; - TreeModel<TypedTreeItem<DeviceProcessTreeItem>, DeviceProcessTreeItem> model; -}; - -} // namespace Internal - -using namespace Internal; - -DeviceProcessList::DeviceProcessList(const IDevice::ConstPtr &device, QObject *parent) - : QObject(parent), d(std::make_unique<DeviceProcessListPrivate>(device)) -{ - d->model.setHeader({Tr::tr("Process ID"), Tr::tr("Command Line")}); -} - -DeviceProcessList::~DeviceProcessList() = default; - -void DeviceProcessList::update() -{ - QTC_ASSERT(d->state == Inactive, return); - QTC_ASSERT(device(), return); - - d->model.clear(); - d->model.rootItem()->appendChild( - new DeviceProcessTreeItem( - {0, Tr::tr("Fetching process list. This might take a while."), ""}, - Qt::NoItemFlags)); - d->state = Listing; - doUpdate(); -} - -void DeviceProcessList::reportProcessListUpdated(const QList<ProcessInfo> &processes) -{ - QTC_ASSERT(d->state == Listing, return); - setFinished(); - d->model.clear(); - for (const ProcessInfo &process : processes) { - Qt::ItemFlags fl; - if (process.processId != d->ownPid) - fl = Qt::ItemIsEnabled | Qt::ItemIsSelectable; - d->model.rootItem()->appendChild(new DeviceProcessTreeItem(process, fl)); - } - - emit processListUpdated(); -} - -void DeviceProcessList::killProcess(int row) -{ - QTC_ASSERT(row >= 0 && row < d->model.rootItem()->childCount(), return); - QTC_ASSERT(d->state == Inactive, return); - QTC_ASSERT(device(), return); - - d->state = Killing; - doKillProcess(at(row)); -} - -void DeviceProcessList::setOwnPid(qint64 pid) -{ - d->ownPid = pid; -} - -void DeviceProcessList::reportProcessKilled() -{ - QTC_ASSERT(d->state == Killing, return); - setFinished(); - emit processKilled(); -} - -ProcessInfo DeviceProcessList::at(int row) const -{ - return d->model.rootItem()->childAt(row)->process; -} - -QAbstractItemModel *DeviceProcessList::model() const -{ - return &d->model; -} - -QVariant DeviceProcessTreeItem::data(int column, int role) const -{ - if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { - if (column == 0) - return process.processId ? process.processId : QVariant(); - else - return process.commandLine; - } - return QVariant(); -} - -void DeviceProcessList::setFinished() -{ - d->state = Inactive; -} - -IDevice::ConstPtr DeviceProcessList::device() const -{ - return d->device; -} - -void DeviceProcessList::reportError(const QString &message) -{ - QTC_ASSERT(d->state != Inactive, return); - setFinished(); - emit error(message); -} - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h b/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h deleted file mode 100644 index 1faa5c19881..00000000000 --- a/src/plugins/projectexplorer/devicesupport/deviceprocesslist.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "../projectexplorer_export.h" -#include "idevicefwd.h" - -#include <QAbstractItemModel> -#include <QList> - -#include <memory> - -namespace Utils { class ProcessInfo; } - -namespace ProjectExplorer { - -namespace Internal { class DeviceProcessListPrivate; } - -class PROJECTEXPLORER_EXPORT DeviceProcessList : public QObject -{ - Q_OBJECT - -public: - DeviceProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); - ~DeviceProcessList() override; - - void update(); - void killProcess(int row); - void setOwnPid(qint64 pid); - - Utils::ProcessInfo at(int row) const; - QAbstractItemModel *model() const; - -signals: - void processListUpdated(); - void error(const QString &errorMsg); - void processKilled(); - -protected: - void reportError(const QString &message); - void reportProcessKilled(); - void reportProcessListUpdated(const QList<Utils::ProcessInfo> &processes); - - IDeviceConstPtr device() const; - -private: - virtual void doUpdate() = 0; - virtual void doKillProcess(const Utils::ProcessInfo &process) = 0; - - void setFinished(); - - const std::unique_ptr<Internal::DeviceProcessListPrivate> d; -}; - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp index 6fdb19f38d1..81e0eec15ab 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.cpp @@ -3,14 +3,411 @@ #include "devicesettingspage.h" -#include "devicesettingswidget.h" +#include "devicefactoryselectiondialog.h" +#include "devicemanager.h" +#include "devicemanagermodel.h" +#include "deviceprocessesdialog.h" +#include "devicetestdialog.h" +#include "idevice.h" +#include "idevicefactory.h" +#include "idevicewidget.h" #include "../projectexplorerconstants.h" +#include "../projectexplorericons.h" #include "../projectexplorertr.h" -#include <QCoreApplication> +#include <coreplugin/icore.h> -namespace ProjectExplorer { -namespace Internal { +#include <utils/algorithm.h> +#include <utils/async.h> +#include <utils/layoutbuilder.h> +#include <utils/optionpushbutton.h> +#include <utils/qtcassert.h> + +#include <QComboBox> +#include <QGroupBox> +#include <QLabel> +#include <QLineEdit> +#include <QMenu> +#include <QPushButton> +#include <QScrollArea> +#include <QTextStream> +#include <QVBoxLayout> +#include <QValidator> + +#include <algorithm> + +using namespace Core; +using namespace Utils; + +namespace ProjectExplorer::Internal { + +const char LastDeviceIndexKey[] = "LastDisplayedMaemoDeviceConfig"; + +class DeviceSettingsWidget final : public Core::IOptionsPageWidget +{ +public: + DeviceSettingsWidget(); + ~DeviceSettingsWidget() final + { + DeviceManager::removeClonedInstance(); + delete m_configWidget; + } + +private: + void apply() final { saveSettings(); } + + void saveSettings(); + + void handleDeviceUpdated(Utils::Id id); + void currentDeviceChanged(int index); + void addDevice(); + void removeDevice(); + void setDefaultDevice(); + void testDevice(); + void handleProcessListRequested(); + + void initGui(); + void displayCurrent(); + void setDeviceInfoWidgetsEnabled(bool enable); + IDeviceConstPtr currentDevice() const; + int currentIndex() const; + void clearDetails(); + QString parseTestOutput(); + void updateDeviceFromUi(); + + DeviceManager * const m_deviceManager; + DeviceManagerModel * const m_deviceManagerModel; + QList<QPushButton *> m_additionalActionButtons; + IDeviceWidget *m_configWidget; + + QLabel *m_configurationLabel; + QComboBox *m_configurationComboBox; + QGroupBox *m_generalGroupBox; + QLabel *m_osTypeValueLabel; + QLabel *m_autoDetectionLabel; + QLabel *m_deviceStateIconLabel; + QLabel *m_deviceStateTextLabel; + QGroupBox *m_osSpecificGroupBox; + QPushButton *m_removeConfigButton; + QPushButton *m_defaultDeviceButton; + QVBoxLayout *m_buttonsLayout; + QWidget *m_deviceNameEditWidget; + QFormLayout *m_generalFormLayout; +}; + +DeviceSettingsWidget::DeviceSettingsWidget() + : m_deviceManager(DeviceManager::cloneInstance()) + , m_deviceManagerModel(new DeviceManagerModel(m_deviceManager, this)) + , m_configWidget(nullptr) +{ + m_configurationLabel = new QLabel(Tr::tr("&Device:")); + m_configurationComboBox = new QComboBox; + m_configurationComboBox->setModel(m_deviceManagerModel); + m_generalGroupBox = new QGroupBox(Tr::tr("General")); + m_osTypeValueLabel = new QLabel; + m_autoDetectionLabel = new QLabel; + m_deviceStateIconLabel = new QLabel; + m_deviceStateTextLabel = new QLabel; + m_osSpecificGroupBox = new QGroupBox(Tr::tr("Type Specific")); + m_osSpecificGroupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + m_removeConfigButton = new QPushButton(Tr::tr("&Remove")); + m_defaultDeviceButton = new QPushButton(Tr::tr("Set As Default")); + + OptionPushButton *addButton = new OptionPushButton(Tr::tr("&Add...")); + connect(addButton, &OptionPushButton::clicked, this, &DeviceSettingsWidget::addDevice); + + QMenu *deviceTypeMenu = new QMenu(addButton); + QAction *defaultAction = new QAction(Tr::tr("&Start Wizard to Add Device...")); + connect(defaultAction, &QAction::triggered, this, &DeviceSettingsWidget::addDevice); + deviceTypeMenu->addAction(defaultAction); + deviceTypeMenu->addSeparator(); + + for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) { + if (!factory->canCreate()) + continue; + if (!factory->quickCreationAllowed()) + continue; + + //: Add <Device Type Name> + QAction *action = new QAction(Tr::tr("Add %1").arg(factory->displayName())); + deviceTypeMenu->addAction(action); + + connect(action, &QAction::triggered, this, [factory, this] { + IDevice::Ptr device = factory->construct(); + QTC_ASSERT(device, return); + m_deviceManager->addDevice(device); + m_removeConfigButton->setEnabled(true); + m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); + saveSettings(); + }); + } + + addButton->setOptionalMenu(deviceTypeMenu); + + m_buttonsLayout = new QVBoxLayout; + m_buttonsLayout->setContentsMargins({}); + auto scrollAreaWidget = new QWidget; + auto scrollArea = new QScrollArea; + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(scrollAreaWidget); + + using namespace Layouting; + Column { + m_generalGroupBox, + m_osSpecificGroupBox, + }.attachTo(scrollAreaWidget); + + // Just a placeholder for the device name edit widget. + m_deviceNameEditWidget = new QWidget(); + + // clang-format off + Form { + bindTo(&m_generalFormLayout), + Tr::tr("&Name:"), m_deviceNameEditWidget, br, + Tr::tr("Type:"), m_osTypeValueLabel, br, + Tr::tr("Auto-detected:"), m_autoDetectionLabel, br, + Tr::tr("Current state:"), Row { m_deviceStateIconLabel, m_deviceStateTextLabel, st, }, br, + }.attachTo(m_generalGroupBox); + + Row { + Column { + Form { m_configurationLabel, m_configurationComboBox, br, }, + scrollArea, + }, + Column { + addButton, + Space(30), + m_removeConfigButton, + m_defaultDeviceButton, + m_buttonsLayout, + st, + }, + }.attachTo(this); + // clang-format on + + bool hasDeviceFactories = Utils::anyOf(IDeviceFactory::allDeviceFactories(), + &IDeviceFactory::canCreate); + + addButton->setEnabled(hasDeviceFactories); + + int lastIndex = ICore::settings()->value(LastDeviceIndexKey, 0).toInt(); + if (lastIndex == -1) + lastIndex = 0; + if (lastIndex < m_configurationComboBox->count()) + m_configurationComboBox->setCurrentIndex(lastIndex); + + connect(m_configurationComboBox, &QComboBox::currentIndexChanged, + this, &DeviceSettingsWidget::currentDeviceChanged); + currentDeviceChanged(currentIndex()); + connect(m_defaultDeviceButton, &QAbstractButton::clicked, + this, &DeviceSettingsWidget::setDefaultDevice); + connect(m_removeConfigButton, &QAbstractButton::clicked, + this, &DeviceSettingsWidget::removeDevice); + connect(m_deviceManager, &DeviceManager::deviceUpdated, + this, &DeviceSettingsWidget::handleDeviceUpdated); +} + +void DeviceSettingsWidget::addDevice() +{ + DeviceFactorySelectionDialog d; + if (d.exec() != QDialog::Accepted) + return; + + Id toCreate = d.selectedId(); + if (!toCreate.isValid()) + return; + IDeviceFactory *factory = IDeviceFactory::find(toCreate); + if (!factory) + return; + IDevice::Ptr device = factory->create(); + if (device.isNull()) + return; + + Utils::asyncRun([device] { device->checkOsType(); }); + + m_deviceManager->addDevice(device); + m_removeConfigButton->setEnabled(true); + m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); + saveSettings(); + if (device->hasDeviceTester()) + testDevice(); +} + +void DeviceSettingsWidget::removeDevice() +{ + m_deviceManager->removeDevice(currentDevice()->id()); + if (m_deviceManager->deviceCount() == 0) + currentDeviceChanged(-1); +} + +void DeviceSettingsWidget::displayCurrent() +{ + const IDevice::ConstPtr ¤t = currentDevice(); + m_defaultDeviceButton->setEnabled( + m_deviceManager->defaultDevice(current->type()) != current); + m_osTypeValueLabel->setText(current->displayType()); + m_autoDetectionLabel->setText(current->isAutoDetected() + ? Tr::tr("Yes (id is \"%1\")").arg(current->id().toString()) : Tr::tr("No")); + m_deviceStateIconLabel->show(); + switch (current->deviceState()) { + case IDevice::DeviceReadyToUse: + m_deviceStateIconLabel->setPixmap(Icons::DEVICE_READY_INDICATOR.pixmap()); + break; + case IDevice::DeviceConnected: + m_deviceStateIconLabel->setPixmap(Icons::DEVICE_CONNECTED_INDICATOR.pixmap()); + break; + case IDevice::DeviceDisconnected: + m_deviceStateIconLabel->setPixmap(Icons::DEVICE_DISCONNECTED_INDICATOR.pixmap()); + break; + case IDevice::DeviceStateUnknown: + m_deviceStateIconLabel->hide(); + break; + } + m_deviceStateTextLabel->setText(current->deviceStateToString()); + + m_removeConfigButton->setEnabled(!current->isAutoDetected() + || current->deviceState() == IDevice::DeviceDisconnected); +} + +void DeviceSettingsWidget::setDeviceInfoWidgetsEnabled(bool enable) +{ + m_configurationLabel->setEnabled(enable); + m_configurationComboBox->setEnabled(enable); + m_generalGroupBox->setEnabled(enable); + m_osSpecificGroupBox->setEnabled(enable); +} + +void DeviceSettingsWidget::updateDeviceFromUi() +{ + currentDevice()->settings()->apply(); + if (m_configWidget) + m_configWidget->updateDeviceFromUi(); +} + +void DeviceSettingsWidget::saveSettings() +{ + updateDeviceFromUi(); + ICore::settings()->setValueWithDefault(LastDeviceIndexKey, currentIndex(), 0); + DeviceManager::replaceInstance(); +} + +int DeviceSettingsWidget::currentIndex() const +{ + return m_configurationComboBox->currentIndex(); +} + +IDevice::ConstPtr DeviceSettingsWidget::currentDevice() const +{ + Q_ASSERT(currentIndex() != -1); + return m_deviceManagerModel->device(currentIndex()); +} + + +void DeviceSettingsWidget::setDefaultDevice() +{ + m_deviceManager->setDefaultDevice(currentDevice()->id()); + m_defaultDeviceButton->setEnabled(false); +} + +void DeviceSettingsWidget::testDevice() +{ + const IDevice::ConstPtr &device = currentDevice(); + QTC_ASSERT(device && device->hasDeviceTester(), return); + auto dlg = new DeviceTestDialog(m_deviceManager->mutableDevice(device->id()), this); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setModal(true); + dlg->show(); +} + +void DeviceSettingsWidget::handleDeviceUpdated(Id id) +{ + const int index = m_deviceManagerModel->indexForId(id); + if (index == currentIndex()) + currentDeviceChanged(index); +} + +void DeviceSettingsWidget::currentDeviceChanged(int index) +{ + qDeleteAll(m_additionalActionButtons); + delete m_configWidget; + m_configWidget = nullptr; + m_additionalActionButtons.clear(); + const IDevice::ConstPtr device = m_deviceManagerModel->device(index); + if (device.isNull()) { + setDeviceInfoWidgetsEnabled(false); + m_removeConfigButton->setEnabled(false); + clearDetails(); + m_defaultDeviceButton->setEnabled(false); + return; + } + + Layouting::Column item{Layouting::noMargin()}; + device->settings()->displayName.addToLayout(item); + QWidget *newEdit = item.emerge(); + m_generalFormLayout->replaceWidget(m_deviceNameEditWidget, newEdit); + + delete m_deviceNameEditWidget; + m_deviceNameEditWidget = newEdit; + + setDeviceInfoWidgetsEnabled(true); + m_removeConfigButton->setEnabled(true); + + if (device->hasDeviceTester()) { + QPushButton * const button = new QPushButton(Tr::tr("Test")); + m_additionalActionButtons << button; + connect(button, &QAbstractButton::clicked, this, &DeviceSettingsWidget::testDevice); + m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); + } + + if (device->canCreateProcessModel()) { + QPushButton * const button = new QPushButton(Tr::tr("Show Running Processes...")); + m_additionalActionButtons << button; + connect(button, &QAbstractButton::clicked, + this, &DeviceSettingsWidget::handleProcessListRequested); + m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); + } + + for (const IDevice::DeviceAction &deviceAction : device->deviceActions()) { + QPushButton * const button = new QPushButton(deviceAction.display); + m_additionalActionButtons << button; + connect(button, &QAbstractButton::clicked, this, [this, deviceAction] { + const IDevice::Ptr device = m_deviceManager->mutableDevice(currentDevice()->id()); + QTC_ASSERT(device, return); + updateDeviceFromUi(); + deviceAction.execute(device, this); + // Widget must be set up from scratch, because the action could have + // changed random attributes. + currentDeviceChanged(currentIndex()); + }); + + m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); + } + + if (!m_osSpecificGroupBox->layout()) + new QVBoxLayout(m_osSpecificGroupBox); + m_configWidget = m_deviceManager->mutableDevice(device->id())->createWidget(); + if (m_configWidget) + m_osSpecificGroupBox->layout()->addWidget(m_configWidget); + displayCurrent(); +} + +void DeviceSettingsWidget::clearDetails() +{ + m_osTypeValueLabel->clear(); + m_autoDetectionLabel->clear(); +} + +void DeviceSettingsWidget::handleProcessListRequested() +{ + QTC_ASSERT(currentDevice()->canCreateProcessModel(), return); + updateDeviceFromUi(); + DeviceProcessesDialog dlg; + dlg.addCloseButton(); + dlg.setDevice(currentDevice()); + dlg.exec(); +} + +// DeviceSettingsPage DeviceSettingsPage::DeviceSettingsPage() { @@ -22,5 +419,4 @@ DeviceSettingsPage::DeviceSettingsPage() setWidgetCreator([] { return new DeviceSettingsWidget; }); } -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingspage.h b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h index 202f5010815..f154d91ce57 100644 --- a/src/plugins/projectexplorer/devicesupport/devicesettingspage.h +++ b/src/plugins/projectexplorer/devicesupport/devicesettingspage.h @@ -5,8 +5,7 @@ #include <coreplugin/dialogs/ioptionspage.h> -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { class DeviceSettingsPage final : public Core::IOptionsPage { @@ -14,5 +13,4 @@ public: DeviceSettingsPage(); }; -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp deleted file mode 100644 index 280b6139f87..00000000000 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.cpp +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "devicesettingswidget.h" - -#include "devicefactoryselectiondialog.h" -#include "devicemanager.h" -#include "devicemanagermodel.h" -#include "deviceprocessesdialog.h" -#include "devicetestdialog.h" -#include "idevice.h" -#include "idevicefactory.h" -#include "idevicewidget.h" -#include "../projectexplorericons.h" -#include "../projectexplorertr.h" - -#include <coreplugin/icore.h> - -#include <utils/algorithm.h> -#include <utils/async.h> -#include <utils/layoutbuilder.h> -#include <utils/optionpushbutton.h> -#include <utils/qtcassert.h> - -#include <QComboBox> -#include <QGroupBox> -#include <QLabel> -#include <QLineEdit> -#include <QMenu> -#include <QPushButton> -#include <QScrollArea> -#include <QTextStream> -#include <QVBoxLayout> -#include <QValidator> - -#include <algorithm> - -using namespace Core; -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { -const char LastDeviceIndexKey[] = "LastDisplayedMaemoDeviceConfig"; - -class NameValidator : public QValidator -{ -public: - NameValidator(const DeviceManager *deviceManager, QWidget *parent = nullptr) - : QValidator(parent), m_deviceManager(deviceManager) - { - } - - void setDisplayName(const QString &name) { m_oldName = name; } - - State validate(QString &input, int & /* pos */) const override - { - if (input.trimmed().isEmpty() - || (input != m_oldName && m_deviceManager->hasDevice(input))) - return Intermediate; - return Acceptable; - } - - void fixup(QString &input) const override - { - int dummy = 0; - if (validate(input, dummy) != Acceptable) - input = m_oldName; - } - -private: - QString m_oldName; - const DeviceManager * const m_deviceManager; -}; - -DeviceSettingsWidget::DeviceSettingsWidget() - : m_deviceManager(DeviceManager::cloneInstance()), - m_deviceManagerModel(new DeviceManagerModel(m_deviceManager, this)), - m_nameValidator(new NameValidator(m_deviceManager, this)), - m_configWidget(nullptr) -{ - initGui(); - connect(m_deviceManager, &DeviceManager::deviceUpdated, - this, &DeviceSettingsWidget::handleDeviceUpdated); -} - -DeviceSettingsWidget::~DeviceSettingsWidget() -{ - DeviceManager::removeClonedInstance(); - delete m_configWidget; -} - -void DeviceSettingsWidget::initGui() -{ - m_configurationLabel = new QLabel(Tr::tr("&Device:")); - m_configurationComboBox = new QComboBox; - m_configurationComboBox->setModel(m_deviceManagerModel); - m_generalGroupBox = new QGroupBox(Tr::tr("General")); - m_nameLineEdit = new QLineEdit; - m_nameLineEdit->setValidator(m_nameValidator); - m_osTypeValueLabel = new QLabel; - m_autoDetectionLabel = new QLabel; - m_deviceStateIconLabel = new QLabel; - m_deviceStateTextLabel = new QLabel; - m_osSpecificGroupBox = new QGroupBox(Tr::tr("Type Specific")); - m_osSpecificGroupBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - m_removeConfigButton = new QPushButton(Tr::tr("&Remove")); - m_defaultDeviceButton = new QPushButton(Tr::tr("Set As Default")); - - OptionPushButton *addButton = new OptionPushButton(Tr::tr("&Add...")); - connect(addButton, &OptionPushButton::clicked, this, &DeviceSettingsWidget::addDevice); - - QMenu *deviceTypeMenu = new QMenu(addButton); - QAction *defaultAction = new QAction(Tr::tr("&Start Wizard to Add Device...")); - connect(defaultAction, &QAction::triggered, this, &DeviceSettingsWidget::addDevice); - deviceTypeMenu->addAction(defaultAction); - deviceTypeMenu->addSeparator(); - - for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) { - if (!factory->canCreate()) - continue; - if (!factory->quickCreationAllowed()) - continue; - - //: Add <Device Type Name> - QAction *action = new QAction(Tr::tr("Add %1").arg(factory->displayName())); - deviceTypeMenu->addAction(action); - - connect(action, &QAction::triggered, this, [factory, this] { - IDevice::Ptr device = factory->construct(); - QTC_ASSERT(device, return); - m_deviceManager->addDevice(device); - m_removeConfigButton->setEnabled(true); - m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); - saveSettings(); - }); - } - - addButton->setOptionalMenu(deviceTypeMenu); - - m_buttonsLayout = new QVBoxLayout; - m_buttonsLayout->setContentsMargins({}); - auto scrollAreaWidget = new QWidget; - auto scrollArea = new QScrollArea; - scrollArea->setWidgetResizable(true); - scrollArea->setWidget(scrollAreaWidget); - - using namespace Layouting; - Column { - m_generalGroupBox, - m_osSpecificGroupBox, - }.attachTo(scrollAreaWidget); - - Form { - Tr::tr("&Name:"), m_nameLineEdit, br, - Tr::tr("Type:"), m_osTypeValueLabel, br, - Tr::tr("Auto-detected:"), m_autoDetectionLabel, br, - Tr::tr("Current state:"), Row { m_deviceStateIconLabel, m_deviceStateTextLabel, st, }, br, - }.attachTo(m_generalGroupBox); - - // clang-format off - Row { - Column { - Form { m_configurationLabel, m_configurationComboBox, br, }, - scrollArea, - }, - Column { - addButton, - Space(30), - m_removeConfigButton, - m_defaultDeviceButton, - m_buttonsLayout, - st, - }, - }.attachTo(this); - // clang-format on - - bool hasDeviceFactories = Utils::anyOf(IDeviceFactory::allDeviceFactories(), - &IDeviceFactory::canCreate); - - addButton->setEnabled(hasDeviceFactories); - - int lastIndex = ICore::settings() - ->value(QLatin1String(LastDeviceIndexKey), 0).toInt(); - if (lastIndex == -1) - lastIndex = 0; - if (lastIndex < m_configurationComboBox->count()) - m_configurationComboBox->setCurrentIndex(lastIndex); - connect(m_configurationComboBox, &QComboBox::currentIndexChanged, - this, &DeviceSettingsWidget::currentDeviceChanged); - currentDeviceChanged(currentIndex()); - connect(m_defaultDeviceButton, &QAbstractButton::clicked, - this, &DeviceSettingsWidget::setDefaultDevice); - connect(m_removeConfigButton, &QAbstractButton::clicked, - this, &DeviceSettingsWidget::removeDevice); - connect(m_nameLineEdit, - &QLineEdit::editingFinished, - this, - &DeviceSettingsWidget::deviceNameEditingFinished); -} - -void DeviceSettingsWidget::addDevice() -{ - DeviceFactorySelectionDialog d; - if (d.exec() != QDialog::Accepted) - return; - - Id toCreate = d.selectedId(); - if (!toCreate.isValid()) - return; - IDeviceFactory *factory = IDeviceFactory::find(toCreate); - if (!factory) - return; - IDevice::Ptr device = factory->create(); - if (device.isNull()) - return; - - Utils::asyncRun([device] { device->checkOsType(); }); - - m_deviceManager->addDevice(device); - m_removeConfigButton->setEnabled(true); - m_configurationComboBox->setCurrentIndex(m_deviceManagerModel->indexOf(device)); - saveSettings(); - if (device->hasDeviceTester()) - testDevice(); -} - -void DeviceSettingsWidget::removeDevice() -{ - m_deviceManager->removeDevice(currentDevice()->id()); - if (m_deviceManager->deviceCount() == 0) - currentDeviceChanged(-1); -} - -void DeviceSettingsWidget::displayCurrent() -{ - const IDevice::ConstPtr ¤t = currentDevice(); - m_defaultDeviceButton->setEnabled( - m_deviceManager->defaultDevice(current->type()) != current); - m_osTypeValueLabel->setText(current->displayType()); - m_autoDetectionLabel->setText(current->isAutoDetected() - ? Tr::tr("Yes (id is \"%1\")").arg(current->id().toString()) : Tr::tr("No")); - m_nameValidator->setDisplayName(current->displayName()); - m_deviceStateIconLabel->show(); - switch (current->deviceState()) { - case IDevice::DeviceReadyToUse: - m_deviceStateIconLabel->setPixmap(Icons::DEVICE_READY_INDICATOR.pixmap()); - break; - case IDevice::DeviceConnected: - m_deviceStateIconLabel->setPixmap(Icons::DEVICE_CONNECTED_INDICATOR.pixmap()); - break; - case IDevice::DeviceDisconnected: - m_deviceStateIconLabel->setPixmap(Icons::DEVICE_DISCONNECTED_INDICATOR.pixmap()); - break; - case IDevice::DeviceStateUnknown: - m_deviceStateIconLabel->hide(); - break; - } - m_deviceStateTextLabel->setText(current->deviceStateToString()); - - m_removeConfigButton->setEnabled(!current->isAutoDetected() - || current->deviceState() == IDevice::DeviceDisconnected); - fillInValues(); -} - -void DeviceSettingsWidget::setDeviceInfoWidgetsEnabled(bool enable) -{ - m_configurationLabel->setEnabled(enable); - m_configurationComboBox->setEnabled(enable); - m_generalGroupBox->setEnabled(enable); - m_osSpecificGroupBox->setEnabled(enable); -} - -void DeviceSettingsWidget::fillInValues() -{ - const IDevice::ConstPtr ¤t = currentDevice(); - m_nameLineEdit->setText(current->displayName()); -} - -void DeviceSettingsWidget::updateDeviceFromUi() -{ - deviceNameEditingFinished(); - if (m_configWidget) - m_configWidget->updateDeviceFromUi(); -} - -void DeviceSettingsWidget::saveSettings() -{ - updateDeviceFromUi(); - ICore::settings()->setValueWithDefault(LastDeviceIndexKey, currentIndex(), 0); - DeviceManager::replaceInstance(); -} - -int DeviceSettingsWidget::currentIndex() const -{ - return m_configurationComboBox->currentIndex(); -} - -IDevice::ConstPtr DeviceSettingsWidget::currentDevice() const -{ - Q_ASSERT(currentIndex() != -1); - return m_deviceManagerModel->device(currentIndex()); -} - -void DeviceSettingsWidget::deviceNameEditingFinished() -{ - if (m_configurationComboBox->count() == 0) - return; - - const QString &newName = m_nameLineEdit->text(); - m_deviceManager->mutableDevice(currentDevice()->id())->setDisplayName(newName); - m_nameValidator->setDisplayName(newName); - m_deviceManagerModel->updateDevice(currentDevice()->id()); -} - -void DeviceSettingsWidget::setDefaultDevice() -{ - m_deviceManager->setDefaultDevice(currentDevice()->id()); - m_defaultDeviceButton->setEnabled(false); -} - -void DeviceSettingsWidget::testDevice() -{ - const IDevice::ConstPtr &device = currentDevice(); - QTC_ASSERT(device && device->hasDeviceTester(), return); - auto dlg = new DeviceTestDialog(m_deviceManager->mutableDevice(device->id()), this); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->setModal(true); - dlg->show(); -} - -void DeviceSettingsWidget::handleDeviceUpdated(Id id) -{ - const int index = m_deviceManagerModel->indexForId(id); - if (index == currentIndex()) - currentDeviceChanged(index); -} - -void DeviceSettingsWidget::currentDeviceChanged(int index) -{ - qDeleteAll(m_additionalActionButtons); - delete m_configWidget; - m_configWidget = nullptr; - m_additionalActionButtons.clear(); - const IDevice::ConstPtr device = m_deviceManagerModel->device(index); - if (device.isNull()) { - setDeviceInfoWidgetsEnabled(false); - m_removeConfigButton->setEnabled(false); - clearDetails(); - m_defaultDeviceButton->setEnabled(false); - return; - } - setDeviceInfoWidgetsEnabled(true); - m_removeConfigButton->setEnabled(true); - - if (device->hasDeviceTester()) { - QPushButton * const button = new QPushButton(Tr::tr("Test")); - m_additionalActionButtons << button; - connect(button, &QAbstractButton::clicked, this, &DeviceSettingsWidget::testDevice); - m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); - } - - if (device->canCreateProcessModel()) { - QPushButton * const button = new QPushButton(Tr::tr("Show Running Processes...")); - m_additionalActionButtons << button; - connect(button, &QAbstractButton::clicked, - this, &DeviceSettingsWidget::handleProcessListRequested); - m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); - } - - for (const IDevice::DeviceAction &deviceAction : device->deviceActions()) { - QPushButton * const button = new QPushButton(deviceAction.display); - m_additionalActionButtons << button; - connect(button, &QAbstractButton::clicked, this, [this, deviceAction] { - const IDevice::Ptr device = m_deviceManager->mutableDevice(currentDevice()->id()); - QTC_ASSERT(device, return); - updateDeviceFromUi(); - deviceAction.execute(device, this); - // Widget must be set up from scratch, because the action could have - // changed random attributes. - currentDeviceChanged(currentIndex()); - }); - - m_buttonsLayout->insertWidget(m_buttonsLayout->count() - 1, button); - } - - if (!m_osSpecificGroupBox->layout()) - new QVBoxLayout(m_osSpecificGroupBox); - m_configWidget = m_deviceManager->mutableDevice(device->id())->createWidget(); - if (m_configWidget) - m_osSpecificGroupBox->layout()->addWidget(m_configWidget); - displayCurrent(); -} - -void DeviceSettingsWidget::clearDetails() -{ - m_nameLineEdit->clear(); - m_osTypeValueLabel->clear(); - m_autoDetectionLabel->clear(); -} - -void DeviceSettingsWidget::handleProcessListRequested() -{ - QTC_ASSERT(currentDevice()->canCreateProcessModel(), return); - updateDeviceFromUi(); - DeviceProcessesDialog dlg; - dlg.addCloseButton(); - dlg.setDevice(currentDevice()); - dlg.exec(); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h b/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h deleted file mode 100644 index 207b60cf749..00000000000 --- a/src/plugins/projectexplorer/devicesupport/devicesettingswidget.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "idevicefwd.h" - -#include <QList> -#include <QString> -#include <QWidget> - -#include <coreplugin/dialogs/ioptionspage.h> - -QT_BEGIN_NAMESPACE -class QComboBox; -class QGroupBox; -class QLabel; -class QLineEdit; -class QPushButton; -class QVBoxLayout; -QT_END_NAMESPACE - -namespace ProjectExplorer { -class DeviceManager; -class DeviceManagerModel; -class IDeviceWidget; - -namespace Internal { -namespace Ui { class DeviceSettingsWidget; } -class NameValidator; - -class DeviceSettingsWidget final : public Core::IOptionsPageWidget -{ - Q_OBJECT -public: - DeviceSettingsWidget(); - ~DeviceSettingsWidget() final; - -private: - void apply() final { saveSettings(); } - - void saveSettings(); - - void handleDeviceUpdated(Utils::Id id); - void currentDeviceChanged(int index); - void addDevice(); - void removeDevice(); - void deviceNameEditingFinished(); - void setDefaultDevice(); - void testDevice(); - void handleProcessListRequested(); - - void initGui(); - void displayCurrent(); - void setDeviceInfoWidgetsEnabled(bool enable); - IDeviceConstPtr currentDevice() const; - int currentIndex() const; - void clearDetails(); - QString parseTestOutput(); - void fillInValues(); - void updateDeviceFromUi(); - - Ui::DeviceSettingsWidget *m_ui; - DeviceManager * const m_deviceManager; - DeviceManagerModel * const m_deviceManagerModel; - NameValidator * const m_nameValidator; - QList<QPushButton *> m_additionalActionButtons; - IDeviceWidget *m_configWidget; - - QLabel *m_configurationLabel; - QComboBox *m_configurationComboBox; - QGroupBox *m_generalGroupBox; - QLineEdit *m_nameLineEdit; - QLabel *m_osTypeValueLabel; - QLabel *m_autoDetectionLabel; - QLabel *m_deviceStateIconLabel; - QLabel *m_deviceStateTextLabel; - QGroupBox *m_osSpecificGroupBox; - QPushButton *m_removeConfigButton; - QPushButton *m_defaultDeviceButton; - QVBoxLayout *m_buttonsLayout; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h index 20b0c9c9e8f..ee6b2760060 100644 --- a/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h +++ b/src/plugins/projectexplorer/devicesupport/deviceusedportsgatherer.h @@ -87,7 +87,6 @@ private: QVector<Internal::SubChannelProvider *> m_channelProviders; }; -} // namespace ProjectExplorer +using DeviceUsedPortsGathererTask = Tasking::CustomTask<DeviceUsedPortsGathererTaskAdapter>; -TASKING_DECLARE_TASK(DeviceUsedPortsGathererTask, - ProjectExplorer::DeviceUsedPortsGathererTaskAdapter); +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.h b/src/plugins/projectexplorer/devicesupport/filetransfer.h index f73a3741682..7ad6a5c5bc3 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransfer.h +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.h @@ -59,7 +59,7 @@ public: void start() final { task()->test(); } }; -} // namespace ProjectExplorer +using FileTransferTask = Tasking::CustomTask<FileTransferTaskAdapter>; +using FileTransferTestTask = Tasking::CustomTask<FileTransferTestTaskAdapter>; -TASKING_DECLARE_TASK(FileTransferTask, ProjectExplorer::FileTransferTaskAdapter); -TASKING_DECLARE_TASK(FileTransferTestTask, ProjectExplorer::FileTransferTestTaskAdapter); +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index ab5eb6df35e..489e6b337e6 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -4,12 +4,12 @@ #include "idevice.h" #include "devicemanager.h" -#include "deviceprocesslist.h" #include "idevicefactory.h" +#include "processlist.h" #include "sshparameters.h" #include "../kit.h" -#include "../kitinformation.h" +#include "../kitaspects.h" #include "../projectexplorertr.h" #include "../target.h" @@ -123,9 +123,13 @@ namespace Internal { class IDevicePrivate { public: - IDevicePrivate() = default; + IDevicePrivate(std::unique_ptr<DeviceSettings> s) + : settings(std::move(s)) + { + if (!settings) + settings = std::make_unique<DeviceSettings>(); + } - DisplayName displayName; QString displayType; Id type; IDevice::Origin origin = IDevice::AutoDetected; @@ -146,14 +150,60 @@ public: QList<Icon> deviceIcons; QList<IDevice::DeviceAction> deviceActions; - QVariantMap extraData; + Store extraData; IDevice::OpenTerminal openTerminal; + + std::unique_ptr<DeviceSettings> settings; }; } // namespace Internal +DeviceSettings::DeviceSettings() +{ + setAutoApply(false); + + displayName.setSettingsKey(DisplayNameKey); + displayName.setDisplayStyle(StringAspect::DisplayStyle::LineEditDisplay); + + auto validateDisplayName = [](const QString &old, + const QString &newValue) -> expected_str<void> { + if (old == newValue) + return {}; + + if (newValue.trimmed().isEmpty()) + return make_unexpected(Tr::tr("The device name cannot be empty.")); + + if (DeviceManager::clonedInstance()->hasDevice(newValue)) + return make_unexpected(Tr::tr("A device with this name already exists.")); + + return {}; + }; + + displayName.setValidationFunction( + [this, validateDisplayName](FancyLineEdit *edit, QString *errorMsg) -> bool { + auto result = validateDisplayName(displayName.value(), edit->text()); + if (result) + return true; + + if (errorMsg) + *errorMsg = result.error(); + + return false; + }); + + displayName.setValueAcceptor( + [validateDisplayName](const QString &old, + const QString &newValue) -> std::optional<QString> { + if (validateDisplayName(old, newValue)) + return std::nullopt; + + return old; + }); +} + DeviceTester::DeviceTester(QObject *parent) : QObject(parent) { } -IDevice::IDevice() : d(new Internal::IDevicePrivate) +IDevice::IDevice(std::unique_ptr<DeviceSettings> settings) + : d(new Internal::IDevicePrivate(std::move(settings))) { } @@ -176,10 +226,11 @@ bool IDevice::canOpenTerminal() const return bool(d->openTerminal); } -void IDevice::openTerminal(const Environment &env, const FilePath &workingDir) const +expected_str<void> IDevice::openTerminal(const Environment &env, const FilePath &workingDir) const { - QTC_ASSERT(canOpenTerminal(), return); - d->openTerminal(env, workingDir); + QTC_ASSERT(canOpenTerminal(), + return make_unexpected(Tr::tr("Opening a terminal is not supported."))); + return d->openTerminal(env, workingDir); } bool IDevice::isEmptyCommandAllowed() const @@ -239,7 +290,6 @@ FilePath IDevice::searchExecutable(const QString &fileName, const FilePaths &dir ProcessInterface *IDevice::createProcessInterface() const { - QTC_CHECK(false); return nullptr; } @@ -252,29 +302,22 @@ FileTransferInterface *IDevice::createFileTransferInterface( } Environment IDevice::systemEnvironment() const +{ + expected_str<Environment> env = systemEnvironmentWithError(); + QTC_ASSERT_EXPECTED(env, return {}); + return *env; +} + +expected_str<Environment> IDevice::systemEnvironmentWithError() const { DeviceFileAccess *access = fileAccess(); QTC_ASSERT(access, return Environment::systemEnvironment()); return access->deviceEnvironment(); } -/*! - Specifies a free-text name for the device to be displayed in GUI elements. -*/ - QString IDevice::displayName() const { - return d->displayName.value(); -} - -void IDevice::setDisplayName(const QString &name) -{ - d->displayName.setValue(name); -} - -void IDevice::setDefaultDisplayName(const QString &name) -{ - d->displayName.setDefaultValue(name); + return d->settings->displayName(); } QString IDevice::displayType() const @@ -390,13 +433,6 @@ PortsGatheringMethod IDevice::portsGatheringMethod() const return {filePath("netstat"), {"-a", "-n"}}; }, &Port::parseFromCommandOutput}; -}; - -DeviceProcessList *IDevice::createProcessListModel(QObject *parent) const -{ - Q_UNUSED(parent) - QTC_ASSERT(false, qDebug("This should not have been called..."); return nullptr); - return nullptr; } DeviceTester *IDevice::createDeviceTester() const @@ -427,14 +463,14 @@ void IDevice::setDeviceState(const IDevice::DeviceState state) d->deviceState = state; } -Id IDevice::typeFromMap(const QVariantMap &map) +Id IDevice::typeFromMap(const Store &map) { - return Id::fromSetting(map.value(QLatin1String(TypeKey))); + return Id::fromSetting(map.value(TypeKey)); } -Id IDevice::idFromMap(const QVariantMap &map) +Id IDevice::idFromMap(const Store &map) { - return Id::fromSetting(map.value(QLatin1String(IdKey))); + return Id::fromSetting(map.value(IdKey)); } /*! @@ -443,24 +479,24 @@ Id IDevice::idFromMap(const QVariantMap &map) base class implementation. */ -void IDevice::fromMap(const QVariantMap &map) +void IDevice::fromMap(const Store &map) { d->type = typeFromMap(map); - d->displayName.fromMap(map, DisplayNameKey); - d->id = Id::fromSetting(map.value(QLatin1String(IdKey))); - d->osType = osTypeFromString( - map.value(QLatin1String(ClientOsTypeKey), osTypeToString(OsTypeLinux)).toString()); + settings()->fromMap(map); + + d->id = Id::fromSetting(map.value(IdKey)); + d->osType = osTypeFromString(map.value(ClientOsTypeKey, osTypeToString(OsTypeLinux)).toString()); if (!d->id.isValid()) d->id = newId(); - d->origin = static_cast<Origin>(map.value(QLatin1String(OriginKey), ManuallyAdded).toInt()); + d->origin = static_cast<Origin>(map.value(OriginKey, ManuallyAdded).toInt()); QWriteLocker locker(&d->lock); - d->sshParameters.setHost(map.value(QLatin1String(HostKey)).toString()); - d->sshParameters.setPort(map.value(QLatin1String(SshPortKey), 22).toInt()); - d->sshParameters.setUserName(map.value(QLatin1String(UserNameKey)).toString()); + d->sshParameters.setHost(map.value(HostKey).toString()); + d->sshParameters.setPort(map.value(SshPortKey, 22).toInt()); + d->sshParameters.setUserName(map.value(UserNameKey).toString()); // Pre-4.9, the authentication enum used to have more values - const int storedAuthType = map.value(QLatin1String(AuthKey), DefaultAuthType).toInt(); + const int storedAuthType = map.value(AuthKey, DefaultAuthType).toInt(); const bool outdatedAuthType = storedAuthType > SshParameters::AuthenticationTypeSpecificKey; d->sshParameters.authenticationType = outdatedAuthType @@ -468,22 +504,22 @@ void IDevice::fromMap(const QVariantMap &map) : static_cast<AuthType>(storedAuthType); d->sshParameters.privateKeyFile = - FilePath::fromSettings(map.value(QLatin1String(KeyFileKey), defaultPrivateKeyFilePath())); - d->sshParameters.timeout = map.value(QLatin1String(TimeoutKey), DefaultTimeout).toInt(); + FilePath::fromSettings(map.value(KeyFileKey, defaultPrivateKeyFilePath())); + d->sshParameters.timeout = map.value(TimeoutKey, DefaultTimeout).toInt(); d->sshParameters.hostKeyCheckingMode = static_cast<SshHostKeyCheckingMode> - (map.value(QLatin1String(HostKeyCheckingKey), SshHostKeyCheckingNone).toInt()); + (map.value(HostKeyCheckingKey, SshHostKeyCheckingNone).toInt()); QString portsSpec = map.value(PortsSpecKey).toString(); if (portsSpec.isEmpty()) portsSpec = "10000-10100"; d->freePorts = PortList::fromString(portsSpec); - d->machineType = static_cast<MachineType>(map.value(QLatin1String(MachineTypeKey), DefaultMachineType).toInt()); - d->version = map.value(QLatin1String(VersionKey), 0).toInt(); + d->machineType = static_cast<MachineType>(map.value(MachineTypeKey, DefaultMachineType).toInt()); + d->version = map.value(VersionKey, 0).toInt(); - d->debugServerPath = FilePath::fromSettings(map.value(QLatin1String(DebugServerKey))); - const FilePath qmlRunCmd = FilePath::fromSettings(map.value(QLatin1String(QmlRuntimeKey))); + d->debugServerPath = FilePath::fromSettings(map.value(DebugServerKey)); + const FilePath qmlRunCmd = FilePath::fromSettings(map.value(QmlRuntimeKey)); d->qmlRunCommand = qmlRunCmd; - d->extraData = map.value(ExtraDataKey).toMap(); + d->extraData = storeFromVariant(map.value(ExtraDataKey)); } /*! @@ -492,32 +528,33 @@ void IDevice::fromMap(const QVariantMap &map) call the base class implementation. */ -QVariantMap IDevice::toMap() const +Store IDevice::toMap() const { - QVariantMap map; - d->displayName.toMap(map, DisplayNameKey); - map.insert(QLatin1String(TypeKey), d->type.toString()); - map.insert(QLatin1String(ClientOsTypeKey), osTypeToString(d->osType)); - map.insert(QLatin1String(IdKey), d->id.toSetting()); - map.insert(QLatin1String(OriginKey), d->origin); + Store map; + d->settings->toMap(map); + + map.insert(TypeKey, d->type.toString()); + map.insert(ClientOsTypeKey, osTypeToString(d->osType)); + map.insert(IdKey, d->id.toSetting()); + map.insert(OriginKey, d->origin); QReadLocker locker(&d->lock); - map.insert(QLatin1String(MachineTypeKey), d->machineType); - map.insert(QLatin1String(HostKey), d->sshParameters.host()); - map.insert(QLatin1String(SshPortKey), d->sshParameters.port()); - map.insert(QLatin1String(UserNameKey), d->sshParameters.userName()); - map.insert(QLatin1String(AuthKey), d->sshParameters.authenticationType); - map.insert(QLatin1String(KeyFileKey), d->sshParameters.privateKeyFile.toSettings()); - map.insert(QLatin1String(TimeoutKey), d->sshParameters.timeout); - map.insert(QLatin1String(HostKeyCheckingKey), d->sshParameters.hostKeyCheckingMode); + map.insert(MachineTypeKey, d->machineType); + map.insert(HostKey, d->sshParameters.host()); + map.insert(SshPortKey, d->sshParameters.port()); + map.insert(UserNameKey, d->sshParameters.userName()); + map.insert(AuthKey, d->sshParameters.authenticationType); + map.insert(KeyFileKey, d->sshParameters.privateKeyFile.toSettings()); + map.insert(TimeoutKey, d->sshParameters.timeout); + map.insert(HostKeyCheckingKey, d->sshParameters.hostKeyCheckingMode); - map.insert(QLatin1String(PortsSpecKey), d->freePorts.toString()); - map.insert(QLatin1String(VersionKey), d->version); + map.insert(PortsSpecKey, d->freePorts.toString()); + map.insert(VersionKey, d->version); - map.insert(QLatin1String(DebugServerKey), d->debugServerPath.toSettings()); - map.insert(QLatin1String(QmlRuntimeKey), d->qmlRunCommand.toSettings()); + map.insert(DebugServerKey, d->debugServerPath.toSettings()); + map.insert(QmlRuntimeKey, d->qmlRunCommand.toSettings()); - map.insert(ExtraDataKey, d->extraData); + map.insert(ExtraDataKey, variantFromStore(d->extraData)); return map; } @@ -536,6 +573,11 @@ IDevice::Ptr IDevice::clone() const return device; } +DeviceSettings *IDevice::settings() const +{ + return d->settings.get(); +} + QString IDevice::deviceStateToString() const { switch (d->deviceState) { @@ -615,12 +657,12 @@ void IDevice::setQmlRunCommand(const FilePath &path) void IDevice::setExtraData(Id kind, const QVariant &data) { - d->extraData.insert(kind.toString(), data); + d->extraData.insert(keyFromString(kind.toString()), data); } QVariant IDevice::extraData(Id kind) const { - return d->extraData.value(kind.toString()); + return d->extraData.value(keyFromString(kind.toString())); } int IDevice::version() const diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index e28f88aaf46..a6f9d860b84 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -8,10 +8,12 @@ #include <solutions/tasking/tasktree.h> -#include <utils/id.h> +#include <utils/aspects.h> #include <utils/expected.h> #include <utils/filepath.h> #include <utils/hostosinfo.h> +#include <utils/id.h> +#include <utils/store.h> #include <QAbstractSocket> #include <QCoreApplication> @@ -19,7 +21,6 @@ #include <QObject> #include <QSharedPointer> #include <QUrl> -#include <QVariantMap> #include <functional> #include <memory> @@ -32,16 +33,14 @@ namespace Utils { class CommandLine; class DeviceFileAccess; class Environment; -class Icon; class PortList; class Port; -class ProcessInterface; class Process; +class ProcessInterface; } // Utils namespace ProjectExplorer { -class DeviceProcessList; class FileTransferInterface; class FileTransferSetupData; class Kit; @@ -85,6 +84,14 @@ public: std::function<QList<Utils::Port>(const QByteArray &commandOutput)> parsePorts; }; +class PROJECTEXPLORER_EXPORT DeviceSettings : public Utils::AspectContainer +{ +public: + DeviceSettings(); + + Utils::StringAspect displayName{this}; +}; + // See cpp file for documentation. class PROJECTEXPLORER_EXPORT IDevice : public QEnableSharedFromThis<IDevice> { @@ -101,9 +108,9 @@ public: Ptr clone() const; + DeviceSettings *settings() const; + QString displayName() const; - void setDisplayName(const QString &name); - void setDefaultDisplayName(const QString &name); // Provide some information on the device suitable for formated // output, e.g. in tool tips. Get a list of name value pairs. @@ -142,7 +149,6 @@ public: virtual PortsGatheringMethod portsGatheringMethod() const; virtual bool canCreateProcessModel() const { return false; } - virtual DeviceProcessList *createProcessListModel(QObject *parent = nullptr) const; virtual bool hasDeviceTester() const { return false; } virtual DeviceTester *createDeviceTester() const; @@ -153,8 +159,8 @@ public: void setDeviceState(const DeviceState state); QString deviceStateToString() const; - static Utils::Id typeFromMap(const QVariantMap &map); - static Utils::Id idFromMap(const QVariantMap &map); + static Utils::Id typeFromMap(const Utils::Store &map); + static Utils::Id idFromMap(const Utils::Store &map); static QString defaultPrivateKeyFilePath(); static QString defaultPublicKeyFilePath(); @@ -186,7 +192,8 @@ public: void setupId(Origin origin, Utils::Id id = Utils::Id()); bool canOpenTerminal() const; - void openTerminal(const Utils::Environment &env, const Utils::FilePath &workingDir) const; + Utils::expected_str<void> openTerminal(const Utils::Environment &env, + const Utils::FilePath &workingDir) const; bool isEmptyCommandAllowed() const; void setAllowEmptyCommand(bool allow); @@ -206,7 +213,9 @@ public: virtual Utils::ProcessInterface *createProcessInterface() const; virtual FileTransferInterface *createFileTransferInterface( const FileTransferSetupData &setup) const; - virtual Utils::Environment systemEnvironment() const; + + Utils::Environment systemEnvironment() const; + virtual Utils::expected_str<Utils::Environment> systemEnvironmentWithError() const; virtual void aboutToBeRemoved() const {} @@ -219,12 +228,13 @@ public: virtual void checkOsType() {} protected: - IDevice(); + IDevice(std::unique_ptr<DeviceSettings> settings = nullptr); - virtual void fromMap(const QVariantMap &map); - virtual QVariantMap toMap() const; + virtual void fromMap(const Utils::Store &map); + virtual Utils::Store toMap() const; - using OpenTerminal = std::function<void(const Utils::Environment &, const Utils::FilePath &)>; + using OpenTerminal = std::function<Utils::expected_str<void>(const Utils::Environment &, + const Utils::FilePath &)>; void setOpenTerminal(const OpenTerminal &openTerminal); void setDisplayType(const QString &type); void setOsType(Utils::OsType osType); @@ -285,6 +295,6 @@ public: void start() final; }; -} // namespace ProjectExplorer +using DeviceProcessKillerTask = Tasking::CustomTask<DeviceProcessKillerTaskAdapter>; -TASKING_DECLARE_TASK(DeviceProcessKillerTask, ProjectExplorer::DeviceProcessKillerTaskAdapter); +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp index b5178445702..9d229924749 100644 --- a/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.cpp @@ -37,14 +37,14 @@ namespace ProjectExplorer { */ /*! - \fn virtual bool canRestore(const QVariantMap &map) const = 0 + \fn virtual bool canRestore(const Utils::Storage &map) const = 0 Checks whether this factory can restore a device from the serialized state specified by \a map. */ /*! - \fn virtual IDevice::Ptr restore(const QVariantMap &map) const = 0 + \fn virtual IDevice::Ptr restore(const Utils::Storage &map) const = 0 Loads a device from a serialized state. Only called if \c canRestore() returns true for \a map. @@ -69,7 +69,6 @@ IDevice::Ptr IDeviceFactory::create() const IDevice::Ptr device = m_creator(); if (!device) // e.g. Cancel used on the dialog to create a device return {}; - device->setDefaultDisplayName(displayName()); return device; } @@ -80,7 +79,7 @@ IDevice::Ptr IDeviceFactory::construct() const IDevice::Ptr device = m_constructor(); QTC_ASSERT(device, return {}); - device->setDefaultDisplayName(displayName()); + device->settings()->displayName.setDefaultValue(displayName()); return device; } @@ -112,7 +111,7 @@ void IDeviceFactory::setCombinedIcon(const FilePath &small, const FilePath &larg Icon({{large, Theme::IconsBaseColor}})}); } -void IDeviceFactory::setCreator(const std::function<IDevice::Ptr ()> &creator) +void IDeviceFactory::setCreator(const std::function<IDevice::Ptr()> &creator) { QTC_ASSERT(creator, return); m_creator = creator; diff --git a/src/plugins/projectexplorer/devicesupport/idevicefactory.h b/src/plugins/projectexplorer/devicesupport/idevicefactory.h index 665059f5b2c..8921128d808 100644 --- a/src/plugins/projectexplorer/devicesupport/idevicefactory.h +++ b/src/plugins/projectexplorer/devicesupport/idevicefactory.h @@ -4,11 +4,13 @@ #pragma once #include "idevicefwd.h" + #include <projectexplorer/projectexplorer_export.h> + #include <utils/id.h> +#include <utils/store.h> #include <QIcon> -#include <QVariantMap> namespace Utils { class FilePath; } @@ -28,7 +30,7 @@ public: IDevicePtr create() const; bool quickCreationAllowed() const; - virtual bool canRestore(const QVariantMap &) const { return true; } + virtual bool canRestore(const Utils::Store &) const { return true; } static IDeviceFactory *find(Utils::Id type); diff --git a/src/plugins/projectexplorer/devicesupport/processlist.cpp b/src/plugins/projectexplorer/devicesupport/processlist.cpp index 11e0932832d..b707b9b2569 100644 --- a/src/plugins/projectexplorer/devicesupport/processlist.cpp +++ b/src/plugins/projectexplorer/devicesupport/processlist.cpp @@ -3,8 +3,12 @@ #include "processlist.h" -#include <projectexplorer/devicesupport/idevice.h> +#include "idevice.h" +#include "../projectexplorertr.h" + #include <utils/processinfo.h> +#include <utils/qtcassert.h> +#include <utils/treemodel.h> #include <QTimer> @@ -17,45 +21,140 @@ using namespace Utils; namespace ProjectExplorer { +namespace Internal { + +enum State { Inactive, Listing, Killing }; + +class DeviceProcessTreeItem : public TreeItem +{ +public: + DeviceProcessTreeItem(const ProcessInfo &p, Qt::ItemFlags f) : process(p), fl(f) {} + + QVariant data(int column, int role) const final; + Qt::ItemFlags flags(int) const final { return fl; } + + ProcessInfo process; + Qt::ItemFlags fl; +}; + +class DeviceProcessListPrivate +{ +public: + DeviceProcessListPrivate(const IDevice::ConstPtr &device) + : device(device) + { + // FIXME: This should not be used for non-desktop cases. +#if defined(Q_OS_UNIX) + ownPid = getpid(); +#elif defined(Q_OS_WIN) + ownPid = GetCurrentProcessId(); +#endif + } + + qint64 ownPid = -1; + const IDevice::ConstPtr device; + State state = Inactive; + TreeModel<TypedTreeItem<DeviceProcessTreeItem>, DeviceProcessTreeItem> model; + DeviceProcessSignalOperation::Ptr signalOperation; +}; + +} // namespace Internal + +using namespace Internal; ProcessList::ProcessList(const IDevice::ConstPtr &device, QObject *parent) - : DeviceProcessList(device, parent) + : QObject(parent), d(std::make_unique<DeviceProcessListPrivate>(device)) { -#if defined(Q_OS_UNIX) - setOwnPid(getpid()); -#elif defined(Q_OS_WIN) - setOwnPid(GetCurrentProcessId()); -#endif + d->model.setHeader({Tr::tr("Process ID"), Tr::tr("Command Line")}); } -void ProcessList::doKillProcess(const ProcessInfo &processInfo) +ProcessList::~ProcessList() = default; + +void ProcessList::update() { - m_signalOperation = device()->signalOperation(); - connect(m_signalOperation.data(), - &DeviceProcessSignalOperation::finished, - this, - &ProcessList::reportDelayedKillStatus); - m_signalOperation->killProcess(processInfo.processId); + QTC_ASSERT(d->state == Inactive, return); + QTC_ASSERT(d->device, return); + + d->model.clear(); + d->model.rootItem()->appendChild( + new DeviceProcessTreeItem( + {0, Tr::tr("Fetching process list. This might take a while."), ""}, + Qt::NoItemFlags)); + d->state = Listing; + + QTimer::singleShot(0, this, &ProcessList::handleUpdate); +} + +void ProcessList::killProcess(int row) +{ + QTC_ASSERT(row >= 0 && row < d->model.rootItem()->childCount(), return); + QTC_ASSERT(d->state == Inactive, return); + QTC_ASSERT(d->device, return); + + d->state = Killing; + + const ProcessInfo processInfo = at(row); + d->signalOperation = d->device->signalOperation(); + connect(d->signalOperation.data(), &DeviceProcessSignalOperation::finished, + this, &ProcessList::reportDelayedKillStatus); + d->signalOperation->killProcess(processInfo.processId); +} + +ProcessInfo ProcessList::at(int row) const +{ + return d->model.rootItem()->childAt(row)->process; +} + +QAbstractItemModel *ProcessList::model() const +{ + return &d->model; +} + +QVariant DeviceProcessTreeItem::data(int column, int role) const +{ + if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { + if (column == 0) + return process.processId ? process.processId : QVariant(); + else + return process.commandLine; + } + return {}; +} + +void ProcessList::setFinished() +{ + d->state = Inactive; } void ProcessList::handleUpdate() { - reportProcessListUpdated(ProcessInfo::processInfoList(DeviceProcessList::device()->rootPath())); -} + const QList<ProcessInfo> processes = ProcessInfo::processInfoList(d->device->rootPath()); + QTC_ASSERT(d->state == Listing, return); + setFinished(); + d->model.clear(); + for (const ProcessInfo &process : processes) { + Qt::ItemFlags fl; + if (process.processId != d->ownPid) + fl = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + d->model.rootItem()->appendChild(new DeviceProcessTreeItem(process, fl)); + } -void ProcessList::doUpdate() -{ - QTimer::singleShot(0, this, &ProcessList::handleUpdate); + emit processListUpdated(); } void ProcessList::reportDelayedKillStatus(const QString &errorMessage) { - if (errorMessage.isEmpty()) - reportProcessKilled(); - else - reportError(errorMessage); + if (errorMessage.isEmpty()) { + QTC_CHECK(d->state == Killing); + setFinished(); + emit processKilled(); + } else { + QTC_CHECK(d->state != Inactive); + setFinished(); + emit error(errorMessage); + } - m_signalOperation.reset(); + d->signalOperation.reset(); } -} // namespace ProjectExplorer +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/processlist.h b/src/plugins/projectexplorer/devicesupport/processlist.h index caebaf22f97..6b9f9b9cc5f 100644 --- a/src/plugins/projectexplorer/devicesupport/processlist.h +++ b/src/plugins/projectexplorer/devicesupport/processlist.h @@ -3,28 +3,46 @@ #pragma once -#include "deviceprocesslist.h" -#include "idevice.h" +#include "../projectexplorer_export.h" +#include "idevicefwd.h" + +#include <QAbstractItemModel> +#include <QList> + +#include <memory> + +namespace Utils { class ProcessInfo; } namespace ProjectExplorer { -class PROJECTEXPLORER_EXPORT ProcessList : public DeviceProcessList +namespace Internal { class DeviceProcessListPrivate; } + +class PROJECTEXPLORER_EXPORT ProcessList : public QObject { Q_OBJECT public: - explicit ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); + ProcessList(const IDeviceConstPtr &device, QObject *parent = nullptr); + ~ProcessList() override; -private: - void doUpdate() override; - void doKillProcess(const Utils::ProcessInfo &process) override; + void update(); + void killProcess(int row); + + Utils::ProcessInfo at(int row) const; + QAbstractItemModel *model() const; + +signals: + void processListUpdated(); + void error(const QString &errorMsg); + void processKilled(); private: void handleUpdate(); void reportDelayedKillStatus(const QString &errorMessage); -private: - DeviceProcessSignalOperation::Ptr m_signalOperation; + void setFinished(); + + const std::unique_ptr<Internal::DeviceProcessListPrivate> d; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp index 744e350eaef..1467f958b99 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.cpp @@ -27,6 +27,12 @@ QString SshParameters::userAtHost() const if (!m_userName.isEmpty()) res = m_userName + '@'; res += m_host; + return res; +} + +QString SshParameters::userAtHostAndPort() const +{ + QString res = SshParameters::userAtHost(); if (m_port != 22) res += QString(":%1").arg(m_port); return res; @@ -77,6 +83,7 @@ bool SshParameters::setupSshEnvironment(Process *process) const bool hasDisplay = env.hasKey("DISPLAY") && (env.value("DISPLAY") != QString(":0")); if (SshSettings::askpassFilePath().exists()) { env.set("SSH_ASKPASS", SshSettings::askpassFilePath().toUserOutput()); + env.set("SSH_ASKPASS_REQUIRE", "force"); // OpenSSH only uses the askpass program if DISPLAY is set, regardless of the platform. if (!env.hasKey("DISPLAY")) @@ -141,6 +148,15 @@ const QString userAtHost() return userMidFix + getHostFromEnvironment(); } +const QString userAtHostAndPort() +{ + QString res = userAtHost(); + const int port = getPortFromEnvironment(); + if (port != 22) + res += QString(":%1").arg(port); + return res; +} + SshParameters getParameters() { SshParameters params; diff --git a/src/plugins/projectexplorer/devicesupport/sshparameters.h b/src/plugins/projectexplorer/devicesupport/sshparameters.h index 3ee483d5f3d..5a5f32da268 100644 --- a/src/plugins/projectexplorer/devicesupport/sshparameters.h +++ b/src/plugins/projectexplorer/devicesupport/sshparameters.h @@ -32,6 +32,7 @@ public: QString userName() const { return m_userName; } QString userAtHost() const; + QString userAtHostAndPort() const; void setHost(const QString &host) { m_host = host; } void setPort(int port) { m_port = port; } @@ -63,6 +64,7 @@ quint16 PROJECTEXPLORER_EXPORT getPortFromEnvironment(); const QString PROJECTEXPLORER_EXPORT getUserFromEnvironment(); const QString PROJECTEXPLORER_EXPORT getKeyFileFromEnvironment(); const PROJECTEXPLORER_EXPORT QString userAtHost(); +const PROJECTEXPLORER_EXPORT QString userAtHostAndPort(); SshParameters PROJECTEXPLORER_EXPORT getParameters(); bool PROJECTEXPLORER_EXPORT checkParameters(const SshParameters ¶ms); void PROJECTEXPLORER_EXPORT printSetupHelp(); diff --git a/src/plugins/projectexplorer/devicesupport/sshsettings.cpp b/src/plugins/projectexplorer/devicesupport/sshsettings.cpp index be863437f80..f815ef058b4 100644 --- a/src/plugins/projectexplorer/devicesupport/sshsettings.cpp +++ b/src/plugins/projectexplorer/devicesupport/sshsettings.cpp @@ -5,9 +5,9 @@ #include <utils/environment.h> #include <utils/hostosinfo.h> +#include <utils/qtcsettings.h> #include <QReadWriteLock> -#include <QSettings> using namespace Utils; @@ -33,23 +33,23 @@ Q_GLOBAL_STATIC(Internal::SshSettings, sshSettings) class AccessSettingsGroup { public: - AccessSettingsGroup(QSettings *settings) : m_settings(settings) + AccessSettingsGroup(QtcSettings *settings) : m_settings(settings) { settings->beginGroup("SshSettings"); } ~AccessSettingsGroup() { m_settings->endGroup(); } private: - QSettings * const m_settings; + QtcSettings * const m_settings; }; -static QString connectionSharingKey() { return QString("UseConnectionSharing"); } -static QString connectionSharingTimeoutKey() { return QString("ConnectionSharingTimeout"); } -static QString sshFilePathKey() { return QString("SshFilePath"); } -static QString sftpFilePathKey() { return QString("SftpFilePath"); } -static QString askPassFilePathKey() { return QString("AskpassFilePath"); } -static QString keygenFilePathKey() { return QString("KeygenFilePath"); } +static Key connectionSharingKey() { return Key("UseConnectionSharing"); } +static Key connectionSharingTimeoutKey() { return Key("ConnectionSharingTimeout"); } +static Key sshFilePathKey() { return Key("SshFilePath"); } +static Key sftpFilePathKey() { return Key("SftpFilePath"); } +static Key askPassFilePathKey() { return Key("AskpassFilePath"); } +static Key keygenFilePathKey() { return Key("KeygenFilePath"); } -void SshSettings::loadSettings(QSettings *settings) +void SshSettings::loadSettings(QtcSettings *settings) { QWriteLocker locker(&sshSettings->lock); AccessSettingsGroup g(settings); @@ -67,7 +67,7 @@ void SshSettings::loadSettings(QSettings *settings) settings->value(keygenFilePathKey()).toString()); } -void SshSettings::storeSettings(QSettings *settings) +void SshSettings::storeSettings(QtcSettings *settings) { QReadLocker locker(&sshSettings->lock); AccessSettingsGroup g(settings); @@ -114,7 +114,7 @@ static FilePath filePathValue(const FilePath &value, const QStringList &candidat if (!filePath.isEmpty()) return filePath; } - return FilePath(); + return {}; } // Keep read locker locked while calling this method diff --git a/src/plugins/projectexplorer/devicesupport/sshsettings.h b/src/plugins/projectexplorer/devicesupport/sshsettings.h index 47d54413de6..ce039bd06bc 100644 --- a/src/plugins/projectexplorer/devicesupport/sshsettings.h +++ b/src/plugins/projectexplorer/devicesupport/sshsettings.h @@ -5,21 +5,19 @@ #include "../projectexplorer_export.h" -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <functional> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace ProjectExplorer { class PROJECTEXPLORER_EXPORT SshSettings { public: - static void loadSettings(QSettings *settings); - static void storeSettings(QSettings *settings); + static void loadSettings(Utils::QtcSettings *settings); + static void storeSettings(Utils::QtcSettings *settings); static void setConnectionSharingEnabled(bool share); static bool connectionSharingEnabled(); diff --git a/src/plugins/projectexplorer/editorconfiguration.cpp b/src/plugins/projectexplorer/editorconfiguration.cpp index ff99bc25e83..26b247f7ae0 100644 --- a/src/plugins/projectexplorer/editorconfiguration.cpp +++ b/src/plugins/projectexplorer/editorconfiguration.cpp @@ -27,16 +27,17 @@ #include <QTextCodec> #include <QDebug> -static const QLatin1String kPrefix("EditorConfiguration."); -static const QLatin1String kUseGlobal("EditorConfiguration.UseGlobal"); -static const QLatin1String kCodec("EditorConfiguration.Codec"); -static const QLatin1String kCodeStylePrefix("EditorConfiguration.CodeStyle."); -static const QLatin1String kCodeStyleCount("EditorConfiguration.CodeStyle.Count"); - using namespace TextEditor; +using namespace Utils; namespace ProjectExplorer { +const Key kPrefix("EditorConfiguration."); +const Key kUseGlobal("EditorConfiguration.UseGlobal"); +const Key kCodec("EditorConfiguration.Codec"); +const Key kCodeStylePrefix("EditorConfiguration.CodeStyle."); +const Key kCodeStyleCount("EditorConfiguration.CodeStyle.Count"); + struct EditorConfigurationPrivate { EditorConfigurationPrivate() : @@ -158,15 +159,15 @@ QMap<Utils::Id, ICodeStylePreferences *> EditorConfiguration::codeStyles() const return d->m_languageCodeStylePreferences; } -static void toMapWithPrefix(QVariantMap *map, const QVariantMap &source) +static void toMapWithPrefix(Store *map, const Store &source) { for (auto it = source.constBegin(), end = source.constEnd(); it != end; ++it) map->insert(kPrefix + it.key(), it.value()); } -QVariantMap EditorConfiguration::toMap() const +Store EditorConfiguration::toMap() const { - QVariantMap map = { + Store map = { {kUseGlobal, d->m_useGlobal}, {kCodec, d->m_textCodec->name()}, {kCodeStyleCount, d->m_languageCodeStylePreferences.count()} @@ -176,11 +177,11 @@ QVariantMap EditorConfiguration::toMap() const for (auto itCodeStyle = d->m_languageCodeStylePreferences.cbegin(), end = d->m_languageCodeStylePreferences.cend(); itCodeStyle != end; ++itCodeStyle) { - const QVariantMap settingsIdMap = { - {"language", itCodeStyle.key().toSetting()}, - {"value", itCodeStyle.value()->toMap()} + const Store settingsIdMap = { + {"language", QVariant::fromValue(itCodeStyle.key().toSetting())}, + {"value", QVariant::fromValue(itCodeStyle.value()->toMap())} }; - map.insert(kCodeStylePrefix + QString::number(i), settingsIdMap); + map.insert(numberedKey(kCodeStylePrefix, i), variantFromStore(settingsIdMap)); i++; } @@ -194,7 +195,7 @@ QVariantMap EditorConfiguration::toMap() const return map; } -void EditorConfiguration::fromMap(const QVariantMap &map) +void EditorConfiguration::fromMap(const Store &map) { const QByteArray &codecName = map.value(kCodec, d->m_textCodec->name()).toByteArray(); d->m_textCodec = QTextCodec::codecForName(codecName); @@ -203,22 +204,22 @@ void EditorConfiguration::fromMap(const QVariantMap &map) const int codeStyleCount = map.value(kCodeStyleCount, 0).toInt(); for (int i = 0; i < codeStyleCount; ++i) { - QVariantMap settingsIdMap = map.value(kCodeStylePrefix + QString::number(i)).toMap(); + Store settingsIdMap = storeFromVariant(map.value(numberedKey(kCodeStylePrefix, i))); if (settingsIdMap.isEmpty()) { qWarning() << "No data for code style settings list" << i << "found!"; continue; } - Utils::Id languageId = Utils::Id::fromSetting(settingsIdMap.value(QLatin1String("language"))); - QVariantMap value = settingsIdMap.value(QLatin1String("value")).toMap(); + Id languageId = Id::fromSetting(settingsIdMap.value("language")); + Store value = storeFromVariant(settingsIdMap.value("value")); ICodeStylePreferences *preferences = d->m_languageCodeStylePreferences.value(languageId); if (preferences) preferences->fromMap(value); } - QVariantMap submap; + Store submap; for (auto it = map.constBegin(), end = map.constEnd(); it != end; ++it) { - if (it.key().startsWith(kPrefix)) - submap.insert(it.key().mid(kPrefix.size()), it.value()); + if (it.key().view().startsWith(kPrefix.view())) + submap.insert(it.key().toByteArray().mid(kPrefix.view().size()), it.value()); } d->m_defaultCodeStyle->fromMap(submap); d->m_typingSettings.fromMap(submap); diff --git a/src/plugins/projectexplorer/editorconfiguration.h b/src/plugins/projectexplorer/editorconfiguration.h index 031e5762876..8b5aab4230a 100644 --- a/src/plugins/projectexplorer/editorconfiguration.h +++ b/src/plugins/projectexplorer/editorconfiguration.h @@ -6,9 +6,9 @@ #include "projectexplorer_export.h" #include <utils/id.h> +#include <utils/store.h> #include <QObject> -#include <QVariantMap> #include <memory> @@ -64,8 +64,8 @@ public: void configureEditor(TextEditor::BaseTextEditor *textEditor) const; void deconfigureEditor(TextEditor::BaseTextEditor *textEditor) const; - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); void setTypingSettings(const TextEditor::TypingSettings &settings); void setStorageSettings(const TextEditor::StorageSettings &settings); diff --git a/src/plugins/projectexplorer/environmentaspect.cpp b/src/plugins/projectexplorer/environmentaspect.cpp index 09bfeb9c80d..89d75e36195 100644 --- a/src/plugins/projectexplorer/environmentaspect.cpp +++ b/src/plugins/projectexplorer/environmentaspect.cpp @@ -21,7 +21,8 @@ const char PRINT_ON_RUN_KEY[] = "PE.EnvironmentAspect.PrintOnRun"; // EnvironmentAspect: // -------------------------------------------------------------------- -EnvironmentAspect::EnvironmentAspect() +EnvironmentAspect::EnvironmentAspect(AspectContainer *container) + : BaseAspect(container) { setDisplayName(Tr::tr("Environment")); setId("EnvironmentAspect"); @@ -29,6 +30,12 @@ EnvironmentAspect::EnvironmentAspect() addDataExtractor(this, &EnvironmentAspect::environment, &Data::environment); } +void EnvironmentAspect::setDeviceSelector(Target *target, DeviceSelector selector) +{ + m_target = target; + m_selector = selector; +} + int EnvironmentAspect::baseEnvironmentBase() const { return m_base; @@ -126,17 +133,17 @@ void EnvironmentAspect::setSupportForBuildEnvironment(Target *target) this, &EnvironmentAspect::environmentChanged); } -void EnvironmentAspect::fromMap(const QVariantMap &map) +void EnvironmentAspect::fromMap(const Store &map) { - m_base = map.value(QLatin1String(BASE_KEY), -1).toInt(); - m_userChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(CHANGES_KEY)).toStringList()); + m_base = map.value(BASE_KEY, -1).toInt(); + m_userChanges = EnvironmentItem::fromStringList(map.value(CHANGES_KEY).toStringList()); m_printOnRun = map.value(PRINT_ON_RUN_KEY).toBool(); } -void EnvironmentAspect::toMap(QVariantMap &data) const +void EnvironmentAspect::toMap(Store &data) const { - data.insert(QLatin1String(BASE_KEY), m_base); - data.insert(QLatin1String(CHANGES_KEY), Utils::EnvironmentItem::toStringList(m_userChanges)); + data.insert(BASE_KEY, m_base); + data.insert(CHANGES_KEY, EnvironmentItem::toStringList(m_userChanges)); data.insert(PRINT_ON_RUN_KEY, m_printOnRun); } diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h index 97ce26f017d..26dd43f6a41 100644 --- a/src/plugins/projectexplorer/environmentaspect.h +++ b/src/plugins/projectexplorer/environmentaspect.h @@ -9,9 +9,7 @@ #include <utils/aspects.h> #include <utils/environment.h> - -#include <QList> -#include <QVariantMap> +#include <utils/store.h> namespace ProjectExplorer { @@ -20,7 +18,10 @@ class PROJECTEXPLORER_EXPORT EnvironmentAspect : public Utils::BaseAspect Q_OBJECT public: - EnvironmentAspect(); + EnvironmentAspect(Utils::AspectContainer *container = nullptr); + + enum DeviceSelector { HostDevice, BuildDevice, RunDevice }; + void setDeviceSelector(Target *target, DeviceSelector selector); // The environment including the user's modifications. Utils::Environment environment() const; @@ -50,6 +51,8 @@ public: bool isLocal() const { return m_isLocal; } + Target *target() const { return m_target; } + bool isPrintOnRunAllowed() const { return m_allowPrintOnRun; } bool isPrintOnRunEnabled() const { return m_printOnRun; } void setPrintOnRun(bool enabled) { m_printOnRun = enabled; } @@ -59,8 +62,7 @@ public: Utils::Environment environment; }; - using Utils::BaseAspect::setupLabel; - using Utils::BaseAspect::label; + using Utils::BaseAspect::createLabel; signals: void baseEnvironmentChanged(); @@ -68,8 +70,8 @@ signals: void environmentChanged(); protected: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; void setIsLocal(bool local) { m_isLocal = local; } void setAllowPrintOnRun(bool allow) { m_allowPrintOnRun = allow; } @@ -93,6 +95,8 @@ private: bool m_isLocal = false; bool m_allowPrintOnRun = true; bool m_printOnRun = false; + Target *m_target = nullptr; + DeviceSelector m_selector = RunDevice; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp index c167cd4e52f..d47b4e82cc3 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.cpp +++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp @@ -37,8 +37,7 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect) auto label = [aspect]() { if (aspect->labelText().isEmpty()) aspect->setLabelText(Tr::tr("Base environment for this run configuration:")); - aspect->setupLabel(); - return aspect->label(); + return aspect->createLabel(); }; m_baseLayout->addWidget(label()); diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index 5fa21953113..64e406ace22 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -212,7 +212,6 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, Type type, QWidget *additi d->m_environmentView->setItemDelegate(new EnvironmentDelegate(d->m_model, d->m_environmentView)); d->m_environmentView->setMinimumHeight(400); d->m_environmentView->setRootIsDecorated(false); - d->m_environmentView->setUniformRowHeights(true); const auto stretcher = new HeaderViewStretcher(d->m_environmentView->header(), 1); connect(d->m_model, &QAbstractItemModel::dataChanged, stretcher, &HeaderViewStretcher::softStretch); diff --git a/src/plugins/projectexplorer/expanddata.cpp b/src/plugins/projectexplorer/expanddata.cpp index fb84bd0a5f0..d0c776471e1 100644 --- a/src/plugins/projectexplorer/expanddata.cpp +++ b/src/plugins/projectexplorer/expanddata.cpp @@ -8,27 +8,25 @@ using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; -ExpandData::ExpandData(const QString &path_, const QString &displayName_) : - path(path_), displayName(displayName_) -{ } +ExpandData::ExpandData(const QString &path, int priority) : path(path), priority(priority) {} bool ExpandData::operator==(const ExpandData &other) const { - return path == other.path && displayName == other.displayName; + return path == other.path && priority == other.priority; } ExpandData ExpandData::fromSettings(const QVariant &v) { - QStringList list = v.toStringList(); - return list.size() == 2 ? ExpandData(list.at(0), list.at(1)) : ExpandData(); + const QVariantList list = v.toList(); + return list.size() == 2 ? ExpandData(list.at(0).toString(), list.at(1).toInt()) : ExpandData(); } QVariant ExpandData::toSettings() const { - return QVariant::fromValue(QStringList({path, displayName})); + return QVariantList{path, priority}; } size_t ProjectExplorer::Internal::qHash(const ExpandData &data) { - return qHash(data.path) ^ qHash(data.displayName); + return qHash(data.path) ^ data.priority; } diff --git a/src/plugins/projectexplorer/expanddata.h b/src/plugins/projectexplorer/expanddata.h index 3a8a35d9b8a..e8678d2d851 100644 --- a/src/plugins/projectexplorer/expanddata.h +++ b/src/plugins/projectexplorer/expanddata.h @@ -14,14 +14,14 @@ class ExpandData { public: ExpandData() = default; - ExpandData(const QString &path_, const QString &displayName_); + ExpandData(const QString &path, int priority); bool operator==(const ExpandData &other) const; static ExpandData fromSettings(const QVariant &v); QVariant toSettings() const; QString path; - QString displayName; + int priority = 0; }; size_t qHash(const ExpandData &data); diff --git a/src/plugins/projectexplorer/extraabi.cpp b/src/plugins/projectexplorer/extraabi.cpp index 19f970b11a6..619d66733f1 100644 --- a/src/plugins/projectexplorer/extraabi.cpp +++ b/src/plugins/projectexplorer/extraabi.cpp @@ -8,12 +8,12 @@ #include <coreplugin/icore.h> #include <utils/algorithm.h> -#include <utils/fileutils.h> +#include <utils/filepath.h> +#include <utils/settingsaccessor.h> #include <utils/settingsaccessor.h> -#include <app/app_version.h> - #include <QDebug> +#include <QGuiApplication> using namespace Utils; @@ -29,7 +29,7 @@ class AbiFlavorUpgraderV0 : public VersionUpgrader public: AbiFlavorUpgraderV0() : VersionUpgrader(0, "") { } - QVariantMap upgrade(const QVariantMap &data) override { return data; } + Store upgrade(const Store &data) override { return data; } }; class AbiFlavorAccessor : public UpgradingSettingsAccessor @@ -38,7 +38,7 @@ public: AbiFlavorAccessor() { setDocType("QtCreatorExtraAbi"); - setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + setApplicationDisplayName(QGuiApplication::applicationDisplayName()); setBaseFilePath(Core::ICore::installerResourcePath("abi.xml")); addVersionUpgrader(std::make_unique<AbiFlavorUpgraderV0>()); } @@ -52,9 +52,9 @@ public: void ExtraAbi::load() { AbiFlavorAccessor accessor; - const QVariantMap data = accessor.restoreSettings(Core::ICore::dialogParent()).value("Flavors").toMap(); + const Store data = storeFromVariant(accessor.restoreSettings(Core::ICore::dialogParent()).value("Flavors")); for (auto it = data.constBegin(); it != data.constEnd(); ++it) { - const QString flavor = it.key(); + const Key flavor = it.key(); if (flavor.isEmpty()) continue; @@ -62,13 +62,15 @@ void ExtraAbi::load() std::vector<Abi::OS> oses; for (const QString &osName : osNames) { Abi::OS os = Abi::osFromString(osName); - if (Abi::toString(os) != osName) - qWarning() << "Invalid OS found when registering extra abi flavor" << it.key(); - else + if (Abi::toString(os) != osName) { + qWarning() << "Invalid OS found when registering extra abi flavor" + << it.key().toByteArray(); + } else { oses.push_back(os); + } } - Abi::registerOsFlavor(oses, flavor); + Abi::registerOsFlavor(oses, stringFromKey(flavor)); } } diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index bbd34a798fa..a81ddda93fe 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -4,7 +4,7 @@ #include "extracompiler.h" #include "buildmanager.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "projectmanager.h" #include "target.h" @@ -350,12 +350,12 @@ GroupItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider) FilePath ProcessExtraCompiler::workingDirectory() const { - return FilePath(); + return {}; } QStringList ProcessExtraCompiler::arguments() const { - return QStringList(); + return {}; } bool ProcessExtraCompiler::prepareToRun(const QByteArray &sourceContents) diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp index 0f9d6c40e25..f3554f18a9e 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp +++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp @@ -8,11 +8,11 @@ #include "projectmanager.h" #include <coreplugin/editormanager/editormanager.h> + #include <utils/algorithm.h> -#include <utils/filesearch.h> - -#include <QSettings> +#include <utils/qtcsettings.h> +using namespace TextEditor; using namespace Utils; namespace ProjectExplorer { @@ -30,14 +30,14 @@ QString FilesInAllProjectsFind::displayName() const const char kSettingsKey[] = "FilesInAllProjectDirectories"; -void FilesInAllProjectsFind::writeSettings(QSettings *settings) +void FilesInAllProjectsFind::writeSettings(QtcSettings *settings) { settings->beginGroup(kSettingsKey); writeCommonSettings(settings); settings->endGroup(); } -void FilesInAllProjectsFind::readSettings(QSettings *settings) +void FilesInAllProjectsFind::readSettings(QtcSettings *settings) { settings->beginGroup(kSettingsKey); readCommonSettings( @@ -47,18 +47,15 @@ void FilesInAllProjectsFind::readSettings(QSettings *settings) settings->endGroup(); } -Utils::FileIterator *FilesInAllProjectsFind::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider FilesInAllProjectsFind::fileContainerProvider() const { - Q_UNUSED(additionalParameters) - const QSet<FilePath> dirs = Utils::transform<QSet>(ProjectManager::projects(), [](Project *p) { - return p->projectFilePath().parentDir(); - }); - return new SubDirFileIterator(FilePaths(dirs.constBegin(), dirs.constEnd()), - nameFilters, - exclusionFilters, - Core::EditorManager::defaultTextCodec()); + return [nameFilters = fileNameFilters(), exclusionFilters = fileExclusionFilters()] { + const QSet<FilePath> dirs = Utils::transform<QSet>(ProjectManager::projects(), [](Project *p) { + return p->projectFilePath().parentDir(); + }); + return SubDirFileContainer(FilePaths(dirs.constBegin(), dirs.constEnd()), nameFilters, + exclusionFilters, Core::EditorManager::defaultTextCodec()); + }; } QString FilesInAllProjectsFind::label() const diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.h b/src/plugins/projectexplorer/filesinallprojectsfind.h index 613d1d2a2a9..f96d2a6df9a 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.h +++ b/src/plugins/projectexplorer/filesinallprojectsfind.h @@ -16,13 +16,11 @@ public: QString id() const override; QString displayName() const override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; + TextEditor::FileContainerProvider fileContainerProvider() const override; QString label() const override; }; diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp index b773f59abc5..ce9ab5e2849 100644 --- a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp @@ -18,26 +18,25 @@ using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { class FilterTreeItem : public TreeItem { public: - FilterTreeItem(const KitAspect *aspect, bool enabled) : m_aspect(aspect), m_enabled(enabled) + FilterTreeItem(const KitAspectFactory *factory, bool enabled) : m_factory(factory), m_enabled(enabled) { } QString displayName() const { - if (m_aspect->displayName().indexOf('<') < 0) - return m_aspect->displayName(); + if (m_factory->displayName().indexOf('<') < 0) + return m_factory->displayName(); // removing HTML tag because KitAspect::displayName could contain html // e.g. "CMake <a href=\"generator\">generator</a>" (CMakeGeneratorKitAspect) QTextDocument html; - html.setHtml(m_aspect->displayName()); + html.setHtml(m_factory->displayName()); return html.toPlainText(); } - Utils::Id id() const { return m_aspect->id(); } + Utils::Id id() const { return m_factory->id(); } bool enabled() const { return m_enabled; } private: @@ -48,12 +47,12 @@ private: return displayName(); if (column == 1 && role == Qt::CheckStateRole) return m_enabled ? Qt::Checked : Qt::Unchecked; - return QVariant(); + return {}; } bool setData(int column, const QVariant &data, int role) override { - QTC_ASSERT(column == 1 && !m_aspect->isEssential(), return false); + QTC_ASSERT(column == 1 && !m_factory->isEssential(), return false); if (role == Qt::CheckStateRole) { m_enabled = data.toInt() == Qt::Checked; return true; @@ -65,14 +64,14 @@ private: { QTC_ASSERT(column < 2, return Qt::ItemFlags()); Qt::ItemFlags flags = Qt::ItemIsSelectable; - if (column == 0 || !m_aspect->isEssential()) + if (column == 0 || !m_factory->isEssential()) flags |= Qt::ItemIsEnabled; - if (column == 1 && !m_aspect->isEssential()) + if (column == 1 && !m_factory->isEssential()) flags |= Qt::ItemIsUserCheckable; return flags; } - const KitAspect * const m_aspect; + const KitAspectFactory * const m_factory; bool m_enabled; }; @@ -82,13 +81,13 @@ public: FilterKitAspectsModel(const Kit *kit, QObject *parent) : TreeModel(parent) { setHeader({Tr::tr("Setting"), Tr::tr("Visible")}); - for (const KitAspect * const aspect : KitManager::kitAspects()) { - if (kit && !aspect->isApplicableToKit(kit)) + for (const KitAspectFactory * const factory : KitManager::kitAspectFactories()) { + if (kit && !factory->isApplicableToKit(kit)) continue; const QSet<Utils::Id> irrelevantAspects = kit ? kit->irrelevantAspects() : KitManager::irrelevantAspects(); - auto * const item = new FilterTreeItem(aspect, - !irrelevantAspects.contains(aspect->id())); + auto * const item = new FilterTreeItem(factory, + !irrelevantAspects.contains(factory->id())); rootItem()->appendChild(item); } static const auto cmp = [](const TreeItem *item1, const TreeItem *item2) { @@ -114,10 +113,7 @@ public: class FilterTreeView : public TreeView { public: - FilterTreeView(QWidget *parent) : TreeView(parent) - { - setUniformRowHeights(true); - } + FilterTreeView(QWidget *parent) : TreeView(parent) {} private: QSize sizeHint() const override @@ -148,5 +144,4 @@ QSet<Utils::Id> FilterKitAspectsDialog::irrelevantAspects() const return static_cast<FilterKitAspectsModel *>(m_model)->disabledItems(); } -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.h b/src/plugins/projectexplorer/filterkitaspectsdialog.h index 0f7e8c2d414..7ad98f3da25 100644 --- a/src/plugins/projectexplorer/filterkitaspectsdialog.h +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.h @@ -15,7 +15,6 @@ namespace Internal { class FilterKitAspectsDialog : public QDialog { - Q_OBJECT public: FilterKitAspectsDialog(const Kit *kit, QWidget *parent); QSet<Utils::Id> irrelevantAspects() const; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index f69ab0afc8f..78b5ba4737a 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -8,6 +8,7 @@ #include "devicesupport/idevice.h" #include "gccparser.h" #include "linuxiccparser.h" +#include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "projectmacro.h" #include "toolchainconfigwidget.h" @@ -22,11 +23,11 @@ #include <utils/pathchooser.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <utils/scopedtimer.h> #include <QBuffer> #include <QCheckBox> #include <QComboBox> -#include <QCoreApplication> #include <QDir> #include <QFileInfo> #include <QFormLayout> @@ -47,15 +48,25 @@ using namespace Utils; namespace ProjectExplorer { namespace Internal { +static const QStringList languageOption(Id languageId) +{ + if (languageId == Constants::C_LANGUAGE_ID) + return {"-x", "c"}; + return {"-x", "c++"}; +} + +const QStringList gccPredefinedMacrosOptions(Id languageId) +{ + return languageOption(languageId) + QStringList({"-E", "-dM"}); +} + class TargetTripleWidget; class GccToolChainConfigWidget : public ToolChainConfigWidget { - Q_OBJECT - public: explicit GccToolChainConfigWidget(GccToolChain *tc); -protected: +private: void handleCompilerCommandChange(); void handlePlatformCodeGenFlagsChange(); void handlePlatformLinkerFlagsChange(); @@ -67,32 +78,21 @@ protected: void setFromToolchain(); + void updateParentToolChainComboBox(); // Clang + AbiWidget *m_abiWidget; -private: - Utils::PathChooser *m_compilerCommand; + GccToolChain::SubType m_subType = GccToolChain::RealGcc; + + PathChooser *m_compilerCommand; QLineEdit *m_platformCodeGenFlagsLineEdit; QLineEdit *m_platformLinkerFlagsLineEdit; TargetTripleWidget * const m_targetTripleWidget; bool m_isReadOnly = false; ProjectExplorer::Macros m_macros; -}; -class ClangToolChainConfigWidget : public GccToolChainConfigWidget -{ - Q_OBJECT -public: - explicit ClangToolChainConfigWidget(ClangToolChain *tc); - -private: - void applyImpl() override; - void discardImpl() override { setFromClangToolchain(); } - bool isDirtyImpl() const override; - void makeReadOnlyImpl() override; - - void setFromClangToolchain(); - void updateParentToolChainComboBox(); + // Clang only QList<QMetaObject::Connection> m_parentToolChainConnections; QComboBox *m_parentToolchainCombo = nullptr; }; @@ -303,12 +303,44 @@ static FilePath gccInstallDir(const FilePath &compiler, // GccToolChain // -------------------------------------------------------------------------- -GccToolChain::GccToolChain(Utils::Id typeId) : - ToolChain(typeId) +static Id idForSubType(GccToolChain::SubType subType) +{ + switch (subType) { + case GccToolChain::RealGcc: + return Constants::GCC_TOOLCHAIN_TYPEID; + case GccToolChain::Clang: + return Constants::CLANG_TOOLCHAIN_TYPEID; + case GccToolChain::MinGW: + return Constants::MINGW_TOOLCHAIN_TYPEID; + case GccToolChain::LinuxIcc: + return Constants::LINUXICC_TOOLCHAIN_TYPEID; + } + QTC_CHECK(false); + return Constants::GCC_TOOLCHAIN_TYPEID; +} + +GccToolChain::GccToolChain(Id typeId, SubType subType) + : ToolChain(typeId.isValid() ? typeId : idForSubType(subType)), m_subType(subType) { setTypeDisplayName(Tr::tr("GCC")); setTargetAbiKey(targetAbiKeyC); setCompilerCommandKey("ProjectExplorer.GccToolChain.Path"); + if (m_subType == LinuxIcc) { + setTypeDisplayName(Tr::tr("ICC")); + } else if (m_subType == MinGW) { + setTypeDisplayName(Tr::tr("MinGW")); + } else if (m_subType == Clang) { + setTypeDisplayName(Tr::tr("Clang")); + syncAutodetectedWithParentToolchains(); + } +} + +GccToolChain::~GccToolChain() +{ + if (m_subType == Clang) { + QObject::disconnect(m_thisToolchainRemovedConnection); + QObject::disconnect(m_mingwToolchainAddedConnection); + } } void GccToolChain::setSupportedAbis(const Abis &abis) @@ -329,7 +361,7 @@ void GccToolChain::setOriginalTargetTriple(const QString &targetTriple) toolChainUpdated(); } -void GccToolChain::setInstallDir(const Utils::FilePath &installDir) +void GccToolChain::setInstallDir(const FilePath &installDir) { if (m_installDir == installDir) return; @@ -360,8 +392,33 @@ LanguageExtensions GccToolChain::defaultLanguageExtensions() const return LanguageExtension::Gnu; } +static const Toolchains mingwToolChains() +{ + return ToolChainManager::toolchains([](const ToolChain *tc) -> bool { + return tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID; + }); +} + +static const GccToolChain *mingwToolChainFromId(const QByteArray &id) +{ + if (id.isEmpty()) + return nullptr; + + for (const ToolChain *tc : mingwToolChains()) { + if (tc->id() == id) + return static_cast<const GccToolChain *>(tc); + } + + return nullptr; +} + QString GccToolChain::originalTargetTriple() const { + if (m_subType == Clang) { + if (const GccToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId)) + return parentTC->originalTargetTriple(); + } + if (m_originalTargetTriple.isEmpty()) m_originalTargetTriple = detectSupportedAbis().originalTargetTriple; return m_originalTargetTriple; @@ -391,25 +448,24 @@ static bool isNetworkCompiler(const QString &dirPath) return dirPath.contains("icecc") || dirPath.contains("distcc"); } -static Utils::FilePath findLocalCompiler(const Utils::FilePath &compilerPath, - const Environment &env) +static FilePath findLocalCompiler(const FilePath &compilerPath, const Environment &env) { // Find the "real" compiler if icecc, distcc or similar are in use. Ignore ccache, since that // is local already. // Get the path to the compiler, ignoring direct calls to icecc and distcc as we cannot // do anything about those. - if (!isNetworkCompiler(compilerPath.parentDir().toString())) + if (!isNetworkCompiler(compilerPath.parentDir().path())) return compilerPath; // Filter out network compilers const FilePaths pathComponents = Utils::filtered(env.path(), [] (const FilePath &dirPath) { - return !isNetworkCompiler(dirPath.toString()); + return !isNetworkCompiler(dirPath.path()); }); // This effectively searches the PATH twice, once via pathComponents and once via PATH itself: // searchInPath filters duplicates, so that will not hurt. - const Utils::FilePath path = env.searchInPath(compilerPath.fileName(), pathComponents); + const FilePath path = env.searchInPath(compilerPath.fileName(), pathComponents); return path.isEmpty() ? compilerPath : path; } @@ -456,7 +512,7 @@ ToolChain::MacroInspectionRunner GccToolChain::createMacroInspectionRunner() con OptionsReinterpreter reinterpretOptions = m_optionsReinterpreter; QTC_CHECK(reinterpretOptions); MacrosCache macroCache = predefinedMacrosCache(); - Utils::Id lang = language(); + Id lang = language(); /* * Asks compiler for set of predefined macros @@ -502,7 +558,7 @@ ToolChain::MacroInspectionRunner GccToolChain::createMacroInspectionRunner() con * @brief Parses gcc flags -std=*, -fopenmp, -fms-extensions. * @see http://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html */ -Utils::LanguageExtensions GccToolChain::languageExtensions(const QStringList &cxxflags) const +LanguageExtensions GccToolChain::languageExtensions(const QStringList &cxxflags) const { LanguageExtensions extensions = defaultLanguageExtensions(); @@ -521,6 +577,27 @@ Utils::LanguageExtensions GccToolChain::languageExtensions(const QStringList &cx } } + // Clang knows -fborland-extensions". + if (m_subType == Clang && cxxflags.contains("-fborland-extensions")) + extensions |= LanguageExtension::Borland; + + if (m_subType == LinuxIcc) { + // and "-fms-dialect[=ver]" instead of "-fms-extensions". + // see UNIX manual for "icc" + // FIXME: This copy seems unneeded. + QStringList copy = cxxflags; + copy.removeAll("-fopenmp"); + copy.removeAll("-fms-extensions"); + + if (cxxflags.contains("-openmp")) + extensions |= LanguageExtension::OpenMP; + if (cxxflags.contains("-fms-dialect") + || cxxflags.contains("-fms-dialect=8") + || cxxflags.contains("-fms-dialect=9") + || cxxflags.contains("-fms-dialect=10")) + extensions |= LanguageExtension::Microsoft; + } + return extensions; } @@ -566,6 +643,17 @@ WarningFlags GccToolChain::warningFlags(const QStringList &cflags) const add("unused-value", WarningFlags::UnusedValue); add("uninitialized", WarningFlags::UninitializedVars); } + + if (m_subType == Clang) { + for (int end = cflags.size(), i = 0; i != end; ++i) { + const QString &flag = cflags[i]; + if (flag == "-Wdocumentation") + flags |= WarningFlags::Documentation; + if (flag == "-Wno-documentation") + flags &= ~WarningFlags::Documentation; + } + } + return flags; } @@ -574,11 +662,10 @@ FilePaths GccToolChain::includedFiles(const QStringList &flags, const FilePath & return ToolChain::includedFiles("-include", flags, directoryPath, PossiblyConcatenatedFlag::No); } -QStringList GccToolChain::gccPrepareArguments(const QStringList &flags, - const FilePath &sysRoot, - const QStringList &platformCodeGenFlags, - Id languageId, - OptionsReinterpreter reinterpretOptions) +static QStringList gccPrepareArguments(const QStringList &flags, + const FilePath &sysRoot, + const QStringList &platformCodeGenFlags, + Id languageId) { QStringList arguments; const bool hasKitSysroot = !sysRoot.isEmpty(); @@ -589,7 +676,6 @@ QStringList GccToolChain::gccPrepareArguments(const QStringList &flags, allFlags << platformCodeGenFlags << flags; arguments += filteredFlags(allFlags, !hasKitSysroot); arguments << languageOption(languageId) << "-E" << "-v" << "-"; - arguments = reinterpretOptions(arguments); return arguments; } @@ -600,22 +686,22 @@ void GccToolChain::initExtraHeaderPathsFunction(ExtraHeaderPathsFunction &&extra m_extraHeaderPathsFunction = std::move(extraHeaderPathsFunction); } -HeaderPaths GccToolChain::builtInHeaderPaths(const Utils::Environment &env, - const Utils::FilePath &compilerCommand, +HeaderPaths GccToolChain::builtInHeaderPaths(const Environment &env, + const FilePath &compilerCommand, const QStringList &platformCodeGenFlags, OptionsReinterpreter reinterpretOptions, HeaderPathsCache headerCache, - Utils::Id languageId, + Id languageId, ExtraHeaderPathsFunction extraHeaderPathsFunction, const QStringList &flags, - const Utils::FilePath &sysRoot, + const FilePath &sysRoot, const QString &originalTargetTriple) { QStringList arguments = gccPrepareArguments(flags, sysRoot, platformCodeGenFlags, - languageId, - reinterpretOptions); + languageId); + arguments = reinterpretOptions(arguments); // Must be clang case only. if (!originalTargetTriple.isEmpty()) @@ -648,6 +734,30 @@ ToolChain::BuiltInHeaderPathsRunner GccToolChain::createBuiltInHeaderPathsRunner Environment fullEnv = env; addToEnvironment(fullEnv); + if (m_subType == Clang) { + // This runner must be thread-safe! + return [fullEnv, + compilerCommand = compilerCommand(), + platformCodeGenFlags = m_platformCodeGenFlags, + reinterpretOptions = m_optionsReinterpreter, + headerCache = headerPathsCache(), + languageId = language(), + extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags, + const FilePath &sysRoot, + const QString &target) { + return builtInHeaderPaths(fullEnv, + compilerCommand, + platformCodeGenFlags, + reinterpretOptions, + headerCache, + languageId, + extraHeaderPathsFunction, + flags, + sysRoot, + target); + }; + } + // This runner must be thread-safe! return [fullEnv, compilerCommand = compilerCommand(), @@ -682,10 +792,51 @@ void GccToolChain::addToEnvironment(Environment &env) const // cc1plus depends on libwinpthread-1.dll which is in bin, so bin must be in the PATH. if (compilerCommand().osType() == OsTypeWindows) addCommandPathToEnvironment(compilerCommand(), env); + + if (m_subType == Clang) { + const QString sysroot = sysRoot(); + if (!sysroot.isEmpty()) + env.prependOrSetPath(FilePath::fromString(sysroot) / "bin"); + + // Clang takes PWD as basis for debug info, if set. + // When running Qt Creator from a shell, PWD is initially set to an "arbitrary" value. + // Since the tools are not called through a shell, PWD is never changed to the actual cwd, + // so we better make sure PWD is empty to begin with + env.unset("PWD"); + } } QStringList GccToolChain::suggestedMkspecList() const { + if (m_subType == LinuxIcc) + return {QString("linux-icc-%1").arg(targetAbi().wordWidth())}; + + if (m_subType == MinGW) { + if (HostOsInfo::isWindowsHost()) + return {"win32-g++"}; + if (HostOsInfo::isLinuxHost()) { + if (version().startsWith("4.6.")) + return {"win32-g++-4.6-cross", "unsupported/win32-g++-4.6-cross"}; + return {"win32-g++-cross", "unsupported/win32-g++-cross"}; + } + return {}; + } + + if (m_subType == Clang) { + if (const ToolChain * const parentTc = ToolChainManager::findToolChain(m_parentToolChainId)) + return parentTc->suggestedMkspecList(); + const Abi abi = targetAbi(); + if (abi.os() == Abi::DarwinOS) + return {"macx-clang", "macx-clang-32", "unsupported/macx-clang", "macx-ios-clang"}; + if (abi.os() == Abi::LinuxOS) + return {"linux-clang", "unsupported/linux-clang"}; + if (abi.os() == Abi::WindowsOS) + return {"win32-clang-g++"}; + if (abi.architecture() == Abi::AsmJsArchitecture && abi.binaryFormat() == Abi::EmscriptenFormat) + return {"wasm-emscripten"}; + return {}; // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with + } + const Abi abi = targetAbi(); const Abi host = Abi::hostAbi(); @@ -722,14 +873,37 @@ QStringList GccToolChain::suggestedMkspecList() const return {}; } +static FilePath mingwAwareMakeCommand(const Environment &environment) +{ + const QStringList makes + = HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) : QStringList({"make"}); + + FilePath tmp; + for (const QString &make : makes) { + tmp = environment.searchInPath(make); + if (!tmp.isEmpty()) + return tmp; + } + return FilePath::fromString(makes.first()); +} + FilePath GccToolChain::makeCommand(const Environment &environment) const { + if (m_subType == Clang || m_subType == MinGW) + return mingwAwareMakeCommand(environment); + const FilePath tmp = environment.searchInPath("make"); return tmp.isEmpty() ? "make" : tmp; } QList<OutputLineParser *> GccToolChain::createOutputParsers() const { + if (m_subType == LinuxIcc) + return LinuxIccParser::iccParserSuite(); + + if (m_subType == Clang) + return ClangParser::clangParserSuite(); + return GccParser::gccParserSuite(); } @@ -795,20 +969,25 @@ QStringList GccToolChain::platformLinkerFlags() const return m_platformLinkerFlags; } -QVariantMap GccToolChain::toMap() const +void GccToolChain::toMap(Store &data) const { - QVariantMap data = ToolChain::toMap(); + ToolChain::toMap(data); data.insert(compilerPlatformCodeGenFlagsKeyC, m_platformCodeGenFlags); data.insert(compilerPlatformLinkerFlagsKeyC, m_platformLinkerFlags); data.insert(originalTargetTripleKeyC, m_originalTargetTriple); data.insert(supportedAbisKeyC, Utils::transform<QStringList>(m_supportedAbis, &Abi::toString)); - return data; + + if (m_subType == Clang) { + data.insert(parentToolChainIdKeyC, m_parentToolChainId); + data.insert(priorityKeyC, m_priority); + } } -bool GccToolChain::fromMap(const QVariantMap &data) +void GccToolChain::fromMap(const Store &data) { - if (!ToolChain::fromMap(data)) - return false; + ToolChain::fromMap(data); + if (hasError()) + return; m_platformCodeGenFlags = data.value(compilerPlatformCodeGenFlagsKeyC).toStringList(); m_platformLinkerFlags = data.value(compilerPlatformLinkerFlagsKeyC).toStringList(); @@ -822,7 +1001,11 @@ bool GccToolChain::fromMap(const QVariantMap &data) if (targetAbiString.isEmpty()) resetToolChain(compilerCommand()); - return true; + if (m_subType == Clang) { + m_parentToolChainId = data.value(parentToolChainIdKeyC).toByteArray(); + m_priority = data.value(priorityKeyC, PriorityNormal).toInt(); + syncAutodetectedWithParentToolchains(); + } } bool GccToolChain::operator ==(const ToolChain &other) const @@ -874,7 +1057,7 @@ QString GccToolChain::detectVersion() const filteredFlags(platformCodeGenFlags(), true)); } -Utils::FilePath GccToolChain::detectInstallDir() const +FilePath GccToolChain::detectInstallDir() const { Environment env = compilerCommand().deviceEnvironment(); addToEnvironment(env); @@ -886,7 +1069,7 @@ Utils::FilePath GccToolChain::detectInstallDir() const // GccToolChainFactory // -------------------------------------------------------------------------- -static Utils::FilePaths gnuSearchPathsFromRegistry() +static FilePaths gnuSearchPathsFromRegistry() { if (!HostOsInfo::isWindowsHost()) return {}; @@ -895,7 +1078,7 @@ static Utils::FilePaths gnuSearchPathsFromRegistry() static const char kRegistryToken[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" \ "Windows\\CurrentVersion\\Uninstall\\"; - Utils::FilePaths searchPaths; + FilePaths searchPaths; QSettings registry(kRegistryToken, QSettings::NativeFormat); const auto productGroups = registry.childGroups(); @@ -1028,153 +1211,290 @@ static Utils::FilePaths renesasRl78SearchPathsFromRegistry() return searchPaths; } -GccToolChainFactory::GccToolChainFactory() +static ToolChain *constructRealGccToolchain() { - setDisplayName(Tr::tr("GCC")); - setSupportedToolChainType(Constants::GCC_TOOLCHAIN_TYPEID); + return new GccToolChain(Constants::GCC_TOOLCHAIN_TYPEID, GccToolChain::RealGcc); +} + +static ToolChain *constructClangToolchain() +{ + return new GccToolChain(Constants::CLANG_TOOLCHAIN_TYPEID, GccToolChain::Clang); +} + +static ToolChain *constructMinGWToolchain() +{ + return new GccToolChain(Constants::MINGW_TOOLCHAIN_TYPEID, GccToolChain::MinGW); +} + +static ToolChain *constructLinuxIccToolchain() +{ + return new GccToolChain(Constants::LINUXICC_TOOLCHAIN_TYPEID, GccToolChain::LinuxIcc); +} + +GccToolChainFactory::GccToolChainFactory(GccToolChain::SubType subType) + : m_autoDetecting(subType == GccToolChain::RealGcc) +{ + switch (subType) { + case GccToolChain::RealGcc: + setDisplayName(Tr::tr("GCC")); + setSupportedToolChainType(Constants::GCC_TOOLCHAIN_TYPEID); + setToolchainConstructor(&constructRealGccToolchain); + break; + case GccToolChain::Clang: + setDisplayName(Tr::tr("Clang")); + setSupportedToolChainType(Constants::CLANG_TOOLCHAIN_TYPEID); + setToolchainConstructor(&constructClangToolchain); + break; + case GccToolChain::MinGW: + setDisplayName(Tr::tr("MinGW")); + setSupportedToolChainType(Constants::MINGW_TOOLCHAIN_TYPEID); + setToolchainConstructor(&constructMinGWToolchain); + break; + case GccToolChain::LinuxIcc: + setDisplayName(Tr::tr("ICC")); + setSupportedToolChainType(Constants::LINUXICC_TOOLCHAIN_TYPEID); + setToolchainConstructor(&constructLinuxIccToolchain); + break; + } setSupportedLanguages({Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}); - setToolchainConstructor([] { return new GccToolChain(Constants::GCC_TOOLCHAIN_TYPEID); }); setUserCreatable(true); } -Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) const -{ - // GCC is almost never what you want on macOS, but it is by default found in /usr/bin - if (HostOsInfo::isMacHost() && detector.device->type() == Constants::DESKTOP_DEVICE_TYPE) - return {}; - - Toolchains tcs; - static const auto tcChecker = [](const ToolChain *tc) { - return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor - && tc->compilerCommand().fileName() != "c89-gcc" - && tc->compilerCommand().fileName() != "c99-gcc"; - }; - tcs.append(autoDetectToolchains("g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, - Constants::GCC_TOOLCHAIN_TYPEID, detector, tcChecker)); - tcs.append(autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, - Constants::GCC_TOOLCHAIN_TYPEID, detector, tcChecker)); - return tcs; -} - -Toolchains GccToolChainFactory::detectForImport(const ToolChainDescription &tcd) const -{ - const QString fileName = tcd.compilerPath.completeBaseName(); - const QString resolvedSymlinksFileName = tcd.compilerPath.resolveSymlinks().completeBaseName(); - - const bool isCCompiler = tcd.language == Constants::C_LANGUAGE_ID - && (fileName.startsWith("gcc") - || fileName.endsWith("gcc") - || (fileName == "cc" && !resolvedSymlinksFileName.contains("clang"))); - - const bool isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID - && (fileName.startsWith("g++") - || fileName.endsWith("g++") - || (fileName == "c++" && !resolvedSymlinksFileName.contains("clang"))); - - if (isCCompiler || isCxxCompiler) { - return autoDetectToolChain(tcd, [](const ToolChain *tc) { - return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor; - }); - } - return {}; -} - -static FilePaths findCompilerCandidates(const ToolchainDetector &detector, +static FilePaths findCompilerCandidates(OsType os, + const FilePaths &executables, const QString &compilerName, bool detectVariants) { - const IDevice::ConstPtr device = detector.device; - const QFileInfo fi(compilerName); - if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE && fi.isAbsolute() && fi.isFile()) - return {FilePath::fromString(compilerName)}; - - QStringList nameFilters(compilerName); - if (detectVariants) { - nameFilters - << compilerName + "-[1-9]*" // "clang-8", "gcc-5" - << ("*-" + compilerName) // "avr-gcc", "avr32-gcc" - << ("*-" + compilerName + "-[1-9]*")// "avr-gcc-4.8.1", "avr32-gcc-4.4.7" - << ("*-*-*-" + compilerName) // "arm-none-eabi-gcc" - << ("*-*-*-" + compilerName + "-[1-9]*") // "arm-none-eabi-gcc-9.1.0" - << ("*-*-*-*-" + compilerName) // "x86_64-pc-linux-gnu-gcc" - << ("*-*-*-*-" + compilerName - + "-[1-9]*"); // "x86_64-pc-linux-gnu-gcc-7.4.1" - } - nameFilters = transform(nameFilters, - [os = device ? device->osType() : HostOsInfo::hostOs()](const QString &baseName) { - return OsSpecificAspects::withExecutableSuffix(os, baseName); - }); + // We expect the following patterns: + // compilerName "clang", "gcc" + // compilerName + "-[1-9]*" "clang-8", "gcc-5" + // "*-" + compilerName "avr-gcc", "avr32-gcc" + // "arm-none-eabi-gcc" + // "x86_64-pc-linux-gnu-gcc" + // "*-" + compilerName + "-[1-9]*" "avr-gcc-4.8.1", "avr32-gcc-4.4.7" + // "arm-none-eabi-gcc-9.1.0" + // "x86_64-pc-linux-gnu-gcc-7.4.1" + // but not "c89-gcc" or "c99-gcc" FilePaths compilerPaths; + const int cl = compilerName.size(); + for (const FilePath &executable : executables) { + QStringView fileName = executable.fileNameView(); + if (os == OsTypeWindows && fileName.endsWith(u".exe", Qt::CaseInsensitive)) + fileName.chop(4); - if (device && device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - // FIXME: Merge with block below - FilePaths searchPaths = detector.searchPaths; - if (searchPaths.isEmpty()) - searchPaths = device->systemEnvironment().path(); - for (const FilePath &deviceDir : std::as_const(searchPaths)) { - static const QRegularExpression regexp(binaryRegexp); - const auto callBack = [&compilerPaths, compilerName](const FilePath &candidate) { - if (candidate.fileName() == compilerName) - compilerPaths << candidate; - else if (regexp.match(candidate.path()).hasMatch()) - compilerPaths << candidate; - return IterationPolicy::Continue; - }; - const FilePath globalDir = device->filePath(deviceDir.path()); - globalDir.iterateDirectory(callBack, {nameFilters, QDir::Files | QDir::Executable}); - } - } else { - // The normal, local host case. - FilePaths searchPaths = detector.searchPaths; - if (searchPaths.isEmpty()) { - searchPaths = Environment::systemEnvironment().path(); - searchPaths << gnuSearchPathsFromRegistry(); - searchPaths << atmelSearchPathsFromRegistry(); - searchPaths << renesasRl78SearchPathsFromRegistry(); - if (HostOsInfo::isMacHost()) { - searchPaths << "/opt/homebrew/opt/ccache/libexec" // homebrew arm - << "/usr/local/opt/ccache/libexec" // homebrew intel - << "/opt/local/libexec/ccache"; // macports, no links are created automatically though - } - if (HostOsInfo::isAnyUnixHost()) { - FilePath ccachePath = "/usr/lib/ccache/bin"; - if (!ccachePath.exists()) - ccachePath = "/usr/lib/ccache"; - if (ccachePath.exists() && !searchPaths.contains(ccachePath)) - searchPaths << ccachePath; - } - } - for (const FilePath &dir : std::as_const(searchPaths)) { - static const QRegularExpression regexp(binaryRegexp); - QDir binDir(dir.toString()); - const QStringList fileNames = binDir.entryList(nameFilters, - QDir::Files | QDir::Executable); - for (const QString &fileName : fileNames) { - if (fileName != compilerName && - !regexp.match(QFileInfo(fileName).completeBaseName()).hasMatch()) { - continue; - } - compilerPaths << FilePath::fromString(binDir.filePath(fileName)); - } + if (fileName == compilerName) + compilerPaths << executable; + + if (!detectVariants) + continue; + + if (fileName == u"c89-gcc" || fileName == u"c99-gcc") + continue; + + int pos = fileName.indexOf(compilerName); + if (pos == -1) + continue; + + // if not at the beginning, it must be preceded by a hyphen. + if (pos > 0 && fileName.at(pos - 1) != '-') + continue; + + // if not at the end, it must by followed by a hyphen and a digit between 1 and 9 + pos += cl; + if (pos != fileName.size()) { + if (pos + 2 >= fileName.size()) + continue; + if (fileName.at(pos) != '-') + continue; + const QChar c = fileName.at(pos + 1); + if (c < '1' || c > '9') + continue; } + + compilerPaths << executable; } return compilerPaths; } -Toolchains GccToolChainFactory::autoDetectToolchains( - const QString &compilerName, - DetectVariants detectVariants, - const Id language, - const Id requiredTypeId, - const ToolchainDetector &detector, - const ToolchainChecker &checker) const + +Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) const { - const FilePaths compilerPaths = - findCompilerCandidates(detector, compilerName, detectVariants == DetectVariants::Yes); - Toolchains existingCandidates = filtered(detector.alreadyKnown, + QTC_ASSERT(detector.device, return {}); + + // Do all autodetection in th 'RealGcc' case, and none in the others. + if (!m_autoDetecting) + return {}; + + const bool isLocal = detector.device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; + FilePaths searchPaths = detector.searchPaths; + if (!isLocal) { + if (searchPaths.isEmpty()) + searchPaths = detector.device->systemEnvironment().path(); + searchPaths = Utils::transform(searchPaths, [&](const FilePath &onDevice) { + return detector.device->filePath(onDevice.path()); + }); + } else if (searchPaths.isEmpty()) { + searchPaths = Environment::systemEnvironment().path(); + searchPaths << gnuSearchPathsFromRegistry(); + searchPaths << atmelSearchPathsFromRegistry(); + searchPaths << renesasRl78SearchPathsFromRegistry(); + if (HostOsInfo::isMacHost()) { + searchPaths << "/opt/homebrew/opt/ccache/libexec" // homebrew arm + << "/usr/local/opt/ccache/libexec" // homebrew intel + << "/opt/local/libexec/ccache"; // macports, no links are created automatically though + } + if (HostOsInfo::isAnyUnixHost()) { + FilePath ccachePath = "/usr/lib/ccache/bin"; + if (!ccachePath.exists()) + ccachePath = "/usr/lib/ccache"; + if (ccachePath.exists() && !searchPaths.contains(ccachePath)) + searchPaths << ccachePath; + } + } + + FilePaths executables; + + QStringList nameFilters = {"*icpc*", "*icc*", "*clang++*", "*clang*", "*gcc*", "*g++*"}; + + FilePath::iterateDirectories(searchPaths, [&executables](const FilePath &path) { + executables.append(path); + return IterationPolicy::Continue; + }, {nameFilters, QDir::Files | QDir::Executable }); + + // Gcc is almost never what you want on macOS, but it is by default found in /usr/bin + if (HostOsInfo::isMacHost() && detector.device->type() == Constants::DESKTOP_DEVICE_TYPE) { + executables.removeOne(FilePath::fromPathPart(u"/usr/bin/gcc")); + executables.removeOne(FilePath::fromPathPart(u"/usr/bin/g++")); + } + + const OsType os = detector.device->osType(); + + Toolchains result; + + // Linux ICC + + result += autoDetectToolchains(findCompilerCandidates(os, executables, "icpc", false), + Constants::CXX_LANGUAGE_ID, + Constants::LINUXICC_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::LinuxIcc); + + + result += autoDetectToolchains(findCompilerCandidates(os, executables, "icc", true), + Constants::C_LANGUAGE_ID, + Constants::LINUXICC_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::LinuxIcc); + + // Clang + + Toolchains tcs; + Toolchains known = detector.alreadyKnown; + + tcs.append(autoDetectToolchains(findCompilerCandidates(os, executables, "clang++", true), + Constants::CXX_LANGUAGE_ID, + Constants::CLANG_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::Clang)); + tcs.append(autoDetectToolchains(findCompilerCandidates(os, executables, "clang", true), + Constants::C_LANGUAGE_ID, + Constants::CLANG_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::Clang)); + known.append(tcs); + + tcs.append(autoDetectSdkClangToolchain(known)); + + result += tcs; + + // Real GCC or MingGW + + result += autoDetectToolchains(findCompilerCandidates(os, executables, "g++", true), + Constants::CXX_LANGUAGE_ID, + Constants::GCC_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::RealGcc /*sic!*/); + result += autoDetectToolchains(findCompilerCandidates(os, executables, "gcc", true), + Constants::C_LANGUAGE_ID, + Constants::GCC_TOOLCHAIN_TYPEID, + detector.alreadyKnown, + GccToolChain::RealGcc /*sic!*/); + + return result; +} + +Toolchains GccToolChainFactory::detectForImport(const ToolChainDescription &tcd) const +{ + Toolchains result; + + // Do all autodetection in th 'RealGcc' case, and none in the others. + if (!m_autoDetecting) + return result; + + const QString fileName = tcd.compilerPath.completeBaseName(); + const QString resolvedSymlinksFileName = tcd.compilerPath.resolveSymlinks().completeBaseName(); + + // Linux ICC + + if ((tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("icpc")) || + (tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("icc"))) { + result += autoDetectToolChain(tcd, GccToolChain::LinuxIcc); + } + + bool isCCompiler = tcd.language == Constants::C_LANGUAGE_ID + && ((fileName.startsWith("clang") && !fileName.startsWith("clang++")) + || (fileName == "cc" && resolvedSymlinksFileName.contains("clang"))); + + bool isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID + && (fileName.startsWith("clang++") + || (fileName == "c++" && resolvedSymlinksFileName.contains("clang"))); + + if (isCCompiler || isCxxCompiler) + result += autoDetectToolChain(tcd, GccToolChain::Clang); + + // GCC + + isCCompiler = tcd.language == Constants::C_LANGUAGE_ID + && (fileName.startsWith("gcc") + || fileName.endsWith("gcc") + || (fileName == "cc" && !resolvedSymlinksFileName.contains("clang"))); + + isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID + && (fileName.startsWith("g++") + || fileName.endsWith("g++") + || (fileName == "c++" && !resolvedSymlinksFileName.contains("clang"))); + + if (isCCompiler || isCxxCompiler) + result += autoDetectToolChain(tcd, GccToolChain::RealGcc); + + return result; +} + +Toolchains GccToolChainFactory::autoDetectSdkClangToolchain(const Toolchains &known) +{ + const FilePath compilerPath = Core::ICore::clangExecutable(CLANG_BINDIR); + if (compilerPath.isEmpty()) + return {}; + + for (ToolChain * const existingTc : known) { + if (existingTc->compilerCommand() == compilerPath) + return {existingTc}; + } + + return {autoDetectToolChain({compilerPath, Constants::C_LANGUAGE_ID}, GccToolChain::Clang)}; +} + +Toolchains GccToolChainFactory::autoDetectToolchains(const FilePaths &compilerPaths, + const Id language, + const Id requiredTypeId, + const Toolchains &known, + const GccToolChain::SubType subType) +{ + Toolchains existingCandidates = filtered(known, [language](const ToolChain *tc) { return tc->language() == language; }); + Toolchains result; for (const FilePath &compilerPath : std::as_const(compilerPaths)) { bool alreadyExists = false; @@ -1198,21 +1518,17 @@ Toolchains GccToolChainFactory::autoDetectToolchains( && HostOsInfo::isWindowsHost() && !existingCommand.needsDevice() && !compilerPath.needsDevice()) { - existingTcMatches = existingCommand.fileSize() - == compilerPath.fileSize(); + existingTcMatches = existingCommand.fileSize() == compilerPath.fileSize(); } } if (existingTcMatches) { - if (existingTc->typeId() == requiredTypeId && (!checker || checker(existingTc)) - && !result.contains(existingTc)) { + if (existingTc->typeId() == requiredTypeId && !result.contains(existingTc)) result << existingTc; - } alreadyExists = true; } } if (!alreadyExists) { - const QList<ToolChain *> newToolchains - = autoDetectToolChain({compilerPath, language}, checker); + const Toolchains newToolchains = autoDetectToolChain({compilerPath, language}, subType); result << newToolchains; existingCandidates << newToolchains; } @@ -1222,7 +1538,7 @@ Toolchains GccToolChainFactory::autoDetectToolchains( } Toolchains GccToolChainFactory::autoDetectToolChain(const ToolChainDescription &tcd, - const ToolchainChecker &checker) const + GccToolChain::SubType subType) { Toolchains result; @@ -1242,9 +1558,11 @@ Toolchains GccToolChainFactory::autoDetectToolChain(const ToolChainDescription & systemEnvironment, macros); for (const Abi &abi : detectedAbis.supportedAbis) { - std::unique_ptr<GccToolChain> tc(dynamic_cast<GccToolChain *>(create())); - if (!tc) - return result; + GccToolChain::SubType detectedSubType = subType; + if (detectedSubType == GccToolChain::RealGcc && abi.osFlavor() == Abi::WindowsMSysFlavor) + detectedSubType = GccToolChain::MinGW; + + auto tc = new GccToolChain({}, detectedSubType); tc->setLanguage(tcd.language); tc->setDetection(ToolChain::AutoDetection); @@ -1257,8 +1575,10 @@ Toolchains GccToolChainFactory::autoDetectToolChain(const ToolChainDescription & tc->setTargetAbi(abi); tc->setOriginalTargetTriple(detectedAbis.originalTargetTriple); tc->setDisplayName(tc->defaultDisplayName()); // reset displayname - if (!checker || checker(tc.get())) - result.append(tc.release()); + // lower priority of g++/gcc on macOS - usually just a frontend to clang + if (detectedSubType == GccToolChain::RealGcc && abi.binaryFormat() == Abi::MachOFormat) + tc->setPriority(ToolChain::PriorityLow); + result.append(tc); } return result; } @@ -1312,11 +1632,10 @@ private: GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) : ToolChainConfigWidget(tc), m_abiWidget(new AbiWidget), + m_subType(tc->m_subType), m_compilerCommand(new PathChooser), m_targetTripleWidget(new TargetTripleWidget(tc)) { - Q_ASSERT(tc); - const QStringList gnuVersionArgs = QStringList("--version"); m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); @@ -1346,6 +1665,44 @@ GccToolChainConfigWidget::GccToolChainConfigWidget(GccToolChain *tc) : connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolChainConfigWidget::dirty); connect(m_targetTripleWidget, &TargetTripleWidget::valueChanged, this, &ToolChainConfigWidget::dirty); + + if (m_subType == GccToolChain::Clang) { + if (!HostOsInfo::isWindowsHost() || tc->typeId() != Constants::CLANG_TOOLCHAIN_TYPEID) + return; + + // Remove m_abiWidget row because the parent toolchain abi is going to be used. + m_mainLayout->removeRow(m_mainLayout->rowCount() - 3); // FIXME: Do something sane instead. + m_abiWidget = nullptr; + + m_parentToolchainCombo = new QComboBox(this); + m_mainLayout->insertRow(m_mainLayout->rowCount() - 1, + Tr::tr("Parent toolchain:"), + m_parentToolchainCombo); + + ToolChainManager *tcManager = ToolChainManager::instance(); + m_parentToolChainConnections.append( + connect(tcManager, &ToolChainManager::toolChainUpdated, this, [this](ToolChain *tc) { + if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) + updateParentToolChainComboBox(); + })); + m_parentToolChainConnections.append( + connect(tcManager, &ToolChainManager::toolChainAdded, this, [this](ToolChain *tc) { + if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) + updateParentToolChainComboBox(); + })); + m_parentToolChainConnections.append( + connect(tcManager, &ToolChainManager::toolChainRemoved, this, [this](ToolChain *tc) { + if (tc->id() == toolChain()->id()) { + for (QMetaObject::Connection &connection : m_parentToolChainConnections) + QObject::disconnect(connection); + return; + } + if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) + updateParentToolChainComboBox(); + })); + + updateParentToolChainComboBox(); + } } void GccToolChainConfigWidget::applyImpl() @@ -1376,6 +1733,23 @@ void GccToolChainConfigWidget::applyImpl() ToolChain::MacroInspectionReport{m_macros, ToolChain::languageVersion(tc->language(), m_macros)}); + + if (m_subType == GccToolChain::Clang && m_parentToolchainCombo) { + + tc->m_parentToolChainId.clear(); + + const QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray(); + if (!parentId.isEmpty()) { + for (const ToolChain *mingwTC : mingwToolChains()) { + if (parentId == mingwTC->id()) { + tc->m_parentToolChainId = mingwTC->id(); + tc->setTargetAbi(mingwTC->targetAbi()); + tc->setSupportedAbis(mingwTC->supportedAbis()); + break; + } + } + } + } } void GccToolChainConfigWidget::setFromToolchain() @@ -1393,20 +1767,32 @@ void GccToolChainConfigWidget::setFromToolchain() if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty()) m_abiWidget->setEnabled(true); } + + if (m_parentToolchainCombo) + updateParentToolChainComboBox(); } bool GccToolChainConfigWidget::isDirtyImpl() const { auto tc = static_cast<GccToolChain *>(toolChain()); - Q_ASSERT(tc); - return m_compilerCommand->filePath() != tc->compilerCommand() + + if (m_compilerCommand->filePath() != tc->compilerCommand() || m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(tc->platformCodeGenFlags()) || m_platformLinkerFlagsLineEdit->text() != ProcessArgs::joinArgs(tc->platformLinkerFlags()) || m_targetTripleWidget->explicitCodeModelTargetTriple() != tc->explicitCodeModelTargetTriple() - || (m_abiWidget && m_abiWidget->currentAbi() != tc->targetAbi()); + || (m_abiWidget && m_abiWidget->currentAbi() != tc->targetAbi())) { + return true; + } + + if (!m_parentToolchainCombo) + return false; + + const GccToolChain *parentTC = mingwToolChainFromId(tc->m_parentToolChainId); + const QByteArray parentId = parentTC ? parentTC->id() : QByteArray(); + return parentId != m_parentToolchainCombo->currentData(); } void GccToolChainConfigWidget::makeReadOnlyImpl() @@ -1418,6 +1804,9 @@ void GccToolChainConfigWidget::makeReadOnlyImpl() m_platformLinkerFlagsLineEdit->setEnabled(false); m_targetTripleWidget->setEnabled(false); m_isReadOnly = true; + + if (m_parentToolchainCombo) + m_parentToolchainCombo->setEnabled(false); } void GccToolChainConfigWidget::handleCompilerCommandChange() @@ -1479,27 +1868,7 @@ void GccToolChainConfigWidget::handlePlatformLinkerFlagsChange() // ClangToolChain // -------------------------------------------------------------------------- -static const Toolchains mingwToolChains() -{ - return ToolChainManager::toolchains([](const ToolChain *tc) -> bool { - return tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID; - }); -} - -static const MingwToolChain *mingwToolChainFromId(const QByteArray &id) -{ - if (id.isEmpty()) - return nullptr; - - for (const ToolChain *tc : mingwToolChains()) { - if (tc->id() == id) - return static_cast<const MingwToolChain *>(tc); - } - - return nullptr; -} - -void ClangToolChain::syncAutodetectedWithParentToolchains() +void GccToolChain::syncAutodetectedWithParentToolchains() { if (!HostOsInfo::isWindowsHost() || typeId() != Constants::CLANG_TOOLCHAIN_TYPEID || !isAutoDetected()) { @@ -1510,339 +1879,85 @@ void ClangToolChain::syncAutodetectedWithParentToolchains() QObject::disconnect(m_mingwToolchainAddedConnection); if (!ToolChainManager::isLoaded()) { - QObject::connect(ToolChainManager::instance(), &ToolChainManager::toolChainsLoaded, - [id = id()] { + connect(ToolChainManager::instance(), &ToolChainManager::toolChainsLoaded, this, + [id = id()] { if (ToolChain * const tc = ToolChainManager::findToolChain(id)) { if (tc->typeId() == Constants::CLANG_TOOLCHAIN_TYPEID) - static_cast<ClangToolChain *>(tc)->syncAutodetectedWithParentToolchains(); + static_cast<GccToolChain *>(tc)->syncAutodetectedWithParentToolchains(); } }); return; } if (!mingwToolChainFromId(m_parentToolChainId)) { - const QList<ToolChain *> mingwTCs = mingwToolChains(); + const Toolchains mingwTCs = mingwToolChains(); m_parentToolChainId = mingwTCs.isEmpty() ? QByteArray() : mingwTCs.front()->id(); } // Subscribe only autodetected toolchains. ToolChainManager *tcManager = ToolChainManager::instance(); m_mingwToolchainAddedConnection - = QObject::connect(tcManager, &ToolChainManager::toolChainAdded, [this](ToolChain *tc) { + = connect(tcManager, &ToolChainManager::toolChainAdded, this, [this](ToolChain *tc) { if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID && !mingwToolChainFromId(m_parentToolChainId)) { m_parentToolChainId = tc->id(); } }); m_thisToolchainRemovedConnection - = QObject::connect(tcManager, &ToolChainManager::toolChainRemoved, [this](ToolChain *tc) { + = connect(tcManager, &ToolChainManager::toolChainRemoved, this, [this](ToolChain *tc) { if (tc == this) { QObject::disconnect(m_thisToolchainRemovedConnection); QObject::disconnect(m_mingwToolchainAddedConnection); } else if (m_parentToolChainId == tc->id()) { - const QList<ToolChain *> mingwTCs = mingwToolChains(); + const Toolchains mingwTCs = mingwToolChains(); m_parentToolChainId = mingwTCs.isEmpty() ? QByteArray() : mingwTCs.front()->id(); } }); } -ClangToolChain::ClangToolChain() : - ClangToolChain(Constants::CLANG_TOOLCHAIN_TYPEID) +bool GccToolChain::matchesCompilerCommand(const FilePath &command) const { -} - -ClangToolChain::ClangToolChain(Utils::Id typeId) : - GccToolChain(typeId) -{ - setTypeDisplayName(Tr::tr("Clang")); - syncAutodetectedWithParentToolchains(); -} - -ClangToolChain::~ClangToolChain() -{ - QObject::disconnect(m_thisToolchainRemovedConnection); - QObject::disconnect(m_mingwToolchainAddedConnection); -} - -bool ClangToolChain::matchesCompilerCommand(const FilePath &command) const -{ - if (!m_resolvedCompilerCommand) { - m_resolvedCompilerCommand = FilePath(); - if (HostOsInfo::isMacHost() - && compilerCommand().parentDir() == FilePath::fromString("/usr/bin")) { - Process xcrun; - xcrun.setCommand({"/usr/bin/xcrun", {"-f", compilerCommand().fileName()}}); - xcrun.runBlocking(); - const FilePath output = FilePath::fromString(xcrun.cleanedStdOut().trimmed()); - if (output.isExecutableFile() && output != compilerCommand()) - m_resolvedCompilerCommand = output; + if (m_subType == Clang) { + if (!m_resolvedCompilerCommand) { + m_resolvedCompilerCommand = FilePath(); + if (HostOsInfo::isMacHost() + && compilerCommand().parentDir() == FilePath::fromString("/usr/bin")) { + Process xcrun; + xcrun.setCommand({"/usr/bin/xcrun", {"-f", compilerCommand().fileName()}}); + xcrun.runBlocking(); + const FilePath output = FilePath::fromString(xcrun.cleanedStdOut().trimmed()); + if (output.isExecutableFile() && output != compilerCommand()) + m_resolvedCompilerCommand = output; + } } + if (!m_resolvedCompilerCommand->isEmpty() + && m_resolvedCompilerCommand->isSameExecutable(command)) + return true; } - if (!m_resolvedCompilerCommand->isEmpty() - && m_resolvedCompilerCommand->isSameExecutable(command)) - return true; - return GccToolChain::matchesCompilerCommand(command); + return ToolChain::matchesCompilerCommand(command); } -static FilePath mingwAwareMakeCommand(const Environment &environment) +QString GccToolChain::sysRoot() const { - const QStringList makes - = HostOsInfo::isWindowsHost() ? QStringList({"mingw32-make.exe", "make.exe"}) : QStringList({"make"}); - - FilePath tmp; - for (const QString &make : makes) { - tmp = environment.searchInPath(make); - if (!tmp.isEmpty()) - return tmp; - } - return FilePath::fromString(makes.first()); -} - -FilePath ClangToolChain::makeCommand(const Environment &environment) const -{ - return mingwAwareMakeCommand(environment); -} - -/** - * @brief Similar to \a GccToolchain::languageExtensions, but recognizes - * "-fborland-extensions". - */ -LanguageExtensions ClangToolChain::languageExtensions(const QStringList &cxxflags) const -{ - LanguageExtensions extensions = GccToolChain::languageExtensions(cxxflags); - if (cxxflags.contains("-fborland-extensions")) - extensions |= LanguageExtension::Borland; - return extensions; -} - -WarningFlags ClangToolChain::warningFlags(const QStringList &cflags) const -{ - WarningFlags flags = GccToolChain::warningFlags(cflags); - for (int end = cflags.size(), i = 0; i != end; ++i) { - const QString &flag = cflags[i]; - if (flag == "-Wdocumentation") - flags |= WarningFlags::Documentation; - if (flag == "-Wno-documentation") - flags &= ~WarningFlags::Documentation; - } - return flags; -} - -QStringList ClangToolChain::suggestedMkspecList() const -{ - if (const ToolChain * const parentTc = ToolChainManager::findToolChain(m_parentToolChainId)) - return parentTc->suggestedMkspecList(); - const Abi abi = targetAbi(); - if (abi.os() == Abi::DarwinOS) - return {"macx-clang", "macx-clang-32", "unsupported/macx-clang", "macx-ios-clang"}; - if (abi.os() == Abi::LinuxOS) - return {"linux-clang", "unsupported/linux-clang"}; - if (abi.os() == Abi::WindowsOS) - return {"win32-clang-g++"}; - if (abi.architecture() == Abi::AsmJsArchitecture && abi.binaryFormat() == Abi::EmscriptenFormat) - return {"wasm-emscripten"}; - return {}; // Note: Not supported by Qt yet, so default to the mkspec the Qt was build with -} - -void ClangToolChain::addToEnvironment(Environment &env) const -{ - GccToolChain::addToEnvironment(env); - - const QString sysroot = sysRoot(); - if (!sysroot.isEmpty()) - env.prependOrSetPath(FilePath::fromString(sysroot) / "bin"); - - // Clang takes PWD as basis for debug info, if set. - // When running Qt Creator from a shell, PWD is initially set to an "arbitrary" value. - // Since the tools are not called through a shell, PWD is never changed to the actual cwd, - // so we better make sure PWD is empty to begin with - env.unset("PWD"); -} - -QString ClangToolChain::originalTargetTriple() const -{ - const MingwToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId); - if (parentTC) - return parentTC->originalTargetTriple(); - - return GccToolChain::originalTargetTriple(); -} - -QString ClangToolChain::sysRoot() const -{ - const MingwToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId); - if (!parentTC) - return QString(); - - const FilePath mingwCompiler = parentTC->compilerCommand(); - return mingwCompiler.parentDir().parentDir().toString(); -} - -ToolChain::BuiltInHeaderPathsRunner ClangToolChain::createBuiltInHeaderPathsRunner( - const Environment &env) const -{ - // Using a clean environment breaks ccache/distcc/etc. - Environment fullEnv = env; - addToEnvironment(fullEnv); - - // This runner must be thread-safe! - return [fullEnv, - compilerCommand = compilerCommand(), - platformCodeGenFlags = m_platformCodeGenFlags, - reinterpretOptions = m_optionsReinterpreter, - headerCache = headerPathsCache(), - languageId = language(), - extraHeaderPathsFunction = m_extraHeaderPathsFunction](const QStringList &flags, - const FilePath &sysRoot, - const QString &target) { - return builtInHeaderPaths(fullEnv, - compilerCommand, - platformCodeGenFlags, - reinterpretOptions, - headerCache, - languageId, - extraHeaderPathsFunction, - flags, - sysRoot, - target); - }; -} - -std::unique_ptr<ToolChainConfigWidget> ClangToolChain::createConfigurationWidget() -{ - return std::make_unique<ClangToolChainConfigWidget>(this); -} - -QVariantMap ClangToolChain::toMap() const -{ - QVariantMap data = GccToolChain::toMap(); - data.insert(parentToolChainIdKeyC, m_parentToolChainId); - data.insert(priorityKeyC, m_priority); - return data; -} - -bool ClangToolChain::fromMap(const QVariantMap &data) -{ - if (!GccToolChain::fromMap(data)) - return false; - - m_parentToolChainId = data.value(parentToolChainIdKeyC).toByteArray(); - m_priority = data.value(priorityKeyC, PriorityNormal).toInt(); - syncAutodetectedWithParentToolchains(); - return true; -} - -LanguageExtensions ClangToolChain::defaultLanguageExtensions() const -{ - return LanguageExtension::Gnu; -} - -QList<OutputLineParser *> ClangToolChain::createOutputParsers() const -{ - return ClangParser::clangParserSuite(); -} - -// -------------------------------------------------------------------------- -// ClangToolChainFactory -// -------------------------------------------------------------------------- - -ClangToolChainFactory::ClangToolChainFactory() -{ - setDisplayName(Tr::tr("Clang")); - setSupportedToolChainType(Constants::CLANG_TOOLCHAIN_TYPEID); - setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); - setToolchainConstructor([] { return new ClangToolChain; }); -} - -Toolchains ClangToolChainFactory::autoDetect(const ToolchainDetector &detector) const -{ - Toolchains tcs; - Toolchains known = detector.alreadyKnown; - - tcs.append(autoDetectToolchains("clang++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, - Constants::CLANG_TOOLCHAIN_TYPEID, detector)); - tcs.append(autoDetectToolchains("clang", DetectVariants::Yes, Constants::C_LANGUAGE_ID, - Constants::CLANG_TOOLCHAIN_TYPEID, detector)); - known.append(tcs); - - const FilePath compilerPath = Core::ICore::clangExecutable(CLANG_BINDIR); - if (!compilerPath.isEmpty()) { - const FilePath clang = compilerPath.parentDir().pathAppended("clang").withExecutableSuffix(); - tcs.append(autoDetectToolchains(clang.toString(), DetectVariants::No, - Constants::C_LANGUAGE_ID, Constants::CLANG_TOOLCHAIN_TYPEID, - ToolchainDetector(known, detector.device, detector.searchPaths))); - } - - return tcs; -} - -Toolchains ClangToolChainFactory::detectForImport(const ToolChainDescription &tcd) const -{ - const QString fileName = tcd.compilerPath.completeBaseName(); - const QString resolvedSymlinksFileName = tcd.compilerPath.resolveSymlinks().completeBaseName(); - - const bool isCCompiler = tcd.language == Constants::C_LANGUAGE_ID - && ((fileName.startsWith("clang") && !fileName.startsWith("clang++")) - || (fileName == "cc" && resolvedSymlinksFileName.contains("clang"))); - - const bool isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID - && (fileName.startsWith("clang++") - || (fileName == "c++" && resolvedSymlinksFileName.contains("clang"))); - - if (isCCompiler || isCxxCompiler) { - return autoDetectToolChain(tcd); + if (m_subType == Clang) { + if (const GccToolChain *parentTC = mingwToolChainFromId(m_parentToolChainId)) { + const FilePath mingwCompiler = parentTC->compilerCommand(); + return mingwCompiler.parentDir().parentDir().toString(); + } } return {}; } -ClangToolChainConfigWidget::ClangToolChainConfigWidget(ClangToolChain *tc) : - GccToolChainConfigWidget(tc) +void GccToolChainConfigWidget::updateParentToolChainComboBox() { - if (!HostOsInfo::isWindowsHost() || tc->typeId() != Constants::CLANG_TOOLCHAIN_TYPEID) - return; + QTC_ASSERT(m_parentToolchainCombo, return); - // Remove m_abiWidget row because the parent toolchain abi is going to be used. - m_mainLayout->removeRow(m_mainLayout->rowCount() - 3); // FIXME: Do something sane instead. - m_abiWidget = nullptr; - - m_parentToolchainCombo = new QComboBox(this); - m_mainLayout->insertRow(m_mainLayout->rowCount() - 1, - Tr::tr("Parent toolchain:"), - m_parentToolchainCombo); - - ToolChainManager *tcManager = ToolChainManager::instance(); - m_parentToolChainConnections.append( - connect(tcManager, &ToolChainManager::toolChainUpdated, this, [this](ToolChain *tc) { - if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) - updateParentToolChainComboBox(); - })); - m_parentToolChainConnections.append( - connect(tcManager, &ToolChainManager::toolChainAdded, this, [this](ToolChain *tc) { - if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) - updateParentToolChainComboBox(); - })); - m_parentToolChainConnections.append( - connect(tcManager, &ToolChainManager::toolChainRemoved, this, [this](ToolChain *tc) { - if (tc->id() == toolChain()->id()) { - for (QMetaObject::Connection &connection : m_parentToolChainConnections) - QObject::disconnect(connection); - return; - } - if (tc->typeId() == Constants::MINGW_TOOLCHAIN_TYPEID) - updateParentToolChainComboBox(); - })); - - setFromClangToolchain(); -} - -void ClangToolChainConfigWidget::updateParentToolChainComboBox() -{ - auto *tc = static_cast<ClangToolChain *>(toolChain()); + auto *tc = static_cast<GccToolChain *>(toolChain()); QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray(); if (tc->isAutoDetected() || m_parentToolchainCombo->count() == 0) parentId = tc->m_parentToolChainId; - const MingwToolChain *parentTC = mingwToolChainFromId(parentId); + const GccToolChain *parentTC = mingwToolChainFromId(parentId); m_parentToolchainCombo->clear(); m_parentToolchainCombo->addItem(parentTC ? parentTC->displayName() : QString(), @@ -1860,205 +1975,6 @@ void ClangToolChainConfigWidget::updateParentToolChainComboBox() } } -void ClangToolChainConfigWidget::setFromClangToolchain() -{ - GccToolChainConfigWidget::setFromToolchain(); - - if (m_parentToolchainCombo) - updateParentToolChainComboBox(); -} - -void ClangToolChainConfigWidget::applyImpl() -{ - GccToolChainConfigWidget::applyImpl(); - if (!m_parentToolchainCombo) - return; - - auto *tc = static_cast<ClangToolChain *>(toolChain()); - tc->m_parentToolChainId.clear(); - - const QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray(); - if (!parentId.isEmpty()) { - for (const ToolChain *mingwTC : mingwToolChains()) { - if (parentId == mingwTC->id()) { - tc->m_parentToolChainId = mingwTC->id(); - tc->setTargetAbi(mingwTC->targetAbi()); - tc->setSupportedAbis(mingwTC->supportedAbis()); - break; - } - } - } -} - -bool ClangToolChainConfigWidget::isDirtyImpl() const -{ - if (GccToolChainConfigWidget::isDirtyImpl()) - return true; - - if (!m_parentToolchainCombo) - return false; - - auto tc = static_cast<ClangToolChain *>(toolChain()); - Q_ASSERT(tc); - const MingwToolChain *parentTC = mingwToolChainFromId(tc->m_parentToolChainId); - const QByteArray parentId = parentTC ? parentTC->id() : QByteArray(); - return parentId != m_parentToolchainCombo->currentData(); -} - -void ClangToolChainConfigWidget::makeReadOnlyImpl() -{ - GccToolChainConfigWidget::makeReadOnlyImpl(); - if (m_parentToolchainCombo) - m_parentToolchainCombo->setEnabled(false); -} - -// -------------------------------------------------------------------------- -// MingwToolChain -// -------------------------------------------------------------------------- - -MingwToolChain::MingwToolChain() : - GccToolChain(Constants::MINGW_TOOLCHAIN_TYPEID) -{ - setTypeDisplayName(Tr::tr("MinGW")); -} - -QStringList MingwToolChain::suggestedMkspecList() const -{ - if (HostOsInfo::isWindowsHost()) - return {"win32-g++"}; - if (HostOsInfo::isLinuxHost()) { - if (version().startsWith("4.6.")) - return {"win32-g++-4.6-cross", "unsupported/win32-g++-4.6-cross"}; - return {"win32-g++-cross", "unsupported/win32-g++-cross"}; - } - return {}; -} - -FilePath MingwToolChain::makeCommand(const Environment &environment) const -{ - return mingwAwareMakeCommand(environment); -} - -// -------------------------------------------------------------------------- -// MingwToolChainFactory -// -------------------------------------------------------------------------- - -MingwToolChainFactory::MingwToolChainFactory() -{ - setDisplayName(Tr::tr("MinGW")); - setSupportedToolChainType(Constants::MINGW_TOOLCHAIN_TYPEID); - setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); - setToolchainConstructor([] { return new MingwToolChain; }); -} - -Toolchains MingwToolChainFactory::autoDetect(const ToolchainDetector &detector) const -{ - static const auto tcChecker = [](const ToolChain *tc) { - return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor; - }; - Toolchains result = autoDetectToolchains( - "g++", DetectVariants::Yes, Constants::CXX_LANGUAGE_ID, - Constants::MINGW_TOOLCHAIN_TYPEID, detector, tcChecker); - result += autoDetectToolchains("gcc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, - Constants::MINGW_TOOLCHAIN_TYPEID, detector, tcChecker); - return result; -} - -Toolchains MingwToolChainFactory::detectForImport(const ToolChainDescription &tcd) const -{ - const QString fileName = tcd.compilerPath.completeBaseName(); - - const bool cCompiler = tcd.language == Constants::C_LANGUAGE_ID - && ((fileName.startsWith("gcc") || fileName.endsWith("gcc")) - || fileName == "cc"); - - const bool cxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID - && ((fileName.startsWith("g++") || fileName.endsWith("g++")) - || (fileName.startsWith("c++") || fileName.endsWith("c++"))); - - if (cCompiler || cxxCompiler) { - return autoDetectToolChain(tcd, [](const ToolChain *tc) { - return tc->targetAbi().osFlavor() == Abi::WindowsMSysFlavor; - }); - } - - return {}; -} - -// -------------------------------------------------------------------------- -// LinuxIccToolChain -// -------------------------------------------------------------------------- - -LinuxIccToolChain::LinuxIccToolChain() : - GccToolChain(Constants::LINUXICC_TOOLCHAIN_TYPEID) -{ - setTypeDisplayName(Tr::tr("ICC")); -} - -/** - * Similar to \a GccToolchain::languageExtensions, but uses "-openmp" instead of - * "-fopenmp" and "-fms-dialect[=ver]" instead of "-fms-extensions". - * @see UNIX manual for "icc" - */ -LanguageExtensions LinuxIccToolChain::languageExtensions(const QStringList &cxxflags) const -{ - QStringList copy = cxxflags; - copy.removeAll("-fopenmp"); - copy.removeAll("-fms-extensions"); - - LanguageExtensions extensions = GccToolChain::languageExtensions(cxxflags); - if (cxxflags.contains("-openmp")) - extensions |= LanguageExtension::OpenMP; - if (cxxflags.contains("-fms-dialect") - || cxxflags.contains("-fms-dialect=8") - || cxxflags.contains("-fms-dialect=9") - || cxxflags.contains("-fms-dialect=10")) - extensions |= LanguageExtension::Microsoft; - return extensions; -} - -QList<OutputLineParser *> LinuxIccToolChain::createOutputParsers() const -{ - return LinuxIccParser::iccParserSuite(); -} - -QStringList LinuxIccToolChain::suggestedMkspecList() const -{ - return {QString("linux-icc-%1").arg(targetAbi().wordWidth())}; -} - -// -------------------------------------------------------------------------- -// LinuxIccToolChainFactory -// -------------------------------------------------------------------------- - -LinuxIccToolChainFactory::LinuxIccToolChainFactory() -{ - setDisplayName(Tr::tr("ICC")); - setSupportedToolChainType(Constants::LINUXICC_TOOLCHAIN_TYPEID); - setSupportedLanguages({Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}); - setToolchainConstructor([] { return new LinuxIccToolChain; }); -} - -Toolchains LinuxIccToolChainFactory::autoDetect(const ToolchainDetector &detector) const -{ - Toolchains result - = autoDetectToolchains("icpc", DetectVariants::No, Constants::CXX_LANGUAGE_ID, - Constants::LINUXICC_TOOLCHAIN_TYPEID, detector); - result += autoDetectToolchains("icc", DetectVariants::Yes, Constants::C_LANGUAGE_ID, - Constants::LINUXICC_TOOLCHAIN_TYPEID, detector); - return result; -} - -Toolchains LinuxIccToolChainFactory::detectForImport(const ToolChainDescription &tcd) const -{ - const QString fileName = tcd.compilerPath.completeBaseName(); - if ((tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("icpc")) || - (tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("icc"))) { - return autoDetectToolChain(tcd); - } - return {}; -} - GccToolChain::WarningFlagAdder::WarningFlagAdder(const QString &flag, WarningFlags &flags) : m_flags(flags) { diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 1e6353d0a75..207d93f5bd8 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -5,13 +5,10 @@ #include "projectexplorer_export.h" -#include "projectexplorerconstants.h" #include "toolchain.h" #include "abi.h" #include "headerpath.h" -#include <utils/fileutils.h> - #include <functional> #include <memory> #include <optional> @@ -19,34 +16,23 @@ namespace ProjectExplorer { namespace Internal { -class ClangToolChainFactory; -class ClangToolChainConfigWidget; class GccToolChainConfigWidget; class GccToolChainFactory; -class MingwToolChainFactory; -class LinuxIccToolChainFactory; + +const QStringList gccPredefinedMacrosOptions(Utils::Id languageId); } // -------------------------------------------------------------------------- // GccToolChain // -------------------------------------------------------------------------- -inline const QStringList languageOption(Utils::Id languageId) -{ - if (languageId == Constants::C_LANGUAGE_ID) - return {"-x", "c"}; - return {"-x", "c++"}; -} - -inline const QStringList gccPredefinedMacrosOptions(Utils::Id languageId) -{ - return languageOption(languageId) + QStringList({"-E", "-dM"}); -} - class PROJECTEXPLORER_EXPORT GccToolChain : public ToolChain { public: - GccToolChain(Utils::Id typeId); + enum SubType { RealGcc, Clang, MinGW, LinuxIcc }; + + GccToolChain(Utils::Id typeId, SubType subType = RealGcc); + ~GccToolChain() override; QString originalTargetTriple() const override; Utils::FilePath installDir() const override; @@ -66,8 +52,8 @@ public: QStringList suggestedMkspecList() const override; QList<Utils::OutputLineParser *> createOutputParsers() const override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; + void toMap(Utils::Store &data) const override; + void fromMap(const Utils::Store &data) override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; @@ -93,6 +79,11 @@ public: Abis supportedAbis; QString originalTargetTriple; }; + GccToolChain *asGccToolChain() final { return this; } + + bool matchesCompilerCommand(const Utils::FilePath &command) const override; + + void setPriority(int priority) { m_priority = priority; } protected: using CacheItem = QPair<QStringList, Macros>; @@ -134,6 +125,8 @@ protected: const QStringList &args, const Utils::Environment &env); + int priority() const override { return m_priority; } + class WarningFlagAdder { public: @@ -148,13 +141,11 @@ protected: bool m_triggered = false; }; + QString sysRoot() const override; + private: + void syncAutodetectedWithParentToolchains(); void updateSupportedAbis() const; - static QStringList gccPrepareArguments(const QStringList &flags, - const Utils::FilePath &sysRoot, - const QStringList &platformCodeGenFlags, - Utils::Id languageId, - OptionsReinterpreter reinterpretOptions); protected: QStringList m_platformCodeGenFlags; @@ -164,6 +155,7 @@ protected: mutable ExtraHeaderPathsFunction m_extraHeaderPathsFunction = [](HeaderPaths &) {}; private: + SubType m_subType = RealGcc; mutable Abis m_supportedAbis; mutable QString m_originalTargetTriple; mutable HeaderPaths m_headerPaths; @@ -173,150 +165,38 @@ private: friend class Internal::GccToolChainConfigWidget; friend class Internal::GccToolChainFactory; friend class ToolChainFactory; -}; -// -------------------------------------------------------------------------- -// ClangToolChain -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT ClangToolChain : public GccToolChain -{ -public: - ClangToolChain(); - explicit ClangToolChain(Utils::Id typeId); - ~ClangToolChain() override; - - bool matchesCompilerCommand(const Utils::FilePath &command) const override; - - Utils::FilePath makeCommand(const Utils::Environment &environment) const override; - - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; - Utils::WarningFlags warningFlags(const QStringList &cflags) const override; - - QList<Utils::OutputLineParser *> createOutputParsers() const override; - - QStringList suggestedMkspecList() const override; - void addToEnvironment(Utils::Environment &env) const override; - - QString originalTargetTriple() const override; - QString sysRoot() const override; - - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( - const Utils::Environment &env) const override; - - std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; - - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; - - void setPriority(int priority) { m_priority = priority; } - int priority() const override { return m_priority; } - -protected: - Utils::LanguageExtensions defaultLanguageExtensions() const override; - void syncAutodetectedWithParentToolchains(); - -private: // "resolved" on macOS from /usr/bin/clang(++) etc to <DeveloperDir>/usr/bin/clang(++) - // which is used for comparison with matchesCompileCommand + // which is used for comparison with matchesCompilerCommand mutable std::optional<Utils::FilePath> m_resolvedCompilerCommand; QByteArray m_parentToolChainId; int m_priority = PriorityNormal; QMetaObject::Connection m_mingwToolchainAddedConnection; QMetaObject::Connection m_thisToolchainRemovedConnection; - - friend class Internal::ClangToolChainFactory; - friend class Internal::ClangToolChainConfigWidget; - friend class ToolChainFactory; }; -// -------------------------------------------------------------------------- -// MingwToolChain -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT MingwToolChain : public GccToolChain -{ -public: - Utils::FilePath makeCommand(const Utils::Environment &environment) const override; - - QStringList suggestedMkspecList() const override; - -private: - MingwToolChain(); - - friend class Internal::MingwToolChainFactory; - friend class ToolChainFactory; -}; - -// -------------------------------------------------------------------------- -// LinuxIccToolChain -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT LinuxIccToolChain : public GccToolChain -{ -public: - Utils::LanguageExtensions languageExtensions(const QStringList &cxxflags) const override; - QList<Utils::OutputLineParser *> createOutputParsers() const override; - - QStringList suggestedMkspecList() const override; - -private: - LinuxIccToolChain(); - - friend class Internal::LinuxIccToolChainFactory; - friend class ToolChainFactory; -}; - -// -------------------------------------------------------------------------- -// Factories -// -------------------------------------------------------------------------- namespace Internal { + class GccToolChainFactory : public ToolChainFactory { public: - GccToolChainFactory(); - - Toolchains autoDetect(const ToolchainDetector &detector) const override; - Toolchains detectForImport(const ToolChainDescription &tcd) const override; - -protected: - enum class DetectVariants { Yes, No }; - using ToolchainChecker = std::function<bool(const ToolChain *)>; - Toolchains autoDetectToolchains( - const QString &compilerName, DetectVariants detectVariants, const Utils::Id language, - const Utils::Id requiredTypeId, const ToolchainDetector &detector, - const ToolchainChecker &checker = {}) const; - Toolchains autoDetectToolChain( - const ToolChainDescription &tcd, - const ToolchainChecker &checker = {}) const; -}; - -class ClangToolChainFactory : public GccToolChainFactory -{ -public: - ClangToolChainFactory(); + explicit GccToolChainFactory(GccToolChain::SubType subType); Toolchains autoDetect(const ToolchainDetector &detector) const final; Toolchains detectForImport(const ToolChainDescription &tcd) const final; -}; -class MingwToolChainFactory : public GccToolChainFactory -{ -public: - MingwToolChainFactory(); +private: + static Toolchains autoDetectToolchains(const Utils::FilePaths &compilerPaths, + const Utils::Id language, + const Utils::Id requiredTypeId, + const Toolchains &known, + const GccToolChain::SubType subType); + static Toolchains autoDetectToolChain(const ToolChainDescription &tcd, + const GccToolChain::SubType subType); + static Toolchains autoDetectSdkClangToolchain(const Toolchains &known); - Toolchains autoDetect(const ToolchainDetector &detector) const final; - Toolchains detectForImport(const ToolChainDescription &tcd) const final; -}; - -class LinuxIccToolChainFactory : public GccToolChainFactory -{ -public: - LinuxIccToolChainFactory(); - - Toolchains autoDetect(const ToolchainDetector &detector) const final; - Toolchains detectForImport(const ToolChainDescription &tcd) const final; + const bool m_autoDetecting; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/images/cmakeicon.png b/src/plugins/projectexplorer/images/cmakeicon.png new file mode 100644 index 00000000000..1e2403a18fb Binary files /dev/null and b/src/plugins/projectexplorer/images/cmakeicon.png differ diff --git a/src/plugins/projectexplorer/images/cmakeicon@2x.png b/src/plugins/projectexplorer/images/cmakeicon@2x.png new file mode 100644 index 00000000000..89f5c4cc5fc Binary files /dev/null and b/src/plugins/projectexplorer/images/cmakeicon@2x.png differ diff --git a/src/plugins/projectexplorer/importwidget.cpp b/src/plugins/projectexplorer/importwidget.cpp index 623476b6748..1aa6064dc59 100644 --- a/src/plugins/projectexplorer/importwidget.cpp +++ b/src/plugins/projectexplorer/importwidget.cpp @@ -36,7 +36,7 @@ ImportWidget::ImportWidget(QWidget *parent) : layout->addWidget(m_pathChooser); m_pathChooser->setExpectedKind(Utils::PathChooser::ExistingDirectory); - m_pathChooser->setHistoryCompleter(QLatin1String("Import.SourceDir.History")); + m_pathChooser->setHistoryCompleter("Import.SourceDir.History"); auto importButton = new QPushButton(Tr::tr("Import"), widget); layout->addWidget(importButton); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index acb32043dc7..49a0ed89571 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -76,6 +76,22 @@ static void warnAboutUnsupportedKeys(const QVariantMap &map, const QString &name } } +static Key fullSettingsKey(const QString &fieldKey) +{ + return "Wizards/" + keyFromString(fieldKey); +} + +static QString translatedOrUntranslatedText(QVariantMap &map, const QString &key) +{ + if (key.size() >= 1) { + const QString trKey = "tr" + key.at(0).toUpper() + key.mid(1); // "text" -> "trText" + const QString trValue = JsonWizardFactory::localizedString(consumeValue(map, trKey).toString()); + if (!trValue.isEmpty()) + return trValue; + } + + return consumeValue(map, key).toString(); +} // -------------------------------------------------------------------- // Helper: @@ -484,9 +500,9 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage) QVariantMap tmp = data.toMap(); m_isPassword = consumeValue(tmp, "isPassword", false).toBool(); - m_defaultText = JsonWizardFactory::localizedString(consumeValue(tmp, "trText").toString()); - m_disabledText = JsonWizardFactory::localizedString(consumeValue(tmp, "trDisabledText").toString()); - m_placeholderText = JsonWizardFactory::localizedString(consumeValue(tmp, "trPlaceholder").toString()); + m_defaultText = translatedOrUntranslatedText(tmp, "text"); + m_disabledText = translatedOrUntranslatedText(tmp, "disabledText"); + m_placeholderText = translatedOrUntranslatedText(tmp, "placeholder"); m_historyId = consumeValue(tmp, "historyId").toString(); m_restoreLastHistoryItem = consumeValue(tmp, "restoreLastHistoryItem", false).toBool(); QString pattern = consumeValue(tmp, "validator").toString(); @@ -525,7 +541,7 @@ QWidget *LineEditField::createWidget(const QString &displayName, JsonFieldPage * w->setFixupExpando(m_fixupExpando); if (!m_historyId.isEmpty()) - w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem); + w->setHistoryCompleter(keyFromString(m_historyId), m_restoreLastHistoryItem); w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal); QObject::connect(w, &FancyLineEdit::textEdited, [this] { setHasUserChanges(); }); @@ -681,8 +697,8 @@ bool TextEditField::parseData(const QVariant &data, QString *errorMessage) QVariantMap tmp = data.toMap(); - m_defaultText = JsonWizardFactory::localizedString(consumeValue(tmp, "trText").toString()); - m_disabledText = JsonWizardFactory::localizedString(consumeValue(tmp, "trDisabledText").toString()); + m_defaultText = translatedOrUntranslatedText(tmp, "text"); + m_disabledText = translatedOrUntranslatedText(tmp, "disabledText"); m_acceptRichText = consumeValue(tmp, "richText", true).toBool(); warnAboutUnsupportedKeys(tmp, name(), type()); @@ -800,7 +816,7 @@ QWidget *PathChooserField::createWidget(const QString &displayName, JsonFieldPag Q_UNUSED(page) auto w = new PathChooser; if (!m_historyId.isEmpty()) - w->setHistoryCompleter(m_historyId); + w->setHistoryCompleter(keyFromString(m_historyId)); QObject::connect(w, &PathChooser::textChanged, [this, w] { if (w->filePath() != m_path) setHasUserChanges(); @@ -1413,9 +1429,4 @@ JsonFieldPage::Field *JsonFieldPage::createFieldData(const QString &type) return nullptr; } -QString JsonFieldPage::fullSettingsKey(const QString &fieldKey) -{ - return "Wizards/" + fieldKey; -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h index 69ba2a50fe5..578d16c711e 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h @@ -126,7 +126,6 @@ private: static QHash<QString, FieldFactory> m_factories; static Field *createFieldData(const QString &type); - static QString fullSettingsKey(const QString &fieldKey); QFormLayout *m_formLayout; QLabel *m_errorLabel; diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index 6bc09c5e58f..b7207ce45ef 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp @@ -59,7 +59,7 @@ private: QVariant data(int column, int role) const override { if (column != 0 || role != Qt::DisplayRole) - return QVariant(); + return {}; return m_candidate->file.filePath().toUserOutput(); } @@ -192,7 +192,7 @@ JsonWizard::GeneratorFiles JsonWizard::generateFileList() Tr::tr("The wizard failed to generate files.<br>" "The error message was: \"%1\".").arg(errorMessage)); reject(); - return GeneratorFiles(); + return {}; } QList<GeneratorFile *> projectFiles; @@ -216,7 +216,7 @@ QString JsonWizard::stringValue(const QString &n) const { QVariant v = value(n); if (!v.isValid()) - return QString(); + return {}; if (v.typeId() == QVariant::String) { QString tmp = m_expander.expand(v.toString()); @@ -271,7 +271,7 @@ QVariant JsonWizard::value(const QString &n) const return v; if (hasField(n)) return field(n); // Cannot contain macros! - return QVariant(); + return {}; } bool JsonWizard::boolFromVariant(const QVariant &v, MacroExpander *expander) @@ -287,7 +287,7 @@ QString JsonWizard::stringListToArrayString(const QStringList &list, const Macro { // Todo: Handle ' embedded in the strings better. if (list.isEmpty()) - return QString(); + return {}; QStringList tmp = Utils::transform(list, [expander](const QString &i) { return expander->expand(i).replace(QLatin1Char('\''), QLatin1String("\\'")); @@ -424,6 +424,10 @@ void JsonWizard::openFiles(const JsonWizard::GeneratorFiles &files) { QString errorMessage; bool openedSomething = stringValue("DoNotOpenFile") == "true"; + static const auto formatFile = [](Core::IEditor *editor) { + editor->document()->formatContents(); + editor->document()->save(nullptr); + }; for (const JsonWizard::GeneratorFile &f : files) { const Core::GeneratedFile &file = f.file; if (!file.filePath().exists()) { @@ -454,8 +458,12 @@ void JsonWizard::openFiles(const JsonWizard::GeneratorFiles &files) break; } else if (file.attributes() & Core::GeneratedFile::TemporaryFile) { editor->document()->setTemporary(true); + } else { + formatFile(editor); } openedSomething = true; + } else if (file.filePath().fileSize() < 100 * 1024 ) { + Core::EditorManager::runWithTemporaryEditor(file.filePath(), formatFile); } } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp index fa6f044f072..4d364362fd1 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp @@ -68,8 +68,17 @@ const char OPTIONS_KEY[] = "options"; const char PLATFORM_INDEPENDENT_KEY[] = "platformIndependent"; const char DEFAULT_VALUES[] = "defaultValues"; -static QList<JsonWizardPageFactory *> s_pageFactories; -static QList<JsonWizardGeneratorFactory *> s_generatorFactories; +static QList<JsonWizardPageFactory *> &pageFactories() +{ + static QList<JsonWizardPageFactory *> thePageFactories; + return thePageFactories; +} + +static QList<JsonWizardGeneratorFactory *> &generatorFactories() +{ + static QList<JsonWizardGeneratorFactory *> theGeneratorFactories; + return theGeneratorFactories; +} int JsonWizardFactory::m_verbose = 0; @@ -115,11 +124,11 @@ static JsonWizardFactory::Generator parseGenerator(const QVariant &value, QStrin } Id typeId = Id::fromString(QLatin1String(Constants::GENERATOR_ID_PREFIX) + strVal); JsonWizardGeneratorFactory *factory - = findOr(s_generatorFactories, nullptr, [typeId](JsonWizardGeneratorFactory *f) { return f->canCreate(typeId); }); + = findOr(generatorFactories(), nullptr, [typeId](JsonWizardGeneratorFactory *f) { return f->canCreate(typeId); }); if (!factory) { *errorMessage = Tr::tr("TypeId \"%1\" of generator is unknown. Supported typeIds are: \"%2\".") .arg(strVal) - .arg(supportedTypeIds(s_generatorFactories).replace(QLatin1String(Constants::GENERATOR_ID_PREFIX), QLatin1String(""))); + .arg(supportedTypeIds(generatorFactories()).replace(QLatin1String(Constants::GENERATOR_ID_PREFIX), QLatin1String(""))); return gen; } @@ -133,6 +142,26 @@ static JsonWizardFactory::Generator parseGenerator(const QVariant &value, QStrin return gen; } +JsonWizardPageFactory::JsonWizardPageFactory() +{ + pageFactories().append(this); +} + +JsonWizardPageFactory::~JsonWizardPageFactory() +{ + pageFactories().removeOne(this); +} + +JsonWizardGeneratorFactory::JsonWizardGeneratorFactory() +{ + generatorFactories().append(this); +} + +JsonWizardGeneratorFactory::~JsonWizardGeneratorFactory() +{ + generatorFactories().removeOne(this); +} + //FIXME: createWizardFactories() has an almost identical loop. Make the loop return the results instead of //internal processing and create a separate function for it. Then process the results in //loadDefaultValues() and createWizardFactories() @@ -323,11 +352,11 @@ JsonWizardFactory::Page JsonWizardFactory::parsePage(const QVariant &value, QStr Id typeId = Id::fromString(QLatin1String(Constants::PAGE_ID_PREFIX) + strVal); JsonWizardPageFactory *factory - = Utils::findOr(s_pageFactories, nullptr, [typeId](JsonWizardPageFactory *f) { return f->canCreate(typeId); }); + = Utils::findOr(pageFactories(), nullptr, [typeId](JsonWizardPageFactory *f) { return f->canCreate(typeId); }); if (!factory) { *errorMessage = Tr::tr("TypeId \"%1\" of page is unknown. Supported typeIds are: \"%2\".") .arg(strVal) - .arg(supportedTypeIds(s_pageFactories).replace(QLatin1String(Constants::PAGE_ID_PREFIX), QLatin1String(""))); + .arg(supportedTypeIds(pageFactories()).replace(QLatin1String(Constants::PAGE_ID_PREFIX), QLatin1String(""))); return p; } @@ -519,18 +548,6 @@ int JsonWizardFactory::verbose() return m_verbose; } -void JsonWizardFactory::registerPageFactory(JsonWizardPageFactory *factory) -{ - QTC_ASSERT(!s_pageFactories.contains(factory), return); - s_pageFactories.append(factory); -} - -void JsonWizardFactory::registerGeneratorFactory(JsonWizardGeneratorFactory *factory) -{ - QTC_ASSERT(!s_generatorFactories.contains(factory), return); - s_generatorFactories.append(factory); -} - static QString qmlProjectName(const FilePath &folder) { FilePath currentFolder = folder; @@ -598,7 +615,7 @@ Wizard *JsonWizardFactory::runWizardImpl(const FilePath &path, QWidget *parent, continue; havePage = true; - JsonWizardPageFactory *factory = findOr(s_pageFactories, nullptr, + JsonWizardPageFactory *factory = findOr(pageFactories(), nullptr, [&data](JsonWizardPageFactory *f) { return f->canCreate(data.typeId); }); @@ -621,7 +638,7 @@ Wizard *JsonWizardFactory::runWizardImpl(const FilePath &path, QWidget *parent, for (const Generator &data : std::as_const(m_generators)) { QTC_ASSERT(data.isValid(), continue); - JsonWizardGeneratorFactory *factory = Utils::findOr(s_generatorFactories, nullptr, + JsonWizardGeneratorFactory *factory = Utils::findOr(generatorFactories(), nullptr, [&data](JsonWizardGeneratorFactory *f) { return f->canCreate(data.typeId); }); @@ -660,7 +677,7 @@ QList<QVariant> JsonWizardFactory::objectOrList(const QVariant &data, QString *e QString JsonWizardFactory::localizedString(const QVariant &value) { if (value.isNull()) - return QString(); + return {}; if (value.typeId() == QVariant::Map) { QVariantMap tmp = value.toMap(); const QString locale = languageSetting().toLower(); @@ -671,7 +688,7 @@ QString JsonWizardFactory::localizedString(const QVariant &value) if (!result.isEmpty()) return result; } - return QString(); + return {}; } return Tr::tr(value.toByteArray()); } @@ -701,14 +718,6 @@ bool JsonWizardFactory::isAvailable(Id platformId) const return JsonWizard::boolFromVariant(m_enabledExpression, &expander); } -void JsonWizardFactory::destroyAllFactories() -{ - qDeleteAll(s_pageFactories); - s_pageFactories.clear(); - qDeleteAll(s_generatorFactories); - s_generatorFactories.clear(); -} - bool JsonWizardFactory::initialize(const QVariantMap &data, const FilePath &baseDir, QString *errorMessage) { QTC_ASSERT(errorMessage, return false); @@ -871,7 +880,7 @@ QVariant JsonWizardFactoryJsExtension::value(const QString &name) const return Id::toStringList(m_availableFeatures); if (name == "Plugins") return Id::toStringList(m_pluginFeatures); - return QVariant(); + return {}; } } // namespace Internal diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h index 95171f11dbd..118b20330f2 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h @@ -15,9 +15,6 @@ namespace ProjectExplorer { -class JsonWizardFactory; -class JsonWizardPageFactory; -class JsonWizardGeneratorFactory; class ProjectExplorerPlugin; class ProjectExplorerPluginPrivate; @@ -52,9 +49,6 @@ public: QVariant data; }; - static void registerPageFactory(JsonWizardPageFactory *factory); - static void registerGeneratorFactory(JsonWizardGeneratorFactory *factory); - static QList<QVariant> objectOrList(const QVariant &data, QString *errorMessage); static QString localizedString(const QVariant &value); @@ -78,7 +72,6 @@ private: static void setVerbose(int level); static int verbose(); - static void destroyAllFactories(); bool initialize(const QVariantMap &data, const Utils::FilePath &baseDir, QString *errorMessage); JsonWizardFactory::Page parsePage(const QVariant &value, QString *errorMessage); diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.h index 199e359bfb4..2b7683f661d 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardgeneratorfactory.h @@ -47,6 +47,9 @@ class PROJECTEXPLORER_EXPORT JsonWizardGeneratorFactory : public QObject Q_OBJECT public: + JsonWizardGeneratorFactory(); + ~JsonWizardGeneratorFactory() override; + bool canCreate(Utils::Id typeId) const { return m_typeIds.contains(typeId); } QList<Utils::Id> supportedIds() const { return m_typeIds; } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.cpp index 492b4406b4d..0961cfa9064 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.cpp @@ -13,8 +13,6 @@ namespace ProjectExplorer { // JsonWizardPageFactory: // -------------------------------------------------------------------- -JsonWizardPageFactory::~JsonWizardPageFactory() = default; - void JsonWizardPageFactory::setTypeIdsSuffixes(const QStringList &suffixes) { m_typeIds = Utils::transform(suffixes, [](const QString &suffix) { diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.h index 5558022a3e8..b6374772461 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory.h @@ -18,6 +18,7 @@ class JsonWizard; class PROJECTEXPLORER_EXPORT JsonWizardPageFactory { public: + JsonWizardPageFactory(); virtual ~JsonWizardPageFactory(); bool canCreate(Utils::Id typeId) const { return m_typeIds.contains(typeId); } diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index 27bad16e8c8..a43ca8f3816 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -5,7 +5,7 @@ #include "devicesupport/idevice.h" #include "devicesupport/idevicefactory.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitmanager.h" #include "ioutputparser.h" #include "osparser.h" @@ -69,8 +69,8 @@ public: [kit] { return kit->id().toString(); }); m_macroExpander.registerVariable("Kit:FileSystemName", Tr::tr("Kit filesystem-friendly name"), [kit] { return kit->fileSystemFriendlyName(); }); - for (KitAspect *aspect : KitManager::kitAspects()) - aspect->addToMacroExpander(kit, &m_macroExpander); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + factory->addToMacroExpander(kit, &m_macroExpander); m_macroExpander.registerVariable("Kit:Name", Tr::tr("The name of the kit."), @@ -123,41 +123,40 @@ Kit::Kit(Id id) { } -Kit::Kit(const QVariantMap &data) : - d(std::make_unique<Internal::KitPrivate>(Id(), this)) +Kit::Kit(const Store &data) + : d(std::make_unique<Internal::KitPrivate>(Id(), this)) { - d->m_id = Id::fromSetting(data.value(QLatin1String(ID_KEY))); + d->m_id = Id::fromSetting(data.value(ID_KEY)); - d->m_autodetected = data.value(QLatin1String(AUTODETECTED_KEY)).toBool(); - d->m_autoDetectionSource = data.value(QLatin1String(AUTODETECTIONSOURCE_KEY)).toString(); + d->m_autodetected = data.value(AUTODETECTED_KEY).toBool(); + d->m_autoDetectionSource = data.value(AUTODETECTIONSOURCE_KEY).toString(); // if we don't have that setting assume that autodetected implies sdk - QVariant value = data.value(QLatin1String(SDK_PROVIDED_KEY)); + QVariant value = data.value(SDK_PROVIDED_KEY); if (value.isValid()) d->m_sdkProvided = value.toBool(); else d->m_sdkProvided = d->m_autodetected; d->m_unexpandedDisplayName.fromMap(data, DISPLAYNAME_KEY); - d->m_fileSystemFriendlyName = data.value(QLatin1String(FILESYSTEMFRIENDLYNAME_KEY)).toString(); - d->m_iconPath = FilePath::fromString(data.value(QLatin1String(ICON_KEY), - d->m_iconPath.toString()).toString()); + d->m_fileSystemFriendlyName = data.value(FILESYSTEMFRIENDLYNAME_KEY).toString(); + d->m_iconPath = FilePath::fromString(data.value(ICON_KEY, d->m_iconPath.toString()).toString()); d->m_deviceTypeForIcon = Id::fromSetting(data.value(DEVICE_TYPE_FOR_ICON_KEY)); const auto it = data.constFind(IRRELEVANT_ASPECTS_KEY); if (it != data.constEnd()) d->m_irrelevantAspects = transform<QSet<Id>>(it.value().toList(), &Id::fromSetting); - QVariantMap extra = data.value(QLatin1String(DATA_KEY)).toMap(); + Store extra = storeFromVariant(data.value(DATA_KEY)); d->m_data.clear(); // remove default values - const QVariantMap::ConstIterator cend = extra.constEnd(); - for (QVariantMap::ConstIterator it = extra.constBegin(); it != cend; ++it) - d->m_data.insert(Id::fromString(it.key()), it.value()); + const Store::ConstIterator cend = extra.constEnd(); + for (Store::ConstIterator it = extra.constBegin(); it != cend; ++it) + d->m_data.insert(Id::fromString(stringFromKey(it.key())), it.value()); - const QStringList mutableInfoList = data.value(QLatin1String(MUTABLE_INFO_KEY)).toStringList(); + const QStringList mutableInfoList = data.value(MUTABLE_INFO_KEY).toStringList(); for (const QString &mutableInfo : mutableInfoList) d->m_mutable.insert(Id::fromString(mutableInfo)); - const QStringList stickyInfoList = data.value(QLatin1String(STICKY_INFO_KEY)).toStringList(); + const QStringList stickyInfoList = data.value(STICKY_INFO_KEY).toStringList(); for (const QString &stickyInfo : stickyInfoList) d->m_sticky.insert(Id::fromString(stickyInfo)); } @@ -235,8 +234,8 @@ bool Kit::hasWarning() const Tasks Kit::validate() const { Tasks result; - for (KitAspect *aspect : KitManager::kitAspects()) - result.append(aspect->validate(this)); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + result.append(factory->validate(this)); d->m_hasError = containsType(result, Task::TaskType::Error); d->m_hasWarning = containsType(result, Task::TaskType::Warning); @@ -248,16 +247,16 @@ Tasks Kit::validate() const void Kit::fix() { KitGuard g(this); - for (KitAspect *aspect : KitManager::kitAspects()) - aspect->fix(this); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + factory->fix(this); } void Kit::setup() { KitGuard g(this); - const QList<KitAspect *> aspects = KitManager::kitAspects(); - for (KitAspect * const aspect : aspects) - aspect->setup(this); + const QList<KitAspectFactory *> aspects = KitManager::kitAspectFactories(); + for (KitAspectFactory * const factory : aspects) + factory->setup(this); } void Kit::upgrade() @@ -265,8 +264,8 @@ void Kit::upgrade() KitGuard g(this); // Process the KitAspects in reverse order: They may only be based on other information // lower in the stack. - for (KitAspect *aspect : KitManager::kitAspects()) - aspect->upgrade(this); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + factory->upgrade(this); } QString Kit::unexpandedDisplayName() const @@ -336,10 +335,10 @@ Id Kit::id() const int Kit::weight() const { - const QList<KitAspect *> &aspects = KitManager::kitAspects(); + const QList<KitAspectFactory *> &aspects = KitManager::kitAspectFactories(); return std::accumulate(aspects.begin(), aspects.end(), 0, - [this](int sum, const KitAspect *aspect) { - return sum + aspect->weight(this); + [this](int sum, const KitAspectFactory *factory) { + return sum + factory->weight(this); }); } @@ -484,56 +483,56 @@ bool Kit::isEqual(const Kit *other) const && d->m_mutable == other->d->m_mutable; } -QVariantMap Kit::toMap() const +Store Kit::toMap() const { using IdVariantConstIt = QHash<Id, QVariant>::ConstIterator; - QVariantMap data; + Store data; d->m_unexpandedDisplayName.toMap(data, DISPLAYNAME_KEY); - data.insert(QLatin1String(ID_KEY), QString::fromLatin1(d->m_id.name())); - data.insert(QLatin1String(AUTODETECTED_KEY), d->m_autodetected); + data.insert(ID_KEY, QString::fromLatin1(d->m_id.name())); + data.insert(AUTODETECTED_KEY, d->m_autodetected); if (!d->m_fileSystemFriendlyName.isEmpty()) - data.insert(QLatin1String(FILESYSTEMFRIENDLYNAME_KEY), d->m_fileSystemFriendlyName); - data.insert(QLatin1String(AUTODETECTIONSOURCE_KEY), d->m_autoDetectionSource); - data.insert(QLatin1String(SDK_PROVIDED_KEY), d->m_sdkProvided); - data.insert(QLatin1String(ICON_KEY), d->m_iconPath.toString()); + data.insert(FILESYSTEMFRIENDLYNAME_KEY, d->m_fileSystemFriendlyName); + data.insert(AUTODETECTIONSOURCE_KEY, d->m_autoDetectionSource); + data.insert(SDK_PROVIDED_KEY, d->m_sdkProvided); + data.insert(ICON_KEY, d->m_iconPath.toString()); data.insert(DEVICE_TYPE_FOR_ICON_KEY, d->m_deviceTypeForIcon.toSetting()); QStringList mutableInfo; for (const Id id : std::as_const(d->m_mutable)) mutableInfo << id.toString(); - data.insert(QLatin1String(MUTABLE_INFO_KEY), mutableInfo); + data.insert(MUTABLE_INFO_KEY, mutableInfo); QStringList stickyInfo; for (const Id id : std::as_const(d->m_sticky)) stickyInfo << id.toString(); - data.insert(QLatin1String(STICKY_INFO_KEY), stickyInfo); + data.insert(STICKY_INFO_KEY, stickyInfo); if (d->m_irrelevantAspects) { data.insert(IRRELEVANT_ASPECTS_KEY, transform<QVariantList>(d->m_irrelevantAspects.value(), &Id::toSetting)); } - QVariantMap extra; + Store extra; const IdVariantConstIt cend = d->m_data.constEnd(); for (IdVariantConstIt it = d->m_data.constBegin(); it != cend; ++it) - extra.insert(QString::fromLatin1(it.key().name().constData()), it.value()); - data.insert(QLatin1String(DATA_KEY), extra); + extra.insert(keyFromString(QString::fromLatin1(it.key().name().constData())), it.value()); + data.insert(DATA_KEY, variantFromStore(extra)); return data; } void Kit::addToBuildEnvironment(Environment &env) const { - for (KitAspect *aspect : KitManager::kitAspects()) - aspect->addToBuildEnvironment(this, env); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + factory->addToBuildEnvironment(this, env); } void Kit::addToRunEnvironment(Environment &env) const { - for (KitAspect *aspect : KitManager::kitAspects()) - aspect->addToRunEnvironment(this, env); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + factory->addToRunEnvironment(this, env); } Environment Kit::buildEnvironment() const @@ -555,8 +554,8 @@ Environment Kit::runEnvironment() const QList<OutputLineParser *> Kit::createOutputParsers() const { QList<OutputLineParser *> parsers{new OsParser}; - for (KitAspect *aspect : KitManager::kitAspects()) - parsers << aspect->createOutputParsers(this); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + parsers << factory->createOutputParsers(this); return parsers; } @@ -574,9 +573,9 @@ QString Kit::toHtml(const Tasks &additional, const QString &extraText) const str << "<p>" << ProjectExplorer::toHtml(additional + validate()) << "</p>"; str << "<dl style=\"white-space:pre\">"; - for (KitAspect *aspect : KitManager::kitAspects()) { - const KitAspect::ItemList list = aspect->toUserOutput(this); - for (const KitAspect::Item &j : list) { + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) { + const KitAspectFactory::ItemList list = factory->toUserOutput(this); + for (const KitAspectFactory::Item &j : list) { QString contents = j.second; if (contents.size() > 256) { int pos = contents.lastIndexOf("<br>", 256); @@ -619,9 +618,9 @@ void Kit::setSdkProvided(bool sdkProvided) void Kit::makeSticky() { - for (KitAspect *aspect : KitManager::kitAspects()) { - if (hasValue(aspect->id())) - setSticky(aspect->id(), true); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) { + if (hasValue(factory->id())) + setSticky(factory->id(), true); } } @@ -675,8 +674,8 @@ QSet<Id> Kit::irrelevantAspects() const QSet<Id> Kit::supportedPlatforms() const { QSet<Id> platforms; - for (const KitAspect *aspect : KitManager::kitAspects()) { - const QSet<Id> ip = aspect->supportedPlatforms(this); + for (const KitAspectFactory *factory : KitManager::kitAspectFactories()) { + const QSet<Id> ip = factory->supportedPlatforms(this); if (ip.isEmpty()) continue; if (platforms.isEmpty()) @@ -690,8 +689,8 @@ QSet<Id> Kit::supportedPlatforms() const QSet<Id> Kit::availableFeatures() const { QSet<Id> features; - for (const KitAspect *aspect : KitManager::kitAspects()) - features |= aspect->availableFeatures(this); + for (const KitAspectFactory *factory : KitManager::kitAspectFactories()) + features |= factory->availableFeatures(this); return features; } diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index 6f140d827b5..e6611ed7212 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -8,8 +8,9 @@ #include <coreplugin/featureprovider.h> +#include <utils/store.h> + #include <QSet> -#include <QVariant> #include <memory> @@ -39,8 +40,8 @@ public: using Predicate = std::function<bool(const Kit *)>; static Predicate defaultPredicate(); - explicit Kit(Utils::Id id = Utils::Id()); - explicit Kit(const QVariantMap &data); + explicit Kit(Utils::Id id = {}); + explicit Kit(const Utils::Store &data); ~Kit(); // Do not trigger evaluations @@ -138,11 +139,11 @@ private: void kitUpdated(); - QVariantMap toMap() const; + Utils::Store toMap() const; const std::unique_ptr<Internal::KitPrivate> d; - friend class KitAspect; + friend class KitAspectFactory; friend class KitManager; friend class Internal::KitManagerPrivate; friend class Internal::KitModel; // needed for setAutoDetected() when cloning kits diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitaspects.cpp similarity index 71% rename from src/plugins/projectexplorer/kitinformation.cpp rename to src/plugins/projectexplorer/kitaspects.cpp index 950cd9bb67f..489f267b7a1 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitaspects.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "kitinformation.h" +#include "kitaspects.h" #include "abi.h" #include "devicesupport/devicemanager.h" @@ -37,34 +37,30 @@ using namespace Utils; namespace ProjectExplorer { -const char KITINFORMATION_ID_V1[] = "PE.Profile.ToolChain"; -const char KITINFORMATION_ID_V2[] = "PE.Profile.ToolChains"; -const char KITINFORMATION_ID_V3[] = "PE.Profile.ToolChainsV3"; - // -------------------------------------------------------------------------- // SysRootKitAspect: // -------------------------------------------------------------------------- namespace Internal { -class SysRootKitAspectWidget : public KitAspectWidget +class SysRootKitAspectImpl : public KitAspect { public: - SysRootKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki) + SysRootKitAspectImpl(Kit *k, const KitAspectFactory *factory) : KitAspect(k, factory) { m_chooser = createSubWidget<PathChooser>(); m_chooser->setExpectedKind(PathChooser::ExistingDirectory); - m_chooser->setHistoryCompleter(QLatin1String("PE.SysRoot.History")); + m_chooser->setHistoryCompleter("PE.SysRoot.History"); m_chooser->setFilePath(SysRootKitAspect::sysRoot(k)); connect(m_chooser, &PathChooser::textChanged, - this, &SysRootKitAspectWidget::pathWasChanged); + this, &SysRootKitAspectImpl::pathWasChanged); } - ~SysRootKitAspectWidget() override { delete m_chooser; } + ~SysRootKitAspectImpl() override { delete m_chooser; } private: void makeReadOnly() override { m_chooser->setReadOnly(true); } - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_chooser); builder.addItem(Layouting::Span(2, m_chooser)); @@ -87,17 +83,27 @@ private: }; } // namespace Internal -SysRootKitAspect::SysRootKitAspect() +class SysRootKitAspectFactory : public KitAspectFactory +{ +public: + SysRootKitAspectFactory(); + + Tasks validate(const Kit *k) const override; + KitAspect *createKitAspect(Kit *k) const override; + ItemList toUserOutput(const Kit *k) const override; + void addToMacroExpander(Kit *kit, MacroExpander *expander) const override; +}; + +SysRootKitAspectFactory::SysRootKitAspectFactory() { - setObjectName(QLatin1String("SysRootInformation")); setId(SysRootKitAspect::id()); setDisplayName(Tr::tr("Sysroot")); setDescription(Tr::tr("The root directory of the system image to use.<br>" "Leave empty when building for the desktop.")); - setPriority(31000); + setPriority(27000); } -Tasks SysRootKitAspect::validate(const Kit *k) const +Tasks SysRootKitAspectFactory::validate(const Kit *k) const { Tasks result; const FilePath dir = SysRootKitAspect::sysRoot(k); @@ -120,19 +126,19 @@ Tasks SysRootKitAspect::validate(const Kit *k) const return result; } -KitAspectWidget *SysRootKitAspect::createConfigWidget(Kit *k) const +KitAspect *SysRootKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::SysRootKitAspectWidget(k, this); + return new Internal::SysRootKitAspectImpl(k, this); } -KitAspect::ItemList SysRootKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList SysRootKitAspectFactory::toUserOutput(const Kit *k) const { - return {{Tr::tr("Sys Root"), sysRoot(k).toUserOutput()}}; + return {{Tr::tr("Sys Root"), SysRootKitAspect::sysRoot(k).toUserOutput()}}; } -void SysRootKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const +void SysRootKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); @@ -141,7 +147,7 @@ void SysRootKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) con }); } -Utils::Id SysRootKitAspect::id() +Id SysRootKitAspect::id() { return "PE.Profile.SysRoot"; } @@ -149,7 +155,7 @@ Utils::Id SysRootKitAspect::id() FilePath SysRootKitAspect::sysRoot(const Kit *k) { if (!k) - return FilePath(); + return {}; if (!k->value(SysRootKitAspect::id()).toString().isEmpty()) return FilePath::fromSettings(k->value(SysRootKitAspect::id())); @@ -158,8 +164,7 @@ FilePath SysRootKitAspect::sysRoot(const Kit *k) if (!tc->sysRoot().isEmpty()) return FilePath::fromString(tc->sysRoot()); } - - return FilePath(); + return {}; } void SysRootKitAspect::setSysRoot(Kit *k, const FilePath &v) @@ -180,15 +185,17 @@ void SysRootKitAspect::setSysRoot(Kit *k, const FilePath &v) k->setValue(SysRootKitAspect::id(), v.toString()); } +const SysRootKitAspectFactory theSyRootKitAspectFactory; + // -------------------------------------------------------------------------- // ToolChainKitAspect: // -------------------------------------------------------------------------- namespace Internal { -class ToolChainKitAspectWidget final : public KitAspectWidget +class ToolChainKitAspectImpl final : public KitAspect { public: - ToolChainKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki) + ToolChainKitAspectImpl(Kit *k, const KitAspectFactory *factory) : KitAspect(k, factory) { m_mainWidget = createSubWidget<QWidget>(); m_mainWidget->setContentsMargins(0, 0, 0, 0); @@ -207,7 +214,7 @@ public: layout->addWidget(new QLabel(ToolChainManager::displayNameOfLanguageId(l) + ':'), row, 0); auto cb = new QComboBox; cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy()); - cb->setToolTip(ki->description()); + cb->setToolTip(factory->description()); m_languageComboboxMap.insert(l, cb); layout->addWidget(cb, row, 1); @@ -223,14 +230,14 @@ public: m_manageButton = createManageButton(Constants::TOOLCHAIN_SETTINGS_PAGE_ID); } - ~ToolChainKitAspectWidget() override + ~ToolChainKitAspectImpl() override { delete m_mainWidget; delete m_manageButton; } private: - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_mainWidget); builder.addItem(m_mainWidget); @@ -313,18 +320,43 @@ private: }; } // namespace Internal -ToolChainKitAspect::ToolChainKitAspect() +class ToolChainKitAspectFactory : public KitAspectFactory +{ +public: + ToolChainKitAspectFactory(); + +private: + Tasks validate(const Kit *k) const override; + void fix(Kit *k) override; + void setup(Kit *k) override; + + KitAspect *createKitAspect(Kit *k) const override; + + QString displayNamePostfix(const Kit *k) const override; + + ItemList toUserOutput(const Kit *k) const override; + + void addToBuildEnvironment(const Kit *k, Environment &env) const override; + void addToRunEnvironment(const Kit *, Environment &) const override {} + + void addToMacroExpander(Kit *kit, MacroExpander *expander) const override; + QList<OutputLineParser *> createOutputParsers(const Kit *k) const override; + QSet<Id> availableFeatures(const Kit *k) const override; + + void onKitsLoaded() override; + + void toolChainUpdated(ToolChain *tc); + void toolChainRemoved(ToolChain *tc); +}; + +ToolChainKitAspectFactory::ToolChainKitAspectFactory() { - setObjectName(QLatin1String("ToolChainInformation")); setId(ToolChainKitAspect::id()); setDisplayName(Tr::tr("Compiler")); setDescription(Tr::tr("The compiler to use for building.<br>" "Make sure the compiler will produce binaries compatible " "with the target device, Qt version and other libraries used.")); setPriority(30000); - - connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &ToolChainKitAspect::kitsWereLoaded); } // language id -> tool chain id @@ -341,22 +373,21 @@ static QMap<Id, QByteArray> defaultToolChainIds() return toolChains; } -static QVariant defaultToolChainValue() +static Store defaultToolChainValue() { const QMap<Id, QByteArray> toolChains = defaultToolChainIds(); - QVariantMap result; + Store result; auto end = toolChains.end(); - for (auto it = toolChains.begin(); it != end; ++it) { - result.insert(it.key().toString(), it.value()); - } + for (auto it = toolChains.begin(); it != end; ++it) + result.insert(it.key().toKey(), it.value()); return result; } -Tasks ToolChainKitAspect::validate(const Kit *k) const +Tasks ToolChainKitAspectFactory::validate(const Kit *k) const { Tasks result; - const QList<ToolChain*> tcList = toolChains(k); + const QList<ToolChain*> tcList = ToolChainKitAspect::toolChains(k); if (tcList.isEmpty()) { result << BuildSystemTask(Task::Warning, ToolChainKitAspect::msgNoToolChainInTarget()); } else { @@ -374,81 +405,17 @@ Tasks ToolChainKitAspect::validate(const Kit *k) const return result; } -void ToolChainKitAspect::upgrade(Kit *k) -{ - QTC_ASSERT(k, return); - - const Id oldIdV1 = KITINFORMATION_ID_V1; - const Id oldIdV2 = KITINFORMATION_ID_V2; - - // upgrade <=4.1 to 4.2 (keep old settings around for now) - { - const QVariant oldValue = k->value(oldIdV1); - const QVariant value = k->value(oldIdV2); - if (value.isNull() && !oldValue.isNull()) { - QVariantMap newValue; - if (oldValue.typeId() == QVariant::Map) { - // Used between 4.1 and 4.2: - newValue = oldValue.toMap(); - } else { - // Used up to 4.1: - newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx), oldValue.toString()); - - const Id typeId = DeviceTypeKitAspect::deviceTypeId(k); - if (typeId == Constants::DESKTOP_DEVICE_TYPE) { - // insert default C compiler which did not exist before - newValue.insert(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C), - defaultToolChainIds().value(Id(Constants::C_LANGUAGE_ID))); - } - } - k->setValue(oldIdV2, newValue); - k->setSticky(oldIdV2, k->isSticky(oldIdV1)); - } - } - - // upgrade 4.2 to 4.3 (keep old settings around for now) - { - const QVariant oldValue = k->value(oldIdV2); - const QVariant value = k->value(ToolChainKitAspect::id()); - if (value.isNull() && !oldValue.isNull()) { - QVariantMap newValue = oldValue.toMap(); - QVariantMap::iterator it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::C)); - if (it != newValue.end()) - newValue.insert(Id(Constants::C_LANGUAGE_ID).toString(), it.value()); - it = newValue.find(Deprecated::Toolchain::languageId(Deprecated::Toolchain::Cxx)); - if (it != newValue.end()) - newValue.insert(Id(Constants::CXX_LANGUAGE_ID).toString(), it.value()); - k->setValue(ToolChainKitAspect::id(), newValue); - k->setSticky(ToolChainKitAspect::id(), k->isSticky(oldIdV2)); - } - } - - // upgrade 4.3-temporary-master-state to 4.3: - { - const QVariantMap valueMap = k->value(ToolChainKitAspect::id()).toMap(); - QVariantMap result; - for (const QString &key : valueMap.keys()) { - const int pos = key.lastIndexOf('.'); - if (pos >= 0) - result.insert(key.mid(pos + 1), valueMap.value(key)); - else - result.insert(key, valueMap.value(key)); - } - k->setValue(ToolChainKitAspect::id(), result); - } -} - -void ToolChainKitAspect::fix(Kit *k) +void ToolChainKitAspectFactory::fix(Kit *k) { QTC_ASSERT(ToolChainManager::isLoaded(), return); const QList<Id> languages = ToolChainManager::allLanguages(); for (const Id l : languages) { - const QByteArray tcId = toolChainId(k, l); + const QByteArray tcId = ToolChainKitAspect::toolChainId(k, l); if (!tcId.isEmpty() && !ToolChainManager::findToolChain(tcId)) { qWarning("Tool chain set up in kit \"%s\" for \"%s\" not found.", qPrintable(k->displayName()), qPrintable(ToolChainManager::displayNameOfLanguageId(l))); - clearToolChain(k, l); // make sure to clear out no longer known tool chains + ToolChainKitAspect::clearToolChain(k, l); // make sure to clear out no longer known tool chains } } } @@ -460,18 +427,18 @@ static Id findLanguage(const QString &ls) [lsUpper](Id l) { return lsUpper == l.toString().toUpper(); }); } -void ToolChainKitAspect::setup(Kit *k) +void ToolChainKitAspectFactory::setup(Kit *k) { QTC_ASSERT(ToolChainManager::isLoaded(), return); QTC_ASSERT(k, return); - QVariantMap value = k->value(id()).toMap(); + Store value = storeFromVariant(k->value(id())); bool lockToolchains = k->isSdkProvided() && !value.isEmpty(); if (value.empty()) - value = defaultToolChainValue().toMap(); + value = defaultToolChainValue(); for (auto i = value.constBegin(); i != value.constEnd(); ++i) { - Id l = findLanguage(i.key()); + Id l = findLanguage(stringFromKey(i.key())); if (!l.isValid()) { lockToolchains = false; @@ -495,98 +462,100 @@ void ToolChainKitAspect::setup(Kit *k) bestTc = tc; } if (bestTc) - setToolChain(k, bestTc); + ToolChainKitAspect::setToolChain(k, bestTc); else - clearToolChain(k, l); + ToolChainKitAspect::clearToolChain(k, l); } k->setSticky(id(), lockToolchains); } -KitAspectWidget *ToolChainKitAspect::createConfigWidget(Kit *k) const +KitAspect *ToolChainKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::ToolChainKitAspectWidget(k, this); + return new Internal::ToolChainKitAspectImpl(k, this); } -QString ToolChainKitAspect::displayNamePostfix(const Kit *k) const +QString ToolChainKitAspectFactory::displayNamePostfix(const Kit *k) const { - ToolChain *tc = cxxToolChain(k); + ToolChain *tc = ToolChainKitAspect::cxxToolChain(k); return tc ? tc->displayName() : QString(); } -KitAspect::ItemList ToolChainKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList ToolChainKitAspectFactory::toUserOutput(const Kit *k) const { - ToolChain *tc = cxxToolChain(k); + ToolChain *tc = ToolChainKitAspect::cxxToolChain(k); return {{Tr::tr("Compiler"), tc ? tc->displayName() : Tr::tr("None")}}; } -void ToolChainKitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const +void ToolChainKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const { - ToolChain *tc = cxxToolChain(k); + ToolChain *tc = ToolChainKitAspect::cxxToolChain(k); if (tc) tc->addToEnvironment(env); } -void ToolChainKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const +void ToolChainKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); // Compatibility with Qt Creator < 4.2: expander->registerVariable("Compiler:Name", Tr::tr("Compiler"), [kit] { - const ToolChain *tc = cxxToolChain(kit); + const ToolChain *tc = ToolChainKitAspect::cxxToolChain(kit); return tc ? tc->displayName() : Tr::tr("None"); }); expander->registerVariable("Compiler:Executable", Tr::tr("Path to the compiler executable"), [kit] { - const ToolChain *tc = cxxToolChain(kit); + const ToolChain *tc = ToolChainKitAspect::cxxToolChain(kit); return tc ? tc->compilerCommand().path() : QString(); }); // After 4.2 expander->registerPrefix("Compiler:Name", Tr::tr("Compiler for different languages"), [kit](const QString &ls) { - const ToolChain *tc = toolChain(kit, findLanguage(ls)); + const ToolChain *tc = ToolChainKitAspect::toolChain(kit, findLanguage(ls)); return tc ? tc->displayName() : Tr::tr("None"); }); expander->registerPrefix("Compiler:Executable", Tr::tr("Compiler executable for different languages"), [kit](const QString &ls) { - const ToolChain *tc = toolChain(kit, findLanguage(ls)); + const ToolChain *tc = ToolChainKitAspect::toolChain(kit, findLanguage(ls)); return tc ? tc->compilerCommand().path() : QString(); }); } -QList<OutputLineParser *> ToolChainKitAspect::createOutputParsers(const Kit *k) const +QList<OutputLineParser *> ToolChainKitAspectFactory::createOutputParsers(const Kit *k) const { for (const Id langId : {Constants::CXX_LANGUAGE_ID, Constants::C_LANGUAGE_ID}) { - if (const ToolChain * const tc = toolChain(k, langId)) + if (const ToolChain * const tc = ToolChainKitAspect::toolChain(k, langId)) return tc->createOutputParsers(); } return {}; } -QSet<Id> ToolChainKitAspect::availableFeatures(const Kit *k) const +QSet<Id> ToolChainKitAspectFactory::availableFeatures(const Kit *k) const { QSet<Id> result; - for (ToolChain *tc : toolChains(k)) + for (ToolChain *tc : ToolChainKitAspect::toolChains(k)) result.insert(tc->typeId().withPrefix("ToolChain.")); return result; } Id ToolChainKitAspect::id() { - return KITINFORMATION_ID_V3; + // "PE.Profile.ToolChain" until 4.2 + // "PE.Profile.ToolChains" temporarily before 4.3 (May 2017) + return "PE.Profile.ToolChainsV3"; } QByteArray ToolChainKitAspect::toolChainId(const Kit *k, Id language) { QTC_ASSERT(ToolChainManager::isLoaded(), return nullptr); if (!k) - return QByteArray(); - QVariantMap value = k->value(ToolChainKitAspect::id()).toMap(); - return value.value(language.toString(), QByteArray()).toByteArray(); + return {}; + Store value = storeFromVariant(k->value(ToolChainKitAspect::id())); + return value.value(language.toKey(), QByteArray()).toByteArray(); } ToolChain *ToolChainKitAspect::toolChain(const Kit *k, Id language) @@ -607,12 +576,12 @@ ToolChain *ToolChainKitAspect::cxxToolChain(const Kit *k) QList<ToolChain *> ToolChainKitAspect::toolChains(const Kit *k) { - QTC_ASSERT(k, return QList<ToolChain *>()); + QTC_ASSERT(k, return {}); - const QVariantMap value = k->value(ToolChainKitAspect::id()).toMap(); + const Store value = storeFromVariant(k->value(ToolChainKitAspect::id())); const QList<ToolChain *> tcList = transform<QList>(ToolChainManager::allLanguages(), [&value](Id l) { - return ToolChainManager::findToolChain(value.value(l.toString()).toByteArray()); + return ToolChainManager::findToolChain(value.value(l.toKey()).toByteArray()); }); return filtered(tcList, [](ToolChain *tc) { return tc; }); } @@ -621,10 +590,10 @@ void ToolChainKitAspect::setToolChain(Kit *k, ToolChain *tc) { QTC_ASSERT(tc, return); QTC_ASSERT(k, return); - QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); - result.insert(tc->language().toString(), tc->id()); + Store result = storeFromVariant(k->value(ToolChainKitAspect::id())); + result.insert(tc->language().toKey(), tc->id()); - k->setValue(id(), result); + k->setValue(id(), variantFromStore(result)); } /** @@ -645,8 +614,8 @@ void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc) const Toolchains allTcList = ToolChainManager::toolchains(); QTC_ASSERT(allTcList.contains(tc), return); - QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); - result.insert(tc->language().toString(), tc->id()); + Store result = storeFromVariant(k->value(ToolChainKitAspect::id())); + result.insert(tc->language().toKey(), tc->id()); for (const Id l : ToolChainManager::allLanguages()) { if (l == tc->language()) @@ -666,14 +635,14 @@ void ToolChainKitAspect::setAllToolChainsToMatch(Kit *k, ToolChain *tc) } } if (bestMatch) - result.insert(l.toString(), bestMatch->id()); + result.insert(l.toKey(), bestMatch->id()); else if (match) - result.insert(l.toString(), match->id()); + result.insert(l.toKey(), match->id()); else - result.insert(l.toString(), QByteArray()); + result.insert(l.toKey(), QByteArray()); } - k->setValue(id(), result); + k->setValue(id(), variantFromStore(result)); } void ToolChainKitAspect::clearToolChain(Kit *k, Id language) @@ -681,9 +650,9 @@ void ToolChainKitAspect::clearToolChain(Kit *k, Id language) QTC_ASSERT(language.isValid(), return); QTC_ASSERT(k, return); - QVariantMap result = k->value(ToolChainKitAspect::id()).toMap(); - result.insert(language.toString(), QByteArray()); - k->setValue(id(), result); + Store result = storeFromVariant(k->value(ToolChainKitAspect::id())); + result.insert(language.toKey(), QByteArray()); + k->setValue(id(), variantFromStore(result)); } Abi ToolChainKitAspect::targetAbi(const Kit *k) @@ -724,56 +693,56 @@ QString ToolChainKitAspect::msgNoToolChainInTarget() return Tr::tr("No compiler set in kit."); } -void ToolChainKitAspect::kitsWereLoaded() +void ToolChainKitAspectFactory::onKitsLoaded() { - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) + for (Kit *k : KitManager::kits()) fix(k); connect(ToolChainManager::instance(), &ToolChainManager::toolChainRemoved, - this, &ToolChainKitAspect::toolChainRemoved); + this, &ToolChainKitAspectFactory::toolChainRemoved); connect(ToolChainManager::instance(), &ToolChainManager::toolChainUpdated, - this, &ToolChainKitAspect::toolChainUpdated); + this, &ToolChainKitAspectFactory::toolChainUpdated); } -void ToolChainKitAspect::toolChainUpdated(ToolChain *tc) +void ToolChainKitAspectFactory::toolChainUpdated(ToolChain *tc) { for (Kit *k : KitManager::kits()) { - if (toolChain(k, tc->language()) == tc) + if (ToolChainKitAspect::toolChain(k, tc->language()) == tc) notifyAboutUpdate(k); } } -void ToolChainKitAspect::toolChainRemoved(ToolChain *tc) +void ToolChainKitAspectFactory::toolChainRemoved(ToolChain *tc) { Q_UNUSED(tc) - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) + for (Kit *k : KitManager::kits()) fix(k); } +const ToolChainKitAspectFactory thsToolChainKitAspectFactory; + // -------------------------------------------------------------------------- // DeviceTypeKitAspect: // -------------------------------------------------------------------------- namespace Internal { -class DeviceTypeKitAspectWidget final : public KitAspectWidget +class DeviceTypeKitAspectImpl final : public KitAspect { public: - DeviceTypeKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki), m_comboBox(createSubWidget<QComboBox>()) + DeviceTypeKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory), m_comboBox(createSubWidget<QComboBox>()) { for (IDeviceFactory *factory : IDeviceFactory::allDeviceFactories()) m_comboBox->addItem(factory->displayName(), factory->deviceType().toSetting()); - m_comboBox->setToolTip(ki->description()); + m_comboBox->setToolTip(factory->description()); refresh(); connect(m_comboBox, &QComboBox::currentIndexChanged, - this, &DeviceTypeKitAspectWidget::currentTypeChanged); + this, &DeviceTypeKitAspectImpl::currentTypeChanged); } - ~DeviceTypeKitAspectWidget() override { delete m_comboBox; } + ~DeviceTypeKitAspectImpl() override { delete m_comboBox; } private: - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -804,9 +773,22 @@ private: }; } // namespace Internal -DeviceTypeKitAspect::DeviceTypeKitAspect() +class DeviceTypeKitAspectFactory : public KitAspectFactory +{ +public: + DeviceTypeKitAspectFactory(); + + void setup(Kit *k) override; + Tasks validate(const Kit *k) const override; + KitAspect *createKitAspect(Kit *k) const override; + ItemList toUserOutput(const Kit *k) const override; + + QSet<Id> supportedPlatforms(const Kit *k) const override; + QSet<Id> availableFeatures(const Kit *k) const override; +}; + +DeviceTypeKitAspectFactory::DeviceTypeKitAspectFactory() { - setObjectName(QLatin1String("DeviceTypeInformation")); setId(DeviceTypeKitAspect::id()); setDisplayName(Tr::tr("Run device type")); setDescription(Tr::tr("The type of device to run applications on.")); @@ -814,28 +796,28 @@ DeviceTypeKitAspect::DeviceTypeKitAspect() makeEssential(); } -void DeviceTypeKitAspect::setup(Kit *k) +void DeviceTypeKitAspectFactory::setup(Kit *k) { if (k && !k->hasValue(id())) k->setValue(id(), QByteArray(Constants::DESKTOP_DEVICE_TYPE)); } -Tasks DeviceTypeKitAspect::validate(const Kit *k) const +Tasks DeviceTypeKitAspectFactory::validate(const Kit *k) const { Q_UNUSED(k) return {}; } -KitAspectWidget *DeviceTypeKitAspect::createConfigWidget(Kit *k) const +KitAspect *DeviceTypeKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::DeviceTypeKitAspectWidget(k, this); + return new Internal::DeviceTypeKitAspectImpl(k, this); } -KitAspect::ItemList DeviceTypeKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList DeviceTypeKitAspectFactory::toUserOutput(const Kit *k) const { QTC_ASSERT(k, return {}); - Id type = deviceTypeId(k); + Id type = DeviceTypeKitAspect::deviceTypeId(k); QString typeDisplayName = Tr::tr("Unknown device type"); if (type.isValid()) { if (IDeviceFactory *factory = IDeviceFactory::find(type)) @@ -860,12 +842,12 @@ void DeviceTypeKitAspect::setDeviceTypeId(Kit *k, Id type) k->setValue(DeviceTypeKitAspect::id(), type.toSetting()); } -QSet<Id> DeviceTypeKitAspect::supportedPlatforms(const Kit *k) const +QSet<Id> DeviceTypeKitAspectFactory::supportedPlatforms(const Kit *k) const { - return {deviceTypeId(k)}; + return {DeviceTypeKitAspect::deviceTypeId(k)}; } -QSet<Id> DeviceTypeKitAspect::availableFeatures(const Kit *k) const +QSet<Id> DeviceTypeKitAspectFactory::availableFeatures(const Kit *k) const { Id id = DeviceTypeKitAspect::deviceTypeId(k); if (id.isValid()) @@ -873,15 +855,17 @@ QSet<Id> DeviceTypeKitAspect::availableFeatures(const Kit *k) const return {}; } +const DeviceTypeKitAspectFactory theDeviceTypeKitAspectFactory; + // -------------------------------------------------------------------------- // DeviceKitAspect: // -------------------------------------------------------------------------- namespace Internal { -class DeviceKitAspectWidget final : public KitAspectWidget +class DeviceKitAspectImpl final : public KitAspect { public: - DeviceKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki), + DeviceKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory), m_comboBox(createSubWidget<QComboBox>()), m_model(new DeviceManagerModel(DeviceManager::instance())) { @@ -891,17 +875,17 @@ public: m_comboBox->setMinimumContentsLength(16); // Don't stretch too much for Kit Page m_manageButton = createManageButton(Constants::DEVICE_SETTINGS_PAGE_ID); refresh(); - m_comboBox->setToolTip(ki->description()); + m_comboBox->setToolTip(factory->description()); connect(m_model, &QAbstractItemModel::modelAboutToBeReset, - this, &DeviceKitAspectWidget::modelAboutToReset); + this, &DeviceKitAspectImpl::modelAboutToReset); connect(m_model, &QAbstractItemModel::modelReset, - this, &DeviceKitAspectWidget::modelReset); + this, &DeviceKitAspectImpl::modelReset); connect(m_comboBox, &QComboBox::currentIndexChanged, - this, &DeviceKitAspectWidget::currentDeviceChanged); + this, &DeviceKitAspectImpl::currentDeviceChanged); } - ~DeviceKitAspectWidget() override + ~DeviceKitAspectImpl() override { delete m_comboBox; delete m_model; @@ -909,7 +893,7 @@ public: } private: - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -951,19 +935,41 @@ private: }; } // namespace Internal -DeviceKitAspect::DeviceKitAspect() +class DeviceKitAspectFactory : public KitAspectFactory +{ +public: + DeviceKitAspectFactory(); + +private: + Tasks validate(const Kit *k) const override; + void fix(Kit *k) override; + void setup(Kit *k) override; + + KitAspect *createKitAspect(Kit *k) const override; + + QString displayNamePostfix(const Kit *k) const override; + + ItemList toUserOutput(const Kit *k) const override; + + void addToMacroExpander(Kit *kit, MacroExpander *expander) const override; + + QVariant defaultValue(const Kit *k) const; + + void onKitsLoaded() override; + void deviceUpdated(Id dataId); + void devicesChanged(); + void kitUpdated(Kit *k); +}; + +DeviceKitAspectFactory::DeviceKitAspectFactory() { - setObjectName(QLatin1String("DeviceInformation")); setId(DeviceKitAspect::id()); setDisplayName(Tr::tr("Run device")); setDescription(Tr::tr("The device to run the applications on.")); setPriority(32000); - - connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &DeviceKitAspect::kitsWereLoaded); } -QVariant DeviceKitAspect::defaultValue(const Kit *k) const +QVariant DeviceKitAspectFactory::defaultValue(const Kit *k) const { Id type = DeviceTypeKitAspect::deviceTypeId(k); // Use default device if that is compatible: @@ -977,10 +983,10 @@ QVariant DeviceKitAspect::defaultValue(const Kit *k) const return dev->id().toString(); } // Fail: No device set up. - return QString(); + return {}; } -Tasks DeviceKitAspect::validate(const Kit *k) const +Tasks DeviceKitAspectFactory::validate(const Kit *k) const { IDevice::ConstPtr dev = DeviceKitAspect::device(k); Tasks result; @@ -995,45 +1001,45 @@ Tasks DeviceKitAspect::validate(const Kit *k) const return result; } -void DeviceKitAspect::fix(Kit *k) +void DeviceKitAspectFactory::fix(Kit *k) { IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (!dev.isNull() && !dev->isCompatibleWith(k)) { qWarning("Device is no longer compatible with kit \"%s\", removing it.", qPrintable(k->displayName())); - setDeviceId(k, Id()); + DeviceKitAspect::setDeviceId(k, Id()); } } -void DeviceKitAspect::setup(Kit *k) +void DeviceKitAspectFactory::setup(Kit *k) { QTC_ASSERT(DeviceManager::instance()->isLoaded(), return); IDevice::ConstPtr dev = DeviceKitAspect::device(k); if (!dev.isNull() && dev->isCompatibleWith(k)) return; - setDeviceId(k, Id::fromSetting(defaultValue(k))); + DeviceKitAspect::setDeviceId(k, Id::fromSetting(defaultValue(k))); } -KitAspectWidget *DeviceKitAspect::createConfigWidget(Kit *k) const +KitAspect *DeviceKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::DeviceKitAspectWidget(k, this); + return new Internal::DeviceKitAspectImpl(k, this); } -QString DeviceKitAspect::displayNamePostfix(const Kit *k) const +QString DeviceKitAspectFactory::displayNamePostfix(const Kit *k) const { - IDevice::ConstPtr dev = device(k); + IDevice::ConstPtr dev = DeviceKitAspect::device(k); return dev.isNull() ? QString() : dev->displayName(); } -KitAspect::ItemList DeviceKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList DeviceKitAspectFactory::toUserOutput(const Kit *k) const { - IDevice::ConstPtr dev = device(k); + IDevice::ConstPtr dev = DeviceKitAspect::device(k); return {{Tr::tr("Device"), dev.isNull() ? Tr::tr("Unconfigured") : dev->displayName()}}; } -void DeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const +void DeviceKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); expander->registerVariable("Device:HostAddress", Tr::tr("Host address"), [kit] { @@ -1096,54 +1102,54 @@ FilePath DeviceKitAspect::deviceFilePath(const Kit *k, const QString &pathOnDevi return FilePath::fromString(pathOnDevice); } -void DeviceKitAspect::kitsWereLoaded() +void DeviceKitAspectFactory::onKitsLoaded() { - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) + for (Kit *k : KitManager::kits()) fix(k); DeviceManager *dm = DeviceManager::instance(); - connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitAspect::deviceUpdated); + connect(dm, &DeviceManager::deviceListReplaced, this, &DeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceAdded, this, &DeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceRemoved, this, &DeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceUpdated, this, &DeviceKitAspectFactory::deviceUpdated); connect(KitManager::instance(), &KitManager::kitUpdated, - this, &DeviceKitAspect::kitUpdated); + this, &DeviceKitAspectFactory::kitUpdated); connect(KitManager::instance(), &KitManager::unmanagedKitUpdated, - this, &DeviceKitAspect::kitUpdated); + this, &DeviceKitAspectFactory::kitUpdated); } -void DeviceKitAspect::deviceUpdated(Id id) +void DeviceKitAspectFactory::deviceUpdated(Id id) { - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) { - if (deviceId(k) == id) + for (Kit *k : KitManager::kits()) { + if (DeviceKitAspect::deviceId(k) == id) notifyAboutUpdate(k); } } -void DeviceKitAspect::kitUpdated(Kit *k) +void DeviceKitAspectFactory::kitUpdated(Kit *k) { setup(k); // Set default device if necessary } -void DeviceKitAspect::devicesChanged() +void DeviceKitAspectFactory::devicesChanged() { - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) + for (Kit *k : KitManager::kits()) setup(k); // Set default device if necessary } +const DeviceKitAspectFactory theDeviceKitAspectFactory; + + // -------------------------------------------------------------------------- // BuildDeviceKitAspect: // -------------------------------------------------------------------------- namespace Internal { -class BuildDeviceKitAspectWidget final : public KitAspectWidget +class BuildDeviceKitAspectImpl final : public KitAspect { public: - BuildDeviceKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki), + BuildDeviceKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory), m_comboBox(createSubWidget<QComboBox>()), m_model(new DeviceManagerModel(DeviceManager::instance())) { @@ -1151,17 +1157,17 @@ public: m_comboBox->setModel(m_model); m_manageButton = createManageButton(Constants::DEVICE_SETTINGS_PAGE_ID); refresh(); - m_comboBox->setToolTip(ki->description()); + m_comboBox->setToolTip(factory->description()); connect(m_model, &QAbstractItemModel::modelAboutToBeReset, - this, &BuildDeviceKitAspectWidget::modelAboutToReset); + this, &BuildDeviceKitAspectImpl::modelAboutToReset); connect(m_model, &QAbstractItemModel::modelReset, - this, &BuildDeviceKitAspectWidget::modelReset); + this, &BuildDeviceKitAspectImpl::modelReset); connect(m_comboBox, &QComboBox::currentIndexChanged, - this, &BuildDeviceKitAspectWidget::currentDeviceChanged); + this, &BuildDeviceKitAspectImpl::currentDeviceChanged); } - ~BuildDeviceKitAspectWidget() override + ~BuildDeviceKitAspectImpl() override { delete m_comboBox; delete m_model; @@ -1169,7 +1175,7 @@ public: } private: - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_comboBox); builder.addItem(m_comboBox); @@ -1219,19 +1225,43 @@ private: }; } // namespace Internal -BuildDeviceKitAspect::BuildDeviceKitAspect() +class BuildDeviceKitAspectFactory : public KitAspectFactory +{ +public: + BuildDeviceKitAspectFactory(); + +private: + void setup(Kit *k) override; + Tasks validate(const Kit *k) const override; + + KitAspect *createKitAspect(Kit *k) const override; + + QString displayNamePostfix(const Kit *k) const override; + + ItemList toUserOutput(const Kit *k) const override; + + void addToMacroExpander(Kit *kit, MacroExpander *expander) const override; + + void onKitsLoaded() override; + void deviceUpdated(Id dataId); + void devicesChanged(); + void kitUpdated(Kit *k); +}; + +BuildDeviceKitAspectFactory::BuildDeviceKitAspectFactory() { - setObjectName("BuildDeviceInformation"); setId(BuildDeviceKitAspect::id()); setDisplayName(Tr::tr("Build device")); setDescription(Tr::tr("The device used to build applications on.")); setPriority(31900); - - connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &BuildDeviceKitAspect::kitsWereLoaded); } -void BuildDeviceKitAspect::setup(Kit *k) +static IDeviceConstPtr defaultDevice() +{ + return DeviceManager::defaultDesktopDevice(); +} + +void BuildDeviceKitAspectFactory::setup(Kit *k) { QTC_ASSERT(DeviceManager::instance()->isLoaded(), return ); IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k); @@ -1239,15 +1269,10 @@ void BuildDeviceKitAspect::setup(Kit *k) return; dev = defaultDevice(); - setDeviceId(k, dev ? dev->id() : Id()); + BuildDeviceKitAspect::setDeviceId(k, dev ? dev->id() : Id()); } -IDevice::ConstPtr BuildDeviceKitAspect::defaultDevice() -{ - return DeviceManager::defaultDesktopDevice(); -} - -Tasks BuildDeviceKitAspect::validate(const Kit *k) const +Tasks BuildDeviceKitAspectFactory::validate(const Kit *k) const { IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k); Tasks result; @@ -1257,25 +1282,25 @@ Tasks BuildDeviceKitAspect::validate(const Kit *k) const return result; } -KitAspectWidget *BuildDeviceKitAspect::createConfigWidget(Kit *k) const +KitAspect *BuildDeviceKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::BuildDeviceKitAspectWidget(k, this); + return new Internal::BuildDeviceKitAspectImpl(k, this); } -QString BuildDeviceKitAspect::displayNamePostfix(const Kit *k) const +QString BuildDeviceKitAspectFactory::displayNamePostfix(const Kit *k) const { - IDevice::ConstPtr dev = device(k); + IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k); return dev.isNull() ? QString() : dev->displayName(); } -KitAspect::ItemList BuildDeviceKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList BuildDeviceKitAspectFactory::toUserOutput(const Kit *k) const { - IDevice::ConstPtr dev = device(k); + IDevice::ConstPtr dev = BuildDeviceKitAspect::device(k); return {{Tr::tr("Build device"), dev.isNull() ? Tr::tr("Unconfigured") : dev->displayName()}}; } -void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const +void BuildDeviceKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); expander->registerVariable("BuildDevice:HostAddress", Tr::tr("Build host address"), [kit] { @@ -1296,7 +1321,7 @@ void BuildDeviceKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) }); expander->registerVariable("BuildDevice:Name", Tr::tr("Build device name"), [kit] { const IDevice::ConstPtr device = BuildDeviceKitAspect::device(kit); - return device ? device->displayName() : QString(); + return device ? device->settings()->displayName() : QString(); }); expander ->registerFileVariables("BuildDevice::Root", Tr::tr("Build device root directory"), [kit] { @@ -1335,45 +1360,49 @@ void BuildDeviceKitAspect::setDeviceId(Kit *k, Id id) k->setValue(BuildDeviceKitAspect::id(), id.toSetting()); } -void BuildDeviceKitAspect::kitsWereLoaded() +void BuildDeviceKitAspectFactory::onKitsLoaded() { - const QList<Kit *> kits = KitManager::kits(); - for (Kit *k : kits) + for (Kit *k : KitManager::kits()) fix(k); DeviceManager *dm = DeviceManager::instance(); - connect(dm, &DeviceManager::deviceListReplaced, this, &BuildDeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceAdded, this, &BuildDeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceRemoved, this, &BuildDeviceKitAspect::devicesChanged); - connect(dm, &DeviceManager::deviceUpdated, this, &BuildDeviceKitAspect::deviceUpdated); - + connect(dm, &DeviceManager::deviceListReplaced, + this, &BuildDeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceAdded, + this, &BuildDeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceRemoved, + this, &BuildDeviceKitAspectFactory::devicesChanged); + connect(dm, &DeviceManager::deviceUpdated, + this, &BuildDeviceKitAspectFactory::deviceUpdated); connect(KitManager::instance(), &KitManager::kitUpdated, - this, &BuildDeviceKitAspect::kitUpdated); + this, &BuildDeviceKitAspectFactory::kitUpdated); connect(KitManager::instance(), &KitManager::unmanagedKitUpdated, - this, &BuildDeviceKitAspect::kitUpdated); + this, &BuildDeviceKitAspectFactory::kitUpdated); } -void BuildDeviceKitAspect::deviceUpdated(Id id) +void BuildDeviceKitAspectFactory::deviceUpdated(Id id) { const QList<Kit *> kits = KitManager::kits(); for (Kit *k : kits) { - if (deviceId(k) == id) + if (BuildDeviceKitAspect::deviceId(k) == id) notifyAboutUpdate(k); } } -void BuildDeviceKitAspect::kitUpdated(Kit *k) +void BuildDeviceKitAspectFactory::kitUpdated(Kit *k) { setup(k); // Set default device if necessary } -void BuildDeviceKitAspect::devicesChanged() +void BuildDeviceKitAspectFactory::devicesChanged() { const QList<Kit *> kits = KitManager::kits(); for (Kit *k : kits) setup(k); // Set default device if necessary } +const BuildDeviceKitAspectFactory theBuildDeviceKitAspectFactory; + // -------------------------------------------------------------------------- // EnvironmentKitAspect: // -------------------------------------------------------------------------- @@ -1389,11 +1418,11 @@ static bool enforcesMSVCEnglish(const EnvironmentItems &changes) } namespace Internal { -class EnvironmentKitAspectWidget final : public KitAspectWidget +class EnvironmentKitAspectImpl final : public KitAspect { public: - EnvironmentKitAspectWidget(Kit *workingCopy, const KitAspect *ki) - : KitAspectWidget(workingCopy, ki), + EnvironmentKitAspectImpl(Kit *workingCopy, const KitAspectFactory *factory) + : KitAspect(workingCopy, factory), m_summaryLabel(createSubWidget<ElidingLabel>()), m_manageButton(createSubWidget<QPushButton>()), m_mainWidget(createSubWidget<QWidget>()) @@ -1407,11 +1436,11 @@ public: refresh(); m_manageButton->setText(Tr::tr("Change...")); connect(m_manageButton, &QAbstractButton::clicked, - this, &EnvironmentKitAspectWidget::editEnvironmentChanges); + this, &EnvironmentKitAspectImpl::editEnvironmentChanges); } private: - void addToLayout(Layouting::LayoutItem &builder) override + void addToLayoutImpl(Layouting::LayoutItem &builder) override { addMutableAction(m_mainWidget); builder.addItem(m_mainWidget); @@ -1488,16 +1517,31 @@ private: }; } // namespace Internal -EnvironmentKitAspect::EnvironmentKitAspect() +class EnvironmentKitAspectFactory : public KitAspectFactory +{ +public: + EnvironmentKitAspectFactory(); + + Tasks validate(const Kit *k) const override; + void fix(Kit *k) override; + + void addToBuildEnvironment(const Kit *k, Environment &env) const override; + void addToRunEnvironment(const Kit *, Environment &) const override; + + KitAspect *createKitAspect(Kit *k) const override; + + ItemList toUserOutput(const Kit *k) const override; +}; + +EnvironmentKitAspectFactory::EnvironmentKitAspectFactory() { - setObjectName(QLatin1String("EnvironmentKitAspect")); setId(EnvironmentKitAspect::id()); setDisplayName(Tr::tr("Environment")); setDescription(Tr::tr("Additional build environment settings when using this kit.")); setPriority(29000); } -Tasks EnvironmentKitAspect::validate(const Kit *k) const +Tasks EnvironmentKitAspectFactory::validate(const Kit *k) const { Tasks result; QTC_ASSERT(k, return result); @@ -1509,39 +1553,40 @@ Tasks EnvironmentKitAspect::validate(const Kit *k) const return result; } -void EnvironmentKitAspect::fix(Kit *k) +void EnvironmentKitAspectFactory::fix(Kit *k) { QTC_ASSERT(k, return); const QVariant variant = k->value(EnvironmentKitAspect::id()); if (!variant.isNull() && !variant.canConvert(QVariant::List)) { qWarning("Kit \"%s\" has a wrong environment value set.", qPrintable(k->displayName())); - setEnvironmentChanges(k, EnvironmentItems()); + EnvironmentKitAspect::setEnvironmentChanges(k, EnvironmentItems()); } } -void EnvironmentKitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const +void EnvironmentKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const { const QStringList values - = transform(EnvironmentItem::toStringList(environmentChanges(k)), + = transform(EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)), [k](const QString &v) { return k->macroExpander()->expand(v); }); env.modify(EnvironmentItem::fromStringList(values)); } -void EnvironmentKitAspect::addToRunEnvironment(const Kit *k, Environment &env) const +void EnvironmentKitAspectFactory::addToRunEnvironment(const Kit *k, Environment &env) const { addToBuildEnvironment(k, env); } -KitAspectWidget *EnvironmentKitAspect::createConfigWidget(Kit *k) const +KitAspect *EnvironmentKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::EnvironmentKitAspectWidget(k, this); + return new Internal::EnvironmentKitAspectImpl(k, this); } -KitAspect::ItemList EnvironmentKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList EnvironmentKitAspectFactory::toUserOutput(const Kit *k) const { - return {{Tr::tr("Environment"), EnvironmentItem::toStringList(environmentChanges(k)).join("<br>")}}; + return {{Tr::tr("Environment"), + EnvironmentItem::toStringList(EnvironmentKitAspect::environmentChanges(k)).join("<br>")}}; } Id EnvironmentKitAspect::id() @@ -1553,7 +1598,7 @@ EnvironmentItems EnvironmentKitAspect::environmentChanges(const Kit *k) { if (k) return EnvironmentItem::fromStringList(k->value(EnvironmentKitAspect::id()).toStringList()); - return EnvironmentItems(); + return {}; } void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const EnvironmentItems &changes) @@ -1562,4 +1607,6 @@ void EnvironmentKitAspect::setEnvironmentChanges(Kit *k, const EnvironmentItems k->setValue(EnvironmentKitAspect::id(), EnvironmentItem::toStringList(changes)); } +const EnvironmentKitAspectFactory theEnvironmentKitAspectFactory; + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitaspects.h b/src/plugins/projectexplorer/kitaspects.h new file mode 100644 index 00000000000..049116a21ee --- /dev/null +++ b/src/plugins/projectexplorer/kitaspects.h @@ -0,0 +1,92 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "abi.h" +#include "devicesupport/idevicefwd.h" +#include "kitmanager.h" +#include "kit.h" + +#include <utils/environment.h> + +namespace ProjectExplorer { + +class ToolChain; + +// SysRootKitAspect + +class PROJECTEXPLORER_EXPORT SysRootKitAspect +{ +public: + static Utils::Id id(); + static Utils::FilePath sysRoot(const Kit *k); + static void setSysRoot(Kit *k, const Utils::FilePath &v); +}; + +// ToolChainKitAspect + +class PROJECTEXPLORER_EXPORT ToolChainKitAspect +{ +public: + static Utils::Id id(); + static QByteArray toolChainId(const Kit *k, Utils::Id language); + static ToolChain *toolChain(const Kit *k, Utils::Id language); + static ToolChain *cToolChain(const Kit *k); + static ToolChain *cxxToolChain(const Kit *k); + static QList<ToolChain *> toolChains(const Kit *k); + static void setToolChain(Kit *k, ToolChain *tc); + static void setAllToolChainsToMatch(Kit *k, ToolChain *tc); + static void clearToolChain(Kit *k, Utils::Id language); + static Abi targetAbi(const Kit *k); + + static QString msgNoToolChainInTarget(); +}; + +// DeviceTypeKitAspect + +class PROJECTEXPLORER_EXPORT DeviceTypeKitAspect +{ +public: + static const Utils::Id id(); + static const Utils::Id deviceTypeId(const Kit *k); + static void setDeviceTypeId(Kit *k, Utils::Id type); +}; + +// DeviceKitAspect + +class PROJECTEXPLORER_EXPORT DeviceKitAspect +{ +public: + static Utils::Id id(); + static IDeviceConstPtr device(const Kit *k); + static Utils::Id deviceId(const Kit *k); + static void setDevice(Kit *k, IDeviceConstPtr dev); + static void setDeviceId(Kit *k, Utils::Id dataId); + static Utils::FilePath deviceFilePath(const Kit *k, const QString &pathOnDevice); +}; + + +// BuildDeviceKitAspect + +class PROJECTEXPLORER_EXPORT BuildDeviceKitAspect +{ +public: + static Utils::Id id(); + static IDeviceConstPtr device(const Kit *k); + static Utils::Id deviceId(const Kit *k); + static void setDevice(Kit *k, IDeviceConstPtr dev); + static void setDeviceId(Kit *k, Utils::Id dataId); +}; + +// EnvironmentKitAspect + +class PROJECTEXPLORER_EXPORT EnvironmentKitAspect +{ +public: + static Utils::Id id(); + static Utils::EnvironmentItems environmentChanges(const Kit *k); + static void setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes); +}; + +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitchooser.cpp b/src/plugins/projectexplorer/kitchooser.cpp index 9e45cacd7dd..4830f617f5f 100644 --- a/src/plugins/projectexplorer/kitchooser.cpp +++ b/src/plugins/projectexplorer/kitchooser.cpp @@ -14,7 +14,6 @@ #include <QComboBox> #include <QHBoxLayout> #include <QPushButton> -#include <QSettings> using namespace Core; using namespace Utils; @@ -29,7 +28,7 @@ KitChooser::KitChooser(QWidget *parent) : { m_chooser = new QComboBox(this); m_chooser->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - m_manageButton = new QPushButton(KitAspectWidget::msgManage(), this); + m_manageButton = new QPushButton(KitAspect::msgManage(), this); auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -102,8 +101,7 @@ void KitChooser::populate() m_hasStartupKit = true; } } - const QList<Kit *> kits = KitManager::sortKits(KitManager::kits()); - for (Kit *kit : kits) { + for (Kit *kit : KitManager::sortedKits()) { if (m_kitPredicate(kit)) { m_chooser->addItem(kitText(kit), kit->id().toSetting()); const int pos = m_chooser->count() - 1; diff --git a/src/plugins/projectexplorer/kitinformation.h b/src/plugins/projectexplorer/kitinformation.h deleted file mode 100644 index 65924ec1aec..00000000000 --- a/src/plugins/projectexplorer/kitinformation.h +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "abi.h" -#include "devicesupport/idevicefwd.h" -#include "kitmanager.h" -#include "kit.h" - -#include <utils/environment.h> - -#include <QVariant> - -namespace ProjectExplorer { -class OutputTaskParser; -class ToolChain; - -class KitAspectWidget; - -// -------------------------------------------------------------------------- -// SysRootInformation: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT SysRootKitAspect : public KitAspect -{ - Q_OBJECT - -public: - SysRootKitAspect(); - - Tasks validate(const Kit *k) const override; - KitAspectWidget *createConfigWidget(Kit *k) const override; - ItemList toUserOutput(const Kit *k) const override; - void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override; - - static Utils::Id id(); - static Utils::FilePath sysRoot(const Kit *k); - static void setSysRoot(Kit *k, const Utils::FilePath &v); -}; - -// -------------------------------------------------------------------------- -// ToolChainInformation: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT ToolChainKitAspect : public KitAspect -{ - Q_OBJECT - -public: - ToolChainKitAspect(); - - Tasks validate(const Kit *k) const override; - void upgrade(Kit *k) override; - void fix(Kit *k) override; - void setup(Kit *k) override; - - KitAspectWidget *createConfigWidget(Kit *k) const override; - - QString displayNamePostfix(const Kit *k) const override; - - ItemList toUserOutput(const Kit *k) const override; - - void addToBuildEnvironment(const Kit *k, Utils::Environment &env) const override; - void addToRunEnvironment(const Kit *, Utils::Environment &) const override {} - - void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override; - QList<Utils::OutputLineParser *> createOutputParsers(const Kit *k) const override; - QSet<Utils::Id> availableFeatures(const Kit *k) const override; - - static Utils::Id id(); - static QByteArray toolChainId(const Kit *k, Utils::Id language); - static ToolChain *toolChain(const Kit *k, Utils::Id language); - static ToolChain *cToolChain(const Kit *k); - static ToolChain *cxxToolChain(const Kit *k); - static QList<ToolChain *> toolChains(const Kit *k); - static void setToolChain(Kit *k, ToolChain *tc); - static void setAllToolChainsToMatch(Kit *k, ToolChain *tc); - static void clearToolChain(Kit *k, Utils::Id language); - static Abi targetAbi(const Kit *k); - - static QString msgNoToolChainInTarget(); - -private: - void kitsWereLoaded(); - void toolChainUpdated(ProjectExplorer::ToolChain *tc); - void toolChainRemoved(ProjectExplorer::ToolChain *tc); -}; - -// -------------------------------------------------------------------------- -// DeviceTypeInformation: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT DeviceTypeKitAspect : public KitAspect -{ - Q_OBJECT - -public: - DeviceTypeKitAspect(); - - void setup(Kit *k) override; - Tasks validate(const Kit *k) const override; - KitAspectWidget *createConfigWidget(Kit *k) const override; - ItemList toUserOutput(const Kit *k) const override; - - static const Utils::Id id(); - static const Utils::Id deviceTypeId(const Kit *k); - static void setDeviceTypeId(Kit *k, Utils::Id type); - - QSet<Utils::Id> supportedPlatforms(const Kit *k) const override; - QSet<Utils::Id> availableFeatures(const Kit *k) const override; -}; - -// -------------------------------------------------------------------------- -// DeviceInformation: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT DeviceKitAspect : public KitAspect -{ - Q_OBJECT - -public: - DeviceKitAspect(); - - Tasks validate(const Kit *k) const override; - void fix(Kit *k) override; - void setup(Kit *k) override; - - KitAspectWidget *createConfigWidget(Kit *k) const override; - - QString displayNamePostfix(const Kit *k) const override; - - ItemList toUserOutput(const Kit *k) const override; - - void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; - - static Utils::Id id(); - static IDeviceConstPtr device(const Kit *k); - static Utils::Id deviceId(const Kit *k); - static void setDevice(Kit *k, IDeviceConstPtr dev); - static void setDeviceId(Kit *k, Utils::Id dataId); - static Utils::FilePath deviceFilePath(const Kit *k, const QString &pathOnDevice); - -private: - QVariant defaultValue(const Kit *k) const; - - void kitsWereLoaded(); - void deviceUpdated(Utils::Id dataId); - void devicesChanged(); - void kitUpdated(ProjectExplorer::Kit *k); -}; - -// -------------------------------------------------------------------------- -// BuildDeviceInformation: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT BuildDeviceKitAspect : public KitAspect -{ - Q_OBJECT - -public: - BuildDeviceKitAspect(); - - void setup(Kit *k) override; - Tasks validate(const Kit *k) const override; - - KitAspectWidget *createConfigWidget(Kit *k) const override; - - QString displayNamePostfix(const Kit *k) const override; - - ItemList toUserOutput(const Kit *k) const override; - - void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; - - static Utils::Id id(); - static IDeviceConstPtr device(const Kit *k); - static Utils::Id deviceId(const Kit *k); - static void setDevice(Kit *k, IDeviceConstPtr dev); - static void setDeviceId(Kit *k, Utils::Id dataId); - -private: - static IDeviceConstPtr defaultDevice(); - - void kitsWereLoaded(); - void deviceUpdated(Utils::Id dataId); - void devicesChanged(); - void kitUpdated(ProjectExplorer::Kit *k); -}; - -// -------------------------------------------------------------------------- -// EnvironmentKitAspect: -// -------------------------------------------------------------------------- - -class PROJECTEXPLORER_EXPORT EnvironmentKitAspect : public KitAspect -{ - Q_OBJECT - -public: - EnvironmentKitAspect(); - - Tasks validate(const Kit *k) const override; - void fix(Kit *k) override; - - void addToBuildEnvironment(const Kit *k, Utils::Environment &env) const override; - void addToRunEnvironment(const Kit *, Utils::Environment &) const override; - - KitAspectWidget *createConfigWidget(Kit *k) const override; - - ItemList toUserOutput(const Kit *k) const override; - - static Utils::Id id(); - static Utils::EnvironmentItems environmentChanges(const Kit *k); - static void setEnvironmentChanges(Kit *k, const Utils::EnvironmentItems &changes); -}; - -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 86a3b533c15..73d9d48feab 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -7,12 +7,13 @@ #include "devicesupport/idevicefactory.h" #include "kit.h" #include "kitfeatureprovider.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "toolchainmanager.h" #include <coreplugin/icore.h> +#include <coreplugin/progressmanager/progressmanager.h> #include <android/androidconstants.h> #include <baremetal/baremetalconstants.h> @@ -26,11 +27,12 @@ #include <utils/qtcassert.h> #include <utils/stringutils.h> +#include <nanotrace/nanotrace.h> + #include <QAction> #include <QHash> #include <QLabel> #include <QPushButton> -#include <QSettings> #include <QStyle> using namespace Core; @@ -66,6 +68,51 @@ static FilePath settingsFileName() // KitManagerPrivate: // -------------------------------------------------------------------------- +class KitAspectFactories +{ +public: + void onKitsLoaded() const + { + for (KitAspectFactory *factory : m_aspectList) + factory->onKitsLoaded(); + } + + void addKitAspect(KitAspectFactory *factory) + { + QTC_ASSERT(!m_aspectList.contains(factory), return); + m_aspectList.append(factory); + m_aspectListIsSorted = false; + } + + void removeKitAspect(KitAspectFactory *factory) + { + int removed = m_aspectList.removeAll(factory); + QTC_CHECK(removed == 1); + } + + const QList<KitAspectFactory *> kitAspectFactories() + { + if (!m_aspectListIsSorted) { + Utils::sort(m_aspectList, [](const KitAspectFactory *a, const KitAspectFactory *b) { + return a->priority() > b->priority(); + }); + m_aspectListIsSorted = true; + } + return m_aspectList; + } + + // Sorted by priority, in descending order... + QList<KitAspectFactory *> m_aspectList; + // ... if this here is set: + bool m_aspectListIsSorted = true; +}; + +static KitAspectFactories &kitAspectFactoriesStorage() +{ + static KitAspectFactories theKitAspectFactories; + return theKitAspectFactories; +} + class KitManagerPrivate { public: @@ -74,40 +121,6 @@ public: std::vector<std::unique_ptr<Kit>> m_kitList; std::unique_ptr<PersistentSettingsWriter> m_writer; QSet<Id> m_irrelevantAspects; - - void addKitAspect(KitAspect *ki) - { - QTC_ASSERT(!m_aspectList.contains(ki), return); - m_aspectList.append(ki); - m_aspectListIsSorted = false; - } - - void removeKitAspect(KitAspect *ki) - { - int removed = m_aspectList.removeAll(ki); - QTC_CHECK(removed == 1); - } - - const QList<KitAspect *> kitAspects() - { - if (!m_aspectListIsSorted) { - Utils::sort(m_aspectList, [](const KitAspect *a, const KitAspect *b) { - return a->priority() > b->priority(); - }); - m_aspectListIsSorted = true; - } - return m_aspectList; - } - - void setBinaryForKit(const FilePath &fp) { m_binaryForKit = fp; } - FilePath binaryForKit() const { return m_binaryForKit; } - -private: - // Sorted by priority, in descending order... - QList<KitAspect *> m_aspectList; - // ... if this here is set: - bool m_aspectListIsSorted = true; - FilePath m_binaryForKit; }; @@ -118,22 +131,16 @@ private: // -------------------------------------------------------------------------- static Internal::KitManagerPrivate *d = nullptr; -static KitManager *m_instance = nullptr; KitManager *KitManager::instance() { - if (!m_instance) - m_instance = new KitManager; - return m_instance; + static KitManager theManager; + return &theManager; } KitManager::KitManager() { d = new KitManagerPrivate; - QTC_CHECK(!m_instance); - m_instance = this; - - connect(ICore::instance(), &ICore::saveSettingsRequested, this, &KitManager::saveKits); connect(this, &KitManager::kitAdded, this, &KitManager::kitsChanged); connect(this, &KitManager::kitRemoved, this, &KitManager::kitsChanged); @@ -144,14 +151,74 @@ void KitManager::destroy() { delete d; d = nullptr; - delete m_instance; - m_instance = nullptr; } +static bool kitMatchesAbiList(const Kit *kit, const Abis &abis) +{ + const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit); + for (const ToolChain * const tc : toolchains) { + const Abi tcAbi = tc->targetAbi(); + for (const Abi &abi : abis) { + if (tcAbi.os() == abi.os() && tcAbi.architecture() == abi.architecture() + && (tcAbi.os() != Abi::LinuxOS || tcAbi.osFlavor() == abi.osFlavor())) { + return true; + } + } + } + return false; +}; + +static bool isHostKit(const Kit *kit) +{ + const Abi hostAbi = Abi::hostAbi(); + if (HostOsInfo::isMacHost() && hostAbi.architecture() == Abi::ArmArchitecture) { + const Abi x86Abi(Abi::X86Architecture, + hostAbi.os(), + hostAbi.osFlavor(), + hostAbi.binaryFormat(), + hostAbi.wordWidth()); + + return kitMatchesAbiList(kit, {hostAbi, x86Abi}); + } + return kitMatchesAbiList(kit, {hostAbi}); +}; + +static Id deviceTypeForKit(const Kit *kit) +{ + if (isHostKit(kit)) + return Constants::DESKTOP_DEVICE_TYPE; + const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit); + for (const ToolChain * const tc : toolchains) { + const Abi tcAbi = tc->targetAbi(); + switch (tcAbi.os()) { + case Abi::BareMetalOS: + return BareMetal::Constants::BareMetalOsType; + case Abi::BsdOS: + case Abi::DarwinOS: + case Abi::UnixOS: + return RemoteLinux::Constants::GenericLinuxOsType; + case Abi::LinuxOS: + if (tcAbi.osFlavor() == Abi::AndroidLinuxFlavor) + return Android::Constants::ANDROID_DEVICE_TYPE; + return RemoteLinux::Constants::GenericLinuxOsType; + case Abi::QnxOS: + return Qnx::Constants::QNX_QNX_OS_TYPE; + case Abi::VxWorks: + return "VxWorks.Device.Type"; + default: + break; + } + } + return Constants::DESKTOP_DEVICE_TYPE; +}; + void KitManager::restoreKits() { + NANOTRACE_SCOPE("ProjectExplorer", "KitManager::restoreKits"); QTC_ASSERT(!d->m_initialized, return ); + connect(ICore::instance(), &ICore::saveSettingsRequested, &KitManager::saveKits); + std::vector<std::unique_ptr<Kit>> resultList; // read all kits from user file @@ -196,11 +263,12 @@ void KitManager::restoreKits() Kit *ptr = i->get(); // Overwrite settings that the SDK sets to those values: - for (const KitAspect *aspect : KitManager::kitAspects()) { + for (const KitAspectFactory *factory : KitManager::kitAspectFactories()) { + const Id id = factory->id(); // Copy sticky settings over: - ptr->setSticky(aspect->id(), toStore->isSticky(aspect->id())); - if (ptr->isSticky(aspect->id())) - ptr->setValue(aspect->id(), toStore->value(aspect->id())); + ptr->setSticky(id, toStore->isSticky(id)); + if (ptr->isSticky(id)) + ptr->setValue(id, toStore->value(id)); } toStore = std::move(*i); kitsToCheck.erase(i); @@ -224,28 +292,11 @@ void KitManager::restoreKits() } } - static const auto kitMatchesAbiList = [](const Kit *kit, const Abis &abis) { - const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit); - for (const ToolChain * const tc : toolchains) { - const Abi tcAbi = tc->targetAbi(); - for (const Abi &abi : abis) { - if (tcAbi.os() == abi.os() && tcAbi.architecture() == abi.architecture() - && (tcAbi.os() != Abi::LinuxOS || tcAbi.osFlavor() == abi.osFlavor())) { - return true; - } - } - } - return false; - }; - - const Abis abisOfBinary = d->binaryForKit().isEmpty() - ? Abis() : Abi::abisOfBinary(d->binaryForKit()); - const auto kitMatchesAbiOfBinary = [&abisOfBinary](const Kit *kit) { - return kitMatchesAbiList(kit, abisOfBinary); - }; + const Abis abisOfBinary = d->m_binaryForKit.isEmpty() + ? Abis() : Abi::abisOfBinary(d->m_binaryForKit); const bool haveKitForBinary = abisOfBinary.isEmpty() - || contains(resultList, [&kitMatchesAbiOfBinary](const std::unique_ptr<Kit> &kit) { - return kitMatchesAbiOfBinary(kit.get()); + || contains(resultList, [&abisOfBinary](const std::unique_ptr<Kit> &kit) { + return kitMatchesAbiList(kit.get(), abisOfBinary); }); Kit *kitForBinary = nullptr; @@ -293,49 +344,6 @@ void KitManager::restoreKits() bestTc = tc; } - static const auto isHostKit = [](const Kit *kit) { - const Abi hostAbi = Abi::hostAbi(); - if (HostOsInfo::isMacHost() && hostAbi.architecture() == Abi::ArmArchitecture) { - const Abi x86Abi(Abi::X86Architecture, - hostAbi.os(), - hostAbi.osFlavor(), - hostAbi.binaryFormat(), - hostAbi.wordWidth()); - - return kitMatchesAbiList(kit, {hostAbi, x86Abi}); - } - - return kitMatchesAbiList(kit, {hostAbi}); - }; - - static const auto deviceTypeForKit = [](const Kit *kit) { - if (isHostKit(kit)) - return Constants::DESKTOP_DEVICE_TYPE; - const QList<ToolChain *> toolchains = ToolChainKitAspect::toolChains(kit); - for (const ToolChain * const tc : toolchains) { - const Abi tcAbi = tc->targetAbi(); - switch (tcAbi.os()) { - case Abi::BareMetalOS: - return BareMetal::Constants::BareMetalOsType; - case Abi::BsdOS: - case Abi::DarwinOS: - case Abi::UnixOS: - return RemoteLinux::Constants::GenericLinuxOsType; - case Abi::LinuxOS: - if (tcAbi.osFlavor() == Abi::AndroidLinuxFlavor) - return Android::Constants::ANDROID_DEVICE_TYPE; - return RemoteLinux::Constants::GenericLinuxOsType; - case Abi::QnxOS: - return Qnx::Constants::QNX_QNX_OS_TYPE; - case Abi::VxWorks: - return "VxWorks.Device.Type"; - default: - break; - } - } - return Constants::DESKTOP_DEVICE_TYPE; - }; - // Create temporary kits for all toolchains found. decltype(resultList) tempList; for (auto it = uniqueToolchains.cbegin(); it != uniqueToolchains.cend(); ++it) { @@ -371,7 +379,7 @@ void KitManager::restoreKits() }); if (!abisOfBinary.isEmpty()) { for (auto it = tempList.begin(); it != tempList.end(); ++it) { - if (kitMatchesAbiOfBinary(it->get())) { + if (kitMatchesAbiList(it->get(), abisOfBinary)) { kitForBinary = it->get(); resultList.emplace_back(std::move(*it)); tempList.erase(it); @@ -412,12 +420,15 @@ void KitManager::restoreKits() if (!k) k = Utils::findOrDefault(resultList, &Kit::isValid); std::swap(resultList, d->m_kitList); + d->m_initialized = true; setDefaultKit(k); d->m_writer = std::make_unique<PersistentSettingsWriter>(settingsFileName(), "QtCreatorProfiles"); - d->m_initialized = true; - emit m_instance->kitsLoaded(); - emit m_instance->kitsChanged(); + + kitAspectFactoriesStorage().onKitsLoaded(); + + emit instance()->kitsLoaded(); + emit instance()->kitsChanged(); } KitManager::~KitManager() @@ -430,20 +441,20 @@ void KitManager::saveKits() if (!d->m_writer) // ignore save requests while we are not initialized. return; - QVariantMap data; - data.insert(QLatin1String(KIT_FILE_VERSION_KEY), 1); + Store data; + data.insert(KIT_FILE_VERSION_KEY, 1); int count = 0; const QList<Kit *> kits = KitManager::kits(); for (Kit *k : kits) { - QVariantMap tmp = k->toMap(); + Store tmp = k->toMap(); if (tmp.isEmpty()) continue; - data.insert(QString::fromLatin1(KIT_DATA_KEY) + QString::number(count), tmp); + data.insert(numberedKey(KIT_DATA_KEY, count), variantFromStore(tmp)); ++count; } - data.insert(QLatin1String(KIT_COUNT_KEY), count); - data.insert(QLatin1String(KIT_DEFAULT_KEY), + data.insert(KIT_COUNT_KEY, count); + data.insert(KIT_DEFAULT_KEY, d->m_defaultKit ? QString::fromLatin1(d->m_defaultKit->id().name()) : QString()); data.insert(KIT_IRRELEVANT_ASPECTS_KEY, transform<QVariantList>(d->m_irrelevantAspects, &Id::toSetting)); @@ -455,52 +466,59 @@ bool KitManager::isLoaded() return d->m_initialized; } -void KitManager::registerKitAspect(KitAspect *ki) +bool KitManager::waitForLoaded(const int timeout) { - instance(); - QTC_ASSERT(d, return); - d->addKitAspect(ki); - - // Adding this aspect to possibly already existing kits is currently not - // needed here as kits are only created after all aspects are created - // in *Plugin::initialize(). - // Make sure we notice when this assumption breaks: - QTC_CHECK(d->m_kitList.empty()); + if (isLoaded()) + return true; + showLoadingProgress(); + QElapsedTimer timer; + timer.start(); + while (!isLoaded() && !timer.hasExpired(timeout)) + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + return KitManager::isLoaded(); } -void KitManager::deregisterKitAspect(KitAspect *ki) +void KitManager::showLoadingProgress() { - // Happens regularly for the aspects from the ProjectExplorerPlugin as these - // are destroyed after the manual call to KitManager::destroy() there, but as - // this here is just for sanity reasons that the KitManager does not access - // a destroyed aspect, a destroyed KitManager is not a problem. - if (d) - d->removeKitAspect(ki); + if (isLoaded()) + return; + static QFutureInterface<void> futureInterface; + if (futureInterface.isRunning()) + return; + futureInterface.reportStarted(); + Core::ProgressManager::addTimedTask(futureInterface.future(), + Tr::tr("Loading Kits"), + "LoadingKitsProgress", + 5); + connect(instance(), &KitManager::kitsLoaded, []() { futureInterface.reportFinished(); }); } void KitManager::setBinaryForKit(const FilePath &binary) { QTC_ASSERT(d, return); - d->setBinaryForKit(binary); + d->m_binaryForKit = binary; } -QList<Kit *> KitManager::sortKits(const QList<Kit *> &kits) +const QList<Kit *> KitManager::sortedKits() { + QTC_ASSERT(KitManager::isLoaded(), return {}); // This method was added to delay the sorting of kits as long as possible. // Since the displayName can contain variables it can be costly (e.g. involve // calling executables to find version information, etc.) to call that // method! // Avoid lots of potentially expensive calls to Kit::displayName(): - QList<QPair<QString, Kit *>> sortList = Utils::transform(kits, [](Kit *k) { - return qMakePair(k->displayName(), k); + std::vector<QPair<QString, Kit *>> sortList = + Utils::transform(d->m_kitList, [](const std::unique_ptr<Kit> &k) { + return qMakePair(k->displayName(), k.get()); }); Utils::sort(sortList, [](const QPair<QString, Kit *> &a, const QPair<QString, Kit *> &b) -> bool { - if (a.first == b.first) - return a.second < b.second; - return a.first < b.first; + const int nameResult = Utils::caseFriendlyCompare(a.first, b.first); + if (nameResult != 0) + return nameResult < 0; + return a.second < b.second; }); - return Utils::transform(sortList, &QPair<QString, Kit *>::second); + return Utils::transform<QList>(sortList, &QPair<QString, Kit *>::second); } static KitList restoreKitsHelper(const FilePath &fileName) @@ -516,22 +534,22 @@ static KitList restoreKitsHelper(const FilePath &fileName) qPrintable(fileName.toUserOutput())); return result; } - QVariantMap data = reader.restoreValues(); + Store data = reader.restoreValues(); // Check version: - int version = data.value(QLatin1String(KIT_FILE_VERSION_KEY), 0).toInt(); + int version = data.value(KIT_FILE_VERSION_KEY, 0).toInt(); if (version < 1) { qWarning("Warning: Kit file version %d not supported, cannot restore kits!", version); return result; } - const int count = data.value(QLatin1String(KIT_COUNT_KEY), 0).toInt(); + const int count = data.value(KIT_COUNT_KEY, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = QString::fromLatin1(KIT_DATA_KEY) + QString::number(i); + const Key key = numberedKey(KIT_DATA_KEY, i); if (!data.contains(key)) break; - const QVariantMap stMap = data.value(key).toMap(); + const Store stMap = storeFromVariant(data.value(key)); auto k = std::make_unique<Kit>(stMap); if (k->id().isValid()) { @@ -542,7 +560,7 @@ static KitList restoreKitsHelper(const FilePath &fileName) i); } } - const Id id = Id::fromSetting(data.value(QLatin1String(KIT_DEFAULT_KEY))); + const Id id = Id::fromSetting(data.value(KIT_DEFAULT_KEY)); if (!id.isValid()) return result; @@ -557,6 +575,7 @@ static KitList restoreKitsHelper(const FilePath &fileName) const QList<Kit *> KitManager::kits() { + QTC_ASSERT(KitManager::isLoaded(), return {}); return Utils::toRawPointer<QList>(d->m_kitList); } @@ -565,6 +584,7 @@ Kit *KitManager::kit(Id id) if (!id.isValid()) return nullptr; + QTC_ASSERT(KitManager::isLoaded(), return {}); return Utils::findOrDefault(d->m_kitList, Utils::equal(&Kit::id, id)); } @@ -575,12 +595,13 @@ Kit *KitManager::kit(const Kit::Predicate &predicate) Kit *KitManager::defaultKit() { + QTC_ASSERT(KitManager::isLoaded(), return {}); return d->m_defaultKit; } -const QList<KitAspect *> KitManager::kitAspects() +const QList<KitAspectFactory *> KitManager::kitAspectFactories() { - return d->kitAspects(); + return kitAspectFactoriesStorage().kitAspectFactories(); } const QSet<Id> KitManager::irrelevantAspects() @@ -599,14 +620,14 @@ void KitManager::notifyAboutUpdate(Kit *k) return; if (Utils::contains(d->m_kitList, k)) - emit m_instance->kitUpdated(k); + emit instance()->kitUpdated(k); else - emit m_instance->unmanagedKitUpdated(k); + emit instance()->unmanagedKitUpdated(k); } Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Utils::Id id) { - QTC_ASSERT(isLoaded(), return nullptr); + QTC_ASSERT(isLoaded(), return {}); auto k = std::make_unique<Kit>(id); QTC_ASSERT(k->id().isValid(), return nullptr); @@ -623,12 +644,14 @@ Kit *KitManager::registerKit(const std::function<void (Kit *)> &init, Utils::Id if (!d->m_defaultKit || (!d->m_defaultKit->isValid() && kptr->isValid())) setDefaultKit(kptr); - emit m_instance->kitAdded(kptr); + emit instance()->kitAdded(kptr); return kptr; } void KitManager::deregisterKit(Kit *k) { + QTC_ASSERT(KitManager::isLoaded(), return); + if (!k || !Utils::contains(d->m_kitList, k)) return; auto taken = Utils::take(d->m_kitList, k); @@ -636,29 +659,31 @@ void KitManager::deregisterKit(Kit *k) Kit *newDefault = Utils::findOrDefault(kits(), [](Kit *k) { return k->isValid(); }); setDefaultKit(newDefault); } - emit m_instance->kitRemoved(k); + emit instance()->kitRemoved(k); } void KitManager::setDefaultKit(Kit *k) { + QTC_ASSERT(KitManager::isLoaded(), return); + if (defaultKit() == k) return; if (k && !Utils::contains(d->m_kitList, k)) return; d->m_defaultKit = k; - emit m_instance->defaultkitChanged(); + emit instance()->defaultkitChanged(); } void KitManager::completeKit(Kit *k) { QTC_ASSERT(k, return); KitGuard g(k); - for (KitAspect *ki : d->kitAspects()) { - ki->upgrade(k); - if (!k->hasValue(ki->id())) - ki->setup(k); + for (KitAspectFactory *factory : kitAspectFactories()) { + factory->upgrade(k); + if (!k->hasValue(factory->id())) + factory->setup(k); else - ki->fix(k); + factory->fix(k); } } @@ -666,73 +691,73 @@ void KitManager::completeKit(Kit *k) // KitAspect: // -------------------------------------------------------------------- -KitAspect::KitAspect() +KitAspectFactory::KitAspectFactory() { - KitManager::registerKitAspect(this); + kitAspectFactoriesStorage().addKitAspect(this); } -KitAspect::~KitAspect() +KitAspectFactory::~KitAspectFactory() { - KitManager::deregisterKitAspect(this); + kitAspectFactoriesStorage().removeKitAspect(this); } -int KitAspect::weight(const Kit *k) const +int KitAspectFactory::weight(const Kit *k) const { return k->value(id()).isValid() ? 1 : 0; } -void KitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const +void KitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const { Q_UNUSED(k) Q_UNUSED(env) } -void KitAspect::addToRunEnvironment(const Kit *k, Environment &env) const +void KitAspectFactory::addToRunEnvironment(const Kit *k, Environment &env) const { Q_UNUSED(k) Q_UNUSED(env) } -QList<OutputLineParser *> KitAspect::createOutputParsers(const Kit *k) const +QList<OutputLineParser *> KitAspectFactory::createOutputParsers(const Kit *k) const { Q_UNUSED(k) return {}; } -QString KitAspect::displayNamePostfix(const Kit *k) const +QString KitAspectFactory::displayNamePostfix(const Kit *k) const { Q_UNUSED(k) - return QString(); + return {}; } -QSet<Id> KitAspect::supportedPlatforms(const Kit *k) const +QSet<Id> KitAspectFactory::supportedPlatforms(const Kit *k) const { Q_UNUSED(k) - return QSet<Id>(); + return {}; } -QSet<Id> KitAspect::availableFeatures(const Kit *k) const +QSet<Id> KitAspectFactory::availableFeatures(const Kit *k) const { Q_UNUSED(k) - return QSet<Id>(); + return {}; } -void KitAspect::addToMacroExpander(Kit *k, MacroExpander *expander) const +void KitAspectFactory::addToMacroExpander(Kit *k, MacroExpander *expander) const { Q_UNUSED(k) Q_UNUSED(expander) } -void KitAspect::notifyAboutUpdate(Kit *k) +void KitAspectFactory::notifyAboutUpdate(Kit *k) { if (k) k->kitUpdated(); } -KitAspectWidget::KitAspectWidget(Kit *kit, const KitAspect *ki) - : m_kit(kit), m_kitInformation(ki) +KitAspect::KitAspect(Kit *kit, const KitAspectFactory *factory) + : m_kit(kit), m_factory(factory) { - const Id id = ki->id(); + const Id id = factory->id(); m_mutableAction = new QAction(Tr::tr("Mark as Mutable")); m_mutableAction->setCheckable(true); m_mutableAction->setChecked(m_kit->isMutable(id)); @@ -742,33 +767,32 @@ KitAspectWidget::KitAspectWidget(Kit *kit, const KitAspect *ki) }); } -KitAspectWidget::~KitAspectWidget() +KitAspect::~KitAspect() { delete m_mutableAction; } -void KitAspectWidget::addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent) +void KitAspect::addToLayout(Layouting::LayoutItem &parentItem) { - QTC_ASSERT(parent, return); - auto label = createSubWidget<QLabel>(m_kitInformation->displayName() + ':'); - label->setToolTip(m_kitInformation->description()); + auto label = createSubWidget<QLabel>(m_factory->displayName() + ':'); + label->setToolTip(m_factory->description()); connect(label, &QLabel::linkActivated, this, [this](const QString &link) { emit labelLinkActivated(link); }); parentItem.addItem(label); - addToLayout(parentItem); + addToLayoutImpl(parentItem); parentItem.addItem(Layouting::br); } -void KitAspectWidget::addMutableAction(QWidget *child) +void KitAspect::addMutableAction(QWidget *child) { QTC_ASSERT(child, return); child->addAction(m_mutableAction); child->setContextMenuPolicy(Qt::ActionsContextMenu); } -QWidget *KitAspectWidget::createManageButton(Id pageId) +QWidget *KitAspect::createManageButton(Id pageId) { auto button = createSubWidget<QPushButton>(msgManage()); connect(button, &QPushButton::clicked, this, [pageId] { @@ -777,7 +801,7 @@ QWidget *KitAspectWidget::createManageButton(Id pageId) return button; } -QString KitAspectWidget::msgManage() +QString KitAspect::msgManage() { return Tr::tr("Manage..."); } @@ -816,7 +840,7 @@ QString KitFeatureProvider::displayNameForPlatform(Id id) const QTC_CHECK(!dn.isEmpty()); return dn; } - return QString(); + return {}; } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmanager.h b/src/plugins/projectexplorer/kitmanager.h index f25f83ca7f4..377f96498ce 100644 --- a/src/plugins/projectexplorer/kitmanager.h +++ b/src/plugins/projectexplorer/kitmanager.h @@ -17,10 +17,6 @@ #include <functional> -QT_BEGIN_NAMESPACE -class QLabel; -QT_END_NAMESPACE - namespace Utils { class Environment; class FilePath; @@ -29,8 +25,7 @@ class OutputLineParser; } // namespace Utils namespace ProjectExplorer { -class Task; -class KitAspectWidget; +class KitAspect; class KitManager; namespace Internal { @@ -38,16 +33,15 @@ class KitManagerConfigWidget; } // namespace Internal /** - * @brief The KitAspect class + * @brief The KitAspectFactory class * - * One piece of information stored in the kit. + * A KitAspectFactory can create instances of one type of KitAspect. + * A KitAspect handles a specific piece of information stored in the kit. * * They auto-register with the \a KitManager for their life time */ -class PROJECTEXPLORER_EXPORT KitAspect : public QObject +class PROJECTEXPLORER_EXPORT KitAspectFactory : public QObject { - Q_OBJECT - public: using Item = QPair<QString, QString>; using ItemList = QList<Item>; @@ -71,7 +65,7 @@ public: virtual ItemList toUserOutput(const Kit *) const = 0; - virtual KitAspectWidget *createConfigWidget(Kit *) const = 0; + virtual KitAspect *createKitAspect(Kit *) const = 0; virtual void addToBuildEnvironment(const Kit *k, Utils::Environment &env) const; virtual void addToRunEnvironment(const Kit *k, Utils::Environment &env) const; @@ -87,9 +81,11 @@ public: virtual bool isApplicableToKit(const Kit *) const { return true; } + virtual void onKitsLoaded() {} + protected: - KitAspect(); - ~KitAspect(); + KitAspectFactory(); + ~KitAspectFactory(); void setId(Utils::Id id) { m_id = id; } void setDisplayName(const QString &name) { m_displayName = name; } @@ -106,47 +102,49 @@ private: bool m_essential = false; }; -class PROJECTEXPLORER_EXPORT KitAspectWidget : public Utils::BaseAspect +class PROJECTEXPLORER_EXPORT KitAspect : public Utils::BaseAspect { Q_OBJECT public: - KitAspectWidget(Kit *kit, const KitAspect *ki); - ~KitAspectWidget(); + KitAspect(Kit *kit, const KitAspectFactory *factory); + ~KitAspect(); virtual void makeReadOnly() = 0; virtual void refresh() = 0; - void addToLayoutWithLabel(Layouting::LayoutItem &parentItem, QWidget *parent); + void addToLayout(Layouting::LayoutItem &parentItem) override; static QString msgManage(); Kit *kit() const { return m_kit; } - const KitAspect *kitInformation() const { return m_kitInformation; } + const KitAspectFactory *factory() const { return m_factory; } QAction *mutableAction() const { return m_mutableAction; } void addMutableAction(QWidget *child); QWidget *createManageButton(Utils::Id pageId); protected: + virtual void addToLayoutImpl(Layouting::LayoutItem &parentItem) = 0; + Kit *m_kit; - const KitAspect *m_kitInformation; + const KitAspectFactory *m_factory; QAction *m_mutableAction = nullptr; }; -class PROJECTEXPLORER_EXPORT KitManager : public QObject +class PROJECTEXPLORER_EXPORT KitManager final : public QObject { Q_OBJECT public: static KitManager *instance(); - ~KitManager() override; static const QList<Kit *> kits(); + static const QList<Kit *> sortedKits(); // Avoid sorting whenever possible! static Kit *kit(const Kit::Predicate &predicate); static Kit *kit(Utils::Id id); static Kit *defaultKit(); - static const QList<KitAspect *> kitAspects(); + static const QList<KitAspectFactory *> kitAspectFactories(); static const QSet<Utils::Id> irrelevantAspects(); static void setIrrelevantAspects(const QSet<Utils::Id> &aspects); @@ -154,11 +152,11 @@ public: static void deregisterKit(Kit *k); static void setDefaultKit(Kit *k); - static QList<Kit *> sortKits(const QList<Kit *> &kits); // Avoid sorting whenever possible! - static void saveKits(); static bool isLoaded(); + static bool waitForLoaded(const int timeout = 60 * 1000); // timeout in ms + static void showLoadingProgress(); signals: void kitAdded(ProjectExplorer::Kit *); @@ -176,12 +174,10 @@ signals: private: KitManager(); + ~KitManager() override; static void destroy(); - static void registerKitAspect(KitAspect *ki); - static void deregisterKitAspect(KitAspect *ki); - static void setBinaryForKit(const Utils::FilePath &binary); // Make sure the this is only called after all @@ -194,7 +190,6 @@ private: friend class ProjectExplorerPlugin; // for constructor friend class Kit; friend class Internal::KitManagerConfigWidget; - friend class KitAspect; // for notifyAboutUpdate and self-registration }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp index 822844f57d0..b939921ebec 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.cpp @@ -5,7 +5,7 @@ #include "devicesupport/idevicefactory.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitmanager.h" #include "projectexplorertr.h" #include "task.h" @@ -30,19 +30,18 @@ #include <QToolButton> #include <QSizePolicy> -static const char WORKING_COPY_KIT_ID[] = "modified kit"; +const char WORKING_COPY_KIT_ID[] = "modified kit"; using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool &hasUniqueName) : m_iconButton(new QToolButton), m_nameEdit(new QLineEdit), m_fileSystemFriendlyNameLineEdit(new QLineEdit), m_kit(k), - m_modifiedKit(std::make_unique<Kit>(Utils::Id(WORKING_COPY_KIT_ID))), + m_modifiedKit(std::make_unique<Kit>(Id(WORKING_COPY_KIT_ID))), m_isDefaultKit(isDefaultKit), m_hasUniqueName(hasUniqueName) { @@ -102,8 +101,8 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool chooser->addSupportedWidget(m_nameEdit); chooser->addMacroExpanderProvider([this] { return m_modifiedKit->macroExpander(); }); - for (KitAspect *aspect : KitManager::kitAspects()) - addAspectToWorkingCopy(page, aspect); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) + addAspectToWorkingCopy(page, factory); page.attachTo(this); @@ -116,8 +115,8 @@ KitManagerConfigWidget::KitManagerConfigWidget(Kit *k, bool &isDefaultKit, bool KitManagerConfigWidget::~KitManagerConfigWidget() { - qDeleteAll(m_widgets); - m_widgets.clear(); + qDeleteAll(m_kitAspects); + m_kitAspects.clear(); // Make sure our workingCopy did not get registered somehow: QTC_CHECK(!Utils::contains(KitManager::kits(), @@ -135,7 +134,7 @@ QIcon KitManagerConfigWidget::displayIcon() const { // Special case: Extra warning if there are no errors but name is not unique. if (m_modifiedKit->isValid() && !m_hasUniqueName) { - static const QIcon warningIcon(Utils::Icons::WARNING.icon()); + static const QIcon warningIcon(Icons::WARNING.icon()); return warningIcon; } @@ -195,37 +194,35 @@ QString KitManagerConfigWidget::validityMessage() const return m_modifiedKit->toHtml(tmp); } -void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect) +void KitManagerConfigWidget::addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspectFactory *factory) { + QTC_ASSERT(factory, return); + KitAspect *aspect = factory->createKitAspect(workingCopy()); QTC_ASSERT(aspect, return); - KitAspectWidget *widget = aspect->createConfigWidget(workingCopy()); - QTC_ASSERT(widget, return); - QTC_ASSERT(!m_widgets.contains(widget), return); + QTC_ASSERT(!m_kitAspects.contains(aspect), return); - widget->addToLayoutWithLabel(parent, this); - m_widgets.append(widget); + aspect->addToLayout(parent); + m_kitAspects.append(aspect); - connect(widget->mutableAction(), &QAction::toggled, + connect(aspect->mutableAction(), &QAction::toggled, this, &KitManagerConfigWidget::dirty); } void KitManagerConfigWidget::updateVisibility() { - int count = m_widgets.count(); - for (int i = 0; i < count; ++i) { - KitAspectWidget *widget = m_widgets.at(i); - const KitAspect *ki = widget->kitInformation(); - const bool visibleInKit = ki->isApplicableToKit(m_modifiedKit.get()); - const bool irrelevant = m_modifiedKit->irrelevantAspects().contains(ki->id()); - widget->setVisible(visibleInKit && !irrelevant); + for (KitAspect *aspect : std::as_const(m_kitAspects)) { + const KitAspectFactory *factory = aspect->factory(); + const bool visibleInKit = factory->isApplicableToKit(m_modifiedKit.get()); + const bool irrelevant = m_modifiedKit->irrelevantAspects().contains(factory->id()); + aspect->setVisible(visibleInKit && !irrelevant); } } void KitManagerConfigWidget::makeStickySubWidgetsReadOnly() { - for (KitAspectWidget *w : std::as_const(m_widgets)) { - if (w->kit()->isSticky(w->kitInformation()->id())) - w->makeReadOnly(); + for (KitAspect *aspect : std::as_const(m_kitAspects)) { + if (aspect->kit()->isSticky(aspect->factory()->id())) + aspect->makeReadOnly(); } } @@ -241,7 +238,7 @@ bool KitManagerConfigWidget::isDefaultKit() const void KitManagerConfigWidget::setIcon() { - const Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get()); + const Id deviceType = DeviceTypeKitAspect::deviceTypeId(m_modifiedKit.get()); QList<IDeviceFactory *> allDeviceFactories = IDeviceFactory::allDeviceFactories(); if (deviceType.isValid()) { const auto less = [deviceType](const IDeviceFactory *f1, const IDeviceFactory *f2) { @@ -286,7 +283,7 @@ void KitManagerConfigWidget::setIcon() void KitManagerConfigWidget::resetIcon() { - m_modifiedKit->setIconPath(Utils::FilePath()); + m_modifiedKit->setIconPath({}); emit dirty(); } @@ -314,7 +311,7 @@ void KitManagerConfigWidget::workingCopyWasUpdated(Kit *k) k->fix(); m_fixingKit = false; - for (KitAspectWidget *w : std::as_const(m_widgets)) + for (KitAspect *w : std::as_const(m_kitAspects)) w->refresh(); m_cachedDisplayName.clear(); @@ -342,9 +339,8 @@ void KitManagerConfigWidget::kitWasUpdated(Kit *k) void KitManagerConfigWidget::showEvent(QShowEvent *event) { Q_UNUSED(event) - for (KitAspectWidget *widget : std::as_const(m_widgets)) - widget->refresh(); + for (KitAspect *aspect : std::as_const(m_kitAspects)) + aspect->refresh(); } -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/kitmanagerconfigwidget.h b/src/plugins/projectexplorer/kitmanagerconfigwidget.h index 94218c5de8a..86f637c5e35 100644 --- a/src/plugins/projectexplorer/kitmanagerconfigwidget.h +++ b/src/plugins/projectexplorer/kitmanagerconfigwidget.h @@ -35,7 +35,7 @@ public: void discard(); bool isDirty() const; QString validityMessage() const; - void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspect *aspect); + void addAspectToWorkingCopy(Layouting::LayoutItem &parent, KitAspectFactory *factory); void makeStickySubWidgetsReadOnly(); Kit *workingCopy() const; @@ -66,7 +66,7 @@ private: QToolButton *m_iconButton; QLineEdit *m_nameEdit; QLineEdit *m_fileSystemFriendlyNameLineEdit; - QList<KitAspectWidget *> m_widgets; + QList<KitAspect *> m_kitAspects; Kit *m_kit; std::unique_ptr<Kit> m_modifiedKit; bool &m_isDefaultKit; diff --git a/src/plugins/projectexplorer/kitmodel.cpp b/src/plugins/projectexplorer/kitmodel.cpp deleted file mode 100644 index 1011ffd100f..00000000000 --- a/src/plugins/projectexplorer/kitmodel.cpp +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "kitmodel.h" - -#include "kit.h" -#include "kitmanagerconfigwidget.h" -#include "kitmanager.h" -#include "projectexplorerconstants.h" -#include "projectexplorertr.h" - -#include <utils/algorithm.h> -#include <utils/qtcassert.h> -#include <utils/utilsicons.h> - -#include <QApplication> -#include <QBoxLayout> - -using namespace Utils; - -namespace ProjectExplorer { -namespace Internal { - -class KitNode : public TreeItem -{ -public: - KitNode(Kit *k, KitModel *m, QBoxLayout *parentLayout) - : m_kit(k), m_model(m), m_parentLayout(parentLayout) - {} - - ~KitNode() override { delete m_widget; } - - Kit *kit() const { return m_kit; } - - QVariant data(int, int role) const override - { - if (role == Qt::FontRole) { - QFont f = QApplication::font(); - if (isDirty()) - f.setBold(!f.bold()); - if (isDefaultKit()) - f.setItalic(f.style() != QFont::StyleItalic); - return f; - } - if (role == Qt::DisplayRole) { - QString baseName = displayName(); - if (isDefaultKit()) - //: Mark up a kit as the default one. - baseName = Tr::tr("%1 (default)").arg(baseName); - return baseName; - } - - if (role == Qt::DecorationRole) - return displayIcon(); - - if (role == Qt::ToolTipRole) - return widget()->validityMessage(); - - return {}; - } - - bool isDirty() const - { - if (m_widget) - return m_widget->isDirty(); - return false; - } - - QIcon displayIcon() const - { - if (m_widget) - return m_widget->displayIcon(); - QTC_ASSERT(m_kit, return {}); - return m_kit->displayIcon(); - } - - QString displayName() const - { - if (m_widget) - return m_widget->displayName(); - QTC_ASSERT(m_kit, return {}); - return m_kit->displayName(); - } - - bool isDefaultKit() const - { - return m_isDefaultKit; - } - - bool isRegistering() const - { - if (m_widget) - return m_widget->isRegistering(); - return false; - } - - void setIsDefaultKit(bool on) - { - if (m_isDefaultKit == on) - return; - m_isDefaultKit = on; - if (m_widget) - emit m_widget->dirty(); - } - - KitManagerConfigWidget *widget() const - { - const_cast<KitNode *>(this)->ensureWidget(); - return m_widget; - } - - void setHasUniqueName(bool on) - { - m_hasUniqueName = on; - } - -private: - void ensureWidget() - { - if (m_widget) - return; - - m_widget = new KitManagerConfigWidget(m_kit, m_isDefaultKit, m_hasUniqueName); - - QObject::connect(m_widget, &KitManagerConfigWidget::dirty, m_model, [this] { update(); }); - - QObject::connect(m_widget, &KitManagerConfigWidget::isAutoDetectedChanged, m_model, [this] { - TreeItem *oldParent = parent(); - TreeItem *newParent = - m_model->rootItem()->childAt(m_widget->workingCopy()->isAutoDetected() ? 0 : 1); - if (oldParent && oldParent != newParent) { - m_model->takeItem(this); - newParent->appendChild(this); - } - }); - m_parentLayout->addWidget(m_widget); - } - - Kit *m_kit = m_kit; - KitModel *m_model = nullptr; - KitManagerConfigWidget *m_widget = nullptr; - QBoxLayout *m_parentLayout = nullptr; - bool m_isDefaultKit = false; - bool m_hasUniqueName = true; -}; - -// -------------------------------------------------------------------------- -// KitModel -// -------------------------------------------------------------------------- - -KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent) - : TreeModel<TreeItem, TreeItem, KitNode>(parent), - m_parentLayout(parentLayout) -{ - setHeader(QStringList(Tr::tr("Name"))); - m_autoRoot = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()}, - {ProjectExplorer::Constants::msgAutoDetectedToolTip()}); - m_manualRoot = new StaticTreeItem(ProjectExplorer::Constants::msgManual()); - rootItem()->appendChild(m_autoRoot); - rootItem()->appendChild(m_manualRoot); - - const QList<Kit *> kits = KitManager::sortKits(KitManager::kits()); - for (Kit *k : kits) - addKit(k); - - changeDefaultKit(); - - connect(KitManager::instance(), &KitManager::kitAdded, - this, &KitModel::addKit); - connect(KitManager::instance(), &KitManager::kitUpdated, - this, &KitModel::updateKit); - connect(KitManager::instance(), &KitManager::unmanagedKitUpdated, - this, &KitModel::updateKit); - connect(KitManager::instance(), &KitManager::kitRemoved, - this, &KitModel::removeKit); - connect(KitManager::instance(), &KitManager::defaultkitChanged, - this, &KitModel::changeDefaultKit); -} - -Kit *KitModel::kit(const QModelIndex &index) -{ - KitNode *n = kitNode(index); - return n ? n->widget()->workingCopy() : nullptr; -} - -KitNode *KitModel::kitNode(const QModelIndex &index) -{ - TreeItem *n = itemForIndex(index); - return (n && n->level() == 2) ? static_cast<KitNode *>(n) : nullptr; -} - -QModelIndex KitModel::indexOf(Kit *k) const -{ - KitNode *n = findWorkingCopy(k); - return n ? indexForItem(n) : QModelIndex(); -} - -void KitModel::setDefaultKit(const QModelIndex &index) -{ - if (KitNode *n = kitNode(index)) - setDefaultNode(n); -} - -bool KitModel::isDefaultKit(Kit *k) const -{ - return m_defaultNode && m_defaultNode->widget()->workingCopy() == k; -} - -KitManagerConfigWidget *KitModel::widget(const QModelIndex &index) -{ - KitNode *n = kitNode(index); - return n ? n->widget() : nullptr; -} - -void KitModel::validateKitNames() -{ - QHash<QString, int> nameHash; - forItemsAtLevel<2>([&nameHash](KitNode *n) { - const QString displayName = n->displayName(); - if (nameHash.contains(displayName)) - ++nameHash[displayName]; - else - nameHash.insert(displayName, 1); - }); - - forItemsAtLevel<2>([&nameHash](KitNode *n) { - const QString displayName = n->displayName(); - n->setHasUniqueName(nameHash.value(displayName) == 1); - }); -} - -void KitModel::apply() -{ - // Add/update dirty nodes before removing kits. This ensures the right kit ends up as default. - forItemsAtLevel<2>([](KitNode *n) { - if (n->isDirty()) { - n->widget()->apply(); - n->update(); - } - }); - - // Remove unused kits: - const QList<KitNode *> removeList = m_toRemoveList; - for (KitNode *n : removeList) - KitManager::deregisterKit(n->kit()); - - emit layoutChanged(); // Force update. -} - -void KitModel::markForRemoval(Kit *k) -{ - KitNode *node = findWorkingCopy(k); - if (!node) - return; - - if (node == m_defaultNode) { - TreeItem *newDefault = m_autoRoot->firstChild(); - if (!newDefault) - newDefault = m_manualRoot->firstChild(); - setDefaultNode(static_cast<KitNode *>(newDefault)); - } - - if (node == m_defaultNode) - setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; })); - - takeItem(node); - if (node->kit() == nullptr) - delete node; - else - m_toRemoveList.append(node); - validateKitNames(); -} - -Kit *KitModel::markForAddition(Kit *baseKit) -{ - const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString()); - KitNode *node = createNode(nullptr); - m_manualRoot->appendChild(node); - Kit *k = node->widget()->workingCopy(); - KitGuard g(k); - if (baseKit) { - k->copyFrom(baseKit); - k->setAutoDetected(false); // Make sure we have a manual kit! - k->setSdkProvided(false); - } else { - k->setup(); - } - k->setUnexpandedDisplayName(newName); - - if (!m_defaultNode) - setDefaultNode(node); - - return k; -} - -void KitModel::updateVisibility() -{ - forItemsAtLevel<2>([](const TreeItem *ti) { - static_cast<const KitNode *>(ti)->widget()->updateVisibility(); - }); -} - -QString KitModel::newKitName(const QString &sourceName) const -{ - QList<Kit *> allKits; - forItemsAtLevel<2>([&allKits](const TreeItem *ti) { - allKits << static_cast<const KitNode *>(ti)->widget()->workingCopy(); - }); - return Kit::newKitName(sourceName, allKits); -} - -KitNode *KitModel::findWorkingCopy(Kit *k) const -{ - return findItemAtLevel<2>([k](KitNode *n) { return n->widget()->workingCopy() == k; }); -} - -KitNode *KitModel::createNode(Kit *k) -{ - auto node = new KitNode(k, this, m_parentLayout); - return node; -} - -void KitModel::setDefaultNode(KitNode *node) -{ - if (m_defaultNode) { - m_defaultNode->setIsDefaultKit(false); - m_defaultNode->update(); - } - m_defaultNode = node; - if (m_defaultNode) { - m_defaultNode->setIsDefaultKit(true); - m_defaultNode->update(); - } -} - -void KitModel::addKit(Kit *k) -{ - for (TreeItem *n : *m_manualRoot) { - // Was added by us - if (static_cast<KitNode *>(n)->isRegistering()) - return; - } - - TreeItem *parent = k->isAutoDetected() ? m_autoRoot : m_manualRoot; - parent->appendChild(createNode(k)); - - validateKitNames(); - emit kitStateChanged(); -} - -void KitModel::updateKit(Kit *) -{ - validateKitNames(); - emit kitStateChanged(); -} - -void KitModel::removeKit(Kit *k) -{ - QList<KitNode *> nodes = m_toRemoveList; - for (KitNode *n : std::as_const(nodes)) { - if (n->kit() == k) { - m_toRemoveList.removeOne(n); - if (m_defaultNode == n) - m_defaultNode = nullptr; - delete n; - validateKitNames(); - return; - } - } - - KitNode *node = findItemAtLevel<2>([k](KitNode *n) { - return n->kit() == k; - }); - - if (node == m_defaultNode) - setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; })); - - destroyItem(node); - - validateKitNames(); - emit kitStateChanged(); -} - -void KitModel::changeDefaultKit() -{ - Kit *defaultKit = KitManager::defaultKit(); - KitNode *node = findItemAtLevel<2>([defaultKit](KitNode *n) { - return n->kit() == defaultKit; - }); - setDefaultNode(node); -} - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitmodel.h b/src/plugins/projectexplorer/kitmodel.h deleted file mode 100644 index f402b265d40..00000000000 --- a/src/plugins/projectexplorer/kitmodel.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "projectexplorer_export.h" - -#include <utils/treemodel.h> - -QT_BEGIN_NAMESPACE -class QBoxLayout; -QT_END_NAMESPACE - -namespace ProjectExplorer { - -class Kit; -class KitFactory; -class KitManager; - -namespace Internal { - -class KitManagerConfigWidget; -class KitNode; - -// -------------------------------------------------------------------------- -// KitModel: -// -------------------------------------------------------------------------- - -class KitModel : public Utils::TreeModel<Utils::TreeItem, Utils::TreeItem, KitNode> -{ - Q_OBJECT - -public: - explicit KitModel(QBoxLayout *parentLayout, QObject *parent = nullptr); - - Kit *kit(const QModelIndex &); - KitNode *kitNode(const QModelIndex &); - QModelIndex indexOf(Kit *k) const; - - void setDefaultKit(const QModelIndex &index); - bool isDefaultKit(Kit *k) const; - - ProjectExplorer::Internal::KitManagerConfigWidget *widget(const QModelIndex &); - - void apply(); - - void markForRemoval(Kit *k); - Kit *markForAddition(Kit *baseKit); - - void updateVisibility(); - - QString newKitName(const QString &sourceName) const; - -signals: - void kitStateChanged(); - -private: - void addKit(ProjectExplorer::Kit *k); - void updateKit(ProjectExplorer::Kit *k); - void removeKit(ProjectExplorer::Kit *k); - void changeDefaultKit(); - void validateKitNames(); - - KitNode *findWorkingCopy(Kit *k) const; - KitNode *createNode(Kit *k); - void setDefaultNode(KitNode *node); - - Utils::TreeItem *m_autoRoot; - Utils::TreeItem *m_manualRoot; - - QList<KitNode *> m_toRemoveList; - - QBoxLayout *m_parentLayout; - KitNode *m_defaultNode = nullptr; - - bool m_keepUnique = true; -}; - -} // namespace Internal -} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/kitoptionspage.cpp b/src/plugins/projectexplorer/kitoptionspage.cpp index db578a403e6..62c119a46a2 100644 --- a/src/plugins/projectexplorer/kitoptionspage.cpp +++ b/src/plugins/projectexplorer/kitoptionspage.cpp @@ -4,15 +4,21 @@ #include "kitoptionspage.h" #include "filterkitaspectsdialog.h" -#include "kitmodel.h" #include "kit.h" +#include "kitmanager.h" +#include "kitmanagerconfigwidget.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include "kitmanagerconfigwidget.h" -#include "kitmanager.h" +#include <coreplugin/dialogs/ioptionspage.h> + +#include <utils/algorithm.h> +#include <utils/id.h> #include <utils/qtcassert.h> +#include <utils/utilsicons.h> +#include <QApplication> +#include <QBoxLayout> #include <QHBoxLayout> #include <QHeaderView> #include <QItemSelectionModel> @@ -20,15 +26,449 @@ #include <QTreeView> #include <QVBoxLayout> +using namespace Utils; + namespace ProjectExplorer { + +bool KitSettingsSortModel::lessThan(const QModelIndex &source_left, + const QModelIndex &source_right) const +{ + const auto defaultCmp = [&] { return SortModel::lessThan(source_left, source_right); }; + + if (m_sortedCategories.isEmpty() || source_left.parent().isValid()) + return defaultCmp(); + + QTC_ASSERT(!source_right.parent().isValid(), return defaultCmp()); + const int leftIndex = m_sortedCategories.indexOf(sourceModel()->data(source_left)); + QTC_ASSERT(leftIndex != -1, return defaultCmp()); + if (leftIndex == 0) + return true; + const int rightIndex = m_sortedCategories.indexOf(sourceModel()->data(source_right)); + QTC_ASSERT(rightIndex != -1, return defaultCmp()); + return leftIndex < rightIndex; +} + namespace Internal { +// Page pre-selection + +static Id selectedKitId; + +void setSelectectKitId(const Id &kitId) +{ + selectedKitId = kitId; +} + +class KitManagerConfigWidget; + +class KitNode : public TreeItem +{ +public: + KitNode(Kit *k, KitModel *m, QBoxLayout *parentLayout) + : m_kit(k), m_model(m), m_parentLayout(parentLayout) + {} + + ~KitNode() override { delete m_widget; } + + Kit *kit() const { return m_kit; } + + QVariant data(int, int role) const override + { + if (role == Qt::FontRole) { + QFont f = QApplication::font(); + if (isDirty()) + f.setBold(!f.bold()); + if (isDefaultKit()) + f.setItalic(f.style() != QFont::StyleItalic); + return f; + } + if (role == Qt::DisplayRole) { + QString baseName = displayName(); + if (isDefaultKit()) + //: Mark up a kit as the default one. + baseName = Tr::tr("%1 (default)").arg(baseName); + return baseName; + } + + if (role == Qt::DecorationRole) + return displayIcon(); + + if (role == Qt::ToolTipRole) + return widget()->validityMessage(); + + return {}; + } + + bool isDirty() const + { + if (m_widget) + return m_widget->isDirty(); + return false; + } + + QIcon displayIcon() const + { + if (m_widget) + return m_widget->displayIcon(); + QTC_ASSERT(m_kit, return {}); + return m_kit->displayIcon(); + } + + QString displayName() const + { + if (m_widget) + return m_widget->displayName(); + QTC_ASSERT(m_kit, return {}); + return m_kit->displayName(); + } + + bool isDefaultKit() const + { + return m_isDefaultKit; + } + + bool isRegistering() const + { + if (m_widget) + return m_widget->isRegistering(); + return false; + } + + void setIsDefaultKit(bool on) + { + if (m_isDefaultKit == on) + return; + m_isDefaultKit = on; + if (m_widget) + emit m_widget->dirty(); + } + + KitManagerConfigWidget *widget() const + { + const_cast<KitNode *>(this)->ensureWidget(); + return m_widget; + } + + void setHasUniqueName(bool on) + { + m_hasUniqueName = on; + } + +private: + void ensureWidget(); + + Kit *m_kit = m_kit; + KitModel *m_model = nullptr; + KitManagerConfigWidget *m_widget = nullptr; + QBoxLayout *m_parentLayout = nullptr; + bool m_isDefaultKit = false; + bool m_hasUniqueName = true; +}; + +// KitModel + +class KitModel : public TreeModel<TreeItem, TreeItem, KitNode> +{ + Q_OBJECT + +public: + explicit KitModel(QBoxLayout *parentLayout, QObject *parent = nullptr); + + Kit *kit(const QModelIndex &); + KitNode *kitNode(const QModelIndex &); + QModelIndex indexOf(Kit *k) const; + QModelIndex indexOf(Id kitId) const; + + void setDefaultKit(const QModelIndex &index); + bool isDefaultKit(Kit *k) const; + + KitManagerConfigWidget *widget(const QModelIndex &); + + void apply(); + + void markForRemoval(Kit *k); + Kit *markForAddition(Kit *baseKit); + + void updateVisibility(); + + QString newKitName(const QString &sourceName) const; + +signals: + void kitStateChanged(); + +private: + void initializeFromKitManager(); + void addKit(Kit *k); + void updateKit(Kit *k); + void removeKit(Kit *k); + void changeDefaultKit(); + void validateKitNames(); + + KitNode *findWorkingCopy(Kit *k) const; + KitNode *createNode(Kit *k); + void setDefaultNode(KitNode *node); + + TreeItem *m_autoRoot; + TreeItem *m_manualRoot; + + QList<KitNode *> m_toRemoveList; + + QBoxLayout *m_parentLayout; + KitNode *m_defaultNode = nullptr; +}; + +KitModel::KitModel(QBoxLayout *parentLayout, QObject *parent) + : TreeModel<TreeItem, TreeItem, KitNode>(parent), + m_parentLayout(parentLayout) +{ + setHeader(QStringList(Tr::tr("Name"))); + m_autoRoot = new StaticTreeItem({ProjectExplorer::Constants::msgAutoDetected()}, + {ProjectExplorer::Constants::msgAutoDetectedToolTip()}); + m_manualRoot = new StaticTreeItem(ProjectExplorer::Constants::msgManual()); + rootItem()->appendChild(m_autoRoot); + rootItem()->appendChild(m_manualRoot); + + if (KitManager::isLoaded()) { + for (Kit *k : KitManager::sortedKits()) + addKit(k); + changeDefaultKit(); + } + + connect(KitManager::instance(), &KitManager::kitAdded, + this, &KitModel::addKit); + connect(KitManager::instance(), &KitManager::kitUpdated, + this, &KitModel::updateKit); + connect(KitManager::instance(), &KitManager::unmanagedKitUpdated, + this, &KitModel::updateKit); + connect(KitManager::instance(), &KitManager::kitRemoved, + this, &KitModel::removeKit); + connect(KitManager::instance(), &KitManager::defaultkitChanged, + this, &KitModel::changeDefaultKit); +} + +Kit *KitModel::kit(const QModelIndex &index) +{ + KitNode *n = kitNode(index); + return n ? n->widget()->workingCopy() : nullptr; +} + +KitNode *KitModel::kitNode(const QModelIndex &index) +{ + TreeItem *n = itemForIndex(index); + return (n && n->level() == 2) ? static_cast<KitNode *>(n) : nullptr; +} + +QModelIndex KitModel::indexOf(Id kitId) const +{ + KitNode *n = findItemAtLevel<2>([kitId](KitNode *n) { return n->kit()->id() == kitId; }); + return n ? indexForItem(n) : QModelIndex(); +} + +QModelIndex KitModel::indexOf(Kit *k) const +{ + KitNode *n = findWorkingCopy(k); + return n ? indexForItem(n) : QModelIndex(); +} + +void KitModel::setDefaultKit(const QModelIndex &index) +{ + if (KitNode *n = kitNode(index)) + setDefaultNode(n); +} + +bool KitModel::isDefaultKit(Kit *k) const +{ + return m_defaultNode && m_defaultNode->widget()->workingCopy() == k; +} + +KitManagerConfigWidget *KitModel::widget(const QModelIndex &index) +{ + KitNode *n = kitNode(index); + return n ? n->widget() : nullptr; +} + +void KitModel::validateKitNames() +{ + QHash<QString, int> nameHash; + forItemsAtLevel<2>([&nameHash](KitNode *n) { + const QString displayName = n->displayName(); + if (nameHash.contains(displayName)) + ++nameHash[displayName]; + else + nameHash.insert(displayName, 1); + }); + + forItemsAtLevel<2>([&nameHash](KitNode *n) { + const QString displayName = n->displayName(); + n->setHasUniqueName(nameHash.value(displayName) == 1); + }); +} + +void KitModel::apply() +{ + emit layoutAboutToBeChanged(); + + // Add/update dirty nodes before removing kits. This ensures the right kit ends up as default. + forItemsAtLevel<2>([](KitNode *n) { + if (n->isDirty()) { + n->widget()->apply(); + n->update(); + } + }); + + // Remove unused kits: + const QList<KitNode *> removeList = m_toRemoveList; + for (KitNode *n : removeList) + KitManager::deregisterKit(n->kit()); + + emit layoutChanged(); // Force update. +} + +void KitModel::markForRemoval(Kit *k) +{ + KitNode *node = findWorkingCopy(k); + if (!node) + return; + + if (node == m_defaultNode) { + TreeItem *newDefault = m_autoRoot->firstChild(); + if (!newDefault) + newDefault = m_manualRoot->firstChild(); + setDefaultNode(static_cast<KitNode *>(newDefault)); + } + + if (node == m_defaultNode) + setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; })); + + takeItem(node); + if (node->kit() == nullptr) + delete node; + else + m_toRemoveList.append(node); + validateKitNames(); +} + +Kit *KitModel::markForAddition(Kit *baseKit) +{ + const QString newName = newKitName(baseKit ? baseKit->unexpandedDisplayName() : QString()); + KitNode *node = createNode(nullptr); + m_manualRoot->appendChild(node); + Kit *k = node->widget()->workingCopy(); + KitGuard g(k); + if (baseKit) { + k->copyFrom(baseKit); + k->setAutoDetected(false); // Make sure we have a manual kit! + k->setSdkProvided(false); + } else { + k->setup(); + } + k->setUnexpandedDisplayName(newName); + + if (!m_defaultNode) + setDefaultNode(node); + + return k; +} + +void KitModel::updateVisibility() +{ + forItemsAtLevel<2>([](const TreeItem *ti) { + static_cast<const KitNode *>(ti)->widget()->updateVisibility(); + }); +} + +QString KitModel::newKitName(const QString &sourceName) const +{ + QList<Kit *> allKits; + forItemsAtLevel<2>([&allKits](const TreeItem *ti) { + allKits << static_cast<const KitNode *>(ti)->widget()->workingCopy(); + }); + return Kit::newKitName(sourceName, allKits); +} + +KitNode *KitModel::findWorkingCopy(Kit *k) const +{ + return findItemAtLevel<2>([k](KitNode *n) { return n->widget()->workingCopy() == k; }); +} + +KitNode *KitModel::createNode(Kit *k) +{ + auto node = new KitNode(k, this, m_parentLayout); + return node; +} + +void KitModel::setDefaultNode(KitNode *node) +{ + if (m_defaultNode) { + m_defaultNode->setIsDefaultKit(false); + m_defaultNode->update(); + } + m_defaultNode = node; + if (m_defaultNode) { + m_defaultNode->setIsDefaultKit(true); + m_defaultNode->update(); + } +} + +void KitModel::addKit(Kit *k) +{ + for (TreeItem *n : *m_manualRoot) { + // Was added by us + if (static_cast<KitNode *>(n)->isRegistering()) + return; + } + + TreeItem *parent = k->isAutoDetected() ? m_autoRoot : m_manualRoot; + parent->appendChild(createNode(k)); + + validateKitNames(); + emit kitStateChanged(); +} + +void KitModel::updateKit(Kit *) +{ + validateKitNames(); + emit kitStateChanged(); +} + +void KitModel::removeKit(Kit *k) +{ + QList<KitNode *> nodes = m_toRemoveList; + for (KitNode *n : std::as_const(nodes)) { + if (n->kit() == k) { + m_toRemoveList.removeOne(n); + if (m_defaultNode == n) + m_defaultNode = nullptr; + delete n; + validateKitNames(); + return; + } + } + + KitNode *node = findItemAtLevel<2>([k](KitNode *n) { + return n->kit() == k; + }); + + if (node == m_defaultNode) + setDefaultNode(findItemAtLevel<2>([node](KitNode *kn) { return kn != node; })); + + destroyItem(node); + + validateKitNames(); + emit kitStateChanged(); +} + +void KitModel::changeDefaultKit() +{ + Kit *defaultKit = KitManager::defaultKit(); + KitNode *node = findItemAtLevel<2>([defaultKit](KitNode *n) { + return n->kit() == defaultKit; + }); + setDefaultNode(node); +} + // KitOptionsPageWidget -class KitOptionsPageWidget; - -static QPointer<KitOptionsPageWidget> kitOptionsPageWidget; - class KitOptionsPageWidget : public Core::IOptionsPageWidget { public: @@ -44,6 +484,8 @@ public: void makeDefaultKit(); void updateState(); + void scrollToSelectedKit(); + void apply() final { m_model->apply(); } public: @@ -56,13 +498,13 @@ public: QPushButton *m_defaultFilterButton = nullptr; KitModel *m_model = nullptr; + KitSettingsSortModel *m_sortModel = nullptr; QItemSelectionModel *m_selectionModel = nullptr; KitManagerConfigWidget *m_currentWidget = nullptr; }; KitOptionsPageWidget::KitOptionsPageWidget() { - kitOptionsPageWidget = this; m_kitsView = new QTreeView(this); m_kitsView->setUniformRowHeights(true); m_kitsView->header()->setStretchLastSection(true); @@ -101,10 +543,15 @@ KitOptionsPageWidget::KitOptionsPageWidget() this, &KitOptionsPageWidget::updateState); verticalLayout->setStretch(0, 1); verticalLayout->setStretch(1, 0); + m_sortModel = new KitSettingsSortModel(this); + m_sortModel->setSortedCategories({Constants::msgAutoDetected(), Constants::msgManual()}); + m_sortModel->setSourceModel(m_model); - m_kitsView->setModel(m_model); + m_kitsView->setModel(m_sortModel); m_kitsView->header()->setSectionResizeMode(0, QHeaderView::Stretch); m_kitsView->expandAll(); + m_kitsView->setSortingEnabled(true); + m_kitsView->sortByColumn(0, Qt::AscendingOrder); m_selectionModel = m_kitsView->selectionModel(); connect(m_selectionModel, &QItemSelectionModel::selectionChanged, @@ -140,13 +587,26 @@ KitOptionsPageWidget::KitOptionsPageWidget() m_model->updateVisibility(); } }); + + scrollToSelectedKit(); + updateState(); } +void KitOptionsPageWidget::scrollToSelectedKit() +{ + QModelIndex index = m_sortModel->mapFromSource(m_model->indexOf(selectedKitId)); + m_selectionModel->select(index, + QItemSelectionModel::Clear + | QItemSelectionModel::SelectCurrent + | QItemSelectionModel::Rows); + m_kitsView->scrollTo(index); +} + void KitOptionsPageWidget::kitSelectionChanged() { QModelIndex current = currentIndex(); - KitManagerConfigWidget * const newWidget = m_model->widget(current); + KitManagerConfigWidget * const newWidget = m_model->widget(m_sortModel->mapToSource(current)); if (newWidget == m_currentWidget) return; @@ -167,7 +627,7 @@ void KitOptionsPageWidget::addNewKit() { Kit *k = m_model->markForAddition(nullptr); - QModelIndex newIdx = m_model->indexOf(k); + QModelIndex newIdx = m_sortModel->mapFromSource(m_model->indexOf(k)); m_selectionModel->select(newIdx, QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent @@ -176,7 +636,7 @@ void KitOptionsPageWidget::addNewKit() Kit *KitOptionsPageWidget::currentKit() const { - return m_model->kit(currentIndex()); + return m_model->kit(m_sortModel->mapToSource(currentIndex())); } void KitOptionsPageWidget::cloneKit() @@ -186,7 +646,7 @@ void KitOptionsPageWidget::cloneKit() return; Kit *k = m_model->markForAddition(current); - QModelIndex newIdx = m_model->indexOf(k); + QModelIndex newIdx = m_sortModel->mapFromSource(m_model->indexOf(k)); m_kitsView->scrollTo(newIdx); m_selectionModel->select(newIdx, QItemSelectionModel::Clear @@ -202,7 +662,7 @@ void KitOptionsPageWidget::removeKit() void KitOptionsPageWidget::makeDefaultKit() { - m_model->setDefaultKit(currentIndex()); + m_model->setDefaultKit(m_sortModel->mapToSource(currentIndex())); updateState(); } @@ -230,45 +690,54 @@ void KitOptionsPageWidget::updateState() QModelIndex KitOptionsPageWidget::currentIndex() const { if (!m_selectionModel) - return QModelIndex(); + return {}; QModelIndexList idxs = m_selectionModel->selectedRows(); if (idxs.count() != 1) - return QModelIndex(); + return {}; return idxs.at(0); } -} // namespace Internal - -// -------------------------------------------------------------------------- -// KitOptionsPage: -// -------------------------------------------------------------------------- - -KitOptionsPage::KitOptionsPage() +void KitNode::ensureWidget() { - setId(Constants::KITS_SETTINGS_PAGE_ID); - setDisplayName(Tr::tr("Kits")); - setCategory(Constants::KITS_SETTINGS_CATEGORY); - setDisplayCategory(Tr::tr("Kits")); - setCategoryIconPath(":/projectexplorer/images/settingscategory_kits.png"); - setWidgetCreator([] { return new Internal::KitOptionsPageWidget; }); -} - -void KitOptionsPage::showKit(Kit *k) -{ - if (!k) + if (m_widget) return; - Internal::KitOptionsPageWidget *widget = Internal::kitOptionsPageWidget; - if (!widget) - return; + m_widget = new KitManagerConfigWidget(m_kit, m_isDefaultKit, m_hasUniqueName); - QModelIndex index = widget->m_model->indexOf(k); - widget->m_selectionModel->select(index, - QItemSelectionModel::Clear - | QItemSelectionModel::SelectCurrent - | QItemSelectionModel::Rows); - widget->m_kitsView->scrollTo(index); + QObject::connect(m_widget, &KitManagerConfigWidget::dirty, m_model, [this] { update(); }); + + QObject::connect(m_widget, &KitManagerConfigWidget::isAutoDetectedChanged, m_model, [this] { + TreeItem *oldParent = parent(); + TreeItem *newParent = + m_model->rootItem()->childAt(m_widget->workingCopy()->isAutoDetected() ? 0 : 1); + if (oldParent && oldParent != newParent) { + m_model->takeItem(this); + newParent->appendChild(this); + } + }); + m_parentLayout->addWidget(m_widget); } -} // namespace ProjectExplorer +// KitOptionsPage + +class KitsSettingsPage : public Core::IOptionsPage +{ +public: + KitsSettingsPage() + { + setId(Constants::KITS_SETTINGS_PAGE_ID); + setDisplayName(Tr::tr("Kits")); + setCategory(Constants::KITS_SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("Kits")); + setCategoryIconPath(":/projectexplorer/images/settingscategory_kits.png"); + setWidgetCreator([] { return new Internal::KitOptionsPageWidget; }); + } +}; + +const KitsSettingsPage theKitsSettingsPage; + +} // Internal +} // ProjectExplorer + +#include "kitoptionspage.moc" diff --git a/src/plugins/projectexplorer/kitoptionspage.h b/src/plugins/projectexplorer/kitoptionspage.h index a7a4ba29dbc..8734024574f 100644 --- a/src/plugins/projectexplorer/kitoptionspage.h +++ b/src/plugins/projectexplorer/kitoptionspage.h @@ -5,18 +5,28 @@ #include "projectexplorer_export.h" -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/treemodel.h> + +namespace Utils { class Id; } namespace ProjectExplorer { -class Kit; - -class PROJECTEXPLORER_EXPORT KitOptionsPage : public Core::IOptionsPage +class PROJECTEXPLORER_EXPORT KitSettingsSortModel : public Utils::SortModel { public: - KitOptionsPage(); + using SortModel::SortModel; - static void showKit(Kit *k); + void setSortedCategories(const QStringList &categories) { m_sortedCategories = categories; } + +private: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; + + QStringList m_sortedCategories; }; -} // namespace ProjectExplorer +namespace Internal { + +void setSelectectKitId(const Utils::Id &kitId); + +} // Internal +} // ProjectExplorer diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index 028706fa2db..bcf3bab8f02 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -6,7 +6,7 @@ #include "buildconfiguration.h" #include "devicesupport/idevice.h" #include "gnumakeparser.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "processparameters.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -47,23 +47,24 @@ MakeStep::MakeStep(BuildStepList *parent, Id id) setCommandLineProvider([this] { return effectiveMakeCommand(Execution); }); - m_makeCommandAspect.setSettingsKey(id.withSuffix(MAKE_COMMAND_SUFFIX).toString()); + // FIXME: Replace with id.name() + MAKE_COMMAND_SUFFIX after the Key/Store transition + m_makeCommandAspect.setSettingsKey(id.toKey() + MAKE_COMMAND_SUFFIX); m_makeCommandAspect.setExpectedKind(PathChooser::ExistingCommand); m_makeCommandAspect.setBaseFileName(PathChooser::homePath()); m_makeCommandAspect.setHistoryCompleter("PE.MakeCommand.History"); - m_userArgumentsAspect.setSettingsKey(id.withSuffix(MAKE_ARGUMENTS_SUFFIX).toString()); + m_userArgumentsAspect.setSettingsKey(id.toKey() + MAKE_ARGUMENTS_SUFFIX); m_userArgumentsAspect.setLabelText(Tr::tr("Make arguments:")); m_userArgumentsAspect.setDisplayStyle(StringAspect::LineEditDisplay); - m_jobCountAspect.setSettingsKey(id.withSuffix(JOBCOUNT_SUFFIX).toString()); + m_jobCountAspect.setSettingsKey(id.toKey() + JOBCOUNT_SUFFIX); m_jobCountAspect.setLabel(Tr::tr("Parallel jobs:")); m_jobCountAspect.setRange(1, 999); m_jobCountAspect.setValue(defaultJobCount()); m_jobCountAspect.setDefaultValue(defaultJobCount()); const QString text = Tr::tr("Override MAKEFLAGS"); - m_overrideMakeflagsAspect.setSettingsKey(id.withSuffix(OVERRIDE_MAKEFLAGS_SUFFIX).toString()); + m_overrideMakeflagsAspect.setSettingsKey(id.toKey() + OVERRIDE_MAKEFLAGS_SUFFIX); m_overrideMakeflagsAspect.setLabel(text, BoolAspect::LabelPlacement::AtCheckBox); m_nonOverrideWarning.setText("<html><body><p>" + @@ -71,11 +72,11 @@ MakeStep::MakeStep(BuildStepList *parent, Id id) .arg(text) + "</p></body></html>"); m_nonOverrideWarning.setIconType(InfoLabel::Warning); - m_disabledForSubdirsAspect.setSettingsKey(id.withSuffix(".disabledForSubdirs").toString()); + m_disabledForSubdirsAspect.setSettingsKey(id.toKey() + ".disabledForSubdirs"); m_disabledForSubdirsAspect.setLabel(Tr::tr("Disable in subdirectories:")); m_disabledForSubdirsAspect.setToolTip(Tr::tr("Runs this step only for a top-level build.")); - m_buildTargetsAspect.setSettingsKey(id.withSuffix(BUILD_TARGETS_SUFFIX).toString()); + m_buildTargetsAspect.setSettingsKey(id.toKey() + BUILD_TARGETS_SUFFIX); m_buildTargetsAspect.setLabelText(Tr::tr("Targets:")); const auto updateMakeLabel = [this] { @@ -252,7 +253,7 @@ Environment MakeStep::makeEnvironment() const void MakeStep::setMakeCommand(const FilePath &command) { - m_makeCommandAspect.setFilePath(command); + m_makeCommandAspect.setValue(command); } int MakeStep::defaultJobCount() @@ -303,7 +304,7 @@ CommandLine MakeStep::effectiveMakeCommand(MakeCommandType type) const cmd.addArgs(displayArguments()); cmd.addArgs(userArguments(), CommandLine::Raw); cmd.addArgs(jobArguments()); - cmd.addArgs(m_buildTargetsAspect.value()); + cmd.addArgs(m_buildTargetsAspect()); return cmd; } diff --git a/src/plugins/projectexplorer/miniprojecttargetselector.cpp b/src/plugins/projectexplorer/miniprojecttargetselector.cpp index 9ad218548bc..3fa03df9583 100644 --- a/src/plugins/projectexplorer/miniprojecttargetselector.cpp +++ b/src/plugins/projectexplorer/miniprojecttargetselector.cpp @@ -32,7 +32,6 @@ #include <coreplugin/modemanager.h> #include <QAction> -#include <QCollator> #include <QGuiApplication> #include <QItemDelegate> #include <QKeyEvent> @@ -49,8 +48,7 @@ using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { const int RunColumnWidth = 30; @@ -142,18 +140,8 @@ private: static bool compareItems(const TreeItem *ti1, const TreeItem *ti2) { - static const QCollator collator = [] { - QCollator collator; - collator.setNumericMode(true); - collator.setCaseSensitivity(Qt::CaseInsensitive); - return collator; - }(); - - const int result = collator.compare(static_cast<const GenericItem *>(ti1)->rawDisplayName(), - static_cast<const GenericItem *>(ti2)->rawDisplayName()); - if (result != 0) - return result < 0; - return ti1 < ti2; + return caseFriendlyCompare(static_cast<const GenericItem *>(ti1)->rawDisplayName(), + static_cast<const GenericItem *>(ti2)->rawDisplayName()) < 0; } class GenericModel : public TreeModel<GenericItem, GenericItem> @@ -500,7 +488,6 @@ SelectorView::SelectorView(QWidget *parent) : TreeView(parent) setFocusPolicy(Qt::NoFocus); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setAlternatingRowColors(false); - setUniformRowHeights(true); setIndentation(0); setFocusPolicy(Qt::WheelFocus); setItemDelegate(new TargetSelectorDelegate(this)); @@ -565,9 +552,9 @@ int SelectorView::padding() ///////// // KitAreaWidget ///////// -void doLayout(KitAspectWidget *widget, Layouting::LayoutItem &builder) +void doLayout(KitAspect *aspect, Layouting::LayoutItem &builder) { - widget->addToLayout(builder); + aspect->addToLayout(builder); } class KitAreaWidget : public QWidget @@ -586,8 +573,8 @@ public: { qDeleteAll(m_labels); m_labels.clear(); - qDeleteAll(m_widgets); - m_widgets.clear(); + qDeleteAll(m_kitAspects); + m_kitAspects.clear(); if (!k) return; @@ -595,13 +582,13 @@ public: delete layout(); Layouting::Grid grid; - for (KitAspect *aspect : KitManager::kitAspects()) { - if (k && k->isMutable(aspect->id())) { - KitAspectWidget *widget = aspect->createConfigWidget(k); + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) { + if (k && k->isMutable(factory->id())) { + KitAspect *aspect = factory->createKitAspect(k); + m_kitAspects << aspect; auto label = new QLabel(aspect->displayName()); m_labels << label; - m_widgets << widget; - grid.addItems({label, widget, Layouting::br}); + grid.addItems({label, aspect, Layouting::br}); } } grid.attachTo(this); @@ -609,7 +596,7 @@ public: m_kit = k; - setHidden(m_widgets.isEmpty()); + setHidden(m_kitAspects.isEmpty()); } private: @@ -619,12 +606,12 @@ private: return; bool addedMutables = false; - QList<const KitAspect *> knownList - = Utils::transform(m_widgets, &KitAspectWidget::kitInformation); + QList<const KitAspectFactory *> knownList + = Utils::transform(m_kitAspects, &KitAspect::factory); - for (KitAspect *aspect : KitManager::kitAspects()) { - const Utils::Id currentId = aspect->id(); - if (m_kit->isMutable(currentId) && !knownList.removeOne(aspect)) { + for (KitAspectFactory *factory : KitManager::kitAspectFactories()) { + const Utils::Id currentId = factory->id(); + if (m_kit->isMutable(currentId) && !knownList.removeOne(factory)) { addedMutables = true; break; } @@ -636,14 +623,14 @@ private: setKit(m_kit); } else { // Refresh all widgets if the number of mutable settings did not change - for (KitAspectWidget *w : std::as_const(m_widgets)) + for (KitAspect *w : std::as_const(m_kitAspects)) w->refresh(); } } Kit *m_kit = nullptr; QList<QWidget *> m_labels; - QList<KitAspectWidget *> m_widgets; + QList<KitAspect *> m_kitAspects; }; ///////// @@ -933,13 +920,12 @@ void MiniProjectTargetSelector::doLayout(bool keepSize) if (keepSize) { heightWithoutKitArea = height() - oldSummaryLabelY + 1; } else { - // Clamp the size of the listwidgets to be - // at least as high as the sidebar button - // and at most twice as high + // Clamp the size of the listwidgets to be at least as high as the sidebar button + // and at most half the height of the entire Qt Creator window. heightWithoutKitArea = summaryLabelHeight + qBound(alignedWithActionHeight, maxItemCount * 30 + bottomMargin + titleWidgetsHeight, - alignedWithActionHeight * 2); + Core::ICore::mainWindow()->height() / 2); } int titleY = summaryLabelY + summaryLabelHeight; @@ -1597,7 +1583,6 @@ void MiniProjectTargetSelector::switchToProjectsMode() hide(); } -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal #include <miniprojecttargetselector.moc> diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index d4d518de44d..dd5181990de 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -116,7 +116,7 @@ static QString platformName(MsvcToolChain::Platform t) { if (const MsvcPlatform *p = platformEntry(t)) return QLatin1String(p->name); - return QString(); + return {}; } static bool hostPrefersPlatform(MsvcToolChain::Platform platform) @@ -780,7 +780,7 @@ void MsvcToolChain::environmentModifications(QPromise<MsvcToolChain::GenerateEnv void MsvcToolChain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future) { - QObject::connect(&m_envModWatcher, &QFutureWatcher<GenerateEnvResult>::resultReadyAt, [&]() { + connect(&m_envModWatcher, &QFutureWatcher<GenerateEnvResult>::resultReadyAt, this, [this] { const GenerateEnvResult &result = m_envModWatcher.result(); if (result.error) { QString errorMessage = *result.error; @@ -978,39 +978,37 @@ Abis MsvcToolChain::supportedAbis() const return abis; } -QVariantMap MsvcToolChain::toMap() const +void MsvcToolChain::toMap(Store &data) const { - QVariantMap data = ToolChain::toMap(); - data.insert(QLatin1String(varsBatKeyC), m_vcvarsBat); + ToolChain::toMap(data); + data.insert(varsBatKeyC, m_vcvarsBat); if (!m_varsBatArg.isEmpty()) - data.insert(QLatin1String(varsBatArgKeyC), m_varsBatArg); - Utils::EnvironmentItem::sort(&m_environmentModifications); - data.insert(QLatin1String(environModsKeyC), - Utils::EnvironmentItem::toVariantList(m_environmentModifications)); - return data; + data.insert(varsBatArgKeyC, m_varsBatArg); + EnvironmentItem::sort(&m_environmentModifications); + data.insert(environModsKeyC, EnvironmentItem::toVariantList(m_environmentModifications)); } -bool MsvcToolChain::fromMap(const QVariantMap &data) +void MsvcToolChain::fromMap(const Store &data) { - if (!ToolChain::fromMap(data)) { + ToolChain::fromMap(data); + if (hasError()) { g_availableMsvcToolchains.removeOne(this); - return false; + return; } - m_vcvarsBat = QDir::fromNativeSeparators(data.value(QLatin1String(varsBatKeyC)).toString()); - m_varsBatArg = data.value(QLatin1String(varsBatArgKeyC)).toString(); + m_vcvarsBat = QDir::fromNativeSeparators(data.value(varsBatKeyC).toString()); + m_varsBatArg = data.value(varsBatArgKeyC).toString(); - m_environmentModifications = Utils::EnvironmentItem::itemsFromVariantList( - data.value(QLatin1String(environModsKeyC)).toList()); + m_environmentModifications = EnvironmentItem::itemsFromVariantList( + data.value(environModsKeyC).toList()); rescanForCompiler(); initEnvModWatcher(Utils::asyncRun(envModThreadPool(), &MsvcToolChain::environmentModifications, m_vcvarsBat, m_varsBatArg)); - const bool valid = !m_vcvarsBat.isEmpty() && targetAbi().isValid(); - if (!valid) + if (m_vcvarsBat.isEmpty() || !targetAbi().isValid()) { + reportError(); g_availableMsvcToolchains.removeOne(this); - - return valid; + } } std::unique_ptr<ToolChainConfigWidget> MsvcToolChain::createConfigurationWidget() @@ -1212,8 +1210,8 @@ void MsvcToolChain::rescanForCompiler() env.searchInPath(QLatin1String("cl.exe"), {}, [](const Utils::FilePath &name) { QDir dir(QDir::cleanPath(name.toFileInfo().absolutePath() + QStringLiteral("/.."))); do { - if (QFile::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat"))) - || QFile::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat")) + if (QFileInfo::exists(dir.absoluteFilePath(QStringLiteral("vcvarsall.bat"))) + || QFileInfo::exists(dir.absolutePath() + "/Auxiliary/Build/vcvarsall.bat")) return true; } while (dir.cdUp() && !dir.isRoot()); return false; @@ -1723,28 +1721,30 @@ QList<OutputLineParser *> ClangClToolChain::createOutputParsers() const return {new ClangClParser}; } -static inline QString llvmDirKey() +static Key llvmDirKey() { - return QStringLiteral("ProjectExplorer.ClangClToolChain.LlvmDir"); + return "ProjectExplorer.ClangClToolChain.LlvmDir"; } -QVariantMap ClangClToolChain::toMap() const +void ClangClToolChain::toMap(Store &data) const { - QVariantMap result = MsvcToolChain::toMap(); - result.insert(llvmDirKey(), m_clangPath.toString()); - return result; + MsvcToolChain::toMap(data); + data.insert(llvmDirKey(), m_clangPath.toString()); } -bool ClangClToolChain::fromMap(const QVariantMap &data) +void ClangClToolChain::fromMap(const Store &data) { - if (!MsvcToolChain::fromMap(data)) - return false; + MsvcToolChain::fromMap(data); + if (hasError()) + return; + const QString clangPath = data.value(llvmDirKey()).toString(); - if (clangPath.isEmpty()) - return false; - m_clangPath = FilePath::fromString(clangPath); + if (clangPath.isEmpty()) { + reportError(); + return; + } - return true; + m_clangPath = FilePath::fromString(clangPath); } std::unique_ptr<ToolChainConfigWidget> ClangClToolChain::createConfigurationWidget() @@ -2057,7 +2057,7 @@ Toolchains ClangClToolChainFactory::autoDetect(const ToolchainDetector &detector } const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment(); - const Utils::FilePath clangClPath = systemEnvironment.searchInPath("clang-cl"); + const Utils::FilePath clangClPath = systemEnvironment.searchInPath("clang-cl.exe"); if (!clangClPath.isEmpty()) results.append(detectClangClToolChainInPath(clangClPath, known, "")); @@ -2119,7 +2119,7 @@ std::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::E saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); if (!saver.finalize()) { qWarning("%s: %s", Q_FUNC_INFO, qPrintable(saver.errorString())); - return QString(); + return {}; } Utils::Process run; @@ -2160,13 +2160,13 @@ std::optional<QString> MsvcToolChain::generateEnvironmentSettings(const Utils::E const int start = stdOut.indexOf(marker); if (start == -1) { qWarning("Could not find start marker in stdout output."); - return QString(); + return {}; } const int end = stdOut.indexOf(marker, start + 1); if (end == -1) { qWarning("Could not find end marker in stdout output."); - return QString(); + return {}; } const QString output = stdOut.mid(start, end - start); diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index 68f8b9dcf19..cfe2d4d7d9e 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -46,8 +46,8 @@ public: QStringList suggestedMkspecList() const override; Abis supportedAbis() const override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; + void toMap(Utils::Store &data) const override; + void fromMap(const Utils::Store &data) override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; bool hostPrefersToolchain() const override; @@ -146,8 +146,8 @@ public: void addToEnvironment(Utils::Environment &env) const override; Utils::FilePath compilerCommand() const override; // FIXME: Remove QList<Utils::OutputLineParser *> createOutputParsers() const override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; + void toMap(Utils::Store &data) const override; + void fromMap(const Utils::Store &data) override; std::unique_ptr<ToolChainConfigWidget> createConfigurationWidget() override; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( const Utils::Environment &env) const override; diff --git a/src/plugins/projectexplorer/panelswidget.cpp b/src/plugins/projectexplorer/panelswidget.cpp index e327f05b542..e83eb9045c8 100644 --- a/src/plugins/projectexplorer/panelswidget.cpp +++ b/src/plugins/projectexplorer/panelswidget.cpp @@ -32,7 +32,7 @@ const int BELOW_CONTENTS_MARGIN = 16; // PanelsWidget /// -PanelsWidget::PanelsWidget(QWidget *parent) : QWidget(parent) +PanelsWidget::PanelsWidget(QWidget *parent, bool addStretch) : QWidget(parent) { m_root = new QWidget(nullptr); m_root->setFocusPolicy(Qt::NoFocus); @@ -53,7 +53,8 @@ PanelsWidget::PanelsWidget(QWidget *parent) : QWidget(parent) m_layout->setSpacing(0); topLayout->addLayout(m_layout); - topLayout->addStretch(100); + if (addStretch) + topLayout->addStretch(1); auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -72,7 +73,7 @@ PanelsWidget::PanelsWidget(const QString &displayName, QWidget *widget) } PanelsWidget::PanelsWidget(const QString &displayName, ProjectSettingsWidget *widget) - : PanelsWidget(nullptr) + : PanelsWidget(nullptr, !widget->expanding()) { addPropertiesPanel(displayName); addGlobalSettingsProperties(widget); diff --git a/src/plugins/projectexplorer/panelswidget.h b/src/plugins/projectexplorer/panelswidget.h index e5e117bab6b..3d5603d5400 100644 --- a/src/plugins/projectexplorer/panelswidget.h +++ b/src/plugins/projectexplorer/panelswidget.h @@ -19,7 +19,7 @@ class PROJECTEXPLORER_EXPORT PanelsWidget : public QWidget Q_OBJECT public: - explicit PanelsWidget(QWidget *parent = nullptr); + explicit PanelsWidget(QWidget *parent = nullptr, bool addStretch = true); PanelsWidget(const QString &displayName, QWidget *widget); PanelsWidget(const QString &displayName, ProjectSettingsWidget *widget); ~PanelsWidget() override; diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp index 331773fe7b6..4abf2d3f988 100644 --- a/src/plugins/projectexplorer/parseissuesdialog.cpp +++ b/src/plugins/projectexplorer/parseissuesdialog.cpp @@ -4,7 +4,7 @@ #include "parseissuesdialog.h" #include "ioutputparser.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitchooser.h" #include "kitmanager.h" #include "projectexplorerconstants.h" diff --git a/src/plugins/projectexplorer/processparameters.cpp b/src/plugins/projectexplorer/processparameters.cpp index 0b3d6d83c7a..d9338ad03a2 100644 --- a/src/plugins/projectexplorer/processparameters.cpp +++ b/src/plugins/projectexplorer/processparameters.cpp @@ -35,7 +35,7 @@ ProcessParameters::ProcessParameters() = default; */ void ProcessParameters::setCommandLine(const CommandLine &cmdLine) { - m_command = cmdLine; + m_runData.command = cmdLine; m_effectiveCommand.clear(); m_effectiveArguments.clear(); @@ -51,7 +51,7 @@ void ProcessParameters::setCommandLine(const CommandLine &cmdLine) void ProcessParameters::setWorkingDirectory(const FilePath &workingDirectory) { - m_workingDirectory = workingDirectory; + m_runData.workingDirectory = workingDirectory; m_effectiveWorkingDirectory.clear(); effectiveWorkingDirectory(); @@ -79,12 +79,12 @@ void ProcessParameters::setWorkingDirectory(const FilePath &workingDirectory) FilePath ProcessParameters::effectiveWorkingDirectory() const { if (m_effectiveWorkingDirectory.isEmpty()) { - m_effectiveWorkingDirectory = m_workingDirectory; - QString path = m_workingDirectory.path(); + m_effectiveWorkingDirectory = m_runData.workingDirectory; + QString path = m_runData.workingDirectory.path(); if (m_macroExpander) path = m_macroExpander->expand(path); - m_effectiveWorkingDirectory = - m_effectiveWorkingDirectory.withNewPath(QDir::cleanPath(m_environment.expandVariables(path))); + m_effectiveWorkingDirectory = m_effectiveWorkingDirectory.withNewPath( + QDir::cleanPath(m_runData.environment.expandVariables(path))); } return m_effectiveWorkingDirectory; } @@ -96,15 +96,15 @@ FilePath ProcessParameters::effectiveWorkingDirectory() const FilePath ProcessParameters::effectiveCommand() const { if (m_effectiveCommand.isEmpty()) { - FilePath cmd = m_command.executable(); + FilePath cmd = m_runData.command.executable(); if (m_macroExpander) cmd = m_macroExpander->expand(cmd); if (cmd.needsDevice()) { // Assume this is already good. FIXME: It is possibly not, so better fix searchInPath. m_effectiveCommand = cmd; } else { - m_effectiveCommand = m_environment.searchInPath(cmd.toString(), - {effectiveWorkingDirectory()}); + m_effectiveCommand = m_runData.environment.searchInPath(cmd.toString(), + {effectiveWorkingDirectory()}); } m_commandMissing = m_effectiveCommand.isEmpty(); if (m_commandMissing) @@ -126,7 +126,7 @@ bool ProcessParameters::commandMissing() const QString ProcessParameters::effectiveArguments() const { if (m_effectiveArguments.isEmpty()) { - m_effectiveArguments = m_command.arguments(); + m_effectiveArguments = m_runData.command.arguments(); if (m_macroExpander) m_effectiveArguments = m_macroExpander->expand(m_effectiveArguments); } @@ -135,7 +135,7 @@ QString ProcessParameters::effectiveArguments() const QString ProcessParameters::prettyCommand() const { - QString cmd = m_command.executable().toString(); + QString cmd = m_runData.command.executable().toString(); if (m_macroExpander) cmd = m_macroExpander->expand(cmd); return FilePath::fromString(cmd).fileName(); @@ -143,11 +143,11 @@ QString ProcessParameters::prettyCommand() const QString ProcessParameters::prettyArguments() const { - QString margs = effectiveArguments(); - FilePath workDir = effectiveWorkingDirectory(); + const QString margs = effectiveArguments(); + const FilePath workDir = effectiveWorkingDirectory(); ProcessArgs::SplitError err; - ProcessArgs args = - ProcessArgs::prepareArgs(margs, &err, HostOsInfo::hostOs(), &m_environment, &workDir); + const ProcessArgs args = ProcessArgs::prepareArgs(margs, &err, HostOsInfo::hostOs(), + &m_runData.environment, &workDir); if (err != ProcessArgs::SplitOk) return margs; // Sorry, too complex - just fall back. return args.toString(); diff --git a/src/plugins/projectexplorer/processparameters.h b/src/plugins/projectexplorer/processparameters.h index 23ebad028dc..ce418b65c28 100644 --- a/src/plugins/projectexplorer/processparameters.h +++ b/src/plugins/projectexplorer/processparameters.h @@ -5,9 +5,7 @@ #include "projectexplorer_export.h" -#include <utils/commandline.h> -#include <utils/environment.h> -#include <utils/fileutils.h> +#include <utils/processinterface.h> namespace Utils { class MacroExpander; @@ -22,13 +20,13 @@ public: ProcessParameters(); void setCommandLine(const Utils::CommandLine &cmdLine); - Utils::CommandLine command() const { return m_command; } + Utils::CommandLine command() const { return m_runData.command; } void setWorkingDirectory(const Utils::FilePath &workingDirectory); - Utils::FilePath workingDirectory() const { return m_workingDirectory; } + Utils::FilePath workingDirectory() const { return m_runData.workingDirectory; } - void setEnvironment(const Utils::Environment &env) { m_environment = env; } - Utils::Environment environment() const { return m_environment; } + void setEnvironment(const Utils::Environment &env) { m_runData.environment = env; } + Utils::Environment environment() const { return m_runData.environment; } void setMacroExpander(Utils::MacroExpander *mx) { m_macroExpander = mx; } Utils::MacroExpander *macroExpander() const { return m_macroExpander; } @@ -49,9 +47,7 @@ public: QString summaryInWorkdir(const QString &displayName) const; private: - Utils::FilePath m_workingDirectory; - Utils::CommandLine m_command; - Utils::Environment m_environment; + Utils::ProcessRunData m_runData; Utils::MacroExpander *m_macroExpander = nullptr; mutable Utils::FilePath m_effectiveWorkingDirectory; diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index a34ff10b611..782eadf91df 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -11,13 +11,11 @@ #include "projectexplorertr.h" #include <utils/aspects.h> -#include <utils/fileutils.h> #include <utils/outputformatter.h> using namespace Utils; -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { const char PROCESS_COMMAND_KEY[] = "ProjectExplorer.ProcessStep.Command"; const char PROCESS_WORKINGDIRECTORY_KEY[] = "ProjectExplorer.ProcessStep.WorkingDirectory"; @@ -26,60 +24,58 @@ const char PROCESS_ARGUMENTS_KEY[] = "ProjectExplorer.ProcessStep.Arguments"; class ProcessStep final : public AbstractProcessStep { public: - ProcessStep(BuildStepList *bsl, Id id); + ProcessStep(BuildStepList *bsl, Id id) + : AbstractProcessStep(bsl, id) + { + command.setSettingsKey(PROCESS_COMMAND_KEY); + command.setLabelText(Tr::tr("Command:")); + command.setExpectedKind(PathChooser::Command); + command.setHistoryCompleter("PE.ProcessStepCommand.History"); - void setupOutputFormatter(OutputFormatter *formatter) final; + arguments.setSettingsKey(PROCESS_ARGUMENTS_KEY); + arguments.setDisplayStyle(StringAspect::LineEditDisplay); + arguments.setLabelText(Tr::tr("Arguments:")); + + workingDirectory.setSettingsKey(PROCESS_WORKINGDIRECTORY_KEY); + workingDirectory.setValue(QString(Constants::DEFAULT_WORKING_DIR)); + workingDirectory.setLabelText(Tr::tr("Working directory:")); + workingDirectory.setExpectedKind(PathChooser::Directory); + + setWorkingDirectoryProvider([this] { + const FilePath workingDir = workingDirectory(); + if (workingDir.isEmpty()) + return FilePath::fromString(fallbackWorkingDirectory()); + return workingDir; + }); + + setCommandLineProvider([this] { + return CommandLine{command(), arguments(), CommandLine::Raw}; + }); + + setSummaryUpdater([this] { + QString display = displayName(); + if (display.isEmpty()) + display = Tr::tr("Custom Process Step"); + ProcessParameters param; + setupProcessParameters(¶m); + return param.summary(display); + }); + + addMacroExpander(); + } + +private: + void setupOutputFormatter(OutputFormatter *formatter) final + { + formatter->addLineParsers(kit()->createOutputParsers()); + AbstractProcessStep::setupOutputFormatter(formatter); + } + + FilePathAspect command{this}; + StringAspect arguments{this}; + FilePathAspect workingDirectory{this}; }; -ProcessStep::ProcessStep(BuildStepList *bsl, Id id) - : AbstractProcessStep(bsl, id) -{ - auto command = addAspect<FilePathAspect>(); - command->setSettingsKey(PROCESS_COMMAND_KEY); - command->setLabelText(Tr::tr("Command:")); - command->setExpectedKind(PathChooser::Command); - command->setHistoryCompleter("PE.ProcessStepCommand.History"); - - auto arguments = addAspect<StringAspect>(); - arguments->setSettingsKey(PROCESS_ARGUMENTS_KEY); - arguments->setDisplayStyle(StringAspect::LineEditDisplay); - arguments->setLabelText(Tr::tr("Arguments:")); - - auto workingDirectory = addAspect<FilePathAspect>(); - workingDirectory->setSettingsKey(PROCESS_WORKINGDIRECTORY_KEY); - workingDirectory->setValue(Constants::DEFAULT_WORKING_DIR); - workingDirectory->setLabelText(Tr::tr("Working directory:")); - workingDirectory->setExpectedKind(PathChooser::Directory); - - setWorkingDirectoryProvider([this, workingDirectory] { - const FilePath workingDir = workingDirectory->filePath(); - if (workingDir.isEmpty()) - return FilePath::fromString(fallbackWorkingDirectory()); - return workingDir; - }); - - setCommandLineProvider([command, arguments] { - return CommandLine{command->filePath(), arguments->value(), CommandLine::Raw}; - }); - - setSummaryUpdater([this] { - QString display = displayName(); - if (display.isEmpty()) - display = Tr::tr("Custom Process Step"); - ProcessParameters param; - setupProcessParameters(¶m); - return param.summary(display); - }); - - addMacroExpander(); -} - -void ProcessStep::setupOutputFormatter(OutputFormatter *formatter) -{ - formatter->addLineParsers(kit()->createOutputParsers()); - AbstractProcessStep::setupOutputFormatter(formatter); -} - // ProcessStepFactory ProcessStepFactory::ProcessStepFactory() @@ -89,5 +85,4 @@ ProcessStepFactory::ProcessStepFactory() setDisplayName(Tr::tr("Custom Process Step")); } -} // Internal -} // ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/processstep.h b/src/plugins/projectexplorer/processstep.h index 40384383057..04e9b7183ce 100644 --- a/src/plugins/projectexplorer/processstep.h +++ b/src/plugins/projectexplorer/processstep.h @@ -5,8 +5,7 @@ #include "buildstep.h" -namespace ProjectExplorer { -namespace Internal { +namespace ProjectExplorer::Internal { class ProcessStepFactory final : public BuildStepFactory { @@ -14,5 +13,4 @@ public: ProcessStepFactory(); }; -} // namespace Internal -} // namespace ProjectExplorer +} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index d917672c6b9..20c65655627 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -10,7 +10,7 @@ #include "editorconfiguration.h" #include "environmentaspect.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "msvctoolchain.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -24,13 +24,13 @@ #include "toolchainmanager.h" #include "userfileaccessor.h" -#include <coreplugin/idocument.h> #include <coreplugin/documentmanager.h> +#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> +#include <coreplugin/idocument.h> #include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> -#include <coreplugin/editormanager/documentmodel.h> #include <projectexplorer/buildmanager.h> #include <projectexplorer/kitmanager.h> @@ -184,7 +184,7 @@ public: Target *m_activeTarget = nullptr; EditorConfiguration m_editorConfiguration; Context m_projectLanguages; - QVariantMap m_pluginSettings; + Store m_pluginSettings; std::unique_ptr<Internal::UserFileAccessor> m_accessor; QHash<Id, QPair<QString, std::function<void()>>> m_generators; @@ -194,7 +194,7 @@ public: FilePath m_rootProjectDirectory; mutable QVector<const Node *> m_sortedNodeList; - QVariantMap m_extraData; + Store m_extraData; }; ProjectPrivate::~ProjectPrivate() @@ -634,15 +634,24 @@ void Project::saveSettings() emit aboutToSaveSettings(); if (!d->m_accessor) d->m_accessor = std::make_unique<Internal::UserFileAccessor>(this); - if (!targets().isEmpty()) - d->m_accessor->saveSettings(toMap(), ICore::dialogParent()); + if (!targets().isEmpty()) { + Store map; + toMap(map); + d->m_accessor->saveSettings(map, ICore::dialogParent()); + } } Project::RestoreResult Project::restoreSettings(QString *errorMessage) { + if (!KitManager::waitForLoaded()) { + if (errorMessage) + *errorMessage = Tr::tr("Could not load kits in a reasonable amount of time."); + return RestoreResult::Error; + } + if (!d->m_accessor) d->m_accessor = std::make_unique<Internal::UserFileAccessor>(this); - QVariantMap map(d->m_accessor->restoreSettings(ICore::dialogParent())); + Store map(d->m_accessor->restoreSettings(ICore::dialogParent())); RestoreResult result = fromMap(map, errorMessage); if (result == RestoreResult::Ok) emit settingsLoaded(); @@ -678,7 +687,7 @@ FilePaths Project::files(const NodeMatcher &filter) const } /*! - Serializes all data into a QVariantMap. + Serializes all data into a Store. This map is then saved in the .user file of the project. Just put all your data into the map. @@ -688,21 +697,18 @@ FilePaths Project::files(const NodeMatcher &filter) const creating new build configurations. */ -QVariantMap Project::toMap() const +void Project::toMap(Store &map) const { const QList<Target *> ts = targets(); - QVariantMap map; - map.insert(QLatin1String(ACTIVE_TARGET_KEY), ts.indexOf(d->m_activeTarget)); - map.insert(QLatin1String(TARGET_COUNT_KEY), ts.size()); + map.insert(ACTIVE_TARGET_KEY, ts.indexOf(d->m_activeTarget)); + map.insert(TARGET_COUNT_KEY, ts.size()); for (int i = 0; i < ts.size(); ++i) - map.insert(QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(i), ts.at(i)->toMap()); + map.insert(numberedKey(TARGET_KEY_PREFIX, i), variantFromStore(ts.at(i)->toMap())); - map.insert(QLatin1String(EDITOR_SETTINGS_KEY), d->m_editorConfiguration.toMap()); + map.insert(EDITOR_SETTINGS_KEY, variantFromStore(d->m_editorConfiguration.toMap())); if (!d->m_pluginSettings.isEmpty()) - map.insert(QLatin1String(PLUGIN_SETTINGS_KEY), d->m_pluginSettings); - - return map; + map.insert(PLUGIN_SETTINGS_KEY, variantFromStore(d->m_pluginSettings)); } /*! @@ -725,7 +731,7 @@ FilePath Project::projectDirectory() const FilePath Project::projectDirectory(const FilePath &top) { if (top.isEmpty()) - return FilePath(); + return {}; return top.absolutePath(); } @@ -764,22 +770,22 @@ ContainerNode *Project::containerNode() const return d->m_containerNode.get(); } -Project::RestoreResult Project::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult Project::fromMap(const Store &map, QString *errorMessage) { Q_UNUSED(errorMessage) - if (map.contains(QLatin1String(EDITOR_SETTINGS_KEY))) { - QVariantMap values(map.value(QLatin1String(EDITOR_SETTINGS_KEY)).toMap()); + if (map.contains(EDITOR_SETTINGS_KEY)) { + Store values = storeFromVariant(map.value(EDITOR_SETTINGS_KEY)); d->m_editorConfiguration.fromMap(values); } - if (map.contains(QLatin1String(PLUGIN_SETTINGS_KEY))) - d->m_pluginSettings = map.value(QLatin1String(PLUGIN_SETTINGS_KEY)).toMap(); + if (map.contains(PLUGIN_SETTINGS_KEY)) + d->m_pluginSettings = storeFromVariant(map.value(PLUGIN_SETTINGS_KEY)); bool ok; - int maxI(map.value(QLatin1String(TARGET_COUNT_KEY), 0).toInt(&ok)); + int maxI(map.value(TARGET_COUNT_KEY, 0).toInt(&ok)); if (!ok || maxI < 0) maxI = 0; - int active(map.value(QLatin1String(ACTIVE_TARGET_KEY), 0).toInt(&ok)); + int active = map.value(ACTIVE_TARGET_KEY, 0).toInt(&ok); if (!ok || active < 0 || active >= maxI) active = 0; @@ -799,13 +805,13 @@ Project::RestoreResult Project::fromMap(const QVariantMap &map, QString *errorMe return RestoreResult::Ok; } -void Project::createTargetFromMap(const QVariantMap &map, int index) +void Project::createTargetFromMap(const Store &map, int index) { - const QString key = QString::fromLatin1(TARGET_KEY_PREFIX) + QString::number(index); + const Key key = numberedKey(TARGET_KEY_PREFIX, index); if (!map.contains(key)) return; - const QVariantMap targetMap = map.value(key).toMap(); + const Store targetMap = storeFromVariant(map.value(key)); Id id = idFromMap(targetMap); if (target(id)) { @@ -815,7 +821,7 @@ void Project::createTargetFromMap(const QVariantMap &map, int index) } Kit *k = KitManager::kit(id); - if (!k) { + if (!k && !ICore::isQtDesignStudio()) { Id deviceTypeId = Id::fromSetting(targetMap.value(Target::deviceTypeKey())); if (!deviceTypeId.isValid()) deviceTypeId = Constants::DESKTOP_DEVICE_TYPE; @@ -978,12 +984,12 @@ Context Project::projectLanguages() const return d->m_projectLanguages; } -QVariant Project::namedSettings(const QString &name) const +QVariant Project::namedSettings(const Key &name) const { return d->m_pluginSettings.value(name); } -void Project::setNamedSettings(const QString &name, const QVariant &value) +void Project::setNamedSettings(const Key &name, const QVariant &value) { if (value.isNull()) d->m_pluginSettings.remove(name); @@ -1075,12 +1081,12 @@ void Project::setCanBuildProducts() d->m_canBuildProducts = true; } -void Project::setExtraData(const QString &key, const QVariant &data) +void Project::setExtraData(const Key &key, const QVariant &data) { d->m_extraData.insert(key, data); } -QVariant Project::extraData(const QString &key) const +QVariant Project::extraData(const Key &key) const { return d->m_extraData.value(key); } @@ -1232,10 +1238,10 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix, //: %1 is something like "Active project" ::PE::Tr::tr("%1: Variables in the active build environment.") .arg(descriptor), - [bcGetter](const QString &var) { + [bcGetter](const QString &var) -> QString { if (BuildConfiguration *const bc = bcGetter()) return bc->environment().expandedValueForKey(var); - return QString(); + return {}; }); expander->registerVariable(fullPrefix + "RunConfig:Name", @@ -1245,7 +1251,7 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix, [rcGetter]() -> QString { if (const RunConfiguration *const rc = rcGetter()) return rc->displayName(); - return QString(); + return {}; }); expander->registerFileVariables(fullPrefix + "RunConfig:Executable", //: %1 is something like "Active project" @@ -1262,25 +1268,25 @@ void Project::addVariablesToMacroExpander(const QByteArray &prefix, ::PE::Tr::tr( "%1: Variables in the environment of the active run configuration.") .arg(descriptor), - [rcGetter](const QString &var) { + [rcGetter](const QString &var) -> QString { if (const RunConfiguration *const rc = rcGetter()) { if (const auto envAspect = rc->aspect<EnvironmentAspect>()) return envAspect->environment().expandedValueForKey(var); } - return QString(); + return {}; }); expander->registerVariable(fullPrefix + "RunConfig:WorkingDir", //: %1 is something like "Active project" ::PE::Tr::tr( "%1: Working directory of the active run configuration.") .arg(descriptor), - [rcGetter] { + [rcGetter]() -> QString { if (const RunConfiguration *const rc = rcGetter()) { if (const auto wdAspect = rc->aspect<WorkingDirectoryAspect>()) return wdAspect->workingDirectory().toString(); } - return QString(); + return {}; }); } @@ -1494,8 +1500,10 @@ void ProjectExplorerPlugin::testProject_multipleBuildConfigs() QVERIFY(tempDir->isValid()); const FilePath projectDir = FilePath::fromString(tempDir->path() + "/generic-project"); const auto copyResult = FilePath(":/projectexplorer/testdata/generic-project").copyRecursively(projectDir); + if (!copyResult) + qDebug() << copyResult.error(); + QVERIFY(copyResult); - QVERIFY2(copyResult, qPrintable(copyResult.error())); const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files | QDir::Dirs); for (const QFileInfo &f : files) QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser); @@ -1556,7 +1564,9 @@ void ProjectExplorerPlugin::testSourceToBinaryMapping() if (!projectDir.exists()) { const auto result = FilePath(":/projectexplorer/testdata/multi-target-project") .copyRecursively(projectDir); - QVERIFY2(result, qPrintable(result.error())); + if (!result) + qDebug() << result.error(); + QVERIFY(result); const QFileInfoList files = QDir(projectDir.toString()).entryInfoList(QDir::Files); for (const QFileInfo &f : files) QFile(f.absoluteFilePath()).setPermissions(f.permissions() | QFile::WriteUser); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 832fc3c9423..40b26d6faa0 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -12,6 +12,7 @@ #include <utils/environmentfwd.h> #include <utils/filepath.h> +#include <utils/store.h> #include <QFileSystemModel> @@ -111,13 +112,13 @@ public: const NodeMatcher &extraMatcher = {}) const; Utils::FilePaths binariesForSourceFile(const Utils::FilePath &sourceFile) const; - virtual QVariantMap toMap() const; + virtual void toMap(Utils::Store &map) const; Core::Context projectContext() const; Core::Context projectLanguages() const; - QVariant namedSettings(const QString &name) const; - void setNamedSettings(const QString &name, const QVariant &value); + QVariant namedSettings(const Utils::Key &name) const; + void setNamedSettings(const Utils::Key &name, const QVariant &value); void setAdditionalEnvironment(const Utils::EnvironmentItems &envItems); Utils::EnvironmentItems additionalEnvironment() const; @@ -155,8 +156,8 @@ public: void setDisplayName(const QString &name); void setProjectLanguage(Utils::Id id, bool enabled); - void setExtraData(const QString &key, const QVariant &data); - QVariant extraData(const QString &key) const; + void setExtraData(const Utils::Key &key, const QVariant &data); + QVariant extraData(const Utils::Key &key) const; QStringList availableQmlPreviewTranslations(QString *errorMessage); @@ -204,8 +205,8 @@ signals: #endif protected: - virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage); - void createTargetFromMap(const QVariantMap &map, int index); + virtual RestoreResult fromMap(const Utils::Store &map, QString *errorMessage); + void createTargetFromMap(const Utils::Store &map, int index); virtual bool setupTarget(Target *t); void setCanBuildProducts(); diff --git a/src/plugins/projectexplorer/projectcommentssettings.cpp b/src/plugins/projectexplorer/projectcommentssettings.cpp new file mode 100644 index 00000000000..b07cea9b290 --- /dev/null +++ b/src/plugins/projectexplorer/projectcommentssettings.cpp @@ -0,0 +1,134 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "projectcommentssettings.h" + +#include "project.h" + +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> + +#include <QCheckBox> +#include <QVBoxLayout> + +using namespace TextEditor; +using namespace Utils; + +namespace ProjectExplorer { +namespace Internal { + +const char kUseGlobalKey[] = "UseGlobalKey"; + +ProjectCommentsSettings::ProjectCommentsSettings(ProjectExplorer::Project *project) + : m_project(project) +{ + loadSettings(); +} + +CommentsSettings::Data ProjectCommentsSettings::settings() const +{ + return m_useGlobalSettings ? CommentsSettings::data() : m_customSettings; +} + +void ProjectCommentsSettings::setSettings(const CommentsSettings::Data &settings) +{ + if (settings == m_customSettings) + return; + m_customSettings = settings; + saveSettings(); + emit TextEditorSettings::instance()->commentsSettingsChanged(); +} + +void ProjectCommentsSettings::setUseGlobalSettings(bool useGlobal) +{ + if (useGlobal == m_useGlobalSettings) + return; + m_useGlobalSettings = useGlobal; + saveSettings(); + emit TextEditorSettings::instance()->commentsSettingsChanged(); +} + +void ProjectCommentsSettings::loadSettings() +{ + if (!m_project) + return; + + const QVariant entry = m_project->namedSettings(CommentsSettings::mainSettingsKey()); + if (!entry.isValid()) + return; + + const Store data = storeFromVariant(entry); + m_useGlobalSettings = data.value(kUseGlobalKey, true).toBool(); + m_customSettings.enableDoxygen = data.value(CommentsSettings::enableDoxygenSettingsKey(), + m_customSettings.enableDoxygen).toBool(); + m_customSettings.generateBrief = data.value(CommentsSettings::generateBriefSettingsKey(), + m_customSettings.generateBrief).toBool(); + m_customSettings.leadingAsterisks = data.value(CommentsSettings::leadingAsterisksSettingsKey(), + m_customSettings.leadingAsterisks).toBool(); + m_customSettings.commandPrefix = static_cast<CommentsSettings::CommandPrefix>( + data.value(CommentsSettings::commandPrefixKey(), + int(m_customSettings.commandPrefix)).toInt()); +} + +void ProjectCommentsSettings::saveSettings() +{ + if (!m_project) + return; + + // Optimization: Don't save anything if the user never switched away from the default. + if (m_useGlobalSettings && !m_project->namedSettings + (CommentsSettings::mainSettingsKey()).isValid()) { + return; + } + + Store data; + data.insert(kUseGlobalKey, m_useGlobalSettings); + data.insert(CommentsSettings::enableDoxygenSettingsKey(), m_customSettings.enableDoxygen); + data.insert(CommentsSettings::generateBriefSettingsKey(), m_customSettings.generateBrief); + data.insert(CommentsSettings::leadingAsterisksSettingsKey(), m_customSettings.leadingAsterisks); + data.insert(CommentsSettings::commandPrefixKey(), int(m_customSettings.commandPrefix)); + m_project->setNamedSettings(CommentsSettings::mainSettingsKey(), variantFromStore(data)); +} + +class ProjectCommentsSettingsWidget::Private +{ +public: + Private(ProjectExplorer::Project *project) : settings(project) {} + + ProjectCommentsSettings settings; + CommentsSettingsWidget widget{settings.settings()}; + QCheckBox useGlobalSettingsCheckBox; +}; + +ProjectCommentsSettingsWidget::ProjectCommentsSettingsWidget(ProjectExplorer::Project *project) + : d(new Private(project)) +{ + setGlobalSettingsId(TextEditor::Constants::TEXT_EDITOR_COMMENTS_SETTINGS); + const auto layout = new QVBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(&d->widget); + + const auto updateGlobalSettingsCheckBox = [this] { + setUseGlobalSettingsCheckBoxEnabled(true); + setUseGlobalSettings(d->settings.useGlobalSettings()); + d->widget.setEnabled(!useGlobalSettings()); + }; + updateGlobalSettingsCheckBox(); + connect(TextEditorSettings::instance(), &TextEditorSettings::commentsSettingsChanged, + this, updateGlobalSettingsCheckBox); + connect(this, &ProjectSettingsWidget::useGlobalSettingsChanged, this, + [this](bool checked) { + d->widget.setEnabled(!checked); + d->settings.setUseGlobalSettings(checked); + if (!checked) + d->settings.setSettings(d->widget.settingsData()); + }); + connect(&d->widget, &CommentsSettingsWidget::settingsChanged, this, [this] { + d->settings.setSettings(d->widget.settingsData()); + }); +} + +ProjectCommentsSettingsWidget::~ProjectCommentsSettingsWidget() { delete d; } + +} // namespace Internal +} // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectcommentssettings.h b/src/plugins/projectexplorer/projectcommentssettings.h new file mode 100644 index 00000000000..5beb4f27a7a --- /dev/null +++ b/src/plugins/projectexplorer/projectcommentssettings.h @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "projectsettingswidget.h" + +#include <texteditor/commentssettings.h> + +namespace ProjectExplorer { +class Project; + +namespace Internal { + +class ProjectCommentsSettings +{ +public: + // Passing a null ptr is allowed and yields the global settings, so you can use + // this class transparently for both cases. + ProjectCommentsSettings(Project *project); + + TextEditor::CommentsSettings::Data settings() const; + void setSettings(const TextEditor::CommentsSettings::Data &settings); + bool useGlobalSettings() const { return m_useGlobalSettings; } + void setUseGlobalSettings(bool useGlobal); + +private: + void loadSettings(); + void saveSettings(); + + ProjectExplorer::Project * const m_project; + TextEditor::CommentsSettings::Data m_customSettings; + bool m_useGlobalSettings = true; +}; + +class ProjectCommentsSettingsWidget : public ProjectSettingsWidget +{ +public: + ProjectCommentsSettingsWidget(ProjectExplorer::Project *project); + ~ProjectCommentsSettingsWidget(); + +private: + class Private; + Private * const d; +}; + +} // namespace Internal +} // namespace ProjectExplorer + diff --git a/src/plugins/projectexplorer/projectconfiguration.cpp b/src/plugins/projectexplorer/projectconfiguration.cpp index 4aad7c4a392..33285507453 100644 --- a/src/plugins/projectexplorer/projectconfiguration.cpp +++ b/src/plugins/projectexplorer/projectconfiguration.cpp @@ -3,7 +3,6 @@ #include "projectconfiguration.h" -#include "kitinformation.h" #include "target.h" #include <utils/algorithm.h> @@ -17,20 +16,13 @@ const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayNam // ProjectConfiguration -ProjectConfiguration::ProjectConfiguration(QObject *parent, Utils::Id id) - : AspectContainer(parent) +ProjectConfiguration::ProjectConfiguration(Target *target, Id id) + : m_target(target) , m_id(id) { - QTC_CHECK(parent); + QTC_CHECK(target); QTC_CHECK(id.isValid()); setObjectName(id.toString()); - - for (QObject *obj = this; obj; obj = obj->parent()) { - m_target = qobject_cast<Target *>(obj); - if (m_target) - break; - } - QTC_CHECK(m_target); } ProjectConfiguration::~ProjectConfiguration() = default; @@ -45,14 +37,14 @@ Kit *ProjectConfiguration::kit() const return m_target->kit(); } -Utils::Id ProjectConfiguration::id() const +Id ProjectConfiguration::id() const { return m_id; } -QString ProjectConfiguration::settingsIdKey() +Key ProjectConfiguration::settingsIdKey() { - return QString(CONFIGURATION_ID_KEY); + return CONFIGURATION_ID_KEY; } void ProjectConfiguration::setDisplayName(const QString &name) @@ -80,14 +72,12 @@ QString ProjectConfiguration::toolTip() const return m_toolTip; } -QVariantMap ProjectConfiguration::toMap() const +void ProjectConfiguration::toMap(Store &map) const { QTC_CHECK(m_id.isValid()); - QVariantMap map; - map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting()); + map.insert(CONFIGURATION_ID_KEY, m_id.toSetting()); m_displayName.toMap(map, DISPLAY_NAME_KEY); AspectContainer::toMap(map); - return map; } Target *ProjectConfiguration::target() const @@ -95,21 +85,20 @@ Target *ProjectConfiguration::target() const return m_target; } -bool ProjectConfiguration::fromMap(const QVariantMap &map) +void ProjectConfiguration::fromMap(const Store &map) { - Utils::Id id = Utils::Id::fromSetting(map.value(QLatin1String(CONFIGURATION_ID_KEY))); + Id id = Id::fromSetting(map.value(CONFIGURATION_ID_KEY)); // Note: This is only "startsWith", not ==, as RunConfigurations currently still // mangle in their build keys. - QTC_ASSERT(id.toString().startsWith(m_id.toString()), return false); + QTC_ASSERT(id.name().startsWith(m_id.name()), reportError(); return); m_displayName.fromMap(map, DISPLAY_NAME_KEY); AspectContainer::fromMap(map); - return true; } -Id ProjectExplorer::idFromMap(const QVariantMap &map) +Id ProjectExplorer::idFromMap(const Store &map) { - return Id::fromSetting(map.value(QLatin1String(CONFIGURATION_ID_KEY))); + return Id::fromSetting(map.value(CONFIGURATION_ID_KEY)); } QString ProjectConfiguration::expandedDisplayName() const diff --git a/src/plugins/projectexplorer/projectconfiguration.h b/src/plugins/projectexplorer/projectconfiguration.h index 790d9ebf6b5..329c660916c 100644 --- a/src/plugins/projectexplorer/projectconfiguration.h +++ b/src/plugins/projectexplorer/projectconfiguration.h @@ -8,11 +8,9 @@ #include <utils/aspects.h> #include <utils/displayname.h> #include <utils/id.h> +#include <utils/store.h> -#include <QObject> #include <QPointer> -#include <QString> -#include <QVariantMap> #include <QWidget> namespace ProjectExplorer { @@ -26,7 +24,7 @@ class PROJECTEXPLORER_EXPORT ProjectConfiguration : public Utils::AspectContaine Q_OBJECT protected: - explicit ProjectConfiguration(QObject *parent, Utils::Id id); + explicit ProjectConfiguration(Target *target, Utils::Id id); public: ~ProjectConfiguration() override; @@ -38,21 +36,24 @@ public: bool usesDefaultDisplayName() const { return m_displayName.usesDefaultValue(); } void setDisplayName(const QString &name); void setDefaultDisplayName(const QString &name); + void forceDisplayNameSerialization() { m_displayName.forceSerialization(); } void setToolTip(const QString &text); QString toolTip() const; - // Note: Make sure subclasses call the superclasses' fromMap() function! - virtual bool fromMap(const QVariantMap &map); + void reportError() { m_hasError = true; } + bool hasError() const { return m_hasError; } + // Note: Make sure subclasses call the superclasses' fromMap() function! + virtual void fromMap(const Utils::Store &map) override; // Note: Make sure subclasses call the superclasses' toMap() function! - virtual QVariantMap toMap() const; + virtual void toMap(Utils::Store &map) const override; Target *target() const; Project *project() const; Kit *kit() const; - static QString settingsIdKey(); + static Utils::Key settingsIdKey(); signals: void displayNameChanged(); @@ -63,9 +64,10 @@ private: const Utils::Id m_id; Utils::DisplayName m_displayName; QString m_toolTip; + bool m_hasError = false; }; // helper function: -PROJECTEXPLORER_EXPORT Utils::Id idFromMap(const QVariantMap &map); +PROJECTEXPLORER_EXPORT Utils::Id idFromMap(const Utils::Store &map); } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/projectconfigurationmodel.cpp b/src/plugins/projectexplorer/projectconfigurationmodel.cpp index 61e608dc4b4..f2b731a6e1a 100644 --- a/src/plugins/projectexplorer/projectconfigurationmodel.cpp +++ b/src/plugins/projectexplorer/projectconfigurationmodel.cpp @@ -5,9 +5,10 @@ #include "buildconfiguration.h" #include "deployconfiguration.h" +#include "projectconfiguration.h" +#include "projectexplorertr.h" #include "runconfiguration.h" #include "target.h" -#include "projectconfiguration.h" #include <utils/algorithm.h> #include <utils/stringutils.h> @@ -30,6 +31,9 @@ static bool isOrderedBefore(const ProjectConfiguration *a, const ProjectConfigur ProjectConfigurationModel::ProjectConfigurationModel(Target *target) : m_target(target) { + connect(target, &Target::runConfigurationsUpdated, this, [this] { + emit dataChanged(index(0, 0), index(rowCount(), 0)); + }); } int ProjectConfigurationModel::rowCount(const QModelIndex &parent) const @@ -87,13 +91,17 @@ void ProjectConfigurationModel::displayNameChanged(ProjectConfiguration *pc) QVariant ProjectConfigurationModel::data(const QModelIndex &index, int role) const { - if (role == Qt::DisplayRole) { - const int row = index.row(); - if (row < m_projectConfigurations.size()) - return m_projectConfigurations.at(row)->expandedDisplayName(); - } + if (index.row() >= m_projectConfigurations.size()) + return {}; - return QVariant(); + if (role == Qt::DisplayRole) { + ProjectConfiguration * const config = m_projectConfigurations.at(index.row()); + QString displayName = config->expandedDisplayName(); + if (const auto rc = qobject_cast<RunConfiguration *>(config); rc && !rc->hasCreator()) + displayName += QString(" [%1]").arg(Tr::tr("unavailable")); + return displayName; + } + return {}; } ProjectConfiguration *ProjectConfigurationModel::projectConfigurationAt(int i) const diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index d313cb433de..98c8690900c 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -47,14 +47,15 @@ #include "jsonwizard/jsonwizardgeneratorfactory.h" #include "jsonwizard/jsonwizardpagefactory_p.h" #include "kitfeatureprovider.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitmanager.h" -#include "kitoptionspage.h" #include "miniprojecttargetselector.h" #include "namedwidget.h" #include "parseissuesdialog.h" #include "processstep.h" #include "project.h" +#include "projectcommentssettings.h" +#include "projectexplorerconstants.h" #include "projectexplorericons.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" @@ -81,10 +82,10 @@ #include "msvctoolchain.h" #endif +#include "customparser.h" #include "projecttree.h" #include "projectwelcomepage.h" -#include <app/app_version.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -103,6 +104,7 @@ #include <coreplugin/imode.h> #include <coreplugin/iversioncontrol.h> #include <coreplugin/locator/directoryfilter.h> +#include <coreplugin/messagemanager.h> #include <coreplugin/minisplitter.h> #include <coreplugin/modemanager.h> #include <coreplugin/navigationwidget.h> @@ -116,6 +118,7 @@ #include <texteditor/findinfiles.h> #include <texteditor/textdocument.h> #include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> #include <utils/algorithm.h> #include <utils/fileutils.h> @@ -123,6 +126,7 @@ #include <utils/mimeutils.h> #include <utils/parameteraction.h> #include <utils/processhandle.h> +#include <utils/processinterface.h> #include <utils/proxyaction.h> #include <utils/qtcassert.h> #include <utils/removefiledialog.h> @@ -131,6 +135,8 @@ #include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> +#include <nanotrace/nanotrace.h> + #include <QAction> #include <QActionGroup> #include <QApplication> @@ -143,7 +149,6 @@ #include <QMessageBox> #include <QPair> #include <QPushButton> -#include <QSettings> #include <QThreadPool> #include <QTimer> #include <QVBoxLayout> @@ -423,6 +428,18 @@ private: Core::LocatorMatcherTasks matchers() final; }; +class DefaultDeployConfigurationFactory final : public DeployConfigurationFactory +{ +public: + DefaultDeployConfigurationFactory() + { + setConfigBaseId("ProjectExplorer.DefaultDeployConfiguration"); + addSupportedTargetDeviceType(Constants::DESKTOP_DEVICE_TYPE); + //: Display name of the default deploy configuration + setDefaultDisplayName(Tr::tr("Deploy Configuration")); + } +}; + class ProjectExplorerPluginPrivate : public QObject { public: @@ -501,6 +518,8 @@ public: void extendFolderNavigationWidgetFactory(); + QString projectFilterString() const; + public: QMenu *m_openWithMenu; QMenu *m_openTerminalMenu; @@ -586,10 +605,8 @@ public: FilePath m_lastOpenDirectory; QPointer<RunConfiguration> m_defaultRunConfiguration; QPointer<RunConfiguration> m_delayedRunConfiguration; - QString m_projectFilterString; MiniProjectTargetSelector * m_targetSelector; ProjectExplorerSettings m_projectExplorerSettings; - BuildPropertiesSettings m_buildPropertiesSettings; QList<CustomParserSettings> m_customParsers; bool m_shouldHaveRunConfiguration = false; Id m_runMode = Constants::NO_RUN_MODE; @@ -608,21 +625,21 @@ public: MsvcToolChainFactory m_mscvToolChainFactory; ClangClToolChainFactory m_clangClToolChainFactory; #else - LinuxIccToolChainFactory m_linuxToolChainFactory; + GccToolChainFactory m_linuxToolChainFactory{GccToolChain::LinuxIcc}; #endif #ifndef Q_OS_MACOS - MingwToolChainFactory m_mingwToolChainFactory; // Mingw offers cross-compiling to windows + // Mingw offers cross-compiling to windows + GccToolChainFactory m_mingwToolChainFactory{GccToolChain::MinGW}; #endif - GccToolChainFactory m_gccToolChainFactory; - ClangToolChainFactory m_clangToolChainFactory; + GccToolChainFactory m_gccToolChainFactory{GccToolChain::RealGcc}; + GccToolChainFactory m_clangToolChainFactory{GccToolChain::Clang}; CustomToolChainFactory m_customToolChainFactory; DesktopDeviceFactory m_desktopDeviceFactory; ToolChainOptionsPage m_toolChainOptionsPage; - KitOptionsPage m_kitOptionsPage; TaskHub m_taskHub; @@ -677,13 +694,6 @@ public: IDocumentFactory m_taskFileFactory; StopMonitoringHandler closeTaskFile; - DeviceTypeKitAspect deviceTypeKitAspect; - DeviceKitAspect deviceKitAspect; - BuildDeviceKitAspect buildDeviceKitAspect; - ToolChainKitAspect toolChainKitAspect; - SysRootKitAspect sysRootKitAspect; - EnvironmentKitAspect environmentKitAspect; - DesktopQmakeRunConfigurationFactory qmakeRunConfigFactory; QbsRunConfigurationFactory qbsRunConfigFactory; CMakeRunConfigurationFactory cmakeRunConfigFactory; @@ -695,6 +705,15 @@ public: DeviceCheckBuildStepFactory deviceCheckBuildStepFactory; SanitizerOutputFormatterFactory sanitizerFormatterFactory; + + // JsonWizard related + FieldPageFactory fieldPageFactory; + FilePageFactory filePageFactory; + KitsPageFactory kitsPageFactory; + ProjectPageFactory projectPageFactory; + SummaryPageFactory summaryPageFactory; + FileGeneratorFactory fileGeneratorFactory; + ScannerGeneratorFactory scannerGeneratorFactory; }; static ProjectExplorerPlugin *m_instance = nullptr; @@ -761,7 +780,6 @@ ProjectExplorerPlugin::~ProjectExplorerPlugin() QTC_ASSERT(dd, return); delete dd->m_proWindow; // Needs access to the kit manager. - JsonWizardFactory::destroyAllFactories(); // Force sequence of deletion: KitManager::destroy(); // remove all the profile information @@ -848,15 +866,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er : FilePath()); }); - // For JsonWizard: - JsonWizardFactory::registerPageFactory(new FieldPageFactory); - JsonWizardFactory::registerPageFactory(new FilePageFactory); - JsonWizardFactory::registerPageFactory(new KitsPageFactory); - JsonWizardFactory::registerPageFactory(new ProjectPageFactory); - JsonWizardFactory::registerPageFactory(new SummaryPageFactory); - JsonWizardFactory::registerGeneratorFactory(new FileGeneratorFactory); - JsonWizardFactory::registerGeneratorFactory(new ScannerGeneratorFactory); - dd->m_proWindow = new ProjectWindow; Context projectTreeContext(Constants::C_PROJECT_TREE); @@ -885,6 +894,17 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er panelFactory->setCreateWidgetFunction([](Project *project) { return new CodeStyleSettingsWidget(project); }); ProjectPanelFactory::registerFactory(panelFactory); + panelFactory = new ProjectExplorer::ProjectPanelFactory; + panelFactory->setPriority(45); + panelFactory->setDisplayName(Tr::tr("Documentation Comments")); + panelFactory->setCreateWidgetFunction([](ProjectExplorer::Project *project) { + return new ProjectCommentsSettingsWidget(project); + }); + ProjectExplorer::ProjectPanelFactory::registerFactory(panelFactory); + TextEditor::TextEditorSettings::setCommentsSettingsRetriever([](const FilePath &filePath) { + return ProjectCommentsSettings(ProjectManager::projectForFile(filePath)).settings(); + }); + panelFactory = new ProjectPanelFactory; panelFactory->setPriority(50); panelFactory->setDisplayName(Tr::tr("Dependencies")); @@ -1292,7 +1312,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er // FIXME: This menu will never become visible if the user tried to open it once // without a project loaded. - connect(generatorContainer->menu(), &QMenu::aboutToShow, [menu = generatorContainer->menu()] { + connect(generatorContainer->menu(), &QMenu::aboutToShow, + this, [menu = generatorContainer->menu()] { menu->clear(); if (Project * const project = ProjectManager::startupProject()) { for (const auto &generator : project->allGenerators()) { @@ -1611,7 +1632,7 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd->updateWelcomePage(); }); - QSettings *s = ICore::settings(); + QtcSettings *s = ICore::settings(); const QStringList fileNames = s->value(Constants::RECENTPROJECTS_FILE_NAMES_KEY).toStringList(); const QStringList displayNames = s->value(Constants::RECENTPROJECTS_DISPLAY_NAMES_KEY) .toStringList(); @@ -1682,13 +1703,11 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er = s->value(Constants::LOW_BUILD_PRIORITY_SETTINGS_KEY, defaultSettings.lowBuildPriority) .toBool(); - dd->m_buildPropertiesSettings.readSettings(s); - const int customParserCount = s->value(Constants::CUSTOM_PARSER_COUNT_KEY).toInt(); for (int i = 0; i < customParserCount; ++i) { CustomParserSettings settings; - settings.fromMap(s->value(Constants::CUSTOM_PARSER_PREFIX_KEY - + QString::number(i)).toMap()); + settings.fromMap(storeFromVariant( + s->value(numberedKey(Constants::CUSTOM_PARSER_PREFIX_KEY, i)))); dd->m_customParsers << settings; } @@ -1953,8 +1972,9 @@ void ProjectExplorerPluginPrivate::loadAction() } FilePath filePath = Utils::FileUtils::getOpenFilePath(ICore::dialogParent(), - Tr::tr("Load Project"), dir, - dd->m_projectFilterString); + Tr::tr("Load Project"), + dir, + dd->projectFilterString()); if (filePath.isEmpty()) return; @@ -2035,10 +2055,6 @@ void ProjectExplorerPlugin::extensionsInitialized() JsonWizardFactory::createWizardFactories(); // Register factories for all project managers - QStringList allGlobPatterns; - - const QString filterSeparator = QLatin1String(";;"); - QStringList filterStrings; dd->m_documentFactory.setOpener([](FilePath filePath) { if (filePath.isDir()) { @@ -2057,9 +2073,6 @@ void ProjectExplorerPlugin::extensionsInitialized() for (auto it = dd->m_projectCreators.cbegin(); it != dd->m_projectCreators.cend(); ++it) { const QString &mimeType = it.key(); dd->m_documentFactory.addMimeType(mimeType); - MimeType mime = Utils::mimeTypeForName(mimeType); - allGlobPatterns.append(mime.globPatterns()); - filterStrings.append(mime.filterString()); dd->m_profileMimeTypes += mimeType; } @@ -2068,16 +2081,13 @@ void ProjectExplorerPlugin::extensionsInitialized() return TaskFile::openTasks(filePath); }); - QString allProjectsFilter = Tr::tr("All Projects"); - allProjectsFilter += QLatin1String(" (") + allGlobPatterns.join(QLatin1Char(' ')) - + QLatin1Char(')'); - filterStrings.prepend(allProjectsFilter); - dd->m_projectFilterString = filterStrings.join(filterSeparator); - BuildManager::extensionsInitialized(); - TaskHub::addCategory(Constants::TASK_CATEGORY_SANITIZER, - Tr::tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'")); - TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, Tr::tr("My Tasks")); + TaskHub::addCategory({Constants::TASK_CATEGORY_SANITIZER, + Tr::tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'"), + Tr::tr("Memory handling issues that the address sanitizer found.")}); + TaskHub::addCategory({Constants::TASK_CATEGORY_TASKLIST_ID, + Tr::tr("My Tasks"), + Tr::tr("Issues from a task list file (.tasks).")}); SshSettings::loadSettings(ICore::settings()); const auto searchPathRetriever = [] { @@ -2112,19 +2122,15 @@ void ProjectExplorerPlugin::extensionsInitialized() // Load devices immediately, as other plugins might want to use them DeviceManager::instance()->load(); - - // delay restoring kits until UI is shown for improved perceived startup performance - QTimer::singleShot(0, this, &ProjectExplorerPlugin::restoreKits); } -void ProjectExplorerPlugin::restoreKits() +bool ProjectExplorerPlugin::delayedInitialize() { + NANOTRACE_SCOPE("ProjectExplorer", "ProjectExplorerPlugin::restoreKits"); ExtraAbi::load(); // Load this before Toolchains! ToolChainManager::restoreToolChains(); KitManager::restoreKits(); - // restoring startup session is supposed to be done as a result of ICore::coreOpened, - // and that is supposed to happen after restoring kits: - QTC_CHECK(!SessionManager::isStartupSessionRestored()); + return true; } void ProjectExplorerPluginPrivate::updateRunWithoutDeployMenu() @@ -2198,7 +2204,7 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() } QtcSettings *s = ICore::settings(); - s->remove(QLatin1String("ProjectExplorer/RecentProjects/Files")); + s->remove("ProjectExplorer/RecentProjects/Files"); QStringList fileNames; QStringList displayNames; @@ -2256,12 +2262,12 @@ void ProjectExplorerPluginPrivate::savePersistentSettings() int(dd->m_projectExplorerSettings.stopBeforeBuild), int(defaultSettings.stopBeforeBuild)); - dd->m_buildPropertiesSettings.writeSettings(s); + buildPropertiesSettings().writeSettings(); // FIXME: Should not be needed. s->setValueWithDefault(Constants::CUSTOM_PARSER_COUNT_KEY, int(dd->m_customParsers.count()), 0); for (int i = 0; i < dd->m_customParsers.count(); ++i) { - s->setValue(Constants::CUSTOM_PARSER_PREFIX_KEY + QString::number(i), - dd->m_customParsers.at(i).toMap()); + s->setValue(numberedKey(Constants::CUSTOM_PARSER_PREFIX_KEY, i), + variantFromStore(dd->m_customParsers.at(i).toMap())); } } @@ -2369,8 +2375,7 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con dd->updateActions(); const bool switchToProjectsMode = Utils::anyOf(openedPro, &Project::needsConfiguration); - const bool switchToEditMode = Utils::allOf(openedPro, - [](Project *p) { return p->isEditModePreferred(); }); + const bool switchToEditMode = Utils::allOf(openedPro, &Project::isEditModePreferred); if (!openedPro.isEmpty()) { if (switchToProjectsMode) ModeManager::activateMode(Constants::MODE_SESSION); @@ -2434,7 +2439,9 @@ MiniProjectTargetSelector *ProjectExplorerPlugin::targetSelector() void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *runConfiguration, Id runMode) { - const Tasks runConfigIssues = runConfiguration->checkForIssues(); + const Tasks runConfigIssues = runMode == Constants::DAP_CMAKE_DEBUG_RUN_MODE + ? Tasks() + : runConfiguration->checkForIssues(); if (!runConfigIssues.isEmpty()) { for (const Task &t : runConfigIssues) TaskHub::addTask(t); @@ -2772,6 +2779,24 @@ void ProjectExplorerPluginPrivate::extendFolderNavigationWidgetFactory() }); } +QString ProjectExplorerPluginPrivate::projectFilterString() const +{ + const QString filterSeparator = QLatin1String(";;"); + QStringList filterStrings; + QStringList allGlobPatterns; + for (auto it = m_projectCreators.cbegin(); it != m_projectCreators.cend(); ++it) { + const QString &mimeType = it.key(); + MimeType mime = Utils::mimeTypeForName(mimeType); + allGlobPatterns.append(mime.globPatterns()); + filterStrings.append(mime.filterString()); + } + QString allProjectsFilter = Tr::tr("All Projects"); + allProjectsFilter += QLatin1String(" (") + allGlobPatterns.join(QLatin1Char(' ')) + + QLatin1Char(')'); + filterStrings.prepend(allProjectsFilter); + return filterStrings.join(filterSeparator); +} + void ProjectExplorerPluginPrivate::runProjectContextMenu(RunConfiguration *rc) { const Node *node = ProjectTree::currentNode(); @@ -2864,10 +2889,11 @@ bool ProjectExplorerPlugin::coreAboutToClose() QPushButton *closeAnyway = box.addButton(Tr::tr("Cancel Build && Close"), QMessageBox::AcceptRole); QPushButton *cancelClose = box.addButton(Tr::tr("Do Not Close"), QMessageBox::RejectRole); box.setDefaultButton(cancelClose); - box.setWindowTitle(Tr::tr("Close %1?").arg(Core::Constants::IDE_DISPLAY_NAME)); + box.setWindowTitle(Tr::tr("Close %1?").arg(QGuiApplication::applicationDisplayName())); box.setText(Tr::tr("A project is currently being built.")); - box.setInformativeText(Tr::tr("Do you want to cancel the build process and close %1 anyway?") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + box.setInformativeText( + Tr::tr("Do you want to cancel the build process and close %1 anyway?") + .arg(QGuiApplication::applicationDisplayName())); box.exec(); if (box.clickedButton() != closeAnyway) return false; @@ -2929,10 +2955,15 @@ void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc, dd->m_delayedRunConfiguration = rc; dd->m_shouldHaveRunConfiguration = true; }; - const BuildForRunConfigStatus buildStatus = forceSkipDeploy + + BuildForRunConfigStatus buildStatus = forceSkipDeploy ? BuildManager::isBuilding(rc->project()) ? BuildForRunConfigStatus::Building : BuildForRunConfigStatus::NotBuilding : BuildManager::potentiallyBuildForRunConfig(rc); + + if (dd->m_runMode == Constants::DAP_CMAKE_DEBUG_RUN_MODE) + buildStatus = BuildForRunConfigStatus::NotBuilding; + switch (buildStatus) { case BuildForRunConfigStatus::BuildFailed: return; @@ -2951,17 +2982,6 @@ void ProjectExplorerPlugin::runRunConfiguration(RunConfiguration *rc, dd->doUpdateRunActions(); } -QList<QPair<CommandLine, ProcessHandle>> ProjectExplorerPlugin::runningRunControlProcesses() -{ - QList<QPair<CommandLine, ProcessHandle>> processes; - const QList<RunControl *> runControls = allRunControls(); - for (RunControl *rc : runControls) { - if (rc->isRunning()) - processes.push_back({rc->commandLine(), rc->applicationProcessHandle()}); - } - return processes; -} - QList<RunControl *> ProjectExplorerPlugin::allRunControls() { return dd->m_outputPane.allRunControls(); @@ -3032,85 +3052,63 @@ void ProjectExplorerPluginPrivate::updateDeployActions() doUpdateRunActions(); } -bool ProjectExplorerPlugin::canRunStartupProject(Id runMode, QString *whyNot) +expected_str<void> ProjectExplorerPlugin::canRunStartupProject(Utils::Id runMode) { Project *project = ProjectManager::startupProject(); - if (!project) { - if (whyNot) - *whyNot = Tr::tr("No active project."); - return false; - } + if (!project) + return make_unexpected(Tr::tr("No active project.")); if (project->needsConfiguration()) { - if (whyNot) - *whyNot = Tr::tr("The project \"%1\" is not configured.").arg(project->displayName()); - return false; + return make_unexpected(Tr::tr("The project \"%1\" is not configured.") + .arg(project->displayName())); } Target *target = project->activeTarget(); if (!target) { - if (whyNot) - *whyNot = Tr::tr("The project \"%1\" has no active kit.").arg(project->displayName()); - return false; + return make_unexpected(Tr::tr("The project \"%1\" has no active kit.") + .arg(project->displayName())); } RunConfiguration *activeRC = target->activeRunConfiguration(); if (!activeRC) { - if (whyNot) - *whyNot = Tr::tr("The kit \"%1\" for the project \"%2\" has no active run configuration.") - .arg(target->displayName(), project->displayName()); - return false; + return make_unexpected( + Tr::tr("The kit \"%1\" for the project \"%2\" has no active run configuration.") + .arg(target->displayName(), project->displayName())); } - if (!activeRC->isEnabled()) { - if (whyNot) - *whyNot = activeRC->disabledReason(); - return false; - } + if (!activeRC->isEnabled()) + return make_unexpected(activeRC->disabledReason()); if (dd->m_projectExplorerSettings.buildBeforeDeploy != BuildBeforeRunMode::Off && dd->m_projectExplorerSettings.deployBeforeRun && !BuildManager::isBuilding(project) && hasBuildSettings(project)) { QPair<bool, QString> buildState = dd->buildSettingsEnabled(project); - if (!buildState.first) { - if (whyNot) - *whyNot = buildState.second; - return false; - } + if (!buildState.first) + return make_unexpected(buildState.second); - if (BuildManager::isBuilding()) { - if (whyNot) - *whyNot = Tr::tr("A build is still in progress."); - return false; - } + if (BuildManager::isBuilding()) + return make_unexpected(Tr::tr("A build is still in progress.")); } // shouldn't actually be shown to the user... - if (!RunControl::canRun(runMode, - DeviceTypeKitAspect::deviceTypeId(target->kit()), + if (!RunControl::canRun(runMode, DeviceTypeKitAspect::deviceTypeId(target->kit()), activeRC->id())) { - if (whyNot) - *whyNot = Tr::tr("Cannot run \"%1\".").arg(activeRC->displayName()); - return false; + return make_unexpected(Tr::tr("Cannot run \"%1\".").arg(activeRC->displayName())); } - if (dd->m_delayedRunConfiguration && dd->m_delayedRunConfiguration->project() == project) { - if (whyNot) - *whyNot = Tr::tr("A run action is already scheduled for the active project."); - return false; - } + if (dd->m_delayedRunConfiguration && dd->m_delayedRunConfiguration->project() == project) + return make_unexpected(Tr::tr("A run action is already scheduled for the active project.")); - return true; + return {}; } void ProjectExplorerPluginPrivate::doUpdateRunActions() { - QString whyNot; - const bool state = ProjectExplorerPlugin::canRunStartupProject(Constants::NORMAL_RUN_MODE, &whyNot); - m_runAction->setEnabled(state); - m_runAction->setToolTip(whyNot); - m_runWithoutDeployAction->setEnabled(state); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject(Constants::NORMAL_RUN_MODE); + m_runAction->setEnabled(bool(canRun)); + m_runAction->setToolTip(canRun ? QString() : canRun.error()); + m_runWithoutDeployAction->setEnabled(bool(canRun)); emit m_instance->runActionsUpdated(); } @@ -3141,8 +3139,8 @@ void ProjectExplorerPluginPrivate::updateUnloadProjectMenu() menu->clear(); for (Project *project : ProjectManager::projects()) { QAction *action = menu->addAction(Tr::tr("Close Project \"%1\"").arg(project->displayName())); - connect(action, &QAction::triggered, - [project] { ProjectExplorerPlugin::unloadProject(project); } ); + connect(action, &QAction::triggered, this, + [project] { ProjectExplorerPlugin::unloadProject(project); }); } } @@ -3630,7 +3628,7 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env BuildConfiguration *bc = activeBuildConfiguration(ProjectTree::projectForNode(currentNode)); if (!bc) { - Terminal::Hooks::instance().openTerminal({{}, currentNode->directory(), environment}); + Terminal::Hooks::instance().openTerminal({currentNode->directory(), environment}); return; } @@ -3644,13 +3642,18 @@ void ProjectExplorerPluginPrivate::openTerminalHere(const EnvironmentGetter &env && !buildDevice->ensureReachable(workingDir)) workingDir.clear(); - const FilePath shell = Terminal::defaultShellForDevice(buildDevice->rootPath()); + const expected_str<FilePath> shell = Terminal::defaultShellForDevice(buildDevice->rootPath()); - if (!shell.isEmpty() && buildDevice->rootPath().needsDevice()) { - Terminal::Hooks::instance().openTerminal({CommandLine{shell, {}}, workingDir, environment}); - } else { - Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, environment}); + if (!shell) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed opening terminal.\n%1").arg(shell.error())); + return; } + + if (buildDevice->rootPath().needsDevice()) + Terminal::Hooks::instance().openTerminal({CommandLine{*shell, {}}, workingDir, environment}); + else + Terminal::Hooks::instance().openTerminal({workingDir, environment}); } void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() @@ -3666,7 +3669,7 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() currentNode->asProjectNode()); QTC_ASSERT(runConfig, return); - const Runnable runnable = runConfig->runnable(); + const ProcessRunData runnable = runConfig->runnable(); IDevice::ConstPtr device = DeviceManager::deviceForPath(runnable.command.executable()); if (!device) device = DeviceKitAspect::device(target->kit()); @@ -3679,12 +3682,19 @@ void ProjectExplorerPluginPrivate::openTerminalHereWithRunEnv() if (!device->filePath(workingDir.path()).exists() && !device->ensureReachable(workingDir)) workingDir.clear(); - const FilePath shell = Terminal::defaultShellForDevice(device->rootPath()); - if (!shell.isEmpty() && device->rootPath().needsDevice()) { + const expected_str<FilePath> shell = Terminal::defaultShellForDevice(device->rootPath()); + + if (!shell) { + Core::MessageManager::writeDisrupting( + Tr::tr("Failed opening terminal.\n%1").arg(shell.error())); + return; + } + + if (device->rootPath().needsDevice()) { Terminal::Hooks::instance().openTerminal( - {CommandLine{shell, {}}, workingDir, runnable.environment}); + {CommandLine{*shell, {}}, workingDir, runnable.environment}); } else { - Terminal::Hooks::instance().openTerminal({std::nullopt, workingDir, runnable.environment}); + Terminal::Hooks::instance().openTerminal({workingDir, runnable.environment}); } } @@ -3870,7 +3880,8 @@ void ProjectExplorerPlugin::renameFile(Node *node, const QString &newFileName) const HandleIncludeGuards handleGuards = canTryToRenameIncludeGuards(node); if (!folderNode->canRenameFile(oldFilePath, newFilePath)) { - QTimer::singleShot(0, [oldFilePath, newFilePath, projectFileName, handleGuards] { + QTimer::singleShot(0, m_instance, + [oldFilePath, newFilePath, projectFileName, handleGuards] { int res = QMessageBox::question(ICore::dialogParent(), Tr::tr("Project Editing Failed"), Tr::tr("The project file %1 cannot be automatically changed.\n\n" @@ -3894,7 +3905,7 @@ void ProjectExplorerPlugin::renameFile(Node *node, const QString &newFileName) .arg(newFilePath.toUserOutput()) .arg(projectFileName); - QTimer::singleShot(0, [renameFileError] { + QTimer::singleShot(0, m_instance, [renameFileError] { QMessageBox::warning(ICore::dialogParent(), Tr::tr("Project Editing Failed"), renameFileError); @@ -3905,7 +3916,7 @@ void ProjectExplorerPlugin::renameFile(Node *node, const QString &newFileName) .arg(oldFilePath.toUserOutput()) .arg(newFilePath.toUserOutput()); - QTimer::singleShot(0, [renameFileError] { + QTimer::singleShot(0, m_instance, [renameFileError] { QMessageBox::warning(ICore::dialogParent(), Tr::tr("Cannot Rename File"), renameFileError); }); } @@ -3941,16 +3952,6 @@ const AppOutputSettings &ProjectExplorerPlugin::appOutputSettings() return dd->m_outputPane.settings(); } -BuildPropertiesSettings &ProjectExplorerPlugin::buildPropertiesSettings() -{ - return dd->m_buildPropertiesSettings; -} - -void ProjectExplorerPlugin::showQtSettings() -{ - dd->m_buildPropertiesSettings.showQtSettings.setValue(true); -} - void ProjectExplorerPlugin::setCustomParsers(const QList<CustomParserSettings> &settings) { if (dd->m_customParsers != settings) { @@ -4009,26 +4010,11 @@ void ProjectExplorerPlugin::openOpenProjectDialog() const FilePath path = DocumentManager::useProjectsDirectory() ? DocumentManager::projectsDirectory() : FilePath(); - const FilePaths files = DocumentManager::getOpenFileNames(dd->m_projectFilterString, path); + const FilePaths files = DocumentManager::getOpenFileNames(dd->projectFilterString(), path); if (!files.isEmpty()) ICore::openFiles(files, ICore::SwitchMode); } -/*! - Returns the current build directory template. - - \sa setBuildDirectoryTemplate -*/ -QString ProjectExplorerPlugin::buildDirectoryTemplate() -{ - return dd->m_buildPropertiesSettings.buildDirectoryTemplate.value(); -} - -QString ProjectExplorerPlugin::defaultBuildDirectoryTemplate() -{ - return dd->m_buildPropertiesSettings.defaultBuildDirectoryTemplate(); -} - void ProjectExplorerPlugin::updateActions() { dd->updateActions(); diff --git a/src/plugins/projectexplorer/projectexplorer.h b/src/plugins/projectexplorer/projectexplorer.h index 0b87f53310b..eec4fae8601 100644 --- a/src/plugins/projectexplorer/projectexplorer.h +++ b/src/plugins/projectexplorer/projectexplorer.h @@ -7,6 +7,7 @@ #include <extensionsystem/iplugin.h> +#include <utils/expected.h> #include <utils/filepath.h> #include <utils/id.h> @@ -21,13 +22,9 @@ namespace Core { class OutputWindow; } // Core -namespace Utils { -class CommandLine; -class ProcessHandle; -} // Utils +namespace Utils { class CommandLine; } namespace ProjectExplorer { -class BuildPropertiesSettings; class CustomParserSettings; class FolderNode; class Node; @@ -109,7 +106,7 @@ public: //PluginInterface bool initialize(const QStringList &arguments, QString *errorMessage) override; void extensionsInitialized() override; - void restoreKits(); + bool delayedInitialize() override; ShutdownFlag aboutToShutdown() override; static void setProjectExplorerSettings(const ProjectExplorerSettings &pes); @@ -118,9 +115,6 @@ public: static void setAppOutputSettings(const Internal::AppOutputSettings &settings); static const Internal::AppOutputSettings &appOutputSettings(); - static BuildPropertiesSettings &buildPropertiesSettings(); - static void showQtSettings(); - static void setCustomParsers(const QList<CustomParserSettings> &settings); static void addCustomParser(const CustomParserSettings &settings); static void removeCustomParser(Utils::Id id); @@ -138,12 +132,11 @@ public: static void renameFilesForSymbol(const QString &oldSymbolName, const QString &newSymbolName, const Utils::FilePaths &files, bool preferLowerCaseFileNames); - static bool canRunStartupProject(Utils::Id runMode, QString *whyNot = nullptr); + static Utils::expected_str<void> canRunStartupProject(Utils::Id runMode); static void runProject(Project *pro, Utils::Id, const bool forceSkipDeploy = false); static void runStartupProject(Utils::Id runMode, bool forceSkipDeploy = false); static void runRunConfiguration(RunConfiguration *rc, Utils::Id runMode, const bool forceSkipDeploy = false); - static QList<QPair<Utils::CommandLine, Utils::ProcessHandle>> runningRunControlProcesses(); static QList<RunControl *> allRunControls(); static void addExistingFiles(FolderNode *folderNode, const Utils::FilePaths &filePaths); @@ -158,9 +151,6 @@ public: static void openNewProjectDialog(); static void openOpenProjectDialog(); - static QString buildDirectoryTemplate(); - static QString defaultBuildDirectoryTemplate(); - static void updateActions(); static void activateProjectPanel(Utils::Id panelId); diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index c955a485905..8f5e7e75f57 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -1,264 +1,257 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "ProjectExplorer" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "qml"] } - Depends { name: "Aggregation" } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "qml"] } + Depends { name: "Aggregation" } + Depends { name: "Utils" } - Depends { name: "Core" } - Depends { name: "TextEditor" } - Depends { name: "app_version_header" } + Depends { name: "Core" } + Depends { name: "TextEditor" } - Depends { name: "libclang"; required: false } - Depends { name: "clang_defines" } + Depends { name: "libclang"; required: false } + Depends { name: "clang_defines" } - pluginTestDepends: ["GenericProjectManager"] + pluginTestDepends: ["GenericProjectManager"] - Group { - name: "General" - files: [ - "abi.cpp", "abi.h", - "abiwidget.cpp", "abiwidget.h", - "abstractprocessstep.cpp", "abstractprocessstep.h", - "addrunconfigdialog.cpp", "addrunconfigdialog.h", - "allprojectsfilter.cpp", "allprojectsfilter.h", - "allprojectsfind.cpp", "allprojectsfind.h", - "appoutputpane.cpp", "appoutputpane.h", - "baseprojectwizarddialog.cpp", "baseprojectwizarddialog.h", - "buildaspects.cpp", "buildaspects.h", - "buildconfiguration.cpp", "buildconfiguration.h", - "buildinfo.cpp", "buildinfo.h", - "buildmanager.cpp", "buildmanager.h", - "buildprogress.cpp", "buildprogress.h", - "buildpropertiessettings.cpp", "buildpropertiessettings.h", - "buildsettingspropertiespage.cpp", "buildsettingspropertiespage.h", - "buildstep.cpp", "buildstep.h", - "buildsteplist.cpp", "buildsteplist.h", - "buildstepspage.cpp", "buildstepspage.h", - "buildsystem.cpp", "buildsystem.h", - "buildtargetinfo.h", - "buildtargettype.h", - "clangparser.cpp", "clangparser.h", - "codestylesettingspropertiespage.cpp", "codestylesettingspropertiespage.h", - "compileoutputwindow.cpp", "compileoutputwindow.h", - "configtaskhandler.cpp", "configtaskhandler.h", - "copystep.cpp", "copystep.h", - "copytaskhandler.cpp", "copytaskhandler.h", - "currentprojectfilter.cpp", "currentprojectfilter.h", - "currentprojectfind.cpp", "currentprojectfind.h", - "customexecutablerunconfiguration.cpp", "customexecutablerunconfiguration.h", - "customparser.cpp", "customparser.h", - "customparserconfigdialog.cpp", "customparserconfigdialog.h", - "customparserssettingspage.cpp", "customparserssettingspage.h", - "customtoolchain.cpp", "customtoolchain.h", - "dependenciespanel.cpp", "dependenciespanel.h", - "deployablefile.cpp", "deployablefile.h", - "deployconfiguration.cpp", "deployconfiguration.h", - "deploymentdata.cpp", - "deploymentdata.h", - "deploymentdataview.cpp", - "deploymentdataview.h", - "desktoprunconfiguration.cpp", "desktoprunconfiguration.h", - "editorconfiguration.cpp", "editorconfiguration.h", - "editorsettingspropertiespage.cpp", "editorsettingspropertiespage.h", - "environmentaspect.cpp", "environmentaspect.h", - "environmentaspectwidget.cpp", "environmentaspectwidget.h", - "environmentwidget.cpp", "environmentwidget.h", - "expanddata.cpp", "expanddata.h", - "extraabi.cpp", "extraabi.h", - "extracompiler.cpp", "extracompiler.h", - "fileinsessionfinder.cpp", "fileinsessionfinder.h", - "filesinallprojectsfind.cpp", "filesinallprojectsfind.h", - "filterkitaspectsdialog.cpp", "filterkitaspectsdialog.h", - "gccparser.cpp", "gccparser.h", - "gcctoolchain.cpp", "gcctoolchain.h", - "gnumakeparser.cpp", "gnumakeparser.h", - "headerpath.h", - "importwidget.cpp", "importwidget.h", - "ioutputparser.cpp", "ioutputparser.h", - "ipotentialkit.h", - "itaskhandler.h", - "kit.cpp", "kit.h", - "kitchooser.cpp", "kitchooser.h", - "kitfeatureprovider.h", - "kitinformation.cpp", "kitinformation.h", - "kitmanager.cpp", "kitmanager.h", - "kitmanagerconfigwidget.cpp", "kitmanagerconfigwidget.h", - "kitmodel.cpp", "kitmodel.h", - "kitoptionspage.cpp", "kitoptionspage.h", - "ldparser.cpp", "ldparser.h", - "lldparser.cpp", "lldparser.h", - "linuxiccparser.cpp", "linuxiccparser.h", - "makestep.cpp", "makestep.h", - "miniprojecttargetselector.cpp", "miniprojecttargetselector.h", - "msvcparser.cpp", "msvcparser.h", - "namedwidget.cpp", "namedwidget.h", - "osparser.cpp", "osparser.h", - "panelswidget.cpp", "panelswidget.h", - "parseissuesdialog.cpp", "parseissuesdialog.h", - "processparameters.cpp", "processparameters.h", - "processstep.cpp", "processstep.h", - "project.cpp", "project.h", - "projectconfiguration.cpp", "projectconfiguration.h", - "projectconfigurationmodel.cpp", "projectconfigurationmodel.h", - "projectexplorer.cpp", "projectexplorer.h", - "projectexplorer.qrc", - "projectexplorer_export.h", - "projectexplorerconstants.cpp", - "projectexplorerconstants.h", - "projectexplorericons.h", "projectexplorericons.cpp", - "projectexplorersettings.h", "projectexplorersettings.cpp", - "projectexplorertr.h", - "projectfilewizardextension.cpp", "projectfilewizardextension.h", - "projectimporter.cpp", "projectimporter.h", - "projectmacro.cpp", "projectmacro.h", - "projectmanager.cpp", "projectmanager.h", - "projectmodels.cpp", "projectmodels.h", - "projectnodes.cpp", "projectnodes.h", - "projectpanelfactory.cpp", "projectpanelfactory.h", - "projectsettingswidget.cpp", "projectsettingswidget.h", - "projecttree.cpp", - "projecttree.h", - "projecttreewidget.cpp", "projecttreewidget.h", - "projectwindow.cpp", "projectwindow.h", - "projectwizardpage.cpp", "projectwizardpage.h", - "rawprojectpart.cpp", "rawprojectpart.h", - "removetaskhandler.cpp", "removetaskhandler.h", - "runconfiguration.cpp", "runconfiguration.h", - "runcontrol.cpp", "runcontrol.h", - "runconfigurationaspects.cpp", "runconfigurationaspects.h", - "runsettingspropertiespage.cpp", "runsettingspropertiespage.h", - "sanitizerparser.cpp", "sanitizerparser.h", - "selectablefilesmodel.cpp", "selectablefilesmodel.h", - "showineditortaskhandler.cpp", "showineditortaskhandler.h", - "showoutputtaskhandler.cpp", "showoutputtaskhandler.h", - "simpleprojectwizard.cpp", "simpleprojectwizard.h", - "target.cpp", "target.h", - "targetsettingspanel.cpp", "targetsettingspanel.h", - "targetsetuppage.cpp", "targetsetuppage.h", - "targetsetupwidget.cpp", "targetsetupwidget.h", - "task.cpp", "task.h", - "taskfile.cpp", "taskfile.h", - "taskhub.cpp", "taskhub.h", - "taskmodel.cpp", "taskmodel.h", - "taskwindow.cpp", "taskwindow.h", - "toolchain.cpp", "toolchain.h", - "toolchaincache.h", - "toolchainconfigwidget.cpp", "toolchainconfigwidget.h", - "toolchainmanager.cpp", "toolchainmanager.h", - "toolchainoptionspage.cpp", "toolchainoptionspage.h", - "toolchainsettingsaccessor.cpp", "toolchainsettingsaccessor.h", - "treescanner.cpp", "treescanner.h", - "userfileaccessor.cpp", "userfileaccessor.h", - "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", - "waitforstopdialog.cpp", "waitforstopdialog.h", - "windebuginterface.cpp", "windebuginterface.h", - "xcodebuildparser.cpp", "xcodebuildparser.h" - ] - } + Group { + name: "General" + files: [ + "abi.cpp", "abi.h", + "abiwidget.cpp", "abiwidget.h", + "abstractprocessstep.cpp", "abstractprocessstep.h", + "addrunconfigdialog.cpp", "addrunconfigdialog.h", + "allprojectsfilter.cpp", "allprojectsfilter.h", + "allprojectsfind.cpp", "allprojectsfind.h", + "appoutputpane.cpp", "appoutputpane.h", + "baseprojectwizarddialog.cpp", "baseprojectwizarddialog.h", + "buildaspects.cpp", "buildaspects.h", + "buildconfiguration.cpp", "buildconfiguration.h", + "buildinfo.cpp", "buildinfo.h", + "buildmanager.cpp", "buildmanager.h", + "buildprogress.cpp", "buildprogress.h", + "buildpropertiessettings.cpp", "buildpropertiessettings.h", + "buildsettingspropertiespage.cpp", "buildsettingspropertiespage.h", + "buildstep.cpp", "buildstep.h", + "buildsteplist.cpp", "buildsteplist.h", + "buildstepspage.cpp", "buildstepspage.h", + "buildsystem.cpp", "buildsystem.h", + "buildtargetinfo.h", + "buildtargettype.h", + "clangparser.cpp", "clangparser.h", + "codestylesettingspropertiespage.cpp", "codestylesettingspropertiespage.h", + "compileoutputwindow.cpp", "compileoutputwindow.h", + "configtaskhandler.cpp", "configtaskhandler.h", + "copystep.cpp", "copystep.h", + "copytaskhandler.cpp", "copytaskhandler.h", + "currentprojectfilter.cpp", "currentprojectfilter.h", + "currentprojectfind.cpp", "currentprojectfind.h", + "customexecutablerunconfiguration.cpp", "customexecutablerunconfiguration.h", + "customparser.cpp", "customparser.h", + "customparserconfigdialog.cpp", "customparserconfigdialog.h", + "customparserssettingspage.cpp", "customparserssettingspage.h", + "customtoolchain.cpp", "customtoolchain.h", + "dependenciespanel.cpp", "dependenciespanel.h", + "deployablefile.cpp", "deployablefile.h", + "deployconfiguration.cpp", "deployconfiguration.h", + "deploymentdata.cpp", + "deploymentdata.h", + "deploymentdataview.cpp", + "deploymentdataview.h", + "desktoprunconfiguration.cpp", "desktoprunconfiguration.h", + "editorconfiguration.cpp", "editorconfiguration.h", + "editorsettingspropertiespage.cpp", "editorsettingspropertiespage.h", + "environmentaspect.cpp", "environmentaspect.h", + "environmentaspectwidget.cpp", "environmentaspectwidget.h", + "environmentwidget.cpp", "environmentwidget.h", + "expanddata.cpp", "expanddata.h", + "extraabi.cpp", "extraabi.h", + "extracompiler.cpp", "extracompiler.h", + "fileinsessionfinder.cpp", "fileinsessionfinder.h", + "filesinallprojectsfind.cpp", "filesinallprojectsfind.h", + "filterkitaspectsdialog.cpp", "filterkitaspectsdialog.h", + "gccparser.cpp", "gccparser.h", + "gcctoolchain.cpp", "gcctoolchain.h", + "gnumakeparser.cpp", "gnumakeparser.h", + "headerpath.h", + "importwidget.cpp", "importwidget.h", + "ioutputparser.cpp", "ioutputparser.h", + "ipotentialkit.h", + "itaskhandler.h", + "kit.cpp", "kit.h", + "kitaspects.cpp", "kitaspects.h", + "kitchooser.cpp", "kitchooser.h", + "kitfeatureprovider.h", + "kitmanager.cpp", "kitmanager.h", + "kitmanagerconfigwidget.cpp", "kitmanagerconfigwidget.h", + "kitoptionspage.cpp", "kitoptionspage.h", + "ldparser.cpp", "ldparser.h", + "lldparser.cpp", "lldparser.h", + "linuxiccparser.cpp", "linuxiccparser.h", + "makestep.cpp", "makestep.h", + "miniprojecttargetselector.cpp", "miniprojecttargetselector.h", + "msvcparser.cpp", "msvcparser.h", + "namedwidget.cpp", "namedwidget.h", + "osparser.cpp", "osparser.h", + "panelswidget.cpp", "panelswidget.h", + "parseissuesdialog.cpp", "parseissuesdialog.h", + "processparameters.cpp", "processparameters.h", + "processstep.cpp", "processstep.h", + "project.cpp", "project.h", + "projectcommentssettings.cpp", "projectcommentssettings.h", + "projectconfiguration.cpp", "projectconfiguration.h", + "projectconfigurationmodel.cpp", "projectconfigurationmodel.h", + "projectexplorer.cpp", "projectexplorer.h", + "projectexplorer.qrc", + "projectexplorer_export.h", + "projectexplorerconstants.cpp", + "projectexplorerconstants.h", + "projectexplorericons.h", "projectexplorericons.cpp", + "projectexplorersettings.h", "projectexplorersettings.cpp", + "projectexplorertr.h", + "projectfilewizardextension.cpp", "projectfilewizardextension.h", + "projectimporter.cpp", "projectimporter.h", + "projectmacro.cpp", "projectmacro.h", + "projectmanager.cpp", "projectmanager.h", + "projectmodels.cpp", "projectmodels.h", + "projectnodes.cpp", "projectnodes.h", + "projectpanelfactory.cpp", "projectpanelfactory.h", + "projectsettingswidget.cpp", "projectsettingswidget.h", + "projecttree.cpp", + "projecttree.h", + "projecttreewidget.cpp", "projecttreewidget.h", + "projectwindow.cpp", "projectwindow.h", + "projectwizardpage.cpp", "projectwizardpage.h", + "rawprojectpart.cpp", "rawprojectpart.h", + "removetaskhandler.cpp", "removetaskhandler.h", + "runconfiguration.cpp", "runconfiguration.h", + "runcontrol.cpp", "runcontrol.h", + "runconfigurationaspects.cpp", "runconfigurationaspects.h", + "runsettingspropertiespage.cpp", "runsettingspropertiespage.h", + "sanitizerparser.cpp", "sanitizerparser.h", + "selectablefilesmodel.cpp", "selectablefilesmodel.h", + "showineditortaskhandler.cpp", "showineditortaskhandler.h", + "showoutputtaskhandler.cpp", "showoutputtaskhandler.h", + "simpleprojectwizard.cpp", "simpleprojectwizard.h", + "target.cpp", "target.h", + "targetsettingspanel.cpp", "targetsettingspanel.h", + "targetsetuppage.cpp", "targetsetuppage.h", + "targetsetupwidget.cpp", "targetsetupwidget.h", + "task.cpp", "task.h", + "taskfile.cpp", "taskfile.h", + "taskhub.cpp", "taskhub.h", + "taskmodel.cpp", "taskmodel.h", + "taskwindow.cpp", "taskwindow.h", + "toolchain.cpp", "toolchain.h", + "toolchaincache.h", + "toolchainconfigwidget.cpp", "toolchainconfigwidget.h", + "toolchainmanager.cpp", "toolchainmanager.h", + "toolchainoptionspage.cpp", "toolchainoptionspage.h", + "toolchainsettingsaccessor.cpp", "toolchainsettingsaccessor.h", + "treescanner.cpp", "treescanner.h", + "userfileaccessor.cpp", "userfileaccessor.h", + "vcsannotatetaskhandler.cpp", "vcsannotatetaskhandler.h", + "waitforstopdialog.cpp", "waitforstopdialog.h", + "windebuginterface.cpp", "windebuginterface.h", + "xcodebuildparser.cpp", "xcodebuildparser.h" + ] + } - Group { - name: "Project Welcome Page" - files: [ - "projectwelcomepage.cpp", - "projectwelcomepage.h" - ] - } + Group { + name: "Project Welcome Page" + files: [ + "projectwelcomepage.cpp", + "projectwelcomepage.h" + ] + } - Group { - name: "JsonWizard" - prefix: "jsonwizard/" - files: [ - "jsonfieldpage.cpp", "jsonfieldpage_p.h", "jsonfieldpage.h", - "jsonfilepage.cpp", "jsonfilepage.h", - "jsonkitspage.cpp", "jsonkitspage.h", - "jsonprojectpage.cpp", "jsonprojectpage.h", - "jsonsummarypage.cpp", "jsonsummarypage.h", - "jsonwizard.cpp", "jsonwizard.h", - "jsonwizardfactory.cpp", "jsonwizardfactory.h", - "jsonwizardfilegenerator.cpp", "jsonwizardfilegenerator.h", - "jsonwizardgeneratorfactory.cpp", "jsonwizardgeneratorfactory.h", - "jsonwizardpagefactory.cpp", "jsonwizardpagefactory.h", - "jsonwizardpagefactory_p.cpp", "jsonwizardpagefactory_p.h", - "jsonwizardscannergenerator.cpp", "jsonwizardscannergenerator.h", - "wizarddebug.h" - ] - } + Group { + name: "JsonWizard" + prefix: "jsonwizard/" + files: [ + "jsonfieldpage.cpp", "jsonfieldpage_p.h", "jsonfieldpage.h", + "jsonfilepage.cpp", "jsonfilepage.h", + "jsonkitspage.cpp", "jsonkitspage.h", + "jsonprojectpage.cpp", "jsonprojectpage.h", + "jsonsummarypage.cpp", "jsonsummarypage.h", + "jsonwizard.cpp", "jsonwizard.h", + "jsonwizardfactory.cpp", "jsonwizardfactory.h", + "jsonwizardfilegenerator.cpp", "jsonwizardfilegenerator.h", + "jsonwizardgeneratorfactory.cpp", "jsonwizardgeneratorfactory.h", + "jsonwizardpagefactory.cpp", "jsonwizardpagefactory.h", + "jsonwizardpagefactory_p.cpp", "jsonwizardpagefactory_p.h", + "jsonwizardscannergenerator.cpp", "jsonwizardscannergenerator.h", + "wizarddebug.h" + ] + } - Group { - name: "CustomWizard" - prefix: "customwizard/" - files: [ - "customwizard.cpp", "customwizard.h", - "customwizardpage.cpp", "customwizardpage.h", - "customwizardparameters.cpp", "customwizardparameters.h", - "customwizardscriptgenerator.cpp", "customwizardscriptgenerator.h" - ] - } + Group { + name: "CustomWizard" + prefix: "customwizard/" + files: [ + "customwizard.cpp", "customwizard.h", + "customwizardpage.cpp", "customwizardpage.h", + "customwizardparameters.cpp", "customwizardparameters.h", + "customwizardscriptgenerator.cpp", "customwizardscriptgenerator.h" + ] + } - Group { - name: "Device Support" - prefix: "devicesupport/" - files: [ - "desktopdevice.cpp", "desktopdevice.h", - "desktopdevicefactory.cpp", "desktopdevicefactory.h", - "devicecheckbuildstep.cpp", "devicecheckbuildstep.h", - "devicefactoryselectiondialog.cpp", "devicefactoryselectiondialog.h", - "devicemanager.cpp", "devicemanager.h", - "devicemanagermodel.cpp", "devicemanagermodel.h", - "deviceprocessesdialog.cpp", "deviceprocessesdialog.h", - "deviceprocesslist.cpp", "deviceprocesslist.h", - "devicesettingspage.cpp", "devicesettingspage.h", - "devicesettingswidget.cpp", "devicesettingswidget.h", - "devicetestdialog.cpp", "devicetestdialog.h", - "deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h", - "filetransfer.cpp", "filetransfer.h", - "filetransferinterface.h", - "idevice.cpp", "idevice.h", - "idevicefactory.cpp", "idevicefactory.h", - "idevicefwd.h", - "idevicewidget.h", - "processlist.cpp", "processlist.h", - "sshparameters.cpp", "sshparameters.h", - "sshsettings.cpp", "sshsettings.h", - "sshsettingspage.cpp", "sshsettingspage.h", - "desktopprocesssignaloperation.cpp", "desktopprocesssignaloperation.h" - ] - } + Group { + name: "Device Support" + prefix: "devicesupport/" + files: [ + "desktopdevice.cpp", "desktopdevice.h", + "desktopdevicefactory.cpp", "desktopdevicefactory.h", + "devicecheckbuildstep.cpp", "devicecheckbuildstep.h", + "devicefactoryselectiondialog.cpp", "devicefactoryselectiondialog.h", + "devicemanager.cpp", "devicemanager.h", + "devicemanagermodel.cpp", "devicemanagermodel.h", + "deviceprocessesdialog.cpp", "deviceprocessesdialog.h", + "devicesettingspage.cpp", "devicesettingspage.h", + "devicetestdialog.cpp", "devicetestdialog.h", + "deviceusedportsgatherer.cpp", "deviceusedportsgatherer.h", + "filetransfer.cpp", "filetransfer.h", + "filetransferinterface.h", + "idevice.cpp", "idevice.h", + "idevicefactory.cpp", "idevicefactory.h", + "idevicefwd.h", + "idevicewidget.h", + "processlist.cpp", "processlist.h", + "sshparameters.cpp", "sshparameters.h", + "sshsettings.cpp", "sshsettings.h", + "sshsettingspage.cpp", "sshsettingspage.h", + "desktopprocesssignaloperation.cpp", "desktopprocesssignaloperation.h" + ] + } - Group { - name: "Images" - prefix: "images/" - files: ["*.png"] - } + Group { + name: "Images" + prefix: "images/" + files: ["*.png"] + } - Group { - name: "WindowsToolChains" - condition: qbs.targetOS.contains("windows") || qtc.testsEnabled - files: [ - "msvctoolchain.cpp", - "msvctoolchain.h", - ] - } + Group { + name: "WindowsToolChains" + condition: qbs.targetOS.contains("windows") || qtc.withPluginTests + files: [ + "msvctoolchain.cpp", + "msvctoolchain.h", + ] + } - QtcTestFiles { - files: ["outputparser_test.h", "outputparser_test.cpp"] - } + QtcTestFiles { + files: ["outputparser_test.h", "outputparser_test.cpp"] + } - Group { - name: "Test resources" - condition: qtc.testsEnabled - files: ["testdata/**"] - fileTags: ["qt.core.resource_data"] - Qt.core.resourcePrefix: "/projectexplorer" - Qt.core.resourceSourceBase: path - } + Group { + name: "Test resources" + condition: qtc.withPluginTests + files: ["testdata/**"] + fileTags: ["qt.core.resource_data"] + Qt.core.resourcePrefix: "/projectexplorer" + Qt.core.resourceSourceBase: path + } - Export { - Depends { name: "Qt.network" } - } + Export { + Depends { name: "Qt.network" } } } diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc index 0cc88e3331a..0926397f423 100644 --- a/src/plugins/projectexplorer/projectexplorer.qrc +++ b/src/plugins/projectexplorer/projectexplorer.qrc @@ -20,8 +20,6 @@ <file>images/run@2x.png</file> <file>images/run_mask.png</file> <file>images/run_mask@2x.png</file> - <file>images/debugger_overlay_small.png</file> - <file>images/debugger_overlay_small@2x.png</file> <file>images/analyzer_overlay_small.png</file> <file>images/analyzer_overlay_small@2x.png</file> <file>images/devicestatusindicator.png</file> @@ -34,10 +32,6 @@ <file>images/build_hammerhead_mask@2x.png</file> <file>images/targetpanel_bottom.png</file> <file>images/window.png</file> - <file>images/continue_1_small.png</file> - <file>images/continue_1_small@2x.png</file> - <file>images/continue_2_small.png</file> - <file>images/continue_2_small@2x.png</file> <file>images/buildstepdisable.png</file> <file>images/buildstepdisable@2x.png</file> <file>images/buildstepmovedown.png</file> @@ -76,6 +70,8 @@ <file>images/fileoverlay_unknown@2x.png</file> <file>images/cancelbuild_overlay.png</file> <file>images/cancelbuild_overlay@2x.png</file> + <file>images/cmakeicon.png</file> + <file>images/cmakeicon@2x.png</file> <file>images/settingscategory_buildrun.png</file> <file>images/settingscategory_buildrun@2x.png</file> <file>images/settingscategory_devices.png</file> diff --git a/src/plugins/projectexplorer/projectexplorerconstants.cpp b/src/plugins/projectexplorer/projectexplorerconstants.cpp index 0481d82a020..0b1772c9cd5 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.cpp +++ b/src/plugins/projectexplorer/projectexplorerconstants.cpp @@ -5,7 +5,7 @@ #include "projectexplorertr.h" -#include <coreplugin/icore.h> +#include <QGuiApplication> namespace ProjectExplorer { namespace Constants { @@ -18,7 +18,7 @@ QString msgAutoDetected() QString msgAutoDetectedToolTip() { return Tr::tr("Automatically managed by %1 or the installer.") - .arg(Core::ICore::ideDisplayName()); + .arg(QGuiApplication::applicationDisplayName()); } QString msgManual() diff --git a/src/plugins/projectexplorer/projectexplorerconstants.h b/src/plugins/projectexplorer/projectexplorerconstants.h index 75054120a83..6677081f4ca 100644 --- a/src/plugins/projectexplorer/projectexplorerconstants.h +++ b/src/plugins/projectexplorer/projectexplorerconstants.h @@ -178,6 +178,9 @@ const char GENERATOR_ID_PREFIX[] = "PE.Wizard.Generator."; const char NO_RUN_MODE[]="RunConfiguration.NoRunMode"; const char NORMAL_RUN_MODE[]="RunConfiguration.NormalRunMode"; const char DEBUG_RUN_MODE[]="RunConfiguration.DebugRunMode"; +const char DAP_CMAKE_DEBUG_RUN_MODE[]="RunConfiguration.CmakeDebugRunMode"; +const char DAP_GDB_DEBUG_RUN_MODE[]="RunConfiguration.DapGdbDebugRunMode"; +const char DAP_PY_DEBUG_RUN_MODE[]="RunConfiguration.DapPyDebugRunMode"; const char QML_PROFILER_RUN_MODE[]="RunConfiguration.QmlProfilerRunMode"; const char QML_PROFILER_RUNNER[]="RunConfiguration.QmlProfilerRunner"; const char QML_PREVIEW_RUN_MODE[]="RunConfiguration.QmlPreviewRunMode"; @@ -213,6 +216,10 @@ const char SESSION_TASKFILE_KEY[] = "TaskList.File"; const char CLEAR_SYSTEM_ENVIRONMENT_KEY[] = "ProjectExplorer.BuildConfiguration.ClearSystemEnvironment"; const char USER_ENVIRONMENT_CHANGES_KEY[] = "ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"; +// Called "RemoteLinux." for backwards compatibility +const char SUPPORTS_RSYNC[] = "RemoteLinux.SupportsRSync"; +const char SUPPORTS_SFTP[] = "RemoteLinux.SupportsSftp"; + // UI texts PROJECTEXPLORER_EXPORT QString msgAutoDetected(); PROJECTEXPLORER_EXPORT QString msgAutoDetectedToolTip(); diff --git a/src/plugins/projectexplorer/projectexplorericons.cpp b/src/plugins/projectexplorer/projectexplorericons.cpp index 59b72adbda0..2a61f5bb2f8 100644 --- a/src/plugins/projectexplorer/projectexplorericons.cpp +++ b/src/plugins/projectexplorer/projectexplorericons.cpp @@ -44,16 +44,20 @@ const Icon DEVICE_DISCONNECTED_INDICATOR_OVERLAY({ {":/projectexplorer/images/devicestatusindicator.png", Theme::IconsStopToolBarColor}}); const Icon WIZARD_IMPORT_AS_PROJECT({ {":/projectexplorer/images/importasproject.png", Theme::PanelTextColorDark}}, Icon::Tint); +const Icon CMAKE_LOGO({ + {":/projectexplorer/images/cmakeicon.png", Theme::PanelTextColorMid}}, Icon::Tint); +const Icon CMAKE_LOGO_TOOLBAR({ + {":/projectexplorer/images/cmakeicon.png", Theme::IconsBaseColor}}); const Icon DEBUG_START_FLAT({ {":/projectexplorer/images/run_mask.png", Theme::IconsRunToolBarColor}, {":/projectexplorer/images/debugger_beetle_mask.png", Theme::IconsDebugColor}}); const Icon DEBUG_START_SMALL({ {":/utils/images/run_small.png", Theme::IconsRunColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); + {":/utils/images/debugger_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); const Icon DEBUG_START_SMALL_TOOLBAR({ {":/utils/images/run_small.png", Theme::IconsRunToolBarColor}, - {":/projectexplorer/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); + {":/utils/images/debugger_overlay_small.png", Theme::IconsDebugColor}}); const Icon ANALYZER_START_SMALL({ {":/utils/images/run_small.png", Theme::IconsRunColor}, {":/projectexplorer/images/analyzer_overlay_small.png", Theme::PanelTextColorMid}}, Icon::MenuTintedStyle); diff --git a/src/plugins/projectexplorer/projectexplorericons.h b/src/plugins/projectexplorer/projectexplorericons.h index 112b5a967b0..fbd46c8cc68 100644 --- a/src/plugins/projectexplorer/projectexplorericons.h +++ b/src/plugins/projectexplorer/projectexplorericons.h @@ -26,6 +26,8 @@ PROJECTEXPLORER_EXPORT extern const Utils::Icon DEVICE_CONNECTED_INDICATOR_OVERL PROJECTEXPLORER_EXPORT extern const Utils::Icon DEVICE_DISCONNECTED_INDICATOR; PROJECTEXPLORER_EXPORT extern const Utils::Icon DEVICE_DISCONNECTED_INDICATOR_OVERLAY; PROJECTEXPLORER_EXPORT extern const Utils::Icon WIZARD_IMPORT_AS_PROJECT; +PROJECTEXPLORER_EXPORT extern const Utils::Icon CMAKE_LOGO; +PROJECTEXPLORER_EXPORT extern const Utils::Icon CMAKE_LOGO_TOOLBAR; PROJECTEXPLORER_EXPORT extern const Utils::Icon DEBUG_START_FLAT; PROJECTEXPLORER_EXPORT extern const Utils::Icon DEBUG_START_SMALL; diff --git a/src/plugins/projectexplorer/projectimporter.cpp b/src/plugins/projectexplorer/projectimporter.cpp index 9c9b1f707a5..fec26598c33 100644 --- a/src/plugins/projectexplorer/projectimporter.cpp +++ b/src/plugins/projectexplorer/projectimporter.cpp @@ -5,7 +5,7 @@ #include "buildinfo.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitmanager.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" diff --git a/src/plugins/projectexplorer/projectmacro.cpp b/src/plugins/projectexplorer/projectmacro.cpp index 471cf65939d..9dfe0e2b168 100644 --- a/src/plugins/projectexplorer/projectmacro.cpp +++ b/src/plugins/projectexplorer/projectmacro.cpp @@ -24,8 +24,7 @@ QByteArray Macro::toByteArray() const case MacroType::Undefine: return QByteArray("#undef ") + key; case MacroType::Invalid: break; } - - return QByteArray(); + return {}; } QByteArray Macro::toByteArray(const Macros ¯os) diff --git a/src/plugins/projectexplorer/projectmanager.cpp b/src/plugins/projectexplorer/projectmanager.cpp index e3c8e65c59b..4943773f4b0 100644 --- a/src/plugins/projectexplorer/projectmanager.cpp +++ b/src/plugins/projectexplorer/projectmanager.cpp @@ -3,7 +3,6 @@ #include "projectmanager.h" - #include "buildconfiguration.h" #include "editorconfiguration.h" #include "project.h" @@ -27,10 +26,9 @@ #include <texteditor/texteditor.h> #include <utils/algorithm.h> -#include <utils/filepath.h> +#include <utils/persistentsettings.h> #include <utils/qtcassert.h> #include <utils/stylehelper.h> -#include <utils/qtcassert.h> #include <QDebug> #include <QMessageBox> @@ -349,7 +347,7 @@ void ProjectManagerPrivate::saveSession() depMap.insert(key, values); ++i; } - SessionManager::setSessionValue(QLatin1String("ProjectDependencies"), QVariant(depMap)); + SessionManager::setSessionValue("ProjectDependencies", QVariant(depMap)); } /*! @@ -406,14 +404,14 @@ QString ProjectManagerPrivate::sessionTitle(const FilePath &filePath) } else { return sessionName.isEmpty() ? Tr::tr("Untitled") : sessionName; } - return QString(); + return {}; } QString ProjectManagerPrivate::locationInProject(const FilePath &filePath) { const Project *project = ProjectManager::projectForFile(filePath); if (!project) - return QString(); + return {}; const FilePath parentDir = filePath.parentDir(); if (parentDir == project->projectDirectory()) @@ -520,12 +518,11 @@ Project *ProjectManager::projectWithProjectFilePath(const FilePath &filePath) [&filePath](const Project *p) { return p->projectFilePath() == filePath; }); } -void ProjectManager::configureEditor(IEditor *editor, const QString &fileName) +void ProjectManager::configureEditor(IEditor *editor, const FilePath &filePath) { if (auto textEditor = qobject_cast<TextEditor::BaseTextEditor*>(editor)) { - Project *project = projectForFile(Utils::FilePath::fromString(fileName)); // Global settings are the default. - if (project) + if (Project *project = projectForFile(filePath)) project->editorConfiguration()->configureEditor(textEditor); } } @@ -576,7 +573,8 @@ void ProjectManager::removeProjects(const QList<Project *> &remove) void ProjectManagerPrivate::restoreDependencies() { - QMap<QString, QVariant> depMap = SessionManager::sessionValue("ProjectDependencies").toMap(); + QMap<QString, QVariant> depMap = mapEntryFromStoreEntry(SessionManager::sessionValue( + "ProjectDependencies")).toMap(); auto i = depMap.constBegin(); while (i != depMap.constEnd()) { const QString &key = i.key(); @@ -655,7 +653,7 @@ void ProjectManagerPrivate::loadSession() d->m_casadeSetActive = false; // not ideal that this is in ProjectManager - Id modeId = Id::fromSetting(SessionManager::value(QLatin1String("ActiveMode"))); + Id modeId = Id::fromSetting(SessionManager::value("ActiveMode")); if (!modeId.isValid()) modeId = Id(Core::Constants::MODE_EDIT); @@ -706,8 +704,7 @@ FilePaths ProjectManager::projectsForSessionName(const QString &session) return {}; } } - return transform(reader.restoreValue(QLatin1String("ProjectList")).toStringList(), - &FilePath::fromUserInput); + return transform(reader.restoreValue("ProjectList").toStringList(), &FilePath::fromUserInput); } #ifdef WITH_TESTS diff --git a/src/plugins/projectexplorer/projectmanager.h b/src/plugins/projectexplorer/projectmanager.h index f49cc96e54c..27913a00cd0 100644 --- a/src/plugins/projectexplorer/projectmanager.h +++ b/src/plugins/projectexplorer/projectmanager.h @@ -97,7 +97,7 @@ signals: void projectFinishedParsing(ProjectExplorer::Project *project); private: - static void configureEditor(Core::IEditor *editor, const QString &fileName); + static void configureEditor(Core::IEditor *editor, const Utils::FilePath &filePath); static void configureEditors(Project *project); static void registerProjectCreator(const QString &mimeType, diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 64fabcb30ab..0af76f1bbef 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -12,8 +12,6 @@ #include "projecttree.h" #include "target.h" -#include <app/app_version.h> - #include <coreplugin/documentmanager.h> #include <coreplugin/icore.h> #include <coreplugin/iversioncontrol.h> @@ -35,11 +33,12 @@ #include <QDialogButtonBox> #include <QFileInfo> #include <QFont> +#include <QGuiApplication> #include <QHBoxLayout> #include <QLabel> +#include <QLoggingCategory> #include <QMessageBox> #include <QMimeData> -#include <QLoggingCategory> #include <QPushButton> #include <QRadioButton> #include <QVBoxLayout> @@ -191,7 +190,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const { const Node * const node = nodeForIndex(index); if (!node) - return QVariant(); + return {}; const FolderNode * const folderNode = node->asFolderNode(); const ContainerNode * const containerNode = node->asContainerNode(); @@ -248,8 +247,7 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const case Project::isParsingRole: return project && bs ? bs->isParsing() && !project->needsConfiguration() : false; } - - return QVariant(); + return {}; } Qt::ItemFlags FlatModel::flags(const QModelIndex &index) const @@ -374,20 +372,15 @@ void FlatModel::addOrRebuildProjectModel(Project *project) container->forAllChildren([this](WrapperNode *node) { if (node->m_node) { - const QString path = node->m_node->filePath().toString(); - const QString displayName = node->m_node->displayName(); - ExpandData ed(path, displayName); - if (m_toExpand.contains(ed)) + if (m_toExpand.contains(expandDataForNode(node->m_node))) emit requestExpansion(node->index()); } else { emit requestExpansion(node->index()); } }); - const QString path = container->m_node->filePath().toString(); - const QString displayName = container->m_node->displayName(); - ExpandData ed(path, displayName); - if (m_toExpand.contains(ed)) + + if (m_toExpand.contains(expandDataForNode(container->m_node))) emit requestExpansion(container->index()); } @@ -426,18 +419,14 @@ void FlatModel::onExpanded(const QModelIndex &idx) ExpandData FlatModel::expandDataForNode(const Node *node) const { - QTC_ASSERT(node, return ExpandData()); - const QString path = node->filePath().toString(); - const QString displayName = node->displayName(); - return ExpandData(path, displayName); + QTC_ASSERT(node, return {}); + return {node->filePath().toString(), node->priority()}; } void FlatModel::handleProjectAdded(Project *project) { QTC_ASSERT(project, return); - auto oldName = project->displayName(); - project->setProperty("_q_oldProjectName", oldName); connect(project, &Project::anyParsingStarted, this, [this, project]() { if (nodeForProject(project)) @@ -445,28 +434,8 @@ void FlatModel::handleProjectAdded(Project *project) }); connect(project, &Project::anyParsingFinished, this, [this, project]() { - auto wrapper = nodeForProject(project); - if (wrapper) { - // In case the project was renamed, change the name in expand data as well - // FIXME: Redesign node expansion so that it does not rely on display name of a node - auto oldName = project->property("_q_oldProjectName").toString(); - auto currentName = project->displayName(); - if (oldName != currentName) { - project->setProperty("_q_oldProjectName", currentName); - auto node = wrapper->m_node; - ExpandData oldData(node->filePath().toString(), oldName); - ExpandData newData(oldData.path, currentName); - auto it = m_toExpand.find(oldData); - if (it != m_toExpand.end()) { - m_toExpand.erase(it); - m_toExpand.insert(newData); - emit requestExpansion(wrapper->index()); - } else if (m_toExpand.contains(newData)) { - emit requestExpansion(wrapper->index()); - } - } + if (nodeForProject(project)) parsingStateChanged(project); - } emit ProjectTree::instance()->nodeActionsChanged(); }); addOrRebuildProjectModel(project); @@ -498,7 +467,7 @@ void FlatModel::saveExpandData() { // TODO if there are multiple ProjectTreeWidgets, the last one saves the data QList<QVariant> data = Utils::transform<QList>(m_toExpand, &ExpandData::toSettings); - SessionManager::setValue(QLatin1String("ProjectTree.ExpandData"), data); + SessionManager::setValue("ProjectTree.ExpandData", data); } void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen) @@ -521,8 +490,7 @@ void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet< } } } - if (!isHidden && !seen->contains(subFolderNode)) { - seen->insert(subFolderNode); + if (!isHidden && Utils::insert(*seen, subFolderNode)) { auto node = new WrapperNode(subFolderNode); parent->appendChild(node); addFolderNode(node, subFolderNode, seen); @@ -531,10 +499,8 @@ void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet< addFolderNode(parent, subFolderNode, seen); } } else if (FileNode *fileNode = node->asFileNode()) { - if (!seen->contains(fileNode)) { - seen->insert(fileNode); + if (Utils::insert(*seen, fileNode)) parent->appendChild(new WrapperNode(fileNode)); - } } } @@ -603,7 +569,7 @@ public: setWindowTitle(Tr::tr("Choose Drop Action")); const bool offerFileIo = !defaultTargetDir.isEmpty(); auto * const layout = new QVBoxLayout(this); - const QString idename(Core::Constants::IDE_DISPLAY_NAME); + const QString idename(QGuiApplication::applicationDisplayName()); layout->addWidget(new QLabel(Tr::tr("You just dragged some files from one project node to " "another.\nWhat should %1 do now?").arg(idename), this)); auto * const copyButton = new QRadioButton(this); diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index e5c8a9a3c64..11ab5fe0561 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -911,7 +911,7 @@ bool ProjectNode::addSubProject(const FilePath &proFilePath) QStringList ProjectNode::subProjectFileNamePatterns() const { - return QStringList(); + return {}; } bool ProjectNode::removeSubProject(const FilePath &proFilePath) diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 0e0068ba7b0..5e424b62bbc 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -142,7 +142,7 @@ public: virtual ContainerNode *asContainerNode() { return nullptr; } virtual const ContainerNode *asContainerNode() const { return nullptr; } - virtual QString buildKey() const { return QString(); } + virtual QString buildKey() const { return {}; } static bool sortByPath(const Node *a, const Node *b); void setParentFolderNode(FolderNode *parentFolder); @@ -248,7 +248,7 @@ public: const Utils::FilePath &overrideBaseDir = Utils::FilePath(), const FolderNodeFactory &factory = [](const Utils::FilePath &fn) { return std::make_unique<FolderNode>(fn); }); - void compress(); + virtual void compress(); // takes ownership of newNode. // Will delete newNode if oldNode is not a child of this node. diff --git a/src/plugins/projectexplorer/projectsettingswidget.cpp b/src/plugins/projectexplorer/projectsettingswidget.cpp index ab8d4270b28..43912b4968c 100644 --- a/src/plugins/projectexplorer/projectsettingswidget.cpp +++ b/src/plugins/projectexplorer/projectsettingswidget.cpp @@ -65,4 +65,14 @@ void ProjectSettingsWidget::setGlobalSettingsId(Utils::Id globalId) m_globalSettingsId = globalId; } +bool ProjectSettingsWidget::expanding() const +{ + return m_expanding; +} + +void ProjectSettingsWidget::setExpanding(bool expanding) +{ + m_expanding = expanding; +} + } // ProjectExplorer diff --git a/src/plugins/projectexplorer/projectsettingswidget.h b/src/plugins/projectexplorer/projectsettingswidget.h index 3971acb0a88..a8d564eb291 100644 --- a/src/plugins/projectexplorer/projectsettingswidget.h +++ b/src/plugins/projectexplorer/projectsettingswidget.h @@ -27,6 +27,9 @@ public: bool isUseGlobalSettingsLabelVisible() const; Utils::Id globalSettingsId() const; + bool expanding() const; + void setExpanding(bool expanding); + protected: void setUseGlobalSettingsCheckBoxVisible(bool visible); void setUseGlobalSettingsLabelVisible(bool visible); @@ -41,6 +44,7 @@ private: bool m_useGlobalSettingsCheckBoxEnabled = true; bool m_useGlobalSettingsCheckBoxVisibleVisible = true; bool m_useGlobalSettingsLabelVisibleVisible = true; + bool m_expanding = false; Utils::Id m_globalSettingsId; }; diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index a3d608ebd80..cb505a43a7c 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -33,7 +33,6 @@ #include <QLineEdit> #include <QMenu> #include <QPainter> -#include <QSettings> #include <QStyledItemDelegate> #include <QToolButton> #include <QVBoxLayout> @@ -126,6 +125,7 @@ class ProjectTreeView : public NavigationTreeView public: ProjectTreeView() { + setObjectName("projectTreeView"); // used by Squish setEditTriggers(QAbstractItemView::EditKeyPressed); setContextMenuPolicy(Qt::CustomContextMenu); setDragEnabled(true); @@ -638,7 +638,7 @@ void ProjectTreeWidgetFactory::saveSettings(QtcSettings *settings, int position, { auto ptw = qobject_cast<ProjectTreeWidget *>(widget); Q_ASSERT(ptw); - const QString baseKey = kBaseKey + QString::number(position); + const Key baseKey = numberedKey(kBaseKey, position); settings->setValueWithDefault(baseKey + kProjectFilterKey, ptw->projectFilter(), kProjectFilterDefault); @@ -657,11 +657,11 @@ void ProjectTreeWidgetFactory::saveSettings(QtcSettings *settings, int position, settings->setValueWithDefault(baseKey + kSyncKey, ptw->autoSynchronization(), kSyncDefault); } -void ProjectTreeWidgetFactory::restoreSettings(QSettings *settings, int position, QWidget *widget) +void ProjectTreeWidgetFactory::restoreSettings(QtcSettings *settings, int position, QWidget *widget) { auto ptw = qobject_cast<ProjectTreeWidget *>(widget); Q_ASSERT(ptw); - const QString baseKey = kBaseKey + QString::number(position); + const Key baseKey = numberedKey(kBaseKey, position); ptw->setProjectFilter( settings->value(baseKey + kProjectFilterKey, kProjectFilterDefault).toBool()); ptw->setGeneratedFilesFilter( diff --git a/src/plugins/projectexplorer/projecttreewidget.h b/src/plugins/projectexplorer/projecttreewidget.h index d4124287ea9..c6c2cad2d1a 100644 --- a/src/plugins/projectexplorer/projecttreewidget.h +++ b/src/plugins/projectexplorer/projecttreewidget.h @@ -92,7 +92,7 @@ public: ProjectTreeWidgetFactory(); Core::NavigationView createWidget() override; - void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; }; diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 5def44f9482..4fb585dee57 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -81,10 +81,10 @@ QVariant ProjectModel::data(const QModelIndex &index, int role) const const Id projectBase = PROJECT_BASE_ID; if (Command *cmd = ActionManager::command(projectBase.withSuffix(index.row() + 1))) return cmd->keySequence().toString(QKeySequence::NativeText); - return QVariant(); + return {}; } default: - return QVariant(); + return {}; } } @@ -523,14 +523,14 @@ public: QAction *action = new QAction(Tr::tr("Remove Project from Recent Projects")); const auto projectModel = qobject_cast<ProjectModel *>(model); contextMenu.addAction(action); - connect(action, &QAction::triggered, [idx, projectModel](){ + connect(action, &QAction::triggered, this, [idx, projectModel] { const QVariant projectFile = idx.data(ProjectModel::FilePathRole); ProjectExplorerPlugin::removeFromRecentProjects(FilePath::fromVariant(projectFile)); projectModel->resetProjects(); }); contextMenu.addSeparator(); action = new QAction(Tr::tr("Clear Recent Project List")); - connect(action, &QAction::triggered, [projectModel]() { + connect(action, &QAction::triggered, this, [projectModel] { ProjectExplorerPlugin::clearRecentProjects(); projectModel->resetProjects(); }); diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index a69ee4c855a..66ce26e41f6 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -246,8 +246,7 @@ QVariant MiscSettingsPanelItem::data(int column, int role) const if (role == ActiveItemRole) // We are the active one. return QVariant::fromValue<TreeItem *>(const_cast<MiscSettingsPanelItem *>(this)); - - return QVariant(); + return {}; } Qt::ItemFlags MiscSettingsPanelItem::flags(int column) const @@ -300,7 +299,7 @@ public: if (0 <= m_currentPanelIndex && m_currentPanelIndex < childCount()) return childAt(m_currentPanelIndex)->data(column, role); } - return QVariant(); + return {}; } bool setData(int column, const QVariant &data, int role) override @@ -363,7 +362,7 @@ public: if (m_currentChildIndex == 1) return m_miscItem->data(column, role); } - return QVariant(); + return {}; } bool setData(int column, const QVariant &dat, int role) override @@ -742,8 +741,17 @@ public: void handleManageKits() { - if (ProjectItem *projectItem = m_projectsModel.rootItem()->childAt(0)) { - KitOptionsPage::showKit(KitManager::kit(Id::fromSetting(projectItem->data(0, KitIdRole)))); + const QModelIndexList selected = m_selectorTree->selectionModel()->selectedIndexes(); + if (!selected.isEmpty()) { + TreeItem *treeItem = m_projectsModel.itemForIndex(selected.front()); + while (treeItem) { + const Id kitId = Id::fromSetting(treeItem->data(0, KitIdRole)); + if (kitId.isValid()) { + setSelectectKitId(kitId); + break; + } + treeItem = treeItem->parent(); + } } ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID); } @@ -854,7 +862,7 @@ void ProjectWindow::savePersistentSettings() const { if (!centralWidget()) return; - QSettings * const settings = ICore::settings(); + QtcSettings * const settings = ICore::settings(); settings->beginGroup(PROJECT_WINDOW_KEY); saveSettings(settings); settings->endGroup(); @@ -864,7 +872,7 @@ void ProjectWindow::loadPersistentSettings() { if (!centralWidget()) return; - QSettings * const settings = ICore::settings(); + QtcSettings * const settings = ICore::settings(); settings->beginGroup(PROJECT_WINDOW_KEY); restoreSettings(settings); settings->endGroup(); diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 75f573430dd..d8a815a7545 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -108,7 +108,7 @@ QVariant AddNewTree::data(int, int role) const case Qt::UserRole: return QVariant::fromValue(static_cast<void*>(node())); default: - return QVariant(); + return {}; } } @@ -201,7 +201,7 @@ QString BestNodeSelector::deployingProjects() const { if (m_deploys) return m_deployText; - return QString(); + return {}; } // -------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp index 81d4951ed0c..4ac7f3947a8 100644 --- a/src/plugins/projectexplorer/rawprojectpart.cpp +++ b/src/plugins/projectexplorer/rawprojectpart.cpp @@ -6,7 +6,7 @@ #include "abi.h" #include "buildconfiguration.h" #include "buildsystem.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "target.h" diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 29b59ad8984..4a1e03d031d 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -6,8 +6,7 @@ #include "buildconfiguration.h" #include "buildsystem.h" #include "environmentaspect.h" -#include "kitinformation.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -15,7 +14,6 @@ #include "projectmanager.h" #include "projectnodes.h" #include "runconfigurationaspects.h" -#include "runcontrol.h" #include "target.h" #include <coreplugin/icontext.h> @@ -29,6 +27,7 @@ #include <utils/detailswidget.h> #include <utils/layoutbuilder.h> #include <utils/outputformatter.h> +#include <utils/processinterface.h> #include <utils/qtcassert.h> #include <utils/utilsicons.h> #include <utils/variablechooser.h> @@ -37,7 +36,6 @@ #include <QPushButton> #include <QTimer> #include <QLoggingCategory> -#include <QSettings> using namespace Utils; using namespace ProjectExplorer::Internal; @@ -45,25 +43,7 @@ using namespace ProjectExplorer::Internal; namespace ProjectExplorer { const char BUILD_KEY[] = "ProjectExplorer.RunConfiguration.BuildKey"; - -/////////////////////////////////////////////////////////////////////// -// -// ISettingsAspect -// -/////////////////////////////////////////////////////////////////////// - -ISettingsAspect::ISettingsAspect() = default; - -QWidget *ISettingsAspect::createConfigWidget() const -{ - QTC_ASSERT(m_configWidgetCreator, return nullptr); - return m_configWidgetCreator(); -} - -void ISettingsAspect::setConfigWidgetCreator(const ConfigWidgetCreator &configWidgetCreator) -{ - m_configWidgetCreator = configWidgetCreator; -} +const char CUSTOMIZED_KEY[] = "ProjectExplorer.RunConfiguration.Customized"; /////////////////////////////////////////////////////////////////////// @@ -82,14 +62,16 @@ GlobalOrProjectAspect::~GlobalOrProjectAspect() delete m_projectSettings; } -void GlobalOrProjectAspect::setProjectSettings(ISettingsAspect *settings) +void GlobalOrProjectAspect::setProjectSettings(AspectContainer *settings) { m_projectSettings = settings; + m_projectSettings->setAutoApply(true); } -void GlobalOrProjectAspect::setGlobalSettings(ISettingsAspect *settings) +void GlobalOrProjectAspect::setGlobalSettings(AspectContainer *settings) { m_globalSettings = settings; + m_projectSettings->setAutoApply(false); } void GlobalOrProjectAspect::setUsingGlobalSettings(bool value) @@ -97,26 +79,26 @@ void GlobalOrProjectAspect::setUsingGlobalSettings(bool value) m_useGlobalSettings = value; } -ISettingsAspect *GlobalOrProjectAspect::currentSettings() const +AspectContainer *GlobalOrProjectAspect::currentSettings() const { return m_useGlobalSettings ? m_globalSettings : m_projectSettings; } -void GlobalOrProjectAspect::fromMap(const QVariantMap &map) +void GlobalOrProjectAspect::fromMap(const Store &map) { if (m_projectSettings) m_projectSettings->fromMap(map); - m_useGlobalSettings = map.value(id().toString() + ".UseGlobalSettings", true).toBool(); + m_useGlobalSettings = map.value(id().toKey() + ".UseGlobalSettings", true).toBool(); } -void GlobalOrProjectAspect::toMap(QVariantMap &map) const +void GlobalOrProjectAspect::toMap(Store &map) const { if (m_projectSettings) m_projectSettings->toMap(map); - map.insert(id().toString() + ".UseGlobalSettings", m_useGlobalSettings); + map.insert(id().toKey() + ".UseGlobalSettings", m_useGlobalSettings); } -void GlobalOrProjectAspect::toActiveMap(QVariantMap &data) const +void GlobalOrProjectAspect::toActiveMap(Store &data) const { if (m_useGlobalSettings) m_globalSettings->toMap(data); @@ -130,7 +112,7 @@ void GlobalOrProjectAspect::toActiveMap(QVariantMap &data) const void GlobalOrProjectAspect::resetProjectToGlobalSettings() { QTC_ASSERT(m_globalSettings, return); - QVariantMap map; + Store map; m_globalSettings->toMap(map); if (m_projectSettings) m_projectSettings->fromMap(map); @@ -158,10 +140,12 @@ void GlobalOrProjectAspect::resetProjectToGlobalSettings() static std::vector<RunConfiguration::AspectFactory> theAspectFactories; +static QList<RunConfigurationFactory *> g_runConfigurationFactories; + RunConfiguration::RunConfiguration(Target *target, Utils::Id id) : ProjectConfiguration(target, id) { - QTC_CHECK(target && target == this->target()); + forceDisplayNameSerialization(); connect(target, &Target::parsingFinished, this, &RunConfiguration::update); m_expander.setDisplayName(Tr::tr("Run Settings")); @@ -239,14 +223,47 @@ bool RunConfiguration::isConfigured() const return !Utils::anyOf(checkForIssues(), [](const Task &t) { return t.type == Task::Error; }); } +bool RunConfiguration::isCustomized() const +{ + if (m_customized) + return true; + Store state; + toMapSimple(state); + + // TODO: Why do we save this at all? It's a computed value. + state.remove("RunConfiguration.WorkingDirectory.default"); + + return state != m_pristineState; +} + +bool RunConfiguration::hasCreator() const +{ + for (RunConfigurationFactory *factory : std::as_const(g_runConfigurationFactories)) { + if (factory->runConfigurationId() == id()) { + if (factory->supportsBuildKey(target(), buildKey())) + return true; + } + } + return false; +} + +void RunConfiguration::setPristineState() +{ + if (!m_customized) { + m_pristineState.clear(); + toMapSimple(m_pristineState); + m_pristineState.remove("RunConfiguration.WorkingDirectory.default"); + } +} + void RunConfiguration::addAspectFactory(const AspectFactory &aspectFactory) { theAspectFactories.push_back(aspectFactory); } -QMap<Utils::Id, QVariantMap> RunConfiguration::settingsData() const +QMap<Id, Store> RunConfiguration::settingsData() const { - QMap<Utils::Id, QVariantMap> data; + QMap<Id, Store> data; for (BaseAspect *aspect : *this) aspect->toActiveMap(data[aspect->id()]); return data; @@ -275,10 +292,15 @@ Task RunConfiguration::createConfigurationIssue(const QString &description) cons return BuildSystemTask(Task::Error, description); } -QVariantMap RunConfiguration::toMap() const +void RunConfiguration::toMap(Store &map) const { - QVariantMap map = ProjectConfiguration::toMap(); + toMapSimple(map); + map.insert(CUSTOMIZED_KEY, isCustomized()); +} +void RunConfiguration::toMapSimple(Store &map) const +{ + ProjectConfiguration::toMap(map); map.insert(BUILD_KEY, m_buildKey); // FIXME: Remove this id mangling, e.g. by using a separate entry for the build key. @@ -286,8 +308,6 @@ QVariantMap RunConfiguration::toMap() const const Utils::Id mangled = id().withSuffix(m_buildKey); map.insert(settingsIdKey(), mangled.toSetting()); } - - return map; } void RunConfiguration::setCommandLineGetter(const CommandLineGetter &cmdGetter) @@ -339,11 +359,13 @@ ProjectNode *RunConfiguration::productNode() const }); } -bool RunConfiguration::fromMap(const QVariantMap &map) +void RunConfiguration::fromMap(const Store &map) { - if (!ProjectConfiguration::fromMap(map)) - return false; + ProjectConfiguration::fromMap(map); + if (hasError()) + return; + m_customized = m_customized || map.value(CUSTOMIZED_KEY, false).toBool(); m_buildKey = map.value(BUILD_KEY).toString(); if (m_buildKey.isEmpty()) { @@ -356,8 +378,6 @@ bool RunConfiguration::fromMap(const QVariantMap &map) if (magicIndex != -1) m_buildKey = m_buildKey.mid(magicIndex + magicSeparator.length()); } - - return true; } /*! @@ -395,12 +415,12 @@ bool RunConfiguration::fromMap(const QVariantMap &map) \brief Returns a \l Runnable described by this RunConfiguration. */ -Runnable RunConfiguration::runnable() const +ProcessRunData RunConfiguration::runnable() const { - Runnable r; + ProcessRunData r; r.command = commandLine(); if (auto workingDirectoryAspect = aspect<WorkingDirectoryAspect>()) - r.workingDirectory = r.command.executable().withNewPath(workingDirectoryAspect->workingDirectory().path()); + r.workingDirectory = r.command.executable().withNewMappedPath(workingDirectoryAspect->workingDirectory()); if (auto environmentAspect = aspect<EnvironmentAspect>()) r.environment = environmentAspect->environment(); if (m_runnableModifier) @@ -408,6 +428,14 @@ Runnable RunConfiguration::runnable() const return r; } +QVariantHash RunConfiguration::extraData() const +{ + QVariantHash data; + if (auto forwardingAspect = aspect<X11ForwardingAspect>()) + data.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); + return data; +} + /*! \class ProjectExplorer::RunConfigurationFactory \inmodule QtCreator @@ -430,8 +458,6 @@ Runnable RunConfiguration::runnable() const ExtensionSystem::IPlugin::initialize() method. */ -static QList<RunConfigurationFactory *> g_runConfigurationFactories; - /*! Constructs a RunConfigurationFactory instance and registers it into a global list. @@ -499,6 +525,14 @@ RunConfigurationFactory::availableCreators(Target *target) const }); } +bool RunConfigurationFactory::supportsBuildKey(Target *target, const QString &key) const +{ + if (!canHandle(target)) + return false; + const QList<BuildTargetInfo> buildTargets = target->buildSystem()->applicationTargets(); + return anyOf(buildTargets, [&key](const BuildTargetInfo &info) { return info.buildKey == key; }); +} + /*! Adds a device type for which this RunConfigurationFactory can create RunConfigurations. @@ -582,19 +616,22 @@ RunConfiguration *RunConfigurationCreationInfo::create(Target *target) const rc->m_buildKey = buildKey; rc->update(); rc->setDisplayName(displayName); + rc->setPristineState(); return rc; } -RunConfiguration *RunConfigurationFactory::restore(Target *parent, const QVariantMap &map) +RunConfiguration *RunConfigurationFactory::restore(Target *parent, const Store &map) { for (RunConfigurationFactory *factory : std::as_const(g_runConfigurationFactories)) { if (factory->canHandle(parent)) { const Utils::Id id = idFromMap(map); if (id.name().startsWith(factory->m_runConfigurationId.name())) { RunConfiguration *rc = factory->create(parent); - if (rc->fromMap(map)) { + rc->fromMap(map); + if (!rc->hasError()) { rc->update(); + rc->setPristineState(); return rc; } delete rc; @@ -607,7 +644,9 @@ RunConfiguration *RunConfigurationFactory::restore(Target *parent, const QVarian RunConfiguration *RunConfigurationFactory::clone(Target *parent, RunConfiguration *source) { - return restore(parent, source->toMap()); + Store map; + source->toMap(map); + return restore(parent, map); } const QList<RunConfigurationCreationInfo> RunConfigurationFactory::creatorsForTarget(Target *parent) @@ -646,4 +685,11 @@ FixedRunConfigurationFactory::availableCreators(Target *parent) const return {rci}; } +bool FixedRunConfigurationFactory::supportsBuildKey(Target *target, const QString &key) const +{ + Q_UNUSED(target) + Q_UNUSED(key) + return false; +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index 59704141562..629fcd2c614 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -11,50 +11,24 @@ #include <utils/environment.h> #include <utils/macroexpander.h> -#include <QWidget> - #include <functional> #include <memory> -namespace Utils { class OutputFormatter; } +namespace Utils { +class OutputFormatter; +class ProcessRunData; +} namespace ProjectExplorer { class BuildConfiguration; class BuildSystem; class GlobalOrProjectAspect; class ProjectNode; -class Runnable; class RunConfigurationFactory; class RunConfiguration; class RunConfigurationCreationInfo; class Target; -/** - * An interface for a hunk of global or per-project - * configuration data. - * - */ - -class PROJECTEXPLORER_EXPORT ISettingsAspect : public Utils::AspectContainer -{ - Q_OBJECT - -public: - ISettingsAspect(); - - /// Create a configuration widget for this settings aspect. - QWidget *createConfigWidget() const; - -protected: - using ConfigWidgetCreator = std::function<QWidget *()>; - void setConfigWidgetCreator(const ConfigWidgetCreator &configWidgetCreator); - - friend class GlobalOrProjectAspect; - - ConfigWidgetCreator m_configWidgetCreator; -}; - - /** * An interface to facilitate switching between hunks of * global and per-project configuration data. @@ -69,32 +43,31 @@ public: GlobalOrProjectAspect(); ~GlobalOrProjectAspect() override; - void setProjectSettings(ISettingsAspect *settings); - void setGlobalSettings(ISettingsAspect *settings); + void setProjectSettings(Utils::AspectContainer *settings); + void setGlobalSettings(Utils::AspectContainer *settings); bool isUsingGlobalSettings() const { return m_useGlobalSettings; } void setUsingGlobalSettings(bool value); void resetProjectToGlobalSettings(); - ISettingsAspect *projectSettings() const { return m_projectSettings; } - ISettingsAspect *globalSettings() const { return m_globalSettings; } - ISettingsAspect *currentSettings() const; + Utils::AspectContainer *projectSettings() const { return m_projectSettings; } + Utils::AspectContainer *currentSettings() const; struct Data : Utils::BaseAspect::Data { - ISettingsAspect *currentSettings = nullptr; + Utils::AspectContainer *currentSettings = nullptr; }; protected: friend class RunConfiguration; - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &data) const override; - void toActiveMap(QVariantMap &data) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &data) const override; + void toActiveMap(Utils::Store &data) const override; private: bool m_useGlobalSettings = false; - ISettingsAspect *m_projectSettings = nullptr; // Owned if present. - ISettingsAspect *m_globalSettings = nullptr; // Not owned. + Utils::AspectContainer *m_projectSettings = nullptr; // Owned if present. + Utils::AspectContainer *m_globalSettings = nullptr; // Not owned. }; // Documentation inside. @@ -111,17 +84,21 @@ public: QWidget *createConfigurationWidget(); bool isConfigured() const; + bool isCustomized() const; + bool hasCreator() const; virtual Tasks checkForIssues() const { return {}; } + void setPristineState(); using CommandLineGetter = std::function<Utils::CommandLine()>; void setCommandLineGetter(const CommandLineGetter &cmdGetter); Utils::CommandLine commandLine() const; bool isPrintEnvironmentEnabled() const; - using RunnableModifier = std::function<void(Runnable &)>; + using RunnableModifier = std::function<void(Utils::ProcessRunData &)>; void setRunnableModifier(const RunnableModifier &extraModifier); - virtual Runnable runnable() const; + virtual Utils::ProcessRunData runnable() const; + virtual QVariantHash extraData() const; // Return a handle to the build system target that created this run configuration. // May return an empty string if no target built the executable! @@ -131,7 +108,7 @@ public: ProjectExplorer::ProjectNode *productNode() const; - template <class T = ISettingsAspect> T *currentSettings(Utils::Id id) const + template <class T = Utils::AspectContainer> T *currentSettings(Utils::Id id) const { if (auto a = qobject_cast<GlobalOrProjectAspect *>(aspect(id))) return qobject_cast<T *>(a->currentSettings()); @@ -145,7 +122,7 @@ public: addAspectFactory([](Target *target) { return new T(target); }); } - QMap<Utils::Id, QVariantMap> settingsData() const; // FIXME: Merge into aspectData? + QMap<Utils::Id, Utils::Store> settingsData() const; // FIXME: Merge into aspectData? Utils::AspectContainerData aspectData() const; void update(); @@ -168,8 +145,9 @@ protected: private: // Any additional data should be handled by aspects. - bool fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + void fromMap(const Utils::Store &map) final; + void toMap(Utils::Store &map) const final; + void toMapSimple(Utils::Store &map) const; static void addAspectFactory(const AspectFactory &aspectFactory); @@ -182,6 +160,8 @@ private: RunnableModifier m_runnableModifier; Updater m_updater; Utils::MacroExpander m_expander; + Utils::Store m_pristineState; + bool m_customized = false; }; class RunConfigurationCreationInfo @@ -207,7 +187,7 @@ public: RunConfigurationFactory operator=(const RunConfigurationFactory &) = delete; virtual ~RunConfigurationFactory(); - static RunConfiguration *restore(Target *parent, const QVariantMap &map); + static RunConfiguration *restore(Target *parent, const Utils::Store &map); static RunConfiguration *clone(Target *parent, RunConfiguration *source); static const QList<RunConfigurationCreationInfo> creatorsForTarget(Target *parent); @@ -217,6 +197,7 @@ public: protected: virtual QList<RunConfigurationCreationInfo> availableCreators(Target *target) const; + virtual bool supportsBuildKey(Target *target, const QString &key) const; using RunConfigurationCreator = std::function<RunConfiguration *(Target *)>; @@ -238,6 +219,7 @@ private: RunConfiguration *create(Target *target) const; friend class RunConfigurationCreationInfo; + friend class RunConfiguration; RunConfigurationCreator m_creator; Utils::Id m_runConfigurationId; QList<Utils::Id> m_supportedProjectTypes; @@ -251,13 +233,12 @@ public: explicit FixedRunConfigurationFactory(const QString &displayName, bool addDeviceName = false); - QList<RunConfigurationCreationInfo> availableCreators(Target *parent) const override; - private: + QList<RunConfigurationCreationInfo> availableCreators(Target *parent) const override; + bool supportsBuildKey(Target *target, const QString &key) const override; + const QString m_fixedBuildTarget; const bool m_decorateTargetName; }; } // namespace ProjectExplorer - -Q_DECLARE_METATYPE(ProjectExplorer::ISettingsAspect *); diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 52b594d0a14..f9a3491de3d 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -6,7 +6,7 @@ #include "devicesupport/devicemanager.h" #include "devicesupport/idevice.h" #include "environmentaspect.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "projectexplorer.h" #include "projectexplorersettings.h" #include "projectexplorertr.h" @@ -47,7 +47,8 @@ namespace ProjectExplorer { The initial value is provided as a hint from the build systems. */ -TerminalAspect::TerminalAspect() +TerminalAspect::TerminalAspect(AspectContainer *container) + : BaseAspect(container) { setDisplayName(Tr::tr("Terminal")); setId("TerminalAspect"); @@ -79,7 +80,7 @@ void TerminalAspect::addToLayout(LayoutItem &parent) /*! \reimp */ -void TerminalAspect::fromMap(const QVariantMap &map) +void TerminalAspect::fromMap(const Store &map) { if (map.contains(settingsKey())) { m_useTerminal = map.value(settingsKey()).toBool(); @@ -95,7 +96,7 @@ void TerminalAspect::fromMap(const QVariantMap &map) /*! \reimp */ -void TerminalAspect::toMap(QVariantMap &data) const +void TerminalAspect::toMap(Store &data) const { if (m_userSet) data.insert(settingsKey(), m_useTerminal); @@ -152,15 +153,24 @@ bool TerminalAspect::isUserSet() const working directory for running the executable. */ -WorkingDirectoryAspect::WorkingDirectoryAspect(const MacroExpander *expander, - EnvironmentAspect *envAspect) - : m_envAspect(envAspect), m_macroExpander(expander) +WorkingDirectoryAspect::WorkingDirectoryAspect(AspectContainer *container) + : BaseAspect(container) { setDisplayName(Tr::tr("Working Directory")); setId("WorkingDirectoryAspect"); setSettingsKey("RunConfiguration.WorkingDirectory"); } +void WorkingDirectoryAspect::setMacroExpander(const MacroExpander *expander) +{ + m_macroExpander = expander; +} + +void WorkingDirectoryAspect::setEnvironment(EnvironmentAspect *envAspect) +{ + m_envAspect = envAspect; +} + /*! \reimp */ @@ -204,7 +214,7 @@ void WorkingDirectoryAspect::resetPath() /*! \reimp */ -void WorkingDirectoryAspect::fromMap(const QVariantMap &map) +void WorkingDirectoryAspect::fromMap(const Store &map) { m_workingDirectory = FilePath::fromString(map.value(settingsKey()).toString()); m_defaultWorkingDirectory = FilePath::fromString(map.value(settingsKey() + ".default").toString()); @@ -219,7 +229,7 @@ void WorkingDirectoryAspect::fromMap(const QVariantMap &map) /*! \reimp */ -void WorkingDirectoryAspect::toMap(QVariantMap &data) const +void WorkingDirectoryAspect::toMap(Store &data) const { const QString wd = m_workingDirectory == m_defaultWorkingDirectory ? QString() : m_workingDirectory.toString(); @@ -297,8 +307,8 @@ PathChooser *WorkingDirectoryAspect::pathChooser() const arguments for an executable. */ -ArgumentsAspect::ArgumentsAspect(const MacroExpander *macroExpander) - : m_macroExpander(macroExpander) +ArgumentsAspect::ArgumentsAspect(AspectContainer *container) + : BaseAspect(container) { setDisplayName(Tr::tr("Arguments")); setId("ArgumentsAspect"); @@ -309,6 +319,11 @@ ArgumentsAspect::ArgumentsAspect(const MacroExpander *macroExpander) m_labelText = Tr::tr("Command line arguments:"); } +void ArgumentsAspect::setMacroExpander(const MacroExpander *expander) +{ + m_macroExpander = expander; +} + /*! Returns the main value of this aspect. @@ -382,7 +397,7 @@ void ArgumentsAspect::resetArguments() /*! \reimp */ -void ArgumentsAspect::fromMap(const QVariantMap &map) +void ArgumentsAspect::fromMap(const Store &map) { QVariant args = map.value(settingsKey()); // Until 3.7 a QStringList was stored for Remote Linux @@ -404,7 +419,7 @@ void ArgumentsAspect::fromMap(const QVariantMap &map) /*! \reimp */ -void ArgumentsAspect::toMap(QVariantMap &map) const +void ArgumentsAspect::toMap(Store &map) const { saveToMap(map, m_arguments, QString(), settingsKey()); saveToMap(map, m_multiLine, false, settingsKey() + ".multi"); @@ -495,18 +510,16 @@ void ArgumentsAspect::addToLayout(LayoutItem &builder) by the build system's parsing results with an optional manual override. */ -ExecutableAspect::ExecutableAspect(Target *target, ExecutionDeviceSelector selector) - : m_target(target), m_selector(selector) +ExecutableAspect::ExecutableAspect(AspectContainer *container) + : BaseAspect(container) { setDisplayName(Tr::tr("Executable")); setId("ExecutableAspect"); + setReadOnly(true); addDataExtractor(this, &ExecutableAspect::executable, &Data::executable); m_executable.setPlaceHolderText(Tr::tr("Enter the path to the executable")); m_executable.setLabelText(Tr::tr("Executable:")); - m_executable.setDisplayStyle(StringAspect::LabelDisplay); - - updateDevice(); connect(&m_executable, &StringAspect::changed, this, &ExecutableAspect::changed); } @@ -533,8 +546,11 @@ ExecutableAspect::~ExecutableAspect() m_alternativeExecutable = nullptr; } -void ExecutableAspect::updateDevice() +void ExecutableAspect::setDeviceSelector(Target *target, ExecutionDeviceSelector selector) { + m_target = target; + m_selector = selector; + const IDevice::ConstPtr dev = executionDevice(m_target, m_selector); const OsType osType = dev ? dev->osType() : HostOsInfo::hostOs(); @@ -548,7 +564,7 @@ void ExecutableAspect::updateDevice() \sa Utils::PathChooser::setHistoryCompleter() */ -void ExecutableAspect::setHistoryCompleter(const QString &historyCompleterKey) +void ExecutableAspect::setHistoryCompleter(const Key &historyCompleterKey) { m_executable.setHistoryCompleter(historyCompleterKey); if (m_alternativeExecutable) @@ -579,14 +595,9 @@ void ExecutableAspect::setEnvironment(const Environment &env) m_alternativeExecutable->setEnvironment(env); } -/*! - Sets the display \a style for aspect. - - \sa Utils::StringAspect::setDisplayStyle() -*/ -void ExecutableAspect::setDisplayStyle(StringAspect::DisplayStyle style) +void ExecutableAspect::setReadOnly(bool readOnly) { - m_executable.setDisplayStyle(style); + m_executable.setReadOnly(readOnly); } /*! @@ -598,14 +609,13 @@ void ExecutableAspect::setDisplayStyle(StringAspect::DisplayStyle style) \sa Utils::StringAspect::makeCheckable() */ -void ExecutableAspect::makeOverridable(const QString &overridingKey, const QString &useOverridableKey) +void ExecutableAspect::makeOverridable(const Key &overridingKey, const Key &useOverridableKey) { QTC_ASSERT(!m_alternativeExecutable, return); - m_alternativeExecutable = new StringAspect; - m_alternativeExecutable->setDisplayStyle(StringAspect::LineEditDisplay); + m_alternativeExecutable = new FilePathAspect; m_alternativeExecutable->setLabelText(Tr::tr("Alternate executable on device:")); m_alternativeExecutable->setSettingsKey(overridingKey); - m_alternativeExecutable->makeCheckable(StringAspect::CheckBoxPlacement::Right, + m_alternativeExecutable->makeCheckable(CheckBoxPlacement::Right, Tr::tr("Use this command instead"), useOverridableKey); connect(m_alternativeExecutable, &StringAspect::changed, this, &ExecutableAspect::changed); @@ -621,8 +631,8 @@ void ExecutableAspect::makeOverridable(const QString &overridingKey, const QStri FilePath ExecutableAspect::executable() const { FilePath exe = m_alternativeExecutable && m_alternativeExecutable->isChecked() - ? m_alternativeExecutable->filePath() - : m_executable.filePath(); + ? (*m_alternativeExecutable)() + : m_executable(); if (const IDevice::ConstPtr dev = executionDevice(m_target, m_selector)) exe = dev->rootPath().withNewMappedPath(exe); @@ -667,14 +677,14 @@ void ExecutableAspect::setPlaceHolderText(const QString &placeHolderText) */ void ExecutableAspect::setExecutable(const FilePath &executable) { - m_executable.setFilePath(executable); + m_executable.setValue(executable); m_executable.setShowToolTipOnLabel(true); } /*! Sets the settings key to \a key. */ -void ExecutableAspect::setSettingsKey(const QString &key) +void ExecutableAspect::setSettingsKey(const Key &key) { BaseAspect::setSettingsKey(key); m_executable.setSettingsKey(key); @@ -683,7 +693,7 @@ void ExecutableAspect::setSettingsKey(const QString &key) /*! \reimp */ -void ExecutableAspect::fromMap(const QVariantMap &map) +void ExecutableAspect::fromMap(const Store &map) { m_executable.fromMap(map); if (m_alternativeExecutable) @@ -693,7 +703,7 @@ void ExecutableAspect::fromMap(const QVariantMap &map) /*! \reimp */ -void ExecutableAspect::toMap(QVariantMap &map) const +void ExecutableAspect::toMap(Store &map) const { m_executable.toMap(map); if (m_alternativeExecutable) @@ -713,7 +723,8 @@ void ExecutableAspect::toMap(QVariantMap &map) const on Windows and LD_LIBRARY_PATH everywhere else. */ -UseLibraryPathsAspect::UseLibraryPathsAspect() +UseLibraryPathsAspect::UseLibraryPathsAspect(AspectContainer *container) + : BoolAspect(container) { setId("UseLibraryPath"); setSettingsKey("RunConfiguration.UseLibrarySearchPath"); @@ -738,7 +749,8 @@ UseLibraryPathsAspect::UseLibraryPathsAspect() DYLD_IMAGE_SUFFIX environment variable should be used on Mac. */ -UseDyldSuffixAspect::UseDyldSuffixAspect() +UseDyldSuffixAspect::UseDyldSuffixAspect(AspectContainer *container) + : BoolAspect(container) { setId("UseDyldSuffix"); setSettingsKey("RunConfiguration.UseDyldImageSuffix"); @@ -754,7 +766,8 @@ UseDyldSuffixAspect::UseDyldSuffixAspect() application should run with root permissions. */ -RunAsRootAspect::RunAsRootAspect() +RunAsRootAspect::RunAsRootAspect(AspectContainer *container) + : BoolAspect(container) { setId("RunAsRoot"); setSettingsKey("RunConfiguration.RunAsRoot"); @@ -783,7 +796,8 @@ Interpreter::Interpreter(const QString &_id, to use with files or projects using an interpreted language. */ -InterpreterAspect::InterpreterAspect() +InterpreterAspect::InterpreterAspect(AspectContainer *container) + : BaseAspect(container) { addDataExtractor(this, &InterpreterAspect::currentInterpreter, &Data::interpreter); } @@ -795,6 +809,8 @@ Interpreter InterpreterAspect::currentInterpreter() const void InterpreterAspect::updateInterpreters(const QList<Interpreter> &interpreters) { + if (m_interpreters == interpreters) + return; m_interpreters = interpreters; if (m_comboBox) updateComboBox(); @@ -802,9 +818,11 @@ void InterpreterAspect::updateInterpreters(const QList<Interpreter> &interpreter void InterpreterAspect::setDefaultInterpreter(const Interpreter &interpreter) { + if (m_defaultId == interpreter.id) + return; m_defaultId = interpreter.id; if (m_currentId.isEmpty()) - m_currentId = m_defaultId; + setCurrentInterpreter(interpreter); } void InterpreterAspect::setCurrentInterpreter(const Interpreter &interpreter) @@ -815,17 +833,16 @@ void InterpreterAspect::setCurrentInterpreter(const Interpreter &interpreter) return; m_comboBox->setCurrentIndex(index); } else { - m_currentId = interpreter.id; + setCurrentInterpreterId(interpreter.id); } - emit changed(); } -void InterpreterAspect::fromMap(const QVariantMap &map) +void InterpreterAspect::fromMap(const Store &map) { - m_currentId = map.value(settingsKey(), m_defaultId).toString(); + setCurrentInterpreterId(map.value(settingsKey(), m_defaultId).toString()); } -void InterpreterAspect::toMap(QVariantMap &map) const +void InterpreterAspect::toMap(Store &map) const { if (m_currentId != m_defaultId) saveToMap(map, m_currentId, QString(), settingsKey()); @@ -841,22 +858,29 @@ void InterpreterAspect::addToLayout(LayoutItem &builder) this, &InterpreterAspect::updateCurrentInterpreter); auto manageButton = new QPushButton(Tr::tr("Manage...")); - connect(manageButton, &QPushButton::clicked, [this] { + connect(manageButton, &QPushButton::clicked, this, [this] { Core::ICore::showOptionsDialog(m_settingsDialogId); }); builder.addItems({Tr::tr("Interpreter:"), m_comboBox.data(), manageButton}); } +void InterpreterAspect::setCurrentInterpreterId(const QString &id) +{ + if (id == m_currentId) + return; + m_currentId = id; + emit changed(); +} + void InterpreterAspect::updateCurrentInterpreter() { const int index = m_comboBox->currentIndex(); if (index < 0) return; QTC_ASSERT(index < m_interpreters.size(), return); - m_currentId = m_interpreters[index].id; m_comboBox->setToolTip(m_interpreters[index].command.toUserOutput()); - emit changed(); + setCurrentInterpreterId(m_interpreters[index].id); } void InterpreterAspect::updateComboBox() @@ -893,8 +917,8 @@ static QString defaultDisplay() return qtcEnvironmentVariable("DISPLAY"); } -X11ForwardingAspect::X11ForwardingAspect(const MacroExpander *expander) - : m_macroExpander(expander) +X11ForwardingAspect::X11ForwardingAspect(AspectContainer *container) + : StringAspect(container) { setLabelText(Tr::tr("X11 Forwarding:")); setDisplayStyle(LineEditDisplay); @@ -907,10 +931,33 @@ X11ForwardingAspect::X11ForwardingAspect(const MacroExpander *expander) addDataExtractor(this, &X11ForwardingAspect::display, &Data::display); } +void X11ForwardingAspect::setMacroExpander(const MacroExpander *expander) +{ + m_macroExpander = expander; +} + QString X11ForwardingAspect::display() const { QTC_ASSERT(m_macroExpander, return value()); return !isChecked() ? QString() : m_macroExpander->expandProcessArgs(value()); } + +/*! + \class ProjectExplorer::SymbolFileAspect + \inmodule QtCreator + + \brief The SymbolFileAspect class lets a user specify a symbol file + for debugging. +*/ + + +SymbolFileAspect::SymbolFileAspect(AspectContainer *container) + : FilePathAspect(container) +{} + +MainScriptAspect::MainScriptAspect(AspectContainer *container) + : FilePathAspect(container) +{} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/runconfigurationaspects.h b/src/plugins/projectexplorer/runconfigurationaspects.h index 9fc7364772d..bec508d69e8 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.h +++ b/src/plugins/projectexplorer/runconfigurationaspects.h @@ -27,7 +27,7 @@ class PROJECTEXPLORER_EXPORT TerminalAspect : public Utils::BaseAspect Q_OBJECT public: - TerminalAspect(); + explicit TerminalAspect(Utils::AspectContainer *container = nullptr); void addToLayout(Layouting::LayoutItem &parent) override; @@ -43,8 +43,8 @@ public: }; private: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; void calculateUseTerminal(); @@ -59,20 +59,22 @@ class PROJECTEXPLORER_EXPORT WorkingDirectoryAspect : public Utils::BaseAspect Q_OBJECT public: - explicit WorkingDirectoryAspect(const Utils::MacroExpander *expander, - EnvironmentAspect *envAspect); + explicit WorkingDirectoryAspect(Utils::AspectContainer *container = nullptr); void addToLayout(Layouting::LayoutItem &parent) override; + Utils::FilePath operator()() const { return workingDirectory(); } Utils::FilePath workingDirectory() const; Utils::FilePath defaultWorkingDirectory() const; Utils::FilePath unexpandedWorkingDirectory() const; void setDefaultWorkingDirectory(const Utils::FilePath &defaultWorkingDirectory); Utils::PathChooser *pathChooser() const; + void setMacroExpander(const Utils::MacroExpander *expander); + void setEnvironment(EnvironmentAspect *envAspect); private: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; void resetPath(); @@ -89,10 +91,11 @@ class PROJECTEXPLORER_EXPORT ArgumentsAspect : public Utils::BaseAspect Q_OBJECT public: - explicit ArgumentsAspect(const Utils::MacroExpander *macroExpander); + explicit ArgumentsAspect(Utils::AspectContainer *container = nullptr); void addToLayout(Layouting::LayoutItem &parent) override; + QString operator()() const { return arguments(); } QString arguments() const; QString unexpandedArguments() const; @@ -100,6 +103,7 @@ public: void setLabelText(const QString &labelText); void setResetter(const std::function<QString()> &resetter); void resetArguments(); + void setMacroExpander(const Utils::MacroExpander *macroExpander); struct Data : BaseAspect::Data { @@ -107,8 +111,8 @@ public: }; private: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; QWidget *setupChooser(); @@ -129,7 +133,7 @@ class PROJECTEXPLORER_EXPORT UseLibraryPathsAspect : public Utils::BoolAspect Q_OBJECT public: - UseLibraryPathsAspect(); + UseLibraryPathsAspect(Utils::AspectContainer *container = nullptr); }; class PROJECTEXPLORER_EXPORT UseDyldSuffixAspect : public Utils::BoolAspect @@ -137,7 +141,7 @@ class PROJECTEXPLORER_EXPORT UseDyldSuffixAspect : public Utils::BoolAspect Q_OBJECT public: - UseDyldSuffixAspect(); + UseDyldSuffixAspect(Utils::AspectContainer *container = nullptr); }; class PROJECTEXPLORER_EXPORT RunAsRootAspect : public Utils::BoolAspect @@ -145,7 +149,7 @@ class PROJECTEXPLORER_EXPORT RunAsRootAspect : public Utils::BoolAspect Q_OBJECT public: - RunAsRootAspect(); + RunAsRootAspect(Utils::AspectContainer *container = nullptr); }; class PROJECTEXPLORER_EXPORT ExecutableAspect : public Utils::BaseAspect @@ -155,21 +159,23 @@ class PROJECTEXPLORER_EXPORT ExecutableAspect : public Utils::BaseAspect public: enum ExecutionDeviceSelector { HostDevice, BuildDevice, RunDevice }; - explicit ExecutableAspect(Target *target, ExecutionDeviceSelector selector); + explicit ExecutableAspect(Utils::AspectContainer *container = nullptr); ~ExecutableAspect() override; + Utils::FilePath operator()() const { return executable(); } Utils::FilePath executable() const; void setExecutable(const Utils::FilePath &executable); - void setSettingsKey(const QString &key); - void makeOverridable(const QString &overridingKey, const QString &useOverridableKey); + void setDeviceSelector(Target *target, ExecutionDeviceSelector selector); + void setSettingsKey(const Utils::Key &key); + void makeOverridable(const Utils::Key &overridingKey, const Utils::Key &useOverridableKey); void addToLayout(Layouting::LayoutItem &parent) override; void setLabelText(const QString &labelText); void setPlaceHolderText(const QString &placeHolderText); - void setHistoryCompleter(const QString &historyCompleterKey); + void setHistoryCompleter(const Utils::Key &historyCompleterKey); void setExpectedKind(const Utils::PathChooser::Kind expectedKind); void setEnvironment(const Utils::Environment &env); - void setDisplayStyle(Utils::StringAspect::DisplayStyle style); + void setReadOnly(bool readOnly); struct Data : BaseAspect::Data { @@ -177,25 +183,24 @@ public: }; protected: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; private: QString executableText() const; - void updateDevice(); - Utils::StringAspect m_executable; - Utils::StringAspect *m_alternativeExecutable = nullptr; + Utils::FilePathAspect m_executable; + Utils::FilePathAspect *m_alternativeExecutable = nullptr; Target *m_target = nullptr; ExecutionDeviceSelector m_selector = RunDevice; }; -class PROJECTEXPLORER_EXPORT SymbolFileAspect : public Utils::StringAspect +class PROJECTEXPLORER_EXPORT SymbolFileAspect : public Utils::FilePathAspect { Q_OBJECT public: - SymbolFileAspect() = default; + SymbolFileAspect(Utils::AspectContainer *container = nullptr); }; class PROJECTEXPLORER_EXPORT Interpreter @@ -225,7 +230,7 @@ class PROJECTEXPLORER_EXPORT InterpreterAspect : public Utils::BaseAspect Q_OBJECT public: - InterpreterAspect(); + InterpreterAspect(Utils::AspectContainer *container = nullptr); Interpreter currentInterpreter() const; void updateInterpreters(const QList<Interpreter> &interpreters); @@ -233,13 +238,14 @@ public: void setCurrentInterpreter(const Interpreter &interpreter); void setSettingsDialogId(Utils::Id id) { m_settingsDialogId = id; } - void fromMap(const QVariantMap &) override; - void toMap(QVariantMap &) const override; + void fromMap(const Utils::Store &) override; + void toMap(Utils::Store &) const override; void addToLayout(Layouting::LayoutItem &parent) override; struct Data : Utils::BaseAspect::Data { Interpreter interpreter; }; private: + void setCurrentInterpreterId(const QString &id); void updateCurrentInterpreter(); void updateComboBox(); QList<Interpreter> m_interpreters; @@ -249,12 +255,12 @@ private: Utils::Id m_settingsDialogId; }; -class PROJECTEXPLORER_EXPORT MainScriptAspect : public Utils::StringAspect +class PROJECTEXPLORER_EXPORT MainScriptAspect : public Utils::FilePathAspect { Q_OBJECT public: - MainScriptAspect() = default; + MainScriptAspect(Utils::AspectContainer *container = nullptr); }; class PROJECTEXPLORER_EXPORT X11ForwardingAspect : public Utils::StringAspect @@ -262,7 +268,9 @@ class PROJECTEXPLORER_EXPORT X11ForwardingAspect : public Utils::StringAspect Q_OBJECT public: - X11ForwardingAspect(const Utils::MacroExpander *macroExpander); + X11ForwardingAspect(Utils::AspectContainer *container = nullptr); + + void setMacroExpander(const Utils::MacroExpander *macroExpander); struct Data : StringAspect::Data { QString display; }; diff --git a/src/plugins/projectexplorer/runcontrol.cpp b/src/plugins/projectexplorer/runcontrol.cpp index 21f944989b8..8eb6f5e6ba0 100644 --- a/src/plugins/projectexplorer/runcontrol.cpp +++ b/src/plugins/projectexplorer/runcontrol.cpp @@ -9,7 +9,7 @@ #include "devicesupport/idevice.h" #include "devicesupport/idevicefactory.h" #include "devicesupport/sshsettings.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "project.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" @@ -21,6 +21,8 @@ #include <coreplugin/icore.h> +#include <solutions/tasking/tasktree.h> + #include <utils/algorithm.h> #include <utils/checkablemessagebox.h> #include <utils/fileinprojectfinder.h> @@ -200,7 +202,7 @@ public: QList<RunWorker *> stopDependencies; QString id; - QVariantMap data; + Store data; bool supportsReRunning = true; bool essential = false; }; @@ -211,9 +213,7 @@ enum class RunControlState Starting, // Actual process/tool starts. Running, // All good and running. Stopping, // initiateStop() was called, stop application/tool - Stopped, // all good, but stopped. Can possibly be re-started - Finishing, // Application tab manually closed - Finished // Final state, will self-destruct with deleteLater() + Stopped // all good, but stopped. Can possibly be re-started }; static QString stateName(RunControlState s) @@ -225,8 +225,6 @@ static QString stateName(RunControlState s) SN(RunControlState::Running) SN(RunControlState::Stopping) SN(RunControlState::Stopped) - SN(RunControlState::Finishing) - SN(RunControlState::Finished) } return QString("<unknown: %1>").arg(int(s)); # undef SN @@ -236,13 +234,14 @@ class RunControlPrivateData { public: QString displayName; - Runnable runnable; + ProcessRunData runnable; + QVariantHash extraData; IDevice::ConstPtr device; Icon icon; const MacroExpander *macroExpander = nullptr; AspectContainerData aspectData; QString buildKey; - QMap<Id, QVariantMap> settingsData; + QMap<Id, Store> settingsData; Id runConfigId; BuildTargetInfo buildTargetInfo; FilePath buildDirectory; @@ -259,6 +258,9 @@ public: QList<QPointer<RunWorker>> m_workers; RunControlState state = RunControlState::Initialized; bool printEnvironment = false; + bool autoDelete = false; + bool m_supportsReRunning = true; + std::optional<Tasking::Group> m_runRecipe; }; class RunControlPrivate : public QObject, public RunControlPrivateData @@ -274,7 +276,7 @@ public: ~RunControlPrivate() override { - QTC_CHECK(state == RunControlState::Finished || state == RunControlState::Initialized); + QTC_CHECK(state == RunControlState::Stopped || state == RunControlState::Initialized); disconnect(); q = nullptr; qDeleteAll(m_workers); @@ -306,9 +308,13 @@ public: static bool isAllowedTransition(RunControlState from, RunControlState to); bool supportsReRunning() const; + bool isUsingTaskTree() const { return bool(m_runRecipe); } + void startTaskTree(); + void checkAutoDeleteAndEmitStopped(); RunControl *q; Id runMode; + std::unique_ptr<Tasking::TaskTree> m_taskTree; }; } // Internal @@ -331,6 +337,7 @@ void RunControl::copyDataFromRunConfiguration(RunConfiguration *runConfig) QTC_ASSERT(runConfig, return); d->runConfigId = runConfig->id(); d->runnable = runConfig->runnable(); + d->extraData = runConfig->extraData(); d->displayName = runConfig->expandedDisplayName(); d->buildKey = runConfig->buildKey(); d->settingsData = runConfig->settingsData(); @@ -407,31 +414,54 @@ RunControl::~RunControl() #endif } +void RunControl::setAutoDeleteOnStop(bool autoDelete) +{ + d->autoDelete = autoDelete; +} + +void RunControl::setRunRecipe(const Tasking::Group &group) +{ + d->m_runRecipe = group; +} + void RunControl::initiateStart() { - emit aboutToStart(); - d->initiateStart(); + if (d->isUsingTaskTree()) { + d->startTaskTree(); + } else { + emit aboutToStart(); + d->initiateStart(); + } } void RunControl::initiateReStart() { - emit aboutToStart(); - d->initiateReStart(); + if (d->isUsingTaskTree()) { + d->startTaskTree(); + } else { + emit aboutToStart(); + d->initiateReStart(); + } } void RunControl::initiateStop() { - d->initiateStop(); + if (d->isUsingTaskTree()) { + d->m_taskTree.reset(); + d->checkAutoDeleteAndEmitStopped(); + } else { + d->initiateStop(); + } } void RunControl::forceStop() { - d->forceStop(); -} - -void RunControl::initiateFinish() -{ - QTimer::singleShot(0, d.get(), &RunControlPrivate::initiateFinish); + if (d->isUsingTaskTree()) { + d->m_taskTree.reset(); + emit stopped(); + } else { + d->forceStop(); + } } RunWorker *RunControl::createWorker(Id workerId) @@ -472,6 +502,11 @@ bool RunControl::canRun(Id runMode, Id deviceType, Utils::Id runConfigId) return false; } +void RunControl::postMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine) +{ + emit appendMessage((appendNewLine && !msg.endsWith('\n')) ? msg + '\n': msg, format); +} + void RunControlPrivate::initiateStart() { checkState(RunControlState::Initialized); @@ -542,7 +577,7 @@ void RunControlPrivate::continueStart() void RunControlPrivate::initiateStop() { - if (state != RunControlState::Starting && state != RunControlState::Running) + if (state == RunControlState::Initialized) qDebug() << "Unexpected initiateStop() in state" << stateName(state); setState(RunControlState::Stopping); @@ -596,12 +631,8 @@ void RunControlPrivate::continueStopOrFinish() } RunControlState targetState; - if (state == RunControlState::Finishing) { - targetState = RunControlState::Finished; - } else { - checkState(RunControlState::Stopping); + if (state == RunControlState::Stopping) targetState = RunControlState::Stopped; - } if (allDone) { debugMessage("All Stopped"); @@ -613,7 +644,7 @@ void RunControlPrivate::continueStopOrFinish() void RunControlPrivate::forceStop() { - if (state == RunControlState::Finished) { + if (state == RunControlState::Stopped) { debugMessage("Was finished, too late to force Stop"); return; } @@ -648,14 +679,6 @@ void RunControlPrivate::forceStop() debugMessage("All Stopped"); } -void RunControlPrivate::initiateFinish() -{ - setState(RunControlState::Finishing); - debugMessage("Ramping down"); - - continueStopOrFinish(); -} - void RunControlPrivate::onWorkerStarted(RunWorker *worker) { worker->d->state = RunWorkerState::Running; @@ -688,11 +711,9 @@ void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg) initiateStop(); break; case RunControlState::Stopping: - case RunControlState::Finishing: continueStopOrFinish(); break; case RunControlState::Stopped: - case RunControlState::Finished: QTC_CHECK(false); // Should not happen. continueStopOrFinish(); break; @@ -723,7 +744,7 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker) break; } - if (state == RunControlState::Finishing || state == RunControlState::Stopping) { + if (state == RunControlState::Stopping) { continueStopOrFinish(); return; } else if (worker->isEssential()) { @@ -793,7 +814,7 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker) void RunControlPrivate::showError(const QString &msg) { if (!msg.isEmpty()) - emit q->appendMessage(msg + '\n', ErrorMessageFormat); + q->postMessage(msg + '\n', ErrorMessageFormat); } void RunControl::setupFormatter(OutputFormatter *formatter) const @@ -824,7 +845,7 @@ bool RunControl::isPrintEnvironmentEnabled() const return d->printEnvironment; } -const Runnable &RunControl::runnable() const +const ProcessRunData &RunControl::runnable() const { return d->runnable; } @@ -861,12 +882,12 @@ void RunControl::setEnvironment(const Environment &environment) const QVariantHash &RunControl::extraData() const { - return d->runnable.extraData; + return d->extraData; } void RunControl::setExtraData(const QVariantHash &extraData) { - d->runnable.extraData = extraData; + d->extraData = extraData; } QString RunControl::displayName() const @@ -926,7 +947,7 @@ const BaseAspect::Data *RunControl::aspect(BaseAspect::Data::ClassId classId) co return d->aspectData.aspect(classId); } -QVariantMap RunControl::settingsData(Id id) const +Store RunControl::settingsData(Id id) const { return d->settingsData.value(id); } @@ -1003,8 +1024,15 @@ void RunControl::setPromptToStop(const std::function<bool (bool *)> &promptToSto d->promptToStop = promptToStop; } +void RunControl::setSupportsReRunning(bool reRunningSupported) +{ + d->m_supportsReRunning = reRunningSupported; +} + bool RunControl::supportsReRunning() const { + if (d->isUsingTaskTree()) + return d->m_supportsReRunning; return d->supportsReRunning(); } @@ -1019,23 +1047,50 @@ bool RunControlPrivate::supportsReRunning() const return true; } +void RunControlPrivate::startTaskTree() +{ + using namespace Tasking; + + m_taskTree.reset(new TaskTree(*m_runRecipe)); + connect(m_taskTree.get(), &TaskTree::started, q, &RunControl::started); + const auto finalize = [this] { + m_taskTree.release()->deleteLater(); + checkAutoDeleteAndEmitStopped(); + }; + connect(m_taskTree.get(), &TaskTree::done, this, finalize); + connect(m_taskTree.get(), &TaskTree::errorOccurred, this, finalize); + m_taskTree->start(); +} + +void RunControlPrivate::checkAutoDeleteAndEmitStopped() +{ + if (autoDelete) { + debugMessage("All finished. Deleting myself"); + q->deleteLater(); + } else { + q->setApplicationProcessHandle(Utils::ProcessHandle()); + } + emit q->stopped(); +} + bool RunControl::isRunning() const { + if (d->isUsingTaskTree()) + return d->m_taskTree.get(); return d->state == RunControlState::Running; } bool RunControl::isStarting() const { + if (d->isUsingTaskTree()) + return false; return d->state == RunControlState::Starting; } -bool RunControl::isStopping() const -{ - return d->state == RunControlState::Stopping; -} - bool RunControl::isStopped() const { + if (d->isUsingTaskTree()) + return !d->m_taskTree.get(); return d->state == RunControlState::Stopped; } @@ -1067,7 +1122,9 @@ bool RunControl::showPromptToStopDialog(const QString &title, text, decider, QMessageBox::Yes | QMessageBox::Cancel, - QMessageBox::Yes); + QMessageBox::Yes, + QMessageBox::Yes, + buttonTexts); return selected == QMessageBox::Yes; } @@ -1083,26 +1140,15 @@ bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlStat { switch (from) { case RunControlState::Initialized: - return to == RunControlState::Starting - || to == RunControlState::Finishing; + return to == RunControlState::Starting; case RunControlState::Starting: - return to == RunControlState::Running - || to == RunControlState::Stopping - || to == RunControlState::Finishing; + return to == RunControlState::Running || to == RunControlState::Stopping; case RunControlState::Running: - return to == RunControlState::Stopping - || to == RunControlState::Stopped - || to == RunControlState::Finishing; + return to == RunControlState::Stopping || to == RunControlState::Stopped; case RunControlState::Stopping: - return to == RunControlState::Stopped - || to == RunControlState::Finishing; + return to == RunControlState::Stopped; case RunControlState::Stopped: - return to == RunControlState::Starting - || to == RunControlState::Finishing; - case RunControlState::Finishing: - return to == RunControlState::Finished; - case RunControlState::Finished: - return false; + return to != RunControlState::Initialized; } return false; } @@ -1130,13 +1176,7 @@ void RunControlPrivate::setState(RunControlState newState) emit q->started(); break; case RunControlState::Stopped: - q->setApplicationProcessHandle(Utils::ProcessHandle()); - emit q->stopped(); - break; - case RunControlState::Finished: - emit q->finished(); - debugMessage("All finished. Deleting myself"); - q->deleteLater(); + checkAutoDeleteAndEmitStopped(); break; default: break; @@ -1302,7 +1342,7 @@ void SimpleTargetRunnerPrivate::handleStandardOutput() const QByteArray data = m_process.readAllRawStandardOutput(); const QString msg = m_outputCodec->toUnicode( data.constData(), data.length(), &m_outputCodecState); - q->appendMessageChunk(msg, StdOutFormat); + q->appendMessage(msg, StdOutFormat, false); } void SimpleTargetRunnerPrivate::handleStandardError() @@ -1310,7 +1350,7 @@ void SimpleTargetRunnerPrivate::handleStandardError() const QByteArray data = m_process.readAllRawStandardError(); const QString msg = m_outputCodec->toUnicode( data.constData(), data.length(), &m_errorCodecState); - q->appendMessageChunk(msg, StdErrFormat); + q->appendMessage(msg, StdErrFormat, false); } void SimpleTargetRunnerPrivate::start() @@ -1670,15 +1710,7 @@ void RunWorker::reportFailure(const QString &msg) */ void RunWorker::appendMessage(const QString &msg, OutputFormat format, bool appendNewLine) { - if (!appendNewLine || msg.endsWith('\n')) - emit d->runControl->appendMessage(msg, format); - else - emit d->runControl->appendMessage(msg + '\n', format); -} - -void RunWorker::appendMessageChunk(const QString &msg, OutputFormat format) -{ - emit d->runControl->appendMessage(msg, format); + d->runControl->postMessage(msg, format, appendNewLine); } IDevice::ConstPtr RunWorker::device() const @@ -1706,12 +1738,12 @@ void RunWorker::setId(const QString &id) d->id = id; } -void RunWorker::recordData(const QString &channel, const QVariant &data) +void RunWorker::recordData(const Key &channel, const QVariant &data) { d->data[channel] = data; } -QVariant RunWorker::recordedData(const QString &channel) const +QVariant RunWorker::recordedData(const Key &channel) const { return d->data[channel]; } @@ -1721,11 +1753,6 @@ void RunWorker::setSupportsReRunning(bool reRunningSupported) d->supportsReRunning = reRunningSupported; } -bool RunWorker::supportsReRunning() const -{ - return d->supportsReRunning; -} - QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const FilePath &program) { QString failedToStart = Tr::tr("The process failed to start."); @@ -1743,7 +1770,7 @@ QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, cons // "The last waitFor...() function timed out. " // "The state of QProcess is unchanged, and you can try calling " // "waitFor...() again." - return QString(); // sic! + return {}; // sic! case QProcess::WriteError: msg = Tr::tr("An error occurred when attempting to write " "to the process. For example, the process may not be running, " diff --git a/src/plugins/projectexplorer/runcontrol.h b/src/plugins/projectexplorer/runcontrol.h index 84a5b5c1667..b31aa21fc47 100644 --- a/src/plugins/projectexplorer/runcontrol.h +++ b/src/plugins/projectexplorer/runcontrol.h @@ -19,10 +19,13 @@ #include <functional> #include <memory> +namespace Tasking { class Group; } + namespace Utils { class Icon; class MacroExpander; class OutputLineParser; +class ProcessRunData; } // Utils namespace ProjectExplorer { @@ -36,18 +39,6 @@ class RunWorkerPrivate; class SimpleTargetRunnerPrivate; } // Internal - -class PROJECTEXPLORER_EXPORT Runnable -{ -public: - Runnable() = default; - - Utils::CommandLine command; - Utils::FilePath workingDirectory; - Utils::Environment environment; - QVariantHash extraData; -}; - class PROJECTEXPLORER_EXPORT RunWorker : public QObject { Q_OBJECT @@ -63,12 +54,11 @@ public: void setId(const QString &id); - void recordData(const QString &channel, const QVariant &data); - QVariant recordedData(const QString &channel) const; + void recordData(const Utils::Key &channel, const QVariant &data); + QVariant recordedData(const Utils::Key &channel) const; // Part of read-only interface of RunControl for convenience. void appendMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); - void appendMessageChunk(const QString &msg, Utils::OutputFormat format); IDeviceConstPtr device() const; // States @@ -82,7 +72,6 @@ public: void reportFailure(const QString &msg = QString()); void setSupportsReRunning(bool reRunningSupported); - bool supportsReRunning() const; static QString userMessageForProcessError(QProcess::ProcessError, const Utils::FilePath &programName); @@ -156,15 +145,20 @@ public: void copyDataFromRunConfiguration(RunConfiguration *runConfig); void copyDataFromRunControl(RunControl *runControl); + void setAutoDeleteOnStop(bool autoDelete); + + void setRunRecipe(const Tasking::Group &group); + void initiateStart(); void initiateReStart(); void initiateStop(); void forceStop(); - void initiateFinish(); bool promptToStop(bool *optionalPrompt = nullptr) const; void setPromptToStop(const std::function<bool(bool *)> &promptToStop); + // Note: Works only in the task tree mode + void setSupportsReRunning(bool reRunningSupported); bool supportsReRunning() const; QString displayName() const; @@ -172,7 +166,6 @@ public: bool isRunning() const; bool isStarting() const; - bool isStopping() const; bool isStopped() const; void setIcon(const Utils::Icon &icon); @@ -198,7 +191,7 @@ public: Utils::FilePath buildDirectory() const; Utils::Environment buildEnvironment() const; - QVariantMap settingsData(Utils::Id id) const; + Utils::Store settingsData(Utils::Id id) const; Utils::FilePath targetFilePath() const; Utils::FilePath projectFilePath() const; @@ -207,7 +200,7 @@ public: Utils::Id runMode() const; bool isPrintEnvironmentEnabled() const; - const Runnable &runnable() const; + const Utils::ProcessRunData &runnable() const; const Utils::CommandLine &commandLine() const; void setCommandLine(const Utils::CommandLine &command); @@ -232,14 +225,14 @@ public: bool createMainWorker(); static bool canRun(Utils::Id runMode, Utils::Id deviceType, Utils::Id runConfigId); + void postMessage(const QString &msg, Utils::OutputFormat format, bool appendNewLine = true); signals: void appendMessage(const QString &msg, Utils::OutputFormat format); void aboutToStart(); void started(); void stopped(); - void finished(); - void applicationProcessHandleChanged(QPrivateSignal); // Use setApplicationProcessHandle + void applicationProcessHandleChanged(QPrivateSignal); private: void setDevice(const IDeviceConstPtr &device); @@ -279,8 +272,8 @@ private: void start() final; void stop() final; - const Runnable &runnable() const = delete; - void setRunnable(const Runnable &) = delete; + const Utils::ProcessRunData &runnable() const = delete; + void setRunnable(const Utils::ProcessRunData &) = delete; const std::unique_ptr<Internal::SimpleTargetRunnerPrivate> d; }; diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.cpp b/src/plugins/projectexplorer/runsettingspropertiespage.cpp index 43edee9b668..2c675b0a043 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.cpp +++ b/src/plugins/projectexplorer/runsettingspropertiespage.cpp @@ -53,6 +53,7 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : m_addRunToolButton = new QPushButton(Tr::tr("Add..."), this); m_removeRunToolButton = new QPushButton(Tr::tr("Remove"), this); + m_removeAllRunConfigsButton = new QPushButton(Tr::tr("Remove All"), this); m_renameRunButton = new QPushButton(Tr::tr("Rename..."), this); m_cloneRunButton = new QPushButton(Tr::tr("Clone..."), this); @@ -92,9 +93,10 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : m_gridLayout->addWidget(m_runConfigurationCombo, 4, 1, 1, 1); m_gridLayout->addWidget(m_addRunToolButton, 4, 2, 1, 1); m_gridLayout->addWidget(m_removeRunToolButton, 4, 3, 1, 1); - m_gridLayout->addWidget(m_renameRunButton, 4, 4, 1, 1); - m_gridLayout->addWidget(m_cloneRunButton, 4, 5, 1, 1); - m_gridLayout->addItem(spacer1, 4, 6, 1, 1); + m_gridLayout->addWidget(m_removeAllRunConfigsButton, 4, 4, 1, 1); + m_gridLayout->addWidget(m_renameRunButton, 4, 5, 1, 1); + m_gridLayout->addWidget(m_cloneRunButton, 4, 6, 1, 1); + m_gridLayout->addItem(spacer1, 4, 7, 1, 1); m_gridLayout->addWidget(runWidget, 5, 0, 1, -1); m_gridLayout->addItem(spacer2, 6, 0, 1, 1); @@ -144,7 +146,7 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : m_runConfigurationCombo->setModel(model); m_runConfigurationCombo->setCurrentIndex(model->indexFor(rc)); - m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); + updateRemoveToolButtons(); m_renameRunButton->setEnabled(rc); m_cloneRunButton->setEnabled(rc); @@ -156,20 +158,22 @@ RunSettingsWidget::RunSettingsWidget(Target *target) : this, &RunSettingsWidget::currentRunConfigurationChanged); connect(m_removeRunToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeRunConfiguration); + connect(m_removeAllRunConfigsButton, &QAbstractButton::clicked, + this, &RunSettingsWidget::removeAllRunConfigurations); connect(m_renameRunButton, &QAbstractButton::clicked, this, &RunSettingsWidget::renameRunConfiguration); connect(m_cloneRunButton, &QAbstractButton::clicked, this, &RunSettingsWidget::cloneRunConfiguration); connect(m_target, &Target::addedRunConfiguration, - this, &RunSettingsWidget::updateRemoveToolButton); + this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::removedRunConfiguration, - this, &RunSettingsWidget::updateRemoveToolButton); + this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::addedDeployConfiguration, - this, &RunSettingsWidget::updateRemoveToolButton); + this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::removedDeployConfiguration, - this, &RunSettingsWidget::updateRemoveToolButton); + this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::activeRunConfigurationChanged, this, &RunSettingsWidget::activeRunConfigurationChanged); @@ -188,7 +192,7 @@ void RunSettingsWidget::showAddRunConfigDialog() QTC_CHECK(newRC->id() == rci.factory->runConfigurationId()); m_target->addRunConfiguration(newRC); m_target->setActiveRunConfiguration(newRC); - m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); + updateRemoveToolButtons(); } void RunSettingsWidget::cloneRunConfiguration() @@ -226,11 +230,30 @@ void RunSettingsWidget::removeRunConfiguration() return; m_target->removeRunConfiguration(rc); - m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); + updateRemoveToolButtons(); m_renameRunButton->setEnabled(m_target->activeRunConfiguration()); m_cloneRunButton->setEnabled(m_target->activeRunConfiguration()); } +void RunSettingsWidget::removeAllRunConfigurations() +{ + QMessageBox msgBox(QMessageBox::Question, + Tr::tr("Remove Run Configurations?"), + Tr::tr("Do you really want to delete all run configurations?"), + QMessageBox::Cancel, + this); + msgBox.addButton(Tr::tr("Delete"), QMessageBox::YesRole); + msgBox.setDefaultButton(QMessageBox::Cancel); + msgBox.setEscapeButton(QMessageBox::Cancel); + if (msgBox.exec() == QMessageBox::Cancel) + return; + + m_target->removeAllRunConfigurations(); + updateRemoveToolButtons(); + m_renameRunButton->setEnabled(false); + m_cloneRunButton->setEnabled(false); +} + void RunSettingsWidget::activeRunConfigurationChanged() { if (m_ignoreChanges.isLocked()) @@ -304,7 +327,7 @@ void RunSettingsWidget::aboutToShowDeployMenu() for (DeployConfigurationFactory *factory : DeployConfigurationFactory::find(m_target)) { QAction *action = m_addDeployMenu->addAction(factory->defaultDisplayName()); - connect(action, &QAction::triggered, [factory, this]() { + connect(action, &QAction::triggered, this, [factory, this] { DeployConfiguration *newDc = factory->create(m_target); if (!newDc) return; @@ -367,10 +390,12 @@ void RunSettingsWidget::renameDeployConfiguration() m_target->activeDeployConfiguration()->setDisplayName(name); } -void RunSettingsWidget::updateRemoveToolButton() +void RunSettingsWidget::updateRemoveToolButtons() { m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().count() > 1); - m_removeRunToolButton->setEnabled(m_target->runConfigurations().size() > 1); + const bool hasRunConfigs = !m_target->runConfigurations().isEmpty(); + m_removeRunToolButton->setEnabled(hasRunConfigs); + m_removeAllRunConfigsButton->setEnabled(hasRunConfigs); } void RunSettingsWidget::updateDeployConfiguration(DeployConfiguration *dc) @@ -474,7 +499,7 @@ void RunSettingsWidget::addRunControlWidgets() void RunSettingsWidget::addSubWidget(QWidget *widget, QLabel *label) { - widget->setContentsMargins(0, 2, 0, 0); + widget->setContentsMargins({}); QFont f = label->font(); f.setBold(true); diff --git a/src/plugins/projectexplorer/runsettingspropertiespage.h b/src/plugins/projectexplorer/runsettingspropertiespage.h index 00998e1817c..18db4568a45 100644 --- a/src/plugins/projectexplorer/runsettingspropertiespage.h +++ b/src/plugins/projectexplorer/runsettingspropertiespage.h @@ -41,6 +41,7 @@ private: void showAddRunConfigDialog(); void cloneRunConfiguration(); void removeRunConfiguration(); + void removeAllRunConfigurations(); void activeRunConfigurationChanged(); void renameRunConfiguration(); void currentDeployConfigurationChanged(int index); @@ -49,7 +50,7 @@ private: void activeDeployConfigurationChanged(); void renameDeployConfiguration(); - void updateRemoveToolButton(); + void updateRemoveToolButtons(); QString uniqueDCName(const QString &name); QString uniqueRCName(const QString &name); @@ -82,6 +83,7 @@ private: QPushButton *m_removeDeployToolButton; QPushButton *m_addRunToolButton; QPushButton *m_removeRunToolButton; + QPushButton *m_removeAllRunConfigsButton; QPushButton *m_renameRunButton; QPushButton *m_cloneRunButton; QPushButton *m_renameDeployButton; diff --git a/src/plugins/projectexplorer/selectablefilesmodel.cpp b/src/plugins/projectexplorer/selectablefilesmodel.cpp index de5b0b79b40..7eef9c3ff41 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.cpp +++ b/src/plugins/projectexplorer/selectablefilesmodel.cpp @@ -22,6 +22,8 @@ #include <QPushButton> #include <QTreeView> +using namespace Utils; + namespace ProjectExplorer { const char HIDE_FILE_FILTER_DEFAULT[] = "Makefile*; *.o; *.lo; *.la; *.obj; *~; *.files;" @@ -36,7 +38,6 @@ SelectableFilesModel::SelectableFilesModel(QObject *parent) : QAbstractItemModel void SelectableFilesModel::setInitialMarkedFiles(const Utils::FilePaths &files) { m_files = Utils::toSet(files); - m_allFiles = files.isEmpty(); } void SelectableFilesFromDirModel::startParsing(const Utils::FilePath &baseDir) @@ -109,13 +110,18 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree bool allUnchecked = true; for (const QFileInfo &fileInfo : fileInfoList) { Utils::FilePath fn = Utils::FilePath::fromFileInfo(fileInfo); - if (m_futureCount % 100) { + if ((m_futureCount % 100) == 0) { emit parsingProgress(fn); if (promise.isCanceled()) return; } ++m_futureCount; if (fileInfo.isDir()) { + if (fileInfo.isSymLink()) { + const FilePath target = FilePath::fromString(fileInfo.symLinkTarget()); + if (target == baseDir || baseDir.isChildOf(target)) + continue; + } auto t = new Tree; t->parent = tree; t->name = fileInfo.fileName(); @@ -129,8 +135,8 @@ void SelectableFilesFromDirModel::buildTree(const Utils::FilePath &baseDir, Tree auto t = new Tree; t->parent = tree; t->name = fileInfo.fileName(); - FilterState state = filter(t); - t->checked = ((m_allFiles && state == FilterState::CHECKED) + const FilterState state = filter(t); + t->checked = ((m_files.isEmpty() && state == FilterState::CHECKED) || m_files.contains(fn)) ? Qt::Checked : Qt::Unchecked; t->fullPath = fn; t->isDir = false; @@ -184,12 +190,12 @@ QModelIndex SelectableFilesModel::index(int row, int column, const QModelIndex & QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const { if (!child.isValid()) - return QModelIndex(); + return {}; if (!child.internalPointer()) - return QModelIndex(); + return {}; auto parent = static_cast<Tree *>(child.internalPointer())->parent; if (!parent) - return QModelIndex(); + return {}; if (!parent->parent) //then the parent is the root return createIndex(0, 0, parent); // figure out where the parent is @@ -202,7 +208,7 @@ QModelIndex SelectableFilesModel::parent(const QModelIndex &child) const QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) - return QVariant(); + return {}; auto t = static_cast<Tree *>(index.internalPointer()); if (role == Qt::DisplayRole) return t->name; @@ -213,7 +219,7 @@ QVariant SelectableFilesModel::data(const QModelIndex &index, int role) const t->icon = Utils::FileIconProvider::icon(t->fullPath); return t->icon; } - return QVariant(); + return {}; } bool SelectableFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) @@ -534,7 +540,7 @@ SelectableFilesWidget::SelectableFilesWidget(QWidget *parent) : layout->setContentsMargins(0, 0, 0, 0); m_baseDirLabel->setText(Tr::tr("Source directory:")); - m_baseDirChooser->setHistoryCompleter(QLatin1String("PE.AddToProjectDir.History")); + m_baseDirChooser->setHistoryCompleter("PE.AddToProjectDir.History"); m_startParsingButton->setText(Tr::tr("Start Parsing")); layout->addWidget(m_baseDirLabel, static_cast<int>(SelectableFilesWidgetRows::BaseDirectory), 0); layout->addWidget(m_baseDirChooser->lineEdit(), static_cast<int>(SelectableFilesWidgetRows::BaseDirectory), 1); @@ -638,7 +644,7 @@ void SelectableFilesWidget::cancelParsing() m_model->cancel(); } -void SelectableFilesWidget::enableFilterHistoryCompletion(const QString &keyPrefix) +void SelectableFilesWidget::enableFilterHistoryCompletion(const Key &keyPrefix) { m_selectFilesFilterEdit->setHistoryCompleter(keyPrefix + ".select", true); m_hideFilesFilterEdit->setHistoryCompleter(keyPrefix + ".hide", true); diff --git a/src/plugins/projectexplorer/selectablefilesmodel.h b/src/plugins/projectexplorer/selectablefilesmodel.h index 8c47fbd990b..b8ce51111ed 100644 --- a/src/plugins/projectexplorer/selectablefilesmodel.h +++ b/src/plugins/projectexplorer/selectablefilesmodel.h @@ -5,7 +5,8 @@ #include "projectexplorer_export.h" -#include <utils/fileutils.h> +#include <utils/filepath.h> +#include <utils/storekey.h> #include <QAbstractItemModel> #include <QDialog> @@ -120,7 +121,6 @@ private: void selectAllFiles(Tree *root); protected: - bool m_allFiles = true; QSet<Utils::FilePath> m_outOfBaseDirFiles; QSet<Utils::FilePath> m_files; Tree *m_root = nullptr; @@ -178,7 +178,7 @@ public: void resetModel(const Utils::FilePath &path, const Utils::FilePaths &files); void cancelParsing(); - void enableFilterHistoryCompletion(const QString &keyPrefix); + void enableFilterHistoryCompletion(const Utils::Key &keyPrefix); signals: void selectedFilesChanged(); diff --git a/src/plugins/projectexplorer/simpleprojectwizard.cpp b/src/plugins/projectexplorer/simpleprojectwizard.cpp index a73725daa19..455e253abea 100644 --- a/src/plugins/projectexplorer/simpleprojectwizard.cpp +++ b/src/plugins/projectexplorer/simpleprojectwizard.cpp @@ -6,8 +6,6 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include <app/app_version.h> - #include <coreplugin/basefilewizard.h> #include <coreplugin/icore.h> @@ -151,11 +149,13 @@ SimpleProjectWizard::SimpleProjectWizard() setIcon(ProjectExplorer::Icons::WIZARD_IMPORT_AS_PROJECT.icon()); setDisplayName(Tr::tr("Import as qmake or CMake Project (Limited Functionality)")); setId("Z.DummyProFile"); - setDescription(Tr::tr("Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools.<p>" - "This creates a project file that allows you to use %1 as a code editor " - "and as a launcher for debugging and analyzing tools. " - "If you want to build the project, you might need to edit the generated project file.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + setDescription( + Tr::tr( + "Imports existing projects that do not use qmake, CMake, Qbs, Meson, or Autotools.<p>" + "This creates a project file that allows you to use %1 as a code editor " + "and as a launcher for debugging and analyzing tools. " + "If you want to build the project, you might need to edit the generated project file.") + .arg(QGuiApplication::applicationDisplayName())); setCategory(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY); setDisplayCategory(Tr::tr(ProjectExplorer::Constants::IMPORT_WIZARD_CATEGORY_DISPLAY)); setFlags(IWizardFactory::PlatformIndependent); @@ -217,16 +217,16 @@ GeneratedFiles generateQmakeFiles(const SimpleProjectWizardDialog *wizard, GeneratedFile generatedProFile(proFileName); generatedProFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute); generatedProFile.setContents( - "# Created by and for " + QLatin1String(Core::Constants::IDE_DISPLAY_NAME) + "# Created by and for " + QGuiApplication::applicationDisplayName() + " This file was created for editing the project sources only.\n" - "# You may attempt to use it for building too, by modifying this file here.\n\n" - "#TARGET = " + projectName + "\n\n" - "QT = " + wizard->qtModules() + "\n\n" - + proHeaders + "\n\n" - + proSources + "\n\n" - + proIncludes + "\n\n" - "#DEFINES = \n\n" - ); + "# You may attempt to use it for building too, by modifying this file here.\n\n" + "#TARGET = " + + projectName + + "\n\n" + "QT = " + + wizard->qtModules() + "\n\n" + proHeaders + "\n\n" + proSources + "\n\n" + proIncludes + + "\n\n" + "#DEFINES = \n\n"); return GeneratedFiles{generatedProFile}; } @@ -290,22 +290,22 @@ GeneratedFiles generateCmakeFiles(const SimpleProjectWizardDialog *wizard, GeneratedFile generatedProFile(projectFileName); generatedProFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute); generatedProFile.setContents( - "# Created by and for " + QLatin1String(Core::Constants::IDE_DISPLAY_NAME) + "# Created by and for " + QGuiApplication::applicationDisplayName() + " This file was created for editing the project sources only.\n" "# You may attempt to use it for building too, by modifying this file here.\n\n" "cmake_minimum_required(VERSION 3.5)\n" - "project("+ projectName +")\n\n" + "project(" + + projectName + + ")\n\n" "set(CMAKE_AUTOUIC ON)\n" "set(CMAKE_AUTOMOC ON)\n" "set(CMAKE_AUTORCC ON)\n" "set(CMAKE_CXX_STANDARD 11)\n" "set(CMAKE_CXX_STANDARD_REQUIRED ON)\n" - + components + "\n\n" - + includes + "\n\n" - + srcs + "\n\n" + + components + "\n\n" + includes + "\n\n" + srcs + + "\n\n" "add_executable(${CMAKE_PROJECT_NAME} ${SRCS})\n\n" - + libs - ); + + libs); return GeneratedFiles{generatedProFile}; } diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp index d2e59aab063..3c5e87effe6 100644 --- a/src/plugins/projectexplorer/target.cpp +++ b/src/plugins/projectexplorer/target.cpp @@ -12,7 +12,7 @@ #include "deploymentdata.h" #include "devicesupport/devicemanager.h" #include "kit.h" -#include "kitinformation.h" +#include "kitaspects.h" #include "kitmanager.h" #include "miniprojecttargetselector.h" #include "project.h" @@ -91,7 +91,7 @@ public: DeployConfiguration *m_activeDeployConfiguration = nullptr; QList<RunConfiguration *> m_runConfigurations; RunConfiguration* m_activeRunConfiguration = nullptr; - QVariantMap m_pluginSettings; + Store m_pluginSettings; Kit *const m_kit; MacroExpander m_macroExpander; @@ -321,14 +321,14 @@ QString Target::toolTip() const return d->m_kit->toHtml(); } -QString Target::displayNameKey() +Key Target::displayNameKey() { - return QString("ProjectExplorer.ProjectConfiguration.DisplayName"); + return "ProjectExplorer.ProjectConfiguration.DisplayName"; } -QString Target::deviceTypeKey() +Key Target::deviceTypeKey() { - return QString("DeviceType"); + return "DeviceType"; } void Target::addBuildConfiguration(BuildConfiguration *bc) @@ -525,6 +525,20 @@ void Target::removeRunConfiguration(RunConfiguration *rc) delete rc; } +void Target::removeAllRunConfigurations() +{ + QList<RunConfiguration *> runConfigs = d->m_runConfigurations; + d->m_runConfigurations.clear(); + setActiveRunConfiguration(nullptr); + while (!runConfigs.isEmpty()) { + RunConfiguration * const rc = runConfigs.takeFirst(); + emit removedRunConfiguration(rc); + ProjectExplorerPlugin::targetSelector()->removedRunConfiguration(rc); + d->m_runConfigurationModel.removeProjectConfiguration(rc); + delete rc; + } +} + RunConfiguration *Target::activeRunConfiguration() const { return d->m_activeRunConfiguration; @@ -567,12 +581,12 @@ QString Target::overlayIconToolTip() return current.isNull() ? QString() : formatDeviceInfo(current->deviceInformation()); } -QVariantMap Target::toMap() const +Store Target::toMap() const { if (!d->m_kit) // Kit was deleted, target is only around to be copied. - return QVariantMap(); + return {}; - QVariantMap map; + Store map; map.insert(displayNameKey(), displayName()); map.insert(deviceTypeKey(), DeviceTypeKitAspect::deviceTypeId(kit()).toSetting()); @@ -581,30 +595,39 @@ QVariantMap Target::toMap() const // This is only read by older versions of Creator, but even there not actively used. const char CONFIGURATION_ID_KEY[] = "ProjectExplorer.ProjectConfiguration.Id"; const char DEFAULT_DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; - map.insert(QLatin1String(CONFIGURATION_ID_KEY), id().toSetting()); - map.insert(QLatin1String(DEFAULT_DISPLAY_NAME_KEY), displayName()); + map.insert(CONFIGURATION_ID_KEY, id().toSetting()); + map.insert(DEFAULT_DISPLAY_NAME_KEY, displayName()); } const QList<BuildConfiguration *> bcs = buildConfigurations(); - map.insert(QLatin1String(ACTIVE_BC_KEY), bcs.indexOf(d->m_activeBuildConfiguration)); - map.insert(QLatin1String(BC_COUNT_KEY), bcs.size()); - for (int i = 0; i < bcs.size(); ++i) - map.insert(QString::fromLatin1(BC_KEY_PREFIX) + QString::number(i), bcs.at(i)->toMap()); + map.insert(ACTIVE_BC_KEY, bcs.indexOf(d->m_activeBuildConfiguration)); + map.insert(BC_COUNT_KEY, bcs.size()); + for (int i = 0; i < bcs.size(); ++i) { + Store data; + bcs.at(i)->toMap(data); + map.insert(numberedKey(BC_KEY_PREFIX, i), variantFromStore(data)); + } const QList<DeployConfiguration *> dcs = deployConfigurations(); - map.insert(QLatin1String(ACTIVE_DC_KEY), dcs.indexOf(d->m_activeDeployConfiguration)); - map.insert(QLatin1String(DC_COUNT_KEY), dcs.size()); - for (int i = 0; i < dcs.size(); ++i) - map.insert(QString::fromLatin1(DC_KEY_PREFIX) + QString::number(i), dcs.at(i)->toMap()); + map.insert(ACTIVE_DC_KEY, dcs.indexOf(d->m_activeDeployConfiguration)); + map.insert(DC_COUNT_KEY, dcs.size()); + for (int i = 0; i < dcs.size(); ++i) { + Store data; + dcs.at(i)->toMap(data); + map.insert(numberedKey(DC_KEY_PREFIX, i), variantFromStore(data)); + } const QList<RunConfiguration *> rcs = runConfigurations(); - map.insert(QLatin1String(ACTIVE_RC_KEY), rcs.indexOf(d->m_activeRunConfiguration)); - map.insert(QLatin1String(RC_COUNT_KEY), rcs.size()); - for (int i = 0; i < rcs.size(); ++i) - map.insert(QString::fromLatin1(RC_KEY_PREFIX) + QString::number(i), rcs.at(i)->toMap()); + map.insert(ACTIVE_RC_KEY, rcs.indexOf(d->m_activeRunConfiguration)); + map.insert(RC_COUNT_KEY, rcs.size()); + for (int i = 0; i < rcs.size(); ++i) { + Store data; + rcs.at(i)->toMap(data); + map.insert(numberedKey(RC_KEY_PREFIX, i), variantFromStore(data)); + } if (!d->m_pluginSettings.isEmpty()) - map.insert(QLatin1String(PLUGIN_SETTINGS_KEY), d->m_pluginSettings); + map.insert(PLUGIN_SETTINGS_KEY, variantFromStore(d->m_pluginSettings)); return map; } @@ -692,7 +715,7 @@ void Target::updateDefaultRunConfigurations() present = true; } } - if (!present) + if (!present && !rc->isCustomized()) toRemove.append(rc); } configuredCount -= toRemove.count(); @@ -783,14 +806,16 @@ void Target::updateDefaultRunConfigurations() // Remove the RCs that are no longer needed: for (RunConfiguration *rc : std::as_const(removalList)) removeRunConfiguration(rc); + + emit runConfigurationsUpdated(); } -QVariant Target::namedSettings(const QString &name) const +QVariant Target::namedSettings(const Key &name) const { return d->m_pluginSettings.value(name); } -void Target::setNamedSettings(const QString &name, const QVariant &value) +void Target::setNamedSettings(const Key &name, const QVariant &value) { if (value.isNull()) d->m_pluginSettings.remove(name); @@ -860,25 +885,25 @@ void Target::updateDeviceState() setOverlayIcon(overlay); } -bool Target::fromMap(const QVariantMap &map) +bool Target::fromMap(const Store &map) { QTC_ASSERT(d->m_kit == KitManager::kit(id()), return false); bool ok; - int bcCount = map.value(QLatin1String(BC_COUNT_KEY), 0).toInt(&ok); + int bcCount = map.value(BC_COUNT_KEY, 0).toInt(&ok); if (!ok || bcCount < 0) bcCount = 0; - int activeConfiguration = map.value(QLatin1String(ACTIVE_BC_KEY), 0).toInt(&ok); + int activeConfiguration = map.value(ACTIVE_BC_KEY, 0).toInt(&ok); if (!ok || activeConfiguration < 0) activeConfiguration = 0; if (0 > activeConfiguration || bcCount < activeConfiguration) activeConfiguration = 0; for (int i = 0; i < bcCount; ++i) { - const QString key = QString::fromLatin1(BC_KEY_PREFIX) + QString::number(i); + const Key key = numberedKey(BC_KEY_PREFIX, i); if (!map.contains(key)) return false; - const QVariantMap valueMap = map.value(key).toMap(); + const Store valueMap = storeFromVariant(map.value(key)); BuildConfiguration *bc = BuildConfigurationFactory::restore(this, valueMap); if (!bc) { qWarning("No factory found to restore build configuration!"); @@ -892,20 +917,20 @@ bool Target::fromMap(const QVariantMap &map) if (buildConfigurations().isEmpty() && BuildConfigurationFactory::find(this)) return false; - int dcCount = map.value(QLatin1String(DC_COUNT_KEY), 0).toInt(&ok); + int dcCount = map.value(DC_COUNT_KEY, 0).toInt(&ok); if (!ok || dcCount < 0) dcCount = 0; - activeConfiguration = map.value(QLatin1String(ACTIVE_DC_KEY), 0).toInt(&ok); + activeConfiguration = map.value(ACTIVE_DC_KEY, 0).toInt(&ok); if (!ok || activeConfiguration < 0) activeConfiguration = 0; if (0 > activeConfiguration || dcCount < activeConfiguration) activeConfiguration = 0; for (int i = 0; i < dcCount; ++i) { - const QString key = QString::fromLatin1(DC_KEY_PREFIX) + QString::number(i); + const Key key = numberedKey(DC_KEY_PREFIX, i); if (!map.contains(key)) return false; - QVariantMap valueMap = map.value(key).toMap(); + Store valueMap = storeFromVariant(map.value(key)); DeployConfiguration *dc = DeployConfigurationFactory::restore(this, valueMap); if (!dc) { Utils::Id id = idFromMap(valueMap); @@ -919,27 +944,27 @@ bool Target::fromMap(const QVariantMap &map) setActiveDeployConfiguration(dc); } - int rcCount = map.value(QLatin1String(RC_COUNT_KEY), 0).toInt(&ok); + int rcCount = map.value(RC_COUNT_KEY, 0).toInt(&ok); if (!ok || rcCount < 0) rcCount = 0; - activeConfiguration = map.value(QLatin1String(ACTIVE_RC_KEY), 0).toInt(&ok); + activeConfiguration = map.value(ACTIVE_RC_KEY, 0).toInt(&ok); if (!ok || activeConfiguration < 0) activeConfiguration = 0; if (0 > activeConfiguration || rcCount < activeConfiguration) activeConfiguration = 0; for (int i = 0; i < rcCount; ++i) { - const QString key = QString::fromLatin1(RC_KEY_PREFIX) + QString::number(i); + const Key key = numberedKey(RC_KEY_PREFIX, i); if (!map.contains(key)) return false; // Ignore missing RCs: We will just populate them using the default ones. - QVariantMap valueMap = map.value(key).toMap(); + Store valueMap = storeFromVariant(map.value(key)); RunConfiguration *rc = RunConfigurationFactory::restore(this, valueMap); if (!rc) continue; const Utils::Id theIdFromMap = ProjectExplorer::idFromMap(valueMap); - if (!theIdFromMap.toString().contains("///::///")) { // Hack for cmake 4.10 -> 4.11 + if (!theIdFromMap.name().contains("///::///")) { // Hack for cmake 4.10 -> 4.11 QTC_CHECK(rc->id().withSuffix(rc->buildKey()) == theIdFromMap); } addRunConfiguration(rc); @@ -947,8 +972,8 @@ bool Target::fromMap(const QVariantMap &map) setActiveRunConfiguration(rc); } - if (map.contains(QLatin1String(PLUGIN_SETTINGS_KEY))) - d->m_pluginSettings = map.value(QLatin1String(PLUGIN_SETTINGS_KEY)).toMap(); + if (map.contains(PLUGIN_SETTINGS_KEY)) + d->m_pluginSettings = storeFromVariant(map.value(PLUGIN_SETTINGS_KEY)); return true; } diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h index aeca2fd9e0b..fa8fa4b3b0e 100644 --- a/src/plugins/projectexplorer/target.h +++ b/src/plugins/projectexplorer/target.h @@ -6,6 +6,7 @@ #include "projectexplorer_export.h" #include <utils/id.h> +#include <utils/store.h> #include <QObject> @@ -56,8 +57,8 @@ public: QString displayName() const; QString toolTip() const; - static QString displayNameKey(); - static QString deviceTypeKey(); + static Utils::Key displayNameKey(); + static Utils::Key deviceTypeKey(); // Build configuration void addBuildConfiguration(BuildConfiguration *bc); @@ -77,6 +78,7 @@ public: const QList<RunConfiguration *> runConfigurations() const; void addRunConfiguration(RunConfiguration *rc); void removeRunConfiguration(RunConfiguration *rc); + void removeAllRunConfigurations(); RunConfiguration *activeRunConfiguration() const; void setActiveRunConfiguration(RunConfiguration *rc); @@ -86,14 +88,14 @@ public: void setOverlayIcon(const QIcon &icon); QString overlayIconToolTip(); - QVariantMap toMap() const; + Utils::Store toMap() const; void updateDefaultBuildConfigurations(); void updateDefaultDeployConfigurations(); void updateDefaultRunConfigurations(); - QVariant namedSettings(const QString &name) const; - void setNamedSettings(const QString &name, const QVariant &value); + QVariant namedSettings(const Utils::Key &name) const; + void setNamedSettings(const Utils::Key &name, const QVariant &value); QVariant additionalData(Utils::Id id) const; @@ -130,6 +132,7 @@ signals: void removedRunConfiguration(ProjectExplorer::RunConfiguration *rc); void addedRunConfiguration(ProjectExplorer::RunConfiguration *rc); void activeRunConfigurationChanged(ProjectExplorer::RunConfiguration *rc); + void runConfigurationsUpdated(); void removedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc); void addedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc); @@ -143,7 +146,7 @@ signals: void deploymentDataChanged(); private: - bool fromMap(const QVariantMap &map); + bool fromMap(const Utils::Store &map); void updateDeviceState(); diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index d2fdc3b43a8..0a95a7219b5 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -19,8 +19,6 @@ #include "targetsetuppage.h" #include "task.h" -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> #include <coreplugin/modemanager.h> @@ -311,8 +309,7 @@ public: default: break; } - - return QVariant(); + return {}; } bool setData(int column, const QVariant &data, int role) override @@ -556,8 +553,7 @@ public: default: break; } - - return QVariant(); + return {}; } Qt::ItemFlags flags(int column) const override @@ -613,8 +609,7 @@ public: font.setItalic(true); return font; } - - return QVariant(); + return {}; } bool setData(int column, const QVariant &data, int role) override @@ -702,8 +697,7 @@ QVariant TargetGroupItem::data(int column, int role) const d->ensureWidget(); return QVariant::fromValue<QWidget *>(d->m_configurePage.data()); } - - return QVariant(); + return {}; } bool TargetGroupItem::setData(int column, const QVariant &data, int role) @@ -770,8 +764,7 @@ void TargetGroupItemPrivate::rebuildContents() { q->removeChildren(); - const QList<Kit *> kits = KitManager::sortKits(KitManager::kits()); - for (Kit *kit : kits) + for (Kit *kit : KitManager::sortedKits()) q->appendChild(new TargetItem(m_project, kit->id(), m_project->projectIssues(kit))); if (q->parent()) diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 61b23b2b78e..406ddcb76df 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -11,6 +11,7 @@ #include "project.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "projectimporter.h" #include "target.h" #include "targetsetupwidget.h" #include "task.h" @@ -57,19 +58,30 @@ static FilePath importDirectory(const FilePath &projectPath) return path; } -class TargetSetupPageUi +static TasksGenerator defaultTasksGenerator(const TasksGenerator &childGenerator) +{ + return [childGenerator](const Kit *k) -> Tasks { + if (!k->isValid()) + return {CompileTask(Task::Error, Tr::tr("Kit is not valid."))}; + if (childGenerator) + return childGenerator(k); + return {}; + }; +} + +class TargetSetupPagePrivate : public QObject { public: - QWidget *centralWidget; - QWidget *scrollAreaWidget; - QScrollArea *scrollArea; - QLabel *headerLabel; - QLabel *noValidKitLabel; - QCheckBox *allKitsCheckBox; - FancyLineEdit *kitFilterLineEdit; - - void setupUi(TargetSetupPage *q) + explicit TargetSetupPagePrivate(TargetSetupPage *parent) + : q(parent) { + m_tasksGenerator = defaultTasksGenerator({}); + + m_importWidget = new ImportWidget(q); + m_importWidget->setVisible(false); + + m_spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + auto setupTargetPage = new QWidget(q); headerLabel = new QLabel(setupTargetPage); @@ -94,12 +106,12 @@ public: kitFilterLineEdit->setFiltering(true); kitFilterLineEdit->setPlaceholderText(Tr::tr("Type to filter kits by name...")); - centralWidget = new QWidget(setupTargetPage); + m_centralWidget = new QWidget(setupTargetPage); QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Fixed); policy.setHorizontalStretch(0); policy.setVerticalStretch(0); - policy.setHeightForWidth(centralWidget->sizePolicy().hasHeightForWidth()); - centralWidget->setSizePolicy(policy); + policy.setHeightForWidth(m_centralWidget->sizePolicy().hasHeightForWidth()); + m_centralWidget->setSizePolicy(policy); scrollAreaWidget = new QWidget(setupTargetPage); scrollArea = new QScrollArea(scrollAreaWidget); @@ -123,13 +135,20 @@ public: verticalLayout_2->addWidget(headerLabel); verticalLayout_2->addLayout(horizontalLayout); verticalLayout_2->addWidget(noValidKitLabel); - verticalLayout_2->addWidget(centralWidget); + verticalLayout_2->addWidget(m_centralWidget); verticalLayout_2->addWidget(scrollAreaWidget); auto verticalLayout_3 = new QVBoxLayout(q); verticalLayout_3->setContentsMargins(0, 0, 0, -1); verticalLayout_3->addWidget(setupTargetPage); + auto centralWidget = new QWidget(q); + scrollArea->setWidget(centralWidget); + centralWidget->setLayout(new QVBoxLayout); + + m_centralWidget->setLayout(new QVBoxLayout); + m_centralWidget->layout()->setContentsMargins(0, 0, 0, 0); + QObject::connect(noValidKitLabel, &QLabel::linkActivated, q, &TargetSetupPage::openOptions); @@ -137,91 +156,128 @@ public: q, &TargetSetupPage::changeAllKitsSelections); QObject::connect(kitFilterLineEdit, &FancyLineEdit::filterChanged, - q, &TargetSetupPage::kitFilterChanged); + this, &TargetSetupPagePrivate::kitFilterChanged); + + for (IPotentialKit *pk : std::as_const(g_potentialKits)) { + if (pk->isEnabled()) + m_potentialWidgets.append(pk->createWidget(q)); + } + + setUseScrollArea(true); + + KitManager *km = KitManager::instance(); + // do note that those slots are triggered once *per* targetsetuppage + // thus the same slot can be triggered multiple times on different instances! + connect(km, &KitManager::kitAdded, this, &TargetSetupPagePrivate::handleKitAddition); + connect(km, &KitManager::kitRemoved, this, &TargetSetupPagePrivate::handleKitRemoval); + connect(km, &KitManager::kitUpdated, this, &TargetSetupPagePrivate::handleKitUpdate); + connect(m_importWidget, &ImportWidget::importFrom, + this, [this](const FilePath &dir) { import(dir); }); + connect(KitManager::instance(), &KitManager::kitsChanged, + this, &TargetSetupPagePrivate::updateVisibility); + } + + void doInitializePage(); + void handleKitAddition(Kit *k); + void handleKitRemoval(Kit *k); + void handleKitUpdate(Kit *k); + void updateVisibility(); + + void reLayout(); + static bool compareKits(const Kit *k1, const Kit *k2); + std::vector<Internal::TargetSetupWidget *> sortedWidgetList() const; + + void kitSelectionChanged(); + + bool isUpdating() const; + void selectAtLeastOneEnabledKit(); + void removeWidget(Kit *k) { removeWidget(widget(k)); } + void removeWidget(Internal::TargetSetupWidget *w); + Internal::TargetSetupWidget *addWidget(Kit *k); + void addAdditionalWidgets(); + void removeAdditionalWidgets(QLayout *layout); + void removeAdditionalWidgets(); + void updateWidget(Internal::TargetSetupWidget *widget); + bool isUsable(const Kit *kit) const; + + void setupImports(); + void import(const FilePath &path, bool silent = false); + + void setupWidgets(const QString &filterText = QString()); + void reset(); + + TargetSetupWidget *widget(const Id kitId, TargetSetupWidget *fallback = nullptr) const; + TargetSetupWidget *widget(const Kit *k, TargetSetupWidget *fallback = nullptr) const + { + return k ? widget(k->id(), fallback) : fallback; + } + + void setUseScrollArea(bool b); + void kitFilterChanged(const QString &filterText); + + TargetSetupPage *q; + QWidget *m_centralWidget; + QWidget *scrollAreaWidget; + QScrollArea *scrollArea; + QLabel *headerLabel; + QLabel *noValidKitLabel; + QCheckBox *allKitsCheckBox; + FancyLineEdit *kitFilterLineEdit; + + TasksGenerator m_tasksGenerator; + QPointer<ProjectImporter> m_importer; + QLayout *m_baseLayout = nullptr; + FilePath m_projectPath; + QString m_defaultShadowBuildLocation; + std::vector<Internal::TargetSetupWidget *> m_widgets; + + Internal::ImportWidget *m_importWidget = nullptr; + QSpacerItem *m_spacer; + QList<QWidget *> m_potentialWidgets; + + bool m_widgetsWereSetUp = false; }; } // namespace Internal -static TasksGenerator defaultTasksGenerator(const TasksGenerator &childGenerator) -{ - return [childGenerator](const Kit *k) -> Tasks { - if (!k->isValid()) - return {CompileTask(Task::Error, Tr::tr("Kit is not valid."))}; - if (childGenerator) - return childGenerator(k); - return {}; - }; -} - using namespace Internal; TargetSetupPage::TargetSetupPage(QWidget *parent) : WizardPage(parent) - , m_tasksGenerator(defaultTasksGenerator({})) - , m_ui(new TargetSetupPageUi) - , m_importWidget(new ImportWidget(this)) - , m_spacer(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding)) + , d(new TargetSetupPagePrivate(this)) { - m_importWidget->setVisible(false); - setObjectName(QLatin1String("TargetSetupPage")); setWindowTitle(Tr::tr("Select Kits for Your Project")); - m_ui->setupUi(this); + setTitle(Tr::tr("Kit Selection")); QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred); policy.setHorizontalStretch(0); policy.setVerticalStretch(0); - policy.setHeightForWidth(sizePolicy().hasHeightForWidth()); setSizePolicy(policy); - auto centralWidget = new QWidget(this); - m_ui->scrollArea->setWidget(centralWidget); - centralWidget->setLayout(new QVBoxLayout); - m_ui->centralWidget->setLayout(new QVBoxLayout); - m_ui->centralWidget->layout()->setContentsMargins(0, 0, 0, 0); - - setTitle(Tr::tr("Kit Selection")); - - for (IPotentialKit *pk : std::as_const(g_potentialKits)) - if (pk->isEnabled()) - m_potentialWidgets.append(pk->createWidget(this)); - - setUseScrollArea(true); - - KitManager *km = KitManager::instance(); - // do note that those slots are triggered once *per* targetsetuppage - // thus the same slot can be triggered multiple times on different instances! - connect(km, &KitManager::kitAdded, this, &TargetSetupPage::handleKitAddition); - connect(km, &KitManager::kitRemoved, this, &TargetSetupPage::handleKitRemoval); - connect(km, &KitManager::kitUpdated, this, &TargetSetupPage::handleKitUpdate); - connect(m_importWidget, &ImportWidget::importFrom, - this, [this](const FilePath &dir) { import(dir); }); - connect(KitManager::instance(), &KitManager::kitsChanged, - this, &TargetSetupPage::updateVisibility); - setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Kits")); } void TargetSetupPage::initializePage() { if (KitManager::isLoaded()) { - doInitializePage(); + d->doInitializePage(); } else { connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &TargetSetupPage::doInitializePage); + d, &TargetSetupPagePrivate::doInitializePage); } } void TargetSetupPage::setTasksGenerator(const TasksGenerator &tasksGenerator) { - m_tasksGenerator = defaultTasksGenerator(tasksGenerator); + d->m_tasksGenerator = defaultTasksGenerator(tasksGenerator); } -QList<Utils::Id> TargetSetupPage::selectedKits() const +QList<Id> TargetSetupPage::selectedKits() const { - QList<Utils::Id> result; - for (TargetSetupWidget *w : m_widgets) { + QList<Id> result; + for (TargetSetupWidget *w : d->m_widgets) { if (w->isKitSelected()) result.append(w->kit()->id()); } @@ -231,28 +287,28 @@ QList<Utils::Id> TargetSetupPage::selectedKits() const TargetSetupPage::~TargetSetupPage() { disconnect(); - reset(); - delete m_spacer; - delete m_ui; + d->reset(); + delete d->m_spacer; + delete d; } bool TargetSetupPage::isComplete() const { - return anyOf(m_widgets, [](const TargetSetupWidget *w) { + return anyOf(d->m_widgets, [](const TargetSetupWidget *w) { return w->isKitSelected(); }); } -void TargetSetupPage::setupWidgets(const QString &filterText) +void TargetSetupPagePrivate::setupWidgets(const QString &filterText) { - const auto kitList = KitManager::sortKits(KitManager::kits()); - for (Kit *k : kitList) { + for (Kit *k : KitManager::sortedKits()) { if (!filterText.isEmpty() && !k->displayName().contains(filterText, Qt::CaseInsensitive)) continue; const auto widget = new TargetSetupWidget(k, m_projectPath); connect(widget, &TargetSetupWidget::selectedToggled, - this, &TargetSetupPage::kitSelectionChanged); - connect(widget, &TargetSetupWidget::selectedToggled, this, &QWizardPage::completeChanged); + this, &TargetSetupPagePrivate::kitSelectionChanged); + connect(widget, &TargetSetupWidget::selectedToggled, + q, &QWizardPage::completeChanged); updateWidget(widget); m_widgets.push_back(widget); m_baseLayout->addWidget(widget); @@ -266,7 +322,7 @@ void TargetSetupPage::setupWidgets(const QString &filterText) updateVisibility(); } -void TargetSetupPage::reset() +void TargetSetupPagePrivate::reset() { removeAdditionalWidgets(); while (m_widgets.size() > 0) { @@ -279,11 +335,10 @@ void TargetSetupPage::reset() removeWidget(w); } - m_ui->allKitsCheckBox->setChecked(false); + allKitsCheckBox->setChecked(false); } -TargetSetupWidget *TargetSetupPage::widget(const Utils::Id kitId, - TargetSetupWidget *fallback) const +TargetSetupWidget *TargetSetupPagePrivate::widget(const Id kitId, TargetSetupWidget *fallback) const { return findOr(m_widgets, fallback, [kitId](const TargetSetupWidget *w) { return w->kit() && w->kit()->id() == kitId; @@ -292,40 +347,40 @@ TargetSetupWidget *TargetSetupPage::widget(const Utils::Id kitId, void TargetSetupPage::setProjectPath(const FilePath &path) { - m_projectPath = path; - if (!m_projectPath.isEmpty()) { + d->m_projectPath = path; + if (!d->m_projectPath.isEmpty()) { QFileInfo fileInfo(QDir::cleanPath(path.toString())); QStringList subDirsList = fileInfo.absolutePath().split('/'); - m_ui->headerLabel->setText(Tr::tr("The following kits can be used for project <b>%1</b>:", + d->headerLabel->setText(Tr::tr("The following kits can be used for project <b>%1</b>:", "%1: Project name").arg(subDirsList.last())); } - m_ui->headerLabel->setVisible(!m_projectPath.isEmpty()); + d->headerLabel->setVisible(!d->m_projectPath.isEmpty()); - if (m_widgetsWereSetUp) + if (d->m_widgetsWereSetUp) initializePage(); } void TargetSetupPage::setProjectImporter(ProjectImporter *importer) { - if (importer == m_importer) + if (importer == d->m_importer) return; - if (m_widgetsWereSetUp) - reset(); // Reset before changing the importer! + if (d->m_widgetsWereSetUp) + d->reset(); // Reset before changing the importer! - m_importer = importer; - m_importWidget->setVisible(m_importer); + d->m_importer = importer; + d->m_importWidget->setVisible(d->m_importer); - if (m_widgetsWereSetUp) + if (d->m_widgetsWereSetUp) initializePage(); } bool TargetSetupPage::importLineEditHasFocus() const { - return m_importWidget->ownsReturnKey(); + return d->m_importWidget->ownsReturnKey(); } -void TargetSetupPage::setupImports() +void TargetSetupPagePrivate::setupImports() { if (!m_importer || m_projectPath.isEmpty()) return; @@ -335,18 +390,18 @@ void TargetSetupPage::setupImports() import(path, true); } -void TargetSetupPage::handleKitAddition(Kit *k) +void TargetSetupPagePrivate::handleKitAddition(Kit *k) { if (isUpdating()) return; - Q_ASSERT(!widget(k)); + QTC_ASSERT(!widget(k), return); addWidget(k); kitSelectionChanged(); updateVisibility(); } -void TargetSetupPage::handleKitRemoval(Kit *k) +void TargetSetupPagePrivate::handleKitRemoval(Kit *k) { if (isUpdating()) return; @@ -359,7 +414,7 @@ void TargetSetupPage::handleKitRemoval(Kit *k) updateVisibility(); } -void TargetSetupPage::handleKitUpdate(Kit *k) +void TargetSetupPagePrivate::handleKitUpdate(Kit *k) { if (isUpdating()) return; @@ -377,7 +432,7 @@ void TargetSetupPage::handleKitUpdate(Kit *k) updateVisibility(); } -void TargetSetupPage::selectAtLeastOneEnabledKit() +void TargetSetupPagePrivate::selectAtLeastOneEnabledKit() { if (anyOf(m_widgets, [](const TargetSetupWidget *w) { return w->isKitSelected(); })) { // Something is already selected, we are done. @@ -419,24 +474,24 @@ void TargetSetupPage::selectAtLeastOneEnabledKit() if (toCheckWidget) { toCheckWidget->setKitSelected(true); - emit completeChanged(); // Is this necessary? + emit q->completeChanged(); // FIXME: Is this necessary? } } -void TargetSetupPage::updateVisibility() +void TargetSetupPagePrivate::updateVisibility() { // Always show the widgets, the import widget always makes sense to show. - m_ui->scrollAreaWidget->setVisible(m_baseLayout == m_ui->scrollArea->widget()->layout()); - m_ui->centralWidget->setVisible(m_baseLayout == m_ui->centralWidget->layout()); + scrollAreaWidget->setVisible(m_baseLayout == scrollArea->widget()->layout()); + m_centralWidget->setVisible(m_baseLayout == m_centralWidget->layout()); const bool hasUsableKits = KitManager::kit([this](const Kit *k) { return isUsable(k); }); - m_ui->noValidKitLabel->setVisible(!hasUsableKits); - m_ui->allKitsCheckBox->setVisible(hasUsableKits); + noValidKitLabel->setVisible(!hasUsableKits); + allKitsCheckBox->setVisible(hasUsableKits); - emit completeChanged(); + emit q->completeChanged(); } -void TargetSetupPage::reLayout() +void TargetSetupPagePrivate::reLayout() { removeAdditionalWidgets(); for (TargetSetupWidget * const w : std::as_const(m_widgets)) @@ -446,7 +501,7 @@ void TargetSetupPage::reLayout() addAdditionalWidgets(); } -bool TargetSetupPage::compareKits(const Kit *k1, const Kit *k2) +bool TargetSetupPagePrivate::compareKits(const Kit *k1, const Kit *k2) { const QString name1 = k1->displayName(); const QString name2 = k2->displayName(); @@ -457,7 +512,7 @@ bool TargetSetupPage::compareKits(const Kit *k1, const Kit *k2) return k1 < k2; } -std::vector<TargetSetupWidget *> TargetSetupPage::sortedWidgetList() const +std::vector<TargetSetupWidget *> TargetSetupPagePrivate::sortedWidgetList() const { return sorted(m_widgets, [](const TargetSetupWidget *w1, const TargetSetupWidget *w2) { return compareKits(w1->kit(), w2->kit()); @@ -469,7 +524,7 @@ void TargetSetupPage::openOptions() Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, this); } -void TargetSetupPage::kitSelectionChanged() +void TargetSetupPagePrivate::kitSelectionChanged() { int selected = 0; int deselected = 0; @@ -480,39 +535,22 @@ void TargetSetupPage::kitSelectionChanged() ++deselected; } if (selected > 0 && deselected > 0) - m_ui->allKitsCheckBox->setCheckState(Qt::PartiallyChecked); + allKitsCheckBox->setCheckState(Qt::PartiallyChecked); else if (selected > 0 && deselected == 0) - m_ui->allKitsCheckBox->setCheckState(Qt::Checked); + allKitsCheckBox->setCheckState(Qt::Checked); else - m_ui->allKitsCheckBox->setCheckState(Qt::Unchecked); + allKitsCheckBox->setCheckState(Qt::Unchecked); } -void TargetSetupPage::kitFilterChanged(const QString &filterText) +void TargetSetupPagePrivate::kitFilterChanged(const QString &filterText) { - QPointer<QWidget> focusWidget = QApplication::focusWidget(); - // Remember selected kits: - const std::vector<TargetSetupWidget *> selectedWidgets - = filtered(m_widgets, &TargetSetupWidget::isKitSelected); - const QVector<Utils::Id> selectedKitIds = transform<QVector>(selectedWidgets, - [](const TargetSetupWidget *w) { - return w->kit()->id(); - }); - - // Reset currently shown kits - reset(); - setupWidgets(filterText); - - // Re-select kits: - for (TargetSetupWidget *w : std::as_const(m_widgets)) - w->setKitSelected(selectedKitIds.contains(w->kit()->id())); - - emit completeChanged(); - - if (focusWidget) - focusWidget->setFocus(); + for (TargetSetupWidget *widget : m_widgets) { + Kit *kit = widget->kit(); + widget->setVisible(filterText.isEmpty() || kit->displayName().contains(filterText, Qt::CaseInsensitive)); + } } -void TargetSetupPage::doInitializePage() +void TargetSetupPagePrivate::doInitializePage() { reset(); setupWidgets(); @@ -526,25 +564,25 @@ void TargetSetupPage::doInitializePage() void TargetSetupPage::showEvent(QShowEvent *event) { WizardPage::showEvent(event); - m_ui->kitFilterLineEdit->setFocus(); // Ensure "Configure Project" gets triggered on <Return> + d->kitFilterLineEdit->setFocus(); // Ensure "Configure Project" gets triggered on <Return> } void TargetSetupPage::changeAllKitsSelections() { - if (m_ui->allKitsCheckBox->checkState() == Qt::PartiallyChecked) - m_ui->allKitsCheckBox->setCheckState(Qt::Checked); - bool checked = m_ui->allKitsCheckBox->isChecked(); - for (TargetSetupWidget *widget : m_widgets) + if (d->allKitsCheckBox->checkState() == Qt::PartiallyChecked) + d->allKitsCheckBox->setCheckState(Qt::Checked); + bool checked = d->allKitsCheckBox->isChecked(); + for (TargetSetupWidget *widget : d->m_widgets) widget->setKitSelected(checked); emit completeChanged(); } -bool TargetSetupPage::isUpdating() const +bool TargetSetupPagePrivate::isUpdating() const { return m_importer && m_importer->isUpdating(); } -void TargetSetupPage::import(const FilePath &path, bool silent) +void TargetSetupPagePrivate::import(const FilePath &path, bool silent) { if (!m_importer) return; @@ -553,7 +591,7 @@ void TargetSetupPage::import(const FilePath &path, bool silent) TargetSetupWidget *w = widget(info.kitId); if (!w) { Kit *k = KitManager::kit(info.kitId); - Q_ASSERT(k); + QTC_CHECK(k); addWidget(k); } w = widget(info.kitId); @@ -565,10 +603,10 @@ void TargetSetupPage::import(const FilePath &path, bool silent) w->expandWidget(); kitSelectionChanged(); } - emit completeChanged(); + emit q->completeChanged(); } -void TargetSetupPage::removeWidget(TargetSetupWidget *w) +void TargetSetupPagePrivate::removeWidget(TargetSetupWidget *w) { if (!w) return; @@ -577,13 +615,14 @@ void TargetSetupPage::removeWidget(TargetSetupWidget *w) m_widgets.erase(std::find(m_widgets.begin(), m_widgets.end(), w)); } -TargetSetupWidget *TargetSetupPage::addWidget(Kit *k) +TargetSetupWidget *TargetSetupPagePrivate::addWidget(Kit *k) { const auto widget = new TargetSetupWidget(k, m_projectPath); updateWidget(widget); connect(widget, &TargetSetupWidget::selectedToggled, - this, &TargetSetupPage::kitSelectionChanged); - connect(widget, &TargetSetupWidget::selectedToggled, this, &QWizardPage::completeChanged); + this, &TargetSetupPagePrivate::kitSelectionChanged); + connect(widget, &TargetSetupWidget::selectedToggled, + q, &QWizardPage::completeChanged); // Insert widget, sorted. @@ -604,7 +643,7 @@ TargetSetupWidget *TargetSetupPage::addWidget(Kit *k) return widget; } -void TargetSetupPage::addAdditionalWidgets() +void TargetSetupPagePrivate::addAdditionalWidgets() { m_baseLayout->addWidget(m_importWidget); for (QWidget * const widget : std::as_const(m_potentialWidgets)) @@ -612,7 +651,7 @@ void TargetSetupPage::addAdditionalWidgets() m_baseLayout->addItem(m_spacer); } -void TargetSetupPage::removeAdditionalWidgets(QLayout *layout) +void TargetSetupPagePrivate::removeAdditionalWidgets(QLayout *layout) { layout->removeWidget(m_importWidget); for (QWidget * const potentialWidget : std::as_const(m_potentialWidgets)) @@ -620,13 +659,18 @@ void TargetSetupPage::removeAdditionalWidgets(QLayout *layout) layout->removeItem(m_spacer); } -void TargetSetupPage::updateWidget(TargetSetupWidget *widget) +void TargetSetupPagePrivate::removeAdditionalWidgets() +{ + removeAdditionalWidgets(m_baseLayout); +} + +void TargetSetupPagePrivate::updateWidget(TargetSetupWidget *widget) { QTC_ASSERT(widget, return ); widget->update(m_tasksGenerator); } -bool TargetSetupPage::isUsable(const Kit *kit) const +bool TargetSetupPagePrivate::isUsable(const Kit *kit) const { return !containsType(m_tasksGenerator(kit), Task::Error); } @@ -634,14 +678,14 @@ bool TargetSetupPage::isUsable(const Kit *kit) const bool TargetSetupPage::setupProject(Project *project) { QList<BuildInfo> toSetUp; - for (TargetSetupWidget *widget : m_widgets) { + for (TargetSetupWidget *widget : d->m_widgets) { if (!widget->isKitSelected()) continue; Kit *k = widget->kit(); - if (k && m_importer) - m_importer->makePersistent(k); + if (k && d->m_importer) + d->m_importer->makePersistent(k); toSetUp << widget->selectedBuildInfoList(); widget->clearKit(); } @@ -650,11 +694,11 @@ bool TargetSetupPage::setupProject(Project *project) toSetUp.clear(); // Only reset now that toSetUp has been cleared! - reset(); + d->reset(); Target *activeTarget = nullptr; - if (m_importer) - activeTarget = m_importer->preferredTarget(project->targets()); + if (d->m_importer) + activeTarget = d->m_importer->preferredTarget(project->targets()); if (activeTarget) project->setActiveTarget(activeTarget, SetActive::NoCascade); @@ -662,13 +706,18 @@ bool TargetSetupPage::setupProject(Project *project) } void TargetSetupPage::setUseScrollArea(bool b) +{ + d->setUseScrollArea(b); +} + +void TargetSetupPagePrivate::setUseScrollArea(bool b) { QLayout *oldBaseLayout = m_baseLayout; - m_baseLayout = b ? m_ui->scrollArea->widget()->layout() : m_ui->centralWidget->layout(); + m_baseLayout = b ? scrollArea->widget()->layout() : m_centralWidget->layout(); if (oldBaseLayout == m_baseLayout) return; - m_ui->scrollAreaWidget->setVisible(b); - m_ui->centralWidget->setVisible(!b); + scrollAreaWidget->setVisible(b); + m_centralWidget->setVisible(!b); if (oldBaseLayout) removeAdditionalWidgets(oldBaseLayout); diff --git a/src/plugins/projectexplorer/targetsetuppage.h b/src/plugins/projectexplorer/targetsetuppage.h index 99448693c6f..ecff0fa6391 100644 --- a/src/plugins/projectexplorer/targetsetuppage.h +++ b/src/plugins/projectexplorer/targetsetuppage.h @@ -6,27 +6,17 @@ #include "projectexplorer_export.h" #include "kit.h" -#include "projectimporter.h" #include <utils/wizardpage.h> -#include <QPointer> -#include <QString> -#include <QMap> - -QT_FORWARD_DECLARE_CLASS(QSpacerItem) - namespace Utils { class FilePath; } namespace ProjectExplorer { class Kit; class Project; +class ProjectImporter; -namespace Internal { -class ImportWidget; -class TargetSetupPageUi; -class TargetSetupWidget; -} // namespace Internal +namespace Internal { class TargetSetupPagePrivate; } /// \internal class PROJECTEXPLORER_EXPORT TargetSetupPage : public Utils::WizardPage @@ -59,63 +49,10 @@ public: void openOptions(); void changeAllKitsSelections(); - void kitFilterChanged(const QString &filterText); - private: - void doInitializePage(); - void showEvent(QShowEvent *event) final; - void handleKitAddition(Kit *k); - void handleKitRemoval(Kit *k); - void handleKitUpdate(Kit *k); - void updateVisibility(); - - void reLayout(); - static bool compareKits(const Kit *k1, const Kit *k2); - std::vector<Internal::TargetSetupWidget *> sortedWidgetList() const; - - void kitSelectionChanged(); - - bool isUpdating() const; - void selectAtLeastOneEnabledKit(); - void removeWidget(Kit *k) { removeWidget(widget(k)); } - void removeWidget(Internal::TargetSetupWidget *w); - Internal::TargetSetupWidget *addWidget(Kit *k); - void addAdditionalWidgets(); - void removeAdditionalWidgets(QLayout *layout); - void removeAdditionalWidgets() { removeAdditionalWidgets(m_baseLayout); } - void updateWidget(Internal::TargetSetupWidget *widget); - bool isUsable(const Kit *kit) const; - - void setupImports(); - void import(const Utils::FilePath &path, bool silent = false); - - void setupWidgets(const QString &filterText = QString()); - void reset(); - - Internal::TargetSetupWidget *widget(const Kit *k, - Internal::TargetSetupWidget *fallback = nullptr) const - { - return k ? widget(k->id(), fallback) : fallback; - } - Internal::TargetSetupWidget *widget(const Utils::Id kitId, - Internal::TargetSetupWidget *fallback = nullptr) const; - - TasksGenerator m_tasksGenerator; - QPointer<ProjectImporter> m_importer; - QLayout *m_baseLayout = nullptr; - Utils::FilePath m_projectPath; - QString m_defaultShadowBuildLocation; - std::vector<Internal::TargetSetupWidget *> m_widgets; - - Internal::TargetSetupPageUi *m_ui; - - Internal::ImportWidget *m_importWidget; - QSpacerItem *m_spacer; - QList<QWidget *> m_potentialWidgets; - - bool m_widgetsWereSetUp = false; + Internal::TargetSetupPagePrivate *d; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/targetsetupwidget.cpp b/src/plugins/projectexplorer/targetsetupwidget.cpp index 63b61401d5f..99b08d45f97 100644 --- a/src/plugins/projectexplorer/targetsetupwidget.cpp +++ b/src/plugins/projectexplorer/targetsetupwidget.cpp @@ -52,7 +52,7 @@ TargetSetupWidget::TargetSetupWidget(Kit *k, const FilePath &projectPath) : auto panel = new FadingWidget(m_detailsWidget); auto panelLayout = new QHBoxLayout(panel); - m_manageButton = new QPushButton(KitAspectWidget::msgManage()); + m_manageButton = new QPushButton(KitAspect::msgManage()); panelLayout->addWidget(m_manageButton); m_detailsWidget->setToolWidget(panel); @@ -138,7 +138,7 @@ void TargetSetupWidget::addBuildInfo(const BuildInfo &info, bool isImport) store.pathChooser = new PathChooser(); store.pathChooser->setExpectedKind(PathChooser::Directory); store.pathChooser->setFilePath(info.buildDirectory); - store.pathChooser->setHistoryCompleter(QLatin1String("TargetSetup.BuildDir.History")); + store.pathChooser->setHistoryCompleter("TargetSetup.BuildDir.History"); store.pathChooser->setReadOnly(isImport); m_newBuildsLayout->addWidget(store.pathChooser, pos * 2, 1); @@ -180,7 +180,7 @@ void TargetSetupWidget::manageKit() if (!m_kit) return; - KitOptionsPage::showKit(m_kit); + setSelectectKitId(m_kit->id()); Core::ICore::showOptionsDialog(Constants::KITS_SETTINGS_PAGE_ID, parentWidget()); } diff --git a/src/plugins/projectexplorer/task.cpp b/src/plugins/projectexplorer/task.cpp index 03c2a282be0..a16a7283446 100644 --- a/src/plugins/projectexplorer/task.cpp +++ b/src/plugins/projectexplorer/task.cpp @@ -7,7 +7,6 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" -#include <app/app_version.h> #include <texteditor/fontsettings.h> #include <texteditor/textmark.h> #include <utils/algorithm.h> @@ -15,6 +14,7 @@ #include <utils/qtcassert.h> #include <QFileInfo> +#include <QGuiApplication> #include <QTextStream> using namespace Utils; @@ -61,8 +61,8 @@ Task Task::compilerMissingTask() { return BuildSystemTask(Task::Error, Tr::tr("%1 needs a compiler set up to build. " - "Configure a compiler in the kit options.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + "Configure a compiler in the kit options.") + .arg(QGuiApplication::applicationDisplayName())); } void Task::setMark(TextEditor::TextMark *mark) diff --git a/src/plugins/projectexplorer/taskhub.cpp b/src/plugins/projectexplorer/taskhub.cpp index 58e797de803..e7ec30d4779 100644 --- a/src/plugins/projectexplorer/taskhub.cpp +++ b/src/plugins/projectexplorer/taskhub.cpp @@ -107,13 +107,12 @@ TaskHub::~TaskHub() m_instance = nullptr; } -void TaskHub::addCategory(Utils::Id categoryId, const QString &displayName, bool visible, - int priority) +void TaskHub::addCategory(const TaskCategory &category) { - QTC_CHECK(!displayName.isEmpty()); - QTC_ASSERT(!m_registeredCategories.contains(categoryId), return); - m_registeredCategories.push_back(categoryId); - emit m_instance->categoryAdded(categoryId, displayName, visible, priority); + QTC_CHECK(!category.displayName.isEmpty()); + QTC_ASSERT(!m_registeredCategories.contains(category.id), return); + m_registeredCategories.push_back(category.id); + emit m_instance->categoryAdded(category); } TaskHub *TaskHub::instance() diff --git a/src/plugins/projectexplorer/taskhub.h b/src/plugins/projectexplorer/taskhub.h index 60cb09b4bad..0afc8d51f47 100644 --- a/src/plugins/projectexplorer/taskhub.h +++ b/src/plugins/projectexplorer/taskhub.h @@ -12,6 +12,16 @@ namespace ProjectExplorer { class ProjectExplorerPlugin; +class PROJECTEXPLORER_EXPORT TaskCategory +{ +public: + Utils::Id id; + QString displayName; + QString description; + bool visible = true; + int priority = 0; +}; + class PROJECTEXPLORER_EXPORT TaskHub : public QObject { Q_OBJECT @@ -28,8 +38,7 @@ public slots: static void removeTask(const ProjectExplorer::Task &task); public: - static void addCategory(Utils::Id categoryId, const QString &displayName, bool visible = true, - int priority = 0); + static void addCategory(const TaskCategory &category); static void updateTaskFileName(const Task &task, const QString &fileName); static void updateTaskLineNumber(const Task &task, int line); static void taskMarkClicked(const Task &task); @@ -39,8 +48,7 @@ public: static void requestPopup(); signals: - void categoryAdded(Utils::Id categoryId, const QString &displayName, bool visible, - int priority); + void categoryAdded(const TaskCategory &category); void taskAdded(const ProjectExplorer::Task &task); void taskRemoved(const ProjectExplorer::Task &task); void tasksCleared(Utils::Id categoryId); diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp index 11591e707d7..0dae3344df6 100644 --- a/src/plugins/projectexplorer/taskmodel.cpp +++ b/src/plugins/projectexplorer/taskmodel.cpp @@ -60,13 +60,12 @@ bool TaskModel::hasFile(const QModelIndex &index) const return !m_tasks.at(row).file.isEmpty(); } -void TaskModel::addCategory(Utils::Id categoryId, const QString &categoryName, int priority) +void TaskModel::addCategory(const TaskCategory &category) { - QTC_ASSERT(categoryId.isValid(), return); + QTC_ASSERT(category.id.isValid(), return); CategoryData data; - data.displayName = categoryName; - data.priority = priority; - m_categories.insert(categoryId, data); + data.category = category; + m_categories.insert(category.id, data); } Tasks TaskModel::tasks(Utils::Id categoryId) const @@ -88,8 +87,8 @@ bool TaskModel::compareTasks(const Task &task1, const Task &task2) return task1.taskId < task2.taskId; // Higher-priority task should appear higher up in the view and thus compare less-than. - const int prio1 = m_categories.value(task1.category).priority; - const int prio2 = m_categories.value(task2.category).priority; + const int prio1 = m_categories.value(task1.category).category.priority; + const int prio2 = m_categories.value(task2.category).category.priority; if (prio1 < prio2) return false; if (prio1 > prio2) @@ -284,7 +283,7 @@ Task TaskModel::task(const QModelIndex &index) const int row = index.row(); if (!index.isValid() || row < 0 || row >= m_tasks.count() || index.internalId() || index.column() > 0) { - return Task(); + return {}; } return m_tasks.at(row); } @@ -296,16 +295,10 @@ Tasks TaskModel::tasks(const QModelIndexList &indexes) const [](const Task &t) { return !t.isNull(); }); } -QList<Utils::Id> TaskModel::categoryIds() const +QList<TaskCategory> TaskModel::categories() const { - QList<Utils::Id> categories = m_categories.keys(); - categories.removeAll(Utils::Id()); // remove global category we added for bookkeeping - return categories; -} - -QString TaskModel::categoryDisplayName(Utils::Id categoryId) const -{ - return m_categories.value(categoryId).displayName; + const QList<TaskCategory> cat = Utils::transform<QList>(m_categories, &CategoryData::category); + return Utils::filtered(cat, [](const TaskCategory &c) { return c.id.isValid(); }); } int TaskModel::sizeOfFile(const QFont &font) diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h index 64df152cdf9..26f42f72ebd 100644 --- a/src/plugins/projectexplorer/taskmodel.h +++ b/src/plugins/projectexplorer/taskmodel.h @@ -9,8 +9,11 @@ #include <QRegularExpression> #include "task.h" +#include "taskhub.h" namespace ProjectExplorer { +class TaskCategory; + namespace Internal { class TaskModel : public QAbstractItemModel @@ -29,9 +32,8 @@ public: Task task(const QModelIndex &index) const; Tasks tasks(const QModelIndexList &indexes) const; - QList<Utils::Id> categoryIds() const; - QString categoryDisplayName(Utils::Id categoryId) const; - void addCategory(Utils::Id categoryId, const QString &categoryName, int priority); + QList<TaskCategory> categories() const; + void addCategory(const TaskCategory &category); Tasks tasks(Utils::Id categoryId = Utils::Id()) const; void addTask(const Task &t); @@ -84,8 +86,7 @@ private: errors = 0; } - QString displayName; - int priority = 0; + TaskCategory category; int count = 0; int warnings = 0; int errors = 0; diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp index dfc72e667f5..b00e7a14188 100644 --- a/src/plugins/projectexplorer/taskwindow.cpp +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -21,6 +21,7 @@ #include <coreplugin/session.h> #include <utils/algorithm.h> +#include <utils/execmenu.h> #include <utils/fileinprojectfinder.h> #include <utils/hostosinfo.h> #include <utils/itemviews.h> @@ -167,6 +168,10 @@ static QToolButton *createFilterButton(const QIcon &icon, const QString &toolTip TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) { + setId("Issues"); + setDisplayName(Tr::tr("Issues")); + setPriorityInStatusBar(100); + d->m_model = new Internal::TaskModel(this); d->m_filter = new Internal::TaskFilterModel(d->m_model); d->m_filter->setAutoAcceptChildRows(true); @@ -221,6 +226,7 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) d->m_categoriesMenu = new QMenu(d->m_categoriesButton); connect(d->m_categoriesMenu, &QMenu::aboutToShow, this, &TaskWindow::updateCategoriesMenu); + Utils::addToolTipsToMenu(d->m_categoriesMenu); d->m_categoriesButton->setMenu(d->m_categoriesMenu); @@ -239,17 +245,17 @@ TaskWindow::TaskWindow() : d(std::make_unique<TaskWindowPrivate>()) connect(hub, &TaskHub::showTask, this, &TaskWindow::showTask); connect(hub, &TaskHub::openTask, this, &TaskWindow::openTask); - connect(d->m_filter, &TaskFilterModel::rowsAboutToBeRemoved, + connect(d->m_filter, &TaskFilterModel::rowsAboutToBeRemoved, this, [this](const QModelIndex &, int first, int last) { d->m_visibleIssuesCount -= d->m_filter->issuesCount(first, last); emit setBadgeNumber(d->m_visibleIssuesCount); }); - connect(d->m_filter, &TaskFilterModel::rowsInserted, + connect(d->m_filter, &TaskFilterModel::rowsInserted, this, [this](const QModelIndex &, int first, int last) { d->m_visibleIssuesCount += d->m_filter->issuesCount(first, last); emit setBadgeNumber(d->m_visibleIssuesCount); }); - connect(d->m_filter, &TaskFilterModel::modelReset, [this] { + connect(d->m_filter, &TaskFilterModel::modelReset, this, [this] { d->m_visibleIssuesCount = d->m_filter->issuesCount(0, d->m_filter->rowCount()); emit setBadgeNumber(d->m_visibleIssuesCount); }); @@ -304,11 +310,6 @@ QList<QWidget*> TaskWindow::toolBarWidgets() const return {d->m_filterWarningsButton, d->m_categoriesButton, filterWidget()}; } -QString TaskWindow::displayName() const -{ - return Tr::tr("Issues"); -} - QWidget *TaskWindow::outputWidget(QWidget *) { return &d->m_treeView; @@ -341,19 +342,19 @@ void TaskWindow::saveSettings() { const QStringList categories = Utils::toList( Utils::transform(d->m_filter->filteredCategories(), &Id::toString)); - SessionManager::setValue(QLatin1String(SESSION_FILTER_CATEGORIES), categories); - SessionManager::setValue(QLatin1String(SESSION_FILTER_WARNINGS), d->m_filter->filterIncludesWarnings()); + SessionManager::setValue(SESSION_FILTER_CATEGORIES, categories); + SessionManager::setValue(SESSION_FILTER_WARNINGS, d->m_filter->filterIncludesWarnings()); } void TaskWindow::loadSettings() { - QVariant value = SessionManager::value(QLatin1String(SESSION_FILTER_CATEGORIES)); + QVariant value = SessionManager::value(SESSION_FILTER_CATEGORIES); if (value.isValid()) { const QSet<Id> categories = Utils::toSet( Utils::transform(value.toStringList(), &Id::fromString)); d->m_filter->setFilteredCategories(categories); } - value = SessionManager::value(QLatin1String(SESSION_FILTER_WARNINGS)); + value = SessionManager::value(SESSION_FILTER_WARNINGS); if (value.isValid()) { bool includeWarnings = value.toBool(); d->m_filter->setFilterIncludesWarnings(includeWarnings); @@ -367,12 +368,12 @@ void TaskWindow::visibilityChanged(bool visible) delayedInitialization(); } -void TaskWindow::addCategory(Id categoryId, const QString &displayName, bool visible, int priority) +void TaskWindow::addCategory(const TaskCategory &category) { - d->m_model->addCategory(categoryId, displayName, priority); - if (!visible) { + d->m_model->addCategory(category); + if (!category.visible) { QSet<Id> filters = d->m_filter->filteredCategories(); - filters.insert(categoryId); + filters.insert(category.id); d->m_filter->setFilteredCategories(filters); } } @@ -467,27 +468,20 @@ void TaskWindow::setShowWarnings(bool show) void TaskWindow::updateCategoriesMenu() { - using NameToIdsConstIt = QMap<QString, Id>::ConstIterator; - d->m_categoriesMenu->clear(); const QSet<Id> filteredCategories = d->m_filter->filteredCategories(); + const QList<TaskCategory> categories = Utils::sorted(d->m_model->categories(), + &TaskCategory::displayName); - QMap<QString, Id> nameToIds; - const QList<Id> ids = d->m_model->categoryIds(); - for (const Id categoryId : ids) - nameToIds.insert(d->m_model->categoryDisplayName(categoryId), categoryId); - - const NameToIdsConstIt cend = nameToIds.constEnd(); - for (NameToIdsConstIt it = nameToIds.constBegin(); it != cend; ++it) { - const QString &displayName = it.key(); - const Id categoryId = it.value(); + for (const TaskCategory &c : categories) { auto action = new QAction(d->m_categoriesMenu); action->setCheckable(true); - action->setText(displayName); - action->setChecked(!filteredCategories.contains(categoryId)); - connect(action, &QAction::triggered, this, [this, action, categoryId] { - setCategoryVisibility(categoryId, action->isChecked()); + action->setText(c.displayName); + action->setToolTip(c.description); + action->setChecked(!filteredCategories.contains(c.id)); + connect(action, &QAction::triggered, this, [this, action, id = c.id] { + setCategoryVisibility(id, action->isChecked()); }); d->m_categoriesMenu->addAction(action); } @@ -508,11 +502,6 @@ int TaskWindow::warningTaskCount(Id category) const return d->m_model->warningTaskCount(category); } -int TaskWindow::priorityInStatusBar() const -{ - return 90; -} - void TaskWindow::clearContents() { // clear all tasks in all displays @@ -672,6 +661,7 @@ TaskView::TaskView() { setMouseTracking(true); setVerticalScrollMode(ScrollPerPixel); + setUniformRowHeights(false); } void TaskView::resizeColumns() diff --git a/src/plugins/projectexplorer/taskwindow.h b/src/plugins/projectexplorer/taskwindow.h index c2e7a78a242..2d53928d97d 100644 --- a/src/plugins/projectexplorer/taskwindow.h +++ b/src/plugins/projectexplorer/taskwindow.h @@ -14,6 +14,7 @@ class QPoint; QT_END_NAMESPACE namespace ProjectExplorer { +class TaskCategory; class TaskHub; class Task; @@ -39,8 +40,6 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void visibilityChanged(bool visible) override; @@ -60,7 +59,7 @@ signals: private: void updateFilter() override; - void addCategory(Utils::Id categoryId, const QString &displayName, bool visible, int priority); + void addCategory(const TaskCategory &category); void addTask(const ProjectExplorer::Task &task); void removeTask(const ProjectExplorer::Task &task); void updatedTaskFileName(const Task &task, const QString &fileName); diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 8b8cfce2c80..5d51c501715 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -5,35 +5,34 @@ #include "abi.h" #include "devicesupport/idevice.h" -#include "headerpath.h" #include "projectexplorerconstants.h" #include "toolchainmanager.h" #include "task.h" -#include <utils/fileutils.h> #include <utils/qtcassert.h> -#include <QCoreApplication> -#include <QDir> -#include <QFileInfo> #include <QUuid> #include <utility> using namespace Utils; -static const char ID_KEY[] = "ProjectExplorer.ToolChain.Id"; -static const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ToolChain.DisplayName"; -static const char AUTODETECT_KEY[] = "ProjectExplorer.ToolChain.Autodetect"; -static const char DETECTION_SOURCE_KEY[] = "ProjectExplorer.ToolChain.DetectionSource"; -static const char LANGUAGE_KEY_V1[] = "ProjectExplorer.ToolChain.Language"; // For QtCreator <= 4.2 -static const char LANGUAGE_KEY_V2[] = "ProjectExplorer.ToolChain.LanguageV2"; // For QtCreator > 4.2 -const char CODE_MODEL_TRIPLE_KEY[] = "ExplicitCodeModelTargetTriple"; - namespace ProjectExplorer { namespace Internal { -static QList<ToolChainFactory *> g_toolChainFactories; +const char ID_KEY[] = "ProjectExplorer.ToolChain.Id"; +const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ToolChain.DisplayName"; +const char AUTODETECT_KEY[] = "ProjectExplorer.ToolChain.Autodetect"; +const char DETECTION_SOURCE_KEY[] = "ProjectExplorer.ToolChain.DetectionSource"; +const char LANGUAGE_KEY_V1[] = "ProjectExplorer.ToolChain.Language"; // For QtCreator <= 4.2 +const char LANGUAGE_KEY_V2[] = "ProjectExplorer.ToolChain.LanguageV2"; // For QtCreator > 4.2 +const char CODE_MODEL_TRIPLE_KEY[] = "ExplicitCodeModelTargetTriple"; + +QList<ToolChainFactory *> &toolChainFactories() +{ + static QList<ToolChainFactory *> theToolChainFactories; + return theToolChainFactories; +} // -------------------------------------------------------------------------- // ToolChainPrivate @@ -44,26 +43,26 @@ class ToolChainPrivate public: using Detection = ToolChain::Detection; - explicit ToolChainPrivate(Utils::Id typeId) : + explicit ToolChainPrivate(Id typeId) : m_id(QUuid::createUuid().toByteArray()), m_typeId(typeId), m_predefinedMacrosCache(new ToolChain::MacrosCache::element_type()), m_headerPathsCache(new ToolChain::HeaderPathsCache::element_type()) { QTC_ASSERT(m_typeId.isValid(), return); - QTC_ASSERT(!m_typeId.toString().contains(QLatin1Char(':')), return); + QTC_ASSERT(!m_typeId.name().contains(':'), return); } QByteArray m_id; FilePath m_compilerCommand; - QString m_compilerCommandKey; + Key m_compilerCommandKey; Abi m_targetAbi; - QString m_targetAbiKey; - QSet<Utils::Id> m_supportedLanguages; + Key m_targetAbiKey; + QSet<Id> m_supportedLanguages; mutable QString m_displayName; QString m_typeDisplayName; - Utils::Id m_typeId; - Utils::Id m_language; + Id m_typeId; + Id m_language; Detection m_detection = ToolChain::UninitializedDetection; QString m_detectionSource; QString m_explicitCodeModelTargetTriple; @@ -71,29 +70,30 @@ public: ToolChain::MacrosCache m_predefinedMacrosCache; ToolChain::HeaderPathsCache m_headerPathsCache; std::optional<bool> m_isValid; + bool m_hasError = false; }; // Deprecated used from QtCreator <= 4.2 -Utils::Id fromLanguageV1(int language) +Id fromLanguageV1(int language) { switch (language) { case Deprecated::Toolchain::C : - return Utils::Id(Constants::C_LANGUAGE_ID); + return Id(Constants::C_LANGUAGE_ID); case Deprecated::Toolchain::Cxx: - return Utils::Id(Constants::CXX_LANGUAGE_ID); + return Id(Constants::CXX_LANGUAGE_ID); case Deprecated::Toolchain::None: default: - return Utils::Id(); + return {}; } } } // namespace Internal -namespace Deprecated { -namespace Toolchain { +namespace Deprecated::Toolchain { + QString languageId(Language l) { switch (l) { @@ -104,10 +104,12 @@ QString languageId(Language l) case Language::Cxx: return QStringLiteral("Cxx"); }; - return QString(); + return {}; } -} // namespace Toolchain -} // namespace Deprecated + +} // namespace Deprecated::ToolChain + +using namespace Internal; /*! \class ProjectExplorer::ToolChain @@ -117,12 +119,12 @@ QString languageId(Language l) // -------------------------------------------------------------------------- -ToolChain::ToolChain(Utils::Id typeId) : - d(std::make_unique<Internal::ToolChainPrivate>(typeId)) +ToolChain::ToolChain(Id typeId) : + d(std::make_unique<ToolChainPrivate>(typeId)) { } -void ToolChain::setLanguage(Utils::Id language) +void ToolChain::setLanguage(Id language) { QTC_ASSERT(!d->m_language.isValid() || isAutoDetected(), return); QTC_ASSERT(language.isValid(), return); @@ -174,7 +176,7 @@ QStringList ToolChain::suggestedMkspecList() const return {}; } -Utils::Id ToolChain::typeId() const +Id ToolChain::typeId() const { return d->m_typeId; } @@ -199,7 +201,7 @@ FilePaths ToolChain::includedFiles(const QStringList &flags, const FilePath &dir return {}; } -Utils::Id ToolChain::language() const +Id ToolChain::language() const { return d->m_language; } @@ -217,11 +219,13 @@ bool ToolChain::operator == (const ToolChain &tc) const ToolChain *ToolChain::clone() const { - for (ToolChainFactory *f : std::as_const(Internal::g_toolChainFactories)) { + for (ToolChainFactory *f : std::as_const(toolChainFactories())) { if (f->supportedToolChainType() == d->m_typeId) { ToolChain *tc = f->create(); QTC_ASSERT(tc, return nullptr); - tc->fromMap(toMap()); + Store data; + toMap(data); + tc->fromMap(data); // New ID for the clone. It's different. tc->d->m_id = QUuid::createUuid().toByteArray(); return tc; @@ -237,14 +241,15 @@ ToolChain *ToolChain::clone() const Make sure to call this function when deriving. */ -QVariantMap ToolChain::toMap() const +void ToolChain::toMap(Store &result) const { - QVariantMap result; + AspectContainer::toMap(result); + QString idToSave = d->m_typeId.toString() + QLatin1Char(':') + QString::fromUtf8(id()); - result.insert(QLatin1String(ID_KEY), idToSave); - result.insert(QLatin1String(DISPLAY_NAME_KEY), displayName()); - result.insert(QLatin1String(AUTODETECT_KEY), isAutoDetected()); - result.insert(QLatin1String(DETECTION_SOURCE_KEY), d->m_detectionSource); + result.insert(ID_KEY, idToSave); + result.insert(DISPLAY_NAME_KEY, displayName()); + result.insert(AUTODETECT_KEY, isAutoDetected()); + result.insert(DETECTION_SOURCE_KEY, d->m_detectionSource); result.insert(CODE_MODEL_TRIPLE_KEY, d->m_explicitCodeModelTargetTriple); // <Compatibility with QtC 4.2> int oldLanguageId = -1; @@ -255,12 +260,11 @@ QVariantMap ToolChain::toMap() const if (oldLanguageId >= 0) result.insert(LANGUAGE_KEY_V1, oldLanguageId); // </Compatibility> - result.insert(QLatin1String(LANGUAGE_KEY_V2), language().toSetting()); + result.insert(LANGUAGE_KEY_V2, language().toSetting()); if (!d->m_targetAbiKey.isEmpty()) result.insert(d->m_targetAbiKey, d->m_targetAbi.toString()); if (!d->m_compilerCommandKey.isEmpty()) result.insert(d->m_compilerCommandKey, d->m_compilerCommand.toSettings()); - return result; } void ToolChain::toolChainUpdated() @@ -305,7 +309,7 @@ void ToolChain::setTargetAbiNoSignal(const Abi &abi) d->m_targetAbi = abi; } -void ToolChain::setTargetAbiKey(const QString &abiKey) +void ToolChain::setTargetAbiKey(const Key &abiKey) { d->m_targetAbiKey = abiKey; } @@ -330,7 +334,7 @@ bool ToolChain::matchesCompilerCommand(const FilePath &command) const return compilerCommand().isSameExecutable(command); } -void ToolChain::setCompilerCommandKey(const QString &commandKey) +void ToolChain::setCompilerCommandKey(const Key &commandKey) { d->m_compilerCommandKey = commandKey; } @@ -346,18 +350,20 @@ void ToolChain::setTypeDisplayName(const QString &typeName) Make sure to call this function when deriving. */ -bool ToolChain::fromMap(const QVariantMap &data) +void ToolChain::fromMap(const Store &data) { - d->m_displayName = data.value(QLatin1String(DISPLAY_NAME_KEY)).toString(); + AspectContainer::fromMap(data); + + d->m_displayName = data.value(DISPLAY_NAME_KEY).toString(); // make sure we have new style ids: - const QString id = data.value(QLatin1String(ID_KEY)).toString(); + const QString id = data.value(ID_KEY).toString(); int pos = id.indexOf(QLatin1Char(':')); - QTC_ASSERT(pos > 0, return false); - d->m_typeId = Utils::Id::fromString(id.left(pos)); + QTC_ASSERT(pos > 0, reportError(); return); + d->m_typeId = Id::fromString(id.left(pos)); d->m_id = id.mid(pos + 1).toUtf8(); - const bool autoDetect = data.value(QLatin1String(AUTODETECT_KEY), false).toBool(); + const bool autoDetect = data.value(AUTODETECT_KEY, false).toBool(); d->m_detection = autoDetect ? AutoDetection : ManualDetection; d->m_detectionSource = data.value(DETECTION_SOURCE_KEY).toString(); @@ -366,26 +372,34 @@ bool ToolChain::fromMap(const QVariantMap &data) if (data.contains(LANGUAGE_KEY_V2)) { // remove hack to trim language id in 4.4: This is to fix up broken language // ids that happened in 4.3 master branch - const QString langId = data.value(QLatin1String(LANGUAGE_KEY_V2)).toString(); + const QString langId = data.value(LANGUAGE_KEY_V2).toString(); const int pos = langId.lastIndexOf('.'); if (pos >= 0) - d->m_language = Utils::Id::fromString(langId.mid(pos + 1)); + d->m_language = Id::fromString(langId.mid(pos + 1)); else - d->m_language = Utils::Id::fromString(langId); + d->m_language = Id::fromString(langId); } else if (data.contains(LANGUAGE_KEY_V1)) { // Import from old settings - d->m_language = Internal::fromLanguageV1(data.value(QLatin1String(LANGUAGE_KEY_V1)).toInt()); + d->m_language = Internal::fromLanguageV1(data.value(LANGUAGE_KEY_V1).toInt()); } if (!d->m_language.isValid()) - d->m_language = Utils::Id(Constants::CXX_LANGUAGE_ID); + d->m_language = Id(Constants::CXX_LANGUAGE_ID); if (!d->m_targetAbiKey.isEmpty()) d->m_targetAbi = Abi::fromString(data.value(d->m_targetAbiKey).toString()); d->m_compilerCommand = FilePath::fromSettings(data.value(d->m_compilerCommandKey)); d->m_isValid.reset(); +} - return true; +void ToolChain::reportError() +{ + d->m_hasError = true; +} + +bool ToolChain::hasError() const +{ + return d->m_hasError; } const ToolChain::HeaderPathsCache &ToolChain::headerPathsCache() const @@ -410,9 +424,8 @@ static long toLanguageVersionAsLong(QByteArray dateAsByteArray) return result; } -Utils::LanguageVersion ToolChain::cxxLanguageVersion(const QByteArray &cplusplusMacroValue) +LanguageVersion ToolChain::cxxLanguageVersion(const QByteArray &cplusplusMacroValue) { - using Utils::LanguageVersion; const long version = toLanguageVersionAsLong(cplusplusMacroValue); if (version > 201703L) @@ -427,10 +440,8 @@ Utils::LanguageVersion ToolChain::cxxLanguageVersion(const QByteArray &cplusplus return LanguageVersion::CXX03; } -Utils::LanguageVersion ToolChain::languageVersion(const Utils::Id &language, const Macros ¯os) +LanguageVersion ToolChain::languageVersion(const Id &language, const Macros ¯os) { - using Utils::LanguageVersion; - if (language == Constants::CXX_LANGUAGE_ID) { for (const ProjectExplorer::Macro ¯o : macros) { if (macro.key == "__cplusplus") // Check for the C++ identifying macro @@ -502,7 +513,7 @@ Tasks ToolChain::validateKit(const Kit *) const QString ToolChain::sysRoot() const { - return QString(); + return {}; } QString ToolChain::explicitCodeModelTargetTriple() const @@ -542,23 +553,23 @@ void ToolChain::setExplicitCodeModelTargetTriple(const QString &triple) */ /*! - \fn bool ProjectExplorer::ToolChainFactory::canRestore(const QVariantMap &data) + \fn bool ProjectExplorer::ToolChainFactory::canRestore(const Store &data) Used by the tool chain manager to restore user-generated tool chains. */ ToolChainFactory::ToolChainFactory() { - Internal::g_toolChainFactories.append(this); + toolChainFactories().append(this); } ToolChainFactory::~ToolChainFactory() { - Internal::g_toolChainFactories.removeOne(this); + toolChainFactories().removeOne(this); } const QList<ToolChainFactory *> ToolChainFactory::allToolChainFactories() { - return Internal::g_toolChainFactories; + return toolChainFactories(); } Toolchains ToolChainFactory::autoDetect(const ToolchainDetector &detector) const @@ -583,7 +594,7 @@ ToolChain *ToolChainFactory::create() const return m_toolchainConstructor ? m_toolchainConstructor() : nullptr; } -ToolChain *ToolChainFactory::restore(const QVariantMap &data) +ToolChain *ToolChainFactory::restore(const Store &data) { if (!m_toolchainConstructor) return nullptr; @@ -591,39 +602,40 @@ ToolChain *ToolChainFactory::restore(const QVariantMap &data) ToolChain *tc = m_toolchainConstructor(); QTC_ASSERT(tc, return nullptr); - if (tc->fromMap(data)) + tc->fromMap(data); + if (!tc->hasError()) return tc; delete tc; return nullptr; } -static QPair<QString, QString> rawIdData(const QVariantMap &data) +static QPair<QString, QString> rawIdData(const Store &data) { - const QString raw = data.value(QLatin1String(ID_KEY)).toString(); + const QString raw = data.value(ID_KEY).toString(); const int pos = raw.indexOf(QLatin1Char(':')); QTC_ASSERT(pos > 0, return qMakePair(QString::fromLatin1("unknown"), QString::fromLatin1("unknown"))); return {raw.mid(0, pos), raw.mid(pos + 1)}; } -QByteArray ToolChainFactory::idFromMap(const QVariantMap &data) +QByteArray ToolChainFactory::idFromMap(const Store &data) { return rawIdData(data).second.toUtf8(); } -Utils::Id ToolChainFactory::typeIdFromMap(const QVariantMap &data) +Id ToolChainFactory::typeIdFromMap(const Store &data) { - return Utils::Id::fromString(rawIdData(data).first); + return Id::fromString(rawIdData(data).first); } -void ToolChainFactory::autoDetectionToMap(QVariantMap &data, bool detected) +void ToolChainFactory::autoDetectionToMap(Store &data, bool detected) { - data.insert(QLatin1String(AUTODETECT_KEY), detected); + data.insert(AUTODETECT_KEY, detected); } -ToolChain *ToolChainFactory::createToolChain(Utils::Id toolChainType) +ToolChain *ToolChainFactory::createToolChain(Id toolChainType) { - for (ToolChainFactory *factory : std::as_const(Internal::g_toolChainFactories)) { + for (ToolChainFactory *factory : std::as_const(toolChainFactories())) { if (factory->m_supportedToolChainType == toolChainType) { if (ToolChain *tc = factory->create()) { tc->d->m_typeId = toolChainType; @@ -634,22 +646,22 @@ ToolChain *ToolChainFactory::createToolChain(Utils::Id toolChainType) return nullptr; } -QList<Utils::Id> ToolChainFactory::supportedLanguages() const +QList<Id> ToolChainFactory::supportedLanguages() const { return m_supportsAllLanguages ? ToolChainManager::allLanguages() : m_supportedLanguages; } -Utils::Id ToolChainFactory::supportedToolChainType() const +Id ToolChainFactory::supportedToolChainType() const { return m_supportedToolChainType; } -void ToolChainFactory::setSupportedToolChainType(const Utils::Id &supportedToolChain) +void ToolChainFactory::setSupportedToolChainType(const Id &supportedToolChain) { m_supportedToolChainType = supportedToolChain; } -void ToolChainFactory::setSupportedLanguages(const QList<Utils::Id> &supportedLanguages) +void ToolChainFactory::setSupportedLanguages(const QList<Id> &supportedLanguages) { m_supportedLanguages = supportedLanguages; } @@ -659,12 +671,16 @@ void ToolChainFactory::setSupportsAllLanguages(bool supportsAllLanguages) m_supportsAllLanguages = supportsAllLanguages; } -void ToolChainFactory::setToolchainConstructor - (const std::function<ToolChain *()> &toolchainContructor) +void ToolChainFactory::setToolchainConstructor(const ToolChainConstructor &toolchainContructor) { m_toolchainConstructor = toolchainContructor; } +ToolChainFactory::ToolChainConstructor ToolChainFactory::toolchainConstructor() const +{ + return m_toolchainConstructor; +} + void ToolChainFactory::setUserCreatable(bool userCreatable) { m_userCreatable = userCreatable; @@ -678,28 +694,28 @@ ToolchainDetector::ToolchainDetector(const Toolchains &alreadyKnown, QTC_CHECK(device); } -BadToolchain::BadToolchain(const Utils::FilePath &filePath) +BadToolchain::BadToolchain(const FilePath &filePath) : BadToolchain(filePath, filePath.symLinkTarget(), filePath.lastModified()) {} -BadToolchain::BadToolchain(const Utils::FilePath &filePath, const Utils::FilePath &symlinkTarget, +BadToolchain::BadToolchain(const FilePath &filePath, const FilePath &symlinkTarget, const QDateTime ×tamp) : filePath(filePath), symlinkTarget(symlinkTarget), timestamp(timestamp) {} -static QString badToolchainFilePathKey() { return {"FilePath"}; } -static QString badToolchainSymlinkTargetKey() { return {"TargetFilePath"}; } -static QString badToolchainTimestampKey() { return {"Timestamp"}; } +static Key badToolchainFilePathKey() { return {"FilePath"}; } +static Key badToolchainSymlinkTargetKey() { return {"TargetFilePath"}; } +static Key badToolchainTimestampKey() { return {"Timestamp"}; } -QVariantMap BadToolchain::toMap() const +Store BadToolchain::toMap() const { return {{badToolchainFilePathKey(), filePath.toSettings()}, {badToolchainSymlinkTargetKey(), symlinkTarget.toSettings()}, {badToolchainTimestampKey(), timestamp.toMSecsSinceEpoch()}}; } -BadToolchain BadToolchain::fromMap(const QVariantMap &map) +BadToolchain BadToolchain::fromMap(const Store &map) { return { FilePath::fromSettings(map.value(badToolchainFilePathKey())), @@ -725,13 +741,15 @@ bool BadToolchains::isBadToolchain(const FilePath &toolchain) const QVariant BadToolchains::toVariant() const { - return Utils::transform<QVariantList>(toolchains, &BadToolchain::toMap); + return Utils::transform<QVariantList>(toolchains, [](const BadToolchain &bdc) { + return variantFromStore(bdc.toMap()); + }); } BadToolchains BadToolchains::fromVariant(const QVariant &v) { return Utils::transform<QList<BadToolchain>>(v.toList(), - [](const QVariant &e) { return BadToolchain::fromMap(e.toMap()); }); + [](const QVariant &e) { return BadToolchain::fromMap(storeFromVariant(e)); }); } } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 35cad5333ee..d63f3476dfc 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -12,15 +12,12 @@ #include "task.h" #include "toolchaincache.h" +#include <utils/aspects.h> #include <utils/cpplanguage_details.h> #include <utils/environment.h> -#include <utils/fileutils.h> -#include <utils/id.h> +#include <utils/store.h> #include <QDateTime> -#include <QObject> -#include <QStringList> -#include <QVariantMap> #include <functional> #include <memory> @@ -43,6 +40,7 @@ QString languageId(Language l); } // namespace Toolchain } // namespace Deprecated +class GccToolChain; class ToolChainConfigWidget; class ToolChainFactory; class Kit; @@ -60,7 +58,7 @@ public: // ToolChain (documentation inside) // -------------------------------------------------------------------------- -class PROJECTEXPLORER_EXPORT ToolChain +class PROJECTEXPLORER_EXPORT ToolChain : public Utils::AspectContainer { public: enum Detection { @@ -93,9 +91,9 @@ public: void setTargetAbi(const Abi &abi); virtual ProjectExplorer::Abis supportedAbis() const; - virtual QString originalTargetTriple() const { return QString(); } - virtual QStringList extraCodeModelFlags() const { return QStringList(); } - virtual Utils::FilePath installDir() const { return Utils::FilePath(); } + virtual QString originalTargetTriple() const { return {}; } + virtual QStringList extraCodeModelFlags() const { return {}; } + virtual Utils::FilePath installDir() const { return {}; } virtual bool hostPrefersToolchain() const { return true; } virtual bool isValid() const; @@ -145,7 +143,7 @@ public: // Used by the toolchainmanager to save user-generated tool chains. // Make sure to call this function when deriving! - virtual QVariantMap toMap() const; + virtual void toMap(Utils::Store &map) const; virtual Tasks validateKit(const Kit *k) const; virtual bool isJobCountSupported() const { return true; } @@ -164,6 +162,7 @@ public: }; virtual int priority() const { return PriorityNormal; } + virtual GccToolChain *asGccToolChain() { return nullptr; } protected: explicit ToolChain(Utils::Id typeId); @@ -171,9 +170,9 @@ protected: void setTypeDisplayName(const QString &typeName); void setTargetAbiNoSignal(const Abi &abi); - void setTargetAbiKey(const QString &abiKey); + void setTargetAbiKey(const Utils::Key &abiKey); - void setCompilerCommandKey(const QString &commandKey); + void setCompilerCommandKey(const Utils::Key &commandKey); const MacrosCache &predefinedMacrosCache() const; const HeaderPathsCache &headerPathsCache() const; @@ -181,7 +180,10 @@ protected: void toolChainUpdated(); // Make sure to call this function when deriving! - virtual bool fromMap(const QVariantMap &data); + virtual void fromMap(const Utils::Store &data); + + void reportError(); + bool hasError() const; enum class PossiblyConcatenatedFlag { No, Yes }; static Utils::FilePaths includedFiles(const QString &option, @@ -208,8 +210,8 @@ public: BadToolchain(const Utils::FilePath &filePath, const Utils::FilePath &symlinkTarget, const QDateTime ×tamp); - QVariantMap toMap() const; - static BadToolchain fromMap(const QVariantMap &map); + Utils::Store toMap() const; + static BadToolchain fromMap(const Utils::Store &map); Utils::FilePath filePath; Utils::FilePath symlinkTarget; @@ -235,9 +237,6 @@ public: const IDeviceConstPtr &device, const Utils::FilePaths &searchPaths); - bool isBadToolchain(const Utils::FilePath &toolchain) const; - void addBadToolchain(const Utils::FilePath &toolchain) const; - const Toolchains alreadyKnown; const IDeviceConstPtr device; const Utils::FilePaths searchPaths; // If empty use device path and/or magic. @@ -261,13 +260,13 @@ public: virtual Toolchains detectForImport(const ToolChainDescription &tcd) const; virtual bool canCreate() const; - virtual ToolChain *create() const; + ToolChain *create() const; - ToolChain *restore(const QVariantMap &data); + ToolChain *restore(const Utils::Store &data); - static QByteArray idFromMap(const QVariantMap &data); - static Utils::Id typeIdFromMap(const QVariantMap &data); - static void autoDetectionToMap(QVariantMap &data, bool detected); + static QByteArray idFromMap(const Utils::Store &data); + static Utils::Id typeIdFromMap(const Utils::Store &data); + static void autoDetectionToMap(Utils::Store &data, bool detected); static ToolChain *createToolChain(Utils::Id toolChainType); @@ -280,7 +279,9 @@ protected: void setSupportedToolChainType(const Utils::Id &supportedToolChainType); void setSupportedLanguages(const QList<Utils::Id> &supportedLanguages); void setSupportsAllLanguages(bool supportsAllLanguages); - void setToolchainConstructor(const std::function<ToolChain *()> &constructor); + using ToolChainConstructor = std::function<ToolChain *()>; + void setToolchainConstructor(const ToolChainConstructor &constructor); + ToolChainConstructor toolchainConstructor() const; class Candidate { public: @@ -301,7 +302,7 @@ private: QList<Utils::Id> m_supportedLanguages; bool m_supportsAllLanguages = false; bool m_userCreatable = false; - std::function<ToolChain *()> m_toolchainConstructor; + ToolChainConstructor m_toolchainConstructor; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index d7f20d6f0f5..d04510679ed 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -16,7 +16,7 @@ #include <utils/qtcassert.h> #include <utils/algorithm.h> -#include <QSettings> +#include <nanotrace/nanotrace.h> using namespace Utils; @@ -62,7 +62,7 @@ using namespace Internal; const char DETECT_X64_AS_X32_KEY[] = "ProjectExplorer/Toolchains/DetectX64AsX32"; -static QString badToolchainsKey() { return {"BadToolChains"}; } +static Key badToolchainsKey() { return "BadToolChains"; } // -------------------------------------------------------------------------- // ToolChainManager @@ -82,7 +82,7 @@ ToolChainManager::ToolChainManager(QObject *parent) : connect(this, &ToolChainManager::toolChainRemoved, this, &ToolChainManager::toolChainsChanged); connect(this, &ToolChainManager::toolChainUpdated, this, &ToolChainManager::toolChainsChanged); - QSettings * const s = Core::ICore::settings(); + QtcSettings * const s = Core::ICore::settings(); d->m_detectionSettings.detectX64AsX32 = s->value(DETECT_X64_AS_X32_KEY, ToolchainDetectionSettings().detectX64AsX32).toBool(); d->m_badToolchains = BadToolchains::fromVariant(s->value(badToolchainsKey())); @@ -102,6 +102,7 @@ ToolChainManager *ToolChainManager::instance() void ToolChainManager::restoreToolChains() { + NANOTRACE_SCOPE("ProjectExplorer", "ToolChainManager::restoreToolChains"); QTC_ASSERT(!d->m_accessor, return); d->m_accessor = std::make_unique<Internal::ToolChainSettingsAccessor>(); @@ -126,6 +127,7 @@ void ToolChainManager::saveToolChains() const Toolchains &ToolChainManager::toolchains() { + QTC_CHECK(d->m_loaded); return d->m_toolChains; } @@ -137,11 +139,13 @@ Toolchains ToolChainManager::toolchains(const ToolChain::Predicate &predicate) ToolChain *ToolChainManager::toolChain(const ToolChain::Predicate &predicate) { + QTC_CHECK(d->m_loaded); return Utils::findOrDefault(d->m_toolChains, predicate); } Toolchains ToolChainManager::findToolChains(const Abi &abi) { + QTC_CHECK(d->m_loaded); Toolchains result; for (ToolChain *tc : std::as_const(d->m_toolChains)) { bool isCompatible = Utils::anyOf(tc->supportedAbis(), [abi](const Abi &supportedAbi) { @@ -156,6 +160,7 @@ Toolchains ToolChainManager::findToolChains(const Abi &abi) ToolChain *ToolChainManager::findToolChain(const QByteArray &id) { + QTC_CHECK(d->m_loaded); if (id.isEmpty()) return nullptr; @@ -211,6 +216,7 @@ bool ToolChainManager::registerToolChain(ToolChain *tc) void ToolChainManager::deregisterToolChain(ToolChain *tc) { + QTC_CHECK(d->m_loaded); if (!tc || !d->m_toolChains.contains(tc)) return; d->m_toolChains.removeOne(tc); diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index 0c87f248dbb..3585050f6dc 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -5,13 +5,13 @@ #include "abi.h" #include "devicesupport/devicemanager.h" +#include "kitoptionspage.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "toolchain.h" #include "toolchainconfigwidget.h" #include "toolchainmanager.h" -#include <app/app_version.h> #include <coreplugin/icore.h> #include <extensionsystem/pluginmanager.h> @@ -79,7 +79,7 @@ public: return column == 0 && !toolChain->isValid() ? Utils::Icons::CRITICAL.icon() : QVariant(); } - return QVariant(); + return {}; } ToolChainConfigWidget *widget() @@ -118,10 +118,14 @@ public: const auto layout = new QVBoxLayout(this); m_detectX64AsX32CheckBox.setText(Tr::tr("Detect x86_64 GCC compilers " "as x86_64 and x86")); - m_detectX64AsX32CheckBox.setToolTip(Tr::tr("If checked, %1 will " - "set up two instances of each x86_64 compiler:\nOne for the native x86_64 target, and " - "one for a plain x86 target.\nEnable this if you plan to create 32-bit x86 binaries " - "without using a dedicated cross compiler.").arg(Core::Constants::IDE_DISPLAY_NAME)); + m_detectX64AsX32CheckBox.setToolTip( + Tr::tr("If checked, %1 will " + "set up two instances of each x86_64 compiler:\nOne for the native x86_64 " + "target, and " + "one for a plain x86 target.\nEnable this if you plan to create 32-bit x86 " + "binaries " + "without using a dedicated cross compiler.") + .arg(QGuiApplication::applicationDisplayName())); m_detectX64AsX32CheckBox.setChecked(settings.detectX64AsX32); layout->addWidget(&m_detectX64AsX32CheckBox); const auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -178,7 +182,11 @@ public: m_toolChainView->setUniformRowHeights(true); m_toolChainView->setSelectionMode(QAbstractItemView::SingleSelection); m_toolChainView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_toolChainView->setModel(&m_model); + m_sortModel.setSourceModel(&m_model); + m_sortModel.setSortedCategories({Constants::msgAutoDetected(), Constants::msgManual()}); + m_toolChainView->setModel(&m_sortModel); + m_toolChainView->setSortingEnabled(true); + m_toolChainView->sortByColumn(0, Qt::AscendingOrder); m_toolChainView->header()->setStretchLastSection(false); m_toolChainView->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); m_toolChainView->header()->setSectionResizeMode(1, QHeaderView::Stretch); @@ -207,7 +215,7 @@ public: m_addButton->setStyleSheet("text-align:center;"); m_cloneButton = new QPushButton(Tr::tr("Clone"), this); - connect(m_cloneButton, &QAbstractButton::clicked, [this] { cloneToolChain(); }); + connect(m_cloneButton, &QAbstractButton::clicked, this, [this] { cloneToolChain(); }); m_delButton = new QPushButton(Tr::tr("Remove"), this); @@ -277,7 +285,7 @@ public: connect(ToolChainManager::instance(), &ToolChainManager::toolChainsChanged, this, &ToolChainOptionsWidget::toolChainSelectionChanged); - connect(m_delButton, &QAbstractButton::clicked, [this] { + connect(m_delButton, &QAbstractButton::clicked, this, [this] { if (ToolChainTreeItem *item = currentTreeItem()) markForRemoval(item); }); @@ -300,7 +308,8 @@ public: QAction *createAction(const QString &name, ToolChainFactory *factory, Utils::Id language) { auto action = new QAction(name, nullptr); - connect(action, &QAction::triggered, [this, factory, language] { createToolChain(factory, language); }); + connect(action, &QAction::triggered, this, + [this, factory, language] { createToolChain(factory, language); }); return action; } @@ -310,6 +319,7 @@ public: private: TreeModel<TreeItem, ToolChainTreeItem> m_model; + KitSettingsSortModel m_sortModel; QList<ToolChainFactory *> m_factories; QTreeView *m_toolChainView; DetailsWidget *m_container; @@ -514,7 +524,7 @@ void ToolChainOptionsWidget::createToolChain(ToolChainFactory *factory, const Ut auto item = insertToolChain(tc, true); m_toAddList.append(item); - m_toolChainView->setCurrentIndex(m_model.indexForItem(item)); + m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } void ToolChainOptionsWidget::cloneToolChain() @@ -533,7 +543,7 @@ void ToolChainOptionsWidget::cloneToolChain() auto item = insertToolChain(tc, true); m_toAddList.append(item); - m_toolChainView->setCurrentIndex(m_model.indexForItem(item)); + m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } void ToolChainOptionsWidget::updateState() @@ -552,8 +562,7 @@ void ToolChainOptionsWidget::updateState() ToolChainTreeItem *ToolChainOptionsWidget::currentTreeItem() { - QModelIndex index = m_toolChainView->currentIndex(); - TreeItem *item = m_model.itemForIndex(index); + TreeItem *item = m_model.itemForIndex(m_sortModel.mapToSource(m_toolChainView->currentIndex())); return (item && item->level() == 3) ? static_cast<ToolChainTreeItem *>(item) : nullptr; } diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 35224e4d02a..1683e8b9c89 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -10,11 +10,12 @@ #include <coreplugin/icore.h> -#include <app/app_version.h> - #include <utils/algorithm.h> +#include <nanotrace/nanotrace.h> + #include <QElapsedTimer> +#include <QGuiApplication> #include <QLoggingCategory> using namespace Utils; @@ -35,7 +36,7 @@ public: ToolChainSettingsUpgraderV0() : Utils::VersionUpgrader(0, "4.6") { } // NOOP - QVariantMap upgrade(const QVariantMap &data) final { return data; } + Store upgrade(const Store &data) final { return data; } }; // -------------------------------------------------------------------- @@ -57,6 +58,9 @@ static Toolchains autoDetectToolChains(const ToolchainDetector &detector) { Toolchains result; for (ToolChainFactory *f : ToolChainFactory::allToolChainFactories()) { + NANOTRACE_SCOPE_ARGS("ProjectExplorer", + "ToolChainSettingsAccessor::autoDetectToolChains", + {"factory", f->displayName().toStdString()}); QElapsedTimer et; et.start(); result.append(f->autoDetect(detector)); @@ -172,7 +176,7 @@ static ToolChainOperations mergeToolChainLists(const Toolchains &systemFileTcs, ToolChainSettingsAccessor::ToolChainSettingsAccessor() { setDocType("QtCreatorToolChains"); - setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + setApplicationDisplayName(QGuiApplication::applicationDisplayName()); setBaseFilePath(Core::ICore::userResourcePath(TOOLCHAIN_FILENAME)); addVersionUpgrader(std::make_unique<ToolChainSettingsUpgraderV0>()); @@ -180,6 +184,7 @@ ToolChainSettingsAccessor::ToolChainSettingsAccessor() Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const { + NANOTRACE_SCOPE("ProjectExplorer", "ToolChainSettingsAccessor::restoreToolChains"); // read all tool chains from SDK const Toolchains systemFileTcs = toolChains( restoreSettings(Core::ICore::installerResourcePath(TOOLCHAIN_FILENAME), parent)); @@ -215,16 +220,17 @@ Toolchains ToolChainSettingsAccessor::restoreToolChains(QWidget *parent) const void ToolChainSettingsAccessor::saveToolChains(const Toolchains &toolchains, QWidget *parent) { - QVariantMap data; + Store data; int count = 0; for (const ToolChain *tc : toolchains) { if (!tc || (!tc->isValid() && tc->isAutoDetected())) continue; - const QVariantMap tmp = tc->toMap(); + Store tmp; + tc->toMap(tmp); if (tmp.isEmpty()) continue; - data.insert(QString::fromLatin1(TOOLCHAIN_DATA_KEY) + QString::number(count), tmp); + data.insert(numberedKey(TOOLCHAIN_DATA_KEY, count), variantFromStore(tmp)); ++count; } data.insert(TOOLCHAIN_COUNT_KEY, count); @@ -234,18 +240,18 @@ void ToolChainSettingsAccessor::saveToolChains(const Toolchains &toolchains, QWi saveSettings(data, parent); } -Toolchains ToolChainSettingsAccessor::toolChains(const QVariantMap &data) const +Toolchains ToolChainSettingsAccessor::toolChains(const Store &data) const { Toolchains result; const QList<ToolChainFactory *> factories = ToolChainFactory::allToolChainFactories(); const int count = data.value(TOOLCHAIN_COUNT_KEY, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = QString::fromLatin1(TOOLCHAIN_DATA_KEY) + QString::number(i); + const Key key = numberedKey(TOOLCHAIN_DATA_KEY, i); if (!data.contains(key)) break; - const QVariantMap tcMap = data.value(key).toMap(); + const Store tcMap = storeFromVariant(data.value(key)); bool restored = false; const Utils::Id tcType = ToolChainFactory::typeIdFromMap(tcMap); @@ -306,10 +312,10 @@ public: static bool hasToolChains() { return !m_toolChains.isEmpty(); } bool isValid() const override { return m_valid; } - MacroInspectionRunner createMacroInspectionRunner() const override { return MacroInspectionRunner(); } + MacroInspectionRunner createMacroInspectionRunner() const override { return {}; } LanguageExtensions languageExtensions(const QStringList &cxxflags) const override { Q_UNUSED(cxxflags) return LanguageExtension::None; } WarningFlags warningFlags(const QStringList &cflags) const override { Q_UNUSED(cflags) return WarningFlags::NoWarnings; } - BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &) const override { return BuiltInHeaderPathsRunner(); } + BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner(const Utils::Environment &) const override { return {}; } void addToEnvironment(Environment &env) const override { Q_UNUSED(env) } FilePath makeCommand(const Environment &) const override { return "make"; } QList<OutputLineParser *> createOutputParsers() const override { return {}; } @@ -320,18 +326,16 @@ public: return static_cast<const TTC *>(&other)->token == token; } - bool fromMap(const QVariantMap &data) final + void fromMap(const Store &data) final { ToolChain::fromMap(data); token = data.value(TestTokenKey).toByteArray(); - return true; } - QVariantMap toMap() const final + void toMap(Store &data) const final { - QVariantMap data = ToolChain::toMap(); + ToolChain::toMap(data); data[TestTokenKey] = token; - return data; } QByteArray token; @@ -340,8 +344,6 @@ private: bool m_valid = false; static QList<TTC *> m_toolChains; - -public: }; QList<TTC *> TTC::m_toolChains; diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.h b/src/plugins/projectexplorer/toolchainsettingsaccessor.h index 418a6ea3eef..aa2b44bc650 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.h +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.h @@ -23,7 +23,7 @@ public: void saveToolChains(const QList<ToolChain *> &toolchains, QWidget *parent); private: - QList<ToolChain *> toolChains(const QVariantMap &data) const; + QList<ToolChain *> toolChains(const Utils::Store &data) const; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp index 294ef4988e1..1eefc3baf42 100644 --- a/src/plugins/projectexplorer/treescanner.cpp +++ b/src/plugins/projectexplorer/treescanner.cpp @@ -77,7 +77,7 @@ TreeScanner::Result TreeScanner::result() const { if (isFinished()) return m_scanFuture.result(); - return Result(); + return {}; } TreeScanner::Result TreeScanner::release() @@ -88,7 +88,7 @@ TreeScanner::Result TreeScanner::release() return result; } m_scanFuture = Future(); - return Result(); + return {}; } void TreeScanner::reset() diff --git a/src/plugins/projectexplorer/userfileaccessor.cpp b/src/plugins/projectexplorer/userfileaccessor.cpp index 5a765cb537f..467fe0c0a87 100644 --- a/src/plugins/projectexplorer/userfileaccessor.cpp +++ b/src/plugins/projectexplorer/userfileaccessor.cpp @@ -13,21 +13,29 @@ #include "kit.h" #include "kitmanager.h" -#include <app/app_version.h> #include <coreplugin/icore.h> + +#include <utils/appinfo.h> #include <utils/environment.h> #include <utils/hostosinfo.h> #include <utils/persistentsettings.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <QGuiApplication> #include <QRegularExpression> using namespace Utils; using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; -using StringVariantPair = std::pair<const QString, QVariant>; +using KeyVariantPair = std::pair<const Key, QVariant>; + +static QString userFileExtension() +{ + const QString ext = Utils::appInfo().userFileExtension; + return ext.isEmpty() ? QLatin1String(".user") : ext; +} namespace { @@ -35,22 +43,12 @@ const char OBSOLETE_VERSION_KEY[] = "ProjectExplorer.Project.Updater.FileVersion const char SHARED_SETTINGS[] = "SharedSettings"; const char USER_STICKY_KEYS_KEY[] = "UserStickyKeys"; -#ifdef PROJECT_USER_FILE_EXTENSION -#define STRINGIFY_INTERNAL(x) #x -#define STRINGIFY(x) STRINGIFY_INTERNAL(x) - -const char FILE_EXTENSION_STR[] = STRINGIFY(PROJECT_USER_FILE_EXTENSION); -#else -const char FILE_EXTENSION_STR[] = ".user"; - -#endif - // Version 14 Move builddir into BuildConfiguration class UserFileVersion14Upgrader : public VersionUpgrader { public: UserFileVersion14Upgrader() : VersionUpgrader(14, "3.0-pre1") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; }; // Version 15 Use settingsaccessor based class for user file reading/writing @@ -58,7 +56,7 @@ class UserFileVersion15Upgrader : public VersionUpgrader { public: UserFileVersion15Upgrader() : VersionUpgrader(15, "3.2-pre1") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; }; // Version 16 Changed android deployment @@ -66,15 +64,15 @@ class UserFileVersion16Upgrader : public VersionUpgrader { public: UserFileVersion16Upgrader() : VersionUpgrader(16, "3.3-pre1") { } - QVariantMap upgrade(const QVariantMap &data) final; + Store upgrade(const Store &data) final; private: class OldStepMaps { public: QString defaultDisplayName; QString displayName; - QVariantMap androidPackageInstall; - QVariantMap androidDeployQt; + Store androidPackageInstall; + Store androidDeployQt; bool isEmpty() { return androidPackageInstall.isEmpty() || androidDeployQt.isEmpty(); @@ -82,10 +80,10 @@ private: }; - QVariantMap removeAndroidPackageStep(QVariantMap deployMap); - OldStepMaps extractStepMaps(const QVariantMap &deployMap); + Store removeAndroidPackageStep(Store deployMap); + OldStepMaps extractStepMaps(const Store &deployMap); enum NamePolicy { KeepName, RenameBuildConfiguration }; - QVariantMap insertSteps(QVariantMap buildConfigurationMap, + Store insertSteps(Store buildConfigurationMap, const OldStepMaps &oldStepMap, NamePolicy policy); }; @@ -95,7 +93,7 @@ class UserFileVersion17Upgrader : public VersionUpgrader { public: UserFileVersion17Upgrader() : VersionUpgrader(17, "3.3-pre2") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; QVariant process(const QVariant &entry); @@ -110,7 +108,7 @@ class UserFileVersion18Upgrader : public VersionUpgrader { public: UserFileVersion18Upgrader() : VersionUpgrader(18, "4.8-pre1") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; static QVariant process(const QVariant &entry); }; @@ -121,9 +119,9 @@ class UserFileVersion19Upgrader : public VersionUpgrader { public: UserFileVersion19Upgrader() : VersionUpgrader(19, "4.8-pre2") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; - static QVariant process(const QVariant &entry, const QStringList &path); + static QVariant process(const QVariant &entry, const KeyList &path); }; // Version 20 renames "Qbs.Deploy" to "ProjectExplorer.DefaultDeployConfiguration" @@ -133,7 +131,7 @@ class UserFileVersion20Upgrader : public VersionUpgrader { public: UserFileVersion20Upgrader() : VersionUpgrader(20, "4.9-pre1") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; static QVariant process(const QVariant &entry); }; @@ -145,7 +143,7 @@ class UserFileVersion21Upgrader : public VersionUpgrader { public: UserFileVersion21Upgrader() : VersionUpgrader(21, "4.10-pre1") { } - QVariantMap upgrade(const QVariantMap &map) final; + Store upgrade(const Store &map) final; static QVariant process(const QVariant &entry); }; @@ -288,7 +286,7 @@ UserFileAccessor::UserFileAccessor(Project *project) { setStrategy(std::make_unique<VersionedBackUpStrategy>(this)); setDocType("QtCreatorProject"); - setApplicationDisplayName(Core::Constants::IDE_DISPLAY_NAME); + setApplicationDisplayName(QGuiApplication::applicationDisplayName()); // Setup: const FilePath externalUser = externalUserFile(); @@ -325,9 +323,9 @@ SettingsMergeResult UserFileAccessor::merge(const MergingSettingsAccessor::SettingsMergeData &global, const MergingSettingsAccessor::SettingsMergeData &local) const { - const QStringList stickyKeys = global.main.value(USER_STICKY_KEYS_KEY).toStringList(); + const KeyList stickyKeys = keysFromStrings(global.main.value(USER_STICKY_KEYS_KEY).toStringList()); - const QString key = local.key; + const Key key = local.key; const QVariant mainValue = local.main.value(key); const QVariant secondaryValue = local.secondary.value(key); @@ -352,11 +350,11 @@ UserFileAccessor::merge(const MergingSettingsAccessor::SettingsMergeData &global // Although this approach is more flexible than permanent/forever sticky settings, it has // the side-effect that if a particular value unintentionally becomes the same in both // the .user and .shared files, this setting will "unstick". -SettingsMergeFunction UserFileAccessor::userStickyTrackerFunction(QStringList &stickyKeys) const +SettingsMergeFunction UserFileAccessor::userStickyTrackerFunction(KeyList &stickyKeys) const { return [&stickyKeys](const SettingsMergeData &global, const SettingsMergeData &local) -> SettingsMergeResult { - const QString key = local.key; + const Key key = local.key; const QVariant main = local.main.value(key); const QVariant secondary = local.secondary.value(key); @@ -385,15 +383,15 @@ QVariant UserFileAccessor::retrieveSharedSettings() const FilePath UserFileAccessor::projectUserFile() const { static const QString qtcExt = qtcEnvironmentVariable("QTC_EXTENSION"); - return m_project->projectFilePath() - .stringAppended(generateSuffix(qtcExt.isEmpty() ? FILE_EXTENSION_STR : qtcExt)); + return m_project->projectFilePath().stringAppended( + generateSuffix(qtcExt.isEmpty() ? userFileExtension() : qtcExt)); } FilePath UserFileAccessor::externalUserFile() const { static const QString qtcExt = qtcEnvironmentVariable("QTC_EXTENSION"); return externalUserFilePath(m_project->projectFilePath(), - generateSuffix(qtcExt.isEmpty() ? FILE_EXTENSION_STR : qtcExt)); + generateSuffix(qtcExt.isEmpty() ? userFileExtension() : qtcExt)); } FilePath UserFileAccessor::sharedFile() const @@ -403,22 +401,22 @@ FilePath UserFileAccessor::sharedFile() const .stringAppended(generateSuffix(qtcExt.isEmpty() ? ".shared" : qtcExt)); } -QVariantMap UserFileAccessor::postprocessMerge(const QVariantMap &main, - const QVariantMap &secondary, - const QVariantMap &result) const +Store UserFileAccessor::postprocessMerge(const Store &main, + const Store &secondary, + const Store &result) const { - project()->setProperty(SHARED_SETTINGS, secondary); + project()->setProperty(SHARED_SETTINGS, variantFromStore(secondary)); return MergingSettingsAccessor::postprocessMerge(main, secondary, result); } -QVariantMap UserFileAccessor::preprocessReadSettings(const QVariantMap &data) const +Store UserFileAccessor::preprocessReadSettings(const Store &data) const { - QVariantMap tmp = MergingSettingsAccessor::preprocessReadSettings(data); + Store tmp = MergingSettingsAccessor::preprocessReadSettings(data); // Move from old Version field to new one: // This cannot be done in a normal upgrader since the version information is needed // to decide which upgraders to run - const QString obsoleteKey = OBSOLETE_VERSION_KEY; + const Key obsoleteKey = OBSOLETE_VERSION_KEY; const int obsoleteVersion = tmp.value(obsoleteKey, -1).toInt(); if (obsoleteVersion > versionFromMap(tmp)) @@ -428,16 +426,16 @@ QVariantMap UserFileAccessor::preprocessReadSettings(const QVariantMap &data) co return tmp; } -QVariantMap UserFileAccessor::prepareToWriteSettings(const QVariantMap &data) const +Store UserFileAccessor::prepareToWriteSettings(const Store &data) const { - const QVariantMap tmp = MergingSettingsAccessor::prepareToWriteSettings(data); - const QVariantMap shared = retrieveSharedSettings().toMap(); - QVariantMap result; + const Store tmp = MergingSettingsAccessor::prepareToWriteSettings(data); + const Store shared = storeFromVariant(retrieveSharedSettings()); + Store result; if (!shared.isEmpty()) { - QStringList stickyKeys; + KeyList stickyKeys; SettingsMergeFunction merge = userStickyTrackerFunction(stickyKeys); - result = mergeQVariantMaps(tmp, shared, merge).toMap(); - result.insert(USER_STICKY_KEYS_KEY, stickyKeys); + result = storeFromVariant(mergeQVariantMaps(tmp, shared, merge)); + result.insert(USER_STICKY_KEYS_KEY, stringsFromKeys(stickyKeys)); } else { result = tmp; } @@ -451,12 +449,12 @@ QVariantMap UserFileAccessor::prepareToWriteSettings(const QVariantMap &data) co // UserFileVersion14Upgrader: // -------------------------------------------------------------------- -QVariantMap UserFileVersion14Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion14Upgrader::upgrade(const Store &map) { - QVariantMap result; + Store result; for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) { if (it.value().typeId() == QVariant::Map) - result.insert(it.key(), upgrade(it.value().toMap())); + result.insert(it.key(), variantFromStore(upgrade(storeFromVariant(it.value())))); else if (it.key() == "AutotoolsProjectManager.AutotoolsBuildConfiguration.BuildDirectory" || it.key() == "CMakeProjectManager.CMakeBuildConfiguration.BuildDirectory" || it.key() == "GenericProjectManager.GenericBuildConfiguration.BuildDirectory" @@ -473,30 +471,30 @@ QVariantMap UserFileVersion14Upgrader::upgrade(const QVariantMap &map) // UserFileVersion15Upgrader: // -------------------------------------------------------------------- -QVariantMap UserFileVersion15Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion15Upgrader::upgrade(const Store &map) { - const QList<Change> changes{{QLatin1String("ProjectExplorer.Project.Updater.EnvironmentId"), - QLatin1String("EnvironmentId")}, - {QLatin1String("ProjectExplorer.Project.UserStickyKeys"), - QLatin1String("UserStickyKeys")}}; - return renameKeys(changes, QVariantMap(map)); + const QList<Change> changes{ + {"ProjectExplorer.Project.Updater.EnvironmentId", "EnvironmentId"}, + {"ProjectExplorer.Project.UserStickyKeys", "UserStickyKeys"} + }; + return renameKeys(changes, map); } // -------------------------------------------------------------------- // UserFileVersion16Upgrader: // -------------------------------------------------------------------- -UserFileVersion16Upgrader::OldStepMaps UserFileVersion16Upgrader::extractStepMaps(const QVariantMap &deployMap) +UserFileVersion16Upgrader::OldStepMaps UserFileVersion16Upgrader::extractStepMaps(const Store &deployMap) { OldStepMaps result; result.defaultDisplayName = deployMap.value("ProjectExplorer.ProjectConfiguration.DefaultDisplayName").toString(); result.displayName = deployMap.value("ProjectExplorer.ProjectConfiguration.DisplayName").toString(); - const QString stepListKey = "ProjectExplorer.BuildConfiguration.BuildStepList.0"; - QVariantMap stepListMap = deployMap.value(stepListKey).toMap(); + const Key stepListKey = "ProjectExplorer.BuildConfiguration.BuildStepList.0"; + Store stepListMap = storeFromVariant(deployMap.value(stepListKey)); int stepCount = stepListMap.value("ProjectExplorer.BuildStepList.StepsCount", 0).toInt(); - QString stepKey = "ProjectExplorer.BuildStepList.Step."; + Key stepKey = "ProjectExplorer.BuildStepList.Step."; for (int i = 0; i < stepCount; ++i) { - QVariantMap stepMap = stepListMap.value(stepKey + QString::number(i)).toMap(); + Store stepMap = storeFromVariant(stepListMap.value(numberedKey(stepKey, i))); const QString id = stepMap.value("ProjectExplorer.ProjectConfiguration.Id").toString(); if (id == "Qt4ProjectManager.AndroidDeployQtStep") result.androidDeployQt = stepMap; @@ -509,19 +507,19 @@ UserFileVersion16Upgrader::OldStepMaps UserFileVersion16Upgrader::extractStepMap return result; } -QVariantMap UserFileVersion16Upgrader::removeAndroidPackageStep(QVariantMap deployMap) +Store UserFileVersion16Upgrader::removeAndroidPackageStep(Store deployMap) { - const QString stepListKey = "ProjectExplorer.BuildConfiguration.BuildStepList.0"; - QVariantMap stepListMap = deployMap.value(stepListKey).toMap(); - const QString stepCountKey = "ProjectExplorer.BuildStepList.StepsCount"; + const Key stepListKey = "ProjectExplorer.BuildConfiguration.BuildStepList.0"; + Store stepListMap = storeFromVariant(deployMap.value(stepListKey)); + const Key stepCountKey = "ProjectExplorer.BuildStepList.StepsCount"; int stepCount = stepListMap.value(stepCountKey, 0).toInt(); - QString stepKey = "ProjectExplorer.BuildStepList.Step."; + Key stepKey = "ProjectExplorer.BuildStepList.Step."; int targetPosition = 0; for (int sourcePosition = 0; sourcePosition < stepCount; ++sourcePosition) { - QVariantMap stepMap = stepListMap.value(stepKey + QString::number(sourcePosition)).toMap(); + Store stepMap = storeFromVariant(stepListMap.value(numberedKey(stepKey, sourcePosition))); if (stepMap.value("ProjectExplorer.ProjectConfiguration.Id").toString() != "Qt4ProjectManager.AndroidPackageInstallationStep") { - stepListMap.insert(stepKey + QString::number(targetPosition), stepMap); + stepListMap.insert(numberedKey(stepKey, targetPosition), variantFromStore(stepMap)); ++targetPosition; } } @@ -529,64 +527,64 @@ QVariantMap UserFileVersion16Upgrader::removeAndroidPackageStep(QVariantMap depl stepListMap.insert(stepCountKey, targetPosition); for (int i = targetPosition; i < stepCount; ++i) - stepListMap.remove(stepKey + QString::number(i)); + stepListMap.remove(numberedKey(stepKey, i)); - deployMap.insert(stepListKey, stepListMap); + deployMap.insert(stepListKey, variantFromStore(stepListMap)); return deployMap; } -QVariantMap UserFileVersion16Upgrader::insertSteps(QVariantMap buildConfigurationMap, +Store UserFileVersion16Upgrader::insertSteps(Store buildConfigurationMap, const OldStepMaps &oldStepMap, NamePolicy policy) { - const QString bslCountKey = "ProjectExplorer.BuildConfiguration.BuildStepListCount"; + const Key bslCountKey = "ProjectExplorer.BuildConfiguration.BuildStepListCount"; int stepListCount = buildConfigurationMap.value(bslCountKey).toInt(); - const QString bslKey = "ProjectExplorer.BuildConfiguration.BuildStepList."; - const QString bslTypeKey = "ProjectExplorer.ProjectConfiguration.Id"; + const Key bslKey = "ProjectExplorer.BuildConfiguration.BuildStepList."; + const Key bslTypeKey = "ProjectExplorer.ProjectConfiguration.Id"; for (int bslNumber = 0; bslNumber < stepListCount; ++bslNumber) { - QVariantMap buildStepListMap = buildConfigurationMap.value(bslKey + QString::number(bslNumber)).toMap(); + Store buildStepListMap = buildConfigurationMap.value(numberedKey(bslKey, bslNumber)).value<Store>(); if (buildStepListMap.value(bslTypeKey) != "ProjectExplorer.BuildSteps.Build") continue; - const QString bslStepCountKey = "ProjectExplorer.BuildStepList.StepsCount"; + const Key bslStepCountKey = "ProjectExplorer.BuildStepList.StepsCount"; int stepCount = buildStepListMap.value(bslStepCountKey).toInt(); buildStepListMap.insert(bslStepCountKey, stepCount + 2); - QVariantMap androidPackageInstallStep; - QVariantMap androidBuildApkStep; + Store androidPackageInstallStep; + Store androidBuildApkStep; // common settings of all buildsteps - const QString enabledKey = "ProjectExplorer.BuildStep.Enabled"; - const QString idKey = "ProjectExplorer.ProjectConfiguration.Id"; - const QString displayNameKey = "ProjectExplorer.ProjectConfiguration.DisplayName"; - const QString defaultDisplayNameKey = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; + const Key enabledKey = "ProjectExplorer.BuildStep.Enabled"; + const Key idKey = "ProjectExplorer.ProjectConfiguration.Id"; + const Key displayNameKey = "ProjectExplorer.ProjectConfiguration.DisplayName"; + const Key defaultDisplayNameKey = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; QString displayName = oldStepMap.androidPackageInstall.value(displayNameKey).toString(); QString defaultDisplayName = oldStepMap.androidPackageInstall.value(defaultDisplayNameKey).toString(); bool enabled = oldStepMap.androidPackageInstall.value(enabledKey).toBool(); - androidPackageInstallStep.insert(idKey, Utils::Id("Qt4ProjectManager.AndroidPackageInstallationStep").toSetting()); + androidPackageInstallStep.insert(idKey, Id("Qt4ProjectManager.AndroidPackageInstallationStep").toSetting()); androidPackageInstallStep.insert(displayNameKey, displayName); androidPackageInstallStep.insert(defaultDisplayNameKey, defaultDisplayName); androidPackageInstallStep.insert(enabledKey, enabled); - displayName = oldStepMap.androidDeployQt.value(displayName).toString(); + displayName = oldStepMap.androidDeployQt.value(keyFromString(displayName)).toString(); defaultDisplayName = oldStepMap.androidDeployQt.value(defaultDisplayNameKey).toString(); enabled = oldStepMap.androidDeployQt.value(enabledKey).toBool(); - androidBuildApkStep.insert(idKey, Utils::Id("QmakeProjectManager.AndroidBuildApkStep").toSetting()); + androidBuildApkStep.insert(idKey, Id("QmakeProjectManager.AndroidBuildApkStep").toSetting()); androidBuildApkStep.insert(displayNameKey, displayName); androidBuildApkStep.insert(defaultDisplayNameKey, defaultDisplayName); androidBuildApkStep.insert(enabledKey, enabled); // settings transferred from AndroidDeployQtStep to QmakeBuildApkStep - const QString ProFilePathForInputFile = "ProFilePathForInputFile"; - const QString DeployActionKey = "Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction"; - const QString KeystoreLocationKey = "KeystoreLocation"; - const QString BuildTargetSdkKey = "BuildTargetSdk"; - const QString VerboseOutputKey = "VerboseOutput"; + const Key ProFilePathForInputFile = "ProFilePathForInputFile"; + const Key DeployActionKey = "Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction"; + const Key KeystoreLocationKey = "KeystoreLocation"; + const Key BuildTargetSdkKey = "BuildTargetSdk"; + const Key VerboseOutputKey = "VerboseOutput"; QString inputFile = oldStepMap.androidDeployQt.value(ProFilePathForInputFile).toString(); int oldDeployAction = oldStepMap.androidDeployQt.value(DeployActionKey).toInt(); @@ -599,16 +597,16 @@ QVariantMap UserFileVersion16Upgrader::insertSteps(QVariantMap buildConfiguratio androidBuildApkStep.insert(BuildTargetSdkKey, buildTargetSdk); androidBuildApkStep.insert(VerboseOutputKey, verbose); - const QString buildStepKey = "ProjectExplorer.BuildStepList.Step."; - buildStepListMap.insert(buildStepKey + QString::number(stepCount), androidPackageInstallStep); - buildStepListMap.insert(buildStepKey + QString::number(stepCount + 1), androidBuildApkStep); + const Key buildStepKey = "ProjectExplorer.BuildStepList.Step."; + buildStepListMap.insert(numberedKey(buildStepKey, stepCount), variantFromStore(androidPackageInstallStep)); + buildStepListMap.insert(numberedKey(buildStepKey, stepCount + 1), variantFromStore(androidBuildApkStep)); - buildConfigurationMap.insert(bslKey + QString::number(bslNumber), buildStepListMap); + buildConfigurationMap.insert(numberedKey(bslKey, bslNumber), variantFromStore(buildStepListMap)); } if (policy == RenameBuildConfiguration) { - const QString displayNameKey = "ProjectExplorer.ProjectConfiguration.DisplayName"; - const QString defaultDisplayNameKey = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; + const Key displayNameKey = "ProjectExplorer.ProjectConfiguration.DisplayName"; + const Key defaultDisplayNameKey = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; QString defaultDisplayName = buildConfigurationMap.value(defaultDisplayNameKey).toString(); QString displayName = buildConfigurationMap.value(displayNameKey).toString(); @@ -630,61 +628,61 @@ QVariantMap UserFileVersion16Upgrader::insertSteps(QVariantMap buildConfiguratio return buildConfigurationMap; } -QVariantMap UserFileVersion16Upgrader::upgrade(const QVariantMap &data) +Store UserFileVersion16Upgrader::upgrade(const Store &data) { int targetCount = data.value("ProjectExplorer.Project.TargetCount", 0).toInt(); if (!targetCount) return data; - QVariantMap result = data; + Store result = data; for (int i = 0; i < targetCount; ++i) { - QString targetKey = QLatin1String("ProjectExplorer.Project.Target.") + QString::number(i); - QVariantMap targetMap = data.value(targetKey).toMap(); + Key targetKey = numberedKey("ProjectExplorer.Project.Target.", i); + Store targetMap = storeFromVariant(data.value(targetKey)); - const QString dcCountKey = "ProjectExplorer.Target.DeployConfigurationCount"; + const Key dcCountKey = "ProjectExplorer.Target.DeployConfigurationCount"; int deployconfigurationCount = targetMap.value(dcCountKey).toInt(); if (!deployconfigurationCount) // should never happen continue; QList<OldStepMaps> oldSteps; - QList<QVariantMap> oldBuildConfigurations; + QList<Store> oldBuildConfigurations; - QString deployKey = "ProjectExplorer.Target.DeployConfiguration."; + Key deployKey = "ProjectExplorer.Target.DeployConfiguration."; for (int j = 0; j < deployconfigurationCount; ++j) { - QVariantMap deployConfigurationMap - = targetMap.value(deployKey + QString::number(j)).toMap(); + Store deployConfigurationMap + = storeFromVariant(targetMap.value(numberedKey(deployKey, j))); OldStepMaps oldStep = extractStepMaps(deployConfigurationMap); if (!oldStep.isEmpty()) { oldSteps.append(oldStep); deployConfigurationMap = removeAndroidPackageStep(deployConfigurationMap); - targetMap.insert(deployKey + QString::number(j), deployConfigurationMap); + targetMap.insert(numberedKey(deployKey, j), QVariant::fromValue(deployConfigurationMap)); } } if (oldSteps.isEmpty()) // no android target? continue; - const QString bcCountKey = "ProjectExplorer.Target.BuildConfigurationCount"; + const Key bcCountKey = "ProjectExplorer.Target.BuildConfigurationCount"; int buildConfigurationCount = targetMap.value(bcCountKey).toInt(); if (!buildConfigurationCount) // should never happen continue; - QString bcKey = "ProjectExplorer.Target.BuildConfiguration."; + Key bcKey = "ProjectExplorer.Target.BuildConfiguration."; for (int j = 0; j < buildConfigurationCount; ++j) { - QVariantMap oldBuildConfigurationMap = targetMap.value(bcKey + QString::number(j)).toMap(); + Store oldBuildConfigurationMap = storeFromVariant(targetMap.value(numberedKey(bcKey, j))); oldBuildConfigurations.append(oldBuildConfigurationMap); } - QList<QVariantMap> newBuildConfigurations; + QList<Store> newBuildConfigurations; NamePolicy policy = oldSteps.size() > 1 ? RenameBuildConfiguration : KeepName; - for (const QVariantMap &oldBuildConfiguration : std::as_const(oldBuildConfigurations)) { + for (const Store &oldBuildConfiguration : std::as_const(oldBuildConfigurations)) { for (const OldStepMaps &oldStep : std::as_const(oldSteps)) { - QVariantMap newBuildConfiguration = insertSteps(oldBuildConfiguration, oldStep, policy); + Store newBuildConfiguration = insertSteps(oldBuildConfiguration, oldStep, policy); if (!newBuildConfiguration.isEmpty()) newBuildConfigurations.append(newBuildConfiguration); } @@ -693,19 +691,19 @@ QVariantMap UserFileVersion16Upgrader::upgrade(const QVariantMap &data) targetMap.insert(bcCountKey, newBuildConfigurations.size()); for (int j = 0; j < newBuildConfigurations.size(); ++j) - targetMap.insert(bcKey + QString::number(j), newBuildConfigurations.at(j)); - result.insert(targetKey, targetMap); + targetMap.insert(numberedKey(bcKey, j), variantFromStore(newBuildConfigurations.at(j))); + result.insert(targetKey, variantFromStore(targetMap)); } return result; } -QVariantMap UserFileVersion17Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion17Upgrader::upgrade(const Store &map) { m_sticky = map.value(USER_STICKY_KEYS_KEY).toList(); if (m_sticky.isEmpty()) return map; - return process(map).toMap(); + return storeFromVariant(process(variantFromStore(map))); } QVariant UserFileVersion17Upgrader::process(const QVariant &entry) @@ -718,22 +716,22 @@ QVariant UserFileVersion17Upgrader::process(const QVariant &entry) return result; } case QVariant::Map: { - QVariantMap result = entry.toMap(); - for (QVariantMap::iterator i = result.begin(), end = result.end(); i != end; ++i) { + Store result = storeFromVariant(entry); + for (Store::iterator i = result.begin(), end = result.end(); i != end; ++i) { QVariant &v = i.value(); v = process(v); } result.insert(USER_STICKY_KEYS_KEY, m_sticky); - return result; + return variantFromStore(result); } default: return entry; } } -QVariantMap UserFileVersion18Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion18Upgrader::upgrade(const Store &map) { - return process(map).toMap(); + return storeFromVariant(process(variantFromStore(map))); } QVariant UserFileVersion18Upgrader::process(const QVariant &entry) @@ -741,88 +739,98 @@ QVariant UserFileVersion18Upgrader::process(const QVariant &entry) switch (entry.typeId()) { case QVariant::List: return Utils::transform(entry.toList(), &UserFileVersion18Upgrader::process); - case QVariant::Map: - return Utils::transform<QMap<QString, QVariant>>( - entry.toMap().toStdMap(), [](const StringVariantPair &item) -> StringVariantPair { - const QString key = (item.first - == "AutotoolsProjectManager.MakeStep.AdditionalArguments" - ? QString("AutotoolsProjectManager.MakeStep.MakeArguments") - : item.first); - return {key, UserFileVersion18Upgrader::process(item.second)}; - }); + case QVariant::Map: { + Store map = storeFromVariant(entry); + Store result; + for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) { + Key key = it.key() == "AutotoolsProjectManager.MakeStep.AdditionalArguments" + ? Key("AutotoolsProjectManager.MakeStep.MakeArguments") + : it.key(); + result.insert(key, UserFileVersion18Upgrader::process(it.value())); + } + return variantFromStore(result); + } default: return entry; } } -QVariantMap UserFileVersion19Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion19Upgrader::upgrade(const Store &map) { - return process(map, QStringList()).toMap(); + return storeFromVariant(process(variantFromStore(map), KeyList())); } -QVariant UserFileVersion19Upgrader::process(const QVariant &entry, const QStringList &path) +QVariant UserFileVersion19Upgrader::process(const QVariant &entry, const KeyList &path) { - static const QStringList argsKeys = {"Qt4ProjectManager.MaemoRunConfiguration.Arguments", - "CMakeProjectManager.CMakeRunConfiguration.Arguments", - "Ios.run_arguments", - "Nim.NimRunConfiguration.ArgumentAspect", - "ProjectExplorer.CustomExecutableRunConfiguration.Arguments", - "PythonEditor.RunConfiguration.Arguments", - "Qbs.RunConfiguration.CommandLineArguments", - "Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments", - "RemoteLinux.CustomRunConfig.Arguments", - "WinRtRunConfigurationArgumentsId", - "CommandLineArgs"}; - static const QStringList wdKeys = {"BareMetal.RunConfig.WorkingDirectory", - "CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory", - "Nim.NimRunConfiguration.WorkingDirectoryAspect", - "ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory", - "Qbs.RunConfiguration.WorkingDirectory", - "Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory", - "RemoteLinux.CustomRunConfig.WorkingDirectory", - "RemoteLinux.RunConfig.WorkingDirectory", - "WorkingDir"}; - static const QStringList termKeys = {"CMakeProjectManager.CMakeRunConfiguration.UseTerminal", - "Nim.NimRunConfiguration.TerminalAspect", - "ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal", - "PythonEditor.RunConfiguration.UseTerminal", - "Qbs.RunConfiguration.UseTerminal", - "Qt4ProjectManager.Qt4RunConfiguration.UseTerminal"}; - static const QStringList libsKeys = {"Qbs.RunConfiguration.UsingLibraryPaths", - "QmakeProjectManager.QmakeRunConfiguration.UseLibrarySearchPath"}; - static const QStringList dyldKeys = {"Qbs.RunConfiguration.UseDyldImageSuffix", - "QmakeProjectManager.QmakeRunConfiguration.UseDyldImageSuffix"}; + static const KeyList argsKeys = {"Qt4ProjectManager.MaemoRunConfiguration.Arguments", + "CMakeProjectManager.CMakeRunConfiguration.Arguments", + "Ios.run_arguments", + "Nim.NimRunConfiguration.ArgumentAspect", + "ProjectExplorer.CustomExecutableRunConfiguration.Arguments", + "PythonEditor.RunConfiguration.Arguments", + "Qbs.RunConfiguration.CommandLineArguments", + "Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments", + "RemoteLinux.CustomRunConfig.Arguments", + "WinRtRunConfigurationArgumentsId", + "CommandLineArgs"}; + static const KeyList wdKeys = {"BareMetal.RunConfig.WorkingDirectory", + "CMakeProjectManager.CMakeRunConfiguration.UserWorkingDirectory", + "Nim.NimRunConfiguration.WorkingDirectoryAspect", + "ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory", + "Qbs.RunConfiguration.WorkingDirectory", + "Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory", + "RemoteLinux.CustomRunConfig.WorkingDirectory", + "RemoteLinux.RunConfig.WorkingDirectory", + "WorkingDir"}; + static const KeyList termKeys = {"CMakeProjectManager.CMakeRunConfiguration.UseTerminal", + "Nim.NimRunConfiguration.TerminalAspect", + "ProjectExplorer.CustomExecutableRunConfiguration.UseTerminal", + "PythonEditor.RunConfiguration.UseTerminal", + "Qbs.RunConfiguration.UseTerminal", + "Qt4ProjectManager.Qt4RunConfiguration.UseTerminal"}; + static const KeyList libsKeys = {"Qbs.RunConfiguration.UsingLibraryPaths", + "QmakeProjectManager.QmakeRunConfiguration.UseLibrarySearchPath"}; + static const KeyList dyldKeys = {"Qbs.RunConfiguration.UseDyldImageSuffix", + "QmakeProjectManager.QmakeRunConfiguration.UseDyldImageSuffix"}; switch (entry.typeId()) { case QVariant::List: return Utils::transform(entry.toList(), std::bind(&UserFileVersion19Upgrader::process, std::placeholders::_1, path)); - case QVariant::Map: - return Utils::transform<QVariantMap>(entry.toMap().toStdMap(), - [&](const StringVariantPair &item) -> StringVariantPair { - if (path.size() == 2 && path.at(1).startsWith("ProjectExplorer.Target.RunConfiguration.")) { - if (argsKeys.contains(item.first)) - return {"RunConfiguration.Arguments", item.second}; - if (wdKeys.contains(item.first)) - return {"RunConfiguration.WorkingDirectory", item.second}; - if (termKeys.contains(item.first)) - return {"RunConfiguration.UseTerminal", item.second}; - if (libsKeys.contains(item.first)) - return {"RunConfiguration.UseLibrarySearchPath", item.second}; - if (dyldKeys.contains(item.first)) - return {"RunConfiguration.UseDyldImageSuffix", item.second}; + case QVariant::Map: { + Store map = storeFromVariant(entry); + Store result; + for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) { + Key key = it.key(); + QVariant value = it.value(); + if (path.size() == 2 + && path.at(1).view().startsWith("ProjectExplorer.Target.RunConfiguration.")) { + if (argsKeys.contains(key)) + key = "RunConfiguration.Arguments"; + else if (wdKeys.contains(key)) + key = "RunConfiguration.WorkingDirectory"; + else if (termKeys.contains(key)) + key = "RunConfiguration.UseTerminal"; + else if (libsKeys.contains(key)) + key = "RunConfiguration.UseLibrarySearchPath"; + else if (dyldKeys.contains(key)) + key = "RunConfiguration.UseDyldImageSuffix"; + else + value = UserFileVersion19Upgrader::process(value, path + KeyList{key}); + } else { + value = UserFileVersion19Upgrader::process(value, path + KeyList{key}); } - QStringList newPath = path; - newPath.append(item.first); - return {item.first, UserFileVersion19Upgrader::process(item.second, newPath)}; - }); + result.insert(key, value); + }; + return variantFromStore(result); + } default: return entry; } } -QVariantMap UserFileVersion20Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion20Upgrader::upgrade(const Store &map) { - return process(map).toMap(); + return storeFromVariant(process(variantFromStore(map))); } QVariant UserFileVersion20Upgrader::process(const QVariant &entry) @@ -830,25 +838,28 @@ QVariant UserFileVersion20Upgrader::process(const QVariant &entry) switch (entry.typeId()) { case QVariant::List: return Utils::transform(entry.toList(), &UserFileVersion20Upgrader::process); - case QVariant::Map: - return Utils::transform<QMap<QString, QVariant>>( - entry.toMap().toStdMap(), [](const StringVariantPair &item) { - StringVariantPair res = {item.first, item.second}; - if (item.first == "ProjectExplorer.ProjectConfiguration.Id" - && item.second == "Qbs.Deploy") - res.second = QVariant("ProjectExplorer.DefaultDeployConfiguration"); - else - res.second = UserFileVersion20Upgrader::process(item.second); - return res; - }); + case QVariant::Map: { + Store map = storeFromVariant(entry); + Store result; + for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) { + Key key = it.key(); + QVariant value = it.value(); + if (key == "ProjectExplorer.ProjectConfiguration.Id" && value == "Qbs.Deploy") + value = "ProjectExplorer.DefaultDeployConfiguration"; + else + value = UserFileVersion20Upgrader::process(value); + result.insert(key, value); + } + return variantFromStore(result); + } default: return entry; } } -QVariantMap UserFileVersion21Upgrader::upgrade(const QVariantMap &map) +Store UserFileVersion21Upgrader::upgrade(const Store &map) { - return process(map).toMap(); + return storeFromVariant(process(variantFromStore(map))); } QVariant UserFileVersion21Upgrader::process(const QVariant &entry) @@ -857,16 +868,17 @@ QVariant UserFileVersion21Upgrader::process(const QVariant &entry) case QVariant::List: return Utils::transform(entry.toList(), &UserFileVersion21Upgrader::process); case QVariant::Map: { - QVariantMap entryMap = entry.toMap(); + Store entryMap = storeFromVariant(entry); if (entryMap.value("ProjectExplorer.ProjectConfiguration.Id").toString() == "DeployToGenericLinux") { entryMap.insert("_checkMakeInstall", true); - return entryMap; + return variantFromStore(entryMap); } - return Utils::transform<QVariantMap>( - entryMap.toStdMap(), [](const StringVariantPair &item) -> StringVariantPair{ - return {item.first, UserFileVersion21Upgrader::process(item.second)}; - }); + Store map = storeFromVariant(entry); + Store result; + for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) + result.insert(it.key(), UserFileVersion21Upgrader::process(it.value())); + return variantFromStore(result); } default: return entry; @@ -886,8 +898,8 @@ class TestUserFileAccessor : public UserFileAccessor public: TestUserFileAccessor(Project *project) : UserFileAccessor(project) { } - void storeSharedSettings(const QVariantMap &data) const { m_storedSettings = data; } - QVariant retrieveSharedSettings() const override { return m_storedSettings; } + void storeSharedSettings(const Store &data) const { m_storedSettings = data; } + QVariant retrieveSharedSettings() const override { return variantFromStore(m_storedSettings); } using UserFileAccessor::preprocessReadSettings; using UserFileAccessor::prepareToWriteSettings; @@ -895,7 +907,7 @@ public: using UserFileAccessor::mergeSettings; private: - mutable QVariantMap m_storedSettings; + mutable Store m_storedSettings; }; @@ -914,11 +926,11 @@ void ProjectExplorerPlugin::testUserFileAccessor_prepareToReadSettings() TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap data; + Store data; data.insert("Version", 4); data.insert("Foo", "bar"); - QVariantMap result = accessor.preprocessReadSettings(data); + Store result = accessor.preprocessReadSettings(data); QCOMPARE(result, data); } @@ -928,11 +940,11 @@ void ProjectExplorerPlugin::testUserFileAccessor_prepareToReadSettingsObsoleteVe TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap data; + Store data; data.insert("ProjectExplorer.Project.Updater.FileVersion", 4); data.insert("Foo", "bar"); - QVariantMap result = accessor.preprocessReadSettings(data); + Store result = accessor.preprocessReadSettings(data); QCOMPARE(result.count(), data.count()); QCOMPARE(result.value("Foo"), data.value("Foo")); @@ -944,12 +956,12 @@ void ProjectExplorerPlugin::testUserFileAccessor_prepareToReadSettingsObsoleteVe TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap data; + Store data; data.insert("ProjectExplorer.Project.Updater.FileVersion", 4); data.insert("Version", 5); data.insert("Foo", "bar"); - QVariantMap result = accessor.preprocessReadSettings(data); + Store result = accessor.preprocessReadSettings(data); QCOMPARE(result.count(), data.count() - 1); QCOMPARE(result.value("Foo"), data.value("Foo")); @@ -961,7 +973,7 @@ void ProjectExplorerPlugin::testUserFileAccessor_prepareToWriteSettings() TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap sharedData; + Store sharedData; sharedData.insert("Version", 10); sharedData.insert("shared1", "bar"); sharedData.insert("shared2", "baz"); @@ -969,12 +981,12 @@ void ProjectExplorerPlugin::testUserFileAccessor_prepareToWriteSettings() accessor.storeSharedSettings(sharedData); - QVariantMap data; + Store data; data.insert("Version", 10); data.insert("shared1", "bar1"); data.insert("unique1", 1234); data.insert("shared3", "foo"); - QVariantMap result = accessor.prepareToWriteSettings(data); + Store result = accessor.prepareToWriteSettings(data); QCOMPARE(result.count(), data.count() + 3); QCOMPARE(result.value("EnvironmentId").toByteArray(), @@ -992,14 +1004,14 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettings() TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap sharedData; + Store sharedData; sharedData.insert("Version", accessor.currentVersion()); sharedData.insert("shared1", "bar"); sharedData.insert("shared2", "baz"); sharedData.insert("shared3", "foooo"); TestUserFileAccessor::RestoreData shared("/shared/data", sharedData); - QVariantMap data; + Store data; data.insert("Version", accessor.currentVersion()); data.insert("EnvironmentId", projectExplorerSettings().environmentId.toByteArray()); data.insert("UserStickyKeys", QStringList({"shared1"})); @@ -1027,14 +1039,14 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettingsEmptyUser() TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap sharedData; + Store sharedData; sharedData.insert("Version", accessor.currentVersion()); sharedData.insert("shared1", "bar"); sharedData.insert("shared2", "baz"); sharedData.insert("shared3", "foooo"); TestUserFileAccessor::RestoreData shared("/shared/data", sharedData); - QVariantMap data; + Store data; TestUserFileAccessor::RestoreData user("/shared/data", data); TestUserFileAccessor::RestoreData result = accessor.mergeSettings(user, shared); @@ -1048,10 +1060,10 @@ void ProjectExplorerPlugin::testUserFileAccessor_mergeSettingsEmptyShared() TestProject project; TestUserFileAccessor accessor(&project); - QVariantMap sharedData; + Store sharedData; TestUserFileAccessor::RestoreData shared("/shared/data", sharedData); - QVariantMap data; + Store data; data.insert("Version", accessor.currentVersion()); data.insert("OriginalVersion", accessor.currentVersion()); data.insert("EnvironmentId", projectExplorerSettings().environmentId.toByteArray()); diff --git a/src/plugins/projectexplorer/userfileaccessor.h b/src/plugins/projectexplorer/userfileaccessor.h index 778e42855ad..4e48a223789 100644 --- a/src/plugins/projectexplorer/userfileaccessor.h +++ b/src/plugins/projectexplorer/userfileaccessor.h @@ -3,11 +3,11 @@ #pragma once -#include <utils/fileutils.h> #include <utils/settingsaccessor.h> #include <QHash> #include <QVariantMap> +#include <QMessageBox> namespace ProjectExplorer { @@ -29,17 +29,17 @@ public: Utils::FilePath sharedFile() const; protected: - QVariantMap postprocessMerge(const QVariantMap &main, - const QVariantMap &secondary, - const QVariantMap &result) const final; + Utils::Store postprocessMerge(const Utils::Store &main, + const Utils::Store &secondary, + const Utils::Store &result) const final; - QVariantMap preprocessReadSettings(const QVariantMap &data) const final; - QVariantMap prepareToWriteSettings(const QVariantMap &data) const final; + Utils::Store preprocessReadSettings(const Utils::Store &data) const final; + Utils::Store prepareToWriteSettings(const Utils::Store &data) const final; Utils::SettingsMergeResult merge(const SettingsMergeData &global, const SettingsMergeData &local) const final; private: - Utils::SettingsMergeFunction userStickyTrackerFunction(QStringList &stickyKeys) const; + Utils::SettingsMergeFunction userStickyTrackerFunction(Utils::KeyList &stickyKeys) const; Project *m_project; }; diff --git a/src/plugins/python/CMakeLists.txt b/src/plugins/python/CMakeLists.txt index 0e4ca944194..25275cebdfc 100644 --- a/src/plugins/python/CMakeLists.txt +++ b/src/plugins/python/CMakeLists.txt @@ -1,6 +1,6 @@ add_qtc_plugin(Python DEPENDS QmlJS - PLUGIN_DEPENDS Core LanguageClient ProjectExplorer TextEditor + PLUGIN_DEPENDS Core LanguageClient ProjectExplorer TextEditor QtSupport SOURCES pipsupport.cpp pipsupport.h pyside.cpp pyside.h diff --git a/src/plugins/python/Python.json.in b/src/plugins/python/Python.json.in index 98fd52ec8d5..5dfb4c6d49e 100644 --- a/src/plugins/python/Python.json.in +++ b/src/plugins/python/Python.json.in @@ -1,45 +1,45 @@ { - \"Name\" : \"Python\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Python", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Other Languages\", - \"Description\" : \"Plugin for supporting the Python language.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Other Languages", + "Description" : "Plugin for supporting the Python language.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-python-gui\'>\", - \" <sub-class-of type=\'text/x-python\'/>\", - \" <comment>Python source file without console</comment>\", - \" <glob pattern=\'*.pyw\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/x-python-interface\'>\", - \" <sub-class-of type=\'text/x-python\'/>\", - \" <comment>Python module interface file</comment>\", - \" <glob pattern=\'*.pyi\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/x-pyqt-project\'>\", - \" <sub-class-of type=\'text/x-python\'/>\", - \" <comment>Qt Creator Python project file</comment>\", - \" <glob pattern=\'*.pyqtc\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/x-python-project\'>\", - \" <sub-class-of type=\'application/json\'/>\", - \" <comment>Qt Creator Python project file</comment>\", - \" <glob pattern=\'*.pyproject\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-python-gui'>", + " <sub-class-of type='text/x-python'/>", + " <comment>Python source file without console</comment>", + " <glob pattern='*.pyw'/>", + " </mime-type>", + " <mime-type type='text/x-python-interface'>", + " <sub-class-of type='text/x-python'/>", + " <comment>Python module interface file</comment>", + " <glob pattern='*.pyi'/>", + " </mime-type>", + " <mime-type type='text/x-pyqt-project'>", + " <sub-class-of type='text/x-python'/>", + " <comment>Qt Creator Python project file</comment>", + " <glob pattern='*.pyqtc'/>", + " </mime-type>", + " <mime-type type='text/x-python-project'>", + " <sub-class-of type='application/json'/>", + " <comment>Qt Creator Python project file</comment>", + " <glob pattern='*.pyproject'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp index d6070663fb6..a4d29a56303 100644 --- a/src/plugins/python/pipsupport.cpp +++ b/src/plugins/python/pipsupport.cpp @@ -35,6 +35,16 @@ PipInstallTask::PipInstallTask(const FilePath &python) m_watcher.setFuture(m_future.future()); } +void PipInstallTask::setRequirements(const Utils::FilePath &requirementFile) +{ + m_requirementsFile = requirementFile; +} + +void PipInstallTask::setWorkingDirectory(const Utils::FilePath &workingDirectory) +{ + m_process.setWorkingDirectory(workingDirectory); +} + void PipInstallTask::addPackage(const PipPackage &package) { m_packages << package; @@ -47,18 +57,22 @@ void PipInstallTask::setPackages(const QList<PipPackage> &packages) void PipInstallTask::run() { - if (m_packages.isEmpty()) { + if (m_packages.isEmpty() && m_requirementsFile.isEmpty()) { emit finished(false); return; } const QString taskTitle = Tr::tr("Install Python Packages"); Core::ProgressManager::addTask(m_future.future(), taskTitle, pipInstallTaskId); QStringList arguments = {"-m", "pip", "install"}; - for (const PipPackage &package : m_packages) { - QString pipPackage = package.packageName; - if (!package.version.isEmpty()) - pipPackage += "==" + package.version; - arguments << pipPackage; + if (!m_requirementsFile.isEmpty()) + arguments << "-r" << m_requirementsFile.toString(); + else { + for (const PipPackage &package : m_packages) { + QString pipPackage = package.packageName; + if (!package.version.isEmpty()) + pipPackage += "==" + package.version; + arguments << pipPackage; + } } // add --user to global pythons, but skip it for venv pythons @@ -66,9 +80,10 @@ void PipInstallTask::run() arguments << "--user"; m_process.setCommand({m_python, arguments}); + m_process.setTerminalMode(TerminalMode::Run); m_process.start(); - Core::MessageManager::writeDisrupting( + Core::MessageManager::writeSilently( Tr::tr("Running \"%1\" to install %2.") .arg(m_process.commandLine().toUserOutput(), packagesDisplayName())); @@ -115,7 +130,9 @@ void PipInstallTask::handleError() QString PipInstallTask::packagesDisplayName() const { - return Utils::transform(m_packages, &PipPackage::displayName).join(", "); + return m_requirementsFile.isEmpty() + ? Utils::transform(m_packages, &PipPackage::displayName).join(", ") + : m_requirementsFile.toUserOutput(); } void PipPackageInfo::parseField(const QString &field, const QStringList &data) diff --git a/src/plugins/python/pipsupport.h b/src/plugins/python/pipsupport.h index 4d08a3b1ea6..586a2f55e70 100644 --- a/src/plugins/python/pipsupport.h +++ b/src/plugins/python/pipsupport.h @@ -63,6 +63,8 @@ class PipInstallTask : public QObject Q_OBJECT public: explicit PipInstallTask(const Utils::FilePath &python); + void setRequirements(const Utils::FilePath &requirementFile); + void setWorkingDirectory(const Utils::FilePath &workingDirectory); void addPackage(const PipPackage &package); void setPackages(const QList<PipPackage> &packages); void run(); @@ -80,6 +82,7 @@ private: const Utils::FilePath m_python; QList<PipPackage> m_packages; + Utils::FilePath m_requirementsFile; Utils::Process m_process; QFutureInterface<void> m_future; QFutureWatcher<void> m_watcher; diff --git a/src/plugins/python/pyside.cpp b/src/plugins/python/pyside.cpp index 4b9df185a37..a17ae89dca8 100644 --- a/src/plugins/python/pyside.cpp +++ b/src/plugins/python/pyside.cpp @@ -13,6 +13,8 @@ #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> +#include <qtsupport/qtoptionspage.h> + #include <texteditor/textdocument.h> #include <utils/algorithm.h> @@ -21,6 +23,9 @@ #include <utils/process.h> #include <utils/qtcassert.h> +#include <QBoxLayout> +#include <QComboBox> +#include <QDialogButtonBox> #include <QRegularExpression> #include <QTextCursor> @@ -79,7 +84,38 @@ void PySideInstaller::installPyside(const FilePath &python, const QString &pySide, TextEditor::TextDocument *document) { - document->infoBar()->removeInfo(installPySideInfoBarId); + QMap<QVersionNumber, Utils::FilePath> availablePySides; + + const Utils::QtcSettings *settings = Core::ICore::settings(QSettings::SystemScope); + + const FilePaths requirementsList + = Utils::transform(settings->value("Python/PySideWheelsRequirements").toList(), + &FilePath::fromSettings); + for (const FilePath &requirements : requirementsList) { + if (requirements.exists()) { + auto version = QVersionNumber::fromString(requirements.parentDir().fileName()); + availablePySides[version] = requirements; + } + } + + if (requirementsList.isEmpty()) { // fallback remove in Qt Creator 13 + const QString hostQtTail = HostOsInfo::isMacHost() + ? QString("Tools/sdktool") + : QString("Tools/sdktool/share/qtcreator"); + + const std::optional<FilePath> qtInstallDir + = QtSupport::LinkWithQtSupport::linkedQt().tailRemoved(hostQtTail); + if (qtInstallDir) { + const FilePath qtForPythonDir = qtInstallDir->pathAppended("QtForPython"); + for (const FilePath &versionDir : + qtForPythonDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) { + FilePath requirements = versionDir.pathAppended("requirements.txt"); + if (!requirementsList.contains(requirements) && requirements.exists()) + availablePySides[QVersionNumber::fromString(versionDir.fileName())] + = requirements; + } + } + } auto install = new PipInstallTask(python); connect(install, &PipInstallTask::finished, install, &QObject::deleteLater); @@ -87,7 +123,40 @@ void PySideInstaller::installPyside(const FilePath &python, if (success) emit pySideInstalled(python, pySide); }); - install->setPackages({PipPackage(pySide)}); + if (availablePySides.isEmpty()) { + install->setPackages({PipPackage(pySide)}); + } else { + QDialog dialog; + dialog.setWindowTitle(Tr::tr("Select PySide Version")); + dialog.setLayout(new QVBoxLayout()); + dialog.layout()->addWidget(new QLabel(Tr::tr("Select which PySide version to install:"))); + QComboBox *pySideSelector = new QComboBox(); + pySideSelector->addItem(Tr::tr("Latest PySide from the Python Package Index")); + for (const Utils::FilePath &version : std::as_const(availablePySides)) { + const FilePath dir = version.parentDir(); + const QString text + = Tr::tr("PySide %1 Wheel (%2)").arg(dir.fileName(), dir.toUserOutput()); + pySideSelector->addItem(text, version.toVariant()); + } + dialog.layout()->addWidget(pySideSelector); + QDialogButtonBox box; + box.setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.layout()->addWidget(&box); + connect(&box, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); + connect(&box, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); + + if (dialog.exec() == QDialog::Rejected) + return; + + const FilePath requirementsFile = FilePath::fromVariant(pySideSelector->currentData()); + if (requirementsFile.isEmpty()) { + install->setPackages({PipPackage(pySide)}); + } else { + install->setWorkingDirectory(requirementsFile.parentDir()); + install->setRequirements(requirementsFile); + } + } + document->infoBar()->removeInfo(installPySideInfoBarId); install->run(); } diff --git a/src/plugins/python/pysidebuildconfiguration.cpp b/src/plugins/python/pysidebuildconfiguration.cpp index 5f6f7dadea5..8525e248c4f 100644 --- a/src/plugins/python/pysidebuildconfiguration.cpp +++ b/src/plugins/python/pysidebuildconfiguration.cpp @@ -15,6 +15,7 @@ #include <projectexplorer/target.h> #include <utils/commandline.h> +#include <utils/process.h> using namespace ProjectExplorer; using namespace Utils; @@ -42,7 +43,7 @@ PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id) const FilePath pySideProjectPath = FilePath("pyside6-project").searchInPath(); if (pySideProjectPath.isExecutableFile()) - m_pysideProject.setFilePath(pySideProjectPath); + m_pysideProject.setValue(pySideProjectPath); setCommandLineProvider([this] { return CommandLine(m_pysideProject(), {"build"}); }); setWorkingDirectoryProvider([this] { @@ -55,17 +56,21 @@ PySideBuildStep::PySideBuildStep(BuildStepList *bsl, Id id) void PySideBuildStep::updatePySideProjectPath(const FilePath &pySideProjectPath) { - m_pysideProject.setFilePath(pySideProjectPath); + m_pysideProject.setValue(pySideProjectPath); } -void PySideBuildStep::doRun() +Tasking::GroupItem PySideBuildStep::runRecipe() { - if (processParameters()->effectiveCommand().isExecutableFile()) - AbstractProcessStep::doRun(); - else - emit finished(true); -} + using namespace Tasking; + const auto onSetup = [this] { + if (!processParameters()->effectiveCommand().isExecutableFile()) + return SetupResult::StopWithDone; + return SetupResult::Continue; + }; + + return Group { onGroupSetup(onSetup), defaultProcessTask() }; +} // PySideBuildConfiguration diff --git a/src/plugins/python/pysidebuildconfiguration.h b/src/plugins/python/pysidebuildconfiguration.h index e686b9c2220..58ae930ea87 100644 --- a/src/plugins/python/pysidebuildconfiguration.h +++ b/src/plugins/python/pysidebuildconfiguration.h @@ -17,7 +17,7 @@ public: void updatePySideProjectPath(const Utils::FilePath &pySideProjectPath); private: - void doRun() override; + Tasking::GroupItem runRecipe() final; Utils::FilePathAspect m_pysideProject{this}; }; diff --git a/src/plugins/python/python.qbs b/src/plugins/python/python.qbs index 5186dafcdcd..dd7aba1b697 100644 --- a/src/plugins/python/python.qbs +++ b/src/plugins/python/python.qbs @@ -9,10 +9,11 @@ QtcPlugin { Depends { name: "Utils" } Depends { name: "Core" } - Depends { name: "TextEditor" } - Depends { name: "ProjectExplorer" } Depends { name: "LanguageClient" } Depends { name: "LanguageServerProtocol" } + Depends { name: "ProjectExplorer" } + Depends { name: "QtSupport" } + Depends { name: "TextEditor" } Group { name: "General" diff --git a/src/plugins/python/pythonconstants.h b/src/plugins/python/pythonconstants.h index d5c5e3c6cd8..315aca8da66 100644 --- a/src/plugins/python/pythonconstants.h +++ b/src/plugins/python/pythonconstants.h @@ -27,6 +27,7 @@ const char PYLS_SETTINGS_ID[] = "Python.PyLSSettingsID"; * MIME type ******************************************************************************/ const char C_PY_MIMETYPE[] = "text/x-python"; +const char C_PY_GUI_MIMETYPE[] = "text/x-python-gui"; const char C_PY3_MIMETYPE[] = "text/x-python3"; const char C_PY_MIME_ICON[] = "text-x-python"; diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index d23685bc3b7..6a6a377ba0a 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -268,7 +268,7 @@ void PythonEditorWidget::updateInterpretersSelector() PythonEditorFactory::PythonEditorFactory() { - registerReplAction(this); + registerReplAction(&m_guard); setId(Constants::C_PYTHONEDITOR_ID); setDisplayName(::Core::Tr::tr(Constants::C_EDITOR_DISPLAY_NAME)); diff --git a/src/plugins/python/pythoneditor.h b/src/plugins/python/pythoneditor.h index b713e3dbce3..e2eef32b88d 100644 --- a/src/plugins/python/pythoneditor.h +++ b/src/plugins/python/pythoneditor.h @@ -11,6 +11,8 @@ class PythonEditorFactory : public TextEditor::TextEditorFactory { public: PythonEditorFactory(); +private: + QObject m_guard; }; } // Python::Internal diff --git a/src/plugins/python/pythonlanguageclient.cpp b/src/plugins/python/pythonlanguageclient.cpp index eecfb35c97f..b13aef42077 100644 --- a/src/plugins/python/pythonlanguageclient.cpp +++ b/src/plugins/python/pythonlanguageclient.cpp @@ -207,8 +207,12 @@ void PyLSClient::openDocument(TextEditor::TextDocument *document) const FilePath documentPath = document->filePath(); if (PythonProject *project = pythonProjectForFile(documentPath)) { if (Target *target = project->activeTarget()) { - if (auto rc = qobject_cast<PythonRunConfiguration *>(target->activeRunConfiguration())) - updateExtraCompilers(project, rc->extraCompilers()); + if (RunConfiguration *rc = target->activeRunConfiguration()) + if (auto aspect = rc->aspect<InterpreterAspect>()) { + updateExtraCompilers(project, + static_cast<PythonInterpreterAspect *>(aspect) + ->extraCompilers()); + } } } else if (isSupportedDocument(document)) { const FilePath workspacePath = documentPath.parentDir(); @@ -297,10 +301,13 @@ void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python, auto install = new PipInstallTask(python); connect(install, &PipInstallTask::finished, this, [=](const bool success) { + const QList<TextEditor::TextDocument *> additionalDocuments = m_infoBarEntries.take(python); if (success) { - if (document) { - if (PyLSClient *client = clientForPython(python)) + if (PyLSClient *client = clientForPython(python)) { + if (document) LanguageClientManager::openDocumentWithClient(document, client); + for (TextEditor::TextDocument *additionalDocument : additionalDocuments) + LanguageClientManager::openDocumentWithClient(additionalDocument, client); } } install->deleteLater(); @@ -371,15 +378,6 @@ void PyLSConfigureAssistant::handlePyLSState(const FilePath &python, } } -void PyLSConfigureAssistant::updateEditorInfoBars(const FilePath &python, Client *client) -{ - for (TextEditor::TextDocument *document : instance()->m_infoBarEntries.take(python)) { - instance()->resetEditorInfoBar(document); - if (client) - LanguageClientManager::openDocumentWithClient(document, client); - } -} - void PyLSConfigureAssistant::resetEditorInfoBar(TextEditor::TextDocument *document) { for (QList<TextEditor::TextDocument *> &documents : m_infoBarEntries) diff --git a/src/plugins/python/pythonlanguageclient.h b/src/plugins/python/pythonlanguageclient.h index 82a4a73ea4f..41b559a0427 100644 --- a/src/plugins/python/pythonlanguageclient.h +++ b/src/plugins/python/pythonlanguageclient.h @@ -53,8 +53,6 @@ class PyLSConfigureAssistant : public QObject public: static PyLSConfigureAssistant *instance(); - static void updateEditorInfoBars(const Utils::FilePath &python, - LanguageClient::Client *client); static void openDocumentWithPython(const Utils::FilePath &python, TextEditor::TextDocument *document); diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp index 3cbbe817f9d..a7e8cc3c747 100644 --- a/src/plugins/python/pythonplugin.cpp +++ b/src/plugins/python/pythonplugin.cpp @@ -8,6 +8,7 @@ #include "pythonproject.h" #include "pythonrunconfiguration.h" #include "pythonsettings.h" +#include "pythontr.h" #include "pythonwizardpage.h" #include <projectexplorer/buildtargetinfo.h> @@ -36,6 +37,7 @@ public: PySideBuildConfigurationFactory buildConfigFactory; SimpleTargetRunnerFactory runWorkerFactory{{runConfigFactory.runConfigurationId()}}; PythonSettings settings; + PythonWizardPageFactory pythonWizardPageFactory; }; PythonPlugin::PythonPlugin() @@ -60,7 +62,6 @@ void PythonPlugin::initialize() ProjectManager::registerProjectType<PythonProject>(PythonMimeType); ProjectManager::registerProjectType<PythonProject>(PythonMimeTypeLegacy); - JsonWizardFactory::registerPageFactory(new PythonWizardPageFactory); } void PythonPlugin::extensionsInitialized() @@ -70,7 +71,10 @@ void PythonPlugin::extensionsInitialized() ::Constants::FILEOVERLAY_PY); FileIconProvider::registerIconOverlayForSuffix(imageFile, "py"); - TaskHub::addCategory(PythonErrorTaskCategory, "Python", true); + TaskHub::addCategory({PythonErrorTaskCategory, + "Python", + Tr::tr("Issues parsed from Python runtime output."), + true}); } } // Python::Internal diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp index c9bb0f0b6ce..036d5e7ce4b 100644 --- a/src/plugins/python/pythonproject.cpp +++ b/src/plugins/python/pythonproject.cpp @@ -131,10 +131,9 @@ static QStringList readLines(const FilePath &projectFile) const QString line = stream.readLine(); if (line.isNull()) break; - if (visited.contains(line)) + if (!Utils::insert(visited, line)) continue; lines.append(line); - visited.insert(line); } } @@ -149,10 +148,8 @@ static QStringList readLinesJson(const FilePath &projectFile, QString *errorMess const QJsonObject obj = readObjJson(projectFile, errorMessage); for (const QJsonValue &file : obj.value("files").toArray()) { const QString fileName = file.toString(); - if (visited.contains(fileName)) - continue; - lines.append(fileName); - visited.insert(fileName); + if (Utils::insert(visited, fileName)) + lines.append(fileName); } return lines; @@ -232,7 +229,8 @@ void PythonBuildSystem::triggerParsing() newRoot->addNestedNode(std::make_unique<PythonFileNode>(entry.filePath, displayName, fileType)); const MimeType mt = mimeTypeForFile(entry.filePath, MimeMatchMode::MatchExtension); - if (mt.matchesName(Constants::C_PY_MIMETYPE) || mt.matchesName(Constants::C_PY3_MIMETYPE)) { + if (mt.matchesName(Constants::C_PY_MIMETYPE) || mt.matchesName(Constants::C_PY3_MIMETYPE) + || mt.matchesName(Constants::C_PY_GUI_MIMETYPE)) { BuildTargetInfo bti; bti.displayName = displayName; bti.buildKey = entry.filePath.toString(); @@ -422,7 +420,7 @@ QList<PythonBuildSystem::FileEntry> PythonBuildSystem::processEntries( return processed; } -Project::RestoreResult PythonProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult PythonProject::fromMap(const Store &map, QString *errorMessage) { Project::RestoreResult res = Project::fromMap(map, errorMessage); if (res == RestoreResult::Ok) { diff --git a/src/plugins/python/pythonproject.h b/src/plugins/python/pythonproject.h index 676e010a015..c249548d2a1 100644 --- a/src/plugins/python/pythonproject.h +++ b/src/plugins/python/pythonproject.h @@ -21,7 +21,7 @@ public: bool needsConfiguration() const final { return false; } private: - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; + RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) override; }; } // Python::Internal diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index e7c5b4dee14..d8a842ef3fb 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -23,7 +23,7 @@ #include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsystem.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/target.h> #include <projectexplorer/taskhub.h> @@ -119,55 +119,68 @@ private: //////////////////////////////////////////////////////////////// -class PythonRunConfigurationPrivate +class PythonInterpreterAspectPrivate : public QObject { public: - PythonRunConfigurationPrivate(PythonRunConfiguration *rc) - : q(rc) - {} - ~PythonRunConfigurationPrivate() + PythonInterpreterAspectPrivate(PythonInterpreterAspect *parent, RunConfiguration *rc) + : q(parent), rc(rc) { - qDeleteAll(m_extraCompilers); + connect(q, &InterpreterAspect::changed, + this, &PythonInterpreterAspectPrivate::currentInterpreterChanged); + + connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this, + [this](const FilePath &python) { + if (python == q->currentInterpreter().command) + checkForPySide(python); + } + ); + + connect(rc->target(), &Target::buildSystemUpdated, + this, &PythonInterpreterAspectPrivate::updateExtraCompilers); } - void checkForPySide(const Utils::FilePath &python); - void checkForPySide(const Utils::FilePath &python, const QString &pySidePackageName); + ~PythonInterpreterAspectPrivate() { qDeleteAll(m_extraCompilers); } + + void checkForPySide(const FilePath &python); + void checkForPySide(const FilePath &python, const QString &pySidePackageName); void handlePySidePackageInfo(const PipPackageInfo &pySideInfo, - const Utils::FilePath &python, + const FilePath &python, const QString &requestedPackageName); void updateExtraCompilers(); - Utils::FilePath m_pySideUicPath; + void currentInterpreterChanged(); - PythonRunConfiguration *q; + struct PySideTools + { + FilePath pySideProjectPath; + FilePath pySideUicPath; + }; + void updateTools(const PySideTools &tools); + + FilePath m_pySideUicPath; + + PythonInterpreterAspect *q; + RunConfiguration *rc; QList<PySideUicExtraCompiler *> m_extraCompilers; - QFutureWatcher<PipPackageInfo> m_watcher; + QFutureWatcher<PipPackageInfo> *m_watcher = nullptr; QMetaObject::Connection m_watcherConnection; - }; -PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) - : RunConfiguration(target, id) - , d(new PythonRunConfigurationPrivate(this)) +PythonInterpreterAspect::PythonInterpreterAspect(AspectContainer *container, RunConfiguration *rc) + : InterpreterAspect(container), d(new PythonInterpreterAspectPrivate(this, rc)) { - auto interpreterAspect = addAspect<InterpreterAspect>(); - interpreterAspect->setSettingsKey("PythonEditor.RunConfiguation.Interpreter"); - interpreterAspect->setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); + setSettingsKey("PythonEditor.RunConfiguation.Interpreter"); + setSettingsDialogId(Constants::C_PYTHONOPTIONS_PAGE_ID); - connect(interpreterAspect, &InterpreterAspect::changed, - this, &PythonRunConfiguration::currentInterpreterChanged); - - connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, - interpreterAspect, &InterpreterAspect::updateInterpreters); + updateInterpreters(PythonSettings::interpreters()); const QList<Interpreter> interpreters = PythonSettings::detectPythonVenvs( - project()->projectDirectory()); - interpreterAspect->updateInterpreters(PythonSettings::interpreters()); + rc->project()->projectDirectory()); Interpreter defaultInterpreter = interpreters.isEmpty() ? PythonSettings::defaultInterpreter() : interpreters.first(); if (!defaultInterpreter.command.isExecutableFile()) defaultInterpreter = PythonSettings::interpreters().value(0); if (defaultInterpreter.command.isExecutableFile()) { - const IDeviceConstPtr device = DeviceKitAspect::device(target->kit()); + const IDeviceConstPtr device = DeviceKitAspect::device(rc->kit()); if (device && !device->handlesFile(defaultInterpreter.command)) { defaultInterpreter = Utils::findOr(PythonSettings::interpreters(), defaultInterpreter, @@ -176,104 +189,53 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Id id) }); } } - interpreterAspect->setDefaultInterpreter(defaultInterpreter); + setDefaultInterpreter(defaultInterpreter); - auto bufferedAspect = addAspect<BoolAspect>(); - bufferedAspect->setSettingsKey("PythonEditor.RunConfiguation.Buffered"); - bufferedAspect->setLabel(Tr::tr("Buffered output"), BoolAspect::LabelPlacement::AtCheckBox); - bufferedAspect->setToolTip(Tr::tr("Enabling improves output performance, " - "but results in delayed output.")); - - auto scriptAspect = addAspect<MainScriptAspect>(); - scriptAspect->setSettingsKey("PythonEditor.RunConfiguation.Script"); - scriptAspect->setLabelText(Tr::tr("Script:")); - scriptAspect->setDisplayStyle(StringAspect::LabelDisplay); - - auto envAspect = addAspect<EnvironmentAspect>(); - envAspect->setSupportForBuildEnvironment(target); - - auto argumentsAspect = addAspect<ArgumentsAspect>(macroExpander()); - - addAspect<WorkingDirectoryAspect>(macroExpander(), nullptr); - addAspect<TerminalAspect>(); - - setCommandLineGetter([bufferedAspect, interpreterAspect, argumentsAspect, scriptAspect] { - CommandLine cmd{interpreterAspect->currentInterpreter().command}; - if (!bufferedAspect->value()) - cmd.addArg("-u"); - cmd.addArg(scriptAspect->filePath().fileName()); - cmd.addArgs(argumentsAspect->arguments(), CommandLine::Raw); - return cmd; - }); - - setUpdater([this, scriptAspect] { - const BuildTargetInfo bti = buildTargetInfo(); - const QString script = bti.targetFilePath.toUserOutput(); - setDefaultDisplayName(Tr::tr("Run %1").arg(script)); - scriptAspect->setValue(script); - aspect<WorkingDirectoryAspect>()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir()); - }); - - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); - connect(target, &Target::buildSystemUpdated, this, [this]() { d->updateExtraCompilers(); }); - currentInterpreterChanged(); - - setRunnableModifier([](Runnable &r) { - r.workingDirectory = r.command.executable().withNewMappedPath(r.workingDirectory); // FIXME: Needed? - }); - - connect(PySideInstaller::instance(), &PySideInstaller::pySideInstalled, this, - [this](const FilePath &python) { - if (python == aspect<InterpreterAspect>()->currentInterpreter().command) - d->checkForPySide(python); - }); + connect(PythonSettings::instance(), &PythonSettings::interpretersChanged, + this, &InterpreterAspect::updateInterpreters); } -PythonRunConfiguration::~PythonRunConfiguration() +PythonInterpreterAspect::~PythonInterpreterAspect() { delete d; } -void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python) +void PythonInterpreterAspectPrivate::checkForPySide(const FilePath &python) { - checkForPySide(python, "PySide6-Essentials"); + PySideTools tools; + const FilePath dir = python.parentDir(); + tools.pySideProjectPath = dir.pathAppended("pyside6-project").withExecutableSuffix(); + tools.pySideUicPath = dir.pathAppended("pyside6-uic").withExecutableSuffix(); + + if (tools.pySideProjectPath.isExecutableFile() && tools.pySideUicPath.isExecutableFile()) + updateTools(tools); + else + checkForPySide(python, "PySide6-Essentials"); } -void PythonRunConfigurationPrivate::checkForPySide(const FilePath &python, - const QString &pySidePackageName) +void PythonInterpreterAspectPrivate::checkForPySide(const FilePath &python, + const QString &pySidePackageName) { const PipPackage package(pySidePackageName); QObject::disconnect(m_watcherConnection); - m_watcherConnection = QObject::connect(&m_watcher, &QFutureWatcherBase::finished, q, [=] { - handlePySidePackageInfo(m_watcher.result(), python, pySidePackageName); + delete m_watcher; + m_watcher = new QFutureWatcher<PipPackageInfo>(this); + m_watcherConnection = QObject::connect(m_watcher, &QFutureWatcherBase::finished, q, [=] { + handlePySidePackageInfo(m_watcher->result(), python, pySidePackageName); }); const auto future = Pip::instance(python)->info(package); - m_watcher.setFuture(future); + m_watcher->setFuture(future); ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(future); } -void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo, - const Utils::FilePath &python, - const QString &requestedPackageName) +void PythonInterpreterAspectPrivate::handlePySidePackageInfo(const PipPackageInfo &pySideInfo, + const FilePath &python, + const QString &requestedPackageName) { - struct PythonTools - { - FilePath pySideProjectPath; - FilePath pySideUicPath; - }; - - BuildStepList *buildSteps = nullptr; - if (Target *target = q->target()) { - if (auto buildConfiguration = target->activeBuildConfiguration()) - buildSteps = buildConfiguration->buildSteps(); - } - if (!buildSteps) - return; - const auto findPythonTools = [](const FilePaths &files, const FilePath &location, - const FilePath &python) -> PythonTools { - PythonTools result; + const FilePath &python) -> PySideTools { + PySideTools result; const QString pySide6ProjectName = OsSpecificAspects::withExecutableSuffix(python.osType(), "pyside6-project"); const QString pySide6UicName @@ -294,26 +256,21 @@ void PythonRunConfigurationPrivate::handlePySidePackageInfo(const PipPackageInfo return {}; }; - PythonTools pythonTools = findPythonTools(pySideInfo.files, pySideInfo.location, python); - if (!pythonTools.pySideProjectPath.isExecutableFile() && requestedPackageName != "PySide6") { + PySideTools tools = findPythonTools(pySideInfo.files, pySideInfo.location, python); + if (!tools.pySideProjectPath.isExecutableFile() && requestedPackageName != "PySide6") { checkForPySide(python, "PySide6"); return; } - m_pySideUicPath = pythonTools.pySideUicPath; - - updateExtraCompilers(); - - if (auto pySideBuildStep = buildSteps->firstOfType<PySideBuildStep>()) - pySideBuildStep->updatePySideProjectPath(pythonTools.pySideProjectPath); + updateTools(tools); } -void PythonRunConfiguration::currentInterpreterChanged() +void PythonInterpreterAspectPrivate::currentInterpreterChanged() { - const FilePath python = aspect<InterpreterAspect>()->currentInterpreter().command; - d->checkForPySide(python); + const FilePath python = q->currentInterpreter().command; + checkForPySide(python); - for (FilePath &file : project()->files(Project::AllFiles)) { + for (FilePath &file : rc->project()->files(Project::AllFiles)) { if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { if (document->mimeType() == Constants::C_PY_MIMETYPE || document->mimeType() == Constants::C_PY3_MIMETYPE) { @@ -324,37 +281,53 @@ void PythonRunConfiguration::currentInterpreterChanged() } } -QList<PySideUicExtraCompiler *> PythonRunConfiguration::extraCompilers() const +void PythonInterpreterAspectPrivate::updateTools(const PySideTools &tools) +{ + m_pySideUicPath = tools.pySideUicPath; + + updateExtraCompilers(); + + if (Target *target = rc->target()) { + if (BuildConfiguration *buildConfiguration = target->activeBuildConfiguration()) { + if (BuildStepList *buildSteps = buildConfiguration->buildSteps()) { + if (auto buildStep = buildSteps->firstOfType<PySideBuildStep>()) + buildStep->updatePySideProjectPath(tools.pySideProjectPath); + } + } + } +} + +QList<PySideUicExtraCompiler *> PythonInterpreterAspect::extraCompilers() const { return d->m_extraCompilers; } -void PythonRunConfigurationPrivate::updateExtraCompilers() +void PythonInterpreterAspectPrivate::updateExtraCompilers() { QList<PySideUicExtraCompiler *> oldCompilers = m_extraCompilers; m_extraCompilers.clear(); if (m_pySideUicPath.isExecutableFile()) { - auto uiMatcher = [](const ProjectExplorer::Node *node) { - if (const ProjectExplorer::FileNode *fileNode = node->asFileNode()) - return fileNode->fileType() == ProjectExplorer::FileType::Form; + auto uiMatcher = [](const Node *node) { + if (const FileNode *fileNode = node->asFileNode()) + return fileNode->fileType() == FileType::Form; return false; }; - const FilePaths uiFiles = q->project()->files(uiMatcher); + const FilePaths uiFiles = rc->project()->files(uiMatcher); for (const FilePath &uiFile : uiFiles) { FilePath generated = uiFile.parentDir(); generated = generated.pathAppended("/ui_" + uiFile.baseName() + ".py"); int index = Utils::indexOf(oldCompilers, [&](PySideUicExtraCompiler *oldCompiler) { return oldCompiler->pySideUicPath() == m_pySideUicPath - && oldCompiler->project() == q->project() && oldCompiler->source() == uiFile + && oldCompiler->project() == rc->project() && oldCompiler->source() == uiFile && oldCompiler->targets() == FilePaths{generated}; }); if (index < 0) { m_extraCompilers << new PySideUicExtraCompiler(m_pySideUicPath, - q->project(), + rc->project(), uiFile, {generated}, - q); + this); } else { m_extraCompilers << oldCompilers.takeAt(index); } @@ -362,11 +335,69 @@ void PythonRunConfigurationPrivate::updateExtraCompilers() } for (LanguageClient::Client *client : LanguageClient::LanguageClientManager::clients()) { if (auto pylsClient = qobject_cast<PyLSClient *>(client)) - pylsClient->updateExtraCompilers(q->project(), m_extraCompilers); + pylsClient->updateExtraCompilers(rc->project(), m_extraCompilers); } qDeleteAll(oldCompilers); } +// RunConfiguration + +class PythonRunConfiguration : public RunConfiguration +{ +public: + PythonRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + buffered.setSettingsKey("PythonEditor.RunConfiguation.Buffered"); + buffered.setLabelText(Tr::tr("Buffered output")); + buffered.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + buffered.setToolTip(Tr::tr("Enabling improves output performance, " + "but results in delayed output.")); + + mainScript.setSettingsKey("PythonEditor.RunConfiguation.Script"); + mainScript.setLabelText(Tr::tr("Script:")); + mainScript.setReadOnly(true); + + environment.setSupportForBuildEnvironment(target); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + + x11Forwarding.setMacroExpander(macroExpander()); + x11Forwarding.setVisible(HostOsInfo::isAnyUnixHost()); + + setCommandLineGetter([this] { + CommandLine cmd{interpreter.currentInterpreter().command}; + if (!buffered()) + cmd.addArg("-u"); + cmd.addArg(mainScript().fileName()); + cmd.addArgs(arguments(), CommandLine::Raw); + return cmd; + }); + + setUpdater([this] { + const BuildTargetInfo bti = buildTargetInfo(); + setDefaultDisplayName(Tr::tr("Run %1").arg(bti.targetFilePath.toUserOutput())); + mainScript.setValue(bti.targetFilePath); + workingDir.setDefaultWorkingDirectory(bti.targetFilePath.parentDir()); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + } + + PythonInterpreterAspect interpreter{this, this}; + BoolAspect buffered{this}; + MainScriptAspect mainScript{this}; + EnvironmentAspect environment{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + X11ForwardingAspect x11Forwarding{this}; +}; + +// Factories + PythonRunConfigurationFactory::PythonRunConfigurationFactory() { registerRunConfiguration<PythonRunConfiguration>(Constants::C_PYTHONRUNCONFIGURATION_ID); diff --git a/src/plugins/python/pythonrunconfiguration.h b/src/plugins/python/pythonrunconfiguration.h index dc620ea898f..b4eb54b12f8 100644 --- a/src/plugins/python/pythonrunconfiguration.h +++ b/src/plugins/python/pythonrunconfiguration.h @@ -4,24 +4,25 @@ #pragma once #include <projectexplorer/runconfiguration.h> +#include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/runcontrol.h> namespace Python::Internal { -class PythonRunConfigurationPrivate; class PySideUicExtraCompiler; +class PythonRunConfiguration; -class PythonRunConfiguration : public ProjectExplorer::RunConfiguration +class PythonInterpreterAspect final : public ProjectExplorer::InterpreterAspect { - Q_OBJECT public: - PythonRunConfiguration(ProjectExplorer::Target *target, Utils::Id id); - ~PythonRunConfiguration() override; - void currentInterpreterChanged(); + PythonInterpreterAspect(Utils::AspectContainer *container, ProjectExplorer::RunConfiguration *rc); + ~PythonInterpreterAspect() final; + QList<PySideUicExtraCompiler *> extraCompilers() const; private: - PythonRunConfigurationPrivate *d = nullptr; + friend class PythonRunConfiguration; + class PythonInterpreterAspectPrivate *d = nullptr; }; class PythonRunConfigurationFactory : public ProjectExplorer::RunConfigurationFactory diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index 020aad52259..410714af090 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -21,13 +21,14 @@ #include <texteditor/texteditor.h> #include <utils/algorithm.h> -#include <utils/qtcassert.h> +#include <utils/async.h> #include <utils/detailswidget.h> #include <utils/environment.h> -#include <utils/listmodel.h> #include <utils/layoutbuilder.h> +#include <utils/listmodel.h> #include <utils/pathchooser.h> #include <utils/process.h> +#include <utils/qtcassert.h> #include <utils/treemodel.h> #include <utils/utilsicons.h> @@ -169,6 +170,8 @@ InterpreterOptionsWidget::InterpreterOptionsWidget() return f; } case Qt::ToolTipRole: + if (interpreter.command.needsDevice()) + break; if (interpreter.command.isEmpty()) return Tr::tr("Executable is empty."); if (!interpreter.command.exists()) @@ -178,6 +181,8 @@ InterpreterOptionsWidget::InterpreterOptionsWidget() .arg(interpreter.command.toUserOutput()); break; case Qt::DecorationRole: + if (interpreter.command.needsDevice()) + break; if (column == 0 && !interpreter.command.isExecutableFile()) return Utils::Icons::CRITICAL.icon(); break; @@ -366,14 +371,6 @@ private: InterpreterOptionsWidget *m_widget = nullptr; }; -static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath &pythonExecutable) -{ - return Utils::anyOf(pythons, [pythonExecutable](const Interpreter &interpreter) { - return interpreter.command.toFileInfo().canonicalFilePath() - == pythonExecutable.toFileInfo().canonicalFilePath(); - }); -} - static InterpreterOptionsPage &interpreterOptionsPage() { static InterpreterOptionsPage page; @@ -626,8 +623,9 @@ static void disableOutdatedPyls() } } -static void addPythonsFromRegistry(QList<Interpreter> &pythons) +static QList<Interpreter> pythonsFromRegistry() { + QList<Interpreter> pythons; QSettings pythonRegistry("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", QSettings::NativeFormat); for (const QString &versionGroup : pythonRegistry.childGroups()) { @@ -636,7 +634,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons) QVariant regVal = pythonRegistry.value("InstallPath/ExecutablePath"); if (regVal.isValid()) { const FilePath &executable = FilePath::fromUserInput(regVal.toString()); - if (executable.exists() && !alreadyRegistered(pythons, executable)) { + if (executable.exists()) { pythons << Interpreter{QUuid::createUuid().toString(), name, FilePath::fromUserInput(regVal.toString())}; @@ -645,7 +643,7 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons) regVal = pythonRegistry.value("InstallPath/WindowedExecutablePath"); if (regVal.isValid()) { const FilePath &executable = FilePath::fromUserInput(regVal.toString()); - if (executable.exists() && !alreadyRegistered(pythons, executable)) { + if (executable.exists()) { pythons << Interpreter{QUuid::createUuid().toString(), //: <python display name> (Windowed) Tr::tr("%1 (Windowed)").arg(name), @@ -656,28 +654,30 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons) if (regVal.isValid()) { const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath python = path.pathAppended("python").withExecutableSuffix(); - if (python.exists() && !alreadyRegistered(pythons, python)) + if (python.exists()) pythons << createInterpreter(python, "Python " + versionGroup); const FilePath pythonw = path.pathAppended("pythonw").withExecutableSuffix(); - if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) + if (pythonw.exists()) pythons << createInterpreter(pythonw, "Python " + versionGroup, "(Windowed)"); } pythonRegistry.endGroup(); } + return pythons; } -static void addPythonsFromPath(QList<Interpreter> &pythons) +static QList<Interpreter> pythonsFromPath() { + QList<Interpreter> pythons; if (HostOsInfo::isWindowsHost()) { for (const FilePath &executable : FilePath("python").searchAllInPath()) { // Windows creates empty redirector files that may interfere if (executable.toFileInfo().size() == 0) continue; - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path"); } for (const FilePath &executable : FilePath("pythonw").searchAllInPath()) { - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path", "(Windowed)"); } } else { @@ -690,11 +690,12 @@ static void addPythonsFromPath(QList<Interpreter> &pythons) const QDir dir(path.toString()); for (const QFileInfo &fi : dir.entryInfoList(filters)) { const FilePath executable = Utils::FilePath::fromFileInfo(fi); - if (executable.exists() && !alreadyRegistered(pythons, executable)) + if (executable.exists()) pythons << createInterpreter(executable, "Python from Path"); } } } + return pythons; } static QString idForPythonFromPath(const QList<Interpreter> &pythons) @@ -713,6 +714,51 @@ static QString idForPythonFromPath(const QList<Interpreter> &pythons) static PythonSettings *settingsInstance = nullptr; +static bool alreadyRegistered(const Interpreter &candidate) +{ + return Utils::anyOf(settingsInstance->interpreters(), + [candidate = candidate.command](const Interpreter &interpreter) { + return interpreter.command.isSameDevice(candidate) + && interpreter.command.resolveSymlinks() + == candidate.resolveSymlinks(); + }); +} + +static void scanPath() +{ + auto watcher = new QFutureWatcher<QList<Interpreter>>(); + QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() { + for (const Interpreter &interpreter : watcher->result()) { + if (!alreadyRegistered(interpreter)) + settingsInstance->addInterpreter(interpreter); + } + watcher->deleteLater(); + }); + watcher->setFuture(Utils::asyncRun(pythonsFromPath)); +} + +static void scanRegistry() +{ + auto watcher = new QFutureWatcher<QList<Interpreter>>(); + QObject::connect(watcher, &QFutureWatcher<QList<Interpreter>>::finished, [watcher]() { + for (const Interpreter &interpreter : watcher->result()) { + if (!alreadyRegistered(interpreter)) + settingsInstance->addInterpreter(interpreter); + } + watcher->deleteLater(); + scanPath(); + }); + watcher->setFuture(Utils::asyncRun(pythonsFromRegistry)); +} + +static void scanSystemForInterpreters() +{ + if (Utils::HostOsInfo::isWindowsHost()) + scanRegistry(); + else + scanPath(); +} + PythonSettings::PythonSettings() { QTC_ASSERT(!settingsInstance, return); @@ -723,9 +769,7 @@ PythonSettings::PythonSettings() initFromSettings(Core::ICore::settings()); - if (HostOsInfo::isWindowsHost()) - addPythonsFromRegistry(m_interpreters); - addPythonsFromPath(m_interpreters); + scanSystemForInterpreters(); if (m_defaultInterpreterId.isEmpty()) m_defaultInterpreterId = idForPythonFromPath(m_interpreters); @@ -908,7 +952,7 @@ QList<Interpreter> PythonSettings::detectPythonVenvs(const FilePath &path) return result; } -void PythonSettings::initFromSettings(QSettings *settings) +void PythonSettings::initFromSettings(QtcSettings *settings) { settings->beginGroup(settingsGroupKey); const QVariantList interpreters = settings->value(interpreterKey).toList(); @@ -954,7 +998,7 @@ void PythonSettings::initFromSettings(QSettings *settings) settings->endGroup(); } -void PythonSettings::writeToSettings(QSettings *settings) +void PythonSettings::writeToSettings(QtcSettings *settings) { settings->beginGroup(settingsGroupKey); QVariantList interpretersVar; diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h index 35939c1ecd1..373054094e1 100644 --- a/src/plugins/python/pythonsettings.h +++ b/src/plugins/python/pythonsettings.h @@ -57,8 +57,8 @@ public slots: void listDetectedPython(const QString &detectionSource, QString *logMessage); private: - void initFromSettings(QSettings *settings); - void writeToSettings(QSettings *settings); + void initFromSettings(Utils::QtcSettings *settings); + void writeToSettings(Utils::QtcSettings *settings); QList<Interpreter> m_interpreters; QString m_defaultInterpreterId; diff --git a/src/plugins/python/pythonwizardpage.cpp b/src/plugins/python/pythonwizardpage.cpp index 2216dbfa574..8b631e2c414 100644 --- a/src/plugins/python/pythonwizardpage.cpp +++ b/src/plugins/python/pythonwizardpage.cpp @@ -109,16 +109,15 @@ PythonWizardPage::PythonWizardPage(const QList<QPair<QString, QVariant>> &pySide m_stateLabel->setWordWrap(true); m_stateLabel->setFilled(true); m_stateLabel->setType(InfoLabel::Error); - connect(&m_venvPath, &StringAspect::valueChanged, this, &PythonWizardPage::updateStateLabel); - connect(&m_createVenv, &BoolAspect::valueChanged, this, &PythonWizardPage::updateStateLabel); + connect(&m_venvPath, &FilePathAspect::validChanged, this, &PythonWizardPage::updateStateLabel); + connect(&m_createVenv, &BaseAspect::changed, this, &PythonWizardPage::updateStateLabel); - Grid { - m_pySideVersion, br, - m_interpreter, br, - m_createVenv, br, + Form { + m_pySideVersion, st, br, + m_interpreter, st, br, + m_createVenv, st, br, m_venvPath, br, - m_stateLabel, br, - noMargin + m_stateLabel, br }.attachTo(this); } @@ -132,8 +131,8 @@ void PythonWizardPage::initializePage() const FilePath projectDir = FilePath::fromString(wiz->property("ProjectDirectory").toString()); m_createVenv.setValue(!projectDir.isEmpty()); - if (m_venvPath.filePath().isEmpty()) - m_venvPath.setFilePath(projectDir.isEmpty() ? FilePath{} : projectDir / "venv"); + if (m_venvPath().isEmpty()) + m_venvPath.setValue(projectDir.isEmpty() ? FilePath{} : projectDir / "venv"); updateInterpreters(); updateStateLabel(); @@ -141,7 +140,7 @@ void PythonWizardPage::initializePage() bool PythonWizardPage::validatePage() { - if (m_createVenv.value() && !m_venvPath.pathChooser()->isValid()) + if (m_createVenv() && !m_venvPath.pathChooser()->isValid()) return false; auto wiz = qobject_cast<JsonWizard *>(wizard()); const QMap<QString, QVariant> data = m_pySideVersion.itemValue().toMap(); @@ -157,7 +156,7 @@ void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files) Interpreter interpreter = m_interpreter.currentInterpreter(); Project *project = ProjectManager::openProject(Utils::mimeTypeForFile(f.file.filePath()), f.file.filePath().absoluteFilePath()); - if (m_createVenv.value()) { + if (m_createVenv()) { auto openProjectWithInterpreter = [f](const std::optional<Interpreter> &interpreter) { if (!interpreter) return; @@ -171,7 +170,7 @@ void PythonWizardPage::setupProject(const JsonWizard::GeneratorFiles &files) } } }; - PythonSettings::createVirtualEnvironment(m_venvPath.filePath(), + PythonSettings::createVirtualEnvironment(m_venvPath(), interpreter, openProjectWithInterpreter, project ? project->displayName() @@ -203,7 +202,7 @@ void PythonWizardPage::updateInterpreters() void PythonWizardPage::updateStateLabel() { QTC_ASSERT(m_stateLabel, return); - if (m_createVenv.value()) { + if (m_createVenv()) { if (PathChooser *pathChooser = m_venvPath.pathChooser()) { if (!pathChooser->isValid()) { m_stateLabel->show(); diff --git a/src/plugins/qbsprojectmanager/CMakeLists.txt b/src/plugins/qbsprojectmanager/CMakeLists.txt index 885edbf0bf7..f039198419e 100644 --- a/src/plugins/qbsprojectmanager/CMakeLists.txt +++ b/src/plugins/qbsprojectmanager/CMakeLists.txt @@ -11,7 +11,7 @@ add_qtc_plugin(QbsProjectManager qbsbuildstep.cpp qbsbuildstep.h qbscleanstep.cpp qbscleanstep.h qbsinstallstep.cpp qbsinstallstep.h - qbskitinformation.cpp qbskitinformation.h + qbskitaspect.cpp qbskitaspect.h qbsnodes.cpp qbsnodes.h qbsnodetreebuilder.cpp qbsnodetreebuilder.h qbspmlogging.cpp qbspmlogging.h @@ -25,6 +25,7 @@ add_qtc_plugin(QbsProjectManager qbsprojectmanagerconstants.h qbsprojectmanagerplugin.cpp qbsprojectmanagerplugin.h qbsprojectparser.cpp qbsprojectparser.h + qbsrequest.cpp qbsrequest.h qbssession.cpp qbssession.h qbssettings.cpp qbssettings.h ) diff --git a/src/plugins/qbsprojectmanager/QbsProjectManager.json.in b/src/plugins/qbsprojectmanager/QbsProjectManager.json.in index 6f74bda34f9..263722d28da 100644 --- a/src/plugins/qbsprojectmanager/QbsProjectManager.json.in +++ b/src/plugins/qbsprojectmanager/QbsProjectManager.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"QbsProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QbsProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"QBS support.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Build Systems", + "Description" : "QBS support.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index e2d1ae9ee7b..4f755b1f1a3 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -3,7 +3,7 @@ #include "defaultpropertyprovider.h" -#include "qbskitinformation.h" +#include "qbskitaspect.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" @@ -13,7 +13,7 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/gcctoolchain.h> #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/toolchain.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/msvctoolchain.h> @@ -24,7 +24,7 @@ #include <android/androidconstants.h> #include <ios/iosconstants.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QDir> #include <QFileInfo> @@ -336,7 +336,7 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor } data.insert(QLatin1String(CPP_TOOLCHAINPATH), mainFilePath.absolutePath().toString()); - if (auto gcc = dynamic_cast<ProjectExplorer::GccToolChain *>(mainTc)) { + if (auto gcc = mainTc->asGccToolChain()) { QStringList compilerFlags = gcc->platformCodeGenFlags(); filterCompilerLinkerFlags(targetAbi, compilerFlags); data.insert(QLatin1String(CPP_PLATFORMCOMMONCOMPILERFLAGS), compilerFlags); diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp index 41ae75d8b9b..efc029bce44 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.cpp @@ -4,8 +4,6 @@ #include "qbsbuildconfiguration.h" #include "qbsbuildstep.h" -#include "qbscleanstep.h" -#include "qbsinstallstep.h" #include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" @@ -17,7 +15,7 @@ #include <projectexplorer/buildsteplist.h> #include <projectexplorer/deployconfiguration.h> #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorertr.h> @@ -25,7 +23,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/process.h> #include <utils/qtcassert.h> @@ -98,7 +96,7 @@ QbsBuildConfiguration::QbsBuildConfiguration(Target *target, Utils::Id id) + '_' + kit->fileSystemFriendlyName().left(8) + '_' + hash.toHex().left(16); - m_configurationName->setValue(uniqueConfigName); + configurationName.setValue(uniqueConfigName); auto bs = buildSteps()->firstOfType<QbsBuildStep>(); QTC_ASSERT(bs, return); @@ -107,23 +105,21 @@ QbsBuildConfiguration::QbsBuildConfiguration(Target *target, Utils::Id id) emit qbsConfigurationChanged(); }); - m_configurationName = addAspect<StringAspect>(); - m_configurationName->setLabelText(QbsProjectManager::Tr::tr("Configuration name:")); - m_configurationName->setSettingsKey("Qbs.configName"); - m_configurationName->setDisplayStyle(StringAspect::LineEditDisplay); - connect(m_configurationName, &StringAspect::changed, + configurationName.setSettingsKey("Qbs.configName"); + configurationName.setLabelText(QbsProjectManager::Tr::tr("Configuration name:")); + configurationName.setDisplayStyle(StringAspect::LineEditDisplay); + connect(&configurationName, &StringAspect::changed, this, &BuildConfiguration::buildDirectoryChanged); - const auto separateDebugInfoAspect = addAspect<SeparateDebugInfoAspect>(); - connect(separateDebugInfoAspect, &SeparateDebugInfoAspect::changed, + connect(&separateDebugInfoSetting, &BaseAspect::changed, this, &QbsBuildConfiguration::qbsConfigurationChanged); - const auto qmlDebuggingAspect = addAspect<QtSupport::QmlDebuggingAspect>(this); - connect(qmlDebuggingAspect, &QtSupport::QmlDebuggingAspect::changed, + qmlDebuggingSetting.setBuildConfiguration(this); + connect(&qmlDebuggingSetting, &BaseAspect::changed, this, &QbsBuildConfiguration::qbsConfigurationChanged); - const auto qtQuickCompilerAspect = addAspect<QtSupport::QtQuickCompilerAspect>(this); - connect(qtQuickCompilerAspect, &QtSupport::QtQuickCompilerAspect::changed, + qtQuickCompilerSetting.setBuildConfiguration(this); + connect(&qtQuickCompilerSetting, &BaseAspect::changed, this, &QbsBuildConfiguration::qbsConfigurationChanged); connect(this, &BuildConfiguration::environmentChanged, @@ -142,14 +138,6 @@ QbsBuildConfiguration::QbsBuildConfiguration(Target *target, Utils::Id id) QbsBuildConfiguration::~QbsBuildConfiguration() { - for (BuildStep * const bs : buildSteps()->steps()) { - if (const auto qbs = qobject_cast<QbsBuildStep *>(bs)) - qbs->dropSession(); - } - for (BuildStep * const cs : cleanSteps()->steps()) { - if (const auto qcs = qobject_cast<QbsCleanStep *>(cs)) - qcs->dropSession(); - } delete m_buildSystem; } @@ -164,19 +152,18 @@ void QbsBuildConfiguration::triggerReparseIfActive() m_buildSystem->delayParsing(); } -bool QbsBuildConfiguration::fromMap(const QVariantMap &map) +void QbsBuildConfiguration::fromMap(const Store &map) { - if (!BuildConfiguration::fromMap(map)) - return false; + BuildConfiguration::fromMap(map); + if (hasError()) + return; - if (m_configurationName->value().isEmpty()) { // pre-4.4 backwards compatibility + if (configurationName().isEmpty()) { // pre-4.4 backwards compatibility const QString profileName = QbsProfileManager::profileNameForKit(target()->kit()); const QString buildVariant = qbsConfiguration() .value(QLatin1String(Constants::QBS_CONFIG_VARIANT_KEY)).toString(); - m_configurationName->setValue(profileName + '-' + buildVariant); + configurationName.setValue(profileName + '-' + buildVariant); } - - return true; } void QbsBuildConfiguration::restrictNextBuild(const RunConfiguration *rc) @@ -249,11 +236,6 @@ QStringList QbsBuildConfiguration::products() const return m_products; } -QString QbsBuildConfiguration::configurationName() const -{ - return m_configurationName->value(); -} - QString QbsBuildConfiguration::equivalentCommandLine(const QbsBuildStepData &stepData) const { CommandLine commandLine; @@ -300,21 +282,6 @@ QString QbsBuildConfiguration::equivalentCommandLine(const QbsBuildStepData &ste return commandLine.arguments(); } -TriState QbsBuildConfiguration::qmlDebuggingSetting() const -{ - return aspect<QtSupport::QmlDebuggingAspect>()->value(); -} - -TriState QbsBuildConfiguration::qtQuickCompilerSetting() const -{ - return aspect<QtSupport::QtQuickCompilerAspect>()->value(); -} - -TriState QbsBuildConfiguration::separateDebugInfoSetting() const -{ - return aspect<SeparateDebugInfoAspect>()->value(); -} - // --------------------------------------------------------------------------- // QbsBuildConfigurationFactory: // --------------------------------------------------------------------------- diff --git a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h index 89ee9624371..2ea114864f2 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h +++ b/src/plugins/qbsprojectmanager/qbsbuildconfiguration.h @@ -60,25 +60,24 @@ public: void setProducts(const QStringList &products); QStringList products() const; - QString configurationName() const; QString equivalentCommandLine(const QbsBuildStepData &stepData) const; - Utils::TriState qmlDebuggingSetting() const; - Utils::TriState qtQuickCompilerSetting() const; - Utils::TriState separateDebugInfoSetting() const; + Utils::StringAspect configurationName{this}; + ProjectExplorer::SeparateDebugInfoAspect separateDebugInfoSetting{this}; + QtSupport::QmlDebuggingAspect qmlDebuggingSetting{this}; + QtSupport::QtQuickCompilerAspect qtQuickCompilerSetting{this}; signals: void qbsConfigurationChanged(); private: - bool fromMap(const QVariantMap &map) override; + void fromMap(const Utils::Store &map) override; void restrictNextBuild(const ProjectExplorer::RunConfiguration *rc) override; void triggerReparseIfActive(); QStringList m_changedFiles; QStringList m_activeFileTags; QStringList m_products; - Utils::StringAspect *m_configurationName = nullptr; QbsBuildSystem *m_buildSystem = nullptr; }; diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp index afb94652416..cea9b83d14d 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.cpp @@ -7,35 +7,29 @@ #include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" +#include "qbsrequest.h" #include "qbssession.h" #include "qbssettings.h" -#include <coreplugin/icore.h> - #include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kit.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorertr.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> -#include <qtsupport/qtversionmanager.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> -#include <utils/aspects.h> #include <utils/guard.h> #include <utils/layoutbuilder.h> #include <utils/macroexpander.h> #include <utils/outputformatter.h> #include <utils/pathchooser.h> -#include <utils/process.h> #include <utils/qtcassert.h> #include <utils/variablechooser.h> #include <QCheckBox> #include <QJsonArray> #include <QJsonObject> -#include <QLabel> #include <QThread> // -------------------------------------------------------------------- @@ -53,30 +47,10 @@ using namespace ProjectExplorer; using namespace QtSupport; using namespace Utils; -namespace QbsProjectManager { -namespace Internal { +namespace QbsProjectManager::Internal { -class ArchitecturesAspect : public Utils::MultiSelectionAspect -{ - Q_OBJECT -public: - ArchitecturesAspect(); - - void setKit(const ProjectExplorer::Kit *kit) { m_kit = kit; } - void addToLayout(Layouting::LayoutItem &parent) override; - QStringList selectedArchitectures() const; - void setSelectedArchitectures(const QStringList& architectures); - bool isManagedByTarget() const { return m_isManagedByTarget; } - -private: - void setVisibleDynamic(bool visible); - - const ProjectExplorer::Kit *m_kit = nullptr; - QMap<QString, QString> m_abisToArchMap; - bool m_isManagedByTarget = false; -}; - -ArchitecturesAspect::ArchitecturesAspect() +ArchitecturesAspect::ArchitecturesAspect(AspectContainer *container) + : MultiSelectionAspect(container) { m_abisToArchMap = { {ProjectExplorer::Constants::ANDROID_ABI_ARMEABI_V7A, "armv7a"}, @@ -158,7 +132,7 @@ private: QbsBuildStep *qbsStep() const; - bool validateProperties(Utils::FancyLineEdit *edit, QString *errorMessage); + bool validateProperties(FancyLineEdit *edit, QString *errorMessage); class Property { @@ -192,7 +166,7 @@ private: // QbsBuildStep: // -------------------------------------------------------------------- -QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Utils::Id id) : +QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Id id) : BuildStep(bsl, id) { setDisplayName(QbsProjectManager::Tr::tr("Qbs Build")); @@ -205,94 +179,86 @@ QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Utils::Id id) : connect(this, &QbsBuildStep::qbsConfigurationChanged, qbsBuildConfig, &QbsBuildConfiguration::qbsConfigurationChanged); - m_buildVariant = addAspect<SelectionAspect>(); - m_buildVariant->setDisplayName(QbsProjectManager::Tr::tr("Build variant:")); - m_buildVariant->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - m_buildVariant->addOption({ProjectExplorer::Tr::tr("Debug"), {}, Constants::QBS_VARIANT_DEBUG}); - m_buildVariant->addOption({ProjectExplorer::Tr::tr("Release"), {}, - Constants::QBS_VARIANT_RELEASE}); - m_buildVariant->addOption({ProjectExplorer::Tr::tr("Profile"), {}, - Constants::QBS_VARIANT_PROFILING}); + buildVariantHolder.setDisplayName(QbsProjectManager::Tr::tr("Build variant:")); + buildVariantHolder.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + buildVariantHolder.addOption({ProjectExplorer::Tr::tr("Debug"), {}, Constants::QBS_VARIANT_DEBUG}); + buildVariantHolder.addOption({ProjectExplorer::Tr::tr("Release"), {}, + Constants::QBS_VARIANT_RELEASE}); + buildVariantHolder.addOption({ProjectExplorer::Tr::tr("Profile"), {}, + Constants::QBS_VARIANT_PROFILING}); - m_selectedAbis = addAspect<ArchitecturesAspect>(); - m_selectedAbis->setLabelText(QbsProjectManager::Tr::tr("ABIs:")); - m_selectedAbis->setDisplayStyle(MultiSelectionAspect::DisplayStyle::ListView); - m_selectedAbis->setKit(target()->kit()); + selectedAbis.setLabelText(QbsProjectManager::Tr::tr("ABIs:")); + selectedAbis.setDisplayStyle(MultiSelectionAspect::DisplayStyle::ListView); + selectedAbis.setKit(target()->kit()); - m_keepGoing = addAspect<BoolAspect>(); - m_keepGoing->setSettingsKey(QBS_KEEP_GOING); - m_keepGoing->setToolTip( - QbsProjectManager::Tr::tr("Keep going when errors occur (if at all possible).")); - m_keepGoing->setLabel(QbsProjectManager::Tr::tr("Keep going"), + keepGoing.setSettingsKey(QBS_KEEP_GOING); + keepGoing.setToolTip( + QbsProjectManager::Tr::tr("Keep going when errors occur (if at all possible).")); + keepGoing.setLabel(QbsProjectManager::Tr::tr("Keep going"), + BoolAspect::LabelPlacement::AtCheckBox); + + maxJobCount.setSettingsKey(QBS_MAXJOBCOUNT); + maxJobCount.setLabel(QbsProjectManager::Tr::tr("Parallel jobs:")); + maxJobCount.setToolTip(QbsProjectManager::Tr::tr("Number of concurrent build jobs.")); + maxJobCount.setValue(QThread::idealThreadCount()); + + showCommandLines.setSettingsKey(QBS_SHOWCOMMANDLINES); + showCommandLines.setLabel(QbsProjectManager::Tr::tr("Show command lines"), + BoolAspect::LabelPlacement::AtCheckBox); + + install.setSettingsKey(QBS_INSTALL); + install.setValue(true); + install.setLabel(QbsProjectManager::Tr::tr("Install"), BoolAspect::LabelPlacement::AtCheckBox); + + cleanInstallRoot.setSettingsKey(QBS_CLEAN_INSTALL_ROOT); + cleanInstallRoot.setLabel(QbsProjectManager::Tr::tr("Clean install root"), + BoolAspect::LabelPlacement::AtCheckBox); + + forceProbes.setSettingsKey("Qbs.forceProbesKey"); + forceProbes.setLabel(QbsProjectManager::Tr::tr("Force probes"), BoolAspect::LabelPlacement::AtCheckBox); - m_maxJobCount = addAspect<IntegerAspect>(); - m_maxJobCount->setSettingsKey(QBS_MAXJOBCOUNT); - m_maxJobCount->setLabel(QbsProjectManager::Tr::tr("Parallel jobs:")); - m_maxJobCount->setToolTip(QbsProjectManager::Tr::tr("Number of concurrent build jobs.")); - m_maxJobCount->setValue(QThread::idealThreadCount()); + commandLine.setDisplayStyle(StringAspect::TextEditDisplay); + commandLine.setLabelText(QbsProjectManager::Tr::tr("Equivalent command line:")); + commandLine.setReadOnly(true); - m_showCommandLines = addAspect<BoolAspect>(); - m_showCommandLines->setSettingsKey(QBS_SHOWCOMMANDLINES); - m_showCommandLines->setLabel(QbsProjectManager::Tr::tr("Show command lines"), - BoolAspect::LabelPlacement::AtCheckBox); + connect(&maxJobCount, &BaseAspect::changed, this, &QbsBuildStep::updateState); + connect(&keepGoing, &BaseAspect::changed, this, &QbsBuildStep::updateState); + connect(&showCommandLines, &BaseAspect::changed, this, &QbsBuildStep::updateState); + connect(&install, &BaseAspect::changed, this, &QbsBuildStep::updateState); + connect(&cleanInstallRoot, &BaseAspect::changed, this, &QbsBuildStep::updateState); + connect(&forceProbes, &BaseAspect::changed, this, &QbsBuildStep::updateState); - m_install = addAspect<BoolAspect>(); - m_install->setSettingsKey(QBS_INSTALL); - m_install->setValue(true); - m_install->setLabel(QbsProjectManager::Tr::tr("Install"), BoolAspect::LabelPlacement::AtCheckBox); - - m_cleanInstallDir = addAspect<BoolAspect>(); - m_cleanInstallDir->setSettingsKey(QBS_CLEAN_INSTALL_ROOT); - m_cleanInstallDir->setLabel(QbsProjectManager::Tr::tr("Clean install root"), - BoolAspect::LabelPlacement::AtCheckBox); - - m_forceProbes = addAspect<BoolAspect>(); - m_forceProbes->setSettingsKey("Qbs.forceProbesKey"); - m_forceProbes->setLabel(QbsProjectManager::Tr::tr("Force probes"), - BoolAspect::LabelPlacement::AtCheckBox); - - m_commandLine = addAspect<StringAspect>(); - m_commandLine->setDisplayStyle(StringAspect::TextEditDisplay); - m_commandLine->setLabelText(QbsProjectManager::Tr::tr("Equivalent command line:")); - m_commandLine->setUndoRedoEnabled(false); - m_commandLine->setReadOnly(true); - - connect(m_maxJobCount, &BaseAspect::changed, this, &QbsBuildStep::updateState); - connect(m_keepGoing, &BaseAspect::changed, this, &QbsBuildStep::updateState); - connect(m_showCommandLines, &BaseAspect::changed, this, &QbsBuildStep::updateState); - connect(m_install, &BaseAspect::changed, this, &QbsBuildStep::updateState); - connect(m_cleanInstallDir, &BaseAspect::changed, this, &QbsBuildStep::updateState); - connect(m_forceProbes, &BaseAspect::changed, this, &QbsBuildStep::updateState); - - connect(m_buildVariant, &SelectionAspect::changed, this, [this] { - setBuildVariant(m_buildVariant->itemValue().toString()); + connect(&buildVariantHolder, &BaseAspect::changed, this, [this] { + const QString variant = buildVariantHolder.itemValue().toString(); + if (m_qbsConfiguration.value(Constants::QBS_CONFIG_VARIANT_KEY).toString() == variant) + return; + m_qbsConfiguration.insert(Constants::QBS_CONFIG_VARIANT_KEY, variant); + emit qbsConfigurationChanged(); + if (BuildConfiguration *bc = buildConfiguration()) + emit bc->buildTypeChanged(); + }); + connect(&selectedAbis, &BaseAspect::changed, this, [this] { + const QStringList architectures = selectedAbis.selectedArchitectures(); + if (configuredArchitectures() == architectures) + return; + if (architectures.isEmpty()) + m_qbsConfiguration.remove(Constants::QBS_ARCHITECTURES); + else + m_qbsConfiguration.insert(Constants::QBS_ARCHITECTURES, architectures.join(',')); + emit qbsConfigurationChanged(); }); - connect(m_selectedAbis, &SelectionAspect::changed, [this] { - setConfiguredArchitectures(m_selectedAbis->selectedArchitectures()); }); -} - -QbsBuildStep::~QbsBuildStep() -{ - doCancel(); - if (m_session) - m_session->disconnect(this); } bool QbsBuildStep::init() { - if (m_session) - return false; - - auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration()); - + const auto bc = qbsBuildConfiguration(); if (!bc) return false; m_changedFiles = bc->changedFiles(); m_activeFileTags = bc->activeFileTags(); m_products = bc->products(); - return true; } @@ -302,32 +268,16 @@ void QbsBuildStep::setupOutputFormatter(OutputFormatter *formatter) BuildStep::setupOutputFormatter(formatter); } -void QbsBuildStep::doRun() -{ - // We need a pre-build parsing step in order not to lose project file changes done - // right before building (but before the delay has elapsed). - m_parsingAfterBuild = false; - parseProject(); -} - QWidget *QbsBuildStep::createConfigWidget() { return new QbsBuildStepConfigWidget(this); } -void QbsBuildStep::doCancel() -{ - if (m_parsingProject) - qbsBuildSystem()->cancelParsing(); - else if (m_session) - m_session->cancelCurrentJob(); -} - QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) const { QVariantMap config = m_qbsConfiguration; - const auto qbsBuildConfig = static_cast<QbsBuildConfiguration *>(buildConfiguration()); - config.insert(Constants::QBS_FORCE_PROBES_KEY, m_forceProbes->value()); + const auto qbsBuildConfig = qbsBuildConfiguration(); + config.insert(Constants::QBS_FORCE_PROBES_KEY, forceProbes()); const auto store = [&config](TriState ts, const QString &key) { if (ts == TriState::Enabled) @@ -370,9 +320,8 @@ void QbsBuildStep::setQbsConfiguration(const QVariantMap &config) if (tmp == m_qbsConfiguration) return; m_qbsConfiguration = tmp; - if (m_buildVariant) - m_buildVariant->setValue(m_buildVariant->indexForItemValue(buildVariant)); - if (ProjectExplorer::BuildConfiguration *bc = buildConfiguration()) + buildVariantHolder.setValue(buildVariantHolder.indexForItemValue(buildVariant)); + if (BuildConfiguration *bc = buildConfiguration()) emit bc->buildTypeChanged(); emit qbsConfigurationChanged(); } @@ -382,12 +331,12 @@ bool QbsBuildStep::hasCustomInstallRoot() const return m_qbsConfiguration.contains(Constants::QBS_INSTALL_ROOT_KEY); } -Utils::FilePath QbsBuildStep::installRoot(VariableHandling variableHandling) const +FilePath QbsBuildStep::installRoot(VariableHandling variableHandling) const { const QString root = qbsConfiguration(variableHandling).value(Constants::QBS_INSTALL_ROOT_KEY).toString(); if (!root.isNull()) - return Utils::FilePath::fromUserInput(root); + return FilePath::fromUserInput(root); QString defaultInstallDir = QbsSettings::defaultInstallDirTemplate(); if (variableHandling == VariableHandling::ExpandVariables) defaultInstallDir = macroExpander()->expand(defaultInstallDir); @@ -396,110 +345,23 @@ Utils::FilePath QbsBuildStep::installRoot(VariableHandling variableHandling) con int QbsBuildStep::maxJobs() const { - if (m_maxJobCount->value() > 0) - return m_maxJobCount->value(); + if (maxJobCount() > 0) + return maxJobCount(); return QThread::idealThreadCount(); } -bool QbsBuildStep::fromMap(const QVariantMap &map) +void QbsBuildStep::fromMap(const Store &map) { - if (!ProjectExplorer::BuildStep::fromMap(map)) - return false; - - setQbsConfiguration(map.value(QBS_CONFIG).toMap()); - return true; -} - -QVariantMap QbsBuildStep::toMap() const -{ - QVariantMap map = ProjectExplorer::BuildStep::toMap(); - map.insert(QBS_CONFIG, m_qbsConfiguration); - return map; -} - -void QbsBuildStep::buildingDone(const ErrorInfo &error) -{ - m_session->disconnect(this); - m_session = nullptr; - m_lastWasSuccess = !error.hasError(); - for (const ErrorInfoItem &item : std::as_const(error.items)) { - createTaskAndOutput( - ProjectExplorer::Task::Error, - item.description, - item.filePath.toString(), - item.line); - } - - // Building can uncover additional target artifacts. - qbsBuildSystem()->updateAfterBuild(); - - // The reparsing, if it is necessary, has to be done before finished() is emitted, as - // otherwise a potential additional build step could conflict with the parsing step. - if (qbsBuildSystem()->parsingScheduled()) { - m_parsingAfterBuild = true; - parseProject(); - } else { - finish(); - } -} - -void QbsBuildStep::reparsingDone(bool success) -{ - disconnect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone); - m_parsingProject = false; - if (m_parsingAfterBuild) { - finish(); - } else if (!success) { - m_lastWasSuccess = false; - finish(); - } else { - build(); - } -} - -void QbsBuildStep::handleTaskStarted(const QString &desciption, int max) -{ - m_currentTask = desciption; - m_maxProgress = max; -} - -void QbsBuildStep::handleProgress(int value) -{ - if (m_maxProgress > 0) - emit progress(value * 100 / m_maxProgress, m_currentTask); -} - -void QbsBuildStep::handleCommandDescription(const QString &message) -{ - emit addOutput(message, OutputFormat::Stdout); -} - -void QbsBuildStep::handleProcessResult( - const FilePath &executable, - const QStringList &arguments, - const FilePath &workingDir, - const QStringList &stdOut, - const QStringList &stdErr, - bool success) -{ - Q_UNUSED(workingDir); - const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty(); - if (success && !hasOutput) + BuildStep::fromMap(map); + if (hasError()) return; - - emit addOutput(executable.toUserOutput() + ' ' + ProcessArgs::joinArgs(arguments), - OutputFormat::Stdout); - for (const QString &line : stdErr) - emit addOutput(line, OutputFormat::Stderr); - for (const QString &line : stdOut) - emit addOutput(line, OutputFormat::Stdout); + setQbsConfiguration(mapEntryFromStoreEntry(map.value(QBS_CONFIG)).toMap()); } -void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, - const QString &file, int line) +void QbsBuildStep::toMap(Store &map) const { - emit addOutput(message, OutputFormat::Stdout); - emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1); + ProjectExplorer::BuildStep::toMap(map); + map.insert(QBS_CONFIG, m_qbsConfiguration); } QString QbsBuildStep::buildVariant() const @@ -507,79 +369,74 @@ QString QbsBuildStep::buildVariant() const return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_VARIANT_KEY).toString(); } +QbsBuildConfiguration *QbsBuildStep::qbsBuildConfiguration() const +{ + return static_cast<QbsBuildConfiguration *>(buildConfiguration()); +} + QbsBuildSystem *QbsBuildStep::qbsBuildSystem() const { return static_cast<QbsBuildSystem *>(buildSystem()); } -void QbsBuildStep::setBuildVariant(const QString &variant) +Tasking::GroupItem QbsBuildStep::runRecipe() { - if (m_qbsConfiguration.value(Constants::QBS_CONFIG_VARIANT_KEY).toString() == variant) - return; - m_qbsConfiguration.insert(Constants::QBS_CONFIG_VARIANT_KEY, variant); - emit qbsConfigurationChanged(); - if (ProjectExplorer::BuildConfiguration *bc = buildConfiguration()) - emit bc->buildTypeChanged(); -} + using namespace Tasking; + const auto onPreParserSetup = [this](QbsRequest &request) { + request.setParseData(qbsBuildSystem()); + }; + const auto onBuildSetup = [this](QbsRequest &request) { + QbsSession *session = qbsBuildSystem()->session(); + if (!session) { + emit addOutput(Tr::tr("No qbs session exists for this target."), + OutputFormat::ErrorMessage); + return SetupResult::StopWithError; + } + QJsonObject requestData; + requestData.insert("type", "build-project"); + requestData.insert("max-job-count", maxJobs()); + requestData.insert("keep-going", keepGoing()); + requestData.insert("command-echo-mode", showCommandLines() ? "command-line" : "summary"); + requestData.insert("install", install()); + QbsSession::insertRequestedModuleProperties(requestData); + requestData.insert("clean-install-root", cleanInstallRoot()); + if (!m_products.isEmpty()) + requestData.insert("products", QJsonArray::fromStringList(m_products)); + if (!m_changedFiles.isEmpty()) { + const auto changedFilesArray = QJsonArray::fromStringList(m_changedFiles); + requestData.insert("changed-files", changedFilesArray); + requestData.insert("files-to-consider", changedFilesArray); + } + if (!m_activeFileTags.isEmpty()) + requestData.insert("active-file-tags", QJsonArray::fromStringList(m_activeFileTags)); + requestData.insert("data-mode", "only-if-changed"); -QString QbsBuildStep::profile() const -{ - return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_PROFILE_KEY).toString(); -} + request.setSession(session); + request.setRequestData(requestData); + connect(&request, &QbsRequest::progressChanged, this, &BuildStep::progress); + connect(&request, &QbsRequest::outputAdded, this, + [this](const QString &output, OutputFormat format) { + emit addOutput(output, format); + }); + connect(&request, &QbsRequest::taskAdded, this, [this](const Task &task) { + emit addTask(task, 1); + }); + return SetupResult::Continue; + }; -void QbsBuildStep::parseProject() -{ - m_parsingProject = true; - connect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone); - qbsBuildSystem()->parseCurrentBuildConfiguration(); -} + const Group root { + // We need a pre-build parsing step in order not to lose project file changes done + // right before building (but before the delay has elapsed). + QbsRequestTask(onPreParserSetup), + Group { + continueOnError, + QbsRequestTask(onBuildSetup), + // Building can uncover additional target artifacts. + Sync([this] { qbsBuildSystem()->updateAfterBuild(); }), + } + }; -void QbsBuildStep::build() -{ - m_session = qbsBuildSystem()->session(); - if (!m_session) { - emit addOutput(QbsProjectManager::Tr::tr("No qbs session exists for this target."), - OutputFormat::ErrorMessage); - emit finished(false); - return; - } - - QJsonObject request; - request.insert("type", "build-project"); - request.insert("max-job-count", maxJobs()); - request.insert("keep-going", keepGoing()); - request.insert("command-echo-mode", showCommandLines() ? "command-line" : "summary"); - request.insert("install", install()); - QbsSession::insertRequestedModuleProperties(request); - request.insert("clean-install-root", cleanInstallRoot()); - if (!m_products.isEmpty()) - request.insert("products", QJsonArray::fromStringList(m_products)); - if (!m_changedFiles.isEmpty()) { - const auto changedFilesArray = QJsonArray::fromStringList(m_changedFiles); - request.insert("changed-files", changedFilesArray); - request.insert("files-to-consider", changedFilesArray); - } - if (!m_activeFileTags.isEmpty()) - request.insert("active-file-tags", QJsonArray::fromStringList(m_activeFileTags)); - request.insert("data-mode", "only-if-changed"); - - m_session->sendRequest(request); - m_maxProgress = 0; - connect(m_session, &QbsSession::projectBuilt, this, &QbsBuildStep::buildingDone); - connect(m_session, &QbsSession::taskStarted, this, &QbsBuildStep::handleTaskStarted); - connect(m_session, &QbsSession::taskProgress, this, &QbsBuildStep::handleProgress); - connect(m_session, &QbsSession::commandDescription, - this, &QbsBuildStep::handleCommandDescription); - connect(m_session, &QbsSession::processResult, this, &QbsBuildStep::handleProcessResult); - connect(m_session, &QbsSession::errorOccurred, this, [this] { - buildingDone(ErrorInfo(QbsProjectManager::Tr::tr("Build canceled: Qbs session failed."))); - }); -} - -void QbsBuildStep::finish() -{ - m_session = nullptr; - emit finished(m_lastWasSuccess); + return root; } void QbsBuildStep::updateState() @@ -587,17 +444,6 @@ void QbsBuildStep::updateState() emit qbsConfigurationChanged(); } -void QbsBuildStep::setConfiguredArchitectures(const QStringList &architectures) -{ - if (configuredArchitectures() == architectures) - return; - if (architectures.isEmpty()) - m_qbsConfiguration.remove(Constants::QBS_ARCHITECTURES); - else - m_qbsConfiguration.insert(Constants::QBS_ARCHITECTURES, architectures.join(',')); - emit qbsConfigurationChanged(); -} - QStringList QbsBuildStep::configuredArchitectures() const { return m_qbsConfiguration[Constants::QBS_ARCHITECTURES].toString().split(',', @@ -609,27 +455,17 @@ QbsBuildStepData QbsBuildStep::stepData() const QbsBuildStepData data; data.command = "build"; data.dryRun = false; - data.keepGoing = m_keepGoing->value(); - data.forceProbeExecution = m_forceProbes->value(); - data.showCommandLines = m_showCommandLines->value(); - data.noInstall = !m_install->value(); + data.keepGoing = keepGoing(); + data.forceProbeExecution = forceProbes(); + data.showCommandLines = showCommandLines(); + data.noInstall = !install(); data.noBuild = false; - data.cleanInstallRoot = m_cleanInstallDir->value(); + data.cleanInstallRoot = cleanInstallRoot(); data.jobCount = maxJobs(); data.installRoot = installRoot(); return data; } -void QbsBuildStep::dropSession() -{ - if (m_session) { - doCancel(); - m_session->disconnect(this); - m_session = nullptr; - } -} - - // -------------------------------------------------------------------- // QbsBuildStepConfigWidget: // -------------------------------------------------------------------- @@ -639,10 +475,7 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) { connect(step, &ProjectConfiguration::displayNameChanged, this, &QbsBuildStepConfigWidget::updateState); - connect(static_cast<QbsBuildConfiguration *>(step->buildConfiguration()), - &QbsBuildConfiguration::qbsConfigurationChanged, - this, &QbsBuildStepConfigWidget::updateState); - connect(step, &QbsBuildStep::qbsBuildOptionsChanged, + connect(step->qbsBuildConfiguration(), &QbsBuildConfiguration::qbsConfigurationChanged, this, &QbsBuildStepConfigWidget::updateState); connect(&QbsSettings::instance(), &QbsSettings::settingsChanged, this, &QbsBuildStepConfigWidget::updateState); @@ -660,23 +493,23 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) using namespace Layouting; Form { - m_qbsStep->m_buildVariant, br, - m_qbsStep->m_selectedAbis, br, - m_qbsStep->m_maxJobCount, br, + step->buildVariantHolder, br, + step->selectedAbis, br, + step->maxJobCount, br, QbsProjectManager::Tr::tr("Properties:"), propertyEdit, br, QbsProjectManager::Tr::tr("Flags:"), - m_qbsStep->m_keepGoing, - m_qbsStep->m_showCommandLines, - m_qbsStep->m_forceProbes, br, + step->keepGoing, + step->showCommandLines, + step->forceProbes, br, QbsProjectManager::Tr::tr("Installation flags:"), - m_qbsStep->m_install, - m_qbsStep->m_cleanInstallDir, + step->install, + step->cleanInstallRoot, defaultInstallDirCheckBox, br, QbsProjectManager::Tr::tr("Installation directory:"), installDirChooser, br, - m_qbsStep->m_commandLine, br, + step->commandLine, br, noMargin, }.attachTo(this); @@ -694,7 +527,7 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step) connect(defaultInstallDirCheckBox, &QCheckBox::toggled, this, &QbsBuildStepConfigWidget::changeUseDefaultInstallDir); - connect(installDirChooser, &Utils::PathChooser::rawPathChanged, this, + connect(installDirChooser, &PathChooser::rawPathChanged, this, &QbsBuildStepConfigWidget::changeInstallDir); updateState(); @@ -706,10 +539,10 @@ void QbsBuildStepConfigWidget::updateState() updatePropertyEdit(m_qbsStep->qbsConfiguration(QbsBuildStep::PreserveVariables)); installDirChooser->setFilePath(m_qbsStep->installRoot(QbsBuildStep::PreserveVariables)); defaultInstallDirCheckBox->setChecked(!m_qbsStep->hasCustomInstallRoot()); - m_qbsStep->m_selectedAbis->setSelectedArchitectures(m_qbsStep->configuredArchitectures()); + m_qbsStep->selectedAbis.setSelectedArchitectures(m_qbsStep->configuredArchitectures()); } - const auto qbsBuildConfig = static_cast<QbsBuildConfiguration *>(m_qbsStep->buildConfiguration()); + const auto qbsBuildConfig = m_qbsStep->qbsBuildConfiguration(); QString command = qbsBuildConfig->equivalentCommandLine(m_qbsStep->stepData()); @@ -717,7 +550,7 @@ void QbsBuildStepConfigWidget::updateState() command += ' ' + m_propertyCache.at(i).name + ':' + m_propertyCache.at(i).effectiveValue; } - if (m_qbsStep->m_selectedAbis->isManagedByTarget()) { + if (m_qbsStep->selectedAbis.isManagedByTarget()) { QStringList selectedArchitectures = m_qbsStep->configuredArchitectures(); if (!selectedArchitectures.isEmpty()) { command += ' ' + QLatin1String(Constants::QBS_ARCHITECTURES) + ':' + @@ -742,7 +575,7 @@ void QbsBuildStepConfigWidget::updateState() addToCommand(qbsBuildConfig->qtQuickCompilerSetting(), Constants::QBS_CONFIG_QUICK_COMPILER_KEY); - m_qbsStep->m_commandLine->setValue(command); + m_qbsStep->commandLine.setValue(command); } @@ -759,7 +592,7 @@ void QbsBuildStepConfigWidget::updatePropertyEdit(const QVariantMap &data) editable.remove(Constants::QBS_CONFIG_QUICK_COMPILER_KEY); editable.remove(Constants::QBS_FORCE_PROBES_KEY); editable.remove(Constants::QBS_INSTALL_ROOT_KEY); - if (m_qbsStep->m_selectedAbis->isManagedByTarget()) + if (m_qbsStep->selectedAbis.isManagedByTarget()) editable.remove(Constants::QBS_ARCHITECTURES); QStringList propertyList; @@ -806,7 +639,7 @@ void QbsBuildStepConfigWidget::applyCachedProperties() Constants::QBS_CONFIG_QUICK_COMPILER_KEY, Constants::QBS_CONFIG_SEPARATE_DEBUG_INFO_KEY, Constants::QBS_INSTALL_ROOT_KEY}); - if (m_qbsStep->m_selectedAbis->isManagedByTarget()) + if (m_qbsStep->selectedAbis.isManagedByTarget()) additionalSpecialKeys << Constants::QBS_ARCHITECTURES; for (const QString &key : std::as_const(additionalSpecialKeys)) { const auto it = tmp.constFind(key); @@ -828,7 +661,7 @@ QbsBuildStep *QbsBuildStepConfigWidget::qbsStep() const return m_qbsStep; } -bool QbsBuildStepConfigWidget::validateProperties(Utils::FancyLineEdit *edit, QString *errorMessage) +bool QbsBuildStepConfigWidget::validateProperties(FancyLineEdit *edit, QString *errorMessage) { ProcessArgs::SplitError err; const QStringList argList = ProcessArgs::splitArgs(edit->text(), HostOsInfo::hostOs(), false, &err); @@ -849,7 +682,7 @@ bool QbsBuildStepConfigWidget::validateProperties(Utils::FancyLineEdit *edit, QS Constants::QBS_CONFIG_QUICK_DEBUG_KEY, Constants::QBS_CONFIG_QUICK_COMPILER_KEY, Constants::QBS_INSTALL_ROOT_KEY, Constants::QBS_CONFIG_SEPARATE_DEBUG_INFO_KEY, }; - if (m_qbsStep->m_selectedAbis->isManagedByTarget()) + if (m_qbsStep->selectedAbis.isManagedByTarget()) specialProperties << Constants::QBS_ARCHITECTURES; if (specialProperties.contains(propertyName)) { if (errorMessage) { @@ -888,7 +721,6 @@ QbsBuildStepFactory::QbsBuildStepFactory() setSupportedProjectType(Constants::PROJECT_ID); } -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal #include "qbsbuildstep.moc" diff --git a/src/plugins/qbsprojectmanager/qbsbuildstep.h b/src/plugins/qbsprojectmanager/qbsbuildstep.h index e15ceb79e83..c1b3b458830 100644 --- a/src/plugins/qbsprojectmanager/qbsbuildstep.h +++ b/src/plugins/qbsprojectmanager/qbsbuildstep.h @@ -3,20 +3,35 @@ #pragma once -#include "qbsbuildconfiguration.h" - #include <projectexplorer/buildstep.h> -#include <projectexplorer/task.h> -namespace QbsProjectManager { -namespace Internal { +namespace QbsProjectManager::Internal { -class ErrorInfo; -class QbsProject; -class QbsSession; - -class ArchitecturesAspect; +class QbsBuildConfiguration; class QbsBuildStepConfigWidget; +class QbsBuildStepData; +class QbsBuildSystem; + +class ArchitecturesAspect : public Utils::MultiSelectionAspect +{ + Q_OBJECT + +public: + ArchitecturesAspect(Utils::AspectContainer *container = nullptr); + + void setKit(const ProjectExplorer::Kit *kit) { m_kit = kit; } + void addToLayout(Layouting::LayoutItem &parent) override; + QStringList selectedArchitectures() const; + void setSelectedArchitectures(const QStringList& architectures); + bool isManagedByTarget() const { return m_isManagedByTarget; } + +private: + void setVisibleDynamic(bool visible); + + const ProjectExplorer::Kit *m_kit = nullptr; + QMap<QString, QString> m_abisToArchMap; + bool m_isManagedByTarget = false; +}; class QbsBuildStep final : public ProjectExplorer::BuildStep { @@ -30,91 +45,50 @@ public: }; QbsBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - ~QbsBuildStep() override; QVariantMap qbsConfiguration(VariableHandling variableHandling) const; void setQbsConfiguration(const QVariantMap &config); - bool keepGoing() const { return m_keepGoing->value(); } - bool showCommandLines() const { return m_showCommandLines->value(); } - bool install() const { return m_install->value(); } - bool cleanInstallRoot() const { return m_cleanInstallDir->value(); } - bool hasCustomInstallRoot() const; Utils::FilePath installRoot(VariableHandling variableHandling = ExpandVariables) const; - int maxJobs() const; QString buildVariant() const; - bool forceProbes() const { return m_forceProbes->value(); } - - QbsBuildSystem *qbsBuildSystem() const; - QbsBuildStepData stepData() const; - - void dropSession(); + Utils::SelectionAspect buildVariantHolder{this}; + ArchitecturesAspect selectedAbis{this}; + Utils::IntegerAspect maxJobCount{this}; + Utils::BoolAspect keepGoing{this}; + Utils::BoolAspect showCommandLines{this}; + Utils::BoolAspect install{this}; + Utils::BoolAspect cleanInstallRoot{this}; + Utils::BoolAspect forceProbes{this}; + Utils::StringAspect commandLine{this}; signals: void qbsConfigurationChanged(); - void qbsBuildOptionsChanged(); private: bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; - void doRun() override; - void doCancel() override; + Tasking::GroupItem runRecipe() final; QWidget *createConfigWidget() override; - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; - void buildingDone(const ErrorInfo &error); - void reparsingDone(bool success); - void handleTaskStarted(const QString &desciption, int max); - void handleProgress(int value); - void handleCommandDescription(const QString &message); - void handleProcessResult( - const Utils::FilePath &executable, - const QStringList &arguments, - const Utils::FilePath &workingDir, - const QStringList &stdOut, - const QStringList &stdErr, - bool success); - - void createTaskAndOutput(ProjectExplorer::Task::TaskType type, - const QString &message, const QString &file, int line); - - void setBuildVariant(const QString &variant); - void setConfiguredArchitectures(const QStringList &architectures); - QString profile() const; - - void parseProject(); - void build(); - void finish(); + QbsBuildConfiguration *qbsBuildConfiguration() const; + QbsBuildSystem *qbsBuildSystem() const; + QbsBuildStepData stepData() const; + bool hasCustomInstallRoot() const; + int maxJobs() const; void updateState(); QStringList configuredArchitectures() const; QVariantMap m_qbsConfiguration; - Utils::SelectionAspect *m_buildVariant = nullptr; - ArchitecturesAspect *m_selectedAbis = nullptr; - Utils::IntegerAspect *m_maxJobCount = nullptr; - Utils::BoolAspect *m_keepGoing = nullptr; - Utils::BoolAspect *m_showCommandLines = nullptr; - Utils::BoolAspect *m_install = nullptr; - Utils::BoolAspect *m_cleanInstallDir = nullptr; - Utils::BoolAspect *m_forceProbes = nullptr; - Utils::StringAspect *m_commandLine = nullptr; // Temporary data: QStringList m_changedFiles; QStringList m_activeFileTags; QStringList m_products; - QbsSession *m_session = nullptr; - - QString m_currentTask; - int m_maxProgress; - bool m_lastWasSuccess; - bool m_parsingProject = false; - bool m_parsingAfterBuild = false; - friend class QbsBuildStepConfigWidget; }; @@ -124,5 +98,4 @@ public: QbsBuildStepFactory(); }; -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbscleanstep.cpp b/src/plugins/qbsprojectmanager/qbscleanstep.cpp index b68b1400308..e3de616166e 100644 --- a/src/plugins/qbsprojectmanager/qbscleanstep.cpp +++ b/src/plugins/qbsprojectmanager/qbscleanstep.cpp @@ -4,79 +4,55 @@ #include "qbscleanstep.h" #include "qbsbuildconfiguration.h" -#include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" +#include "qbsrequest.h" #include "qbssession.h" -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/kit.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/buildmanager.h> -#include <projectexplorer/target.h> -#include <utils/qtcassert.h> #include <QJsonArray> #include <QJsonObject> using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; -namespace QbsProjectManager { -namespace Internal { +namespace QbsProjectManager::Internal { // -------------------------------------------------------------------- // QbsCleanStep: // -------------------------------------------------------------------- -QbsCleanStep::QbsCleanStep(BuildStepList *bsl, Utils::Id id) +QbsCleanStep::QbsCleanStep(BuildStepList *bsl, Id id) : BuildStep(bsl, id) { setDisplayName(Tr::tr("Qbs Clean")); - m_dryRunAspect = addAspect<BoolAspect>(); - m_dryRunAspect->setSettingsKey("Qbs.DryRun"); - m_dryRunAspect->setLabel(Tr::tr("Dry run:"), BoolAspect::LabelPlacement::InExtraLabel); + dryRun.setSettingsKey("Qbs.DryRun"); + dryRun.setLabel(Tr::tr("Dry run:"), BoolAspect::LabelPlacement::InExtraLabel); - m_keepGoingAspect = addAspect<BoolAspect>(); - m_keepGoingAspect->setSettingsKey("Qbs.DryKeepGoing"); - m_keepGoingAspect->setLabel(Tr::tr("Keep going:"), BoolAspect::LabelPlacement::InExtraLabel); + keepGoing.setSettingsKey("Qbs.DryKeepGoing"); + keepGoing.setLabel(Tr::tr("Keep going:"), BoolAspect::LabelPlacement::InExtraLabel); - auto effectiveCommandAspect = addAspect<StringAspect>(); - effectiveCommandAspect->setDisplayStyle(StringAspect::TextEditDisplay); - effectiveCommandAspect->setLabelText(Tr::tr("Equivalent command line:")); + effectiveCommand.setDisplayStyle(StringAspect::TextEditDisplay); + effectiveCommand.setLabelText(Tr::tr("Equivalent command line:")); - setSummaryUpdater([this, effectiveCommandAspect] { + setSummaryUpdater([this] { QbsBuildStepData data; data.command = "clean"; - data.dryRun = m_dryRunAspect->value(); - data.keepGoing = m_keepGoingAspect->value(); + data.dryRun = dryRun(); + data.keepGoing = keepGoing(); QString command = static_cast<QbsBuildConfiguration *>(buildConfiguration()) ->equivalentCommandLine(data); - effectiveCommandAspect->setValue(command); + effectiveCommand.setValue(command); return Tr::tr("<b>Qbs:</b> %1").arg("clean"); }); } -QbsCleanStep::~QbsCleanStep() -{ - doCancel(); - if (m_session) - m_session->disconnect(this); -} - -void QbsCleanStep::dropSession() -{ - if (m_session) { - doCancel(); - m_session->disconnect(this); - m_session = nullptr; - } -} - bool QbsCleanStep::init() { - if (buildSystem()->isParsing() || m_session) + if (buildSystem()->isParsing()) return false; const auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration()); if (!bc) @@ -85,63 +61,36 @@ bool QbsCleanStep::init() return true; } -void QbsCleanStep::doRun() +GroupItem QbsCleanStep::runRecipe() { - m_session = static_cast<QbsBuildSystem*>(buildSystem())->session(); - if (!m_session) { - emit addOutput(Tr::tr("No qbs session exists for this target."), OutputFormat::ErrorMessage); - emit finished(false); - return; - } + const auto onSetup = [this](QbsRequest &request) { + QbsSession *session = static_cast<QbsBuildSystem*>(buildSystem())->session(); + if (!session) { + emit addOutput(Tr::tr("No qbs session exists for this target."), + OutputFormat::ErrorMessage); + return SetupResult::StopWithError; + } + QJsonObject requestData; + requestData.insert("type", "clean-project"); + if (!m_products.isEmpty()) + requestData.insert("products", QJsonArray::fromStringList(m_products)); + requestData.insert("dry-run", dryRun()); + requestData.insert("keep-going", keepGoing()); - QJsonObject request; - request.insert("type", "clean-project"); - if (!m_products.isEmpty()) - request.insert("products", QJsonArray::fromStringList(m_products)); - request.insert("dry-run", m_dryRunAspect->value()); - request.insert("keep-going", m_keepGoingAspect->value()); - m_session->sendRequest(request); - m_maxProgress = 0; - connect(m_session, &QbsSession::projectCleaned, this, &QbsCleanStep::cleaningDone); - connect(m_session, &QbsSession::taskStarted, this, &QbsCleanStep::handleTaskStarted); - connect(m_session, &QbsSession::taskProgress, this, &QbsCleanStep::handleProgress); - connect(m_session, &QbsSession::errorOccurred, this, [this] { - cleaningDone(ErrorInfo(Tr::tr("Cleaning canceled: Qbs session failed."))); - }); -} + request.setSession(session); + request.setRequestData(requestData); + connect(&request, &QbsRequest::progressChanged, this, &BuildStep::progress); + connect(&request, &QbsRequest::outputAdded, this, + [this](const QString &output, OutputFormat format) { + emit addOutput(output, format); + }); + connect(&request, &QbsRequest::taskAdded, this, [this](const Task &task) { + emit addTask(task, 1); + }); + return SetupResult::Continue; + }; -void QbsCleanStep::doCancel() -{ - if (m_session) - m_session->cancelCurrentJob(); -} - -void QbsCleanStep::cleaningDone(const ErrorInfo &error) -{ - m_session->disconnect(this); - m_session = nullptr; - - for (const ErrorInfoItem &item : error.items) - createTaskAndOutput(Task::Error, item.description, item.filePath.toString(), item.line); - emit finished(!error.hasError()); -} - -void QbsCleanStep::handleTaskStarted(const QString &desciption, int max) -{ - Q_UNUSED(desciption) - m_maxProgress = max; -} - -void QbsCleanStep::handleProgress(int value) -{ - if (m_maxProgress > 0) - emit progress(value * 100 / m_maxProgress, m_description); -} - -void QbsCleanStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message, const QString &file, int line) -{ - emit addOutput(message, OutputFormat::Stdout); - emit addTask(CompileTask(type, message, Utils::FilePath::fromString(file), line), 1); + return QbsRequestTask(onSetup); } // -------------------------------------------------------------------- @@ -156,5 +105,4 @@ QbsCleanStepFactory::QbsCleanStepFactory() setDisplayName(Tr::tr("Qbs Clean")); } -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbscleanstep.h b/src/plugins/qbsprojectmanager/qbscleanstep.h index 921678c65a7..132287afd7c 100644 --- a/src/plugins/qbsprojectmanager/qbscleanstep.h +++ b/src/plugins/qbsprojectmanager/qbscleanstep.h @@ -3,18 +3,9 @@ #pragma once -#include "qbsbuildconfiguration.h" - #include <projectexplorer/buildstep.h> -#include <projectexplorer/task.h> -#include <utils/aspects.h> - -namespace QbsProjectManager { -namespace Internal { - -class ErrorInfo; -class QbsSession; +namespace QbsProjectManager::Internal { class QbsCleanStep final : public ProjectExplorer::BuildStep { @@ -22,32 +13,16 @@ class QbsCleanStep final : public ProjectExplorer::BuildStep public: QbsCleanStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - ~QbsCleanStep() override; - - QbsBuildStepData stepData() const; - - void dropSession(); private: bool init() override; - void doRun() override; - void doCancel() override; + Tasking::GroupItem runRecipe() final; - void cleaningDone(const ErrorInfo &error); - void handleTaskStarted(const QString &desciption, int max); - void handleProgress(int value); - - void createTaskAndOutput(ProjectExplorer::Task::TaskType type, - const QString &message, const QString &file, int line); - - Utils::BoolAspect *m_dryRunAspect = nullptr; - Utils::BoolAspect *m_keepGoingAspect = nullptr; + Utils::BoolAspect dryRun{this}; + Utils::BoolAspect keepGoing{this}; + Utils::StringAspect effectiveCommand{this}; QStringList m_products; - QbsSession *m_session = nullptr; - QString m_description; - int m_maxProgress; - bool m_showCompilerOutput = true; }; class QbsCleanStepFactory : public ProjectExplorer::BuildStepFactory @@ -56,5 +31,4 @@ public: QbsCleanStepFactory(); }; -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp index 87fe3ae0f9c..4cd0fed5fc5 100644 --- a/src/plugins/qbsprojectmanager/qbsinstallstep.cpp +++ b/src/plugins/qbsprojectmanager/qbsinstallstep.cpp @@ -8,12 +8,9 @@ #include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" +#include "qbsrequest.h" #include "qbssession.h" -#include <coreplugin/icore.h> -#include <projectexplorer/buildsteplist.h> -#include <projectexplorer/deployconfiguration.h> -#include <projectexplorer/kit.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -25,81 +22,68 @@ #include <QPlainTextEdit> using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; -namespace QbsProjectManager { -namespace Internal { - -// -------------------------------------------------------------------- -// Constants: -// -------------------------------------------------------------------- - -const char QBS_REMOVE_FIRST[] = "Qbs.RemoveFirst"; -const char QBS_DRY_RUN[] = "Qbs.DryRun"; -const char QBS_KEEP_GOING[] = "Qbs.DryKeepGoing"; +namespace QbsProjectManager::Internal { // -------------------------------------------------------------------- // QbsInstallStep: // -------------------------------------------------------------------- -QbsInstallStep::QbsInstallStep(BuildStepList *bsl, Utils::Id id) +QbsInstallStep::QbsInstallStep(BuildStepList *bsl, Id id) : BuildStep(bsl, id) { setDisplayName(Tr::tr("Qbs Install")); setSummaryText(Tr::tr("<b>Qbs:</b> %1").arg("install")); const auto labelPlacement = BoolAspect::LabelPlacement::AtCheckBox; - m_dryRun = addAspect<BoolAspect>(); - m_dryRun->setSettingsKey(QBS_DRY_RUN); - m_dryRun->setLabel(Tr::tr("Dry run"), labelPlacement); + dryRun.setSettingsKey("Qbs.DryRun"); + dryRun.setLabel(Tr::tr("Dry run"), labelPlacement); - m_keepGoing = addAspect<BoolAspect>(); - m_keepGoing->setSettingsKey(QBS_KEEP_GOING); - m_keepGoing->setLabel(Tr::tr("Keep going"), labelPlacement); + keepGoing.setSettingsKey("Qbs.DryKeepGoing"); + keepGoing.setLabel(Tr::tr("Keep going"), labelPlacement); - m_cleanInstallRoot = addAspect<BoolAspect>(); - m_cleanInstallRoot->setSettingsKey(QBS_REMOVE_FIRST); - m_cleanInstallRoot->setLabel(Tr::tr("Remove first"), labelPlacement); -} - -QbsInstallStep::~QbsInstallStep() -{ - doCancel(); - if (m_session) - m_session->disconnect(this); + cleanInstallRoot.setSettingsKey("Qbs.RemoveFirst"); + cleanInstallRoot.setLabel(Tr::tr("Remove first"), labelPlacement); } bool QbsInstallStep::init() { - QTC_ASSERT(!target()->buildSystem()->isParsing() && !m_session, return false); + QTC_ASSERT(!target()->buildSystem()->isParsing(), return false); return true; } -void QbsInstallStep::doRun() +GroupItem QbsInstallStep::runRecipe() { - m_session = static_cast<QbsBuildSystem *>(target()->buildSystem())->session(); + const auto onSetup = [this](QbsRequest &request) { + QbsSession *session = static_cast<QbsBuildSystem*>(buildSystem())->session(); + if (!session) { + emit addOutput(Tr::tr("No qbs session exists for this target."), + OutputFormat::ErrorMessage); + return SetupResult::StopWithError; + } + QJsonObject requestData; + requestData.insert("type", "install-project"); + requestData.insert("install-root", installRoot().path()); + requestData.insert("clean-install-root", cleanInstallRoot()); + requestData.insert("keep-going", keepGoing()); + requestData.insert("dry-run", dryRun()); - QJsonObject request; - request.insert("type", "install-project"); - request.insert("install-root", installRoot().path()); - request.insert("clean-install-root", m_cleanInstallRoot->value()); - request.insert("keep-going", m_keepGoing->value()); - request.insert("dry-run", m_dryRun->value()); - m_session->sendRequest(request); + request.setSession(session); + request.setRequestData(requestData); + connect(&request, &QbsRequest::progressChanged, this, &BuildStep::progress); + connect(&request, &QbsRequest::outputAdded, this, + [this](const QString &output, OutputFormat format) { + emit addOutput(output, format); + }); + connect(&request, &QbsRequest::taskAdded, this, [this](const Task &task) { + emit addTask(task, 1); + }); + return SetupResult::Continue; + }; - m_maxProgress = 0; - connect(m_session, &QbsSession::projectInstalled, this, &QbsInstallStep::installDone); - connect(m_session, &QbsSession::taskStarted, this, &QbsInstallStep::handleTaskStarted); - connect(m_session, &QbsSession::taskProgress, this, &QbsInstallStep::handleProgress); - connect(m_session, &QbsSession::errorOccurred, this, [this] { - installDone(ErrorInfo(Tr::tr("Installing canceled: Qbs session failed."))); - }); -} - -void QbsInstallStep::doCancel() -{ - if (m_session) - m_session->cancelCurrentJob(); + return QbsRequestTask(onSetup); } FilePath QbsInstallStep::installRoot() const @@ -113,51 +97,6 @@ const QbsBuildConfiguration *QbsInstallStep::buildConfig() const return static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration()); } -void QbsInstallStep::installDone(const ErrorInfo &error) -{ - m_session->disconnect(this); - m_session = nullptr; - - for (const ErrorInfoItem &item : error.items) - createTaskAndOutput(Task::Error, item.description, item.filePath, item.line); - - emit finished(!error.hasError()); -} - -void QbsInstallStep::handleTaskStarted(const QString &desciption, int max) -{ - m_description = desciption; - m_maxProgress = max; -} - -void QbsInstallStep::handleProgress(int value) -{ - if (m_maxProgress > 0) - emit progress(value * 100 / m_maxProgress, m_description); -} - -void QbsInstallStep::createTaskAndOutput(Task::TaskType type, const QString &message, - const Utils::FilePath &file, int line) -{ - emit addOutput(message, OutputFormat::Stdout); - emit addTask(CompileTask(type, message, file, line), 1); -} - -QbsBuildStepData QbsInstallStep::stepData() const -{ - QbsBuildStepData data; - data.command = "install"; - data.dryRun = m_dryRun->value(); - data.keepGoing = m_keepGoing->value(); - data.noBuild = true; - data.cleanInstallRoot = m_cleanInstallRoot->value(); - data.isInstallStep = true; - auto bs = static_cast<QbsBuildConfiguration *>(target()->activeBuildConfiguration())->qbsStep(); - if (bs) - data.installRoot = bs->installRoot(); - return data; -} - QWidget *QbsInstallStep::createConfigWidget() { auto widget = new QWidget; @@ -175,29 +114,32 @@ QWidget *QbsInstallStep::createConfigWidget() using namespace Layouting; Form { Tr::tr("Install root:"), installRootValueLabel, br, - Tr::tr("Flags:"), m_dryRun, m_keepGoing, m_cleanInstallRoot, br, + Tr::tr("Flags:"), dryRun, keepGoing, cleanInstallRoot, br, commandLineKeyLabel, commandLineTextEdit }.attachTo(widget); const auto updateState = [this, commandLineTextEdit, installRootValueLabel] { installRootValueLabel->setText(installRoot().toUserOutput()); - commandLineTextEdit->setPlainText(buildConfig()->equivalentCommandLine(stepData())); + QbsBuildStepData data; + data.command = "install"; + data.dryRun = dryRun(); + data.keepGoing = keepGoing(); + data.noBuild = true; + data.cleanInstallRoot = cleanInstallRoot(); + data.isInstallStep = true; + data.installRoot = installRoot(); + commandLineTextEdit->setPlainText(buildConfig()->equivalentCommandLine(data)); }; connect(target(), &Target::parsingFinished, this, updateState); + connect(buildConfig(), &QbsBuildConfiguration::qbsConfigurationChanged, this, updateState); connect(this, &ProjectConfiguration::displayNameChanged, this, updateState); - connect(m_dryRun, &BoolAspect::changed, this, updateState); - connect(m_keepGoing, &BoolAspect::changed, this, updateState); - connect(m_cleanInstallRoot, &BoolAspect::changed, this, updateState); - - const QbsBuildConfiguration * const bc = buildConfig(); - connect(bc, &QbsBuildConfiguration::qbsConfigurationChanged, this, updateState); - if (bc->qbsStep()) - connect(bc->qbsStep(), &QbsBuildStep::qbsBuildOptionsChanged, this, updateState); + connect(&dryRun, &BaseAspect::changed, this, updateState); + connect(&keepGoing, &BaseAspect::changed, this, updateState); + connect(&cleanInstallRoot, &BaseAspect::changed, this, updateState); updateState(); - return widget; } @@ -214,5 +156,4 @@ QbsInstallStepFactory::QbsInstallStepFactory() setDisplayName(Tr::tr("Qbs Install")); } -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbsinstallstep.h b/src/plugins/qbsprojectmanager/qbsinstallstep.h index e0063da8189..3a80a560778 100644 --- a/src/plugins/qbsprojectmanager/qbsinstallstep.h +++ b/src/plugins/qbsprojectmanager/qbsinstallstep.h @@ -3,19 +3,12 @@ #pragma once -#include "qbsbuildconfiguration.h" -#include "qbssession.h" - #include <projectexplorer/buildstep.h> -#include <projectexplorer/task.h> -#include <utils/aspects.h> +namespace QbsProjectManager::Internal { -namespace QbsProjectManager { -namespace Internal { - -class ErrorInfo; -class QbsSession; +class QbsBuildConfiguration; +class QbsBuildStepData; class QbsInstallStep final : public ProjectExplorer::BuildStep { @@ -23,32 +16,19 @@ class QbsInstallStep final : public ProjectExplorer::BuildStep public: QbsInstallStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); - ~QbsInstallStep() override; Utils::FilePath installRoot() const; - QbsBuildStepData stepData() const; private: bool init() override; - void doRun() override; - void doCancel() override; + Tasking::GroupItem runRecipe() final; QWidget *createConfigWidget() override; const QbsBuildConfiguration *buildConfig() const; - void installDone(const ErrorInfo &error); - void handleTaskStarted(const QString &desciption, int max); - void handleProgress(int value); - void createTaskAndOutput(ProjectExplorer::Task::TaskType type, - const QString &message, const Utils::FilePath &file, int line); - - Utils::BoolAspect *m_cleanInstallRoot = nullptr; - Utils::BoolAspect *m_dryRun = nullptr; - Utils::BoolAspect *m_keepGoing = nullptr; - - QbsSession *m_session = nullptr; - QString m_description; - int m_maxProgress; + Utils::BoolAspect cleanInstallRoot{this}; + Utils::BoolAspect dryRun{this}; + Utils::BoolAspect keepGoing{this}; friend class QbsInstallStepConfigWidget; }; @@ -59,5 +39,4 @@ public: QbsInstallStepFactory(); }; -} // namespace Internal -} // namespace QbsProjectManager +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbskitinformation.cpp b/src/plugins/qbsprojectmanager/qbskitaspect.cpp similarity index 64% rename from src/plugins/qbsprojectmanager/qbskitinformation.cpp rename to src/plugins/qbsprojectmanager/qbskitaspect.cpp index b9cb79231d9..647f44f02c7 100644 --- a/src/plugins/qbsprojectmanager/qbskitinformation.cpp +++ b/src/plugins/qbsprojectmanager/qbskitaspect.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "qbskitinformation.h" +#include "qbskitaspect.h" #include "customqbspropertiesdialog.h" #include "qbsprofilemanager.h" @@ -17,25 +17,24 @@ using namespace ProjectExplorer; -namespace QbsProjectManager { -namespace Internal { +namespace QbsProjectManager::Internal { -class AspectWidget final : public KitAspectWidget +class QbsKitAspectImpl final : public KitAspect { public: - AspectWidget(Kit *kit, const KitAspect *kitInfo) - : KitAspectWidget(kit, kitInfo), + QbsKitAspectImpl(Kit *kit, const KitAspectFactory *kitInfo) + : KitAspect(kit, kitInfo), m_contentLabel(createSubWidget<Utils::ElidingLabel>()), m_changeButton(createSubWidget<QPushButton>(Tr::tr("Change..."))) { - connect(m_changeButton, &QPushButton::clicked, this, &AspectWidget::changeProperties); + connect(m_changeButton, &QPushButton::clicked, this, &QbsKitAspectImpl::changeProperties); } private: void makeReadOnly() override { m_changeButton->setEnabled(false); } void refresh() override { m_contentLabel->setText(QbsKitAspect::representation(kit())); } - void addToLayout(Layouting::LayoutItem &parent) override + void addToLayoutImpl(Layouting::LayoutItem &parent) override { addMutableAction(m_contentLabel); parent.addItem(m_contentLabel); @@ -53,14 +52,6 @@ private: QPushButton * const m_changeButton; }; -QbsKitAspect::QbsKitAspect() -{ - setObjectName(QLatin1String("QbsKitAspect")); - setId(QbsKitAspect::id()); - setDisplayName(Tr::tr("Additional Qbs Profile Settings")); - setPriority(22000); -} - QString QbsKitAspect::representation(const Kit *kit) { const QVariantMap props = properties(kit); @@ -90,17 +81,32 @@ Utils::Id QbsKitAspect::id() return "Qbs.KitInformation"; } -Tasks QbsKitAspect::validate(const Kit *) const { return {}; } +// QbsKitAspectFactory -KitAspect::ItemList QbsKitAspect::toUserOutput(const Kit *k) const +class QbsKitAspectFactory final : public KitAspectFactory { - return {{displayName(), representation(k)}}; -} +public: + QbsKitAspectFactory() + { + setId(QbsKitAspect::id()); + setDisplayName(Tr::tr("Additional Qbs Profile Settings")); + setPriority(22000); + } -KitAspectWidget *QbsKitAspect::createConfigWidget(Kit *k) const -{ - return new AspectWidget(k, this); -} +private: + Tasks validate(const Kit *) const override { return {}; } -} // namespace Internal -} // namespace QbsProjectManager + ItemList toUserOutput(const Kit *k) const override + { + return {{displayName(), QbsKitAspect::representation(k)}}; + } + + KitAspect *createKitAspect(Kit *k) const override + { + return new QbsKitAspectImpl(k, this); + } +}; + +const QbsKitAspectFactory theQbsKitAspectFactory; + +} // QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbskitaspect.h b/src/plugins/qbsprojectmanager/qbskitaspect.h new file mode 100644 index 00000000000..2ee4c60f42e --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbskitaspect.h @@ -0,0 +1,20 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <projectexplorer/kitaspects.h> + +namespace QbsProjectManager::Internal { + +class QbsKitAspect final +{ +public: + static QString representation(const ProjectExplorer::Kit *kit); + static QVariantMap properties(const ProjectExplorer::Kit *kit); + static void setProperties(ProjectExplorer::Kit *kit, const QVariantMap &properties); + + static Utils::Id id(); +}; + +} // QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbskitinformation.h b/src/plugins/qbsprojectmanager/qbskitinformation.h deleted file mode 100644 index 95276df31eb..00000000000 --- a/src/plugins/qbsprojectmanager/qbskitinformation.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/kitinformation.h> - -namespace QbsProjectManager { -namespace Internal { - -class QbsKitAspect final : public ProjectExplorer::KitAspect -{ - Q_OBJECT - -public: - QbsKitAspect(); - - static QString representation(const ProjectExplorer::Kit *kit); - static QVariantMap properties(const ProjectExplorer::Kit *kit); - static void setProperties(ProjectExplorer::Kit *kit, const QVariantMap &properties); - -private: - static Utils::Id id(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *) const override; - ItemList toUserOutput(const ProjectExplorer::Kit *) const override; - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *) const override; -}; - -} // namespace Internal -} // namespace QbsProjectManager diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp index 5a0fb654a3e..ecab48bc4f9 100644 --- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp +++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp @@ -17,7 +17,7 @@ #include <projectexplorer/projectexplorer.h> #include <qmljstools/qmljstoolsconstants.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/process.h> #include <utils/qtcassert.h> @@ -89,21 +89,27 @@ QString toJSLiteral(const QVariant &val) return QString::fromLatin1("Unconvertible type %1").arg(QLatin1String(val.typeName())); } - -static QbsProfileManager *m_instance = nullptr; +static PropertyProvider &defaultPropertyProvider() +{ + static DefaultPropertyProvider theDefaultPropertyProvider; + return theDefaultPropertyProvider; +} static QString kitNameKeyInQbsSettings(const ProjectExplorer::Kit *kit) { return "preferences.qtcreator.kit." + kit->id().toString(); } -QbsProfileManager::QbsProfileManager() : m_defaultPropertyProvider(new DefaultPropertyProvider) +QbsProfileManager::QbsProfileManager() { - m_instance = this; - setObjectName(QLatin1String("QbsProjectManager")); - connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsLoaded, this, - [this] { m_kitsToBeSetupForQbs = ProjectExplorer::KitManager::kits(); } ); + + if (ProjectExplorer::KitManager::instance()->isLoaded()) { + m_kitsToBeSetupForQbs = ProjectExplorer::KitManager::kits(); + } else { + connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitsLoaded, + this, [this] { m_kitsToBeSetupForQbs = ProjectExplorer::KitManager::kits(); } ); + } connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitAdded, this, &QbsProfileManager::addProfileFromKit); connect(ProjectExplorer::KitManager::instance(), &ProjectExplorer::KitManager::kitUpdated, this, @@ -114,15 +120,12 @@ QbsProfileManager::QbsProfileManager() : m_defaultPropertyProvider(new DefaultPr this, &QbsProfileManager::updateAllProfiles); } -QbsProfileManager::~QbsProfileManager() -{ - delete m_defaultPropertyProvider; - m_instance = nullptr; -} +QbsProfileManager::~QbsProfileManager() = default; QbsProfileManager *QbsProfileManager::instance() { - return m_instance; + static QbsProfileManager theQbsProfileManager; + return &theQbsProfileManager; } QString QbsProfileManager::ensureProfileForKit(const ProjectExplorer::Kit *k) @@ -137,8 +140,8 @@ void QbsProfileManager::updateProfileIfNecessary(const ProjectExplorer::Kit *kit { // kit in list <=> profile update is necessary // Note that the const_cast is safe, as we do not call any non-const methods on the object. - if (m_instance->m_kitsToBeSetupForQbs.removeOne(const_cast<ProjectExplorer::Kit *>(kit))) - m_instance->addProfileFromKit(kit); + if (instance()->m_kitsToBeSetupForQbs.removeOne(const_cast<ProjectExplorer::Kit *>(kit))) + instance()->addProfileFromKit(kit); } void QbsProfileManager::updateAllProfiles() @@ -154,7 +157,7 @@ void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k) runQbsConfig(QbsConfigOp::Set, kitNameKeyInQbsSettings(k), name); // set up properties: - QVariantMap data = m_defaultPropertyProvider->properties(k, QVariantMap()); + QVariantMap data = defaultPropertyProvider().properties(k, QVariantMap()); for (PropertyProvider *provider : std::as_const(g_propertyProviders)) { if (provider->canHandle(k)) data = provider->properties(k, data); @@ -174,8 +177,8 @@ void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k) void QbsProfileManager::handleKitUpdate(ProjectExplorer::Kit *kit) { - m_kitsToBeSetupForQbs.removeOne(kit); - addProfileFromKit(kit); + if (!m_kitsToBeSetupForQbs.contains(kit)) + addProfileFromKit(kit); } void QbsProfileManager::handleKitRemoval(ProjectExplorer::Kit *kit) diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.h b/src/plugins/qbsprojectmanager/qbsprofilemanager.h index 3b6d98f697c..5e9dacea819 100644 --- a/src/plugins/qbsprojectmanager/qbsprofilemanager.h +++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.h @@ -12,7 +12,6 @@ namespace ProjectExplorer { class Kit; } namespace QbsProjectManager { namespace Internal { -class DefaultPropertyProvider; QString toJSLiteral(const QVariant &val); QVariant fromJSLiteral(const QString &str); @@ -43,7 +42,6 @@ private: void handleKitUpdate(ProjectExplorer::Kit *kit); void handleKitRemoval(ProjectExplorer::Kit *kit); - DefaultPropertyProvider *m_defaultPropertyProvider; QList<ProjectExplorer::Kit *> m_kitsToBeSetupForQbs; }; diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 3b59618310c..737f7fc17e9 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -13,6 +13,7 @@ #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" #include "qbsprojectparser.h" +#include "qbsrequest.h" #include "qbssession.h" #include "qbssettings.h" @@ -34,7 +35,7 @@ #include <projectexplorer/deploymentdata.h> #include <projectexplorer/headerpath.h> #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -48,7 +49,7 @@ #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljstools/qmljsmodelmanager.h> #include <qtsupport/qtcppkitinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QCoreApplication> #include <QElapsedTimer> @@ -56,7 +57,6 @@ #include <QJsonArray> #include <QMessageBox> #include <QSet> -#include <QTimer> #include <QVariantMap> #include <algorithm> @@ -199,6 +199,7 @@ QbsBuildSystem::QbsBuildSystem(QbsBuildConfiguration *bc) QbsBuildSystem::~QbsBuildSystem() { + m_parseRequest.reset(); delete m_cppCodeModelUpdater; delete m_qbsProjectParser; if (m_qbsUpdateFutureInterface) { @@ -424,21 +425,6 @@ QString QbsBuildSystem::profile() const return QbsProfileManager::ensureProfileForKit(target()->kit()); } -bool QbsBuildSystem::checkCancelStatus() -{ - const CancelStatus cancelStatus = m_cancelStatus; - m_cancelStatus = CancelStatusNone; - if (cancelStatus != CancelStatusCancelingForReparse) - return false; - qCDebug(qbsPmLog) << "Cancel request while parsing, starting re-parse"; - m_qbsProjectParser->deleteLater(); - m_qbsProjectParser = nullptr; - m_treeCreationWatcher = nullptr; - m_guard = {}; - parseCurrentBuildConfiguration(); - return true; -} - void QbsBuildSystem::updateAfterParse() { qCDebug(qbsPmLog) << "Updating data after parse"; @@ -456,11 +442,6 @@ void QbsBuildSystem::updateAfterParse() }); } -void QbsBuildSystem::delayedUpdateAfterParse() -{ - QTimer::singleShot(0, this, &QbsBuildSystem::updateAfterParse); -} - void QbsBuildSystem::updateProjectNodes(const std::function<void ()> &continuation) { m_treeCreationWatcher = new TreeCreationWatcher(this); @@ -512,9 +493,6 @@ void QbsBuildSystem::handleQbsParsingDone(bool success) qCDebug(qbsPmLog) << "Parsing done, success:" << success; - if (checkCancelStatus()) - return; - generateErrors(m_qbsProjectParser->error()); bool dataChanged = false; @@ -549,9 +527,9 @@ void QbsBuildSystem::handleQbsParsingDone(bool success) if (dataChanged) { updateAfterParse(); return; - } - else if (envChanged) + } else if (envChanged) { updateCppCodeModel(); + } if (success) m_guard.markAsSuccess(); m_guard = {}; @@ -570,14 +548,7 @@ void QbsBuildSystem::changeActiveTarget(Target *t) void QbsBuildSystem::triggerParsing() { - // Qbs does update the build graph during the build. So we cannot - // start to parse while a build is running or we will lose information. - if (BuildManager::isBuilding(project())) { - scheduleParsing(); - return; - } - - parseCurrentBuildConfiguration(); + scheduleParsing(); } void QbsBuildSystem::delayParsing() @@ -591,28 +562,19 @@ ExtraCompiler *QbsBuildSystem::findExtraCompiler(const ExtraCompilerFilter &filt return Utils::findOrDefault(m_extraCompilers, filter); } -void QbsBuildSystem::parseCurrentBuildConfiguration() +void QbsBuildSystem::scheduleParsing() { - m_parsingScheduled = false; - if (m_cancelStatus == CancelStatusCancelingForReparse) - return; + m_parseRequest.reset(new QbsRequest); + m_parseRequest->setParseData(this); + connect(m_parseRequest.get(), &QbsRequest::done, this, [this] { + m_parseRequest.release()->deleteLater(); + }); + m_parseRequest->start(); +} - // The CancelStatusCancelingAltoghether type can only be set by a build job, during - // which no other parse requests come through to this point (except by the build job itself, - // but of course not while canceling is in progress). - QTC_ASSERT(m_cancelStatus == CancelStatusNone, return); - - // New parse requests override old ones. - // NOTE: We need to wait for the current operation to finish, since otherwise there could - // be a conflict. Consider the case where the old qbs::ProjectSetupJob is writing - // to the build graph file when the cancel request comes in. If we don't wait for - // acknowledgment, it might still be doing that when the new one already reads from the - // same file. - if (m_qbsProjectParser) { - m_cancelStatus = CancelStatusCancelingForReparse; - m_qbsProjectParser->cancel(); - return; - } +void QbsBuildSystem::startParsing() +{ + QTC_ASSERT(!m_qbsProjectParser, return); QVariantMap config = m_buildConfiguration->qbsConfiguration(); if (!config.contains(Constants::QBS_INSTALL_ROOT_KEY)) { @@ -641,7 +603,6 @@ void QbsBuildSystem::parseCurrentBuildConfiguration() void QbsBuildSystem::cancelParsing() { QTC_ASSERT(m_qbsProjectParser, return); - m_cancelStatus = CancelStatusCancelingAltoghether; m_qbsProjectParser->cancel(); } diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index 048971db7ca..e3dcc6f3466 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -28,6 +28,7 @@ namespace Internal { class ErrorInfo; class QbsBuildConfiguration; class QbsProjectParser; +class QbsRequest; class QbsSession; class QbsProject : public ProjectExplorer::Project @@ -88,10 +89,7 @@ public: static ProjectExplorer::FileType fileTypeFor(const QSet<QString> &tags); QString profile() const; - void parseCurrentBuildConfiguration(); - void scheduleParsing() { m_parsingScheduled = true; } - bool parsingScheduled() const { return m_parsingScheduled; } - void cancelParsing(); + void scheduleParsing(); void updateAfterBuild(); QbsSession *session() const { return m_session; } @@ -103,6 +101,10 @@ public: private: friend class QbsProject; + friend class QbsRequestObject; + + void startParsing(); + void cancelParsing(); ProjectExplorer::ExtraCompiler *findExtraCompiler( const ExtraCompilerFilter &filter) const override; @@ -117,9 +119,7 @@ private: void updateApplicationTargets(); void updateDeploymentInfo(); void updateBuildTargetData(); - bool checkCancelStatus(); void updateAfterParse(); - void delayedUpdateAfterParse(); void updateProjectNodes(const std::function<void()> &continuation); Utils::FilePath installRoot(); @@ -134,13 +134,7 @@ private: using TreeCreationWatcher = QFutureWatcher<QbsProjectNode *>; TreeCreationWatcher *m_treeCreationWatcher = nullptr; Utils::Environment m_lastParseEnv; - bool m_parsingScheduled = false; - - enum CancelStatus { - CancelStatusNone, - CancelStatusCancelingForReparse, - CancelStatusCancelingAltoghether - } m_cancelStatus = CancelStatusNone; + std::unique_ptr<QbsRequest> m_parseRequest; CppEditor::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; diff --git a/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp b/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp index 33dc3013055..6fbae2dc4ab 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectimporter.cpp @@ -10,13 +10,14 @@ #include <coreplugin/documentmanager.h> #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildinfo.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/toolchain.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> +#include <utils/algorithm.h> #include <utils/filepath.h> #include <utils/hostosinfo.h> @@ -100,10 +101,8 @@ FilePaths QbsProjectImporter::importCandidates() for (Kit * const k : kits) { FilePath bdir = buildDir(projectFilePath(), k); const FilePath candidate = bdir.absolutePath(); - if (!seenCandidates.contains(candidate)) { - seenCandidates.insert(candidate); + if (Utils::insert(seenCandidates, candidate)) candidates << candidatesForDirectory(candidate); - } } qCDebug(qbsPmLog) << "build directory candidates:" << candidates; return candidates; diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs index 99c4345f452..dec42fd8902 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs +++ b/src/plugins/qbsprojectmanager/qbsprojectmanager.qbs @@ -16,7 +16,6 @@ QtcPlugin { Depends { name: "CppEditor" } Depends { name: "QtSupport" } Depends { name: "QmlJSTools" } - Depends { name: "app_version_header" } files: [ "customqbspropertiesdialog.h", @@ -32,8 +31,8 @@ QtcPlugin { "qbscleanstep.h", "qbsinstallstep.cpp", "qbsinstallstep.h", - "qbskitinformation.cpp", - "qbskitinformation.h", + "qbskitaspect.cpp", + "qbskitaspect.h", "qbsnodes.cpp", "qbsnodes.h", "qbsnodetreebuilder.cpp", @@ -55,6 +54,8 @@ QtcPlugin { "qbsprojectmanagerplugin.h", "qbsprojectparser.cpp", "qbsprojectparser.h", + "qbsrequest.cpp", + "qbsrequest.h", "qbssession.cpp", "qbssession.h", "qbssettings.cpp", diff --git a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp index 3245b609bef..fc0eb4abebb 100644 --- a/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp +++ b/src/plugins/qbsprojectmanager/qbsprojectmanagerplugin.cpp @@ -7,13 +7,13 @@ #include "qbsbuildstep.h" #include "qbscleanstep.h" #include "qbsinstallstep.h" -#include "qbskitinformation.h" #include "qbsnodes.h" #include "qbsprofilemanager.h" #include "qbsprofilessettingspage.h" #include "qbsproject.h" #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" +#include "qbssession.h" #include "qbssettings.h" #include <coreplugin/actionmanager/actioncontainer.h> @@ -66,14 +66,12 @@ static QbsProject *currentEditorProject() class QbsProjectManagerPluginPrivate { public: - QbsProfileManager manager; QbsBuildConfigurationFactory buildConfigFactory; QbsBuildStepFactory buildStepFactory; QbsCleanStepFactory cleanStepFactory; QbsInstallStepFactory installStepFactory; QbsSettingsPage settingsPage; QbsProfilesSettingsPage profilesSetttingsPage; - QbsKitAspect qbsKitAspect; }; QbsProjectManagerPlugin::~QbsProjectManagerPlugin() @@ -356,7 +354,7 @@ void QbsProjectManagerPlugin::buildFileContextMenu() { const Node *node = ProjectTree::currentNode(); QTC_ASSERT(node, return); - auto project = dynamic_cast<QbsProject *>(ProjectTree::currentProject()); + auto project = qobject_cast<QbsProject *>(ProjectTree::currentProject()); QTC_ASSERT(project, return); buildSingleFile(project, node->filePath().toString()); } @@ -391,7 +389,7 @@ void QbsProjectManagerPlugin::runStepsForProductContextMenu(const QList<Utils::I { const Node *node = ProjectTree::currentNode(); QTC_ASSERT(node, return); - auto project = dynamic_cast<QbsProject *>(ProjectTree::currentProject()); + auto project = qobject_cast<QbsProject *>(ProjectTree::currentProject()); QTC_ASSERT(project, return); const auto * const productNode = dynamic_cast<const QbsProductNode *>(node); @@ -454,7 +452,7 @@ void QbsProjectManagerPlugin::runStepsForSubprojectContextMenu(const QList<Utils { const Node *node = ProjectTree::currentNode(); QTC_ASSERT(node, return); - auto project = dynamic_cast<QbsProject *>(ProjectTree::currentProject()); + auto project = qobject_cast<QbsProject *>(ProjectTree::currentProject()); QTC_ASSERT(project, return); const auto subProject = dynamic_cast<const QbsProjectNode *>(node); @@ -532,12 +530,12 @@ void QbsProjectManagerPlugin::runStepsForProducts(QbsProject *project, void QbsProjectManagerPlugin::reparseSelectedProject() { - reparseProject(dynamic_cast<QbsProject *>(ProjectTree::currentProject())); + reparseProject(qobject_cast<QbsProject *>(ProjectTree::currentProject())); } void QbsProjectManagerPlugin::reparseCurrentProject() { - reparseProject(dynamic_cast<QbsProject *>(ProjectManager::startupProject())); + reparseProject(qobject_cast<QbsProject *>(ProjectManager::startupProject())); } void QbsProjectManagerPlugin::reparseProject(QbsProject *project) @@ -549,16 +547,8 @@ void QbsProjectManagerPlugin::reparseProject(QbsProject *project) if (!t) return; - QbsBuildSystem *bs = static_cast<QbsBuildSystem *>(t->buildSystem()); - if (!bs) - return; - - // Qbs does update the build graph during the build. So we cannot - // start to parse while a build is running or we will lose information. - if (BuildManager::isBuilding(project)) + if (auto bs = qobject_cast<QbsBuildSystem *>(t->buildSystem())) bs->scheduleParsing(); - else - bs->parseCurrentBuildConfiguration(); } void QbsProjectManagerPlugin::buildNamedProduct(QbsProject *project, const QString &product) diff --git a/src/plugins/qbsprojectmanager/qbsrequest.cpp b/src/plugins/qbsprojectmanager/qbsrequest.cpp new file mode 100644 index 00000000000..1355922b448 --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbsrequest.cpp @@ -0,0 +1,217 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "qbsrequest.h" + +#include "qbsproject.h" +#include "qbssession.h" + +#include <projectexplorer/target.h> +#include <projectexplorer/task.h> + +#include <utils/commandline.h> +#include <utils/qtcassert.h> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace QbsProjectManager::Internal { + +class QbsRequestManager : public QObject +{ +public: + void sendRequest(QbsRequestObject *requestObject); + void cancelRequest(QbsRequestObject *requestObject); +private: + void continueSessionQueue(QbsSession *session); + QHash<QObject *, QList<QbsRequestObject *>> m_queuedRequests; +}; + +class QbsRequestObject final : public QObject +{ + Q_OBJECT + +public: + void setSession(QbsSession *session) { m_session = session; } + QbsSession *session() const { return m_session; } + void setRequestData(const QJsonObject &requestData) { m_requestData = requestData; } + void setParseData(const QPointer<QbsBuildSystem> &buildSystem) { m_parseData = buildSystem; } + void start(); + void cancel(); + +signals: + void done(bool success); + void progressChanged(int progress, const QString &info); // progress in % + void outputAdded(const QString &output, ProjectExplorer::BuildStep::OutputFormat format); + void taskAdded(const ProjectExplorer::Task &task); + +private: + QbsSession *m_session = nullptr; + QJsonObject m_requestData; + QPointer<QbsBuildSystem> m_parseData; + QString m_description; + int m_maxProgress = 100; +}; + +void QbsRequestManager::sendRequest(QbsRequestObject *requestObject) +{ + QbsSession *session = requestObject->session(); + QList<QbsRequestObject *> &queue = m_queuedRequests[session]; + if (queue.isEmpty()) { + connect(session, &QObject::destroyed, this, [this, session] { + qDeleteAll(m_queuedRequests.value(session)); + m_queuedRequests.remove(session); + }); + } + queue.append(requestObject); + if (queue.size() == 1) + continueSessionQueue(session); +} + +void QbsRequestManager::cancelRequest(QbsRequestObject *requestObject) +{ + QbsSession *session = requestObject->session(); + QList<QbsRequestObject *> &queue = m_queuedRequests[session]; + const int index = queue.indexOf(requestObject); + QTC_ASSERT(index >= 0, return); + if (index > 0) { + delete queue.takeAt(index); + return; + } + requestObject->cancel(); +} + +void QbsRequestManager::continueSessionQueue(QbsSession *session) +{ + const QList<QbsRequestObject *> &queue = m_queuedRequests[session]; + if (queue.isEmpty()) { + m_queuedRequests.remove(session); + disconnect(session, &QObject::destroyed, this, nullptr); + return; + } + QbsRequestObject *requestObject = queue.first(); + connect(requestObject, &QbsRequestObject::done, this, [this, requestObject] { + disconnect(requestObject, &QbsRequestObject::done, this, nullptr); + QbsSession *session = requestObject->session(); + requestObject->deleteLater(); + QList<QbsRequestObject *> &queue = m_queuedRequests[session]; + QTC_ASSERT(!queue.isEmpty(), return); + QTC_CHECK(queue.first() == requestObject); + queue.removeFirst(); + continueSessionQueue(session); + }); + requestObject->start(); +} + +static QbsRequestManager &manager() +{ + static QbsRequestManager theManager; + return theManager; +} + +void QbsRequestObject::start() +{ + if (m_parseData) { + connect(m_parseData->target(), &Target::parsingFinished, this, [this](bool success) { + disconnect(m_parseData->target(), &Target::parsingFinished, this, nullptr); + emit done(success); + }); + QMetaObject::invokeMethod(m_parseData.get(), &QbsBuildSystem::startParsing, + Qt::QueuedConnection); + return; + } + + const auto handleDone = [this](const ErrorInfo &error) { + m_session->disconnect(this); + for (const ErrorInfoItem &item : error.items) { + emit outputAdded(item.description, BuildStep::OutputFormat::Stdout); + emit taskAdded(CompileTask(Task::Error, item.description, item.filePath, item.line)); + } + emit done(error.items.isEmpty()); + }; + connect(m_session, &QbsSession::projectBuilt, this, handleDone); + connect(m_session, &QbsSession::projectCleaned, this, handleDone); + connect(m_session, &QbsSession::projectInstalled, this, handleDone); + connect(m_session, &QbsSession::errorOccurred, this, [handleDone](QbsSession::Error error) { + handleDone(ErrorInfo(QbsSession::errorString(error))); + }); + connect(m_session, &QbsSession::taskStarted, this, [this](const QString &desciption, int max) { + m_description = desciption; + m_maxProgress = max; + }); + connect(m_session, &QbsSession::maxProgressChanged, this, [this](int max) { + m_maxProgress = max; + }); + connect(m_session, &QbsSession::taskProgress, this, [this](int progress) { + if (m_maxProgress > 0) + emit progressChanged(progress * 100 / m_maxProgress, m_description); + }); + connect(m_session, &QbsSession::commandDescription, this, [this](const QString &message) { + emit outputAdded(message, BuildStep::OutputFormat::Stdout); + }); + connect(m_session, &QbsSession::processResult, this, [this](const FilePath &executable, + const QStringList &arguments, + const FilePath &workingDir, + const QStringList &stdOut, + const QStringList &stdErr, + bool success) { + Q_UNUSED(workingDir); + const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty(); + if (success && !hasOutput) + return; + emit outputAdded(executable.toUserOutput() + ' ' + ProcessArgs::joinArgs(arguments), + BuildStep::OutputFormat::Stdout); + for (const QString &line : stdErr) + emit outputAdded(line, BuildStep::OutputFormat::Stderr); + for (const QString &line : stdOut) + emit outputAdded(line, BuildStep::OutputFormat::Stdout); + }); + m_session->sendRequest(m_requestData); +} + +void QbsRequestObject::cancel() +{ + if (m_parseData) + m_parseData->cancelParsing(); + else + m_session->cancelCurrentJob(); +} + +QbsRequest::~QbsRequest() +{ + if (!m_requestObject) + return; + disconnect(m_requestObject, nullptr, this, nullptr); + manager().cancelRequest(m_requestObject); +} + +void QbsRequest::start() +{ + QTC_ASSERT(!m_requestObject, return); + QTC_ASSERT(m_parseData || (m_session && m_requestData), emit done(false); return); + + m_requestObject = new QbsRequestObject; + m_requestObject->setSession(m_session); + if (m_requestData) + m_requestObject->setRequestData(*m_requestData); + if (m_parseData) { + m_requestObject->setSession(m_parseData->session()); + m_requestObject->setParseData(m_parseData); + } + + connect(m_requestObject, &QbsRequestObject::done, this, [this](bool success) { + m_requestObject->deleteLater(); + m_requestObject = nullptr; + emit done(success); + }); + connect(m_requestObject, &QbsRequestObject::progressChanged, + this, &QbsRequest::progressChanged); + connect(m_requestObject, &QbsRequestObject::outputAdded, this, &QbsRequest::outputAdded); + connect(m_requestObject, &QbsRequestObject::taskAdded, this, &QbsRequest::taskAdded); + + manager().sendRequest(m_requestObject); +} + +} // namespace QbsProjectManager::Internal + +#include "qbsrequest.moc" diff --git a/src/plugins/qbsprojectmanager/qbsrequest.h b/src/plugins/qbsprojectmanager/qbsrequest.h new file mode 100644 index 00000000000..e86909504b6 --- /dev/null +++ b/src/plugins/qbsprojectmanager/qbsrequest.h @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <projectexplorer/buildstep.h> + +#include <solutions/tasking/tasktree.h> + +#include <QJsonObject> + +namespace QbsProjectManager::Internal { + +class QbsBuildSystem; +class QbsRequestObject; +class QbsSession; + +class QbsRequest final : public QObject +{ + Q_OBJECT + +public: + ~QbsRequest() override; + + void setSession(QbsSession *session) { m_session = session; } + void setRequestData(const QJsonObject &requestData) { m_requestData = requestData; } + void setParseData(const QPointer<QbsBuildSystem> &buildSystem) { m_parseData = buildSystem; } + void start(); + +signals: + void done(bool success); + void progressChanged(int progress, const QString &info); // progress in % + void outputAdded(const QString &output, ProjectExplorer::BuildStep::OutputFormat format); + void taskAdded(const ProjectExplorer::Task &task); + +private: + QbsSession *m_session = nullptr; // TODO: Should we keep a QPointer? + std::optional<QJsonObject> m_requestData; + QPointer<QbsBuildSystem> m_parseData; + QbsRequestObject *m_requestObject = nullptr; +}; + +class QbsRequestTaskAdapter : public Tasking::TaskAdapter<QbsRequest> +{ +public: + QbsRequestTaskAdapter() { connect(task(), &QbsRequest::done, this, &TaskInterface::done); } + +private: + void start() final { task()->start(); } +}; + +using QbsRequestTask = Tasking::CustomTask<QbsRequestTaskAdapter>; + +} // namespace QbsProjectManager::Internal diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp index d2e5d130f0e..49d8b31b56f 100644 --- a/src/plugins/qbsprojectmanager/qbssession.cpp +++ b/src/plugins/qbsprojectmanager/qbssession.cpp @@ -8,7 +8,6 @@ #include "qbsprojectmanagertr.h" #include "qbssettings.h" -#include <app/app_version.h> #include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/taskhub.h> @@ -19,6 +18,7 @@ #include <QDir> #include <QEventLoop> +#include <QGuiApplication> #include <QJsonArray> #include <QJsonDocument> #include <QProcessEnvironment> @@ -243,7 +243,7 @@ QString QbsSession::errorString(QbsSession::Error error) //: %1 == "Qt Creator" or "Qt Design Studio" return Tr::tr("The qbs API level is not compatible with " "what %1 expects.") - .arg(Core::Constants::IDE_DISPLAY_NAME); + .arg(QGuiApplication::applicationDisplayName()); } return QString(); // For dumb compilers. } diff --git a/src/plugins/qbsprojectmanager/qbssettings.cpp b/src/plugins/qbsprojectmanager/qbssettings.cpp index 409ebcd88b0..317bbf92c9c 100644 --- a/src/plugins/qbsprojectmanager/qbssettings.cpp +++ b/src/plugins/qbsprojectmanager/qbssettings.cpp @@ -6,7 +6,6 @@ #include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagertr.h" -#include <app/app_version.h> #include <coreplugin/icore.h> #include <projectexplorer/projectexplorerconstants.h> #include <utils/environment.h> @@ -15,9 +14,9 @@ #include <utils/process.h> #include <utils/qtcsettings.h> -#include <QCoreApplication> #include <QCheckBox> #include <QFormLayout> +#include <QGuiApplication> #include <QLabel> #include <QPushButton> @@ -132,7 +131,7 @@ QbsSettings::QbsSettings() void QbsSettings::loadSettings() { - QSettings * const s = Core::ICore::settings(); + QtcSettings * const s = Core::ICore::settings(); m_settings.qbsExecutableFilePath = FilePath::fromString(s->value(QBS_EXE_KEY).toString()); m_settings.defaultInstallDirTemplate = s->value( QBS_DEFAULT_INSTALL_DIR_KEY, @@ -142,9 +141,9 @@ void QbsSettings::loadSettings() void QbsSettings::storeSettings() const { - QSettings * const s = Core::ICore::settings(); - QtcSettings::setValueWithDefault(s, QBS_EXE_KEY, m_settings.qbsExecutableFilePath.toString(), - defaultQbsExecutableFilePath().toString()); + QtcSettings * const s = Core::ICore::settings(); + s->setValueWithDefault(QBS_EXE_KEY, m_settings.qbsExecutableFilePath.toString(), + defaultQbsExecutableFilePath().toString()); s->setValue(QBS_DEFAULT_INSTALL_DIR_KEY, m_settings.defaultInstallDirTemplate); s->setValue(USE_CREATOR_SETTINGS_KEY, m_settings.useCreatorSettings); } @@ -161,7 +160,7 @@ public: m_versionLabel.setText(getQbsVersionString()); //: %1 == "Qt Creator" or "Qt Design Studio" m_settingsDirCheckBox.setText(Tr::tr("Use %1 settings directory for Qbs") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + .arg(QGuiApplication::applicationDisplayName())); m_settingsDirCheckBox.setChecked(QbsSettings::useCreatorSettingsDirForQbs()); const auto layout = new QFormLayout(this); @@ -173,10 +172,10 @@ public: layout->addRow(Tr::tr("Default installation directory:"), &m_defaultInstallDirLineEdit); layout->addRow(Tr::tr("Qbs version:"), &m_versionLabel); - connect(&m_qbsExePathChooser, &PathChooser::textChanged, [this] { + connect(&m_qbsExePathChooser, &PathChooser::textChanged, this, [this] { m_versionLabel.setText(getQbsVersionString()); }); - connect(&m_resetQbsExeButton, &QPushButton::clicked, [this] { + connect(&m_resetQbsExeButton, &QPushButton::clicked, this, [this] { m_qbsExePathChooser.setFilePath(QbsSettings::defaultQbsExecutableFilePath()); }); } diff --git a/src/plugins/qmakeprojectmanager/CMakeLists.txt b/src/plugins/qmakeprojectmanager/CMakeLists.txt index 83548526cd1..855aafbfc74 100644 --- a/src/plugins/qmakeprojectmanager/CMakeLists.txt +++ b/src/plugins/qmakeprojectmanager/CMakeLists.txt @@ -20,7 +20,7 @@ add_qtc_plugin(QmakeProjectManager profilehoverhandler.cpp profilehoverhandler.h qmakebuildconfiguration.cpp qmakebuildconfiguration.h qmakebuildinfo.h - qmakekitinformation.cpp qmakekitinformation.h + qmakekitaspect.cpp qmakekitaspect.h qmakemakestep.cpp qmakemakestep.h qmakenodes.cpp qmakenodes.h qmakenodetreebuilder.cpp qmakenodetreebuilder.h diff --git a/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in b/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in index db4ebc7edc3..a080d69dece 100644 --- a/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in +++ b/src/plugins/qmakeprojectmanager/QmakeProjectManager.json.in @@ -1,55 +1,55 @@ { - \"Name\" : \"QmakeProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmakeProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Provides project type for Qt/QMake .pro files and tools.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Build Systems", + "Description" : "Provides project type for Qt/QMake .pro files and tools.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/vnd.qt.qmakeprofile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project file</comment>\", - \" <glob pattern=\'*.pro\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/vnd.qt.qmakeproincludefile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project include file</comment>\", - \" <glob pattern=\'*.pri\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/vnd.qt.qmakeprofeaturefile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project feature file</comment>\", - \" <glob pattern=\'*.prf\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/vnd.qt.qmakeproconfigurationfile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project configuration file</comment>\", - \" <glob pattern=\'.qmake.conf\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/vnd.qt.qmakeprocachefile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project cache file</comment>\", - \" <glob pattern=\'.qmake.cache\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/vnd.qt.qmakeprostashfile\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Qt Project stash file</comment>\", - \" <glob pattern=\'.qmake.stash\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/vnd.qt.qmakeprofile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project file</comment>", + " <glob pattern='*.pro'/>", + " </mime-type>", + " <mime-type type='application/vnd.qt.qmakeproincludefile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project include file</comment>", + " <glob pattern='*.pri'/>", + " </mime-type>", + " <mime-type type='application/vnd.qt.qmakeprofeaturefile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project feature file</comment>", + " <glob pattern='*.prf'/>", + " </mime-type>", + " <mime-type type='application/vnd.qt.qmakeproconfigurationfile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project configuration file</comment>", + " <glob pattern='.qmake.conf'/>", + " </mime-type>", + " <mime-type type='application/vnd.qt.qmakeprocachefile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project cache file</comment>", + " <glob pattern='.qmake.cache'/>", + " </mime-type>", + " <mime-type type='application/vnd.qt.qmakeprostashfile'>", + " <sub-class-of type='text/plain'/>", + " <comment>Qt Project stash file</comment>", + " <glob pattern='.qmake.stash'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp index ff74701cf71..c6500df0cfd 100644 --- a/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp +++ b/src/plugins/qmakeprojectmanager/addlibrarywizard.cpp @@ -40,13 +40,12 @@ static QStringList qt_clean_filter_list(const QString &filter) return f.split(QLatin1Char(' '), Qt::SkipEmptyParts); } -static bool validateLibraryPath(const FilePath &filePath, - const PathChooser *pathChooser, - QString *errorMessage) +static FancyLineEdit::AsyncValidationResult validateLibraryPath(const QString &input, + const QString &promptDialogFilter) { - Q_UNUSED(errorMessage) + const FilePath filePath = FilePath::fromUserInput(input); if (!filePath.exists()) - return false; + return make_unexpected(::QmakeProjectManager::Tr::tr("File does not exist.")); const QString fileName = filePath.fileName(); @@ -55,14 +54,14 @@ static bool validateLibraryPath(const FilePath &filePath, ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption; - const QStringList filters = qt_clean_filter_list(pathChooser->promptDialogFilter()); + const QStringList filters = qt_clean_filter_list(promptDialogFilter); for (const QString &filter : filters) { QString pattern = QRegularExpression::wildcardToRegularExpression(filter); QRegularExpression regExp(pattern, option); if (regExp.match(fileName).hasMatch()) - return true; - } - return false; + return input; + } + return make_unexpected(::QmakeProjectManager::Tr::tr("File does not match filter.")); } AddLibraryWizard::AddLibraryWizard(const FilePath &proFile, QWidget *parent) @@ -182,10 +181,15 @@ DetailsPage::DetailsPage(AddLibraryWizard *parent) PathChooser * const libPathChooser = m_libraryDetailsWidget->libraryPathChooser; libPathChooser->setHistoryCompleter("Qmake.LibDir.History"); - const auto pathValidator = [libPathChooser](FancyLineEdit *edit, QString *errorMessage) { - return libPathChooser->defaultValidationFunction()(edit, errorMessage) - && validateLibraryPath(libPathChooser->filePath(), - libPathChooser, errorMessage); + const auto pathValidator = + [libPathChooser](const QString &text) -> FancyLineEdit::AsyncValidationFuture { + return libPathChooser->defaultValidationFunction()(text).then( + [pDialogFilter = libPathChooser->promptDialogFilter()]( + const FancyLineEdit::AsyncValidationResult &result) { + if (!result) + return result; + return validateLibraryPath(result.value(), pDialogFilter); + }); }; libPathChooser->setValidationFunction(pathValidator); setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Details")); diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp index 22958e08197..d3c543ab9d7 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/classdefinition.cpp @@ -43,7 +43,7 @@ ClassDefinition::ClassDefinition(QWidget *parent) : m_pluginSourceEdit = new QLineEdit; m_iconPathChooser = new Utils::PathChooser; m_iconPathChooser->setExpectedKind(Utils::PathChooser::File); - m_iconPathChooser->setHistoryCompleter(QLatin1String("Qmake.Icon.History")); + m_iconPathChooser->setHistoryCompleter("Qmake.Icon.History"); m_iconPathChooser->setPromptDialogTitle(Tr::tr("Select Icon")); m_iconPathChooser->setPromptDialogFilter(Tr::tr("Icon files (*.png *.ico *.jpg *.xpm *.tif *.svg)")); Form { diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/classlist.h b/src/plugins/qmakeprojectmanager/customwidgetwizard/classlist.h index 2cbab6c50d4..723d06f7089 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/classlist.h +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/classlist.h @@ -5,8 +5,6 @@ #include <QListView> -QT_FORWARD_DECLARE_CLASS(QModelIndex) - namespace QmakeProjectManager { namespace Internal { class ClassModel; diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp index 2b9df3ee590..62bc9628f42 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/customwidgetwizarddialog.cpp @@ -9,7 +9,7 @@ #include <projectexplorer/projectexplorerconstants.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> namespace QmakeProjectManager { diff --git a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp index 28e67ec3ece..0a36120a64b 100644 --- a/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp +++ b/src/plugins/qmakeprojectmanager/customwidgetwizard/plugingenerator.cpp @@ -7,7 +7,7 @@ #include <coreplugin/generatedfile.h> #include <cppeditor/abstracteditorsupport.h> - +#include <projectexplorer/projecttree.h> #include <utils/algorithm.h> #include <utils/fileutils.h> #include <utils/macroexpander.h> @@ -55,8 +55,8 @@ static QString qt5PluginMetaData(const QString &interfaceName) + interfaceName + QLatin1String("\")"); } -QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationParameters& p, const PluginOptions &options, - QString *errorMessage) +QList<Core::GeneratedFile> PluginGenerator::generatePlugin( + const GenerationParameters& p, const PluginOptions &options, QString *errorMessage) { const QChar slash = QLatin1Char('/'); const QChar blank = QLatin1Char(' '); @@ -82,6 +82,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara // First create the widget wrappers (plugins) and - if requested - skeletons // for the widgets. const int widgetCount = options.widgetOptions.size(); + ProjectExplorer::Project *project = ProjectExplorer::ProjectTree::currentProject(); for (int i = 0; i < widgetCount; i++) { const PluginOptions::WidgetOptions &wo = options.widgetOptions.at(i); sm.clear(); @@ -92,10 +93,11 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara qt5PluginMetaData(QLatin1String("QDesignerCustomWidgetInterface")) : QString()); const QString pluginHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.h"), sm, errorMessage); if (pluginHeaderContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile pluginHeader(baseDir / wo.pluginHeaderFile); pluginHeader.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( - FilePath::fromString(wo.pluginHeaderFile), wo.pluginClassName) + project, FilePath::fromString(wo.pluginHeaderFile), + wo.pluginClassName) + pluginHeaderContents); rc.push_back(pluginHeader); @@ -119,9 +121,10 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara const QString pluginSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_single.cpp"), sm, errorMessage); if (pluginSourceContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile pluginSource(baseDir / wo.pluginSourceFile); pluginSource.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(wo.pluginSourceFile), wo.pluginClassName) + pluginSourceContents); if (i == 0 && widgetCount == 1) // Open first widget unless collection @@ -154,7 +157,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara if (pc.library != wo.widgetLibrary) { *errorMessage = Tr::tr("Creating multiple widget libraries (%1, %2) in one project (%3) is not supported.") .arg(pc.library, wo.widgetLibrary, wo.widgetProjectFile); - return QList<Core::GeneratedFile>(); + return {}; } } pc.headers += blank + wo.widgetHeaderFile; @@ -166,9 +169,10 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("WIDGET_CLASS"), wo.widgetClassName); const QString widgetHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.h"), sm, errorMessage); if (widgetHeaderContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile widgetHeader(baseDir / wo.widgetHeaderFile); widgetHeader.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(wo.widgetHeaderFile), wo.widgetClassName) + widgetHeaderContents); @@ -178,9 +182,10 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("WIDGET_HEADER"), wo.widgetHeaderFile); const QString widgetSourceContents = processTemplate(p.templatePath + QLatin1String("/tpl_widget.cpp"), sm, errorMessage); if (widgetSourceContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile widgetSource(baseDir / wo.widgetSourceFile); widgetSource.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(wo.widgetSourceFile), wo.widgetClassName) + widgetSourceContents); @@ -201,7 +206,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("WIDGET_LIBRARY"), pc.library); const QString widgetPriContents = processTemplate(pc.tmpl, sm, errorMessage); if (widgetPriContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile widgetPri(baseDir / it.key()); widgetPri.setContents(widgetPriContents); rc.push_back(widgetPri); @@ -215,9 +220,10 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("COLLECTION_PLUGIN_METADATA"), qt5PluginMetaData(QLatin1String("QDesignerCustomWidgetCollectionInterface"))); const QString collectionHeaderContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.h"), sm, errorMessage); if (collectionHeaderContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile collectionHeader(baseDir / options.collectionHeaderFile); collectionHeader.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(options.collectionHeaderFile), options.collectionClassName) + collectionHeaderContents); @@ -232,9 +238,10 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("PLUGIN_ADDITIONS"), pluginAdditions); const QString collectionSourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_collection.cpp"), sm, errorMessage); if (collectionSourceFileContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile collectionSource(baseDir / options.collectionSourceFile); collectionSource.setContents(CppEditor::AbstractEditorSupport::licenseTemplate( + project, FilePath::fromString(options.collectionSourceFile), options.collectionClassName) + collectionSourceFileContents); @@ -256,7 +263,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara newIcon, errorMessage); if (iconFile.filePath().isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; rc.push_back(iconFile); icon = qfi.fileName(); } @@ -267,7 +274,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("ICON_FILES"), iconFiles); const QString resourceFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_resources.qrc"), sm, errorMessage); if (resourceFileContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile resourceFile(baseDir / options.resourceFile); resourceFile.setContents(resourceFileContents); rc.push_back(resourceFile); @@ -282,7 +289,7 @@ QList<Core::GeneratedFile> PluginGenerator::generatePlugin(const GenerationPara sm.insert(QLatin1String("INCLUSIONS"), QStringList(Utils::toList(widgetProjects)).join(QLatin1Char('\n'))); const QString proFileContents = processTemplate(p.templatePath + QLatin1String("/tpl_plugin.pro"), sm, errorMessage); if (proFileContents.isEmpty()) - return QList<Core::GeneratedFile>(); + return {}; Core::GeneratedFile proFile(baseDir.pathAppended(p.fileName + ".pro")); proFile.setContents(proFileContents); proFile.setAttributes(Core::GeneratedFile::OpenProjectAttribute); diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp index 990fd0ff417..b5a6d82a1f9 100644 --- a/src/plugins/qmakeprojectmanager/makefileparse.cpp +++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp @@ -225,7 +225,7 @@ static FilePath findQMakeBinaryFromMakefile(const FilePath &makefile) } } } - return FilePath(); + return {}; } MakeFileParse::MakeFileParse(const FilePath &makefile, Mode mode) : m_mode(mode) diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index c494f5544a6..8806e2a79b3 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -6,7 +6,7 @@ #include "makefileparse.h" #include "qmakebuildconfiguration.h" #include "qmakebuildinfo.h" -#include "qmakekitinformation.h" +#include "qmakekitaspect.h" #include "qmakenodes.h" #include "qmakeproject.h" #include "qmakeprojectmanagerconstants.h" @@ -26,7 +26,6 @@ #include <projectexplorer/buildsteplist.h> #include <projectexplorer/kit.h> #include <projectexplorer/makestep.h> -#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorertr.h> #include <projectexplorer/runconfiguration.h> @@ -34,7 +33,7 @@ #include <projectexplorer/toolchain.h> #include <qtsupport/qtbuildaspects.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <utils/process.h> @@ -53,24 +52,12 @@ using namespace QmakeProjectManager::Internal; namespace QmakeProjectManager { -class RunSystemAspect : public TriStateAspect -{ - Q_OBJECT -public: - RunSystemAspect() - : TriStateAspect(nullptr, Tr::tr("Run"), Tr::tr("Ignore"), Tr::tr("Use global setting")) - { - setSettingsKey("RunSystemFunction"); - setDisplayName(Tr::tr("qmake system() behavior when parsing:")); - } -}; - QmakeExtraBuildInfo::QmakeExtraBuildInfo() { - const BuildPropertiesSettings &settings = ProjectExplorerPlugin::buildPropertiesSettings(); - config.separateDebugInfo = settings.separateDebugInfo.value(); - config.linkQmlDebuggingQQ2 = settings.qmlDebugging.value(); - config.useQtQuickCompiler = settings.qtQuickCompiler.value(); + const BuildPropertiesSettings &settings = buildPropertiesSettings(); + config.separateDebugInfo = settings.separateDebugInfo(); + config.linkQmlDebuggingQQ2 = settings.qmlDebugging(); + config.useQtQuickCompiler = settings.qtQuickCompiler(); } // -------------------------------------------------------------------- @@ -104,7 +91,7 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Id id) appendInitialCleanStep(Constants::MAKESTEP_BS_ID); setInitializer([this, target](const BuildInfo &info) { - auto qmakeStep = buildSteps()->firstOfType<QMakeStep>(); + QMakeStep *qmakeStep = buildSteps()->firstOfType<QMakeStep>(); QTC_ASSERT(qmakeStep, return); const QmakeExtraBuildInfo qmakeExtra = info.extraInfo.value<QmakeExtraBuildInfo>(); @@ -118,11 +105,11 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Id id) QString additionalArguments = qmakeExtra.additionalArguments; if (!additionalArguments.isEmpty()) - qmakeStep->setUserArguments(additionalArguments); + qmakeStep->userArguments.setArguments(additionalArguments); - aspect<SeparateDebugInfoAspect>()->setValue(qmakeExtra.config.separateDebugInfo); - aspect<QmlDebuggingAspect>()->setValue(qmakeExtra.config.linkQmlDebuggingQQ2); - aspect<QtQuickCompilerAspect>()->setValue(qmakeExtra.config.useQtQuickCompiler); + separateDebugInfo.setValue(qmakeExtra.config.separateDebugInfo); + qmlDebugging.setValue(qmakeExtra.config.linkQmlDebuggingQQ2); + useQtQuickCompiler.setValue(qmakeExtra.config.useQtQuickCompiler); setQMakeBuildConfiguration(config); @@ -166,28 +153,33 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Id id) connect(target, &Target::parsingFinished, this, &QmakeBuildConfiguration::updateProblemLabel); connect(target, &Target::kitChanged, this, &QmakeBuildConfiguration::updateProblemLabel); - const auto separateDebugInfoAspect = addAspect<SeparateDebugInfoAspect>(); - connect(separateDebugInfoAspect, &SeparateDebugInfoAspect::changed, this, [this] { + connect(&separateDebugInfo, &BaseAspect::changed, this, [this] { emit separateDebugInfoChanged(); emit qmakeBuildConfigurationChanged(); qmakeBuildSystem()->scheduleUpdateAllNowOrLater(); }); - const auto qmlDebuggingAspect = addAspect<QmlDebuggingAspect>(this); - connect(qmlDebuggingAspect, &QmlDebuggingAspect::changed, this, [this] { + qmlDebugging.setBuildConfiguration(this); + connect(&qmlDebugging, &BaseAspect::changed, this, [this] { emit qmlDebuggingChanged(); emit qmakeBuildConfigurationChanged(); qmakeBuildSystem()->scheduleUpdateAllNowOrLater(); }); - const auto qtQuickCompilerAspect = addAspect<QtQuickCompilerAspect>(this); - connect(qtQuickCompilerAspect, &QtQuickCompilerAspect::changed, this, [this] { + useQtQuickCompiler.setBuildConfiguration(this); + connect(&useQtQuickCompiler, &QtQuickCompilerAspect::changed, this, [this] { emit useQtQuickCompilerChanged(); emit qmakeBuildConfigurationChanged(); qmakeBuildSystem()->scheduleUpdateAllNowOrLater(); }); - addAspect<RunSystemAspect>(); + runSystemFunctions.setSettingsKey("RunSystemFunction"); + runSystemFunctions.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + runSystemFunctions.setDisplayName(Tr::tr("qmake system() behavior when parsing:")); + runSystemFunctions.addOption(Tr::tr("Run")); + runSystemFunctions.addOption(Tr::tr("Ignore")); + runSystemFunctions.addOption(Tr::tr("Use global setting")); + runSystemFunctions.setDefaultValue(2); } QmakeBuildConfiguration::~QmakeBuildConfiguration() @@ -195,22 +187,21 @@ QmakeBuildConfiguration::~QmakeBuildConfiguration() delete m_buildSystem; } -QVariantMap QmakeBuildConfiguration::toMap() const +void QmakeBuildConfiguration::toMap(Store &map) const { - QVariantMap map(BuildConfiguration::toMap()); - map.insert(QLatin1String(BUILD_CONFIGURATION_KEY), int(m_qmakeBuildConfiguration)); - return map; + BuildConfiguration::toMap(map); + map.insert(BUILD_CONFIGURATION_KEY, int(m_qmakeBuildConfiguration)); } -bool QmakeBuildConfiguration::fromMap(const QVariantMap &map) +void QmakeBuildConfiguration::fromMap(const Store &map) { - if (!BuildConfiguration::fromMap(map)) - return false; + BuildConfiguration::fromMap(map); + if (hasError()) + return; - m_qmakeBuildConfiguration = QtVersion::QmakeBuildConfigs(map.value(QLatin1String(BUILD_CONFIGURATION_KEY)).toInt()); + m_qmakeBuildConfiguration = QtVersion::QmakeBuildConfigs(map.value(BUILD_CONFIGURATION_KEY).toInt()); m_lastKitState = LastKitState(kit()); - return true; } void QmakeBuildConfiguration::kitChanged() @@ -388,44 +379,27 @@ bool QmakeBuildConfiguration::isBuildDirAtSafeLocation() const return isBuildDirAtSafeLocation(project()->projectDirectory(), buildDirectory()); } -TriState QmakeBuildConfiguration::separateDebugInfo() const -{ - return aspect<SeparateDebugInfoAspect>()->value(); -} - void QmakeBuildConfiguration::forceSeparateDebugInfo(bool sepDebugInfo) { - aspect<SeparateDebugInfoAspect>()->setValue(sepDebugInfo - ? TriState::Enabled - : TriState::Disabled); -} - -TriState QmakeBuildConfiguration::qmlDebugging() const -{ - return aspect<QmlDebuggingAspect>()->value(); + separateDebugInfo.setValue(sepDebugInfo ? TriState::Enabled : TriState::Disabled); } void QmakeBuildConfiguration::forceQmlDebugging(bool enable) { - aspect<QmlDebuggingAspect>()->setValue(enable ? TriState::Enabled : TriState::Disabled); -} - -TriState QmakeBuildConfiguration::useQtQuickCompiler() const -{ - return aspect<QtQuickCompilerAspect>()->value(); + qmlDebugging.setValue(enable ? TriState::Enabled : TriState::Disabled); } void QmakeBuildConfiguration::forceQtQuickCompiler(bool enable) { - aspect<QtQuickCompilerAspect>()->setValue(enable ? TriState::Enabled : TriState::Disabled); + useQtQuickCompiler.setValue(enable ? TriState::Enabled : TriState::Disabled); } -bool QmakeBuildConfiguration::runSystemFunction() const +bool QmakeBuildConfiguration::runQmakeSystemFunctions() const { - const TriState runSystem = aspect<RunSystemAspect>()->value(); - if (runSystem == TriState::Enabled) + const int sel = runSystemFunctions(); + if (sel == 0) return true; - if (runSystem == TriState::Disabled) + if (sel == 1) return false; return settings().runSystemFunction(); } @@ -679,7 +653,7 @@ QString QmakeBuildConfiguration::extractSpecFromArguments(QString *args, static BuildInfo createBuildInfo(const Kit *k, const FilePath &projectPath, BuildConfiguration::BuildType type) { - const BuildPropertiesSettings &settings = ProjectExplorerPlugin::buildPropertiesSettings(); + const BuildPropertiesSettings &settings = buildPropertiesSettings(); QtVersion *version = QtKitAspect::qtVersion(k); QmakeExtraBuildInfo extraInfo; BuildInfo info; @@ -690,7 +664,7 @@ static BuildInfo createBuildInfo(const Kit *k, const FilePath &projectPath, info.displayName = ::ProjectExplorer::Tr::tr("Release"); //: Non-ASCII characters in directory suffix may cause build issues. suffix = Tr::tr("Release", "Shadow build directory suffix"); - if (settings.qtQuickCompiler.value() == TriState::Default) { + if (settings.qtQuickCompiler() == TriState::Default) { if (version && version->isQtQuickCompilerSupported()) extraInfo.config.useQtQuickCompiler = TriState::Enabled; } @@ -705,15 +679,15 @@ static BuildInfo createBuildInfo(const Kit *k, const FilePath &projectPath, info.displayName = ::ProjectExplorer::Tr::tr("Profile"); //: Non-ASCII characters in directory suffix may cause build issues. suffix = Tr::tr("Profile", "Shadow build directory suffix"); - if (settings.separateDebugInfo.value() == TriState::Default) + if (settings.separateDebugInfo() == TriState::Default) extraInfo.config.separateDebugInfo = TriState::Enabled; - if (settings.qtQuickCompiler.value() == TriState::Default) { + if (settings.qtQuickCompiler() == TriState::Default) { if (version && version->isQtQuickCompilerSupported()) extraInfo.config.useQtQuickCompiler = TriState::Enabled; } } - if (settings.qmlDebugging.value() == TriState::Default) { + if (settings.qmlDebugging() == TriState::Default) { if (version && version->isQmlDebuggingSupported()) extraInfo.config.linkQmlDebuggingQQ2 = TriState::Enabled; } @@ -855,5 +829,3 @@ void QmakeBuildConfiguration::restrictNextBuild(const RunConfiguration *rc) } } // namespace QmakeProjectManager - -#include <qmakebuildconfiguration.moc> diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h index 3b842cfd7cc..05ccbb29836 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h @@ -5,8 +5,10 @@ #include "qmakeprojectmanager_global.h" +#include <projectexplorer/buildaspects.h> #include <projectexplorer/buildconfiguration.h> #include <qtsupport/baseqtversion.h> +#include <qtsupport/qtbuildaspects.h> #include <utils/aspects.h> @@ -65,7 +67,7 @@ public: QString *arguments, const Utils::FilePath &directory, const QtSupport::QtVersion *version, QStringList *outArgs = nullptr); - QVariantMap toMap() const override; + void toMap(Utils::Store &map) const override; BuildType buildType() const override; @@ -76,16 +78,16 @@ public: const Utils::FilePath &buildDir); bool isBuildDirAtSafeLocation() const; - Utils::TriState separateDebugInfo() const; void forceSeparateDebugInfo(bool sepDebugInfo); - - Utils::TriState qmlDebugging() const; void forceQmlDebugging(bool enable); - - Utils::TriState useQtQuickCompiler() const; void forceQtQuickCompiler(bool enable); - bool runSystemFunction() const; + ProjectExplorer::SeparateDebugInfoAspect separateDebugInfo{this}; + QtSupport::QmlDebuggingAspect qmlDebugging{this}; + QtSupport::QtQuickCompilerAspect useQtQuickCompiler{this}; + Utils::SelectionAspect runSystemFunctions{this}; + + bool runQmakeSystemFunctions() const; signals: /// emitted for setQMakeBuildConfig, not emitted for Qt version changes, even @@ -97,7 +99,7 @@ signals: void useQtQuickCompilerChanged(); protected: - bool fromMap(const QVariantMap &map) override; + void fromMap(const Utils::Store &map) override; bool regenerateBuildFiles(ProjectExplorer::Node *node = nullptr) override; private: diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp similarity index 51% rename from src/plugins/qmakeprojectmanager/qmakekitinformation.cpp rename to src/plugins/qmakeprojectmanager/qmakekitaspect.cpp index c413758048c..18033f9dfc1 100644 --- a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp +++ b/src/plugins/qmakeprojectmanager/qmakekitaspect.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "qmakekitinformation.h" +#include "qmakekitaspect.h" #include "qmakeprojectmanagerconstants.h" #include "qmakeprojectmanagertr.h" @@ -10,7 +10,7 @@ #include <projectexplorer/toolchain.h> #include <projectexplorer/toolchainmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/guard.h> @@ -23,24 +23,23 @@ using namespace ProjectExplorer; using namespace Utils; -namespace QmakeProjectManager { -namespace Internal { +namespace QmakeProjectManager::Internal { -class QmakeKitAspectWidget final : public KitAspectWidget +class QmakeKitAspectImpl final : public KitAspect { public: - QmakeKitAspectWidget(Kit *k, const KitAspect *ki) - : KitAspectWidget(k, ki), m_lineEdit(createSubWidget<QLineEdit>()) + QmakeKitAspectImpl(Kit *k, const KitAspectFactory *ki) + : KitAspect(k, ki), m_lineEdit(createSubWidget<QLineEdit>()) { refresh(); // set up everything according to kit m_lineEdit->setToolTip(ki->description()); - connect(m_lineEdit, &QLineEdit::textEdited, this, &QmakeKitAspectWidget::mkspecWasChanged); + connect(m_lineEdit, &QLineEdit::textEdited, this, &QmakeKitAspectImpl::mkspecWasChanged); } - ~QmakeKitAspectWidget() override { delete m_lineEdit; } + ~QmakeKitAspectImpl() override { delete m_lineEdit; } private: - void addToLayout(Layouting::LayoutItem &parent) override + void addToLayoutImpl(Layouting::LayoutItem &parent) override { addMutableAction(m_lineEdit); parent.addItem(m_lineEdit); @@ -64,49 +63,6 @@ private: Guard m_ignoreChanges; }; - -QmakeKitAspect::QmakeKitAspect() -{ - setObjectName(QLatin1String("QmakeKitAspect")); - setId(QmakeKitAspect::id()); - setDisplayName(Tr::tr("Qt mkspec")); - setDescription(Tr::tr("The mkspec to use when building the project with qmake.<br>" - "This setting is ignored when using other build systems.")); - setPriority(24000); -} - -Tasks QmakeKitAspect::validate(const Kit *k) const -{ - Tasks result; - QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); - - const QString mkspec = QmakeKitAspect::mkspec(k); - if (!version && !mkspec.isEmpty()) - result << BuildSystemTask(Task::Warning, Tr::tr("No Qt version set, so mkspec is ignored.")); - if (version && !version->hasMkspec(mkspec)) - result << BuildSystemTask(Task::Error, Tr::tr("Mkspec not found for Qt version.")); - - return result; -} - -KitAspectWidget *QmakeKitAspect::createConfigWidget(Kit *k) const -{ - return new Internal::QmakeKitAspectWidget(k, this); -} - -KitAspect::ItemList QmakeKitAspect::toUserOutput(const Kit *k) const -{ - return {{Tr::tr("mkspec"), QDir::toNativeSeparators(mkspec(k))}}; -} - -void QmakeKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const -{ - expander->registerVariable("Qmake:mkspec", Tr::tr("Mkspec configured for qmake by the kit."), - [kit]() -> QString { - return QDir::toNativeSeparators(mkspec(kit)); - }); -} - Id QmakeKitAspect::id() { return Constants::KIT_INFORMATION_ID; @@ -145,5 +101,53 @@ QString QmakeKitAspect::defaultMkspec(const Kit *k) return version->mkspecFor(ToolChainKitAspect::cxxToolChain(k)); } -} // namespace Internal -} // namespace QmakeProjectManager +// QmakeKitAspectFactory + +class QmakeKitAspectFactory : public KitAspectFactory +{ +public: + QmakeKitAspectFactory() + { + setId(QmakeKitAspect::id()); + setDisplayName(Tr::tr("Qt mkspec")); + setDescription(Tr::tr("The mkspec to use when building the project with qmake.<br>" + "This setting is ignored when using other build systems.")); + setPriority(24000); + } + + Tasks validate(const Kit *k) const override + { + Tasks result; + QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k); + + const QString mkspec = QmakeKitAspect::mkspec(k); + if (!version && !mkspec.isEmpty()) + result << BuildSystemTask(Task::Warning, Tr::tr("No Qt version set, so mkspec is ignored.")); + if (version && !version->hasMkspec(mkspec)) + result << BuildSystemTask(Task::Error, Tr::tr("Mkspec not found for Qt version.")); + + return result; + } + + KitAspect *createKitAspect(Kit *k) const override + { + return new QmakeKitAspectImpl(k, this); + } + + ItemList toUserOutput(const Kit *k) const override + { + return {{Tr::tr("mkspec"), QDir::toNativeSeparators(QmakeKitAspect::mkspec(k))}}; + } + + void addToMacroExpander(Kit *kit, Utils::MacroExpander *expander) const override + { + expander->registerVariable("Qmake:mkspec", Tr::tr("Mkspec configured for qmake by the kit."), + [kit]() -> QString { + return QDir::toNativeSeparators(QmakeKitAspect::mkspec(kit)); + }); + } +}; + +const QmakeKitAspectFactory theQmakeKitAspectFactory; + +} // QmakeProjectManager::Internal diff --git a/src/plugins/qmakeprojectmanager/qmakekitaspect.h b/src/plugins/qmakeprojectmanager/qmakekitaspect.h new file mode 100644 index 00000000000..634facb6e3d --- /dev/null +++ b/src/plugins/qmakeprojectmanager/qmakekitaspect.h @@ -0,0 +1,21 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <projectexplorer/kitmanager.h> + +namespace QmakeProjectManager::Internal { + +class QmakeKitAspect +{ +public: + static Utils::Id id(); + enum class MkspecSource { User, Code }; + static void setMkspec(ProjectExplorer::Kit *k, const QString &mkspec, MkspecSource source); + static QString mkspec(const ProjectExplorer::Kit *k); + static QString effectiveMkspec(const ProjectExplorer::Kit *k); + static QString defaultMkspec(const ProjectExplorer::Kit *k); +}; + +} // QmakeProjectManager::Internal diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.h b/src/plugins/qmakeprojectmanager/qmakekitinformation.h deleted file mode 100644 index 0db044b8309..00000000000 --- a/src/plugins/qmakeprojectmanager/qmakekitinformation.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/kitmanager.h> - -namespace QmakeProjectManager { -namespace Internal { - -class QmakeKitAspect : public ProjectExplorer::KitAspect -{ - Q_OBJECT - -public: - QmakeKitAspect(); - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const override; - - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const override; - - ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; - - void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; - - static Utils::Id id(); - enum class MkspecSource { User, Code }; - static void setMkspec(ProjectExplorer::Kit *k, const QString &mkspec, MkspecSource source); - static QString mkspec(const ProjectExplorer::Kit *k); - static QString effectiveMkspec(const ProjectExplorer::Kit *k); - static QString defaultMkspec(const ProjectExplorer::Kit *k); -}; - -} // namespace Internal -} // namespace QmakeProjectManager diff --git a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp index e72d3467582..617c0d63e80 100644 --- a/src/plugins/qmakeprojectmanager/qmakemakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakemakestep.cpp @@ -12,14 +12,14 @@ #include "qmakesettings.h" #include "qmakestep.h" -#include <projectexplorer/target.h> -#include <projectexplorer/toolchain.h> #include <projectexplorer/buildsteplist.h> #include <projectexplorer/gnumakeparser.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/target.h> +#include <projectexplorer/toolchain.h> #include <projectexplorer/xcodebuildparser.h> #include <utils/process.h> @@ -40,10 +40,9 @@ public: QmakeMakeStep(BuildStepList *bsl, Id id); private: - void finish(ProcessResult result) override; bool init() override; void setupOutputFormatter(OutputFormatter *formatter) override; - void doRun() override; + Tasking::GroupItem runRecipe() final; QStringList displayArguments() const override; bool m_scriptTarget = false; @@ -201,33 +200,38 @@ void QmakeMakeStep::setupOutputFormatter(OutputFormatter *formatter) AbstractProcessStep::setupOutputFormatter(formatter); } -void QmakeMakeStep::doRun() +Tasking::GroupItem QmakeMakeStep::runRecipe() { - if (m_scriptTarget || m_ignoredNonTopLevelBuild) { - emit finished(true); - return; - } + using namespace Tasking; - if (!m_makeFileToCheck.exists()) { - if (!ignoreReturnValue()) - emit addOutput(Tr::tr("Cannot find Makefile. Check your build settings."), BuildStep::OutputFormat::NormalMessage); - const bool success = ignoreReturnValue(); - emit finished(success); - return; - } + const auto onSetup = [this] { + if (m_scriptTarget || m_ignoredNonTopLevelBuild) + return SetupResult::StopWithDone; - AbstractProcessStep::doRun(); -} + if (!m_makeFileToCheck.exists()) { + const bool success = ignoreReturnValue(); + if (!success) { + emit addOutput(Tr::tr("Cannot find Makefile. Check your build settings."), + OutputFormat::NormalMessage); + } + return success ? SetupResult::StopWithDone : SetupResult::StopWithError; + } + return SetupResult::Continue; + }; + const auto onError = [this] { + if (m_unalignedBuildDir && settings().warnAgainstUnalignedBuildDir()) { + const QString msg = Tr::tr("The build directory is not at the same level as the source " + "directory, which could be the reason for the build failure."); + emit addTask(BuildSystemTask(Task::Warning, msg)); + } + }; -void QmakeMakeStep::finish(ProcessResult result) -{ - if (!isSuccess(result) && !isCanceled() && m_unalignedBuildDir - && settings().warnAgainstUnalignedBuildDir()) { - const QString msg = Tr::tr("The build directory is not at the same level as the source " - "directory, which could be the reason for the build failure."); - emit addTask(BuildSystemTask(Task::Warning, msg)); - } - MakeStep::finish(result); + return Group { + ignoreReturnValue() ? finishAllAndDone : stopOnError, + onGroupSetup(onSetup), + onGroupError(onError), + defaultProcessTask() + }; } QStringList QmakeMakeStep::displayArguments() const diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index 144a9cc0c9f..d70efc763cc 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <resourceeditor/resourcenode.h> @@ -167,7 +167,7 @@ bool QmakePriFileNode::removeSubProject(const FilePath &proFilePath) QStringList QmakePriFileNode::subProjectFileNamePatterns() const { - return QStringList("*.pro"); + return {"*.pro"}; } bool QmakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *notAdded) diff --git a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp index 528b18c95eb..01bae3472fd 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodetreebuilder.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <resourceeditor/resourcenode.h> @@ -195,7 +195,6 @@ static void createTree(QmakeBuildSystem *buildSystem, fileNode->setEnabled(fn.second == FileOrigin::ExactParse); vfolder->addNestedNode(std::move(fileNode)); } - vfolder->forEachFolderNode([](FolderNode *fn) { fn->compress(); }); } node->addNode(std::move(vfolder)); } @@ -265,6 +264,7 @@ std::unique_ptr<QmakeProFileNode> QmakeNodeTreeBuilder::buildTree(QmakeBuildSyst buildSystem->rootProFile()); root->setIcon(iconForProfile(buildSystem->rootProFile())); createTree(buildSystem, buildSystem->rootProFile(), root.get(), toExclude); + root->compress(); return root; } diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index 31a55faab01..8a18d238017 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -28,7 +28,6 @@ #include <texteditor/tabsettings.h> #include <texteditor/texteditorsettings.h> -#include <utils/QtConcurrentTools> #include <utils/algorithm.h> #include <utils/async.h> #include <utils/filesystemwatcher.h> @@ -1918,7 +1917,7 @@ FilePaths QmakeProFile::subDirsPaths(QtSupport::ProFileReader *reader, else realFile = realDir; - if (QFile::exists(realFile)) { + if (QFileInfo::exists(realFile)) { realFile = QDir::cleanPath(realFile); subProjectPaths << FilePath::fromString(realFile); if (subProjectsNotToDeploy && !subProjectsNotToDeploy->contains(realFile) diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h index 9296addfa80..1fca2d9866f 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h @@ -17,6 +17,7 @@ #include <QLoggingCategory> #include <QMap> #include <QPair> +#include <QPointer> #include <QStringList> #include <memory> diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index d7aefb50b8b..53639778950 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -45,7 +45,7 @@ #include <qtsupport/profilereader.h> #include <qtsupport/qtcppkitinfo.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <utils/algorithm.h> @@ -171,7 +171,7 @@ QmakeProject::~QmakeProject() setRootProjectNode(nullptr); } -Project::RestoreResult QmakeProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult QmakeProject::fromMap(const Store &map, QString *errorMessage) { RestoreResult result = Project::fromMap(map, errorMessage); if (result != RestoreResult::Ok) @@ -872,7 +872,7 @@ QtSupport::ProFileReader *QmakeBuildSystem::createProFileReader(const QmakeProFi }); m_qmakeGlobals->setCommandLineArguments(rootProFileName, qmakeArgs); - m_qmakeGlobals->runSystemFunction = bc->runSystemFunction(); + m_qmakeGlobals->runSystemFunction = bc->runQmakeSystemFunctions(); QtSupport::ProFileCacheManager::instance()->incRefCount(); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index 3097ae7b579..10f2a563709 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -45,7 +45,7 @@ public: ProjectExplorer::ProjectImporter *projectImporter() const final; protected: - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) final; + RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) final; private: ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override; diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp index 1a929bf9a49..832fb1a6809 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectimporter.cpp @@ -6,7 +6,7 @@ #include "makefileparse.h" #include "qmakebuildconfiguration.h" #include "qmakebuildinfo.h" -#include "qmakekitinformation.h" +#include "qmakekitaspect.h" #include "qmakeproject.h" #include "qmakeprojectmanagertr.h" #include "qmakestep.h" @@ -17,7 +17,7 @@ #include <projectexplorer/toolchain.h> #include <projectexplorer/toolchainmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionfactory.h> #include <qtsupport/qtversionmanager.h> @@ -27,9 +27,9 @@ #include <utils/qtcassert.h> #include <QDir> -#include <QFileInfo> -#include <QStringList> #include <QLoggingCategory> +#include <QSet> +#include <QStringList> #include <memory> @@ -61,19 +61,18 @@ QmakeProjectImporter::QmakeProjectImporter(const FilePath &path) : FilePaths QmakeProjectImporter::importCandidates() { - FilePaths candidates; - - const FilePath pfp = projectFilePath(); - const QString prefix = pfp.baseName(); - candidates << pfp.absolutePath(); + FilePaths candidates{projectFilePath().absolutePath()}; + QSet<FilePath> seenBaseDirs; for (Kit *k : KitManager::kits()) { const FilePath sbdir = QmakeBuildConfiguration::shadowBuildDirectory (projectFilePath(), k, QString(), BuildConfiguration::Unknown); const FilePath baseDir = sbdir.absolutePath(); - for (const FilePath &path : baseDir.dirEntries(QDir::Filters())) { - if (path.fileName().startsWith(prefix) && !candidates.contains(path)) + if (!Utils::insert(seenBaseDirs, baseDir)) + continue; + for (const FilePath &path : baseDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot)) { + if (!candidates.contains(path)) candidates << path; } } diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.qbs b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.qbs index e4276322c75..9252ad10503 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanager.qbs +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanager.qbs @@ -1,86 +1,81 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "QmakeProjectManager" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "network"] } - Depends { name: "QmlJS" } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["widgets", "network"] } + Depends { name: "QmlJS" } + Depends { name: "Utils" } - Depends { name: "Core" } - Depends { name: "ProjectExplorer" } + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } + Depends { name: "QtSupport" } + Depends { name: "CppEditor" } + Depends { name: "TextEditor" } + Depends { name: "ResourceEditor" } + + Group { + name: "General" + files: [ + "addlibrarywizard.cpp", "addlibrarywizard.h", + "librarydetailscontroller.cpp", "librarydetailscontroller.h", + "makefileparse.cpp", "makefileparse.h", + "profilecompletionassist.cpp", "profilecompletionassist.h", + "profileeditor.cpp", "profileeditor.h", + "profilehighlighter.cpp", "profilehighlighter.h", + "profilehoverhandler.cpp", "profilehoverhandler.h", + "qmakebuildinfo.h", + "qmakekitaspect.cpp", "qmakekitaspect.h", + "qmakemakestep.cpp", "qmakemakestep.h", + "qmakeparser.cpp", "qmakeparser.h", + "qmakeparsernodes.cpp", "qmakeparsernodes.h", + "qmakeprojectimporter.cpp", "qmakeprojectimporter.h", + "qmakesettings.cpp", "qmakesettings.h", + "qmakestep.cpp", "qmakestep.h", + "qmakebuildconfiguration.cpp", "qmakebuildconfiguration.h", + "qmakenodes.cpp", "qmakenodes.h", + "qmakenodetreebuilder.cpp", "qmakenodetreebuilder.h", + "qmakeproject.cpp", "qmakeproject.h", + "qmakeprojectmanager.qrc", + "qmakeprojectmanager_global.h", "qmakeprojectmanagertr.h", + "qmakeprojectmanagerconstants.h", + "qmakeprojectmanagerplugin.cpp", "qmakeprojectmanagerplugin.h", + ] + } + + Group { + name: "Custom Widget Wizard" + prefix: "customwidgetwizard/" + files: [ + "classdefinition.cpp", "classdefinition.h", + "classlist.cpp", "classlist.h", + "customwidgetpluginwizardpage.cpp", "customwidgetpluginwizardpage.h", + "customwidgetwidgetswizardpage.cpp", "customwidgetwidgetswizardpage.h", + "customwidgetwizard.cpp", "customwidgetwizard.h", + "customwidgetwizarddialog.cpp", "customwidgetwizarddialog.h", + "filenamingparameters.h", + "plugingenerator.cpp", "plugingenerator.h", + "pluginoptions.h" + ] + } + + Group { + name: "Wizards" + prefix: "wizards/" + files: [ + "qtprojectparameters.cpp", "qtprojectparameters.h", + "qtwizard.cpp", "qtwizard.h", + "subdirsprojectwizard.cpp", "subdirsprojectwizard.h", + "subdirsprojectwizarddialog.cpp", "subdirsprojectwizarddialog.h", + "wizards.qrc" + ] + } + + Group { + name: "Wizard Images" + prefix: "wizards/images/" + files: ["*.png"] + } + + Export { Depends { name: "QtSupport" } - Depends { name: "CppEditor" } - Depends { name: "TextEditor" } - Depends { name: "ResourceEditor" } - Depends { name: "app_version_header" } - - Group { - name: "General" - files: [ - "addlibrarywizard.cpp", "addlibrarywizard.h", - "librarydetailscontroller.cpp", "librarydetailscontroller.h", - "makefileparse.cpp", "makefileparse.h", - "profilecompletionassist.cpp", "profilecompletionassist.h", - "profileeditor.cpp", "profileeditor.h", - "profilehighlighter.cpp", "profilehighlighter.h", - "profilehoverhandler.cpp", "profilehoverhandler.h", - "qmakebuildinfo.h", - "qmakekitinformation.cpp", "qmakekitinformation.h", - "qmakemakestep.cpp", "qmakemakestep.h", - "qmakeparser.cpp", "qmakeparser.h", - "qmakeparsernodes.cpp", "qmakeparsernodes.h", - "qmakeprojectimporter.cpp", "qmakeprojectimporter.h", - "qmakesettings.cpp", "qmakesettings.h", - "qmakestep.cpp", "qmakestep.h", - "qmakebuildconfiguration.cpp", "qmakebuildconfiguration.h", - "qmakenodes.cpp", "qmakenodes.h", - "qmakenodetreebuilder.cpp", "qmakenodetreebuilder.h", - "qmakeproject.cpp", "qmakeproject.h", - "qmakeprojectmanager.qrc", - "qmakeprojectmanager_global.h", "qmakeprojectmanagertr.h", - "qmakeprojectmanagerconstants.h", - "qmakeprojectmanagerplugin.cpp", "qmakeprojectmanagerplugin.h", - ] - } - - Group { - name: "Custom Widget Wizard" - prefix: "customwidgetwizard/" - files: [ - "classdefinition.cpp", "classdefinition.h", - "classlist.cpp", "classlist.h", - "customwidgetpluginwizardpage.cpp", "customwidgetpluginwizardpage.h", - "customwidgetwidgetswizardpage.cpp", "customwidgetwidgetswizardpage.h", - "customwidgetwizard.cpp", "customwidgetwizard.h", - "customwidgetwizarddialog.cpp", "customwidgetwizarddialog.h", - "filenamingparameters.h", - "plugingenerator.cpp", "plugingenerator.h", - "pluginoptions.h" - ] - } - - Group { - name: "Wizards" - prefix: "wizards/" - files: [ - "qtprojectparameters.cpp", "qtprojectparameters.h", - "qtwizard.cpp", "qtwizard.h", - "subdirsprojectwizard.cpp", "subdirsprojectwizard.h", - "subdirsprojectwizarddialog.cpp", "subdirsprojectwizarddialog.h", - "wizards.qrc" - ] - } - - Group { - name: "Wizard Images" - prefix: "wizards/images/" - files: ["*.png"] - } - - Export { - Depends { name: "QtSupport" } - } } } diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index 61493bf05c3..7e06ff6bbab 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -7,13 +7,11 @@ #include "customwidgetwizard/customwidgetwizard.h" #include "profileeditor.h" #include "qmakebuildconfiguration.h" -#include "qmakekitinformation.h" #include "qmakemakestep.h" #include "qmakenodes.h" #include "qmakeproject.h" #include "qmakeprojectmanagerconstants.h" #include "qmakeprojectmanagertr.h" -#include "qmakesettings.h" #include "qmakestep.h" #include "wizards/subdirsprojectwizard.h" @@ -80,8 +78,6 @@ public: ProFileEditorFactory profileEditorFactory; - QmakeSettings settings; - QmakeProject *m_previousStartupProject = nullptr; Target *m_previousTarget = nullptr; @@ -99,8 +95,6 @@ public: QAction *m_addLibraryAction = nullptr; QAction *m_addLibraryActionContextMenu = nullptr; - QmakeKitAspect qmakeKitAspect; - void addLibrary(); void addLibraryContextMenu(); void runQMake(); diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp index e69526901f0..e0ab9a47082 100644 --- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp +++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp @@ -4,6 +4,8 @@ #include "qmakesettings.h" #include "qmakeprojectmanagertr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <projectexplorer/projectexplorerconstants.h> #include <utils/hostosinfo.h> @@ -13,17 +15,15 @@ using namespace Utils; namespace QmakeProjectManager::Internal { -static QmakeSettings *theSettings; - -QmakeSettings &settings() { return *theSettings; } +QmakeSettings &settings() +{ + static QmakeSettings theSettings; + return theSettings; +} QmakeSettings::QmakeSettings() { - theSettings = this; - - setId("K.QmakeProjectManager.QmakeSettings"); - setDisplayName(Tr::tr("Qmake")); - setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setAutoApply(false); setSettingsGroup("QmakeProjectManager"); warnAgainstUnalignedBuildDir.setSettingsKey("WarnAgainstUnalignedBuildDir"); @@ -61,4 +61,18 @@ QmakeSettings::QmakeSettings() readSettings(); } +class QmakeSettingsPage : public Core::IOptionsPage +{ +public: + QmakeSettingsPage() + { + setId("K.QmakeProjectManager.QmakeSettings"); + setDisplayName(Tr::tr("Qmake")); + setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +static const QmakeSettingsPage settingsPage; + } // QmakeProjectManager::Internal diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.h b/src/plugins/qmakeprojectmanager/qmakesettings.h index 9c2b277f538..90d08eafdc8 100644 --- a/src/plugins/qmakeprojectmanager/qmakesettings.h +++ b/src/plugins/qmakeprojectmanager/qmakesettings.h @@ -3,11 +3,11 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace QmakeProjectManager::Internal { -class QmakeSettings : public Core::PagedSettings +class QmakeSettings : public Utils::AspectContainer { public: QmakeSettings(); diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index ccaf52ff93e..95a2d487e4b 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -4,7 +4,7 @@ #include "qmakestep.h" #include "qmakebuildconfiguration.h" -#include "qmakekitinformation.h" +#include "qmakekitaspect.h" #include "qmakenodes.h" #include "qmakeparser.h" #include "qmakeproject.h" @@ -27,7 +27,7 @@ #include <coreplugin/icore.h> #include <coreplugin/icontext.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <qtsupport/qtsupportconstants.h> @@ -63,22 +63,19 @@ QMakeStep::QMakeStep(BuildStepList *bsl, Id id) { setLowPriority(); - m_buildType = addAspect<SelectionAspect>(); - m_buildType->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - m_buildType->setDisplayName(Tr::tr("qmake build configuration:")); - m_buildType->addOption(Tr::tr("Debug")); - m_buildType->addOption(Tr::tr("Release")); + buildType.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + buildType.setDisplayName(Tr::tr("qmake build configuration:")); + buildType.addOption(Tr::tr("Debug")); + buildType.addOption(Tr::tr("Release")); - m_userArgs = addAspect<ArgumentsAspect>(macroExpander()); - m_userArgs->setSettingsKey(QMAKE_ARGUMENTS_KEY); - m_userArgs->setLabelText(Tr::tr("Additional arguments:")); + userArguments.setMacroExpander(macroExpander()); + userArguments.setSettingsKey(QMAKE_ARGUMENTS_KEY); + userArguments.setLabelText(Tr::tr("Additional arguments:")); - m_effectiveCall = addAspect<StringAspect>(); - m_effectiveCall->setDisplayStyle(StringAspect::TextEditDisplay); - m_effectiveCall->setLabelText(Tr::tr("Effective qmake call:")); - m_effectiveCall->setReadOnly(true); - m_effectiveCall->setUndoRedoEnabled(false); - m_effectiveCall->setEnabled(true); + effectiveCall.setDisplayStyle(StringAspect::TextEditDisplay); + effectiveCall.setLabelText(Tr::tr("Effective qmake call:")); + effectiveCall.setReadOnly(true); + effectiveCall.setEnabled(true); auto updateSummary = [this] { QtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit()); @@ -180,7 +177,7 @@ bool QMakeStep::init() const QtVersion *qtVersion = QtKitAspect::qtVersion(kit()); if (!qtVersion) { - emit addOutput(Tr::tr("No Qt version configured."), BuildStep::OutputFormat::ErrorMessage); + emit addOutput(Tr::tr("No Qt version configured."), OutputFormat::ErrorMessage); return false; } @@ -216,7 +213,7 @@ bool QMakeStep::init() if (make.isEmpty()) { emit addOutput(Tr::tr("Could not determine which \"make\" command to run. " "Check the \"make\" step in the build configuration."), - BuildStep::OutputFormat::ErrorMessage); + OutputFormat::ErrorMessage); return false; } m_makeCommand = CommandLine{make, makeArguments(makeFile.path()), CommandLine::Raw}; @@ -264,31 +261,25 @@ void QMakeStep::setupOutputFormatter(OutputFormatter *formatter) AbstractProcessStep::setupOutputFormatter(formatter); } -void QMakeStep::doRun() +Tasking::GroupItem QMakeStep::runRecipe() { - if (m_scriptTemplate) { - emit finished(true); - return; - } - - if (!m_needToRunQMake) { - emit addOutput(Tr::tr("Configuration unchanged, skipping qmake step."), BuildStep::OutputFormat::NormalMessage); - emit finished(true); - return; - } - - if (!checkWorkingDirectory()) - return; - - m_needToRunQMake = false; - using namespace Tasking; + const auto onSetup = [this] { + if (m_scriptTemplate) + return SetupResult::StopWithDone; + if (m_needToRunQMake) + return SetupResult::Continue; + emit addOutput(Tr::tr("Configuration unchanged, skipping qmake step."), + OutputFormat::NormalMessage); + return SetupResult::StopWithDone; + }; + const auto setupQMake = [this](Process &process) { m_outputFormatter->setLineParsers({new QMakeParser}); ProcessParameters *pp = processParameters(); pp->setCommandLine(m_qmakeCommand); - setupProcess(&process); + return setupProcess(process) ? SetupResult::Continue : SetupResult::StopWithError; }; const auto setupMakeQMake = [this](Process &process) { @@ -297,45 +288,23 @@ void QMakeStep::doRun() m_outputFormatter->setLineParsers({parser}); ProcessParameters *pp = processParameters(); pp->setCommandLine(m_makeCommand); - setupProcess(&process); + return setupProcess(process) ? SetupResult::Continue : SetupResult::StopWithError; }; - const auto onProcessDone = [this](const Process &) { - const QString command = displayedParameters()->effectiveCommand().toUserOutput(); - emit addOutput(Tr::tr("The process \"%1\" exited normally.").arg(command), - OutputFormat::NormalMessage); - }; - - const auto onProcessError = [this](const Process &process) { - const QString command = displayedParameters()->effectiveCommand().toUserOutput(); - if (process.result() == ProcessResult::FinishedWithError) { - emit addOutput(Tr::tr("The process \"%1\" exited with code %2.") - .arg(command, QString::number(process.exitCode())), - OutputFormat::ErrorMessage); - } else if (process.result() == ProcessResult::StartFailed) { - emit addOutput(Tr::tr("Could not start process \"%1\" %2.") - .arg(command, displayedParameters()->prettyArguments()), - OutputFormat::ErrorMessage); - const QString errorString = process.errorString(); - if (!errorString.isEmpty()) - emit addOutput(errorString, OutputFormat::ErrorMessage); - } else { - emit addOutput(Tr::tr("The process \"%1\" crashed.").arg(command), - OutputFormat::ErrorMessage); - } - m_needToRunQMake = true; - }; + const auto onProcessDone = [this](const Process &process) { handleProcessDone(process); }; const auto onDone = [this] { emit buildConfiguration()->buildDirectoryInitialized(); + m_needToRunQMake = false; }; - QList<GroupItem> processList = {ProcessTask(setupQMake, onProcessDone, onProcessError)}; + QList<GroupItem> processList = {onGroupSetup(onSetup), + onGroupDone(onDone), + ProcessTask(setupQMake, onProcessDone, onProcessDone)}; if (m_runMakeQmake) - processList << ProcessTask(setupMakeQMake, onProcessDone, onProcessError); - processList << onGroupDone(onDone); + processList << ProcessTask(setupMakeQMake, onProcessDone, onProcessDone); - runTaskTree(Group(processList)); + return Group(processList); } void QMakeStep::setForced(bool b) @@ -343,11 +312,6 @@ void QMakeStep::setForced(bool b) m_forced = b; } -void QMakeStep::setUserArguments(const QString &arguments) -{ - m_userArgs->setArguments(arguments); -} - QStringList QMakeStep::extraArguments() const { return m_extraArgs; @@ -376,7 +340,7 @@ FilePath QMakeStep::makeCommand() const { if (auto ms = stepList()->firstOfType<MakeStep>()) return ms->makeExecutable(); - return FilePath(); + return {}; } QString QMakeStep::makeArguments(const QString &makefile) const @@ -416,7 +380,7 @@ QStringList QMakeStep::parserArguments() // NOTE: extra parser args placed before the other args intentionally QStringList result = m_extraParserArgs; QtVersion *qt = QtKitAspect::qtVersion(kit()); - QTC_ASSERT(qt, return QStringList()); + QTC_ASSERT(qt, return {}); for (ProcessArgs::ConstArgIterator ait(allArguments(qt, ArgumentFlag::Expand)); ait.next(); ) { if (ait.isSimple()) result << ait.value(); @@ -424,11 +388,6 @@ QStringList QMakeStep::parserArguments() return result; } -QString QMakeStep::userArguments() const -{ - return m_userArgs->arguments(); -} - QString QMakeStep::mkspec() const { QString additionalArguments = userArguments(); @@ -443,19 +402,18 @@ QString QMakeStep::mkspec() const return QmakeKitAspect::effectiveMkspec(target()->kit()); } -QVariantMap QMakeStep::toMap() const +void QMakeStep::toMap(Store &map) const { - QVariantMap map(AbstractProcessStep::toMap()); + AbstractProcessStep::toMap(map); map.insert(QMAKE_FORCED_KEY, m_forced); map.insert(QMAKE_SELECTED_ABIS_KEY, m_selectedAbis); - return map; } -bool QMakeStep::fromMap(const QVariantMap &map) +void QMakeStep::fromMap(const Store &map) { m_forced = map.value(QMAKE_FORCED_KEY, false).toBool(); m_selectedAbis = map.value(QMAKE_SELECTED_ABIS_KEY).toStringList(); - return BuildStep::fromMap(map); + BuildStep::fromMap(map); } QWidget *QMakeStep::createConfigWidget() @@ -466,9 +424,9 @@ QWidget *QMakeStep::createConfigWidget() abisListWidget = new QListWidget; Layouting::Form builder; - builder.addRow({m_buildType}); - builder.addRow({m_userArgs}); - builder.addRow({m_effectiveCall}); + builder.addRow({buildType}); + builder.addRow({userArguments}); + builder.addRow({effectiveCall}); builder.addRow({abisLabel, abisListWidget}); builder.addItem(Layouting::noMargin); auto widget = builder.emerge(); @@ -479,7 +437,7 @@ QWidget *QMakeStep::createConfigWidget() updateAbiWidgets(); updateEffectiveQMakeCall(); - connect(m_userArgs, &BaseAspect::changed, widget, [this] { + connect(&userArguments, &BaseAspect::changed, widget, [this] { updateAbiWidgets(); updateEffectiveQMakeCall(); @@ -487,7 +445,7 @@ QWidget *QMakeStep::createConfigWidget() qmakeBuildSystem()->scheduleUpdateAllNowOrLater(); }); - connect(m_buildType, &BaseAspect::changed, + connect(&buildType, &BaseAspect::changed, widget, [this] { buildConfigurationSelected(); }); connect(qmakeBuildConfiguration(), &QmakeBuildConfiguration::qmlDebuggingChanged, @@ -539,7 +497,7 @@ void QMakeStep::qmakeBuildConfigChanged() const bool debug = bc->qmakeBuildConfiguration() & QtVersion::DebugBuild; { const GuardLocker locker(m_ignoreChanges); - m_buildType->setValue(debug ? 0 : 1); + buildType.setValue(debug ? 0 : 1); } updateAbiWidgets(); updateEffectiveQMakeCall(); @@ -628,7 +586,7 @@ void QMakeStep::buildConfigurationSelected() return; QmakeBuildConfiguration *bc = qmakeBuildConfiguration(); QtVersion::QmakeBuildConfigs buildConfiguration = bc->qmakeBuildConfiguration(); - if (m_buildType->value() == 0) { // debug + if (buildType() == 0) { // debug buildConfiguration = buildConfiguration | QtVersion::DebugBuild; } else { buildConfiguration = buildConfiguration & ~QtVersion::DebugBuild; @@ -712,7 +670,7 @@ void QMakeStep::updateAbiWidgets() void QMakeStep::updateEffectiveQMakeCall() { - m_effectiveCall->setValue(effectiveQMakeCall()); + effectiveCall.setValue(effectiveQMakeCall()); } void QMakeStep::recompileMessageBoxFinished(int button) @@ -737,11 +695,6 @@ QMakeStepFactory::QMakeStepFactory() setFlags(BuildStep::UniqueStep); } -QMakeStepConfig::TargetArchConfig QMakeStepConfig::targetArchFor(const Abi &, const QtVersion *) -{ - return NoArch; -} - QMakeStepConfig::OsType QMakeStepConfig::osTypeFor(const Abi &targetAbi, const QtVersion *version) { OsType os = NoOsType; diff --git a/src/plugins/qmakeprojectmanager/qmakestep.h b/src/plugins/qmakeprojectmanager/qmakestep.h index f72c48d4612..1cebb2b756f 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.h +++ b/src/plugins/qmakeprojectmanager/qmakestep.h @@ -6,27 +6,20 @@ #include "qmakeprojectmanager_global.h" #include <projectexplorer/abstractprocessstep.h> +#include <projectexplorer/runconfigurationaspects.h> #include <utils/aspects.h> #include <utils/commandline.h> #include <utils/guard.h> -#include <memory> - #include <QDebug> QT_BEGIN_NAMESPACE -class QComboBox; class QLabel; -class QLineEdit; -class QPlainTextEdit; class QListWidget; QT_END_NAMESPACE -namespace ProjectExplorer { -class Abi; -class ArgumentsAspect; -} // namespace ProjectExplorer +namespace ProjectExplorer { class Abi; } namespace QtSupport { class QtVersion; } @@ -47,42 +40,33 @@ public: class QMAKEPROJECTMANAGER_EXPORT QMakeStepConfig { public: - // TODO remove, does nothing - enum TargetArchConfig { NoArch, X86, X86_64, PowerPC, PowerPC64 }; - enum OsType { NoOsType, IphoneSimulator, IphoneOS }; - // TODO remove, does nothing - static TargetArchConfig targetArchFor(const ProjectExplorer::Abi &targetAbi, - const QtSupport::QtVersion *version); static OsType osTypeFor(const ProjectExplorer::Abi &targetAbi, const QtSupport::QtVersion *version); QStringList toArguments() const; friend bool operator==(const QMakeStepConfig &a, const QMakeStepConfig &b) { - return std::tie(a.archConfig, a.osType, a.linkQmlDebuggingQQ2) - == std::tie(b.archConfig, b.osType, b.linkQmlDebuggingQQ2) - && std::tie(a.useQtQuickCompiler, a.separateDebugInfo) - == std::tie(b.useQtQuickCompiler, b.separateDebugInfo); + return a.osType == b.osType + && a.linkQmlDebuggingQQ2 == b.linkQmlDebuggingQQ2 + && a.useQtQuickCompiler == b.useQtQuickCompiler + && a.separateDebugInfo == b.separateDebugInfo; } friend bool operator!=(const QMakeStepConfig &a, const QMakeStepConfig &b) { return !(a == b); } friend QDebug operator<<(QDebug dbg, const QMakeStepConfig &c) { - dbg << c.archConfig << c.osType + dbg << c.osType << (c.linkQmlDebuggingQQ2 == Utils::TriState::Enabled) << (c.useQtQuickCompiler == Utils::TriState::Enabled) << (c.separateDebugInfo == Utils::TriState::Enabled); return dbg; } - // Actual data QString sysRoot; QString targetTriple; - // TODO remove, does nothing - TargetArchConfig archConfig = NoArch; OsType osType = NoOsType; Utils::TriState separateDebugInfo; Utils::TriState linkQmlDebuggingQQ2; @@ -101,7 +85,6 @@ public: QmakeBuildSystem *qmakeBuildSystem() const; bool init() override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; - void doRun() override; QWidget *createConfigWidget() override; void setForced(bool b); @@ -116,9 +99,7 @@ public: QMakeStepConfig deducedArguments() const; // arguments passed to the pro file parser QStringList parserArguments(); - // arguments set by the user - QString userArguments() const; - void setUserArguments(const QString &arguments); + // Extra arguments for qmake and pro file parser. Not user editable via UI. QStringList extraArguments() const; void setExtraArguments(const QStringList &args); @@ -132,12 +113,17 @@ public: QString makeArguments(const QString &makefile) const; QString effectiveQMakeCall() const; - QVariantMap toMap() const override; + void toMap(Utils::Store &map) const override; + + Utils::SelectionAspect buildType{this}; + ProjectExplorer::ArgumentsAspect userArguments{this}; + Utils::StringAspect effectiveCall{this}; protected: - bool fromMap(const QVariantMap &map) override; + void fromMap(const Utils::Store &map) override; private: + Tasking::GroupItem runRecipe() final; // slots for handling buildconfiguration/step signals void qtVersionChanged(); void qmakeBuildConfigChanged(); @@ -158,7 +144,7 @@ private: Utils::CommandLine m_qmakeCommand; Utils::CommandLine m_makeCommand; - ProjectExplorer::ArgumentsAspect *m_userArgs = nullptr; + // Extra arguments for qmake and pro file parser QStringList m_extraArgs; // Extra arguments for pro file parser only @@ -176,8 +162,6 @@ private: Utils::Guard m_ignoreChanges; QLabel *abisLabel = nullptr; - Utils::SelectionAspect *m_buildType = nullptr; - Utils::StringAspect *m_effectiveCall = nullptr; QListWidget *abisListWidget = nullptr; }; diff --git a/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp b/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp index 816101dd31e..f7735415c47 100644 --- a/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp +++ b/src/plugins/qmakeprojectmanager/wizards/qtwizard.cpp @@ -10,14 +10,16 @@ #include <coreplugin/icore.h> #include <cppeditor/cppeditorconstants.h> +#include <cppeditor/cpptoolsreuse.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projecttree.h> #include <projectexplorer/targetsetuppage.h> #include <projectexplorer/task.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <utils/algorithm.h> @@ -27,6 +29,7 @@ using namespace ProjectExplorer; using namespace QtSupport; +using namespace Utils; namespace QmakeProjectManager { namespace Internal { @@ -39,12 +42,12 @@ QtWizard::QtWizard() QString QtWizard::sourceSuffix() { - return preferredSuffix(QLatin1String(ProjectExplorer::Constants::CPP_SOURCE_MIMETYPE)); + return CppEditor::preferredCxxSourceSuffix(ProjectTree::currentProject()); } QString QtWizard::headerSuffix() { - return preferredSuffix(QLatin1String(ProjectExplorer::Constants::CPP_HEADER_MIMETYPE)); + return CppEditor::preferredCxxHeaderSuffix(ProjectTree::currentProject()); } QString QtWizard::formSuffix() @@ -86,9 +89,9 @@ QString QtWizard::templateDir() bool QtWizard::lowerCaseFiles() { - QString lowerCaseSettingsKey = QLatin1String(CppEditor::Constants::CPPEDITOR_SETTINGSGROUP); - lowerCaseSettingsKey += QLatin1Char('/'); - lowerCaseSettingsKey += QLatin1String(CppEditor::Constants::LOWERCASE_CPPFILES_KEY); + QByteArray lowerCaseSettingsKey = CppEditor::Constants::CPPEDITOR_SETTINGSGROUP; + lowerCaseSettingsKey += '/'; + lowerCaseSettingsKey += CppEditor::Constants::LOWERCASE_CPPFILES_KEY; const bool lowerCaseDefault = CppEditor::Constants::LOWERCASE_CPPFILES_DEFAULT; return Core::ICore::settings()->value(lowerCaseSettingsKey, QVariant(lowerCaseDefault)).toBool(); } diff --git a/src/plugins/qmldesigner/.clang-format b/src/plugins/qmldesigner/.clang-format index 968dfdec7d1..87648a20579 100644 --- a/src/plugins/qmldesigner/.clang-format +++ b/src/plugins/qmldesigner/.clang-format @@ -110,7 +110,6 @@ SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index eae9460cdce..a4243932c7b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -1,7 +1,7 @@ #only if the plugin is requested by qtc_plugin_enabled continue if not stop as early as possible -find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate) +find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate QUIET) set(QmlDesignerPluginInstallPrefix "${IDE_PLUGIN_PATH}/qmldesigner") if (APPLE) @@ -1049,11 +1049,11 @@ extend_qtc_plugin(QmlDesigner timeline.qrc timelineabstracttool.cpp timelineabstracttool.h timelineactions.cpp timelineactions.h - timelineanimationform.cpp timelineanimationform.h timelineanimationform.ui + timelineanimationform.cpp timelineanimationform.h timelineconstants.h timelinecontext.cpp timelinecontext.h timelinecontrols.cpp timelinecontrols.h - timelineform.cpp timelineform.h timelineform.ui + timelineform.cpp timelineform.h timelinegraphicslayout.cpp timelinegraphicslayout.h timelinegraphicsscene.cpp timelinegraphicsscene.h timelineicons.h @@ -1064,8 +1064,7 @@ extend_qtc_plugin(QmlDesigner timelinepropertyitem.cpp timelinepropertyitem.h timelinesectionitem.cpp timelinesectionitem.h timelineselectiontool.cpp timelineselectiontool.h - timelinesettingsdialog.cpp - timelinesettingsdialog.h timelinesettingsdialog.ui + timelinesettingsdialog.cpp timelinesettingsdialog.h timelinesettingsmodel.cpp timelinesettingsmodel.h timelinetoolbar.cpp timelinetoolbar.h timelinetoolbutton.cpp timelinetoolbutton.h diff --git a/src/plugins/qmldesigner/QmlDesigner.json.in b/src/plugins/qmldesigner/QmlDesigner.json.in index 9770f14a2c4..3dea4ee8885 100644 --- a/src/plugins/qmldesigner/QmlDesigner.json.in +++ b/src/plugins/qmldesigner/QmlDesigner.json.in @@ -1,27 +1,27 @@ { - \"Name\" : \"QmlDesigner\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlDesigner", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Visual Designer for QML files.\", - \"DisabledByDefault\" : true, - \"Url\" : \"http://www.qt.io\", - \"Arguments\" : [ + "Category" : "Qt Quick", + "Description" : "Visual Designer for QML files.", + "DisabledByDefault" : true, + "Url" : "http://www.qt.io", + "Arguments" : [ { - \"Name\" : \"-capture-puppet-stream\", - \"Parameter\" : \"capture file\", - \"Description\" : \"Captures the Qml Puppet stream\" + "Name" : "-capture-puppet-stream", + "Parameter" : "capture file", + "Description" : "Captures the Qml Puppet stream" } ], - $$dependencyList + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index 3ab1d1a6869..ea0899d07c6 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -38,8 +38,10 @@ namespace QmlDesigner { AssetExporterPlugin::AssetExporterPlugin() { - ProjectExplorer::TaskHub::addCategory( Constants::TASK_CATEGORY_ASSET_EXPORT, - tr("Asset Export"), false); + ProjectExplorer::TaskHub::addCategory({Constants::TASK_CATEGORY_ASSET_EXPORT, + tr("Asset Export"), + tr("Issues with exporting assets."), + false}); auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); auto &viewManager = designerPlugin->viewManager(); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 71cde5f0ba8..1d6fc0dd77d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -115,7 +115,7 @@ void AssetsLibraryModel::deleteFiles(const QStringList &filePaths, bool dontAskA QmlDesignerPlugin::settings().insert(DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, false); for (const QString &filePath : filePaths) { - if (QFile::exists(filePath) && !QFile::remove(filePath)) { + if (QFileInfo::exists(filePath) && !QFile::remove(filePath)) { QMessageBox::warning(Core::ICore::dialogParent(), tr("Failed to Delete File"), tr("Could not delete \"%1\".").arg(filePath)); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp index 75976fd7210..4a119d88a53 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp @@ -86,7 +86,7 @@ bool fileComponentExists(const ModelNode &modelNode) if (fileName.contains("qml/QtQuick")) return false; - return QFile::exists(fileName); + return QFileInfo::exists(fileName); } bool selectionIsComponent(const SelectionContext &selectionState) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index fa2d7f854c4..12bb4d54604 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -51,7 +51,7 @@ #include "projectexplorer/target.h" #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/fileutils.h> @@ -77,6 +77,8 @@ #include <bindingeditor/signallist.h> +using namespace Utils; + namespace QmlDesigner { namespace { @@ -1694,8 +1696,8 @@ QString getEffectIcon(const QString &effectPath) bool useLayerEffect() { - QSettings *settings = Core::ICore::settings(); - const QString layerEffectEntry = "QML/Designer/UseLayerEffect"; + QtcSettings *settings = Core::ICore::settings(); + const Key layerEffectEntry = "QML/Designer/UseLayerEffect"; return settings->value(layerEffectEntry, true).toBool(); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp index 75046f92bee..c92a675ecd7 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp @@ -17,6 +17,7 @@ #include <viewmanager.h> #include <qmldesignerconstants.h> #include <qmldesignerplugin.h> +#include <qmldesignertr.h> #include <studioquickwidget.h> @@ -127,8 +128,8 @@ private: errorString += "\n" + error.toString(); Core::AsynchronousMessageBox::warning( - tr("Cannot Create QtQuick View"), - tr("ConnectionsEditorWidget: %1 cannot be created.%2") + Tr::tr("Cannot Create QtQuick View"), + Tr::tr("ConnectionsEditorWidget: %1 cannot be created.%2") .arg(qmlSourcesPath(), errorString)); return; } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp index 800d399df2f..dee10006068 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp @@ -305,7 +305,7 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir) for (const QString &s : std::as_const(sharedFiles)) { const QString fullSharedFilePath = matBundleDir.filePath(s); - if (!QFile::exists(fullSharedFilePath)) + if (!QFileInfo::exists(fullSharedFilePath)) missingSharedFiles.push_back(s); } diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp index 572572a4c23..81be76ed320 100644 --- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp +++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp @@ -26,7 +26,7 @@ #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #endif #include <QVector3D> diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index eb48b5a00b9..319a4d510a2 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -31,7 +31,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/kit.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> @@ -746,7 +746,7 @@ void Edit3DView::createEdit3DActions() QmlDesigner::Constants::EDIT3D_EDIT_CAMERA, View3DActionType::CameraToggle, QCoreApplication::translate("CameraToggleAction", - "Toggle Perspective/Orthographic Edit Camera"), + "Toggle Perspective/Orthographic Camera Mode"), QKeySequence(Qt::Key_T), true, false, diff --git a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp index 919a74193a3..5ba29bb0dff 100644 --- a/src/plugins/qmldesigner/components/eventlist/eventlist.cpp +++ b/src/plugins/qmldesigner/components/eventlist/eventlist.cpp @@ -26,7 +26,7 @@ Utils::FilePath projectFilePath() if (auto *proj = ProjectExplorer::ProjectManager::projectForFile(doc->fileName())) return proj->projectDirectory(); } - return Utils::FilePath(); + return {}; } static Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp index 76b8f9a5f64..0d486b0f4f9 100644 --- a/src/plugins/qmldesigner/components/formeditor/movetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp @@ -355,10 +355,9 @@ QList<FormEditorItem*> MoveTool::movingItems(const QList<FormEditorItem*> &selec if (ancestorItem != nullptr && ancestorItem->qmlItemNode().isRootNode()) { // view()->changeToSelectionTool(); - return QList<FormEditorItem*>(); + return {}; } - if (ancestorItem != nullptr && ancestorItem->parentItem() != nullptr) { QList<FormEditorItem*> ancestorItemList; ancestorItemList.append(ancestorItem); @@ -367,7 +366,7 @@ QList<FormEditorItem*> MoveTool::movingItems(const QList<FormEditorItem*> &selec if (!haveSameParent(filteredItemList)) { // view()->changeToSelectionTool(); - return QList<FormEditorItem*>(); + return {}; } return filteredItemList; diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 854a210565d..91ac4d8f160 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -25,7 +25,7 @@ #include <projectexplorer/target.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/kit.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionmanager.h> #include <coreplugin/icore.h> @@ -346,10 +346,7 @@ void DesignDocument::updateFileName(const Utils::FilePath & /*oldFileName*/, con Utils::FilePath DesignDocument::fileName() const { - if (editor()) - return editor()->document()->filePath(); - - return Utils::FilePath(); + return editor() ? editor()->document()->filePath() : Utils::FilePath(); } ProjectExplorer::Target *DesignDocument::currentTarget() const diff --git a/src/plugins/qmldesigner/components/itemlibrary/assetimportupdatetreeview.cpp b/src/plugins/qmldesigner/components/itemlibrary/assetimportupdatetreeview.cpp index 89f36c7437b..a683ab2b8b3 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/assetimportupdatetreeview.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/assetimportupdatetreeview.cpp @@ -16,7 +16,6 @@ AssetImportUpdateTreeView::AssetImportUpdateTreeView(QWidget *parent) { setModel(m_model); setItemDelegate(new AssetImportUpdateTreeItemDelegate(this)); - setUniformRowHeights(true); setExpandsOnDoubleClick(true); header()->hide(); } diff --git a/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp b/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp index e0e090338ff..ee968e38548 100644 --- a/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp +++ b/src/plugins/qmldesigner/components/pathtool/cubicsegment.cpp @@ -247,15 +247,15 @@ QPointF CubicSegment::sample(double t) const double CubicSegment::minimumDistance(const QPointF &pickPoint, double &tReturnValue) const { double actualMinimumDistance = 10000000.; - for (double t = 0.0; t <= 1.0; t += 0.1) { - QPointF samplePoint = sample(t); - QPointF distanceVector = pickPoint - samplePoint; + const int tMax = 10; + for (int t = 0; t <= tMax; ++t) { + const QPointF samplePoint = sample(double(t) / tMax); + const QPointF distanceVector = pickPoint - samplePoint; if (distanceVector.manhattanLength() < actualMinimumDistance) { actualMinimumDistance = distanceVector.manhattanLength(); tReturnValue = t; } } - return actualMinimumDistance; } diff --git a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp index f14e423c9f0..b74190bb911 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/aligndistribute.cpp @@ -19,6 +19,8 @@ #include <cmath> +using namespace Utils; + namespace QmlDesigner { AlignDistribute::AlignDistribute(QObject *parent) @@ -663,7 +665,7 @@ AlignDistribute::Dimension AlignDistribute::getDimension(Target target) const bool AlignDistribute::executePixelPerfectDialog() const { - Utils::CheckableDecider decider(QString("WarnAboutPixelPerfectDistribution")); + Utils::CheckableDecider decider(Key("WarnAboutPixelPerfectDistribution")); QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question( Core::ICore::dialogParent(), diff --git a/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp index f2f5ba77063..8b48ca0ce7f 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/colorpalettebackend.cpp @@ -4,7 +4,6 @@ #include "colorpalettebackend.h" #include <QDebug> -#include <QSettings> #include <QColorDialog> #include <QTimer> diff --git a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetlistmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetlistmodel.cpp index b4b41773321..da6e67993e6 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/gradientpresetlistmodel.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/gradientpresetlistmodel.cpp @@ -7,7 +7,6 @@ #include <QHash> #include <QByteArray> #include <QDebug> -#include <QSettings> GradientPresetListModel::GradientPresetListModel(QObject *parent) : QAbstractListModel(parent) diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 52323a3615e..03199d274d1 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -131,7 +131,7 @@ QStringList PropertyEditorContextObject::autoComplete(const QString &text, int p return Utils::filtered(m_model->rewriterView()->autoComplete(text, pos, explicitComplete), [filter](const QString &string) { return !filter || (!string.isEmpty() && string.at(0).isUpper()); }); - return QStringList(); + return {}; } void PropertyEditorContextObject::toogleExportAlias() diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp index 2ec6f0e6a02..c317374c8da 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditormodel.cpp @@ -219,7 +219,7 @@ QStringList StatesEditorModel::autoComplete(const QString &text, int pos, bool e if (model && model->rewriterView()) return model->rewriterView()->autoComplete(text, pos, explicitComplete); - return QStringList(); + return {}; } QVariant StatesEditorModel::stateModelNode(int internalNodeId) diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp index 03cd658f22a..5df44802e4b 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp @@ -499,7 +499,7 @@ bool StatesEditorView::hasExtend() const QStringList StatesEditorView::extendedStates() const { if (!model()) - return QStringList(); + return {}; QStringList states; diff --git a/src/plugins/qmldesigner/components/timelineeditor/easingcurve.cpp b/src/plugins/qmldesigner/components/timelineeditor/easingcurve.cpp index ae9833e46ec..133beaae154 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/easingcurve.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/easingcurve.cpp @@ -65,15 +65,14 @@ int EasingCurve::segmentCount() const bool EasingCurve::isLegal() const { QPainterPath painterPath(path()); - - double increment = 1.0 / 30.0; - QPointF max = painterPath.pointAtPercent(0.0); - for (double i = increment; i <= 1.0; i += increment) { - QPointF current = painterPath.pointAtPercent(i); - if (current.x() < max.x()) + qreal maxX = painterPath.pointAtPercent(0.0).x(); + const int denominator = 30; + for (int i = 1; i <= denominator; ++i) { + const qreal currentX = painterPath.pointAtPercent(qreal(i) / denominator).x(); + if (currentX < maxX) return false; else - max = current; + maxX = currentX; } return true; } diff --git a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp index 0dd61b61eb7..0f12c6ce2de 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/preseteditor.cpp @@ -434,7 +434,7 @@ QList<NamedEasingCurve> PresetList::storedCurves() const QVariant presetSettings = settings.value(Internal::settingsKey); if (!presetSettings.isValid()) - return QList<NamedEasingCurve>(); + return {}; QList<QVariant> presets = presetSettings.toList(); diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp index dbb34c02c8e..83551378eea 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "timelineanimationform.h" -#include "ui_timelineanimationform.h" #include <abstractview.h> #include <bindingproperty.h> @@ -18,42 +17,118 @@ #include <coreplugin/messagebox.h> #include <utils/algorithm.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> +#include <QCheckBox> +#include <QSpinBox> +#include <QLabel> +#include <QLineEdit> +#include <QComboBox> + namespace QmlDesigner { TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) : QWidget(parent) - , ui(new Ui::TimelineAnimationForm) { - ui->setupUi(this); + constexpr int minimumLabelWidth = 160; + constexpr int spinBoxWidth = 80; - connectSpinBox(ui->duration, "duration"); - connectSpinBox(ui->loops, "loops"); + auto mainL = new QLabel(tr("Animation Settings")); + QFont f = mainL->font(); + f.setBold(true); + mainL->setFont(f); - connectSpinBox(ui->startFrame, "from"); - connectSpinBox(ui->endFrame, "to"); + auto idL = new QLabel(tr("Animation ID:")); + idL->setToolTip(tr("Name for the animation.")); + idL->setMinimumWidth(minimumLabelWidth); + m_idLineEdit = new QLineEdit; - connect(ui->loops, &QSpinBox::valueChanged, this, [this] { - ui->continuous->setChecked(ui->loops->value() == -1); + auto runningL = new QLabel(tr("Running in base state")); + runningL->setToolTip( + tr("Runs the animation automatically when the base state is active.")); + m_running = new QCheckBox; + m_running->setEnabled(false); + + auto startFrameL = new QLabel(tr("Start frame:")); + startFrameL->setToolTip(tr("First frame of the animation.")); + m_startFrame = new QSpinBox; + m_startFrame->setFixedWidth(spinBoxWidth); + m_startFrame->setRange(-100000, 100000); + + auto endFrameL = new QLabel(tr("End frame:")); + endFrameL->setToolTip(tr("Last frame of the animation.")); + m_endFrame = new QSpinBox; + m_endFrame->setFixedWidth(spinBoxWidth); + m_endFrame->setRange(-100000, 100000); + + auto durationL = new QLabel(tr("Duration:")); + durationL->setToolTip(tr("Length of the animation in milliseconds. If you set a shorter duration than the number of frames, frames are left out from the end of the animation.")); + m_duration = new QSpinBox; + m_duration->setFixedWidth(spinBoxWidth); + m_duration->setRange(0, 100000); + + auto continuousL = new QLabel(tr("Continuous")); + continuousL->setToolTip(tr("Sets the animation to loop indefinitely.")); + m_continuous = new QCheckBox; + + auto loopsL = new QLabel(tr("Loops:")); + loopsL->setToolTip(tr("Number of times the animation runs before it stops.")); + m_loops = new QSpinBox; + m_loops->setFixedWidth(spinBoxWidth); + m_loops->setRange(-1, 1000); + + auto pingPongL = new QLabel(tr("Ping pong")); + pingPongL->setToolTip( + tr("Runs the animation backwards to the beginning when it reaches the end.")); + m_pingPong = new QCheckBox; + + auto transitionToStateL = new QLabel(tr("Finished:")); + transitionToStateL->setToolTip(tr("State to activate when the animation finishes.")); + m_transitionToState = new QComboBox; + m_transitionToState->addItem(tr("none")); + + auto str = new QWidget; + QSizePolicy sp(QSizePolicy::MinimumExpanding, QSizePolicy::Ignored); + sp.setHorizontalStretch(2); + str->setSizePolicy(sp); + + using namespace Layouting; + Grid { + Span(4, mainL), br, + empty(), br, + idL, Span(2, m_idLineEdit), Span(2, Row{ runningL, m_running }), br, + empty(), startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br, + empty(), continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br, + tr("Transition to state:"), transitionToStateL, m_transitionToState, br, + }.attachTo(this); + + connectSpinBox(m_duration, "duration"); + connectSpinBox(m_loops, "loops"); + + connectSpinBox(m_startFrame, "from"); + connectSpinBox(m_endFrame, "to"); + + connect(m_loops, &QSpinBox::valueChanged, this, [this] { + m_continuous->setChecked(m_loops->value() == -1); }); - connect(ui->continuous, &QCheckBox::toggled, this, [this](bool checked) { + connect(m_continuous, &QCheckBox::toggled, this, [this](bool checked) { if (checked) { setProperty("loops", -1); - ui->loops->setValue(-1); + m_loops->setValue(-1); } else { setProperty("loops", 1); - ui->loops->setValue(1); + m_loops->setValue(1); } }); - connect(ui->idLineEdit, &QLineEdit::editingFinished, this, [this] { + connect(m_idLineEdit, &QLineEdit::editingFinished, this, [this] { QTC_ASSERT(m_timeline.isValid(), return ); static QString lastString; - const QString newId = ui->idLineEdit->text(); + const QString newId = m_idLineEdit->text(); if (lastString == newId) return; @@ -79,11 +154,11 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) if (error) { lastString.clear(); - ui->idLineEdit->setText(animation().id()); + m_idLineEdit->setText(animation().id()); } }); - connect(ui->running, &QCheckBox::clicked, this, [this](bool checked) { + connect(m_running, &QCheckBox::clicked, this, [this](bool checked) { if (checked) { setProperty("running", true); } else { @@ -91,7 +166,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) } }); - connect(ui->pingPong, &QCheckBox::clicked, this, [this](bool checked) { + connect(m_pingPong, &QCheckBox::clicked, this, [this](bool checked) { if (checked) { setProperty("pingPong", true); } else { @@ -99,7 +174,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) } }); - connect(ui->transitionToState, &QComboBox::activated, this, [this](int index) { + connect(m_transitionToState, &QComboBox::activated, this, [this](int index) { if (!m_animation.isValid()) return; if (!m_animation.view()->rootModelNode().hasId()) @@ -116,16 +191,11 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent) } else { m_animation.signalHandlerProperty("onFinished") .setSource(rootNode.id() + ".state = \"" - + ui->transitionToState->currentText() + "\""); + + m_transitionToState->currentText() + "\""); } }); } -TimelineAnimationForm::~TimelineAnimationForm() -{ - delete ui; -} - void TimelineAnimationForm::setup(const ModelNode &animation) { m_timeline = QmlTimeline(animation.parentProperty().parentModelNode()); @@ -151,40 +221,40 @@ void TimelineAnimationForm::setupAnimation() if (m_animation.isValid()) { setEnabled(true); - ui->idLineEdit->setText(m_animation.id()); + m_idLineEdit->setText(m_animation.id()); if (m_animation.hasVariantProperty("duration")) - ui->duration->setValue(m_animation.variantProperty("duration").value().toInt()); + m_duration->setValue(m_animation.variantProperty("duration").value().toInt()); else - ui->duration->setValue(0); + m_duration->setValue(0); - ui->startFrame->setValue(m_animation.variantProperty("from").value().toInt()); - ui->endFrame->setValue(m_animation.variantProperty("to").value().toInt()); + m_startFrame->setValue(m_animation.variantProperty("from").value().toInt()); + m_endFrame->setValue(m_animation.variantProperty("to").value().toInt()); if (m_animation.hasVariantProperty("loops")) - ui->loops->setValue(m_animation.variantProperty("loops").value().toInt()); + m_loops->setValue(m_animation.variantProperty("loops").value().toInt()); else - ui->loops->setValue(0); + m_loops->setValue(0); if (m_animation.hasVariantProperty("running")) - ui->running->setChecked(m_animation.variantProperty("running").value().toBool()); + m_running->setChecked(m_animation.variantProperty("running").value().toBool()); else - ui->running->setChecked(false); + m_running->setChecked(false); if (m_animation.hasVariantProperty("pingPong")) - ui->pingPong->setChecked(m_animation.variantProperty("pingPong").value().toBool()); + m_pingPong->setChecked(m_animation.variantProperty("pingPong").value().toBool()); else - ui->pingPong->setChecked(false); + m_pingPong->setChecked(false); - ui->continuous->setChecked(ui->loops->value() == -1); + m_continuous->setChecked(m_loops->value() == -1); } populateStateComboBox(); - ui->duration->setEnabled(m_animation.isValid()); - ui->running->setEnabled(m_animation.isValid()); - ui->continuous->setEnabled(m_animation.isValid()); - ui->loops->setEnabled(m_animation.isValid()); + m_duration->setEnabled(m_animation.isValid()); + m_running->setEnabled(m_animation.isValid()); + m_continuous->setEnabled(m_animation.isValid()); + m_loops->setEnabled(m_animation.isValid()); } void TimelineAnimationForm::setProperty(const PropertyName &propertyName, const QVariant &value) @@ -207,15 +277,15 @@ void TimelineAnimationForm::connectSpinBox(QSpinBox *spinBox, const PropertyName void TimelineAnimationForm::populateStateComboBox() { - ui->transitionToState->clear(); - ui->transitionToState->addItem(tr("none")); - ui->transitionToState->addItem(tr("Base State")); + m_transitionToState->clear(); + m_transitionToState->addItem(tr("none")); + m_transitionToState->addItem(tr("Base State")); if (!m_animation.isValid()) return; QmlObjectNode rootNode = QmlObjectNode(m_animation.view()->rootModelNode()); if (rootNode.isValid() && rootNode.modelNode().hasId()) { for (const QmlModelState &state : QmlVisualNode(rootNode).states().allStates()) { - ui->transitionToState + m_transitionToState ->addItem(state.modelNode().variantProperty("name").value().toString(), QVariant::fromValue<ModelNode>(state.modelNode())); } @@ -227,9 +297,9 @@ void TimelineAnimationForm::populateStateComboBox() name.chop(1); name.remove(0, 1); if (name.isEmpty()) - ui->transitionToState->setCurrentIndex(1); + m_transitionToState->setCurrentIndex(1); else - ui->transitionToState->setCurrentText(name); + m_transitionToState->setCurrentText(name); } } } diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.h b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.h index bc1fc08d0ca..8cdb4c1d5f0 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.h @@ -7,21 +7,21 @@ #include <QWidget> -QT_FORWARD_DECLARE_CLASS(QSpinBox) +QT_BEGIN_NAMESPACE +class QCheckBox; +class QComboBox; +class QLineEdit; +class QSpinBox; +QT_END_NAMESPACE namespace QmlDesigner { -namespace Ui { -class TimelineAnimationForm; -} - class TimelineAnimationForm : public QWidget { Q_OBJECT public: explicit TimelineAnimationForm(QWidget *parent); - ~TimelineAnimationForm() override; void setup(const ModelNode &animation); ModelNode animation() const; @@ -33,7 +33,15 @@ private: void connectSpinBox(QSpinBox *spinBox, const PropertyName &propertyName); void populateStateComboBox(); - Ui::TimelineAnimationForm *ui; + QLineEdit *m_idLineEdit; + QCheckBox *m_running; + QSpinBox *m_startFrame; + QSpinBox *m_endFrame; + QSpinBox *m_duration; + QCheckBox *m_continuous; + QSpinBox *m_loops; + QCheckBox *m_pingPong; + QComboBox *m_transitionToState; QmlTimeline m_timeline; ModelNode m_animation; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.ui b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.ui deleted file mode 100644 index 3329d30af66..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.ui +++ /dev/null @@ -1,354 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QmlDesigner::TimelineAnimationForm</class> - <widget class="QWidget" name="QmlDesigner::TimelineAnimationForm"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>641</width> - <height>176</height> - </rect> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="4" column="3"> - <widget class="QLabel" name="label_16"> - <property name="toolTip"> - <string>Number of times the animation runs before it stops.</string> - </property> - <property name="text"> - <string>Loops:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="label_10"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="4" column="4" colspan="2"> - <widget class="QSpinBox" name="loops"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-1</number> - </property> - <property name="maximum"> - <number>1000</number> - </property> - </widget> - </item> - <item row="4" column="1"> - <widget class="QLabel" name="label_15"> - <property name="toolTip"> - <string>Sets the animation to loop indefinitely.</string> - </property> - <property name="text"> - <string>Continuous</string> - </property> - </widget> - </item> - <item row="4" column="2"> - <widget class="QCheckBox" name="continuous"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="5" column="2"> - <widget class="QComboBox" name="transitionToState"> - <item> - <property name="text"> - <string>none</string> - </property> - </item> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_2"> - <property name="minimumSize"> - <size> - <width>160</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Animation Settings</string> - </property> - </widget> - </item> - <item row="1" column="9"> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>213</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_11"> - <property name="minimumSize"> - <size> - <width>140</width> - <height>0</height> - </size> - </property> - <property name="toolTip"> - <string>Name for the animation.</string> - </property> - <property name="text"> - <string>Animation ID:</string> - </property> - </widget> - </item> - <item row="3" column="9"> - <spacer name="horizontalSpacer_7"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>213</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="3" column="8"> - <widget class="QSpinBox" name="duration"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>0</number> - </property> - <property name="maximum"> - <number>100000</number> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QLabel" name="label_23"> - <property name="toolTip"> - <string>State to activate when the animation finishes.</string> - </property> - <property name="text"> - <string>Finished:</string> - </property> - </widget> - </item> - <item row="4" column="7"> - <widget class="QLabel" name="label_17"> - <property name="toolTip"> - <string>Runs the animation backwards to the beginning when it reaches the end.</string> - </property> - <property name="text"> - <string>Ping pong</string> - </property> - </widget> - </item> - <item row="4" column="8"> - <widget class="QCheckBox" name="pingPong"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="4" column="9"> - <spacer name="horizontalSpacer_8"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>213</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_20"> - <property name="minimumSize"> - <size> - <width>140</width> - <height>0</height> - </size> - </property> - <property name="text"> - <string>Transition to state:</string> - </property> - </widget> - </item> - <item row="2" column="1" colspan="2"> - <widget class="QLineEdit" name="idLineEdit"> - <property name="text"> - <string>animation02</string> - </property> - </widget> - </item> - <item row="2" column="3" colspan="2"> - <widget class="QLabel" name="label_18"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="toolTip"> - <string>Runs the animation automatically when the base state is active.</string> - </property> - <property name="text"> - <string>Running in base state</string> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QSpinBox" name="startFrame"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-100000</number> - </property> - <property name="maximum"> - <number>100000</number> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLabel" name="label_12"> - <property name="toolTip"> - <string>First frame of the animation.</string> - </property> - <property name="text"> - <string>Start frame:</string> - </property> - </widget> - </item> - <item row="2" column="5"> - <widget class="QCheckBox" name="running"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="2" column="9"> - <spacer name="horizontalSpacer_6"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>213</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="3" column="7"> - <widget class="QLabel" name="label_14"> - <property name="toolTip"> - <string>Length of the animation in milliseconds. If you set a shorter duration than the number of frames, frames are left out from the end of the animation.</string> - </property> - <property name="text"> - <string>Duration:</string> - </property> - </widget> - </item> - <item row="3" column="3"> - <widget class="QLabel" name="label_13"> - <property name="toolTip"> - <string>Last frame of the animation.</string> - </property> - <property name="text"> - <string>End frame:</string> - </property> - </widget> - </item> - <item row="3" column="4" colspan="3"> - <widget class="QSpinBox" name="endFrame"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-100000</number> - </property> - <property name="maximum"> - <number>100000</number> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp index 9068dd7be18..188bd496190 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "timelineform.h" -#include "ui_timelineform.h" #include <abstractview.h> #include <bindingproperty.h> @@ -15,22 +14,80 @@ #include <coreplugin/messagebox.h> #include <utils/algorithm.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> +#include <QLabel> +#include <QLineEdit> +#include <QRadioButton> +#include <QSpinBox> + namespace QmlDesigner { TimelineForm::TimelineForm(QWidget *parent) : QWidget(parent) - , ui(new Ui::TimelineForm) { - ui->setupUi(this); + constexpr int minimumLabelWidth = 160; + constexpr int spinBoxWidth = 80; - connect(ui->expressionBindingLineEdit, &QLineEdit::editingFinished, [this]() { + auto mainL = new QLabel(tr("Timeline Settings")); + QFont f = mainL->font(); + f.setBold(true); + mainL->setFont(f); + + auto idL = new QLabel(tr("Timeline ID:")); + idL->setToolTip(tr("Name for the timeline.")); + m_idLineEdit = new QLineEdit; + + auto startFrameL = new QLabel(tr("Start frame:")); + startFrameL->setToolTip(tr("First frame of the timeline. Negative numbers are allowed.")); + m_startFrame = new QSpinBox; + m_startFrame->setFixedWidth(spinBoxWidth); + m_startFrame->setRange(-100000, 100000); + + auto endFrameL = new QLabel(tr("End frame:")); + endFrameL->setToolTip(tr("Last frame of the timeline.")); + m_endFrame = new QSpinBox; + m_endFrame->setFixedWidth(spinBoxWidth); + m_endFrame->setRange(-100000, 100000); + + m_expressionBinding = new QRadioButton(tr("Expression binding")); + m_expressionBinding->setToolTip(tr("To create an expression binding animation, delete all animations from this timeline.")); + m_expressionBinding->setEnabled(false); + + m_animation = new QRadioButton(tr("Animation")); + m_animation->setEnabled(false); + m_animation->setChecked(true); + + QSizePolicy sp(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + + auto expressionBindingL = new QLabel(tr("Expression binding:")); + expressionBindingL->setToolTip(tr("Sets the expression to bind the current keyframe to.")); + expressionBindingL->setMinimumWidth(minimumLabelWidth); + m_expressionBindingLineEdit = new QLineEdit; + m_expressionBindingLineEdit->setMinimumWidth(240); + sp.setHorizontalStretch(2); + m_expressionBindingLineEdit->setSizePolicy(sp); + + auto str = new QWidget; + sp.setHorizontalStretch(1); + str->setSizePolicy(sp); + + using namespace Layouting; + Grid { + Span(2, mainL), br, + idL, m_idLineEdit, br, + empty(), Row { startFrameL, m_startFrame, st(), endFrameL, m_endFrame }, str, br, + empty(), Row { m_expressionBinding, m_animation, st() }, br, + expressionBindingL, m_expressionBindingLineEdit, br, + }.attachTo(this); + + connect(m_expressionBindingLineEdit, &QLineEdit::editingFinished, [this]() { QTC_ASSERT(m_timeline.isValid(), return ); - const QString bindingText = ui->expressionBindingLineEdit->text(); + const QString bindingText = m_expressionBindingLineEdit->text(); if (bindingText.isEmpty()) { - ui->animation->setChecked(true); + m_animation->setChecked(true); try { m_timeline.modelNode().removeProperty("currentFrame"); } catch (const Exception &e) { @@ -39,7 +96,7 @@ TimelineForm::TimelineForm(QWidget *parent) return; } - ui->expressionBinding->setChecked(true); + m_expressionBinding->setChecked(true); try { m_timeline.modelNode() @@ -50,12 +107,12 @@ TimelineForm::TimelineForm(QWidget *parent) } }); - connect(ui->idLineEdit, &QLineEdit::editingFinished, [this]() { + connect(m_idLineEdit, &QLineEdit::editingFinished, [this]() { QTC_ASSERT(m_timeline.isValid(), return ); static QString lastString; - const QString newId = ui->idLineEdit->text(); + const QString newId = m_idLineEdit->text(); if (newId == lastString) return; @@ -81,37 +138,32 @@ TimelineForm::TimelineForm(QWidget *parent) if (error) { lastString.clear(); - ui->idLineEdit->setText(m_timeline.modelNode().id()); + m_idLineEdit->setText(m_timeline.modelNode().id()); } }); - connectSpinBox(ui->startFrame, "startFrame"); - connectSpinBox(ui->endFrame, "endFrame"); -} - -TimelineForm::~TimelineForm() -{ - delete ui; + connectSpinBox(m_startFrame, "startFrame"); + connectSpinBox(m_endFrame, "endFrame"); } void TimelineForm::setTimeline(const QmlTimeline &timeline) { m_timeline = timeline; - ui->expressionBindingLineEdit->clear(); + m_expressionBindingLineEdit->clear(); if (m_timeline.isValid()) { - ui->idLineEdit->setText(m_timeline.modelNode().displayName()); - ui->startFrame->setValue( + m_idLineEdit->setText(m_timeline.modelNode().displayName()); + m_startFrame->setValue( m_timeline.modelNode().variantProperty("startFrame").value().toInt()); - ui->endFrame->setValue(m_timeline.modelNode().variantProperty("endFrame").value().toInt()); + m_endFrame->setValue(m_timeline.modelNode().variantProperty("endFrame").value().toInt()); if (m_timeline.modelNode().hasBindingProperty("currentFrame")) { - ui->expressionBindingLineEdit->setText( + m_expressionBindingLineEdit->setText( m_timeline.modelNode().bindingProperty("currentFrame").expression()); - ui->expressionBinding->setChecked(true); + m_expressionBinding->setChecked(true); } else { - ui->expressionBinding->setChecked(false); + m_expressionBinding->setChecked(false); } } } @@ -123,9 +175,9 @@ QmlTimeline TimelineForm::timeline() const void TimelineForm::setHasAnimation(bool b) { - ui->expressionBinding->setChecked(!b); - ui->animation->setChecked(b); - ui->expressionBindingLineEdit->setDisabled(b); + m_expressionBinding->setChecked(!b); + m_animation->setChecked(b); + m_expressionBindingLineEdit->setDisabled(b); } void TimelineForm::setProperty(const PropertyName &propertyName, const QVariant &value) diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.h b/src/plugins/qmldesigner/components/timelineeditor/timelineform.h index 5e5f1b977bd..b025bbc2d62 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.h @@ -7,21 +7,20 @@ #include <QWidget> -QT_FORWARD_DECLARE_CLASS(QSpinBox) +QT_BEGIN_NAMESPACE +class QLineEdit; +class QRadioButton; +class QSpinBox; +QT_END_NAMESPACE namespace QmlDesigner { -namespace Ui { -class TimelineForm; -} - class TimelineForm : public QWidget { Q_OBJECT public: explicit TimelineForm(QWidget *parent); - ~TimelineForm() override; void setTimeline(const QmlTimeline &timeline); QmlTimeline timeline() const; void setHasAnimation(bool b); @@ -30,7 +29,13 @@ private: void setProperty(const PropertyName &propertyName, const QVariant &value); void connectSpinBox(QSpinBox *spinBox, const PropertyName &propertyName); - Ui::TimelineForm *ui; + QLineEdit *m_idLineEdit; + QSpinBox *m_startFrame; + QSpinBox *m_endFrame; + QRadioButton *m_expressionBinding; + QRadioButton *m_animation; + QLineEdit *m_expressionBindingLineEdit; + QmlTimeline m_timeline; }; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.ui b/src/plugins/qmldesigner/components/timelineeditor/timelineform.ui deleted file mode 100644 index 53877b5695e..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.ui +++ /dev/null @@ -1,237 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QmlDesigner::TimelineForm</class> - <widget class="QWidget" name="QmlDesigner::TimelineForm"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>641</width> - <height>170</height> - </rect> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="3" column="6" colspan="2"> - <spacer name="horizontalSpacer_12"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>49</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="1" column="6" colspan="2"> - <spacer name="horizontalSpacer_11"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>49</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="2" column="4"> - <widget class="QLabel" name="label_7"> - <property name="toolTip"> - <string>Last frame of the timeline.</string> - </property> - <property name="text"> - <string>End frame:</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_6"> - <property name="toolTip"> - <string>First frame of the timeline. Negative numbers are allowed.</string> - </property> - <property name="text"> - <string>Start frame:</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="2" column="2" colspan="2"> - <widget class="QSpinBox" name="startFrame"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-100000</number> - </property> - <property name="maximum"> - <number>100000</number> - </property> - </widget> - </item> - <item row="3" column="1" colspan="2"> - <widget class="QRadioButton" name="expressionBinding"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="toolTip"> - <string>To create an expression binding animation, delete all animations from this timeline.</string> - </property> - <property name="text"> - <string>Expression binding</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_5"> - <property name="toolTip"> - <string>Name for the timeline.</string> - </property> - <property name="text"> - <string>Timeline ID:</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="minimumSize"> - <size> - <width>160</width> - <height>0</height> - </size> - </property> - <property name="font"> - <font> - <weight>75</weight> - <bold>true</bold> - </font> - </property> - <property name="text"> - <string>Timeline Settings</string> - </property> - </widget> - </item> - <item row="3" column="3" colspan="2"> - <widget class="QRadioButton" name="animation"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="text"> - <string>Animation</string> - </property> - <property name="checked"> - <bool>true</bool> - </property> - </widget> - </item> - <item row="4" column="6" colspan="2"> - <spacer name="horizontalSpacer_9"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>49</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_8"> - <property name="toolTip"> - <string>Sets the expression to bind the current keyframe to.</string> - </property> - <property name="text"> - <string>Expression binding:</string> - </property> - <property name="alignment"> - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="2" column="5"> - <widget class="QSpinBox" name="endFrame"> - <property name="minimumSize"> - <size> - <width>80</width> - <height>0</height> - </size> - </property> - <property name="maximumSize"> - <size> - <width>80</width> - <height>16777215</height> - </size> - </property> - <property name="minimum"> - <number>-100000</number> - </property> - <property name="maximum"> - <number>100000</number> - </property> - </widget> - </item> - <item row="1" column="1" colspan="5"> - <widget class="QLineEdit" name="idLineEdit"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="4" column="1" colspan="5"> - <widget class="QLineEdit" name="expressionBindingLineEdit"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="minimumSize"> - <size> - <width>240</width> - <height>0</height> - </size> - </property> - </widget> - </item> - <item row="2" column="6" colspan="2"> - <spacer name="horizontalSpacer_10"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>49</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </widget> - <resources/> - <connections/> -</ui> diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.cpp index bd4b1c23cdd..e07d394dc13 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "timelinesettingsdialog.h" -#include "ui_timelinesettingsdialog.h" #include "timelineanimationform.h" #include "timelineform.h" @@ -19,9 +18,15 @@ #include <variantproperty.h> #include <utils/algorithm.h> +#include <utils/layoutbuilder.h> #include <utils/qtcassert.h> +#include <QDialogButtonBox> +#include <QHeaderView> #include <QKeyEvent> +#include <QSpinBox> +#include <QTabWidget> +#include <QTableView> #include <QToolBar> namespace QmlDesigner { @@ -75,12 +80,12 @@ static void setTabForAnimation(QTabWidget *tabWidget, const ModelNode &animation TimelineSettingsDialog::TimelineSettingsDialog(QWidget *parent, TimelineView *view) : QDialog(parent) - , ui(new Ui::TimelineSettingsDialog) , m_timelineView(view) { - m_timelineSettingsModel = new TimelineSettingsModel(this, view); + resize(520, 600); + setModal(true); - ui->setupUi(this); + m_timelineSettingsModel = new TimelineSettingsModel(this, view); auto *timelineCornerWidget = new QToolBar; @@ -93,7 +98,7 @@ TimelineSettingsDialog::TimelineSettingsDialog(QWidget *parent, TimelineView *vi }); connect(timelineRemoveAction, &QAction::triggered, this, [this]() { - QmlTimeline timeline = getTimelineFromTabWidget(ui->timelineTab); + QmlTimeline timeline = getTimelineFromTabWidget(m_timelineTab); if (timeline.isValid()) { timeline.destroy(); setupTimelines(QmlTimeline()); @@ -103,10 +108,10 @@ TimelineSettingsDialog::TimelineSettingsDialog(QWidget *parent, TimelineView *vi timelineCornerWidget->addAction(timelineAddAction); timelineCornerWidget->addAction(timelineRemoveAction); - ui->timelineTab->setCornerWidget(timelineCornerWidget, Qt::TopRightCorner); + m_timelineTab = new QTabWidget; + m_timelineTab->setCornerWidget(timelineCornerWidget, Qt::TopRightCorner); auto *animationCornerWidget = new QToolBar; - auto *animationAddAction = new QAction(TimelineIcons::ADD_TIMELINE.icon(), tr("Add Animation")); auto *animationRemoveAction = new QAction(TimelineIcons::REMOVE_TIMELINE.icon(), tr("Remove Animation")); @@ -119,21 +124,40 @@ TimelineSettingsDialog::TimelineSettingsDialog(QWidget *parent, TimelineView *vi }); connect(animationRemoveAction, &QAction::triggered, this, [this]() { - ModelNode node = getAnimationFromTabWidget(ui->animationTab); + ModelNode node = getAnimationFromTabWidget(m_animationTab); if (node.isValid()) { node.destroy(); setupAnimations(m_currentTimeline); } }); - ui->animationTab->setCornerWidget(animationCornerWidget, Qt::TopRightCorner); - ui->buttonBox->clearFocus(); + m_animationTab = new QTabWidget; + m_animationTab->setCornerWidget(animationCornerWidget, Qt::TopRightCorner); + + m_tableView = new QTableView; + QSizePolicy sp(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + sp.setVerticalStretch(1); + m_tableView->setSizePolicy(sp); + + auto buttonBox = new QDialogButtonBox; + buttonBox->setStandardButtons(QDialogButtonBox::Close); + buttonBox->clearFocus(); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + using namespace Layouting; + Column { + m_timelineTab, + m_animationTab, + m_tableView, + buttonBox, + }.attachTo(this); setupTimelines(QmlTimeline()); setupAnimations(m_currentTimeline); - connect(ui->timelineTab, &QTabWidget::currentChanged, this, [this]() { - m_currentTimeline = getTimelineFromTabWidget(ui->timelineTab); + connect(m_timelineTab, &QTabWidget::currentChanged, this, [this]() { + m_currentTimeline = getTimelineFromTabWidget(m_timelineTab); setupAnimations(m_currentTimeline); }); setupTableView(); @@ -142,12 +166,7 @@ TimelineSettingsDialog::TimelineSettingsDialog(QWidget *parent, TimelineView *vi void TimelineSettingsDialog::setCurrentTimeline(const QmlTimeline &timeline) { m_currentTimeline = timeline; - setTabForTimeline(ui->timelineTab, m_currentTimeline); -} - -TimelineSettingsDialog::~TimelineSettingsDialog() -{ - delete ui; + setTabForTimeline(m_timelineTab, m_currentTimeline); } void TimelineSettingsDialog::keyPressEvent(QKeyEvent *event) @@ -165,17 +184,17 @@ void TimelineSettingsDialog::keyPressEvent(QKeyEvent *event) void TimelineSettingsDialog::setupTableView() { - ui->tableView->setModel(m_timelineSettingsModel); - m_timelineSettingsModel->setupDelegates(ui->tableView); - ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - ui->tableView->verticalHeader()->hide(); - ui->tableView->setSelectionMode(QAbstractItemView::NoSelection); + m_tableView->setModel(m_timelineSettingsModel); + m_timelineSettingsModel->setupDelegates(m_tableView); + m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + m_tableView->verticalHeader()->hide(); + m_tableView->setSelectionMode(QAbstractItemView::NoSelection); m_timelineSettingsModel->resetModel(); } void TimelineSettingsDialog::setupTimelines(const QmlTimeline &timeline) { - deleteAllTabs(ui->timelineTab); + deleteAllTabs(m_timelineTab); const QList<QmlTimeline> &timelines = m_timelineView->getTimelines(); @@ -183,7 +202,7 @@ void TimelineSettingsDialog::setupTimelines(const QmlTimeline &timeline) m_currentTimeline = QmlTimeline(); auto timelineForm = new TimelineForm(this); timelineForm->setDisabled(true); - ui->timelineTab->addTab(timelineForm, tr("No Timeline")); + m_timelineTab->addTab(timelineForm, tr("No Timeline")); return; } @@ -196,14 +215,14 @@ void TimelineSettingsDialog::setupTimelines(const QmlTimeline &timeline) m_currentTimeline = timelines.constFirst(); } - setTabForTimeline(ui->timelineTab, m_currentTimeline); + setTabForTimeline(m_timelineTab, m_currentTimeline); setupAnimations(m_currentTimeline); m_timelineSettingsModel->resetModel(); } void TimelineSettingsDialog::setupAnimations(const ModelNode &animation) { - deleteAllTabs(ui->animationTab); + deleteAllTabs(m_animationTab); const QList<ModelNode> animations = m_timelineView->getAnimations(m_currentTimeline); @@ -213,7 +232,7 @@ void TimelineSettingsDialog::setupAnimations(const ModelNode &animation) if (animations.isEmpty()) { auto animationForm = new TimelineAnimationForm(this); animationForm->setDisabled(true); - ui->animationTab->addTab(animationForm, tr("No Animation")); + m_animationTab->addTab(animationForm, tr("No Animation")); if (currentTimelineForm()) currentTimelineForm()->setHasAnimation(false); } else { @@ -222,14 +241,14 @@ void TimelineSettingsDialog::setupAnimations(const ModelNode &animation) } if (animation.isValid()) - setTabForAnimation(ui->animationTab, animation); + setTabForAnimation(m_animationTab, animation); m_timelineSettingsModel->resetModel(); } void TimelineSettingsDialog::addTimelineTab(const QmlTimeline &node) { auto timelineForm = new TimelineForm(this); - ui->timelineTab->addTab(timelineForm, node.modelNode().displayName()); + m_timelineTab->addTab(timelineForm, node.modelNode().displayName()); timelineForm->setTimeline(node); setupAnimations(ModelNode()); } @@ -237,13 +256,13 @@ void TimelineSettingsDialog::addTimelineTab(const QmlTimeline &node) void TimelineSettingsDialog::addAnimationTab(const ModelNode &node) { auto timelineAnimationForm = new TimelineAnimationForm(this); - ui->animationTab->addTab(timelineAnimationForm, node.displayName()); + m_animationTab->addTab(timelineAnimationForm, node.displayName()); timelineAnimationForm->setup(node); } TimelineForm *TimelineSettingsDialog::currentTimelineForm() const { - return qobject_cast<TimelineForm *>(ui->timelineTab->currentWidget()); + return qobject_cast<TimelineForm *>(m_timelineTab->currentWidget()); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.h b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.h index a1626bc4077..e31cc3f1e5a 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.h +++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.h @@ -7,7 +7,11 @@ #include <QDialog> -QT_FORWARD_DECLARE_CLASS(QSpinBox) +QT_BEGIN_NAMESPACE +class QSpinBox; +class QTabWidget; +class QTableView; +QT_END_NAMESPACE namespace QmlDesigner { @@ -16,10 +20,6 @@ class TimelineAnimationForm; class TimelineView; class TimelineSettingsModel; -namespace Ui { -class TimelineSettingsDialog; -} - class TimelineSettingsDialog : public QDialog { Q_OBJECT @@ -27,7 +27,6 @@ class TimelineSettingsDialog : public QDialog public: explicit TimelineSettingsDialog(QWidget *parent, TimelineView *view); void setCurrentTimeline(const QmlTimeline &timeline); - ~TimelineSettingsDialog() override; protected: void keyPressEvent(QKeyEvent *event) override; @@ -42,7 +41,9 @@ private: TimelineForm *currentTimelineForm() const; - Ui::TimelineSettingsDialog *ui; + QTabWidget *m_timelineTab; + QTabWidget *m_animationTab; + QTableView *m_tableView; TimelineView *m_timelineView; QmlTimeline m_currentTimeline; diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.ui b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.ui deleted file mode 100644 index f3dfa6f0947..00000000000 --- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsdialog.ui +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>QmlDesigner::TimelineSettingsDialog</class> - <widget class="QDialog" name="QmlDesigner::TimelineSettingsDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>519</width> - <height>582</height> - </rect> - </property> - <property name="windowTitle"> - <string>Timeline Settings</string> - </property> - <property name="modal"> - <bool>true</bool> - </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTabWidget" name="timelineTab"> - <property name="currentIndex"> - <number>-1</number> - </property> - </widget> - </item> - <item> - <widget class="QTabWidget" name="animationTab"> - <property name="currentIndex"> - <number>-1</number> - </property> - </widget> - </item> - <item> - <widget class="QTableView" name="tableView"/> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Close</set> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>QmlDesigner::TimelineSettingsDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>QmlDesigner::TimelineSettingsDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp index b68dc98eaf8..9cc45d1f336 100644 --- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp +++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp @@ -482,7 +482,7 @@ QList<QmlTimeline> TimelineView::getTimelines() const QList<ModelNode> TimelineView::getAnimations(const QmlTimeline &timeline) { if (!timeline.isValid()) - return QList<ModelNode>(); + return {}; if (isAttached()) { return Utils::filtered(timeline.modelNode().directSubModelNodes(), diff --git a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp index 5facbdda571..893c1809605 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbar.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbar.cpp @@ -10,6 +10,7 @@ #include <qmldesignerconstants.h> #include <coreplugin/icore.h> + #include <utils/filepath.h> #include <utils/qtcassert.h> @@ -18,6 +19,8 @@ #include <QStatusBar> #include <QToolBar> +using namespace Utils; + namespace QmlDesigner { static Utils::FilePath propertyEditorResourcesPath() @@ -129,8 +132,8 @@ Utils::UniqueObjectPtr<QWidget> ToolBar::createStatusBar() bool ToolBar::isVisible() { - QSettings *settings = Core::ICore::settings(); - const QString qdsToolbarEntry = "QML/Designer/TopToolBar"; + QtcSettings *settings = Core::ICore::settings(); + const Key qdsToolbarEntry = "QML/Designer/TopToolBar"; return settings->value(qdsToolbarEntry, false).toBool(); } diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp index 2634629fbbb..4ef8cf311c4 100644 --- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp +++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp @@ -29,7 +29,7 @@ #include <projectexplorer/devicesupport/idevice.h> #include <qmlprojectmanager/qmlproject.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/qtcassert.h> diff --git a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp index 726c049555d..0e0547c1935 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/meshimagecachecollector.cpp @@ -6,7 +6,7 @@ #include <projectexplorer/target.h> #include <utils/smallstring.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QTemporaryFile> diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h index 32a9480d55c..2c5b429c0aa 100644 --- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h +++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h @@ -48,7 +48,7 @@ public: { return false; } QStringList autoComplete(QTextDocument * /*textDocument*/, int /*position*/, bool /*explicitComplete*/) override - { return QStringList(); } + { return {}; } bool moveToComponent(int /* nodeOffset */, const QString & /* importData */) override { return false; } diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp index c7f13c7c09c..7e65f9ce341 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp @@ -51,7 +51,7 @@ #include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <QCoreApplication> diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 88aff952a3a..2038e7f3cee 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -80,7 +80,7 @@ #include <utils/theme/theme.h> #include <utils/threadutils.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <QDirIterator> #include <QFileSystemWatcher> diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index d21d8b44f86..3a38ac06494 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -1186,11 +1186,11 @@ Model *NodeMetaInfoPrivate::model() const QStringList NodeMetaInfoPrivate::keysForEnum(const QString &enumName) const { if (!isValid()) - return QStringList(); + return {}; const CppComponentValue *qmlObjectValue = getNearestCppComponentValue(); if (!qmlObjectValue) - return QStringList(); + return {}; return qmlObjectValue->getEnum(enumName).keys(); } diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index 9a3fca77abb..936c23fa57a 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -384,7 +384,7 @@ QStringList SubComponentManager::importPaths() const if (model()) return model()->importPaths(); - return QStringList(); + return {}; } void SubComponentManager::parseQuick3DAssetsDir(const QString &quick3DAssetsPath) diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp index eb7f0604184..d0d8953415a 100644 --- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp +++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp @@ -133,5 +133,5 @@ QStringList BaseTextEditModifier::autoComplete(QTextDocument *textDocument, int document->filePath(), explicitComplete ? TextEditor::ExplicitlyInvoked : TextEditor::ActivationCharacter, document->semanticInfo()); - return QStringList(); + return {}; } diff --git a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp index 18100e5298f..51ca5eb7050 100644 --- a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp @@ -66,13 +66,13 @@ QList<ModelNode> NodeListProperty::toModelNodeList() const if (internalNodeListProperty()) return internalNodesToModelNodes(m_internalNodeListProperty->nodeList(), model(), view()); - return QList<ModelNode>(); + return {}; } QList<QmlObjectNode> NodeListProperty::toQmlObjectNodeList() const { if (model()->nodeInstanceView()) - return QList<QmlObjectNode>(); + return {}; QList<QmlObjectNode> qmlObjectNodeList; diff --git a/src/plugins/qmldesigner/designercore/pluginmanager/widgetpluginpath.cpp b/src/plugins/qmldesigner/designercore/pluginmanager/widgetpluginpath.cpp index dc96b1a4001..953e4a6bf12 100644 --- a/src/plugins/qmldesigner/designercore/pluginmanager/widgetpluginpath.cpp +++ b/src/plugins/qmldesigner/designercore/pluginmanager/widgetpluginpath.cpp @@ -104,7 +104,7 @@ QStringList WidgetPluginPath::libraryFilePaths(const QDir &dir) { const QFileInfoList infoList = dir.entryInfoList(QDir::Files|QDir::Readable|QDir::NoDotAndDotDot); if (infoList.empty()) - return QStringList(); + return {}; // Load symbolic links but make sure all file names are unique as not // to fall for something like 'libplugin.so.1 -> libplugin.so' QStringList result; diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 75ae05de56d..0ce1f5e07fd 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -361,19 +361,19 @@ QStringList DocumentManager::isoIconsQmakeVariableValue(const QString &proPath) ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(Utils::FilePath::fromString(proPath)); if (!node) { qCWarning(documentManagerLog) << "No node for .pro:" << proPath; - return QStringList(); + return {}; } ProjectExplorer::Node *parentNode = node->parentFolderNode(); if (!parentNode) { qCWarning(documentManagerLog) << "No parent node for node at" << proPath; - return QStringList(); + return {}; } auto proNode = dynamic_cast<QmakeProjectManager::QmakeProFileNode*>(parentNode); if (!proNode) { qCWarning(documentManagerLog) << "Parent node for node at" << proPath << "could not be converted to a QmakeProFileNode"; - return QStringList(); + return {}; } return proNode->variableValue(QmakeProjectManager::Variable::IsoIcons); diff --git a/src/plugins/qmldesigner/generateresource.cpp b/src/plugins/qmldesigner/generateresource.cpp index be7f536662c..de8cd5a80b4 100644 --- a/src/plugins/qmldesigner/generateresource.cpp +++ b/src/plugins/qmldesigner/generateresource.cpp @@ -19,7 +19,7 @@ #include <qmlprojectmanager/qmlprojectmanagerconstants.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/fileutils.h> #include <utils/process.h> @@ -190,9 +190,10 @@ static bool runRcc(const CommandLine &command, const FilePath &workingDir, QByteArray stdOut; QByteArray stdErr; if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) { - Core::MessageManager::writeDisrupting(QCoreApplication::translate( - "QmlDesigner::GenerateResource", "A timeout occurred running \"%1\"") - .arg(rccProcess.commandLine().toUserOutput())); + Core::MessageManager::writeDisrupting( + QCoreApplication::translate("QmlDesigner::GenerateResource", + "A timeout occurred running \"%1\".") + .arg(rccProcess.commandLine().toUserOutput())); return false; } if (!stdOut.trimmed().isEmpty()) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index f0545adadba..7e876816c97 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -7,15 +7,13 @@ #include <model.h> -#include <app/app_version.h> - #include <projectexplorer/kit.h> #include <projectexplorer/target.h> #include <utils/algorithm.h> #include <utils/hostosinfo.h> #include <qmlprojectmanager/qmlmultilanguageaspect.h> #include <qmlprojectmanager/qmlproject.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversions.h> #include <QLibraryInfo> diff --git a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp index 4b2cd9212b3..f2c96de6736 100644 --- a/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp +++ b/src/plugins/qmldesigner/qmldesignerexternaldependencies.cpp @@ -5,7 +5,6 @@ #include "qmldesignerplugin.h" -#include <app/app_version.h> #include <edit3d/edit3dviewconfig.h> #include <itemlibraryimport.h> #include <projectexplorer/kit.h> @@ -14,7 +13,7 @@ #include <puppetenvironmentbuilder.h> #include <qmlpuppetpaths.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qmlprojectmanager/buildsystem/qmlbuildsystem.h> #include <coreplugin/icore.h> diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 8c94a53ce49..c5d0048902f 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -42,7 +42,6 @@ #include <qmlprojectmanager/qmlproject.h> -#include <app/app_version.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -264,7 +263,7 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e ->addAction(cmd, Core::Constants::G_HELP_SUPPORT); connect(action, &QAction::triggered, this, [this] { - lauchFeedbackPopupInternal(Core::Constants::IDE_DISPLAY_NAME); + lauchFeedbackPopupInternal(QGuiApplication::applicationDisplayName()); }); if (!Utils::HostOsInfo::canCreateOpenGLContext(errorMessage)) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index aee98cd5a82..3cae3ecab47 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -20,7 +20,7 @@ #include <sqlitedatabase.h> #include <qmlprojectmanager/qmlproject.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <asynchronousexplicitimagecache.h> #include <asynchronousimagecache.h> diff --git a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp index ccd9a970096..f44cb0ab24d 100644 --- a/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp +++ b/src/plugins/qmldesigner/qmlpreviewplugin/qmlpreviewactions.cpp @@ -10,7 +10,7 @@ #include <utils/utilsicons.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> @@ -33,7 +33,7 @@ const QByteArray livePreviewId = "LivePreview"; static void handleAction(const SelectionContext &context) { - if (context.view()->isAttached()) { + if (context.isValid()) { if (context.toggled()) { bool skipDeploy = false; if (const Target *startupTarget = ProjectManager::startupTarget()) { diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index 2302883b0d9..19b329ad283 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -8,8 +8,6 @@ #include "qmldesignerexternaldependencies.h" #include "qmldesignerplugin.h" -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <qmljseditor/qmljseditorconstants.h> @@ -497,10 +495,11 @@ void SettingsPageWidget::apply() for (const char * const key : restartNecessaryKeys) { if (QmlDesignerPlugin::settings().value(key) != settings.value(key)) { - QMessageBox::information(Core::ICore::dialogParent(), tr("Restart Required"), - tr("The made changes will take effect after a " - "restart of the QML Emulation layer or %1.") - .arg(Core::Constants::IDE_DISPLAY_NAME)); + QMessageBox::information(Core::ICore::dialogParent(), + tr("Restart Required"), + tr("The made changes will take effect after a " + "restart of the QML Emulation layer or %1.") + .arg(QGuiApplication::applicationDisplayName())); break; } } diff --git a/src/plugins/qmldesigner/utils/fileextractor.cpp b/src/plugins/qmldesigner/utils/fileextractor.cpp index e2ba28e8876..11827fca021 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.cpp +++ b/src/plugins/qmldesigner/utils/fileextractor.cpp @@ -2,17 +2,17 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "fileextractor.h" -#include <QQmlEngine> #include <QRandomGenerator> #include <QStorageInfo> #include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginspec.h> -#include <utils/algorithm.h> -#include <utils/archive.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> +#include <utils/unarchiver.h> + +using namespace Utils; namespace QmlDesigner { @@ -22,7 +22,7 @@ FileExtractor::FileExtractor(QObject *parent) m_timer.setInterval(100); m_timer.setSingleShot(false); - QObject::connect(this, &FileExtractor::targetFolderExistsChanged, this, [this]() { + QObject::connect(this, &FileExtractor::targetFolderExistsChanged, this, [this] { if (targetFolderExists()) m_birthTime = QFileInfo(m_targetPath.toString() + "/" + m_archiveName).birthTime(); else @@ -32,7 +32,7 @@ FileExtractor::FileExtractor(QObject *parent) }); QObject::connect( - &m_timer, &QTimer::timeout, this, [this]() { + &m_timer, &QTimer::timeout, this, [this] { static QHash<QString, int> hash; QDirIterator it(m_targetFolder, {"*.*"}, QDir::Files, QDirIterator::Subdirectories); @@ -74,7 +74,7 @@ FileExtractor::~FileExtractor() {} void FileExtractor::changeTargetPath(const QString &path) { - m_targetPath = Utils::FilePath::fromString(path); + m_targetPath = FilePath::fromString(path); emit targetPathChanged(); emit targetFolderExistsChanged(); } @@ -86,7 +86,7 @@ QString FileExtractor::targetPath() const void FileExtractor::setTargetPath(const QString &path) { - m_targetPath = Utils::FilePath::fromString(path); + m_targetPath = FilePath::fromString(path); QDir dir(m_targetPath.toString()); @@ -99,9 +99,8 @@ void FileExtractor::setTargetPath(const QString &path) void FileExtractor::browse() { - const Utils::FilePath path = - Utils::FileUtils::getExistingDirectory(nullptr, tr("Choose Directory"), m_targetPath); - + const FilePath path = FileUtils::getExistingDirectory(nullptr, tr("Choose Directory"), + m_targetPath); if (!path.isEmpty()) m_targetPath = path; @@ -111,7 +110,7 @@ void FileExtractor::browse() void FileExtractor::setSourceFile(const QString &sourceFilePath) { - m_sourceFile = Utils::FilePath::fromString(sourceFilePath); + m_sourceFile = FilePath::fromString(sourceFilePath); emit targetFolderExistsChanged(); } @@ -203,7 +202,7 @@ void FileExtractor::extract() auto uniqueText = QByteArray::number(QRandomGenerator::global()->generate(), 16); QString tempFileName = QDir::tempPath() + "/.qds_" + uniqueText + "_extract_" + m_archiveName + "_dir"; - m_targetPath = Utils::FilePath::fromString(tempFileName); + m_targetPath = FilePath::fromString(tempFileName); } m_targetFolder = m_targetPath.toString() + "/" + m_archiveName; @@ -218,8 +217,12 @@ void FileExtractor::extract() targetDir.mkdir(m_targetFolder); } - Utils::Archive *archive = new Utils::Archive(m_sourceFile, m_targetPath); - QTC_ASSERT(archive->isValid(), delete archive; return); + const auto sourceAndCommand = Unarchiver::sourceAndCommand(m_sourceFile); + QTC_ASSERT(sourceAndCommand, return); + + m_unarchiver.reset(new Unarchiver); + m_unarchiver->setSourceAndCommand(*sourceAndCommand); + m_unarchiver->setDestDir(m_targetPath); m_timer.start(); m_bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); @@ -227,14 +230,14 @@ void FileExtractor::extract() if (m_compressedSize <= 0) qWarning() << "Compressed size for file '" << m_sourceFile << "' is zero or invalid: " << m_compressedSize; - QObject::connect(archive, &Utils::Archive::outputReceived, this, [this](const QString &output) { + connect(m_unarchiver.get(), &Unarchiver::outputReceived, this, [this](const QString &output) { m_detailedText += output; emit detailedTextChanged(); }); - QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) { - archive->deleteLater(); - m_finished = ret; + QObject::connect(m_unarchiver.get(), &Unarchiver::done, this, [this](bool success) { + m_unarchiver.release()->deleteLater(); + m_finished = success; m_timer.stop(); m_progress = 100; @@ -242,9 +245,9 @@ void FileExtractor::extract() emit targetFolderExistsChanged(); emit finishedChanged(); - QTC_CHECK(ret); + QTC_CHECK(success); }); - archive->unarchive(); + m_unarchiver->start(); } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/utils/fileextractor.h b/src/plugins/qmldesigner/utils/fileextractor.h index 5dbf12e903a..c02237fab4f 100644 --- a/src/plugins/qmldesigner/utils/fileextractor.h +++ b/src/plugins/qmldesigner/utils/fileextractor.h @@ -8,6 +8,8 @@ #include <utils/filepath.h> +namespace Utils { class Unarchiver; } + namespace QmlDesigner { class FileExtractor : public QObject @@ -87,6 +89,7 @@ private: qint64 m_bytesBefore = 0; qint64 m_compressedSize = 0; + std::unique_ptr<Utils::Unarchiver> m_unarchiver; }; } // QmlDesigner diff --git a/src/plugins/qmldesignerbase/QmlDesignerBase.json.in b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in index 38fb427ae1b..27c62aeef64 100644 --- a/src/plugins/qmldesignerbase/QmlDesignerBase.json.in +++ b/src/plugins/qmldesignerbase/QmlDesignerBase.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"QmlDesignerBase\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlDesignerBase", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Provides support code for the qml designer and co..\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Quick", + "Description" : "Provides support code for the qml designer and co..", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qmldesignerbase/qmldesignerbase.qbs b/src/plugins/qmldesignerbase/qmldesignerbase.qbs index 98f4cdf5562..5b029f66dc1 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbase.qbs +++ b/src/plugins/qmldesignerbase/qmldesignerbase.qbs @@ -6,7 +6,6 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "ProjectExplorer" } Depends { name: "QtSupport" } - Depends { name: "app_version_header" } Depends { name: "Qt.quickwidgets" } files: [ @@ -18,12 +17,14 @@ QtcPlugin { Group { prefix: "studio/" files: [ + "studioquickwidget.cpp", + "studioquickwidget.h", "studiosettingspage.cpp", "studiosettingspage.h", "studiostyle.cpp", "studiostyle.h", - "studioquickwidget.cpp", - "studioquickwidget.h", + "studiostyle_p.cpp", + "studiostyle_p.h", ] } Group { diff --git a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp index d94efbd666b..05db597c5b8 100644 --- a/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp +++ b/src/plugins/qmldesignerbase/studio/studiosettingspage.cpp @@ -8,8 +8,11 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/dialogs/restartdialog.h> #include <coreplugin/icore.h> + #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> + +#include <utils/qtcsettings.h> #include <utils/theme/theme.h> #include <QApplication> @@ -23,6 +26,8 @@ #include <QStyleFactory> #include <QVBoxLayout> +using namespace Utils; + namespace QmlDesigner { namespace { @@ -53,9 +58,9 @@ bool hideToolsMenuSetting() return Core::ICore::settings()->value(Core::Constants::SETTINGS_MENU_HIDE_TOOLS, false).toBool(); } -void setSettingIfDifferent(const QString &key, bool value, bool &dirty) +void setSettingIfDifferent(const Key &key, bool value, bool &dirty) { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); if (s->value(key, false).toBool() != value) { dirty = true; s->setValue(key, value); @@ -175,18 +180,18 @@ void StudioSettingsPage::apply() restartDialog.exec(); } - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); const QString value = m_pathChooserExamples->filePath().toString(); - if (s->value(Paths::exampleDownloadPath.toString(), false).toString() != value) { - s->setValue(Paths::exampleDownloadPath.toString(), value); + if (s->value(Paths::exampleDownloadPath, false).toString() != value) { + s->setValue(Paths::exampleDownloadPath, value); emit examplesDownloadPathChanged(value); } const QString bundlesPath = m_pathChooserBundles->filePath().toString(); - if (s->value(Paths::bundlesDownloadPath.toString()).toString() != bundlesPath) { - s->setValue(Paths::bundlesDownloadPath.toString(), bundlesPath); + if (s->value(Paths::bundlesDownloadPath).toString() != bundlesPath) { + s->setValue(Paths::bundlesDownloadPath, bundlesPath); emit bundlesDownloadPathChanged(bundlesPath); const QString restartText = tr("Changing bundle path will take effect after restart."); diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.cpp b/src/plugins/qmldesignerbase/utils/designerpaths.cpp index 53b4b7ea37e..da4ce8ce082 100644 --- a/src/plugins/qmldesignerbase/utils/designerpaths.cpp +++ b/src/plugins/qmldesignerbase/utils/designerpaths.cpp @@ -30,15 +30,13 @@ Utils::FilePath defaultBundlesPath() QString examplesPathSetting() { - return Core::ICore::settings() - ->value(exampleDownloadPath.toString(), defaultExamplesPath().toString()) + return Core::ICore::settings()->value(exampleDownloadPath, defaultExamplesPath().toString()) .toString(); } QString bundlesPathSetting() { - return Core::ICore::settings() - ->value(bundlesDownloadPath.toString(), defaultBundlesPath().toString()) + return Core::ICore::settings()->value(bundlesDownloadPath, defaultBundlesPath().toString()) .toString(); } diff --git a/src/plugins/qmldesignerbase/utils/designerpaths.h b/src/plugins/qmldesignerbase/utils/designerpaths.h index df53997457f..b038343b25e 100644 --- a/src/plugins/qmldesignerbase/utils/designerpaths.h +++ b/src/plugins/qmldesignerbase/utils/designerpaths.h @@ -9,8 +9,8 @@ namespace QmlDesigner::Paths { -inline constexpr QStringView exampleDownloadPath = u"StudioConfig/ExamplesDownloadPath"; -inline constexpr QStringView bundlesDownloadPath = u"StudioConfig/BundlesDownloadPath"; +constexpr char exampleDownloadPath[] = "StudioConfig/ExamplesDownloadPath"; +constexpr char bundlesDownloadPath[] = "StudioConfig/BundlesDownloadPath"; QMLDESIGNERBASE_EXPORT Utils::FilePath defaultExamplesPath(); QMLDESIGNERBASE_EXPORT Utils::FilePath defaultBundlesPath(); diff --git a/src/plugins/qmldesignerbase/utils/designersettings.cpp b/src/plugins/qmldesignerbase/utils/designersettings.cpp index 248df496081..af5bd36fc56 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.cpp +++ b/src/plugins/qmldesignerbase/utils/designersettings.cpp @@ -3,7 +3,9 @@ #include "designersettings.h" -#include <QSettings> +#include <utils/qtcsettings.h> + +using namespace Utils; namespace QmlDesigner { @@ -12,7 +14,7 @@ namespace DesignerSettingsGroupKey { const char QML_DESIGNER_SETTINGS_GROUP[] = "Designer"; } -DesignerSettings::DesignerSettings(QSettings *settings) : +DesignerSettings::DesignerSettings(QtcSettings *settings) : m_settings(settings) { fromSettings(settings); @@ -38,15 +40,15 @@ QVariant DesignerSettings::value(const QByteArray &key, const QVariant &defaultV return m_cache.value(key, defaultValue); } -void DesignerSettings::restoreValue(QSettings *settings, const QByteArray &key, const QVariant &defaultValue) +void DesignerSettings::restoreValue(QtcSettings *settings, const QByteArray &key, const QVariant &defaultValue) { - m_cache.insert(key, settings->value(QString::fromLatin1(key), defaultValue)); + m_cache.insert(key, settings->value(key, defaultValue)); } -void DesignerSettings::fromSettings(QSettings *settings) +void DesignerSettings::fromSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String(DesignerSettingsGroupKey::QML_SETTINGS_GROUP)); - settings->beginGroup(QLatin1String(DesignerSettingsGroupKey::QML_DESIGNER_SETTINGS_GROUP)); + settings->beginGroup(DesignerSettingsGroupKey::QML_SETTINGS_GROUP); + settings->beginGroup(DesignerSettingsGroupKey::QML_DESIGNER_SETTINGS_GROUP); restoreValue(settings, DesignerSettingsKey::ITEMSPACING, 6); restoreValue(settings, DesignerSettingsKey::CONTAINERPADDING, 8); @@ -104,17 +106,17 @@ void DesignerSettings::fromSettings(QSettings *settings) settings->endGroup(); } -void DesignerSettings::storeValue(QSettings *settings, const QByteArray &key, const QVariant &value) const +void DesignerSettings::storeValue(QtcSettings *settings, const QByteArray &key, const QVariant &value) const { if (key.isEmpty()) return; - settings->setValue(QString::fromLatin1(key), value); + settings->setValue(key, value); } -void DesignerSettings::toSettings(QSettings *settings) const +void DesignerSettings::toSettings(QtcSettings *settings) const { - settings->beginGroup(QLatin1String(DesignerSettingsGroupKey::QML_SETTINGS_GROUP)); - settings->beginGroup(QLatin1String(DesignerSettingsGroupKey::QML_DESIGNER_SETTINGS_GROUP)); + settings->beginGroup(DesignerSettingsGroupKey::QML_SETTINGS_GROUP); + settings->beginGroup(DesignerSettingsGroupKey::QML_DESIGNER_SETTINGS_GROUP); QHash<QByteArray, QVariant>::const_iterator i = m_cache.constBegin(); while (i != m_cache.constEnd()) { diff --git a/src/plugins/qmldesignerbase/utils/designersettings.h b/src/plugins/qmldesignerbase/utils/designersettings.h index 93eab204397..7bb82555fc5 100644 --- a/src/plugins/qmldesignerbase/utils/designersettings.h +++ b/src/plugins/qmldesignerbase/utils/designersettings.h @@ -10,9 +10,7 @@ #include <QByteArray> #include <QMutex> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace QmlDesigner { @@ -75,21 +73,21 @@ inline constexpr char CONTENT_LIBRARY_NEW_FLAG_EXPIRATION_DAYS[] = "ContentLibra class QMLDESIGNERBASE_EXPORT DesignerSettings { public: - DesignerSettings(QSettings *settings); + DesignerSettings(Utils::QtcSettings *settings); void insert(const QByteArray &key, const QVariant &value); void insert(const QHash<QByteArray, QVariant> &settingsHash); QVariant value(const QByteArray &key, const QVariant &defaultValue = {}) const; private: - void fromSettings(QSettings *); - void toSettings(QSettings *) const; + void fromSettings(Utils::QtcSettings *); + void toSettings(Utils::QtcSettings *) const; - void restoreValue(QSettings *settings, const QByteArray &key, + void restoreValue(Utils::QtcSettings *settings, const QByteArray &key, const QVariant &defaultValue = QVariant()); - void storeValue(QSettings *settings, const QByteArray &key, const QVariant &value) const; + void storeValue(Utils::QtcSettings *settings, const QByteArray &key, const QVariant &value) const; - QSettings *m_settings; + Utils::QtcSettings *m_settings; QHash<QByteArray, QVariant> m_cache; mutable QMutex m_mutex; }; diff --git a/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp index d73ea62ea97..cebac46568f 100644 --- a/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp +++ b/src/plugins/qmldesignerbase/utils/qmlpuppetpaths.cpp @@ -5,12 +5,11 @@ #include "designersettings.h" -#include <app/app_version.h> #include <coreplugin/icore.h> #include <projectexplorer/kit.h> #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> namespace QmlDesigner { namespace QmlPuppetPaths { @@ -19,7 +18,8 @@ namespace { Utils::FilePath qmlPuppetExecutablePath(const Utils::FilePath &workingDirectory) { - return workingDirectory.pathAppended(QString{"qml2puppet-"} + Core::Constants::IDE_VERSION_LONG) + return workingDirectory + .pathAppended(QString{"qml2puppet-"} + QCoreApplication::applicationVersion()) .withExecutableSuffix(); } diff --git a/src/plugins/qmljseditor/QmlJSEditor.json.in b/src/plugins/qmljseditor/QmlJSEditor.json.in index 774031be3f3..c05d4efa808 100644 --- a/src/plugins/qmljseditor/QmlJSEditor.json.in +++ b/src/plugins/qmljseditor/QmlJSEditor.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"QmlJSEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlJSEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Editor for QML and JavaScript.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Quick", + "Description" : "Editor for QML and JavaScript.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qmljseditor/qmljscompletionassist.cpp b/src/plugins/qmljseditor/qmljscompletionassist.cpp index 0c11c625c5b..666f619dee3 100644 --- a/src/plugins/qmljseditor/qmljscompletionassist.cpp +++ b/src/plugins/qmljseditor/qmljscompletionassist.cpp @@ -16,6 +16,7 @@ #include <texteditor/texteditorsettings.h> #include <texteditor/completionsettings.h> +#include <utils/algorithm.h> #include <utils/qtcassert.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -261,10 +262,9 @@ private: void processProperties(const ObjectValue *object) { - if (! object || _processed.contains(object)) + if (! object || !Utils::insert(_processed, object)) return; - _processed.insert(object); processProperties(object->prototype(_scopeChain->context())); _currentObject = object; diff --git a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp index 91bcc0ac89d..4917e8c59ac 100644 --- a/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp +++ b/src/plugins/qmljseditor/qmljscomponentnamedialog.cpp @@ -48,10 +48,9 @@ ComponentNameDialog::ComponentNameDialog(QWidget *parent) : connect(m_buttonBox, &QDialogButtonBox::accepted, this, &ComponentNameDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &ComponentNameDialog::reject); - connect(m_pathEdit, &Utils::PathChooser::rawPathChanged, - this, &ComponentNameDialog::validate); - connect(m_componentNameEdit, &QLineEdit::textChanged, - this, &ComponentNameDialog::validate); + connect(m_pathEdit, &Utils::PathChooser::rawPathChanged, this, &ComponentNameDialog::validate); + connect(m_pathEdit, &Utils::PathChooser::validChanged, this, &ComponentNameDialog::validate); + connect(m_componentNameEdit, &QLineEdit::textChanged, this, &ComponentNameDialog::validate); } bool ComponentNameDialog::go(QString *proposedName, @@ -76,7 +75,7 @@ bool ComponentNameDialog::go(QString *proposedName, *proposedName = QLatin1String("MyComponent"); d.m_componentNameEdit->setText(*proposedName); d.m_pathEdit->setExpectedKind(Utils::PathChooser::ExistingDirectory); - d.m_pathEdit->setHistoryCompleter(QLatin1String("QmlJs.Component.History")); + d.m_pathEdit->setHistoryCompleter("QmlJs.Component.History"); d.m_pathEdit->setPath(*proposedPath); d.m_label->setText(Tr::tr("Property assignments for %1:").arg(oldFileName)); d.m_checkBox->setChecked(isUiFile); diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp index 3b53185bcfe..11c7985fd1c 100644 --- a/src/plugins/qmljseditor/qmljseditingsettingspage.cpp +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.cpp @@ -7,9 +7,11 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/icore.h> + #include <qmljs/qmljscheck.h> #include <qmljs/qmljsstaticanalysismessage.h> #include <qmljstools/qmljstoolsconstants.h> + #include <utils/algorithm.h> #include <utils/layoutbuilder.h> #include <utils/macroexpander.h> @@ -22,7 +24,6 @@ #include <QLabel> #include <QLineEdit> #include <QMenu> -#include <QSettings> #include <QTextStream> #include <QTreeView> @@ -33,6 +34,7 @@ const char QML_CONTEXTPANEPIN_KEY[] = "QmlJSEditor.ContextPanePinned"; const char FOLD_AUX_DATA[] = "QmlJSEditor.FoldAuxData"; const char USE_QMLLS[] = "QmlJSEditor.UseQmlls"; const char USE_LATEST_QMLLS[] = "QmlJSEditor.UseLatestQmlls"; +const char DISABLE_BUILTIN_CODEMODEL[] = "QmlJSEditor.DisableBuiltinCodemodel"; const char UIQML_OPEN_MODE[] = "QmlJSEditor.openUiQmlMode"; const char FORMAT_COMMAND[] = "QmlJSEditor.formatCommand"; const char FORMAT_COMMAND_OPTIONS[] = "QmlJSEditor.formatCommandOptions"; @@ -42,8 +44,10 @@ const char DISABLED_MESSAGES[] = "QmlJSEditor.disabledMessages"; const char DISABLED_MESSAGES_NONQUICKUI[] = "QmlJSEditor.disabledMessagesNonQuickUI"; const char DEFAULT_CUSTOM_FORMAT_COMMAND[] = "%{CurrentDocument:Project:QT_HOST_BINS}/qmlformat"; -using namespace QmlJSEditor; using namespace QmlJSEditor::Internal; +using namespace Utils; + +namespace QmlJSEditor { static QList<int> defaultDisabledMessages() { @@ -89,7 +93,7 @@ static QStringList defaultDisabledNonQuickUiAsString() return result; } -void QmlJsEditingSettings::fromSettings(QSettings *settings) +void QmlJsEditingSettings::fromSettings(QtcSettings *settings) { settings->beginGroup(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML); m_enableContextPane = settings->value(QML_CONTEXTPANE_KEY, QVariant(false)).toBool(); @@ -101,6 +105,8 @@ void QmlJsEditingSettings::fromSettings(QSettings *settings) m_uiQmlOpenMode = settings->value(UIQML_OPEN_MODE, "").toString(); m_qmllsSettings.useQmlls = settings->value(USE_QMLLS, QVariant(false)).toBool(); m_qmllsSettings.useLatestQmlls = settings->value(USE_LATEST_QMLLS, QVariant(false)).toBool(); + m_qmllsSettings.disableBuiltinCodemodel + = settings->value(DISABLE_BUILTIN_CODEMODEL, QVariant(false)).toBool(); m_formatCommand = settings->value(FORMAT_COMMAND, {}).toString(); m_formatCommandOptions = settings->value(FORMAT_COMMAND_OPTIONS, {}).toString(); m_useCustomFormatCommand = settings->value(CUSTOM_COMMAND, QVariant(false)).toBool(); @@ -117,7 +123,7 @@ void QmlJsEditingSettings::fromSettings(QSettings *settings) settings->endGroup(); } -void QmlJsEditingSettings::toSettings(QSettings *settings) const +void QmlJsEditingSettings::toSettings(QtcSettings *settings) const { settings->beginGroup(QmlJSEditor::Constants::SETTINGS_CATEGORY_QML); settings->setValue(QML_CONTEXTPANE_KEY, m_enableContextPane); @@ -128,27 +134,17 @@ void QmlJsEditingSettings::toSettings(QSettings *settings) const settings->setValue(UIQML_OPEN_MODE, m_uiQmlOpenMode); settings->setValue(USE_QMLLS, m_qmllsSettings.useQmlls); settings->setValue(USE_LATEST_QMLLS, m_qmllsSettings.useLatestQmlls); - Utils::QtcSettings::setValueWithDefault(settings, FORMAT_COMMAND, m_formatCommand, {}); - Utils::QtcSettings::setValueWithDefault(settings, - FORMAT_COMMAND_OPTIONS, - m_formatCommandOptions, - {}); - Utils::QtcSettings::setValueWithDefault(settings, - CUSTOM_COMMAND, - m_useCustomFormatCommand, - false); - Utils::QtcSettings::setValueWithDefault(settings, - CUSTOM_ANALYZER, - m_useCustomAnalyzer, - false); - Utils::QtcSettings::setValueWithDefault(settings, - DISABLED_MESSAGES, - intListToStringList(Utils::sorted(Utils::toList(m_disabledMessages))), - defaultDisabledMessagesAsString()); - Utils::QtcSettings::setValueWithDefault(settings, - DISABLED_MESSAGES_NONQUICKUI, - intListToStringList(Utils::sorted(Utils::toList(m_disabledMessagesForNonQuickUi))), - defaultDisabledNonQuickUiAsString()); + settings->setValue(DISABLE_BUILTIN_CODEMODEL, m_qmllsSettings.disableBuiltinCodemodel); + settings->setValueWithDefault(FORMAT_COMMAND, m_formatCommand, {}); + settings->setValueWithDefault(FORMAT_COMMAND_OPTIONS, m_formatCommandOptions, {}); + settings->setValueWithDefault(CUSTOM_COMMAND, m_useCustomFormatCommand, false); + settings->setValueWithDefault(CUSTOM_ANALYZER, m_useCustomAnalyzer, false); + settings->setValueWithDefault(DISABLED_MESSAGES, + intListToStringList(Utils::sorted(Utils::toList(m_disabledMessages))), + defaultDisabledMessagesAsString()); + settings->setValueWithDefault(DISABLED_MESSAGES_NONQUICKUI, + intListToStringList(Utils::sorted(Utils::toList(m_disabledMessagesForNonQuickUi))), + defaultDisabledNonQuickUiAsString()); settings->endGroup(); QmllsSettingsManager::instance()->checkForChanges(); } @@ -396,13 +392,19 @@ public: uiQmlOpenComboBox->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); uiQmlOpenComboBox->setSizeAdjustPolicy(QComboBox::QComboBox::AdjustToContents); - useQmlls = new QCheckBox(Tr::tr("Use qmlls (EXPERIMENTAL!)")); + useQmlls = new QCheckBox(Tr::tr("Enable QML Language Server (EXPERIMENTAL!)")); useQmlls->setChecked(s.qmllsSettings().useQmlls); - useLatestQmlls = new QCheckBox(Tr::tr("Always use latest qmlls")); + disableBuiltInCodemodel = new QCheckBox( + Tr::tr("Use QML Language Server advanced features (renaming, find usages and co.) " + "(EXPERIMENTAL!)")); + disableBuiltInCodemodel->setChecked(s.qmllsSettings().disableBuiltinCodemodel); + disableBuiltInCodemodel->setEnabled(s.qmllsSettings().useQmlls); + useLatestQmlls = new QCheckBox(Tr::tr("Use QML Language Server from latest Qt version")); useLatestQmlls->setChecked(s.qmllsSettings().useLatestQmlls); useLatestQmlls->setEnabled(s.qmllsSettings().useQmlls); QObject::connect(useQmlls, &QCheckBox::stateChanged, this, [this](int checked) { useLatestQmlls->setEnabled(checked != Qt::Unchecked); + disableBuiltInCodemodel->setEnabled(checked != Qt::Unchecked); }); useCustomAnalyzer = new QCheckBox(Tr::tr("Use customized static analyzer")); @@ -454,8 +456,8 @@ public: }, }, Group{ - title(Tr::tr("Language Server")), - Column{useQmlls, useLatestQmlls}, + title(Tr::tr("QML Language Server")), + Column{useQmlls, disableBuiltInCodemodel , useLatestQmlls}, }, Group { title(Tr::tr("Static Analyzer")), @@ -499,6 +501,7 @@ public: s.setFoldAuxData(foldAuxData->isChecked()); s.setUiQmlOpenMode(uiQmlOpenComboBox->currentData().toString()); s.qmllsSettings().useQmlls = useQmlls->isChecked(); + s.qmllsSettings().disableBuiltinCodemodel = disableBuiltInCodemodel->isChecked(); s.qmllsSettings().useLatestQmlls = useLatestQmlls->isChecked(); s.setUseCustomAnalyzer(useCustomAnalyzer->isChecked()); QSet<int> disabled; @@ -557,6 +560,7 @@ private: QCheckBox *foldAuxData; QCheckBox *useQmlls; QCheckBox *useLatestQmlls; + QCheckBox *disableBuiltInCodemodel; QComboBox *uiQmlOpenComboBox; QCheckBox *useCustomAnalyzer; QTreeView *analyzerMessagesView; @@ -574,7 +578,9 @@ QmlJsEditingSettings QmlJsEditingSettings::get() QmlJsEditingSettingsPage::QmlJsEditingSettingsPage() { setId("C.QmlJsEditing"); - setDisplayName(Tr::tr("QML/JS Editing")); + setDisplayName(::QmlJSEditor::Tr::tr("QML/JS Editing")); setCategory(Constants::SETTINGS_CATEGORY_QML); setWidgetCreator([] { return new QmlJsEditingSettingsPageWidget; }); } + +} // QmlJsEditor diff --git a/src/plugins/qmljseditor/qmljseditingsettingspage.h b/src/plugins/qmljseditor/qmljseditingsettingspage.h index d7f8f78bd30..5f6d954fbdb 100644 --- a/src/plugins/qmljseditor/qmljseditingsettingspage.h +++ b/src/plugins/qmljseditor/qmljseditingsettingspage.h @@ -4,14 +4,12 @@ #pragma once #include "qmllssettings.h" + #include <coreplugin/dialogs/ioptionspage.h> + #include <QPointer> #include <QWidget> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - namespace QmlJSEditor { class QmlJsEditingSettings @@ -22,8 +20,8 @@ public: static QmlJsEditingSettings get(); void set(); - void fromSettings(QSettings *); - void toSettings(QSettings *) const; + void fromSettings(Utils::QtcSettings *); + void toSettings(Utils::QtcSettings *) const; bool equals(const QmlJsEditingSettings &other) const; diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index 1bd8ef89ff8..69948be3835 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -60,6 +60,10 @@ #include <utils/qtcassert.h> #include <utils/uncommentselection.h> +#include <languageclient/languageclientmanager.h> +#include <languageclient/locatorfilter.h> +#include <languageclient/languageclientsymbolsupport.h> + #include <QComboBox> #include <QCoreApplication> #include <QFileInfo> @@ -90,6 +94,18 @@ using namespace Utils; namespace QmlJSEditor { +static LanguageClient::Client *getQmllsClient(const Utils::FilePath &fileName) +{ + // the value in disableBuiltinCodemodel is only valid when useQmlls is enabled + if (QmlJsEditingSettings::get().qmllsSettings().useQmlls + && !QmlJsEditingSettings::get().qmllsSettings().disableBuiltinCodemodel) + return nullptr; + + auto client = LanguageClient::LanguageClientManager::clientForFilePath(fileName); + return client; +} + + // // QmlJSEditorWidget // @@ -116,7 +132,7 @@ void QmlJSEditorWidget::finalizeInitialization() this, &QmlJSEditorWidget::updateOutlineIndexNow); m_modelManager = ModelManagerInterface::instance(); - m_contextPane = Internal::QmlJSEditorPlugin::quickToolBar(); + m_contextPane = QuickToolBar::instance(); m_modelManager->activateScan(); @@ -126,7 +142,7 @@ void QmlJSEditorWidget::finalizeInitialization() if (m_contextPane) { connect(this, &QmlJSEditorWidget::cursorPositionChanged, &m_contextPaneTimer, QOverload<>::of(&QTimer::start)); - connect(m_contextPane, &IContextPane::closed, this, &QmlJSEditorWidget::showTextMarker); + connect(m_contextPane, &QuickToolBar::closed, this, &QmlJSEditorWidget::showTextMarker); } connect(this->document(), &QTextDocument::modificationChanged, @@ -737,9 +753,18 @@ void QmlJSEditorWidget::inspectElementUnderCursor() const void QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &processLinkCallback, - bool /*resolveTarget*/, + bool resolveTarget, bool /*inNextSplit*/) { + if (auto client = getQmllsClient(textDocument()->filePath())) { + client->findLinkAt(textDocument(), + cursor, + processLinkCallback, + resolveTarget, + LanguageClient::LinkTarget::SymbolDef); + return; + } + const SemanticInfo semanticInfo = m_qmlJsEditorDocument->semanticInfo(); if (! semanticInfo.isValid()) return processLinkCallback(Utils::Link()); @@ -857,12 +882,26 @@ void QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor, void QmlJSEditorWidget::findUsages() { - m_findReferences->findUsages(textDocument()->filePath(), textCursor().position()); + const Utils::FilePath fileName = textDocument()->filePath(); + + if (auto client = getQmllsClient(fileName)) { + client->symbolSupport().findUsages(textDocument(), textCursor()); + } else { + const int offset = textCursor().position(); + m_findReferences->findUsages(fileName, offset); + } } void QmlJSEditorWidget::renameSymbolUnderCursor() { - m_findReferences->renameUsages(textDocument()->filePath(), textCursor().position()); + const Utils::FilePath fileName = textDocument()->filePath(); + + if (auto client = getQmllsClient(fileName)) { + client->symbolSupport().renameSymbol(textDocument(), textCursor(), QString()); + } else { + const int offset = textCursor().position(); + m_findReferences->renameUsages(fileName, offset); + } } void QmlJSEditorWidget::showContextPane() diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index a5b7687a2ac..37f49d08712 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -741,7 +741,7 @@ static Utils::FilePath qmllsForFile(const Utils::FilePath &file, QmllsSettings settings = settingsManager->lastSettings(); bool enabled = settings.useQmlls; if (!enabled) - return Utils::FilePath(); + return {}; if (settings.useLatestQmlls) return settingsManager->latestQmlls(); QmlJS::ModelManagerInterface::ProjectInfo pInfo = modelManager->projectInfoForPath(file); @@ -784,6 +784,7 @@ void QmlJSEditorDocumentPrivate::settingsChanged() case LanguageClient::Client::State::Initialized: setSourcesWithCapabilities(client->capabilities()); break; + case LanguageClient::Client::State::FailedToInitialize: case LanguageClient::Client::State::Error: qCWarning(qmllsLog) << "qmlls" << newQmlls << "requested for document" << q->filePath() << "had errors, skipping setSourcesWithCababilities"; @@ -856,6 +857,8 @@ Internal::QmlOutlineModel *QmlJSEditorDocument::outlineModel() const TextEditor::IAssistProvider *QmlJSEditorDocument::quickFixAssistProvider() const { + if (const auto baseProvider = TextDocument::quickFixAssistProvider()) + return baseProvider; return Internal::QmlJSEditorPlugin::quickFixAssistProvider(); } diff --git a/src/plugins/qmljseditor/qmljseditorplugin.cpp b/src/plugins/qmljseditor/qmljseditorplugin.cpp index d695989825d..47fd2126d5c 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.cpp +++ b/src/plugins/qmljseditor/qmljseditorplugin.cpp @@ -7,15 +7,16 @@ #include "qmljseditordocument.h" #include "qmljseditorplugin.h" #include "qmljseditortr.h" -#include "qmljshighlighter.h" #include "qmljsoutline.h" #include "qmljsquickfixassist.h" #include "qmltaskmanager.h" #include "quicktoolbar.h" +#include <qmljs/jsoncheck.h> #include <qmljs/qmljsicons.h> #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsreformatter.h> + #include <qmljstools/qmljstoolsconstants.h> #include <qmljstools/qmljstoolssettings.h> #include <qmljstools/qmljscodestylepreferences.h> @@ -26,18 +27,20 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> + #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projecttree.h> #include <projectexplorer/taskhub.h> + #include <texteditor/command.h> #include <texteditor/formattexteditor.h> #include <texteditor/snippets/snippetprovider.h> #include <texteditor/tabsettings.h> #include <texteditor/texteditor.h> #include <texteditor/texteditorconstants.h> + #include <utils/fsengine/fileiconprovider.h> -#include <utils/json.h> #include <utils/macroexpander.h> #include <utils/qtcassert.h> @@ -77,12 +80,11 @@ public: QPointer<QmlJSEditorDocument> m_currentDocument; - Utils::JsonSchemaManager m_jsonManager{ + QmlJS::JsonSchemaManager m_jsonManager{ {ICore::userResourcePath("json/").toString(), ICore::resourcePath("json/").toString()}}; QmlJSEditorFactory m_qmlJSEditorFactory; QmlJSOutlineWidgetFactory m_qmlJSOutlineWidgetFactory; - QuickToolBar m_quickToolBar; QmlJsEditingSettingsPage m_qmJSEditingSettingsPage; }; @@ -205,22 +207,21 @@ void QmlJSEditorPlugin::extensionsInitialized() FileIconProvider::registerIconOverlayForMimeType(ProjectExplorer::Constants::FILEOVERLAY_UI, "application/x-qt.ui+qml"); - TaskHub::addCategory(Constants::TASK_CATEGORY_QML, Tr::tr("QML")); - TaskHub::addCategory(Constants::TASK_CATEGORY_QML_ANALYSIS, Tr::tr("QML Analysis"), false); + TaskHub::addCategory({Constants::TASK_CATEGORY_QML, + Tr::tr("QML"), + Tr::tr("Issues that the QML code parser found.")}); + TaskHub::addCategory({Constants::TASK_CATEGORY_QML_ANALYSIS, + Tr::tr("QML Analysis"), + Tr::tr("Issues that the QML static analyzer found."), + false}); QmllsSettingsManager::instance()->setupAutoupdate(); } -Utils::JsonSchemaManager *QmlJSEditorPlugin::jsonManager() +QmlJS::JsonSchemaManager *QmlJSEditorPlugin::jsonManager() { return &m_instance->d->m_jsonManager; } -QuickToolBar *QmlJSEditorPlugin::quickToolBar() -{ - QTC_ASSERT(m_instance && m_instance->d, return new QuickToolBar()); - return &m_instance->d->m_quickToolBar; -} - void QmlJSEditorPluginPrivate::renameUsages() { if (auto editor = qobject_cast<QmlJSEditorWidget*>(EditorManager::currentEditor()->widget())) diff --git a/src/plugins/qmljseditor/qmljseditorplugin.h b/src/plugins/qmljseditor/qmljseditorplugin.h index aa653ac6cb0..2931c8f0bec 100644 --- a/src/plugins/qmljseditor/qmljseditorplugin.h +++ b/src/plugins/qmljseditor/qmljseditorplugin.h @@ -5,7 +5,7 @@ #include <extensionsystem/iplugin.h> -namespace Utils { class JsonSchemaManager; } +namespace QmlJS { class JsonSchemaManager; } namespace QmlJSEditor { class QuickToolBar; @@ -23,8 +23,7 @@ public: ~QmlJSEditorPlugin() final; static QmlJSQuickFixAssistProvider *quickFixAssistProvider(); - static Utils::JsonSchemaManager *jsonManager(); - static QuickToolBar *quickToolBar(); + static QmlJS::JsonSchemaManager *jsonManager(); private: void initialize() final; diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 5e72cd642b9..3f5d53d60f5 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -952,9 +952,8 @@ QList<FindReferences::Usage> FindReferences::findUsageOfType(const Utils::FilePa QSet<Utils::FilePath> docDone; for (const QmlJS::Document::Ptr &doc : std::as_const(snapshot)) { Utils::FilePath sourceFile = modelManager->fileToSource(doc->fileName()); - if (docDone.contains(sourceFile)) + if (!Utils::insert(docDone, sourceFile)) continue; - docDone.insert(sourceFile); QmlJS::Document::Ptr sourceDoc = doc; if (sourceFile != doc->fileName()) sourceDoc = snapshot.document(sourceFile); diff --git a/src/plugins/qmljseditor/qmljsoutline.cpp b/src/plugins/qmljseditor/qmljsoutline.cpp index 75e0b0fdc22..89d2dd68320 100644 --- a/src/plugins/qmljseditor/qmljsoutline.cpp +++ b/src/plugins/qmljseditor/qmljsoutline.cpp @@ -12,7 +12,6 @@ #include <coreplugin/idocument.h> #include <coreplugin/editormanager/editormanager.h> -#include <QSettings> #include <QAction> #include <QVBoxLayout> #include <QTextBlock> diff --git a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp index 764f125e543..44c1a72f952 100644 --- a/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp +++ b/src/plugins/qmljseditor/qmljssemanticinfoupdater.cpp @@ -14,8 +14,6 @@ #include <coreplugin/icore.h> -#include <utils/json.h> - namespace QmlJSEditor { namespace Internal { @@ -97,7 +95,7 @@ QmlJSTools::SemanticInfo SemanticInfoUpdater::makeNewSemanticInfo(const QmlJS::D semanticInfo.setRootScopeChain(QSharedPointer<const ScopeChain>(scopeChain)); if (doc->language() == Dialect::Json) { - Utils::JsonSchema *schema = QmlJSEditorPlugin::jsonManager()->schemaForFile( + JsonSchema *schema = QmlJSEditorPlugin::jsonManager()->schemaForFile( doc->fileName().toString()); if (schema) { JsonCheck jsonChecker(doc); diff --git a/src/plugins/qmljseditor/qmllsclient.cpp b/src/plugins/qmljseditor/qmllsclient.cpp index 6777f267cc1..4cc8769bb1b 100644 --- a/src/plugins/qmljseditor/qmllsclient.cpp +++ b/src/plugins/qmljseditor/qmllsclient.cpp @@ -39,6 +39,7 @@ QmllsClient *QmllsClient::clientForQmlls(const FilePath &qmlls) case Client::State::InitializeRequested: case Client::State::Initialized: return client; + case Client::State::FailedToInitialize: case Client::State::ShutdownRequested: case Client::State::Shutdown: case Client::State::Error: diff --git a/src/plugins/qmljseditor/qmllssettings.cpp b/src/plugins/qmljseditor/qmllssettings.cpp index eb98c041fa5..76ce535fa79 100644 --- a/src/plugins/qmljseditor/qmllssettings.cpp +++ b/src/plugins/qmljseditor/qmllssettings.cpp @@ -10,6 +10,8 @@ #include <QLoggingCategory> #include <qtsupport/qtversionmanager.h> +#include <nanotrace/nanotrace.h> + #include <limits> using namespace QtSupport; @@ -24,9 +26,9 @@ Q_LOGGING_CATEGORY(qmllsLog, "qtc.qmlls.settings", QtWarningMsg); static FilePath evaluateLatestQmlls() { // find latest qmlls, i.e. vals - if (!QtVersionManager::instance()->isLoaded()) + if (!QtVersionManager::isLoaded()) return {}; - const QtVersions versions = QtVersionManager::instance()->versions(); + const QtVersions versions = QtVersionManager::versions(); FilePath latestQmlls; QVersionNumber latestVersion; FilePath latestQmakeFilePath; @@ -78,7 +80,7 @@ void QmllsSettingsManager::setupAutoupdate() &QtVersionManager::qtVersionsChanged, this, &QmllsSettingsManager::checkForChanges); - if (QtVersionManager::instance()->isLoaded()) + if (QtVersionManager::isLoaded()) checkForChanges(); else QObject::connect(QtVersionManager::instance(), @@ -89,8 +91,9 @@ void QmllsSettingsManager::setupAutoupdate() void QmllsSettingsManager::checkForChanges() { - FilePath newLatest = evaluateLatestQmlls(); QmllsSettings newSettings = QmlJsEditingSettings::get().qmllsSettings(); + FilePath newLatest = newSettings.useLatestQmlls && newSettings.useQmlls ? evaluateLatestQmlls() + : m_latestQmlls; if (m_lastSettings == newSettings && newLatest == m_latestQmlls) return; qCDebug(qmllsLog) << "qmlls settings changed:" << newSettings.useQmlls diff --git a/src/plugins/qmljseditor/qmllssettings.h b/src/plugins/qmljseditor/qmllssettings.h index 040f64d4334..32890d3762a 100644 --- a/src/plugins/qmljseditor/qmllssettings.h +++ b/src/plugins/qmljseditor/qmllssettings.h @@ -15,10 +15,12 @@ struct QmllsSettings { bool useQmlls = true; bool useLatestQmlls = false; + bool disableBuiltinCodemodel = false; friend bool operator==(const QmllsSettings &s1, const QmllsSettings &s2) { - return s1.useQmlls == s2.useQmlls && s1.useLatestQmlls == s2.useLatestQmlls; + return s1.useQmlls == s2.useQmlls && s1.useLatestQmlls == s2.useLatestQmlls + && s1.disableBuiltinCodemodel == s2.disableBuiltinCodemodel; } friend bool operator!=(const QmllsSettings &s1, const QmllsSettings &s2) { return !(s1 == s2); } }; diff --git a/src/plugins/qmljseditor/quicktoolbar.cpp b/src/plugins/qmljseditor/quicktoolbar.cpp index cce60c03cf9..911c4228603 100644 --- a/src/plugins/qmljseditor/quicktoolbar.cpp +++ b/src/plugins/qmljseditor/quicktoolbar.cpp @@ -86,6 +86,12 @@ QuickToolBar::~QuickToolBar() m_widget = nullptr; } +QuickToolBar *QuickToolBar::instance() +{ + static QuickToolBar theQuickToolBar; + return &theQuickToolBar; +} + void QuickToolBar::apply(TextEditor::TextEditorWidget *editorWidget, Document::Ptr document, const ScopeChain *scopeChain, Node *node, bool update, bool force) { if (!QmlJsEditingSettings::get().enableContextPane() && !force && !update) { @@ -318,8 +324,7 @@ void QuickToolBar::removeProperty(const QString &propertyName) Utils::ChangeSet changeSet; Rewriter rewriter(m_doc->source(), &changeSet, m_propertyOrder); rewriter.removeBindingByName(initializer, propertyName); - QTextCursor tc(m_editorWidget->document()); - changeSet.apply(&tc); + changeSet.apply(m_editorWidget->document()); } } } @@ -332,13 +337,11 @@ void QuickToolBar::setEnabled(bool b) widget()->hide(); } - -QWidget* QuickToolBar::widget() +QWidget *QuickToolBar::widget() { return contextWidget(); } - void QuickToolBar::onPropertyChanged(const QString &name, const QVariant &value) { if (m_blockWriting) @@ -406,7 +409,7 @@ void QuickToolBar::indentLines(int startLine, int endLine) } } -ContextPaneWidget* QuickToolBar::contextWidget() +ContextPaneWidget *QuickToolBar::contextWidget() { if (m_widget.isNull()) { //lazily recreate widget m_widget = new ContextPaneWidget; @@ -420,7 +423,8 @@ ContextPaneWidget* QuickToolBar::contextWidget() this, &QuickToolBar::onEnabledChanged); connect(m_widget.data(), &ContextPaneWidget::pinnedChanged, this, &QuickToolBar::onPinnedChanged); - connect(m_widget.data(), &ContextPaneWidget::closed, this, &IContextPane::closed); + connect(m_widget.data(), &ContextPaneWidget::closed, + this, &QuickToolBar::closed); } return m_widget.data(); } diff --git a/src/plugins/qmljseditor/quicktoolbar.h b/src/plugins/qmljseditor/quicktoolbar.h index aea42eaccf4..09147809c4d 100644 --- a/src/plugins/qmljseditor/quicktoolbar.h +++ b/src/plugins/qmljseditor/quicktoolbar.h @@ -3,36 +3,47 @@ #pragma once -#include <qmljs/qmljsicontextpane.h> +#include <qmljs/qmljsdocument.h> +#include <qmljs/parser/qmljsastfwd_p.h> #include <QPointer> +namespace TextEditor { class TextEditorWidget; } + +namespace QmlJS { class ScopeChain; } + namespace QmlEditorWidgets { class ContextPaneWidget; } namespace QmlJSEditor { -class QuickToolBar : public QmlJS::IContextPane +class QuickToolBar : public QObject { Q_OBJECT + QuickToolBar(); public: - QuickToolBar(); - ~QuickToolBar() override; - void apply(TextEditor::TextEditorWidget *widget, QmlJS::Document::Ptr document, const QmlJS::ScopeChain *scopeChain, QmlJS::AST::Node *node, bool update, bool force = false) override; - bool isAvailable(TextEditor::TextEditorWidget *widget, QmlJS::Document::Ptr document, QmlJS::AST::Node *node) override; - void setProperty(const QString &propertyName, const QVariant &value); - void removeProperty(const QString &propertyName); - void setEnabled(bool) override; - QWidget* widget() override; + ~QuickToolBar(); - void onPropertyChanged(const QString &, const QVariant &); - void onPropertyRemoved(const QString &); - void onPropertyRemovedAndChange(const QString &, const QString &, const QVariant &, bool removeFirst = true); - void onPinnedChanged(bool); - void onEnabledChanged(bool); + static QuickToolBar *instance(); + + void apply(TextEditor::TextEditorWidget *widget, QmlJS::Document::Ptr document, const QmlJS::ScopeChain *scopeChain, QmlJS::AST::Node *node, bool update, bool force = false); + bool isAvailable(TextEditor::TextEditorWidget *widget, QmlJS::Document::Ptr document, QmlJS::AST::Node *node); + void setProperty(const QString &propertyName, const QVariant &value); + void removeProperty(const QString &propertyName); + void setEnabled(bool); + QWidget *widget(); + + void onPropertyChanged(const QString &, const QVariant &); + void onPropertyRemoved(const QString &); + void onPropertyRemovedAndChange(const QString &, const QString &, const QVariant &, bool removeFirst = true); + void onPinnedChanged(bool); + void onEnabledChanged(bool); + +signals: + void closed(); private: - void indentLines(int startLine, int endLine); + void indentLines(int startLine, int endLine); QmlEditorWidgets::ContextPaneWidget* contextWidget(); QPointer<QmlEditorWidgets::ContextPaneWidget> m_widget; @@ -45,4 +56,4 @@ private: QString m_oldType; }; -} //QmlDesigner +} // QmlJSEditor diff --git a/src/plugins/qmljstools/QmlJSTools.json.in b/src/plugins/qmljstools/QmlJSTools.json.in index ecc5f1e856a..0edfb8a7bf4 100644 --- a/src/plugins/qmljstools/QmlJSTools.json.in +++ b/src/plugins/qmljstools/QmlJSTools.json.in @@ -1,73 +1,73 @@ { - \"Name\" : \"QmlJSTools\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlJSTools", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Tools for analyzing Qml/JS code.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Qt Quick", + "Description" : "Tools for analyzing Qml/JS code.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/x-qml\'>\", - \" <alias type=\'application/x-qml\'/>\", - \" <!-- sub class is missing in the freedesktop.org definition -->\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>QML file</comment>\", - \" <glob pattern=\'*.qml\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/x-qt.qbs+qml\'>\", - \" <alias type=\'text/x-qt.qbs+qml\'/>\", - \" <sub-class-of type=\'text/x-qml\'/>\", - \" <comment>Qt Build Suite file</comment>\", - \" <glob pattern=\'*.qbs\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/x-qt.ui+qml\'>\", - \" <alias type=\'text/x-qt.ui+qml\'/>\", - \" <sub-class-of type=\'text/x-qml\'/>\", - \" <comment>QtQuick Designer ui file</comment>\", - \" <glob pattern=\'*.ui.qml\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/x-qmlproject\'>\", - \" <alias type=\'text/x-qmlproject\'/>\", - \" <sub-class-of type=\'text/x-qml\'/>\", - \" <comment>Qt Creator Qt UI project file</comment>\", - \" <glob pattern=\'*.qmlproject\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/x-qt.meta-info+qml\'>\", - \" <alias type=\'text/x-qt.meta-info+qml\'/>\", - \" <sub-class-of type=\'text/x-qml\'/>\", - \" <comment>QML file</comment>\", - \" <glob pattern=\'*.qmltypes\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/json\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>JSON file</comment>\", - \" <glob pattern=\'*.json\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/x-javascript-module\'>\", - \" <sub-class-of type=\'application/javascript\'/>\", - \" <comment>Javascript module</comment>\", - \" <glob pattern=\'*.mjs\' weight=\'70\'/>\", - \" </mime-type>\", - \" <mime-type type=\'text/x-qtscript\'>\", - \" <alias type=\'application/x-qtscript\'/>\", - \" <sub-class-of type=\'application/javascript\'/>\", - \" <comment>Qt Script file</comment>\", - \" <glob pattern=\'*.qs\' weight=\'70\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/x-qml'>", + " <alias type='application/x-qml'/>", + " <!-- sub class is missing in the freedesktop.org definition -->", + " <sub-class-of type='text/plain'/>", + " <comment>QML file</comment>", + " <glob pattern='*.qml' weight='70'/>", + " </mime-type>", + " <mime-type type='application/x-qt.qbs+qml'>", + " <alias type='text/x-qt.qbs+qml'/>", + " <sub-class-of type='text/x-qml'/>", + " <comment>Qt Build Suite file</comment>", + " <glob pattern='*.qbs' weight='70'/>", + " </mime-type>", + " <mime-type type='application/x-qt.ui+qml'>", + " <alias type='text/x-qt.ui+qml'/>", + " <sub-class-of type='text/x-qml'/>", + " <comment>QtQuick Designer ui file</comment>", + " <glob pattern='*.ui.qml' weight='70'/>", + " </mime-type>", + " <mime-type type='application/x-qmlproject'>", + " <alias type='text/x-qmlproject'/>", + " <sub-class-of type='text/x-qml'/>", + " <comment>Qt Creator Qt UI project file</comment>", + " <glob pattern='*.qmlproject' weight='70'/>", + " </mime-type>", + " <mime-type type='application/x-qt.meta-info+qml'>", + " <alias type='text/x-qt.meta-info+qml'/>", + " <sub-class-of type='text/x-qml'/>", + " <comment>QML file</comment>", + " <glob pattern='*.qmltypes' weight='70'/>", + " </mime-type>", + " <mime-type type='application/json'>", + " <sub-class-of type='text/plain'/>", + " <comment>JSON file</comment>", + " <glob pattern='*.json' weight='70'/>", + " </mime-type>", + " <mime-type type='application/x-javascript-module'>", + " <sub-class-of type='application/javascript'/>", + " <comment>Javascript module</comment>", + " <glob pattern='*.mjs' weight='70'/>", + " </mime-type>", + " <mime-type type='text/x-qtscript'>", + " <alias type='application/x-qtscript'/>", + " <sub-class-of type='application/javascript'/>", + " <comment>Qt Script file</comment>", + " <glob pattern='*.qs' weight='70'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/qmljstools/qmljsbundleprovider.cpp b/src/plugins/qmljstools/qmljsbundleprovider.cpp index 623a6f4684c..cc72e61276f 100644 --- a/src/plugins/qmljstools/qmljsbundleprovider.cpp +++ b/src/plugins/qmljstools/qmljsbundleprovider.cpp @@ -6,7 +6,7 @@ #include <coreplugin/icore.h> #include <qmljs/qmljsbundle.h> #include <qmljs/qmljsconstants.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <QDir> diff --git a/src/plugins/qmljstools/qmljscodestylepreferences.cpp b/src/plugins/qmljstools/qmljscodestylepreferences.cpp index b482c18fff9..f19c40a7b88 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferences.cpp +++ b/src/plugins/qmljstools/qmljscodestylepreferences.cpp @@ -3,6 +3,8 @@ #include "qmljscodestylepreferences.h" +using namespace Utils; + namespace QmlJSTools { QmlJSCodeStylePreferences::QmlJSCodeStylePreferences(QObject *parent) : @@ -67,18 +69,18 @@ void QmlJSCodeStylePreferences::slotCurrentValueChanged(const QVariant &value) emit currentCodeStyleSettingsChanged(value.value<QmlJSCodeStyleSettings>()); } -QVariantMap QmlJSCodeStylePreferences::toMap() const +Store QmlJSCodeStylePreferences::toMap() const { - QVariantMap map = ICodeStylePreferences::toMap(); + Store map = ICodeStylePreferences::toMap(); if (!currentDelegate()) { - const QVariantMap dataMap = m_data.toMap(); + const Store dataMap = m_data.toMap(); for (auto it = dataMap.begin(), end = dataMap.end(); it != end; ++it) map.insert(it.key(), it.value()); } return map; } -void QmlJSCodeStylePreferences::fromMap(const QVariantMap &map) +void QmlJSCodeStylePreferences::fromMap(const Store &map) { ICodeStylePreferences::fromMap(map); if (!currentDelegate()) diff --git a/src/plugins/qmljstools/qmljscodestylepreferences.h b/src/plugins/qmljstools/qmljscodestylepreferences.h index da52de879d9..7a8dcc83d86 100644 --- a/src/plugins/qmljstools/qmljscodestylepreferences.h +++ b/src/plugins/qmljstools/qmljscodestylepreferences.h @@ -25,8 +25,8 @@ public: // tracks parent hierarchy until currentParentSettings is null QmlJSCodeStyleSettings currentCodeStyleSettings() const; - QVariantMap toMap() const override; - void fromMap(const QVariantMap &map) override; + Utils::Store toMap() const override; + void fromMap(const Utils::Store &map) override; public slots: void setCodeStyleSettings(const QmlJSCodeStyleSettings &data); diff --git a/src/plugins/qmljstools/qmljscodestylesettings.cpp b/src/plugins/qmljstools/qmljscodestylesettings.cpp index 90af4bec52c..c03501ac016 100644 --- a/src/plugins/qmljstools/qmljscodestylesettings.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettings.cpp @@ -15,24 +15,25 @@ #include <cplusplus/Overview.h> #include <utils/qtcassert.h> -#include <utils/settingsutils.h> static const char lineLengthKey[] = "LineLength"; +using namespace Utils; + namespace QmlJSTools { -// ------------------ QmlJSCodeStyleSettingsWidget +// QmlJSCodeStyleSettings QmlJSCodeStyleSettings::QmlJSCodeStyleSettings() = default; -QVariantMap QmlJSCodeStyleSettings::toMap() const +Store QmlJSCodeStyleSettings::toMap() const { return { {lineLengthKey, lineLength} }; } -void QmlJSCodeStyleSettings::fromMap(const QVariantMap &map) +void QmlJSCodeStyleSettings::fromMap(const Store &map) { lineLength = map.value(lineLengthKey, lineLength).toInt(); } diff --git a/src/plugins/qmljstools/qmljscodestylesettings.h b/src/plugins/qmljstools/qmljscodestylesettings.h index 3b79fe092d0..a97cd5a5b9f 100644 --- a/src/plugins/qmljstools/qmljscodestylesettings.h +++ b/src/plugins/qmljstools/qmljscodestylesettings.h @@ -5,7 +5,7 @@ #include "qmljstools_global.h" -#include <QVariantMap> +#include <utils/store.h> #include <optional> @@ -20,8 +20,8 @@ public: int lineLength = 80; - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const QmlJSCodeStyleSettings &rhs) const; bool operator==(const QmlJSCodeStyleSettings &s) const { return equals(s); } diff --git a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp index fc97aa53c82..f332f1ad0dc 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingspage.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettingspage.cpp @@ -144,20 +144,18 @@ public: void apply() final { - QSettings *s = Core::ICore::settings(); - QmlJSCodeStylePreferences *originalPreferences = QmlJSToolsSettings::globalCodeStyle(); if (originalPreferences->codeStyleSettings() != m_preferences.codeStyleSettings()) { originalPreferences->setCodeStyleSettings(m_preferences.codeStyleSettings()); - originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s); + originalPreferences->toSettings(QmlJSTools::Constants::QML_JS_SETTINGS_ID); } if (originalPreferences->tabSettings() != m_preferences.tabSettings()) { originalPreferences->setTabSettings(m_preferences.tabSettings()); - originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s); + originalPreferences->toSettings(QmlJSTools::Constants::QML_JS_SETTINGS_ID); } if (originalPreferences->currentDelegate() != m_preferences.currentDelegate()) { originalPreferences->setCurrentDelegate(m_preferences.currentDelegate()); - originalPreferences->toSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s); + originalPreferences->toSettings(QmlJSTools::Constants::QML_JS_SETTINGS_ID); } } diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 23834e82aa4..c472fdcf795 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -28,7 +28,7 @@ #include <qmljs/qmljsbind.h> #include <qmljs/qmljsfindexportedcpptypes.h> #include <qmljs/qmljsplugindumper.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <texteditor/textdocument.h> @@ -200,7 +200,7 @@ ModelManagerInterface::ProjectInfo ModelManager::defaultProjectInfoForProject( auto v = qtVersion->qtVersion(); projectInfo.qmllsPath = ModelManagerInterface::qmllsForBinPath(qtVersion->hostBinPath(), v); projectInfo.qtVersionString = qtVersion->qtVersionString(); - } else if (!activeKit || !activeKit->value(QtSupport::SuppliesQtQuickImportPath::id(), false).toBool()) { + } else if (!activeKit || !activeKit->value(QtSupport::Constants::FLAGS_SUPPLIES_QTQUICK_IMPORT_PATH, false).toBool()) { projectInfo.qtQmlPath = FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)); projectInfo.qmllsPath = ModelManagerInterface::qmllsForBinPath( FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::BinariesPath)), QLibraryInfo::version()); @@ -261,7 +261,7 @@ QHash<QString,Dialect> ModelManager::languageForSuffix() const ModelManager::ModelManager() { qRegisterMetaType<QmlJSTools::SemanticInfo>("QmlJSTools::SemanticInfo"); - loadDefaultQmlTypeDescriptions(); + CppQmlTypesLoader::defaultObjectsInitializer = [this] { loadDefaultQmlTypeDescriptions(); }; } ModelManager::~ModelManager() = default; diff --git a/src/plugins/qmljstools/qmljstoolssettings.cpp b/src/plugins/qmljstools/qmljstoolssettings.cpp index caeb6dfa26f..b5cdac7e200 100644 --- a/src/plugins/qmljstools/qmljstoolssettings.cpp +++ b/src/plugins/qmljstools/qmljstoolssettings.cpp @@ -7,15 +7,13 @@ #include "qmljstoolssettings.h" #include "qmljstoolstr.h" +#include <coreplugin/icore.h> + #include <texteditor/texteditorsettings.h> #include <texteditor/tabsettings.h> #include <texteditor/codestylepool.h> -#include <utils/settingsutils.h> #include <utils/qtcassert.h> -#include <coreplugin/icore.h> - -#include <QSettings> using namespace TextEditor; @@ -68,8 +66,7 @@ QmlJSToolsSettings::QmlJSToolsSettings() pool->loadCustomCodeStyles(); // load global settings (after built-in settings are added to the pool) - QSettings *s = Core::ICore::settings(); - m_globalCodeStyle->fromSettings(QLatin1String(QmlJSTools::Constants::QML_JS_SETTINGS_ID), s); + m_globalCodeStyle->fromSettings(QmlJSTools::Constants::QML_JS_SETTINGS_ID); // mimetypes to be handled TextEditorSettings::registerMimeTypeForLanguageId(Constants::QML_MIMETYPE, Constants::QML_JS_SETTINGS_ID); diff --git a/src/plugins/qmlpreview/QmlPreview.json.in b/src/plugins/qmlpreview/QmlPreview.json.in index 8a9ce6f6ec9..d053c4040ba 100644 --- a/src/plugins/qmlpreview/QmlPreview.json.in +++ b/src/plugins/qmlpreview/QmlPreview.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"QmlPreview\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlPreview", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Qml Preview Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Quick", + "Description" : "Qml Preview Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qmlpreview/qmlpreview.qbs b/src/plugins/qmlpreview/qmlpreview.qbs index e44f432c41a..b3dcc830f5a 100644 --- a/src/plugins/qmlpreview/qmlpreview.qbs +++ b/src/plugins/qmlpreview/qmlpreview.qbs @@ -16,7 +16,7 @@ QtcPlugin { Depends { name: "Qt" - submodules: ["core"] + submodules: ["core", "qml-private"] } Group { diff --git a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp index 027541d77fb..146195dc276 100644 --- a/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp +++ b/src/plugins/qmlpreview/qmlpreviewfileontargetfinder.cpp @@ -4,7 +4,7 @@ #include "qmlpreviewfileontargetfinder.h" #include <projectexplorer/deploymentdata.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectnodes.h> diff --git a/src/plugins/qmlpreview/qmlpreviewplugin.cpp b/src/plugins/qmlpreview/qmlpreviewplugin.cpp index 2c8f16f53e5..011b6f43d2f 100644 --- a/src/plugins/qmlpreview/qmlpreviewplugin.cpp +++ b/src/plugins/qmlpreview/qmlpreviewplugin.cpp @@ -21,7 +21,7 @@ #include <extensionsystem/pluginmanager.h> #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> @@ -37,13 +37,14 @@ #include <qmlprojectmanager/qmlmultilanguageaspect.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <qtsupport/baseqtversion.h> #include <android/androidconstants.h> #include <QAction> +#include <QMessageBox> #include <QPointer> #include <QTimer> @@ -174,11 +175,6 @@ QmlPreviewPluginPrivate::QmlPreviewPluginPrivate(QmlPreviewPlugin *parent) menu = Core::ActionManager::actionContainer(Constants::M_FILECONTEXT); action = new QAction(Tr::tr("Preview File"), this); - action->setEnabled(false); - connect(q, &QmlPreviewPlugin::runningPreviewsChanged, - action, [action](const QmlPreviewRunControlList &previews) { - action->setEnabled(!previews.isEmpty()); - }); connect(action, &QAction::triggered, q, &QmlPreviewPlugin::previewCurrentFile); menu->addAction( Core::ActionManager::registerAction(action, "QmlPreview.PreviewFile", Core::Context(Constants::C_PROJECT_TREE)), @@ -336,6 +332,11 @@ void QmlPreviewPlugin::previewCurrentFile() || currentNode->asFileNode()->fileType() != FileType::QML) return; + if (runningPreviews().isEmpty()) + QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("QML Preview Not Running"), + Tr::tr("Start the QML Preview for the project before selecting " + "a specific file for preview.")); + const QString file = currentNode->filePath().toString(); if (file != d->m_previewedFile) setPreviewedFile(file); @@ -412,6 +413,8 @@ void QmlPreviewPluginPrivate::attachToEditor() void QmlPreviewPluginPrivate::checkEditor() { + if (m_runningPreviews.isEmpty()) + return; QmlJS::Dialect::Enum dialect = QmlJS::Dialect::AnyLanguage; Core::IDocument *doc = m_lastEditor->document(); const QString mimeType = doc->mimeType(); diff --git a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp index 3d776cb33ff..5bdb2e78a08 100644 --- a/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp +++ b/src/plugins/qmlpreview/qmlpreviewruncontrol.cpp @@ -15,7 +15,7 @@ #include <qmldebug/qmldebugcommandlinearguments.h> #include <qmlprojectmanager/qmlmultilanguageaspect.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/async.h> #include <utils/filepath.h> @@ -30,9 +30,9 @@ using namespace Utils; namespace QmlPreview { -static const QString QmlServerUrl = "QmlServerUrl"; +static const Key QmlServerUrl = "QmlServerUrl"; -class RefreshTranslationWorker final : public ProjectExplorer::RunWorker +class RefreshTranslationWorker final : public RunWorker { public: explicit RefreshTranslationWorker(ProjectExplorer::RunControl *runControl, @@ -42,6 +42,7 @@ public: setId("RefreshTranslationWorker"); connect(this, &RunWorker::started, this, &RefreshTranslationWorker::startRefreshTranslationsAsync); connect(this, &RunWorker::stopped, &m_futureWatcher, &QFutureWatcher<void>::cancel); + connect(&m_futureWatcher, &QFutureWatcherBase::finished, this, &RefreshTranslationWorker::stop); } ~RefreshTranslationWorker() { @@ -54,7 +55,6 @@ private: { m_futureWatcher.setFuture(Utils::asyncRun([this] { m_runnerSettings.refreshTranslationsFunction(); - stop(); })); } QmlPreviewRunnerSetting m_runnerSettings; diff --git a/src/plugins/qmlpreview/tests/qmlpreviewplugin_test.cpp b/src/plugins/qmlpreview/tests/qmlpreviewplugin_test.cpp index 21e1dd18466..2b06bd12e0d 100644 --- a/src/plugins/qmlpreview/tests/qmlpreviewplugin_test.cpp +++ b/src/plugins/qmlpreview/tests/qmlpreviewplugin_test.cpp @@ -15,8 +15,7 @@ Q_DECLARE_METATYPE(QmlPreview::TestFpsHandler) namespace QmlPreview { QmlPreviewPluginTest::QmlPreviewPluginTest(QObject *parent) : QObject(parent) -{ -} +{ } static ExtensionSystem::IPlugin *getPlugin() { diff --git a/src/plugins/qmlprofiler/CMakeLists.txt b/src/plugins/qmlprofiler/CMakeLists.txt index ddf4ae63b05..a94dfce931a 100644 --- a/src/plugins/qmlprofiler/CMakeLists.txt +++ b/src/plugins/qmlprofiler/CMakeLists.txt @@ -38,8 +38,7 @@ set(QMLPROFILER_CPP_SOURCES qmleventtype.cpp qmleventtype.h qmlnote.cpp qmlnote.h qmlprofiler_global.h - qmlprofilertr.h - qmlprofileractions.cpp qmlprofileractions.h + qmlprofilertr.h qmlprofileranimationsmodel.cpp qmlprofileranimationsmodel.h qmlprofilerattachdialog.cpp qmlprofilerattachdialog.h qmlprofilerbindingloopsrenderpass.cpp qmlprofilerbindingloopsrenderpass.h diff --git a/src/plugins/qmlprofiler/QmlProfiler.json.in b/src/plugins/qmlprofiler/QmlProfiler.json.in index 31089e8cc36..45fdb6e8fff 100644 --- a/src/plugins/qmlprofiler/QmlProfiler.json.in +++ b/src/plugins/qmlprofiler/QmlProfiler.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"QmlProfiler\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlProfiler", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Qml Profiler Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Qt Quick", + "Description" : "Qml Profiler Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs index 56a1e00cd6a..4ed25d1bca0 100644 --- a/src/plugins/qmlprofiler/qmlprofiler.qbs +++ b/src/plugins/qmlprofiler/qmlprofiler.qbs @@ -15,7 +15,6 @@ QtcPlugin { Depends { name: "ProjectExplorer" } Depends { name: "QtSupport" } Depends { name: "TextEditor" } - Depends { name: "app_version_header" } Group { name: "General" @@ -31,7 +30,6 @@ QtcPlugin { "qmleventtype.cpp", "qmleventtype.h", "qmlnote.cpp", "qmlnote.h", "qmlprofiler_global.h", "qmlprofilertr.h", - "qmlprofileractions.h", "qmlprofileractions.cpp", "qmlprofileranimationsmodel.h", "qmlprofileranimationsmodel.cpp", "qmlprofilerattachdialog.cpp", "qmlprofilerattachdialog.h", "qmlprofilerbindingloopsrenderpass.cpp","qmlprofilerbindingloopsrenderpass.h", diff --git a/src/plugins/qmlprofiler/qmlprofileractions.cpp b/src/plugins/qmlprofiler/qmlprofileractions.cpp deleted file mode 100644 index 55f5a4edeca..00000000000 --- a/src/plugins/qmlprofiler/qmlprofileractions.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qmlprofileractions.h" -#include "qmlprofilerconstants.h" -#include "qmlprofilermodelmanager.h" -#include "qmlprofilerstatemanager.h" -#include "qmlprofilertool.h" -#include "qmlprofilertr.h" - -#include <debugger/analyzer/analyzerconstants.h> - -#include <coreplugin/actionmanager/actionmanager.h> -#include <coreplugin/actionmanager/command.h> - -#include <QMenu> - -namespace QmlProfiler { -namespace Internal { - -using namespace Core; -using namespace Debugger::Constants; - -QmlProfilerActions::QmlProfilerActions(QObject *parent) : QObject(parent) -{} - -void QmlProfilerActions::attachToTool(QmlProfilerTool *tool) -{ - const QString description = Tr::tr("The QML Profiler can be used to find performance " - "bottlenecks in applications using QML."); - - m_runAction = std::make_unique<QAction>(Tr::tr("QML Profiler")); - m_runAction->setToolTip(description); - QObject::connect(m_runAction.get(), &QAction::triggered, - tool, &QmlProfilerTool::profileStartupProject); - - QAction *toolStartAction = tool->startAction(); - QObject::connect(toolStartAction, &QAction::changed, this, [this, toolStartAction] { - m_runAction->setEnabled(toolStartAction->isEnabled()); - }); - - m_attachAction = std::make_unique<QAction>(Tr::tr("QML Profiler (Attach to Waiting Application)")); - m_attachAction->setToolTip(description); - QObject::connect(m_attachAction.get(), &QAction::triggered, - tool, &QmlProfilerTool::attachToWaitingApplication); - - m_loadQmlTrace = std::make_unique<QAction>(Tr::tr("Load QML Trace")); - connect(m_loadQmlTrace.get(), &QAction::triggered, - tool, &QmlProfilerTool::showLoadDialog, Qt::QueuedConnection); - - m_saveQmlTrace = std::make_unique<QAction>(Tr::tr("Save QML Trace")); - connect(m_saveQmlTrace.get(), &QAction::triggered, - tool, &QmlProfilerTool::showSaveDialog, Qt::QueuedConnection); - - QmlProfilerStateManager *stateManager = tool->stateManager(); - connect(stateManager, &QmlProfilerStateManager::serverRecordingChanged, - this, [this](bool recording) { - m_loadQmlTrace->setEnabled(!recording); - }); - m_loadQmlTrace->setEnabled(!stateManager->serverRecording()); - - QmlProfilerModelManager *modelManager = tool->modelManager(); - connect(modelManager, &QmlProfilerModelManager::traceChanged, - this, [this, modelManager] { - m_saveQmlTrace->setEnabled(!modelManager->isEmpty()); - }); - m_saveQmlTrace->setEnabled(!modelManager->isEmpty()); -} - -void QmlProfilerActions::registerActions() -{ - m_options.reset(ActionManager::createMenu("Analyzer.Menu.QMLOptions")); - m_options->menu()->setTitle(Tr::tr("QML Profiler Options")); - m_options->menu()->setEnabled(true); - ActionContainer *menu = ActionManager::actionContainer(M_DEBUG_ANALYZER); - - menu->addAction(ActionManager::registerAction(m_runAction.get(), - "QmlProfiler.Internal"), - Debugger::Constants::G_ANALYZER_TOOLS); - menu->addAction(ActionManager::registerAction(m_attachAction.get(), - "QmlProfiler.AttachToWaitingApplication"), - Debugger::Constants::G_ANALYZER_REMOTE_TOOLS); - - menu->addMenu(m_options.get(), G_ANALYZER_OPTIONS); - m_options->addAction(ActionManager::registerAction(m_loadQmlTrace.get(), - Constants::QmlProfilerLoadActionId)); - m_options->addAction(ActionManager::registerAction(m_saveQmlTrace.get(), - Constants::QmlProfilerSaveActionId)); -} - -} // namespace Internal -} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/qmlprofileractions.h b/src/plugins/qmlprofiler/qmlprofileractions.h deleted file mode 100644 index 95a884513f1..00000000000 --- a/src/plugins/qmlprofiler/qmlprofileractions.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/actionmanager/actioncontainer.h> - -#include <QObject> -#include <QAction> - -#include <memory> - -namespace QmlProfiler { -namespace Internal { - -class QmlProfilerTool; -class QmlProfilerActions : public QObject -{ - Q_OBJECT -public: - explicit QmlProfilerActions(QObject *parent = nullptr); - - void attachToTool(QmlProfilerTool *tool); - void registerActions(); - -private: - std::unique_ptr<Core::ActionContainer> m_options; - std::unique_ptr<QAction> m_loadQmlTrace; - std::unique_ptr<QAction> m_saveQmlTrace; - std::unique_ptr<QAction> m_runAction; - std::unique_ptr<QAction> m_attachAction; -}; - -} // namespace Internal -} // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/qmlprofilerattachdialog.cpp b/src/plugins/qmlprofiler/qmlprofilerattachdialog.cpp index 1b52367eb12..679166b19c7 100644 --- a/src/plugins/qmlprofiler/qmlprofilerattachdialog.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerattachdialog.cpp @@ -4,8 +4,8 @@ #include "qmlprofilerattachdialog.h" #include "qmlprofilertr.h" +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitchooser.h> -#include <projectexplorer/kitinformation.h> #include <QDialogButtonBox> #include <QFormLayout> diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp index e9f0b1a7807..5fb1f203ef6 100644 --- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp @@ -4,7 +4,7 @@ #include "qmlprofilerdetailsrewriter.h" #include <projectexplorer/kit.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> @@ -98,9 +98,9 @@ Utils::FilePath QmlProfilerDetailsRewriter::getLocalFile(const QString &remoteFi { const Utils::FilePath localFile = m_projectFinder.findFile(remoteFile).constFirst(); if (!localFile.exists() || !localFile.isReadableFile()) - return Utils::FilePath(); + return {}; if (!QmlJS::ModelManagerInterface::guessLanguageOfFile(localFile).isQmlLikeOrJsLanguage()) - return Utils::FilePath(); + return {}; return localFile.canonicalPath(); } diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.cpp b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp index d562131e00f..c60d8f15e6f 100644 --- a/src/plugins/qmlprofiler/qmlprofilerplugin.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp @@ -6,7 +6,6 @@ #include "qmlprofilerruncontrol.h" #include "qmlprofilersettings.h" #include "qmlprofilertool.h" -#include "qmlprofileractions.h" #ifdef WITH_TESTS @@ -41,7 +40,7 @@ #include <extensionsystem/pluginmanager.h> #include <projectexplorer/environmentaspect.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> @@ -53,14 +52,10 @@ using namespace ProjectExplorer; namespace QmlProfiler::Internal { -Q_GLOBAL_STATIC(QmlProfilerSettings, qmlProfilerGlobalSettings) - class QmlProfilerPluginPrivate { public: QmlProfilerTool m_profilerTool; - QmlProfilerOptionsPage m_profilerOptionsPage; - QmlProfilerActions m_actions; // The full local profiler. LocalQmlProfilerRunWorkerFactory localQmlProfilerRunWorkerFactory; @@ -102,8 +97,6 @@ bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorS void QmlProfilerPlugin::extensionsInitialized() { d = new QmlProfilerPluginPrivate; - d->m_actions.attachToTool(&d->m_profilerTool); - d->m_actions.registerActions(); RunConfiguration::registerAspect<QmlProfilerRunConfigurationAspect>(); } @@ -119,9 +112,4 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown() return SynchronousShutdown; } -QmlProfilerSettings *QmlProfilerPlugin::globalSettings() -{ - return qmlProfilerGlobalSettings(); -} - } // QmlProfiler::Internal diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.h b/src/plugins/qmlprofiler/qmlprofilerplugin.h index e189268e705..d04f016a877 100644 --- a/src/plugins/qmlprofiler/qmlprofilerplugin.h +++ b/src/plugins/qmlprofiler/qmlprofilerplugin.h @@ -7,16 +7,11 @@ namespace QmlProfiler::Internal { -class QmlProfilerSettings; - class QmlProfilerPlugin : public ExtensionSystem::IPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlProfiler.json") -public: - static QmlProfilerSettings *globalSettings(); - private: bool initialize(const QStringList &arguments, QString *errorString) final; void extensionsInitialized() final; diff --git a/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp b/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp index ac3056d3da3..e513e3fe6fd 100644 --- a/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerrunconfigurationaspect.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "qmlprofilerconstants.h" -#include "qmlprofilerplugin.h" #include "qmlprofilerrunconfigurationaspect.h" + +#include "qmlprofilerconstants.h" #include "qmlprofilersettings.h" #include "qmlprofilertr.h" @@ -14,7 +14,7 @@ namespace QmlProfiler::Internal { QmlProfilerRunConfigurationAspect::QmlProfilerRunConfigurationAspect(ProjectExplorer::Target *) { setProjectSettings(new QmlProfilerSettings); - setGlobalSettings(QmlProfilerPlugin::globalSettings()); + setGlobalSettings(&Internal::globalSettings()); setId(Constants::SETTINGS); setDisplayName(Tr::tr("QML Profiler Settings")); setUsingGlobalSettings(true); diff --git a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp index 41b67edda78..1fc8f83bd03 100644 --- a/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerruncontrol.cpp @@ -8,14 +8,14 @@ #include <coreplugin/icore.h> #include <coreplugin/helpmanager.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorericons.h> #include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <qmldebug/qmldebugcommandlinearguments.h> @@ -31,7 +31,7 @@ using namespace ProjectExplorer; namespace QmlProfiler::Internal { -const QString QmlServerUrl = "QmlServerUrl"; +const char QmlServerUrl[] = "QmlServerUrl"; // // QmlProfilerRunControlPrivate diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.cpp b/src/plugins/qmlprofiler/qmlprofilersettings.cpp index 1002479baae..9bbac157414 100644 --- a/src/plugins/qmlprofiler/qmlprofilersettings.cpp +++ b/src/plugins/qmlprofiler/qmlprofilersettings.cpp @@ -1,52 +1,33 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "qmlprofilerconstants.h" -#include "qmlprofilerplugin.h" #include "qmlprofilersettings.h" + +#include "qmlprofilerconstants.h" #include "qmlprofilertr.h" -#include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> #include <debugger/analyzer/analyzericons.h> #include <debugger/debuggertr.h> #include <utils/layoutbuilder.h> -#include <QSettings> - using namespace Utils; namespace QmlProfiler::Internal { -class QmlProfilerOptionsPageWidget : public Core::IOptionsPageWidget +QmlProfilerSettings &globalSettings() { -public: - explicit QmlProfilerOptionsPageWidget(QmlProfilerSettings *settings) - { - QmlProfilerSettings &s = *settings; - - using namespace Layouting; - Form { - s.flushEnabled, br, - s.flushInterval, br, - s.aggregateTraces, br, - }.attachTo(this); - } - - void apply() final - { - QmlProfilerPlugin::globalSettings()->writeGlobalSettings(); - } -}; + static QmlProfilerSettings theSettings; + return theSettings; +} QmlProfilerSettings::QmlProfilerSettings() { - setConfigWidgetCreator([this] { return new QmlProfilerOptionsPageWidget(this); }); - + setAutoApply(false); setSettingsGroup(Constants::ANALYZER); - registerAspect(&flushEnabled); flushEnabled.setSettingsKey("Analyzer.QmlProfiler.FlushEnabled"); flushEnabled.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); flushEnabled.setLabelText(Tr::tr("Flush data while profiling:")); @@ -55,17 +36,14 @@ QmlProfilerSettings::QmlProfilerSettings() "data and the memory usage in the application. It distorts the profile as the flushing\n" "itself takes time.")); - registerAspect(&flushInterval); flushInterval.setSettingsKey("Analyzer.QmlProfiler.FlushInterval"); flushInterval.setRange(1, 10000000); flushInterval.setDefaultValue(1000); flushInterval.setLabelText(Tr::tr("Flush interval (ms):")); flushInterval.setEnabler(&flushEnabled); - registerAspect(&lastTraceFile); lastTraceFile.setSettingsKey("Analyzer.QmlProfiler.LastTraceFile"); - registerAspect(&aggregateTraces); aggregateTraces.setSettingsKey("Analyzer.QmlProfiler.AggregateTraces"); aggregateTraces.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); aggregateTraces.setLabelText(Tr::tr("Process data only when process ends:")); @@ -75,27 +53,34 @@ QmlProfilerSettings::QmlProfilerSettings() "for example if multiple QML engines start and stop sequentially during a single run of\n" "the program.")); - // Read stored values - readSettings(Core::ICore::settings()); -} - -void QmlProfilerSettings::writeGlobalSettings() const -{ - writeSettings(Core::ICore::settings()); -} - -// QmlProfilerOptionsPage - -QmlProfilerOptionsPage::QmlProfilerOptionsPage() -{ - setId(Constants::SETTINGS); - setDisplayName(Tr::tr("QML Profiler")); - setCategory("T.Analyzer"); - setDisplayCategory(::Debugger::Tr::tr("Analyzer")); - setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); - setWidgetCreator([] { - return new QmlProfilerOptionsPageWidget(QmlProfilerPlugin::globalSettings()); + setLayouter([this] { + using namespace Layouting; + return Form { + flushEnabled, br, + flushInterval, br, + aggregateTraces, br, + }; }); + + readSettings(); } +// QmlProfilerSettingsPage + +class QmlProfilerSettingsPage final : public Core::IOptionsPage +{ +public: + QmlProfilerSettingsPage() + { + setId(Constants::SETTINGS); + setDisplayName(Tr::tr("QML Profiler")); + setCategory("T.Analyzer"); + setDisplayCategory(::Debugger::Tr::tr("Analyzer")); + setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); + setSettingsProvider([] { return &globalSettings(); }); + } +}; + +const QmlProfilerSettingsPage settingsPage; + } // QmlProfiler::Internal diff --git a/src/plugins/qmlprofiler/qmlprofilersettings.h b/src/plugins/qmlprofiler/qmlprofilersettings.h index 14e35676215..0336aee31a7 100644 --- a/src/plugins/qmlprofiler/qmlprofilersettings.h +++ b/src/plugins/qmlprofiler/qmlprofilersettings.h @@ -3,29 +3,21 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> - -#include <projectexplorer/runconfiguration.h> +#include <utils/aspects.h> namespace QmlProfiler::Internal { -class QmlProfilerSettings : public ProjectExplorer::ISettingsAspect +class QmlProfilerSettings : public Utils::AspectContainer { public: QmlProfilerSettings(); - void writeGlobalSettings() const; - - Utils::BoolAspect flushEnabled; - Utils::IntegerAspect flushInterval; - Utils::StringAspect lastTraceFile; - Utils::BoolAspect aggregateTraces; + Utils::BoolAspect flushEnabled{this}; + Utils::IntegerAspect flushInterval{this}; + Utils::FilePathAspect lastTraceFile{this}; + Utils::BoolAspect aggregateTraces{this}; }; -class QmlProfilerOptionsPage final : public Core::IOptionsPage -{ -public: - QmlProfilerOptionsPage(); -}; +QmlProfilerSettings &globalSettings(); } // QmlProfiler::Internal diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index 6b8a2020c97..92126d42e34 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -121,11 +121,7 @@ QStringList QmlProfilerStatisticsModel::details(int typeIndex) const data = data.left(maxColumnWidth - 1) + ellipsisChar; } - return QStringList({ - displayName, - data, - QString::number(durationPercent(typeIndex), 'f', 2) + QLatin1Char('%') - }); + return {displayName, data, QString::number(durationPercent(typeIndex), 'f', 2) + '%'}; } QString QmlProfilerStatisticsModel::summary(const QVector<int> &typeIds) const diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index f003d5f0e40..ea02239a64e 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -1,6 +1,8 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "qmlprofilertool.h" + #include "qmlprofilerattachdialog.h" #include "qmlprofilerclientmanager.h" #include "qmlprofilerconstants.h" @@ -11,13 +13,11 @@ #include "qmlprofilersettings.h" #include "qmlprofilerstatemanager.h" #include "qmlprofilertextmark.h" -#include "qmlprofilertool.h" #include "qmlprofilertr.h" #include "qmlprofilerviewmanager.h" -#include <app/app_version.h> - #include <coreplugin/actionmanager/command.h> +#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> @@ -29,20 +29,21 @@ #include <coreplugin/modemanager.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <debugger/analyzer/analyzerconstants.h> #include <debugger/analyzer/analyzermanager.h> #include <debugger/debuggericons.h> #include <debugger/debuggermainwindow.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/environmentaspect.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <texteditor/texteditor.h> @@ -107,6 +108,12 @@ public: QElapsedTimer m_recordingElapsedTime; bool m_toolBusy = false; + + std::unique_ptr<Core::ActionContainer> m_options; + std::unique_ptr<QAction> m_loadQmlTrace; + std::unique_ptr<QAction> m_saveQmlTrace; + std::unique_ptr<QAction> m_runAction; + std::unique_ptr<QAction> m_attachAction; }; QmlProfilerTool::QmlProfilerTool() @@ -230,9 +237,9 @@ QmlProfilerTool::QmlProfilerTool() QmlProfilerTextMarkModel *model = d->m_profilerModelManager->textMarkModel(); if (EditorManager *editorManager = EditorManager::instance()) { connect(editorManager, &EditorManager::editorCreated, - model, [this, model](Core::IEditor *editor, const QString &fileName) { + model, [this, model](Core::IEditor *editor, const FilePath &filePath) { Q_UNUSED(editor) - model->createMarks(d->m_viewContainer, fileName); + model->createMarks(d->m_viewContainer, filePath.toString()); }); } @@ -257,6 +264,63 @@ QmlProfilerTool::QmlProfilerTool() connect(d->m_profilerState, &QmlProfilerStateManager::clientRecordingChanged, d->m_recordButton, updateRecordButton); updateRecordButton(); + + + const QString description = Tr::tr("The QML Profiler can be used to find performance " + "bottlenecks in applications using QML."); + + d->m_runAction = std::make_unique<QAction>(Tr::tr("QML Profiler")); + d->m_runAction->setToolTip(description); + QObject::connect(d->m_runAction.get(), &QAction::triggered, + this, &QmlProfilerTool::profileStartupProject); + + QAction *toolStartAction = startAction(); + QObject::connect(toolStartAction, &QAction::changed, this, [this, toolStartAction] { + d->m_runAction->setEnabled(toolStartAction->isEnabled()); + }); + + d->m_attachAction = std::make_unique<QAction>(Tr::tr("QML Profiler (Attach to Waiting Application)")); + d->m_attachAction->setToolTip(description); + QObject::connect(d->m_attachAction.get(), &QAction::triggered, + this, &QmlProfilerTool::attachToWaitingApplication); + + d->m_loadQmlTrace = std::make_unique<QAction>(Tr::tr("Load QML Trace")); + connect(d->m_loadQmlTrace.get(), &QAction::triggered, + this, &QmlProfilerTool::showLoadDialog, Qt::QueuedConnection); + + d->m_saveQmlTrace = std::make_unique<QAction>(Tr::tr("Save QML Trace")); + connect(d->m_saveQmlTrace.get(), &QAction::triggered, + this, &QmlProfilerTool::showSaveDialog, Qt::QueuedConnection); + + connect(d->m_profilerState, &QmlProfilerStateManager::serverRecordingChanged, + this, [this](bool recording) { + d->m_loadQmlTrace->setEnabled(!recording); + }); + d->m_loadQmlTrace->setEnabled(!d->m_profilerState->serverRecording()); + + connect(d->m_profilerModelManager, &QmlProfilerModelManager::traceChanged, + this, [this] { + d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty()); + }); + d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty()); + + d->m_options.reset(ActionManager::createMenu("Analyzer.Menu.QMLOptions")); + d->m_options->menu()->setTitle(Tr::tr("QML Profiler Options")); + d->m_options->menu()->setEnabled(true); + ActionContainer *menu = ActionManager::actionContainer(M_DEBUG_ANALYZER); + + menu->addAction(ActionManager::registerAction(d->m_runAction.get(), + "QmlProfiler.Internal"), + Debugger::Constants::G_ANALYZER_TOOLS); + menu->addAction(ActionManager::registerAction(d->m_attachAction.get(), + "QmlProfiler.AttachToWaitingApplication"), + Debugger::Constants::G_ANALYZER_REMOTE_TOOLS); + + menu->addMenu(d->m_options.get(), G_ANALYZER_OPTIONS); + d->m_options->addAction(ActionManager::registerAction(d->m_loadQmlTrace.get(), + Constants::QmlProfilerLoadActionId)); + d->m_options->addAction(ActionManager::registerAction(d->m_saveQmlTrace.get(), + Constants::QmlProfilerSaveActionId)); } QmlProfilerTool::~QmlProfilerTool() @@ -278,11 +342,11 @@ void QmlProfilerTool::updateRunActions() d->m_startAction->setToolTip(Tr::tr("A QML Profiler analysis is still in progress.")); d->m_stopAction->setEnabled(true); } else { - QString tooltip = Tr::tr("Start QML Profiler analysis."); - bool canRun = ProjectExplorerPlugin::canRunStartupProject - (ProjectExplorer::Constants::QML_PROFILER_RUN_MODE, &tooltip); - d->m_startAction->setToolTip(tooltip); - d->m_startAction->setEnabled(canRun); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject( + ProjectExplorer::Constants::QML_PROFILER_RUN_MODE); + d->m_startAction->setToolTip(canRun ? Tr::tr("Start QML Profiler analysis.") + : canRun.error()); + d->m_startAction->setEnabled(bool(canRun)); d->m_stopAction->setEnabled(false); } } @@ -293,13 +357,16 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunner *runWorker) auto runControl = runWorker->runControl(); if (auto aspect = runControl->aspect<QmlProfilerRunConfigurationAspect>()) { if (auto settings = static_cast<const QmlProfilerSettings *>(aspect->currentSettings)) { - d->m_profilerConnections->setFlushInterval(settings->flushEnabled.value() ? - settings->flushInterval.value() : 0); - d->m_profilerModelManager->setAggregateTraces(settings->aggregateTraces.value()); + d->m_profilerConnections->setFlushInterval(settings->flushEnabled() ? + settings->flushInterval() : 0); + d->m_profilerModelManager->setAggregateTraces(settings->aggregateTraces()); } } - auto handleStop = [this, runControl]() { + auto handleStop = [this, runControl] { + if (!d->m_toolBusy) + return; + d->m_toolBusy = false; updateRunActions(); disconnect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop); @@ -313,11 +380,6 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunner *runWorker) }; connect(runControl, &RunControl::stopped, this, handleStop); - connect(runControl, &RunControl::finished, this, [this, handleStop] { - if (d->m_toolBusy) - handleStop(); - }); - connect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop); updateRunActions(); @@ -333,7 +395,7 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunner *runWorker) runWorker, [this, runWorker]() { auto infoBox = new QMessageBox(ICore::dialogParent()); infoBox->setIcon(QMessageBox::Critical); - infoBox->setWindowTitle(Core::Constants::IDE_DISPLAY_NAME); + infoBox->setWindowTitle(QGuiApplication::applicationDisplayName()); const int interval = d->m_profilerConnections->retryInterval(); const int retries = d->m_profilerConnections->maximumRetries(); @@ -507,10 +569,10 @@ ProjectExplorer::RunControl *QmlProfilerTool::attachToWaitingApplication() Kit *kit = nullptr; { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); - kitId = Id::fromSetting(settings->value(QLatin1String("AnalyzerQmlAttachDialog/kitId"))); - port = settings->value(QLatin1String("AnalyzerQmlAttachDialog/port"), 3768).toInt(); + kitId = Id::fromSetting(settings->value("AnalyzerQmlAttachDialog/kitId")); + port = settings->value("AnalyzerQmlAttachDialog/port", 3768).toInt(); QmlProfilerAttachDialog dialog; @@ -525,8 +587,8 @@ ProjectExplorer::RunControl *QmlProfilerTool::attachToWaitingApplication() QTC_ASSERT(port >= 0, return nullptr); QTC_ASSERT(port <= std::numeric_limits<quint16>::max(), return nullptr); - settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/kitId"), kit->id().toSetting()); - settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/port"), port); + settings->setValue("AnalyzerQmlAttachDialog/kitId", kit->id().toSetting()); + settings->setValue("AnalyzerQmlAttachDialog/port", port); } QUrl serverUrl; @@ -570,10 +632,10 @@ void QmlProfilerTool::showErrorDialog(const QString &error) static void saveLastTraceFile(const FilePath &filePath) { - QmlProfilerSettings *settings = QmlProfilerPlugin::globalSettings(); - if (filePath != settings->lastTraceFile.filePath()) { - settings->lastTraceFile.setFilePath(filePath); - settings->writeGlobalSettings(); + QmlProfilerSettings &s = globalSettings(); + if (filePath != s.lastTraceFile()) { + s.lastTraceFile.setValue(filePath); + s.writeSettings(); } } @@ -583,7 +645,7 @@ void QmlProfilerTool::showSaveDialog() QLatin1String zFile(QztFileExtension); FilePath filePath = FileUtils::getSaveFilePath( nullptr, Tr::tr("Save QML Trace"), - QmlProfilerPlugin::globalSettings()->lastTraceFile.filePath(), + globalSettings().lastTraceFile(), Tr::tr("QML traces (*%1 *%2)").arg(zFile).arg(tFile)); if (!filePath.isEmpty()) { if (!filePath.endsWith(zFile) && !filePath.endsWith(tFile)) @@ -607,7 +669,7 @@ void QmlProfilerTool::showLoadDialog() QLatin1String zFile(QztFileExtension); FilePath filePath = FileUtils::getOpenFilePath( nullptr, Tr::tr("Load QML Trace"), - QmlProfilerPlugin::globalSettings()->lastTraceFile.filePath(), + globalSettings().lastTraceFile(), Tr::tr("QML traces (*%1 *%2)").arg(zFile).arg(tFile)); if (!filePath.isEmpty()) { diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp index c859624f60d..d07b731815a 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp @@ -298,10 +298,10 @@ bool TraceViewFindSupport::supportsReplace() const return false; } -Core::FindFlags TraceViewFindSupport::supportedFindFlags() const +Utils::FindFlags TraceViewFindSupport::supportedFindFlags() const { - return Core::FindBackward | Core::FindCaseSensitively | Core::FindRegularExpression - | Core::FindWholeWords; + return Utils::FindBackward | Utils::FindCaseSensitively | Utils::FindRegularExpression + | Utils::FindWholeWords; } void TraceViewFindSupport::resetIncrementalSearch() @@ -325,7 +325,7 @@ QString TraceViewFindSupport::completedFindString() const } Core::IFindSupport::Result TraceViewFindSupport::findIncremental(const QString &txt, - Core::FindFlags findFlags) + Utils::FindFlags findFlags) { if (m_incrementalStartPos < 0) m_incrementalStartPos = qMax(m_currentPosition, 0); @@ -339,9 +339,9 @@ Core::IFindSupport::Result TraceViewFindSupport::findIncremental(const QString & } Core::IFindSupport::Result TraceViewFindSupport::findStep(const QString &txt, - Core::FindFlags findFlags) + Utils::FindFlags findFlags) { - int start = (findFlags & Core::FindBackward) ? m_currentPosition : m_currentPosition + 1; + int start = (findFlags & Utils::FindBackward) ? m_currentPosition : m_currentPosition + 1; bool wrapped; bool found = find(txt, findFlags, start, &wrapped); if (wrapped) @@ -355,14 +355,14 @@ Core::IFindSupport::Result TraceViewFindSupport::findStep(const QString &txt, // "start" is the model index that is searched first in a forward search, i.e. as if the // "cursor" were between start-1 and start -bool TraceViewFindSupport::find(const QString &txt, Core::FindFlags findFlags, int start, +bool TraceViewFindSupport::find(const QString &txt, Utils::FindFlags findFlags, int start, bool *wrapped) { if (wrapped) *wrapped = false; if (!findOne(txt, findFlags, start)) { int secondStart; - if (findFlags & Core::FindBackward) + if (findFlags & Utils::FindBackward) secondStart = m_modelManager->notesModel()->count(); else secondStart = 0; @@ -376,19 +376,19 @@ bool TraceViewFindSupport::find(const QString &txt, Core::FindFlags findFlags, i // "start" is the model index that is searched first in a forward search, i.e. as if the // "cursor" were between start-1 and start -bool TraceViewFindSupport::findOne(const QString &txt, Core::FindFlags findFlags, int start) +bool TraceViewFindSupport::findOne(const QString &txt, Utils::FindFlags findFlags, int start) { - bool caseSensitiveSearch = (findFlags & Core::FindCaseSensitively); - bool regexSearch = (findFlags & Core::FindRegularExpression); + bool caseSensitiveSearch = (findFlags & Utils::FindCaseSensitively); + bool regexSearch = (findFlags & Utils::FindRegularExpression); QRegularExpression regexp(regexSearch ? txt : QRegularExpression::escape(txt), caseSensitiveSearch ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption); QTextDocument::FindFlags flags; if (caseSensitiveSearch) flags |= QTextDocument::FindCaseSensitively; - if (findFlags & Core::FindWholeWords) + if (findFlags & Utils::FindWholeWords) flags |= QTextDocument::FindWholeWords; - bool forwardSearch = !(findFlags & Core::FindBackward); + bool forwardSearch = !(findFlags & Utils::FindBackward); int increment = forwardSearch ? +1 : -1; int current = forwardSearch ? start : start - 1; Timeline::TimelineNotesModel *model = m_modelManager->notesModel(); diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.h b/src/plugins/qmlprofiler/qmlprofilertraceview.h index 37d22913ac3..ff41857fba9 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.h +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.h @@ -61,17 +61,17 @@ public: TraceViewFindSupport(QmlProfilerTraceView *view, QmlProfilerModelManager *manager); bool supportsReplace() const override; - Core::FindFlags supportedFindFlags() const override; + Utils::FindFlags supportedFindFlags() const override; void resetIncrementalSearch() override; void clearHighlights() override; QString currentFindString() const override; QString completedFindString() const override; - Result findIncremental(const QString &txt, Core::FindFlags findFlags) override; - Result findStep(const QString &txt, Core::FindFlags findFlags) override; + Result findIncremental(const QString &txt, Utils::FindFlags findFlags) override; + Result findStep(const QString &txt, Utils::FindFlags findFlags) override; private: - bool find(const QString &txt, Core::FindFlags findFlags, int start, bool *wrapped); - bool findOne(const QString &txt, Core::FindFlags findFlags, int start); + bool find(const QString &txt, Utils::FindFlags findFlags, int start, bool *wrapped); + bool findOne(const QString &txt, Utils::FindFlags findFlags, int start); QmlProfilerTraceView *m_view; QmlProfilerModelManager *m_modelManager; diff --git a/src/plugins/qmlprofiler/quick3dframeview.cpp b/src/plugins/qmlprofiler/quick3dframeview.cpp index 9853dbe9912..3d0141d0ed9 100644 --- a/src/plugins/qmlprofiler/quick3dframeview.cpp +++ b/src/plugins/qmlprofiler/quick3dframeview.cpp @@ -118,6 +118,7 @@ void Quick3DFrameView::onVisibleFeaturesChanged(quint64) Quick3DMainView::Quick3DMainView(Quick3DFrameModel *model, bool compareView, QWidget *parent) : Utils::TreeView(parent), m_model(model), m_compareView(compareView) { + setUniformRowHeights(false); setObjectName("Quick3DMainView"); setFrameStyle(QFrame::NoFrame); QHeaderView *h = header(); diff --git a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp index 406b8dd4191..7799c960289 100644 --- a/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp +++ b/src/plugins/qmlprofiler/tests/localqmlprofilerrunner_test.cpp @@ -66,10 +66,6 @@ void LocalQmlProfilerRunnerTest::testRunner() running = false; started = false; }); - connect(runControl, &RunControl::finished, this, [&]{ - running = false; - started = false; - }); }; connectRunner(); @@ -84,7 +80,8 @@ void LocalQmlProfilerRunnerTest::testRunner() QCOMPARE(stopCount, 1); QCOMPARE(runCount, 0); - runControl->initiateFinish(); + runControl->setAutoDeleteOnStop(true); + runControl->initiateStop(); QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); @@ -104,7 +101,8 @@ void LocalQmlProfilerRunnerTest::testRunner() QCOMPARE(stopCount, 2); QCOMPARE(runCount, 1); - runControl->initiateFinish(); + runControl->setAutoDeleteOnStop(true); + runControl->initiateStop(); QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); @@ -123,7 +121,8 @@ void LocalQmlProfilerRunnerTest::testRunner() QCOMPARE(stopCount, 3); QCOMPARE(runCount, 2); - runControl->initiateFinish(); + runControl->setAutoDeleteOnStop(true); + runControl->initiateStop(); QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); @@ -146,7 +145,8 @@ void LocalQmlProfilerRunnerTest::testRunner() QCOMPARE(stopCount, 4); QCOMPARE(runCount, 3); - runControl->initiateFinish(); + runControl->setAutoDeleteOnStop(true); + runControl->initiateStop(); QTRY_VERIFY(runControl.isNull()); QVERIFY(profiler.isNull()); } @@ -165,7 +165,7 @@ void LocalQmlProfilerRunnerTest::testFindFreeSocket() QUrl serverUrl = Utils::urlFromLocalSocket(); QString socket = serverUrl.path(); QVERIFY(!socket.isEmpty()); - QVERIFY(!QFile::exists(socket)); + QVERIFY(!QFileInfo::exists(socket)); QFile file(socket); QVERIFY(file.open(QIODevice::WriteOnly)); file.close(); diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerbindingloopsrenderpass_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerbindingloopsrenderpass_test.cpp index 2f720557524..0ddfc2bc927 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilerbindingloopsrenderpass_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilerbindingloopsrenderpass_test.cpp @@ -4,10 +4,10 @@ #include "qmlprofilerbindingloopsrenderpass_test.h" +#include <tracing/timelineabstractrenderer.h> #include <qmlprofiler/qmlprofilerbindingloopsrenderpass.h> #include <qmlprofiler/qmlprofilerrangemodel.h> -#include <tracing/timelineabstractrenderer.h> -#include <tracing/runscenegraphtest.h> + #include <QtTest> namespace QmlProfiler { @@ -131,8 +131,8 @@ void QmlProfilerBindingLoopsRenderPassTest::testUpdate() parentState.setPassState(0, result); parentState.assembleNodeTree(&model, 1, 1); - Timeline::runSceneGraphTest(parentState.collapsedOverlayRoot()); - Timeline::runSceneGraphTest(parentState.expandedRowRoot()); + QVERIFY(parentState.collapsedOverlayRoot()); + QVERIFY(parentState.expandedRowRoot()); } } // namespace Internal diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp index 4cf135f0be7..4539948be37 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp @@ -6,7 +6,7 @@ #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/buildinfo.h> #include <projectexplorer/customexecutablerunconfiguration.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> diff --git a/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp index ea119517ff0..1b16b421141 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilertool_test.cpp @@ -5,18 +5,24 @@ #include "fakedebugserver.h" #include <coreplugin/icore.h> + #include <projectexplorer/kit.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/runcontrol.h> + #include <qmlprofiler/qmlprofilerattachdialog.h> #include <qmlprofiler/qmlprofilerclientmanager.h> #include <qmlprofiler/qmlprofilermodelmanager.h> #include <qmlprofiler/qmlprofilerstatemanager.h> + +#include <utils/qtcsettings.h> #include <utils/url.h> #include <QTcpServer> #include <QtTest> +using namespace Utils; + namespace QmlProfiler { namespace Internal { @@ -26,9 +32,9 @@ void QmlProfilerToolTest::testAttachToWaitingApplication() QVERIFY(kitManager); ProjectExplorer::Kit * const newKit = ProjectExplorer::KitManager::registerKit({}, "fookit"); QVERIFY(newKit); - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); QVERIFY(settings); - settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/kitId"), newKit->id().toSetting()); + settings->setValue("AnalyzerQmlAttachDialog/kitId", newKit->id().toSetting()); QmlProfilerTool &profilerTool = *QmlProfilerTool::instance(); diff --git a/src/plugins/qmlprojectmanager/QmlProjectManager.json.in b/src/plugins/qmlprojectmanager/QmlProjectManager.json.in index 11e400284a7..5df33015f48 100644 --- a/src/plugins/qmlprojectmanager/QmlProjectManager.json.in +++ b/src/plugins/qmlprojectmanager/QmlProjectManager.json.in @@ -1,31 +1,31 @@ { - \"Name\" : \"QmlProjectManager\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QmlProjectManager", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Quick\", - \"Description\" : \"Qt Quick support\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Qt Quick", + "Description" : "Qt Quick support", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", + "Mimetypes" : [ + "<?xml version='1.0'?>", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/x-qmlproject\'>\", - \" <sub-class-of type=\'text/x-qml\'/>\", - \" <comment>QML Project file</comment>\", - \" <glob pattern=\'*.qmlproject\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/x-qmlproject'>", + " <sub-class-of type='text/x-qml'/>", + " <comment>QML Project file</comment>", + " <glob pattern='*.qmlproject'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index a4be2d9ac28..89abb79fcdf 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -44,9 +44,9 @@ QString jsonToQmlProject(const QJsonObject &rootObject) auto appendArray = [&appendItem](const QString &key, const QStringList &vals) { QString finalString; - foreach (const QString &value, vals) { + for (const QString &value : vals) finalString.append("\"").append(value).append("\"").append(","); - } + finalString.remove(finalString.length() - 1, 1); finalString.prepend("[ ").append(" ]"); appendItem(key, finalString, false); @@ -107,9 +107,9 @@ QString jsonToQmlProject(const QJsonObject &rootObject) // append Environment object startObject("Environment"); - foreach (const QString &key, environmentConfig.keys()) { + for (const QString &key : environmentConfig.keys()) appendItem(key, environmentConfig[key].toString(), true); - } + endObject(); // append ShaderTool object @@ -154,9 +154,9 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) auto nodeToJsonObject = [](const QmlJS::SimpleReaderNode::Ptr &node) { QJsonObject tObj; - foreach (const QString &childPropName, node->propertyNames()) { + for (const QString &childPropName : node->propertyNames()) tObj.insert(childPropName, node->property(childPropName).value.toJsonValue()); - } + return tObj; }; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp index 6a90b79badb..c80f13e9b2b 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/qmlprojectitem.cpp @@ -137,9 +137,9 @@ Utils::EnvironmentItems QmlProjectItem::environment() const { Utils::EnvironmentItems envItems; QJsonObject envVariables = m_project["environment"].toObject(); - foreach (const QString &variableName, envVariables.keys()) { + const QStringList variableNames = envVariables.keys(); + for (const QString &variableName : variableNames) envItems.append(Utils::EnvironmentItem(variableName, envVariables[variableName].toString())); - } return envItems; } diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 6b6ef441bfe..4016cc6ac6f 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -27,7 +27,7 @@ #include <projectexplorer/deploymentdata.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp index 349c23a6cf4..1f5b0e69a38 100644 --- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp @@ -37,8 +37,8 @@ static bool caseInsensitiveLessThan(const FilePath &s1, const FilePath &s2) return s1.toString().toCaseFolded() < s2.toString().toCaseFolded(); } -QmlMainFileAspect::QmlMainFileAspect(Target *target) - : m_target(target) +QmlMainFileAspect::QmlMainFileAspect(AspectContainer *container) + : BaseAspect(container) , m_scriptFile(M_CURRENT_FILE) { addDataExtractor(this, &QmlMainFileAspect::mainScript, &Data::mainScript); @@ -70,12 +70,12 @@ void QmlMainFileAspect::addToLayout(Layouting::LayoutItem &parent) parent.addItems({Tr::tr("Main QML file:"), m_fileListCombo.data()}); } -void QmlMainFileAspect::toMap(QVariantMap &map) const +void QmlMainFileAspect::toMap(Store &map) const { map.insert(Constants::QML_MAINSCRIPT_KEY, m_scriptFile); } -void QmlMainFileAspect::fromMap(const QVariantMap &map) +void QmlMainFileAspect::fromMap(const Store &map) { m_scriptFile = map.value(Constants::QML_MAINSCRIPT_KEY, M_CURRENT_FILE).toString(); @@ -158,6 +158,11 @@ void QmlMainFileAspect::setMainScript(int index) } } +void QmlMainFileAspect::setTarget(ProjectExplorer::Target *target) +{ + m_target = target; +} + void QmlMainFileAspect::setScriptSource(MainScriptSource source, const QString &settingsPath) { if (source == FileInEditor) { diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h index 3c75e4744a2..4a64055b6c4 100644 --- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.h +++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.h @@ -27,7 +27,7 @@ class QMLPROJECTMANAGER_EXPORT QmlMainFileAspect : public Utils::BaseAspect Q_OBJECT public: - explicit QmlMainFileAspect(ProjectExplorer::Target *target); + explicit QmlMainFileAspect(Utils::AspectContainer *container = nullptr); ~QmlMainFileAspect() override; enum MainScriptSource { @@ -43,13 +43,14 @@ public: }; void addToLayout(Layouting::LayoutItem &parent) final; - void toMap(QVariantMap &map) const final; - void fromMap(const QVariantMap &map) final; + void toMap(Utils::Store &map) const final; + void fromMap(const Utils::Store &map) final; void updateFileComboBox(); MainScriptSource mainScriptSource() const; void setMainScript(int index); + void setTarget(ProjectExplorer::Target *target); void setScriptSource(MainScriptSource source, const QString &settingsPath = QString()); Utils::FilePath mainScript() const; diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp index 13c2bab9bbb..1b3238a425b 100644 --- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.cpp @@ -14,6 +14,11 @@ #include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> +using namespace ProjectExplorer; +using namespace Utils; + +namespace QmlProjectManager { + static bool isMultilanguagePresent() { const QVector<ExtensionSystem::PluginSpec *> &specs = ExtensionSystem::PluginManager::plugins(); @@ -24,7 +29,7 @@ static bool isMultilanguagePresent() != specs.cend(); } -static Utils::FilePath getMultilanguageDatabaseFilePath(ProjectExplorer::Target *target) +static FilePath getMultilanguageDatabaseFilePath(ProjectExplorer::Target *target) { if (target) { auto filePath = target->project()->projectDirectory().pathAppended("translations.db"); @@ -48,11 +53,8 @@ static QObject *getPreviewPlugin() return nullptr; } - -namespace QmlProjectManager { - -QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target) - : m_target(target) +QmlMultiLanguageAspect::QmlMultiLanguageAspect(AspectContainer *container) + : BoolAspect(container) { setVisible(isMultilanguagePresent()); setSettingsKey(Constants::USE_MULTILANGUAGE_KEY); @@ -60,14 +62,13 @@ QmlMultiLanguageAspect::QmlMultiLanguageAspect(ProjectExplorer::Target *target) setToolTip(Tr::tr("Reads translations from MultiLanguage plugin.")); setDefaultValue(!databaseFilePath().isEmpty()); - QVariantMap getDefaultValues; + Store getDefaultValues; fromMap(getDefaultValues); addDataExtractor(this, &QmlMultiLanguageAspect::origin, &Data::origin); connect(this, &BoolAspect::changed, this, [this] { - for (ProjectExplorer::RunControl *runControl : - ProjectExplorer::ProjectExplorerPlugin::allRunControls()) { + for (RunControl *runControl : ProjectExplorerPlugin::allRunControls()) { if (auto aspect = runControl->aspect<QmlMultiLanguageAspect>()) { if (auto origin = aspect->origin; origin == this) runControl->initiateStop(); @@ -80,6 +81,11 @@ QmlMultiLanguageAspect::~QmlMultiLanguageAspect() { } +void QmlMultiLanguageAspect::setTarget(Target *target) +{ + m_target = target; +} + void QmlMultiLanguageAspect::setCurrentLocale(const QString &locale) { if (m_currentLocale == locale) @@ -101,14 +107,14 @@ Utils::FilePath QmlMultiLanguageAspect::databaseFilePath() const return m_databaseFilePath; } -void QmlMultiLanguageAspect::toMap(QVariantMap &map) const +void QmlMultiLanguageAspect::toMap(Store &map) const { BoolAspect::toMap(map); if (!m_currentLocale.isEmpty()) map.insert(Constants::LAST_USED_LANGUAGE, m_currentLocale); } -void QmlMultiLanguageAspect::fromMap(const QVariantMap &map) +void QmlMultiLanguageAspect::fromMap(const Store &map) { BoolAspect::fromMap(map); setCurrentLocale(map.value(Constants::LAST_USED_LANGUAGE, "en").toString()); @@ -116,19 +122,19 @@ void QmlMultiLanguageAspect::fromMap(const QVariantMap &map) QmlMultiLanguageAspect *QmlMultiLanguageAspect::current() { - if (auto project = ProjectExplorer::ProjectManager::startupProject()) + if (auto project = ProjectManager::startupProject()) return current(project); return {}; } -QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(ProjectExplorer::Project *project) +QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(Project *project) { if (auto target = project->activeTarget()) return current(target); return {}; } -QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(ProjectExplorer::Target *target) +QmlMultiLanguageAspect *QmlMultiLanguageAspect::current(Target *target) { if (!target) return {}; diff --git a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h index b3c047e75b1..8f0b8af4617 100644 --- a/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h +++ b/src/plugins/qmlprojectmanager/qmlmultilanguageaspect.h @@ -15,14 +15,16 @@ class QMLPROJECTMANAGER_EXPORT QmlMultiLanguageAspect : public Utils::BoolAspect { Q_OBJECT public: - explicit QmlMultiLanguageAspect(ProjectExplorer::Target *target); + explicit QmlMultiLanguageAspect(Utils::AspectContainer *container = nullptr); ~QmlMultiLanguageAspect() override; + void setTarget(ProjectExplorer::Target *target); + QString currentLocale() const; void setCurrentLocale(const QString &locale); Utils::FilePath databaseFilePath() const; - void toMap(QVariantMap &map) const final; - void fromMap(const QVariantMap &map) final; + void toMap(Utils::Store &map) const final; + void fromMap(const Utils::Store &map) final; static QmlMultiLanguageAspect *current(); static QmlMultiLanguageAspect *current(ProjectExplorer::Project *project); diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index ce7bd362877..360595e8fa4 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -4,7 +4,7 @@ #include "qmlproject.h" #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <QTimer> @@ -39,8 +39,10 @@ using namespace Core; using namespace ProjectExplorer; +using namespace Utils; namespace QmlProjectManager { + QmlProject::QmlProject(const Utils::FilePath &fileName) : Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName) { @@ -86,7 +88,7 @@ void QmlProject::parsingFinished(const Target *target, bool success) openFile(fileToOpen); } -Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage) +Project::RestoreResult QmlProject::fromMap(const Store &map, QString *errorMessage) { RestoreResult result = Project::fromMap(map, errorMessage); if (result != RestoreResult::Ok) @@ -231,9 +233,9 @@ int QmlProject::preferedQtTarget(Target *target) bool QmlProject::allowOnlySingleProject() { - auto settings = Core::ICore::settings(); - auto key = "QML/Designer/AllowMultipleProjects"; - return !settings->value(QString::fromUtf8(key), false).toBool(); + QtcSettings *settings = Core::ICore::settings(); + const Key key = "QML/Designer/AllowMultipleProjects"; + return !settings->value(key, false).toBool(); } } // namespace QmlProjectManager diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index a2d3aeff11b..a41b05e57bb 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -7,6 +7,8 @@ #include "qmlprojectmanager_global.h" #include <projectexplorer/project.h> +#include <QPointer> + namespace QmlProjectManager { class QmlProject; @@ -23,7 +25,7 @@ public: ProjectExplorer::Tasks projectIssues(const ProjectExplorer::Kit *k) const final; protected: - RestoreResult fromMap(const QVariantMap &map, QString *errorMessage) override; + RestoreResult fromMap(const Utils::Store &map, QString *errorMessage) override; private: ProjectExplorer::DeploymentKnowledge deploymentKnowledge() const override; diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 73f6aa438a6..24fd05a5540 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -41,6 +41,7 @@ #include <utils/fileutils.h> #include <utils/fsengine/fileiconprovider.h> #include <utils/process.h> +#include <utils/qtcsettings.h> #include <QAction> #include <QDesktopServices> @@ -50,6 +51,7 @@ #include <QTimer> using namespace ProjectExplorer; +using namespace Utils; namespace QmlProjectManager::Internal { @@ -131,8 +133,8 @@ void QmlProjectPlugin::openQDS(const Utils::FilePath &fileName) Utils::FilePath QmlProjectPlugin::qdsInstallationEntry() { - QSettings *settings = Core::ICore::settings(); - const QString qdsInstallationEntry = "QML/Designer/DesignStudioInstallation"; //set in installer + QtcSettings *settings = Core::ICore::settings(); + const Key qdsInstallationEntry = "QML/Designer/DesignStudioInstallation"; //set in installer return Utils::FilePath::fromUserInput(settings->value(qdsInstallationEntry).toString()); } diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 44ae1bf40b6..18e61462281 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -16,19 +16,18 @@ #include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/environmentaspect.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> #include <qmldesignerbase/qmldesignerbaseplugin.h> #include <qmldesignerbase/utils/qmlpuppetpaths.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtsupportconstants.h> #include <utils/algorithm.h> @@ -36,6 +35,7 @@ #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/process.h> +#include <utils/processinterface.h> #include <utils/winutils.h> #include <qmljstools/qmljstoolsconstants.h> @@ -60,26 +60,29 @@ private: FilePath mainScript() const; FilePath qmlRuntimeFilePath() const; - void createQtVersionAspect(); + void setupQtVersionAspect(); + + FilePathAspect qmlViewer{this}; + ArgumentsAspect arguments{this}; + QmlMainFileAspect qmlMainFile{this}; + SelectionAspect qtversion{this}; + QmlMultiLanguageAspect multiLanguage{this}; + EnvironmentAspect environment{this}; + X11ForwardingAspect x11Forwarding{this}; - FilePathAspect *m_qmlViewerAspect = nullptr; - QmlMainFileAspect *m_qmlMainFileAspect = nullptr; - QmlMultiLanguageAspect *m_multiLanguageAspect = nullptr; - SelectionAspect *m_qtversionAspect = nullptr; mutable bool usePuppetAsQmlRuntime = false; }; QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - m_qmlViewerAspect = addAspect<FilePathAspect>(); - m_qmlViewerAspect->setLabelText(Tr::tr("Override device QML viewer:")); - m_qmlViewerAspect->setPlaceHolderText(qmlRuntimeFilePath().toUserOutput()); - m_qmlViewerAspect->setHistoryCompleter("QmlProjectManager.viewer.history"); - m_qmlViewerAspect->setSettingsKey(Constants::QML_VIEWER_KEY); + qmlViewer.setSettingsKey(Constants::QML_VIEWER_KEY); + qmlViewer.setLabelText(Tr::tr("Override device QML viewer:")); + qmlViewer.setPlaceHolderText(qmlRuntimeFilePath().toUserOutput()); + qmlViewer.setHistoryCompleter("QmlProjectManager.viewer.history"); - auto argumentAspect = addAspect<ArgumentsAspect>(macroExpander()); - argumentAspect->setSettingsKey(Constants::QML_VIEWER_ARGUMENTS_KEY); + arguments.setSettingsKey(Constants::QML_VIEWER_ARGUMENTS_KEY); + arguments.setMacroExpander(macroExpander()); setCommandLineGetter([this, target] { const FilePath qmlRuntime = qmlRuntimeFilePath(); @@ -88,7 +91,7 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) cmd.addArg("--qml-runtime"); // arguments in .user file - cmd.addArgs(aspect<ArgumentsAspect>()->arguments(), CommandLine::Raw); + cmd.addArgs(arguments(), CommandLine::Raw); // arguments from .qmlproject file const QmlBuildSystem *bs = qobject_cast<QmlBuildSystem *>(target->buildSystem()); @@ -120,33 +123,31 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) return cmd; }); - m_qmlMainFileAspect = addAspect<QmlMainFileAspect>(target); - connect(m_qmlMainFileAspect, &QmlMainFileAspect::changed, this, &RunConfiguration::update); + qmlMainFile.setTarget(target); + connect(&qmlMainFile, &BaseAspect::changed, this, &RunConfiguration::update); - createQtVersionAspect(); + if (Core::ICore::isQtDesignStudio()) + setupQtVersionAspect(); + else + qtversion.setVisible(false); connect(target, &Target::kitChanged, this, &RunConfiguration::update); - m_multiLanguageAspect = addAspect<QmlMultiLanguageAspect>(target); + multiLanguage.setTarget(target); auto buildSystem = qobject_cast<const QmlBuildSystem *>(activeBuildSystem()); if (buildSystem) - m_multiLanguageAspect->setValue(buildSystem->multilanguageSupport()); + multiLanguage.setValue(buildSystem->multilanguageSupport()); - auto envAspect = addAspect<EnvironmentAspect>(); - connect(m_multiLanguageAspect, - &QmlMultiLanguageAspect::changed, - envAspect, - &EnvironmentAspect::environmentChanged); + connect(&multiLanguage, &BaseAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); auto envModifier = [this](Environment env) { if (auto bs = qobject_cast<const QmlBuildSystem *>(activeBuildSystem())) env.modify(bs->environment()); - if (m_multiLanguageAspect && m_multiLanguageAspect->value() - && !m_multiLanguageAspect->databaseFilePath().isEmpty()) { - env.set("QT_MULTILANGUAGE_DATABASE", - m_multiLanguageAspect->databaseFilePath().toString()); - env.set("QT_MULTILANGUAGE_LANGUAGE", m_multiLanguageAspect->currentLocale()); + if (multiLanguage() && !multiLanguage.databaseFilePath().isEmpty()) { + env.set("QT_MULTILANGUAGE_DATABASE", multiLanguage.databaseFilePath().path()); + env.set("QT_MULTILANGUAGE_LANGUAGE", multiLanguage.currentLocale()); } else { env.unset("QT_MULTILANGUAGE_DATABASE"); env.unset("QT_MULTILANGUAGE_LANGUAGE"); @@ -156,24 +157,21 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) const Id deviceTypeId = DeviceTypeKitAspect::deviceTypeId(target->kit()); if (deviceTypeId == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { - envAspect->addPreferredBaseEnvironment(Tr::tr("System Environment"), [envModifier] { + environment.addPreferredBaseEnvironment(Tr::tr("System Environment"), [envModifier] { return envModifier(Environment::systemEnvironment()); }); } - envAspect->addSupportedBaseEnvironment(Tr::tr("Clean Environment"), [envModifier] { + environment.addSupportedBaseEnvironment(Tr::tr("Clean Environment"), [envModifier] { Environment environment; return envModifier(environment); }); - if (HostOsInfo::isAnyUnixHost()) - addAspect<X11ForwardingAspect>(macroExpander()); + x11Forwarding.setMacroExpander(macroExpander()); - setRunnableModifier([this](Runnable &r) { + setRunnableModifier([this](ProcessRunData &r) { const QmlBuildSystem *bs = static_cast<QmlBuildSystem *>(activeBuildSystem()); r.workingDirectory = bs->targetDirectory(); - if (const auto * const forwardingAspect = aspect<X11ForwardingAspect>()) - r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); }); setDisplayName(Tr::tr("QML Utility", "QMLRunConfiguration display name.")); @@ -200,9 +198,8 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const { usePuppetAsQmlRuntime = false; // Give precedence to the manual override in the run configuration. - const FilePath qmlViewer = m_qmlViewerAspect->filePath(); - if (!qmlViewer.isEmpty()) - return qmlViewer; + if (!qmlViewer().isEmpty()) + return qmlViewer(); Kit *kit = target()->kit(); @@ -243,15 +240,14 @@ FilePath QmlProjectRunConfiguration::qmlRuntimeFilePath() const return dev ? dev->filePath("qml").searchInPath() : "qml"; } -void QmlProjectRunConfiguration::createQtVersionAspect() +void QmlProjectRunConfiguration::setupQtVersionAspect() { if (!Core::ICore::isQtDesignStudio()) return; - m_qtversionAspect = addAspect<SelectionAspect>(); - m_qtversionAspect->setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); - m_qtversionAspect->setLabelText(Tr::tr("Qt Version:")); - m_qtversionAspect->setSettingsKey("QmlProjectManager.kit"); + qtversion.setSettingsKey("QmlProjectManager.kit"); + qtversion.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + qtversion.setLabelText(Tr::tr("Qt Version:")); Kit *kit = target()->kit(); QtVersion *version = QtKitAspect::qtVersion(kit); @@ -261,23 +257,23 @@ void QmlProjectRunConfiguration::createQtVersionAspect() const bool isQt6Project = buildSystem && buildSystem->qt6Project(); if (isQt6Project) { - m_qtversionAspect->addOption(Tr::tr("Qt 6")); - m_qtversionAspect->setReadOnly(true); + qtversion.addOption(Tr::tr("Qt 6")); + qtversion.setReadOnly(true); } else { /* Only if this is not a Qt 6 project changing kits makes sense */ - m_qtversionAspect->addOption(Tr::tr("Qt 5")); - m_qtversionAspect->addOption(Tr::tr("Qt 6")); + qtversion.addOption(Tr::tr("Qt 5")); + qtversion.addOption(Tr::tr("Qt 6")); const int valueForVersion = version->qtVersion().majorVersion() == 6 ? 1 : 0; - m_qtversionAspect->setValue(valueForVersion); + qtversion.setValue(valueForVersion); - connect(m_qtversionAspect, &SelectionAspect::changed, this, [&]() { + connect(&qtversion, &BaseAspect::changed, this, [this] { QTC_ASSERT(target(), return ); auto project = target()->project(); QTC_ASSERT(project, return ); - int oldValue = !m_qtversionAspect->value(); - const int preferedQtVersion = m_qtversionAspect->value() > 0 ? 6 : 5; + int oldValue = !qtversion(); + const int preferedQtVersion = qtversion() > 0 ? 6 : 5; Kit *currentKit = target()->kit(); const QList<Kit *> kits = Utils::filtered(KitManager::kits(), [&](const Kit *k) { @@ -298,9 +294,10 @@ void QmlProjectRunConfiguration::createQtVersionAspect() project->setActiveTarget(newTarget, SetActive::Cascade); /* Reset the aspect. We changed the target and this aspect should not change. */ - m_qtversionAspect->blockSignals(true); - m_qtversionAspect->setValue(oldValue); - m_qtversionAspect->blockSignals(false); + // FIXME: That should use setValueSilently() + qtversion.blockSignals(true); + qtversion.setValue(oldValue); + qtversion.blockSignals(false); } }); } @@ -309,13 +306,14 @@ void QmlProjectRunConfiguration::createQtVersionAspect() bool QmlProjectRunConfiguration::isEnabled() const { - return m_qmlMainFileAspect->isQmlFilePresent() && !commandLine().executable().isEmpty() - && activeBuildSystem()->hasParsingData(); + return const_cast<QmlProjectRunConfiguration *>(this)->qmlMainFile.isQmlFilePresent() + && !commandLine().executable().isEmpty() + && activeBuildSystem()->hasParsingData(); } FilePath QmlProjectRunConfiguration::mainScript() const { - return m_qmlMainFileAspect->mainScript(); + return qmlMainFile.mainScript(); } // QmlProjectRunConfigurationFactory diff --git a/src/plugins/qnx/Qnx.json.in b/src/plugins/qnx/Qnx.json.in index 005f669c41c..0340d3b7dc1 100644 --- a/src/plugins/qnx/Qnx.json.in +++ b/src/plugins/qnx/Qnx.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Qnx\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"BlackBerry\", - \"Copyright\" : \"(C) 2017 BlackBerry, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Qnx", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "BlackBerry", + "Copyright" : "(C) 2017 BlackBerry, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Adds support for QNX to Qt Creator.\", - \"Url\" : \"http://www.blackberry.com\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Adds support for QNX to Qt Creator.", + "Url" : "http://www.blackberry.com", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/qnx/qnxdebugsupport.cpp b/src/plugins/qnx/qnxdebugsupport.cpp index 919e9ef1fcc..7d915bb70c8 100644 --- a/src/plugins/qnx/qnxdebugsupport.cpp +++ b/src/plugins/qnx/qnxdebugsupport.cpp @@ -10,7 +10,7 @@ #include <coreplugin/icore.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <debugger/debuggerruncontrol.h> #include <debugger/debuggertr.h> @@ -18,8 +18,8 @@ #include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/kit.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitchooser.h> -#include <projectexplorer/kitinformation.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/runconfigurationaspects.h> @@ -28,7 +28,7 @@ #include <qmldebug/qmldebugcommandlinearguments.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/fileutils.h> #include <utils/pathchooser.h> @@ -195,8 +195,8 @@ public: : DebuggerRunTool(runControl) { setId("QnxAttachDebugSupport"); - setUsePortsGatherer(isCppDebugging(), isQmlDebugging()); + setUseCtrlCStub(true); if (isCppDebugging()) { auto pdebugRunner = new PDebugRunner(runControl, portsGatherer()); @@ -230,7 +230,7 @@ void showAttachToProcessDialog() FilePath localExecutable = dlg.localExecutable(); if (localExecutable.isEmpty()) { if (auto aspect = runConfig->aspect<SymbolFileAspect>()) - localExecutable = aspect->filePath(); + localExecutable = aspect->expandedValue(); } auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE); @@ -239,7 +239,6 @@ void showAttachToProcessDialog() debugger->setStartMode(AttachToRemoteServer); debugger->setCloseMode(DetachAtClose); debugger->setSymbolFile(localExecutable); - debugger->setUseCtrlCStub(true); debugger->setAttachPid(pid); // setRunControlName(Tr::tr("Remote: \"%1\" - Process %2").arg(remoteChannel).arg(m_process.pid)); debugger->setRunControlName(Tr::tr("Remote QNX process %1").arg(pid)); diff --git a/src/plugins/qnx/qnxdevice.cpp b/src/plugins/qnx/qnxdevice.cpp index 306b87e861d..5083fbb782a 100644 --- a/src/plugins/qnx/qnxdevice.cpp +++ b/src/plugins/qnx/qnxdevice.cpp @@ -12,9 +12,9 @@ #include <projectexplorer/devicesupport/sshparameters.h> -#include <remotelinux/genericlinuxdeviceconfigurationwizardpages.h> -#include <remotelinux/remotelinuxsignaloperation.h> #include <remotelinux/linuxdevice.h> +#include <remotelinux/remotelinuxsignaloperation.h> +#include <remotelinux/sshdevicewizard.h> #include <utils/port.h> #include <utils/portlist.h> @@ -62,7 +62,7 @@ public: QnxDevice() { setDisplayType(Tr::tr("QNX")); - setDefaultDisplayName(Tr::tr("QNX Device")); + settings()->displayName.setDefaultValue(Tr::tr("QNX Device")); setOsType(OsTypeOtherUnix); setupId(IDevice::ManuallyAdded); setType(Constants::QNX_QNX_OS_TYPE); @@ -86,36 +86,6 @@ public: DeviceTester *createDeviceTester() const final { return new QnxDeviceTester; } }; -class QnxDeviceWizard : public Wizard -{ -public: - QnxDeviceWizard() : Wizard(Core::ICore::dialogParent()) - { - setWindowTitle(Tr::tr("New QNX Device Configuration Setup")); - - addPage(&m_setupPage); - addPage(&m_keyDeploymentPage); - addPage(&m_finalPage); - m_finalPage.setCommitPage(true); - - m_device.reset(new QnxDevice); - - m_setupPage.setDevice(m_device); - m_keyDeploymentPage.setDevice(m_device); - } - - IDevice::Ptr device() const { return m_device; } - -private: - GenericLinuxDeviceConfigurationWizardSetupPage m_setupPage; - GenericLinuxDeviceConfigurationWizardKeyDeploymentPage m_keyDeploymentPage; - GenericLinuxDeviceConfigurationWizardFinalPage m_finalPage; - - LinuxDevice::Ptr m_device; -}; - -// Factory - QnxDeviceFactory::QnxDeviceFactory() : IDeviceFactory(Constants::QNX_QNX_OS_TYPE) { setDisplayName(Tr::tr("QNX Device")); @@ -123,11 +93,12 @@ QnxDeviceFactory::QnxDeviceFactory() : IDeviceFactory(Constants::QNX_QNX_OS_TYPE ":/qnx/images/qnxdevice.png"); setQuickCreationAllowed(true); setConstructionFunction([] { return IDevice::Ptr(new QnxDevice); }); - setCreator([] { - QnxDeviceWizard wizard; + setCreator([]() -> IDevice::Ptr { + const IDevice::Ptr device = IDevice::Ptr(new QnxDevice); + SshDeviceWizard wizard(Tr::tr("New QNX Device Configuration Setup"), device); if (wizard.exec() != QDialog::Accepted) - return IDevice::Ptr(); - return wizard.device(); + return {}; + return device; }); } diff --git a/src/plugins/qnx/qnxplugin.cpp b/src/plugins/qnx/qnxplugin.cpp index 09fd528086c..bd2aa2d8b18 100644 --- a/src/plugins/qnx/qnxplugin.cpp +++ b/src/plugins/qnx/qnxplugin.cpp @@ -19,16 +19,16 @@ #include <extensionsystem/iplugin.h> +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/devicecheckbuildstep.h> #include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/deployconfiguration.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/environmentaspect.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/kitmanager.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/kitmanager.h> -#include <projectexplorer/environmentaspect.h> -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/project.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> diff --git a/src/plugins/qnx/qnxqtversion.cpp b/src/plugins/qnx/qnxqtversion.cpp index a13f96148b0..b5360c272df 100644 --- a/src/plugins/qnx/qnxqtversion.cpp +++ b/src/plugins/qnx/qnxqtversion.cpp @@ -98,7 +98,7 @@ FilePath QnxQtVersion::qnxTarget() const return FilePath::fromUserInput(item.value); } - return FilePath(); + return {}; } QString QnxQtVersion::cpuDir() const @@ -109,16 +109,16 @@ QString QnxQtVersion::cpuDir() const return QnxUtils::cpuDirFromAbi(abis.at(0)); } -QVariantMap QnxQtVersion::toMap() const +Store QnxQtVersion::toMap() const { - QVariantMap result = QtVersion::toMap(); + Store result = QtVersion::toMap(); result.insert(SDP_PATH_KEY, sdpPath().toSettings()); return result; } -void QnxQtVersion::fromMap(const QVariantMap &map, const Utils::FilePath &) +void QnxQtVersion::fromMap(const Store &map, const FilePath &, bool forceRefreshCache) { - QtVersion::fromMap(map); + QtVersion::fromMap(map, {}, forceRefreshCache); setSdpPath(FilePath::fromSettings(map.value(SDP_PATH_KEY))); } diff --git a/src/plugins/qnx/qnxqtversion.h b/src/plugins/qnx/qnxqtversion.h index bcea9b3be3e..7c3e81dac9f 100644 --- a/src/plugins/qnx/qnxqtversion.h +++ b/src/plugins/qnx/qnxqtversion.h @@ -27,8 +27,10 @@ public: QString cpuDir() const; - QVariantMap toMap() const override; - void fromMap(const QVariantMap &map, const Utils::FilePath &filePath) override; + Utils::Store toMap() const override; + void fromMap(const Utils::Store &map, + const Utils::FilePath &filePath, + bool forceRefreshCache) override; ProjectExplorer::Abis detectQtAbis() const override; diff --git a/src/plugins/qnx/qnxrunconfiguration.cpp b/src/plugins/qnx/qnxrunconfiguration.cpp index e80c4d2c2ae..26616e4e252 100644 --- a/src/plugins/qnx/qnxrunconfiguration.cpp +++ b/src/plugins/qnx/qnxrunconfiguration.cpp @@ -16,66 +16,72 @@ #include <qtsupport/qtoutputformatter.h> +#include <utils/processinterface.h> + using namespace ProjectExplorer; using namespace RemoteLinux; using namespace Utils; namespace Qnx::Internal { -class QnxRunConfiguration final : public ProjectExplorer::RunConfiguration +class QnxRunConfiguration final : public RunConfiguration { public: - QnxRunConfiguration(ProjectExplorer::Target *target, Utils::Id id); + QnxRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setLabelText(Tr::tr("Executable on device:")); + executable.setPlaceHolderText(Tr::tr("Remote path not set")); + executable.makeOverridable("RemoteLinux.RunConfig.AlternateRemoteExecutable", + "RemoteLinux.RunConfig.UseAlternateRemoteExecutable"); + executable.setHistoryCompleter("RemoteLinux.AlternateExecutable.History"); + + symbolFile.setLabelText(Tr::tr("Executable on host:")); + + environment.setDeviceSelector(target, EnvironmentAspect::RunDevice); + + arguments.setMacroExpander(macroExpander()); + + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + qtLibraries.setSettingsKey("Qt4ProjectManager.QnxRunConfiguration.QtLibPath"); + qtLibraries.setLabelText(Tr::tr("Path to Qt libraries on device")); + qtLibraries.setDisplayStyle(StringAspect::LineEditDisplay); + + setUpdater([this, target] { + const BuildTargetInfo bti = buildTargetInfo(); + const FilePath localExecutable = bti.targetFilePath; + const DeployableFile depFile = target->deploymentData() + .deployableForLocalFile(localExecutable); + executable.setExecutable(FilePath::fromString(depFile.remoteFilePath())); + symbolFile.setValue(localExecutable); + }); + + setRunnableModifier([this](ProcessRunData &r) { + QString libPath = qtLibraries(); + if (!libPath.isEmpty()) { + r.environment.appendOrSet("LD_LIBRARY_PATH", libPath + "/lib:$LD_LIBRARY_PATH"); + r.environment.appendOrSet("QML_IMPORT_PATH", libPath + "/imports:$QML_IMPORT_PATH"); + r.environment.appendOrSet("QML2_IMPORT_PATH", libPath + "/qml:$QML2_IMPORT_PATH"); + r.environment.appendOrSet("QT_PLUGIN_PATH", libPath + "/plugins:$QT_PLUGIN_PATH"); + r.environment.set("QT_QPA_FONTDIR", libPath + "/lib/fonts"); + } + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + } + + ExecutableAspect executable{this}; + SymbolFileAspect symbolFile{this}; + RemoteLinuxEnvironmentAspect environment{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + StringAspect qtLibraries{this}; }; -QnxRunConfiguration::QnxRunConfiguration(Target *target, Id id) - : RunConfiguration(target, id) -{ - auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setLabelText(Tr::tr("Executable on device:")); - exeAspect->setPlaceHolderText(Tr::tr("Remote path not set")); - exeAspect->makeOverridable("RemoteLinux.RunConfig.AlternateRemoteExecutable", - "RemoteLinux.RunConfig.UseAlternateRemoteExecutable"); - exeAspect->setHistoryCompleter("RemoteLinux.AlternateExecutable.History"); - - auto symbolsAspect = addAspect<SymbolFileAspect>(); - symbolsAspect->setLabelText(Tr::tr("Executable on host:")); - symbolsAspect->setDisplayStyle(SymbolFileAspect::LabelDisplay); - - auto envAspect = addAspect<RemoteLinuxEnvironmentAspect>(target); - - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - addAspect<TerminalAspect>(); - - auto libAspect = addAspect<StringAspect>(); - libAspect->setSettingsKey("Qt4ProjectManager.QnxRunConfiguration.QtLibPath"); - libAspect->setLabelText(Tr::tr("Path to Qt libraries on device")); - libAspect->setDisplayStyle(StringAspect::LineEditDisplay); - - setUpdater([this, target, exeAspect, symbolsAspect] { - const BuildTargetInfo bti = buildTargetInfo(); - const FilePath localExecutable = bti.targetFilePath; - const DeployableFile depFile = target->deploymentData() - .deployableForLocalFile(localExecutable); - exeAspect->setExecutable(FilePath::fromString(depFile.remoteFilePath())); - symbolsAspect->setFilePath(localExecutable); - }); - - setRunnableModifier([libAspect](Runnable &r) { - QString libPath = libAspect->value(); - if (!libPath.isEmpty()) { - r.environment.appendOrSet("LD_LIBRARY_PATH", libPath + "/lib:$LD_LIBRARY_PATH"); - r.environment.appendOrSet("QML_IMPORT_PATH", libPath + "/imports:$QML_IMPORT_PATH"); - r.environment.appendOrSet("QML2_IMPORT_PATH", libPath + "/qml:$QML2_IMPORT_PATH"); - r.environment.appendOrSet("QT_PLUGIN_PATH", libPath + "/plugins:$QT_PLUGIN_PATH"); - r.environment.set("QT_QPA_FONTDIR", libPath + "/lib/fonts"); - } - }); - - connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); -} - // QnxRunConfigurationFactory QnxRunConfigurationFactory::QnxRunConfigurationFactory() diff --git a/src/plugins/qnx/qnxsettingspage.cpp b/src/plugins/qnx/qnxsettingspage.cpp index 98a740947bb..37ca5aafc2b 100644 --- a/src/plugins/qnx/qnxsettingspage.cpp +++ b/src/plugins/qnx/qnxsettingspage.cpp @@ -13,7 +13,7 @@ #include <debugger/debuggeritem.h> #include <debugger/debuggeritemmanager.h> -#include <debugger/debuggerkitinformation.h> +#include <debugger/debuggerkitaspect.h> #include <projectexplorer/devicesupport/devicemanager.h> #include <projectexplorer/projectexplorerconstants.h> @@ -24,7 +24,7 @@ #include <qtsupport/baseqtversion.h> #include <qtsupport/qtversionmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qmakeprojectmanager/qmakeprojectmanagerconstants.h> @@ -50,18 +50,18 @@ using namespace Debugger; namespace Qnx::Internal { -const QLatin1String QNXEnvFileKey("EnvFile"); -const QLatin1String QNXVersionKey("QNXVersion"); +const char QNXEnvFileKey[] = "EnvFile"; +const char QNXVersionKey[] = "QNXVersion"; // For backward compatibility -const QLatin1String SdpEnvFileKey("NDKEnvFile"); +const char SdpEnvFileKey[] = "NDKEnvFile"; -const QLatin1String QNXConfiguration("QNX_CONFIGURATION"); -const QLatin1String QNXTarget("QNX_TARGET"); -const QLatin1String QNXHost("QNX_HOST"); +const char QNXConfiguration[] = "QNX_CONFIGURATION"; +const char QNXTarget[] = "QNX_TARGET"; +const char QNXHost[] = "QNX_HOST"; -const QLatin1String QNXConfigDataKey("QNXConfiguration."); -const QLatin1String QNXConfigCountKey("QNXConfiguration.Count"); -const QLatin1String QNXConfigsFileVersionKey("Version"); +const char QNXConfigDataKey[] = "QNXConfiguration."; +const char QNXConfigCountKey[] = "QNXConfiguration.Count"; +const char QNXConfigsFileVersionKey[] = "Version"; static FilePath qnxConfigSettingsFileName() { @@ -74,7 +74,7 @@ public: QnxConfiguration() = default; explicit QnxConfiguration(const FilePath &envFile) { m_envFile = envFile; } - void fromMap(const QVariantMap &data) + void fromMap(const Store &data) { QString envFilePath = data.value(QNXEnvFileKey).toString(); if (envFilePath.isEmpty()) @@ -84,11 +84,11 @@ public: m_envFile = FilePath::fromString(envFilePath); } - QVariantMap toMap() const + Store toMap() const { - QVariantMap data; - data.insert(QLatin1String(QNXEnvFileKey), m_envFile.toString()); - data.insert(QLatin1String(QNXVersionKey), m_version.toString()); + Store data; + data.insert(QNXEnvFileKey, m_envFile.toString()); + data.insert(QNXVersionKey, m_version.toString()); return data; } @@ -238,8 +238,8 @@ Toolchains QnxConfiguration::createToolChains(const QnxTarget &target) toolChain->setDisplayName(Tr::tr("QCC for %1 (%2)") .arg(m_configName) .arg(target.shortDescription())); - toolChain->setSdpPath(m_envFile.parentDir()); - toolChain->setCpuDir(target.cpuDir()); + toolChain->sdpPath.setValue(m_envFile.parentDir()); + toolChain->cpuDir.setValue(target.cpuDir()); toolChain->resetToolChain(m_qccCompiler); ToolChainManager::registerToolChain(toolChain); @@ -434,19 +434,19 @@ public: void saveConfigs() { - QVariantMap data; - data.insert(QLatin1String(QNXConfigsFileVersionKey), 1); + Store data; + data.insert(QNXConfigsFileVersionKey, 1); int count = 0; for (const QnxConfiguration &config : std::as_const(m_configurations)) { - QVariantMap tmp = config.toMap(); + Store tmp = config.toMap(); if (tmp.isEmpty()) continue; - data.insert(QNXConfigDataKey + QString::number(count), tmp); + data.insert(numberedKey(QNXConfigDataKey, count), variantFromStore(tmp)); ++count; } - data.insert(QLatin1String(QNXConfigCountKey), count); + data.insert(QNXConfigCountKey, count); m_writer.save(data, Core::ICore::dialogParent()); } @@ -456,15 +456,15 @@ public: if (!reader.load(qnxConfigSettingsFileName())) return; - QVariantMap data = reader.restoreValues(); + Store data = reader.restoreValues(); int count = data.value(QNXConfigCountKey, 0).toInt(); for (int i = 0; i < count; ++i) { - const QString key = QNXConfigDataKey + QString::number(i); + const Key key = numberedKey(QNXConfigDataKey, i); if (!data.contains(key)) continue; QnxConfiguration config; - config.fromMap(data.value(key).toMap()); + config.fromMap(storeFromVariant(data.value(key))); m_configurations[config.m_envFile] = config; } } diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index 8176b3d17c9..2e66fecacea 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -10,6 +10,8 @@ #include <projectexplorer/abiwidget.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/toolchainconfigwidget.h> + #include <utils/algorithm.h> #include <utils/pathchooser.h> @@ -20,9 +22,6 @@ using namespace Utils; namespace Qnx::Internal { -const char CompilerSdpPath[] = "Qnx.QnxToolChain.NDKPath"; -const char CpuDirKey[] = "Qnx.QnxToolChain.CpuDir"; - // QnxToolChainConfigWidget class QnxToolChainConfigWidget : public ToolChainConfigWidget @@ -38,10 +37,9 @@ private: void handleSdpPathChange(); - Utils::PathChooser *m_compilerCommand; - Utils::PathChooser *m_sdpPath; + PathChooser *m_compilerCommand; + PathChooser *m_sdpPath; ProjectExplorer::AbiWidget *m_abiWidget; - }; static Abis detectTargetAbis(const FilePath &sdpPath) @@ -104,6 +102,18 @@ QnxToolChain::QnxToolChain() { setOptionsReinterpreter(&reinterpretOptions); setTypeDisplayName(Tr::tr("QCC")); + + sdpPath.setSettingsKey("Qnx.QnxToolChain.NDKPath"); + connect(&sdpPath, &BaseAspect::changed, this, &QnxToolChain::toolChainUpdated); + + cpuDir.setSettingsKey("Qnx.QnxToolChain.CpuDir"); + connect(&cpuDir, &BaseAspect::changed, this, &QnxToolChain::toolChainUpdated); + + connect(this, &AspectContainer::fromMapFinished, this, [this] { + // Make the ABIs QNX specific (if they aren't already). + setSupportedAbis(QnxUtils::convertAbis(supportedAbis())); + setTargetAbi(QnxUtils::convertAbi(targetAbi())); + }); } std::unique_ptr<ToolChainConfigWidget> QnxToolChain::createConfigurationWidget() @@ -116,7 +126,7 @@ void QnxToolChain::addToEnvironment(Environment &env) const if (env.expandedValueForKey("QNX_HOST").isEmpty() || env.expandedValueForKey("QNX_TARGET").isEmpty() || env.expandedValueForKey("QNX_CONFIGURATION_EXCLUSIVE").isEmpty()) - setQnxEnvironment(env, QnxUtils::qnxEnvironment(m_sdpPath)); + setQnxEnvironment(env, QnxUtils::qnxEnvironment(sdpPath())); GccToolChain::addToEnvironment(env); } @@ -131,55 +141,6 @@ QStringList QnxToolChain::suggestedMkspecList() const }; } -QVariantMap QnxToolChain::toMap() const -{ - QVariantMap data = GccToolChain::toMap(); - data.insert(QLatin1String(CompilerSdpPath), m_sdpPath.toSettings()); - data.insert(QLatin1String(CpuDirKey), m_cpuDir); - return data; -} - -bool QnxToolChain::fromMap(const QVariantMap &data) -{ - if (!GccToolChain::fromMap(data)) - return false; - - m_sdpPath = FilePath::fromSettings(data.value(CompilerSdpPath)); - m_cpuDir = data.value(QLatin1String(CpuDirKey)).toString(); - - // Make the ABIs QNX specific (if they aren't already). - setSupportedAbis(QnxUtils::convertAbis(supportedAbis())); - setTargetAbi(QnxUtils::convertAbi(targetAbi())); - - return true; -} - -FilePath QnxToolChain::sdpPath() const -{ - return m_sdpPath; -} - -void QnxToolChain::setSdpPath(const FilePath &sdpPath) -{ - if (m_sdpPath == sdpPath) - return; - m_sdpPath = sdpPath; - toolChainUpdated(); -} - -QString QnxToolChain::cpuDir() const -{ - return m_cpuDir; -} - -void QnxToolChain::setCpuDir(const QString &cpuDir) -{ - if (m_cpuDir == cpuDir) - return; - m_cpuDir = cpuDir; - toolChainUpdated(); -} - GccToolChain::DetectedAbisResult QnxToolChain::detectSupportedAbis() const { // "unknown-qnx-gnu"is needed to get the "--target=xxx" parameter sent code model, @@ -188,7 +149,7 @@ GccToolChain::DetectedAbisResult QnxToolChain::detectSupportedAbis() const // // Without it on Windows Clang defaults to a MSVC mode, which breaks with // the QNX code, which is mostly GNU based. - return GccToolChain::DetectedAbisResult{detectTargetAbis(m_sdpPath), "unknown-qnx-gnu"}; + return GccToolChain::DetectedAbisResult{detectTargetAbis(sdpPath()), "unknown-qnx-gnu"}; } bool QnxToolChain::operator ==(const ToolChain &other) const @@ -198,7 +159,7 @@ bool QnxToolChain::operator ==(const ToolChain &other) const auto qnxTc = static_cast<const QnxToolChain *>(&other); - return m_sdpPath == qnxTc->m_sdpPath && m_cpuDir == qnxTc->m_cpuDir; + return sdpPath() == qnxTc->sdpPath() && cpuDir() == qnxTc->cpuDir(); } // -------------------------------------------------------------------------- @@ -236,12 +197,12 @@ QnxToolChainConfigWidget::QnxToolChainConfigWidget(QnxToolChain *tc) , m_abiWidget(new AbiWidget) { m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter(QLatin1String("Qnx.ToolChain.History")); + m_compilerCommand->setHistoryCompleter("Qnx.ToolChain.History"); m_compilerCommand->setFilePath(tc->compilerCommand()); m_compilerCommand->setEnabled(!tc->isAutoDetected()); m_sdpPath->setExpectedKind(PathChooser::ExistingDirectory); - m_sdpPath->setHistoryCompleter(QLatin1String("Qnx.Sdp.History")); + m_sdpPath->setHistoryCompleter("Qnx.Sdp.History"); m_sdpPath->setFilePath(tc->sdpPath()); m_sdpPath->setEnabled(!tc->isAutoDetected()); @@ -269,7 +230,7 @@ void QnxToolChainConfigWidget::applyImpl() Q_ASSERT(tc); QString displayName = tc->displayName(); tc->setDisplayName(displayName); // reset display name - tc->setSdpPath(m_sdpPath->filePath()); + tc->sdpPath.setValue(m_sdpPath->filePath()); tc->setTargetAbi(m_abiWidget->currentAbi()); tc->resetToolChain(m_compilerCommand->filePath()); } diff --git a/src/plugins/qnx/qnxtoolchain.h b/src/plugins/qnx/qnxtoolchain.h index e2edb76f8ee..7cbdc8f4983 100644 --- a/src/plugins/qnx/qnxtoolchain.h +++ b/src/plugins/qnx/qnxtoolchain.h @@ -4,10 +4,6 @@ #pragma once #include <projectexplorer/gcctoolchain.h> -#include <projectexplorer/toolchainconfigwidget.h> - -namespace ProjectExplorer { class AbiWidget; } -namespace Utils { class PathChooser; } namespace Qnx::Internal { @@ -21,22 +17,13 @@ public: void addToEnvironment(Utils::Environment &env) const override; QStringList suggestedMkspecList() const override; - QVariantMap toMap() const override; - bool fromMap(const QVariantMap &data) override; - - Utils::FilePath sdpPath() const; - void setSdpPath(const Utils::FilePath &sdpPath); - QString cpuDir() const; - void setCpuDir(const QString &cpuDir); + Utils::FilePathAspect sdpPath{this}; + Utils::StringAspect cpuDir{this}; bool operator ==(const ToolChain &) const override; protected: DetectedAbisResult detectSupportedAbis() const override; - -private: - Utils::FilePath m_sdpPath; - QString m_cpuDir; }; // -------------------------------------------------------------------------- diff --git a/src/plugins/qtsupport/CMakeLists.txt b/src/plugins/qtsupport/CMakeLists.txt index 5d69a7fc38a..9cfbe0a6c0b 100644 --- a/src/plugins/qtsupport/CMakeLists.txt +++ b/src/plugins/qtsupport/CMakeLists.txt @@ -6,7 +6,6 @@ add_qtc_plugin(QtSupport baseqtversion.cpp baseqtversion.h codegenerator.cpp codegenerator.h codegensettings.cpp codegensettings.h - codegensettingspage.cpp codegensettingspage.h exampleslistmodel.cpp exampleslistmodel.h examplesparser.cpp examplesparser.h @@ -17,7 +16,7 @@ add_qtc_plugin(QtSupport qtbuildaspects.cpp qtbuildaspects.h qtconfigwidget.cpp qtconfigwidget.h qtcppkitinfo.cpp qtcppkitinfo.h - qtkitinformation.cpp qtkitinformation.h + qtkitaspect.cpp qtkitaspect.h qtoptionspage.cpp qtoptionspage.h qtoutputformatter.cpp qtoutputformatter.h qtparser.cpp qtparser.h diff --git a/src/plugins/qtsupport/QtSupport.json.in b/src/plugins/qtsupport/QtSupport.json.in index fcfa515688d..0a39a762ce1 100644 --- a/src/plugins/qtsupport/QtSupport.json.in +++ b/src/plugins/qtsupport/QtSupport.json.in @@ -1,34 +1,34 @@ { - \"Name\" : \"QtSupport\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "QtSupport", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Build Systems\", - \"Description\" : \"Provides support code for build systems.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Build Systems", + "Description" : "Provides support code for build systems.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/x-linguist-translation\'>\", - \" <comment>Linguist compiled translations</comment>\", - \" <glob pattern=\'*.qm\'/>\", - \" </mime-type>\", - \" <mime-type type=\'application/scxml+xml\'>\", - \" <comment>SCXML State Chart</comment>\", - \" <sub-class-of type=\'application/xml\'/>\", - \" <glob pattern=\'*.scxml\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/x-linguist-translation'>", + " <comment>Linguist compiled translations</comment>", + " <glob pattern='*.qm'/>", + " </mime-type>", + " <mime-type type='application/scxml+xml'>", + " <comment>SCXML State Chart</comment>", + " <sub-class-of type='application/xml'/>", + " <glob pattern='*.scxml'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index a5d42a6394d..5e5d29c30d9 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -5,7 +5,7 @@ #include "profilereader.h" #include "qtconfigwidget.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include "qtsupportconstants.h" #include "qtsupporttr.h" #include "qtversionfactory.h" @@ -33,6 +33,7 @@ #include <utils/fileinprojectfinder.h> #include <utils/hostosinfo.h> #include <utils/macroexpander.h> +#include <utils/persistentcachestore.h> #include <utils/process.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> @@ -76,11 +77,15 @@ const char MKSPEC_VALUE_NAMESPACE[] = "QT_NAMESPACE"; class QtVersionData { public: + // Update version if you add data members! + static const int version = 2; + bool installed = true; bool hasExamples = false; bool hasDemos = false; bool hasDocumentation = false; - bool hasQtAbis = false; + + std::optional<Abis> qtAbis; DisplayName unexpandedDisplayName; QString qtVersionString; @@ -110,7 +115,102 @@ public: Utils::FilePath hostDataPath; Utils::FilePath hostPrefixPath; - Abis qtAbis; + QHash<ProKey, ProString> versionInfo; + bool versionInfoUpToDate = false; + + static QHash<ProKey, ProString> fromStore(const Store &map) + { + QHash<ProKey, ProString> result; + for (auto it = map.constBegin(); it != map.constEnd(); ++it) + result.insert(ProKey(it.key().toByteArray()), ProString(it.value().toString())); + return result; + } + + static Store toStore(const QHash<ProKey, ProString> &map) + { + Store result; + for (auto it = map.constBegin(); it != map.constEnd(); ++it) + result.insert(it.key().toString().toQString().toUtf8(), it.value().toQString()); + return result; + } + + Store toMap() + { + Store result; + result.insert("CacheDataVersion", version); + result.insert("Installed", installed); + result.insert("HasExamples", hasExamples); + result.insert("HasDemos", hasDemos); + result.insert("HasDocumentation", hasDocumentation); + result.insert("VersionInfoUpToDate", versionInfoUpToDate); + + unexpandedDisplayName.toMap(result, "UnexpandedDisplayName"); + + result.insert("QtVersionString", qtVersionString); + result.insert("SourcePath", sourcePath.toSettings()); + result.insert("QtSources", qtSources.toSettings()); + result.insert("Prefix", prefix.toSettings()); + result.insert("BinPath", binPath.toSettings()); + result.insert("LibExecPath", libExecPath.toSettings()); + result.insert("ConfigurationPath", configurationPath.toSettings()); + result.insert("DataPath", dataPath.toSettings()); + result.insert("DemosPath", demosPath.toSettings()); + result.insert("DocsPath", docsPath.toSettings()); + result.insert("ExamplesPath", examplesPath.toSettings()); + result.insert("HeaderPath", headerPath.toSettings()); + result.insert("ImportsPath", importsPath.toSettings()); + result.insert("LibraryPath", libraryPath.toSettings()); + result.insert("PluginPath", pluginPath.toSettings()); + result.insert("QmlPath", qmlPath.toSettings()); + result.insert("TranslationsPath", translationsPath.toSettings()); + result.insert("HostBinPath", hostBinPath.toSettings()); + result.insert("HostLibexecPath", hostLibexecPath.toSettings()); + result.insert("HostDataPath", hostDataPath.toSettings()); + result.insert("HostPrefixPath", hostPrefixPath.toSettings()); + if (qtAbis) + result.insert("QtAbis", Utils::transform(*qtAbis, &Abi::toString)); + result.insert("VersionInfo", QVariant::fromValue(toStore(versionInfo))); + + return result; + } + + void fromMap(Store map) + { + if (map.value("CacheDataVersion").toInt() < version) + return; + + installed = map.value("Installed").toBool(); + hasExamples = map.value("HasExamples").toBool(); + hasDemos = map.value("HasDemos").toBool(); + hasDocumentation = map.value("HasDocumentation").toBool(); + versionInfoUpToDate = map.value("VersionInfoUpToDate", false).toBool(); + unexpandedDisplayName.fromMap(map, "UnexpandedDisplayName"); + qtVersionString = map.value("QtVersionString").toString(); + sourcePath = FilePath::fromSettings(map.value("SourcePath")); + qtSources = FilePath::fromSettings(map.value("QtSources")); + prefix = FilePath::fromSettings(map.value("Prefix")); + binPath = FilePath::fromSettings(map.value("BinPath")); + libExecPath = FilePath::fromSettings(map.value("LibExecPath")); + configurationPath = FilePath::fromSettings(map.value("ConfigurationPath")); + dataPath = FilePath::fromSettings(map.value("DataPath")); + demosPath = FilePath::fromSettings(map.value("DemosPath")); + docsPath = FilePath::fromSettings(map.value("DocsPath")); + examplesPath = FilePath::fromSettings(map.value("ExamplesPath")); + headerPath = FilePath::fromSettings(map.value("HeaderPath")); + importsPath = FilePath::fromSettings(map.value("ImportsPath")); + libraryPath = FilePath::fromSettings(map.value("LibraryPath")); + pluginPath = FilePath::fromSettings(map.value("PluginPath")); + qmlPath = FilePath::fromSettings(map.value("QmlPath")); + translationsPath = FilePath::fromSettings(map.value("TranslationsPath")); + hostBinPath = FilePath::fromSettings(map.value("HostBinPath")); + hostLibexecPath = FilePath::fromSettings(map.value("HostLibexecPath")); + hostDataPath = FilePath::fromSettings(map.value("HostDataPath")); + hostPrefixPath = FilePath::fromSettings(map.value("HostPrefixPath")); + auto it = map.find("QtAbis"); + if (it != map.end()) + qtAbis = Utils::transform(it.value().toStringList(), &Abi::fromString); + versionInfo = fromStore(map.value("VersionInfo").value<Store>()); + } }; // -------------------------------------------------------------------- @@ -211,7 +311,6 @@ public: bool m_defaultConfigIsDebug = true; bool m_defaultConfigIsDebugAndRelease = true; bool m_frameworkBuild = false; - bool m_versionInfoUpToDate = false; bool m_qmakeIsExecutable = true; QString m_detectionSource; @@ -222,8 +321,6 @@ public: QHash<QString, QString> m_mkspecValues; - QHash<ProKey, ProString> m_versionInfo; - FilePath m_qmakeCommand; FilePath m_rccPath; @@ -585,8 +682,8 @@ FilePath QtVersion::mkspecsPath() const { const FilePath result = hostDataPath(); if (result.isEmpty()) - return FilePath::fromUserInput(QtVersionPrivate::qmakeProperty( - d->m_versionInfo, "QMAKE_MKSPECS")); + return FilePath::fromUserInput( + QtVersionPrivate::qmakeProperty(d->m_data.versionInfo, "QMAKE_MKSPECS")); return result.pathAppended("mkspecs"); } @@ -640,7 +737,7 @@ bool QtVersion::hasReleaseBuild() const return !d->m_defaultConfigIsDebug || d->m_defaultConfigIsDebugAndRelease; } -void QtVersion::fromMap(const QVariantMap &map, const FilePath &filePath) +void QtVersion::fromMap(const Store &map, const FilePath &filePath, bool forceRefreshCache) { d->m_id = map.value(Constants::QTVERSIONID).toInt(); if (d->m_id == -1) // this happens on adding from installer, see updateFromInstaller => get a new unique id @@ -667,14 +764,24 @@ void QtVersion::fromMap(const QVariantMap &map, const FilePath &filePath) } d->m_qmakeCommand = filePath.resolvePath(d->m_qmakeCommand); - d->m_data.qtSources = FilePath::fromSettings(map.value(QTVERSIONSOURCEPATH)); + const expected_str<Utils::Store> persistentStore = PersistentCacheStore::byKey( + Key("QtVersionData" + d->m_qmakeCommand.toString().toUtf8())); - // Handle ABIs provided by the SDKTool: - // Note: Creator does not write these settings itself, so it has to come from the SDKTool! - d->m_data.qtAbis = Utils::transform<Abis>(map.value(QTVERSION_ABIS).toStringList(), - &Abi::fromString); - d->m_data.qtAbis = Utils::filtered(d->m_data.qtAbis, &Abi::isValid); - d->m_data.hasQtAbis = !d->m_data.qtAbis.isEmpty(); + if (persistentStore && !forceRefreshCache) + d->m_data.fromMap(*persistentStore); + else + d->m_data.qtSources = FilePath::fromSettings(map.value(QTVERSIONSOURCEPATH)); + + Store::const_iterator itQtAbis = map.find(QTVERSION_ABIS); + if (itQtAbis != map.end()) { + // Only the SDK Tool writes abis to the settings. If we find abis in the settings, we want + // to make sure to use them as our automatic detection is not perfect. + const QStringList abiList = itQtAbis.value().toStringList(); + if (!abiList.isEmpty()) { + const Abis abis = Utils::transform<Abis>(abiList, &Abi::fromString); + d->m_data.qtAbis = Utils::filtered(abis, &Abi::isValid); + } + } updateDefaultDisplayName(); @@ -682,9 +789,9 @@ void QtVersion::fromMap(const QVariantMap &map, const FilePath &filePath) d->m_qmlRuntimePath.clear(); } -QVariantMap QtVersion::toMap() const +Store QtVersion::toMap() const { - QVariantMap result; + Store result; result.insert(Constants::QTVERSIONID, uniqueId()); d->m_data.unexpandedDisplayName.toMap(result, Constants::QTVERSIONNAME); @@ -694,6 +801,12 @@ QVariantMap QtVersion::toMap() const result.insert(QTVERSION_OVERRIDE_FEATURES, Utils::Id::toStringList(d->m_overrideFeatures)); result.insert(QTVERSIONQMAKEPATH, qmakeFilePath().toSettings()); + + if (d->m_data.versionInfoUpToDate) { + PersistentCacheStore::write(Key("QtVersionData" + d->m_qmakeCommand.toString().toUtf8()), + d->m_data.toMap()); + } + return result; } @@ -738,8 +851,8 @@ QStringList QtVersion::warningReason() const QStringList ret; if (qtAbis().isEmpty()) ret << Tr::tr("ABI detection failed: Make sure to use a matching compiler when building."); - if (d->m_versionInfo.value(ProKey("QT_INSTALL_PREFIX/get")) - != d->m_versionInfo.value(ProKey("QT_INSTALL_PREFIX"))) { + if (d->m_data.versionInfo.value(ProKey("QT_INSTALL_PREFIX/get")) + != d->m_data.versionInfo.value(ProKey("QT_INSTALL_PREFIX"))) { ret << Tr::tr("Non-installed -prefix build - for internal development only."); } return ret; @@ -750,13 +863,22 @@ FilePath QtVersion::qmakeFilePath() const return d->m_qmakeCommand; } +bool QtVersion::hasQtAbisSet() const +{ + return d->m_data.qtAbis.has_value(); +} + Abis QtVersion::qtAbis() const { - if (!d->m_data.hasQtAbis) { + if (!d->m_data.qtAbis) d->m_data.qtAbis = detectQtAbis(); - d->m_data.hasQtAbis = true; - } - return d->m_data.qtAbis; + + return *d->m_data.qtAbis; +} + +void QtVersion::setQtAbis(const Abis &abis) +{ + d->m_data.qtAbis = abis; } Abis QtVersion::detectQtAbis() const @@ -919,7 +1041,7 @@ FilePath QtVersion::sourcePath() const { if (d->m_data.sourcePath.isEmpty()) { d->updateVersionInfo(); - d->m_data.sourcePath = QtVersionPrivate::sourcePath(d->m_versionInfo); + d->m_data.sourcePath = QtVersionPrivate::sourcePath(d->m_data.versionInfo); } return d->m_data.sourcePath; } @@ -1232,19 +1354,19 @@ QVersionNumber QtVersion::qtVersion() const void QtVersionPrivate::updateVersionInfo() { - if (m_versionInfoUpToDate || !m_qmakeIsExecutable || m_isUpdating) + if (m_data.versionInfoUpToDate || !m_qmakeIsExecutable || m_isUpdating) return; m_isUpdating = true; // extract data from qmake executable - m_versionInfo.clear(); + m_data.versionInfo.clear(); m_data.installed = true; m_data.hasExamples = false; m_data.hasDocumentation = false; QString error; - if (!queryQMakeVariables(m_qmakeCommand, q->qmakeRunEnvironment(), &m_versionInfo, &error)) { + if (!queryQMakeVariables(m_qmakeCommand, q->qmakeRunEnvironment(), &m_data.versionInfo, &error)) { m_qmakeIsExecutable = false; qWarning("Cannot update Qt version information from %s: %s.", qPrintable(m_qmakeCommand.displayName()), qPrintable(error)); @@ -1297,13 +1419,16 @@ void QtVersionPrivate::updateVersionInfo() m_data.qtVersionString = qmakeProperty("QT_VERSION"); m_isUpdating = false; - m_versionInfoUpToDate = true; + m_data.versionInfoUpToDate = true; + + PersistentCacheStore::write(Key("QtVersionData" + m_qmakeCommand.toString().toUtf8()), + m_data.toMap()); } QHash<ProKey,ProString> QtVersionPrivate::versionInfo() { updateVersionInfo(); - return m_versionInfo; + return m_data.versionInfo; } QString QtVersionPrivate::qmakeProperty(const QHash<ProKey, ProString> &versionInfo, @@ -1765,7 +1890,7 @@ QString QtVersionPrivate::qmakeProperty(const QByteArray &name, QtVersionPrivate::PropertyVariant variant) { updateVersionInfo(); - return qmakeProperty(m_versionInfo, name, variant); + return qmakeProperty(m_data.versionInfo, name, variant); } FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash<ProKey, ProString> &versionInfo, @@ -1773,7 +1898,7 @@ FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash<ProKey, Pr { QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc); if (dataDir.isEmpty()) - return FilePath(); + return {}; return qmakeCommand.withNewPath(dataDir + "/mkspecs").cleanPath(); } @@ -1782,7 +1907,7 @@ FilePath QtVersionPrivate::mkspecFromVersionInfo(const QHash<ProKey, ProString> { FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo, qmakeCommand); if (baseMkspecDir.isEmpty()) - return FilePath(); + return {}; bool qt5 = false; QString theSpec = qmakeProperty(versionInfo, "QMAKE_XSPEC"); @@ -2116,7 +2241,7 @@ static QByteArray scanQtBinaryForBuildString(const FilePath &library) static QStringList extractFieldsFromBuildString(const QByteArray &buildString) { if (buildString.isEmpty() || buildString.size() > 4096) - return QStringList(); + return {}; const QRegularExpression buildStringMatcher("^Qt " "([\\d\\.a-zA-Z]*) " // Qt version @@ -2132,7 +2257,7 @@ static QStringList extractFieldsFromBuildString(const QByteArray &buildString) const QRegularExpressionMatch match = buildStringMatcher.match(QString::fromUtf8(buildString)); if (!match.hasMatch()) - return QStringList(); + return {}; QStringList result; result.append(match.captured(1)); // qtVersion @@ -2143,7 +2268,7 @@ static QStringList extractFieldsFromBuildString(const QByteArray &buildString) result.append(abiInfo.takeFirst()); // cpu const QString endian = abiInfo.takeFirst(); - QTC_ASSERT(endian.endsWith("_endian"), return QStringList()); + QTC_ASSERT(endian.endsWith("_endian"), return {}); result.append(endian.left(endian.size() - 7)); // without the "_endian" result.append(abiInfo.takeFirst()); // pointer @@ -2243,7 +2368,6 @@ Abis QtVersion::qtAbisFromLibrary(const FilePaths &coreLibraries) void QtVersion::resetCache() const { - d->m_data.hasQtAbis = false; d->m_mkspecReadUpToDate = false; } @@ -2323,7 +2447,7 @@ bool QtVersionFactory::canRestore(const QString &type) return type == m_supportedType; } -QtVersion *QtVersionFactory::restore(const QString &type, const QVariantMap &data, const FilePath &filePath) +QtVersion *QtVersionFactory::restore(const QString &type, const Store &data, const FilePath &filePath) { QTC_ASSERT(canRestore(type), return nullptr); QTC_ASSERT(m_creator, return nullptr); @@ -2340,13 +2464,21 @@ QtVersion *QtVersionFactory::create() const return version; } -QtVersion *QtVersion::clone() const +QtVersion *QtVersion::clone(bool forceRefreshCache) const { for (QtVersionFactory *factory : std::as_const(g_qtVersionFactories)) { if (factory->m_supportedType == d->m_type) { QtVersion *version = factory->create(); QTC_ASSERT(version, return nullptr); - version->fromMap(toMap()); + version->fromMap(toMap(), {}, forceRefreshCache); + + // Qt Abis are either provided by SDK Tool, or detected from the binaries. + // The auto detection is not perfect, and we always want to use the data provided by + // SDK Tool if available. Since the Abis are not contained in toMap() as we + // don't let the user change them, and we probably forced the cache to refresh, we have + // to re-set them here. + if (hasQtAbisSet()) + version->setQtAbis(qtAbis()); return version; } } diff --git a/src/plugins/qtsupport/baseqtversion.h b/src/plugins/qtsupport/baseqtversion.h index b590d15c28a..796d76aa3e9 100644 --- a/src/plugins/qtsupport/baseqtversion.h +++ b/src/plugins/qtsupport/baseqtversion.h @@ -5,15 +5,15 @@ #include "qtsupport_global.h" -#include <utils/fileutils.h> +#include <utils/filepath.h> #include <utils/macroexpander.h> +#include <utils/store.h> #include <projectexplorer/abi.h> #include <projectexplorer/task.h> #include <QSet> #include <QStringList> -#include <QVariantMap> #include <QVersionNumber> QT_BEGIN_NAMESPACE @@ -49,7 +49,9 @@ public: virtual ~QtVersion(); - virtual void fromMap(const QVariantMap &map, const Utils::FilePath &filePath = {}); + virtual void fromMap(const Utils::Store &map, + const Utils::FilePath &filePath = {}, + bool forceRefreshCache = false); virtual bool equals(QtVersion *other); bool isAutodetected() const; @@ -64,7 +66,7 @@ public: QString type() const; - virtual QVariantMap toMap() const; + virtual Utils::Store toMap() const; virtual bool isValid() const; static Predicate isValidPredicate(const Predicate &predicate = {}); virtual QString invalidReason() const; @@ -73,7 +75,9 @@ public: virtual QString description() const = 0; virtual QString toHtml(bool verbose) const; + bool hasQtAbisSet() const; ProjectExplorer::Abis qtAbis() const; + void setQtAbis(const ProjectExplorer::Abis &abis); bool hasAbi(ProjectExplorer::Abi::OS, ProjectExplorer::Abi::OSFlavor flavor = ProjectExplorer::Abi::UnknownFlavor) const; void applyProperties(QMakeGlobals *qmakeGlobals) const; @@ -221,7 +225,7 @@ private: friend class Internal::QtOptionsPageWidget; void setId(int id); - QtVersion *clone() const; + QtVersion *clone(bool forceRefreshCache = false) const; Internal::QtVersionPrivate *d = nullptr; }; diff --git a/src/plugins/qtsupport/codegenerator.cpp b/src/plugins/qtsupport/codegenerator.cpp index eac195865c2..2188e59012a 100644 --- a/src/plugins/qtsupport/codegenerator.cpp +++ b/src/plugins/qtsupport/codegenerator.cpp @@ -11,7 +11,6 @@ #include <utils/qtcassert.h> #include <QDomDocument> -#include <QSettings> #include <QTextStream> #include <QXmlStreamReader> @@ -166,34 +165,28 @@ QString CodeGenerator::uiClassName(const QString &uiXml) QString CodeGenerator::qtIncludes(const QStringList &qt4, const QStringList &qt5) { - CodeGenSettings settings; - settings.fromSettings(Core::ICore::settings()); - QString result; QTextStream str(&result); - Utils::writeQtIncludeSection(qt4, qt5, settings.addQtVersionCheck, settings.includeQtModule, str); + Utils::writeQtIncludeSection(qt4, qt5, + codeGenSettings().addQtVersionCheck(), + codeGenSettings().includeQtModule(), + str); return result; } bool CodeGenerator::uiAsPointer() { - CodeGenSettings settings; - settings.fromSettings(Core::ICore::settings()); - return settings.embedding == CodeGenSettings::PointerAggregatedUiClass; + return codeGenSettings().embedding() == CodeGenSettings::PointerAggregatedUiClass; } bool CodeGenerator::uiAsMember() { - CodeGenSettings settings; - settings.fromSettings(Core::ICore::settings()); - return settings.embedding == CodeGenSettings::AggregatedUiClass; + return codeGenSettings().embedding() == CodeGenSettings::AggregatedUiClass; } bool CodeGenerator::uiAsInheritance() { - CodeGenSettings settings; - settings.fromSettings(Core::ICore::settings()); - return settings.embedding == CodeGenSettings::InheritedUiClass; + return codeGenSettings().embedding() == CodeGenSettings::InheritedUiClass; } } // namespace QtSupport diff --git a/src/plugins/qtsupport/codegensettings.cpp b/src/plugins/qtsupport/codegensettings.cpp index cbf12e31e07..007467e619d 100644 --- a/src/plugins/qtsupport/codegensettings.cpp +++ b/src/plugins/qtsupport/codegensettings.cpp @@ -3,73 +3,85 @@ #include "codegensettings.h" -#include <coreplugin/icore.h> -#include <utils/qtcsettings.h> +#include "qtsupportconstants.h" +#include "qtsupporttr.h" -#include <QSettings> +#include <coreplugin/dialogs/ioptionspage.h> + +#include <cppeditor/cppeditorconstants.h> +#include <cppeditor/cppeditortr.h> + +#include <utils/layoutbuilder.h> using namespace Utils; namespace QtSupport { -const char CODE_GEN_GROUP[] = "FormClassWizardPage"; -const char TRANSLATION_KEY[] = "RetranslationSupport"; -const char EMBEDDING_KEY[] = "Embedding"; -const char INCLUDE_QT_MODULE_KEY[] = "IncludeQtModule"; -const char ADD_QT_VERSION_CHECK_KEY[] = "AddQtVersionCheck"; - -const bool retranslationSupportDefault = false; -const CodeGenSettings::UiClassEmbedding embeddingDefault - = CodeGenSettings::PointerAggregatedUiClass; -const bool includeQtModuleDefault = false; -const bool addQtVersionCheckDefault = false; +CodeGenSettings &codeGenSettings() +{ + static CodeGenSettings theCodeGenSettings; + return theCodeGenSettings; +} CodeGenSettings::CodeGenSettings() - : embedding(embeddingDefault) - , retranslationSupport(retranslationSupportDefault) - , includeQtModule(includeQtModuleDefault) - , addQtVersionCheck(addQtVersionCheckDefault) { + setSettingsGroup("FormClassWizardPage"); + + embedding.setSettingsKey("Embedding"); + embedding.addOption(Tr::tr("Aggregation as a pointer member")); + embedding.addOption(Tr::tr("Aggregation")); + embedding.addOption(Tr::tr("Multiple inheritance")); + embedding.setDefaultValue(CodeGenSettings::PointerAggregatedUiClass); + + retranslationSupport.setSettingsKey("RetranslationSupport"); + retranslationSupport.setLabelText(Tr::tr("Support for changing languages at runtime")); + + includeQtModule.setSettingsKey("IncludeQtModule"); + includeQtModule.setLabelText(Tr::tr("Use Qt module name in #include-directive")); + + addQtVersionCheck.setSettingsKey("AddQtVersionCheck"); + addQtVersionCheck.setLabelText(Tr::tr("Add Qt version #ifdef for module names")); + addQtVersionCheck.setEnabler(&includeQtModule); + + setLayouter([this] { + using namespace Layouting; + return Column { + Group { + title(Tr::tr("Embedding of the UI Class")), + Column { + embedding, + } + }, + Group { + title(Tr::tr("Code Generation")), + Column { + retranslationSupport, + includeQtModule, + addQtVersionCheck + } + }, + st + }; + }); + + + readSettings(); } -bool CodeGenSettings::equals(const CodeGenSettings &rhs) const +class CodeGenSettingsPage final : public Core::IOptionsPage { - return embedding == rhs.embedding - && retranslationSupport == rhs.retranslationSupport - && includeQtModule == rhs.includeQtModule - && addQtVersionCheck == rhs.addQtVersionCheck; -} +public: + CodeGenSettingsPage() + { + setId(Constants::CODEGEN_SETTINGS_PAGE_ID); + setDisplayName(Tr::tr("Qt Class Generation")); + setCategory(CppEditor::Constants::CPP_SETTINGS_CATEGORY); + setDisplayCategory(::CppEditor::Tr::tr(CppEditor::Constants::CPP_SETTINGS_NAME)); + setCategoryIconPath(":/projectexplorer/images/settingscategory_cpp.png"); + setSettingsProvider([] { return &codeGenSettings(); }); + } +}; -void CodeGenSettings::fromSettings(const QSettings *settings) -{ - QString group = QLatin1String(CODE_GEN_GROUP) + '/'; +const CodeGenSettingsPage settingsPage; - retranslationSupport = settings->value(group + TRANSLATION_KEY, retranslationSupportDefault) - .toBool(); - embedding = static_cast<UiClassEmbedding>( - settings->value(group + EMBEDDING_KEY, int(embeddingDefault)).toInt()); - includeQtModule = settings->value(group + INCLUDE_QT_MODULE_KEY, includeQtModuleDefault).toBool(); - addQtVersionCheck = settings->value(group + ADD_QT_VERSION_CHECK_KEY, addQtVersionCheckDefault).toBool(); -} - -void CodeGenSettings::toSettings(QSettings *settings) const -{ - settings->beginGroup(CODE_GEN_GROUP); - QtcSettings::setValueWithDefault(settings, - TRANSLATION_KEY, - retranslationSupport, - retranslationSupportDefault); - QtcSettings::setValueWithDefault(settings, EMBEDDING_KEY, int(embedding), int(embeddingDefault)); - QtcSettings::setValueWithDefault(settings, - INCLUDE_QT_MODULE_KEY, - includeQtModule, - includeQtModuleDefault); - QtcSettings::setValueWithDefault(settings, - ADD_QT_VERSION_CHECK_KEY, - addQtVersionCheck, - addQtVersionCheckDefault); - settings->endGroup(); - -} - -} // namespace QtSupport +} // QtSupport diff --git a/src/plugins/qtsupport/codegensettings.h b/src/plugins/qtsupport/codegensettings.h index 6199754e362..e9ab3fb459e 100644 --- a/src/plugins/qtsupport/codegensettings.h +++ b/src/plugins/qtsupport/codegensettings.h @@ -5,13 +5,15 @@ #include "qtsupport_global.h" -QT_FORWARD_DECLARE_CLASS(QSettings) +#include <utils/aspects.h> namespace QtSupport { -class QTSUPPORT_EXPORT CodeGenSettings +class QTSUPPORT_EXPORT CodeGenSettings : public Utils::AspectContainer { public: + CodeGenSettings(); + // How to embed the Ui::Form class. enum UiClassEmbedding { @@ -20,19 +22,12 @@ public: InheritedUiClass // "...private Ui::Form..." }; - CodeGenSettings(); - bool equals(const CodeGenSettings &rhs) const; - - void fromSettings(const QSettings *settings); - void toSettings(QSettings *settings) const; - - friend bool operator==(const CodeGenSettings &p1, const CodeGenSettings &p2) { return p1.equals(p2); } - friend bool operator!=(const CodeGenSettings &p1, const CodeGenSettings &p2) { return !p1.equals(p2); } - - UiClassEmbedding embedding; - bool retranslationSupport; // Add handling for language change events - bool includeQtModule; // Include "<QtGui/[Class]>" or just "<[Class]>" - bool addQtVersionCheck; // Include #ifdef when using "#include <QtGui/..." + Utils::SelectionAspect embedding{this}; + Utils::BoolAspect retranslationSupport{this}; // Add handling for language change events + Utils::BoolAspect includeQtModule{this}; // Include "<QtGui/[Class]>" or just "<[Class]>" + Utils::BoolAspect addQtVersionCheck{this}; // Include #ifdef when using "#include <QtGui/..." }; +QTSUPPORT_EXPORT CodeGenSettings &codeGenSettings(); + } // namespace QtSupport diff --git a/src/plugins/qtsupport/codegensettingspage.cpp b/src/plugins/qtsupport/codegensettingspage.cpp deleted file mode 100644 index 4a30e5f969f..00000000000 --- a/src/plugins/qtsupport/codegensettingspage.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "codegensettingspage.h" - -#include "codegensettings.h" -#include "qtsupportconstants.h" -#include "qtsupporttr.h" - -#include <coreplugin/icore.h> - -#include <cppeditor/cppeditorconstants.h> -#include <cppeditor/cppeditortr.h> - -#include <utils/layoutbuilder.h> - -#include <QCheckBox> -#include <QRadioButton> - -namespace QtSupport::Internal { - -class CodeGenSettingsPageWidget : public Core::IOptionsPageWidget -{ -public: - CodeGenSettingsPageWidget(); - -private: - void apply() final; - - int uiEmbedding() const; - - QRadioButton *m_ptrAggregationRadioButton; - QRadioButton *m_aggregationButton; - QRadioButton *m_multipleInheritanceButton; - QCheckBox *m_retranslateCheckBox; - QCheckBox *m_includeQtModuleCheckBox; - QCheckBox *m_addQtVersionCheckBox; -}; - -CodeGenSettingsPageWidget::CodeGenSettingsPageWidget() -{ - CodeGenSettings parameters; - parameters.fromSettings(Core::ICore::settings()); - - using namespace Layouting; - - m_ptrAggregationRadioButton = new QRadioButton(Tr::tr("Aggregation as a pointer member")); - m_ptrAggregationRadioButton->setChecked - (parameters.embedding == CodeGenSettings::PointerAggregatedUiClass); - - m_aggregationButton = new QRadioButton(Tr::tr("Aggregation")); - m_aggregationButton->setChecked - (parameters.embedding == CodeGenSettings::AggregatedUiClass); - - m_multipleInheritanceButton = new QRadioButton(Tr::tr("Multiple inheritance")); - m_multipleInheritanceButton->setChecked - (parameters.embedding == CodeGenSettings::InheritedUiClass); - - m_retranslateCheckBox = new QCheckBox(Tr::tr("Support for changing languages at runtime")); - m_retranslateCheckBox->setChecked(parameters.retranslationSupport); - - m_includeQtModuleCheckBox = new QCheckBox(Tr::tr("Use Qt module name in #include-directive")); - m_includeQtModuleCheckBox->setChecked(parameters.includeQtModule); - - m_addQtVersionCheckBox = new QCheckBox(Tr::tr("Add Qt version #ifdef for module names")); - m_addQtVersionCheckBox->setChecked(parameters.addQtVersionCheck); - m_addQtVersionCheckBox->setEnabled(false); - - Column { - Group { - title(Tr::tr("Embedding of the UI Class")), - Column { - m_ptrAggregationRadioButton, - m_aggregationButton, - m_multipleInheritanceButton - } - }, - Group { - title(Tr::tr("Code Generation")), - Column { - m_retranslateCheckBox, - m_includeQtModuleCheckBox, - m_addQtVersionCheckBox - } - }, - st - }.attachTo(this); - - connect(m_includeQtModuleCheckBox, &QAbstractButton::toggled, - m_addQtVersionCheckBox, &QWidget::setEnabled); -} - -void CodeGenSettingsPageWidget::apply() -{ - CodeGenSettings rc; - rc.embedding = static_cast<CodeGenSettings::UiClassEmbedding>(uiEmbedding()); - rc.retranslationSupport = m_retranslateCheckBox->isChecked(); - rc.includeQtModule = m_includeQtModuleCheckBox->isChecked(); - rc.addQtVersionCheck = m_addQtVersionCheckBox->isChecked(); - rc.toSettings(Core::ICore::settings()); -} - -int CodeGenSettingsPageWidget::uiEmbedding() const -{ - if (m_ptrAggregationRadioButton->isChecked()) - return CodeGenSettings::PointerAggregatedUiClass; - if (m_aggregationButton->isChecked()) - return CodeGenSettings::AggregatedUiClass; - return CodeGenSettings::InheritedUiClass; -} - -// ---------- CodeGenSettingsPage - -CodeGenSettingsPage::CodeGenSettingsPage() -{ - setId(Constants::CODEGEN_SETTINGS_PAGE_ID); - setDisplayName(Tr::tr("Qt Class Generation")); - setCategory(CppEditor::Constants::CPP_SETTINGS_CATEGORY); - setDisplayCategory(::CppEditor::Tr::tr(CppEditor::Constants::CPP_SETTINGS_NAME)); - setCategoryIconPath(":/projectexplorer/images/settingscategory_cpp.png"); - setWidgetCreator([] { return new CodeGenSettingsPageWidget; }); -} - -} // QtSupport::Internal diff --git a/src/plugins/qtsupport/codegensettingspage.h b/src/plugins/qtsupport/codegensettingspage.h deleted file mode 100644 index 0f8bd4e196e..00000000000 --- a/src/plugins/qtsupport/codegensettingspage.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace QtSupport::Internal { - -class CodeGenSettingsPage final : public Core::IOptionsPage -{ -public: - CodeGenSettingsPage(); -}; - -} // QtSupport::Internal diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index 5beb9c80a35..3d67706b77f 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -9,7 +9,6 @@ #include <QApplication> #include <QDir> #include <QFile> -#include <QImageReader> #include <QPixmapCache> #include <QUrl> @@ -18,7 +17,7 @@ #include <coreplugin/helpmanager.h> #include <coreplugin/icore.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qtsupport/qtversionmanager.h> #include <utils/algorithm.h> @@ -50,13 +49,13 @@ Q_GLOBAL_STATIC_WITH_ARGS(QVersionNumber, minQtVersionForCategories, (6, 5, 1)); void ExampleSetModel::writeCurrentIdToSettings(int currentIndex) const { - QSettings *settings = Core::ICore::settings(); - settings->setValue(QLatin1String(kSelectedExampleSetKey), getId(currentIndex)); + QtcSettings *settings = Core::ICore::settings(); + settings->setValue(kSelectedExampleSetKey, getId(currentIndex)); } int ExampleSetModel::readCurrentIndexFromSettings() const { - QVariant id = Core::ICore::settings()->value(QLatin1String(kSelectedExampleSetKey)); + QVariant id = Core::ICore::settings()->value(kSelectedExampleSetKey); for (int i=0; i < rowCount(); i++) { if (id == getId(i)) return i; @@ -69,7 +68,7 @@ ExampleSetModel::ExampleSetModel() if (debugExamples() && !log().isDebugEnabled()) log().setEnabled(QtDebugMsg, true); // read extra example sets settings - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); const QStringList list = settings->value("Help/InstalledExamples", QStringList()).toStringList(); qCDebug(log) << "Reading Help/InstalledExamples from settings:" << list; for (const QString &item : list) { @@ -244,19 +243,19 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url) return pixmap; if (url.startsWith("qthelp://")) { - QByteArray fetchedData = Core::HelpManager::fileData(url); + const QByteArray fetchedData = Core::HelpManager::fileData(url); if (!fetchedData.isEmpty()) { - QBuffer imgBuffer(&fetchedData); - imgBuffer.open(QIODevice::ReadOnly); - QImageReader reader(&imgBuffer, QFileInfo(url).suffix().toLatin1()); - QImage img = reader.read(); - img.convertTo(QImage::Format_RGB32); + const QImage img = QImage::fromData(fetchedData, QFileInfo(url).suffix().toLatin1()) + .convertToFormat(QImage::Format_RGB32); const int dpr = qApp->devicePixelRatio(); // boundedTo -> don't scale thumbnails up const QSize scaledSize = WelcomePageHelpers::GridItemImageSize.boundedTo(img.size()) * dpr; - pixmap = QPixmap::fromImage( - img.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + const QImage scaled = img.isNull() ? img + : img.scaled(scaledSize, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + pixmap = QPixmap::fromImage(scaled); pixmap.setDevicePixelRatio(dpr); } } else { @@ -273,11 +272,13 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url) ExamplesViewController::ExamplesViewController(ExampleSetModel *exampleSetModel, SectionedGridView *view, + QLineEdit *searchField, bool isExamples, QObject *parent) : QObject(parent) , m_exampleSetModel(exampleSetModel) , m_view(view) + , m_searchField(searchField) , m_isExamples(isExamples) { if (isExamples) { @@ -290,6 +291,10 @@ ExamplesViewController::ExamplesViewController(ExampleSetModel *exampleSetModel, &Core::HelpManager::Signals::documentationChanged, this, &ExamplesViewController::updateExamples); + connect(m_searchField, + &QLineEdit::textChanged, + m_view, + &SectionedGridView::setSearchStringDelayed); view->setPixmapFunction(fetchPixmapAndUpdatePixmapCache); updateExamples(); } @@ -325,8 +330,33 @@ static bool isValidExampleOrDemo(ExampleItem *item) return ok || debugExamples(); } +// ordered list of "known" categories +// TODO this should be defined in the manifest +Q_GLOBAL_STATIC_WITH_ARGS(QStringList, + defaultOrder, + {QStringList() << "Application Examples" + << "Desktop" + << "Mobile" + << "Embedded" + << "Graphics & Multimedia" + << "Graphics" + << "Data Visualization & 3D" + << "Data Processing & I/O" + << "Input/Output" + << "Connectivity" + << "Networking" + << "Positioning & Location" + << "Web Technologies" + << "Internationalization"}); + void ExamplesViewController::updateExamples() { + if (!isVisible()) { + m_needsUpdateExamples = true; + return; + } + m_needsUpdateExamples = false; + QString examplesInstallPath; QString demosInstallPath; QVersionNumber qtVersion; @@ -334,15 +364,14 @@ void ExamplesViewController::updateExamples() const QStringList sources = m_exampleSetModel->exampleSources(&examplesInstallPath, &demosInstallPath, &qtVersion); - m_view->clear(); - + QStringList categoryOrder; QList<ExampleItem *> items; for (const QString &exampleSource : sources) { const auto manifest = FilePath::fromUserInput(exampleSource); qCDebug(log) << QString::fromLatin1("Reading file \"%1\"...") .arg(manifest.absoluteFilePath().toUserOutput()); - const expected_str<QList<ExampleItem *>> result + const expected_str<ParsedExamples> result = parseExamples(manifest, FilePath::fromUserInput(examplesInstallPath), FilePath::fromUserInput(demosInstallPath), @@ -352,7 +381,9 @@ void ExamplesViewController::updateExamples() << result.error(); continue; } - items += filtered(*result, isValidExampleOrDemo); + items += filtered(result->items, isValidExampleOrDemo); + if (categoryOrder.isEmpty()) + categoryOrder = result->categoryOrder; } if (m_isExamples) { @@ -366,13 +397,37 @@ void ExamplesViewController::updateExamples() } } - const bool sortIntoCategories = qtVersion >= *minQtVersionForCategories; + const bool sortIntoCategories = !m_isExamples || qtVersion >= *minQtVersionForCategories; + const QStringList order = categoryOrder.isEmpty() && m_isExamples ? *defaultOrder + : categoryOrder; const QList<std::pair<Section, QList<ExampleItem *>>> sections - = getCategories(items, sortIntoCategories); + = getCategories(items, sortIntoCategories, order, m_isExamples); + + m_view->setVisible(false); + m_view->clear(); + for (int i = 0; i < sections.size(); ++i) { m_view->addSection(sections.at(i).first, static_container_cast<ListItem *>(sections.at(i).second)); } + if (!m_searchField->text().isEmpty()) + m_view->setSearchString(m_searchField->text()); + + m_view->setVisible(true); +} + +void ExamplesViewController::setVisible(bool visible) +{ + if (m_isVisible == visible) + return; + m_isVisible = visible; + if (m_isVisible && m_needsUpdateExamples) + updateExamples(); +} + +bool ExamplesViewController::isVisible() const +{ + return m_isVisible; } void ExampleSetModel::updateQtVersionList() @@ -410,8 +465,8 @@ void ExampleSetModel::updateQtVersionList() // Make sure to select something even if the above failed if (currentIndex < 0 && rowCount() > 0) currentIndex = 0; // simply select first - selectExampleSet(currentIndex); - emit selectedExampleSetChanged(currentIndex); + if (!selectExampleSet(currentIndex)) + emit selectedExampleSetChanged(currentIndex); // ensure running updateExamples in any case } QtVersion *ExampleSetModel::findHighestQtVersion(const QtVersions &versions) const @@ -494,7 +549,7 @@ QStringList ExampleSetModel::exampleSources(QString *examplesInstallPath, return sources; } -void ExampleSetModel::selectExampleSet(int index) +bool ExampleSetModel::selectExampleSet(int index) { if (index != m_selectedExampleSetIndex) { m_selectedExampleSetIndex = index; @@ -506,7 +561,9 @@ void ExampleSetModel::selectExampleSet(int index) m_selectedQtTypes.clear(); } emit selectedExampleSetChanged(m_selectedExampleSetIndex); + return true; } + return false; } void ExampleSetModel::qtVersionManagerLoaded() diff --git a/src/plugins/qtsupport/exampleslistmodel.h b/src/plugins/qtsupport/exampleslistmodel.h index f0465655b79..142f1c72f6c 100644 --- a/src/plugins/qtsupport/exampleslistmodel.h +++ b/src/plugins/qtsupport/exampleslistmodel.h @@ -38,7 +38,7 @@ public: ExampleSetModel(); int selectedExampleSet() const { return m_selectedExampleSetIndex; } - void selectExampleSet(int index); + bool selectExampleSet(int index); QStringList exampleSources(QString *examplesInstallPath, QString *demosInstallPath, QVersionNumber *qtVersion); @@ -85,19 +85,24 @@ private: class ExamplesViewController : public QObject { - Q_OBJECT public: explicit ExamplesViewController(ExampleSetModel *exampleSetModel, Core::SectionedGridView *view, + QLineEdit *searchField, bool isExamples, QObject *parent); void updateExamples(); + void setVisible(bool isVisible); + bool isVisible() const; private: ExampleSetModel *m_exampleSetModel; Core::SectionedGridView *m_view; + QLineEdit *m_searchField; bool m_isExamples; + bool m_isVisible = false; + bool m_needsUpdateExamples = false; }; } // namespace Internal diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp index 9093241001e..293c19ecb8f 100644 --- a/src/plugins/qtsupport/examplesparser.cpp +++ b/src/plugins/qtsupport/examplesparser.cpp @@ -58,8 +58,7 @@ static QHash<QString, QStringList> parseMeta(QXmlStreamReader *reader) reader->raiseError("Tag \"entry\" requires \"name\" attribute"); break; } - const QString value = reader->readElementText( - QXmlStreamReader::ErrorOnUnexpectedElement); + const QString value = reader->readElementText(); if (!value.isEmpty()) result[key].append(value); } @@ -75,6 +74,26 @@ static QHash<QString, QStringList> parseMeta(QXmlStreamReader *reader) return result; } +static QStringList parseCategories(QXmlStreamReader *reader) +{ + QStringList categoryOrder; + while (!reader->atEnd()) { + switch (reader->readNext()) { + case QXmlStreamReader::StartElement: + if (reader->name() == QLatin1String("category")) + categoryOrder.append(reader->readElementText()); + break; + case QXmlStreamReader::EndElement: + if (reader->name() == QLatin1String("categories")) + return categoryOrder; + break; + default: + break; + } + } + return categoryOrder; +} + static QList<ExampleItem *> parseExamples(QXmlStreamReader *reader, const FilePath &projectsOffset, const FilePath &examplesInstallPath) @@ -105,28 +124,22 @@ static QList<ExampleItem *> parseExamples(QXmlStreamReader *reader, const QString mainFileAttribute = reader->attributes().value(QLatin1String("mainFile")).toString(); const FilePath filePath - = relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText( - QXmlStreamReader::ErrorOnUnexpectedElement)), + = relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText()), projectsOffset, examplesInstallPath); item->filesToOpen.append(filePath); if (mainFileAttribute.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) item->mainFile = filePath; } else if (reader->name() == QLatin1String("description")) { - item->description = fixStringForTags( - reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + item->description = fixStringForTags(reader->readElementText()); } else if (reader->name() == QLatin1String("dependency")) { - item->dependencies.append( - projectsOffset - / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + item->dependencies.append(projectsOffset / reader->readElementText()); } else if (reader->name() == QLatin1String("tags")) { item->tags = trimStringList( - reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) - .split(QLatin1Char(','), Qt::SkipEmptyParts)); + reader->readElementText().split(QLatin1Char(','), Qt::SkipEmptyParts)); } else if (reader->name() == QLatin1String("platforms")) { item->platforms = trimStringList( - reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) - .split(QLatin1Char(','), Qt::SkipEmptyParts)); + reader->readElementText().split(QLatin1Char(','), Qt::SkipEmptyParts)); } else if (reader->name() == QLatin1String("meta")) { item->metaData = parseMeta(reader); } @@ -172,20 +185,18 @@ static QList<ExampleItem *> parseDemos(QXmlStreamReader *reader, == QLatin1String("true"); } else if (reader->name() == QLatin1String("fileToOpen")) { item->filesToOpen.append( - relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText( - QXmlStreamReader::ErrorOnUnexpectedElement)), + relativeOrInstallPath(FilePath::fromUserInput(reader->readElementText()), projectsOffset, demosInstallPath)); } else if (reader->name() == QLatin1String("description")) { item->description = fixStringForTags( - reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + reader->readElementText()); } else if (reader->name() == QLatin1String("dependency")) { item->dependencies.append( projectsOffset - / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + / reader->readElementText()); } else if (reader->name() == QLatin1String("tags")) { - item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) - .split(QLatin1Char(',')); + item->tags = reader->readElementText().split(QLatin1Char(',')); } break; case QXmlStreamReader::EndElement: @@ -215,7 +226,8 @@ static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const FileP QXmlStreamAttributes attributes = reader->attributes(); item->name = attributes.value(QLatin1String("name")).toString(); const QString projectPath = attributes.value(QLatin1String("projectPath")).toString(); - item->projectPath = projectsOffset / projectPath; + item->projectPath = projectPath.isEmpty() ? FilePath() + : projectsOffset / projectPath; item->hasSourceCode = !projectPath.isEmpty(); item->imageUrl = Utils::StyleHelper::dpiSpecificImageFile( attributes.value(QLatin1String("imageUrl")).toString()); @@ -228,17 +240,15 @@ static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const FileP } else if (reader->name() == QLatin1String("fileToOpen")) { item->filesToOpen.append( projectsOffset - / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + / reader->readElementText()); } else if (reader->name() == QLatin1String("description")) { - item->description = fixStringForTags( - reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + item->description = fixStringForTags(reader->readElementText()); } else if (reader->name() == QLatin1String("dependency")) { - item->dependencies.append( - projectsOffset - / reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)); + item->dependencies.append(projectsOffset / reader->readElementText()); } else if (reader->name() == QLatin1String("tags")) { - item->tags = reader->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement) - .split(QLatin1Char(',')); + item->tags = reader->readElementText().split(QLatin1Char(',')); + } else if (reader->name() == QLatin1String("meta")) { + item->metaData = parseMeta(reader); } break; case QXmlStreamReader::EndElement: @@ -254,10 +264,10 @@ static QList<ExampleItem *> parseTutorials(QXmlStreamReader *reader, const FileP return result; } -expected_str<QList<ExampleItem *>> parseExamples(const FilePath &manifest, - const FilePath &examplesInstallPath, - const FilePath &demosInstallPath, - const bool examples) +expected_str<ParsedExamples> parseExamples(const FilePath &manifest, + const FilePath &examplesInstallPath, + const FilePath &demosInstallPath, + const bool examples) { const expected_str<QByteArray> contents = manifest.fileContents(); if (!contents) @@ -266,19 +276,22 @@ expected_str<QList<ExampleItem *>> parseExamples(const FilePath &manifest, return parseExamples(*contents, manifest, examplesInstallPath, demosInstallPath, examples); } -expected_str<QList<ExampleItem *>> parseExamples(const QByteArray &manifestData, - const Utils::FilePath &manifestPath, - const FilePath &examplesInstallPath, - const FilePath &demosInstallPath, - const bool examples) +expected_str<ParsedExamples> parseExamples(const QByteArray &manifestData, + const Utils::FilePath &manifestPath, + const FilePath &examplesInstallPath, + const FilePath &demosInstallPath, + const bool examples) { const FilePath path = manifestPath.parentDir(); + QStringList categoryOrder; QList<ExampleItem *> items; QXmlStreamReader reader(manifestData); while (!reader.atEnd()) { switch (reader.readNext()) { case QXmlStreamReader::StartElement: - if (examples && reader.name() == QLatin1String("examples")) + if (categoryOrder.isEmpty() && reader.name() == QLatin1String("categories")) + categoryOrder = parseCategories(&reader); + else if (examples && reader.name() == QLatin1String("examples")) items += parseExamples(&reader, path, examplesInstallPath); else if (examples && reader.name() == QLatin1String("demos")) items += parseDemos(&reader, path, demosInstallPath); @@ -298,28 +311,9 @@ expected_str<QList<ExampleItem *>> parseExamples(const QByteArray &manifestData, .arg(reader.columnNumber()) .arg(reader.errorString())); } - return items; + return {{items, categoryOrder}}; } -// ordered list of "known" categories -// TODO this should be defined in the manifest -Q_GLOBAL_STATIC_WITH_ARGS(QList<QString>, - defaultOrder, - {QStringList() << "Application Examples" - << "Desktop" - << "Mobile" - << "Embedded" - << "Graphics & Multimedia" - << "Graphics" - << "Data Visualization & 3D" - << "Data Processing & I/O" - << "Input/Output" - << "Connectivity" - << "Networking" - << "Positioning & Location" - << "Web Technologies" - << "Internationalization"}); - static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) { if (first->isHighlighted && !second->isHighlighted) @@ -330,7 +324,9 @@ static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) } QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories(const QList<ExampleItem *> &items, - bool sortIntoCategories) + bool sortIntoCategories, + const QStringList &defaultOrder, + bool restrictRows) { static const QString otherDisplayName = Tr::tr("Other", "Category for all other examples"); const bool useCategories = sortIntoCategories @@ -369,14 +365,17 @@ QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories(const QList< } else { // All original items have been copied into a category or other, delete. qDeleteAll(items); - static const int defaultOrderSize = defaultOrder->size(); + const int defaultOrderSize = defaultOrder.size(); int index = 0; const auto end = categoryMap.constKeyValueEnd(); for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) { // order "known" categories as wanted, others come afterwards - const int defaultIndex = defaultOrder->indexOf(it->first); + const int defaultIndex = defaultOrder.indexOf(it->first); const int priority = defaultIndex >= 0 ? defaultIndex : (index + defaultOrderSize); - categories.append({{it->first, priority, /*maxRows=*/index == 0 ? 2 : 1}, it->second}); + const std::optional<int> maxRows = restrictRows + ? std::make_optional<int>(index == 0 ? 2 : 1) + : std::nullopt; + categories.append({{it->first, priority, maxRows}, it->second}); ++index; } if (!other.isEmpty()) diff --git a/src/plugins/qtsupport/examplesparser.h b/src/plugins/qtsupport/examplesparser.h index 54efaf58875..aee539ad62b 100644 --- a/src/plugins/qtsupport/examplesparser.h +++ b/src/plugins/qtsupport/examplesparser.h @@ -31,13 +31,20 @@ public: QHash<QString, QStringList> metaData; }; -QTSUPPORT_TEST_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples( +class QTSUPPORT_TEST_EXPORT ParsedExamples +{ +public: + QList<ExampleItem *> items; + QStringList categoryOrder; +}; + +QTSUPPORT_TEST_EXPORT Utils::expected_str<ParsedExamples> parseExamples( const Utils::FilePath &manifest, const Utils::FilePath &examplesInstallPath, const Utils::FilePath &demosInstallPath, bool examples); -QTSUPPORT_TEST_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples( +QTSUPPORT_TEST_EXPORT Utils::expected_str<ParsedExamples> parseExamples( const QByteArray &manifestData, const Utils::FilePath &manifestPath, const Utils::FilePath &examplesInstallPath, @@ -45,7 +52,10 @@ QTSUPPORT_TEST_EXPORT Utils::expected_str<QList<ExampleItem *>> parseExamples( bool examples); QTSUPPORT_TEST_EXPORT QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories( - const QList<ExampleItem *> &items, bool sortIntoCategories); + const QList<ExampleItem *> &items, + bool sortIntoCategories, + const QStringList &defaultOrder, + bool restrictRows); } // namespace QtSupport::Internal diff --git a/src/plugins/qtsupport/externaleditors.cpp b/src/plugins/qtsupport/externaleditors.cpp index dffd730d285..36abc53dbdf 100644 --- a/src/plugins/qtsupport/externaleditors.cpp +++ b/src/plugins/qtsupport/externaleditors.cpp @@ -11,7 +11,7 @@ #include <projectexplorer/projectmanager.h> #include <projectexplorer/target.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <designer/designerconstants.h> @@ -179,6 +179,10 @@ static bool getEditorLaunchData(const CommandForQtVersion &commandForQtVersion, // - default kit // - any other kit // As fallback check PATH + if (!KitManager::waitForLoaded()) { + *errorMessage = Tr::tr("Could not load kits in a reasonable amount of time."); + return false; + } data->workingDirectory.clear(); QVector<QtSupport::QtVersion *> qtVersionsToCheck; // deduplicated after being filled if (const Project *project = ProjectManager::projectForFile(filePath)) { @@ -258,63 +262,62 @@ DesignerExternalEditor::DesignerExternalEditor() setId("Qt.Designer"); setDisplayName(::Core::Tr::tr("Qt Designer")); setMimeTypes({ProjectExplorer::Constants::FORM_MIMETYPE}); -} -bool DesignerExternalEditor::startEditor(const FilePath &filePath, QString *errorMessage) -{ - LaunchData data; + setEditorStarter([this](const FilePath &filePath, QString *errorMessage) { + LaunchData data; - // Find the editor binary - if (!getEditorLaunchData(designerBinary, filePath, &data, errorMessage)) - return false; + // Find the editor binary + if (!getEditorLaunchData(designerBinary, filePath, &data, errorMessage)) + return false; - if (HostOsInfo::isMacHost()) - return startEditorProcess(data, errorMessage); + if (HostOsInfo::isMacHost()) + return startEditorProcess(data, errorMessage); - /* Qt Designer on the remaining platforms: Uses Designer's own - * Tcp-based communication mechanism to ensure all files are opened - * in one instance (per version). */ + /* Qt Designer on the remaining platforms: Uses Designer's own + * Tcp-based communication mechanism to ensure all files are opened + * in one instance (per version). */ - // Known one? - const ProcessCache::iterator it = m_processCache.find(data.binary); - if (it != m_processCache.end()) { - // Process is known, write to its socket to cause it to open the file - if (debug) - qDebug() << Q_FUNC_INFO << "\nWriting to socket:" << data.binary << filePath; - QTcpSocket *socket = it.value(); - if (!socket->write(filePath.toString().toUtf8() + '\n')) { - *errorMessage = Tr::tr("Qt Designer is not responding (%1).").arg(socket->errorString()); + // Known one? + const ProcessCache::iterator it = m_processCache.find(data.binary); + if (it != m_processCache.end()) { + // Process is known, write to its socket to cause it to open the file + if (debug) + qDebug() << Q_FUNC_INFO << "\nWriting to socket:" << data.binary << filePath; + QTcpSocket *socket = it.value(); + if (!socket->write(filePath.toString().toUtf8() + '\n')) { + *errorMessage = Tr::tr("Qt Designer is not responding (%1).").arg(socket->errorString()); + return false; + } + return true; + } + // No process yet. Create socket & launch the process + QTcpServer server; + if (!server.listen(QHostAddress::LocalHost)) { + *errorMessage = Tr::tr("Unable to create server socket: %1").arg(server.errorString()); return false; } - return true; - } - // No process yet. Create socket & launch the process - QTcpServer server; - if (!server.listen(QHostAddress::LocalHost)) { - *errorMessage = Tr::tr("Unable to create server socket: %1").arg(server.errorString()); - return false; - } - const quint16 port = server.serverPort(); - if (debug) - qDebug() << Q_FUNC_INFO << "\nLaunching server:" << port << data.binary << filePath; - // Start first one with file and socket as '-client port file' - // Wait for the socket listening - data.arguments.push_front(QString::number(port)); - data.arguments.push_front(QLatin1String("-client")); + const quint16 port = server.serverPort(); + if (debug) + qDebug() << Q_FUNC_INFO << "\nLaunching server:" << port << data.binary << filePath; + // Start first one with file and socket as '-client port file' + // Wait for the socket listening + data.arguments.push_front(QString::number(port)); + data.arguments.push_front(QLatin1String("-client")); - if (!startEditorProcess(data, errorMessage)) - return false; - // Insert into cache if socket is created, else try again next time - if (server.waitForNewConnection(3000)) { - QTcpSocket *socket = server.nextPendingConnection(); - socket->setParent(this); - const QString binary = data.binary; - m_processCache.insert(binary, socket); - auto mapSlot = [binary] { processTerminated(binary); }; - connect(socket, &QAbstractSocket::disconnected, this, mapSlot); - connect(socket, &QAbstractSocket::errorOccurred, this, mapSlot); - } - return true; + if (!startEditorProcess(data, errorMessage)) + return false; + // Insert into cache if socket is created, else try again next time + if (server.waitForNewConnection(3000)) { + QTcpSocket *socket = server.nextPendingConnection(); + socket->setParent(&m_guard); + const QString binary = data.binary; + m_processCache.insert(binary, socket); + auto mapSlot = [binary] { processTerminated(binary); }; + QObject::connect(socket, &QAbstractSocket::disconnected, &m_guard, mapSlot); + QObject::connect(socket, &QAbstractSocket::errorOccurred, &m_guard, mapSlot); + } + return true; + }); } // Linguist @@ -331,13 +334,11 @@ LinguistEditor::LinguistEditor() setId("Qt.Linguist"); setDisplayName(::Core::Tr::tr("Qt Linguist")); setMimeTypes({ProjectExplorer::Constants::LINGUIST_MIMETYPE}); -} - -bool LinguistEditor::startEditor(const Utils::FilePath &filePath, QString *errorMessage) -{ - LaunchData data; - return getEditorLaunchData(linguistBinary, filePath, &data, errorMessage) - && startEditorProcess(data, errorMessage); + setEditorStarter([](const FilePath &filePath, QString *errorMessage) { + LaunchData data; + return getEditorLaunchData(linguistBinary, filePath, &data, errorMessage) + && startEditorProcess(data, errorMessage); + }); } } // QtSupport::Internal diff --git a/src/plugins/qtsupport/externaleditors.h b/src/plugins/qtsupport/externaleditors.h index 14fd2ea2272..5c34940f572 100644 --- a/src/plugins/qtsupport/externaleditors.h +++ b/src/plugins/qtsupport/externaleditors.h @@ -3,24 +3,25 @@ #pragma once -#include <coreplugin/editormanager/iexternaleditor.h> +#include <coreplugin/editormanager/ieditorfactory.h> + +#include <QObject> namespace QtSupport::Internal { -class DesignerExternalEditor : public Core::IExternalEditor +class DesignerExternalEditor : public Core::IEditorFactory { public: DesignerExternalEditor(); - bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) final; +private: + QObject m_guard; }; -class LinguistEditor : public Core::IExternalEditor +class LinguistEditor : public Core::IEditorFactory { public: LinguistEditor(); - - bool startEditor(const Utils::FilePath &filePath, QString *errorMessage) final; }; } // QtSupport::Internal diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index 7bc7192185e..39420845ca6 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -96,7 +96,7 @@ FilePath ExamplesWelcomePage::copyToAlternativeLocation(const FilePath &proFile, auto chooser = new PathChooser; txt->setBuddy(chooser); chooser->setExpectedKind(PathChooser::ExistingDirectory); - chooser->setHistoryCompleter(QLatin1String("Qt.WritableExamplesDir.History")); + chooser->setHistoryCompleter("Qt.WritableExamplesDir.History"); const FilePath defaultRootDirectory = DocumentManager::projectsDirectory(); QtcSettings *settings = ICore::settings(); chooser->setFilePath( @@ -116,7 +116,8 @@ FilePath ExamplesWelcomePage::copyToAlternativeLocation(const FilePath &proFile, if (code == Copy) { const QString exampleDirName = projectDir.fileName(); const FilePath destBaseDir = chooser->filePath(); - settings->setValueWithDefault(C_FALLBACK_ROOT, destBaseDir, defaultRootDirectory); + settings->setValueWithDefault(C_FALLBACK_ROOT, destBaseDir.toSettings(), + defaultRootDirectory.toSettings()); const FilePath targetDir = destBaseDir / exampleDirName; if (targetDir.exists()) { QMessageBox::warning(ICore::dialogParent(), @@ -304,14 +305,14 @@ public: grid->addWidget(WelcomePageHelpers::panelBar(this), 0, 2); auto gridView = new SectionedGridView(this); - new ExamplesViewController(s_exampleSetModel, gridView, isExamples, this); + m_viewController + = new ExamplesViewController(s_exampleSetModel, gridView, m_searcher, isExamples, this); gridView->setItemDelegate(&m_exampleDelegate); grid->addWidget(gridView, 1, 1, 1, 2); connect(&m_exampleDelegate, &ExampleDelegate::tagClicked, this, &ExamplesPageWidget::onTagClicked); - connect(m_searcher, &QLineEdit::textChanged, gridView, &SectionedGridView::setSearchString); } void onTagClicked(const QString &tag) @@ -321,9 +322,22 @@ public: + QString("tag:\"%1\" ").arg(tag)); } + void showEvent(QShowEvent *event) override + { + m_viewController->setVisible(true); + QWidget::showEvent(event); + } + + void hideEvent(QHideEvent *event) override + { + m_viewController->setVisible(false); + QWidget::hideEvent(event); + } + const bool m_isExamples; ExampleDelegate m_exampleDelegate; QLineEdit *m_searcher; + ExamplesViewController *m_viewController = nullptr; }; QWidget *ExamplesWelcomePage::createWidget() const diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp index 7cb67f6171e..e345488e95a 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.cpp +++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp @@ -4,31 +4,64 @@ #include "qscxmlcgenerator.h" #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <projectexplorer/target.h> + #include <utils/qtcassert.h> +#include <utils/temporarydirectory.h> #include <QDateTime> #include <QLoggingCategory> -#include <QUuid> using namespace ProjectExplorer; +using namespace Utils; namespace QtSupport { static QLoggingCategory log("qtc.qscxmlcgenerator", QtWarningMsg); -static const char TaskCategory[] = "Task.Category.ExtraCompiler.QScxmlc"; -QScxmlcGenerator::QScxmlcGenerator(const Project *project, - const Utils::FilePath &source, - const Utils::FilePaths &targets, QObject *parent) : - ProcessExtraCompiler(project, source, targets, parent), - m_tmpdir("qscxmlgenerator") +const char TaskCategory[] = "Task.Category.ExtraCompiler.QScxmlc"; + +class QScxmlcGenerator final : public ProcessExtraCompiler { - QTC_ASSERT(targets.count() == 2, return); - m_header = m_tmpdir.filePath(targets[0].fileName()).toString(); - m_impl = m_tmpdir.filePath(targets[1].fileName()).toString(); -} +public: + QScxmlcGenerator(const Project *project, const FilePath &source, + const FilePaths &targets, QObject *parent) + : ProcessExtraCompiler(project, source, targets, parent) + , m_tmpdir("qscxmlgenerator") + { + QTC_ASSERT(targets.count() == 2, return); + m_header = m_tmpdir.filePath(targets[0].fileName()).toString(); + QTC_ASSERT(!m_header.isEmpty(), return); + m_impl = m_tmpdir.filePath(targets[1].fileName()).toString(); + } + +private: + FilePath command() const override; + + QStringList arguments() const override + { + return {"--header", m_header, "--impl", m_impl, tmpFile().fileName()}; + } + + FilePath workingDirectory() const override + { + return m_tmpdir.path(); + } + + FilePath tmpFile() const + { + return workingDirectory().pathAppended(source().fileName()); + } + + FileNameToContentsHash handleProcessFinished(Process *process) override; + bool prepareToRun(const QByteArray &sourceContents) override; + Tasks parseIssues(const QByteArray &processStderr) override; + + TemporaryDirectory m_tmpdir; + QString m_header; + QString m_impl; +}; Tasks QScxmlcGenerator::parseIssues(const QByteArray &processStderr) { @@ -38,7 +71,7 @@ Tasks QScxmlcGenerator::parseIssues(const QByteArray &processStderr) QByteArrayList tokens = line.split(':'); if (tokens.length() > 4) { - Utils::FilePath file = Utils::FilePath::fromUtf8(tokens[0]); + FilePath file = FilePath::fromUtf8(tokens[0]); int line = tokens[1].toInt(); // int column = tokens[2].toInt(); <- nice, but not needed for now. Task::TaskType type = tokens[3].trimmed() == "error" ? @@ -50,8 +83,7 @@ Tasks QScxmlcGenerator::parseIssues(const QByteArray &processStderr) return issues; } - -Utils::FilePath QScxmlcGenerator::command() const +FilePath QScxmlcGenerator::command() const { QtSupport::QtVersion *version = nullptr; Target *target; @@ -61,27 +93,14 @@ Utils::FilePath QScxmlcGenerator::command() const version = QtSupport::QtKitAspect::qtVersion(KitManager::defaultKit()); if (!version) - return Utils::FilePath(); + return {}; return version->qscxmlcFilePath(); } -QStringList QScxmlcGenerator::arguments() const -{ - QTC_ASSERT(!m_header.isEmpty(), return QStringList()); - - return QStringList({QLatin1String("--header"), m_header, QLatin1String("--impl"), m_impl, - tmpFile().fileName()}); -} - -Utils::FilePath QScxmlcGenerator::workingDirectory() const -{ - return m_tmpdir.path(); -} - bool QScxmlcGenerator::prepareToRun(const QByteArray &sourceContents) { - const Utils::FilePath fn = tmpFile(); + const FilePath fn = tmpFile(); QFile input(fn.toString()); if (!input.open(QIODevice::WriteOnly)) return false; @@ -91,13 +110,13 @@ bool QScxmlcGenerator::prepareToRun(const QByteArray &sourceContents) return true; } -FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::Process *process) +FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Process *process) { Q_UNUSED(process) - const Utils::FilePath wd = workingDirectory(); + const FilePath wd = workingDirectory(); FileNameToContentsHash result; - forEachTarget([&](const Utils::FilePath &target) { - const Utils::FilePath file = wd.pathAppended(target.fileName()); + forEachTarget([&](const FilePath &target) { + const FilePath file = wd.pathAppended(target.fileName()); QFile generated(file.toString()); if (!generated.open(QIODevice::ReadOnly)) return; @@ -106,11 +125,6 @@ FileNameToContentsHash QScxmlcGenerator::handleProcessFinished(Utils::Process *p return result; } -Utils::FilePath QScxmlcGenerator::tmpFile() const -{ - return workingDirectory().pathAppended(source().fileName()); -} - FileType QScxmlcGeneratorFactory::sourceType() const { return FileType::StateChart; @@ -122,8 +136,8 @@ QString QScxmlcGeneratorFactory::sourceTag() const } ExtraCompiler *QScxmlcGeneratorFactory::create( - const Project *project, const Utils::FilePath &source, - const Utils::FilePaths &targets) + const Project *project, const FilePath &source, + const FilePaths &targets) { return new QScxmlcGenerator(project, source, targets, this); } diff --git a/src/plugins/qtsupport/qscxmlcgenerator.h b/src/plugins/qtsupport/qscxmlcgenerator.h index 342db3cb096..9bfe8591033 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.h +++ b/src/plugins/qtsupport/qscxmlcgenerator.h @@ -4,37 +4,13 @@ #pragma once #include <projectexplorer/extracompiler.h> -#include <utils/fileutils.h> -#include <utils/temporarydirectory.h> + +#include <utils/filepath.h> namespace QtSupport { -class QScxmlcGenerator : public ProjectExplorer::ProcessExtraCompiler -{ - Q_OBJECT -public: - QScxmlcGenerator(const ProjectExplorer::Project *project, const Utils::FilePath &source, - const Utils::FilePaths &targets, QObject *parent = nullptr); - -protected: - Utils::FilePath command() const override; - QStringList arguments() const override; - Utils::FilePath workingDirectory() const override; - -private: - Utils::FilePath tmpFile() const; - ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override; - bool prepareToRun(const QByteArray &sourceContents) override; - ProjectExplorer::Tasks parseIssues(const QByteArray &processStderr) override; - - Utils::TemporaryDirectory m_tmpdir; - QString m_header; - QString m_impl; -}; - class QScxmlcGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory { - Q_OBJECT public: QScxmlcGeneratorFactory() = default; diff --git a/src/plugins/qtsupport/qtbuildaspects.cpp b/src/plugins/qtsupport/qtbuildaspects.cpp index 210ea58ee5a..e941dcb1872 100644 --- a/src/plugins/qtsupport/qtbuildaspects.cpp +++ b/src/plugins/qtsupport/qtbuildaspects.cpp @@ -22,12 +22,12 @@ using namespace Utils; namespace QtSupport { -QmlDebuggingAspect::QmlDebuggingAspect(BuildConfiguration *buildConfig) - : m_buildConfig(buildConfig) +QmlDebuggingAspect::QmlDebuggingAspect(AspectContainer *container) + : TriStateAspect(container) { setSettingsKey("EnableQmlDebugging"); setDisplayName(Tr::tr("QML debugging and profiling:")); - setValue(ProjectExplorerPlugin::buildPropertiesSettings().qmlDebugging.value()); + setValue(buildPropertiesSettings().qmlDebugging()); } void QmlDebuggingAspect::addToLayout(Layouting::LayoutItem &parent) @@ -59,12 +59,22 @@ void QmlDebuggingAspect::addToLayout(Layouting::LayoutItem &parent) changeHandler(); } -QtQuickCompilerAspect::QtQuickCompilerAspect(BuildConfiguration *buildConfig) - : m_buildConfig(buildConfig) +void QmlDebuggingAspect::setBuildConfiguration(const BuildConfiguration *buildConfig) +{ + m_buildConfig = buildConfig; +} + +QtQuickCompilerAspect::QtQuickCompilerAspect(AspectContainer *container) + : TriStateAspect(container) { setSettingsKey("QtQuickCompiler"); setDisplayName(Tr::tr("Qt Quick Compiler:")); - setValue(ProjectExplorerPlugin::buildPropertiesSettings().qtQuickCompiler.value()); + setValue(buildPropertiesSettings().qtQuickCompiler()); +} + +void QtQuickCompilerAspect::setBuildConfiguration(const BuildConfiguration *buildConfig) +{ + m_buildConfig = buildConfig; } void QtQuickCompilerAspect::addToLayout(Layouting::LayoutItem &parent) diff --git a/src/plugins/qtsupport/qtbuildaspects.h b/src/plugins/qtsupport/qtbuildaspects.h index e5e0b3332e6..873daf000d2 100644 --- a/src/plugins/qtsupport/qtbuildaspects.h +++ b/src/plugins/qtsupport/qtbuildaspects.h @@ -16,11 +16,13 @@ class QTSUPPORT_EXPORT QmlDebuggingAspect : public Utils::TriStateAspect Q_OBJECT public: - explicit QmlDebuggingAspect(ProjectExplorer::BuildConfiguration *buildConfig); + explicit QmlDebuggingAspect(Utils::AspectContainer *container = nullptr); - void addToLayout(Layouting::LayoutItem &parent) override; + void setBuildConfiguration(const ProjectExplorer::BuildConfiguration *newBuildConfig); private: + void addToLayout(Layouting::LayoutItem &parent) override; + const ProjectExplorer::BuildConfiguration *m_buildConfig = nullptr; }; @@ -29,7 +31,9 @@ class QTSUPPORT_EXPORT QtQuickCompilerAspect : public Utils::TriStateAspect Q_OBJECT public: - QtQuickCompilerAspect(ProjectExplorer::BuildConfiguration *buildConfig); + QtQuickCompilerAspect(Utils::AspectContainer *container = nullptr); + + void setBuildConfiguration(const ProjectExplorer::BuildConfiguration *newBuildConfig); private: void addToLayout(Layouting::LayoutItem &parent) override; diff --git a/src/plugins/qtsupport/qtcppkitinfo.cpp b/src/plugins/qtsupport/qtcppkitinfo.cpp index 77caf01766d..6900122cad5 100644 --- a/src/plugins/qtsupport/qtcppkitinfo.cpp +++ b/src/plugins/qtsupport/qtcppkitinfo.cpp @@ -4,7 +4,7 @@ #include "qtcppkitinfo.h" #include "baseqtversion.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" namespace QtSupport { diff --git a/src/plugins/qtsupport/qtcreator_tutorials.xml b/src/plugins/qtsupport/qtcreator_tutorials.xml index 5f56f7501b7..af657454432 100644 --- a/src/plugins/qtsupport/qtcreator_tutorials.xml +++ b/src/plugins/qtsupport/qtcreator_tutorials.xml @@ -1,196 +1,343 @@ <?xml version="1.0" encoding="utf-8"?> <instructionals module="Qt"> + <categories> + <category>Help</category> + <category>Learning</category> + <category>Online</category> + <category>Talk</category> + </categories> <tutorials> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/creator-build-example-application.html" projectPath="" name="Help: Building and Running an Example"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/creator-build-example-application.html" projectPath="" name="Building and Running an Example"> <description><![CDATA[Testing that your installation is successful by opening an existing example application project.]]></description> <tags>qt creator,build,compile,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/creator-writing-program.html" projectPath="" name="Help: Creating a Qt Widget-Based Application"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/creator-writing-program.html" projectPath="" name="Creating a Qt Widget-Based Application"> <description><![CDATA[Using Qt Creator to create a small Qt application, Text Finder.]]></description> <tags>qt creator,qt designer,widgets,c++,text,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtdoc/qtwidgets/qtwidgets-tutorials-notepad-example.html" projectPath="" name="Help: Getting Started Programming with Qt Widgets"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtdoc/qtwidgets/qtwidgets-tutorials-notepad-example.html" projectPath="" name="Getting Started Programming with Qt Widgets"> <description><![CDATA[Developing Qt applications using C++ and the Qt Widgets module.]]></description> <tags>qt,qt creator,qt designer,widgets,c++,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/qtcreator-transitions-example.html" projectPath="" name="Help: Creating a Qt Quick Application"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/qtcreator-transitions-example.html" projectPath="" name="Creating a Qt Quick Application"> <description><![CDATA[Using basic QML types and learning about basic concepts of Qt Quick.]]></description> <tags>qt creator,qt quick,qml,states,transitions,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/qtcreator-accelbubble-example.html" projectPath="" name="Help: Creating a Mobile Qt Application"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/qtcreator-accelbubble-example.html" projectPath="" name="Creating a Mobile Qt Application"> <description><![CDATA[Developing Qt Quick applications for Android and iOS devices.]]></description> <tags>qt creator,qml,android,ios,controls,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtdoc/qtdoc/qtdoc-tutorials-alarms-example.html" projectPath="" name="Help: Getting Started Programming with Qt Quick"> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtdoc/qtdoc/qtdoc-tutorials-alarms-example.html" projectPath="" name="Getting Started Programming with Qt Quick"> <description><![CDATA[Developing Qt Quick applications using Qt Quick and Qt Quick Controls.]]></description> <tags>qt quick,controls,tumbler,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubehOx3dod5-1A.webp" difficulty="" projectPath="" name="Learning: Qt Creator - UI Tour and the Welcome screen" isVideo="true" videoUrl="https://www.youtube.com/watch?v=hOx3dod5-1A" videoLength="3:21"> + <tutorial imageUrl=":qtsupport/images/icons/youtubehOx3dod5-1A.webp" difficulty="" projectPath="" name="Qt Creator - UI Tour and the Welcome screen" isVideo="true" videoUrl="https://www.youtube.com/watch?v=hOx3dod5-1A" videoLength="3:21"> <description><![CDATA[The parts of the Qt Creator UI and the Welcome mode.]]></description> <tags>qt creator,learning,ui,welcome,2023</tags> + <meta> + <entry name="category">Learning</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeFTSvDmcAgPI.webp" difficulty="" projectPath="" name="Learning: Qt Creator - Create a new project" isVideo="true" videoUrl="https://www.youtube.com/watch?v=FTSvDmcAgPI" videoLength="3:21"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeFTSvDmcAgPI.webp" difficulty="" projectPath="" name="Qt Creator - Create a new project" isVideo="true" videoUrl="https://www.youtube.com/watch?v=FTSvDmcAgPI" videoLength="3:21"> <description><![CDATA[Creating a Qt Widgets application project.]]></description> <tags>qt creator,learning,projects,qt widgets,2023</tags> + <meta> + <entry name="category">Learning</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeLnVjI0I7cKs.webp" difficulty="" projectPath="" name="Learning: Qt Creator - Design and Edit Modes" isVideo="true" videoUrl="https://www.youtube.com/watch?v=LnVjI0I7cKs" videoLength="4:34"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeLnVjI0I7cKs.webp" difficulty="" projectPath="" name="Qt Creator - Design and Edit Modes" isVideo="true" videoUrl="https://www.youtube.com/watch?v=LnVjI0I7cKs" videoLength="4:34"> <description><![CDATA[Developing a Qt Widgets application.]]></description> <tags>qt creator,learning,qt designer,coding,2023</tags> + <meta> + <entry name="category">Learning</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeEhJ1eV_6RH8.webp" difficulty="" projectPath="" name="Learning: Qt Creator - Debugging" isVideo="true" videoUrl="https://www.youtube.com/watch?v=EhJ1eV_6RH8" videoLength="3:22"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeEhJ1eV_6RH8.webp" difficulty="" projectPath="" name="Qt Creator - Debugging" isVideo="true" videoUrl="https://www.youtube.com/watch?v=EhJ1eV_6RH8" videoLength="3:22"> <description><![CDATA[Debugging a Qt Widgets application.]]></description> <tags>qt creator,learning,debugging,2023</tags> + <meta> + <entry name="category">Learning</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube1tSpq5OLkYI.webp" difficulty="" projectPath="" name="Online: How to set up and deploy an application using Qt for Device Creation" isVideo="true" videoUrl="https://youtu.be/1tSpq5OLkYI" videoLength="5:48"> + <tutorial imageUrl=":qtsupport/images/icons/youtube1tSpq5OLkYI.webp" difficulty="" projectPath="" name="How to set up and deploy an application using Qt for Device Creation" isVideo="true" videoUrl="https://youtu.be/1tSpq5OLkYI" videoLength="5:48"> <description><![CDATA[Using Qt Creator to deploy applications to an embedded device.]]></description> <tags>qt creator,embedded,device creation,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube9xqhq9nDiOg.webp" difficulty="" projectPath="" name="Online: Qt SCXML and State Machine Tooling in Qt Creator" isVideo="true" videoUrl="https://youtu.be/9xqhq9nDiOg" videoLength="4:53"> + <tutorial imageUrl=":qtsupport/images/icons/youtube9xqhq9nDiOg.webp" difficulty="" projectPath="" name="Qt SCXML and State Machine Tooling in Qt Creator" isVideo="true" videoUrl="https://youtu.be/9xqhq9nDiOg" videoLength="4:53"> <description><![CDATA[Creating state machines.]]></description> <tags>qt creator,SCXML,video</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube9BcAYDlpuT8.webp" difficulty="" projectPath="" name="Online: Using C++ Models in QML - To-do List" isVideo="true" videoUrl="https://www.youtube.com/watch?v=9BcAYDlpuT8" videoLength="49:48"> + <tutorial imageUrl=":qtsupport/images/icons/youtube9BcAYDlpuT8.webp" difficulty="" projectPath="" name="Using C++ Models in QML - To-do List" isVideo="true" videoUrl="https://www.youtube.com/watch?v=9BcAYDlpuT8" videoLength="49:48"> <description><![CDATA[Creating and using a C++ model in QML.]]></description> <tags>qt creator,qt quick,c++,video</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubezAqSiIGdj8M.webp" difficulty="" projectPath="" name="Online: Qt Creator - Meet Qt Creator" isVideo="true" videoUrl="https://youtu.be/zAqSiIGdj8M" videoLength="2:06"> + <tutorial imageUrl=":qtsupport/images/icons/youtubezAqSiIGdj8M.webp" difficulty="" projectPath="" name="Qt Creator - Meet Qt Creator" isVideo="true" videoUrl="https://youtu.be/zAqSiIGdj8M" videoLength="2:06"> <description><![CDATA[Overview of Qt Creator.]]></description> <tags>qt creator,video</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeR6zWLfHIYJw.webp" difficulty="" projectPath="" name="Online: Qt Creator - Examples" isVideo="true" videoUrl="https://www.youtube.com/watch?v=R6zWLfHIYJw" videoLength="9:29"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeR6zWLfHIYJw.webp" difficulty="" projectPath="" name="Qt Creator - Examples" isVideo="true" videoUrl="https://www.youtube.com/watch?v=R6zWLfHIYJw" videoLength="9:29"> <description><![CDATA[Using Qt Creator tutorials and examples to develop Qt applications.]]></description> <tags>qt creator,video,2018</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeuuhmSZxK1mk.webp" difficulty="" projectPath="" name="Online: Qt Creator - Introduction to Qt Quick Controls" isVideo="true" videoUrl="https://www.youtube.com/watch?v=uuhmSZxK1mk" videoLength="7:09"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeuuhmSZxK1mk.webp" difficulty="" projectPath="" name="Qt Creator - Introduction to Qt Quick Controls" isVideo="true" videoUrl="https://www.youtube.com/watch?v=uuhmSZxK1mk" videoLength="7:09"> <description><![CDATA[Using Qt Quick Controls to develop Qt Quick applications.]]></description> <tags>qt creator,qt quick,controls,video,2018</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeY-MM-9FigTc.webp" difficulty="" projectPath="" name="Online: Debugging inside Qt Creator" isVideo="true" videoUrl="https://youtu.be/Y-MM-9FigTc" videoLength="21:54"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeY-MM-9FigTc.webp" difficulty="" projectPath="" name="Debugging inside Qt Creator" isVideo="true" videoUrl="https://youtu.be/Y-MM-9FigTc" videoLength="21:54"> <description><![CDATA[Debugging applications in Qt Creator.]]></description> - <tags>qt creator,debugging,2021</tags> + <tags>qt creator,debugging,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubexNIz78IPBu0.webp" difficulty="" projectPath="" name="Online: How to do translations with Qt Linguist" isVideo="true" videoUrl="https://youtu.be/xNIz78IPBu0" videoLength="9:14"> + <tutorial imageUrl=":qtsupport/images/icons/youtubexNIz78IPBu0.webp" difficulty="" projectPath="" name="How to do translations with Qt Linguist" isVideo="true" videoUrl="https://youtu.be/xNIz78IPBu0" videoLength="9:14"> <description><![CDATA[Preparing applications for translation, translating them with Qt Linguist, and using the translations in apps.]]></description> - <tags>qt creator,qt linguist,translation,2021</tags> + <tags>qt creator,qt linguist,translation,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube-xhiVA0P4yk.webp" difficulty="" projectPath="" name="Online: Qt Windows Online Installer walkthrough" isVideo="true" videoUrl="https://youtu.be/-xhiVA0P4yk" videoLength="6:17"> + <tutorial imageUrl=":qtsupport/images/icons/youtube-xhiVA0P4yk.webp" difficulty="" projectPath="" name="Qt Windows Online Installer walkthrough" isVideo="true" videoUrl="https://youtu.be/-xhiVA0P4yk" videoLength="6:17"> <description><![CDATA[Downloading and installing Qt with the options that you want.]]></description> <tags>qt,installation,online installer,modules,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeYJfFwDBOvqk.webp" difficulty="" projectPath="" name="Online: How to install and set up Qt for Device Creation on Linux" isVideo="true" videoUrl="https://youtu.be/YJfFwDBOvqk" videoLength="6:52"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeYJfFwDBOvqk.webp" difficulty="" projectPath="" name="How to install and set up Qt for Device Creation on Linux" isVideo="true" videoUrl="https://youtu.be/YJfFwDBOvqk" videoLength="6:52"> <description><![CDATA[Downloading and installing Qt Device Creation with the options that you want.]]></description> - <tags>embedded,installation,device creation,2021</tags> + <tags>embedded,installation,device creation,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubejbx3Oq1Q4gY.webp" difficulty="" projectPath="" name="Online: Creating a simple widget app" isVideo="true" videoUrl="https://youtu.be/jbx3Oq1Q4gY" videoLength="6:08"> + <tutorial imageUrl=":qtsupport/images/icons/youtubejbx3Oq1Q4gY.webp" difficulty="" projectPath="" name="Creating a simple widget app" isVideo="true" videoUrl="https://youtu.be/jbx3Oq1Q4gY" videoLength="6:08"> <description><![CDATA[Creating a simple widget-based application and running it on your development machine using Qt Creator 5.0.]]></description> - <tags>qt creator,widgets,2021</tags> + <tags>qt creator,widgets,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube2RvhhEAZQxM.webp" difficulty="" projectPath="" name="Online: Qt Widgets or Qt Quick" isVideo="true" videoUrl="https://youtu.be/2RvhhEAZQxM" videoLength="5:00"> + <tutorial imageUrl=":qtsupport/images/icons/youtube2RvhhEAZQxM.webp" difficulty="" projectPath="" name="Qt Widgets or Qt Quick" isVideo="true" videoUrl="https://youtu.be/2RvhhEAZQxM" videoLength="5:00"> <description><![CDATA[Learning the differences between Qt Widgets and Qt Quick and making the right choice for your application needs.]]></description> <tags>qt quick,widgets,ui,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeDRFz0Tll4G8.webp" difficulty="" projectPath="" name="Online: How to install and set up Qt for MCUs" isVideo="true" videoUrl="https://youtu.be/DRFz0Tll4G8" videoLength="8:29"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeDRFz0Tll4G8.webp" difficulty="" projectPath="" name="How to install and set up Qt for MCUs" isVideo="true" videoUrl="https://youtu.be/DRFz0Tll4G8" videoLength="8:29"> <description><![CDATA[Downloading and installing Qt for MCUs with the options that you want.]]></description> <tags>qt,mcus,video,STM32H750B-DISCOVERY,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeBkgjJfxYN20.webp" difficulty="" projectPath="" name="Online: How to build your first 'Qt for MCUs' application" isVideo="true" videoUrl="https://youtu.be/BkgjJfxYN20" videoLength="21:54"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeBkgjJfxYN20.webp" difficulty="" projectPath="" name="How to build your first 'Qt for MCUs' application" isVideo="true" videoUrl="https://youtu.be/BkgjJfxYN20" videoLength="21:54"> <description><![CDATA[Building your first application for the NXP IMXRT1050 device.]]></description> <tags>qtformcus,mcus,qt,video,NXP IMXRT1050-EVKB,2020</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubemn-JmXIMCqk.webp" difficulty="" projectPath="" name="Online: How to create a simple application with Qt for MCUs" isVideo="true" videoUrl="https://youtu.be/mn-JmXIMCqk" videoLength="5:16"> + <tutorial imageUrl=":qtsupport/images/icons/youtubemn-JmXIMCqk.webp" difficulty="" projectPath="" name="How to create a simple application with Qt for MCUs" isVideo="true" videoUrl="https://youtu.be/mn-JmXIMCqk" videoLength="5:16"> <description><![CDATA[Creating a simple Qt for MCUs application and running it on your development machine using Qt Creator 5.0.]]></description> <tags>qtformcus,mcus,qt,video,2021</tags> + <meta> + <entry name="category">Online</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeKo3DuCgFamo.webp" difficulty="" projectPath="" name="Talk: Custom Qt Creator Wizards" isVideo="true" videoUrl="https://www.youtube.com/watch?v=Ko3DuCgFamo" videoLength="27:21"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeKo3DuCgFamo.webp" difficulty="" projectPath="" name="Custom Qt Creator Wizards" isVideo="true" videoUrl="https://www.youtube.com/watch?v=Ko3DuCgFamo" videoLength="27:21"> <description><![CDATA[Adding custom file and project creation wizards to Qt Creator.]]></description> <tags>qt creator,wizard,talk,2015</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeDP0lMoLVneY.webp" difficulty="" projectPath="" name="Talk: Extending Qt Creator" isVideo="true" videoUrl="http://www.youtube.com/watch?v=DP0lMoLVneY" videoLength="59:49"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeDP0lMoLVneY.webp" difficulty="" projectPath="" name="Extending Qt Creator" isVideo="true" videoUrl="http://www.youtube.com/watch?v=DP0lMoLVneY" videoLength="59:49"> <description><![CDATA[Customizing Qt Creator to fit your own or your customers' purposes.]]></description> <tags>qt creator,configuration,talk,2013</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubePzV2MYRAUYQ.webp" difficulty="" projectPath="" name="Talk: How to create a plugin for Qt Creator" isVideo="true" videoUrl="https://youtu.be/PzV2MYRAUYQ" videoLength="55:37"> + <tutorial imageUrl=":qtsupport/images/icons/youtubePzV2MYRAUYQ.webp" difficulty="" projectPath="" name="How to create a plugin for Qt Creator" isVideo="true" videoUrl="https://youtu.be/PzV2MYRAUYQ" videoLength="55:37"> <description><![CDATA[Adding plugins to Qt Creator.]]></description> <tags>qt creator,plugins,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeTiJiF0MOOFc.webp" difficulty="" projectPath="" name="Talk: Qt Creator - Using the QML Profiler" isVideo="true" videoUrl="https://www.youtube.com/watch?v=TiJiF0MOOFc" videoLength="55:12"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeTiJiF0MOOFc.webp" difficulty="" projectPath="" name="Qt Creator - Using the QML Profiler" isVideo="true" videoUrl="https://www.youtube.com/watch?v=TiJiF0MOOFc" videoLength="55:12"> <description><![CDATA[Monitoring the performance of a Qt Quick application.]]></description> - <tags>qt quick,qt creator,qml profiler,talk,2014</tags> + <tags>qt quick,qt creator,qml profiler,talk,2014</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeG0AbgVHGdXI.webp" difficulty="" projectPath="" name="Talk: The CPU Usage Analyzer for Device Creation" isVideo="true" videoUrl="https://www.youtube.com/watch?v=G0AbgVHGdXI" videoLength="22:30"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeG0AbgVHGdXI.webp" difficulty="" projectPath="" name="The CPU Usage Analyzer for Device Creation" isVideo="true" videoUrl="https://www.youtube.com/watch?v=G0AbgVHGdXI" videoLength="22:30"> <description><![CDATA[Using the Linux perf tool to generate data for code analysis.]]></description> <tags>qt creator,cpu usage analyzer,perf,embedded,device creation,talk,2015</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeX0kEkB0ewyw.webp" difficulty="" projectPath="" name="Talk: Qt SCXML - State Machines Made Easier" isVideo="true" videoUrl="https://youtu.be/X0kEkB0ewyw" videoLength="42:22"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeX0kEkB0ewyw.webp" difficulty="" projectPath="" name="Qt SCXML - State Machines Made Easier" isVideo="true" videoUrl="https://youtu.be/X0kEkB0ewyw" videoLength="42:22"> <description><![CDATA[Using the Qt SCXML module and Qt Creator SCXML editor.]]></description> <tags>qt creator,scxml,talk,2016</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubev4glCQt2jE0.webp" difficulty="" projectPath="" name="Talk: Effective Multi-Platform Development with Qt Creator, QBS, and QEMU" isVideo="true" videoUrl="https://www.youtube.com/watch?v=v4glCQt2jE0" videoLength="19:08"> + <tutorial imageUrl=":qtsupport/images/icons/youtubev4glCQt2jE0.webp" difficulty="" projectPath="" name="Effective Multi-Platform Development with Qt Creator, QBS, and QEMU" isVideo="true" videoUrl="https://www.youtube.com/watch?v=v4glCQt2jE0" videoLength="19:08"> <description><![CDATA[Using Qt Creator, Qbs, and QEMU for application development.]]></description> <tags>qt creator,qbs,qemu,talk,2015</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeT_13aX5NTPk.webp" difficulty="" projectPath="" name="Talk: Qt for iOS - A to Z" isVideo="true" videoUrl="https://youtu.be/T_13aX5NTPk" videoLength="1:00:13"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeT_13aX5NTPk.webp" difficulty="" projectPath="" name="Qt for iOS - A to Z" isVideo="true" videoUrl="https://youtu.be/T_13aX5NTPk" videoLength="1:00:13"> <description><![CDATA[Developing Qt applications for iOS.]]></description> <tags>qt creator,ios,talk,2016</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubehrKz63Q_Rf0.webp" difficulty="" projectPath="" name="Talk: Qt Creator for Bare Metal Development" isVideo="true" videoUrl="http://www.youtube.com/watch?v=hrKz63Q_Rf0" videoLength="9:35"> + <tutorial imageUrl=":qtsupport/images/icons/youtubehrKz63Q_Rf0.webp" difficulty="" projectPath="" name="Qt Creator for Bare Metal Development" isVideo="true" videoUrl="http://www.youtube.com/watch?v=hrKz63Q_Rf0" videoLength="9:35"> <description><![CDATA[Developing Qt Applications for Bare Metal devices.]]></description> <tags>qt creator,baremetal,talk,2013</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeWIRRoPxIerc.webp" difficulty="" projectPath="" name="Talk: The Curse of Choice - An Overview of GUI Technologies in Qt" isVideo="true" videoUrl="https://youtu.be/WIRRoPxIerc" videoLength="40:45"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeWIRRoPxIerc.webp" difficulty="" projectPath="" name="The Curse of Choice - An Overview of GUI Technologies in Qt" isVideo="true" videoUrl="https://youtu.be/WIRRoPxIerc" videoLength="40:45"> <description><![CDATA[Overview of UI technologies that can be used with Qt.]]></description> <tags>qt quick,ui,widgets,talk,2016</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubesRihJdZFuCg.webp" difficulty="" projectPath="" name="Talk: Code Once Deploy Everywhere: How Qt is ideal for cross-platform development" isVideo="true" videoUrl="https://youtu.be/sRihJdZFuCg" videoLength="42:37"> + <tutorial imageUrl=":qtsupport/images/icons/youtubesRihJdZFuCg.webp" difficulty="" projectPath="" name="Code Once Deploy Everywhere: How Qt is ideal for cross-platform development" isVideo="true" videoUrl="https://youtu.be/sRihJdZFuCg" videoLength="42:37"> <description><![CDATA[Using Qt Creator for cross-platform development.]]></description> <tags>qt creator,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeW3WC-VpKdGQ.webp" difficulty="" projectPath="" name="Talk: WEBASM with Qt - Qt for WebAssembly" isVideo="true" videoUrl="https://youtu.be/W3WC-VpKdGQ" videoLength="27:50"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeW3WC-VpKdGQ.webp" difficulty="" projectPath="" name="WEBASM with Qt - Qt for WebAssembly" isVideo="true" videoUrl="https://youtu.be/W3WC-VpKdGQ" videoLength="27:50"> <description><![CDATA[Running Qt applications on the Web using Qt for WebAssembly.]]></description> <tags>qt creator,webassembly,emscripten,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeqclquZ99ZVQ.webp" difficulty="" projectPath="" name="Talk: How to Develop with Qt for Multiple Screen Resolutions and Platforms and Best Practices for an Efficient App Lifecycle with Qt" isVideo="true" videoUrl="https://youtu.be/qclquZ99ZVQ" videoLength="27:44"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeqclquZ99ZVQ.webp" difficulty="" projectPath="" name="How to Develop with Qt for Multiple Screen Resolutions and Platforms and Best Practices for an Efficient App Lifecycle with Qt" isVideo="true" videoUrl="https://youtu.be/qclquZ99ZVQ" videoLength="27:44"> <description><![CDATA[Best practices for an efficient app lifecycle.]]></description> <tags>qt,qt quick,screen resolution,ui,talk,2016</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeB0X5FOev9Lw.webp" difficulty="" projectPath="" name="Talk: Qt Designer tutorial: Integrate custom widgets" isVideo="true" videoUrl="https://youtu.be/B0X5FOev9Lw" videoLength="27:07"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeB0X5FOev9Lw.webp" difficulty="" projectPath="" name="Qt Designer tutorial: Integrate custom widgets" isVideo="true" videoUrl="https://youtu.be/B0X5FOev9Lw" videoLength="27:07"> <description><![CDATA[Integrating custom widgets into Qt Designer.]]></description> <tags>qt designer,widgets,ui,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube3o2Wo4YzlII.webp" difficulty="" projectPath="" name="Talk: Android & iOS - Put Your App on a Diet" isVideo="true" videoUrl="https://www.youtube.com/watch?v=3o2Wo4YzlII" videoLength="23:41"> + <tutorial imageUrl=":qtsupport/images/icons/youtube3o2Wo4YzlII.webp" difficulty="" projectPath="" name="Android & iOS - Put Your App on a Diet" isVideo="true" videoUrl="https://www.youtube.com/watch?v=3o2Wo4YzlII" videoLength="23:41"> <description><![CDATA[Making Android and iOS apps smaller.]]></description> <tags>android,ios,talk,2017</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubev_ynSET9FHU.webp" difficulty="" projectPath="" name="Talk: LTTng for full stack tracing" isVideo="true" videoUrl="https://youtu.be/v_ynSET9FHU" videoLength="25:46"> + <tutorial imageUrl=":qtsupport/images/icons/youtubev_ynSET9FHU.webp" difficulty="" projectPath="" name="LTTng for full stack tracing" isVideo="true" videoUrl="https://youtu.be/v_ynSET9FHU" videoLength="25:46"> <description><![CDATA[Using tracing and profiling to optimize the startup time of apps.]]></description> <tags>qt creator,qml profiler,ctf viewer,lttng,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeau3brB7lNms.webp" difficulty="" projectPath="" name="Talk: No Limits - How to Make a More Complicated Mobile Business App" isVideo="true" videoUrl="https://www.youtube.com/watch?v=au3brB7lNms" videoLength="23:33"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeau3brB7lNms.webp" difficulty="" projectPath="" name="No Limits - How to Make a More Complicated Mobile Business App" isVideo="true" videoUrl="https://www.youtube.com/watch?v=au3brB7lNms" videoLength="23:33"> <description><![CDATA[Creating mobile business apps using Qt Quick Controls 2.]]></description> <tags>android,ios,qt quick,controls,talk,2017</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubeECA8_oLT0ZE.webp" difficulty="" projectPath="" name="Talk: Qt & Yocto, an ECU development workflow" isVideo="true" videoUrl="https://youtu.be/ECA8_oLT0ZE" videoLength="29:08"> + <tutorial imageUrl=":qtsupport/images/icons/youtubeECA8_oLT0ZE.webp" difficulty="" projectPath="" name="Qt & Yocto, an ECU development workflow" isVideo="true" videoUrl="https://youtu.be/ECA8_oLT0ZE" videoLength="29:08"> <description><![CDATA[Using Qt Creator kits and Yocto when developing for embedded devices.]]></description> <tags>qt creator,kits,yocto,embedded,talk,2019</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtube1w0ak9RNNWY.webp" difficulty="" projectPath="" name="Talk: Qt Creator in Space" isVideo="true" videoUrl="https://youtu.be/1w0ak9RNNWY" videoLength="28:05"> + <tutorial imageUrl=":qtsupport/images/icons/youtube1w0ak9RNNWY.webp" difficulty="" projectPath="" name="Qt Creator in Space" isVideo="true" videoUrl="https://youtu.be/1w0ak9RNNWY" videoLength="28:05"> <description><![CDATA[Creating and maintaining a portfolio of Qt Creator plugins.]]></description> <tags>qt creator,plugins,video,2021</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubenmvurCcsWos.webp" difficulty="" projectPath="" name="Talk: All You Need to Get Your App Done with Qt for Android" isVideo="true" videoUrl="https://youtu.be/nmvurCcsWos" videoLength="24:11"> + <tutorial imageUrl=":qtsupport/images/icons/youtubenmvurCcsWos.webp" difficulty="" projectPath="" name="All You Need to Get Your App Done with Qt for Android" isVideo="true" videoUrl="https://youtu.be/nmvurCcsWos" videoLength="24:11"> <description><![CDATA[Developing an Android app using Qt for Android.]]></description> <tags>android,talk,2021</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubetnZo9umrPtg.webp" difficulty="" projectPath="" name="Talk: Styling a Qt Quick Controls Desktop Application" isVideo="true" videoUrl="https://youtu.be/tnZo9umrPtg" videoLength="29:40"> + <tutorial imageUrl=":qtsupport/images/icons/youtubetnZo9umrPtg.webp" difficulty="" projectPath="" name="Styling a Qt Quick Controls Desktop Application" isVideo="true" videoUrl="https://youtu.be/tnZo9umrPtg" videoLength="29:40"> <description><![CDATA[Styling Qt Quick Controls using the styling API.]]></description> <tags>qt quick,controls,styling,ui,talk,2021</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> - <tutorial imageUrl=":qtsupport/images/icons/youtubepN0pRBUqrrc.webp" difficulty="" projectPath="" name="Talk: The New Property Bindings: Making C++ more QMLish" isVideo="true" videoUrl="https://youtu.be/pN0pRBUqrrc" videoLength="29:54"> + <tutorial imageUrl=":qtsupport/images/icons/youtubepN0pRBUqrrc.webp" difficulty="" projectPath="" name="The New Property Bindings: Making C++ more QMLish" isVideo="true" videoUrl="https://youtu.be/pN0pRBUqrrc" videoLength="29:54"> <description><![CDATA[Using the Qt 6 property system in pure C++ and mixed C++/QML applications.]]></description> <tags>qt,c++,qml,talk,2021</tags> + <meta> + <entry name="category">Talk</entry> + </meta> </tutorial> </tutorials> </instructionals> diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitaspect.cpp similarity index 80% rename from src/plugins/qtsupport/qtkitinformation.cpp rename to src/plugins/qtsupport/qtkitaspect.cpp index fe773f4995e..d6fa4a21225 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitaspect.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include "qtparser.h" #include "qtsupportconstants.h" @@ -30,10 +30,10 @@ using namespace Utils; namespace QtSupport { namespace Internal { -class QtKitAspectWidget final : public KitAspectWidget +class QtKitAspectImpl final : public KitAspect { public: - QtKitAspectWidget(Kit *k, const KitAspect *ki) : KitAspectWidget(k, ki) + QtKitAspectImpl(Kit *k, const KitAspectFactory *ki) : KitAspect(k, ki) { m_combo = createSubWidget<QComboBox>(); m_combo->setSizePolicy(QSizePolicy::Ignored, m_combo->sizePolicy().verticalPolicy()); @@ -51,10 +51,10 @@ public: connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged, this, - &QtKitAspectWidget::refresh); + &QtKitAspectImpl::refresh); } - ~QtKitAspectWidget() final + ~QtKitAspectImpl() final { delete m_combo; delete m_manageButton; @@ -63,7 +63,7 @@ public: private: void makeReadOnly() final { m_combo->setEnabled(false); } - void addToLayout(Layouting::LayoutItem &parent) + void addToLayoutImpl(Layouting::LayoutItem &parent) { addMutableAction(m_combo); parent.addItem(m_combo); @@ -130,21 +130,51 @@ private: }; } // namespace Internal -QtKitAspect::QtKitAspect() +class QtKitAspectFactory : public KitAspectFactory +{ +public: + QtKitAspectFactory(); + +private: + void setup(Kit *k) override; + + Tasks validate(const Kit *k) const override; + void fix(Kit *) override; + + KitAspect *createKitAspect(Kit *k) const override; + + QString displayNamePostfix(const Kit *k) const override; + + ItemList toUserOutput(const Kit *k) const override; + + void addToBuildEnvironment(const Kit *k, Environment &env) const override; + QList<OutputLineParser *> createOutputParsers(const Kit *k) const override; + void addToMacroExpander(Kit *kit, MacroExpander *expander) const override; + + QSet<Id> supportedPlatforms(const Kit *k) const override; + QSet<Id> availableFeatures(const Kit *k) const override; + + int weight(const Kit *k) const override; + + void qtVersionsChanged(const QList<int> &addedIds, + const QList<int> &removedIds, + const QList<int> &changedIds); + void onKitsLoaded() override; +}; + +const QtKitAspectFactory theQtKitAspectFactory; + +QtKitAspectFactory::QtKitAspectFactory() { - setObjectName(QLatin1String("QtKitAspect")); setId(QtKitAspect::id()); setDisplayName(Tr::tr("Qt version")); setDescription(Tr::tr("The Qt library to use for all projects using this kit.<br>" "A Qt version is required for qmake-based projects " "and optional when using other build systems.")); setPriority(26000); - - connect(KitManager::instance(), &KitManager::kitsLoaded, - this, &QtKitAspect::kitsWereLoaded); } -void QtKitAspect::setup(Kit *k) +void QtKitAspectFactory::setup(Kit *k) { if (!k || k->hasValue(id())) return; @@ -175,25 +205,25 @@ void QtKitAspect::setup(Kit *k) k->setValue(id(), candidates.first()->uniqueId()); } -Tasks QtKitAspect::validate(const Kit *k) const +Tasks QtKitAspectFactory::validate(const Kit *k) const { QTC_ASSERT(QtVersionManager::isLoaded(), return {}); - QtVersion *version = qtVersion(k); + QtVersion *version = QtKitAspect::qtVersion(k); if (!version) return {}; return version->validateKit(k); } -void QtKitAspect::fix(Kit *k) +void QtKitAspectFactory::fix(Kit *k) { QTC_ASSERT(QtVersionManager::isLoaded(), return); - QtVersion *version = qtVersion(k); + QtVersion *version = QtKitAspect::qtVersion(k); if (!version) { - if (qtVersionId(k) >= 0) { + if (QtKitAspect::qtVersionId(k) >= 0) { qWarning("Qt version is no longer known, removing from kit \"%s\".", qPrintable(k->displayName())); - setQtVersionId(k, -1); + QtKitAspect::setQtVersionId(k, -1); } return; } @@ -260,34 +290,34 @@ void QtKitAspect::fix(Kit *k) } } -KitAspectWidget *QtKitAspect::createConfigWidget(Kit *k) const +KitAspect *QtKitAspectFactory::createKitAspect(Kit *k) const { QTC_ASSERT(k, return nullptr); - return new Internal::QtKitAspectWidget(k, this); + return new Internal::QtKitAspectImpl(k, this); } -QString QtKitAspect::displayNamePostfix(const Kit *k) const +QString QtKitAspectFactory::displayNamePostfix(const Kit *k) const { - QtVersion *version = qtVersion(k); + QtVersion *version = QtKitAspect::qtVersion(k); return version ? version->displayName() : QString(); } -KitAspect::ItemList QtKitAspect::toUserOutput(const Kit *k) const +KitAspectFactory::ItemList QtKitAspectFactory::toUserOutput(const Kit *k) const { - QtVersion *version = qtVersion(k); + QtVersion *version = QtKitAspect::qtVersion(k); return {{Tr::tr("Qt version"), version ? version->displayName() : Tr::tr("None")}}; } -void QtKitAspect::addToBuildEnvironment(const Kit *k, Environment &env) const +void QtKitAspectFactory::addToBuildEnvironment(const Kit *k, Environment &env) const { - QtVersion *version = qtVersion(k); + QtVersion *version = QtKitAspect::qtVersion(k); if (version) version->addToEnvironment(k, env); } -QList<OutputLineParser *> QtKitAspect::createOutputParsers(const Kit *k) const +QList<OutputLineParser *> QtKitAspectFactory::createOutputParsers(const Kit *k) const { - if (qtVersion(k)) + if (QtKitAspect::qtVersion(k)) return {new Internal::QtTestParser, new QtParser}; return {}; } @@ -308,19 +338,19 @@ public: std::shared_ptr<MacroExpander> expander; }; -void QtKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const +void QtKitAspectFactory::addToMacroExpander(Kit *kit, MacroExpander *expander) const { QTC_ASSERT(kit, return); expander->registerSubProvider(QtMacroSubProvider(kit)); expander->registerVariable("Qt:Name", Tr::tr("Name of Qt Version"), [kit]() -> QString { - QtVersion *version = qtVersion(kit); + QtVersion *version = QtKitAspect::qtVersion(kit); return version ? version->displayName() : Tr::tr("unknown"); }); expander->registerVariable("Qt:qmakeExecutable", Tr::tr("Path to the qmake executable"), [kit]() -> QString { - QtVersion *version = qtVersion(kit); + QtVersion *version = QtKitAspect::qtVersion(kit); return version ? version->qmakeFilePath().path() : QString(); }); } @@ -385,27 +415,27 @@ void QtKitAspect::addHostBinariesToPath(const Kit *k, Environment &env) env.prependOrSetPath(qt->hostBinPath()); } -void QtKitAspect::qtVersionsChanged(const QList<int> &addedIds, - const QList<int> &removedIds, - const QList<int> &changedIds) +void QtKitAspectFactory::qtVersionsChanged(const QList<int> &addedIds, + const QList<int> &removedIds, + const QList<int> &changedIds) { Q_UNUSED(addedIds) Q_UNUSED(removedIds) for (Kit *k : KitManager::kits()) { - if (changedIds.contains(qtVersionId(k))) { + if (changedIds.contains(QtKitAspect::qtVersionId(k))) { k->validate(); // Qt version may have become (in)valid notifyAboutUpdate(k); } } } -void QtKitAspect::kitsWereLoaded() +void QtKitAspectFactory::onKitsLoaded() { for (Kit *k : KitManager::kits()) fix(k); connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged, - this, &QtKitAspect::qtVersionsChanged); + this, &QtKitAspectFactory::qtVersionsChanged); } Kit::Predicate QtKitAspect::platformPredicate(Id platform) @@ -433,21 +463,21 @@ Kit::Predicate QtKitAspect::qtVersionPredicate(const QSet<Id> &required, }; } -QSet<Id> QtKitAspect::supportedPlatforms(const Kit *k) const +QSet<Id> QtKitAspectFactory::supportedPlatforms(const Kit *k) const { QtVersion *version = QtKitAspect::qtVersion(k); return version ? version->targetDeviceTypes() : QSet<Id>(); } -QSet<Id> QtKitAspect::availableFeatures(const Kit *k) const +QSet<Id> QtKitAspectFactory::availableFeatures(const Kit *k) const { QtVersion *version = QtKitAspect::qtVersion(k); return version ? version->features() : QSet<Id>(); } -int QtKitAspect::weight(const Kit *k) const +int QtKitAspectFactory::weight(const Kit *k) const { - const QtVersion * const qt = qtVersion(k); + const QtVersion * const qt = QtKitAspect::qtVersion(k); if (!qt) return 0; if (!qt->targetDeviceTypes().contains(DeviceTypeKitAspect::deviceTypeId(k))) @@ -459,19 +489,4 @@ int QtKitAspect::weight(const Kit *k) const return qtAbi.isCompatibleWith(tcAbi); }) ? 1 : 0; } -Id SuppliesQtQuickImportPath::id() -{ - return QtSupport::Constants::FLAGS_SUPPLIES_QTQUICK_IMPORT_PATH; -} - -Id KitQmlImportPath::id() -{ - return QtSupport::Constants::KIT_QML_IMPORT_PATH; -} - -Id KitHasMergedHeaderPathsWithQmlImportPaths::id() -{ - return QtSupport::Constants::KIT_HAS_MERGED_HEADER_PATHS_WITH_QML_IMPORT_PATHS; -} - } // namespace QtSupport diff --git a/src/plugins/qtsupport/qtkitaspect.h b/src/plugins/qtsupport/qtkitaspect.h new file mode 100644 index 00000000000..f73be247cb2 --- /dev/null +++ b/src/plugins/qtsupport/qtkitaspect.h @@ -0,0 +1,32 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "qtsupport_global.h" + +#include "baseqtversion.h" + +#include <projectexplorer/kitaspects.h> + +namespace QtSupport { + +class QTSUPPORT_EXPORT QtKitAspect +{ +public: + static Utils::Id id(); + static int qtVersionId(const ProjectExplorer::Kit *k); + static void setQtVersionId(ProjectExplorer::Kit *k, const int id); + static QtVersion *qtVersion(const ProjectExplorer::Kit *k); + static void setQtVersion(ProjectExplorer::Kit *k, const QtVersion *v); + + static void addHostBinariesToPath(const ProjectExplorer::Kit *k, Utils::Environment &env); + + static ProjectExplorer::Kit::Predicate platformPredicate(Utils::Id availablePlatforms); + static ProjectExplorer::Kit::Predicate + qtVersionPredicate(const QSet<Utils::Id> &required = QSet<Utils::Id>(), + const QVersionNumber &min = QVersionNumber(0, 0, 0), + const QVersionNumber &max = QVersionNumber(INT_MAX, INT_MAX, INT_MAX)); +}; + +} // namespace QtSupport diff --git a/src/plugins/qtsupport/qtkitinformation.h b/src/plugins/qtsupport/qtkitinformation.h deleted file mode 100644 index c2808514b3f..00000000000 --- a/src/plugins/qtsupport/qtkitinformation.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "qtsupport_global.h" - -#include "baseqtversion.h" - -#include <projectexplorer/kitinformation.h> - -namespace Utils { class MacroExpander; } - -namespace QtSupport { - -class QTSUPPORT_EXPORT QtKitAspect : public ProjectExplorer::KitAspect -{ - Q_OBJECT - -public: - QtKitAspect(); - - void setup(ProjectExplorer::Kit *k) override; - - ProjectExplorer::Tasks validate(const ProjectExplorer::Kit *k) const override; - void fix(ProjectExplorer::Kit *) override; - - ProjectExplorer::KitAspectWidget *createConfigWidget(ProjectExplorer::Kit *k) const override; - - QString displayNamePostfix(const ProjectExplorer::Kit *k) const override; - - ItemList toUserOutput(const ProjectExplorer::Kit *k) const override; - - void addToBuildEnvironment(const ProjectExplorer::Kit *k, Utils::Environment &env) const override; - QList<Utils::OutputLineParser *> createOutputParsers(const ProjectExplorer::Kit *k) const override; - void addToMacroExpander(ProjectExplorer::Kit *kit, Utils::MacroExpander *expander) const override; - - static Utils::Id id(); - static int qtVersionId(const ProjectExplorer::Kit *k); - static void setQtVersionId(ProjectExplorer::Kit *k, const int id); - static QtVersion *qtVersion(const ProjectExplorer::Kit *k); - static void setQtVersion(ProjectExplorer::Kit *k, const QtVersion *v); - - static void addHostBinariesToPath(const ProjectExplorer::Kit *k, Utils::Environment &env); - - static ProjectExplorer::Kit::Predicate platformPredicate(Utils::Id availablePlatforms); - static ProjectExplorer::Kit::Predicate - qtVersionPredicate(const QSet<Utils::Id> &required = QSet<Utils::Id>(), - const QVersionNumber &min = QVersionNumber(0, 0, 0), - const QVersionNumber &max = QVersionNumber(INT_MAX, INT_MAX, INT_MAX)); - - QSet<Utils::Id> supportedPlatforms(const ProjectExplorer::Kit *k) const override; - QSet<Utils::Id> availableFeatures(const ProjectExplorer::Kit *k) const override; - -private: - int weight(const ProjectExplorer::Kit *k) const override; - - void qtVersionsChanged(const QList<int> &addedIds, - const QList<int> &removedIds, - const QList<int> &changedIds); - void kitsWereLoaded(); -}; - -class QTSUPPORT_EXPORT SuppliesQtQuickImportPath -{ -public: - static Utils::Id id(); -}; - -class QTSUPPORT_EXPORT KitQmlImportPath -{ -public: - static Utils::Id id(); -}; - -class QTSUPPORT_EXPORT KitHasMergedHeaderPathsWithQmlImportPaths -{ -public: - static Utils::Id id(); -}; - -} // namespace QtSupport diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index 657420eb780..99a237d7960 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -9,13 +9,12 @@ #include "qtversionmanager.h" #include "qtversionfactory.h" -#include <app/app_version.h> - #include <coreplugin/coreconstants.h> #include <coreplugin/dialogs/restartdialog.h> #include <coreplugin/icore.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <projectexplorer/kitoptionspage.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorericons.h> #include <projectexplorer/toolchain.h> @@ -43,7 +42,6 @@ #include <QLabel> #include <QMessageBox> #include <QPushButton> -#include <QSortFilterProxyModel> #include <QTextBrowser> #include <QTreeView> @@ -54,7 +52,8 @@ using namespace Utils; const char kInstallSettingsKey[] = "Settings/InstallSettings"; -namespace QtSupport::Internal { +namespace QtSupport { +namespace Internal { class QtVersionItem : public TreeItem { @@ -198,7 +197,7 @@ private: void updateVersionItem(QtVersionItem *item); TreeModel<TreeItem, TreeItem, QtVersionItem> *m_model; - QSortFilterProxyModel *m_filterModel; + KitSettingsSortModel *m_filterModel; TreeItem *m_autoItem; TreeItem *m_manualItem; @@ -315,9 +314,10 @@ QtOptionsPageWidget::QtOptionsPageWidget() m_model->rootItem()->appendChild(m_autoItem); m_model->rootItem()->appendChild(m_manualItem); - m_filterModel = new QSortFilterProxyModel(this); + m_filterModel = new KitSettingsSortModel(this); + m_filterModel->setSortedCategories({ProjectExplorer::Constants::msgAutoDetected(), + ProjectExplorer::Constants::msgManual()}); m_filterModel->setSourceModel(m_model); - m_filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_qtdirList->setModel(m_filterModel); m_qtdirList->setSortingEnabled(true); @@ -438,7 +438,15 @@ void QtOptionsPageWidget::toolChainsUpdated() void QtOptionsPageWidget::setInfoWidgetVisibility() { - m_versionInfoWidget->setVisible(m_infoWidget->state() == DetailsWidget::Collapsed); + bool isExpanded = m_infoWidget->state() == DetailsWidget::Expanded; + if (isExpanded && m_infoBrowser->toPlainText().isEmpty()) { + QtVersionItem *item = currentItem(); + const QtVersion *version = item ? item->version() : nullptr; + if (version) + m_infoBrowser->setHtml(version->toHtml(true)); + } + + m_versionInfoWidget->setVisible(!isExpanded); m_infoWidget->setVisible(true); } @@ -533,10 +541,8 @@ QList<ToolChain*> QtOptionsPageWidget::toolChains(const QtVersion *version) for (const Abi &a : abis) { const Toolchains tcList = ToolChainManager::findToolChains(a); for (ToolChain *tc : tcList) { - if (ids.contains(tc->id())) - continue; - ids.insert(tc->id()); - toolChains.append(tc); + if (Utils::insert(ids, tc->id())) + toolChains.append(tc); } } @@ -596,7 +602,7 @@ void QtOptionsPageWidget::updateQtVersions(const QList<int> &additions, const QL // Add changed/added items: for (int a : std::as_const(toAdd)) { - QtVersion *version = QtVersionManager::version(a)->clone(); + QtVersion *version = QtVersionManager::version(a)->clone(true); auto *item = new QtVersionItem(version); // Insert in the right place: @@ -759,11 +765,10 @@ void QtOptionsPageWidget::updateDescriptionLabel() if (item) item->setIcon(info.icon); + m_infoBrowser->clear(); if (version) { - m_infoBrowser->setHtml(version->toHtml(true)); setInfoWidgetVisibility(); } else { - m_infoBrowser->clear(); m_versionInfoWidget->setVisible(false); m_infoWidget->setVisible(false); } @@ -805,20 +810,20 @@ void QtOptionsPageWidget::updateWidgets() static QString settingsFile(const QString &baseDir) { - return baseDir + (baseDir.isEmpty() ? "" : "/") + Core::Constants::IDE_SETTINGSVARIANT_STR + '/' - + Core::Constants::IDE_CASED_ID + ".ini"; + return baseDir + (baseDir.isEmpty() ? "" : "/") + QCoreApplication::organizationName() + '/' + + QCoreApplication::applicationName() + ".ini"; } static QString qtVersionsFile(const QString &baseDir) { - return baseDir + (baseDir.isEmpty() ? "" : "/") + Core::Constants::IDE_SETTINGSVARIANT_STR + '/' - + Core::Constants::IDE_ID + '/' + "qtversion.xml"; + return baseDir + (baseDir.isEmpty() ? "" : "/") + QCoreApplication::organizationName() + '/' + + QCoreApplication::applicationName() + '/' + "qtversion.xml"; } static std::optional<FilePath> currentlyLinkedQtDir(bool *hasInstallSettings) { const QString installSettingsFilePath = settingsFile(Core::ICore::resourcePath().toString()); - const bool installSettingsExist = QFile::exists(installSettingsFilePath); + const bool installSettingsExist = QFileInfo::exists(installSettingsFilePath); if (hasInstallSettings) *hasInstallSettings = installSettingsExist; if (installSettingsExist) { @@ -849,12 +854,12 @@ static bool canLinkWithQt(QString *toolTip) if (!Core::ICore::resourcePath().isWritableDir()) { canLink = false; tip << Tr::tr("%1's resource directory is not writable.") - .arg(Core::Constants::IDE_DISPLAY_NAME); + .arg(QGuiApplication::applicationDisplayName()); } const FilePath link = installSettingsValue ? *installSettingsValue : FilePath(); if (!link.isEmpty()) tip << Tr::tr("%1 is currently linked to \"%2\".") - .arg(QString(Core::Constants::IDE_DISPLAY_NAME), link.toUserOutput()); + .arg(QGuiApplication::applicationDisplayName(), link.toUserOutput()); if (toolTip) *toolTip = tip.join("\n\n"); return canLink; @@ -866,7 +871,7 @@ void QtOptionsPageWidget::setupLinkWithQtButton() const bool canLink = canLinkWithQt(&tip); m_linkWithQtButton->setEnabled(canLink); m_linkWithQtButton->setToolTip(tip); - connect(m_linkWithQtButton, &QPushButton::clicked, this, &QtOptionsPage::linkWithQt); + connect(m_linkWithQtButton, &QPushButton::clicked, this, &LinkWithQtSupport::linkWithQt); } void QtOptionsPageWidget::updateCurrentQtName() @@ -930,29 +935,27 @@ static std::optional<FilePath> settingsDirForQtDir(const FilePath &baseDirectory return qtDir / dir; }); const FilePath validDir = Utils::findOrDefault(dirsToCheck, [baseDirectory](const FilePath &dir) { - return QFile::exists(settingsFile(baseDirectory.resolvePath(dir).toString())) - || QFile::exists(qtVersionsFile(baseDirectory.resolvePath(dir).toString())); + return QFileInfo::exists(settingsFile(baseDirectory.resolvePath(dir).toString())) + || QFileInfo::exists(qtVersionsFile(baseDirectory.resolvePath(dir).toString())); }); if (!validDir.isEmpty()) return validDir; return {}; } -static bool validateQtInstallDir(PathChooser *input, QString *errorString) +static FancyLineEdit::AsyncValidationResult validateQtInstallDir(const QString &input, + const FilePath &baseDirectory) { - const FilePath qtDir = input->rawFilePath(); - if (!settingsDirForQtDir(input->baseDirectory(), qtDir)) { - if (errorString) { - const QStringList filesToCheck = settingsFilesToCheck() + qtversionFilesToCheck(); - *errorString = "<html><body>" - + Tr::tr("Qt installation information was not found in \"%1\". " - "Choose a directory that contains one of the files %2") - .arg(qtDir.toUserOutput(), - "<pre>" + filesToCheck.join('\n') + "</pre>"); - } - return false; + const FilePath qtDir = FilePath::fromUserInput(input); + if (!settingsDirForQtDir(baseDirectory, qtDir)) { + const QStringList filesToCheck = settingsFilesToCheck() + qtversionFilesToCheck(); + return make_unexpected( + "<html><body>" + + ::QtSupport::Tr::tr("Qt installation information was not found in \"%1\". " + "Choose a directory that contains one of the files %2") + .arg(qtDir.toUserOutput(), "<pre>" + filesToCheck.join('\n') + "</pre>")); } - return true; + return input; } static FilePath defaultQtInstallationPath() @@ -980,12 +983,17 @@ void QtOptionsPageWidget::linkWithQt() pathInput->setBaseDirectory(FilePath::fromString(QCoreApplication::applicationDirPath())); pathInput->setPromptDialogTitle(title); pathInput->setMacroExpander(nullptr); - pathInput->setValidationFunction([pathInput](FancyLineEdit *input, QString *errorString) { - if (pathInput->defaultValidationFunction() - && !pathInput->defaultValidationFunction()(input, errorString)) - return false; - return validateQtInstallDir(pathInput, errorString); - }); + pathInput->setValidationFunction( + [pathInput](const QString &input) -> FancyLineEdit::AsyncValidationFuture { + return pathInput->defaultValidationFunction()(input).then( + [baseDir = pathInput->baseDirectory()]( + const FancyLineEdit::AsyncValidationResult &result) + -> FancyLineEdit::AsyncValidationResult { + if (!result) + return result; + return validateQtInstallDir(result.value(), baseDir); + }); + }); const std::optional<FilePath> currentLink = currentlyLinkedQtDir(nullptr); pathInput->setFilePath(currentLink ? *currentLink : defaultQtInstallationPath()); pathInput->setAllowPathFromDevice(true); @@ -1074,19 +1082,26 @@ QStringList QtOptionsPage::keywords() const }; } -bool QtOptionsPage::canLinkWithQt() +} // Internal + +bool LinkWithQtSupport::canLinkWithQt() { return Internal::canLinkWithQt(nullptr); } -bool QtOptionsPage::isLinkedWithQt() +bool LinkWithQtSupport::isLinkedWithQt() { - return currentlyLinkedQtDir(nullptr).has_value(); + return Internal::currentlyLinkedQtDir(nullptr).has_value(); } -void QtOptionsPage::linkWithQt() +Utils::FilePath LinkWithQtSupport::linkedQt() { - QtOptionsPageWidget::linkWithQt(); + return Internal::currentlyLinkedQtDir(nullptr).value_or(Utils::FilePath()); } -} // QtSupport::Internal +void LinkWithQtSupport::linkWithQt() +{ + Internal::QtOptionsPageWidget::linkWithQt(); +} + +} // QtSupport diff --git a/src/plugins/qtsupport/qtoptionspage.h b/src/plugins/qtsupport/qtoptionspage.h index 8aa88ad5f3a..1ee0e28df8b 100644 --- a/src/plugins/qtsupport/qtoptionspage.h +++ b/src/plugins/qtsupport/qtoptionspage.h @@ -3,9 +3,12 @@ #pragma once +#include "qtsupport_global.h" + #include <coreplugin/dialogs/ioptionspage.h> -namespace QtSupport::Internal { +namespace QtSupport { +namespace Internal { class QtOptionsPage final : public Core::IOptionsPage { @@ -14,9 +17,15 @@ public: QStringList keywords() const final; - static bool canLinkWithQt(); - static bool isLinkedWithQt(); - static void linkWithQt(); }; -} // QtSupport::Internal +} // QtSupport + +namespace LinkWithQtSupport { +QTSUPPORT_EXPORT bool canLinkWithQt(); +QTSUPPORT_EXPORT bool isLinkedWithQt(); +QTSUPPORT_EXPORT Utils::FilePath linkedQt(); +QTSUPPORT_EXPORT void linkWithQt(); +} + +} // Internal diff --git a/src/plugins/qtsupport/qtoutputformatter.cpp b/src/plugins/qtsupport/qtoutputformatter.cpp index e54f8470574..a1f97c664d1 100644 --- a/src/plugins/qtsupport/qtoutputformatter.cpp +++ b/src/plugins/qtsupport/qtoutputformatter.cpp @@ -3,7 +3,7 @@ #include "qtoutputformatter.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include "qtsupportconstants.h" #include "qttestparser.h" diff --git a/src/plugins/qtsupport/qtprojectimporter.cpp b/src/plugins/qtsupport/qtprojectimporter.cpp index bc1dc75c06a..adbbd3b737a 100644 --- a/src/plugins/qtsupport/qtprojectimporter.cpp +++ b/src/plugins/qtsupport/qtprojectimporter.cpp @@ -3,7 +3,7 @@ #include "qtprojectimporter.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include "qtversionfactory.h" #include "qtversionmanager.h" diff --git a/src/plugins/qtsupport/qtsupport.qbs b/src/plugins/qtsupport/qtsupport.qbs index 526cf4a24fe..7befd3ab95a 100644 --- a/src/plugins/qtsupport/qtsupport.qbs +++ b/src/plugins/qtsupport/qtsupport.qbs @@ -1,125 +1,118 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "QtSupport" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "xml"]; } - Depends { name: "Utils" } - Depends { name: "app_version_header" } + Depends { name: "Qt"; submodules: ["widgets", "xml"]; } + Depends { name: "Utils" } - Depends { name: "Core" } + Depends { name: "Core" } + Depends { name: "ProParser" } + Depends { name: "ProjectExplorer" } + Depends { name: "ResourceEditor" } + + cpp.defines: base.concat([ + "QMAKE_LIBRARY", + "QMAKE_BUILTIN_PRFS", + ]) + Properties { + condition: qbs.targetOS.contains("windows") + cpp.dynamicLibraries: "advapi32" + } + + Export { Depends { name: "ProParser" } - Depends { name: "ProjectExplorer" } - Depends { name: "ResourceEditor" } - - cpp.defines: base.concat([ - "QMAKE_LIBRARY", - "QMAKE_BUILTIN_PRFS", - ]) - Properties { - condition: qbs.targetOS.contains("windows") - cpp.dynamicLibraries: "advapi32" - } - - Export { - Depends { name: "ProParser" } - } - - Group { - name: "Pro Parser" - prefix: project.sharedSourcesDir + "/proparser/" - files: [ - "ioutils.cpp", - "ioutils.h", - "profileevaluator.cpp", - "profileevaluator.h", - "proitems.cpp", - "proitems.h", - "proparser.qrc", - "prowriter.cpp", - "prowriter.h", - "qmake_global.h", - "qmakebuiltins.cpp", - "qmakeevaluator.cpp", - "qmakeevaluator.h", - "qmakeevaluator_p.h", - "qmakeglobals.cpp", - "qmakeglobals.h", - "qmakeparser.cpp", - "qmakeparser.h", - "qmakevfs.cpp", - "qmakevfs.h", - "registry.cpp", - "registry_p.h", - ] - } + } + Group { + name: "Pro Parser" + prefix: project.sharedSourcesDir + "/proparser/" files: [ - "baseqtversion.cpp", - "baseqtversion.h", - "codegenerator.cpp", - "codegenerator.h", - "codegensettings.cpp", - "codegensettings.h", - "codegensettingspage.cpp", - "codegensettingspage.h", - "externaleditors.cpp", - "externaleditors.h", - "qtbuildaspects.cpp", - "qtbuildaspects.h", - "qtconfigwidget.cpp", - "qtconfigwidget.h", - "qtcppkitinfo.cpp", - "qtcppkitinfo.h", - "qtprojectimporter.cpp", - "qtprojectimporter.h", - "qtsupport.qrc", - "exampleslistmodel.cpp", - "exampleslistmodel.h", - "examplesparser.cpp", - "examplesparser.h", - "profilereader.cpp", - "profilereader.h", - "qscxmlcgenerator.cpp", - "qscxmlcgenerator.h", - "qtkitinformation.cpp", - "qtkitinformation.h", - "qtoptionspage.cpp", - "qtoptionspage.h", - "qtoutputformatter.cpp", - "qtoutputformatter.h", - "qtparser.cpp", - "qtparser.h", - "qtsupport_global.h", "qtsupporttr.h", - "qtsupportconstants.h", - "qtsupportplugin.cpp", - "qtsupportplugin.h", - "qttestparser.cpp", - "qttestparser.h", - "qtversionfactory.h", - "qtversionmanager.cpp", - "qtversionmanager.h", - "translationwizardpage.cpp", - "translationwizardpage.h", - "uicgenerator.cpp", - "uicgenerator.h", + "ioutils.cpp", + "ioutils.h", + "profileevaluator.cpp", + "profileevaluator.h", + "proitems.cpp", + "proitems.h", + "proparser.qrc", + "prowriter.cpp", + "prowriter.h", + "qmake_global.h", + "qmakebuiltins.cpp", + "qmakeevaluator.cpp", + "qmakeevaluator.h", + "qmakeevaluator_p.h", + "qmakeglobals.cpp", + "qmakeglobals.h", + "qmakeparser.cpp", + "qmakeparser.h", + "qmakevfs.cpp", + "qmakevfs.h", + "registry.cpp", + "registry_p.h", ] + } - Group { - name: "QtVersion" - files: [ - "qtversions.cpp", - "qtversions.h", - ] - } + files: [ + "baseqtversion.cpp", + "baseqtversion.h", + "codegenerator.cpp", + "codegenerator.h", + "codegensettings.cpp", + "codegensettings.h", + "externaleditors.cpp", + "externaleditors.h", + "qtbuildaspects.cpp", + "qtbuildaspects.h", + "qtconfigwidget.cpp", + "qtconfigwidget.h", + "qtcppkitinfo.cpp", + "qtcppkitinfo.h", + "qtprojectimporter.cpp", + "qtprojectimporter.h", + "qtsupport.qrc", + "exampleslistmodel.cpp", + "exampleslistmodel.h", + "examplesparser.cpp", + "examplesparser.h", + "profilereader.cpp", + "profilereader.h", + "qscxmlcgenerator.cpp", + "qscxmlcgenerator.h", + "qtkitaspect.cpp", + "qtkitaspect.h", + "qtoptionspage.cpp", + "qtoptionspage.h", + "qtoutputformatter.cpp", + "qtoutputformatter.h", + "qtparser.cpp", + "qtparser.h", + "qtsupport_global.h", "qtsupporttr.h", + "qtsupportconstants.h", + "qtsupportplugin.cpp", + "qtsupportplugin.h", + "qttestparser.cpp", + "qttestparser.h", + "qtversionfactory.h", + "qtversionmanager.cpp", + "qtversionmanager.h", + "translationwizardpage.cpp", + "translationwizardpage.h", + "uicgenerator.cpp", + "uicgenerator.h", + ] - Group { - name: "Getting Started Welcome Page" - files: [ - "gettingstartedwelcomepage.cpp", - "gettingstartedwelcomepage.h" - ] - } + Group { + name: "QtVersion" + files: [ + "qtversions.cpp", + "qtversions.h", + ] + } + + Group { + name: "Getting Started Welcome Page" + files: [ + "gettingstartedwelcomepage.cpp", + "gettingstartedwelcomepage.h" + ] } } diff --git a/src/plugins/qtsupport/qtsupportplugin.cpp b/src/plugins/qtsupport/qtsupportplugin.cpp index 41c88d18e36..090b71c7512 100644 --- a/src/plugins/qtsupport/qtsupportplugin.cpp +++ b/src/plugins/qtsupport/qtsupportplugin.cpp @@ -4,12 +4,11 @@ #include "qtsupportplugin.h" #include "codegenerator.h" -#include "codegensettingspage.h" #include "externaleditors.h" #include "gettingstartedwelcomepage.h" #include "profilereader.h" #include "qscxmlcgenerator.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include "qtoptionspage.h" #include "qtoutputformatter.h" #include "qtsupporttr.h" @@ -22,8 +21,8 @@ #include <coreplugin/jsexpander.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h> +#include <projectexplorer/buildpropertiessettings.h> #include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projecttree.h> #include <projectexplorer/target.h> @@ -35,6 +34,8 @@ #include <utils/macroexpander.h> #include <utils/process.h> +#include <QInputDialog> + using namespace Core; using namespace Utils; using namespace ProjectExplorer; @@ -45,19 +46,14 @@ namespace Internal { class QtSupportPluginPrivate { public: - QtVersionManager qtVersionManager; - DesktopQtVersionFactory desktopQtVersionFactory; EmbeddedLinuxQtVersionFactory embeddedLinuxQtVersionFactory; - CodeGenSettingsPage codeGenSettingsPage; QtOptionsPage qtOptionsPage; ExamplesWelcomePage examplesPage{true}; ExamplesWelcomePage tutorialPage{false}; - QtKitAspect qtKiAspect; - QtOutputFormatterFactory qtOutputFormatterFactory; UicGeneratorFactory uicGeneratorFactory; @@ -65,6 +61,8 @@ public: DesignerExternalEditor designerEditor; LinguistEditor linguistEditor; + + TranslationWizardPageFactory translationWizardPageFactory; }; QtSupportPlugin::~QtSupportPlugin() @@ -94,26 +92,62 @@ void QtSupportPlugin::initialize() { theProcessRunner() = processRunnerCallback; + thePrompter() = [this](const QString &msg, const QStringList &context) -> std::optional<QString> { + std::optional<QString> res; + QEventLoop loop; + + QMetaObject::invokeMethod(this, [msg, context, &res, &loop] { + QString text; + if (!context.isEmpty()) { + text = "Preceding lines:<i><br>   ..." + + context.join("<br>   ") + + "</i><p>"; + } + text += msg; + bool ok = false; + const QString line = QInputDialog::getText( + ICore::dialogParent(), + /*title*/ "QMake Prompt", + /*label*/ text, + /*echo mode*/ QLineEdit::Normal, + /*text*/ QString(), + /*ok*/ &ok, + /*flags*/ Qt::WindowFlags(), + /*QInputMethodHints*/ Qt::ImhNone); + if (ok) + res = line; + loop.quit(); + }, Qt::QueuedConnection); + loop.exec(QEventLoop::ExcludeUserInputEvents); + return res; + }; + QMakeParser::initialize(); ProFileEvaluator::initialize(); new ProFileCacheManager(this); JsExpander::registerGlobalObject<CodeGenerator>("QtSupport"); - ProjectExplorer::JsonWizardFactory::registerPageFactory(new TranslationWizardPageFactory); - ProjectExplorerPlugin::showQtSettings(); + + BuildPropertiesSettings::showQtSettings(); d = new QtSupportPluginPrivate; QtVersionManager::initialized(); } +ExtensionSystem::IPlugin::ShutdownFlag QtSupportPlugin::aboutToShutdown() +{ + QtVersionManager::shutdown(); + return SynchronousShutdown; +} + const char kLinkWithQtInstallationSetting[] = "LinkWithQtInstallation"; static void askAboutQtInstallation() { // if the install settings exist, the Qt Creator installation is (probably) already linked to // a Qt installation, so don't ask - if (!QtOptionsPage::canLinkWithQt() || QtOptionsPage::isLinkedWithQt() + if (!LinkWithQtSupport::canLinkWithQt() || LinkWithQtSupport::isLinkedWithQt() || !ICore::infoBar()->canInfoBeAdded(kLinkWithQtInstallationSetting)) return; @@ -125,7 +159,7 @@ static void askAboutQtInstallation() Utils::InfoBarEntry::GlobalSuppression::Enabled); info.addCustomButton(Tr::tr("Link with Qt"), [] { ICore::infoBar()->removeInfo(kLinkWithQtInstallationSetting); - QTimer::singleShot(0, ICore::dialogParent(), &QtOptionsPage::linkWithQt); + QTimer::singleShot(0, ICore::dialogParent(), &LinkWithQtSupport::linkWithQt); }); ICore::infoBar()->addInfo(info); } diff --git a/src/plugins/qtsupport/qtsupportplugin.h b/src/plugins/qtsupport/qtsupportplugin.h index 7e4c98a9aa0..abd4c7fed37 100644 --- a/src/plugins/qtsupport/qtsupportplugin.h +++ b/src/plugins/qtsupport/qtsupportplugin.h @@ -19,6 +19,7 @@ public: private: void initialize() final; void extensionsInitialized() final; + ShutdownFlag aboutToShutdown() final; class QtSupportPluginPrivate *d = nullptr; diff --git a/src/plugins/qtsupport/qtversionfactory.h b/src/plugins/qtsupport/qtversionfactory.h index 1f29cf0394a..2832fe55fbc 100644 --- a/src/plugins/qtsupport/qtversionfactory.h +++ b/src/plugins/qtsupport/qtversionfactory.h @@ -5,7 +5,7 @@ #include "qtsupport_global.h" -#include <QVariantMap> +#include <utils/store.h> namespace Utils { class FilePath; } @@ -22,7 +22,7 @@ public: static const QList<QtVersionFactory *> allQtVersionFactories(); bool canRestore(const QString &type); - QtVersion *restore(const QString &type, const QVariantMap &data, const Utils::FilePath &workingDirectory); + QtVersion *restore(const QString &type, const Utils::Store &data, const Utils::FilePath &workingDirectory); /// factories with higher priority are asked first to identify /// a qtversion, the priority of the desktop factory is 0 and diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index eedc79e6864..88c88ae15a0 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -24,15 +24,17 @@ #include <utils/process.h> #include <utils/qtcassert.h> +#include <nanotrace/nanotrace.h> + #include <QDir> #include <QFile> #include <QLoggingCategory> -#include <QSettings> #include <QStandardPaths> #include <QStringList> #include <QTextStream> #include <QTimer> +using namespace ProjectExplorer; using namespace Utils; namespace QtSupport { @@ -49,16 +51,17 @@ static VersionMap m_versions; const char DOCUMENTATION_SETTING_KEY[] = "QtSupport/DocumentationSetting"; -static int m_idcount = 0; -// managed by QtProjectManagerPlugin -static QtVersionManager *m_instance = nullptr; -static FileSystemWatcher *m_configFileWatcher = nullptr; -static QTimer *m_fileWatcherTimer = nullptr; -static PersistentSettingsWriter *m_writer = nullptr; -static QVector<ExampleSetModel::ExtraExampleSet> m_pluginRegisteredExampleSets; +QVector<ExampleSetModel::ExtraExampleSet> m_pluginRegisteredExampleSets; + static Q_LOGGING_CATEGORY(log, "qtc.qt.versions", QtWarningMsg); +QtVersionManager *QtVersionManager::instance() +{ + static QtVersionManager theSignals; + return &theSignals; +} + static FilePath globalSettingsFileName() { return Core::ICore::installerResourcePath(QTVERSION_FILENAME); @@ -69,94 +72,129 @@ static FilePath settingsFileName(const QString &path) return Core::ICore::userResourcePath(path); } - // prefer newer qts otherwise compare on id bool qtVersionNumberCompare(QtVersion *a, QtVersion *b) { return a->qtVersion() > b->qtVersion() || (a->qtVersion() == b->qtVersion() && a->uniqueId() < b->uniqueId()); } -static bool restoreQtVersions(); -static void findSystemQt(); -static void saveQtVersions(); - QVector<ExampleSetModel::ExtraExampleSet> ExampleSetModel::pluginRegisteredExampleSets() { return m_pluginRegisteredExampleSets; } -// -------------------------------------------------------------------------- // QtVersionManager -// -------------------------------------------------------------------------- -QtVersionManager::QtVersionManager() +static PersistentSettingsWriter *m_writer = nullptr; + +class QtVersionManagerImpl : public QObject { - m_instance = this; - m_configFileWatcher = nullptr; - m_fileWatcherTimer = new QTimer(this); - m_writer = nullptr; - m_idcount = 1; +public: + QtVersionManagerImpl() + { + qRegisterMetaType<FilePath>(); - qRegisterMetaType<FilePath>(); + // Give the file a bit of time to settle before reading it... + m_fileWatcherTimer.setInterval(2000); + connect(&m_fileWatcherTimer, &QTimer::timeout, this, [this] { updateFromInstaller(); }); - // Give the file a bit of time to settle before reading it... - m_fileWatcherTimer->setInterval(2000); - connect(m_fileWatcherTimer, &QTimer::timeout, this, [this] { updateFromInstaller(); }); + connect(ToolChainManager::instance(), &ToolChainManager::toolChainsLoaded, + this, &QtVersionManagerImpl::triggerQtVersionRestore); + } + + void shutdown() + { + delete m_writer; + m_writer = nullptr; + delete m_configFileWatcher; + m_configFileWatcher = nullptr; + qDeleteAll(m_versions); + m_versions.clear(); + } + + void updateFromInstaller(bool emitSignal = true); + void triggerQtVersionRestore(); + + bool restoreQtVersions(); + void findSystemQt(); + void saveQtVersions(); + + void updateDocumentation(const QtVersions &added, + const QtVersions &removed, + const QtVersions &allNew); + + void setNewQtVersions(const QtVersions &newVersions); + QString qmakePath(const QString &qtchooser, const QString &version); + + QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringList &arguments); + FilePaths gatherQmakePathsFromQtChooser(); + + int m_idcount = 1; + // managed by QtProjectManagerPlugin + FileSystemWatcher *m_configFileWatcher = nullptr; + QTimer m_fileWatcherTimer; +}; + +QtVersionManagerImpl &qtVersionManagerImpl() +{ + static QtVersionManagerImpl theQtVersionManager; + return theQtVersionManager; } -void QtVersionManager::triggerQtVersionRestore() +void QtVersionManagerImpl::triggerQtVersionRestore() { - disconnect(ProjectExplorer::ToolChainManager::instance(), &ProjectExplorer::ToolChainManager::toolChainsLoaded, - this, &QtVersionManager::triggerQtVersionRestore); + NANOTRACE_SCOPE("QtSupport", "QtVersionManagerImpl::triggerQtVersionRestore"); + disconnect(ToolChainManager::instance(), + &ToolChainManager::toolChainsLoaded, + this, + &QtVersionManagerImpl::triggerQtVersionRestore); bool success = restoreQtVersions(); - m_instance->updateFromInstaller(false); + updateFromInstaller(false); if (!success) { // We did neither restore our settings or upgraded // in that case figure out if there's a qt in path // and add it to the Qt versions findSystemQt(); + if (m_versions.size()) + saveQtVersions(); } - emit m_instance->qtVersionsLoaded(); - emit m_instance->qtVersionsChanged(m_versions.keys(), QList<int>(), QList<int>()); - saveQtVersions(); + { + NANOTRACE_SCOPE("QtSupport", "QtVersionManagerImpl::qtVersionsLoaded"); + emit QtVersionManager::instance()->qtVersionsLoaded(); + } + emit QtVersionManager::instance()->qtVersionsChanged( + m_versions.keys(), QList<int>(), QList<int>()); const FilePath configFileName = globalSettingsFileName(); if (configFileName.exists()) { - m_configFileWatcher = new FileSystemWatcher(m_instance); + m_configFileWatcher = new FileSystemWatcher(this); connect(m_configFileWatcher, &FileSystemWatcher::fileChanged, - m_fileWatcherTimer, QOverload<>::of(&QTimer::start)); + &m_fileWatcherTimer, QOverload<>::of(&QTimer::start)); m_configFileWatcher->addFile(configFileName, FileSystemWatcher::WatchModifiedDate); } // exists - const QtVersions vs = versions(); + const QtVersions vs = QtVersionManager::versions(); updateDocumentation(vs, {}, vs); } bool QtVersionManager::isLoaded() { - return m_writer; -} - -QtVersionManager::~QtVersionManager() -{ - delete m_writer; - qDeleteAll(m_versions); - m_versions.clear(); + return m_writer != nullptr; } void QtVersionManager::initialized() { - connect(ProjectExplorer::ToolChainManager::instance(), &ProjectExplorer::ToolChainManager::toolChainsLoaded, - QtVersionManager::instance(), &QtVersionManager::triggerQtVersionRestore); + // Force creation. FIXME: Remove. + qtVersionManagerImpl(); } -QtVersionManager *QtVersionManager::instance() +void QtVersionManager::shutdown() { - return m_instance; + qtVersionManagerImpl().shutdown(); } -static bool restoreQtVersions() +bool QtVersionManagerImpl::restoreQtVersions() { QTC_ASSERT(!m_writer, return false); m_writer = new PersistentSettingsWriter(settingsFileName(QTVERSION_FILENAME), @@ -169,25 +207,25 @@ static bool restoreQtVersions() if (!reader.load(filename)) return false; - QVariantMap data = reader.restoreValues(); + Store data = reader.restoreValues(); // Check version: const int version = data.value(QTVERSION_FILE_VERSION_KEY, 0).toInt(); if (version < 1) return false; - const QString keyPrefix(QTVERSION_DATA_KEY); - const QVariantMap::ConstIterator dcend = data.constEnd(); - for (QVariantMap::ConstIterator it = data.constBegin(); it != dcend; ++it) { - const QString &key = it.key(); - if (!key.startsWith(keyPrefix)) + const QByteArray keyPrefix(QTVERSION_DATA_KEY); + const Store::ConstIterator dcend = data.constEnd(); + for (Store::ConstIterator it = data.constBegin(); it != dcend; ++it) { + const Key &key = it.key(); + if (!key.view().startsWith(keyPrefix)) continue; bool ok; - int count = key.mid(keyPrefix.count()).toInt(&ok); + int count = key.toByteArray().mid(keyPrefix.count()).toInt(&ok); if (!ok || count < 0) continue; - const QVariantMap qtversionMap = it.value().toMap(); + const Store qtversionMap = storeFromVariant(it.value()); const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString(); bool restored = false; @@ -217,9 +255,9 @@ static bool restoreQtVersions() return true; } -void QtVersionManager::updateFromInstaller(bool emitSignal) +void QtVersionManagerImpl::updateFromInstaller(bool emitSignal) { - m_fileWatcherTimer->stop(); + m_fileWatcherTimer.stop(); const FilePath path = globalSettingsFileName(); // Handle overwritting of data: @@ -234,7 +272,7 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) const QList<QtVersionFactory *> factories = QtVersionFactory::allQtVersionFactories(); PersistentSettingsReader reader; - QVariantMap data; + Store data; if (reader.load(path)) data = reader.restoreValues(); @@ -250,18 +288,18 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) QStringList sdkVersions; - const QString keyPrefix(QTVERSION_DATA_KEY); - const QVariantMap::ConstIterator dcend = data.constEnd(); - for (QVariantMap::ConstIterator it = data.constBegin(); it != dcend; ++it) { - const QString &key = it.key(); - if (!key.startsWith(keyPrefix)) + const QByteArray keyPrefix(QTVERSION_DATA_KEY); + const Store::ConstIterator dcend = data.constEnd(); + for (Store::ConstIterator it = data.constBegin(); it != dcend; ++it) { + const Key &key = it.key(); + if (!key.view().startsWith(keyPrefix)) continue; bool ok; - int count = key.mid(keyPrefix.count()).toInt(&ok); + int count = key.toByteArray().mid(keyPrefix.count()).toInt(&ok); if (!ok || count < 0) continue; - QVariantMap qtversionMap = it.value().toMap(); + Store qtversionMap = storeFromVariant(it.value()); const QString type = qtversionMap.value(QTVERSION_TYPE_KEY).toString(); const QString autoDetectionSource = qtversionMap.value("autodetectionSource").toString(); sdkVersions << autoDetectionSource; @@ -342,31 +380,31 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) } } if (emitSignal) - emit qtVersionsChanged(added, removed, changed); + emit QtVersionManager::instance()->qtVersionsChanged(added, removed, changed); } -static void saveQtVersions() +void QtVersionManagerImpl::saveQtVersions() { if (!m_writer) return; - QVariantMap data; + Store data; data.insert(QTVERSION_FILE_VERSION_KEY, 1); int count = 0; for (QtVersion *qtv : std::as_const(m_versions)) { - QVariantMap tmp = qtv->toMap(); + Store tmp = qtv->toMap(); if (tmp.isEmpty()) continue; tmp.insert(QTVERSION_TYPE_KEY, qtv->type()); - data.insert(QString::fromLatin1(QTVERSION_DATA_KEY) + QString::number(count), tmp); + data.insert(numberedKey(QTVERSION_DATA_KEY, count), variantFromStore(tmp)); ++count; } m_writer->save(data, Core::ICore::dialogParent()); } // Executes qtchooser with arguments in a process and returns its output -static QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringList &arguments) +QList<QByteArray> QtVersionManagerImpl::runQtChooser(const QString &qtchooser, const QStringList &arguments) { Process p; p.setCommand({FilePath::fromString(qtchooser), arguments}); @@ -377,7 +415,7 @@ static QList<QByteArray> runQtChooser(const QString &qtchooser, const QStringLis } // Asks qtchooser for the qmake path of a given version -static QString qmakePath(const QString &qtchooser, const QString &version) +QString QtVersionManagerImpl::qmakePath(const QString &qtchooser, const QString &version) { const QList<QByteArray> outputs = runQtChooser(qtchooser, {QStringLiteral("-qt=%1").arg(version), @@ -393,11 +431,11 @@ static QString qmakePath(const QString &qtchooser, const QString &version) return QString(); } -static FilePaths gatherQmakePathsFromQtChooser() +FilePaths QtVersionManagerImpl::gatherQmakePathsFromQtChooser() { const QString qtchooser = QStandardPaths::findExecutable(QStringLiteral("qtchooser")); if (qtchooser.isEmpty()) - return FilePaths(); + return {}; const QList<QByteArray> versions = runQtChooser(qtchooser, QStringList("-l")); QSet<FilePath> foundQMakes; @@ -410,7 +448,7 @@ static FilePaths gatherQmakePathsFromQtChooser() return Utils::toList(foundQMakes); } -static void findSystemQt() +void QtVersionManagerImpl::findSystemQt() { FilePaths systemQMakes = BuildableHelperLibrary::findQtsInEnvironment(Environment::systemEnvironment()); @@ -433,7 +471,6 @@ static void findSystemQt() void QtVersionManager::addVersion(QtVersion *version) { - QTC_ASSERT(m_writer, return); QTC_ASSERT(version, return); if (m_versions.contains(version->uniqueId())) return; @@ -441,16 +478,16 @@ void QtVersionManager::addVersion(QtVersion *version) int uniqueId = version->uniqueId(); m_versions.insert(uniqueId, version); - emit m_instance->qtVersionsChanged(QList<int>() << uniqueId, QList<int>(), QList<int>()); - saveQtVersions(); + emit QtVersionManager::instance()->qtVersionsChanged(QList<int>() << uniqueId, QList<int>(), QList<int>()); + qtVersionManagerImpl().saveQtVersions(); } void QtVersionManager::removeVersion(QtVersion *version) { QTC_ASSERT(version, return); m_versions.remove(version->uniqueId()); - emit m_instance->qtVersionsChanged(QList<int>(), QList<int>() << version->uniqueId(), QList<int>()); - saveQtVersions(); + emit QtVersionManager::instance()->qtVersionsChanged(QList<int>(), QList<int>() << version->uniqueId(), QList<int>()); + qtVersionManagerImpl().saveQtVersions(); delete version; } @@ -496,11 +533,12 @@ static QStringList documentationFiles(const QtVersions &vs, bool highestOnly = f return filePaths.values(); } -void QtVersionManager::updateDocumentation(const QtVersions &added, - const QtVersions &removed, - const QtVersions &allNew) +void QtVersionManagerImpl::updateDocumentation(const QtVersions &added, + const QtVersions &removed, + const QtVersions &allNew) { - const DocumentationSetting setting = documentationSetting(); + using DocumentationSetting = QtVersionManager::DocumentationSetting; + const DocumentationSetting setting = QtVersionManager::documentationSetting(); const QStringList docsOfAll = setting == DocumentationSetting::None ? QStringList() : documentationFiles(allNew, @@ -520,7 +558,7 @@ void QtVersionManager::updateDocumentation(const QtVersions &added, int QtVersionManager::getUniqueId() { - return m_idcount++; + return qtVersionManagerImpl().m_idcount++; } QtVersions QtVersionManager::versions(const QtVersion::Predicate &predicate) @@ -558,6 +596,11 @@ static bool equals(QtVersion *a, QtVersion *b) } void QtVersionManager::setNewQtVersions(const QtVersions &newVersions) +{ + qtVersionManagerImpl().setNewQtVersions(newVersions); +} + +void QtVersionManagerImpl::setNewQtVersions(const QtVersions &newVersions) { // We want to preserve the same order as in the settings dialog // so we sort a copy @@ -628,7 +671,7 @@ void QtVersionManager::setNewQtVersions(const QtVersions &newVersions) saveQtVersions(); if (!changedVersions.isEmpty() || !addedVersions.isEmpty() || !removedVersions.isEmpty()) - emit m_instance->qtVersionsChanged(addedIds, removedIds, changedIds); + emit QtVersionManager::instance()->qtVersionsChanged(addedIds, removedIds, changedIds); } void QtVersionManager::setDocumentationSetting(const QtVersionManager::DocumentationSetting &setting) @@ -639,7 +682,7 @@ void QtVersionManager::setDocumentationSetting(const QtVersionManager::Documenta // force re-evaluating which documentation should be registered // by claiming that all are removed and re-added const QtVersions vs = versions(); - updateDocumentation(vs, vs, vs); + qtVersionManagerImpl().updateDocumentation(vs, vs, vs); } QtVersionManager::DocumentationSetting QtVersionManager::documentationSetting() diff --git a/src/plugins/qtsupport/qtversionmanager.h b/src/plugins/qtsupport/qtversionmanager.h index 49c0351bb0c..7eb07310f87 100644 --- a/src/plugins/qtsupport/qtversionmanager.h +++ b/src/plugins/qtsupport/qtversionmanager.h @@ -8,19 +8,14 @@ namespace QtSupport { -class QTSUPPORT_EXPORT QtVersionManager : public QObject +namespace Internal { class QtSupportPlugin; } + +class QTSUPPORT_EXPORT QtVersionManager final : public QObject { Q_OBJECT - // for getUniqueId(); - friend class QtVersion; - friend class QtVersionFactory; - friend class Internal::QtOptionsPageWidget; public: static QtVersionManager *instance(); - QtVersionManager(); - ~QtVersionManager() override; - static void initialized(); static bool isLoaded(); @@ -52,13 +47,19 @@ signals: void qtVersionsLoaded(); private: - enum class DocumentationSetting { HighestOnly, All, None }; + QtVersionManager() = default; - static void updateDocumentation(const QtVersions &added, - const QtVersions &removed, - const QtVersions &allNew); - void updateFromInstaller(bool emitSignal = true); - void triggerQtVersionRestore(); + // for getUniqueId(); + friend class QtVersion; + friend class QtVersionFactory; + friend class QtVersionManagerImpl; + friend class Internal::QtOptionsPageWidget; + friend class Internal::QtSupportPlugin; + + static void initialized(); + static void shutdown(); + + enum class DocumentationSetting { HighestOnly, All, None }; // Used by QtOptionsPage static void setNewQtVersions(const QtVersions &newVersions); diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp index f1ff905b831..840bc585645 100644 --- a/src/plugins/qtsupport/uicgenerator.cpp +++ b/src/plugins/qtsupport/uicgenerator.cpp @@ -4,7 +4,7 @@ #include "uicgenerator.h" #include "baseqtversion.h" -#include "qtkitinformation.h" +#include "qtkitaspect.h" #include <projectexplorer/buildconfiguration.h> #include <projectexplorer/kitmanager.h> @@ -14,22 +14,30 @@ #include <utils/qtcassert.h> #include <QDateTime> -#include <QDir> -#include <QFileInfo> #include <QLoggingCategory> using namespace ProjectExplorer; +using namespace Utils; namespace QtSupport { -UicGenerator::UicGenerator(const Project *project, const Utils::FilePath &source, - const Utils::FilePaths &targets, QObject *parent) : - ProcessExtraCompiler(project, source, targets, parent) +class UicGenerator final : public ProcessExtraCompiler { - QTC_ASSERT(targets.count() == 1, return); -} +public: + UicGenerator(const Project *project, const FilePath &source, + const FilePaths &targets, QObject *parent) + : ProcessExtraCompiler(project, source, targets, parent) + { + QTC_ASSERT(targets.count() == 1, return); + } -Utils::FilePath UicGenerator::command() const +protected: + FilePath command() const override; + QStringList arguments() const override { return {"-p"}; } + FileNameToContentsHash handleProcessFinished(Process *process) override; +}; + +FilePath UicGenerator::command() const { QtSupport::QtVersion *version = nullptr; Target *target; @@ -39,23 +47,18 @@ Utils::FilePath UicGenerator::command() const version = QtSupport::QtKitAspect::qtVersion(KitManager::defaultKit()); if (!version) - return Utils::FilePath(); + return {}; return version->uicFilePath(); } -QStringList UicGenerator::arguments() const -{ - return {"-p"}; -} - -FileNameToContentsHash UicGenerator::handleProcessFinished(Utils::Process *process) +FileNameToContentsHash UicGenerator::handleProcessFinished(Process *process) { FileNameToContentsHash result; if (process->exitStatus() != QProcess::NormalExit && process->exitCode() != 0) return result; - const Utils::FilePaths targetList = targets(); + const FilePaths targetList = targets(); if (targetList.size() != 1) return result; // As far as I can discover in the UIC sources, it writes out local 8-bit encoding. The @@ -77,8 +80,8 @@ QString UicGeneratorFactory::sourceTag() const } ExtraCompiler *UicGeneratorFactory::create(const Project *project, - const Utils::FilePath &source, - const Utils::FilePaths &targets) + const FilePath &source, + const FilePaths &targets) { return new UicGenerator(project, source, targets, this); } diff --git a/src/plugins/qtsupport/uicgenerator.h b/src/plugins/qtsupport/uicgenerator.h index d68b6814c22..b3b1de17e4a 100644 --- a/src/plugins/qtsupport/uicgenerator.h +++ b/src/plugins/qtsupport/uicgenerator.h @@ -4,26 +4,12 @@ #pragma once #include <projectexplorer/extracompiler.h> -#include <utils/fileutils.h> +#include <utils/filepath.h> namespace QtSupport { -class UicGenerator : public ProjectExplorer::ProcessExtraCompiler -{ - Q_OBJECT -public: - UicGenerator(const ProjectExplorer::Project *project, const Utils::FilePath &source, - const Utils::FilePaths &targets, QObject *parent = nullptr); - -protected: - Utils::FilePath command() const override; - QStringList arguments() const override; - ProjectExplorer::FileNameToContentsHash handleProcessFinished(Utils::Process *process) override; -}; - class UicGeneratorFactory : public ProjectExplorer::ExtraCompilerFactory { - Q_OBJECT public: UicGeneratorFactory() = default; diff --git a/src/plugins/remotelinux/CMakeLists.txt b/src/plugins/remotelinux/CMakeLists.txt index f81f75350ac..188e3609ac5 100644 --- a/src/plugins/remotelinux/CMakeLists.txt +++ b/src/plugins/remotelinux/CMakeLists.txt @@ -5,10 +5,9 @@ add_qtc_plugin(RemoteLinux abstractremotelinuxdeploystep.cpp abstractremotelinuxdeploystep.h customcommanddeploystep.cpp customcommanddeploystep.h deploymenttimeinfo.cpp deploymenttimeinfo.h + genericdeploystep.cpp genericdeploystep.h genericdirectuploadstep.cpp genericdirectuploadstep.h genericlinuxdeviceconfigurationwidget.cpp genericlinuxdeviceconfigurationwidget.h - genericlinuxdeviceconfigurationwizard.cpp genericlinuxdeviceconfigurationwizard.h - genericlinuxdeviceconfigurationwizardpages.cpp genericlinuxdeviceconfigurationwizardpages.h killappstep.cpp killappstep.h linuxdevice.cpp linuxdevice.h linuxdevicetester.cpp linuxdevicetester.h @@ -20,13 +19,12 @@ add_qtc_plugin(RemoteLinux remotelinux_export.h remotelinuxcustomrunconfiguration.cpp remotelinuxcustomrunconfiguration.h remotelinuxdebugsupport.cpp remotelinuxdebugsupport.h - remotelinuxdeployconfiguration.cpp remotelinuxdeployconfiguration.h remotelinuxenvironmentaspect.cpp remotelinuxenvironmentaspect.h remotelinuxplugin.cpp remotelinuxplugin.h remotelinuxrunconfiguration.cpp remotelinuxrunconfiguration.h remotelinuxsignaloperation.cpp remotelinuxsignaloperation.h remotelinuxtr.h - rsyncdeploystep.cpp rsyncdeploystep.h + sshdevicewizard.cpp sshdevicewizard.h sshkeycreationdialog.cpp sshkeycreationdialog.h tarpackagecreationstep.cpp tarpackagecreationstep.h tarpackagedeploystep.cpp tarpackagedeploystep.h diff --git a/src/plugins/remotelinux/RemoteLinux.json.in b/src/plugins/remotelinux/RemoteLinux.json.in index a5cfc60645b..2345b5acab3 100644 --- a/src/plugins/remotelinux/RemoteLinux.json.in +++ b/src/plugins/remotelinux/RemoteLinux.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"RemoteLinux\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "RemoteLinux", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Support for deployment to and execution on a remote Linux host.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Support for deployment to and execution on a remote Linux host.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp index afd4e0e96a2..1130767a928 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.cpp @@ -8,16 +8,13 @@ #include <projectexplorer/deployablefile.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/target.h> +#include <projectexplorer/kitaspects.h> #include <solutions/tasking/tasktree.h> #include <utils/qtcassert.h> #include <QDateTime> -#include <QPointer> using namespace ProjectExplorer; using namespace Tasking; @@ -29,12 +26,9 @@ namespace Internal { class AbstractRemoteLinuxDeployStepPrivate { public: - bool hasError; - std::function<CheckResult()> internalInit; - std::function<void()> runPreparer; + std::function<expected_str<void>()> internalInit; DeploymentTimeInfo deployTimes; - std::unique_ptr<TaskTree> m_taskTree; }; } // Internal @@ -43,7 +37,8 @@ using namespace Internal; AbstractRemoteLinuxDeployStep::AbstractRemoteLinuxDeployStep(BuildStepList *bsl, Id id) : BuildStep(bsl, id), d(new AbstractRemoteLinuxDeployStepPrivate) -{} +{ +} AbstractRemoteLinuxDeployStep::~AbstractRemoteLinuxDeployStep() { @@ -56,7 +51,7 @@ IDevice::ConstPtr AbstractRemoteLinuxDeployStep::deviceConfiguration() const } void AbstractRemoteLinuxDeployStep::saveDeploymentTimeStamp(const DeployableFile &deployableFile, - const QDateTime &remoteTimestamp) + const QDateTime &remoteTimestamp) { d->deployTimes.saveDeploymentTimeStamp(deployableFile, kit(), remoteTimestamp); } @@ -73,94 +68,42 @@ bool AbstractRemoteLinuxDeployStep::hasRemoteFileChanged( return d->deployTimes.hasRemoteFileChanged(deployableFile, kit(), remoteTimestamp); } -CheckResult AbstractRemoteLinuxDeployStep::isDeploymentPossible() const +expected_str<void> AbstractRemoteLinuxDeployStep::isDeploymentPossible() const { if (!deviceConfiguration()) - return CheckResult::failure(Tr::tr("No device configuration set.")); - return CheckResult::success(); + return make_unexpected(Tr::tr("No device configuration set.")); + return {}; } -void AbstractRemoteLinuxDeployStep::setInternalInitializer(const std::function<CheckResult ()> &init) +void AbstractRemoteLinuxDeployStep::setInternalInitializer( + const std::function<expected_str<void>()> &init) { d->internalInit = init; } -void AbstractRemoteLinuxDeployStep::setRunPreparer(const std::function<void ()> &prep) +void AbstractRemoteLinuxDeployStep::fromMap(const Store &map) { - d->runPreparer = prep; -} - -bool AbstractRemoteLinuxDeployStep::fromMap(const QVariantMap &map) -{ - if (!BuildStep::fromMap(map)) - return false; + BuildStep::fromMap(map); + if (hasError()) + return; d->deployTimes.importDeployTimes(map); - return true; } -QVariantMap AbstractRemoteLinuxDeployStep::toMap() const +void AbstractRemoteLinuxDeployStep::toMap(Store &map) const { - QVariantMap map = BuildStep::toMap(); + BuildStep::toMap(map); map.insert(d->deployTimes.exportDeployTimes()); - return map; } bool AbstractRemoteLinuxDeployStep::init() { QTC_ASSERT(d->internalInit, return false); - const CheckResult canDeploy = d->internalInit(); + const auto canDeploy = d->internalInit(); if (!canDeploy) { - emit addOutput(Tr::tr("Cannot deploy: %1").arg(canDeploy.errorMessage()), + emit addOutput(Tr::tr("Cannot deploy: %1").arg(canDeploy.error()), OutputFormat::ErrorMessage); } - return canDeploy; -} - -void AbstractRemoteLinuxDeployStep::doRun() -{ - if (d->runPreparer) - d->runPreparer(); - - d->hasError = false; - - QTC_ASSERT(!d->m_taskTree, return); - - const CheckResult check = isDeploymentPossible(); - if (!check) { - addErrorMessage(check.errorMessage()); - handleFinished(); - return; - } - - if (!isDeploymentNecessary()) { - addProgressMessage(Tr::tr("No deployment action necessary. Skipping.")); - handleFinished(); - return; - } - - d->m_taskTree.reset(new TaskTree(deployRecipe())); - const auto endHandler = [this] { - d->m_taskTree.release()->deleteLater(); - handleFinished(); - }; - connect(d->m_taskTree.get(), &TaskTree::done, this, endHandler); - connect(d->m_taskTree.get(), &TaskTree::errorOccurred, this, endHandler); - d->m_taskTree->start(); -} - -void AbstractRemoteLinuxDeployStep::doCancel() -{ - if (d->hasError) - return; - - emit addOutput(Tr::tr("User requests deployment to stop; cleaning up."), - OutputFormat::NormalMessage); - d->hasError = true; - - if (!d->m_taskTree) - return; - d->m_taskTree.reset(); - handleFinished(); + return bool(canDeploy); } void AbstractRemoteLinuxDeployStep::addProgressMessage(const QString &message) @@ -172,7 +115,6 @@ void AbstractRemoteLinuxDeployStep::addErrorMessage(const QString &message) { emit addOutput(message, OutputFormat::ErrorMessage); emit addTask(DeploymentTask(Task::Error, message), 1); // TODO correct? - d->hasError = true; } void AbstractRemoteLinuxDeployStep::addWarningMessage(const QString &message) @@ -181,16 +123,6 @@ void AbstractRemoteLinuxDeployStep::addWarningMessage(const QString &message) emit addTask(DeploymentTask(Task::Warning, message), 1); // TODO correct? } -void AbstractRemoteLinuxDeployStep::handleFinished() -{ - if (d->hasError) - emit addOutput(Tr::tr("Deploy step failed."), OutputFormat::ErrorMessage); - else - emit addOutput(Tr::tr("Deploy step finished."), OutputFormat::NormalMessage); - - emit finished(!d->hasError); -} - void AbstractRemoteLinuxDeployStep::handleStdOutData(const QString &data) { emit addOutput(data, OutputFormat::Stdout, DontAppendNewline); @@ -206,9 +138,32 @@ bool AbstractRemoteLinuxDeployStep::isDeploymentNecessary() const return true; } -Group AbstractRemoteLinuxDeployStep::deployRecipe() +GroupItem AbstractRemoteLinuxDeployStep::runRecipe() { - return {}; + const auto onSetup = [this] { + const auto canDeploy = isDeploymentPossible(); + if (!canDeploy) { + addErrorMessage(canDeploy.error()); + return SetupResult::StopWithError; + } + if (!isDeploymentNecessary()) { + addProgressMessage(Tr::tr("No deployment action necessary. Skipping.")); + return SetupResult::StopWithDone; + } + return SetupResult::Continue; + }; + const auto onDone = [this] { + emit addOutput(Tr::tr("Deploy step finished."), OutputFormat::NormalMessage); + }; + const auto onError = [this] { + emit addOutput(Tr::tr("Deploy step failed."), OutputFormat::ErrorMessage); + }; + return Group { + onGroupSetup(onSetup), + deployRecipe(), + onGroupDone(onDone), + onGroupError(onError) + }; } } // namespace RemoteLinux diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h index b0af97f5d2b..ee56491a8bf 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h @@ -8,53 +8,29 @@ #include <projectexplorer/buildstep.h> #include <projectexplorer/devicesupport/idevicefwd.h> -#include <QObject> - namespace ProjectExplorer { class DeployableFile; } -namespace Tasking { class Group; } namespace RemoteLinux { namespace Internal { class AbstractRemoteLinuxDeployStepPrivate; } -class REMOTELINUX_EXPORT CheckResult -{ -public: - static CheckResult success() { return {true, {}}; } - static CheckResult failure(const QString &error = {}) { return {false, error}; } - - operator bool() const { return m_ok; } - QString errorMessage() const { return m_error; } - -private: - CheckResult(bool ok, const QString &error) : m_ok(ok), m_error(error) {} - - bool m_ok = false; - QString m_error; -}; - class REMOTELINUX_EXPORT AbstractRemoteLinuxDeployStep : public ProjectExplorer::BuildStep { public: explicit AbstractRemoteLinuxDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); ~AbstractRemoteLinuxDeployStep() override; +protected: ProjectExplorer::IDeviceConstPtr deviceConfiguration() const; - - virtual CheckResult isDeploymentPossible() const; - + virtual Utils::expected_str<void> isDeploymentPossible() const; void handleStdOutData(const QString &data); void handleStdErrData(const QString &data); -protected: - bool fromMap(const QVariantMap &map) override; - QVariantMap toMap() const override; - bool init() override; - void doRun() final; - void doCancel() override; + void fromMap(const Utils::Store &map) final; + void toMap(Utils::Store &map) const final; + bool init() final; - void setInternalInitializer(const std::function<CheckResult()> &init); - void setRunPreparer(const std::function<void()> &prep); + void setInternalInitializer(const std::function<Utils::expected_str<void>()> &init); void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, const QDateTime &remoteTimestamp); @@ -66,11 +42,10 @@ protected: void addErrorMessage(const QString &message); void addWarningMessage(const QString &message); - void handleFinished(); - private: virtual bool isDeploymentNecessary() const; - virtual Tasking::Group deployRecipe(); + virtual Tasking::GroupItem deployRecipe() = 0; + Tasking::GroupItem runRecipe() final; Internal::AbstractRemoteLinuxDeployStepPrivate *d; }; diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp index 32076d34f27..2b5753243fb 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.cpp +++ b/src/plugins/remotelinux/customcommanddeploystep.cpp @@ -25,42 +25,38 @@ public: CustomCommandDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto commandLine = addAspect<StringAspect>(); - commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine"); - commandLine->setLabelText(Tr::tr("Command line:")); - commandLine->setDisplayStyle(StringAspect::LineEditDisplay); - commandLine->setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History"); + commandLine.setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine"); + commandLine.setLabelText(Tr::tr("Command line:")); + commandLine.setDisplayStyle(StringAspect::LineEditDisplay); + commandLine.setHistoryCompleter("RemoteLinuxCustomCommandDeploymentStep.History"); - setInternalInitializer([this, commandLine] { - m_commandLine = commandLine->value().trimmed(); - return isDeploymentPossible(); - }); + setInternalInitializer([this] { return isDeploymentPossible(); }); addMacroExpander(); } - CheckResult isDeploymentPossible() const final; + expected_str<void> isDeploymentPossible() const final; private: - Group deployRecipe() final; + GroupItem deployRecipe() final; - QString m_commandLine; + StringAspect commandLine{this}; }; -CheckResult CustomCommandDeployStep::isDeploymentPossible() const +expected_str<void> CustomCommandDeployStep::isDeploymentPossible() const { - if (m_commandLine.isEmpty()) - return CheckResult::failure(Tr::tr("No command line given.")); + if (commandLine().isEmpty()) + return make_unexpected(Tr::tr("No command line given.")); return AbstractRemoteLinuxDeployStep::isDeploymentPossible(); } -Group CustomCommandDeployStep::deployRecipe() +GroupItem CustomCommandDeployStep::deployRecipe() { const auto setupHandler = [this](Process &process) { - addProgressMessage(Tr::tr("Starting remote command \"%1\"...").arg(m_commandLine)); + addProgressMessage(Tr::tr("Starting remote command \"%1\"...").arg(commandLine())); process.setCommand({deviceConfiguration()->filePath("/bin/sh"), - {"-c", m_commandLine}}); + {"-c", commandLine()}}); Process *proc = &process; connect(proc, &Process::readyReadStandardOutput, this, [this, proc] { handleStdOutData(proc->readAllStandardOutput()); @@ -81,7 +77,7 @@ Group CustomCommandDeployStep::deployRecipe() .arg(process.exitCode())); } }; - return Group { ProcessTask(setupHandler, doneHandler, errorHandler) }; + return ProcessTask(setupHandler, doneHandler, errorHandler); } diff --git a/src/plugins/remotelinux/deploymenttimeinfo.cpp b/src/plugins/remotelinux/deploymenttimeinfo.cpp index e9c59156158..d3c9b028564 100644 --- a/src/plugins/remotelinux/deploymenttimeinfo.cpp +++ b/src/plugins/remotelinux/deploymenttimeinfo.cpp @@ -6,7 +6,7 @@ #include <projectexplorer/deployablefile.h> #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/devicesupport/sshparameters.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/target.h> #include <QDateTime> @@ -103,9 +103,9 @@ bool DeploymentTimeInfo::hasRemoteFileChanged(const DeployableFile &deployableFi return !lastDeployed.remote.isValid() || remoteTimestamp != lastDeployed.remote; } -QVariantMap DeploymentTimeInfo::exportDeployTimes() const +Store DeploymentTimeInfo::exportDeployTimes() const { - QVariantMap map; + Store map; QVariantList hostList; QVariantList fileList; QVariantList sysrootList; @@ -122,33 +122,31 @@ QVariantMap DeploymentTimeInfo::exportDeployTimes() const localTimeList << it.value().local; remoteTimeList << it.value().remote; } - map.insert(QLatin1String(LastDeployedHostsKey), hostList); - map.insert(QLatin1String(LastDeployedSysrootsKey), sysrootList); - map.insert(QLatin1String(LastDeployedFilesKey), fileList); - map.insert(QLatin1String(LastDeployedRemotePathsKey), remotePathList); - map.insert(QLatin1String(LastDeployedLocalTimesKey), localTimeList); - map.insert(QLatin1String(LastDeployedRemoteTimesKey), remoteTimeList); + map.insert(LastDeployedHostsKey, hostList); + map.insert(LastDeployedSysrootsKey, sysrootList); + map.insert(LastDeployedFilesKey, fileList); + map.insert(LastDeployedRemotePathsKey, remotePathList); + map.insert(LastDeployedLocalTimesKey, localTimeList); + map.insert(LastDeployedRemoteTimesKey, remoteTimeList); return map; } -void DeploymentTimeInfo::importDeployTimes(const QVariantMap &map) +void DeploymentTimeInfo::importDeployTimes(const Store &map) { - const QVariantList &hostList = map.value(QLatin1String(LastDeployedHostsKey)).toList(); - const QVariantList &sysrootList = map.value(QLatin1String(LastDeployedSysrootsKey)).toList(); - const QVariantList &fileList = map.value(QLatin1String(LastDeployedFilesKey)).toList(); - const QVariantList &remotePathList - = map.value(QLatin1String(LastDeployedRemotePathsKey)).toList(); + const QVariantList &hostList = map.value(LastDeployedHostsKey).toList(); + const QVariantList &sysrootList = map.value(LastDeployedSysrootsKey).toList(); + const QVariantList &fileList = map.value(LastDeployedFilesKey).toList(); + const QVariantList &remotePathList = map.value(LastDeployedRemotePathsKey).toList(); QVariantList localTimesList; - const auto localTimes = map.find(QLatin1String(LastDeployedLocalTimesKey)); + const auto localTimes = map.find(LastDeployedLocalTimesKey); if (localTimes != map.end()) { localTimesList = localTimes.value().toList(); } else { - localTimesList = map.value(QLatin1String(LastDeployedTimesKey)).toList(); + localTimesList = map.value(LastDeployedTimesKey).toList(); } - const QVariantList remoteTimesList - = map.value(QLatin1String(LastDeployedRemoteTimesKey)).toList(); + const QVariantList remoteTimesList = map.value(LastDeployedRemoteTimesKey).toList(); const int elemCount = qMin(qMin(qMin(hostList.size(), fileList.size()), qMin(remotePathList.size(), localTimesList.size())), diff --git a/src/plugins/remotelinux/deploymenttimeinfo.h b/src/plugins/remotelinux/deploymenttimeinfo.h index 6615dd1c616..69ec215bce3 100644 --- a/src/plugins/remotelinux/deploymenttimeinfo.h +++ b/src/plugins/remotelinux/deploymenttimeinfo.h @@ -3,7 +3,7 @@ #pragma once -#include <QVariantMap> +#include <utils/store.h> QT_BEGIN_NAMESPACE class QDateTime; @@ -24,8 +24,8 @@ public: DeploymentTimeInfo(); ~DeploymentTimeInfo(); - void importDeployTimes(const QVariantMap &map); - QVariantMap exportDeployTimes() const; + void importDeployTimes(const Utils::Store &map); + Utils::Store exportDeployTimes() const; void saveDeploymentTimeStamp(const ProjectExplorer::DeployableFile &deployableFile, const ProjectExplorer::Kit *kit, diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index 2edb78fa7b3..f8afdd22423 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -33,7 +33,7 @@ static const char TEST_DIR[] = "/tmp/testdir"; static const FilePath baseFilePath() { - return FilePath::fromString("ssh://" + SshTest::userAtHost() + QString(TEST_DIR)); + return FilePath::fromString("ssh://" + SshTest::userAtHostAndPort() + QString(TEST_DIR)); } TestLinuxDeviceFactory::TestLinuxDeviceFactory() @@ -55,7 +55,7 @@ TestLinuxDeviceFactory::TestLinuxDeviceFactory() FilePath createFile(const QString &name) { FilePath testFilePath = baseFilePath() / name; - FilePath dummyFilePath = FilePath::fromString("ssh://" + SshTest::userAtHost() + "/dev/null"); + FilePath dummyFilePath = FilePath::fromString("ssh://" + SshTest::userAtHostAndPort() + "/dev/null"); dummyFilePath.copyFile(testFilePath); return testFilePath; } diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp new file mode 100644 index 00000000000..4ae804689c2 --- /dev/null +++ b/src/plugins/remotelinux/genericdeploystep.cpp @@ -0,0 +1,208 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "genericdeploystep.h" + +#include "abstractremotelinuxdeploystep.h" +#include "remotelinux_constants.h" +#include "remotelinuxtr.h" + +#include <projectexplorer/buildsystem.h> +#include <projectexplorer/deploymentdata.h> +#include <projectexplorer/devicesupport/devicemanager.h> +#include <projectexplorer/devicesupport/filetransfer.h> +#include <projectexplorer/devicesupport/idevice.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/runconfigurationaspects.h> +#include <projectexplorer/target.h> + +#include <utils/algorithm.h> +#include <utils/async.h> +#include <utils/process.h> +#include <utils/processinterface.h> + +using namespace ProjectExplorer; +using namespace Tasking; +using namespace Utils; + +namespace RemoteLinux::Internal { + +// RsyncDeployStep + +class GenericDeployStep : public AbstractRemoteLinuxDeployStep +{ +public: + GenericDeployStep(BuildStepList *bsl, Id id) + : AbstractRemoteLinuxDeployStep(bsl, id) + { + flags.setDisplayStyle(StringAspect::LineEditDisplay); + flags.setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); + flags.setLabelText(Tr::tr("Flags for rsync:")); + flags.setValue(FileTransferSetupData::defaultRsyncFlags()); + + ignoreMissingFiles.setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); + ignoreMissingFiles.setLabelText(Tr::tr("Ignore missing files:")); + ignoreMissingFiles.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); + + method.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + method.setDisplayName(Tr::tr("Transfer method:")); + method.addOption(Tr::tr("Use rsync if available. Otherwise use default transfer.")); + method.addOption(Tr::tr("Use sftp if available. Otherwise use default transfer.")); + method.addOption(Tr::tr("Use default transfer. This might be slow.")); + + setInternalInitializer([this]() -> expected_str<void> { + if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) { + // rsync transfer on the same device currently not implemented + // and typically not wanted. + return make_unexpected( + Tr::tr("rsync is only supported for transfers between different devices.")); + } + return isDeploymentPossible(); + }); + } + +private: + bool isDeploymentNecessary() const final; + GroupItem deployRecipe() final; + GroupItem mkdirTask(); + GroupItem transferTask(); + + StringAspect flags{this}; + BoolAspect ignoreMissingFiles{this}; + SelectionAspect method{this}; + + mutable FilesToTransfer m_files; +}; + +bool GenericDeployStep::isDeploymentNecessary() const +{ + const QList<DeployableFile> files = target()->deploymentData().allFiles(); + m_files.clear(); + for (const DeployableFile &f : files) + m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())}); + if (ignoreMissingFiles()) + Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); }); + return !m_files.empty(); +} + +GroupItem GenericDeployStep::mkdirTask() +{ + using ResultType = expected_str<void>; + + const auto onSetup = [this](Async<ResultType> &async) { + FilePaths remoteDirs; + for (const FileToTransfer &file : std::as_const(m_files)) + remoteDirs << file.m_target.parentDir(); + + FilePath::sort(remoteDirs); + FilePath::removeDuplicates(remoteDirs); + + async.setConcurrentCallData([remoteDirs](QPromise<ResultType> &promise) { + for (const FilePath &dir : remoteDirs) { + const expected_str<void> result = dir.ensureWritableDir(); + promise.addResult(result); + if (!result) + promise.future().cancel(); + } + }); + }; + + const auto onError = [this](const Async<ResultType> &async) { + const int numResults = async.future().resultCount(); + if (numResults == 0) { + addErrorMessage( + Tr::tr("Unknown error occurred while trying to create remote directories") + '\n'); + return; + } + + for (int i = 0; i < numResults; ++i) { + const ResultType result = async.future().resultAt(i); + if (!result.has_value()) + addErrorMessage(result.error()); + } + }; + + return AsyncTask<ResultType>(onSetup, {}, onError); +} + +static FileTransferMethod supportedTransferMethodFor(const FileToTransfer &fileToTransfer) +{ + auto sourceDevice = ProjectExplorer::DeviceManager::deviceForPath(fileToTransfer.m_source); + auto targetDevice = ProjectExplorer::DeviceManager::deviceForPath(fileToTransfer.m_target); + + if (sourceDevice && targetDevice) { + // TODO: Check if the devices can reach each other via their ip + if (sourceDevice->extraData(ProjectExplorer::Constants::SUPPORTS_RSYNC).toBool() + && targetDevice->extraData(ProjectExplorer::Constants::SUPPORTS_RSYNC).toBool()) { + return FileTransferMethod::Rsync; + } + + if (sourceDevice->extraData(ProjectExplorer::Constants::SUPPORTS_SFTP).toBool() + && targetDevice->extraData(ProjectExplorer::Constants::SUPPORTS_SFTP).toBool()) { + return FileTransferMethod::Sftp; + } + } + + return FileTransferMethod::GenericCopy; +} + +GroupItem GenericDeployStep::transferTask() +{ + const auto setupHandler = [this](FileTransfer &transfer) { + FileTransferMethod preferredTransferMethod = FileTransferMethod::Rsync; + if (method() == 0) + preferredTransferMethod = FileTransferMethod::Rsync; + else if (method() == 1) + preferredTransferMethod = FileTransferMethod::Sftp; + else + preferredTransferMethod = FileTransferMethod::GenericCopy; + + FileTransferMethod transferMethod = preferredTransferMethod; + + if (transferMethod != FileTransferMethod::GenericCopy) { + for (const FileToTransfer &fileToTransfer : m_files) { + const FileTransferMethod supportedMethod = supportedTransferMethodFor( + fileToTransfer); + + if (supportedMethod != preferredTransferMethod) { + transferMethod = FileTransferMethod::GenericCopy; + break; + } + } + } + + transfer.setTransferMethod(transferMethod); + + transfer.setRsyncFlags(flags()); + transfer.setFilesToTransfer(m_files); + connect(&transfer, &FileTransfer::progress, this, &GenericDeployStep::handleStdOutData); + }; + const auto errorHandler = [this](const FileTransfer &transfer) { + const ProcessResultData result = transfer.resultData(); + if (result.m_error == QProcess::FailedToStart) { + addErrorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString)); + } else if (result.m_exitStatus == QProcess::CrashExit) { + addErrorMessage(Tr::tr("rsync crashed.")); + } else if (result.m_exitCode != 0) { + addErrorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode) + + "\n" + result.m_errorString); + } + }; + return FileTransferTask(setupHandler, {}, errorHandler); +} + +GroupItem GenericDeployStep::deployRecipe() +{ + return Group { mkdirTask(), transferTask() }; +} + +// Factory + +GenericDeployStepFactory::GenericDeployStepFactory() +{ + registerStep<GenericDeployStep>(Constants::GenericDeployStepId); + setDisplayName(Tr::tr("Deploy files")); +} + +} // RemoteLinux::Internal diff --git a/src/plugins/remotelinux/genericdeploystep.h b/src/plugins/remotelinux/genericdeploystep.h new file mode 100644 index 00000000000..4967e2c7bc2 --- /dev/null +++ b/src/plugins/remotelinux/genericdeploystep.h @@ -0,0 +1,16 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <projectexplorer/buildstep.h> + +namespace RemoteLinux::Internal { + +class GenericDeployStepFactory : public ProjectExplorer::BuildStepFactory +{ +public: + GenericDeployStepFactory(); +}; + +} // namespace RemoteLinux::Internal diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp index 62dd58467ed..1bf020ecb23 100644 --- a/src/plugins/remotelinux/genericdirectuploadstep.cpp +++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp @@ -34,41 +34,28 @@ struct UploadStorage QList<DeployableFile> filesToUpload; }; -enum class IncrementalDeployment { Enabled, Disabled, NotSupported }; - class GenericDirectUploadStep : public AbstractRemoteLinuxDeployStep { public: - GenericDirectUploadStep(ProjectExplorer::BuildStepList *bsl, Id id) + GenericDirectUploadStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto incremental = addAspect<BoolAspect>(); - incremental->setSettingsKey("RemoteLinux.GenericDirectUploadStep.Incremental"); - incremental->setLabel(Tr::tr("Incremental deployment"), - BoolAspect::LabelPlacement::AtCheckBox); - incremental->setValue(true); - incremental->setDefaultValue(true); + incremental.setSettingsKey("RemoteLinux.GenericDirectUploadStep.Incremental"); + incremental.setLabelText(Tr::tr("Incremental deployment")); + incremental.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + incremental.setDefaultValue(true); - auto ignoreMissingFiles = addAspect<BoolAspect>(); - ignoreMissingFiles->setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles"); - ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files"), - BoolAspect::LabelPlacement::AtCheckBox); - ignoreMissingFiles->setValue(false); + ignoreMissingFiles.setSettingsKey("RemoteLinux.GenericDirectUploadStep.IgnoreMissingFiles"); + ignoreMissingFiles.setLabelText(Tr::tr("Ignore missing files")); + ignoreMissingFiles.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); - setInternalInitializer([this, incremental, ignoreMissingFiles] { - m_incremental = incremental->value() - ? IncrementalDeployment::Enabled : IncrementalDeployment::Disabled; - m_ignoreMissingFiles = ignoreMissingFiles->value(); + setInternalInitializer([this] { return isDeploymentPossible(); }); - - setRunPreparer([this] { - m_deployableFiles = target()->deploymentData().allFiles(); - }); } bool isDeploymentNecessary() const final; - Group deployRecipe() final; + GroupItem deployRecipe() final; QDateTime timestampFromStat(const DeployableFile &file, Process *statProc); @@ -83,9 +70,10 @@ public: GroupItem chmodTask(const DeployableFile &file); GroupItem chmodTree(const TreeStorage<UploadStorage> &storage); - IncrementalDeployment m_incremental = IncrementalDeployment::NotSupported; - bool m_ignoreMissingFiles = false; mutable QList<DeployableFile> m_deployableFiles; + + BoolAspect incremental{this}; + BoolAspect ignoreMissingFiles{this}; }; static QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployable) @@ -105,6 +93,7 @@ static QList<DeployableFile> collectFilesToUpload(const DeployableFile &deployab bool GenericDirectUploadStep::isDeploymentNecessary() const { + m_deployableFiles = target()->deploymentData().allFiles(); QList<DeployableFile> collected; for (int i = 0; i < m_deployableFiles.count(); ++i) collected.append(collectFilesToUpload(m_deployableFiles.at(i))); @@ -203,7 +192,7 @@ GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> & if (!file.localFilePath().exists()) { const QString message = Tr::tr("Local file \"%1\" does not exist.") .arg(file.localFilePath().toUserOutput()); - if (m_ignoreMissingFiles) { + if (ignoreMissingFiles()) { addWarningMessage(message); continue; } @@ -266,17 +255,15 @@ GroupItem GenericDirectUploadStep::chmodTree(const TreeStorage<UploadStorage> &s return TaskTreeTask(setupChmodHandler); } -Group GenericDirectUploadStep::deployRecipe() +GroupItem GenericDirectUploadStep::deployRecipe() { const auto preFilesToStat = [this](UploadStorage *storage) { QList<DeployableFile> filesToStat; for (const DeployableFile &file : std::as_const(m_deployableFiles)) { - if (m_incremental != IncrementalDeployment::Enabled || hasLocalFileChanged(file)) { + if (!incremental() || hasLocalFileChanged(file)) { storage->filesToUpload.append(file); continue; } - if (m_incremental == IncrementalDeployment::NotSupported) - continue; filesToStat << file; } return filesToStat; @@ -287,9 +274,8 @@ Group GenericDirectUploadStep::deployRecipe() storage->filesToUpload.append(file); }; - const auto postFilesToStat = [this](UploadStorage *storage) { - return m_incremental == IncrementalDeployment::NotSupported - ? QList<DeployableFile>() : storage->filesToUpload; + const auto postFilesToStat = [](UploadStorage *storage) { + return storage->filesToUpload; }; const auto postStatEndHandler = [this](UploadStorage *storage, const DeployableFile &file, const QDateTime ×tamp) { @@ -303,7 +289,7 @@ Group GenericDirectUploadStep::deployRecipe() const TreeStorage<UploadStorage> storage; const Group root { - Storage(storage), + Tasking::Storage(storage), statTree(storage, preFilesToStat, preStatEndHandler), uploadTask(storage), Group { diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp index 566cf3e510f..09895a7ad76 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp @@ -283,7 +283,7 @@ void GenericLinuxDeviceConfigurationWidget::initGui() m_portsWarningLabel->setToolTip(QLatin1String("<font color=\"red\">") + Tr::tr("You will need at least one port.") + QLatin1String("</font>")); m_keyFileLineEdit->setExpectedKind(PathChooser::File); - m_keyFileLineEdit->setHistoryCompleter(QLatin1String("Ssh.KeyFile.History")); + m_keyFileLineEdit->setHistoryCompleter("Ssh.KeyFile.History"); m_keyFileLineEdit->lineEdit()->setMinimumWidth(0); QRegularExpressionValidator * const portsValidator = new QRegularExpressionValidator(QRegularExpression(PortList::regularExpression()), this); diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp deleted file mode 100644 index 7bd047a590e..00000000000 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "genericlinuxdeviceconfigurationwizard.h" - -#include "genericlinuxdeviceconfigurationwizardpages.h" -#include "linuxdevice.h" -#include "remotelinux_constants.h" -#include "remotelinuxtr.h" - -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/devicesupport/sshparameters.h> - -#include <utils/fileutils.h> - -using namespace ProjectExplorer; - -namespace RemoteLinux { -namespace Internal { -enum PageId { SetupPageId, KeyDeploymentPageId, FinalPageId }; - -class GenericLinuxDeviceConfigurationWizardPrivate -{ -public: - GenericLinuxDeviceConfigurationWizardPrivate(QWidget *parent) - : setupPage(parent), keyDeploymentPage(parent), finalPage(parent) - { - } - - GenericLinuxDeviceConfigurationWizardSetupPage setupPage; - GenericLinuxDeviceConfigurationWizardKeyDeploymentPage keyDeploymentPage; - GenericLinuxDeviceConfigurationWizardFinalPage finalPage; - LinuxDevice::Ptr device; -}; -} // namespace Internal - -GenericLinuxDeviceConfigurationWizard::GenericLinuxDeviceConfigurationWizard(QWidget *parent) - : Utils::Wizard(parent), - d(new Internal::GenericLinuxDeviceConfigurationWizardPrivate(this)) -{ - setWindowTitle(Tr::tr("New Remote Linux Device Configuration Setup")); - setPage(Internal::SetupPageId, &d->setupPage); - setPage(Internal::KeyDeploymentPageId, &d->keyDeploymentPage); - setPage(Internal::FinalPageId, &d->finalPage); - d->finalPage.setCommitPage(true); - d->device = LinuxDevice::create(); - d->setupPage.setDevice(d->device); - d->keyDeploymentPage.setDevice(d->device); -} - -GenericLinuxDeviceConfigurationWizard::~GenericLinuxDeviceConfigurationWizard() -{ - delete d; -} - -IDevice::Ptr GenericLinuxDeviceConfigurationWizard::device() -{ - return d->device; -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h deleted file mode 100644 index df25a3b5861..00000000000 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizard.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "remotelinux_export.h" - -#include <projectexplorer/devicesupport/idevicefwd.h> -#include <utils/wizard.h> - -namespace RemoteLinux { -namespace Internal { class GenericLinuxDeviceConfigurationWizardPrivate; } - -class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizard : public Utils::Wizard -{ - Q_OBJECT - -public: - GenericLinuxDeviceConfigurationWizard(QWidget *parent = nullptr); - ~GenericLinuxDeviceConfigurationWizard() override; - - ProjectExplorer::IDevicePtr device(); - -private: - Internal::GenericLinuxDeviceConfigurationWizardPrivate * const d; -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp deleted file mode 100644 index 0a34683bdeb..00000000000 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.cpp +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "genericlinuxdeviceconfigurationwizardpages.h" - -#include "publickeydeploymentdialog.h" -#include "remotelinuxtr.h" -#include "sshkeycreationdialog.h" - -#include <projectexplorer/devicesupport/sshparameters.h> - -#include <utils/filepath.h> -#include <utils/fileutils.h> -#include <utils/fancylineedit.h> -#include <utils/layoutbuilder.h> -#include <utils/pathchooser.h> -#include <utils/utilsicons.h> - -#include <QHBoxLayout> -#include <QLabel> -#include <QPushButton> -#include <QSpinBox> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace RemoteLinux { -namespace Internal { - -class GenericLinuxDeviceConfigurationWizardSetupPagePrivate -{ -public: - FancyLineEdit *nameLineEdit; - FancyLineEdit *hostNameLineEdit; - QSpinBox *sshPortSpinBox; - FancyLineEdit *userNameLineEdit; - - LinuxDevice::Ptr device; -}; - -class GenericLinuxDeviceConfigurationWizardFinalPagePrivate -{ -public: - QLabel infoLabel; -}; - -} // namespace Internal - -GenericLinuxDeviceConfigurationWizardSetupPage::GenericLinuxDeviceConfigurationWizardSetupPage( - QWidget *parent) : - QWizardPage(parent), d(new Internal::GenericLinuxDeviceConfigurationWizardSetupPagePrivate) -{ - setTitle(Tr::tr("Connection")); - setWindowTitle(Tr::tr("WizardPage")); - - d->nameLineEdit = new FancyLineEdit(this); - d->nameLineEdit->setHistoryCompleter("DeviceName"); - - d->hostNameLineEdit = new FancyLineEdit(this); - d->hostNameLineEdit->setHistoryCompleter("HostName"); - - d->sshPortSpinBox = new QSpinBox(this); - - d->userNameLineEdit = new FancyLineEdit(this); - d->userNameLineEdit->setHistoryCompleter("UserName"); - - using namespace Layouting; - Form { - Tr::tr("The name to identify this configuration:"), d->nameLineEdit, br, - Tr::tr("The device's host name or IP address:"), d->hostNameLineEdit, st, br, - Tr::tr("The device's SSH port number:"), d->sshPortSpinBox, st, br, - Tr::tr("The username to log into the device:"), d->userNameLineEdit, st, br - }.attachTo(this); - - setSubTitle(QLatin1String(" ")); // For Qt bug (background color) - connect(d->nameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); - connect(d->hostNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); - connect(d->sshPortSpinBox, &QSpinBox::valueChanged, this, &QWizardPage::completeChanged); - connect(d->userNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); -} - -GenericLinuxDeviceConfigurationWizardSetupPage::~GenericLinuxDeviceConfigurationWizardSetupPage() -{ - delete d; -} - -void GenericLinuxDeviceConfigurationWizardSetupPage::initializePage() -{ - d->nameLineEdit->setText(d->device->displayName()); - d->hostNameLineEdit->setText(d->device->sshParameters().host()); - d->sshPortSpinBox->setValue(22); - d->sshPortSpinBox->setRange(1, 65535); - d->userNameLineEdit->setText(d->device->sshParameters().userName()); -} - -bool GenericLinuxDeviceConfigurationWizardSetupPage::isComplete() const -{ - return !configurationName().isEmpty() - && !d->hostNameLineEdit->text().trimmed().isEmpty() - && !d->userNameLineEdit->text().trimmed().isEmpty(); -} - -bool GenericLinuxDeviceConfigurationWizardSetupPage::validatePage() -{ - d->device->setDisplayName(configurationName()); - SshParameters sshParams = d->device->sshParameters(); - sshParams.setHost(d->hostNameLineEdit->text().trimmed()); - sshParams.setUserName(d->userNameLineEdit->text().trimmed()); - sshParams.setPort(d->sshPortSpinBox->value()); - d->device->setSshParameters(sshParams); - return true; -} - -QString GenericLinuxDeviceConfigurationWizardSetupPage::configurationName() const -{ - return d->nameLineEdit->text().trimmed(); -} - -void GenericLinuxDeviceConfigurationWizardSetupPage::setDevice(const LinuxDevice::Ptr &device) -{ - d->device = device; -} - -GenericLinuxDeviceConfigurationWizardFinalPage::GenericLinuxDeviceConfigurationWizardFinalPage( - QWidget *parent) - : QWizardPage(parent), d(new Internal::GenericLinuxDeviceConfigurationWizardFinalPagePrivate) -{ - setTitle(Tr::tr("Summary")); - setSubTitle(QLatin1String(" ")); // For Qt bug (background color) - d->infoLabel.setWordWrap(true); - auto const layout = new QVBoxLayout(this); - layout->addWidget(&d->infoLabel); -} - -GenericLinuxDeviceConfigurationWizardFinalPage::~GenericLinuxDeviceConfigurationWizardFinalPage() -{ - delete d; -} - -void GenericLinuxDeviceConfigurationWizardFinalPage::initializePage() -{ - d->infoLabel.setText(infoText()); -} - -QString GenericLinuxDeviceConfigurationWizardFinalPage::infoText() const -{ - return Tr::tr("The new device configuration will now be created.\n" - "In addition, device connectivity will be tested."); -} - -struct GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::Private -{ - FilePaths defaultKeys() const - { - const FilePath baseDir = FileUtils::homePath() / ".ssh"; - return {baseDir / "id_rsa", baseDir / "id_ecdsa", baseDir / "id_ed25519"}; - } - - PathChooser keyFileChooser; - QLabel iconLabel; - LinuxDevice::Ptr device; -}; - -GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(QWidget *parent) - : QWizardPage(parent), d(new Private) -{ - setTitle(Tr::tr("Key Deployment")); - setSubTitle(" "); - const QString info = Tr::tr("We recommend that you log into your device using public key " - "authentication.\n" - "If your device is already set up for this, you do not have to do " - "anything here.\n" - "Otherwise, please deploy the public key for the private key " - "with which to connect in the future.\n" - "If you do not have a private key yet, you can also " - "create one here."); - d->keyFileChooser.setExpectedKind(PathChooser::File); - d->keyFileChooser.setHistoryCompleter("Ssh.KeyFile.History"); - d->keyFileChooser.setPromptDialogTitle(Tr::tr("Choose a Private Key File")); - auto const deployButton = new QPushButton(Tr::tr("Deploy Public Key"), this); - connect(deployButton, &QPushButton::clicked, - this, &GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::deployKey); - auto const createButton = new QPushButton(Tr::tr("Create New Key Pair"), this); - connect(createButton, &QPushButton::clicked, - this, &GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::createKey); - auto const mainLayout = new QVBoxLayout(this); - auto const keyLayout = new QHBoxLayout; - auto const deployLayout = new QHBoxLayout; - mainLayout->addWidget(new QLabel(info)); - keyLayout->addWidget(new QLabel(Tr::tr("Private key file:"))); - keyLayout->addWidget(&d->keyFileChooser); - keyLayout->addWidget(createButton); - keyLayout->addStretch(); - mainLayout->addLayout(keyLayout); - deployLayout->addWidget(deployButton); - deployLayout->addWidget(&d->iconLabel); - deployLayout->addStretch(); - mainLayout->addLayout(deployLayout); - connect(&d->keyFileChooser, &PathChooser::textChanged, this, [this, deployButton] { - deployButton->setEnabled(d->keyFileChooser.filePath().exists()); - d->iconLabel.clear(); - emit completeChanged(); - }); - for (const FilePath &defaultKey : d->defaultKeys()) { - if (defaultKey.exists()) { - d->keyFileChooser.setFilePath(defaultKey); - break; - } - } -} - -GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::~GenericLinuxDeviceConfigurationWizardKeyDeploymentPage() -{ - delete d; -} - -void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::setDevice(const LinuxDevice::Ptr &device) -{ - d->device = device; -} - -void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::initializePage() -{ - if (!d->device->sshParameters().privateKeyFile.isEmpty()) - d->keyFileChooser.setFilePath(privateKeyFilePath()); - d->iconLabel.clear(); -} - -bool GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::isComplete() const -{ - return d->keyFileChooser.filePath().toString().isEmpty() || d->keyFileChooser.filePath().exists(); -} - -bool GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::validatePage() -{ - if (!d->defaultKeys().contains(d->keyFileChooser.filePath())) { - SshParameters sshParams = d->device->sshParameters(); - sshParams.authenticationType = SshParameters::AuthenticationTypeSpecificKey; - sshParams.privateKeyFile = d->keyFileChooser.filePath(); - d->device->setSshParameters(sshParams); - } - return true; -} - -FilePath GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::privateKeyFilePath() const -{ - return d->keyFileChooser.filePath(); -} - -void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::createKey() -{ - SshKeyCreationDialog dlg(this); - if (dlg.exec() == QDialog::Accepted) - d->keyFileChooser.setFilePath(dlg.privateKeyFilePath()); -} - -void GenericLinuxDeviceConfigurationWizardKeyDeploymentPage::deployKey() -{ - PublicKeyDeploymentDialog dlg(d->device, privateKeyFilePath().stringAppended(".pub"), this); - d->iconLabel.setPixmap((dlg.exec() == QDialog::Accepted ? Icons::OK : Icons::BROKEN).pixmap()); -} - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h deleted file mode 100644 index 2e2d5298913..00000000000 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwizardpages.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "remotelinux_export.h" - -#include "linuxdevice.h" - -#include <QWizardPage> - -namespace RemoteLinux { -class LinuxDevice; -namespace Internal { -class GenericLinuxDeviceConfigurationWizardSetupPagePrivate; -class GenericLinuxDeviceConfigurationWizardFinalPagePrivate; -} // namespace Internal - -class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardSetupPage : public QWizardPage -{ - Q_OBJECT - -public: - explicit GenericLinuxDeviceConfigurationWizardSetupPage(QWidget *parent = nullptr); - ~GenericLinuxDeviceConfigurationWizardSetupPage() override; - - void setDevice(const LinuxDevice::Ptr &device); - -private: - void initializePage() override; - bool isComplete() const override; - bool validatePage() override; - - QString configurationName() const; - - Internal::GenericLinuxDeviceConfigurationWizardSetupPagePrivate * const d; -}; - -class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardKeyDeploymentPage : public QWizardPage -{ - Q_OBJECT - -public: - explicit GenericLinuxDeviceConfigurationWizardKeyDeploymentPage(QWidget *parent = nullptr); - ~GenericLinuxDeviceConfigurationWizardKeyDeploymentPage() override; - - void setDevice(const LinuxDevice::Ptr &device); - -private: - void initializePage() override; - bool isComplete() const override; - bool validatePage() override; - - Utils::FilePath privateKeyFilePath() const; - void createKey(); - void deployKey(); - - struct Private; - Private * const d; -}; - -class REMOTELINUX_EXPORT GenericLinuxDeviceConfigurationWizardFinalPage final : public QWizardPage -{ - Q_OBJECT -public: - GenericLinuxDeviceConfigurationWizardFinalPage(QWidget *parent = nullptr); - ~GenericLinuxDeviceConfigurationWizardFinalPage() override; - -protected: - virtual QString infoText() const; - -private: - void initializePage() override; - - Internal::GenericLinuxDeviceConfigurationWizardFinalPagePrivate * const d; -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp index d3ec72e8895..b70e597fb84 100644 --- a/src/plugins/remotelinux/killappstep.cpp +++ b/src/plugins/remotelinux/killappstep.cpp @@ -9,9 +9,10 @@ #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runcontrol.h> +#include <projectexplorer/runconfiguration.h> #include <projectexplorer/target.h> +#include <utils/processinterface.h> #include <utils/qtcassert.h> using namespace ProjectExplorer; @@ -27,23 +28,23 @@ public: { setWidgetExpandedByDefault(false); - setInternalInitializer([this] { + setInternalInitializer([this]() -> expected_str<void> { Target * const theTarget = target(); - QTC_ASSERT(theTarget, return CheckResult::failure()); + QTC_ASSERT(theTarget, return make_unexpected(QString())); RunConfiguration * const rc = theTarget->activeRunConfiguration(); m_remoteExecutable = rc ? rc->runnable().command.executable() : FilePath(); - return CheckResult::success(); + return {}; }); } private: bool isDeploymentNecessary() const final { return !m_remoteExecutable.isEmpty(); } - Group deployRecipe() final; + GroupItem deployRecipe() final; FilePath m_remoteExecutable; }; -Group KillAppStep::deployRecipe() +GroupItem KillAppStep::deployRecipe() { const auto setupHandler = [this](DeviceProcessKiller &killer) { killer.setProcessPath(m_remoteExecutable); @@ -57,7 +58,7 @@ Group KillAppStep::deployRecipe() addProgressMessage(Tr::tr("Failed to kill remote application. " "Assuming it was not running.")); }; - return Group { DeviceProcessKillerTask(setupHandler, doneHandler, errorHandler) }; + return DeviceProcessKillerTask(setupHandler, doneHandler, errorHandler); } KillAppStepFactory::KillAppStepFactory() diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 97ea99a4dca..565b0675503 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -4,13 +4,13 @@ #include "linuxdevice.h" #include "genericlinuxdeviceconfigurationwidget.h" -#include "genericlinuxdeviceconfigurationwizard.h" #include "linuxdevicetester.h" #include "linuxprocessinterface.h" #include "publickeydeploymentdialog.h" #include "remotelinux_constants.h" #include "remotelinuxsignaloperation.h" #include "remotelinuxtr.h" +#include "sshdevicewizard.h" #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> @@ -37,6 +37,7 @@ #include <QDateTime> #include <QLoggingCategory> +#include <QMessageBox> #include <QMutex> #include <QReadWriteLock> #include <QRegularExpression> @@ -293,6 +294,12 @@ public: LinuxDevicePrivate *m_dev; }; +class LinuxDeviceSettings : public DeviceSettings +{ +public: + LinuxDeviceSettings() { displayName.setDefaultValue(Tr::tr("Remote Linux Device")); } +}; + class LinuxDevicePrivate { public: @@ -420,7 +427,7 @@ void SshProcessInterface::emitStarted(qint64 processId) void SshProcessInterface::killIfRunning() { - if (d->m_killed || d->m_process.state() != QProcess::Running) + if (d->m_killed || d->m_process.state() != QProcess::Running || d->m_processId == 0) return; sendControlSignal(ControlSignal::Kill); d->m_killed = true; @@ -442,9 +449,12 @@ bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArra process.setCommand(cmd); process.setWriteData(data); process.start(); - bool isFinished = process.waitForFinished(2000); // TODO: it may freeze on some devices - // otherwise we may start producing killers for killers - QTC_CHECK(isFinished); + bool isFinished = process.waitForFinished(2000); // It may freeze on some devices + if (!isFinished) { + Core::MessageManager::writeFlashing(tr("Can't send control signal to the %1 device. " + "The device might have been disconnected.") + .arg(d->m_device->displayName())); + } return isFinished; } @@ -837,11 +847,17 @@ public: << m_displaylessSshParameters.host()); cmd.addArg("/bin/sh"); - m_shell.reset(new LinuxDeviceShell(cmd, FilePath::fromString(QString("ssh://%1/").arg(parameters.userAtHost())))); + m_shell.reset(new LinuxDeviceShell(cmd, + FilePath::fromString(QString("ssh://%1/").arg(parameters.userAtHostAndPort())))); connect(m_shell.get(), &DeviceShell::done, this, [this] { m_shell.release()->deleteLater(); }); - return m_shell->start(); + auto result = m_shell->start(); + if (!result) { + qCWarning(linuxDeviceLog) << "Failed to start shell for:" << parameters.userAtHostAndPort() + << ", " << result.error(); + } + return result.has_value(); } // Call me with shell mutex locked @@ -937,7 +953,8 @@ private: // LinuxDevice LinuxDevice::LinuxDevice() - : d(new LinuxDevicePrivate(this)) + : IDevice(std::make_unique<LinuxDeviceSettings>()) + , d(new LinuxDevicePrivate(this)) { setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Remote Linux")); @@ -952,13 +969,14 @@ LinuxDevice::LinuxDevice() setSshParameters(sshParams); addDeviceAction({Tr::tr("Deploy Public Key..."), [](const IDevice::Ptr &device, QWidget *parent) { - if (auto d = PublicKeyDeploymentDialog::createDialog(device, parent)) { + if (auto d = Internal::PublicKeyDeploymentDialog::createDialog(device, parent)) { d->exec(); delete d; } }}); - setOpenTerminal([this](const Environment &env, const FilePath &workingDir) { + setOpenTerminal([this](const Environment &env, + const FilePath &workingDir) -> expected_str<void> { Process proc; // If we will not set any environment variables, we can leave out the shell executable @@ -972,10 +990,15 @@ LinuxDevice::LinuxDevice() proc.setEnvironment(env); proc.setWorkingDirectory(workingDir); proc.start(); + + return {}; }); addDeviceAction({Tr::tr("Open Remote Shell"), [](const IDevice::Ptr &device, QWidget *) { - device->openTerminal(Environment(), FilePath()); + expected_str<void> result = device->openTerminal(Environment(), FilePath()); + + if (!result) + QMessageBox::warning(nullptr, Tr::tr("Error"), result.error()); }}); } @@ -995,11 +1018,6 @@ IDeviceWidget *LinuxDevice::createWidget() return new Internal::GenericLinuxDeviceConfigurationWidget(sharedFromThis()); } -DeviceProcessList *LinuxDevice::createProcessListModel(QObject *parent) const -{ - return new ProcessList(sharedFromThis(), parent); -} - DeviceTester *LinuxDevice::createDeviceTester() const { return new GenericLinuxDeviceTester; @@ -1020,16 +1038,21 @@ QString LinuxDevice::userAtHost() const return sshParameters().userAtHost(); } +QString LinuxDevice::userAtHostAndPort() const +{ + return sshParameters().userAtHostAndPort(); +} + FilePath LinuxDevice::rootPath() const { - return FilePath::fromParts(u"ssh", userAtHost(), u"/"); + return FilePath::fromParts(u"ssh", userAtHostAndPort(), u"/"); } bool LinuxDevice::handlesFile(const FilePath &filePath) const { if (filePath.scheme() == u"device" && filePath.host() == id().toString()) return true; - if (filePath.scheme() == u"ssh" && filePath.host() == userAtHost()) + if (filePath.scheme() == u"ssh" && filePath.host() == userAtHostAndPort()) return true; return false; } @@ -1487,8 +1510,6 @@ void LinuxDevice::checkOsType() namespace Internal { -// Factory - LinuxDeviceFactory::LinuxDeviceFactory() : IDeviceFactory(Constants::GenericLinuxOsType) { @@ -1496,11 +1517,12 @@ LinuxDeviceFactory::LinuxDeviceFactory() setIcon(QIcon()); setConstructionFunction(&LinuxDevice::create); setQuickCreationAllowed(true); - setCreator([] { - GenericLinuxDeviceConfigurationWizard wizard(Core::ICore::dialogParent()); + setCreator([]() -> IDevice::Ptr { + const IDevice::Ptr device = LinuxDevice::create(); + SshDeviceWizard wizard(Tr::tr("New Remote Linux Device Configuration Setup"), device); if (wizard.exec() != QDialog::Accepted) - return IDevice::Ptr(); - return wizard.device(); + return {}; + return device; }); } diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 2094b426b4c..40ce9c4860e 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -23,13 +23,13 @@ public: ProjectExplorer::IDeviceWidget *createWidget() override; bool canCreateProcessModel() const override { return true; } - ProjectExplorer::DeviceProcessList *createProcessListModel(QObject *parent) const override; bool hasDeviceTester() const override { return true; } ProjectExplorer::DeviceTester *createDeviceTester() const override; ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override; bool usableAsBuildDevice() const override; QString userAtHost() const; + QString userAtHostAndPort() const; Utils::FilePath rootPath() const override; diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index 5392fb4245c..e908541aa57 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -3,11 +3,11 @@ #include "linuxdevicetester.h" -#include "remotelinux_constants.h" #include "remotelinuxtr.h" #include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <projectexplorer/devicesupport/filetransfer.h> +#include <projectexplorer/projectexplorerconstants.h> #include <utils/algorithm.h> #include <utils/process.h> @@ -177,9 +177,9 @@ GroupItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod metho const QString methodName = FileTransfer::transferMethodName(method); emit q->progressMessage(Tr::tr("\"%1\" is functional.\n").arg(methodName)); if (method == FileTransferMethod::Rsync) - m_device->setExtraData(Constants::SupportsRSync, true); + m_device->setExtraData(Constants::SUPPORTS_RSYNC, true); else if (method == FileTransferMethod::Sftp) - m_device->setExtraData(Constants::SupportsSftp, true); + m_device->setExtraData(Constants::SUPPORTS_SFTP, true); else storage->useGenericCopy = true; }; @@ -188,29 +188,34 @@ GroupItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod metho const ProcessResultData resultData = transfer.resultData(); QString error; if (resultData.m_error == QProcess::FailedToStart) { - error = Tr::tr("Failed to start \"%1\": %2\n").arg(methodName, resultData.m_errorString); + error = Tr::tr("Failed to start \"%1\": %2").arg(methodName, resultData.m_errorString) + + "\n"; } else if (resultData.m_exitStatus == QProcess::CrashExit) { - error = Tr::tr("\"%1\" crashed.\n").arg(methodName); + error = Tr::tr("\"%1\" crashed.").arg(methodName) + "\n"; } else if (resultData.m_exitCode != 0) { - error = Tr::tr("\"%1\" failed with exit code %2: %3\n") - .arg(methodName).arg(resultData.m_exitCode).arg(resultData.m_errorString); + error = Tr::tr("\"%1\" failed with exit code %2: %3") + .arg(methodName) + .arg(resultData.m_exitCode) + .arg(resultData.m_errorString) + + "\n"; } emit q->errorMessage(error); if (method == FileTransferMethod::Rsync) - m_device->setExtraData(Constants::SupportsRSync, false); + m_device->setExtraData(Constants::SUPPORTS_RSYNC, false); else if (method == FileTransferMethod::Sftp) - m_device->setExtraData(Constants::SupportsSftp, false); + m_device->setExtraData(Constants::SUPPORTS_SFTP, false); - const QVariant supportsRSync = m_device->extraData(Constants::SupportsRSync); - const QVariant supportsSftp = m_device->extraData(Constants::SupportsSftp); + const QVariant supportsRSync = m_device->extraData(Constants::SUPPORTS_RSYNC); + const QVariant supportsSftp = m_device->extraData(Constants::SUPPORTS_SFTP); if (supportsRSync.isValid() && !supportsRSync.toBool() && supportsSftp.isValid() && !supportsSftp.toBool()) { const QString generic = FileTransfer::transferMethodName(FileTransferMethod::GenericCopy); const QString sftp = FileTransfer::transferMethodName(FileTransferMethod::Sftp); const QString rsync = FileTransfer::transferMethodName(FileTransferMethod::Rsync); emit q->progressMessage(Tr::tr("\"%1\" will be used for deployment, because \"%2\" " - "and \"%3\" are not available.\n") - .arg(generic, sftp, rsync)); + "and \"%3\" are not available.") + .arg(generic, sftp, rsync) + + "\n"); } }; return FileTransferTestTask(setup, done, error); @@ -219,16 +224,16 @@ GroupItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod metho GroupItem GenericLinuxDeviceTesterPrivate::transferTasks() const { TreeStorage<TransferStorage> storage; - return Group { - continueOnDone, - Storage(storage), - transferTask(FileTransferMethod::GenericCopy, storage), - transferTask(FileTransferMethod::Sftp, storage), - transferTask(FileTransferMethod::Rsync, storage), - onGroupError([this] { emit q->errorMessage(Tr::tr("Deployment to this device will not " - "work out of the box.\n")); - }) - }; + return Group{continueOnDone, + Tasking::Storage(storage), + transferTask(FileTransferMethod::GenericCopy, storage), + transferTask(FileTransferMethod::Sftp, storage), + transferTask(FileTransferMethod::Rsync, storage), + onGroupError([this] { + emit q->errorMessage(Tr::tr("Deployment to this device will not " + "work out of the box.") + + "\n"); + })}; } GroupItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName) const diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index a93fccba41b..a061bc4215a 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -11,7 +11,8 @@ #include <projectexplorer/buildsystem.h> #include <projectexplorer/deployconfiguration.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/makestep.h> #include <projectexplorer/processparameters.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> @@ -23,25 +24,45 @@ #include <utils/process.h> #include <utils/qtcassert.h> -#include <QDirIterator> -#include <QFileInfo> #include <QSet> #include <QTemporaryDir> using namespace ProjectExplorer; using namespace Utils; -namespace RemoteLinux { +namespace RemoteLinux::Internal { -const char MakeAspectId[] = "RemoteLinux.MakeInstall.Make"; -const char InstallRootAspectId[] = "RemoteLinux.MakeInstall.InstallRoot"; -const char CleanInstallRootAspectId[] = "RemoteLinux.MakeInstall.CleanInstallRoot"; -const char FullCommandLineAspectId[] = "RemoteLinux.MakeInstall.FullCommandLine"; -const char CustomCommandLineAspectId[] = "RemoteLinux.MakeInstall.CustomCommandLine"; +class MakeInstallStep : public MakeStep +{ +public: + MakeInstallStep(BuildStepList *parent, Id id); + +private: + void fromMap(const Store &map) override; + QWidget *createConfigWidget() override; + bool init() override; + Tasking::GroupItem runRecipe() final; + bool isJobCountSupported() const override { return false; } + + void updateCommandFromAspect(); + void updateArgsFromAspect(); + void updateFullCommandLine(); + void updateFromCustomCommandLineAspect(); + + ExecutableAspect m_makeBinary{this}; + FilePathAspect m_installRoot{this}; + BoolAspect m_cleanInstallRoot{this}; + StringAspect m_fullCommand{this}; + StringAspect m_customCommand{this}; + + DeploymentData m_deploymentData; + bool m_noInstallTarget = false; + bool m_isCmakeProject = false; +}; MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent, id) { - m_makeCommandAspect.setVisible(false); + m_makeBinary.setVisible(false); m_buildTargetsAspect.setVisible(false); m_userArgumentsAspect.setVisible(false); m_overrideMakeflagsAspect.setVisible(false); @@ -61,59 +82,49 @@ MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent rootPath = FilePath::fromString(tmpDir.path()); } - const auto makeAspect = addAspect<ExecutableAspect>(parent->target(), - ExecutableAspect::BuildDevice); - makeAspect->setId(MakeAspectId); - makeAspect->setSettingsKey(MakeAspectId); - makeAspect->setDisplayStyle(StringAspect::PathChooserDisplay); - makeAspect->setLabelText(Tr::tr("Command:")); - connect(makeAspect, &ExecutableAspect::changed, + m_makeBinary.setDeviceSelector(parent->target(), ExecutableAspect::BuildDevice); + m_makeBinary.setSettingsKey("RemoteLinux.MakeInstall.Make"); + m_makeBinary.setReadOnly(false); + m_makeBinary.setLabelText(Tr::tr("Command:")); + connect(&m_makeBinary, &BaseAspect::changed, this, &MakeInstallStep::updateCommandFromAspect); - const auto installRootAspect = addAspect<FilePathAspect>(); - installRootAspect->setId(InstallRootAspectId); - installRootAspect->setSettingsKey(InstallRootAspectId); - installRootAspect->setExpectedKind(PathChooser::Directory); - installRootAspect->setLabelText(Tr::tr("Install root:")); - installRootAspect->setFilePath(rootPath); - connect(installRootAspect, &StringAspect::changed, + m_installRoot.setSettingsKey("RemoteLinux.MakeInstall.InstallRoot"); + m_installRoot.setExpectedKind(PathChooser::Directory); + m_installRoot.setLabelText(Tr::tr("Install root:")); + m_installRoot.setValue(rootPath); + connect(&m_installRoot, &BaseAspect::changed, this, &MakeInstallStep::updateArgsFromAspect); - const auto cleanInstallRootAspect = addAspect<BoolAspect>(); - cleanInstallRootAspect->setId(CleanInstallRootAspectId); - cleanInstallRootAspect->setSettingsKey(CleanInstallRootAspectId); - cleanInstallRootAspect->setLabel(Tr::tr("Clean install root first:"), - BoolAspect::LabelPlacement::InExtraLabel); - cleanInstallRootAspect->setValue(true); + m_cleanInstallRoot.setSettingsKey("RemoteLinux.MakeInstall.CleanInstallRoot"); + m_cleanInstallRoot.setLabelText(Tr::tr("Clean install root first:")); + m_cleanInstallRoot.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel); + m_cleanInstallRoot.setDefaultValue(true); - const auto commandLineAspect = addAspect<StringAspect>(); - commandLineAspect->setId(FullCommandLineAspectId); - commandLineAspect->setDisplayStyle(StringAspect::LabelDisplay); - commandLineAspect->setLabelText(Tr::tr("Full command line:")); + m_fullCommand.setDisplayStyle(StringAspect::LabelDisplay); + m_fullCommand.setLabelText(Tr::tr("Full command line:")); - const auto customCommandLineAspect = addAspect<StringAspect>(); - customCommandLineAspect->setId(CustomCommandLineAspectId); - customCommandLineAspect->setSettingsKey(CustomCommandLineAspectId); - customCommandLineAspect->setDisplayStyle(StringAspect::LineEditDisplay); - customCommandLineAspect->setLabelText(Tr::tr("Custom command line:")); - customCommandLineAspect->makeCheckable(StringAspect::CheckBoxPlacement::Top, - Tr::tr("Use custom command line instead:"), - "RemoteLinux.MakeInstall.EnableCustomCommandLine"); + m_customCommand.setSettingsKey("RemoteLinux.MakeInstall.CustomCommandLine"); + m_customCommand.setDisplayStyle(StringAspect::LineEditDisplay); + m_customCommand.setLabelText(Tr::tr("Custom command line:")); + m_customCommand.makeCheckable(CheckBoxPlacement::Top, + Tr::tr("Use custom command line instead:"), + "RemoteLinux.MakeInstall.EnableCustomCommandLine"); const auto updateCommand = [this] { updateCommandFromAspect(); updateArgsFromAspect(); updateFromCustomCommandLineAspect(); }; - connect(customCommandLineAspect, &StringAspect::checkedChanged, this, updateCommand); - connect(customCommandLineAspect, &StringAspect::changed, + connect(&m_customCommand, &StringAspect::checkedChanged, this, updateCommand); + connect(&m_customCommand, &StringAspect::changed, this, &MakeInstallStep::updateFromCustomCommandLineAspect); connect(target(), &Target::buildSystemUpdated, this, updateCommand); const MakeInstallCommand cmd = buildSystem()->makeInstallCommand(rootPath); QTC_ASSERT(!cmd.command.isEmpty(), return); - makeAspect->setExecutable(cmd.command.executable()); + m_makeBinary.setExecutable(cmd.command.executable()); connect(this, &BuildStep::addOutput, this, [this](const QString &string, OutputFormat format) { // When using Makefiles: "No rule to make target 'install'" @@ -134,12 +145,12 @@ bool MakeInstallStep::init() if (!MakeStep::init()) return false; - const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed? + const FilePath rootDir = makeCommand().withNewPath(m_installRoot().path()); // FIXME: Needed? if (rootDir.isEmpty()) { emit addTask(BuildSystemTask(Task::Error, Tr::tr("You must provide an install root."))); return false; } - if (cleanInstallRoot() && !rootDir.removeRecursively()) { + if (m_cleanInstallRoot() && !rootDir.removeRecursively()) { emit addTask(BuildSystemTask(Task::Error, Tr::tr("The install root \"%1\" could not be cleaned.") .arg(rootDir.displayName()))); @@ -177,10 +188,12 @@ bool MakeInstallStep::init() return true; } -void MakeInstallStep::finish(ProcessResult result) +Tasking::GroupItem MakeInstallStep::runRecipe() { - if (isSuccess(result)) { - const FilePath rootDir = makeCommand().withNewPath(installRoot().path()); // FIXME: Needed? + using namespace Tasking; + + const auto onDone = [this] { + const FilePath rootDir = makeCommand().withNewPath(m_installRoot().path()); // FIXME: Needed? m_deploymentData = DeploymentData(); m_deploymentData.setLocalInstallRoot(rootDir); @@ -192,46 +205,39 @@ void MakeInstallStep::finish(ProcessResult result) auto handleFile = [this, &appFileNames, startPos](const FilePath &filePath) { const DeployableFile::Type type = appFileNames.contains(filePath.fileName()) - ? DeployableFile::TypeExecutable - : DeployableFile::TypeNormal; + ? DeployableFile::TypeExecutable : DeployableFile::TypeNormal; const QString targetDir = filePath.parentDir().path().mid(startPos); m_deploymentData.addFile(filePath, targetDir, type); return IterationPolicy::Continue; }; - rootDir.iterateDirectory(handleFile, - {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories}); + rootDir.iterateDirectory( + handleFile, {{}, QDir::Files | QDir::Hidden, QDirIterator::Subdirectories}); buildSystem()->setDeploymentData(m_deploymentData); - } else if (m_noInstallTarget && m_isCmakeProject) { - emit addTask(DeploymentTask(Task::Warning, Tr::tr("You need to add an install statement " - "to your CMakeLists.txt file for deployment to work."))); - } - MakeStep::finish(result); -} + }; + const auto onError = [this] { + if (m_noInstallTarget && m_isCmakeProject) { + emit addTask(DeploymentTask(Task::Warning, Tr::tr("You need to add an install " + "statement to your CMakeLists.txt file for deployment to work."))); + } + }; -FilePath MakeInstallStep::installRoot() const -{ - return static_cast<StringAspect *>(aspect(InstallRootAspectId))->filePath(); -} - -bool MakeInstallStep::cleanInstallRoot() const -{ - return static_cast<BoolAspect *>(aspect(CleanInstallRootAspectId))->value(); + return Group { onGroupDone(onDone), onGroupError(onError), defaultProcessTask() }; } void MakeInstallStep::updateCommandFromAspect() { - if (customCommandLineAspect()->isChecked()) + if (m_customCommand.isChecked()) return; - setMakeCommand(aspect<ExecutableAspect>()->executable()); + setMakeCommand(m_makeBinary()); updateFullCommandLine(); } void MakeInstallStep::updateArgsFromAspect() { - if (customCommandLineAspect()->isChecked()) + if (m_customCommand.isChecked()) return; - const CommandLine cmd = buildSystem()->makeInstallCommand(installRoot()).command; + const CommandLine cmd = buildSystem()->makeInstallCommand(m_installRoot()).command; setUserArguments(cmd.arguments()); updateFullCommandLine(); } @@ -239,32 +245,26 @@ void MakeInstallStep::updateArgsFromAspect() void MakeInstallStep::updateFullCommandLine() { CommandLine cmd{makeExecutable(), userArguments(), CommandLine::Raw}; - static_cast<StringAspect *>(aspect(FullCommandLineAspectId))->setValue(cmd.toUserOutput()); + m_fullCommand.setValue(cmd.toUserOutput()); } void MakeInstallStep::updateFromCustomCommandLineAspect() { - const StringAspect * const aspect = customCommandLineAspect(); - if (!aspect->isChecked()) + if (m_customCommand.isChecked()) return; - const QStringList tokens = ProcessArgs::splitArgs(aspect->value(), HostOsInfo::hostOs()); + const QStringList tokens = ProcessArgs::splitArgs(m_customCommand(), HostOsInfo::hostOs()); setMakeCommand(tokens.isEmpty() ? FilePath() : FilePath::fromString(tokens.first())); setUserArguments(ProcessArgs::joinArgs(tokens.mid(1))); } -StringAspect *MakeInstallStep::customCommandLineAspect() const +void MakeInstallStep::fromMap(const Store &map) { - return static_cast<StringAspect *>(aspect(CustomCommandLineAspectId)); -} - -bool MakeInstallStep::fromMap(const QVariantMap &map) -{ - if (!MakeStep::fromMap(map)) - return false; + MakeStep::fromMap(map); + if (hasError()) + return; updateCommandFromAspect(); updateArgsFromAspect(); updateFromCustomCommandLineAspect(); - return true; } // Factory @@ -275,4 +275,4 @@ MakeInstallStepFactory::MakeInstallStepFactory() setDisplayName(Tr::tr("Install into temporary host directory")); } -} // RemoteLinux +} // RemoteLinux::Internal diff --git a/src/plugins/remotelinux/makeinstallstep.h b/src/plugins/remotelinux/makeinstallstep.h index 3be2d5df979..d2ffed1356a 100644 --- a/src/plugins/remotelinux/makeinstallstep.h +++ b/src/plugins/remotelinux/makeinstallstep.h @@ -3,46 +3,14 @@ #pragma once -#include "remotelinux_export.h" +#include <projectexplorer/buildstep.h> -#include <projectexplorer/deploymentdata.h> -#include <projectexplorer/makestep.h> +namespace RemoteLinux::Internal { -namespace RemoteLinux { - -class REMOTELINUX_EXPORT MakeInstallStep : public ProjectExplorer::MakeStep -{ - Q_OBJECT -public: - MakeInstallStep(ProjectExplorer::BuildStepList *parent, Utils::Id id); - -private: - bool fromMap(const QVariantMap &map) override; - QWidget *createConfigWidget() override; - bool init() override; - void finish(Utils::ProcessResult result) override; - bool isJobCountSupported() const override { return false; } - - Utils::FilePath installRoot() const; - bool cleanInstallRoot() const; - - void updateCommandFromAspect(); - void updateArgsFromAspect(); - void updateFullCommandLine(); - void updateFromCustomCommandLineAspect(); - - Utils::StringAspect *customCommandLineAspect() const; - - ProjectExplorer::DeploymentData m_deploymentData; - bool m_noInstallTarget = false; - bool m_isCmakeProject = false; -}; - -class REMOTELINUX_EXPORT MakeInstallStepFactory - : public ProjectExplorer::BuildStepFactory +class MakeInstallStepFactory : public ProjectExplorer::BuildStepFactory { public: MakeInstallStepFactory(); }; -} // namespace RemoteLinux +} // namespace RemoteLinux::Internal diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.cpp b/src/plugins/remotelinux/publickeydeploymentdialog.cpp index c7b4a548103..6d0ead4ac6f 100644 --- a/src/plugins/remotelinux/publickeydeploymentdialog.cpp +++ b/src/plugins/remotelinux/publickeydeploymentdialog.cpp @@ -10,6 +10,7 @@ #include <projectexplorer/devicesupport/sshsettings.h> #include <utils/filepath.h> +#include <utils/fileutils.h> #include <utils/process.h> #include <utils/stringutils.h> #include <utils/theme/theme.h> @@ -17,8 +18,7 @@ using namespace ProjectExplorer; using namespace Utils; -namespace RemoteLinux { -namespace Internal { +namespace RemoteLinux::Internal { class PublicKeyDeploymentDialogPrivate { @@ -26,9 +26,6 @@ public: Process m_process; bool m_done; }; -} // namespace Internal; - -using namespace Internal; PublicKeyDeploymentDialog *PublicKeyDeploymentDialog::createDialog( const IDevice::ConstPtr &deviceConfig, QWidget *parent) @@ -128,4 +125,4 @@ void PublicKeyDeploymentDialog::handleDeploymentDone(bool succeeded, const QStri d->m_done = true; } -} // namespace RemoteLinux +} // namespace RemoteLinux::Internal diff --git a/src/plugins/remotelinux/publickeydeploymentdialog.h b/src/plugins/remotelinux/publickeydeploymentdialog.h index 285e4a1fa82..8177e58935e 100644 --- a/src/plugins/remotelinux/publickeydeploymentdialog.h +++ b/src/plugins/remotelinux/publickeydeploymentdialog.h @@ -3,24 +3,23 @@ #pragma once -#include "remotelinux_export.h" - #include <projectexplorer/devicesupport/idevicefwd.h> #include <QProgressDialog> namespace Utils { class FilePath; } -namespace RemoteLinux { -namespace Internal { class PublicKeyDeploymentDialogPrivate; } +namespace RemoteLinux::Internal { -class REMOTELINUX_EXPORT PublicKeyDeploymentDialog : public QProgressDialog +class PublicKeyDeploymentDialogPrivate; + +class PublicKeyDeploymentDialog : public QProgressDialog { Q_OBJECT public: // Asks for public key and returns null if the file dialog is canceled. - static PublicKeyDeploymentDialog *createDialog(const ProjectExplorer::IDeviceConstPtr &deviceConfig, - QWidget *parent = nullptr); + static PublicKeyDeploymentDialog *createDialog( + const ProjectExplorer::IDeviceConstPtr &deviceConfig, QWidget *parent = nullptr); PublicKeyDeploymentDialog(const ProjectExplorer::IDeviceConstPtr &deviceConfig, const Utils::FilePath &publicKeyFileName, QWidget *parent = nullptr); @@ -33,4 +32,4 @@ private: Internal::PublicKeyDeploymentDialogPrivate * const d; }; -} // namespace RemoteLinux +} // namespace RemoteLinux::Internal diff --git a/src/plugins/remotelinux/remotelinux.qbs b/src/plugins/remotelinux/remotelinux.qbs index 03a6afad10f..8eaab69e479 100644 --- a/src/plugins/remotelinux/remotelinux.qbs +++ b/src/plugins/remotelinux/remotelinux.qbs @@ -1,82 +1,74 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "RemoteLinux" - QtcPlugin { - Depends { name: "Qt.widgets" } - Depends { name: "QmlDebug" } - Depends { name: "Utils" } + Depends { name: "Qt.widgets" } + Depends { name: "QmlDebug" } + Depends { name: "Utils" } - Depends { name: "Core" } - Depends { name: "Debugger" } - Depends { name: "ProjectExplorer" } + Depends { name: "Core" } + Depends { name: "Debugger" } + Depends { name: "ProjectExplorer" } + files: [ + "abstractremotelinuxdeploystep.cpp", + "abstractremotelinuxdeploystep.h", + "deploymenttimeinfo.cpp", + "deploymenttimeinfo.h", + "customcommanddeploystep.cpp", + "customcommanddeploystep.h", + "genericdeploystep.cpp", + "genericdeploystep.h", + "genericdirectuploadstep.cpp", + "genericdirectuploadstep.h", + "genericlinuxdeviceconfigurationwidget.cpp", + "genericlinuxdeviceconfigurationwidget.h", + "killappstep.cpp", + "killappstep.h", + "linuxdevice.cpp", + "linuxdevice.h", + "linuxdevicetester.cpp", + "linuxdevicetester.h", + "linuxprocessinterface.h", + "makeinstallstep.cpp", + "makeinstallstep.h", + "publickeydeploymentdialog.cpp", + "publickeydeploymentdialog.h", + "remotelinux.qrc", + "remotelinux_constants.h", + "remotelinux_export.h", + "remotelinuxcustomrunconfiguration.cpp", + "remotelinuxcustomrunconfiguration.h", + "remotelinuxdebugsupport.cpp", + "remotelinuxdebugsupport.h", + "remotelinuxenvironmentaspect.cpp", + "remotelinuxenvironmentaspect.h", + "remotelinuxplugin.cpp", + "remotelinuxplugin.h", + "remotelinuxrunconfiguration.cpp", + "remotelinuxrunconfiguration.h", + "remotelinuxsignaloperation.cpp", + "remotelinuxsignaloperation.h", + "remotelinuxtr.h", + "sshdevicewizard.cpp", + "sshdevicewizard.h", + "sshkeycreationdialog.cpp", + "sshkeycreationdialog.h", + "tarpackagecreationstep.cpp", + "tarpackagecreationstep.h", + "tarpackagedeploystep.cpp", + "tarpackagedeploystep.h", + "images/embeddedtarget.png", + ] + + QtcTestFiles { files: [ - "abstractremotelinuxdeploystep.cpp", - "abstractremotelinuxdeploystep.h", - "deploymenttimeinfo.cpp", - "deploymenttimeinfo.h", - "customcommanddeploystep.cpp", - "customcommanddeploystep.h", - "genericdirectuploadstep.cpp", - "genericdirectuploadstep.h", - "genericlinuxdeviceconfigurationwidget.cpp", - "genericlinuxdeviceconfigurationwidget.h", - "genericlinuxdeviceconfigurationwizard.cpp", - "genericlinuxdeviceconfigurationwizard.h", - "genericlinuxdeviceconfigurationwizardpages.cpp", - "genericlinuxdeviceconfigurationwizardpages.h", - "killappstep.cpp", - "killappstep.h", - "linuxdevice.cpp", - "linuxdevice.h", - "linuxdevicetester.cpp", - "linuxdevicetester.h", - "linuxprocessinterface.h", - "makeinstallstep.cpp", - "makeinstallstep.h", - "publickeydeploymentdialog.cpp", - "publickeydeploymentdialog.h", - "remotelinux.qrc", - "remotelinux_constants.h", - "remotelinux_export.h", - "remotelinuxcustomrunconfiguration.cpp", - "remotelinuxcustomrunconfiguration.h", - "remotelinuxdebugsupport.cpp", - "remotelinuxdebugsupport.h", - "remotelinuxdeployconfiguration.cpp", - "remotelinuxdeployconfiguration.h", - "remotelinuxenvironmentaspect.cpp", - "remotelinuxenvironmentaspect.h", - "remotelinuxplugin.cpp", - "remotelinuxplugin.h", - "remotelinuxrunconfiguration.cpp", - "remotelinuxrunconfiguration.h", - "remotelinuxsignaloperation.cpp", - "remotelinuxsignaloperation.h", - "remotelinuxtr.h", - "rsyncdeploystep.cpp", - "rsyncdeploystep.h", - "sshkeycreationdialog.cpp", - "sshkeycreationdialog.h", - "tarpackagecreationstep.cpp", - "tarpackagecreationstep.h", - "tarpackagedeploystep.cpp", - "tarpackagedeploystep.h", - "images/embeddedtarget.png", + "filesystemaccess_test.cpp", + "filesystemaccess_test.h", ] + } - QtcTestFiles { - files: [ - "filesystemaccess_test.cpp", - "filesystemaccess_test.h", - ] - } - - Export { - Depends { name: "Debugger" } - Depends { name: "Core" } - } + Export { + Depends { name: "Debugger" } + Depends { name: "Core" } } } diff --git a/src/plugins/remotelinux/remotelinux_constants.h b/src/plugins/remotelinux/remotelinux_constants.h index 9c428650c73..90e34826469 100644 --- a/src/plugins/remotelinux/remotelinux_constants.h +++ b/src/plugins/remotelinux/remotelinux_constants.h @@ -15,12 +15,10 @@ const char MakeInstallStepId[] = "RemoteLinux.MakeInstall"; const char TarPackageCreationStepId[] = "MaemoTarPackageCreationStep"; const char TarPackageFilePathId[] = "TarPackageFilePath"; const char TarPackageDeployStepId[] = "MaemoUploadAndInstallTarPackageStep"; -const char RsyncDeployStepId[] = "RemoteLinux.RsyncDeployStep"; +const char GenericDeployStepId[] = "RemoteLinux.RsyncDeployStep"; const char CustomCommandDeployStepId[] = "RemoteLinux.GenericRemoteLinuxCustomCommandDeploymentStep"; const char KillAppStepId[] = "RemoteLinux.KillAppStep"; -const char SupportsRSync[] = "RemoteLinux.SupportsRSync"; -const char SupportsSftp[] = "RemoteLinux.SupportsSftp"; const char SourceProfile[] = "RemoteLinux.SourceProfile"; const char LinkDevice[] = "RemoteLinux.LinkDevice"; diff --git a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp index fd6f75c0761..fd94144215c 100644 --- a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp @@ -8,7 +8,6 @@ #include "remotelinuxenvironmentaspect.h" #include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <utils/hostosinfo.h> @@ -27,51 +26,56 @@ public: private: Tasks checkForIssues() const override; + + RemoteLinuxEnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + SymbolFileAspect symbolFile{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + X11ForwardingAspect x11Forwarding{this}; }; RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<RemoteLinuxEnvironmentAspect>(target); + environment.setDeviceSelector(target, EnvironmentAspect::RunDevice); - auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setSettingsKey("RemoteLinux.CustomRunConfig.RemoteExecutable"); - exeAspect->setLabelText(Tr::tr("Remote executable:")); - exeAspect->setDisplayStyle(StringAspect::LineEditDisplay); - exeAspect->setHistoryCompleter("RemoteLinux.CustomExecutable.History"); - exeAspect->setExpectedKind(PathChooser::Any); + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setSettingsKey("RemoteLinux.CustomRunConfig.RemoteExecutable"); + executable.setLabelText(Tr::tr("Remote executable:")); + executable.setReadOnly(false); + executable.setHistoryCompleter("RemoteLinux.CustomExecutable.History"); + executable.setExpectedKind(PathChooser::Any); - auto symbolsAspect = addAspect<FilePathAspect>(); - symbolsAspect->setSettingsKey("RemoteLinux.CustomRunConfig.LocalExecutable"); - symbolsAspect->setLabelText(Tr::tr("Local executable:")); + symbolFile.setSettingsKey("RemoteLinux.CustomRunConfig.LocalExecutable"); + symbolFile.setLabelText(Tr::tr("Local executable:")); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - if (HostOsInfo::isAnyUnixHost()) - addAspect<TerminalAspect>(); - if (HostOsInfo::isAnyUnixHost()) - addAspect<X11ForwardingAspect>(macroExpander()); + arguments.setMacroExpander(macroExpander()); - setRunnableModifier([this](Runnable &r) { - if (const auto * const forwardingAspect = aspect<X11ForwardingAspect>()) - r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); - }); + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); + + terminal.setVisible(HostOsInfo::isAnyUnixHost()); + + x11Forwarding.setMacroExpander(macroExpander()); setDefaultDisplayName(runConfigDefaultDisplayName()); } QString RemoteLinuxCustomRunConfiguration::runConfigDefaultDisplayName() { - QString remoteExecutable = aspect<ExecutableAspect>()->executable().toString(); + FilePath remoteExecutable = executable(); QString display = remoteExecutable.isEmpty() - ? Tr::tr("Custom Executable") : Tr::tr("Run \"%1\"").arg(remoteExecutable); - return RunConfigurationFactory::decoratedTargetName(display, target()); + ? Tr::tr("Custom Executable") + : Tr::tr("Run \"%1\"").arg(remoteExecutable.toUserOutput()); + return RunConfigurationFactory::decoratedTargetName(display, target()); } Tasks RemoteLinuxCustomRunConfiguration::checkForIssues() const { Tasks tasks; - if (aspect<ExecutableAspect>()->executable().isEmpty()) { + if (executable().isEmpty()) { tasks << createConfigurationIssue(Tr::tr("The remote executable must be set in order to run " "a custom remote run configuration.")); } diff --git a/src/plugins/remotelinux/remotelinuxdebugsupport.cpp b/src/plugins/remotelinux/remotelinuxdebugsupport.cpp index af476d48b94..d4cefc6394d 100644 --- a/src/plugins/remotelinux/remotelinuxdebugsupport.cpp +++ b/src/plugins/remotelinux/remotelinuxdebugsupport.cpp @@ -5,6 +5,7 @@ #include "remotelinux_constants.h" +#include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/runconfigurationaspects.h> @@ -37,7 +38,11 @@ public: setStartMode(AttachToRemoteServer); setCloseMode(KillAndExitMonitorAtClose); setUseExtendedRemote(true); - setLldbPlatform("remote-linux"); + + if (runControl->device()->osType() == Utils::OsTypeMac) + setLldbPlatform("remote-macosx"); + else + setLldbPlatform("remote-linux"); } }; diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp deleted file mode 100644 index 652a03cdf09..00000000000 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "remotelinuxdeployconfiguration.h" - -#include "remotelinux_constants.h" -#include "remotelinuxtr.h" - -#include <projectexplorer/devicesupport/filetransferinterface.h> -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/target.h> - -using namespace ProjectExplorer; - -namespace RemoteLinux::Internal { - -FileTransferMethod defaultTransferMethod(Kit *kit) -{ - auto runDevice = DeviceKitAspect::device(kit); - auto buildDevice = BuildDeviceKitAspect::device(kit); - - if (runDevice != buildDevice) { - // FIXME: That's not the full truth, we need support from the build - // device, too. - if (runDevice && runDevice->extraData(Constants::SupportsRSync).toBool()) - return FileTransferMethod::Rsync; - } - - if (runDevice) { - const QVariant sftpInfo = runDevice->extraData(Constants::SupportsSftp); - if (!sftpInfo.isValid() || sftpInfo.toBool()) - return FileTransferMethod::Sftp; - } - - return FileTransferMethod::GenericCopy; -} - -RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() -{ - setConfigBaseId(RemoteLinux::Constants::DeployToGenericLinux); - addSupportedTargetDeviceType(RemoteLinux::Constants::GenericLinuxOsType); - setDefaultDisplayName(Tr::tr("Deploy to Remote Linux Host")); - setUseDeploymentDataView(); - - const auto needsMakeInstall = [](Target *target) - { - const Project * const prj = target->project(); - return prj->deploymentKnowledge() == DeploymentKnowledge::Bad - && prj->hasMakeInstallEquivalent(); - }; - setPostRestore([needsMakeInstall](DeployConfiguration *dc, const QVariantMap &map) { - // 4.9 -> 4.10. See QTCREATORBUG-22689. - if (map.value("_checkMakeInstall").toBool() && needsMakeInstall(dc->target())) { - dc->stepList()->insertStep(0, Constants::MakeInstallStepId); - } - }); - - addInitialStep(Constants::MakeInstallStepId, needsMakeInstall); - addInitialStep(Constants::KillAppStepId); - - // Todo: Check: Instead of having three different steps here, have one - // and shift the logic into the implementation there? - addInitialStep(Constants::RsyncDeployStepId, [](Target *target) { - return defaultTransferMethod(target->kit()) == FileTransferMethod::Rsync; - }); - addInitialStep(Constants::DirectUploadStepId, [](Target *target) { - return defaultTransferMethod(target->kit()) == FileTransferMethod::Sftp; - }); - addInitialStep(ProjectExplorer::Constants::COPY_FILE_STEP, [](Target *target) { - return defaultTransferMethod(target->kit()) == FileTransferMethod::GenericCopy; - }); -} - -} // RemoteLinux::Internal diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.h b/src/plugins/remotelinux/remotelinuxdeployconfiguration.h deleted file mode 100644 index 61959122b2c..00000000000 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/deployconfiguration.h> - -namespace RemoteLinux::Internal { - -class RemoteLinuxDeployConfigurationFactory : public ProjectExplorer::DeployConfigurationFactory -{ -public: - RemoteLinuxDeployConfigurationFactory(); -}; - -} // RemoteLinux::Internal diff --git a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp index 07880089de2..14005aa1a72 100644 --- a/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp +++ b/src/plugins/remotelinux/remotelinuxenvironmentaspect.cpp @@ -10,7 +10,7 @@ #include <projectexplorer/environmentaspectwidget.h> #include <projectexplorer/environmentwidget.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/target.h> #include <utils/algorithm.h> @@ -32,24 +32,26 @@ const int ENVIRONMENTASPECT_VERSION = 1; // Version was introduced in 4.3 with t class RemoteLinuxEnvironmentAspectWidget : public EnvironmentAspectWidget { public: - RemoteLinuxEnvironmentAspectWidget(RemoteLinuxEnvironmentAspect *aspect, Target *target) + RemoteLinuxEnvironmentAspectWidget(RemoteLinuxEnvironmentAspect *aspect) : EnvironmentAspectWidget(aspect) { auto fetchButton = new QPushButton(Tr::tr("Fetch Device Environment")); addWidget(fetchButton); - connect(target, &Target::kitChanged, aspect, [aspect] { aspect->setRemoteEnvironment({}); }); + connect(aspect->target(), &Target::kitChanged, aspect, [aspect] { + aspect->setRemoteEnvironment({}); + }); - connect(fetchButton, &QPushButton::clicked, this, [aspect, target] { - if (IDevice::ConstPtr device = DeviceKitAspect::device(target->kit())) { + connect(fetchButton, &QPushButton::clicked, this, [aspect] { + if (IDevice::ConstPtr device = DeviceKitAspect::device(aspect->target()->kit())) { DeviceFileAccess *access = device->fileAccess(); QTC_ASSERT(access, return); aspect->setRemoteEnvironment(access->deviceEnvironment()); } }); - envWidget()->setOpenTerminalFunc([target](const Environment &env) { - IDevice::ConstPtr device = DeviceKitAspect::device(target->kit()); + envWidget()->setOpenTerminalFunc([aspect](const Environment &env) { + IDevice::ConstPtr device = DeviceKitAspect::device(aspect->target()->kit()); if (!device) { QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Cannot Open Terminal"), @@ -70,14 +72,13 @@ static bool displayAlreadySet(const Utils::EnvironmentItems &changes) }); } -RemoteLinuxEnvironmentAspect::RemoteLinuxEnvironmentAspect(Target *target) +RemoteLinuxEnvironmentAspect::RemoteLinuxEnvironmentAspect(AspectContainer *container) + : EnvironmentAspect(container) { addSupportedBaseEnvironment(Tr::tr("Clean Environment"), {}); addPreferredBaseEnvironment(Tr::tr("System Environment"), [this] { return m_remoteEnvironment; }); - setConfigWidgetCreator([this, target] { - return new RemoteLinuxEnvironmentAspectWidget(this, target); - }); + setConfigWidgetCreator([this] { return new RemoteLinuxEnvironmentAspectWidget(this); }); } void RemoteLinuxEnvironmentAspect::setRemoteEnvironment(const Utils::Environment &env) @@ -98,11 +99,11 @@ QString RemoteLinuxEnvironmentAspect::userEnvironmentChangesAsString() const return env.mid(0, env.size() - 1); } -void RemoteLinuxEnvironmentAspect::fromMap(const QVariantMap &map) +void RemoteLinuxEnvironmentAspect::fromMap(const Store &map) { ProjectExplorer::EnvironmentAspect::fromMap(map); - const auto version = map.value(QLatin1String(VERSION_KEY), 0).toInt(); + const auto version = map.value(VERSION_KEY, 0).toInt(); if (version == 0) { // In Qt Creator versions prior to 4.3 RemoteLinux included DISPLAY=:0.0 in the base // environment, if DISPLAY was not set. In order to keep existing projects expecting @@ -116,10 +117,10 @@ void RemoteLinuxEnvironmentAspect::fromMap(const QVariantMap &map) } } -void RemoteLinuxEnvironmentAspect::toMap(QVariantMap &map) const +void RemoteLinuxEnvironmentAspect::toMap(Store &map) const { ProjectExplorer::EnvironmentAspect::toMap(map); - map.insert(QLatin1String(VERSION_KEY), ENVIRONMENTASPECT_VERSION); + map.insert(VERSION_KEY, ENVIRONMENTASPECT_VERSION); } } // namespace RemoteLinux diff --git a/src/plugins/remotelinux/remotelinuxenvironmentaspect.h b/src/plugins/remotelinux/remotelinuxenvironmentaspect.h index 552faabe416..6543d2f1208 100644 --- a/src/plugins/remotelinux/remotelinuxenvironmentaspect.h +++ b/src/plugins/remotelinux/remotelinuxenvironmentaspect.h @@ -14,15 +14,15 @@ class REMOTELINUX_EXPORT RemoteLinuxEnvironmentAspect : public ProjectExplorer:: Q_OBJECT public: - RemoteLinuxEnvironmentAspect(ProjectExplorer::Target *target); + explicit RemoteLinuxEnvironmentAspect(Utils::AspectContainer *container = nullptr); void setRemoteEnvironment(const Utils::Environment &env); QString userEnvironmentChangesAsString() const; protected: - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; + void fromMap(const Utils::Store &map) override; + void toMap(Utils::Store &map) const override; private: Utils::Environment m_remoteEnvironment; diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index b3af1fb873d..a84772b3f39 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -4,17 +4,16 @@ #include "remotelinuxplugin.h" #include "customcommanddeploystep.h" +#include "genericdeploystep.h" #include "genericdirectuploadstep.h" #include "killappstep.h" #include "linuxdevice.h" #include "makeinstallstep.h" #include "remotelinux_constants.h" -#include "remotelinuxdeployconfiguration.h" #include "remotelinuxcustomrunconfiguration.h" #include "remotelinuxdebugsupport.h" -#include "remotelinuxdeployconfiguration.h" #include "remotelinuxrunconfiguration.h" -#include "rsyncdeploystep.h" +#include "remotelinuxtr.h" #include "tarpackagecreationstep.h" #include "tarpackagedeploystep.h" @@ -22,7 +21,9 @@ #include "filesystemaccess_test.h" #endif -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/deployconfiguration.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -45,6 +46,37 @@ public: } }; +class RemoteLinuxDeployConfigurationFactory : public DeployConfigurationFactory +{ +public: + RemoteLinuxDeployConfigurationFactory() + { + setConfigBaseId(RemoteLinux::Constants::DeployToGenericLinux); + addSupportedTargetDeviceType(RemoteLinux::Constants::GenericLinuxOsType); + setDefaultDisplayName(Tr::tr("Deploy to Remote Linux Host")); + setUseDeploymentDataView(); + + const auto needsMakeInstall = [](Target *target) + { + const Project * const prj = target->project(); + return prj->deploymentKnowledge() == DeploymentKnowledge::Bad + && prj->hasMakeInstallEquivalent(); + }; + setPostRestore([needsMakeInstall](DeployConfiguration *dc, const Store &map) { + // 4.9 -> 4.10. See QTCREATORBUG-22689. + if (map.value("_checkMakeInstall").toBool() && needsMakeInstall(dc->target())) { + dc->stepList()->insertStep(0, Constants::MakeInstallStepId); + } + }); + + addInitialStep(Constants::MakeInstallStepId, needsMakeInstall); + addInitialStep(Constants::KillAppStepId); + + // TODO: Rename RsyncDeployStep to something more generic. + addInitialStep(Constants::GenericDeployStepId); + } +}; + class RemoteLinuxPluginPrivate { public: @@ -55,7 +87,7 @@ public: TarPackageCreationStepFactory tarPackageCreationStepFactory; TarPackageDeployStepFactory tarPackageDeployStepFactory; RemoteLinuxDeployStepFactory<GenericDirectUploadStepFactory> genericDirectUploadStepFactory; - RemoteLinuxDeployStepFactory<RsyncDeployStepFactory> rsyncDeployStepFactory; + RemoteLinuxDeployStepFactory<GenericDeployStepFactory> rsyncDeployStepFactory; CustomCommandDeployStepFactory customCommandDeployStepFactory; KillAppStepFactory killAppStepFactory; RemoteLinuxDeployStepFactory<MakeInstallStepFactory> makeInstallStepFactory; diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp index 42e80aae41b..6e154f41aef 100644 --- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp @@ -11,10 +11,9 @@ #include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/deploymentdata.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/project.h> #include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> #include <utils/hostosinfo.h> @@ -28,37 +27,44 @@ class RemoteLinuxRunConfiguration final : public RunConfiguration { public: RemoteLinuxRunConfiguration(Target *target, Id id); + + RemoteLinuxEnvironmentAspect environment{this}; + ExecutableAspect executable{this}; + SymbolFileAspect symbolFile{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDir{this}; + TerminalAspect terminal{this}; + X11ForwardingAspect x11Forwarding{this}; + UseLibraryPathsAspect useLibraryPath{this}; }; RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - auto envAspect = addAspect<RemoteLinuxEnvironmentAspect>(target); + environment.setDeviceSelector(target, EnvironmentAspect::RunDevice); - auto exeAspect = addAspect<ExecutableAspect>(target, ExecutableAspect::RunDevice); - exeAspect->setLabelText(Tr::tr("Executable on device:")); - exeAspect->setPlaceHolderText(Tr::tr("Remote path not set")); - exeAspect->makeOverridable("RemoteLinux.RunConfig.AlternateRemoteExecutable", + executable.setDeviceSelector(target, ExecutableAspect::RunDevice); + executable.setLabelText(Tr::tr("Executable on device:")); + executable.setPlaceHolderText(Tr::tr("Remote path not set")); + executable.makeOverridable("RemoteLinux.RunConfig.AlternateRemoteExecutable", "RemoteLinux.RunConfig.UseAlternateRemoteExecutable"); - exeAspect->setHistoryCompleter("RemoteLinux.AlternateExecutable.History"); + executable.setHistoryCompleter("RemoteLinux.AlternateExecutable.History"); - auto symbolsAspect = addAspect<SymbolFileAspect>(); - symbolsAspect->setLabelText(Tr::tr("Executable on host:")); - symbolsAspect->setDisplayStyle(SymbolFileAspect::LabelDisplay); + symbolFile.setLabelText(Tr::tr("Executable on host:")); - addAspect<ArgumentsAspect>(macroExpander()); - addAspect<WorkingDirectoryAspect>(macroExpander(), envAspect); - if (HostOsInfo::isAnyUnixHost()) - addAspect<TerminalAspect>(); - if (HostOsInfo::isAnyUnixHost()) - addAspect<X11ForwardingAspect>(macroExpander()); + arguments.setMacroExpander(macroExpander()); - auto libAspect = addAspect<UseLibraryPathsAspect>(); - libAspect->setValue(false); - connect(libAspect, &UseLibraryPathsAspect::changed, - envAspect, &EnvironmentAspect::environmentChanged); + workingDir.setMacroExpander(macroExpander()); + workingDir.setEnvironment(&environment); - setUpdater([this, target, exeAspect, symbolsAspect, libAspect] { + terminal.setVisible(HostOsInfo::isAnyUnixHost()); + + x11Forwarding.setMacroExpander(macroExpander()); + + connect(&useLibraryPath, &BaseAspect::changed, + &environment, &EnvironmentAspect::environmentChanged); + + setUpdater([this, target] { const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit()); const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit()); QTC_ASSERT(buildDevice, return); @@ -68,20 +74,15 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) const DeploymentData deploymentData = target->deploymentData(); const DeployableFile depFile = deploymentData.deployableForLocalFile(localExecutable); - exeAspect->setExecutable(runDevice->filePath(depFile.remoteFilePath())); - symbolsAspect->setFilePath(localExecutable); - libAspect->setEnabled(buildDevice == runDevice); + executable.setExecutable(runDevice->filePath(depFile.remoteFilePath())); + symbolFile.setValue(localExecutable); + useLibraryPath.setEnabled(buildDevice == runDevice); }); - setRunnableModifier([this](Runnable &r) { - if (const auto * const forwardingAspect = aspect<X11ForwardingAspect>()) - r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); - }); - - envAspect->addModifier([this, libAspect](Environment &env) { + environment.addModifier([this](Environment &env) { BuildTargetInfo bti = buildTargetInfo(); if (bti.runEnvModifier) - bti.runEnvModifier(env, libAspect->value()); + bti.runEnvModifier(env, useLibraryPath()); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp deleted file mode 100644 index e32129fd299..00000000000 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "rsyncdeploystep.h" - -#include "abstractremotelinuxdeploystep.h" -#include "remotelinux_constants.h" -#include "remotelinuxtr.h" - -#include <projectexplorer/deploymentdata.h> -#include <projectexplorer/devicesupport/filetransfer.h> -#include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/runconfigurationaspects.h> -#include <projectexplorer/target.h> - -#include <utils/algorithm.h> -#include <utils/process.h> -#include <utils/processinterface.h> - -using namespace ProjectExplorer; -using namespace Tasking; -using namespace Utils; - -namespace RemoteLinux { - -// RsyncDeployStep - -class RsyncDeployStep : public AbstractRemoteLinuxDeployStep -{ -public: - RsyncDeployStep(BuildStepList *bsl, Id id); - -private: - bool isDeploymentNecessary() const final; - Group deployRecipe() final; - GroupItem mkdirTask(); - GroupItem transferTask(); - - mutable FilesToTransfer m_files; - bool m_ignoreMissingFiles = false; - QString m_flags; -}; - -RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id) - : AbstractRemoteLinuxDeployStep(bsl, id) -{ - auto flags = addAspect<StringAspect>(); - flags->setDisplayStyle(StringAspect::LineEditDisplay); - flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); - flags->setLabelText(Tr::tr("Flags:")); - flags->setValue(FileTransferSetupData::defaultRsyncFlags()); - - auto ignoreMissingFiles = addAspect<BoolAspect>(); - ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); - ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"), - BoolAspect::LabelPlacement::InExtraLabel); - ignoreMissingFiles->setValue(false); - - setInternalInitializer([this, ignoreMissingFiles, flags] { - if (BuildDeviceKitAspect::device(kit()) == DeviceKitAspect::device(kit())) { - // rsync transfer on the same device currently not implemented - // and typically not wanted. - return CheckResult::failure( - Tr::tr("rsync is only supported for transfers between different devices.")); - } - m_ignoreMissingFiles = ignoreMissingFiles->value(); - m_flags = flags->value(); - return isDeploymentPossible(); - }); - - setRunPreparer([this] { - const QList<DeployableFile> files = target()->deploymentData().allFiles(); - m_files.clear(); - for (const DeployableFile &f : files) - m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())}); - }); -} - -bool RsyncDeployStep::isDeploymentNecessary() const -{ - if (m_ignoreMissingFiles) - Utils::erase(m_files, [](const FileToTransfer &file) { return !file.m_source.exists(); }); - return !m_files.empty(); -} - -GroupItem RsyncDeployStep::mkdirTask() -{ - const auto setupHandler = [this](Process &process) { - QStringList remoteDirs; - for (const FileToTransfer &file : std::as_const(m_files)) { - const QString parentDir = file.m_target.parentDir().path(); - if (!parentDir.isEmpty()) - remoteDirs << parentDir; - } - if (remoteDirs.isEmpty()) - return SetupResult::StopWithDone; - remoteDirs.sort(); - remoteDirs.removeDuplicates(); - process.setCommand({deviceConfiguration()->filePath("mkdir"), - QStringList("-p") + remoteDirs}); - connect(&process, &Process::readyReadStandardError, this, [this, proc = &process] { - handleStdErrData(QString::fromLocal8Bit(proc->readAllRawStandardError())); - }); - return SetupResult::Continue; - }; - const auto errorHandler = [this](const Process &process) { - QString finalMessage = process.errorString(); - const QString stdErr = process.cleanedStdErr(); - if (!stdErr.isEmpty()) { - if (!finalMessage.isEmpty()) - finalMessage += '\n'; - finalMessage += stdErr; - } - addErrorMessage(Tr::tr("Deploy via rsync: failed to create remote directories:") - + '\n' + finalMessage); - }; - return ProcessTask(setupHandler, {}, errorHandler); -} - -GroupItem RsyncDeployStep::transferTask() -{ - const auto setupHandler = [this](FileTransfer &transfer) { - transfer.setTransferMethod(FileTransferMethod::Rsync); - transfer.setRsyncFlags(m_flags); - transfer.setFilesToTransfer(m_files); - connect(&transfer, &FileTransfer::progress, - this, &AbstractRemoteLinuxDeployStep::handleStdOutData); - }; - const auto errorHandler = [this](const FileTransfer &transfer) { - const ProcessResultData result = transfer.resultData(); - if (result.m_error == QProcess::FailedToStart) { - addErrorMessage(Tr::tr("rsync failed to start: %1").arg(result.m_errorString)); - } else if (result.m_exitStatus == QProcess::CrashExit) { - addErrorMessage(Tr::tr("rsync crashed.")); - } else if (result.m_exitCode != 0) { - addErrorMessage(Tr::tr("rsync failed with exit code %1.").arg(result.m_exitCode) - + "\n" + result.m_errorString); - } - }; - return FileTransferTask(setupHandler, {}, errorHandler); -} - -Group RsyncDeployStep::deployRecipe() -{ - return Group { mkdirTask(), transferTask() }; -} - -// Factory - -RsyncDeployStepFactory::RsyncDeployStepFactory() -{ - registerStep<RsyncDeployStep>(Constants::RsyncDeployStepId); - setDisplayName(Tr::tr("Deploy files via rsync")); -} - -} // RemoteLinux diff --git a/src/plugins/remotelinux/rsyncdeploystep.h b/src/plugins/remotelinux/rsyncdeploystep.h deleted file mode 100644 index 7450d84fcb6..00000000000 --- a/src/plugins/remotelinux/rsyncdeploystep.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "remotelinux_export.h" - -#include <projectexplorer/buildstep.h> - -namespace RemoteLinux { - -class REMOTELINUX_EXPORT RsyncDeployStepFactory - : public ProjectExplorer::BuildStepFactory -{ -public: - RsyncDeployStepFactory(); -}; - -} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/sshdevicewizard.cpp b/src/plugins/remotelinux/sshdevicewizard.cpp new file mode 100644 index 00000000000..d73e146957e --- /dev/null +++ b/src/plugins/remotelinux/sshdevicewizard.cpp @@ -0,0 +1,205 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "sshdevicewizard.h" + +#include "publickeydeploymentdialog.h" +#include "remotelinuxtr.h" +#include "sshkeycreationdialog.h" + +#include <coreplugin/icore.h> + +#include <projectexplorer/devicesupport/idevice.h> +#include <projectexplorer/devicesupport/sshparameters.h> + +#include <utils/fancylineedit.h> +#include <utils/fileutils.h> +#include <utils/layoutbuilder.h> +#include <utils/pathchooser.h> +#include <utils/utilsicons.h> + +#include <QLabel> +#include <QPushButton> +#include <QSpinBox> + +using namespace ProjectExplorer; +using namespace Utils; + +namespace RemoteLinux { + +class SetupPage : public QWizardPage +{ +public: + explicit SetupPage(const ProjectExplorer::IDevicePtr &device) + : m_device(device) + { + setTitle(Tr::tr("Connection")); + setWindowTitle(Tr::tr("WizardPage")); + + m_nameLineEdit = new FancyLineEdit(this); + m_nameLineEdit->setHistoryCompleter("DeviceName"); + + m_hostNameLineEdit = new FancyLineEdit(this); + m_hostNameLineEdit->setHistoryCompleter("HostName"); + + m_sshPortSpinBox = new QSpinBox(this); + + m_userNameLineEdit = new FancyLineEdit(this); + m_userNameLineEdit->setHistoryCompleter("UserName"); + + using namespace Layouting; + Form { + Tr::tr("The name to identify this configuration:"), m_nameLineEdit, br, + Tr::tr("The device's host name or IP address:"), m_hostNameLineEdit, st, br, + Tr::tr("The device's SSH port number:"), m_sshPortSpinBox, st, br, + Tr::tr("The username to log into the device:"), m_userNameLineEdit, st, br + }.attachTo(this); + + setSubTitle(QLatin1String(" ")); // For Qt bug (background color) + connect(m_nameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); + connect(m_hostNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); + connect(m_sshPortSpinBox, &QSpinBox::valueChanged, this, &QWizardPage::completeChanged); + connect(m_userNameLineEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); + } + +private: + void initializePage() final { + m_nameLineEdit->setText(m_device->displayName()); + m_hostNameLineEdit->setText(m_device->sshParameters().host()); + m_sshPortSpinBox->setValue(22); + m_sshPortSpinBox->setRange(1, 65535); + m_userNameLineEdit->setText(m_device->sshParameters().userName()); + } + bool isComplete() const final { + return !m_nameLineEdit->text().trimmed().isEmpty() + && !m_hostNameLineEdit->text().trimmed().isEmpty() + && !m_userNameLineEdit->text().trimmed().isEmpty(); + } + bool validatePage() final { + m_device->settings()->displayName.setValue(m_nameLineEdit->text().trimmed()); + SshParameters sshParams = m_device->sshParameters(); + sshParams.setHost(m_hostNameLineEdit->text().trimmed()); + sshParams.setUserName(m_userNameLineEdit->text().trimmed()); + sshParams.setPort(m_sshPortSpinBox->value()); + m_device->setSshParameters(sshParams); + return true; + } + + FancyLineEdit *m_nameLineEdit; + FancyLineEdit *m_hostNameLineEdit; + QSpinBox *m_sshPortSpinBox; + FancyLineEdit *m_userNameLineEdit; + IDevicePtr m_device; +}; + +class KeyDeploymentPage : public QWizardPage +{ +public: + explicit KeyDeploymentPage(const ProjectExplorer::IDevicePtr &device) + : m_device(device) + { + setTitle(Tr::tr("Key Deployment")); + setSubTitle(" "); + const QString info = Tr::tr( + "We recommend that you log into your device using public key authentication.\n" + "If your device is already set up for this, you do not have to do anything here.\n" + "Otherwise, please deploy the public key for the private key " + "with which to connect in the future.\n" + "If you do not have a private key yet, you can also create one here."); + m_keyFileChooser.setExpectedKind(PathChooser::File); + m_keyFileChooser.setHistoryCompleter("Ssh.KeyFile.History"); + m_keyFileChooser.setPromptDialogTitle(Tr::tr("Choose a Private Key File")); + auto const deployButton = new QPushButton(Tr::tr("Deploy Public Key"), this); + connect(deployButton, &QPushButton::clicked, this, [this] { + Internal::PublicKeyDeploymentDialog dlg( + m_device, m_keyFileChooser.filePath().stringAppended(".pub"), this); + m_iconLabel.setPixmap((dlg.exec() == QDialog::Accepted ? Icons::OK : Icons::BROKEN).pixmap()); + }); + auto const createButton = new QPushButton(Tr::tr("Create New Key Pair"), this); + connect(createButton, &QPushButton::clicked, this, [this] { + SshKeyCreationDialog dlg(this); + if (dlg.exec() == QDialog::Accepted) + m_keyFileChooser.setFilePath(dlg.privateKeyFilePath()); + }); + auto const mainLayout = new QVBoxLayout(this); + auto const keyLayout = new QHBoxLayout; + auto const deployLayout = new QHBoxLayout; + mainLayout->addWidget(new QLabel(info)); + keyLayout->addWidget(new QLabel(Tr::tr("Private key file:"))); + keyLayout->addWidget(&m_keyFileChooser); + keyLayout->addWidget(createButton); + keyLayout->addStretch(); + mainLayout->addLayout(keyLayout); + deployLayout->addWidget(deployButton); + deployLayout->addWidget(&m_iconLabel); + deployLayout->addStretch(); + mainLayout->addLayout(deployLayout); + connect(&m_keyFileChooser, &PathChooser::textChanged, this, [this, deployButton] { + deployButton->setEnabled(m_keyFileChooser.filePath().exists()); + m_iconLabel.clear(); + emit completeChanged(); + }); + for (const FilePath &defaultKey : defaultKeys()) { + if (defaultKey.exists()) { + m_keyFileChooser.setFilePath(defaultKey); + break; + } + } + } + +private: + void initializePage() final { + if (!m_device->sshParameters().privateKeyFile.isEmpty()) + m_keyFileChooser.setFilePath(m_keyFileChooser.filePath()); + m_iconLabel.clear(); + } + bool isComplete() const final { + return m_keyFileChooser.filePath().toString().isEmpty() + || m_keyFileChooser.filePath().exists(); + } + bool validatePage() final { + if (!defaultKeys().contains(m_keyFileChooser.filePath())) { + SshParameters sshParams = m_device->sshParameters(); + sshParams.authenticationType = SshParameters::AuthenticationTypeSpecificKey; + sshParams.privateKeyFile = m_keyFileChooser.filePath(); + m_device->setSshParameters(sshParams); + } + return true; + } + FilePaths defaultKeys() const { + const FilePath baseDir = FileUtils::homePath() / ".ssh"; + return {baseDir / "id_rsa", baseDir / "id_ecdsa", baseDir / "id_ed25519"}; + } + + PathChooser m_keyFileChooser; + QLabel m_iconLabel; + IDevicePtr m_device; +}; + +class FinalPage final : public QWizardPage +{ +public: + FinalPage() + { + setTitle(Tr::tr("Summary")); + setSubTitle(QLatin1String(" ")); // For Qt bug (background color) + auto infoLabel = new QLabel(Tr::tr("The new device configuration will now be created.\n" + "In addition, device connectivity will be tested.")); + infoLabel->setWordWrap(true); + auto layout = new QVBoxLayout(this); + layout->addWidget(infoLabel); + setCommitPage(true); + } +}; + +SshDeviceWizard::SshDeviceWizard(const QString &title, const ProjectExplorer::IDevicePtr &device) + : Wizard(Core::ICore::dialogParent()) +{ + setWindowTitle(title); + + addPage(new SetupPage(device)); + addPage(new KeyDeploymentPage(device)); + addPage(new FinalPage); +} + +} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/sshdevicewizard.h b/src/plugins/remotelinux/sshdevicewizard.h new file mode 100644 index 00000000000..3bd2af1ab22 --- /dev/null +++ b/src/plugins/remotelinux/sshdevicewizard.h @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "remotelinux_export.h" + +#include <projectexplorer/devicesupport/idevicefwd.h> +#include <utils/wizard.h> + +namespace RemoteLinux { + +class REMOTELINUX_EXPORT SshDeviceWizard : public Utils::Wizard +{ +public: + SshDeviceWizard(const QString &title, const ProjectExplorer::IDevicePtr &device); +}; + +} // namespace RemoteLinux diff --git a/src/plugins/remotelinux/tarpackagecreationstep.cpp b/src/plugins/remotelinux/tarpackagecreationstep.cpp index 7c577812da1..74b5a8fa423 100644 --- a/src/plugins/remotelinux/tarpackagecreationstep.cpp +++ b/src/plugins/remotelinux/tarpackagecreationstep.cpp @@ -29,9 +29,6 @@ using namespace Utils; namespace RemoteLinux::Internal { -const char IgnoreMissingFilesKey[] = "RemoteLinux.TarPackageCreationStep.IgnoreMissingFiles"; -const char IncrementalDeploymentKey[] = "RemoteLinux.TarPackageCreationStep.IncrementalDeployment"; - const int TarBlockSize = 512; struct TarFileHeader { @@ -63,10 +60,9 @@ public: private: bool init() final; - void doRun() final; - void doCancel() final; - bool fromMap(const QVariantMap &map) final; - QVariantMap toMap() const final; + Tasking::GroupItem runRecipe() final; + void fromMap(const Store &map) final; + void toMap(Store &map) const final; QVariant data(Id id) const final; void raiseError(const QString &errorMessage); @@ -74,17 +70,17 @@ private: bool isPackagingNeeded() const; void deployFinished(bool success); void addNeededDeploymentFiles(const DeployableFile &deployable, const Kit *kit); - void doPackage(QPromise<bool> &promise, const Utils::FilePath &tarFilePath, + void doPackage(QPromise<void> &promise, const Utils::FilePath &tarFilePath, bool ignoreMissingFiles); - bool appendFile(QPromise<bool> &promise, QFile &tarFile, const QFileInfo &fileInfo, + bool appendFile(QPromise<void> &promise, QFile &tarFile, const QFileInfo &fileInfo, const QString &remoteFilePath, const Utils::FilePath &tarFilePath, bool ignoreMissingFiles); FilePath m_tarFilePath; bool m_deploymentDataModified = false; DeploymentTimeInfo m_deployTimes; - BoolAspect *m_incrementalDeploymentAspect = nullptr; - BoolAspect *m_ignoreMissingFilesAspect = nullptr; + BoolAspect m_incrementalDeployment{this}; + BoolAspect m_ignoreMissingFiles{this}; bool m_packagingNeeded = false; QList<DeployableFile> m_files; @@ -99,15 +95,14 @@ TarPackageCreationStep::TarPackageCreationStep(BuildStepList *bsl, Id id) }); m_deploymentDataModified = true; - m_ignoreMissingFilesAspect = addAspect<BoolAspect>(); - m_ignoreMissingFilesAspect->setLabel(Tr::tr("Ignore missing files"), - BoolAspect::LabelPlacement::AtCheckBox); - m_ignoreMissingFilesAspect->setSettingsKey(IgnoreMissingFilesKey); + m_incrementalDeployment.setSettingsKey( + "RemoteLinux.TarPackageCreationStep.IncrementalDeployment"); + m_incrementalDeployment.setLabelText(Tr::tr("Package modified files only")); + m_incrementalDeployment.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); - m_incrementalDeploymentAspect = addAspect<BoolAspect>(); - m_incrementalDeploymentAspect->setLabel(Tr::tr("Package modified files only"), - BoolAspect::LabelPlacement::AtCheckBox); - m_incrementalDeploymentAspect->setSettingsKey(IncrementalDeploymentKey); + m_ignoreMissingFiles.setSettingsKey("RemoteLinux.TarPackageCreationStep.IgnoreMissingFiles"); + m_ignoreMissingFiles.setLabelText(Tr::tr("Ignore missing files")); + m_ignoreMissingFiles.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); setSummaryUpdater([this] { FilePath path = packageFilePath(); @@ -133,63 +128,56 @@ bool TarPackageCreationStep::init() return true; } -void TarPackageCreationStep::doRun() +Tasking::GroupItem TarPackageCreationStep::runRecipe() { - const QList<DeployableFile> &files = target()->deploymentData().allFiles(); - - if (m_incrementalDeploymentAspect->value()) { - m_files.clear(); - for (const DeployableFile &file : files) - addNeededDeploymentFiles(file, kit()); - } else { - m_files = files; - } - - emit addOutput(Tr::tr("Creating tarball..."), OutputFormat::NormalMessage); - if (!m_packagingNeeded) { - emit addOutput(Tr::tr("Tarball up to date, skipping packaging."), OutputFormat::NormalMessage); - emit finished(true); - return; - } - - auto * const watcher = new QFutureWatcher<bool>(this); - connect(watcher, &QFutureWatcher<bool>::finished, this, [this, watcher] { - const bool success = !watcher->isCanceled() && watcher->result(); - if (success) { - m_deploymentDataModified = false; - emit addOutput(Tr::tr("Packaging finished successfully."), OutputFormat::NormalMessage); + using namespace Tasking; + const auto onSetup = [this](Async<void> &async) { + const QList<DeployableFile> &files = target()->deploymentData().allFiles(); + if (m_incrementalDeployment()) { + m_files.clear(); + for (const DeployableFile &file : files) + addNeededDeploymentFiles(file, kit()); } else { - emit addOutput(Tr::tr("Packaging failed."), OutputFormat::ErrorMessage); + m_files = files; } - emit finished(success); - watcher->deleteLater(); + + emit addOutput(Tr::tr("Creating tarball..."), OutputFormat::NormalMessage); + if (!m_packagingNeeded) { + emit addOutput(Tr::tr("Tarball up to date, skipping packaging."), + OutputFormat::NormalMessage); + return SetupResult::StopWithDone; + } + + async.setConcurrentCallData(&TarPackageCreationStep::doPackage, this, + m_tarFilePath, m_ignoreMissingFiles()); + async.setFutureSynchronizer(&m_synchronizer); + return SetupResult::Continue; + }; + const auto onDone = [this](const Async<void> &) { + m_deploymentDataModified = false; + emit addOutput(Tr::tr("Packaging finished successfully."), OutputFormat::NormalMessage); + // TODO: Should it be the next task in sequence? connect(BuildManager::instance(), &BuildManager::buildQueueFinished, this, &TarPackageCreationStep::deployFinished); - }); - auto future = Utils::asyncRun(&TarPackageCreationStep::doPackage, this, - m_tarFilePath, m_ignoreMissingFilesAspect->value()); - watcher->setFuture(future); - m_synchronizer.addFuture(future); + }; + const auto onError = [this](const Async<void> &) { + emit addOutput(Tr::tr("Packaging failed."), OutputFormat::ErrorMessage); + }; + return AsyncTask<void>(onSetup, onDone, onError); } -void TarPackageCreationStep::doCancel() +void TarPackageCreationStep::fromMap(const Store &map) { - m_synchronizer.cancelAllFutures(); -} - -bool TarPackageCreationStep::fromMap(const QVariantMap &map) -{ - if (!BuildStep::fromMap(map)) - return false; + BuildStep::fromMap(map); + if (hasError()) + return; m_deployTimes.importDeployTimes(map); - return true; } -QVariantMap TarPackageCreationStep::toMap() const +void TarPackageCreationStep::toMap(Store &map) const { - QVariantMap map = BuildStep::toMap(); + BuildStep::toMap(map); map.insert(m_deployTimes.exportDeployTimes()); - return map; } QVariant TarPackageCreationStep::data(Id id) const @@ -270,7 +258,9 @@ void TarPackageCreationStep::addNeededDeploymentFiles( } } -void TarPackageCreationStep::doPackage(QPromise<bool> &promise, const FilePath &tarFilePath, +// TODO: Fix error / message reporting. Currently, the messages may still be posted +// (and delivered) after the async task was already canceled. +void TarPackageCreationStep::doPackage(QPromise<void> &promise, const FilePath &tarFilePath, bool ignoreMissingFiles) { // TODO: Optimization: Only package changed files @@ -279,7 +269,7 @@ void TarPackageCreationStep::doPackage(QPromise<bool> &promise, const FilePath & if (!tarFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { raiseError(Tr::tr("Error: tar file %1 cannot be opened (%2).") .arg(tarFilePath.toUserOutput(), tarFile.errorString())); - promise.addResult(false); + promise.future().cancel(); return; } @@ -293,19 +283,18 @@ void TarPackageCreationStep::doPackage(QPromise<bool> &promise, const FilePath & if (!appendFile(promise, tarFile, fileInfo, d.remoteDirectory() + QLatin1Char('/') + fileInfo.fileName(), tarFilePath, ignoreMissingFiles)) { - promise.addResult(false); + promise.future().cancel(); return; } } - const QByteArray eofIndicator(2*sizeof(TarFileHeader), 0); + const QByteArray eofIndicator(2 * sizeof(TarFileHeader), 0); if (tarFile.write(eofIndicator) != eofIndicator.length()) { raiseError(Tr::tr("Error writing tar file \"%1\": %2.") .arg(QDir::toNativeSeparators(tarFile.fileName()), tarFile.errorString())); - promise.addResult(false); + promise.future().cancel(); return; } - promise.addResult(true); } static bool setFilePath(TarFileHeader &header, const QByteArray &filePath) @@ -387,7 +376,7 @@ static bool writeHeader(QFile &tarFile, const QFileInfo &fileInfo, const QString return true; } -bool TarPackageCreationStep::appendFile(QPromise<bool> &promise, +bool TarPackageCreationStep::appendFile(QPromise<void> &promise, QFile &tarFile, const QFileInfo &fileInfo, const QString &remoteFilePath, diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index 6deaea90662..570ecdb958d 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -31,7 +31,7 @@ public: { setWidgetExpandedByDefault(false); - setInternalInitializer([this] { + setInternalInitializer([this]() -> expected_str<void> { const BuildStep *tarCreationStep = nullptr; for (BuildStep *step : deployConfiguration()->stepList()->steps()) { @@ -43,7 +43,7 @@ public: } } if (!tarCreationStep) - return CheckResult::failure(Tr::tr("No tarball creation step found.")); + return make_unexpected(Tr::tr("No tarball creation step found.")); m_packageFilePath = FilePath::fromVariant(tarCreationStep->data(Constants::TarPackageFilePathId)); @@ -54,7 +54,7 @@ public: private: QString remoteFilePath() const; bool isDeploymentNecessary() const final; - Group deployRecipe() final; + GroupItem deployRecipe() final; GroupItem uploadTask(); GroupItem installTask(); @@ -115,7 +115,7 @@ GroupItem TarPackageDeployStep::installTask() return ProcessTask(setupHandler, doneHandler, errorHandler); } -Group TarPackageDeployStep::deployRecipe() +GroupItem TarPackageDeployStep::deployRecipe() { return Group { uploadTask(), installTask() }; } diff --git a/src/plugins/resourceeditor/ResourceEditor.json.in b/src/plugins/resourceeditor/ResourceEditor.json.in index 3eb667334d1..be3ace268a7 100644 --- a/src/plugins/resourceeditor/ResourceEditor.json.in +++ b/src/plugins/resourceeditor/ResourceEditor.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"ResourceEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ResourceEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Qt Creator\", - \"Description\" : \"Editor for qrc files.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Qt Creator", + "Description" : "Editor for qrc files.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/vnd.qt.xml.resource\'>\", - \" <sub-class-of type=\'text/xml\'/>\", - \" <comment>Qt Resource file</comment>\", - \" <glob pattern=\'*.qrc\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/vnd.qt.xml.resource'>", + " <sub-class-of type='text/xml'/>", + " <comment>Qt Resource file</comment>", + " <glob pattern='*.qrc'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp index e08f52340fd..4340fc6123d 100644 --- a/src/plugins/resourceeditor/qrceditor/qrceditor.cpp +++ b/src/plugins/resourceeditor/qrceditor/qrceditor.cpp @@ -229,7 +229,7 @@ QString ResolveLocationContext::execCopyFileDialog(QWidget *parent, const QDir & // Helper to copy a file with message boxes static inline bool copyFile(const QString &file, const QString ©Name, QWidget *parent) { - if (QFile::exists(copyName)) { + if (QFileInfo::exists(copyName)) { if (!QFile::remove(copyName)) { QMessageBox::critical(parent, Tr::tr("Overwriting Failed"), Tr::tr("Could not overwrite file %1.") diff --git a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp index 8d8d6dc9f2b..ef1c077ca9b 100644 --- a/src/plugins/resourceeditor/qrceditor/resourcefile.cpp +++ b/src/plugins/resourceeditor/qrceditor/resourcefile.cpp @@ -50,7 +50,7 @@ void File::checkExistence() bool File::exists() { if (!m_checked) { - m_exists = QFile::exists(name); + m_exists = QFileInfo::exists(name); m_checked = true; } @@ -363,7 +363,7 @@ bool ResourceFile::renameFile(const QString &fileName, const QString &newFileNam } if (success) { - const bool exists = QFile::exists(newFileName); + const bool exists = QFileInfo::exists(newFileName); for (File *file : std::as_const(entries)) { file->name = newFileName; file->setExists(exists); @@ -1230,7 +1230,7 @@ EntryBackup * RelativeResourceModel::removeEntry(const QModelIndex &index) } else { const QString fileNameBackup = file(index); const QString aliasBackup = alias(index); - if (!QFile::exists(fileNameBackup)) { + if (!QFileInfo::exists(fileNameBackup)) { deleteItem(index); return new FileEntryBackup(*this, prefixIndex.row(), index.row(), fileNameBackup, aliasBackup); } diff --git a/src/plugins/resourceeditor/resourceeditor.qbs b/src/plugins/resourceeditor/resourceeditor.qbs index eb09034c0ca..eaa1c8a456b 100644 --- a/src/plugins/resourceeditor/resourceeditor.qbs +++ b/src/plugins/resourceeditor/resourceeditor.qbs @@ -1,39 +1,36 @@ -import qbs 1.0 - -Project { +QtcPlugin { name: "ResourceEditor" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "xml"] } - Depends { name: "Aggregation" } - Depends { name: "ProjectExplorer" } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["widgets", "xml"] } - Depends { name: "Core" } + Depends { name: "Aggregation" } + Depends { name: "Utils" } - cpp.defines: base.concat(["RESOURCEEDITOR_LIBRARY"]) + Depends { name: "Core" } + Depends { name: "ProjectExplorer" } - Group { - name: "General" - files: [ - "resourceeditorconstants.h", - "resourceeditorfactory.cpp", "resourceeditorfactory.h", - "resourceeditorplugin.cpp", "resourceeditorplugin.h", - "resourceeditortr.h", - "resourceeditorw.cpp", "resourceeditorw.h", - "resource_global.h", "resourcenode.cpp", "resourcenode.h" - ] - } + cpp.defines: base.concat(["RESOURCEEDITOR_LIBRARY"]) - Group { - name: "QRC Editor" - prefix: "qrceditor/" - files: [ - "qrceditor.cpp", "qrceditor.h", - "resourcefile.cpp", "resourcefile_p.h", - "resourceview.cpp", "resourceview.h", - "undocommands.cpp", "undocommands_p.h", - ] - } + Group { + name: "General" + files: [ + "resourceeditorconstants.h", + "resourceeditorfactory.cpp", "resourceeditorfactory.h", + "resourceeditorplugin.cpp", "resourceeditorplugin.h", + "resourceeditortr.h", + "resourceeditorw.cpp", "resourceeditorw.h", + "resource_global.h", "resourcenode.cpp", "resourcenode.h" + ] + } + + Group { + name: "QRC Editor" + prefix: "qrceditor/" + files: [ + "qrceditor.cpp", "qrceditor.h", + "resourcefile.cpp", "resourcefile_p.h", + "resourceview.cpp", "resourceview.h", + "undocommands.cpp", "undocommands_p.h", + ] } } diff --git a/src/plugins/resourceeditor/resourceeditorw.cpp b/src/plugins/resourceeditor/resourceeditorw.cpp index 3b49b92ae42..ddea065cadc 100644 --- a/src/plugins/resourceeditor/resourceeditorw.cpp +++ b/src/plugins/resourceeditor/resourceeditorw.cpp @@ -125,12 +125,11 @@ bool ResourceEditorDocument::saveImpl(QString *errorString, const FilePath &file if (debugResourceEditorW) qDebug() << ">ResourceEditorW::saveImpl: " << filePath; - const FilePath &actualName = filePath.isEmpty() ? this->filePath() : filePath; - if (actualName.isEmpty()) + if (filePath.isEmpty()) return false; m_blockDirtyChanged = true; - m_model->setFilePath(actualName); + m_model->setFilePath(filePath); if (!m_model->save()) { *errorString = m_model->errorMessage(); m_model->setFilePath(this->filePath()); @@ -146,7 +145,7 @@ bool ResourceEditorDocument::saveImpl(QString *errorString, const FilePath &file return true; } - setFilePath(actualName); + setFilePath(filePath); m_blockDirtyChanged = false; emit changed(); diff --git a/src/plugins/resourceeditor/resourcenode.h b/src/plugins/resourceeditor/resourcenode.h index 8e04267768e..d857a643761 100644 --- a/src/plugins/resourceeditor/resourcenode.h +++ b/src/plugins/resourceeditor/resourcenode.h @@ -25,6 +25,8 @@ public: ProjectExplorer::RemovedFilesFromProject removeFiles(const Utils::FilePaths &filePaths, Utils::FilePaths *notRemoved) override; + void compress() override { } // do not compress + bool addPrefix(const QString &prefix, const QString &lang); bool removePrefix(const QString &prefix, const QString &lang); diff --git a/src/plugins/saferenderer/SafeRenderer.json.in b/src/plugins/saferenderer/SafeRenderer.json.in index c8a6e3ee795..ae60bb3f020 100644 --- a/src/plugins/saferenderer/SafeRenderer.json.in +++ b/src/plugins/saferenderer/SafeRenderer.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"SafeRenderer\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "SafeRenderer", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Helper plugin for Qt Safe Renderer projects.\", - \"Url\" : \"https://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Helper plugin for Qt Safe Renderer projects.", + "Url" : "https://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/saferenderer/saferenderer.qrc b/src/plugins/saferenderer/saferenderer.qrc index 28baa346473..84999a48aac 100644 --- a/src/plugins/saferenderer/saferenderer.qrc +++ b/src/plugins/saferenderer/saferenderer.qrc @@ -8,5 +8,12 @@ <file>wizards/qsrapp/qml.qrc.tpl</file> <file>wizards/qsrapp/safeasset.qrc.tpl</file> <file>wizards/qsrapp/wizard.json</file> + <file>wizards/qsrapp2_1/file.pro</file> + <file>wizards/qsrapp2_1/CMakeLists.txt</file> + <file>wizards/qsrapp2_1/main.cpp.tpl</file> + <file>wizards/qsrapp2_1/main.qml.tpl</file> + <file>wizards/qsrapp2_1/qml.qrc.tpl</file> + <file>wizards/qsrapp2_1/safeasset.qrc.tpl</file> + <file>wizards/qsrapp2_1/wizard.json</file> </qresource> </RCC> diff --git a/src/plugins/saferenderer/wizards/qsrapp/wizard.json b/src/plugins/saferenderer/wizards/qsrapp/wizard.json index e05e5f7971a..6a05c4e1d0b 100644 --- a/src/plugins/saferenderer/wizards/qsrapp/wizard.json +++ b/src/plugins/saferenderer/wizards/qsrapp/wizard.json @@ -3,8 +3,8 @@ "supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ], "id": "E.QSRApp", "category": "D.QtSafeRendererApplication", - "trDescription": "Creates a Qt Safe Renderer project with simple UI and project setup.", - "trDisplayName": "Qt Safe Renderer Application", + "trDescription": "Creates a Qt Safe Renderer 2.0 project with a simple UI and project setup.\n\nThis project template does not work with QSR 2.1 or newer, and does not support CMake.", + "trDisplayName": "Qt Safe Renderer 2.0 Application", "trDisplayCategory": "Application (Qt Safe Renderer)", "icon": "../icon.png", "iconKind": "Themed", diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/CMakeLists.txt b/src/plugins/saferenderer/wizards/qsrapp2_1/CMakeLists.txt new file mode 100644 index 00000000000..e17daccd9c1 --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.16) +project(%{CMakeProjectName} LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) + +# Define the condition for HOST_BUILD +set(HOST_BUILD OFF) # Default to OFF + +if (NOT CMAKE_CROSSCOMPILING AND (NOT UNIX OR NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm|aarch64") OR APPLE)) + set(HOST_BUILD ON) +endif() + +find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick SafeRenderer SafeRendererTools SafePlatformAdaptation) + +set(sources main.cpp +) + +set (safeqmls +"main.qml" +) + +# Resource files are passed to qtsafelayouttool +set(resource_files + "qml.qrc" +) + +#resource.bin is loaded by qtsafelayouttool to find the resource data asset. +qt6_add_binary_resources(resources_%{CMakeProjectName} ${resource_files} DESTINATION resource.bin) +qsr_add_safelayout(generatelayout_%{CMakeProjectName} SAFE_QMLS ${safeqmls} + OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/layoutData + SAFE_RESOURCE "${CMAKE_CURRENT_LIST_DIR}/safeasset.qrc" + INPUT_RESOURCES resource.bin) +qsr_add_resource(buildresource_%{CMakeProjectName} sources "${CMAKE_CURRENT_LIST_DIR}/safeasset.qrc") + +if (HOST_BUILD) + qt6_add_resources(sources ${resource_files}) +endif() + +add_executable(%{CMakeProjectName} WIN32 MACOSX_BUNDLE + ${sources} +) + +#Enable when using monitor feature: +#target_compile_definitions(%{CMakeProjectName} PRIVATE USE_OUTPUTVERIFIER) + +add_dependencies(%{CMakeProjectName} generatelayout_%{CMakeProjectName}) + +if (HOST_BUILD) + target_compile_definitions(%{CMakeProjectName} PUBLIC + HOST_BUILD + ) + + target_link_libraries(%{CMakeProjectName} PUBLIC + Qt::Quick + Qt::Widgets + Qt::Qml + ) +else() + message(STATUS "Project is not linked with Qt when building for embedded systems.") +endif() + +target_link_libraries(%{CMakeProjectName} PUBLIC + Qt::SafeRenderer + Qt::SafePlatformAdaptation +) diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/file.pro b/src/plugins/saferenderer/wizards/qsrapp2_1/file.pro new file mode 100644 index 00000000000..6233d49b9cc --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/file.pro @@ -0,0 +1,46 @@ +QT = qtsaferenderer qsrplatformadaptation + +CONFIG += c++17 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \\ + %{CppFileName} + + +# List of language codes that your application supports. For example, SAFE_LANGUAGES = en fi. +#SAFE_LANGUAGES = en + +# List of translation file names excluding the language code. For example, SAFE_TRANSLATION = $$PWD/safeui. +#SAFE_TRANSLATION = $$PWD/safeui + +# List of translation file names including the language code. There must be one file +# for each language listed in SAFE_LANGUAGES. For example, TRANSLATIONS += safeui_en.ts safeui_fi.ts. +#TRANSLATIONS += safeui_en.ts + +# You can use an lupdate_only{...} conditional statement to specify the QML files that contain texts. +#lupdate_only { +# SOURCES += main.qml +#} + + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = $$PWD/imports + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + + +CONFIG += qtsaferenderer exceptions +SAFE_QML = $$PWD/main.qml +SAFE_LAYOUT_PATH = $$PWD/layoutData +SAFE_RESOURCES += safeasset.qrc + +!cross_compile: DEFINES += HOST_BUILD +!cross_compile: QT += widgets quick svg + +DISTFILES += main.qml diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/main.cpp.tpl b/src/plugins/saferenderer/wizards/qsrapp2_1/main.cpp.tpl new file mode 100644 index 00000000000..80b45e04bb9 --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/main.cpp.tpl @@ -0,0 +1,62 @@ +%{Cpp:LicenseTemplate}\ +%{JS: QtSupport.qtIncludes([ 'QtCore/QCoreApplication' ], + [ 'QtCore/QCoreApplication' ]) }\ + +#include <QtSafeRenderer/qsafelayout.h> +#include <QtSafeRenderer/qsafelayoutresourcereader.h> +#include <QtSafeRenderer/statemanager.h> + +#if defined(HOST_BUILD) +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#endif + +#if defined(USE_OUTPUTVERIFIER) +#include <outputverifier.h> +#include "testverifier.h" +#endif + +#include "safewindow.h" +#include "eventhandler.h" + +int main(int argc, char *argv[]) +{ + Q_UNUSED(argc); + Q_UNUSED(argv); + + static SafeRenderer::QSafeLayoutResourceReader layout("/layoutData/main/main.srl"); + +#if defined(USE_OUTPUTVERIFIER) + static SafeRenderer::OutputVerifier outputVerifier; + SafeRenderer::SafeWindow telltaleWindow(layout.size(), SafeRenderer::QSafePoint(0U, 0U), outputVerifier); +#else + SafeRenderer::SafeWindow telltaleWindow(layout.size(), SafeRenderer::QSafePoint(0U, 0U)); +#endif + + static SafeRenderer::StateManager stateManager(telltaleWindow, layout); + telltaleWindow.requestUpdate(); //Request is required because eventHandler is not running yet. + +#if defined(USE_OUTPUTVERIFIER) + SafeRenderer::EventHandler msgHandler(stateManager, telltaleWindow, outputVerifier); +#else + SafeRenderer::EventHandler msgHandler(stateManager, telltaleWindow); +#endif + +#if defined(HOST_BUILD) + //Mixing the Qt and Qt Safe Renderer renderers is done here only for demonstration purposes on host, not for production purposes of any kind. + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, qApp, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + qDebug() << "Failed to start the main.qml"; + }, Qt::QueuedConnection); + engine.addImportPath(":/imports"); + engine.load(url); +#endif + + msgHandler.handleEvents(); + + return 0; +} diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/main.qml.tpl b/src/plugins/saferenderer/wizards/qsrapp2_1/main.qml.tpl new file mode 100644 index 00000000000..94fccebcb2a --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/main.qml.tpl @@ -0,0 +1,26 @@ +import Qt.SafeRenderer 2.0 +import QtQuick.Window 2.15 + +Window { + id: window + width: 640 + height: 480 + visible: true + title: qsTr("Hello QSR") + + SafeText { + id: safeText + objectName: "safetextitem" + x: 206 + y: 208 + width: 340 + height: 34 + color: "#8ae234" + fillColor: "black" + text: "Hello Qt Safe Renderer!" + font.family: "Lato" + horizontalAlignment: Text.AlignLeft + font.pixelSize: 32 + runtimeEditable: true + } +} diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/qml.qrc.tpl b/src/plugins/saferenderer/wizards/qsrapp2_1/qml.qrc.tpl new file mode 100644 index 00000000000..5f6483ac33f --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/qml.qrc.tpl @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/safeasset.qrc.tpl b/src/plugins/saferenderer/wizards/qsrapp2_1/safeasset.qrc.tpl new file mode 100644 index 00000000000..601b61fc4c2 --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/safeasset.qrc.tpl @@ -0,0 +1,2 @@ +<!DOCTYPE RCC> +<RCC/> diff --git a/src/plugins/saferenderer/wizards/qsrapp2_1/wizard.json b/src/plugins/saferenderer/wizards/qsrapp2_1/wizard.json new file mode 100644 index 00000000000..9f493b9a212 --- /dev/null +++ b/src/plugins/saferenderer/wizards/qsrapp2_1/wizard.json @@ -0,0 +1,116 @@ +{ + "version": 1, + "supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ], + "id": "E.QSRApp2_1", + "category": "D.QtSafeRendererApplication", + "trDescription": "Creates a Qt Safe Renderer 2.1 (and newer) project with a simple UI and project setup.\n\nSupports both qmake and CMake.", + "trDisplayName": "Qt Safe Renderer Application 2.1", + "trDisplayCategory": "Application (Qt Safe Renderer)", + "icon": "../icon.png", + "iconKind": "Themed", + "featuresRequired": [ "QtSupport.Wizards.FeatureQt" ], + "enabled": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0 || value('Plugins').indexOf('CMakeProjectManager') >= 0}", + + "options": + [ + { "key": "ProjectFile", "value": "%{JS: value('BuildSystem') == 'qmake' ? value('ProFile') : value('CMakeFile')}" }, + { "key": "CMakeProjectName", "value": "%{JS: value('ProjectName').replace(/-/g, '_')}" }, + { "key": "ProFile", "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/' + value('ProjectName'), 'pro')}" }, + { "key": "CMakeFile", "value": "%{JS: Util.fileName(value('ProjectDirectory') + '/CMakeLists.txt')}" }, + { "key": "HasTranslation", "value": "%{JS: value('TsFileName') !== ''}" }, + { "key": "CppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src')}" } + ], + + "pages": + [ + { + "trDisplayName": "Project Location", + "trShortTitle": "Location", + "typeId": "Project", + "data": { "trDescription": "This wizard creates a simple Qt Safe Renderer application." } + }, + { + "trDisplayName": "Define Build System", + "trShortTitle": "Build System", + "typeId": "Fields", + "enabled": "%{JS: !value('IsSubproject')}", + "data": + [ + { + "name": "BuildSystem", + "trDisplayName": "Build system:", + "type": "ComboBox", + "persistenceKey": "BuildSystemType", + "data": + { + "index": 1, + "items": + [ + { + "trKey": "qmake", + "value": "qmake", + "condition": "%{JS: value('Plugins').indexOf('QmakeProjectManager') >= 0}" + }, + { + "trKey": "cmake", + "value": "cmake", + "condition": "%{JS: value('Plugins').indexOf('CMakeProjectManager') >= 0}" + } + ] + } + } + ] + }, + { + "trDisplayName": "Kit Selection", + "trShortTitle": "Kits", + "typeId": "Kits", + "enabled": "%{JS: !value('IsSubproject')}", + "data": { "projectFilePath": "%{ProjectFile}" } + }, + { + "trDisplayName": "Project Management", + "trShortTitle": "Summary", + "typeId": "Summary" + } + ], + "generators": + [ + { + "typeId": "File", + "data": + [ + { + "source": "file.pro", + "target": "%{ProFile}", + "openAsProject": true, + "condition": "%{JS: value('BuildSystem') === 'qmake'}" + }, + { + "source": "CMakeLists.txt", + "target": "%{CMakeFile}", + "openAsProject": true, + "condition": "%{JS: value('BuildSystem') === 'cmake'}" + }, + { + "source": "main.cpp.tpl", + "target": "%{CppFileName}", + "openInEditor": false + }, + { + "source": "safeasset.qrc.tpl", + "target": "safeasset.qrc" + }, + { + "source": "qml.qrc.tpl", + "target": "qml.qrc" + }, + { + "source": "main.qml.tpl", + "target": "main.qml", + "openInEditor": true + } + ] + } + ] +} diff --git a/src/plugins/screenrecorder/CMakeLists.txt b/src/plugins/screenrecorder/CMakeLists.txt new file mode 100644 index 00000000000..a16e316b2c8 --- /dev/null +++ b/src/plugins/screenrecorder/CMakeLists.txt @@ -0,0 +1,20 @@ +add_qtc_plugin(ScreenRecorder + PLUGIN_DEPENDS Core + DEPENDS Spinner + SOURCES + cropandtrim.cpp cropandtrim.h + export.cpp export.h + ffmpegutils.cpp ffmpegutils.h + record.cpp record.h + screenrecorder.qrc + screenrecorderconstants.h + screenrecorderplugin.cpp + screenrecordersettings.cpp screenrecordersettings.h +) + +extend_qtc_plugin(ScreenRecorder + CONDITION WITH_TESTS + SOURCES + screenrecorder_test.cpp screenrecorder_test.h + EXPLICIT_MOC screenrecorder_test.h +) diff --git a/src/plugins/screenrecorder/ScreenRecorder.json.in b/src/plugins/screenrecorder/ScreenRecorder.json.in new file mode 100644 index 00000000000..87391fd4380 --- /dev/null +++ b/src/plugins/screenrecorder/ScreenRecorder.json.in @@ -0,0 +1,19 @@ +{ + "Name" : "ScreenRecorder", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." + ], + "DisabledByDefault" : true, + "Description" : "Screen recording.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} +} diff --git a/src/plugins/screenrecorder/cropandtrim.cpp b/src/plugins/screenrecorder/cropandtrim.cpp new file mode 100644 index 00000000000..819f0e285b6 --- /dev/null +++ b/src/plugins/screenrecorder/cropandtrim.cpp @@ -0,0 +1,815 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cropandtrim.h" + +#include "ffmpegutils.h" +#include "screenrecordersettings.h" +#include "screenrecordertr.h" + +#include <utils/fileutils.h> +#include <utils/layoutbuilder.h> +#include <utils/process.h> +#include <utils/qtcsettings.h> +#include <utils/styledbar.h> +#include <utils/stylehelper.h> +#include <utils/utilsicons.h> + +#include <coreplugin/icore.h> + +#include <QAction> +#include <QClipboard> +#include <QDialog> +#include <QDialogButtonBox> +#include <QGuiApplication> +#include <QLabel> +#include <QMouseEvent> +#include <QPainter> +#include <QPushButton> +#include <QScrollArea> +#include <QSlider> +#include <QSpinBox> +#include <QStyleOptionSlider> +#include <QToolButton> + +using namespace Utils; + +namespace ScreenRecorder { + +CropScene::CropScene(QWidget *parent) + : QWidget(parent) +{ + setMouseTracking(true); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); +} + +void CropScene::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + QPainter p(this); + p.drawImage(QPointF(), m_buffer); +} + +void CropScene::initMouseInteraction(const QPoint &imagePos) +{ + static const auto inGripRange = [](int grip, int pos, int &clickOffset) -> bool { + const bool inRange = pos - m_gripWidth <= grip && pos + m_gripWidth >= grip; + if (inRange) + clickOffset = grip - pos; + return inRange; + }; + + m_mouse.clickOffset = {}; + if (inGripRange(imagePos.x(), m_cropRect.left(), m_mouse.clickOffset.rx())) { + m_mouse.margin = EdgeLeft; + m_mouse.cursorShape = Qt::SizeHorCursor; + } else if (inGripRange(imagePos.x(), m_cropRect.right(), m_mouse.clickOffset.rx())) { + m_mouse.margin = EdgeRight; + m_mouse.cursorShape = Qt::SizeHorCursor; + } else if (inGripRange(imagePos.y(), m_cropRect.top(), m_mouse.clickOffset.ry())) { + m_mouse.margin = EdgeTop; + m_mouse.cursorShape = Qt::SizeVerCursor; + } else if (inGripRange(imagePos.y(), m_cropRect.bottom(), m_mouse.clickOffset.ry())) { + m_mouse.margin = EdgeBottom; + m_mouse.cursorShape = Qt::SizeVerCursor; + } else if (!fullySelected() && activeMoveArea().contains(imagePos)) { + m_mouse.margin = Move; + m_mouse.cursorShape = Qt::SizeAllCursor; + m_mouse.clickOffset = imagePos - m_cropRect.topLeft(); + } else { + m_mouse.margin = Free; + m_mouse.cursorShape = Qt::ArrowCursor; + } +} + +void CropScene::updateBuffer() +{ + if (m_buffer.isNull()) + return; + + m_buffer.fill(palette().window().color()); + const qreal dpr = m_image->devicePixelRatioF(); + QPainter p(&m_buffer); + p.drawImage(lineWidth, lineWidth, *m_image); + + const qreal lineOffset = lineWidth / 2.0; + const QRectF r = { + m_cropRect.x() / dpr + lineOffset, + m_cropRect.y() / dpr + lineOffset, + m_cropRect.width() / dpr + lineWidth, + m_cropRect.height() / dpr + lineWidth + }; + + p.save(); + p.setClipRegion(QRegion(m_buffer.rect()).subtracted(QRegion(r.toRect()))); + p.setOpacity(0.85); + p.fillRect(m_buffer.rect(), qRgb(0x30, 0x30, 0x30)); + p.restore(); + + const auto paintLine = [&p](const QLineF &line) + { + const QPen blackPen(Qt::black, lineWidth); + p.setPen(blackPen); + p.drawLine(line); + const QPen whiteDotPen(Qt::white, lineWidth, Qt::DotLine); + p.setPen(whiteDotPen); + p.drawLine(line); + }; + paintLine(QLineF(r.left(), 0, r.left(), m_buffer.height())); + paintLine(QLineF(0, r.top(), m_buffer.width(), r.top())); + paintLine(QLineF(r.right(), 0, r.right(), m_buffer.height())); + paintLine(QLineF(0, r.bottom(), m_buffer.width(), r.bottom())); + + update(); +} + +QPoint CropScene::toImagePos(const QPoint &widgetPos) const +{ + const int dpr = int(m_image->devicePixelRatio()); + return {(widgetPos.x() - lineWidth) * dpr, (widgetPos.y() - lineWidth) * dpr}; +} + +QRect CropScene::activeMoveArea() const +{ + const qreal minRatio = 0.22; // At least 22% width and height of current selection + const int minAbsoluteSize = 40; + QRect result(0, 0, qMax(int(m_cropRect.width() * minRatio), minAbsoluteSize), + qMax(int(m_cropRect.height() * minRatio), minAbsoluteSize)); + result.moveCenter(m_cropRect.center()); + return result; +} + +QRect CropScene::cropRect() const +{ + return m_cropRect; +} + +void CropScene::setCropRect(const QRect &rect) +{ + m_cropRect = rect; + updateBuffer(); + emit cropRectChanged(m_cropRect); +} + +bool CropScene::fullySelected() const +{ + return m_image && m_image->rect() == m_cropRect; +} + +void CropScene::setFullySelected() +{ + if (!m_image) + return; + setCropRect(m_image->rect()); +} + +QRect CropScene::fullRect() const +{ + return m_image ? m_image->rect() : QRect(); +} + +void CropScene::setImage(const QImage &image) +{ + m_image = ℑ + const QSize sceneSize = m_image->deviceIndependentSize().toSize() + .grownBy({lineWidth, lineWidth, lineWidth, lineWidth}); + const QSize sceneSizeDpr = sceneSize * m_image->devicePixelRatio(); + m_buffer = QImage(sceneSizeDpr, QImage::Format_RGB32); + m_buffer.setDevicePixelRatio(m_image->devicePixelRatio()); + updateBuffer(); + resize(sceneSize); +} + +QImage CropScene::croppedImage() const +{ + if (!m_image) + return {}; + + return m_image->copy(m_cropRect); +} + +void CropScene::mouseMoveEvent(QMouseEvent *event) +{ + const QPoint imagePos = toImagePos(event->pos()); + + if (m_mouse.dragging) { + switch (m_mouse.margin) { + case EdgeLeft: + m_cropRect.setLeft(qBound(0, imagePos.x() - m_mouse.clickOffset.x(), + m_cropRect.right())); + break; + case EdgeTop: + m_cropRect.setTop(qBound(0, imagePos.y() - m_mouse.clickOffset.y(), + m_cropRect.bottom())); + break; + case EdgeRight: + m_cropRect.setRight(qBound(m_cropRect.left(), imagePos.x() - m_mouse.clickOffset.x(), + m_image->width() - 1)); + break; + case EdgeBottom: + m_cropRect.setBottom(qBound(m_cropRect.top(), imagePos.y() - m_mouse.clickOffset.y(), + m_image->height() - 1)); + break; + case Free: + m_cropRect = QRect(m_mouse.startImagePos, imagePos).normalized() + .intersected(m_image->rect()); + break; + case Move: { + const QPoint topLeft(qBound(0, imagePos.x() - m_mouse.clickOffset.x(), + m_image->width() - m_cropRect.width()), + qBound(0, imagePos.y() - m_mouse.clickOffset.y(), + m_image->height() - m_cropRect.height())); + m_cropRect = QRect(topLeft, m_cropRect.size()); + break; + } + } + emit cropRectChanged(m_cropRect); + updateBuffer(); + } else { + initMouseInteraction(imagePos); + setCursor(m_mouse.cursorShape); + } + + QWidget::mouseMoveEvent(event); +} + +void CropScene::mousePressEvent(QMouseEvent *event) +{ + const QPoint imagePos = toImagePos(event->pos()); + + m_mouse.dragging = true; + m_mouse.startImagePos = imagePos; + + QWidget::mousePressEvent(event); +} + +void CropScene::mouseReleaseEvent(QMouseEvent *event) +{ + m_mouse.dragging = false; + setCursor(Qt::ArrowCursor); + + QWidget::mouseReleaseEvent(event); +} + +class CropWidget : public QWidget +{ +public: + explicit CropWidget(QWidget *parent = nullptr); + + QRect cropRect() const; + void setCropRect(const QRect &rect); + + void setImage(const QImage &image); + +private: + void updateWidgets(); + void onSpinBoxChanged(); + void onCropRectChanged(); + + CropScene *m_cropScene; + + QSpinBox *m_xSpinBox; + QSpinBox *m_ySpinBox; + QSpinBox *m_widthSpinBox; + QSpinBox *m_heightSpinBox; + + CropSizeWarningIcon *m_warningIcon; + QToolButton *m_resetButton; +}; + +CropWidget::CropWidget(QWidget *parent) + : QWidget(parent) +{ + m_cropScene = new CropScene; + + auto scrollArea = new QScrollArea; + scrollArea->setWidget(m_cropScene); + + for (auto s : {&m_xSpinBox, &m_ySpinBox, &m_widthSpinBox, &m_heightSpinBox}) { + *s = new QSpinBox; + (*s)->setMaximum(99999); // Will be adjusted in CropWidget::setImage + (*s)->setSuffix(" px"); + } + m_widthSpinBox->setMinimum(1); + m_heightSpinBox->setMinimum(1); + + m_resetButton = new QToolButton; + m_resetButton->setIcon(Icons::RESET.icon()); + + m_warningIcon = new CropSizeWarningIcon(CropSizeWarningIcon::StandardVariant); + + auto saveImageButton = new QToolButton; + saveImageButton->setToolTip(Tr::tr("Save current, cropped frame as image file.")); + saveImageButton->setIcon(Icons::SAVEFILE.icon()); + + auto copyImageToClipboardAction = new QAction(Tr::tr("Copy current, cropped frame as image " + "to the clipboard."), this); + copyImageToClipboardAction->setIcon(Icons::SNAPSHOT.icon()); + copyImageToClipboardAction->setShortcut(QKeySequence::Copy); + + auto copyImageToClipboardButton = new QToolButton; + copyImageToClipboardButton->setDefaultAction(copyImageToClipboardAction); + + using namespace Layouting; + Column { + scrollArea, + Row { + Tr::tr("X:"), m_xSpinBox, + Space(4), Tr::tr("Y:"), m_ySpinBox, + Space(16), Tr::tr("Width:"), m_widthSpinBox, + Space(4), Tr::tr("Height:"), m_heightSpinBox, + m_resetButton, + m_warningIcon, + st, + saveImageButton, + copyImageToClipboardButton, + }, + noMargin(), + }.attachTo(this); + + connect(m_xSpinBox, &QSpinBox::valueChanged, this, &CropWidget::onSpinBoxChanged); + connect(m_ySpinBox, &QSpinBox::valueChanged, this, &CropWidget::onSpinBoxChanged); + connect(m_widthSpinBox, &QSpinBox::valueChanged, this, &CropWidget::onSpinBoxChanged); + connect(m_heightSpinBox, &QSpinBox::valueChanged, this, &CropWidget::onSpinBoxChanged); + connect(m_cropScene, &CropScene::cropRectChanged, this, &CropWidget::onCropRectChanged); + connect(m_resetButton, &QToolButton::pressed, this, [this] { + m_cropScene->setFullySelected(); + }); + connect(saveImageButton, &QToolButton::clicked, this, [this] { + FilePathAspect &lastDir = Internal::settings().lastSaveImageDirectory; + const QString ext(".png"); + FilePath file = FileUtils::getSaveFilePath(nullptr, Tr::tr("Save Current Frame As"), + lastDir(), "*" + ext); + if (!file.isEmpty()) { + if (!file.endsWith(ext)) + file = file.stringAppended(ext); + lastDir.setValue(file.parentDir()); + lastDir.writeToSettingsImmediatly(); + const QImage image = m_cropScene->croppedImage(); + image.save(file.toString()); + } + }); + connect(copyImageToClipboardAction, &QAction::triggered, this, [this] { + const QImage image = m_cropScene->croppedImage(); + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setImage(image); + }); + + updateWidgets(); +} + +QRect CropWidget::cropRect() const +{ + return m_cropScene->cropRect(); +} + +void CropWidget::setCropRect(const QRect &rect) +{ + m_cropScene->setCropRect(rect); +} + +void CropWidget::setImage(const QImage &image) +{ + const QRect rBefore = m_cropScene->fullRect(); + m_cropScene->setImage(image); + const QRect rAfter = m_cropScene->fullRect(); + if (rBefore != rAfter) { + m_xSpinBox->setMaximum(rAfter.width() - 1); + m_ySpinBox->setMaximum(rAfter.height() - 1); + m_widthSpinBox->setMaximum(rAfter.width()); + m_heightSpinBox->setMaximum(rAfter.height()); + } + updateWidgets(); +} + +void CropWidget::updateWidgets() +{ + m_resetButton->setEnabled(!m_cropScene->fullySelected()); + const QRect r = m_cropScene->cropRect(); + m_warningIcon->setCropSize(r.size()); +} + +void CropWidget::onSpinBoxChanged() +{ + const QRect spinBoxRect = { + m_xSpinBox->value(), + m_ySpinBox->value(), + m_widthSpinBox->value(), + m_heightSpinBox->value() + }; + m_cropScene->setCropRect(spinBoxRect.intersected(m_cropScene->fullRect())); +} + +void CropWidget::onCropRectChanged() +{ + const QRect rect = m_cropScene->cropRect(); + const struct { QSpinBox *spinBox; int value; } updates[] = { + {m_xSpinBox, rect.x()}, + {m_ySpinBox, rect.y()}, + {m_widthSpinBox, rect.width()}, + {m_heightSpinBox, rect.height()}, + }; + for (const auto &update : updates) { + update.spinBox->blockSignals(true); + update.spinBox->setValue(update.value); + update.spinBox->blockSignals(false); + } + updateWidgets(); +} + +class SelectionSlider : public QSlider +{ +public: + explicit SelectionSlider(QWidget *parent = nullptr); + + void setSelectionRange(FrameRange range); + +protected: + void paintEvent(QPaintEvent *) override; + +private: + FrameRange m_range; +}; + +SelectionSlider::SelectionSlider(QWidget *parent) + : QSlider(Qt::Horizontal, parent) + , m_range({-1, -1}) +{ + setPageStep(50); +} + +void SelectionSlider::setSelectionRange(FrameRange range) +{ + m_range = range; + update(); +} + +void SelectionSlider::paintEvent(QPaintEvent *) +{ + QStyleOptionSlider opt; + initStyleOption(&opt); + opt.subControls = QStyle::SC_SliderHandle; // Draw only the handle. We draw the rest, here. + + const int tickOffset = style()->pixelMetric(QStyle::PM_SliderTickmarkOffset, &opt, this); + QRect grooveRect = style()->subControlRect(QStyle::CC_Slider, &opt, + QStyle::SC_SliderGroove, this) + .adjusted(tickOffset, 0, -tickOffset, 0); + grooveRect.setTop(rect().top()); + grooveRect.setBottom(rect().bottom()); + const QColor bgColor = palette().window().color(); + const QColor fgColor = palette().text().color(); + const QColor grooveColor = StyleHelper::mergedColors(bgColor, fgColor, 80); + const QColor selectionColor = StyleHelper::mergedColors(bgColor, fgColor, 45); + QPainter p(this); + p.fillRect(grooveRect, grooveColor); + const qreal pixelsPerFrame = grooveRect.width() / qreal(maximum()); + const int startPixels = int(m_range.first * pixelsPerFrame); + const int endPixels = int((maximum() - m_range.second) * pixelsPerFrame); + p.fillRect(grooveRect.adjusted(startPixels, 0, -endPixels, 0), selectionColor); + style()->drawComplexControl(QStyle::CC_Slider, &opt, &p, this); +} + +class TrimWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TrimWidget(const ClipInfo &clip, QWidget *parent = nullptr); + + void setCurrentFrame(int frame); + int currentFrame() const; + void setTrimRange(FrameRange range); + FrameRange trimRange() const; + +signals: + void positionChanged(); + void trimRangeChanged(FrameRange range); + +private: + void resetTrimRange(); + void updateTrimWidgets(); + void emitTrimRangeChange(); + + ClipInfo m_clipInfo; + SelectionSlider *m_frameSlider; + TimeLabel *m_currentTime; + TimeLabel *m_clipDuration; + + struct { + QPushButton *button; + TimeLabel *timeLabel; + } m_trimStart, m_trimEnd; + TimeLabel *m_trimRange; + QToolButton *m_trimResetButton; +}; + +TrimWidget::TrimWidget(const ClipInfo &clip, QWidget *parent) + : QWidget(parent) + , m_clipInfo(clip) +{ + m_frameSlider = new SelectionSlider; + + m_currentTime = new TimeLabel(m_clipInfo); + + m_clipDuration = new TimeLabel(m_clipInfo); + + m_trimStart.button = new QPushButton(Tr::tr("Start:")); + m_trimStart.timeLabel = new TimeLabel(m_clipInfo); + + m_trimEnd.button = new QPushButton(Tr::tr("End:")); + m_trimEnd.timeLabel = new TimeLabel(m_clipInfo); + + m_trimRange = new TimeLabel(m_clipInfo); + + m_trimResetButton = new QToolButton; + m_trimResetButton->setIcon(Icons::RESET.icon()); + + using namespace Layouting; + Column { + Row { m_frameSlider, m_currentTime, "/", m_clipDuration }, + Group { + title(Tr::tr("Trimming")), + Row { + m_trimStart.button, m_trimStart.timeLabel, + Space(20), + m_trimEnd.button, m_trimEnd.timeLabel, + Stretch(), Space(20), + Tr::tr("Range:"), m_trimRange, + m_trimResetButton, + }, + }, + noMargin(), + }.attachTo(this); + + connect(m_frameSlider, &QSlider::valueChanged, this, [this]() { + m_currentTime->setFrame(currentFrame()); + updateTrimWidgets(); + emit positionChanged(); + }); + connect(m_trimStart.button, &QPushButton::clicked, this, [this] (){ + m_trimStart.timeLabel->setFrame(currentFrame()); + updateTrimWidgets(); + emitTrimRangeChange(); + }); + connect(m_trimEnd.button, &QPushButton::clicked, this, [this] (){ + m_trimEnd.timeLabel->setFrame(currentFrame()); + updateTrimWidgets(); + emitTrimRangeChange(); + }); + connect(m_trimResetButton, &QToolButton::clicked, this, &TrimWidget::resetTrimRange); + + m_frameSlider->setMaximum(m_clipInfo.framesCount()); + m_currentTime->setFrame(currentFrame()); + m_clipDuration->setFrame(m_clipInfo.framesCount()); + resetTrimRange(); +} + +void TrimWidget::setCurrentFrame(int frame) +{ + m_frameSlider->setValue(frame); +} + +int TrimWidget::currentFrame() const +{ + return m_frameSlider->value(); +} + +void TrimWidget::setTrimRange(FrameRange range) +{ + m_trimStart.timeLabel->setFrame(range.first); + m_trimEnd.timeLabel->setFrame(range.second); + m_frameSlider->setSelectionRange(trimRange()); +} + +FrameRange TrimWidget::trimRange() const +{ + return { m_trimStart.timeLabel->frame(), m_trimEnd.timeLabel->frame() }; +} + +void TrimWidget::resetTrimRange() +{ + setTrimRange({0, m_clipInfo.framesCount()}); + emitTrimRangeChange(); + updateTrimWidgets(); +} + +void TrimWidget::updateTrimWidgets() +{ + const int current = currentFrame(); + const int trimStart = m_trimStart.timeLabel->frame(); + const int trimEnd = m_trimEnd.timeLabel->frame(); + m_trimStart.button->setEnabled(current < m_clipInfo.framesCount() && current < trimEnd); + m_trimEnd.button->setEnabled(current > 0 && current > trimStart); + m_trimRange->setFrame(trimEnd - trimStart); + m_frameSlider->setSelectionRange(trimRange()); + m_trimResetButton->setEnabled(!m_clipInfo.isCompleteRange(trimRange())); +} + +void TrimWidget::emitTrimRangeChange() +{ + emit trimRangeChanged(trimRange()); +} + +class CropAndTrimDialog : public QDialog +{ +public: + explicit CropAndTrimDialog(const ClipInfo &clip, QWidget *parent = nullptr); + + void setCropRect(const QRect &rect); + QRect cropRect() const; + void setTrimRange(FrameRange range); + FrameRange trimRange() const; + + int currentFrame() const; + void setCurrentFrame(int frame); + +private: + void onSeekPositionChanged(); + void startFrameFetch(); + + ClipInfo m_clipInfo; + CropWidget *m_cropWidget; + TrimWidget *m_trimWidget; + QImage m_previewImage; + + Process *m_process; + int m_nextFetchFrame = -1; +}; + +CropAndTrimDialog::CropAndTrimDialog(const ClipInfo &clip, QWidget *parent) + : QDialog(parent, Qt::Window) + , m_clipInfo(clip) +{ + setWindowTitle(Tr::tr("Crop and Trim")); + + m_cropWidget = new CropWidget; + + m_trimWidget = new TrimWidget(m_clipInfo); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + using namespace Layouting; + Column { + Group { + title("Cropping"), + Column { m_cropWidget }, + }, + Space(16), + m_trimWidget, + buttonBox, + }.attachTo(this); + + m_process = new Process(this); + connect(m_process, &Process::done, this, [this] { + if (m_process->exitCode() != 0) { + FFmpegUtils::reportError(m_process->commandLine(), + m_process->readAllRawStandardError()); + return; + } + const QByteArray &imageData = m_process->rawStdOut(); + startFrameFetch(); + if (imageData.isEmpty()) + return; + m_previewImage = QImage(reinterpret_cast<const uchar*>(imageData.constData()), + m_clipInfo.dimensions.width(), m_clipInfo.dimensions.height(), + QImage::Format_RGB32); + m_previewImage.detach(); + m_cropWidget->setImage(m_previewImage); + }); + connect(m_trimWidget, &TrimWidget::positionChanged, + this, &CropAndTrimDialog::onSeekPositionChanged); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + + onSeekPositionChanged(); + resize(1000, 800); +} + +void CropAndTrimDialog::onSeekPositionChanged() +{ + // -1, because frame numbers are 0-based + m_nextFetchFrame = qMin(m_trimWidget->currentFrame(), m_clipInfo.framesCount() - 1); + if (!m_process->isRunning()) + startFrameFetch(); +} + +void CropAndTrimDialog::startFrameFetch() +{ + if (m_nextFetchFrame == -1) + return; + + const CommandLine cl = { + Internal::settings().ffmpegTool(), + { + "-v", "error", + "-ss", m_clipInfo.timeStamp(m_nextFetchFrame), + "-i", m_clipInfo.file.toUserOutput(), + "-threads", "1", + "-frames:v", "1", + "-f", "rawvideo", + "-pix_fmt", "bgra", + "-" + } + }; + m_process->close(); + m_nextFetchFrame = -1; + m_process->setCommand(cl); + m_process->setWorkingDirectory(Internal::settings().ffmpegTool().parentDir()); + m_process->start(); +} + +void CropAndTrimDialog::setCropRect(const QRect &rect) +{ + m_cropWidget->setCropRect(rect); +} + +QRect CropAndTrimDialog::cropRect() const +{ + return m_cropWidget->cropRect(); +} + +void CropAndTrimDialog::setTrimRange(FrameRange range) +{ + m_trimWidget->setTrimRange(range); +} + +FrameRange CropAndTrimDialog::trimRange() const +{ + return m_trimWidget->trimRange(); +} + +int CropAndTrimDialog::currentFrame() const +{ + return m_trimWidget->currentFrame(); +} + +void CropAndTrimDialog::setCurrentFrame(int frame) +{ + m_trimWidget->setCurrentFrame(frame); +} + +CropAndTrimWidget::CropAndTrimWidget(QWidget *parent) + : StyledBar(parent) +{ + m_button = new QToolButton; + m_button->setText(Tr::tr("Crop and Trim...")); + + m_cropSizeWarningIcon = new CropSizeWarningIcon(CropSizeWarningIcon::ToolBarVariant); + + using namespace Layouting; + Row { + m_button, + m_cropSizeWarningIcon, + noMargin(), spacing(0), + }.attachTo(this); + + connect(m_button, &QPushButton::clicked, this, [this] { + CropAndTrimDialog dlg(m_clipInfo, Core::ICore::dialogParent()); + dlg.setCropRect(m_cropRect); + dlg.setTrimRange(m_trimRange); + dlg.setCurrentFrame(m_currentFrame); + if (dlg.exec() == QDialog::Accepted) { + m_cropRect = dlg.cropRect(); + m_trimRange = dlg.trimRange(); + m_currentFrame = dlg.currentFrame(); + emit cropRectChanged(m_cropRect); + emit trimRangeChanged(m_trimRange); + updateWidgets(); + } + }); + + updateWidgets(); +} + +void CropAndTrimWidget::setClip(const ClipInfo &clip) +{ + if (clip.dimensions != m_clipInfo.dimensions) + m_cropRect = {QPoint(), clip.dimensions}; // Reset only if clip size changed + m_clipInfo = clip; + m_currentFrame = 0; + m_trimRange = {m_currentFrame, m_clipInfo.framesCount()}; + updateWidgets(); +} + +void CropAndTrimWidget::updateWidgets() +{ + if (!m_clipInfo.isNull()) { + const QString cropText = + !m_clipInfo.isCompleteArea(m_cropRect) + ? Tr::tr("Crop to %1x%2px.").arg(m_cropRect.width()).arg(m_cropRect.height()) + : Tr::tr("Complete area."); + + const QString trimText = + !m_clipInfo.isCompleteRange(m_trimRange) + ? Tr::tr("Frames %1 to %2.").arg(m_trimRange.first).arg(m_trimRange.second) + : Tr::tr("Complete clip."); + + m_button->setToolTip(cropText + " " + trimText); + } + + m_cropSizeWarningIcon->setCropSize(m_cropRect.size()); +} + +} // namespace ScreenRecorder + +#include "cropandtrim.moc" diff --git a/src/plugins/screenrecorder/cropandtrim.h b/src/plugins/screenrecorder/cropandtrim.h new file mode 100644 index 00000000000..b017a6bba94 --- /dev/null +++ b/src/plugins/screenrecorder/cropandtrim.h @@ -0,0 +1,97 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "ffmpegutils.h" + +#include <utils/styledbar.h> + +QT_BEGIN_NAMESPACE +class QToolButton; +QT_END_NAMESPACE + +namespace ScreenRecorder { + +class CropScene : public QWidget +{ + Q_OBJECT + +public: + CropScene(QWidget *parent = nullptr); + + QRect cropRect() const; + void setCropRect(const QRect &rect); + bool fullySelected() const; + void setFullySelected(); + QRect fullRect() const; + void setImage(const QImage &image); + QImage croppedImage() const; + + const static int lineWidth = 1; + +signals: + void cropRectChanged(const QRect &cropRect); + +protected: + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *event) override; + +private: + enum MarginEditing { + EdgeLeft, + EdgeTop, + EdgeRight, + EdgeBottom, + Free, + Move, + }; + + void initMouseInteraction(const QPoint &pos); + void updateBuffer(); + QPoint toImagePos(const QPoint &widgetCoordinate) const; + QRect activeMoveArea() const; + + const static int m_gripWidth = 8; + QRect m_cropRect; + const QImage *m_image = nullptr; + QImage m_buffer; + + struct MouseInteraction { + bool dragging = false; + MarginEditing margin; + QPoint startImagePos; + QPoint clickOffset; // Due to m_gripWidth, the mouse pointer is not precicely on the drag + // line. Maintain the offset while dragging, to avoid an initial jump. + Qt::CursorShape cursorShape = Qt::ArrowCursor; + } m_mouse; +}; + +class CropAndTrimWidget : public Utils::StyledBar +{ + Q_OBJECT + +public: + CropAndTrimWidget(QWidget *parent = nullptr); + + void setClip(const ClipInfo &clip); + +signals: + void cropRectChanged(const QRect &rect); + void trimRangeChanged(FrameRange range); + +private: + void updateWidgets(); + + QToolButton *m_button; + + ClipInfo m_clipInfo; + QRect m_cropRect; + int m_currentFrame = 0; + FrameRange m_trimRange; + CropSizeWarningIcon *m_cropSizeWarningIcon; +}; + +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/export.cpp b/src/plugins/screenrecorder/export.cpp new file mode 100644 index 00000000000..0c9b67d2a34 --- /dev/null +++ b/src/plugins/screenrecorder/export.cpp @@ -0,0 +1,300 @@ + // Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "export.h" + +#include "ffmpegutils.h" +#include "screenrecordersettings.h" +#include "screenrecordertr.h" + +#include <utils/algorithm.h> +#include <utils/fileutils.h> +#include <utils/layoutbuilder.h> +#include <utils/process.h> +#include <utils/styledbar.h> +#include <utils/utilsicons.h> + +#include <coreplugin/progressmanager/futureprogress.h> +#include <coreplugin/progressmanager/progressmanager.h> + +#include <QFutureWatcher> +#include <QToolButton> + +using namespace Utils; + +namespace ScreenRecorder { + +const char screenRecordingExportId[] = "ScreenRecorder::screenRecordingExportTask"; + +static const QVector<ExportWidget::Format> &formats() +{ + static const QVector<ExportWidget::Format> result = { + { + ExportWidget::Format::AnimatedImage, + ExportWidget::Format::Lossy, + "GIF", + ".gif", + { + }, + }, + { + ExportWidget::Format::AnimatedImage, + ExportWidget::Format::Lossless, + "WebP", + ".webp", + { + "-lossless", "1", + "-compression_level", "6", + "-qscale", "100", + }, + }, + { + ExportWidget::Format::AnimatedImage, + ExportWidget::Format::Lossy, + "WebP/VP8", + ".webp", + { + "-pix_fmt", "yuv420p", + "-compression_level", "6", + }, + }, + { + ExportWidget::Format::Video, + ExportWidget::Format::Lossy, + "MP4/H.264", + ".mp4", + { + "-pix_fmt", "yuv420p", // 4:2:0 chroma subsampling for Firefox compatibility + "-codec", "libx264", + "-preset", "veryslow", + "-level", "5.2", + "-tune", "animation", + "-movflags", "+faststart", + }, + }, + { + ExportWidget::Format::Video, + ExportWidget::Format::Lossy, + "WebM/VP9", + ".webm", + { + "-pix_fmt", "yuv420p", + "-codec", "libvpx-vp9", + "-crf", "36", // Creates slightly smaller files than the "MP4/H.264" preset + "-deadline", "best", + "-row-mt", "1", + }, + }, + { + ExportWidget::Format::AnimatedImage, + ExportWidget::Format::Lossless, + "avif", + ".avif", + { + "-lossless", "1", + }, + }, + { + ExportWidget::Format::Video, + ExportWidget::Format::Lossy, + "WebM/AV1", + ".webm", + { + "-pix_fmt", "yuv422p", + "-codec", "libaom-av1", + }, + }, + { + ExportWidget::Format::Video, + ExportWidget::Format::Lossless, + "Mov/qtrle", + ".mov", + { + "-codec", "qtrle", + }, + }, + }; + return result; +} + +static QString fileDialogFilters() +{ + return transform(formats(), [] (const ExportWidget::Format &fp) { + return fp.fileDialogFilter(); + }).join(";;"); +} + +QString ExportWidget::Format::fileDialogFilter() const +{ + return displayName + + " - " + (kind == Video ? Tr::tr("Video") : Tr::tr("Animated image")) + + " - " + (compression == Lossy ? Tr::tr("Lossy") : Tr::tr("Lossless")) + + " (*" + fileExtension + ")"; +} + +ExportWidget::ExportWidget(QWidget *parent) + : StyledBar(parent) + , m_trimRange({-1, -1}) +{ + m_process = new Process(this); + m_process->setUseCtrlCStub(true); + m_process->setProcessMode(ProcessMode::Writer); + + auto exportButton = new QToolButton; + exportButton->setText(Tr::tr("Export...")); + + using namespace Layouting; + Row { st, new StyledSeparator, exportButton, noMargin(), spacing(0) }.attachTo(this); + + connect(exportButton, &QToolButton::clicked, this, [this] { + FilePathAspect &lastDir = Internal::settings().exportLastDirectory; + StringAspect &lastFormat = Internal::settings().exportLastFormat; + const Format &defaultFormat = formats().at(1); + QTC_CHECK(defaultFormat.displayName == lastFormat.defaultValue()); + QString selectedFilter = findOr(formats(), defaultFormat, + [&lastFormat] (const Format &f) { + return f.displayName == lastFormat(); + }).fileDialogFilter(); + FilePath file = FileUtils::getSaveFilePath(nullptr, Tr::tr("Save As"), lastDir(), + fileDialogFilters(), &selectedFilter); + if (!file.isEmpty()) { + m_currentFormat = findOr(formats(), defaultFormat, + [&selectedFilter] (const Format &fp) { + return fp.fileDialogFilter() == selectedFilter; + }); + if (!file.endsWith(m_currentFormat.fileExtension)) + file = file.stringAppended(m_currentFormat.fileExtension); + m_outputClipInfo.file = file; + lastDir.setValue(file.parentDir()); + lastDir.writeToSettingsImmediatly(); + lastFormat.setValue(m_currentFormat.displayName); + lastFormat.writeToSettingsImmediatly(); + startExport(); + } + }); + connect(m_process, &Process::started, this, [this] { + emit started(); + }); + connect(m_process, &Process::done, this, [this] { + m_futureInterface->reportFinished(); + if (m_process->exitCode() == 0) { + emit finished(m_outputClipInfo.file); + } else { + FFmpegUtils::reportError(m_process->commandLine(), m_lastOutputChunk); + emit finished({}); + } + }); + connect(m_process, &Process::readyReadStandardError, this, [this] { + m_lastOutputChunk = m_process->readAllRawStandardError(); + const int frameProgress = FFmpegUtils::parseFrameProgressFromOutput(m_lastOutputChunk); + if (frameProgress >= 0) + m_futureInterface->setProgressValue(frameProgress); + }); +} + +ExportWidget::~ExportWidget() +{ + interruptExport(); +} + +void ExportWidget::startExport() +{ + m_futureInterface.reset(new QFutureInterface<void>); + m_futureInterface->setProgressRange(0, m_trimRange.second - m_trimRange.first); + Core::ProgressManager::addTask(m_futureInterface->future(), + Tr::tr("Exporting Screen Recording"), screenRecordingExportId); + m_futureInterface->setProgressValue(0); + m_futureInterface->reportStarted(); + const auto watcher = new QFutureWatcher<void>(this); + connect(watcher, &QFutureWatcher<void>::canceled, this, &ExportWidget::interruptExport); + connect(watcher, &QFutureWatcher<void>::finished, this, [watcher] { + watcher->disconnect(); + watcher->deleteLater(); + }); + watcher->setFuture(m_futureInterface->future()); + + m_process->close(); + const CommandLine cl(Internal::settings().ffmpegTool(), ffmpegExportParameters()); + m_process->setCommand(cl); + m_process->setWorkingDirectory(Internal::settings().ffmpegTool().parentDir()); + FFmpegUtils::logFfmpegCall(cl); + m_process->start(); +} + +void ExportWidget::interruptExport() +{ + FFmpegUtils::killFfmpegProcess(m_process); +} + +QStringList ExportWidget::ffmpegExportParameters() const +{ + const bool isGif = m_currentFormat.fileExtension == ".gif"; + const QString trimFilter = + !m_inputClipInfo.isCompleteRange(m_trimRange) + ? QString("[v:0]trim=start=%1:end=%2[trimmed]" + ";[trimmed]setpts=PTS-STARTPTS[setpts]") + .arg(m_inputClipInfo.secondForFrame(m_trimRange.first)) + .arg(m_inputClipInfo.secondForFrame(m_trimRange.second)) + : QString("[v:0]null[setpts]"); + + const QString cropFilter = + !m_inputClipInfo.isCompleteArea(m_cropRect) + ? QString(";[setpts]crop=w=%3:h=%4:x=%5:y=%6[cropped]") + .arg(m_cropRect.width()).arg(m_cropRect.height()) + .arg(m_cropRect.left()).arg(m_cropRect.top()) + : QString(";[setpts]null[cropped]"); + + const QString extraFilter = + isGif + ? QString(";[cropped]split[cropped1][cropped2]" + ";[cropped1]palettegen=" + "reserve_transparent=false" + ":max_colors=256[pal]" + ";[cropped2][pal]paletteuse=" + "diff_mode=rectangle[out]") + : QString(";[cropped]null[out]"); + + QStringList loop; + if (m_currentFormat.kind == Format::AnimatedImage) { + const bool doLoop = Internal::settings().animatedImagesAsEndlessLoop(); + // GIF muxer take different values for "don't loop" than WebP and avif muxer + const QLatin1String dontLoopParam(isGif ? "-1" : "1"); + loop.append({"-loop", doLoop ? QLatin1String("0") : dontLoopParam}); + } + + const QStringList args = + QStringList { + "-y", + "-v", "error", + "-stats", + "-stats_period", "0.25", + "-i", m_inputClipInfo.file.toString(), + } + << "-filter_complex" << trimFilter + cropFilter + extraFilter << "-map" << "[out]" + << m_currentFormat.encodingParameters + << loop + << m_outputClipInfo.file.toString(); + + return args; +} + +void ExportWidget::setClip(const ClipInfo &clip) +{ + if (!qFuzzyCompare(clip.duration, m_inputClipInfo.duration)) + m_trimRange = {0, clip.framesCount()}; + if (clip.dimensions != m_inputClipInfo.dimensions) + m_cropRect = {QPoint(), clip.dimensions}; + m_inputClipInfo = clip; +} + +void ExportWidget::setCropRect(const QRect &rect) +{ + m_cropRect = rect; +} + +void ExportWidget::setTrimRange(FrameRange range) +{ + m_trimRange = range; +} + +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/export.h b/src/plugins/screenrecorder/export.h new file mode 100644 index 00000000000..b6e23d9b854 --- /dev/null +++ b/src/plugins/screenrecorder/export.h @@ -0,0 +1,70 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "ffmpegutils.h" + +#include <utils/styledbar.h> + +#include <QFutureInterface> + +namespace Utils { +class Process; +} + +namespace ScreenRecorder { + +class CropSizeWarningIcon; + +class ExportWidget : public Utils::StyledBar +{ + Q_OBJECT + +public: + struct Format { + enum Kind { + AnimatedImage, + Video, + } kind; + + enum Compression { + Lossy, + Lossless, + } compression; + + QString displayName; + QString fileExtension; + QStringList encodingParameters; + + QString fileDialogFilter() const; + }; + + explicit ExportWidget(QWidget *parent = nullptr); + ~ExportWidget(); + + void setClip(const ClipInfo &clip); + void setCropRect(const QRect &rect); + void setTrimRange(FrameRange range); + +signals: + void started(); + void finished(const Utils::FilePath &clip); + +private: + void startExport(); + void interruptExport(); + QStringList ffmpegExportParameters() const; + + ClipInfo m_inputClipInfo; + ClipInfo m_outputClipInfo; + Format m_currentFormat; + Utils::Process *m_process; + QByteArray m_lastOutputChunk; + std::unique_ptr<QFutureInterface<void>> m_futureInterface; + + QRect m_cropRect; + FrameRange m_trimRange; +}; + +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/ffmpegutils.cpp b/src/plugins/screenrecorder/ffmpegutils.cpp new file mode 100644 index 00000000000..96223c3781e --- /dev/null +++ b/src/plugins/screenrecorder/ffmpegutils.cpp @@ -0,0 +1,471 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "ffmpegutils.h" + +#include "screenrecordersettings.h" +#include "screenrecordertr.h" + +#ifdef WITH_TESTS +#include "screenrecorder_test.h" +#include <QTest> +#endif // WITH_TESTS + +#include <utils/layoutbuilder.h> +#include <utils/process.h> +#include <utils/utilsicons.h> + +#include <coreplugin/messagemanager.h> + +#include <QBuffer> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QPainter> +#include <QTimer> +#include <QVersionNumber> + +using namespace Utils; + +namespace ScreenRecorder { + +int ClipInfo::framesCount() const +{ + return int(duration * rFrameRate); +} + +qreal ClipInfo::secondForFrame(int frame) const +{ + return frame / rFrameRate; +} + +QString ClipInfo::timeStamp(int frame) const +{ + const qreal seconds = secondForFrame(frame); + const QString format = QLatin1String(seconds >= 60 * 60 ? "HH:mm:ss.zzz" : "mm:ss.zzz"); + return QTime::fromMSecsSinceStartOfDay(int(seconds * 1000)).toString(format); +} + +bool ClipInfo::isNull() const +{ + return qFuzzyCompare(duration, -1); +} + +bool ClipInfo::isCompleteArea(const QRect &rect) const +{ + return rect == QRect(QPoint(), dimensions); +} + +bool ClipInfo::isCompleteRange(FrameRange range) const +{ + return (range.first == 0 && (range.second == 0 || range.second == framesCount())); +} + +bool ClipInfo::isLossless() const +{ + return codec == "qtrle" && pixFmt == "rgb24"; + // TODO: Find out how to properly determine "lossless" via ffprobe +} + +TimeLabel::TimeLabel(const ClipInfo &clipInfo, QWidget *parent) + : QLabel(parent) + , m_clipInfo(clipInfo) +{ + setFrame(0); +} + +void TimeLabel::setFrame(int frame) +{ + m_frame = frame; + const QString timeStamp = m_clipInfo.timeStamp(m_frame); + const int maxFrameDigits = qCeil(log10(double(m_clipInfo.framesCount() + 1))); + const QString label = QString("<b>%1</b> (%2)") + .arg(m_frame, maxFrameDigits, 10, QLatin1Char('0')) + .arg(timeStamp); + setText(label); +} + +int TimeLabel::frame() const +{ + return m_frame; +} + +constexpr QSize warningIconSize(16, 16); + +CropSizeWarningIcon::CropSizeWarningIcon(IconVariant backgroundType, QWidget *parent) + : QWidget(parent) + , m_iconVariant(backgroundType) +{ + setMinimumSize(warningIconSize); + setToolTip(Tr::tr("Width and height are not both divisible by 2. " + "The video export for some of the lossy formats will not work.")); + m_updateTimer = new QTimer(this); + m_updateTimer->setInterval(350); + m_updateTimer->setSingleShot(true); + m_updateTimer->callOnTimeout(this, &CropSizeWarningIcon::updateVisibility); +} + +void CropSizeWarningIcon::setCropSize(const QSize &size) +{ + m_cropSize = size; + m_updateTimer->stop(); + if (needsWarning()) + m_updateTimer->start(); + else + setVisible(false); +} + +void CropSizeWarningIcon::paintEvent(QPaintEvent*) +{ + static const QIcon standardIcon = Icons::WARNING.icon(); + static const QIcon toolBarIcon = Icons::WARNING_TOOLBAR.icon(); + QRect iconRect(QPoint(), warningIconSize); + iconRect.moveCenter(rect().center()); + QPainter p(this); + (m_iconVariant == StandardVariant ? standardIcon : toolBarIcon).paint(&p, iconRect); +} + +void CropSizeWarningIcon::updateVisibility() +{ + setVisible(needsWarning()); +} + +bool CropSizeWarningIcon::needsWarning() const +{ + return (m_cropSize.width() % 2 == 1) || (m_cropSize.height() % 2 == 1); +} + +namespace FFmpegUtils { + +static QVersionNumber parseVersionNumber(const QByteArray &toolOutput) +{ + QVersionNumber result; + const QJsonObject jsonObject = QJsonDocument::fromJson(toolOutput).object(); + if (const QJsonObject program_version = jsonObject.value("program_version").toObject(); + !program_version.isEmpty()) { + if (const QJsonValue version = program_version.value("version"); !version.isUndefined()) + result = QVersionNumber::fromString(version.toString()); + } + return result; +} + +QVersionNumber toolVersion() +{ + Process proc; + const CommandLine cl = { + Internal::settings().ffprobeTool(), + { + "-v", "quiet", + "-print_format", "json", + "-show_versions", + } + }; + proc.setCommand(cl); + proc.runBlocking(); + const QByteArray output = proc.allRawOutput(); + return parseVersionNumber(output); +} + +static ClipInfo parseClipInfo(const QByteArray &toolOutput) +{ + ClipInfo result; + const QJsonObject jsonObject = QJsonDocument::fromJson(toolOutput).object(); + if (const QJsonArray streams = jsonObject.value("streams").toArray(); !streams.isEmpty()) { + // With more than 1 video stream, the first one is often just a 1-frame thumbnail + const int streamIndex = int(qMin(streams.count() - 1, 1)); + const QJsonObject stream = streams.at(streamIndex).toObject(); + if (const QJsonValue index = stream.value("index"); !index.isUndefined()) + result.streamIdex = index.toInt(); + if (const QJsonValue width = stream.value("width"); !width.isUndefined()) + result.dimensions.setWidth(width.toInt()); + if (const QJsonValue height = stream.value("height"); !height.isUndefined()) + result.dimensions.setHeight(height.toInt()); + if (const QJsonValue rFrameRate = stream.value("r_frame_rate"); !rFrameRate.isUndefined()) { + const QStringList frNumbers = rFrameRate.toString().split('/'); + result.rFrameRate = frNumbers.count() == 2 ? frNumbers.first().toDouble() + / qMax(1, frNumbers.last().toInt()) + : frNumbers.first().toInt(); + } + if (const QJsonValue codecName = stream.value("codec_name"); !codecName.isUndefined()) + result.codec = codecName.toString(); + if (const QJsonValue pixFmt = stream.value("pix_fmt"); !pixFmt.isUndefined()) + result.pixFmt = pixFmt.toString(); + } + if (const QJsonObject format = jsonObject.value("format").toObject(); !format.isEmpty()) { + if (const QJsonValue duration = format.value("duration"); !duration.isUndefined()) + result.duration = duration.toString().toDouble(); + } + return result; +} + +ClipInfo clipInfo(const FilePath &path) +{ + Process proc; + const CommandLine cl = { + Internal::settings().ffprobeTool(), + { + "-v", "quiet", + "-print_format", "json", + "-show_format", + "-show_streams", + "-select_streams", "V", + path.toUserOutput() + } + }; + proc.setCommand(cl); + proc.runBlocking(); + const QByteArray output = proc.rawStdOut(); + ClipInfo result = parseClipInfo(output); + result.file = path; + return result; +} + +int parseFrameProgressFromOutput(const QByteArray &output) +{ + static const QRegularExpression re(R"(^frame=\s*(?<frame>\d+))"); + const QRegularExpressionMatch match = re.match(QString::fromUtf8(output)); + if (match.hasMatch()) + if (const QString frame = match.captured("frame"); !frame.isEmpty()) + return frame.toInt(); + return -1; +} + +void sendQuitCommand(Process *proc) +{ + if (proc && proc->processMode() == ProcessMode::Writer && proc->isRunning()) + proc->writeRaw("q"); +} + +void killFfmpegProcess(Process *proc) +{ + sendQuitCommand(proc); + if (proc->isRunning()) + proc->kill(); +} + +void reportError(const CommandLine &cmdLn, const QByteArray &error) +{ + if (!Internal::settings().logFfmpegCommandline()) + Core::MessageManager::writeSilently(cmdLn.toUserOutput()); + Core::MessageManager::writeDisrupting("\n" + QString::fromUtf8(error)); +} + +void logFfmpegCall(const CommandLine &cmdLn) +{ + if (Internal::settings().logFfmpegCommandline()) + Core::MessageManager::writeSilently(cmdLn.toUserOutput()); +} + +} // namespace FFmpegUtils +} // namespace ScreenRecorder + +#ifdef WITH_TESTS + +using namespace ScreenRecorder::FFmpegUtils; + +namespace ScreenRecorder::Internal { + +void FFmpegOutputParserTest::testVersionParser_data() +{ + QTest::addColumn<QByteArray>("ffprobeVersionOutput"); + QTest::addColumn<QVersionNumber>("versionNumber"); + + QTest::newRow("4.2.3") + << QByteArray( +R"_({ + "program_version": { + "version": "4.4.2-0ubuntu0.22.04.1", + "copyright": "Copyright (c) 2007-2021 the FFmpeg developers", + "compiler_ident": "gcc 11 (Ubuntu 11.2.0-19ubuntu1)", + "configuration": "--prefix=/usr --extra-version=0ubuntu0.22.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-librsvg --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared" + } +})_") + << QVersionNumber(4, 4, 2); +} + +void FFmpegOutputParserTest::testVersionParser() +{ + QFETCH(QByteArray, ffprobeVersionOutput); + QFETCH(QVersionNumber, versionNumber); + + const QVersionNumber v = parseVersionNumber(ffprobeVersionOutput); + + QCOMPARE(v, versionNumber); +} + +void FFmpegOutputParserTest::testClipInfoParser_data() +{ + QTest::addColumn<QByteArray>("ffmpegVersionOutput"); + QTest::addColumn<ClipInfo>("clipInfo"); + + // ffprobe -v quiet -print_format json -show_format -show_streams -select_streams V <video file> + QTest::newRow("10.623s, 28.33 fps, 640x480, h264, yuv444p") + << QByteArray( +R"({ + "streams": [ + { + "index": 0, + "codec_name": "h264", + "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", + "profile": "High 4:4:4 Predictive", + "codec_type": "video", + "codec_tag_string": "[0][0][0][0]", + "codec_tag": "0x0000", + "width": 640, + "height": 480, + "coded_width": 640, + "coded_height": 480, + "closed_captions": 0, + "film_grain": 0, + "has_b_frames": 2, + "pix_fmt": "yuv444p", + "level": 30, + "chroma_location": "left", + "field_order": "progressive", + "refs": 1, + "is_avc": "true", + "nal_length_size": "4", + "r_frame_rate": "85/3", + "avg_frame_rate": "85/3", + "time_base": "1/1000", + "start_pts": 0, + "start_time": "0.000000", + "bits_per_raw_sample": "8", + "extradata_size": 41, + "disposition": { + "default": 1, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0, + "captions": 0, + "descriptions": 0, + "metadata": 0, + "dependent": 0, + "still_image": 0 + }, + "tags": { + "ENCODER": "Lavc58.54.100 libx264", + "DURATION": "00:00:10.623000000" + } + } + ], + "format": { + "filename": "out.mkv", + "nb_streams": 1, + "nb_programs": 0, + "format_name": "matroska,webm", + "format_long_name": "Matroska / WebM", + "start_time": "0.000000", + "duration": "10.623000", + "size": "392136", + "bit_rate": "295310", + "probe_score": 100, + "tags": { + "ENCODER": "Lavf58.29.100" + } + } +})") + << ClipInfo{ {}, {640, 480}, "h264", 10.623, 28.33333333333, "yuv444p", 0}; +} + +void FFmpegOutputParserTest::testClipInfoParser() +{ + QFETCH(QByteArray, ffmpegVersionOutput); + QFETCH(ClipInfo, clipInfo); + + const ClipInfo ci = parseClipInfo(ffmpegVersionOutput); + + QCOMPARE(ci.duration, clipInfo.duration); + QCOMPARE(ci.rFrameRate, clipInfo.rFrameRate); + QCOMPARE(ci.dimensions, clipInfo.dimensions); + QCOMPARE(ci.codec, clipInfo.codec); + QCOMPARE(ci.pixFmt, clipInfo.pixFmt); +} + +void FFmpegOutputParserTest::testFfmpegOutputParser_data() +{ + QTest::addColumn<QByteArray>("ffmpegRecordingLogLine"); + QTest::addColumn<int>("frameProgress"); + + typedef QByteArray _; + + // ffmpeg -y -video_size vga -f x11grab -i :0.0+100,200 -vcodec qtrle /tmp/QtCreator-VMQjhs/AeOjep.mov + QTest::newRow("skip 01") + << _("ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers\n built with gcc 11 (Ubuntu 11.2.0-19ubuntu1)\n configuration: --prefix=/usr --extra-version=0ubuntu0.22.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzimg --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-librsvg --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared\n") + << -1; + QTest::newRow("skip 02") + << _(" libavutil 56. 70.100 / 56. 70.100\n libavcodec 58.134.100 / 58.134.100\n libavformat 58. 76.100 / 58. 76.100\n libavdevice 58. 13.100 / 58. 13.100\n libavfilter 7.110.100 / 7.110.100\n") + << -1; + QTest::newRow("skip 03") + << _(" libswscale 5. 9.100 / 5. 9.100\n libswresample 3. 9.100 / 3. 9.100\n libpostproc 55. 9.100 / 55. 9.100\n") + << -1; + QTest::newRow("skip 04") + << _("Input #0, x11grab, from ':0.0+100,200':\n Duration: N/A, start: 1691512344.764131, bitrate: 294617 kb/s\n") + << -1; + QTest::newRow("skip 05") + << _(" Stream #0:0: Video: rawvideo (BGR[0] / 0x524742), bgr0, 640x480, 294617 kb/s, ") + << -1; + QTest::newRow("skip 06") + << _("29.97 fps, 30 tbr, 1000k tbn, 1000k tbc\n") + << -1; + QTest::newRow("skip 07") + << _("Stream mapping:\n Stream #0:0 -> #0:0 (rawvideo (native) -> qtrle (native))\n") + << -1; + QTest::newRow("skip 08") + << _("Press [q] to stop, [?] for help\n") + << -1; + QTest::newRow("skip 09") + << _("Output #0, mov, to '/tmp/QtCreator-VMQjhs/AeOjep.mov':\n Metadata:\n encoder : Lavf58.76.100\n") + << -1; + QTest::newRow("skip 10") + << _(" Stream #0:0: Video: qtrle (rle / 0x20656C72), rgb24(pc, gbr/unknown/unknown, progressive), 640x480, q=2-31, 200 kb/s") + << -1; + QTest::newRow("skip 11") + << _(", 30 fps, ") + << -1; + QTest::newRow("skip 12") + << _("15360 tbn\n Metadata:\n encoder : Lavc58.134.100 qtrle") + << -1; + QTest::newRow("skip 13") + << _("\n") + << -1; + QTest::newRow("frame 1") + << _("frame= 1 fps=0.0 q=-0.0 size= 0kB time=00:00:00.00 bitrate=4430.8kbits/s speed=N/A \r") + << 1; + QTest::newRow("frame 21") + << _("frame= 21 fps=0.0 q=-0.0 size= 256kB time=00:00:00.66 bitrate=3145.9kbits/s speed=1.33x \r") + << 21; + QTest::newRow("frame 36") + << _("frame= 36 fps= 36 q=-0.0 size= 256kB time=00:00:01.16 bitrate=1797.7kbits/s speed=1.17x \r") + << 36; + QTest::newRow("frame 51") + << _("frame= 51 fps= 34 q=-0.0 size= 512kB time=00:00:01.66 bitrate=2516.7kbits/s speed=1.11x \r") + << 51; + QTest::newRow("skip 14") + << _("\r\n\r\n[q] command received. Exiting.\r\n\r\n") + << -1; + QTest::newRow("frame 65 - final log line") + << _("frame= 65 fps= 32 q=-0.0 Lsize= 801kB time=00:00:02.13 bitrate=3074.4kbits/s speed=1.07x \nvideo:800kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.125299%\n") + << 65; +} + +void FFmpegOutputParserTest::testFfmpegOutputParser() +{ + QFETCH(QByteArray, ffmpegRecordingLogLine); + QFETCH(int, frameProgress); + + const int parsedFrameProgress = parseFrameProgressFromOutput(ffmpegRecordingLogLine); + + QCOMPARE(parsedFrameProgress, frameProgress); +} + +} // namescace ScreenRecorder::Internal +#endif // WITH_TESTS diff --git a/src/plugins/screenrecorder/ffmpegutils.h b/src/plugins/screenrecorder/ffmpegutils.h new file mode 100644 index 00000000000..cb4cbea92c9 --- /dev/null +++ b/src/plugins/screenrecorder/ffmpegutils.h @@ -0,0 +1,94 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <utils/filepath.h> + +#include <QLabel> +#include <QSize> +#include <QVersionNumber> + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +namespace Utils { +class CommandLine; +class FilePath; +class Process; +} + +namespace ScreenRecorder { + +using FrameRange = std::pair<int, int>; + +struct ClipInfo { + Utils::FilePath file; + + // ffmpeg terminology + QSize dimensions; + QString codec; + qreal duration = -1; // seconds + qreal rFrameRate = -1; // frames per second + QString pixFmt; + int streamIdex = -1; + + int framesCount() const; + qreal secondForFrame(int frame) const; + QString timeStamp(int frame) const; + bool isNull() const; + bool isCompleteArea(const QRect &rect) const; + bool isCompleteRange(FrameRange range) const; + bool isLossless() const; +}; + +class TimeLabel : public QLabel +{ +public: + explicit TimeLabel(const ClipInfo &clipInfo, QWidget *parent = nullptr); + + void setFrame(int frame); + int frame() const; + +private: + const ClipInfo &m_clipInfo; + int m_frame = -1; +}; + +class CropSizeWarningIcon : public QWidget +{ +public: + enum IconVariant { + StandardVariant, + ToolBarVariant, + }; + + explicit CropSizeWarningIcon(IconVariant backgroundType, QWidget *parent = nullptr); + + void setCropSize(const QSize &size); + +protected: + void paintEvent(QPaintEvent*) override; + +private: + void updateVisibility(); + bool needsWarning() const; + + QSize m_cropSize; + const IconVariant m_iconVariant; + QTimer *m_updateTimer; +}; + +namespace FFmpegUtils { + +QVersionNumber toolVersion(); +ClipInfo clipInfo(const Utils::FilePath &path); +int parseFrameProgressFromOutput(const QByteArray &output); +void sendQuitCommand(Utils::Process *proc); +void killFfmpegProcess(Utils::Process *proc); +void logFfmpegCall(const Utils::CommandLine &cmdLn); +void reportError(const Utils::CommandLine &cmdLn, const QByteArray &error); + +} // namespace FFmpegUtils +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/record.cpp b/src/plugins/screenrecorder/record.cpp new file mode 100644 index 00000000000..12aa27f46da --- /dev/null +++ b/src/plugins/screenrecorder/record.cpp @@ -0,0 +1,424 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "record.h" + +#include "cropandtrim.h" +#include "ffmpegutils.h" +#include "screenrecordersettings.h" +#include "screenrecordertr.h" + +#include <utils/environment.h> +#include <utils/fileutils.h> +#include <utils/layoutbuilder.h> +#include <utils/process.h> +#include <utils/qtcsettings.h> +#include <utils/styledbar.h> +#include <utils/utilsicons.h> + +#include <coreplugin/icore.h> + +#include <QAction> +#include <QDialogButtonBox> +#include <QGuiApplication> +#include <QLoggingCategory> +#include <QMessageBox> +#include <QScreen> +#include <QTimer> +#include <QToolButton> + +using namespace Utils; + +namespace ScreenRecorder { + +using namespace Internal; + +struct RecordPreset { + const QString fileExtension; + const QStringList encodingParameters; +}; + +static const RecordPreset &recordPreset() +{ + static const RecordPreset preset = { + ".mkv", + { + "-vcodec", "libx264rgb", + "-crf", "0", + "-preset", "ultrafast", + "-tune", "zerolatency", + "-reserve_index_space", "1M", + } + }; + // Valid alternatives: + // ".mov", { "-vcodec", "qtrle" } // Slower encoding, faster seeking + return preset; +} + +class RecordOptionsDialog : public QDialog +{ +public: + explicit RecordOptionsDialog(QWidget *parent = nullptr); + +private: + QRect screenCropRect() const; + void updateCropScene(); + void updateWidgets(); + + static const int m_factor = 4; + CropScene *m_cropScene; + QImage m_thumbnail; + SelectionAspect m_screenId; + IntegerAspect m_recordFrameRate; + QLabel *m_cropRectLabel; + QToolButton *m_resetButton; +}; + +static QString optionNameForScreen(const QScreen *screen) +{ + const QSize pixelSize = screen->size() * screen->devicePixelRatio(); + QStringList nameElements = { screen->name(), screen->manufacturer(), screen->model() }; + nameElements.removeDuplicates(); // Model and name might be the same on some system + const QString displayName = QLatin1String("%1 - %2x%3").arg(nameElements.join(" ")) + .arg(pixelSize.width()).arg(pixelSize.height()); + return displayName; +} + +RecordOptionsDialog::RecordOptionsDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(Tr::tr("Screen Recording Options")); + + m_screenId.setLabelText(Tr::tr("Display:")); + m_screenId.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + for (const QScreen *screen : QGuiApplication::screens()) + m_screenId.addOption(optionNameForScreen(screen)); + + m_cropScene = new CropScene; + + m_recordFrameRate.setLabelText(Tr::tr("FPS:")); + + m_resetButton = new QToolButton; + m_resetButton->setIcon(Icons::RESET.icon()); + + m_cropRectLabel = new QLabel; + m_cropRectLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + using namespace Layouting; + Column { + Row { m_screenId, st }, + Group { + title(Tr::tr("Recorded screen area:")), + Column { + m_cropScene, + Row { st, m_cropRectLabel, m_resetButton }, + } + }, + Row { m_recordFrameRate, st }, + st, + buttonBox, + }.attachTo(this); + layout()->setSizeConstraint(QLayout::SetFixedSize); + + connect(buttonBox, &QDialogButtonBox::accepted, this, [this] { + const QRect cropRect = m_cropScene->fullySelected() ? QRect() : screenCropRect(); + settings().applyRecordSettings({int(m_screenId()), cropRect, int(m_recordFrameRate())}); + QDialog::accept(); + }); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(&m_screenId, &IntegerAspect::changed, this, [this] { + updateCropScene(); + m_cropScene->setFullySelected(); + }); + connect(m_resetButton, &QToolButton::pressed, this, [this](){ + m_cropScene->setFullySelected(); + }); + connect(m_cropScene, &CropScene::cropRectChanged, this, &RecordOptionsDialog::updateWidgets); + + updateCropScene(); + + const ScreenRecorderSettings::RecordSettings rs = settings().recordSettings(); + m_screenId.setValue(rs.screenId); + if (!rs.cropRect.isNull()) { + m_cropScene->setCropRect({rs.cropRect.x() / m_factor, + rs.cropRect.y() / m_factor, + rs.cropRect.width() / m_factor, + rs.cropRect.height() / m_factor}); + } else { + m_cropScene->setFullySelected(); + } + m_recordFrameRate.setValue(rs.frameRate); +} + +QRect RecordOptionsDialog::screenCropRect() const +{ + const QRect r = m_cropScene->cropRect(); + return {r.x() * m_factor, r.y() * m_factor, r.width() * m_factor, r.height() * m_factor}; +} + +void RecordOptionsDialog::updateCropScene() +{ + const ScreenRecorderSettings::RecordSettings rs = ScreenRecorderSettings + ::sanitizedRecordSettings({int(m_screenId()), screenCropRect(), int(m_recordFrameRate())}); + const QList<QScreen*> screens = QGuiApplication::screens(); + m_thumbnail = QGuiApplication::screens().at(rs.screenId)->grabWindow().toImage(); + const qreal dpr = m_thumbnail.devicePixelRatio(); + m_thumbnail = m_thumbnail.scaled((m_thumbnail.deviceIndependentSize() / m_factor * dpr) + .toSize(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + m_thumbnail.setDevicePixelRatio(dpr); + m_cropScene->setImage(m_thumbnail); + const static int lw = CropScene::lineWidth; + m_cropScene->setFixedSize(m_thumbnail.deviceIndependentSize().toSize() + .grownBy({lw, lw, lw, lw})); + QTimer::singleShot(250, this, [this] { + updateCropScene(); + }); + updateWidgets(); +} + +void RecordOptionsDialog::updateWidgets() +{ + const QRect r = screenCropRect(); + m_cropRectLabel->setText(QString("x:%1 y:%2 w:%3 h:%4") + .arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height())); + m_resetButton->setEnabled(!m_cropScene->fullySelected()); +} + +RecordWidget::RecordWidget(const FilePath &recordFile, QWidget *parent) + : StyledBar(parent) + , m_recordFile(recordFile) +{ + setMinimumWidth(220); + + m_process = new Process(this); + m_process->setUseCtrlCStub(true); + m_process->setProcessMode(ProcessMode::Writer); + + auto settingsButton = new QToolButton; + settingsButton->setIcon(Icons::SETTINGS_TOOLBAR.icon()); + + auto recordButton = new QToolButton; + recordButton->setIcon(Icon({{":/utils/images/filledcircle.png", + Theme::IconsStopToolBarColor}}).icon()); + + auto stopButton = new QToolButton; + stopButton->setIcon(Icon({{":/utils/images/stop_small.png", + Theme::IconsStopToolBarColor}}).icon()); + stopButton->setEnabled(false); + + auto progressLabel = new TimeLabel(m_clipInfo); + progressLabel->setEnabled(false); + progressLabel->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + + m_openClipAction = new QAction(Tr::tr("Open Mov/qtrle rgb24 File"), this); + addAction(m_openClipAction); + setContextMenuPolicy(Qt::ActionsContextMenu); + + using namespace Layouting; + Row { + settingsButton, + recordButton, + stopButton, + st, + progressLabel, + Space(6), + noMargin(), spacing(0), + }.attachTo(this); + + connect(settingsButton, &QToolButton::clicked, this, [this] { + m_optionsDialog = new RecordOptionsDialog(this); + m_optionsDialog->setWindowModality(Qt::WindowModal); + m_optionsDialog->setAttribute(Qt::WA_DeleteOnClose); + m_optionsDialog->show(); + }); + connect(recordButton, &QToolButton::clicked, this, [this, progressLabel] { + m_clipInfo.duration = 0; + progressLabel->setFrame(0); + m_clipInfo = {}; + m_clipInfo.file = m_recordFile; + m_clipInfo.rFrameRate = qreal(Internal::settings().recordFrameRate()); + const CommandLine cl(Internal::settings().ffmpegTool(), ffmpegParameters(m_clipInfo)); + m_process->setCommand(cl); + m_process->setWorkingDirectory(Internal::settings().ffmpegTool().parentDir()); + FFmpegUtils::logFfmpegCall(cl); + m_process->start(); + }); + connect(stopButton, &QToolButton::clicked, this, [this] { + FFmpegUtils::sendQuitCommand(m_process); + }); + connect(m_process, &Process::started, this, [=] { + progressLabel->setEnabled(true); + recordButton->setEnabled(false); + stopButton->setEnabled(true); + settingsButton->setEnabled(false); + m_openClipAction->setEnabled(false); + emit started(); + }); + connect(m_process, &Process::done, this, [=] { + recordButton->setEnabled(true); + stopButton->setEnabled(false); + settingsButton->setEnabled(true); + m_openClipAction->setEnabled(true); + if (m_process->exitCode() == 0) + emit finished(FFmpegUtils::clipInfo(m_clipInfo.file)); + else + FFmpegUtils::reportError(m_process->commandLine(), m_lastOutputChunk); + }); + connect(m_process, &Process::readyReadStandardError, this, [this, progressLabel] { + m_lastOutputChunk = m_process->readAllRawStandardError(); + const int frameProgress = FFmpegUtils::parseFrameProgressFromOutput(m_lastOutputChunk); + if (frameProgress > 0) { + m_clipInfo.duration = m_clipInfo.secondForFrame(frameProgress); + progressLabel->setFrame(m_clipInfo.framesCount()); + } + }); + connect(m_openClipAction, &QAction::triggered, this, [this, progressLabel] { + const FilePath lastDir = Internal::settings().lastOpenDirectory(); + const FilePath file = FileUtils::getOpenFilePath(Core::ICore::dialogParent(), + m_openClipAction->text(), lastDir, + "Mov/qtrle rgb24 (*.mov)"); + if (!file.isEmpty()) { + Internal::settings().lastOpenDirectory.setValue(file.parentDir()); + Internal::settings().lastOpenDirectory.apply(); + Internal::settings().lastOpenDirectory.writeToSettingsImmediatly(); + const ClipInfo clip = FFmpegUtils::clipInfo(file); + if (clip.isNull()) { + QMessageBox::critical(Core::ICore::dialogParent(), + Tr::tr("Cannot Open Clip"), + Tr::tr("FFmpeg cannot open %1.").arg(file.toUserOutput())); + } else if (!clip.isLossless()) { + QMessageBox::critical(Core::ICore::dialogParent(), + Tr::tr("Clip Not Supported"), + Tr::tr("Choose a clip with the \"qtrle\" codec and " + "pixel format \"rgb24\".")); + } else { + m_clipInfo.duration = 0; + progressLabel->setFrame(0); + progressLabel->setEnabled(false); + emit finished(clip); + } + } + }); +} + +RecordWidget::~RecordWidget() +{ + FFmpegUtils::killFfmpegProcess(m_process); +} + +QString RecordWidget::recordFileExtension() +{ + return recordPreset().fileExtension; +} + +static QString sizeStr(const QSize &size) +{ + return QString("%1x%2").arg(size.width()).arg(size.height()); +} + +static QRect cropRectForContinuousMulitScreen( + const Internal::ScreenRecorderSettings::RecordSettings &rS) +{ + const QScreen *screen = QGuiApplication::screens()[rS.screenId]; + const QPoint screenTopLeft = screen->geometry().topLeft(); + const QPoint cropTopLeft = rS.cropRect.translated(screenTopLeft).topLeft(); + const QSize cropSize = rS.cropRect.isNull() + ? screen->size() * screen->devicePixelRatio() + : rS.cropRect.size(); + const QRect cropRect(cropTopLeft, cropSize); + return cropRect; +} + +QStringList RecordWidget::ffmpegParameters(const ClipInfo &clipInfo) const +{ + const Internal::ScreenRecorderSettings::RecordSettings rS = + Internal::settings().recordSettings(); + const QString frameRateStr = QString::number(rS.frameRate); + const QString screenIdStr = QString::number(rS.screenId); + const QString captureCursorStr = Internal::settings().captureCursor() ? "1" : "0"; + QStringList videoGrabParams; + + switch (Internal::settings().volatileScreenCaptureType()) { + case Internal::CaptureType::X11grab: { + const QRect cropRect = cropRectForContinuousMulitScreen(rS); + const QString videoSize = sizeStr(cropRect.size()); + const QString x11display = qtcEnvironmentVariable("DISPLAY", ":0.0"); + videoGrabParams.append({"-f", "x11grab"}); + videoGrabParams.append({"-draw_mouse", captureCursorStr}); + videoGrabParams.append({"-framerate", frameRateStr}); + videoGrabParams.append({"-video_size", videoSize}); + videoGrabParams.append({"-i", QString("%1+%2,%3").arg(x11display) + .arg(cropRect.x()).arg(cropRect.y())}); + break; + } + case Internal::CaptureType::Ddagrab: { + QString filter = "ddagrab=output_idx=" + screenIdStr; + if (!rS.cropRect.isNull()) { + filter.append(":video_size=" + sizeStr(rS.cropRect.size())); + filter.append(QString(":offset_x=%1:offset_y=%2").arg(rS.cropRect.x()) + .arg(rS.cropRect.y())); + } + filter.append(":framerate=" + frameRateStr); + filter.append(":draw_mouse=" + captureCursorStr); + filter.append(",hwdownload"); + filter.append(",format=bgra"); + videoGrabParams = { + "-ss", "00:00.25", // Skip few first frames which are black + "-filter_complex", filter, + }; + break; + } + case Internal::CaptureType::Gdigrab: { + const QRect cropRect = cropRectForContinuousMulitScreen(rS); + const QString videoSize = sizeStr(cropRect.size()); + videoGrabParams.append({"-f", "gdigrab"}); + videoGrabParams.append({"-draw_mouse", captureCursorStr}); + videoGrabParams.append({"-framerate", frameRateStr}); + videoGrabParams.append({"-video_size", videoSize}); + videoGrabParams.append({"-offset_x", QString::number(cropRect.x())}); + videoGrabParams.append({"-offset_y", QString::number(cropRect.y())}); + videoGrabParams.append({"-i", "desktop"}); + break; + } + case Internal::CaptureType::AVFoundation: { + videoGrabParams = { + "-f", "avfoundation", + "-capture_cursor", captureCursorStr, + "-capture_mouse_clicks", Internal::settings().captureMouseClicks() ? "1" : "0", + "-framerate", frameRateStr, + "-pixel_format", "bgr0", + "-i", QString("Capture screen %1:none").arg(rS.screenId), + }; + if (!rS.cropRect.isNull()) { + videoGrabParams.append({ + "-filter_complex", QString("crop=x=%1:y=%2:w=%3:h=%4") + .arg(rS.cropRect.x()).arg(rS.cropRect.y()) + .arg(rS.cropRect.width()).arg(rS.cropRect.height()) + }); + } + break; + } + default: + break; + } + + QStringList args = { + "-y", + "-v", "error", + "-stats", + }; + args.append(videoGrabParams); + if (Internal::settings().enableFileSizeLimit()) + args.append({"-fs", QString::number(Internal::settings().fileSizeLimit()) + "M"}); + if (Internal::settings().enableRtBuffer()) + args.append({"-rtbufsize", QString::number(Internal::settings().rtBufferSize()) + "M"}); + args.append(recordPreset().encodingParameters); + args.append(clipInfo.file.toString()); + + return args; +} + +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/record.h b/src/plugins/screenrecorder/record.h new file mode 100644 index 00000000000..4c6ac960311 --- /dev/null +++ b/src/plugins/screenrecorder/record.h @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "ffmpegutils.h" + +#include <utils/styledbar.h> + +namespace Utils { +class Process; +} + +namespace ScreenRecorder { + +class RecordOptionsDialog; + +class RecordWidget : public Utils::StyledBar +{ + Q_OBJECT + +public: + explicit RecordWidget(const Utils::FilePath &recordFile, QWidget *parent = nullptr); + ~RecordWidget(); + + static QString recordFileExtension(); + +signals: + void started(); + void finished(const ClipInfo &clip); + +private: + QStringList ffmpegParameters(const ClipInfo &clipInfo) const; + + const Utils::FilePath m_recordFile; + ClipInfo m_clipInfo; + Utils::Process *m_process; + QByteArray m_lastOutputChunk; + RecordOptionsDialog *m_optionsDialog; + QAction *m_openClipAction; +}; + +} // namespace ScreenRecorder diff --git a/src/plugins/screenrecorder/screenrecorder.qbs b/src/plugins/screenrecorder/screenrecorder.qbs new file mode 100644 index 00000000000..3d361c6b672 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorder.qbs @@ -0,0 +1,31 @@ +import qbs 1.0 + +QtcPlugin { + name: "ScreenRecorder" + + Depends { name: "Core" } + Depends { name: "Spinner" } + + files: [ + "cropandtrim.cpp", + "cropandtrim.h", + "export.cpp", + "export.h", + "ffmpegutils.cpp", + "ffmpegutils.h", + "record.cpp", + "record.h", + "screenrecorder.qrc", + "screenrecorderconstants.h", + "screenrecorderplugin.cpp", + "screenrecordersettings.cpp", + "screenrecordersettings.h", + ] + + QtcTestFiles { + files: [ + "screenrecorder_test.h", + "screenrecorder_test.cpp", + ] + } +} diff --git a/src/plugins/screenrecorder/screenrecorder.qrc b/src/plugins/screenrecorder/screenrecorder.qrc new file mode 100644 index 00000000000..8536e94067a --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorder.qrc @@ -0,0 +1,4 @@ +<RCC> + <qresource prefix="/screenrecorder"> + </qresource> +</RCC> diff --git a/src/plugins/screenrecorder/screenrecorder_test.cpp b/src/plugins/screenrecorder/screenrecorder_test.cpp new file mode 100644 index 00000000000..9911822c3a5 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorder_test.cpp @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "screenrecorder_test.h" + +#include <QTest> + +namespace ScreenRecorder::Internal { + +FFmpegOutputParserTest::FFmpegOutputParserTest(QObject *parent) + : QObject(parent) +{ } + +FFmpegOutputParserTest::~FFmpegOutputParserTest() = default; + +} // namespace ScreenRecorder::Internal diff --git a/src/plugins/screenrecorder/screenrecorder_test.h b/src/plugins/screenrecorder/screenrecorder_test.h new file mode 100644 index 00000000000..baf10f0c512 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorder_test.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <coreplugin/dialogs/ioptionspage.h> + +#include <utils/aspects.h> + +namespace ScreenRecorder::Internal { + +class FFmpegOutputParserTest : public QObject +{ + Q_OBJECT + +public: + FFmpegOutputParserTest(QObject *parent = nullptr); + ~FFmpegOutputParserTest(); + +private slots: + void testVersionParser_data(); + void testVersionParser(); + void testClipInfoParser_data(); + void testClipInfoParser(); + void testFfmpegOutputParser_data(); + void testFfmpegOutputParser(); +}; + +} // namespace ScreenRecorder::Internal diff --git a/src/plugins/screenrecorder/screenrecorderconstants.h b/src/plugins/screenrecorder/screenrecorderconstants.h new file mode 100644 index 00000000000..7b076f1cfdf --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorderconstants.h @@ -0,0 +1,15 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +namespace ScreenRecorder::Constants { + +const char TOOLSSETTINGSPAGE_ID[] = "Z.ScreenRecorder"; +const char LOGGING_CATEGORY[] = "qtc.screenrecorder"; +const char ACTION_ID[] = "ScreenRecorder.Action"; +const char FFMPEG_COMMAND[] = "ffmpeg"; +const char FFPROBE_COMMAND[] = "ffprobe"; +const char FFMPEG_DOWNLOAD_URL[] = "https://ffmpeg.org/download.html"; + +} // namespace ScreenRecorder::Constants diff --git a/src/plugins/screenrecorder/screenrecorderplugin.cpp b/src/plugins/screenrecorder/screenrecorderplugin.cpp new file mode 100644 index 00000000000..e72ebf945e9 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecorderplugin.cpp @@ -0,0 +1,154 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "screenrecorderconstants.h" + +#include "cropandtrim.h" +#include "export.h" +#include "record.h" +#include "screenrecorderconstants.h" +#include "screenrecordersettings.h" +#include "screenrecordertr.h" + +#ifdef WITH_TESTS +#include "screenrecorder_test.h" +#endif // WITH_TESTS + +#include <extensionsystem/iplugin.h> + +#include <utils/layoutbuilder.h> +#include <utils/styledbar.h> +#include <utils/stylehelper.h> +#include <utils/temporaryfile.h> +#include <utils/utilsicons.h> + +#include <solutions/spinner/spinner.h> + +#include <coreplugin/actionmanager/actioncontainer.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/command.h> +#include <coreplugin/icore.h> + +#include <QAction> +#include <QDialog> +#include <QPushButton> + +using namespace Utils; +using namespace Core; + +namespace ScreenRecorder::Internal { + +class ScreenRecorderDialog : public QDialog +{ +public: + ScreenRecorderDialog(QWidget *parent = nullptr) + : QDialog(parent) + , m_recordFile("XXXXXX" + RecordWidget::recordFileExtension()) + { + setWindowTitle(Tr::tr("Record Screen")); + StyleHelper::setPanelWidget(this); + + m_recordFile.open(); + m_recordWidget = new RecordWidget(FilePath::fromString(m_recordFile.fileName())); + + m_cropAndTrimStatusWidget = new CropAndTrimWidget; + + m_exportWidget = new ExportWidget; + + using namespace Layouting; + Column { + m_recordWidget, + Row { m_cropAndTrimStatusWidget, m_exportWidget }, + noMargin(), spacing(0), + }.attachTo(this); + + auto setLowerRowEndabled = [this] (bool enabled) { + m_cropAndTrimStatusWidget->setEnabled(enabled); + m_exportWidget->setEnabled(enabled); + }; + setLowerRowEndabled(false); + connect(m_recordWidget, &RecordWidget::started, + this, [setLowerRowEndabled] { setLowerRowEndabled(false); }); + connect(m_recordWidget, &RecordWidget::finished, + this, [this, setLowerRowEndabled] (const ClipInfo &clip) { + m_cropAndTrimStatusWidget->setClip(clip); + m_exportWidget->setClip(clip); + setLowerRowEndabled(true); + }); + connect(m_cropAndTrimStatusWidget, &CropAndTrimWidget::cropRectChanged, + m_exportWidget, &ExportWidget::setCropRect); + connect(m_cropAndTrimStatusWidget, &CropAndTrimWidget::trimRangeChanged, + m_exportWidget, &ExportWidget::setTrimRange); + connect(m_exportWidget, &ExportWidget::started, this, [this] { + setEnabled(false); + m_spinner->show(); + }); + connect(m_exportWidget, &ExportWidget::finished, this, [this] { + setEnabled(true); + m_spinner->hide(); + }); + + m_spinner = new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Medium, this); + m_spinner->hide(); + + layout()->setSizeConstraint(QLayout::SetFixedSize); + } + + static void showDialog() + { + static QPointer<QDialog> staticInstance; + + if (staticInstance.isNull()) { + staticInstance = new ScreenRecorderDialog(Core::ICore::dialogParent()); + staticInstance->setAttribute(Qt::WA_DeleteOnClose); + } + staticInstance->show(); + staticInstance->raise(); + staticInstance->activateWindow(); + } + +private: + RecordWidget *m_recordWidget; + TemporaryFile m_recordFile; + CropAndTrimWidget *m_cropAndTrimStatusWidget; + ExportWidget *m_exportWidget; + SpinnerSolution::Spinner *m_spinner; +}; + +class ScreenRecorderPlugin final : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ScreenRecorder.json") + +public: + void initialize() final + { + const QIcon menuIcon = Icon({{":/utils/images/filledcircle.png", Theme::IconsStopColor}}, + Icon::MenuTintedStyle).icon(); + auto action = new QAction(menuIcon, Tr::tr("Record Screen..."), this); + Command *cmd = ActionManager::registerAction(action, Constants::ACTION_ID, + Context(Core::Constants::C_GLOBAL)); + connect(action, &QAction::triggered, this, &ScreenRecorderPlugin::showDialogOrSettings); + ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); + mtools->addAction(cmd); + +#ifdef WITH_TESTS + addTest<FFmpegOutputParserTest>(); +#endif + } + +private: + void showDialogOrSettings() + { + if (!Internal::settings().toolsRegistered() && + !Core::ICore::showOptionsDialog(Constants::TOOLSSETTINGSPAGE_ID)) { + return; + } + + ScreenRecorderDialog::showDialog(); + } +}; + +} // namespace ScreenRecorder::Internal + +#include "screenrecorderplugin.moc" diff --git a/src/plugins/screenrecorder/screenrecordersettings.cpp b/src/plugins/screenrecorder/screenrecordersettings.cpp new file mode 100644 index 00000000000..cc00f6da065 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecordersettings.cpp @@ -0,0 +1,272 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "screenrecordersettings.h" + +#include "screenrecorderconstants.h" +#include "screenrecordertr.h" + +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/icore.h> + +#include <help/helpconstants.h> + +#include <utils/fileutils.h> +#include <utils/environment.h> +#include <utils/layoutbuilder.h> +#include <utils/utilsicons.h> + +#include <QDesktopServices> +#include <QGuiApplication> +#include <QLabel> +#include <QScreen> + +using namespace Utils; + +namespace ScreenRecorder::Internal { + +ScreenRecorderSettings &settings() +{ + static ScreenRecorderSettings theSettings; + return theSettings; +} + +static QRect stringListToRect(const QStringList &stringList) +{ + return stringList.count() == 4 ? QRect(stringList[0].toInt(), stringList[1].toInt(), + stringList[2].toInt(), stringList[3].toInt()) + : QRect(); +} + +static QStringList rectToStringList(const QRect &rect) +{ + return {QString::number(rect.x()), QString::number(rect.y()), + QString::number(rect.width()), QString::number(rect.height())}; +} + +ScreenRecorderSettings::ScreenRecorderSettings() +{ + setSettingsGroup("ScreenRecorder"); + setAutoApply(false); + + const QStringList versionArgs{"-version"}; + + ffmpegTool.setSettingsKey("FFmpegTool"); + ffmpegTool.setExpectedKind(PathChooser::ExistingCommand); + ffmpegTool.setCommandVersionArguments(versionArgs); + const FilePath ffmpegDefault + = Environment::systemEnvironment().searchInPath(Constants::FFMPEG_COMMAND, + FileUtils::usefulExtraSearchPaths()); + ffmpegTool.setDefaultValue(ffmpegDefault.toUserOutput()); + ffmpegTool.setLabelText(Tr::tr("ffmpeg tool:")); + + ffprobeTool.setSettingsKey("FFprobeTool"); + ffprobeTool.setExpectedKind(PathChooser::ExistingCommand); + ffprobeTool.setCommandVersionArguments(versionArgs); + const FilePath ffprobeDefault + = Environment::systemEnvironment().searchInPath(Constants::FFPROBE_COMMAND, + FileUtils::usefulExtraSearchPaths()); + ffprobeTool.setDefaultValue(ffprobeDefault.toUserOutput()); + ffprobeTool.setLabelText(Tr::tr("ffprobe tool:")); + + captureCursor.setSettingsKey("CaptureCursor"); + captureCursor.setDefaultValue(true); + captureCursor.setLabel(Tr::tr("Capture the mouse cursor")); + captureCursor.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + captureMouseClicks.setSettingsKey("CaptureMouseClicks"); + captureMouseClicks.setDefaultValue(false); + captureMouseClicks.setLabel(Tr::tr("Capture the screen mouse clicks")); + captureMouseClicks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + screenCaptureType.setSettingsKey("ScreenCaptureType"); + screenCaptureType.setLabelText(Tr::tr("Capture device/filter:")); + screenCaptureType.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + screenCaptureType.setDefaultValue(0); + screenCaptureType.setVisible(false); + switch (HostOsInfo::hostOs()) { + case OsTypeLinux: + screenCaptureType.addOption({"x11grab", {}, CaptureType::X11grab}); + break; + case OsTypeWindows: + screenCaptureType.addOption({"ddagrab", {}, CaptureType::Ddagrab}); + screenCaptureType.addOption({"gdigrab", {}, CaptureType::Gdigrab}); + screenCaptureType.setVisible(true); + break; + case OsTypeMac: + screenCaptureType.addOption({"AVFoundation", {}, CaptureType::AVFoundation}); + break; + default: + break; + } + auto setCaptureMouseClicksVisible = [this] { + const bool visible = volatileScreenCaptureType() == CaptureType::AVFoundation; + captureMouseClicks.setVisible(visible); + }; + + enableFileSizeLimit.setSettingsKey("EnableFileSizeLimit"); + enableFileSizeLimit.setDefaultValue(true); + enableFileSizeLimit.setLabel(Tr::tr("Size limit for intermediate output file")); + enableFileSizeLimit.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + fileSizeLimit.setSettingsKey("FileSizeLimit"); + fileSizeLimit.setDefaultValue(1024); + fileSizeLimit.setRange(100, 1024 * 1024 * 2); // Up to 2GB + fileSizeLimit.setSuffix("MB"); + fileSizeLimit.setEnabler(&enableFileSizeLimit); + + enableRtBuffer.setSettingsKey("EnableRealTimeBuffer"); + enableRtBuffer.setDefaultValue(true); + enableRtBuffer.setLabel(Tr::tr("RAM buffer for real-time frames")); + enableRtBuffer.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + rtBufferSize.setSettingsKey("RealTimeBufferSize"); + rtBufferSize.setDefaultValue(1024); + rtBufferSize.setRange(100, 1024 * 1024 * 2); // Up to 2GB + rtBufferSize.setSuffix("MB"); + rtBufferSize.setEnabler(&enableRtBuffer); + + logFfmpegCommandline.setSettingsKey("LogFFMpegCommandLine"); + logFfmpegCommandline.setDefaultValue(false); + logFfmpegCommandline.setLabel(Tr::tr("Write command line of FFmpeg calls to General Messages")); + logFfmpegCommandline.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + animatedImagesAsEndlessLoop.setSettingsKey("AnimatedImagesAsEndlessLoop"); + animatedImagesAsEndlessLoop.setDefaultValue(true); + animatedImagesAsEndlessLoop.setLabel(Tr::tr("Export animated images as infinite loop")); + animatedImagesAsEndlessLoop.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); + + lastOpenDirectory.setSettingsKey("LastOpenDir"); + lastOpenDirectory.setExpectedKind(PathChooser::ExistingDirectory); + lastOpenDirectory.setDefaultValue(FileUtils::homePath().toString()); + + exportLastDirectory.setSettingsKey("ExportLastDir"); + exportLastDirectory.setExpectedKind(PathChooser::ExistingDirectory); + exportLastDirectory.setDefaultValue(FileUtils::homePath().toString()); + + exportLastFormat.setSettingsKey("ExportLastFormat"); + exportLastFormat.setDefaultValue("WebP"); + + lastSaveImageDirectory.setSettingsKey("LastSaveImageDir"); + lastSaveImageDirectory.setExpectedKind(PathChooser::ExistingDirectory); + lastSaveImageDirectory.setDefaultValue(FileUtils::homePath().toString()); + + recordFrameRate.setSettingsKey("RecordFrameRate"); + recordFrameRate.setDefaultValue(24); + recordFrameRate.setLabelText(Tr::tr("Recording frame rate:")); + recordFrameRate.setRange(1, 60); + recordFrameRate.setSuffix(" fps"); + + recordScreenId.setSettingsKey("RecordScreenID"); + recordScreenId.setDefaultValue(0); + recordScreenId.setLabelText(Tr::tr("Screen ID:")); + + recordScreenCropRect.setSettingsKey("RecordScreenCropRect"); + recordScreenCropRect.setDefaultValue(rectToStringList({})); + + setLayouter([this] { + using namespace Layouting; + auto websiteLabel = new QLabel; + websiteLabel->setText(QString("<a href=\"%1\">%1</a>").arg(Constants::FFMPEG_DOWNLOAD_URL)); + websiteLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + websiteLabel->setOpenExternalLinks(true); + + // clang-format off + using namespace Layouting; + return Column { + Group { + title(Tr::tr("FFmpeg Installation")), + Form { + ffmpegTool, br, + ffprobeTool, br, + websiteLabel, br, + }, + }, + Group { + title(Tr::tr("Record Settings")), + Column { + captureCursor, + captureMouseClicks, + Row { screenCaptureType, st }, + Row { enableFileSizeLimit, fileSizeLimit, st }, + Row { enableRtBuffer, rtBufferSize, st }, + }, + }, + Group { + title(Tr::tr("Export Settings")), + Column { + animatedImagesAsEndlessLoop, + }, + }, + logFfmpegCommandline, + st, + }; + // clang-format on + }); + + readSettings(); + + setCaptureMouseClicksVisible(); + connect(&screenCaptureType, &SelectionAspect::volatileValueChanged, this, + setCaptureMouseClicksVisible); +} + +bool ScreenRecorderSettings::toolsRegistered() const +{ + return ffmpegTool().isExecutableFile() && ffprobeTool().isExecutableFile(); +} + +ScreenRecorderSettings::RecordSettings ScreenRecorderSettings::sanitizedRecordSettings(const RecordSettings &settings) +{ + const int screenIdFromSettings = settings.screenId; + const QList<QScreen*> screens = QGuiApplication::screens(); + const int effectiveScreenId = qMin(screenIdFromSettings, screens.size() - 1); + const QScreen *screen = screens.at(effectiveScreenId); + const QSize screenSize = screen->size() * screen->devicePixelRatio(); + const QRect screenRect(QPoint(), screenSize); + const QRect cropRectFromSettings = settings.cropRect; + const QRect effectiveCropRect = screenIdFromSettings == effectiveScreenId + ? screenRect.intersected(cropRectFromSettings) : QRect(); + return {effectiveScreenId, effectiveCropRect, settings.frameRate}; +} + +ScreenRecorderSettings::RecordSettings ScreenRecorderSettings::recordSettings() const +{ + return sanitizedRecordSettings({int(recordScreenId()), stringListToRect(recordScreenCropRect()), + int(recordFrameRate())}); +} + +void ScreenRecorderSettings::applyRecordSettings(const RecordSettings &settings) +{ + recordScreenId.setValue(settings.screenId); + recordScreenId.apply(); + recordScreenId.writeToSettingsImmediatly(); + recordScreenCropRect.setValue(rectToStringList(settings.cropRect)); + recordScreenCropRect.apply(); + recordScreenCropRect.writeToSettingsImmediatly(); + recordFrameRate.setValue(settings.frameRate); + recordFrameRate.apply(); + recordFrameRate.writeToSettingsImmediatly(); +} + +CaptureType ScreenRecorderSettings::volatileScreenCaptureType() const +{ + const QVariant value = screenCaptureType.itemValueForIndex(screenCaptureType.volatileValue()); + return value.value<CaptureType>(); +} + +class ScreenRecorderSettingsPage : public Core::IOptionsPage +{ +public: + ScreenRecorderSettingsPage() + { + setId(Constants::TOOLSSETTINGSPAGE_ID); + setDisplayName(Tr::tr("Screen Recording")); + setCategory(Help::Constants::HELP_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +static const ScreenRecorderSettingsPage settingsPage; + +} // ImageViewer::Internal diff --git a/src/plugins/screenrecorder/screenrecordersettings.h b/src/plugins/screenrecorder/screenrecordersettings.h new file mode 100644 index 00000000000..411c20ca106 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecordersettings.h @@ -0,0 +1,59 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <utils/aspects.h> + +namespace ScreenRecorder::Internal { + +enum CaptureType { + X11grab, + Ddagrab, + Gdigrab, + AVFoundation, +}; + +class ScreenRecorderSettings : public Utils::AspectContainer +{ +public: + ScreenRecorderSettings(); + + bool toolsRegistered() const; + + struct RecordSettings { + const int screenId; + const QRect cropRect; + const int frameRate; + }; + static RecordSettings sanitizedRecordSettings(const RecordSettings &settings); + RecordSettings recordSettings() const; + void applyRecordSettings(const RecordSettings &settings); + CaptureType volatileScreenCaptureType() const; + + // Visible in Settings page + Utils::FilePathAspect ffmpegTool{this}; + Utils::FilePathAspect ffprobeTool{this}; + Utils::SelectionAspect screenCaptureType{this}; + Utils::BoolAspect captureCursor{this}; + Utils::BoolAspect captureMouseClicks{this}; + Utils::BoolAspect enableFileSizeLimit{this}; + Utils::IntegerAspect fileSizeLimit{this}; // in MB + Utils::BoolAspect enableRtBuffer{this}; + Utils::IntegerAspect rtBufferSize{this}; // in MB + Utils::BoolAspect logFfmpegCommandline{this}; + Utils::BoolAspect animatedImagesAsEndlessLoop{this}; + + // Used in other places + Utils::FilePathAspect lastOpenDirectory{this}; + Utils::FilePathAspect exportLastDirectory{this}; + Utils::StringAspect exportLastFormat{this}; + Utils::FilePathAspect lastSaveImageDirectory{this}; + Utils::IntegerAspect recordFrameRate{this}; + Utils::IntegerAspect recordScreenId{this}; + Utils::StringListAspect recordScreenCropRect{this}; +}; + +ScreenRecorderSettings &settings(); + +} // ScreenRecorder::Internal diff --git a/src/plugins/screenrecorder/screenrecordertr.h b/src/plugins/screenrecorder/screenrecordertr.h new file mode 100644 index 00000000000..b1e660ba2d3 --- /dev/null +++ b/src/plugins/screenrecorder/screenrecordertr.h @@ -0,0 +1,15 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <QCoreApplication> + +namespace ScreenRecorder { + +struct Tr +{ + Q_DECLARE_TR_FUNCTIONS(QtC::ScreenRecorder) +}; + +} // namespace ScreenRecorder diff --git a/src/plugins/scxmleditor/CMakeLists.txt b/src/plugins/scxmleditor/CMakeLists.txt index 358c407233c..396a03820e3 100644 --- a/src/plugins/scxmleditor/CMakeLists.txt +++ b/src/plugins/scxmleditor/CMakeLists.txt @@ -89,7 +89,7 @@ add_qtc_plugin(ScxmlEditor scxmleditordata.cpp scxmleditordata.h scxmleditordocument.cpp scxmleditordocument.h scxmleditorfactory.cpp scxmleditorfactory.h - scxmleditorplugin.cpp scxmleditorplugin.h + scxmleditorplugin.cpp scxmleditorstack.cpp scxmleditorstack.h scxmltexteditor.cpp scxmltexteditor.h ) diff --git a/src/plugins/scxmleditor/ScxmlEditor.json.in b/src/plugins/scxmleditor/ScxmlEditor.json.in index 34c9d4ea2b3..3b4351f78b1 100644 --- a/src/plugins/scxmleditor/ScxmlEditor.json.in +++ b/src/plugins/scxmleditor/ScxmlEditor.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"ScxmlEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "ScxmlEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Modeling\", - \"Description\" : \"Visual Editor for SCXML (State Chart XML) files.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Modeling", + "Description" : "Visual Editor for SCXML (State Chart XML) files.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/scxml+xml\'>\", - \" <sub-class-of type=\'text/xml\'/>\", - \" <comment>SCXML file</comment>\", - \" <glob pattern=\'*.scxml\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/scxml+xml'>", + " <sub-class-of type='text/xml'/>", + " <comment>SCXML file</comment>", + " <glob pattern='*.scxml'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/scxmleditor/common/colorpicker.cpp b/src/plugins/scxmleditor/common/colorpicker.cpp index 88f4d6d22a7..a367ebbd7d0 100644 --- a/src/plugins/scxmleditor/common/colorpicker.cpp +++ b/src/plugins/scxmleditor/common/colorpicker.cpp @@ -12,7 +12,9 @@ #include <QHBoxLayout> #include <QToolButton> -using namespace ScxmlEditor::Common; +using namespace Utils; + +namespace ScxmlEditor::Common { const char C_SETTINGS_COLORPICKER_LASTUSEDCOLORS[] = "ScxmlEditor/ColorPickerLastUsedColors_%1"; constexpr int C_BUTTON_COLUMNS_COUNT = 5; @@ -55,15 +57,15 @@ ColorPicker::ColorPicker(const QString &key, QWidget *parent) }.attachTo(this); const QStringList lastColors = Core::ICore::settings()->value( - QString::fromLatin1(C_SETTINGS_COLORPICKER_LASTUSEDCOLORS).arg(m_key), QStringList()).toStringList(); + C_SETTINGS_COLORPICKER_LASTUSEDCOLORS + keyFromString(m_key), QStringList()).toStringList(); for (int i = lastColors.count(); i--;) setLastUsedColor(lastColors[i]); } ColorPicker::~ColorPicker() { - Core::ICore::settings()->setValue(QString::fromLatin1(C_SETTINGS_COLORPICKER_LASTUSEDCOLORS).arg(m_key), - m_lastUsedColorNames); + Core::ICore::settings()->setValue( + C_SETTINGS_COLORPICKER_LASTUSEDCOLORS + keyFromString(m_key), m_lastUsedColorNames); } void ColorPicker::setLastUsedColor(const QString &colorName) @@ -99,3 +101,5 @@ QToolButton *ColorPicker::createButton(const QColor &color) return button; } + +} // ScxmlEditor::Common diff --git a/src/plugins/scxmleditor/common/colorpicker.h b/src/plugins/scxmleditor/common/colorpicker.h index 1576e8a9116..73399068904 100644 --- a/src/plugins/scxmleditor/common/colorpicker.h +++ b/src/plugins/scxmleditor/common/colorpicker.h @@ -10,9 +10,7 @@ class QHBoxLayout; class QToolButton; QT_END_NAMESPACE -namespace ScxmlEditor { - -namespace Common { +namespace ScxmlEditor::Common { class ColorPicker : public QFrame { @@ -37,5 +35,4 @@ private: QHBoxLayout *m_lastUsedColorContainer; }; -} // namespace Common -} // namespace ScxmlEditor +} // namespace ScxmlEditor::Common diff --git a/src/plugins/scxmleditor/common/colorsettings.cpp b/src/plugins/scxmleditor/common/colorsettings.cpp index 1419b481bc5..2f84087fa2b 100644 --- a/src/plugins/scxmleditor/common/colorsettings.cpp +++ b/src/plugins/scxmleditor/common/colorsettings.cpp @@ -12,10 +12,13 @@ #include <QToolButton> #include <coreplugin/icore.h> + #include <utils/layoutbuilder.h> #include <utils/utilsicons.h> -using namespace ScxmlEditor::Common; +using namespace Utils; + +namespace ScxmlEditor::Common { ColorSettings::ColorSettings(QWidget *parent) : QFrame(parent) @@ -29,7 +32,7 @@ ColorSettings::ColorSettings(QWidget *parent) auto removeTheme = new QToolButton; removeTheme->setIcon(Utils::Icons::MINUS.icon()); removeTheme->setAutoRaise(true); - const QSettings *s = Core::ICore::settings(); + const QtcSettings *s = Core::ICore::settings(); m_colorThemes = s->value(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES).toMap(); m_comboColorThemes->addItems(m_colorThemes.keys()); m_comboColorThemes->setCurrentText( @@ -57,7 +60,7 @@ ColorSettings::ColorSettings(QWidget *parent) void ColorSettings::save() { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); s->setValue(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES, m_colorThemes); s->setValue(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, m_comboColorThemes->currentText()); } @@ -110,3 +113,5 @@ void ColorSettings::removeTheme() m_colorThemeView->setEnabled(false); } } + +} // ScxmlEditor::Common diff --git a/src/plugins/scxmleditor/common/colorthemes.cpp b/src/plugins/scxmleditor/common/colorthemes.cpp index 5a40783a315..6a98572f2e9 100644 --- a/src/plugins/scxmleditor/common/colorthemes.cpp +++ b/src/plugins/scxmleditor/common/colorthemes.cpp @@ -10,12 +10,16 @@ #include "scxmltag.h" #include <coreplugin/icore.h> + +#include <utils/qtcsettings.h> #include <utils/stringutils.h> #include <QMenu> #include <QToolButton> -using namespace ScxmlEditor::Common; +using namespace Utils; + +namespace ScxmlEditor::Common { ColorThemes::ColorThemes(QObject *parent) : QObject(parent) @@ -69,7 +73,7 @@ void ColorThemes::updateColorThemeMenu() { m_menu->clear(); - const QSettings *s = Core::ICore::settings(); + const QtcSettings *s = Core::ICore::settings(); const QString currentTheme = s->value(Constants::C_SETTINGS_COLORSETTINGS_CURRENTCOLORTHEME, QString(Constants::C_COLOR_SCHEME_DEFAULT)).toString(); const QVariantMap data = s->value(Constants::C_SETTINGS_COLORSETTINGS_COLORTHEMES).toMap(); @@ -100,7 +104,7 @@ void ColorThemes::selectColorTheme(const QString &name) { QVariantMap colorData; if (m_document && !name.isEmpty()) { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); if (name == Constants::C_COLOR_SCHEME_SCXMLDOCUMENT) { colorData = m_documentColors; @@ -168,3 +172,5 @@ void ColorThemes::setCurrentColors(const QVariantMap &colorData) m_document->setLevelColors(colors); m_document->setEditorInfo(m_document->scxmlRootTag(), Constants::C_SCXML_EDITORINFO_COLORS, serializedColors.join(";;")); } + +} // ScxmlEditor::Common diff --git a/src/plugins/scxmleditor/common/mainwidget.cpp b/src/plugins/scxmleditor/common/mainwidget.cpp index 8cd7618944e..a55d59f6ad1 100644 --- a/src/plugins/scxmleditor/common/mainwidget.cpp +++ b/src/plugins/scxmleditor/common/mainwidget.cpp @@ -51,8 +51,6 @@ #include <QStandardPaths> #include <QXmlStreamWriter> -#include <app/app_version.h> - #include <coreplugin/icore.h> #include <coreplugin/minisplitter.h> @@ -374,7 +372,7 @@ void MainWidget::init() m_toolButtons << stateColorButton << fontColorButton << alignToolButton << adjustToolButton; - const QSettings *s = Core::ICore::settings(); + const QtcSettings *s = Core::ICore::settings(); m_horizontalSplitter->restoreState(s->value(Constants::C_SETTINGS_SPLITTER).toByteArray()); m_actionHandler->action(ActionPaste)->setEnabled(false); @@ -414,7 +412,7 @@ void MainWidget::exportToImage() if (suggestedFileName.isEmpty()) suggestedFileName = Tr::tr("Untitled"); - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); const QString documentsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); const QString lastFolder = s->value( Constants::C_SETTINGS_LASTEXPORTFOLDER, documentsLocation).toString(); @@ -448,7 +446,7 @@ void MainWidget::saveScreenShot() if (!view) return; - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); const QString documentsLocation = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); const FilePath lastFolder = FilePath::fromSettings( s->value(Constants::C_SETTINGS_LASTSAVESCREENSHOTFOLDER, documentsLocation)); @@ -469,7 +467,7 @@ void MainWidget::saveScreenShot() void MainWidget::saveSettings() { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); s->setValue(Constants::C_SETTINGS_SPLITTER, m_horizontalSplitter->saveState()); } diff --git a/src/plugins/scxmleditor/outputpane/errorwidget.cpp b/src/plugins/scxmleditor/outputpane/errorwidget.cpp index b38da63f62d..e48fc370caa 100644 --- a/src/plugins/scxmleditor/outputpane/errorwidget.cpp +++ b/src/plugins/scxmleditor/outputpane/errorwidget.cpp @@ -73,7 +73,7 @@ ErrorWidget::ErrorWidget(QWidget *parent) connect(m_warningModel, &WarningModel::warningsChanged, this, &ErrorWidget::updateWarnings); connect(m_warningModel, &WarningModel::countChanged, this, &ErrorWidget::warningCountChanged); - const QSettings *s = Core::ICore::settings(); + const QtcSettings *s = Core::ICore::settings(); m_errorsTable->restoreGeometry(s->value(Constants::C_SETTINGS_ERRORPANE_GEOMETRY).toByteArray()); m_showErrors->setChecked(s->value(Constants::C_SETTINGS_ERRORPANE_SHOWERRORS, true).toBool()); m_showWarnings->setChecked(s->value(Constants::C_SETTINGS_ERRORPANE_SHOWWARNINGS, true).toBool()); @@ -84,7 +84,7 @@ ErrorWidget::ErrorWidget(QWidget *parent) ErrorWidget::~ErrorWidget() { - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); s->setValue(Constants::C_SETTINGS_ERRORPANE_GEOMETRY, m_errorsTable->saveGeometry()); s->setValue(Constants::C_SETTINGS_ERRORPANE_SHOWERRORS, m_showErrors->isChecked()); s->setValue(Constants::C_SETTINGS_ERRORPANE_SHOWWARNINGS, m_showWarnings->isChecked()); diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp index e85a516cf11..a2beac1167b 100644 --- a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp +++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp @@ -13,8 +13,6 @@ #include <QXmlStreamReader> #include <QXmlStreamWriter> -#include <app/app_version.h> - using namespace ScxmlEditor::PluginInterface; ScxmlDocument::ScxmlDocument(const QString &fileName, QObject *parent) @@ -62,7 +60,7 @@ void ScxmlDocument::clear(bool createRoot) if (createRoot) { pushRootTag(createScxmlTag()); - rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG)); + rootTag()->setAttribute("qt:editorversion", QCoreApplication::applicationVersion()); auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext"); ns->setTagVisibility("editorInfo", false); @@ -207,7 +205,7 @@ bool ScxmlDocument::load(QIODevice *io) // Check editorversion m_hasLayouted = rootTag()->hasAttribute("qt:editorversion"); - rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG)); + rootTag()->setAttribute("qt:editorversion", QCoreApplication::applicationVersion()); } } @@ -351,7 +349,7 @@ bool ScxmlDocument::pasteData(const QByteArray &data, const QPointF &minPos, con void ScxmlDocument::load(const QString &fileName) { - if (QFile::exists(fileName)) { + if (QFileInfo::exists(fileName)) { QFile file(fileName); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (load(&file)) { @@ -363,7 +361,7 @@ void ScxmlDocument::load(const QString &fileName) // If loading doesn't work, create root tag here if (m_rootTags.isEmpty()) { pushRootTag(createScxmlTag()); - rootTag()->setAttribute("qt:editorversion", QLatin1String(Core::Constants::IDE_VERSION_LONG)); + rootTag()->setAttribute("qt:editorversion", QCoreApplication::applicationVersion()); } auto ns = new ScxmlNamespace("qt", "http://www.qt.io/2015/02/scxml-ext"); diff --git a/src/plugins/scxmleditor/scxmleditor.qbs b/src/plugins/scxmleditor/scxmleditor.qbs index 39340d8764c..3f8bbad3d03 100644 --- a/src/plugins/scxmleditor/scxmleditor.qbs +++ b/src/plugins/scxmleditor/scxmleditor.qbs @@ -11,8 +11,6 @@ QtcPlugin { Depends { name: "ProjectExplorer" } Depends { name: "QtSupport" } - Depends { name: "app_version_header" } - cpp.includePaths: base.concat([ ".", common.prefix, @@ -26,7 +24,7 @@ QtcPlugin { "scxmleditordata.cpp", "scxmleditordata.h", "scxmleditordocument.cpp", "scxmleditordocument.h", "scxmleditorfactory.cpp", "scxmleditorfactory.h", - "scxmleditorplugin.cpp", "scxmleditorplugin.h", + "scxmleditorplugin.cpp", "scxmleditorstack.cpp", "scxmleditorstack.h", "scxmltexteditor.cpp", "scxmltexteditor.h", ] diff --git a/src/plugins/scxmleditor/scxmleditordocument.cpp b/src/plugins/scxmleditor/scxmleditordocument.cpp index 1ee62362052..64907f8489a 100644 --- a/src/plugins/scxmleditor/scxmleditordocument.cpp +++ b/src/plugins/scxmleditor/scxmleditordocument.cpp @@ -7,7 +7,7 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/fileutils.h> #include <utils/qtcassert.h> @@ -60,26 +60,24 @@ Core::IDocument::OpenResult ScxmlEditorDocument::open(QString *errorString, bool ScxmlEditorDocument::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { - const FilePath oldFileName = this->filePath(); - const FilePath actualName = filePath.isEmpty() ? oldFileName : filePath; - if (actualName.isEmpty()) + if (filePath.isEmpty()) return false; bool dirty = m_designWidget->isDirty(); - m_designWidget->setFileName(actualName.toString()); + m_designWidget->setFileName(filePath.toString()); if (!m_designWidget->save()) { *errorString = m_designWidget->errorMessage(); - m_designWidget->setFileName(oldFileName.toString()); + m_designWidget->setFileName(this->filePath().toString()); return false; } if (autoSave) { - m_designWidget->setFileName(oldFileName.toString()); + m_designWidget->setFileName(this->filePath().toString()); m_designWidget->save(); return true; } - setFilePath(actualName); + setFilePath(filePath); if (dirty != m_designWidget->isDirty()) emit changed(); diff --git a/src/plugins/scxmleditor/scxmleditorplugin.cpp b/src/plugins/scxmleditor/scxmleditorplugin.cpp index f2a0de589e2..47d76a8c089 100644 --- a/src/plugins/scxmleditor/scxmleditorplugin.cpp +++ b/src/plugins/scxmleditor/scxmleditorplugin.cpp @@ -1,37 +1,35 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "scxmleditorplugin.h" - #include "scxmleditorfactory.h" +#include <extensionsystem/iplugin.h> + #include <coreplugin/designmode.h> -using namespace Core; +#include <memory> -namespace ScxmlEditor { -namespace Internal { +namespace ScxmlEditor::Internal { -class ScxmlEditorPluginPrivate +class ScxmlEditorPlugin : public ExtensionSystem::IPlugin { -public: - ScxmlEditorFactory editorFactory; + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ScxmlEditor.json") + +private: + void initialize() final + { + editorFactory = std::make_unique<ScxmlEditorFactory>(); + } + + void extensionsInitialized() final + { + Core::DesignMode::setDesignModeIsRequired(); + } + + std::unique_ptr<ScxmlEditorFactory> editorFactory; }; -ScxmlEditorPlugin::~ScxmlEditorPlugin() -{ - delete d; -} +} // ScxmlEditor::Internal -void ScxmlEditorPlugin::initialize() -{ - d = new ScxmlEditorPluginPrivate; -} - -void ScxmlEditorPlugin::extensionsInitialized() -{ - DesignMode::setDesignModeIsRequired(); -} - -} // Internal -} // ScxmlEditor +#include "scxmleditorplugin.moc" diff --git a/src/plugins/scxmleditor/scxmleditorplugin.h b/src/plugins/scxmleditor/scxmleditorplugin.h deleted file mode 100644 index 485ba92def0..00000000000 --- a/src/plugins/scxmleditor/scxmleditorplugin.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <extensionsystem/iplugin.h> - -namespace ScxmlEditor { -namespace Internal { - -class ScxmlEditorPlugin : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "ScxmlEditor.json") - -public: - ScxmlEditorPlugin() = default; - ~ScxmlEditorPlugin(); - -private: - void initialize() final; - void extensionsInitialized() final; - - class ScxmlEditorPluginPrivate *d = nullptr; -}; - -} // namespace Internal -} // namespace ScxmlEditor diff --git a/src/plugins/serialterminal/SerialTerminal.json.in b/src/plugins/serialterminal/SerialTerminal.json.in index efff034b32a..78ac127ab4d 100644 --- a/src/plugins/serialterminal/SerialTerminal.json.in +++ b/src/plugins/serialterminal/SerialTerminal.json.in @@ -1,20 +1,20 @@ { - \"Name\" : \"SerialTerminal\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"Benjamin Balga\", - \"Experimental\" : true, - \"DisabledByDefault\" : true, - \"Copyright\" : \"(C) 2018 Benjamin Balga\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "SerialTerminal", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "Benjamin Balga", + "Experimental" : true, + "DisabledByDefault" : true, + "Copyright" : "(C) 2018 Benjamin Balga", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Serial Port Terminal\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Serial Port Terminal", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/serialterminal/serialcontrol.cpp b/src/plugins/serialterminal/serialcontrol.cpp index 0c034f1cb18..2ec8fe3523f 100644 --- a/src/plugins/serialterminal/serialcontrol.cpp +++ b/src/plugins/serialterminal/serialcontrol.cpp @@ -151,7 +151,7 @@ QString SerialControl::baudRateText() const void SerialControl::pulseDataTerminalReady() { m_serialPort.setDataTerminalReady(!m_initialDtrState); - QTimer::singleShot(Constants::RESET_DELAY, [&]() { + QTimer::singleShot(Constants::RESET_DELAY, this, [this] { m_serialPort.setDataTerminalReady(m_initialDtrState); }); } diff --git a/src/plugins/serialterminal/serialoutputpane.cpp b/src/plugins/serialterminal/serialoutputpane.cpp index 7a92424d7b8..2f30bf75fcd 100644 --- a/src/plugins/serialterminal/serialoutputpane.cpp +++ b/src/plugins/serialterminal/serialoutputpane.cpp @@ -33,6 +33,8 @@ #include <QToolButton> #include <QVBoxLayout> +using namespace Utils; + namespace SerialTerminal { namespace Internal { @@ -118,6 +120,10 @@ SerialOutputPane::SerialOutputPane(Settings &settings) : m_closeAllTabsAction(new QAction(Tr::tr("Close All Tabs"), this)), m_closeOtherTabsAction(new QAction(Tr::tr("Close Other Tabs"), this)) { + setId("Serial Terminal"); + setDisplayName(Tr::tr(Constants::OUTPUT_PANE_TITLE)); + setPriorityInStatusBar(-70); + createToolButtons(); auto layout = new QVBoxLayout; @@ -172,16 +178,6 @@ QList<QWidget *> SerialOutputPane::toolBarWidgets() const m_resetButton }; } -QString SerialOutputPane::displayName() const -{ - return Tr::tr(Constants::OUTPUT_PANE_TITLE); -} - -int SerialOutputPane::priorityInStatusBar() const -{ - return 30; -} - void SerialOutputPane::clearContents() { auto currentWindow = qobject_cast<Core::OutputWindow *>(m_tabWidget->currentWidget()); @@ -257,14 +253,12 @@ void SerialOutputPane::createNewOutputWindow(SerialControl *rc) return; // Signals to update buttons - connect(rc, &SerialControl::started, - [this, rc]() { + connect(rc, &SerialControl::started, this, [this, rc] { if (isCurrent(rc)) enableButtons(rc, true); }); - connect(rc, &SerialControl::finished, - [this, rc]() { + connect(rc, &SerialControl::finished, this, [this, rc] { const int tabIndex = indexOf(rc); if (tabIndex != -1) m_serialControlTabs[tabIndex].window->flush(); @@ -279,7 +273,7 @@ void SerialOutputPane::createNewOutputWindow(SerialControl *rc) static int counter = 0; Utils::Id contextId = Utils::Id(Constants::C_SERIAL_OUTPUT).withSuffix(counter++); Core::Context context(contextId); - auto ow = new Core::OutputWindow(context, QString(), m_tabWidget); + auto ow = new Core::OutputWindow(context, Key(), m_tabWidget); using TextEditor::TextEditorSettings; auto fontSettingsChanged = [ow] { ow->setBaseFont(TextEditorSettings::fontSettings().font()); diff --git a/src/plugins/serialterminal/serialoutputpane.h b/src/plugins/serialterminal/serialoutputpane.h index af0772a1f3d..1f970e2f18e 100644 --- a/src/plugins/serialterminal/serialoutputpane.h +++ b/src/plugins/serialterminal/serialoutputpane.h @@ -50,9 +50,7 @@ public: // IOutputPane QWidget *outputWidget(QWidget *parent) final; QList<QWidget *> toolBarWidgets() const final; - QString displayName() const final; - int priorityInStatusBar() const final; void clearContents() final; bool canFocus() const final; bool hasFocus() const final; diff --git a/src/plugins/serialterminal/serialterminalsettings.cpp b/src/plugins/serialterminal/serialterminalsettings.cpp index db938ce673a..b8f023f93f8 100644 --- a/src/plugins/serialterminal/serialterminalsettings.cpp +++ b/src/plugins/serialterminal/serialterminalsettings.cpp @@ -5,8 +5,11 @@ #include "serialterminalconstants.h" #include "serialterminaltr.h" +#include <utils/qtcsettings.h> + #include <QLoggingCategory> -#include <QSettings> + +using namespace Utils; namespace SerialTerminal { namespace Internal { @@ -15,7 +18,7 @@ static Q_LOGGING_CATEGORY(log, Constants::LOGGING_CATEGORY, QtWarningMsg) // Set 'value' only if the key exists in the settings template <typename T> -void readSetting(const QSettings &settings, T &value, const QString &key) { +void readSetting(const QtcSettings &settings, T &value, const Key &key) { if (settings.contains(key)) value = settings.value(key).value<T>(); } @@ -32,7 +35,7 @@ Settings::Settings() } // Save settings to a QSettings -void Settings::save(QSettings *settings) +void Settings::save(QtcSettings *settings) { if (!settings || !edited) return; @@ -61,7 +64,7 @@ void Settings::save(QSettings *settings) } // Load available settings from a QSettings -void Settings::load(QSettings *settings) +void Settings::load(QtcSettings *settings) { if (!settings) return; @@ -129,7 +132,7 @@ void Settings::setDefaultLineEndingIndex(unsigned int index) } -void Settings::saveLineEndings(QSettings &settings) +void Settings::saveLineEndings(QtcSettings &settings) { settings.beginWriteArray(Constants::SETTINGS_LINE_ENDINGS, lineEndings.size()); int i = 0; @@ -141,7 +144,7 @@ void Settings::saveLineEndings(QSettings &settings) settings.endArray(); } -void Settings::loadLineEndings(QSettings &settings) +void Settings::loadLineEndings(QtcSettings &settings) { const int size = settings.beginReadArray(Constants::SETTINGS_LINE_ENDINGS); if (size > 0) // If null, keep default line endings diff --git a/src/plugins/serialterminal/serialterminalsettings.h b/src/plugins/serialterminal/serialterminalsettings.h index b2a4ec84e43..48efdb08064 100644 --- a/src/plugins/serialterminal/serialterminalsettings.h +++ b/src/plugins/serialterminal/serialterminalsettings.h @@ -9,9 +9,7 @@ #include <QString> #include <QVector> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { class QtcSettings; } namespace SerialTerminal { namespace Internal { @@ -36,8 +34,8 @@ public: bool clearInputOnSend = false; - void save(QSettings *settings); - void load(QSettings *settings); + void save(Utils::QtcSettings *settings); + void load(Utils::QtcSettings *settings); void setBaudRate(qint32 br); void setPortName(const QString &name); @@ -47,8 +45,8 @@ public: void setDefaultLineEndingIndex(unsigned int index); private: - void saveLineEndings(QSettings &settings); - void loadLineEndings(QSettings &settings); + void saveLineEndings(Utils::QtcSettings &settings); + void loadLineEndings(Utils::QtcSettings &settings); }; } // namespace Internal diff --git a/src/plugins/silversearcher/SilverSearcher.json.in b/src/plugins/silversearcher/SilverSearcher.json.in index a60ade39c20..bfaf5536359 100644 --- a/src/plugins/silversearcher/SilverSearcher.json.in +++ b/src/plugins/silversearcher/SilverSearcher.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"SilverSearcher\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"Przemyslaw Gorszkowski\", - \"Copyright\" : \"(C) 2017 Przemyslaw Gorszkowski, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "SilverSearcher", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "Przemyslaw Gorszkowski", + "Copyright" : "(C) 2017 Przemyslaw Gorszkowski, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Adds possibility to use SilverSearcher tool as an alternative mechanism of 'find in files'\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Adds possibility to use SilverSearcher tool as an alternative mechanism of 'find in files'", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp index 6cf428bb64f..50ceeccdba9 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.cpp +++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp @@ -6,14 +6,15 @@ #include "silversearchertr.h" #include <texteditor/findinfiles.h> + #include <utils/async.h> #include <utils/process.h> #include <utils/qtcassert.h> +#include <utils/qtcsettings.h> #include <QHBoxLayout> #include <QLabel> #include <QLineEdit> -#include <QSettings> using namespace Core; using namespace SilverSearcher; @@ -21,17 +22,13 @@ using namespace TextEditor; using namespace Utils; namespace { -const QLatin1String s_metaCharacters = QLatin1String("+()^$.{}[]|\\"); -const QLatin1String s_searchOptionsString = QLatin1String("SearchOptionsString"); -class SilverSearcherSearchOptions -{ -public: - QString searchOptions; -}; +const char s_searchOptionsString[] = "SearchOptionsString"; static QString convertWildcardToRegex(const QString &wildcard) { + static const QString s_metaCharacters("+()^$.{}[]|\\"); + QString regex; const int wildcardSize = wildcard.size(); regex.append('^'); @@ -60,11 +57,9 @@ static bool isSilverSearcherAvailable() } static void runSilverSeacher(QPromise<SearchResultItems> &promise, - const FileFindParameters ¶meters) + const FileFindParameters ¶meters, const QString &searchOptions) { - const auto setupProcess = [parameters](Process &process) { - const FilePath directory - = FilePath::fromUserInput(parameters.additionalParameters.toString()); + const auto setupProcess = [parameters, searchOptions](Process &process) { QStringList arguments = {"--parallel", "--ackmate"}; if (parameters.flags & FindCaseSensitively) @@ -88,12 +83,10 @@ static void runSilverSeacher(QPromise<SearchResultItems> &promise, arguments << "-G" << nameFiltersAsRegExp; - const SilverSearcherSearchOptions params = parameters.searchEngineParameters - .value<SilverSearcherSearchOptions>(); - if (!params.searchOptions.isEmpty()) - arguments << params.searchOptions.split(' '); + if (!searchOptions.isEmpty()) + arguments << searchOptions.split(' '); - arguments << "--" << parameters.text << directory.normalizedPathName().toString(); + arguments << "--" << parameters.text << parameters.searchDir.normalizedPathName().toString(); process.setCommand({"ag", arguments}); }; @@ -108,8 +101,6 @@ static void runSilverSeacher(QPromise<SearchResultItems> &promise, } // namespace -Q_DECLARE_METATYPE(SilverSearcherSearchOptions) - namespace SilverSearcher { FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent) @@ -137,13 +128,6 @@ FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent) } } -QVariant FindInFilesSilverSearcher::parameters() const -{ - SilverSearcherSearchOptions silverSearcherSearchOptions; - silverSearcherSearchOptions.searchOptions = m_searchOptionsLineEdit->text(); - return QVariant::fromValue(silverSearcherSearchOptions); -} - QString FindInFilesSilverSearcher::title() const { return "Silver Searcher"; @@ -159,24 +143,19 @@ QWidget *FindInFilesSilverSearcher::widget() const return m_widget; } -void FindInFilesSilverSearcher::writeSettings(QSettings *settings) const +void FindInFilesSilverSearcher::writeSettings(QtcSettings *settings) const { settings->setValue(s_searchOptionsString, m_searchOptionsLineEdit->text()); } -QFuture<SearchResultItems> FindInFilesSilverSearcher::executeSearch( - const FileFindParameters ¶meters, BaseFileFind * /*baseFileFind*/) +SearchExecutor FindInFilesSilverSearcher::searchExecutor() const { - return Utils::asyncRun(runSilverSeacher, parameters); + return [searchOptions = m_searchOptionsLineEdit->text()](const FileFindParameters ¶meters) { + return Utils::asyncRun(runSilverSeacher, parameters, searchOptions); + }; } -IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/, - const FileFindParameters & /*parameters*/) -{ - return nullptr; -} - -void FindInFilesSilverSearcher::readSettings(QSettings *settings) +void FindInFilesSilverSearcher::readSettings(QtcSettings *settings) { m_searchOptionsLineEdit->setText(settings->value(s_searchOptionsString).toString()); } diff --git a/src/plugins/silversearcher/findinfilessilversearcher.h b/src/plugins/silversearcher/findinfilessilversearcher.h index bc5e224c21f..1819260a0ec 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.h +++ b/src/plugins/silversearcher/findinfilessilversearcher.h @@ -28,13 +28,9 @@ public: QString title() const override; QString toolTip() const override; QWidget *widget() const override; - QVariant parameters() const override; - void readSettings(QSettings *settings) override; - void writeSettings(QSettings *settings) const override; - QFuture<Utils::SearchResultItems> executeSearch( - const TextEditor::FileFindParameters ¶meters, TextEditor::BaseFileFind *) override; - Core::IEditor *openEditor(const Utils::SearchResultItem &item, - const TextEditor::FileFindParameters ¶meters) override; + void readSettings(Utils::QtcSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) const override; + TextEditor::SearchExecutor searchExecutor() const override; private: QPointer<Core::IFindSupport> m_currentFindSupport; diff --git a/src/plugins/squish/CMakeLists.txt b/src/plugins/squish/CMakeLists.txt index 476a4af2366..11b41a5a2c8 100644 --- a/src/plugins/squish/CMakeLists.txt +++ b/src/plugins/squish/CMakeLists.txt @@ -18,7 +18,7 @@ add_qtc_plugin(Squish squishnavigationwidget.cpp squishnavigationwidget.h squishoutputpane.cpp squishoutputpane.h squishperspective.cpp squishperspective.h - squishplugin.cpp squishplugin.h + squishplugin.cpp squishprocessbase.cpp squishprocessbase.h squishresultmodel.cpp squishresultmodel.h squishrunnerprocess.cpp squishrunnerprocess.h diff --git a/src/plugins/squish/Squish.json.in b/src/plugins/squish/Squish.json.in index 9f32eb46d8c..64e4397aeb3 100644 --- a/src/plugins/squish/Squish.json.in +++ b/src/plugins/squish/Squish.json.in @@ -1,30 +1,30 @@ { -\"Name\" : \"Squish\", -\"Version\" : \"$$QTCREATOR_VERSION\", -\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", -\"Experimental\" : true, -\"Vendor\" : \"The Qt Company Ltd\", -\"Copyright\" : \"(C) 2022 The Qt Company Ltd\", -\"License\" : [ \"Commercial Usage\", -\"\", -\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", -\"\", -\"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.\" +"Name" : "Squish", +"Version" : "${IDE_VERSION}", +"CompatVersion" : "${IDE_VERSION_COMPAT}", +"Experimental" : true, +"Vendor" : "The Qt Company Ltd", +"Copyright" : "(C) 2022 The Qt Company Ltd", +"License" : [ "Commercial Usage", +"", +"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", +"", +"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." ], -\"Description\" : \"Squish plugin. Provides integration of Squish.\", -\"Url\" : \"http://www.qt.io\", -$$dependencyList, +"Description" : "Squish plugin. Provides integration of Squish.", +"Url" : "http://www.qt.io", +${IDE_PLUGIN_DEPENDENCIES}, -\"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/squish-objectsmap\'>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" <comment>Squish objects.map File</comment>\", - \" <glob pattern=\'objects.map\'/>\", - \" </mime-type>\", - \"</mime-info>\" +"Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/squish-objectsmap'>", + " <sub-class-of type='text/plain'/>", + " <comment>Squish objects.map File</comment>", + " <glob pattern='objects.map'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/squish/objectsmapdocument.cpp b/src/plugins/squish/objectsmapdocument.cpp index 96bf8797208..20c77d9ccee 100644 --- a/src/plugins/squish/objectsmapdocument.cpp +++ b/src/plugins/squish/objectsmapdocument.cpp @@ -5,7 +5,6 @@ #include "objectsmaptreeitem.h" #include "squishconstants.h" -#include "squishplugin.h" #include "squishsettings.h" #include "squishtr.h" @@ -42,30 +41,29 @@ Core::IDocument::OpenResult ObjectsMapDocument::open(QString *errorString, } bool ObjectsMapDocument::saveImpl(QString *errorString, - const Utils::FilePath &fileName, + const Utils::FilePath &filePath, bool autoSave) { - const Utils::FilePath actual = fileName.isEmpty() ? filePath() : fileName; - if (actual.isEmpty()) + if (filePath.isEmpty()) return false; - const bool writeOk = writeFile(actual); + const bool writeOk = writeFile(filePath); if (!writeOk) { if (errorString) - *errorString = Tr::tr("Failed to write \"%1\"").arg(actual.toUserOutput()); + *errorString = Tr::tr("Failed to write \"%1\"").arg(filePath.toUserOutput()); return false; } if (!autoSave) { setModified(false); - setFilePath(actual); + setFilePath(filePath); } return true; } Utils::FilePath ObjectsMapDocument::fallbackSaveAsPath() const { - return Utils::FilePath(); + return {}; } QString ObjectsMapDocument::fallbackSaveAsFileName() const @@ -197,7 +195,7 @@ Core::IDocument::OpenResult ObjectsMapDocument::openImpl(QString *error, text = reader.data(); } else { - const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath(); + const Utils::FilePath base = settings().squishPath(); if (base.isEmpty()) { if (error) error->append(Tr::tr("Incomplete Squish settings. " @@ -235,7 +233,7 @@ bool ObjectsMapDocument::writeFile(const Utils::FilePath &fileName) const } // otherwise we need the objectmaptool to write the scripted object map again - const Utils::FilePath base = SquishPlugin::squishSettings()->squishPath(); + const Utils::FilePath base = settings().squishPath(); if (base.isEmpty()) return false; const Utils::FilePath exe = base.pathAppended("lib/exec/objectmaptool").withExecutableSuffix(); diff --git a/src/plugins/squish/objectsmaptreeitem.cpp b/src/plugins/squish/objectsmaptreeitem.cpp index 1fec8eafc34..1fd39366cde 100644 --- a/src/plugins/squish/objectsmaptreeitem.cpp +++ b/src/plugins/squish/objectsmaptreeitem.cpp @@ -346,7 +346,7 @@ void ObjectsMapModel::removeSymbolicName(const QModelIndex &idx) QStringList ObjectsMapModel::allSymbolicNames() const { TreeItem *root = rootItem(); - QTC_ASSERT(root, return QStringList()); + QTC_ASSERT(root, return {}); QMap<QString, PropertyList> objects; forAllItems([&objects](ObjectsMapTreeItem *item) { diff --git a/src/plugins/squish/propertytreeitem.cpp b/src/plugins/squish/propertytreeitem.cpp index 3e16212a2d9..442d7f5b9a0 100644 --- a/src/plugins/squish/propertytreeitem.cpp +++ b/src/plugins/squish/propertytreeitem.cpp @@ -240,7 +240,7 @@ QStringList PropertiesModel::allPropertyNames() const { TreeItem *root = rootItem(); if (!root) - return QStringList(); + return {}; QStringList result; result.reserve(root->childCount()); diff --git a/src/plugins/squish/squish.qbs b/src/plugins/squish/squish.qbs index 29dd1da4042..4b012b05123 100644 --- a/src/plugins/squish/squish.qbs +++ b/src/plugins/squish/squish.qbs @@ -44,7 +44,6 @@ QtcPlugin { "squishperspective.cpp", "squishperspective.h", "squishplugin.cpp", - "squishplugin.h", "squishplugin_global.h", "squishprocessbase.cpp", "squishprocessbase.h", diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp index 8a726cb9baa..ed2ad231378 100644 --- a/src/plugins/squish/squishfilehandler.cpp +++ b/src/plugins/squish/squishfilehandler.cpp @@ -436,7 +436,7 @@ void SquishFileHandler::recordTestCase(const QString &suiteName, const QString & return; conf.setAut(dialog.aut.currentText()); - conf.setArguments(dialog.arguments.value()); + conf.setArguments(dialog.arguments()); } SquishTools::instance()->recordTestCase(suitePath, testCaseName, conf); diff --git a/src/plugins/squish/squishnavigationwidget.cpp b/src/plugins/squish/squishnavigationwidget.cpp index cc9ec76b317..b6e68aad871 100644 --- a/src/plugins/squish/squishnavigationwidget.cpp +++ b/src/plugins/squish/squishnavigationwidget.cpp @@ -6,7 +6,6 @@ #include "squishconstants.h" #include "squishfilehandler.h" #include "squishmessages.h" -#include "squishplugin.h" #include "squishsettings.h" #include "squishtesttreemodel.h" #include "squishtesttreeview.h" @@ -25,11 +24,37 @@ #include <QMenu> #include <QVBoxLayout> -namespace Squish { -namespace Internal { +using namespace Utils; + +namespace Squish::Internal { const int defaultSectionSize = 17; +class SquishNavigationWidget : public QWidget +{ +public: + explicit SquishNavigationWidget(QWidget *parent = nullptr); + ~SquishNavigationWidget() override; + void contextMenuEvent(QContextMenuEvent *event) override; + static QList<QToolButton *> createToolButtons(); + +private: + void onItemActivated(const QModelIndex &idx); + void onExpanded(const QModelIndex &idx); + void onCollapsed(const QModelIndex &idx); + void onRowsInserted(const QModelIndex &parent, int, int); + void onRowsRemoved(const QModelIndex &parent, int, int); + void onRemoveSharedFolderTriggered(int row, const QModelIndex &parent); + void onRemoveAllSharedFolderTriggered(); + void onRecordTestCase(const QString &suiteName, const QString &testCase); + void onNewTestCaseTriggered(const QModelIndex &index); + + SquishTestTreeView *m_view; + SquishTestTreeModel *m_model; // not owned + SquishTestTreeSortModel *m_sortModel; +}; + + SquishNavigationWidget::SquishNavigationWidget(QWidget *parent) : QWidget(parent) { @@ -296,13 +321,13 @@ void SquishNavigationWidget::onRemoveAllSharedFolderTriggered() void SquishNavigationWidget::onRecordTestCase(const QString &suiteName, const QString &testCase) { - QMessageBox::StandardButton pressed = Utils::CheckableMessageBox::question( + QMessageBox::StandardButton pressed = CheckableMessageBox::question( Core::ICore::dialogParent(), Tr::tr("Record Test Case"), Tr::tr("Do you want to record over the test case \"%1\"? The existing content will " "be overwritten by the recorded script.") .arg(testCase), - QString("RecordWithoutApproval")); + Key("RecordWithoutApproval")); if (pressed != QMessageBox::Yes) return; @@ -311,10 +336,7 @@ void SquishNavigationWidget::onRecordTestCase(const QString &suiteName, const QS void SquishNavigationWidget::onNewTestCaseTriggered(const QModelIndex &index) { - auto settings = SquishPlugin::squishSettings(); - QTC_ASSERT(settings, return); - - if (!settings->squishPath().pathAppended("scriptmodules").exists()) { + if (!settings().squishPath().pathAppended("scriptmodules").exists()) { SquishMessages::criticalMessage(Tr::tr("Set up a valid Squish path to be able to create " "a new test case.\n(Edit > Preferences > Squish)")); return; @@ -350,5 +372,4 @@ Core::NavigationView SquishNavigationWidgetFactory::createWidget() return view; } -} // namespace Internal -} // namespace Squish +} // Squish::Internal diff --git a/src/plugins/squish/squishnavigationwidget.h b/src/plugins/squish/squishnavigationwidget.h index d01a48cdd58..c0d9f0af203 100644 --- a/src/plugins/squish/squishnavigationwidget.h +++ b/src/plugins/squish/squishnavigationwidget.h @@ -5,47 +5,10 @@ #include <coreplugin/inavigationwidgetfactory.h> -#include <utils/navigationtreeview.h> - -QT_BEGIN_NAMESPACE -class QToolButton; -QT_END_NAMESPACE - -namespace Squish { -namespace Internal { - -class SquishTestTreeModel; -class SquishTestTreeSortModel; -class SquishTestTreeView; - -class SquishNavigationWidget : public QWidget -{ - Q_OBJECT -public: - explicit SquishNavigationWidget(QWidget *parent = nullptr); - ~SquishNavigationWidget() override; - void contextMenuEvent(QContextMenuEvent *event) override; - static QList<QToolButton *> createToolButtons(); - -private: - void onItemActivated(const QModelIndex &idx); - void onExpanded(const QModelIndex &idx); - void onCollapsed(const QModelIndex &idx); - void onRowsInserted(const QModelIndex &parent, int, int); - void onRowsRemoved(const QModelIndex &parent, int, int); - void onRemoveSharedFolderTriggered(int row, const QModelIndex &parent); - void onRemoveAllSharedFolderTriggered(); - void onRecordTestCase(const QString &suiteName, const QString &testCase); - void onNewTestCaseTriggered(const QModelIndex &index); - - SquishTestTreeView *m_view; - SquishTestTreeModel *m_model; // not owned - SquishTestTreeSortModel *m_sortModel; -}; +namespace Squish::Internal { class SquishNavigationWidgetFactory : public Core::INavigationWidgetFactory { - Q_OBJECT public: SquishNavigationWidgetFactory(); @@ -53,5 +16,4 @@ private: Core::NavigationView createWidget() override; }; -} // namespace Internal -} // namespace Squish +} // Squish::Internal diff --git a/src/plugins/squish/squishoutputpane.cpp b/src/plugins/squish/squishoutputpane.cpp index 8c8047f47f4..1395af9e463 100644 --- a/src/plugins/squish/squishoutputpane.cpp +++ b/src/plugins/squish/squishoutputpane.cpp @@ -16,22 +16,24 @@ #include <QVBoxLayout> #include <coreplugin/editormanager/editormanager.h> -#include <coreplugin/icontext.h> #include <utils/itemviews.h> #include <utils/stylehelper.h> #include <utils/theme/theme.h> #include <utils/utilsicons.h> -namespace Squish { -namespace Internal { +namespace Squish::Internal { static SquishOutputPane *m_instance = nullptr; -SquishOutputPane::SquishOutputPane(QObject *parent) - : Core::IOutputPane(parent) - , m_context(new Core::IContext(this)) +SquishOutputPane::SquishOutputPane() { + setId("Squish"); + setDisplayName(Tr::tr("Squish")); + setPriorityInStatusBar(-60); + + m_instance = this; + m_outputPane = new QTabWidget; m_outputPane->setDocumentMode(true); @@ -99,8 +101,6 @@ SquishOutputPane::SquishOutputPane(QObject *parent) SquishOutputPane *SquishOutputPane::instance() { - if (!m_instance) - m_instance = new SquishOutputPane; return m_instance; } @@ -115,17 +115,7 @@ QWidget *SquishOutputPane::outputWidget(QWidget *parent) QList<QWidget *> SquishOutputPane::toolBarWidgets() const { - return QList<QWidget *>() << m_filterButton << m_expandAll << m_collapseAll; -} - -QString SquishOutputPane::displayName() const -{ - return Tr::tr("Squish"); -} - -int SquishOutputPane::priorityInStatusBar() const -{ - return -777; + return {m_filterButton, m_expandAll, m_collapseAll}; } void SquishOutputPane::clearContents() @@ -395,5 +385,4 @@ void SquishOutputPane::enableAllFiltersTriggered() m_filterModel->enableAllResultTypes(); } -} // namespace Internal -} // namespace Squish +} // namespace Squish::Internal diff --git a/src/plugins/squish/squishoutputpane.h b/src/plugins/squish/squishoutputpane.h index 6f7b60f21f6..914e931f794 100644 --- a/src/plugins/squish/squishoutputpane.h +++ b/src/plugins/squish/squishoutputpane.h @@ -16,29 +16,25 @@ class QTabWidget; class QToolButton; QT_END_NAMESPACE -namespace Core { class IContext; } - namespace Utils { class TreeView; } -namespace Squish { -namespace Internal { +namespace Squish::Internal { class TestResult; class SquishResultItem; class SquishResultModel; class SquishResultFilterModel; -class SquishOutputPane : public Core::IOutputPane +class SquishOutputPane final : public Core::IOutputPane { - Q_OBJECT public: + SquishOutputPane(); + static SquishOutputPane *instance(); // IOutputPane interface QWidget *outputWidget(QWidget *parent) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void visibilityChanged(bool visible) override; void setFocus() override; @@ -50,14 +46,12 @@ public: void goToNext() override; void goToPrev() override; -public slots: void addResultItem(SquishResultItem *item); void addLogOutput(const QString &output); void onTestRunFinished(); void clearOldResults(); private: - SquishOutputPane(QObject *parent = nullptr); void createToolButtons(); void initializeFilterMenu(); void onItemActivated(const QModelIndex &idx); @@ -67,7 +61,6 @@ private: void updateSummaryLabel(); QTabWidget *m_outputPane; - Core::IContext *m_context; QWidget *m_outputWidget; QFrame *m_summaryWidget; QLabel *m_summaryLabel; @@ -81,5 +74,4 @@ private: QMenu *m_filterMenu; }; -} // namespace Internal -} // namespace Squish +} // namespace Squish::Internal diff --git a/src/plugins/squish/squishplugin.cpp b/src/plugins/squish/squishplugin.cpp index e2feea622aa..7ed6a8c7290 100644 --- a/src/plugins/squish/squishplugin.cpp +++ b/src/plugins/squish/squishplugin.cpp @@ -1,8 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "squishplugin.h" - #include "objectsmapeditor.h" #include "squishfilehandler.h" #include "squishmessages.h" @@ -20,6 +18,7 @@ #include <coreplugin/icore.h> #include <extensionsystem/pluginmanager.h> +#include <extensionsystem/iplugin.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h> @@ -27,66 +26,35 @@ #include <utils/qtcassert.h> #include <QMenu> -#include <QtPlugin> using namespace Core; +using namespace Utils; -namespace Squish { -namespace Internal { +namespace Squish::Internal { -class SquishPluginPrivate : public QObject +class SquishPluginPrivate final : public QObject { public: SquishPluginPrivate(); - ~SquishPluginPrivate(); - void initializeMenuEntries(); bool initializeGlobalScripts(); - SquishSettings m_squishSettings; SquishTestTreeModel m_treeModel; SquishNavigationWidgetFactory m_navigationWidgetFactory; ObjectsMapEditorFactory m_objectsMapEditorFactory; - SquishOutputPane *m_outputPane = nullptr; - SquishTools * m_squishTools = nullptr; -}; + SquishOutputPane m_outputPane; + SquishTools m_squishTools; -static SquishPluginPrivate *dd = nullptr; + SquishToolkitsPageFactory m_squishToolkitsPageFactory; + SquishScriptLanguagePageFactory m_squishScriptLanguagePageFactory; + SquishAUTPageFactory m_squishAUTPageFactory; + SquishGeneratorFactory m_squishGeneratorFactory; +}; SquishPluginPrivate::SquishPluginPrivate() { qRegisterMetaType<SquishResultItem*>("SquishResultItem*"); - m_outputPane = SquishOutputPane::instance(); - m_squishTools = new SquishTools; - initializeMenuEntries(); - - ProjectExplorer::JsonWizardFactory::registerPageFactory(new SquishToolkitsPageFactory); - ProjectExplorer::JsonWizardFactory::registerPageFactory(new SquishScriptLanguagePageFactory); - ProjectExplorer::JsonWizardFactory::registerPageFactory(new SquishAUTPageFactory); - ProjectExplorer::JsonWizardFactory::registerGeneratorFactory(new SquishGeneratorFactory); -} - -SquishPluginPrivate::~SquishPluginPrivate() -{ - delete m_outputPane; - delete m_squishTools; -} - -SquishPlugin::~SquishPlugin() -{ - delete dd; - dd = nullptr; -} - -SquishSettings *SquishPlugin::squishSettings() -{ - QTC_ASSERT(dd, return nullptr); - return &dd->m_squishSettings; -} - -void SquishPluginPrivate::initializeMenuEntries() -{ ActionContainer *menu = ActionManager::createMenu("Squish.Menu"); menu->menu()->setTitle(Tr::tr("&Squish")); menu->setOnAllDisabledBehavior(ActionContainer::Show); @@ -95,8 +63,7 @@ void SquishPluginPrivate::initializeMenuEntries() Command *command = ActionManager::registerAction(action, "Squish.ServerSettings"); menu->addAction(command); connect(action, &QAction::triggered, this, [] { - const SquishSettings *settings = SquishPlugin::squishSettings(); - if (!settings->squishPath().exists()) { + if (!settings().squishPath().exists()) { SquishMessages::criticalMessage(Tr::tr("Invalid Squish settings. Configure Squish " "installation path inside " "Preferences... > Squish > General to use " @@ -114,15 +81,14 @@ void SquishPluginPrivate::initializeMenuEntries() bool SquishPluginPrivate::initializeGlobalScripts() { - QTC_ASSERT(dd->m_squishTools, return false); SquishFileHandler::instance()->setSharedFolders({}); - const Utils::FilePath squishserver = dd->m_squishSettings.squishPath().pathAppended( - Utils::HostOsInfo::withExecutableSuffix("bin/squishserver")); + const FilePath squishserver = settings().squishPath().pathAppended("bin/squishserver") + .withExecutableSuffix(); if (!squishserver.isExecutableFile()) return false; - dd->m_squishTools->queryGlobalScripts([](const QString &output, const QString &error) { + m_squishTools.queryGlobalScripts([](const QString &output, const QString &error) { if (output.isEmpty() || !error.isEmpty()) return; // ignore (for now?) @@ -134,31 +100,38 @@ bool SquishPluginPrivate::initializeGlobalScripts() return true; } -void SquishPlugin::initialize() +class SquishPlugin final : public ExtensionSystem::IPlugin { - dd = new SquishPluginPrivate; - ProjectExplorer::JsonWizardFactory::addWizardPath(":/squish/wizard/"); -} + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Squish.json") -bool SquishPlugin::delayedInitialize() -{ - connect(&dd->m_squishSettings.squishPath, &Utils::BaseAspect::changed, - dd, &SquishPluginPrivate::initializeGlobalScripts); +private: + void initialize() final + { + d.reset(new SquishPluginPrivate); + ProjectExplorer::JsonWizardFactory::addWizardPath(":/squish/wizard/"); + } - return dd->initializeGlobalScripts(); -} + bool delayedInitialize() final + { + connect(&settings().squishPath, &BaseAspect::changed, + d.get(), &SquishPluginPrivate::initializeGlobalScripts); -ExtensionSystem::IPlugin::ShutdownFlag SquishPlugin::aboutToShutdown() -{ - if (dd->m_squishTools) { - if (dd->m_squishTools->shutdown()) + return d->initializeGlobalScripts(); + } + + ShutdownFlag aboutToShutdown() final + { + if (d->m_squishTools.shutdown()) return SynchronousShutdown; - connect(dd->m_squishTools, &SquishTools::shutdownFinished, + connect(&d->m_squishTools, &SquishTools::shutdownFinished, this, &ExtensionSystem::IPlugin::asynchronousShutdownFinished); return AsynchronousShutdown; } - return SynchronousShutdown; -} -} // namespace Internal -} // namespace Squish + std::unique_ptr<SquishPluginPrivate> d; +}; + +} // Squish::Internal + +#include "squishplugin.moc" diff --git a/src/plugins/squish/squishplugin.h b/src/plugins/squish/squishplugin.h deleted file mode 100644 index 86b5eca3932..00000000000 --- a/src/plugins/squish/squishplugin.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "squishplugin_global.h" - -#include <extensionsystem/iplugin.h> - -namespace Squish { -namespace Internal { - -class SquishSettings; - -class SquishPlugin : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Squish.json") -public: - SquishPlugin() = default; - ~SquishPlugin() override; - - static SquishSettings *squishSettings(); - - void initialize() override; - bool delayedInitialize() override; - ShutdownFlag aboutToShutdown() override; -}; - -} // namespace Internal -} // namespace Squish diff --git a/src/plugins/squish/squishresultmodel.cpp b/src/plugins/squish/squishresultmodel.cpp index 15bca8d91ae..a5bba80bc0b 100644 --- a/src/plugins/squish/squishresultmodel.cpp +++ b/src/plugins/squish/squishresultmodel.cpp @@ -139,9 +139,7 @@ void SquishResultFilterModel::enableAllResultTypes() void SquishResultFilterModel::toggleResultType(Result::Type type) { - if (m_enabled.contains(type)) - m_enabled.remove(type); - else + if (!m_enabled.remove(type)) m_enabled.insert(type); invalidateFilter(); } diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index f7758a844d3..bfa0a127d18 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -8,11 +8,11 @@ #include "squishtools.h" #include "squishtr.h" +#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icore.h> #include <utils/basetreeview.h> #include <utils/fileutils.h> -#include <utils/icon.h> #include <utils/layoutbuilder.h> #include <utils/progressindicator.h> #include <utils/qtcassert.h> @@ -22,23 +22,21 @@ #include <QFrame> #include <QHeaderView> #include <QPushButton> -#include <QSettings> #include <QVBoxLayout> #include <QXmlStreamReader> using namespace Utils; -namespace Squish { -namespace Internal { +namespace Squish::Internal { + +SquishSettings &settings() +{ + static SquishSettings theSettings; + return theSettings; +} SquishSettings::SquishSettings() { - setId("A.Squish.General"); - setDisplayName(Tr::tr("General")); - setCategory(Constants::SQUISH_SETTINGS_CATEGORY); - setDisplayCategory("Squish"); - setCategoryIcon(Icon({{":/squish/images/settingscategory_squish.png", - Theme::PanelTextColorDark}}, Icon::Tint)); setSettingsGroup("Squish"); setAutoApply(false); @@ -46,17 +44,23 @@ SquishSettings::SquishSettings() squishPath.setLabelText(Tr::tr("Squish path:")); squishPath.setExpectedKind(PathChooser::ExistingDirectory); squishPath.setPlaceHolderText(Tr::tr("Path to Squish installation")); - squishPath.setValidationFunction([this](FancyLineEdit *edit, QString *error) { - QTC_ASSERT(edit, return false); - if (!squishPath.pathChooser()->defaultValidationFunction()(edit, error)) - return false; - const FilePath squishServer = FilePath::fromUserInput(edit->text()) - .pathAppended(HostOsInfo::withExecutableSuffix("bin/squishserver")); - const bool valid = squishServer.isExecutableFile(); - if (!valid && error) - *error = Tr::tr("Path does not contain server executable at its default location."); - return valid; - }); + squishPath.setValidationFunction( + [this](const QString &text) -> FancyLineEdit::AsyncValidationFuture { + return squishPath.pathChooser()->defaultValidationFunction()(text).then( + [](const FancyLineEdit::AsyncValidationResult &result) + -> FancyLineEdit::AsyncValidationResult { + if (!result) + return result; + + const FilePath squishServer + = FilePath::fromUserInput(result.value()) + .pathAppended(HostOsInfo::withExecutableSuffix("bin/squishserver")); + if (!squishServer.isExecutableFile()) + return make_unexpected(Tr::tr( + "Path does not contain server executable at its default location.")); + return result.value(); + }); + }); licensePath.setSettingsKey("LicensePath"); licensePath.setLabelText(Tr::tr("License path:")); @@ -87,7 +91,8 @@ SquishSettings::SquishSettings() minimizeIDE.setToolTip(Tr::tr("Minimize IDE automatically while running or recording test cases.")); minimizeIDE.setDefaultValue(true); - connect(&local, &BoolAspect::volatileValueChanged, this, [this](bool checked) { + connect(&local, &BoolAspect::volatileValueChanged, this, [this] { + const bool checked = local.volatileValue(); serverHost.setEnabled(!checked); serverPort.setEnabled(!checked); }); @@ -106,9 +111,9 @@ SquishSettings::SquishSettings() readSettings(); } -Utils::FilePath SquishSettings::scriptsPath(Language language) const +FilePath SquishSettings::scriptsPath(Language language) const { - Utils::FilePath scripts = squishPath().pathAppended("scriptmodules"); + FilePath scripts = squishPath().pathAppended("scriptmodules"); switch (language) { case Language::Python: scripts = scripts.pathAppended("python"); break; case Language::Perl: scripts = scripts.pathAppended("perl"); break; @@ -117,9 +122,27 @@ Utils::FilePath SquishSettings::scriptsPath(Language language) const case Language::Tcl: scripts = scripts.pathAppended("tcl"); break; } - return scripts.isReadableDir() ? scripts : Utils::FilePath(); + return scripts.isReadableDir() ? scripts : FilePath(); } +class SquishSettingsPage final : public Core::IOptionsPage +{ +public: + SquishSettingsPage() + { + setId("A.Squish.General"); + setDisplayName(Tr::tr("General")); + setCategory(Constants::SQUISH_SETTINGS_CATEGORY); + setDisplayCategory("Squish"); + setCategoryIconPath(":/squish/images/settingscategory_squish.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const SquishSettingsPage settingsPage; + +// SquishServerSettings + SquishServerSettings::SquishServerSettings() { autTimeout.setLabel(Tr::tr("Maximum startup time:")); @@ -516,8 +539,8 @@ void SquishServerSettingsWidget::addAttachableAut(TreeItem *categoryItem, Squish if (dialog.exec() != QDialog::Accepted) return; - const QString executableStr = dialog.executable.value(); - const QString hostStr = dialog.host.value(); + const QString executableStr = dialog.executable(); + const QString hostStr = dialog.host(); if (executableStr.isEmpty() || hostStr.isEmpty()) return; @@ -674,5 +697,4 @@ void SquishServerSettingsDialog::configWriteFailed(QProcess::ProcessError error) SquishMessages::criticalMessage(detail); } -} // namespace Internal -} // namespace Squish +} // Squish::Internal diff --git a/src/plugins/squish/squishsettings.h b/src/plugins/squish/squishsettings.h index 0e25a4215e3..7f8fd680ea9 100644 --- a/src/plugins/squish/squishsettings.h +++ b/src/plugins/squish/squishsettings.h @@ -3,7 +3,7 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> #include <QDialog> #include <QProcess> @@ -29,7 +29,7 @@ public: Utils::BoolAspect animatedCursor{this}; }; -class SquishSettings : public Core::PagedSettings +class SquishSettings : public Utils::AspectContainer { public: SquishSettings(); @@ -45,6 +45,8 @@ public: Utils::BoolAspect minimizeIDE{this}; }; +SquishSettings &settings(); + class SquishServerSettingsDialog : public QDialog { public: diff --git a/src/plugins/squish/squishtesttreeview.cpp b/src/plugins/squish/squishtesttreeview.cpp index d01ab0440f5..cf9af9a9157 100644 --- a/src/plugins/squish/squishtesttreeview.cpp +++ b/src/plugins/squish/squishtesttreeview.cpp @@ -5,7 +5,6 @@ #include "squishconstants.h" #include "squishfilehandler.h" -#include "squishplugin.h" #include "squishsettings.h" #include "squishtesttreemodel.h" #include "suiteconf.h" @@ -163,27 +162,23 @@ void SquishTestTreeItemDelegate::setEditorData(QWidget *editor, const QModelInde static bool copyScriptTemplates(const SuiteConf &suiteConf, const Utils::FilePath &destination) { - const SquishSettings *s = SquishPlugin::squishSettings(); - QTC_ASSERT(s, return false); - // copy template files - - bool ok = destination.ensureWritableDir(); - QTC_ASSERT(ok, return false); + Utils::expected_str<void> result = destination.ensureWritableDir(); + QTC_ASSERT_EXPECTED(result, return false); const bool scripted = suiteConf.objectMapStyle() == "script"; const QString extension = suiteConf.scriptExtension(); const QString testStr = scripted ? QString("script_som_template") : QString("script_template"); - const Utils::FilePath scripts = s->scriptsPath(suiteConf.language()); + const Utils::FilePath scripts = settings().scriptsPath(suiteConf.language()); const Utils::FilePath test = scripts.pathAppended(testStr + extension); const Utils::FilePath testFile = destination.pathAppended("test" + extension); QTC_ASSERT(!testFile.exists(), return false); - const Utils::expected_str<void> result = test.copyFile(testFile); + result = test.copyFile(testFile); QTC_ASSERT_EXPECTED(result, return false); if (scripted) - ok = suiteConf.ensureObjectMapExists(); - return ok; + return suiteConf.ensureObjectMapExists(); + return true; } void SquishTestTreeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp index 21450953068..9c3b074189c 100644 --- a/src/plugins/squish/squishtools.cpp +++ b/src/plugins/squish/squishtools.cpp @@ -6,7 +6,6 @@ #include "scripthelper.h" #include "squishmessages.h" #include "squishoutputpane.h" -#include "squishplugin.h" #include "squishsettings.h" #include "squishtr.h" #include "squishxmloutputhandler.h" @@ -21,14 +20,12 @@ #include <texteditor/textmark.h> #include <utils/algorithm.h> -#include <utils/hostosinfo.h> #include <utils/qtcassert.h> #include <utils/temporaryfile.h> #include <utils/utilsicons.h> #include <QApplication> #include <QDateTime> -#include <QDir> #include <QFile> #include <QFileSystemWatcher> #include <QLoggingCategory> @@ -167,26 +164,21 @@ struct SquishToolsSettings // populate members using current settings void setup() { - const SquishSettings *squishSettings = SquishPlugin::squishSettings(); - QTC_ASSERT(squishSettings, return); - squishPath = squishSettings->squishPath(); + squishPath = settings().squishPath(); if (!squishPath.isEmpty()) { - const FilePath squishBin(squishPath.pathAppended("bin")); - serverPath = squishBin.pathAppended( - HostOsInfo::withExecutableSuffix("squishserver")).absoluteFilePath(); - runnerPath = squishBin.pathAppended( - HostOsInfo::withExecutableSuffix("squishrunner")).absoluteFilePath(); - processComPath = squishBin.pathAppended( - HostOsInfo::withExecutableSuffix("processcomm")).absoluteFilePath(); + const FilePath squishBin = squishPath.pathAppended("bin").absoluteFilePath(); + serverPath = squishBin.pathAppended("squishserver").withExecutableSuffix(); + runnerPath = squishBin.pathAppended("squishrunner").withExecutableSuffix(); + processComPath = squishBin.pathAppended("processcomm").withExecutableSuffix(); } - isLocalServer = squishSettings->local(); - serverHost = squishSettings->serverHost(); - serverPort = squishSettings->serverPort(); - verboseLog = squishSettings->verbose(); - licenseKeyPath = squishSettings->licensePath(); - minimizeIDE = squishSettings->minimizeIDE(); + isLocalServer = settings().local(); + serverHost = settings().serverHost(); + serverPort = settings().serverPort(); + verboseLog = settings().verbose(); + licenseKeyPath = settings().licensePath(); + minimizeIDE = settings().minimizeIDE(); } }; diff --git a/src/plugins/squish/squishwizardpages.cpp b/src/plugins/squish/squishwizardpages.cpp index 33567b11199..f812f0d4684 100644 --- a/src/plugins/squish/squishwizardpages.cpp +++ b/src/plugins/squish/squishwizardpages.cpp @@ -4,7 +4,6 @@ #include "squishwizardpages.h" #include "squishfilehandler.h" -#include "squishplugin.h" #include "squishsettings.h" #include "squishtools.h" #include "squishtr.h" @@ -111,8 +110,7 @@ bool SquishToolkitsPage::handleReject() void SquishToolkitsPage::delayedInitialize() { - const auto s = SquishPlugin::squishSettings(); - const Utils::FilePath server = s->squishPath().pathAppended( + const Utils::FilePath server = settings().squishPath().pathAppended( Utils::HostOsInfo::withExecutableSuffix("bin/squishserver")); if (server.isExecutableFile()) fetchServerSettings(); diff --git a/src/plugins/squish/squishxmloutputhandler.cpp b/src/plugins/squish/squishxmloutputhandler.cpp index a9f59af44a3..d41e4bccc8d 100644 --- a/src/plugins/squish/squishxmloutputhandler.cpp +++ b/src/plugins/squish/squishxmloutputhandler.cpp @@ -50,7 +50,7 @@ void SquishXmlOutputHandler::mergeResultFiles(const Utils::FilePaths &reportFile if (!resultsXML.open(QFile::WriteOnly)) { if (error) *error = Tr::tr("Could not merge results into single results.xml.\n" - "Failed to open file \"%1\"") + "Failed to open file \"%1\".") .arg(resultsXML.fileName()); return; } diff --git a/src/plugins/squish/suiteconf.cpp b/src/plugins/squish/suiteconf.cpp index 9513b566698..03a93b6a172 100644 --- a/src/plugins/squish/suiteconf.cpp +++ b/src/plugins/squish/suiteconf.cpp @@ -3,7 +3,6 @@ #include "suiteconf.h" -#include "squishplugin.h" #include "squishsettings.h" #include <coreplugin/documentmanager.h> @@ -314,7 +313,7 @@ bool SuiteConf::ensureObjectMapExists() const return objectMap.parentDir().ensureWritableDir() && objectMap.ensureExistingFile(); } - const Utils::FilePath scripts = SquishPlugin::squishSettings()->scriptsPath(language()); + const Utils::FilePath scripts = settings().scriptsPath(language()); QTC_ASSERT(scripts.exists(), return false); const QString extension = scriptExtension(); @@ -324,9 +323,9 @@ bool SuiteConf::ensureObjectMapExists() const return true; const Utils::FilePath objectMap = scripts.pathAppended("objectmap_template" + extension); - bool ok = destinationObjectMap.parentDir().ensureWritableDir(); - QTC_ASSERT(ok, return false); - const Utils::expected_str<void> result = objectMap.copyFile(destinationObjectMap); + Utils::expected_str<void> result = destinationObjectMap.parentDir().ensureWritableDir(); + QTC_ASSERT_EXPECTED(result, return false); + result = objectMap.copyFile(destinationObjectMap); QTC_ASSERT_EXPECTED(result, return false); return true; } diff --git a/src/plugins/studiowelcome/StudioWelcome.json.in b/src/plugins/studiowelcome/StudioWelcome.json.in index 94e7e2e2bea..9ee3c251721 100644 --- a/src/plugins/studiowelcome/StudioWelcome.json.in +++ b/src/plugins/studiowelcome/StudioWelcome.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"StudioWelcome\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "StudioWelcome", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Qt Design Studio Welcome Page.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Qt Design Studio Welcome Page.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index b919bf29394..7b13c59fa88 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -11,10 +11,10 @@ #include <studiosettingspage.h> #include <qmldesignerbase/qmldesignerbaseplugin.h> -#include <utils/archive.h> #include <utils/algorithm.h> #include <utils/networkaccessmanager.h> #include <utils/qtcassert.h> +#include <utils/unarchiver.h> #include <private/qqmldata_p.h> @@ -57,7 +57,7 @@ void DataModelDownloader::usageStatisticsDownloadExample(const QString &name) bool DataModelDownloader::downloadEnabled() const { - const QString lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload"; + const Key lastQDSVersionEntry = "QML/Designer/EnableWelcomePageDownload"; return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool(); } @@ -66,11 +66,11 @@ QString DataModelDownloader::targetPath() const return QmlDesigner::Paths::examplesPathSetting(); } -static Utils::FilePath tempFilePath() +static FilePath tempFilePath() { QStandardPaths::StandardLocation location = QStandardPaths::CacheLocation; - return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) + return FilePath::fromString(QStandardPaths::writableLocation(location)) .pathAppended("QtDesignStudio"); } @@ -118,17 +118,18 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) m_started = false; if (m_fileDownloader.finished()) { - const Utils::FilePath archiveFile = Utils::FilePath::fromString( - m_fileDownloader.outputFile()); - QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return ); - auto archive = new Utils::Archive(archiveFile, tempFilePath()); - QTC_ASSERT(archive->isValid(), delete archive; return ); - QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) { - QTC_CHECK(ret); - archive->deleteLater(); + const FilePath archiveFile = FilePath::fromString(m_fileDownloader.outputFile()); + const auto sourceAndCommand = Unarchiver::sourceAndCommand(archiveFile); + QTC_ASSERT(sourceAndCommand, return); + auto unarchiver = new Unarchiver; + unarchiver->setSourceAndCommand(*sourceAndCommand); + unarchiver->setDestDir(tempFilePath()); + QObject::connect(unarchiver, &Unarchiver::done, this, [this, unarchiver](bool success) { + QTC_CHECK(success); + unarchiver->deleteLater(); emit finished(); }); - archive->unarchive(); + unarchiver->start(); } }); } @@ -180,9 +181,9 @@ bool DataModelDownloader::available() const return m_available; } -Utils::FilePath DataModelDownloader::targetFolder() const +FilePath DataModelDownloader::targetFolder() const { - return Utils::FilePath::fromUserInput(tempFilePath().toString() + "/" + "dataImports"); + return FilePath::fromUserInput(tempFilePath().toString() + "/" + "dataImports"); } void DataModelDownloader::setForceDownload(bool b) diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 5a7c4077a74..e01215e3d5c 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -6,8 +6,6 @@ #include "qdsnewdialog.h" -#include <app/app_version.h> - #include <coreplugin/coreconstants.h> #include <coreplugin/dialogs/restartdialog.h> #include <coreplugin/documentmanager.h> @@ -20,7 +18,7 @@ #include "projectexplorer/target.h" #include <projectexplorer/devicesupport/idevice.h> #include <projectexplorer/jsonwizard/jsonwizardfactory.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorerconstants.h> @@ -30,7 +28,7 @@ #include <qmlprojectmanager/qmlproject.h> #include <qtsupport/baseqtversion.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <qmldesigner/components/componentcore/theme.h> #include <qmldesigner/dynamiclicensecheck.h> @@ -39,6 +37,7 @@ #include <qmljs/qmljsmodelmanagerinterface.h> +#include <utils/appinfo.h> #include <utils/checkablemessagebox.h> #include <utils/hostosinfo.h> #include <utils/icon.h> @@ -46,6 +45,8 @@ #include <utils/stringutils.h> #include <utils/theme/theme.h> +#include <nanotrace/nanotrace.h> + #include <QAbstractListModel> #include <QApplication> #include <QDesktopServices> @@ -74,8 +75,8 @@ namespace Internal { static bool useNewWelcomePage() { - QSettings *settings = Core::ICore::settings(); - const QString newWelcomePageEntry = "QML/Designer/NewWelcomePage"; //entry from qml settings + QtcSettings *settings = Core::ICore::settings(); + const Key newWelcomePageEntry = "QML/Designer/NewWelcomePage"; //entry from qml settings return settings->value(newWelcomePageEntry, false).toBool(); } @@ -156,7 +157,7 @@ public: explicit UsageStatisticPluginModel(QObject *parent = nullptr) : QObject(parent) { - m_versionString = Core::Constants::IDE_VERSION_DISPLAY; + m_versionString = Utils::appInfo().displayVersion; setupModel(); } @@ -520,20 +521,19 @@ void StudioWelcomePlugin::initialize() static bool forceDownLoad() { - const QString lastQDSVersionEntry = "QML/Designer/ForceWelcomePageDownload"; + const Key lastQDSVersionEntry = "QML/Designer/ForceWelcomePageDownload"; return Core::ICore::settings()->value(lastQDSVersionEntry, false).toBool(); } static bool showSplashScreen() { - const QString lastQDSVersionEntry = "QML/Designer/lastQDSVersion"; + const Key lastQDSVersionEntry = "QML/Designer/lastQDSVersion"; - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); const QString lastQDSVersion = settings->value(lastQDSVersionEntry).toString(); - - const QString currentVersion = Core::Constants::IDE_VERSION_DISPLAY; + const QString currentVersion = Utils::appInfo().displayVersion; if (currentVersion != lastQDSVersion) { settings->setValue(lastQDSVersionEntry, currentVersion); @@ -564,6 +564,8 @@ void StudioWelcomePlugin::extensionsInitialized() if (showSplashScreen()) { connect(Core::ICore::instance(), &Core::ICore::coreOpened, this, [this] { + NANOTRACE_SCOPE("StudioWelcome", + "StudioWelcomePlugin::extensionsInitialized::coreOpened"); Core::ModeManager::setModeStyle(Core::ModeManager::Style::Hidden); if (Utils::HostOsInfo::isMacHost()) { s_viewWindow = new QQuickView(Core::ICore::mainWindow()->windowHandle()); diff --git a/src/plugins/subversion/Subversion.json.in b/src/plugins/subversion/Subversion.json.in index 86ef4349b6d..604ba2648a9 100644 --- a/src/plugins/subversion/Subversion.json.in +++ b/src/plugins/subversion/Subversion.json.in @@ -1,29 +1,29 @@ { - \"Name\" : \"Subversion\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Subversion", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Subversion integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Category" : "Version Control", + "Description" : "Subversion integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'text/vnd.qtcreator.svn.submit\'>\", - \" <comment>Subversion submit template</comment>\", - \" <sub-class-of type=\'text/plain\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='text/vnd.qtcreator.svn.submit'>", + " <comment>Subversion submit template</comment>", + " <sub-class-of type='text/plain'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp index 4ab11b6c913..98f0cfdde90 100644 --- a/src/plugins/subversion/subversionclient.cpp +++ b/src/plugins/subversion/subversionclient.cpp @@ -89,7 +89,7 @@ Id SubversionClient::vcsEditorKind(VcsCommandTag cmd) const case VcsBaseClient::LogCommand: return Constants::SUBVERSION_LOG_EDITOR_ID; case VcsBaseClient::AnnotateCommand: return Constants::SUBVERSION_BLAME_EDITOR_ID; default: - return Id(); + return {}; } } @@ -163,7 +163,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume using namespace Tasking; - const TreeStorage<QString> diffInputStorage = inputStorage(); + const TreeStorage<QString> diffInputStorage; const auto setupDescription = [this](Process &process) { if (m_changeNumber == 0) @@ -201,7 +201,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume }; const Group root { - Storage(diffInputStorage), + Tasking::Storage(diffInputStorage), parallel, Group { finishAllAndDone, @@ -209,7 +209,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume }, Group { ProcessTask(setupDiff, onDiffDone), - postProcessTask() + postProcessTask(diffInputStorage) } }; setReloadRecipe(root); @@ -234,13 +234,12 @@ void SubversionDiffEditorController::setChangeNumber(int changeNumber) SubversionDiffEditorController *SubversionClient::findOrCreateDiffEditor(const QString &documentId, const FilePath &source, const QString &title, const FilePath &workingDirectory) { - SubversionSettings &settings = Internal::settings(); IDocument *document = DiffEditorController::findOrCreateDocument(documentId, title); auto controller = qobject_cast<SubversionDiffEditorController *>( DiffEditorController::controller(document)); if (!controller) { controller = new SubversionDiffEditorController(document); - controller->setVcsBinary(settings.binaryPath()); + controller->setVcsBinary(settings().binaryPath()); controller->setProcessEnvironment(processEnvironment()); controller->setWorkingDirectory(workingDirectory); } @@ -271,8 +270,7 @@ void SubversionClient::log(const FilePath &workingDir, bool enableAnnotationContextMenu, const std::function<void(Utils::CommandLine &)> &addAuthOptions) { - auto &settings = static_cast<SubversionSettings &>(this->settings()); - const int logCount = settings.logCount(); + const int logCount = settings().logCount(); QStringList svnExtraOptions = extraOptions; if (logCount > 0) svnExtraOptions << QLatin1String("-l") << QString::number(logCount); diff --git a/src/plugins/subversion/subversioneditor.cpp b/src/plugins/subversion/subversioneditor.cpp index cf27b606a2d..4549c6e14da 100644 --- a/src/plugins/subversion/subversioneditor.cpp +++ b/src/plugins/subversion/subversioneditor.cpp @@ -46,7 +46,7 @@ QString SubversionEditorWidget::changeUnderCursor(const QTextCursor &c) const // Any number is regarded as change number. cursor.select(QTextCursor::LineUnderCursor); if (!cursor.hasSelection()) - return QString(); + return {}; const QString change = cursor.selectedText(); const int pos = c.position() - cursor.selectionStart() + 1; // Annotation output has number, log output has revision numbers, @@ -72,7 +72,7 @@ QString SubversionEditorWidget::changeUnderCursor(const QTextCursor &c) const if (pos > start && pos <= end) return rev; } - return QString(); + return {}; } VcsBase::BaseAnnotationHighlighter *SubversionEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const @@ -85,6 +85,6 @@ QStringList SubversionEditorWidget::annotationPreviousVersions(const QString &v) bool ok; const int revision = v.toInt(&ok); if (!ok || revision < 2) - return QStringList(); + return {}; return QStringList(QString::number(revision - 1)); } diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index e584d981818..fb6fca48d4f 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -261,7 +261,6 @@ private: const QStringList m_svnDirectories; - SubversionSettings m_settings; SubversionClient *m_client = nullptr; QString m_commitMessageFileName; FilePath m_commitRepository; @@ -865,7 +864,7 @@ void SubversionPluginPrivate::vcsAnnotateHelper(const FilePath &workingDir, cons CommandLine args{settings().binaryPath(), {"annotate"}}; args << SubversionClient::AddAuthOptions(); - if (settings().spaceIgnorantAnnotation.value()) + if (settings().spaceIgnorantAnnotation()) args << "-x" << "-uw"; if (!revision.isEmpty()) args << "-r" << revision; @@ -995,7 +994,7 @@ QString SubversionPluginPrivate::monitorFile(const FilePath &repository) const return fi.absoluteFilePath(); } } - return QString(); + return {}; } QString SubversionPluginPrivate::synchronousTopic(const FilePath &repository) const diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp index 9b9225ccae7..284472938ff 100644 --- a/src/plugins/subversion/subversionsettings.cpp +++ b/src/plugins/subversion/subversionsettings.cpp @@ -5,6 +5,8 @@ #include "subversiontr.h" +#include <coreplugin/dialogs/ioptionspage.h> + #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> @@ -16,20 +18,15 @@ using namespace VcsBase; namespace Subversion::Internal { -static SubversionSettings *theSettings; - SubversionSettings &settings() { - return *theSettings; + static SubversionSettings theSettings; + return theSettings; } SubversionSettings::SubversionSettings() { - theSettings = this; - - setId(VcsBase::Constants::VCS_ID_SUBVERSION); - setDisplayName(Tr::tr("Subversion")); - setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setAutoApply(false); setSettingsGroup("Subversion"); binaryPath.setExpectedKind(PathChooser::ExistingCommand); @@ -97,6 +94,8 @@ SubversionSettings::SubversionSettings() st }; }); + + readSettings(); } bool SubversionSettings::hasAuthentication() const @@ -104,4 +103,20 @@ bool SubversionSettings::hasAuthentication() const return useAuthentication() && !userName().isEmpty(); } +// SubversionSettingsPage + +class SubversionSettingsPage final : public Core::IOptionsPage +{ +public: + SubversionSettingsPage() + { + setId(VcsBase::Constants::VCS_ID_SUBVERSION); + setDisplayName(Tr::tr("Subversion")); + setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const SubversionSettingsPage settingsPage; + } // Subversion::Internal diff --git a/src/plugins/subversion/subversionsettings.h b/src/plugins/subversion/subversionsettings.h index c5bcb8e9cf5..0c615bb0f91 100644 --- a/src/plugins/subversion/subversionsettings.h +++ b/src/plugins/subversion/subversionsettings.h @@ -7,7 +7,7 @@ namespace Subversion::Internal { -class SubversionSettings : public VcsBase::VcsBaseSettings +class SubversionSettings final : public VcsBase::VcsBaseSettings { public: SubversionSettings(); diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt index c7b64cc8ad4..fd73e5d6e67 100644 --- a/src/plugins/terminal/CMakeLists.txt +++ b/src/plugins/terminal/CMakeLists.txt @@ -1,12 +1,8 @@ add_qtc_plugin(Terminal PLUGIN_DEPENDS Core ProjectExplorer - DEPENDS libvterm + DEPENDS TerminalLib SOURCES - celliterator.cpp celliterator.h - glyphcache.cpp glyphcache.h - keys.cpp keys.h - scrollback.cpp scrollback.h shellintegration.cpp shellintegration.h shellmodel.cpp shellmodel.h shortcutmap.cpp shortcutmap.h @@ -16,9 +12,7 @@ add_qtc_plugin(Terminal terminalpane.cpp terminalpane.h terminalplugin.cpp terminalprocessimpl.cpp terminalprocessimpl.h - terminalsearch.cpp terminalsearch.h terminalsettings.cpp terminalsettings.h - terminalsurface.cpp terminalsurface.h terminaltr.h terminalwidget.cpp terminalwidget.h ) diff --git a/src/plugins/terminal/Terminal.json.in b/src/plugins/terminal/Terminal.json.in index 5640012c081..8c631dd6519 100644 --- a/src/plugins/terminal/Terminal.json.in +++ b/src/plugins/terminal/Terminal.json.in @@ -1,18 +1,18 @@ { - \"Name\" : \"Terminal\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Terminal", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Terminal window.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Terminal window.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/terminal/shellintegration.cpp b/src/plugins/terminal/shellintegration.cpp index d981dc7269e..cc5c50706ea 100644 --- a/src/plugins/terminal/shellintegration.cpp +++ b/src/plugins/terminal/shellintegration.cpp @@ -3,10 +3,13 @@ #include "shellintegration.h" +#include "terminalsettings.h" + #include <utils/environment.h> #include <utils/filepath.h> #include <utils/stringutils.h> +#include <QApplication> #include <QLoggingCategory> Q_LOGGING_CATEGORY(integrationLog, "qtc.terminal.shellintegration", QtWarningMsg) @@ -74,9 +77,17 @@ bool ShellIntegration::canIntegrate(const Utils::CommandLine &cmdLine) return false; } -void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment) +void ShellIntegration::onOsc(int cmd, std::string_view str, bool initial, bool final) { - QString d = QString::fromLocal8Bit(fragment.str, fragment.len); + if (initial) + m_oscBuffer.clear(); + + m_oscBuffer.append(str); + + if (!final) + return; + + QString d = QString::fromLocal8Bit(m_oscBuffer); const auto [command, data] = Utils::splitAtFirst(d, ';'); if (cmd == 1337) { @@ -103,6 +114,17 @@ void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment) } } +void ShellIntegration::onBell() +{ + if (settings().audibleBell.value()) + QApplication::beep(); +} + +void ShellIntegration::onTitle(const QString &title) +{ + emit titleChanged(title); +} + void ShellIntegration::prepareProcess(Utils::Process &process) { Environment env = process.environment().hasChanges() ? process.environment() @@ -124,7 +146,7 @@ void ShellIntegration::prepareProcess(Utils::Process &process) CommandLine newCmd = {cmd.executable(), {"--init-file", tmpRc.nativePath()}}; if (cmd.arguments() == "-l") - newCmd.addArg("-l"); + env.set("VSCODE_SHELL_LOGIN", "1"); cmd = newCmd; } else if (cmd.executable().baseName() == "zsh") { @@ -162,4 +184,9 @@ void ShellIntegration::prepareProcess(Utils::Process &process) process.setEnvironment(env); } +void ShellIntegration::onSetClipboard(const QByteArray &text) +{ + setClipboardAndSelection(QString::fromLocal8Bit(text)); +} + } // namespace Terminal diff --git a/src/plugins/terminal/shellintegration.h b/src/plugins/terminal/shellintegration.h index a4a813c8a65..4f1215fad94 100644 --- a/src/plugins/terminal/shellintegration.h +++ b/src/plugins/terminal/shellintegration.h @@ -7,28 +7,34 @@ #include <utils/commandline.h> #include <utils/process.h> -#include <vterm.h> +#include <solutions/terminal/surfaceintegration.h> #include <QTemporaryDir> namespace Terminal { -class ShellIntegration : public QObject +class ShellIntegration : public QObject, public TerminalSolution::SurfaceIntegration { Q_OBJECT public: static bool canIntegrate(const Utils::CommandLine &cmdLine); - void onOsc(int cmd, const VTermStringFragment &fragment); + void onOsc(int cmd, std::string_view str, bool initial, bool final) override; + void onBell() override; + void onTitle(const QString &title) override; + + void onSetClipboard(const QByteArray &text) override; void prepareProcess(Utils::Process &process); signals: void commandChanged(const Utils::CommandLine &command); void currentDirChanged(const QString &dir); + void titleChanged(const QString &title); private: QTemporaryDir m_tempDir; + QByteArray m_oscBuffer; }; } // namespace Terminal diff --git a/src/plugins/terminal/shellintegrations/shellintegration-bash.sh b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh index 7db188be08e..d9792f34baa 100755 --- a/src/plugins/terminal/shellintegrations/shellintegration-bash.sh +++ b/src/plugins/terminal/shellintegrations/shellintegration-bash.sh @@ -3,7 +3,7 @@ # Prevent the script recursing when setting up -if [[ -n "$VSCODE_SHELL_INTEGRATION" ]]; then +if [[ -n "${VSCODE_SHELL_INTEGRATION:-}" ]]; then builtin return fi @@ -32,7 +32,7 @@ if [ "$VSCODE_INJECTION" == "1" ]; then builtin unset VSCODE_SHELL_LOGIN # Apply any explicit path prefix (see #99878) - if [ -n "$VSCODE_PATH_PREFIX" ]; then + if [ -n "${VSCODE_PATH_PREFIX:-}" ]; then export PATH=$VSCODE_PATH_PREFIX$PATH builtin unset VSCODE_PATH_PREFIX fi @@ -44,6 +44,35 @@ if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then builtin return fi +# Apply EnvironmentVariableCollections if needed +if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then + IFS=':' read -ra ADDR <<< "$VSCODE_ENV_REPLACE" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo $ITEM | cut -d "=" -f 1)" + VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2)" + export $VARNAME="$VALUE" + done + builtin unset VSCODE_ENV_REPLACE +fi +if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then + IFS=':' read -ra ADDR <<< "$VSCODE_ENV_PREPEND" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo $ITEM | cut -d "=" -f 1)" + VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2)" + export $VARNAME="$VALUE${!VARNAME}" + done + builtin unset VSCODE_ENV_PREPEND +fi +if [ -n "${VSCODE_ENV_APPEND:-}" ]; then + IFS=':' read -ra ADDR <<< "$VSCODE_ENV_APPEND" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo $ITEM | cut -d "=" -f 1)" + VALUE="$(echo -e "$ITEM" | cut -d "=" -f 2)" + export $VARNAME="${!VARNAME}$VALUE" + done + builtin unset VSCODE_ENV_APPEND +fi + __vsc_get_trap() { # 'trap -p DEBUG' outputs a shell command like `trap -- '…shellcode…' DEBUG`. # The terms are quoted literals, but are not guaranteed to be on a single line. @@ -110,6 +139,10 @@ __vsc_custom_PS2="" __vsc_in_command_execution="1" __vsc_current_command="" +# It's fine this is in the global scope as it getting at it requires access to the shell environment +__vsc_nonce="$VSCODE_NONCE" +unset VSCODE_NONCE + __vsc_prompt_start() { builtin printf '\e]633;A\a' } @@ -124,7 +157,7 @@ __vsc_update_cwd() { __vsc_command_output_start() { builtin printf '\e]633;C\a' - builtin printf '\e]633;E;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")" + builtin printf '\e]633;E;%s;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")" $__vsc_nonce } __vsc_continuation_start() { @@ -241,10 +274,10 @@ __vsc_prompt_cmd() { # PROMPT_COMMAND arrays and strings seem to be handled the same (handling only the first entry of # the array?) -__vsc_original_prompt_command=$PROMPT_COMMAND +__vsc_original_prompt_command=${PROMPT_COMMAND:-} if [[ -z "${bash_preexec_imported:-}" ]]; then - if [[ -n "$__vsc_original_prompt_command" && "$__vsc_original_prompt_command" != "__vsc_prompt_cmd" ]]; then + if [[ -n "${__vsc_original_prompt_command:-}" && "${__vsc_original_prompt_command:-}" != "__vsc_prompt_cmd" ]]; then PROMPT_COMMAND=__vsc_prompt_cmd_original else PROMPT_COMMAND=__vsc_prompt_cmd diff --git a/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh index df4109131a9..119c85be9e0 100644 --- a/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh +++ b/src/plugins/terminal/shellintegrations/shellintegration-rc.zsh @@ -37,6 +37,32 @@ if [ -z "$VSCODE_SHELL_INTEGRATION" ]; then builtin return fi +# Apply EnvironmentVariableCollections if needed +if [ -n "${VSCODE_ENV_REPLACE:-}" ]; then + IFS=':' read -rA ADDR <<< "$VSCODE_ENV_REPLACE" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo ${ITEM%%=*})" + export $VARNAME="$(echo -e ${ITEM#*=})" + done + unset VSCODE_ENV_REPLACE +fi +if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then + IFS=':' read -rA ADDR <<< "$VSCODE_ENV_PREPEND" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo ${ITEM%%=*})" + export $VARNAME="$(echo -e ${ITEM#*=})${(P)VARNAME}" + done + unset VSCODE_ENV_PREPEND +fi +if [ -n "${VSCODE_ENV_APPEND:-}" ]; then + IFS=':' read -rA ADDR <<< "$VSCODE_ENV_APPEND" + for ITEM in "${ADDR[@]}"; do + VARNAME="$(echo ${ITEM%%=*})" + export $VARNAME="${(P)VARNAME}$(echo -e ${ITEM#*=})" + done + unset VSCODE_ENV_APPEND +fi + # The property (P) and command (E) codes embed values which require escaping. # Backslashes are doubled. Non-alphanumeric characters are converted to escaped hex. __vsc_escape_value() { @@ -66,6 +92,10 @@ __vsc_escape_value() { __vsc_in_command_execution="1" __vsc_current_command="" +# It's fine this is in the global scope as it getting at it requires access to the shell environment +__vsc_nonce="$VSCODE_NONCE" +unset VSCODE_NONCE + __vsc_prompt_start() { builtin printf '\e]633;A\a' } @@ -80,7 +110,7 @@ __vsc_update_cwd() { __vsc_command_output_start() { builtin printf '\e]633;C\a' - builtin printf '\e]633;E;%s\a' "${__vsc_current_command}" + builtin printf '\e]633;E;%s;%s\a' "$(__vsc_escape_value "${__vsc_current_command}")" $__vsc_nonce } __vsc_continuation_start() { @@ -149,6 +179,7 @@ __vsc_preexec() { RPROMPT="$__vsc_prior_rprompt" fi __vsc_in_command_execution="1" + # For some reason Qt Creator needs this to be $2 instead of $1 __vsc_current_command=$2 __vsc_command_output_start } diff --git a/src/plugins/terminal/shellintegrations/shellintegration.fish b/src/plugins/terminal/shellintegrations/shellintegration.fish index 7495bab3f40..21cc823c480 100644 --- a/src/plugins/terminal/shellintegrations/shellintegration.fish +++ b/src/plugins/terminal/shellintegrations/shellintegration.fish @@ -26,16 +26,48 @@ if status --is-login; and set -q VSCODE_PATH_PREFIX end set -e VSCODE_PATH_PREFIX +# Apply EnvironmentVariableCollections if needed +if test -n "$VSCODE_ENV_REPLACE" + set ITEMS (string split : $VSCODE_ENV_REPLACE) + for B in $ITEMS + set split (string split = $B) + set -gx "$split[1]" (echo -e "$split[2]") + end + set -e VSCODE_ENV_REPLACE +end +if test -n "$VSCODE_ENV_PREPEND" + set ITEMS (string split : $VSCODE_ENV_PREPEND) + for B in $ITEMS + set split (string split = $B) + set -gx "$split[1]" (echo -e "$split[2]")"$$split[1]" # avoid -p as it adds a space + end + set -e VSCODE_ENV_PREPEND +end +if test -n "$VSCODE_ENV_APPEND" + set ITEMS (string split : $VSCODE_ENV_APPEND) + for B in $ITEMS + set split (string split = $B) + set -gx "$split[1]" "$$split[1]"(echo -e "$split[2]") # avoid -a as it adds a space + end + set -e VSCODE_ENV_APPEND +end + +# Handle the shell integration nonce +if set -q VSCODE_NONCE + set -l __vsc_nonce $VSCODE_NONCE + set -e VSCODE_NONCE +end + # Helper function function __vsc_esc -d "Emit escape sequences for VS Code shell integration" - builtin printf "\e]633;%s\a" (string join ";" $argv) + builtin printf "\e]633;%s\a" (string join ";" -- $argv) end # Sent right before executing an interactive command. # Marks the beginning of command output. function __vsc_cmd_executed --on-event fish_preexec __vsc_esc C - __vsc_esc E (__vsc_escape_value "$argv") + __vsc_esc E (__vsc_escape_value "$argv") $__vsc_nonce # Creates a marker to indicate a command was run. set --global _vsc_has_cmd @@ -64,6 +96,31 @@ function __vsc_cmd_clear --on-event fish_cancel __vsc_esc D end +# Preserve the user's existing prompt, to wrap in our escape sequences. +function __preserve_fish_prompt --on-event fish_prompt + if functions --query fish_prompt + if functions --query __vsc_fish_prompt + # Erase the fallback so it can be set to the user's prompt + functions --erase __vsc_fish_prompt + end + functions --copy fish_prompt __vsc_fish_prompt + functions --erase __preserve_fish_prompt + # Now __vsc_fish_prompt is guaranteed to be defined + __init_vscode_shell_integration + else + if functions --query __vsc_fish_prompt + functions --erase __preserve_fish_prompt + __init_vscode_shell_integration + else + # There is no fish_prompt set, so stick with the default + # Now __vsc_fish_prompt is guaranteed to be defined + function __vsc_fish_prompt + echo -n (whoami)@(prompt_hostname) (prompt_pwd) '~> ' + end + end + end +end + # Sent whenever a new fish prompt is about to be displayed. # Updates the current working directory. function __vsc_update_cwd --on-event fish_prompt @@ -94,29 +151,29 @@ function __vsc_fish_has_mode_prompt -d "Returns true if fish_mode_prompt is defi functions fish_mode_prompt | string match -rvq '^ *(#|function |end$|$)' end -# Preserve the user's existing prompt, to wrap in our escape sequences. -functions --copy fish_prompt __vsc_fish_prompt - # Preserve and wrap fish_mode_prompt (which appears to the left of the regular # prompt), but only if it's not defined as an empty function (which is the # officially documented way to disable that feature). -if __vsc_fish_has_mode_prompt - functions --copy fish_mode_prompt __vsc_fish_mode_prompt +function __init_vscode_shell_integration + if __vsc_fish_has_mode_prompt + functions --copy fish_mode_prompt __vsc_fish_mode_prompt - function fish_mode_prompt - __vsc_fish_prompt_start - __vsc_fish_mode_prompt - end + function fish_mode_prompt + __vsc_fish_prompt_start + __vsc_fish_mode_prompt + __vsc_fish_cmd_start + end - function fish_prompt - __vsc_fish_prompt - __vsc_fish_cmd_start - end -else - # No fish_mode_prompt, so put everything in fish_prompt. - function fish_prompt - __vsc_fish_prompt_start - __vsc_fish_prompt - __vsc_fish_cmd_start + function fish_prompt + __vsc_fish_prompt + end + else + # No fish_mode_prompt, so put everything in fish_prompt. + function fish_prompt + __vsc_fish_prompt_start + __vsc_fish_prompt + __vsc_fish_cmd_start + end end end +__preserve_fish_prompt diff --git a/src/plugins/terminal/shellintegrations/shellintegration.ps1 b/src/plugins/terminal/shellintegrations/shellintegration.ps1 index 4fd978a8844..e277724b0a2 100644 --- a/src/plugins/terminal/shellintegrations/shellintegration.ps1 +++ b/src/plugins/terminal/shellintegrations/shellintegration.ps1 @@ -15,6 +15,35 @@ $Global:__VSCodeOriginalPrompt = $function:Prompt $Global:__LastHistoryId = -1 +# Store the nonce in script scope and unset the global +$Nonce = $env:VSCODE_NONCE +$env:VSCODE_NONCE = $null + +if ($env:VSCODE_ENV_REPLACE) { + $Split = $env:VSCODE_ENV_REPLACE.Split(":") + foreach ($Item in $Split) { + $Inner = $Item.Split('=') + [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':')) + } + $env:VSCODE_ENV_REPLACE = $null +} +if ($env:VSCODE_ENV_PREPEND) { + $Split = $env:VSCODE_ENV_PREPEND.Split(":") + foreach ($Item in $Split) { + $Inner = $Item.Split('=') + [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':') + [Environment]::GetEnvironmentVariable($Inner[0])) + } + $env:VSCODE_ENV_PREPEND = $null +} +if ($env:VSCODE_ENV_APPEND) { + $Split = $env:VSCODE_ENV_APPEND.Split(":") + foreach ($Item in $Split) { + $Inner = $Item.Split('=') + [Environment]::SetEnvironmentVariable($Inner[0], [Environment]::GetEnvironmentVariable($Inner[0]) + $Inner[1].Replace('\x3a', ':')) + } + $env:VSCODE_ENV_APPEND = $null +} + function Global:__VSCode-Escape-Value([string]$value) { # NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`. # Replace any non-alphanumeric characters. @@ -40,7 +69,7 @@ function Global:Prompt() { $Result += "$([char]0x1b)]633;D`a" } else { # Command finished command line - # OSC 633 ; A ; <CommandLine?> ST + # OSC 633 ; E ; <CommandLine?> ; <Nonce?> ST $Result = "$([char]0x1b)]633;E;" # Sanitize the command line to ensure it can get transferred to the terminal and can be parsed # correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter @@ -51,6 +80,7 @@ function Global:Prompt() { $CommandLine = "" } $Result += $(__VSCode-Escape-Value $CommandLine) + $Result += ";$Nonce" $Result += "`a" # Command finished exit code # OSC 633 ; D [; <ExitCode>] ST @@ -88,7 +118,12 @@ if (Get-Module -Name PSReadLine) { } # Set IsWindows property -[Console]::Write("$([char]0x1b)]633;P;IsWindows=$($IsWindows)`a") +if ($PSVersionTable.PSVersion -lt "6.0") { + # Windows PowerShell is only available on Windows + [Console]::Write("$([char]0x1b)]633;P;IsWindows=$true`a") +} else { + [Console]::Write("$([char]0x1b)]633;P;IsWindows=$IsWindows`a") +} # Set always on key handlers which map to default VS Code keybindings function Set-MappedKeyHandler { diff --git a/src/plugins/terminal/shellmodel.cpp b/src/plugins/terminal/shellmodel.cpp index 0b13b0f8863..a9667c95e13 100644 --- a/src/plugins/terminal/shellmodel.cpp +++ b/src/plugins/terminal/shellmodel.cpp @@ -3,6 +3,9 @@ #include "shellmodel.h" +#include <projectexplorer/devicesupport/devicemanager.h> +#include <projectexplorer/projectexplorerconstants.h> + #include <utils/algorithm.h> #include <utils/environment.h> #include <utils/filepath.h> @@ -16,11 +19,13 @@ using namespace Utils; struct ShellItemBuilder { - explicit ShellItemBuilder(const CommandLine &value) + explicit ShellItemBuilder(const QFileIconProvider &iconProvider, const CommandLine &value) : + iconProvider(iconProvider) { m_item.openParameters.shellCommand = value; } - explicit ShellItemBuilder(const FilePath &value) + explicit ShellItemBuilder(const QFileIconProvider &iconProvider, const FilePath &value) : + iconProvider(iconProvider) { m_item.openParameters.shellCommand = CommandLine(value); } @@ -31,8 +36,7 @@ struct ShellItemBuilder } ShellItemBuilder &icon(const FilePath &value) { - static QFileIconProvider iconProvider; - m_item.icon = iconProvider.icon(value.toFileInfo()); + m_item.openParameters.icon = iconProvider.icon(value.toFileInfo()); return *this; } @@ -41,13 +45,14 @@ struct ShellItemBuilder { if (m_item.name.isEmpty()) m_item.name = m_item.openParameters.shellCommand->executable().toUserOutput(); - if (m_item.icon.isNull()) + if (m_item.openParameters.icon.isNull()) icon(m_item.openParameters.shellCommand->executable()); return m_item; } private: ShellModelItem m_item; + const QFileIconProvider &iconProvider; }; static QSet<FilePath> msysPaths() @@ -74,31 +79,32 @@ struct ShellModelPrivate ShellModelPrivate::ShellModelPrivate() { + QFileIconProvider iconProvider; if (Utils::HostOsInfo::isWindowsHost()) { const FilePath comspec = FilePath::fromUserInput(qtcEnvironmentVariable("COMSPEC")); - localShells << ShellItemBuilder(comspec); + localShells << ShellItemBuilder(iconProvider, comspec); if (comspec.fileName() != "cmd.exe") { FilePath cmd = FilePath::fromUserInput(QStandardPaths::findExecutable("cmd.exe")); - localShells << ShellItemBuilder(cmd); + localShells << ShellItemBuilder(iconProvider, cmd); } const FilePath powershell = FilePath::fromUserInput( QStandardPaths::findExecutable("powershell.exe")); if (powershell.exists()) - localShells << ShellItemBuilder(powershell); + localShells << ShellItemBuilder(iconProvider, powershell); const FilePath sys_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("bash.exe")); if (sys_bash.exists()) - localShells << ShellItemBuilder({sys_bash, {"--login"}}); + localShells << ShellItemBuilder(iconProvider, {sys_bash, {"--login"}}); const FilePath git_exe = FilePath::fromUserInput(QStandardPaths::findExecutable("git.exe")); if (git_exe.exists()) { FilePath git_bash = git_exe.parentDir().parentDir().pathAppended("bin/bash.exe"); if (git_bash.exists()) { - localShells << ShellItemBuilder({git_bash, {"--login"}}) + localShells << ShellItemBuilder(iconProvider, {git_bash, {"--login"}}) .icon(git_bash.parentDir().parentDir().pathAppended("git-bash.exe")); } } @@ -111,7 +117,7 @@ ShellModelPrivate::ShellModelPrivate() QDirIterator it(type.path().replace(".ico", "/bin"), QDir::Files); if (!it.hasNext()) continue; - localShells << ShellItemBuilder( + localShells << ShellItemBuilder(iconProvider, {msys2_cmd, msys2_args + QStringList{"-" + type.baseName()}}) .icon(type) .name(type.toUserOutput().replace(".ico", ".exe")); @@ -135,11 +141,10 @@ ShellModelPrivate::ShellModelPrivate() }); // ... and filter out non-existing shells. - localShells = Utils::transform( - Utils::filtered(shells, [](const FilePath &shell) {return shell.exists(); }), - [](const FilePath &shell) { - return ShellItemBuilder(shell).item(); - }); + localShells = Utils::transform(Utils::filtered(shells, &FilePath::exists), + [&iconProvider](const FilePath &shell) { + return ShellItemBuilder(iconProvider, shell).item(); + }); } } @@ -158,14 +163,15 @@ QList<ShellModelItem> ShellModel::local() const QList<ShellModelItem> ShellModel::remote() const { - const auto deviceCmds = Utils::Terminal::Hooks::instance().getTerminalCommandsForDevicesHook()(); + QList<ShellModelItem> result; - const QList<ShellModelItem> deviceItems = Utils::transform( - deviceCmds, [](const Utils::Terminal::NameAndCommandLine &item) -> ShellModelItem { - return ShellModelItem{item.name, {}, {item.commandLine, std::nullopt, std::nullopt}}; + ProjectExplorer::DeviceManager::instance()->forEachDevice( + [&result](const QSharedPointer<const ProjectExplorer::IDevice> &device) { + if (device->type() != ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) + result << ShellModelItem{device->displayName(), {{device->rootPath(), {}}}}; }); - return deviceItems; + return result; } } // namespace Terminal::Internal diff --git a/src/plugins/terminal/shellmodel.h b/src/plugins/terminal/shellmodel.h index 272f3fcd394..8b1b1c8e656 100644 --- a/src/plugins/terminal/shellmodel.h +++ b/src/plugins/terminal/shellmodel.h @@ -17,7 +17,6 @@ struct ShellModelPrivate; struct ShellModelItem { QString name; - QIcon icon; Utils::Terminal::OpenTerminalParameters openParameters; }; diff --git a/src/plugins/terminal/shortcutmap.cpp b/src/plugins/terminal/shortcutmap.cpp index 72023e808a0..4e17f0c35e4 100644 --- a/src/plugins/terminal/shortcutmap.cpp +++ b/src/plugins/terminal/shortcutmap.cpp @@ -9,6 +9,7 @@ #include <algorithm> +#include <QAction> #include <QGuiApplication> #include <QKeyEvent> #include <QLoggingCategory> @@ -216,8 +217,6 @@ QKeySequence::SequenceMatch ShortcutMap::state() */ bool ShortcutMap::tryShortcut(QKeyEvent *e) { - Q_D(ShortcutMap); - if (e->key() == Qt::Key_unknown) return false; @@ -234,14 +233,8 @@ bool ShortcutMap::tryShortcut(QKeyEvent *e) // but we need to say we did, so that we get the follow-up key-presses. return true; case QKeySequence::ExactMatch: { - // Save number of identical matches before dispatching - // to keep ShortcutMap and tryShortcut reentrant. - const int identicalMatches = d->identicals.size(); resetState(); - dispatchEvent(e); - // If there are no identicals we've only found disabled shortcuts, and - // shouldn't say that we handled the event. - return identicalMatches > 0; + return dispatchEvent(e); } } #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) @@ -510,11 +503,11 @@ QList<const ShortcutEntry *> ShortcutMap::matches() const /*! \internal Dispatches QShortcutEvents to widgets who grabbed the matched key sequence. */ -void ShortcutMap::dispatchEvent(QKeyEvent *e) +bool ShortcutMap::dispatchEvent(QKeyEvent *e) { Q_D(ShortcutMap); if (!d->identicals.size()) - return; + return false; const QKeySequence &curKey = d->identicals.at(0)->keyseq; if (d->prevSequence != curKey) { @@ -541,7 +534,7 @@ void ShortcutMap::dispatchEvent(QKeyEvent *e) // Don't trigger shortcut if we're autorepeating and the shortcut is // grabbed with not accepting autorepeats. if (!next || (e->isAutoRepeat() && !next->autorepeat)) - return; + return false; // Dispatch next enabled if (lcShortcutMap().isDebugEnabled()) { if (ambiguousShortcuts.size() > 1) { @@ -557,8 +550,20 @@ void ShortcutMap::dispatchEvent(QKeyEvent *e) << "\", " << next->id << ", " << static_cast<bool>(enabledShortcuts > 1) << ") to object(" << next->owner << ')'; } - QShortcutEvent se(next->keyseq, next->id, enabledShortcuts > 1); - QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se); + + if (auto action = qobject_cast<QAction *>(next->owner)) { + // We call the action here ourselves instead of relying on sending a ShortCut event, + // as the action will try to match the shortcut id to the global shortcutmap. + // This triggers an annoying Q_ASSERT when linking against a debug Qt. Calling trigger + // directly circumvents this. + action->trigger(); + return action->isEnabled(); + } else { + QShortcutEvent se(next->keyseq, next->id, enabledShortcuts > 1); + QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se); + } + + return true; } } // namespace Terminal::Internal diff --git a/src/plugins/terminal/shortcutmap.h b/src/plugins/terminal/shortcutmap.h index 956c3b5c9e7..3433a4d172d 100644 --- a/src/plugins/terminal/shortcutmap.h +++ b/src/plugins/terminal/shortcutmap.h @@ -40,7 +40,7 @@ public: private: void resetState(); QKeySequence::SequenceMatch nextState(QKeyEvent *e); - void dispatchEvent(QKeyEvent *e); + bool dispatchEvent(QKeyEvent *e); QKeySequence::SequenceMatch find(QKeyEvent *e, int ignoredModifiers = 0); QKeySequence::SequenceMatch matches(const QKeySequence &seq1, const QKeySequence &seq2) const; diff --git a/src/plugins/terminal/terminal.qbs b/src/plugins/terminal/terminal.qbs index f9b3409bebd..4cf3bb7a1b7 100644 --- a/src/plugins/terminal/terminal.qbs +++ b/src/plugins/terminal/terminal.qbs @@ -5,18 +5,9 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "ProjectExplorer" } - Depends { name: "vterm" } - Depends { name: "ptyqt" } + Depends { name: "TerminalLib" } files: [ - "celliterator.cpp", - "celliterator.h", - "glyphcache.cpp", - "glyphcache.h", - "keys.cpp", - "keys.h", - "scrollback.cpp", - "scrollback.h", "shellmodel.cpp", "shellmodel.h", "shellintegration.cpp", @@ -31,12 +22,8 @@ QtcPlugin { "terminalplugin.cpp", "terminalprocessimpl.cpp", "terminalprocessimpl.h", - "terminalsearch.cpp", - "terminalsearch.h", "terminalsettings.cpp", "terminalsettings.h", - "terminalsurface.cpp", - "terminalsurface.h", "terminaltr.h", "terminalwidget.cpp", "terminalwidget.h", diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp index d30d7f79357..a11284ef1bd 100644 --- a/src/plugins/terminal/terminalpane.cpp +++ b/src/plugins/terminal/terminalpane.cpp @@ -25,6 +25,8 @@ #include <utils/terminalhooks.h> #include <utils/utilsicons.h> +#include <QFileIconProvider> +#include <QGuiApplication> #include <QMenu> #include <QStandardPaths> #include <QToolButton> @@ -39,6 +41,10 @@ TerminalPane::TerminalPane(QObject *parent) : IOutputPane(parent) , m_selfContext("Terminal.Pane") { + setId("Terminal"); + setDisplayName(Tr::tr("Terminal")); + setPriorityInStatusBar(20); + setupContext(m_selfContext, &m_tabWidget); setZoomButtonsEnabled(true); @@ -53,9 +59,6 @@ TerminalPane::TerminalPane(QObject *parent) initActions(); - m_lockKeyboardButton = new QToolButton(); - m_lockKeyboardButton->setDefaultAction(&lockKeyboard); - m_newTerminalButton = new QToolButton(); m_newTerminalButton->setDefaultAction(&newTerminal); @@ -71,18 +74,21 @@ TerminalPane::TerminalPane(QObject *parent) }); const auto updateEscButton = [this] { - m_escSettingButton->setChecked(TerminalSettings::instance().sendEscapeToTerminal()); + m_escSettingButton->setChecked(settings().sendEscapeToTerminal()); static const QString escKey = QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText); static const QString shiftEsc = QKeySequence( QKeyCombination(Qt::ShiftModifier, Qt::Key_Escape)) .toString(QKeySequence::NativeText); - if (TerminalSettings::instance().sendEscapeToTerminal.value()) { + if (settings().sendEscapeToTerminal()) { m_escSettingButton->setText(escKey); - m_escSettingButton->setToolTip(Tr::tr("Sends Esc to terminal instead of Qt Creator.")); + //: %1 is the application name (Qt Creator) + m_escSettingButton->setToolTip(Tr::tr("Sends Esc to terminal instead of %1.") + .arg(QGuiApplication::applicationDisplayName())); } else { m_escSettingButton->setText(shiftEsc); - m_escSettingButton->setToolTip(Tr::tr("Press %1 to send Esc to terminal.").arg(shiftEsc)); + m_escSettingButton->setToolTip( + Tr::tr("Press %1 to send Esc to terminal.").arg(shiftEsc)); } }; @@ -92,12 +98,39 @@ TerminalPane::TerminalPane(QObject *parent) updateEscButton(); connect(m_escSettingButton, &QToolButton::toggled, this, [this, updateEscButton] { - TerminalSettings::instance().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked()); - TerminalSettings::instance().writeSettings(ICore::settings()); + settings().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked()); updateEscButton(); }); - connect(&TerminalSettings::instance(), &TerminalSettings::applied, this, updateEscButton); + connect(&settings(), &TerminalSettings::applied, this, updateEscButton); + + const auto updateLockButton = [this] { + m_lockKeyboardButton->setChecked(settings().lockKeyboard()); + if (settings().lockKeyboard()) { + m_lockKeyboardButton->setIcon(LOCK_KEYBOARD_ICON.icon()); + m_lockKeyboardButton->setToolTip( + //: %1 is the application name (Qt Creator) + Tr::tr("%1 shortcuts are blocked when focus is inside the terminal.") + .arg(QGuiApplication::applicationDisplayName())); + } else { + m_lockKeyboardButton->setIcon(UNLOCK_KEYBOARD_ICON.icon()); + //: %1 is the application name (Qt Creator) + m_lockKeyboardButton->setToolTip(Tr::tr("%1 shortcuts take precedence.") + .arg(QGuiApplication::applicationDisplayName())); + } + }; + + m_lockKeyboardButton = new QToolButton(); + m_lockKeyboardButton->setCheckable(true); + + updateLockButton(); + + connect(m_lockKeyboardButton, &QToolButton::toggled, this, [this, updateLockButton] { + settings().lockKeyboard.setValue(m_lockKeyboardButton->isChecked()); + updateLockButton(); + }); + + connect(&settings(), &TerminalSettings::applied, this, updateLockButton); } TerminalPane::~TerminalPane() {} @@ -134,7 +167,18 @@ void TerminalPane::openTerminal(const OpenTerminalParameters ¶meters) terminalWidget->unlockGlobalAction(NEXTTERMINAL); terminalWidget->unlockGlobalAction(PREVTERMINAL); - m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminalWidget, Tr::tr("Terminal"))); + QIcon icon; + if (HostOsInfo::isWindowsHost()) { + icon = parametersCopy.icon; + if (icon.isNull()) { + QFileIconProvider iconProvider; + const FilePath command = parametersCopy.shellCommand + ? parametersCopy.shellCommand->executable() + : settings().shell(); + icon = iconProvider.icon(command.toFileInfo()); + } + } + m_tabWidget.setCurrentIndex(m_tabWidget.addTab(terminalWidget, icon, Tr::tr("Terminal"))); setupTerminalWidget(terminalWidget); if (!m_isVisible) @@ -219,23 +263,15 @@ void TerminalPane::setupTerminalWidget(TerminalWidget *terminal) if (!terminal) return; - const auto setTabText = [this, terminal]() { + const auto setTabText = [this, terminal] { const int index = m_tabWidget.indexOf(terminal); - const FilePath cwd = terminal->cwd(); - - const QString exe = terminal->currentCommand().isEmpty() - ? terminal->shellName() - : terminal->currentCommand().executable().fileName(); - - if (cwd.isEmpty()) - m_tabWidget.setTabText(index, exe); - else - m_tabWidget.setTabText(index, exe + " - " + cwd.fileName()); + m_tabWidget.setTabText(index, terminal->title()); }; connect(terminal, &TerminalWidget::started, this, setTabText); connect(terminal, &TerminalWidget::cwdChanged, this, setTabText); connect(terminal, &TerminalWidget::commandChanged, this, setTabText); + connect(terminal, &TerminalWidget::titleChanged, this, setTabText); if (!terminal->shellName().isEmpty()) setTabText(); @@ -245,23 +281,6 @@ void TerminalPane::initActions() { createShellMenu(); - lockKeyboard.setCheckable(true); - lockKeyboard.setChecked(TerminalSettings::instance().lockKeyboard()); - - auto updateLockKeyboard = [this](bool locked) { - TerminalSettings::instance().lockKeyboard.setValue(locked); - if (locked) { - lockKeyboard.setIcon(LOCK_KEYBOARD_ICON.icon()); - lockKeyboard.setToolTip(Tr::tr("Sends keyboard shortcuts to Terminal.")); - } else { - lockKeyboard.setIcon(UNLOCK_KEYBOARD_ICON.icon()); - lockKeyboard.setToolTip(Tr::tr("Sends keyboard shortcuts to Qt Creator.")); - } - }; - - updateLockKeyboard(TerminalSettings::instance().lockKeyboard()); - connect(&lockKeyboard, &QAction::toggled, this, updateLockKeyboard); - newTerminal.setText(Tr::tr("New Terminal")); newTerminal.setIcon(NEW_TERMINAL_ICON.icon()); newTerminal.setToolTip(Tr::tr("Create a new Terminal.")); @@ -314,7 +333,7 @@ void TerminalPane::createShellMenu() const auto addItems = [this](const QList<Internal::ShellModelItem> &items) { for (const Internal::ShellModelItem &item : items) { - QAction *action = new QAction(item.icon, item.name, &m_shellMenu); + QAction *action = new QAction(item.openParameters.icon, item.name, &m_shellMenu); connect(action, &QAction::triggered, action, [item, this]() { openTerminal(item.openParameters); @@ -340,16 +359,6 @@ QList<QWidget *> TerminalPane::toolBarWidgets() const return widgets << m_openSettingsButton << m_lockKeyboardButton << m_escSettingButton; } -QString TerminalPane::displayName() const -{ - return Tr::tr("Terminal"); -} - -int TerminalPane::priorityInStatusBar() const -{ - return 50; -} - void TerminalPane::clearContents() { if (const auto t = currentTerminal()) diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h index 8a24e1cf302..c21d1525120 100644 --- a/src/plugins/terminal/terminalpane.h +++ b/src/plugins/terminal/terminalpane.h @@ -27,8 +27,6 @@ public: QWidget *outputWidget(QWidget *parent) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void visibilityChanged(bool visible) override; void setFocus() override; @@ -68,7 +66,6 @@ private: QAction nextTerminal; QAction prevTerminal; QAction closeTerminal; - QAction lockKeyboard; QMenu m_shellMenu; diff --git a/src/plugins/terminal/terminalplugin.cpp b/src/plugins/terminal/terminalplugin.cpp index 38b58b1722f..7940fb712b9 100644 --- a/src/plugins/terminal/terminalplugin.cpp +++ b/src/plugins/terminal/terminalplugin.cpp @@ -39,8 +39,6 @@ public: m_terminalPane = nullptr; } - void initialize() final { addManaged<TerminalSettings>(); } - void extensionsInitialized() final { m_terminalPane = new TerminalPane; @@ -61,8 +59,8 @@ public: static bool isEnabled = false; auto settingsChanged = [enable, disable] { - if (isEnabled != TerminalSettings::instance().enableTerminal()) { - isEnabled = TerminalSettings::instance().enableTerminal(); + if (isEnabled != settings().enableTerminal()) { + isEnabled = settings().enableTerminal(); if (isEnabled) enable(); else @@ -70,7 +68,7 @@ public: } }; - QObject::connect(&TerminalSettings::instance(), + QObject::connect(&settings(), &Utils::AspectContainer::applied, this, settingsChanged); diff --git a/src/plugins/terminal/terminalprocessimpl.cpp b/src/plugins/terminal/terminalprocessimpl.cpp index b5d116ec48b..f26383dfade 100644 --- a/src/plugins/terminal/terminalprocessimpl.cpp +++ b/src/plugins/terminal/terminalprocessimpl.cpp @@ -40,11 +40,9 @@ public: TerminalWidget *terminal = m_terminalPane->stoppedTerminalWithId(id); - const OpenTerminalParameters openParameters{setup.m_commandLine, - std::nullopt, - std::nullopt, - ExitBehavior::Keep, - id}; + OpenTerminalParameters openParameters{setup.m_commandLine}; + openParameters.m_exitBehavior = ExitBehavior::Keep; + openParameters.identifier = id; if (!terminal) { terminal = new TerminalWidget(nullptr, openParameters); diff --git a/src/plugins/terminal/terminalsearch.h b/src/plugins/terminal/terminalsearch.h deleted file mode 100644 index 3daa05c04d2..00000000000 --- a/src/plugins/terminal/terminalsearch.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "terminalsurface.h" - -#include <coreplugin/find/ifindsupport.h> -#include <coreplugin/find/textfindconstants.h> - -#include <QTimer> - -namespace Terminal { - -struct SearchHit -{ - int start{-1}; - int end{-1}; - - bool operator!=(const SearchHit &other) const - { - return start != other.start || end != other.end; - } - bool operator==(const SearchHit &other) const { return !operator!=(other); } -}; - -struct SearchHitWithText : SearchHit -{ - QString text; -}; - -class TerminalSearch : public Core::IFindSupport -{ - Q_OBJECT -public: - TerminalSearch(Internal::TerminalSurface *surface); - - void setCurrentSelection(std::optional<SearchHitWithText> selection); - void setSearchString(const QString &searchString, Core::FindFlags findFlags); - void nextHit(); - void previousHit(); - - const QList<SearchHit> &hits() const { return m_hits; } - SearchHit currentHit() const - { - return m_currentHit >= 0 ? m_hits.at(m_currentHit) : SearchHit{}; - } - -public: - bool supportsReplace() const override { return false; } - Core::FindFlags supportedFindFlags() const override; - void resetIncrementalSearch() override; - void clearHighlights() override; - QString currentFindString() const override; - QString completedFindString() const override; - Result findIncremental(const QString &txt, Core::FindFlags findFlags) override; - Result findStep(const QString &txt, Core::FindFlags findFlags) override; - - void highlightAll(const QString &, Core::FindFlags) override; - -signals: - void hitsChanged(); - void currentHitChanged(); - -protected: - void updateHits(); - void debouncedUpdateHits(); - QList<SearchHit> search(); - QList<SearchHit> searchRegex(); - -private: - std::optional<SearchHitWithText> m_currentSelection; - QString m_currentSearchString; - Core::FindFlags m_findFlags; - Internal::TerminalSurface *m_surface; - - int m_currentHit{-1}; - QList<SearchHit> m_hits; - QTimer m_debounceTimer; -}; - -} // namespace Terminal diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp index bb90e24bec8..b6bfe3c37c7 100644 --- a/src/plugins/terminal/terminalsettings.cpp +++ b/src/plugins/terminal/terminalsettings.cpp @@ -6,6 +6,7 @@ #include "terminaltr.h" #include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> #include <utils/dropsupport.h> #include <utils/environment.h> @@ -14,17 +15,21 @@ #include <utils/hostosinfo.h> #include <utils/layoutbuilder.h> #include <utils/pathchooser.h> +#include <utils/stringutils.h> #include <utils/theme/theme.h> #include <QFontComboBox> #include <QJsonDocument> #include <QJsonObject> +#include <QLoggingCategory> #include <QMessageBox> #include <QPushButton> #include <QRegularExpression> #include <QTemporaryFile> #include <QXmlStreamReader> +Q_LOGGING_CATEGORY(schemeLog, "qtc.terminal.scheme", QtWarningMsg) + using namespace Utils; namespace Terminal { @@ -54,23 +59,23 @@ static QString defaultShell() if (HostOsInfo::isWindowsHost()) return qtcEnvironmentVariable("COMSPEC"); - QString defaultShell = qtcEnvironmentVariable("SHELL"); - if (FilePath::fromUserInput(defaultShell).isExecutableFile()) - return defaultShell; + FilePath defaultShell = FilePath::fromUserInput(qtcEnvironmentVariable("SHELL")); + if (defaultShell.isExecutableFile()) + return defaultShell.toUserOutput(); - Utils::FilePath shPath = Utils::Environment::systemEnvironment().searchInPath("sh"); - return shPath.nativePath(); + return Environment::systemEnvironment().searchInPath("sh").toUserOutput(); } void setupColor(TerminalSettings *settings, ColorAspect &color, const QString &label, - const QColor &defaultColor) + const QColor &defaultColor, + const QString &humanReadableName = {}) { - color.setSettingsKey(label); + color.setSettingsKey(keyFromString(label)); color.setDefaultValue(defaultColor); - color.setToolTip(Tr::tr("The color used for %1.").arg(label)); - + color.setToolTip(Tr::tr("The color used for %1.") + .arg(humanReadableName.isEmpty() ? label : humanReadableName)); settings->registerAspect(&color); } @@ -91,13 +96,13 @@ static expected_str<void> loadXdefaults(const FilePath &path) const QString colorName = match.captured(1); const QColor color(match.captured(2)); if (colorName == "foreground") { - TerminalSettings::instance().foregroundColor.setVolatileValue(color); + settings().foregroundColor.setVolatileValue(color); } else if (colorName == "background") { - TerminalSettings::instance().backgroundColor.setVolatileValue(color); + settings().backgroundColor.setVolatileValue(color); } else { const int colorIndex = colorName.mid(5).toInt(); if (colorIndex >= 0 && colorIndex < 16) - TerminalSettings::instance().colors[colorIndex].setVolatileValue(color); + settings().colors[colorIndex].setVolatileValue(color); } } } @@ -153,14 +158,14 @@ static expected_str<void> loadItermColors(const FilePath &path) const auto c = colorName.mid(5, 2); const int colorIndex = c.toInt(); if (colorIndex >= 0 && colorIndex < 16) - TerminalSettings::instance().colors[colorIndex].setVolatileValue( + settings().colors[colorIndex].setVolatileValue( color); } else if (colorName == "Foreground Color") { - TerminalSettings::instance().foregroundColor.setVolatileValue(color); + settings().foregroundColor.setVolatileValue(color); } else if (colorName == "Background Color") { - TerminalSettings::instance().backgroundColor.setVolatileValue(color); + settings().backgroundColor.setVolatileValue(color); } else if (colorName == "Selection Color") { - TerminalSettings::instance().selectionColor.setVolatileValue(color); + settings().selectionColor.setVolatileValue(color); } } } @@ -175,6 +180,72 @@ static expected_str<void> loadItermColors(const FilePath &path) return {}; } +static expected_str<void> loadWindowsTerminalColors(const FilePath &path) +{ + const expected_str<QByteArray> readResult = path.fileContents(); + if (!readResult) + return make_unexpected(readResult.error()); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(*readResult, &error); + if (error.error != QJsonParseError::NoError) + return make_unexpected(Tr::tr("JSON parsing error: \"%1\", at offset: %2") + .arg(error.errorString()) + .arg(error.offset)); + + const QJsonObject colors = doc.object(); + + // clang-format off + const QList<QPair<QStringView, ColorAspect *>> colorKeys = { + qMakePair(u"background", &settings().backgroundColor), + qMakePair(u"foreground", &settings().foregroundColor), + qMakePair(u"selectionBackground", &settings().selectionColor), + + qMakePair(u"black", &settings().colors[0]), + qMakePair(u"brightBlack", &settings().colors[8]), + + qMakePair(u"red", &settings().colors[1]), + qMakePair(u"brightRed", &settings().colors[9]), + + qMakePair(u"green", &settings().colors[2]), + qMakePair(u"brightGreen", &settings().colors[10]), + + qMakePair(u"yellow", &settings().colors[3]), + qMakePair(u"brightYellow", &settings().colors[11]), + + qMakePair(u"blue", &settings().colors[4]), + qMakePair(u"brightBlue", &settings().colors[12]), + + qMakePair(u"magenta", &settings().colors[5]), + qMakePair(u"brightMagenta", &settings().colors[13]), + + qMakePair(u"cyan", &settings().colors[6]), + qMakePair(u"brightCyan", &settings().colors[14]), + + qMakePair(u"white", &settings().colors[7]), + qMakePair(u"brightWhite", &settings().colors[15]) + }; + // clang-format on + + for (const auto &pair : colorKeys) { + const auto it = colors.find(pair.first); + if (it != colors.end()) { + const QString colorString = it->toString(); + if (colorString.startsWith("#")) { + QColor color(colorString.mid(0, 7)); + if (colorString.size() > 7) { + int alpha = colorString.mid(7).toInt(nullptr, 16); + color.setAlpha(alpha); + } + if (color.isValid()) + pair.second->setVolatileValue(color); + } + } + } + + return {}; +} + static expected_str<void> loadVsCodeColors(const FilePath &path) { const expected_str<QByteArray> readResult = path.fileContents(); @@ -197,33 +268,33 @@ static expected_str<void> loadVsCodeColors(const FilePath &path) // clang-format off const QList<QPair<QStringView, ColorAspect *>> colorKeys = { - qMakePair(u"editor.background", &TerminalSettings::instance().backgroundColor), - qMakePair(u"terminal.foreground", &TerminalSettings::instance().foregroundColor), - qMakePair(u"terminal.selectionBackground", &TerminalSettings::instance().selectionColor), + qMakePair(u"editor.background", &settings().backgroundColor), + qMakePair(u"terminal.foreground", &settings().foregroundColor), + qMakePair(u"terminal.selectionBackground", &settings().selectionColor), - qMakePair(u"terminal.ansiBlack", &TerminalSettings::instance().colors[0]), - qMakePair(u"terminal.ansiBrightBlack", &TerminalSettings::instance().colors[8]), + qMakePair(u"terminal.ansiBlack", &settings().colors[0]), + qMakePair(u"terminal.ansiBrightBlack", &settings().colors[8]), - qMakePair(u"terminal.ansiRed", &TerminalSettings::instance().colors[1]), - qMakePair(u"terminal.ansiBrightRed", &TerminalSettings::instance().colors[9]), + qMakePair(u"terminal.ansiRed", &settings().colors[1]), + qMakePair(u"terminal.ansiBrightRed", &settings().colors[9]), - qMakePair(u"terminal.ansiGreen", &TerminalSettings::instance().colors[2]), - qMakePair(u"terminal.ansiBrightGreen", &TerminalSettings::instance().colors[10]), + qMakePair(u"terminal.ansiGreen", &settings().colors[2]), + qMakePair(u"terminal.ansiBrightGreen", &settings().colors[10]), - qMakePair(u"terminal.ansiYellow", &TerminalSettings::instance().colors[3]), - qMakePair(u"terminal.ansiBrightYellow", &TerminalSettings::instance().colors[11]), + qMakePair(u"terminal.ansiYellow", &settings().colors[3]), + qMakePair(u"terminal.ansiBrightYellow", &settings().colors[11]), - qMakePair(u"terminal.ansiBlue", &TerminalSettings::instance().colors[4]), - qMakePair(u"terminal.ansiBrightBlue", &TerminalSettings::instance().colors[12]), + qMakePair(u"terminal.ansiBlue", &settings().colors[4]), + qMakePair(u"terminal.ansiBrightBlue", &settings().colors[12]), - qMakePair(u"terminal.ansiMagenta", &TerminalSettings::instance().colors[5]), - qMakePair(u"terminal.ansiBrightMagenta", &TerminalSettings::instance().colors[13]), + qMakePair(u"terminal.ansiMagenta", &settings().colors[5]), + qMakePair(u"terminal.ansiBrightMagenta", &settings().colors[13]), - qMakePair(u"terminal.ansiCyan", &TerminalSettings::instance().colors[6]), - qMakePair(u"terminal.ansiBrightCyan", &TerminalSettings::instance().colors[14]), + qMakePair(u"terminal.ansiCyan", &settings().colors[6]), + qMakePair(u"terminal.ansiBrightCyan", &settings().colors[14]), - qMakePair(u"terminal.ansiWhite", &TerminalSettings::instance().colors[7]), - qMakePair(u"terminal.ansiBrightWhite", &TerminalSettings::instance().colors[15]) + qMakePair(u"terminal.ansiWhite", &settings().colors[7]), + qMakePair(u"terminal.ansiBrightWhite", &settings().colors[15]) }; // clang-format on @@ -248,8 +319,6 @@ static expected_str<void> loadVsCodeColors(const FilePath &path) static expected_str<void> loadKonsoleColorScheme(const FilePath &path) { - QSettings settings(path.toFSPathString(), QSettings::IniFormat); - auto parseColor = [](const QStringList &parts) -> expected_str<QColor> { if (parts.size() != 3 && parts.size() != 4) return make_unexpected(Tr::tr("Invalid color format.")); @@ -258,39 +327,41 @@ static expected_str<void> loadKonsoleColorScheme(const FilePath &path) }; // clang-format off - const QList<QPair<QString, ColorAspect *>> colorKeys = { - qMakePair(QLatin1String("Background/Color"), &TerminalSettings::instance().backgroundColor), - qMakePair(QLatin1String("Foreground/Color"), &TerminalSettings::instance().foregroundColor), + TerminalSettings &s = settings(); + const QPair<QString, ColorAspect *> colorKeys[] = { + { "Background/Color", &s.backgroundColor }, + { "Foreground/Color", &s.foregroundColor}, - qMakePair(QLatin1String("Color0/Color"), &TerminalSettings::instance().colors[0]), - qMakePair(QLatin1String("Color0Intense/Color"), &TerminalSettings::instance().colors[8]), + { "Color0/Color", &s.colors[0] }, + { "Color0Intense/Color", &s.colors[8] }, - qMakePair(QLatin1String("Color1/Color"), &TerminalSettings::instance().colors[1]), - qMakePair(QLatin1String("Color1Intense/Color"), &TerminalSettings::instance().colors[9]), + { "Color1/Color", &s.colors[1] }, + { "Color1Intense/Color", &s.colors[9] }, - qMakePair(QLatin1String("Color2/Color"), &TerminalSettings::instance().colors[2]), - qMakePair(QLatin1String("Color2Intense/Color"), &TerminalSettings::instance().colors[10]), + { "Color2/Color", &s.colors[2] }, + { "Color2Intense/Color", &s.colors[10] }, - qMakePair(QLatin1String("Color3/Color"), &TerminalSettings::instance().colors[3]), - qMakePair(QLatin1String("Color3Intense/Color"), &TerminalSettings::instance().colors[11]), + { "Color3/Color", &s.colors[3] }, + { "Color3Intense/Color", &s.colors[11] }, - qMakePair(QLatin1String("Color4/Color"), &TerminalSettings::instance().colors[4]), - qMakePair(QLatin1String("Color4Intense/Color"), &TerminalSettings::instance().colors[12]), + { "Color4/Color", &s.colors[4] }, + { "Color4Intense/Color", &s.colors[12] }, - qMakePair(QLatin1String("Color5/Color"), &TerminalSettings::instance().colors[5]), - qMakePair(QLatin1String("Color5Intense/Color"), &TerminalSettings::instance().colors[13]), + { "Color5/Color", &s.colors[5] }, + { "Color5Intense/Color", &s.colors[13] }, - qMakePair(QLatin1String("Color6/Color"), &TerminalSettings::instance().colors[6]), - qMakePair(QLatin1String("Color6Intense/Color"), &TerminalSettings::instance().colors[14]), + { "Color6/Color", &s.colors[6] }, + { "Color6Intense/Color", &s.colors[14] }, - qMakePair(QLatin1String("Color7/Color"), &TerminalSettings::instance().colors[7]), - qMakePair(QLatin1String("Color7Intense/Color"), &TerminalSettings::instance().colors[15]) + { "Color7/Color", &s.colors[7] }, + { "Color7Intense/Color", &s.colors[15] } }; // clang-format on + QSettings ini(path.toFSPathString(), QSettings::IniFormat); for (const auto &colorKey : colorKeys) { - if (settings.contains(colorKey.first)) { - const auto color = parseColor(settings.value(colorKey.first).toStringList()); + if (ini.contains(colorKey.first)) { + const auto color = parseColor(ini.value(colorKey.first).toStringList()); if (!color) return make_unexpected(color.error()); @@ -314,30 +385,35 @@ static expected_str<void> loadXFCE4ColorScheme(const FilePath &path) f.write(*arr); f.close(); - QSettings settings(f.fileName(), QSettings::IniFormat); + QSettings ini(f.fileName(), QSettings::IniFormat); + TerminalSettings &s = settings(); // clang-format off - const QList<QPair<QString, ColorAspect *>> colorKeys = { - qMakePair(QLatin1String("Scheme/ColorBackground"), &TerminalSettings::instance().backgroundColor), - qMakePair(QLatin1String("Scheme/ColorForeground"), &TerminalSettings::instance().foregroundColor), + const QPair<QString, ColorAspect *> colorKeys[] = { + { "Scheme/ColorBackground", &s.backgroundColor }, + { "Scheme/ColorForeground", &s.foregroundColor } }; // clang-format on for (const auto &colorKey : colorKeys) { - if (settings.contains(colorKey.first)) { - colorKey.second->setVolatileValue(QColor(settings.value(colorKey.first).toString())); - } + if (ini.contains(colorKey.first)) + colorKey.second->setVolatileValue(QColor(ini.value(colorKey.first).toString())); } - QStringList colors = settings.value(QLatin1String("Scheme/ColorPalette")).toStringList(); + QStringList colors = ini.value(QLatin1String("Scheme/ColorPalette")).toStringList(); int i = 0; - for (const auto &color : colors) { - TerminalSettings::instance().colors[i++].setVolatileValue(QColor(color)); - } + for (const QString &color : colors) + s.colors[i++].setVolatileValue(QColor(color)); return {}; } +static expected_str<void> loadVsCodeOrWindows(const FilePath &path) +{ + return loadVsCodeColors(path).or_else( + [path](const auto &) { return loadWindowsTerminalColors(path); }); +} + static expected_str<void> loadColorScheme(const FilePath &path) { if (path.endsWith("Xdefaults")) @@ -345,7 +421,7 @@ static expected_str<void> loadColorScheme(const FilePath &path) else if (path.suffix() == "itermcolors") return loadItermColors(path); else if (path.suffix() == "json") - return loadVsCodeColors(path); + return loadVsCodeOrWindows(path); else if (path.suffix() == "colorscheme") return loadKonsoleColorScheme(path); else if (path.suffix() == "theme" || path.completeSuffix() == "theme.txt") @@ -354,23 +430,16 @@ static expected_str<void> loadColorScheme(const FilePath &path) return make_unexpected(Tr::tr("Unknown color scheme format.")); } -static TerminalSettings *s_instance; - -TerminalSettings &TerminalSettings::instance() +TerminalSettings &settings() { - return *s_instance; + static TerminalSettings theSettings; + return theSettings; } TerminalSettings::TerminalSettings() { - s_instance = this; - setSettingsGroup("Terminal"); - setId("Terminal.General"); - setDisplayName("Terminal"); - setCategory("ZY.Terminal"); - setDisplayCategory("Terminal"); - setCategoryIconPath(":/terminal/images/settingscategory_terminal.png"); + setAutoApply(false); enableTerminal.setSettingsKey("EnableTerminal"); enableTerminal.setLabelText(Tr::tr("Use internal terminal")); @@ -413,58 +482,58 @@ TerminalSettings::TerminalSettings() sendEscapeToTerminal.setSettingsKey("SendEscapeToTerminal"); sendEscapeToTerminal.setLabelText(Tr::tr("Send escape key to terminal")); - sendEscapeToTerminal.setToolTip( - Tr::tr("Sends the escape key to the terminal when pressed" - "instead of closing the terminal.")); + sendEscapeToTerminal.setToolTip(Tr::tr("Sends the escape key to the terminal when pressed " + "instead of closing the terminal.")); sendEscapeToTerminal.setDefaultValue(false); + lockKeyboard.setSettingsKey("LockKeyboard"); + lockKeyboard.setLabelText(Tr::tr("Block shortcuts in terminal")); + lockKeyboard.setToolTip( + Tr::tr("Keeps Qt Creator shortcuts from interfering with the terminal.")); + lockKeyboard.setDefaultValue(true); + audibleBell.setSettingsKey("AudibleBell"); audibleBell.setLabelText(Tr::tr("Audible bell")); audibleBell.setToolTip(Tr::tr("Makes the terminal beep when a bell " "character is received.")); audibleBell.setDefaultValue(true); - setupColor(this, - foregroundColor, - "Foreground", - Utils::creatorTheme()->color(Theme::TerminalForeground)); - setupColor(this, - backgroundColor, - "Background", - Utils::creatorTheme()->color(Theme::TerminalBackground)); - setupColor(this, - selectionColor, - "Selection", - Utils::creatorTheme()->color(Theme::TerminalSelection)); + enableMouseTracking.setSettingsKey("EnableMouseTracking"); + enableMouseTracking.setLabelText(Tr::tr("Enable mouse tracking")); + enableMouseTracking.setToolTip(Tr::tr("Enables mouse tracking in the terminal.")); + enableMouseTracking.setDefaultValue(true); - setupColor(this, - findMatchColor, - "Find matches", - Utils::creatorTheme()->color(Theme::TerminalFindMatch)); + Theme *theme = Utils::creatorTheme(); - setupColor(this, colors[0], "0", Utils::creatorTheme()->color(Theme::TerminalAnsi0)); - setupColor(this, colors[8], "8", Utils::creatorTheme()->color(Theme::TerminalAnsi8)); + setupColor(this, foregroundColor, "Foreground", theme->color(Theme::TerminalForeground)); + setupColor(this, backgroundColor, "Background", theme->color(Theme::TerminalBackground)); + setupColor(this, selectionColor, "Selection", theme->color(Theme::TerminalSelection)); - setupColor(this, colors[1], "1", Utils::creatorTheme()->color(Theme::TerminalAnsi1)); - setupColor(this, colors[9], "9", Utils::creatorTheme()->color(Theme::TerminalAnsi9)); + setupColor(this, findMatchColor, "Find matches", theme->color(Theme::TerminalFindMatch)); - setupColor(this, colors[2], "2", Utils::creatorTheme()->color(Theme::TerminalAnsi2)); - setupColor(this, colors[10], "10", Utils::creatorTheme()->color(Theme::TerminalAnsi10)); + setupColor(this, colors[0], "0", theme->color(Theme::TerminalAnsi0), "black"); + setupColor(this, colors[8], "8", theme->color(Theme::TerminalAnsi8), "bright black"); - setupColor(this, colors[3], "3", Utils::creatorTheme()->color(Theme::TerminalAnsi3)); - setupColor(this, colors[11], "11", Utils::creatorTheme()->color(Theme::TerminalAnsi11)); + setupColor(this, colors[1], "1", theme->color(Theme::TerminalAnsi1), "red"); + setupColor(this, colors[9], "9", theme->color(Theme::TerminalAnsi9), "bright red"); - setupColor(this, colors[4], "4", Utils::creatorTheme()->color(Theme::TerminalAnsi4)); - setupColor(this, colors[12], "12", Utils::creatorTheme()->color(Theme::TerminalAnsi12)); + setupColor(this, colors[2], "2", theme->color(Theme::TerminalAnsi2), "green"); + setupColor(this, colors[10], "10", theme->color(Theme::TerminalAnsi10), "bright green"); - setupColor(this, colors[5], "5", Utils::creatorTheme()->color(Theme::TerminalAnsi5)); - setupColor(this, colors[13], "13", Utils::creatorTheme()->color(Theme::TerminalAnsi13)); + setupColor(this, colors[3], "3", theme->color(Theme::TerminalAnsi3), "yellow"); + setupColor(this, colors[11], "11", theme->color(Theme::TerminalAnsi11), "bright yellow"); - setupColor(this, colors[6], "6", Utils::creatorTheme()->color(Theme::TerminalAnsi6)); - setupColor(this, colors[14], "14", Utils::creatorTheme()->color(Theme::TerminalAnsi14)); + setupColor(this, colors[4], "4", theme->color(Theme::TerminalAnsi4), "blue"); + setupColor(this, colors[12], "12", theme->color(Theme::TerminalAnsi12), "bright blue"); - setupColor(this, colors[7], "7", Utils::creatorTheme()->color(Theme::TerminalAnsi7)); - setupColor(this, colors[15], "15", Utils::creatorTheme()->color(Theme::TerminalAnsi15)); + setupColor(this, colors[5], "5", theme->color(Theme::TerminalAnsi5), "magenta"); + setupColor(this, colors[13], "13", theme->color(Theme::TerminalAnsi13), "bright magenta"); + + setupColor(this, colors[6], "6", theme->color(Theme::TerminalAnsi6), "cyan"); + setupColor(this, colors[14], "14", theme->color(Theme::TerminalAnsi14), "bright cyan"); + + setupColor(this, colors[7], "7", theme->color(Theme::TerminalAnsi7), "white"); + setupColor(this, colors[15], "15", theme->color(Theme::TerminalAnsi15), "bright white"); setLayouter([this] { using namespace Layouting; @@ -474,11 +543,13 @@ TerminalSettings::TerminalSettings() fontComboBox->setCurrentFont(font()); connect(fontComboBox, &QFontComboBox::currentFontChanged, this, [this](const QFont &f) { - font.setValue(f.family()); + font.setVolatileValue(f.family()); }); auto loadThemeButton = new QPushButton(Tr::tr("Load Theme...")); auto resetTheme = new QPushButton(Tr::tr("Reset Theme")); + auto copyTheme = schemeLog().isDebugEnabled() ? new QPushButton(Tr::tr("Copy Theme")) + : nullptr; connect(loadThemeButton, &QPushButton::clicked, this, [] { const FilePath path = FileUtils::getOpenFilePath( @@ -489,6 +560,7 @@ TerminalSettings::TerminalSettings() "Xdefaults (.Xdefaults Xdefaults);;" "iTerm Color Schemes(*.itermcolors);;" "VS Code Color Schemes(*.json);;" + "Windows Terminal Schemes(*.json);;" "Konsole Color Schemes(*.colorscheme);;" "XFCE4 Terminal Color Schemes(*.theme *.theme.txt);;" "All files (*)", @@ -514,20 +586,31 @@ TerminalSettings::TerminalSettings() color.setVolatileValue(color.defaultValue()); }); -// FIXME: Implement and use a Layouting::DropArea item + if (schemeLog().isDebugEnabled()) { + connect(copyTheme, &QPushButton::clicked, this, [this] { + auto toThemeColor = [](const ColorAspect &color) -> QString { + QColor c = color.value(); + QString a = c.alpha() != 255 ? QString("%1").arg(c.alpha(), 2, 16, QChar('0')) + : QString(); + return QString("%1%2%3%4") + .arg(a) + .arg(c.red(), 2, 16, QChar('0')) + .arg(c.green(), 2, 16, QChar('0')) + .arg(c.blue(), 2, 16, QChar('0')); + }; -// DropSupport *dropSupport = new DropSupport; -// connect(dropSupport, -// &DropSupport::filesDropped, -// this, -// [this](const QList<DropSupport::FileSpec> &files) { -// if (files.size() != 1) -// return; + QString theme; + QTextStream stream(&theme); + stream << "TerminalForeground=" << toThemeColor(foregroundColor) << '\n'; + stream << "TerminalBackground=" << toThemeColor(backgroundColor) << '\n'; + stream << "TerminalSelection=" << toThemeColor(selectionColor) << '\n'; + stream << "TerminalFindMatch=" << toThemeColor(findMatchColor) << '\n'; + for (int i = 0; i < 16; ++i) + stream << "TerminalAnsi" << i << '=' << toThemeColor(colors[i]) << '\n'; -// const expected_str<void> result = loadColorScheme(files.at(0).filePath); -// if (!result) -// QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Error"), result.error()); -// }); + setClipboardAndSelection(theme); + }); + } // clang-format off return Column { @@ -536,8 +619,10 @@ TerminalSettings::TerminalSettings() Column { enableTerminal, st, sendEscapeToTerminal, st, + lockKeyboard, st, audibleBell, st, allowBlinkingCursor, st, + enableMouseTracking, st, }, }, Group { @@ -569,7 +654,7 @@ TerminalSettings::TerminalSettings() colors[14], colors[15] }, Row { - loadThemeButton, resetTheme, st, + loadThemeButton, resetTheme, copyTheme, st, } }, }, @@ -588,4 +673,20 @@ TerminalSettings::TerminalSettings() readSettings(); } +class TerminalSettingsPage final : public Core::IOptionsPage +{ +public: + TerminalSettingsPage() + { + setId("Terminal.General"); + setDisplayName("Terminal"); + setCategory("ZY.Terminal"); + setDisplayCategory("Terminal"); + setCategoryIconPath(":/terminal/images/settingscategory_terminal.png"); + setSettingsProvider([] { return &settings(); }); + } +}; + +const TerminalSettingsPage settingsPage; + } // Terminal diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h index 1c07f9ab033..1400339dd8d 100644 --- a/src/plugins/terminal/terminalsettings.h +++ b/src/plugins/terminal/terminalsettings.h @@ -3,17 +3,15 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace Terminal { -class TerminalSettings : public Core::PagedSettings +class TerminalSettings : public Utils::AspectContainer { public: TerminalSettings(); - static TerminalSettings &instance(); - Utils::BoolAspect enableTerminal{this}; Utils::StringAspect font{this}; @@ -33,6 +31,10 @@ public: Utils::BoolAspect sendEscapeToTerminal{this}; Utils::BoolAspect audibleBell{this}; Utils::BoolAspect lockKeyboard{this}; + + Utils::BoolAspect enableMouseTracking{this}; }; +TerminalSettings &settings(); + } // Terminal diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index a2c566c8f0a..69f0256ef5c 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -2,10 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include "terminalwidget.h" -#include "glyphcache.h" #include "terminalconstants.h" #include "terminalsettings.h" -#include "terminalsurface.h" #include "terminaltr.h" #include <aggregation/aggregate.h> @@ -14,9 +12,12 @@ #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/fileutils.h> +#include <coreplugin/find/textfindconstants.h> #include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/environment.h> #include <utils/fileutils.h> #include <utils/hostosinfo.h> @@ -24,8 +25,6 @@ #include <utils/proxyaction.h> #include <utils/stringutils.h> -#include <vterm.h> - #include <QApplication> #include <QCache> #include <QClipboard> @@ -46,95 +45,36 @@ #include <QTextLayout> #include <QToolTip> -Q_LOGGING_CATEGORY(terminalLog, "qtc.terminal", QtWarningMsg) -Q_LOGGING_CATEGORY(selectionLog, "qtc.terminal.selection", QtWarningMsg) -Q_LOGGING_CATEGORY(paintLog, "qtc.terminal.paint", QtWarningMsg) - using namespace Utils; using namespace Utils::Terminal; using namespace Core; namespace Terminal { - -namespace ColorIndex { -enum Indices { - Foreground = Internal::ColorIndex::Foreground, - Background = Internal::ColorIndex::Background, - Selection, - FindMatch, -}; -} - -using namespace std::chrono_literals; - -// Minimum time between two refreshes. (30fps) -static constexpr std::chrono::milliseconds minRefreshInterval = 1s / 30; - TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters) - : QAbstractScrollArea(parent) + : Core::SearchableTerminal(parent) , m_context(Utils::Id("TerminalWidget_").withSuffix(QString::number((uintptr_t) this))) , m_openParameters(openParameters) - , m_lastFlush(std::chrono::system_clock::now()) - , m_lastDoubleClick(std::chrono::system_clock::now()) { auto contextObj = new IContext(this); contextObj->setWidget(this); contextObj->setContext(m_context); ICore::addContextObject(contextObj); - setupSurface(); setupFont(); setupColors(); setupActions(); - m_cursorBlinkTimer.setInterval(750); - m_cursorBlinkTimer.setSingleShot(false); + surfaceChanged(); - connect(&m_cursorBlinkTimer, &QTimer::timeout, this, [this]() { - if (hasFocus()) - m_cursorBlinkState = !m_cursorBlinkState; - else - m_cursorBlinkState = true; - updateViewportRect(gridToViewport(QRect{m_cursor.position, m_cursor.position})); - }); + setAllowBlinkingCursor(settings().allowBlinkingCursor()); - setAttribute(Qt::WA_InputMethodEnabled); - setAttribute(Qt::WA_MouseTracking); - setAcceptDrops(true); - - setCursor(Qt::IBeamCursor); - - setViewportMargins(1, 1, 1, 1); - - setFocus(); - setFocusPolicy(Qt::StrongFocus); - - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); - - m_flushDelayTimer.setSingleShot(true); - m_flushDelayTimer.setInterval(minRefreshInterval); - - connect(&m_flushDelayTimer, &QTimer::timeout, this, [this]() { flushVTerm(true); }); - - m_scrollTimer.setSingleShot(false); - m_scrollTimer.setInterval(1s / 2); - connect(&m_scrollTimer, &QTimer::timeout, this, [this] { - if (m_scrollDirection < 0) - verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub); - else if (m_scrollDirection > 0) - verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd); - }); - - connect(&TerminalSettings::instance(), &AspectContainer::applied, this, [this] { + connect(&settings(), &AspectContainer::applied, this, [this] { // Setup colors first, as setupFont will redraw the screen. setupColors(); setupFont(); configBlinkTimer(); + setAllowBlinkingCursor(settings().allowBlinkingCursor()); }); - - m_aggregate = new Aggregation::Aggregate(this); - m_aggregate->add(this); - m_aggregate->add(m_search.get()); } void TerminalWidget::setupPty() @@ -142,9 +82,37 @@ void TerminalWidget::setupPty() m_process = std::make_unique<Process>(); CommandLine shellCommand = m_openParameters.shellCommand.value_or( - CommandLine{TerminalSettings::instance().shell.filePath(), - TerminalSettings::instance().shellArguments.value(), - CommandLine::Raw}); + CommandLine{settings().shell(), settings().shellArguments(), CommandLine::Raw}); + + if (shellCommand.executable().isRootPath()) { + writeToTerminal((Tr::tr("Connecting...") + "\r\n").toUtf8(), true); + // We still have to find the shell to start ... + m_findShellWatcher.reset(new QFutureWatcher<expected_str<FilePath>>()); + connect(m_findShellWatcher.get(), &QFutureWatcher<FilePath>::finished, this, [this] { + const expected_str<FilePath> result = m_findShellWatcher->result(); + if (result) { + m_openParameters.shellCommand->setExecutable(*result); + restart(m_openParameters); + return; + } + + writeToTerminal(("\r\n\033[31m" + + Tr::tr("Failed to start shell: %1").arg(result.error()) + "\r\n") + .toUtf8(), + true); + }); + + m_findShellWatcher->setFuture(Utils::asyncRun([shellCommand]() -> expected_str<FilePath> { + const expected_str<FilePath> result = Utils::Terminal::defaultShellForDevice( + shellCommand.executable()); + if (result && !result->isExecutableFile()) + return make_unexpected( + Tr::tr("\"%1\" is not executable.").arg(result->toUserOutput())); + return result; + })); + + return; + } Environment env = m_openParameters.environment.value_or(Environment{}) .appliedToEnvironment(shellCommand.executable().deviceEnvironment()); @@ -162,33 +130,34 @@ void TerminalWidget::setupPty() env.unset("CLINK_NOAUTORUN"); m_process->setProcessMode(ProcessMode::Writer); - m_process->setPtyData(Utils::Pty::Data()); + Utils::Pty::Data data; + data.setPtyInputFlagsChangedHandler([this](Pty::PtyInputFlag flags) { + const bool password = (flags & Pty::InputModeHidden); + setPasswordMode(password); + }); + m_process->setPtyData(data); m_process->setCommand(shellCommand); if (m_openParameters.workingDirectory.has_value()) m_process->setWorkingDirectory(*m_openParameters.workingDirectory); m_process->setEnvironment(env); - if (m_surface->shellIntegration()) { - m_surface->shellIntegration()->prepareProcess(*m_process.get()); - } + if (m_shellIntegration) + m_shellIntegration->prepareProcess(*m_process.get()); connect(m_process.get(), &Process::readyReadStandardOutput, this, [this]() { onReadyRead(false); }); connect(m_process.get(), &Process::done, this, [this] { + QString errorMessage; + if (m_process) { if (m_process->exitCode() != 0) { - QByteArray msg = QString("\r\n\033[31mProcess exited with code: %1") - .arg(m_process->exitCode()) - .toUtf8(); + errorMessage + = Tr::tr("Terminal process exited with code %1").arg(m_process->exitCode()); if (!m_process->errorString().isEmpty()) - msg += QString(" (%1)").arg(m_process->errorString()).toUtf8(); - - m_surface->dataFromPty(msg); - - return; + errorMessage += QString(" (%1)").arg(m_process->errorString()); } } @@ -207,11 +176,18 @@ void TerminalWidget::setupPty() deleteLater(); if (m_openParameters.m_exitBehavior == ExitBehavior::Keep) { - QByteArray msg = QString("\r\nProcess exited with code: %1") - .arg(m_process ? m_process->exitCode() : -1) - .toUtf8(); + if (!errorMessage.isEmpty()) { + QByteArray msg = QString("\r\n\033[31m%1").arg(errorMessage).toUtf8(); - m_surface->dataFromPty(msg); + writeToTerminal(msg, true); + } else { + QString exitMsg = Tr::tr("Process exited with code: %1") + .arg(m_process ? m_process->exitCode() : -1); + QByteArray msg = QString("\r\n%1").arg(exitMsg).toUtf8(); + writeToTerminal(msg, true); + } + } else if (!errorMessage.isEmpty()) { + Core::MessageManager::writeFlashing(errorMessage); } }); @@ -232,8 +208,8 @@ void TerminalWidget::setupFont() { QFont f; f.setFixedPitch(true); - f.setFamily(TerminalSettings::instance().font.value()); - f.setPointSize(TerminalSettings::instance().fontSize.value()); + f.setFamily(settings().font()); + f.setPointSize(settings().fontSize()); setFont(f); } @@ -243,20 +219,14 @@ void TerminalWidget::setupColors() // Check if the colors have changed. std::array<QColor, 20> newColors; for (int i = 0; i < 16; ++i) { - newColors[i] = TerminalSettings::instance().colors[i].value(); + newColors[i] = settings().colors[i](); } - newColors[ColorIndex::Background] = TerminalSettings::instance().backgroundColor.value(); - newColors[ColorIndex::Foreground] = TerminalSettings::instance().foregroundColor.value(); - newColors[ColorIndex::Selection] = TerminalSettings::instance().selectionColor.value(); - newColors[ColorIndex::FindMatch] = TerminalSettings::instance().findMatchColor.value(); + newColors[(size_t) WidgetColorIdx::Background] = settings().backgroundColor.value(); + newColors[(size_t) WidgetColorIdx::Foreground] = settings().foregroundColor.value(); + newColors[(size_t) WidgetColorIdx::Selection] = settings().selectionColor.value(); + newColors[(size_t) WidgetColorIdx::FindMatch] = settings().findMatchColor.value(); - if (m_currentColors == newColors) - return; - - m_currentColors = newColors; - - updateViewport(); - update(); + setColors(newColors); } static bool contextMatcher(QObject *, Qt::ShortcutContext) @@ -342,111 +312,57 @@ qint64 TerminalWidget::writeToPty(const QByteArray &data) return data.size(); } -void TerminalWidget::setupSurface() +void TerminalWidget::resizePty(QSize newSize) { + if (m_process && m_process->ptyData()) + m_process->ptyData()->resize(newSize); +} + +void TerminalWidget::surfaceChanged() +{ + Core::SearchableTerminal::surfaceChanged(); + m_shellIntegration.reset(new ShellIntegration()); - m_surface = std::make_unique<Internal::TerminalSurface>(QSize{80, 60}, m_shellIntegration.get()); - m_search = TerminalSearchPtr(new TerminalSearch(m_surface.get()), [this](TerminalSearch *p) { - m_aggregate->remove(p); - delete p; - }); + setSurfaceIntegration(m_shellIntegration.get()); - connect(m_search.get(), &TerminalSearch::hitsChanged, this, &TerminalWidget::updateViewport); - connect(m_search.get(), &TerminalSearch::currentHitChanged, this, [this] { - SearchHit hit = m_search->currentHit(); - if (hit.start >= 0) { - setSelection(Selection{hit.start, hit.end, true}, hit != m_lastSelectedHit); - m_lastSelectedHit = hit; - } - }); - - m_surface->setWriteToPty([this](const QByteArray &data) { return writeToPty(data); }); - - connect(m_surface.get(), &Internal::TerminalSurface::fullSizeChanged, this, [this] { - updateScrollBars(); - }); - connect(m_surface.get(), - &Internal::TerminalSurface::invalidated, + connect(m_shellIntegration.get(), + &ShellIntegration::titleChanged, this, - [this](const QRect &rect) { - setSelection(std::nullopt); - updateViewportRect(gridToViewport(rect)); - verticalScrollBar()->setValue(m_surface->fullSize().height()); + [this](const QString &title) { + const FilePath titleFile = FilePath::fromUserInput(title); + if (!m_title.isEmpty() + || m_openParameters.shellCommand.value_or(CommandLine{}).executable() + != titleFile) { + m_title = titleFile.isFile() ? titleFile.baseName() : title; + } + emit titleChanged(); }); - connect(m_surface.get(), - &Internal::TerminalSurface::cursorChanged, + + connect(m_shellIntegration.get(), + &ShellIntegration::commandChanged, this, - [this](const Internal::Cursor &oldCursor, const Internal::Cursor &newCursor) { - int startX = oldCursor.position.x(); - int endX = newCursor.position.x(); - - if (startX > endX) - std::swap(startX, endX); - - int startY = oldCursor.position.y(); - int endY = newCursor.position.y(); - if (startY > endY) - std::swap(startY, endY); - - m_cursor = newCursor; - - updateViewportRect( - gridToViewport(QRect{QPoint{startX, startY}, QPoint{endX, endY}})); - configBlinkTimer(); + [this](const CommandLine &command) { + m_currentCommand = command; + emit commandChanged(m_currentCommand); + }); + connect(m_shellIntegration.get(), + &ShellIntegration::currentDirChanged, + this, + [this](const QString ¤tDir) { + m_cwd = FilePath::fromUserInput(currentDir); + emit cwdChanged(m_cwd); }); - connect(m_surface.get(), &Internal::TerminalSurface::altscreenChanged, this, [this] { - updateScrollBars(); - if (!setSelection(std::nullopt)) - updateViewport(); - }); - connect(m_surface.get(), &Internal::TerminalSurface::unscroll, this, [this] { - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); - }); - connect(m_surface.get(), &Internal::TerminalSurface::bell, this, [] { - if (TerminalSettings::instance().audibleBell.value()) - QApplication::beep(); - }); - - if (m_shellIntegration) { - connect(m_shellIntegration.get(), - &ShellIntegration::commandChanged, - this, - [this](const CommandLine &command) { - m_currentCommand = command; - emit commandChanged(m_currentCommand); - }); - connect(m_shellIntegration.get(), - &ShellIntegration::currentDirChanged, - this, - [this](const QString ¤tDir) { - m_cwd = FilePath::fromUserInput(currentDir); - emit cwdChanged(m_cwd); - }); - } } -void TerminalWidget::configBlinkTimer() +QString TerminalWidget::title() const { - bool shouldRun = m_cursor.visible && m_cursor.blink && hasFocus() - && TerminalSettings::instance().allowBlinkingCursor.value(); - if (shouldRun != m_cursorBlinkTimer.isActive()) { - if (shouldRun) - m_cursorBlinkTimer.start(); - else - m_cursorBlinkTimer.stop(); - } -} - -QColor TerminalWidget::toQColor(std::variant<int, QColor> color) const -{ - if (std::holds_alternative<int>(color)) { - int idx = std::get<int>(color); - if (idx >= 0 && idx < 18) - return m_currentColors[idx]; - - return m_currentColors[ColorIndex::Background]; - } - return std::get<QColor>(color); + const FilePath dir = cwd(); + QString title = m_title; + if (title.isEmpty()) + title = currentCommand().isEmpty() ? shellName() : currentCommand().executable().fileName(); + if (dir.isEmpty()) + return title; + return title + " - " + dir.fileName(); } void TerminalWidget::updateCopyState() @@ -454,194 +370,44 @@ void TerminalWidget::updateCopyState() if (!hasFocus()) return; - m_copy->setEnabled(m_selection.has_value()); + m_copy->setEnabled(selection().has_value()); } -void TerminalWidget::setFont(const QFont &font) +void TerminalWidget::setClipboard(const QString &text) { - m_font = font; - - QFontMetricsF qfm{m_font}; - const qreal w = [qfm]() -> qreal { - if (HostOsInfo::isMacHost()) - return qfm.maxWidth(); - return qfm.averageCharWidth(); - }(); - - qCInfo(terminalLog) << font.family() << font.pointSize() << w << viewport()->size(); - - m_cellSize = {w, (double) qCeil(qfm.height())}; - - QAbstractScrollArea::setFont(m_font); - - if (m_process) { - applySizeChange(); - } -} - -void TerminalWidget::copyToClipboard() -{ - QTC_ASSERT(m_selection.has_value(), return); - - QString text = textFromSelection(); - - qCDebug(selectionLog) << "Copied to clipboard: " << text; - setClipboardAndSelection(text); } -void TerminalWidget::pasteFromClipboard() +std::optional<TerminalSolution::TerminalView::Link> TerminalWidget::toLink(const QString &text) { - QClipboard *clipboard = QApplication::clipboard(); - const QString clipboardText = clipboard->text(QClipboard::Clipboard); + if (text.size() > 0) { + QString result = chopIfEndsWith(text, ':'); - if (clipboardText.isEmpty()) - return; + if (!result.isEmpty()) { + if (result.startsWith("~/")) + result = QDir::homePath() + result.mid(1); - m_surface->pasteFromClipboard(clipboardText); -} + Utils::Link link = Utils::Link::fromString(result, true); -void TerminalWidget::copyLinkToClipboard() -{ - if (m_linkSelection) - setClipboardAndSelection(m_linkSelection->link.targetFilePath.toUserOutput()); -} + if (!link.targetFilePath.isEmpty() && !link.targetFilePath.isAbsolutePath()) + link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path()); -void TerminalWidget::clearSelection() -{ - setSelection(std::nullopt); - m_surface->sendKey(Qt::Key_Escape); -} + if (link.hasValidTarget() + && (link.targetFilePath.scheme().toString().startsWith("http") + || link.targetFilePath.exists())) { + return Link{link.targetFilePath.toString(), link.targetLine, link.targetColumn}; + } + } + } -void TerminalWidget::zoomIn() -{ - m_font.setPointSize(m_font.pointSize() + 1); - setFont(m_font); -} - -void TerminalWidget::zoomOut() -{ - m_font.setPointSize(qMax(m_font.pointSize() - 1, 1)); - setFont(m_font); -} - -void TerminalWidget::moveCursorWordLeft() -{ - writeToPty("\x1b\x62"); -} - -void TerminalWidget::moveCursorWordRight() -{ - writeToPty("\x1b\x66"); -} - -void TerminalWidget::clearContents() -{ - m_surface->clearAll(); + return std::nullopt; } void TerminalWidget::onReadyRead(bool forceFlush) { QByteArray data = m_process->readAllRawStandardOutput(); - m_surface->dataFromPty(data); - - flushVTerm(forceFlush); -} - -void TerminalWidget::flushVTerm(bool force) -{ - const std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - const std::chrono::milliseconds timeSinceLastFlush - = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_lastFlush); - - const bool shouldFlushImmediately = timeSinceLastFlush > minRefreshInterval; - if (force || shouldFlushImmediately) { - if (m_flushDelayTimer.isActive()) - m_flushDelayTimer.stop(); - - m_lastFlush = now; - m_surface->flush(); - return; - } - - if (!m_flushDelayTimer.isActive()) { - const std::chrono::milliseconds timeToNextFlush = (minRefreshInterval - timeSinceLastFlush); - m_flushDelayTimer.start(timeToNextFlush.count()); - } -} - -QString TerminalWidget::textFromSelection() const -{ - if (!m_selection) - return {}; - - Internal::CellIterator it = m_surface->iteratorAt(m_selection->start); - Internal::CellIterator end = m_surface->iteratorAt(m_selection->end); - - QTC_ASSERT(it.position() < end.position(), return {}); - - std::u32string s; - bool previousWasZero = false; - for (; it != end; ++it) { - if (it.gridPos().x() == 0 && !s.empty() && previousWasZero) - s += U'\n'; - - if (*it != 0) { - previousWasZero = false; - s += *it; - } else { - previousWasZero = true; - } - } - - return QString::fromUcs4(s.data(), static_cast<int>(s.size())); -} - -bool TerminalWidget::setSelection(const std::optional<Selection> &selection, bool scroll) -{ - qCDebug(selectionLog) << "setSelection" << selection.has_value(); - if (selection.has_value()) - qCDebug(selectionLog) << "start:" << selection->start << "end:" << selection->end - << "final:" << selection->final; - - if (selectionLog().isDebugEnabled()) - updateViewport(); - - if (selection == m_selection) - return false; - - m_selection = selection; - - updateCopyState(); - - if (m_selection && m_selection->final) { - qCDebug(selectionLog) << "Copy enabled:" << selection.has_value(); - QString text = textFromSelection(); - - QClipboard *clipboard = QApplication::clipboard(); - if (clipboard->supportsSelection()) { - qCDebug(selectionLog) << "Selection set to clipboard: " << text; - clipboard->setText(text, QClipboard::Selection); - } - - if (scroll) { - QPoint start = m_surface->posToGrid(m_selection->start); - QPoint end = m_surface->posToGrid(m_selection->end); - QRect viewRect = gridToViewport(QRect{start, end}); - if (viewRect.y() >= viewport()->height() || viewRect.y() < 0) { - // Selection is outside of the viewport, scroll to it. - verticalScrollBar()->setValue(start.y()); - } - } - - m_search->setCurrentSelection(SearchHitWithText{{selection->start, selection->end}, text}); - } - - if (!selectionLog().isDebugEnabled()) - updateViewport(); - - return true; + writeToTerminal(data, forceFlush); } void TerminalWidget::setShellName(const QString &shellName) @@ -681,841 +447,64 @@ void TerminalWidget::restart(const OpenTerminalParameters &openParameters) { QTC_ASSERT(!m_process || !m_process->isRunning(), return); m_openParameters = openParameters; - m_process.reset(); - setupSurface(); + TerminalView::restart(); setupPty(); } -QPoint TerminalWidget::viewportToGlobal(QPoint p) const +void TerminalWidget::selectionChanged(const std::optional<Selection> &newSelection) { - int y = p.y() - topMargin(); - const double offset = verticalScrollBar()->value() * m_cellSize.height(); - y += offset; + SearchableTerminal::selectionChanged(newSelection); - return {p.x(), y}; -} + updateCopyState(); -QPoint TerminalWidget::globalToViewport(QPoint p) const -{ - int y = p.y() + topMargin(); - const double offset = verticalScrollBar()->value() * m_cellSize.height(); - y -= offset; + if (selection() && selection()->final) { + QString text = textFromSelection(); - return {p.x(), y}; -} - -QPoint TerminalWidget::globalToGrid(QPointF p) const -{ - return QPoint(p.x() / m_cellSize.width(), p.y() / m_cellSize.height()); -} - -QPointF TerminalWidget::gridToGlobal(QPoint p, bool bottom, bool right) const -{ - QPointF result = QPointF(p.x() * m_cellSize.width(), p.y() * m_cellSize.height()); - if (bottom || right) - result += {right ? m_cellSize.width() : 0, bottom ? m_cellSize.height() : 0}; - return result; -} - -qreal TerminalWidget::topMargin() const -{ - return viewport()->size().height() - (m_surface->liveSize().height() * m_cellSize.height()); -} - -static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) -{ - const qreal radiusBase = qMax(qreal(1), maxRadius); - const qreal pWidth = pen.widthF(); - - const QString key = QLatin1String("WaveUnderline-") % pen.color().name() - % QString::number(int(radiusBase), 16) - % QString::number(int(pWidth), 16); - - QPixmap pixmap; - if (QPixmapCache::find(key, &pixmap)) - return pixmap; - - const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio - const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod); - const qreal radius = qFloor(radiusBase * 2) / 2.; - - QPainterPath path; - - qreal xs = 0; - qreal ys = radius; - - while (xs < width) { - xs += halfPeriod; - ys = -ys; - path.quadTo(xs - halfPeriod / 2, ys, xs, 0); + QClipboard *clipboard = QApplication::clipboard(); + if (clipboard->supportsSelection()) + clipboard->setText(text, QClipboard::Selection); } - - pixmap = QPixmap(width, radius * 2); - pixmap.fill(Qt::transparent); - { - QPen wavePen = pen; - wavePen.setCapStyle(Qt::SquareCap); - - // This is to protect against making the line too fat, as happens on macOS - // due to it having a rather thick width for the regular underline. - const qreal maxPenWidth = .8 * radius; - if (wavePen.widthF() > maxPenWidth) - wavePen.setWidthF(maxPenWidth); - - QPainter imgPainter(&pixmap); - imgPainter.setPen(wavePen); - imgPainter.setRenderHint(QPainter::Antialiasing); - imgPainter.translate(0, radius); - imgPainter.drawPath(path); - } - - QPixmapCache::insert(key, pixmap); - - return pixmap; } -// Copied from qpainter.cpp -static void drawTextItemDecoration(QPainter &painter, - const QPointF &pos, - QTextCharFormat::UnderlineStyle underlineStyle, - QTextItem::RenderFlags flags, - qreal width, - const QColor &underlineColor, - const QRawFont &font) +void TerminalWidget::linkActivated(const Link &link) { - if (underlineStyle == QTextCharFormat::NoUnderline - && !(flags & (QTextItem::StrikeOut | QTextItem::Overline))) + FilePath filePath = FilePath::fromUserInput(link.text); + + if (filePath.scheme().toString().startsWith("http")) { + QDesktopServices::openUrl(filePath.toUrl()); return; - - const QPen oldPen = painter.pen(); - const QBrush oldBrush = painter.brush(); - painter.setBrush(Qt::NoBrush); - QPen pen = oldPen; - pen.setStyle(Qt::SolidLine); - pen.setWidthF(font.lineThickness()); - pen.setCapStyle(Qt::FlatCap); - - QLineF line(qFloor(pos.x()), pos.y(), qFloor(pos.x() + width), pos.y()); - - const qreal underlineOffset = font.underlinePosition(); - - /*if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { - QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); - if (theme) - underlineStyle = QTextCharFormat::UnderlineStyle( - theme->themeHint(QPlatformTheme::SpellCheckUnderlineStyle).toInt()); - if (underlineStyle == QTextCharFormat::SpellCheckUnderline) // still not resolved - underlineStyle = QTextCharFormat::WaveUnderline; - }*/ - - if (underlineStyle == QTextCharFormat::WaveUnderline) { - painter.save(); - painter.translate(0, pos.y() + 1); - qreal maxHeight = font.descent() - qreal(1); - - QColor uc = underlineColor; - if (uc.isValid()) - pen.setColor(uc); - - // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms - const QPixmap wave = generateWavyPixmap(qMin(qMax(underlineOffset, pen.widthF()), - maxHeight / qreal(2.)), - pen); - const int descent = qFloor(maxHeight); - - painter.setBrushOrigin(painter.brushOrigin().x(), 0); - painter.fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave); - painter.restore(); - } else if (underlineStyle != QTextCharFormat::NoUnderline) { - // Deliberately ceil the offset to avoid the underline coming too close to - // the text above it, but limit it to stay within descent. - qreal adjustedUnderlineOffset = std::ceil(underlineOffset) + 0.5; - if (underlineOffset <= font.descent()) - adjustedUnderlineOffset = qMin(adjustedUnderlineOffset, font.descent() - qreal(0.5)); - const qreal underlinePos = pos.y() + adjustedUnderlineOffset; - QColor uc = underlineColor; - if (uc.isValid()) - pen.setColor(uc); - - pen.setStyle((Qt::PenStyle)(underlineStyle)); - painter.setPen(pen); - QLineF underline(line.x1(), underlinePos, line.x2(), underlinePos); - painter.drawLine(underline); } - pen.setStyle(Qt::SolidLine); - pen.setColor(oldPen.color()); - - if (flags & QTextItem::StrikeOut) { - QLineF strikeOutLine = line; - strikeOutLine.translate(0., -font.ascent() / 3.); - QColor uc = underlineColor; - if (uc.isValid()) - pen.setColor(uc); - painter.setPen(pen); - painter.drawLine(strikeOutLine); - } - - if (flags & QTextItem::Overline) { - QLineF overline = line; - overline.translate(0., -font.ascent()); - QColor uc = underlineColor; - if (uc.isValid()) - pen.setColor(uc); - painter.setPen(pen); - painter.drawLine(overline); - } - - painter.setPen(oldPen); - painter.setBrush(oldBrush); -} - -bool TerminalWidget::paintFindMatches(QPainter &p, - QList<SearchHit>::const_iterator &it, - const QRectF &cellRect, - const QPoint gridPos) const -{ - if (it == m_search->hits().constEnd()) - return false; - - const int pos = m_surface->gridToPos(gridPos); - while (it != m_search->hits().constEnd()) { - if (pos < it->start) - return false; - - if (pos >= it->end) { - ++it; - continue; - } - break; - } - - if (it == m_search->hits().constEnd()) - return false; - - p.fillRect(cellRect, m_currentColors[ColorIndex::FindMatch]); - - return true; -} - -bool TerminalWidget::paintSelection(QPainter &p, const QRectF &cellRect, const QPoint gridPos) const -{ - bool isInSelection = false; - const int pos = m_surface->gridToPos(gridPos); - - if (m_selection) - isInSelection = pos >= m_selection->start && pos < m_selection->end; - - if (isInSelection) - p.fillRect(cellRect, m_currentColors[ColorIndex::Selection]); - - return isInSelection; -} - -int TerminalWidget::paintCell(QPainter &p, - const QRectF &cellRect, - QPoint gridPos, - const Internal::TerminalCell &cell, - QFont &f, - QList<SearchHit>::const_iterator &searchIt) const -{ - bool paintBackground = !paintSelection(p, cellRect, gridPos) - && !paintFindMatches(p, searchIt, cellRect, gridPos); - - bool isDefaultBg = std::holds_alternative<int>(cell.backgroundColor) - && std::get<int>(cell.backgroundColor) == 17; - - if (paintBackground && !isDefaultBg) - p.fillRect(cellRect, toQColor(cell.backgroundColor)); - - p.setPen(toQColor(cell.foregroundColor)); - - f.setBold(cell.bold); - f.setItalic(cell.italic); - - if (!cell.text.isEmpty()) { - const auto r = Internal::GlyphCache::instance().get(f, cell.text); - - if (r) { - const auto brSize = r->boundingRect().size(); - QPointF brOffset; - if (brSize.width() > cellRect.size().width()) - brOffset.setX(-(brSize.width() - cellRect.size().width()) / 2.0); - if (brSize.height() > cellRect.size().height()) - brOffset.setY(-(brSize.height() - cellRect.size().height()) / 2.0); - - QPointF finalPos = cellRect.topLeft() + brOffset; - - p.drawGlyphRun(finalPos, *r); - - bool tempLink = false; - if (m_linkSelection) { - int chPos = m_surface->gridToPos(gridPos); - tempLink = chPos >= m_linkSelection->start && chPos < m_linkSelection->end; - } - if (cell.underlineStyle != QTextCharFormat::NoUnderline || cell.strikeOut || tempLink) { - QTextItem::RenderFlags flags; - //flags.setFlag(QTextItem::RenderFlag::Underline, cell.format.fontUnderline()); - flags.setFlag(QTextItem::StrikeOut, cell.strikeOut); - finalPos.setY(finalPos.y() + r->rawFont().ascent()); - drawTextItemDecoration(p, - finalPos, - tempLink ? QTextCharFormat::DashUnderline - : cell.underlineStyle, - flags, - cellRect.size().width(), - {}, - r->rawFont()); - } - } - } - - return cell.width; -} - -void TerminalWidget::paintCursor(QPainter &p) const -{ - if (!m_process || !m_process->isRunning()) - return; - - auto cursor = m_surface->cursor(); - - if (!m_preEditString.isEmpty()) - cursor.shape = Internal::Cursor::Shape::Underline; - - const bool blinkState = !cursor.blink || m_cursorBlinkState - || !TerminalSettings::instance().allowBlinkingCursor.value(); - - if (cursor.visible && blinkState) { - const int cursorCellWidth = m_surface->cellWidthAt(cursor.position.x(), cursor.position.y()); - - QRectF cursorRect = QRectF(gridToGlobal(cursor.position), - gridToGlobal({cursor.position.x() + cursorCellWidth, - cursor.position.y()}, - true)) - .toAlignedRect(); - - cursorRect.adjust(1, 1, -1, -1); - - QPen pen(Qt::white, 0, Qt::SolidLine); - p.setPen(pen); - - if (hasFocus()) { - QPainter::CompositionMode oldMode = p.compositionMode(); - p.setCompositionMode(QPainter::RasterOp_NotDestination); - switch (cursor.shape) { - case Internal::Cursor::Shape::Block: - p.fillRect(cursorRect, p.pen().brush()); - break; - case Internal::Cursor::Shape::Underline: - p.drawLine(cursorRect.bottomLeft(), cursorRect.bottomRight()); - break; - case Internal::Cursor::Shape::LeftBar: - p.drawLine(cursorRect.topLeft(), cursorRect.bottomLeft()); - break; - } - p.setCompositionMode(oldMode); - } else { - p.drawRect(cursorRect); - } - } -} - -void TerminalWidget::paintPreedit(QPainter &p) const -{ - auto cursor = m_surface->cursor(); - if (!m_preEditString.isEmpty()) { - QRectF rect = QRectF(gridToGlobal(cursor.position), - gridToGlobal({cursor.position.x(), cursor.position.y()}, true, true)); - - rect.setWidth(viewport()->width() - rect.x()); - - p.setPen(toQColor(ColorIndex::Foreground)); - QFont f = font(); - f.setUnderline(true); - p.setFont(f); - p.drawText(rect, Qt::TextDontClip | Qt::TextWrapAnywhere, m_preEditString); - } -} - -void TerminalWidget::paintCells(QPainter &p, QPaintEvent *event) const -{ - QFont f = m_font; - - const int scrollOffset = verticalScrollBar()->value(); - - const int maxRow = m_surface->fullSize().height(); - const int startRow = qFloor((qreal) event->rect().y() / m_cellSize.height()) + scrollOffset; - const int endRow = qMin(maxRow, - qCeil((event->rect().y() + event->rect().height()) / m_cellSize.height()) - + scrollOffset); - - QList<SearchHit>::const_iterator searchIt - = std::lower_bound(m_search->hits().constBegin(), - m_search->hits().constEnd(), - startRow, - [this](const SearchHit &hit, int value) { - return m_surface->posToGrid(hit.start).y() < value; - }); - - for (int cellY = startRow; cellY < endRow; ++cellY) { - for (int cellX = 0; cellX < m_surface->liveSize().width();) { - const auto cell = m_surface->fetchCell(cellX, cellY); - - QRectF cellRect(gridToGlobal({cellX, cellY}), - QSizeF{m_cellSize.width() * cell.width, m_cellSize.height()}); - - int numCells = paintCell(p, cellRect, {cellX, cellY}, cell, f, searchIt); - - cellX += numCells; - } - } -} - -void TerminalWidget::paintDebugSelection(QPainter &p, const Selection &selection) const -{ - auto s = globalToViewport(gridToGlobal(m_surface->posToGrid(selection.start)).toPoint()); - const auto e = globalToViewport( - gridToGlobal(m_surface->posToGrid(selection.end), true).toPoint()); - - p.setPen(QPen(Qt::green, 1, Qt::DashLine)); - p.drawLine(s.x(), 0, s.x(), height()); - p.drawLine(0, s.y(), width(), s.y()); - - p.setPen(QPen(Qt::red, 1, Qt::DashLine)); - - p.drawLine(e.x(), 0, e.x(), height()); - p.drawLine(0, e.y(), width(), e.y()); -} - -void TerminalWidget::paintEvent(QPaintEvent *event) -{ - QElapsedTimer t; - t.start(); - event->accept(); - QPainter p(viewport()); - - p.save(); - - if (paintLog().isDebugEnabled()) - p.fillRect(event->rect(), QColor::fromRgb(rand() % 60, rand() % 60, rand() % 60)); + if (filePath.isDir()) + Core::FileUtils::showInFileSystemView(filePath); else - p.fillRect(event->rect(), m_currentColors[ColorIndex::Background]); - - int scrollOffset = verticalScrollBar()->value(); - int offset = -(scrollOffset * m_cellSize.height()); - - qreal margin = topMargin(); - - p.translate(QPointF{0.0, offset + margin}); - - paintCells(p, event); - paintCursor(p); - paintPreedit(p); - - p.restore(); - - p.fillRect(QRectF{{0, 0}, QSizeF{(qreal) width(), topMargin()}}, - m_currentColors[ColorIndex::Background]); - - if (selectionLog().isDebugEnabled()) { - if (m_selection) - paintDebugSelection(p, *m_selection); - if (m_linkSelection) - paintDebugSelection(p, *m_linkSelection); - } - - if (paintLog().isDebugEnabled()) { - QToolTip::showText(this->mapToGlobal(QPoint(width() - 200, 0)), - QString("Paint: %1ms").arg(t.elapsed())); - } + EditorManager::openEditorAt(Utils::Link{filePath, link.targetLine, link.targetColumn}); } -void TerminalWidget::keyPressEvent(QKeyEvent *event) +void TerminalWidget::focusInEvent(QFocusEvent *event) { - // Don't blink during typing - if (m_cursorBlinkTimer.isActive()) { - m_cursorBlinkTimer.start(); - m_cursorBlinkState = true; - } - - if (event->key() == Qt::Key_Escape) { - bool sendToTerminal = TerminalSettings::instance().sendEscapeToTerminal.value(); - bool send = false; - if (sendToTerminal && event->modifiers() == Qt::NoModifier) - send = true; - else if (!sendToTerminal && event->modifiers() == Qt::ShiftModifier) - send = true; - - if (send) { - event->setModifiers(Qt::NoModifier); - m_surface->sendKey(event); - return; - } - - if (m_selection) - m_clearSelection->trigger(); - else { - QAction *returnAction = ActionManager::command(Core::Constants::S_RETURNTOEDITOR) - ->actionForContext(Core::Constants::C_GLOBAL); - QTC_ASSERT(returnAction, return); - returnAction->trigger(); - } - return; - } - - if (event->key() == Qt::Key_Control) { - if (!m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) { - setCursor(Qt::PointingHandCursor); - } - } - - event->accept(); - - m_surface->sendKey(event); -} - -void TerminalWidget::keyReleaseEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Control && m_linkSelection.has_value()) { - m_linkSelection.reset(); - updateCopyState(); - setCursor(Qt::IBeamCursor); - updateViewport(); - } -} - -void TerminalWidget::applySizeChange() -{ - QSize newLiveSize = { - qFloor((qreal) (viewport()->size().width()) / (qreal) m_cellSize.width()), - qFloor((qreal) (viewport()->size().height()) / m_cellSize.height()), - }; - - if (newLiveSize.height() <= 0) - newLiveSize.setHeight(1); - - if (newLiveSize.width() <= 0) - newLiveSize.setWidth(1); - - if (m_process && m_process->ptyData()) - m_process->ptyData()->resize(newLiveSize); - - m_surface->resize(newLiveSize); - flushVTerm(true); -} - -void TerminalWidget::updateScrollBars() -{ - int scrollSize = m_surface->fullSize().height() - m_surface->liveSize().height(); - verticalScrollBar()->setRange(0, scrollSize); - verticalScrollBar()->setValue(verticalScrollBar()->maximum()); - updateViewport(); -} - -void TerminalWidget::resizeEvent(QResizeEvent *event) -{ - event->accept(); - - // If increasing in size, we'll trigger libvterm to call sb_popline in - // order to pull lines out of the history. This will cause the scrollback - // to decrease in size which reduces the size of the verticalScrollBar. - // That will trigger a scroll offset increase which we want to ignore. - m_ignoreScroll = true; - - applySizeChange(); - - setSelection(std::nullopt); - m_ignoreScroll = false; -} - -QRect TerminalWidget::gridToViewport(QRect rect) const -{ - int offset = verticalScrollBar()->value(); - - int startRow = rect.y() - offset; - int numRows = rect.height(); - int numCols = rect.width(); - - QRectF r{rect.x() * m_cellSize.width(), - startRow * m_cellSize.height(), - numCols * m_cellSize.width(), - numRows * m_cellSize.height()}; - - r.translate(0, topMargin()); - - return r.toAlignedRect(); -} - -void TerminalWidget::updateViewport() -{ - viewport()->update(); -} - -void TerminalWidget::updateViewportRect(const QRect &rect) -{ - viewport()->update(rect); -} - -void TerminalWidget::wheelEvent(QWheelEvent *event) -{ - verticalScrollBar()->event(event); -} - -void TerminalWidget::focusInEvent(QFocusEvent *) -{ - updateViewport(); - configBlinkTimer(); + TerminalView::focusInEvent(event); updateCopyState(); } -void TerminalWidget::focusOutEvent(QFocusEvent *) + +void TerminalWidget::contextMenuRequested(const QPoint &pos) { - updateViewport(); - configBlinkTimer(); -} - -void TerminalWidget::inputMethodEvent(QInputMethodEvent *event) -{ - m_preEditString = event->preeditString(); - - if (event->commitString().isEmpty()) { - updateViewport(); - return; - } - - m_surface->sendKey(event->commitString()); -} - -void TerminalWidget::mousePressEvent(QMouseEvent *event) -{ - m_scrollDirection = 0; - - m_activeMouseSelect.start = viewportToGlobal(event->pos()); - - if (event->button() == Qt::LeftButton && event->modifiers() & Qt::ControlModifier) { - if (m_linkSelection) { - if (event->modifiers() & Qt::ShiftModifier) { - copyLinkToClipboard(); - return; - } - - if (m_linkSelection->link.targetFilePath.scheme().toString().startsWith("http")) { - QDesktopServices::openUrl(m_linkSelection->link.targetFilePath.toUrl()); - return; - } - - if (m_linkSelection->link.targetFilePath.isDir()) - Core::FileUtils::showInFileSystemView(m_linkSelection->link.targetFilePath); - else - EditorManager::openEditorAt(m_linkSelection->link); - } - return; - } - - if (event->button() == Qt::LeftButton) { - if (std::chrono::system_clock::now() - m_lastDoubleClick < 500ms) { - m_selectLineMode = true; - const Selection newSelection{m_surface->gridToPos( - {0, m_surface->posToGrid(m_selection->start).y()}), - m_surface->gridToPos( - {m_surface->liveSize().width(), - m_surface->posToGrid(m_selection->end).y()}), - false}; - setSelection(newSelection); - } else { - m_selectLineMode = false; - int pos = m_surface->gridToPos(globalToGrid(viewportToGlobal(event->pos()))); - setSelection(Selection{pos, pos, false}); - } - event->accept(); - updateViewport(); - } else if (event->button() == Qt::RightButton) { - if (event->modifiers() & Qt::ShiftModifier) { - QMenu *contextMenu = new QMenu(this); - QAction *configureAction = new QAction(contextMenu); - configureAction->setText(Tr::tr("Configure...")); - connect(configureAction, &QAction::triggered, this, [] { - ICore::showOptionsDialog("Terminal.General"); - }); - - contextMenu->addAction(ActionManager::command(Constants::COPY)->action()); - contextMenu->addAction(ActionManager::command(Constants::PASTE)->action()); - contextMenu->addSeparator(); - contextMenu->addAction(ActionManager::command(Constants::CLEAR_TERMINAL)->action()); - contextMenu->addSeparator(); - contextMenu->addAction(configureAction); - - contextMenu->popup(event->globalPosition().toPoint()); - } else if (m_selection) { - copyToClipboard(); - setSelection(std::nullopt); - } else { - pasteFromClipboard(); - } - } else if (event->button() == Qt::MiddleButton) { - QClipboard *clipboard = QApplication::clipboard(); - if (clipboard->supportsSelection()) { - const QString selectionText = clipboard->text(QClipboard::Selection); - if (!selectionText.isEmpty()) - m_surface->pasteFromClipboard(selectionText); - } else { - m_surface->pasteFromClipboard(textFromSelection()); - } - } -} -void TerminalWidget::mouseMoveEvent(QMouseEvent *event) -{ - if (m_selection && event->buttons() & Qt::LeftButton) { - Selection newSelection = *m_selection; - int scrollVelocity = 0; - if (event->pos().y() < 0) { - scrollVelocity = (event->pos().y()); - } else if (event->pos().y() > viewport()->height()) { - scrollVelocity = (event->pos().y() - viewport()->height()); - } - - if ((scrollVelocity != 0) != m_scrollTimer.isActive()) { - if (scrollVelocity != 0) - m_scrollTimer.start(); - else - m_scrollTimer.stop(); - } - - m_scrollDirection = scrollVelocity; - - if (m_scrollTimer.isActive() && scrollVelocity != 0) { - const std::chrono::milliseconds scrollInterval = 1000ms / qAbs(scrollVelocity); - if (m_scrollTimer.intervalAsDuration() != scrollInterval) - m_scrollTimer.setInterval(scrollInterval); - } - - QPoint posBoundedToViewport = event->pos(); - posBoundedToViewport.setX(qBound(0, posBoundedToViewport.x(), viewport()->width())); - - int start = m_surface->gridToPos(globalToGrid(m_activeMouseSelect.start)); - int newEnd = m_surface->gridToPos(globalToGrid(viewportToGlobal(posBoundedToViewport))); - - if (start > newEnd) { - std::swap(start, newEnd); - } - if (start < 0) - start = 0; - - if (m_selectLineMode) { - newSelection.start = m_surface->gridToPos({0, m_surface->posToGrid(start).y()}); - newSelection.end = m_surface->gridToPos( - {m_surface->liveSize().width(), m_surface->posToGrid(newEnd).y()}); - } else { - newSelection.start = start; - newSelection.end = newEnd; - } - - setSelection(newSelection); - } else if (event->modifiers() & Qt::ControlModifier) { - checkLinkAt(event->pos()); - } else if (m_linkSelection) { - m_linkSelection.reset(); - updateCopyState(); - updateViewport(); - } - - if (m_linkSelection) { - setCursor(Qt::PointingHandCursor); - } else { - setCursor(Qt::IBeamCursor); - } -} - -bool TerminalWidget::checkLinkAt(const QPoint &pos) -{ - const TextAndOffsets hit = textAt(pos); - - if (hit.text.size() > 0) { - QString t = QString::fromUcs4(hit.text.c_str(), hit.text.size()).trimmed(); - t = chopIfEndsWith(t, ':'); - - if (!t.isEmpty()) { - if (t.startsWith("~/")) - t = QDir::homePath() + t.mid(1); - - Link link = Link::fromString(t, true); - - if (!link.targetFilePath.isEmpty() && !link.targetFilePath.isAbsolutePath()) - link.targetFilePath = m_cwd.pathAppended(link.targetFilePath.path()); - - if (link.hasValidTarget() - && (link.targetFilePath.scheme().toString().startsWith("http") - || link.targetFilePath.exists())) { - const LinkSelection newSelection = LinkSelection{{hit.start, hit.end}, link}; - if (!m_linkSelection || *m_linkSelection != newSelection) { - m_linkSelection = newSelection; - updateViewport(); - updateCopyState(); - } - return true; - } - } - } - - if (m_linkSelection) { - m_linkSelection.reset(); - updateCopyState(); - updateViewport(); - } - return false; -} - -void TerminalWidget::mouseReleaseEvent(QMouseEvent *event) -{ - m_scrollTimer.stop(); - - if (m_selection && event->button() == Qt::LeftButton) { - if (m_selection->end - m_selection->start == 0) - setSelection(std::nullopt); - else - setSelection(Selection{m_selection->start, m_selection->end, true}); - } -} - -TerminalWidget::TextAndOffsets TerminalWidget::textAt(const QPoint &pos) const -{ - auto it = m_surface->iteratorAt(globalToGrid(viewportToGlobal(pos))); - auto itRev = m_surface->rIteratorAt(globalToGrid(viewportToGlobal(pos))); - - std::u32string whiteSpaces = U" \t\x00a0"; - - const bool inverted = whiteSpaces.find(*it) != std::u32string::npos || *it == 0; - - auto predicate = [inverted, whiteSpaces](const std::u32string::value_type &ch) { - if (inverted) - return ch != 0 && whiteSpaces.find(ch) == std::u32string::npos; - else - return ch == 0 || whiteSpaces.find(ch) != std::u32string::npos; - }; - - auto itRight = std::find_if(it, m_surface->end(), predicate); - auto itLeft = std::find_if(itRev, m_surface->rend(), predicate); - - std::u32string text; - std::copy(itLeft.base(), it, std::back_inserter(text)); - std::copy(it, itRight, std::back_inserter(text)); - std::transform(text.begin(), text.end(), text.begin(), [](const char32_t &ch) { - return ch == 0 ? U' ' : ch; + QMenu *contextMenu = new QMenu(this); + QAction *configureAction = new QAction(contextMenu); + configureAction->setText(Tr::tr("Configure...")); + connect(configureAction, &QAction::triggered, this, [] { + ICore::showOptionsDialog("Terminal.General"); }); - return {(itLeft.base()).position(), itRight.position(), text}; -} + contextMenu->addAction(ActionManager::command(Constants::COPY)->action()); + contextMenu->addAction(ActionManager::command(Constants::PASTE)->action()); + contextMenu->addSeparator(); + contextMenu->addAction(ActionManager::command(Constants::CLEAR_TERMINAL)->action()); + contextMenu->addSeparator(); + contextMenu->addAction(configureAction); -void TerminalWidget::mouseDoubleClickEvent(QMouseEvent *event) -{ - const auto hit = textAt(event->pos()); - - setSelection(Selection{hit.start, hit.end, true}); - - m_lastDoubleClick = std::chrono::system_clock::now(); - - event->accept(); + contextMenu->popup(mapToGlobal(pos)); } void TerminalWidget::dragEnterEvent(QDragEnterEvent *event) @@ -1544,38 +533,65 @@ void TerminalWidget::showEvent(QShowEvent *event) if (!m_process) setupPty(); - QAbstractScrollArea::showEvent(event); + TerminalView::showEvent(event); +} + +void TerminalWidget::handleEscKey(QKeyEvent *event) +{ + bool sendToTerminal = settings().sendEscapeToTerminal(); + bool send = false; + if (sendToTerminal && event->modifiers() == Qt::NoModifier) + send = true; + else if (!sendToTerminal && event->modifiers() == Qt::ShiftModifier) + send = true; + + if (send) { + event->setModifiers(Qt::NoModifier); + TerminalView::keyPressEvent(event); + return; + } + + if (selection()) { + clearSelection(); + } else { + QAction *returnAction = ActionManager::command(Core::Constants::S_RETURNTOEDITOR) + ->actionForContext(Core::Constants::C_GLOBAL); + QTC_ASSERT(returnAction, return); + returnAction->trigger(); + } } bool TerminalWidget::event(QEvent *event) { - if (TerminalSettings::instance().lockKeyboard() && event->type() == QEvent::ShortcutOverride) { - event->accept(); - return true; - } + if (event->type() == QEvent::ShortcutOverride) { + auto keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Escape && keyEvent->modifiers() == Qt::NoModifier + && settings().sendEscapeToTerminal()) { + event->accept(); + return true; + } - if (event->type() == QEvent::Paint) { - QPainter p(this); - p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]); - return true; + if (settings().lockKeyboard()) { + event->accept(); + return true; + } } if (event->type() == QEvent::KeyPress) { auto k = static_cast<QKeyEvent *>(event); - if (TerminalSettings::instance().lockKeyboard() && m_shortcutMap.tryShortcut(k)) + if (k->key() == Qt::Key_Escape) { + handleEscKey(k); + return true; + } + + if (settings().lockKeyboard() && m_shortcutMap.tryShortcut(k)) return true; keyPressEvent(k); return true; } - if (event->type() == QEvent::KeyRelease) { - auto k = static_cast<QKeyEvent *>(event); - keyReleaseEvent(k); - return true; - } - - return QAbstractScrollArea::event(event); + return TerminalView::event(event); } void TerminalWidget::initActions() @@ -1598,13 +614,21 @@ void TerminalWidget::initActions() moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right")); close.setText(Tr::tr("Close Terminal")); - ActionManager::registerAction(©, Constants::COPY, context) - ->setDefaultKeySequences({QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") : QLatin1String("Ctrl+Shift+C"))}); + auto copyCmd = ActionManager::registerAction(©, Constants::COPY, context); + auto pasteCmd = ActionManager::registerAction(&paste, Constants::PASTE, context); - ActionManager::registerAction(&paste, Constants::PASTE, context) - ->setDefaultKeySequences({QKeySequence( - HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") : QLatin1String("Ctrl+Shift+V"))}); + if (HostOsInfo::isMacHost()) { + copyCmd->setDefaultKeySequence(QKeySequence(QLatin1String("Ctrl+C"))); + pasteCmd->setDefaultKeySequence(QKeySequence(QLatin1String("Ctrl+V"))); + } else if (HostOsInfo::isLinuxHost()) { + copyCmd->setDefaultKeySequence(QKeySequence(QLatin1String("Ctrl+Shift+C"))); + pasteCmd->setDefaultKeySequence(QKeySequence(QLatin1String("Ctrl+Shift+V"))); + } else if (HostOsInfo::isWindowsHost()) { + copyCmd->setDefaultKeySequences( + {QKeySequence(QLatin1String("Ctrl+C")), QKeySequence(QLatin1String("Ctrl+Shift+C"))}); + pasteCmd->setDefaultKeySequences( + {QKeySequence(QLatin1String("Ctrl+V")), QKeySequence(QLatin1String("Ctrl+Shift+V"))}); + } ActionManager::registerAction(&clearSelection, Constants::CLEARSELECTION, context); diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 2d0f6b16df7..e30366bf0cc 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -3,85 +3,39 @@ #pragma once +#include "shellintegration.h" #include "shortcutmap.h" -#include "terminalsearch.h" -#include "terminalsurface.h" #include <aggregation/aggregate.h> -#include <coreplugin/icontext.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/icontext.h> +#include <coreplugin/terminal/searchableterminal.h> #include <utils/link.h> #include <utils/process.h> #include <utils/terminalhooks.h> -#include <QAbstractScrollArea> -#include <QAction> -#include <QTextLayout> -#include <QTimer> +#include <QFutureWatcher> -#include <chrono> #include <memory> namespace Terminal { using RegisteredAction = std::unique_ptr<QAction, std::function<void(QAction *)>>; -class TerminalWidget : public QAbstractScrollArea +class TerminalWidget : public Core::SearchableTerminal { - friend class CellIterator; Q_OBJECT public: TerminalWidget(QWidget *parent = nullptr, const Utils::Terminal::OpenTerminalParameters &openParameters = {}); - void setFont(const QFont &font); - - void copyToClipboard(); - void pasteFromClipboard(); - void copyLinkToClipboard(); - - void clearSelection(); - - void zoomIn(); - void zoomOut(); - - void moveCursorWordLeft(); - void moveCursorWordRight(); - - void clearContents(); - void closeTerminal(); - TerminalSearch *search() { return m_search.get(); } - - struct Selection - { - int start; - int end; - bool final{false}; - - bool operator!=(const Selection &other) const - { - return start != other.start || end != other.end || final != other.final; - } - - bool operator==(const Selection &other) const { return !operator!=(other); } - }; - - struct LinkSelection : public Selection - { - Utils::Link link; - - bool operator!=(const LinkSelection &other) const - { - return link != other.link || Selection::operator!=(other); - } - }; - void setShellName(const QString &shellName); QString shellName() const; + QString title() const; Utils::FilePath cwd() const; Utils::CommandLine currentCommand() const; @@ -98,151 +52,54 @@ signals: void started(qint64 pid); void cwdChanged(const Utils::FilePath &cwd); void commandChanged(const Utils::CommandLine &cmd); + void titleChanged(); protected: - void paintEvent(QPaintEvent *event) override; - void keyPressEvent(QKeyEvent *event) override; - void keyReleaseEvent(QKeyEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - void wheelEvent(QWheelEvent *event) override; - void focusInEvent(QFocusEvent *event) override; - void focusOutEvent(QFocusEvent *event) override; - void inputMethodEvent(QInputMethodEvent *event) override; - - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void dragEnterEvent(QDragEnterEvent *event) override; void dropEvent(QDropEvent *event) override; - void showEvent(QShowEvent *event) override; - + void focusInEvent(QFocusEvent *event) override; bool event(QEvent *event) override; -protected: void onReadyRead(bool forceFlush); - void setupSurface(); void setupFont(); void setupPty(); void setupColors(); void setupActions(); - qint64 writeToPty(const QByteArray &data); + void handleEscKey(QKeyEvent *event); - int paintCell(QPainter &p, - const QRectF &cellRect, - QPoint gridPos, - const Internal::TerminalCell &cell, - QFont &f, - QList<SearchHit>::const_iterator &searchIt) const; - void paintCells(QPainter &painter, QPaintEvent *event) const; - void paintCursor(QPainter &painter) const; - void paintPreedit(QPainter &painter) const; - bool paintFindMatches(QPainter &painter, - QList<SearchHit>::const_iterator &searchIt, - const QRectF &cellRect, - const QPoint gridPos) const; - bool paintSelection(QPainter &painter, const QRectF &cellRect, const QPoint gridPos) const; - void paintDebugSelection(QPainter &painter, const Selection &selection) const; + void surfaceChanged() override; - qreal topMargin() const; + void selectionChanged(const std::optional<Selection> &newSelection) override; + void linkActivated(const Link &link) override; + void contextMenuRequested(const QPoint &pos) override; - QPoint viewportToGlobal(QPoint p) const; - QPoint globalToViewport(QPoint p) const; - QPoint globalToGrid(QPointF p) const; - QPointF gridToGlobal(QPoint p, bool bottom = false, bool right = false) const; - QRect gridToViewport(QRect rect) const; - - void updateViewport(); - void updateViewportRect(const QRect &rect); - - int textLineFromPixel(int y) const; - std::optional<int> textPosFromPoint(const QTextLayout &textLayout, QPoint p) const; - - std::optional<QTextLayout::FormatRange> selectionToFormatRange( - TerminalWidget::Selection selection, const QTextLayout &layout, int rowOffset) const; - - bool checkLinkAt(const QPoint &pos); - - struct TextAndOffsets - { - int start; - int end; - std::u32string text; - }; - - TextAndOffsets textAt(const QPoint &pos) const; - - void applySizeChange(); - void updateScrollBars(); - - void flushVTerm(bool force); - - bool setSelection(const std::optional<Selection> &selection, bool scroll = true); - QString textFromSelection() const; - - void configBlinkTimer(); - - QColor toQColor(std::variant<int, QColor> color) const; - - void updateCopyState(); + qint64 writeToPty(const QByteArray &data) override; + void resizePty(QSize newSize) override; + void setClipboard(const QString &text) override; + std::optional<TerminalView::Link> toLink(const QString &text) override; RegisteredAction registerAction(Utils::Id commandId, const Core::Context &context); void registerShortcut(Core::Command *command); + void updateCopyState(); + private: Core::Context m_context; std::unique_ptr<Utils::Process> m_process; - std::unique_ptr<Internal::TerminalSurface> m_surface; std::unique_ptr<ShellIntegration> m_shellIntegration; QString m_shellName; + QString m_title; + Utils::Id m_identifier; - QFont m_font; - QSizeF m_cellSize; - - bool m_ignoreScroll{false}; - - QString m_preEditString; - - std::optional<Selection> m_selection; - std::optional<LinkSelection> m_linkSelection; - - struct - { - QPoint start; - QPoint end; - } m_activeMouseSelect; - - QTimer m_flushDelayTimer; - - QTimer m_scrollTimer; - int m_scrollDirection{0}; - - std::array<QColor, 20> m_currentColors; - Utils::Terminal::OpenTerminalParameters m_openParameters; - std::chrono::system_clock::time_point m_lastFlush; - std::chrono::system_clock::time_point m_lastDoubleClick; - bool m_selectLineMode{false}; - - Internal::Cursor m_cursor; - QTimer m_cursorBlinkTimer; - bool m_cursorBlinkState{true}; - Utils::FilePath m_cwd; Utils::CommandLine m_currentCommand; - using TerminalSearchPtr = std::unique_ptr<TerminalSearch, std::function<void(TerminalSearch *)>>; - TerminalSearchPtr m_search; - - Aggregation::Aggregate *m_aggregate{nullptr}; - SearchHit m_lastSelectedHit{}; - RegisteredAction m_copy; RegisteredAction m_paste; RegisteredAction m_clearSelection; @@ -252,6 +109,8 @@ private: RegisteredAction m_close; Internal::ShortcutMap m_shortcutMap; + + std::unique_ptr<QFutureWatcher<Utils::expected_str<Utils::FilePath>>> m_findShellWatcher; }; } // namespace Terminal diff --git a/src/plugins/terminal/tests/copy b/src/plugins/terminal/tests/copy new file mode 100755 index 00000000000..fdd9e71d187 --- /dev/null +++ b/src/plugins/terminal/tests/copy @@ -0,0 +1,45 @@ +#!/bin/bash + +function set_clipboard() { + b=$(echo -ne "$2" | base64); + printf "\033]52;$1;$b\a\n" +} + +function test_clipboard() { + echo -e "\033[1m ⎆ The Clipboard should now contain '$2'\033[0m" + + set_clipboard "$1" "$2" + + read -p " ⎆ Press enter to continue " -n1 -s + echo + echo +} + +test_clipboard "c" "copypasta!" +test_clipboard "p" "Mask: p" +test_clipboard "q" "Mask: q" +test_clipboard "s" "Mask: s" +test_clipboard "cpqs" "Mask: cpqs" + +echo -e "\033[1m ⎆ The Clipboard should now be empty\033[0m" +printf "\033]52;cpqs;\a\n" + +read -p " ⎆ Press enter to continue " -n1 -s +echo +echo + +echo -e "\033[1m ⎆ The Clipboard should still be empty\033[0m" +set_clipboard "01234567" "Hello World!" + +read -p " ⎆ Press enter to continue " -n1 -s +echo +echo + +test_clipboard "c01234567" "Hello World!" + +echo -e "\033[1m ⎆ The Clipboard should now contain the source of terminalsurface.cpp\033[0m" +printf "\033]52;c;Ly8gQ29weXJpZ2h0IChDKSAyMDIyIFRoZSBRdCBDb21wYW55IEx0ZC4KLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IExpY2Vuc2VSZWYtUXQtQ29tbWVyY2lhbCBPUiBHUEwtMy4wKyBPUiBHUEwtMy4wIFdJVEggUXQtR1BMLWV4Y2VwdGlvbi0xLjAKCiNpbmNsdWRlICJ0ZXJtaW5hbHN1cmZhY2UuaCIKI2luY2x1ZGUgInN1cmZhY2VpbnRlZ3JhdGlvbi5oIgoKI2luY2x1ZGUgImtleXMuaCIKI2luY2x1ZGUgInNjcm9sbGJhY2suaCIKCiNpbmNsdWRlIDx2dGVybS5oPgoKI2luY2x1ZGUgPFFMb2dnaW5nQ2F0ZWdvcnk+CiNpbmNsdWRlIDxRVGltZXI+CgpuYW1lc3BhY2UgVGVybWluYWxTb2x1dGlvbiB7CgpRX0xPR0dJTkdfQ0FURUdPUlkobG9nLCAicXRjLnRlcm1pbmFsLnN1cmZhY2UiLCBRdFdhcm5pbmdNc2cpOwoKUUNvbG9yIHRvUUNvbG9yKGNvbnN0IFZUZXJtQ29sb3IgJmMpCnsKICAgIHJldHVybiBRQ29sb3IocVJnYihjLnJnYi5yZWQsIGMucmdiLmdyZWVuLCBjLnJnYi5ibHVlKSk7Cn07Cgpjb25zdGV4cHIgaW50IGJhdGNoRmx1c2hTaXplID0gMjU2OwoKc3RydWN0IFRlcm1pbmFsU3VyZmFjZVByaXZhdGUKewogICAgVGVybWluYWxTdXJmYWNlUHJpdmF0ZShUZXJtaW5hbFN1cmZhY2UgKnN1cmZhY2UsIGNvbnN0IFFTaXplICZpbml0aWFsR3JpZFNpemUpCiAgICAgICAgOiBtX3Z0ZXJtKHZ0ZXJtX25ldyhpbml0aWFsR3JpZFNpemUuaGVpZ2h0KCksIGluaXRpYWxHcmlkU2l6ZS53aWR0aCgpKSwgdnRlcm1fZnJlZSkKICAgICAgICAsIG1fdnRlcm1TY3JlZW4odnRlcm1fb2J0YWluX3NjcmVlbihtX3Z0ZXJtLmdldCgpKSkKICAgICAgICAsIG1fc2Nyb2xsYmFjayhzdGQ6Om1ha2VfdW5pcXVlPFNjcm9sbGJhY2s+KDUwMDApKQogICAgICAgICwgcShzdXJmYWNlKQogICAge30KCiAgICB2b2lkIGZsdXNoKCkKICAgIHsKICAgICAgICBpZiAobV93cml0ZUJ1ZmZlci5pc0VtcHR5KCkpCiAgICAgICAgICAgIHJldHVybjsKCiAgICAgICAgUUJ5dGVBcnJheSBkYXRhID0gbV93cml0ZUJ1ZmZlci5sZWZ0KGJhdGNoRmx1c2hTaXplKTsKICAgICAgICBxaW50NjQgcmVzdWx0ID0gbV93cml0ZVRvUHR5KGRhdGEpOwoKICAgICAgICBpZiAocmVzdWx0ICE9IGRhdGEuc2l6ZSgpKSB7CiAgICAgICAgICAgIC8vIE5vdCBhbGwgZGF0YSB3YXMgd3JpdHRlbiwgcmVtb3ZlIHRoZSB1bndyaXR0ZW4gZGF0YSBmcm9tIHRoZSBhcnJheQogICAgICAgICAgICBkYXRhLnJlc2l6ZShxTWF4KDAsIHJlc3VsdCkpOwogICAgICAgIH0KCiAgICAgICAgLy8gUmVtb3ZlIHRoZSB3cml0dGVuIGRhdGEgZnJvbSB0aGUgYnVmZmVyCiAgICAgICAgaWYgKGRhdGEuc2l6ZSgpID4gMCkKICAgICAgICAgICAgbV93cml0ZUJ1ZmZlciA9IG1fd3JpdGVCdWZmZXIubWlkKGRhdGEuc2l6ZSgpKTsKCiAgICAgICAgaWYgKCFtX3dyaXRlQnVmZmVyLmlzRW1wdHkoKSkKICAgICAgICAgICAgbV9kZWxheVdyaXRlVGltZXIuc3RhcnQoKTsKICAgIH0KCiAgICB2b2lkIGluaXQoKQogICAgewogICAgICAgIG1fZGVsYXlXcml0ZVRpbWVyLnNldEludGVydmFsKDEpOwogICAgICAgIG1fZGVsYXlXcml0ZVRpbWVyLnNldFNpbmdsZVNob3QodHJ1ZSk7CgogICAgICAgIFFPYmplY3Q6OmNvbm5lY3QoJm1fZGVsYXlXcml0ZVRpbWVyLCAmUVRpbWVyOjp0aW1lb3V0LCAmbV9kZWxheVdyaXRlVGltZXIsIFt0aGlzXSB7CiAgICAgICAgICAgIGZsdXNoKCk7CiAgICAgICAgfSk7CgogICAgICAgIHZ0ZXJtX3NldF91dGY4KG1fdnRlcm0uZ2V0KCksIHRydWUpOwoKICAgICAgICBzdGF0aWMgYXV0byB3cml0ZVRvUHR5ID0gW10oY29uc3QgY2hhciAqcywgc2l6ZV90IGxlbiwgdm9pZCAqdXNlcikgewogICAgICAgICAgICBhdXRvIHAgPSBzdGF0aWNfY2FzdDxUZXJtaW5hbFN1cmZhY2VQcml2YXRlICo+KHVzZXIpOwogICAgICAgICAgICBRQnl0ZUFycmF5IGQocywgbGVuKTsKCiAgICAgICAgICAgIC8vIElmIGl0cyBqdXN0IGEgY291cGxlIG9mIGNoYXJzLCBvciB3ZSBhbHJlYWR5IGhhdmUgZGF0YSBpbiB0aGUgd3JpdGVCdWZmZXIsCiAgICAgICAgICAgIC8vIGFkZCB0aGUgbmV3IGRhdGEgdG8gdGhlIHdyaXRlIGJ1ZmZlciBhbmQgc3RhcnQgdGhlIGRlbGF5IHRpbWVyCiAgICAgICAgICAgIGlmIChkLnNpemUoKSA8IGJhdGNoRmx1c2hTaXplIHx8ICFwLT5tX3dyaXRlQnVmZmVyLmlzRW1wdHkoKSkgewogICAgICAgICAgICAgICAgcC0+bV93cml0ZUJ1ZmZlci5hcHBlbmQoZCk7CiAgICAgICAgICAgICAgICBwLT5tX2RlbGF5V3JpdGVUaW1lci5zdGFydCgpOwogICAgICAgICAgICAgICAgcmV0dXJuOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBUcnkgdG8gd3JpdGUgdGhlIGRhdGEgLi4uCiAgICAgICAgICAgIHFpbnQ2NCByZXN1bHQgPSBwLT5tX3dyaXRlVG9QdHkoZCk7CgogICAgICAgICAgICBpZiAocmVzdWx0ICE9IGQuc2l6ZSgpKSB7CiAgICAgICAgICAgICAgICAvLyBpZiB3cml0aW5nIGZhaWxlZCwgYXBwZW5kIHRoZSBkYXRhIHRvIHRoZSB3cml0ZUJ1ZmZlciBhbmQgc3RhcnQgdGhlIGRlbGF5IHRpbWVyCgogICAgICAgICAgICAgICAgLy8gQ2hlY2sgaWYgcGFydGlhbCBkYXRhIG1heSBoYXZlIGFscmVhZHkgYmVlbiB3cml0dGVuIC4uLgogICAgICAgICAgICAgICAgaWYgKHJlc3VsdCA8PSAwKQogICAgICAgICAgICAgICAgICAgIHAtPm1fd3JpdGVCdWZmZXIuYXBwZW5kKGQpOwogICAgICAgICAgICAgICAgZWxzZQogICAgICAgICAgICAgICAgICAgIHAtPm1fd3JpdGVCdWZmZXIuYXBwZW5kKGQubWlkKHJlc3VsdCkpOwoKICAgICAgICAgICAgICAgIHAtPm1fZGVsYXlXcml0ZVRpbWVyLnN0YXJ0KCk7CiAgICAgICAgICAgIH0KICAgICAgICB9OwoKICAgICAgICB2dGVybV9vdXRwdXRfc2V0X2NhbGxiYWNrKG1fdnRlcm0uZ2V0KCksIHdyaXRlVG9QdHksIHRoaXMpOwoKICAgICAgICBtZW1zZXQoJm1fdnRlcm1TY3JlZW5DYWxsYmFja3MsIDAsIHNpemVvZihtX3Z0ZXJtU2NyZWVuQ2FsbGJhY2tzKSk7CgogICAgICAgIG1fdnRlcm1TY3JlZW5DYWxsYmFja3MuZGFtYWdlID0gW10oVlRlcm1SZWN0IHJlY3QsIHZvaWQgKnVzZXIpIHsKICAgICAgICAgICAgYXV0byBwID0gc3RhdGljX2Nhc3Q8VGVybWluYWxTdXJmYWNlUHJpdmF0ZSAqPih1c2VyKTsKICAgICAgICAgICAgcC0+aW52YWxpZGF0ZShyZWN0KTsKICAgICAgICAgICAgcmV0dXJuIDE7CiAgICAgICAgfTsKICAgICAgICBtX3Z0ZXJtU2NyZWVuQ2FsbGJhY2tzLnNiX3B1c2hsaW5lID0gW10oaW50IGNvbHMsIGNvbnN0IFZUZXJtU2NyZWVuQ2VsbCAqY2VsbHMsIHZvaWQgKnVzZXIpIHsKICAgICAgICAgICAgYXV0byBwID0gc3RhdGljX2Nhc3Q8VGVybWluYWxTdXJmYWNlUHJpdmF0ZSAqPih1c2VyKTsKICAgICAgICAgICAgcmV0dXJuIHAtPnNiX3B1c2hsaW5lKGNvbHMsIGNlbGxzKTsKICAgICAgICB9OwogICAgICAgIG1fdnRlcm1TY3JlZW5DYWxsYmFja3Muc2JfcG9wbGluZSA9IFtdKGludCBjb2xzLCBWVGVybVNjcmVlbkNlbGwgKmNlbGxzLCB2b2lkICp1c2VyKSB7CiAgICAgICAgICAgIGF1dG8gcCA9IHN0YXRpY19jYXN0PFRlcm1pbmFsU3VyZmFjZVByaXZhdGUgKj4odXNlcik7CiAgICAgICAgICAgIHJldHVybiBwLT5zYl9wb3BsaW5lKGNvbHMsIGNlbGxzKTsKICAgICAgICB9OwogICAgICAgIG1fdnRlcm1TY3JlZW5DYWxsYmFja3Muc2V0dGVybXByb3AgPSBbXShWVGVybVByb3AgcHJvcCwgVlRlcm1WYWx1ZSAqdmFsLCB2b2lkICp1c2VyKSB7CiAgICAgICAgICAgIGF1dG8gcCA9IHN0YXRpY19jYXN0PFRlcm1pbmFsU3VyZmFjZVByaXZhdGUgKj4odXNlcik7CiAgICAgICAgICAgIHJldHVybiBwLT5zZXRUZXJtaW5hbFByb3BlcnRpZXMocHJvcCwgdmFsKTsKICAgICAgICB9OwogICAgICAgIG1fdnRlcm1TY3JlZW5DYWxsYmFja3MubW92ZWN1cnNvciA9CiAgICAgICAgICAgIFtdKFZUZXJtUG9zIHBvcywgVlRlcm1Qb3Mgb2xkcG9zLCBpbnQgdmlzaWJsZSwgdm9pZCAqdXNlcikgewogICAgICAgICAgICAgICAgYXV0byBwID0gc3RhdGljX2Nhc3Q8VGVybWluYWxTdXJmYWNlUHJpdmF0ZSAqPih1c2VyKTsKICAgICAgICAgICAgICAgIHJldHVybiBwLT5tb3ZlY3Vyc29yKHBvcywgb2xkcG9zLCB2aXNpYmxlKTsKICAgICAgICAgICAgfTsKICAgICAgICBtX3Z0ZXJtU2NyZWVuQ2FsbGJhY2tzLnNiX2NsZWFyID0gW10odm9pZCAqdXNlcikgewogICAgICAgICAgICBhdXRvIHAgPSBzdGF0aWNfY2FzdDxUZXJtaW5hbFN1cmZhY2VQcml2YXRlICo+KHVzZXIpOwogICAgICAgICAgICByZXR1cm4gcC0+c2JfY2xlYXIoKTsKICAgICAgICB9OwogICAgICAgIG1fdnRlcm1TY3JlZW5DYWxsYmFja3MuYmVsbCA9IFtdKHZvaWQgKnVzZXIpIHsKICAgICAgICAgICAgYXV0byBwID0gc3RhdGljX2Nhc3Q8VGVybWluYWxTdXJmYWNlUHJpdmF0ZSAqPih1c2VyKTsKICAgICAgICAgICAgaWYgKHAtPm1fc3VyZmFjZUludGVncmF0aW9uKQogICAgICAgICAgICAgICAgcC0+bV9zdXJmYWNlSW50ZWdyYXRpb24tPm9uQmVsbCgpOwogICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICB9OwoKICAgICAgICB2dGVybV9zY3JlZW5fc2V0X2NhbGxiYWNrcyhtX3Z0ZXJtU2NyZWVuLCAmbV92dGVybVNjcmVlbkNhbGxiYWNrcywgdGhpcyk7CiAgICAgICAgdnRlcm1fc2NyZWVuX3NldF9kYW1hZ2VfbWVyZ2UobV92dGVybVNjcmVlbiwgVlRFUk1fREFNQUdFX1NDUk9MTCk7CiAgICAgICAgdnRlcm1fc2NyZWVuX2VuYWJsZV9hbHRzY3JlZW4obV92dGVybVNjcmVlbiwgdHJ1ZSk7CgogICAgICAgIG1lbXNldCgmbV92dGVybVN0YXRlRmFsbGJhY2tzLCAwLCBzaXplb2YobV92dGVybVN0YXRlRmFsbGJhY2tzKSk7CgogICAgICAgIG1fdnRlcm1TdGF0ZUZhbGxiYWNrcy5vc2MgPSBbXShpbnQgY21kLCBWVGVybVN0cmluZ0ZyYWdtZW50IGZyYWdtZW50LCB2b2lkICp1c2VyKSB7CiAgICAgICAgICAgIGF1dG8gcCA9IHN0YXRpY19jYXN0PFRlcm1pbmFsU3VyZmFjZVByaXZhdGUgKj4odXNlcik7CiAgICAgICAgICAgIHJldHVybiBwLT5vc2MoY21kLCBmcmFnbWVudCk7CiAgICAgICAgfTsKCiAgICAgICAgVlRlcm1TdGF0ZSAqdnRzID0gdnRlcm1fb2J0YWluX3N0YXRlKG1fdnRlcm0uZ2V0KCkpOwogICAgICAgIHZ0ZXJtX3N0YXRlX3NldF91bnJlY29nbmlzZWRfZmFsbGJhY2tzKHZ0cywgJm1fdnRlcm1TdGF0ZUZhbGxiYWNrcywgdGhpcyk7CgogICAgICAgIG1lbXNldCgmbV92dGVybVNlbGVjdGlvbkNhbGxiYWNrcywgMCwgc2l6ZW9mKG1fdnRlcm1TZWxlY3Rpb25DYWxsYmFja3MpKTsKCiAgICAgICAgbV92dGVybVNlbGVjdGlvbkNhbGxiYWNrcy5xdWVyeSA9IFtdKFZUZXJtU2VsZWN0aW9uTWFzayBtYXNrLCB2b2lkICp1c2VyKSB7CiAgICAgICAgICAgIGlmICghKG1hc2sgJiAweEYpKQogICAgICAgICAgICAgICAgcmV0dXJuIDA7CgogICAgICAgICAgICBhdXRvIHAgPSBzdGF0aWNfY2FzdDxUZXJtaW5hbFN1cmZhY2VQcml2YXRlICo+KHVzZXIpOwogICAgICAgICAgICBpZiAocC0+bV9zdXJmYWNlSW50ZWdyYXRpb24pCiAgICAgICAgICAgICAgICBwLT5tX3N1cmZhY2VJbnRlZ3JhdGlvbi0+b25HZXRDbGlwYm9hcmQoKTsKCiAgICAgICAgICAgIHJldHVybiAwOwogICAgICAgIH07CgogICAgICAgIG1fdnRlcm1TZWxlY3Rpb25DYWxsYmFja3Muc2V0ID0KICAgICAgICAgICAgW10oVlRlcm1TZWxlY3Rpb25NYXNrIG1hc2ssIFZUZXJtU3RyaW5nRnJhZ21lbnQgZnJhZywgdm9pZCAqdXNlcikgewogICAgICAgICAgICAgICAgaWYgKCEobWFzayAmIDB4RikpCiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIDA7CgogICAgICAgICAgICAgICAgYXV0byBwID0gc3RhdGljX2Nhc3Q8VGVybWluYWxTdXJmYWNlUHJpdmF0ZSAqPih1c2VyKTsKICAgICAgICAgICAgICAgIGlmIChmcmFnLmluaXRpYWwpCiAgICAgICAgICAgICAgICAgICAgcC0+bV9zZWxlY3Rpb25CdWZmZXIuY2xlYXIoKTsKCiAgICAgICAgICAgICAgICBwLT5tX3NlbGVjdGlvbkJ1ZmZlci5hcHBlbmQoZnJhZy5zdHIsIGZyYWcubGVuKTsKICAgICAgICAgICAgICAgIGlmICghZnJhZy5maW5hbCkKICAgICAgICAgICAgICAgICAgICByZXR1cm4gMTsKCiAgICAgICAgICAgICAgICBpZiAocC0+bV9zdXJmYWNlSW50ZWdyYXRpb24pCiAgICAgICAgICAgICAgICAgICAgcC0+bV9zdXJmYWNlSW50ZWdyYXRpb24tPm9uU2V0Q2xpcGJvYXJkKHAtPm1fc2VsZWN0aW9uQnVmZmVyKTsKCiAgICAgICAgICAgICAgICByZXR1cm4gMTsKICAgICAgICAgICAgfTsKCiAgICAgICAgdnRlcm1fc3RhdGVfc2V0X3NlbGVjdGlvbl9jYWxsYmFja3ModnRzLCAmbV92dGVybVNlbGVjdGlvbkNhbGxiYWNrcywgdGhpcywgbnVsbHB0ciwgMjU2KTsKCiAgICAgICAgdnRlcm1fc3RhdGVfc2V0X2JvbGRfaGlnaGJyaWdodCh2dHMsIHRydWUpOwoKICAgICAgICBWVGVybUNvbG9yIGZnOwogICAgICAgIFZUZXJtQ29sb3IgYmc7CiAgICAgICAgdnRlcm1fY29sb3JfaW5kZXhlZCgmZmcsIENvbG9ySW5kZXg6OkZvcmVncm91bmQpOwogICAgICAgIHZ0ZXJtX2NvbG9yX2luZGV4ZWQoJmJnLCBDb2xvckluZGV4OjpCYWNrZ3JvdW5kKTsKICAgICAgICB2dGVybV9zdGF0ZV9zZXRfZGVmYXVsdF9jb2xvcnModnRzLCAmZmcsICZiZyk7CgogICAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgMTY7ICsraSkgewogICAgICAgICAgICBWVGVybUNvbG9yIGNvbDsKICAgICAgICAgICAgdnRlcm1fY29sb3JfaW5kZXhlZCgmY29sLCBpKTsKICAgICAgICAgICAgdnRlcm1fc3RhdGVfc2V0X3BhbGV0dGVfY29sb3IodnRzLCBpLCAmY29sKTsKICAgICAgICB9CgogICAgICAgIHZ0ZXJtX3NjcmVlbl9yZXNldChtX3Z0ZXJtU2NyZWVuLCAxKTsKICAgIH0KCiAgICBRU2l6ZSBsaXZlU2l6ZSgpIGNvbnN0CiAgICB7CiAgICAgICAgaW50IHJvd3M7CiAgICAgICAgaW50IGNvbHM7CiAgICAgICAgdnRlcm1fZ2V0X3NpemUobV92dGVybS5nZXQoKSwgJnJvd3MsICZjb2xzKTsKCiAgICAgICAgcmV0dXJuIFFTaXplKGNvbHMsIHJvd3MpOwogICAgfQoKICAgIHN0ZDo6dmFyaWFudDxpbnQsIFFDb2xvcj4gdG9WYXJpYW50Q29sb3IoY29uc3QgVlRlcm1Db2xvciAmY29sb3IpCiAgICB7CiAgICAgICAgaWYgKGNvbG9yLnR5cGUgJiBWVEVSTV9DT0xPUl9ERUZBVUxUX0JHKQogICAgICAgICAgICByZXR1cm4gQ29sb3JJbmRleDo6QmFja2dyb3VuZDsKICAgICAgICBlbHNlIGlmIChjb2xvci50eXBlICYgVlRFUk1fQ09MT1JfREVGQVVMVF9GRykKICAgICAgICAgICAgcmV0dXJuIENvbG9ySW5kZXg6OkZvcmVncm91bmQ7CiAgICAgICAgZWxzZSBpZiAoY29sb3IudHlwZSAmIFZURVJNX0NPTE9SX0lOREVYRUQpIHsKICAgICAgICAgICAgaWYgKGNvbG9yLmluZGV4ZWQuaWR4ID49IDE2KSB7CiAgICAgICAgICAgICAgICBWVGVybUNvbG9yIGMgPSBjb2xvcjsKICAgICAgICAgICAgICAgIHZ0ZXJtX3N0YXRlX2NvbnZlcnRfY29sb3JfdG9fcmdiKHZ0ZXJtX29idGFpbl9zdGF0ZShtX3Z0ZXJtLmdldCgpKSwgJmMpOwogICAgICAgICAgICAgICAgcmV0dXJuIHRvUUNvbG9yKGMpOwogICAgICAgICAgICB9CiAgICAgICAgICAgIHJldHVybiBjb2xvci5pbmRleGVkLmlkeDsKICAgICAgICB9IGVsc2UgaWYgKGNvbG9yLnR5cGUgPT0gVlRFUk1fQ09MT1JfUkdCKQogICAgICAgICAgICByZXR1cm4gdG9RQ29sb3IoY29sb3IpOwogICAgICAgIGVsc2UKICAgICAgICAgICAgcmV0dXJuIC0xOwogICAgfQoKICAgIFRlcm1pbmFsQ2VsbCB0b0NlbGwoY29uc3QgVlRlcm1TY3JlZW5DZWxsICZjZWxsKQogICAgewogICAgICAgIFRlcm1pbmFsQ2VsbCByZXN1bHQ7CiAgICAgICAgcmVzdWx0LndpZHRoID0gY2VsbC53aWR0aDsKICAgICAgICByZXN1bHQudGV4dCA9IFFTdHJpbmc6OmZyb21VY3M0KGNlbGwuY2hhcnMpOwoKICAgICAgICBjb25zdCBWVGVybUNvbG9yICpiZyA9ICZjZWxsLmJnOwogICAgICAgIGNvbnN0IFZUZXJtQ29sb3IgKmZnID0gJmNlbGwuZmc7CgogICAgICAgIGlmIChzdGF0aWNfY2FzdDxib29sPihjZWxsLmF0dHJzLnJldmVyc2UpKQogICAgICAgICAgICBzdGQ6OnN3YXAoZmcsIGJnKTsKCiAgICAgICAgcmVzdWx0LmJhY2tncm91bmRDb2xvciA9IHRvVmFyaWFudENvbG9yKCpiZyk7CiAgICAgICAgcmVzdWx0LmZvcmVncm91bmRDb2xvciA9IHRvVmFyaWFudENvbG9yKCpmZyk7CgogICAgICAgIHJlc3VsdC5ib2xkID0gY2VsbC5hdHRycy5ib2xkOwogICAgICAgIHJlc3VsdC5zdHJpa2VPdXQgPSBjZWxsLmF0dHJzLnN0cmlrZTsKCiAgICAgICAgaWYgKGNlbGwuYXR0cnMudW5kZXJsaW5lID4gMCkgewogICAgICAgICAgICByZXN1bHQudW5kZXJsaW5lU3R5bGUgPSBRVGV4dENoYXJGb3JtYXQ6Ok5vVW5kZXJsaW5lOwogICAgICAgICAgICBzd2l0Y2ggKGNlbGwuYXR0cnMudW5kZXJsaW5lKSB7CiAgICAgICAgICAgIGNhc2UgVlRFUk1fVU5ERVJMSU5FX1NJTkdMRToKICAgICAgICAgICAgICAgIHJlc3VsdC51bmRlcmxpbmVTdHlsZSA9IFFUZXh0Q2hhckZvcm1hdDo6U2luZ2xlVW5kZXJsaW5lOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgVlRFUk1fVU5ERVJMSU5FX0RPVUJMRToKICAgICAgICAgICAgICAgIC8vIFRPRE86IERvdWJsZSB1bmRlcmxpbmUKICAgICAgICAgICAgICAgIHJlc3VsdC51bmRlcmxpbmVTdHlsZSA9IFFUZXh0Q2hhckZvcm1hdDo6U2luZ2xlVW5kZXJsaW5lOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgVlRFUk1fVU5ERVJMSU5FX0NVUkxZOgogICAgICAgICAgICAgICAgcmVzdWx0LnVuZGVybGluZVN0eWxlID0gUVRleHRDaGFyRm9ybWF0OjpXYXZlVW5kZXJsaW5lOwogICAgICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgICAgIGNhc2UgVlRFUk1fVU5ERVJMSU5FX0RBU0hFRDoKICAgICAgICAgICAgICAgIHJlc3VsdC51bmRlcmxpbmVTdHlsZSA9IFFUZXh0Q2hhckZvcm1hdDo6RGFzaFVuZGVybGluZTsKICAgICAgICAgICAgICAgIGJyZWFrOwogICAgICAgICAgICBjYXNlIFZURVJNX1VOREVSTElORV9ET1RURUQ6CiAgICAgICAgICAgICAgICByZXN1bHQudW5kZXJsaW5lU3R5bGUgPSBRVGV4dENoYXJGb3JtYXQ6OkRvdExpbmU7CiAgICAgICAgICAgICAgICBicmVhazsKICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgcmVzdWx0LnN0cmlrZU91dCA9IGNlbGwuYXR0cnMuc3RyaWtlOwoKICAgICAgICByZXR1cm4gcmVzdWx0OwogICAgfQoKICAgIC8vIENhbGxiYWNrcyBmcm9tIHZ0ZXJtCiAgICB2b2lkIGludmFsaWRhdGUoVlRlcm1SZWN0IHJlY3QpCiAgICB7CiAgICAgICAgaWYgKCFtX2FsdHNjcmVlbikgewogICAgICAgICAgICByZWN0LnN0YXJ0X3JvdyArPSBtX3Njcm9sbGJhY2stPnNpemUoKTsKICAgICAgICAgICAgcmVjdC5lbmRfcm93ICs9IG1fc2Nyb2xsYmFjay0+c2l6ZSgpOwogICAgICAgIH0KCiAgICAgICAgZW1pdCBxLT5pbnZhbGlkYXRlZCgKICAgICAgICAgICAgUVJlY3R7UVBvaW50e3JlY3Quc3RhcnRfY29sLCByZWN0LnN0YXJ0X3Jvd30sIFFQb2ludHtyZWN0LmVuZF9jb2wsIHJlY3QuZW5kX3JvdyAtIDF9fSk7CiAgICB9CgogICAgaW50IHNiX3B1c2hsaW5lKGludCBjb2xzLCBjb25zdCBWVGVybVNjcmVlbkNlbGwgKmNlbGxzKQogICAgewogICAgICAgIG1fc2Nyb2xsYmFjay0+ZW1wbGFjZShjb2xzLCBjZWxscyk7CiAgICAgICAgZW1pdCBxLT5mdWxsU2l6ZUNoYW5nZWQocS0+ZnVsbFNpemUoKSk7CiAgICAgICAgcmV0dXJuIDE7CiAgICB9CgogICAgaW50IHNiX3BvcGxpbmUoaW50IGNvbHMsIFZUZXJtU2NyZWVuQ2VsbCAqY2VsbHMpCiAgICB7CiAgICAgICAgaWYgKG1fc2Nyb2xsYmFjay0+c2l6ZSgpID09IDApCiAgICAgICAgICAgIHJldHVybiAwOwoKICAgICAgICBtX3Njcm9sbGJhY2stPnBvcHRvKGNvbHMsIGNlbGxzKTsKICAgICAgICBlbWl0IHEtPmZ1bGxTaXplQ2hhbmdlZChxLT5mdWxsU2l6ZSgpKTsKICAgICAgICByZXR1cm4gMTsKICAgIH0KCiAgICBpbnQgc2JfY2xlYXIoKQogICAgewogICAgICAgIG1fc2Nyb2xsYmFjay0+Y2xlYXIoKTsKICAgICAgICBlbWl0IHEtPmZ1bGxTaXplQ2hhbmdlZChxLT5mdWxsU2l6ZSgpKTsKICAgICAgICByZXR1cm4gMTsKICAgIH0KCiAgICBpbnQgb3NjKGludCBjbWQsIGNvbnN0IFZUZXJtU3RyaW5nRnJhZ21lbnQgJmZyYWdtZW50KQogICAgewogICAgICAgIGlmIChtX3N1cmZhY2VJbnRlZ3JhdGlvbikgewogICAgICAgICAgICBtX3N1cmZhY2VJbnRlZ3JhdGlvbi0+b25Pc2MoY21kLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge2ZyYWdtZW50LnN0ciwgZnJhZ21lbnQubGVufSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyYWdtZW50LmluaXRpYWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcmFnbWVudC5maW5hbCk7CiAgICAgICAgfQoKICAgICAgICByZXR1cm4gMTsKICAgIH0KCiAgICBpbnQgc2V0VGVybWluYWxQcm9wZXJ0aWVzKFZUZXJtUHJvcCBwcm9wLCBWVGVybVZhbHVlICp2YWwpCiAgICB7CiAgICAgICAgc3dpdGNoIChwcm9wKSB7CiAgICAgICAgY2FzZSBWVEVSTV9QUk9QX0NVUlNPUlZJU0lCTEU6IHsKICAgICAgICAgICAgQ3Vyc29yIG9sZCA9IHEtPmN1cnNvcigpOwogICAgICAgICAgICBtX2N1cnNvci52aXNpYmxlID0gdmFsLT5ib29sZWFuOwogICAgICAgICAgICBxLT5jdXJzb3JDaGFuZ2VkKG9sZCwgcS0+Y3Vyc29yKCkpOwogICAgICAgICAgICBicmVhazsKICAgICAgICB9CiAgICAgICAgY2FzZSBWVEVSTV9QUk9QX0NVUlNPUkJMSU5LOiB7CiAgICAgICAgICAgIEN1cnNvciBvbGQgPSBxLT5jdXJzb3IoKTsKICAgICAgICAgICAgbV9jdXJzb3IuYmxpbmsgPSB2YWwtPmJvb2xlYW47CiAgICAgICAgICAgIGVtaXQgcS0+Y3Vyc29yQ2hhbmdlZChvbGQsIHEtPmN1cnNvcigpKTsKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgfQogICAgICAgIGNhc2UgVlRFUk1fUFJPUF9DVVJTT1JTSEFQRTogewogICAgICAgICAgICBDdXJzb3Igb2xkID0gcS0+Y3Vyc29yKCk7CiAgICAgICAgICAgIG1fY3Vyc29yLnNoYXBlID0gKEN1cnNvcjo6U2hhcGUpIHZhbC0+bnVtYmVyOwogICAgICAgICAgICBlbWl0IHEtPmN1cnNvckNoYW5nZWQob2xkLCBxLT5jdXJzb3IoKSk7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIH0KICAgICAgICBjYXNlIFZURVJNX1BST1BfSUNPTk5BTUU6CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIGNhc2UgVlRFUk1fUFJPUF9USVRMRToKICAgICAgICAgICAgaWYgKG1fc3VyZmFjZUludGVncmF0aW9uKQogICAgICAgICAgICAgICAgbV9zdXJmYWNlSW50ZWdyYXRpb24tPm9uVGl0bGUoUVN0cmluZzo6ZnJvbVV0ZjgodmFsLT5zdHJpbmcuc3RyLCB2YWwtPnN0cmluZy5sZW4pKTsKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgY2FzZSBWVEVSTV9QUk9QX0FMVFNDUkVFTjoKICAgICAgICAgICAgbV9hbHRzY3JlZW4gPSB2YWwtPmJvb2xlYW47CiAgICAgICAgICAgIGVtaXQgcS0+YWx0c2NyZWVuQ2hhbmdlZChtX2FsdHNjcmVlbik7CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIGNhc2UgVlRFUk1fUFJPUF9NT1VTRToKICAgICAgICAgICAgcUNEZWJ1Zyhsb2cpIDw8ICJJZ25vcmluZyBWVEVSTV9QUk9QX01PVVNFIiA8PCB2YWwtPm51bWJlcjsKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgY2FzZSBWVEVSTV9QUk9QX1JFVkVSU0U6CiAgICAgICAgICAgIHFDRGVidWcobG9nKSA8PCAiSWdub3JpbmcgVlRFUk1fUFJPUF9SRVZFUlNFIiA8PCB2YWwtPmJvb2xlYW47CiAgICAgICAgICAgIGJyZWFrOwogICAgICAgIGNhc2UgVlRFUk1fTl9QUk9QUzoKICAgICAgICAgICAgYnJlYWs7CiAgICAgICAgfQogICAgICAgIHJldHVybiAxOwogICAgfQogICAgaW50IG1vdmVjdXJzb3IoVlRlcm1Qb3MgcG9zLCBWVGVybVBvcyBvbGRwb3MsIGludCB2aXNpYmxlKQogICAgewogICAgICAgIFFfVU5VU0VEKG9sZHBvcyk7CiAgICAgICAgQ3Vyc29yIG9sZEN1cnNvciA9IHEtPmN1cnNvcigpOwogICAgICAgIG1fY3Vyc29yLnBvc2l0aW9uID0ge3Bvcy5jb2wsIHBvcy5yb3d9OwogICAgICAgIG1fY3Vyc29yLnZpc2libGUgPSB2aXNpYmxlID4gMDsKICAgICAgICBxLT5jdXJzb3JDaGFuZ2VkKG9sZEN1cnNvciwgcS0+Y3Vyc29yKCkpOwogICAgICAgIHJldHVybiAxOwogICAgfQoKICAgIGNvbnN0IFZUZXJtU2NyZWVuQ2VsbCAqY2VsbEF0KGludCB4LCBpbnQgeSkKICAgIHsKICAgICAgICBpZiAoeSA8IDAgfHwgeCA8IDAgfHwgeSA+PSBxLT5mdWxsU2l6ZSgpLmhlaWdodCgpIHx8IHggPj0gbGl2ZVNpemUoKS53aWR0aCgpKSB7CiAgICAgICAgICAgIHFDV2FybmluZyhsb2cpIDw8ICJJbnZhbGlkIFBhcmFtZXRlciBmb3IgY2VsbEF0OiIgPDwgeCA8PCB5IDw8ICJsaXZlU2l6ZToiIDw8IGxpdmVTaXplKCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgPDwgImZ1bGxTaXplOiIgPDwgcS0+ZnVsbFNpemUoKTsKICAgICAgICAgICAgcmV0dXJuIG51bGxwdHI7CiAgICAgICAgfQoKICAgICAgICBpZiAoIW1fYWx0c2NyZWVuICYmIHkgPCBtX3Njcm9sbGJhY2stPnNpemUoKSkgewogICAgICAgICAgICBjb25zdCBhdXRvICZzYmwgPSBtX3Njcm9sbGJhY2stPmxpbmUoKG1fc2Nyb2xsYmFjay0+c2l6ZSgpIC0gMSkgLSB5KTsKICAgICAgICAgICAgaWYgKHggPCBzYmwuY29scygpKSB7CiAgICAgICAgICAgICAgICByZXR1cm4gc2JsLmNlbGwoeCk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcmV0dXJuIG51bGxwdHI7CiAgICAgICAgfQoKICAgICAgICBpZiAoIW1fYWx0c2NyZWVuKQogICAgICAgICAgICB5IC09IG1fc2Nyb2xsYmFjay0+c2l6ZSgpOwoKICAgICAgICBzdGF0aWMgVlRlcm1TY3JlZW5DZWxsIHJlZkNlbGx7fTsKICAgICAgICBWVGVybVBvcyB2dHB7eSwgeH07CiAgICAgICAgdnRlcm1fc2NyZWVuX2dldF9jZWxsKG1fdnRlcm1TY3JlZW4sIHZ0cCwgJnJlZkNlbGwpOwoKICAgICAgICByZXR1cm4gJnJlZkNlbGw7CiAgICB9CgogICAgc3RkOjp1bmlxdWVfcHRyPFZUZXJtLCB2b2lkICgqKShWVGVybSAqKT4gbV92dGVybTsKICAgIFZUZXJtU2NyZWVuICptX3Z0ZXJtU2NyZWVuOwogICAgVlRlcm1TY3JlZW5DYWxsYmFja3MgbV92dGVybVNjcmVlbkNhbGxiYWNrczsKICAgIFZUZXJtU3RhdGVGYWxsYmFja3MgbV92dGVybVN0YXRlRmFsbGJhY2tzOwoKICAgIFZUZXJtU2VsZWN0aW9uQ2FsbGJhY2tzIG1fdnRlcm1TZWxlY3Rpb25DYWxsYmFja3M7CgogICAgQ3Vyc29yIG1fY3Vyc29yOwogICAgUVN0cmluZyBtX2N1cnJlbnRDb21tYW5kOwoKICAgIGJvb2wgbV9hbHRzY3JlZW57ZmFsc2V9OwoKICAgIHN0ZDo6dW5pcXVlX3B0cjxTY3JvbGxiYWNrPiBtX3Njcm9sbGJhY2s7CgogICAgU3VyZmFjZUludGVncmF0aW9uICptX3N1cmZhY2VJbnRlZ3JhdGlvbntudWxscHRyfTsKCiAgICBUZXJtaW5hbFN1cmZhY2UgKnE7CiAgICBRVGltZXIgbV9kZWxheVdyaXRlVGltZXI7CiAgICBRQnl0ZUFycmF5IG1fd3JpdGVCdWZmZXI7CiAgICBRQnl0ZUFycmF5IG1fc2VsZWN0aW9uQnVmZmVyOwoKICAgIFRlcm1pbmFsU3VyZmFjZTo6V3JpdGVUb1B0eSBtX3dyaXRlVG9QdHk7Cn07CgpUZXJtaW5hbFN1cmZhY2U6OlRlcm1pbmFsU3VyZmFjZShRU2l6ZSBpbml0aWFsR3JpZFNpemUpCiAgICA6IGQoc3RkOjptYWtlX3VuaXF1ZTxUZXJtaW5hbFN1cmZhY2VQcml2YXRlPih0aGlzLCBpbml0aWFsR3JpZFNpemUpKQp7CiAgICBkLT5pbml0KCk7Cn0KClRlcm1pbmFsU3VyZmFjZTo6flRlcm1pbmFsU3VyZmFjZSgpID0gZGVmYXVsdDsKCmludCBUZXJtaW5hbFN1cmZhY2U6OmNlbGxXaWR0aEF0KGludCB4LCBpbnQgeSkgY29uc3QKewogICAgY29uc3QgVlRlcm1TY3JlZW5DZWxsICpjZWxsID0gZC0+Y2VsbEF0KHgsIHkpOwogICAgaWYgKCFjZWxsKQogICAgICAgIHJldHVybiAwOwogICAgcmV0dXJuIGNlbGwtPndpZHRoOwp9CgpRU2l6ZSBUZXJtaW5hbFN1cmZhY2U6OmxpdmVTaXplKCkgY29uc3QKewogICAgcmV0dXJuIGQtPmxpdmVTaXplKCk7Cn0KClFTaXplIFRlcm1pbmFsU3VyZmFjZTo6ZnVsbFNpemUoKSBjb25zdAp7CiAgICBpZiAoZC0+bV9hbHRzY3JlZW4pCiAgICAgICAgcmV0dXJuIGxpdmVTaXplKCk7CiAgICByZXR1cm4gUVNpemV7ZC0+bGl2ZVNpemUoKS53aWR0aCgpLCBkLT5saXZlU2l6ZSgpLmhlaWdodCgpICsgZC0+bV9zY3JvbGxiYWNrLT5zaXplKCl9Owp9CgpzdGQ6OnUzMnN0cmluZzo6dmFsdWVfdHlwZSBUZXJtaW5hbFN1cmZhY2U6OmZldGNoQ2hhckF0KGludCB4LCBpbnQgeSkgY29uc3QKewogICAgY29uc3QgVlRlcm1TY3JlZW5DZWxsICpjZWxsID0gZC0+Y2VsbEF0KHgsIHkpOwogICAgaWYgKCFjZWxsKQogICAgICAgIHJldHVybiAwOwoKICAgIGlmIChjZWxsLT53aWR0aCA9PSAwKQogICAgICAgIHJldHVybiAwOwoKICAgIFFTdHJpbmcgcyA9IFFTdHJpbmc6OmZyb21VY3M0KGNlbGwtPmNoYXJzLCA2KS5ub3JtYWxpemVkKFFTdHJpbmc6Ok5vcm1hbGl6YXRpb25Gb3JtX0MpOwogICAgY29uc3QgUUxpc3Q8dWludD4gdWNzNCA9IHMudG9VY3M0KCk7CiAgICByZXR1cm4gc3RkOjp1MzJzdHJpbmcodWNzNC5iZWdpbigpLCB1Y3M0LmVuZCgpKS5mcm9udCgpOwp9CgpUZXJtaW5hbENlbGwgVGVybWluYWxTdXJmYWNlOjpmZXRjaENlbGwoaW50IHgsIGludCB5KSBjb25zdAp7CiAgICBzdGF0aWMgVGVybWluYWxDZWxsIGVtcHR5Q2VsbHsxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge30sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7fSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29sb3JJbmRleDo6Rm9yZWdyb3VuZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbG9ySW5kZXg6OkJhY2tncm91bmQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRVGV4dENoYXJGb3JtYXQ6Ok5vVW5kZXJsaW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2V9OwoKICAgIGlmICh5IDwgMCB8fCB5ID49IGZ1bGxTaXplKCkuaGVpZ2h0KCkgfHwgeCA+PSBmdWxsU2l6ZSgpLndpZHRoKCkpIHsKICAgICAgICBxQ1dhcm5pbmcobG9nKSA8PCAiSW52YWxpZCBQYXJhbWV0ZXIgZm9yIGZldGNoQ2VsbDoiIDw8IHggPDwgeSA8PCAiZnVsbFNpemU6IiA8PCBmdWxsU2l6ZSgpOwogICAgICAgIHJldHVybiBlbXB0eUNlbGw7CiAgICB9CgogICAgY29uc3QgVlRlcm1TY3JlZW5DZWxsICpyZWZDZWxsID0gZC0+Y2VsbEF0KHgsIHkpOwogICAgaWYgKCFyZWZDZWxsKQogICAgICAgIHJldHVybiBlbXB0eUNlbGw7CgogICAgcmV0dXJuIGQtPnRvQ2VsbCgqcmVmQ2VsbCk7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpjbGVhckFsbCgpCnsKICAgIC8vIEZha2UgYSBzY3JvbGxiYWNrIGNsZWFyaW5nCiAgICBRQnl0ZUFycmF5IGRhdGF7Ilx4MWJbM0oifTsKICAgIHZ0ZXJtX2lucHV0X3dyaXRlKGQtPm1fdnRlcm0uZ2V0KCksIGRhdGEuY29uc3REYXRhKCksIGRhdGEuc2l6ZSgpKTsKCiAgICAvLyBTZW5kIEN0cmwrTCB3aGljaCB3aWxsIGNsZWFyIHRoZSBzY3JlZW4KICAgIGQtPm1fd3JpdGVUb1B0eShRQnl0ZUFycmF5KCJcZiIpKTsKfQoKdm9pZCBUZXJtaW5hbFN1cmZhY2U6OnJlc2l6ZShRU2l6ZSBuZXdTaXplKQp7CiAgICB2dGVybV9zZXRfc2l6ZShkLT5tX3Z0ZXJtLmdldCgpLCBuZXdTaXplLmhlaWdodCgpLCBuZXdTaXplLndpZHRoKCkpOwp9CgpRUG9pbnQgVGVybWluYWxTdXJmYWNlOjpwb3NUb0dyaWQoaW50IHBvcykgY29uc3QKewogICAgcmV0dXJuIHtwb3MgJSBkLT5saXZlU2l6ZSgpLndpZHRoKCksIHBvcyAvIGQtPmxpdmVTaXplKCkud2lkdGgoKX07Cn0KaW50IFRlcm1pbmFsU3VyZmFjZTo6Z3JpZFRvUG9zKFFQb2ludCBncmlkUG9zKSBjb25zdAp7CiAgICByZXR1cm4gZ3JpZFBvcy55KCkgKiBkLT5saXZlU2l6ZSgpLndpZHRoKCkgKyBncmlkUG9zLngoKTsKfQoKdm9pZCBUZXJtaW5hbFN1cmZhY2U6OmRhdGFGcm9tUHR5KGNvbnN0IFFCeXRlQXJyYXkgJmRhdGEpCnsKICAgIHZ0ZXJtX2lucHV0X3dyaXRlKGQtPm1fdnRlcm0uZ2V0KCksIGRhdGEuY29uc3REYXRhKCksIGRhdGEuc2l6ZSgpKTsKICAgIHZ0ZXJtX3NjcmVlbl9mbHVzaF9kYW1hZ2UoZC0+bV92dGVybVNjcmVlbik7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpmbHVzaCgpCnsKICAgIHZ0ZXJtX3NjcmVlbl9mbHVzaF9kYW1hZ2UoZC0+bV92dGVybVNjcmVlbik7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpwYXN0ZUZyb21DbGlwYm9hcmQoY29uc3QgUVN0cmluZyAmY2xpcGJvYXJkVGV4dCkKewogICAgaWYgKGNsaXBib2FyZFRleHQuaXNFbXB0eSgpKQogICAgICAgIHJldHVybjsKCiAgICB2dGVybV9rZXlib2FyZF9zdGFydF9wYXN0ZShkLT5tX3Z0ZXJtLmdldCgpKTsKICAgIGZvciAodW5zaWduZWQgaW50IGNoIDogY2xpcGJvYXJkVGV4dC50b1VjczQoKSkgewogICAgICAgIC8vIFdvcmthcm91bmQgZm9yIHdlaXJkIG5hbm8gYmVoYXZpb3IgdG8gY29ycmVjdGx5IHBhc3RlIG5ld2xpbmVzCiAgICAgICAgLy8gc2VlOiBodHRwOi8vc2F2YW5uYWguZ251Lm9yZy9idWdzLz80OTE3NgogICAgICAgIC8vIGFuZDogaHR0cHM6Ly9naXRodWIuY29tL2tvdmlkZ295YWwva2l0dHkvaXNzdWVzLzk5NAogICAgICAgIGlmIChjaCA9PSAnXG4nKQogICAgICAgICAgICBjaCA9ICdccic7CiAgICAgICAgdnRlcm1fa2V5Ym9hcmRfdW5pY2hhcihkLT5tX3Z0ZXJtLmdldCgpLCBjaCwgVlRFUk1fTU9EX05PTkUpOwogICAgfQogICAgdnRlcm1fa2V5Ym9hcmRfZW5kX3Bhc3RlKGQtPm1fdnRlcm0uZ2V0KCkpOwoKICAgIGlmICghZC0+bV9hbHRzY3JlZW4pIHsKICAgICAgICBlbWl0IHVuc2Nyb2xsKCk7CiAgICB9Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpzZW5kS2V5KFF0OjpLZXkga2V5KQp7CiAgICBpZiAoa2V5ID09IFF0OjpLZXlfRXNjYXBlKQogICAgICAgIHZ0ZXJtX2tleWJvYXJkX2tleShkLT5tX3Z0ZXJtLmdldCgpLCBWVEVSTV9LRVlfRVNDQVBFLCBWVEVSTV9NT0RfTk9ORSk7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpzZW5kS2V5KGNvbnN0IFFTdHJpbmcgJnRleHQpCnsKICAgIGZvciAoY29uc3QgdW5zaWduZWQgaW50IGNoIDogdGV4dC50b1VjczQoKSkKICAgICAgICB2dGVybV9rZXlib2FyZF91bmljaGFyKGQtPm1fdnRlcm0uZ2V0KCksIGNoLCBWVEVSTV9NT0RfTk9ORSk7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpzZW5kS2V5KFFLZXlFdmVudCAqZXZlbnQpCnsKICAgIGJvb2wga2V5cGFkID0gZXZlbnQtPm1vZGlmaWVycygpICYgUXQ6OktleXBhZE1vZGlmaWVyOwogICAgVlRlcm1Nb2RpZmllciBtb2QgPSBxdE1vZGlmaWVyVG9WVGVybShldmVudC0+bW9kaWZpZXJzKCkpOwogICAgVlRlcm1LZXkga2V5ID0gcXRLZXlUb1ZUZXJtKFF0OjpLZXkoZXZlbnQtPmtleSgpKSwga2V5cGFkKTsKCiAgICBpZiAoa2V5ICE9IFZURVJNX0tFWV9OT05FKSB7CiAgICAgICAgaWYgKG1vZCA9PSBWVEVSTV9NT0RfU0hJRlQgJiYgKGtleSA9PSBWVEVSTV9LRVlfRVNDQVBFIHx8IGtleSA9PSBWVEVSTV9LRVlfQkFDS1NQQUNFKSkKICAgICAgICAgICAgbW9kID0gVlRFUk1fTU9EX05PTkU7CgogICAgICAgIHZ0ZXJtX2tleWJvYXJkX2tleShkLT5tX3Z0ZXJtLmdldCgpLCBrZXksIG1vZCk7CiAgICB9IGVsc2UgaWYgKGV2ZW50LT50ZXh0KCkubGVuZ3RoKCkgPT0gMSkgewogICAgICAgIC8vIFRoaXMgbWFwcyB0byBkZWxldGUgd29yZCBhbmQgaXMgd2F5IHRvIGVhc3kgdG8gbWlzdGFrZW5seSB0eXBlCiAgICAgICAgLy8gICAgICAgIGlmIChldmVudC0+a2V5KCkgPT0gUXQ6OktleV9TcGFjZSAmJiBtb2QgPT0gVlRFUk1fTU9EX1NISUZUKQogICAgICAgIC8vICAgICAgICAgICAgbW9kID0gVlRFUk1fTU9EX05PTkU7CgogICAgICAgIC8vIFBlciBodHRwczovL2dpdGh1Yi5jb20vanVzdGlubWsvbmVvdmltL2NvbW1pdC8zMTdkNWNhN2IwZjkyZWY0MmRlOTg5YjM1NTZjYTk1MDNmMGEzYmY2CiAgICAgICAgLy8gbGlidnRlcm0gcHJlZmVycyB3ZSBzZW5kIHRoZSBmdWxsIGtleWNvZGUgcmF0aGVyIHRoYW4gc2VuZGluZyB0aGUKICAgICAgICAvLyBjdHJsIG1vZGlmaWVyLiAgVGhpcyBoZWxwcyB3aXRoIG5jdXJzZXMgYXBwbGljYXRpb25zIHdoaWNoIG90aGVyd2lzZQogICAgICAgIC8vIGRvIG5vdCByZWNvZ25pemUgY3RybCs8a2V5PiBhbmQgaW4gdGhlIHNoZWxsIGZvciBnZXR0aW5nIGNvbW1vbiBjb250cm9sIGNoYXJhY3RlcnMKICAgICAgICAvLyBsaWtlIGN0cmwraSBmb3IgdGFiIG9yIGN0cmwraiBmb3IgbmV3bGluZS4KCiAgICAgICAgLy8gV29ya2Fyb3VuZCBmb3IgIkFMVCtTSElGVCsvIiAoXCBvbiBnZXJtYW4gbWFjIGtleWJvYXJkcykKICAgICAgICBpZiAobW9kID09IChWVEVSTV9NT0RfU0hJRlQgfCBWVEVSTV9NT0RfQUxUKSAmJiBldmVudC0+a2V5KCkgPT0gUXQ6OktleV9TbGFzaCkgewogICAgICAgICAgICBtb2QgPSBWVEVSTV9NT0RfTk9ORTsKICAgICAgICB9CgogICAgICAgIHZ0ZXJtX2tleWJvYXJkX3VuaWNoYXIoZC0+bV92dGVybS5nZXQoKSwgZXZlbnQtPnRleHQoKS50b1VjczQoKVswXSwgVlRFUk1fTU9EX05PTkUpOwogICAgfSBlbHNlIGlmIChtb2QgPT0gVlRFUk1fTU9EX0NUUkwgJiYgZXZlbnQtPmtleSgpID49IFF0OjpLZXlfQSAmJiBldmVudC0+a2V5KCkgPCBRdDo6S2V5X1opIHsKICAgICAgICB2dGVybV9rZXlib2FyZF91bmljaGFyKGQtPm1fdnRlcm0uZ2V0KCksICdhJyArIChldmVudC0+a2V5KCkgLSBRdDo6S2V5X0EpLCBtb2QpOwogICAgfQp9CgpDdXJzb3IgVGVybWluYWxTdXJmYWNlOjpjdXJzb3IoKSBjb25zdAp7CiAgICBDdXJzb3IgY3Vyc29yID0gZC0+bV9jdXJzb3I7CiAgICBpZiAoIWQtPm1fYWx0c2NyZWVuKQogICAgICAgIGN1cnNvci5wb3NpdGlvbi5zZXRZKGN1cnNvci5wb3NpdGlvbi55KCkgKyBkLT5tX3Njcm9sbGJhY2stPnNpemUoKSk7CgogICAgcmV0dXJuIGN1cnNvcjsKfQoKU3VyZmFjZUludGVncmF0aW9uICpUZXJtaW5hbFN1cmZhY2U6OnN1cmZhY2VJbnRlZ3JhdGlvbigpIGNvbnN0CnsKICAgIHJldHVybiBkLT5tX3N1cmZhY2VJbnRlZ3JhdGlvbjsKfQoKdm9pZCBUZXJtaW5hbFN1cmZhY2U6OnNldFN1cmZhY2VJbnRlZ3JhdGlvbihTdXJmYWNlSW50ZWdyYXRpb24gKnN1cmZhY2VJbnRlZ3JhdGlvbikKewogICAgZC0+bV9zdXJmYWNlSW50ZWdyYXRpb24gPSBzdXJmYWNlSW50ZWdyYXRpb247Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjptb3VzZU1vdmUoUVBvaW50IHBvcywgUXQ6OktleWJvYXJkTW9kaWZpZXJzIG1vZGlmaWVycykKewogICAgdnRlcm1fbW91c2VfbW92ZShkLT5tX3Z0ZXJtLmdldCgpLCBwb3MueSgpLCBwb3MueCgpLCBxdE1vZGlmaWVyVG9WVGVybShtb2RpZmllcnMpKTsKfQoKdm9pZCBUZXJtaW5hbFN1cmZhY2U6Om1vdXNlQnV0dG9uKFF0OjpNb3VzZUJ1dHRvbiBidXR0b24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBib29sIHByZXNzZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRdDo6S2V5Ym9hcmRNb2RpZmllcnMgbW9kaWZpZXJzKQp7CiAgICBpbnQgYnRuSWR4ID0gMDsKICAgIHN3aXRjaCAoYnV0dG9uKSB7CiAgICBjYXNlIFF0OjpMZWZ0QnV0dG9uOgogICAgICAgIGJ0bklkeCA9IDE7CiAgICAgICAgYnJlYWs7CiAgICBjYXNlIFF0OjpSaWdodEJ1dHRvbjoKICAgICAgICBidG5JZHggPSAzOwogICAgICAgIGJyZWFrOwogICAgY2FzZSBRdDo6TWlkZGxlQnV0dG9uOgogICAgICAgIGJ0bklkeCA9IDI7CiAgICAgICAgYnJlYWs7CiAgICBjYXNlIFF0OjpFeHRyYUJ1dHRvbjE6CiAgICAgICAgYnRuSWR4ID0gNDsKICAgICAgICBicmVhazsKICAgIGNhc2UgUXQ6OkV4dHJhQnV0dG9uMjoKICAgICAgICBidG5JZHggPSA1OwogICAgICAgIGJyZWFrOwogICAgZGVmYXVsdDoKICAgICAgICByZXR1cm47CiAgICB9CgogICAgdnRlcm1fbW91c2VfYnV0dG9uKGQtPm1fdnRlcm0uZ2V0KCksIGJ0bklkeCwgcHJlc3NlZCwgcXRNb2RpZmllclRvVlRlcm0obW9kaWZpZXJzKSk7Cn0KCnZvaWQgVGVybWluYWxTdXJmYWNlOjpzZXRXcml0ZVRvUHR5KFdyaXRlVG9QdHkgd3JpdGVUb1B0eSkKewogICAgZC0+bV93cml0ZVRvUHR5ID0gd3JpdGVUb1B0eTsKfQoKQ2VsbEl0ZXJhdG9yIFRlcm1pbmFsU3VyZmFjZTo6YmVnaW4oKSBjb25zdAp7CiAgICBhdXRvIHJlcyA9IENlbGxJdGVyYXRvcih0aGlzLCB7MCwgMH0pOwogICAgcmVzLm1fc3RhdGUgPSBDZWxsSXRlcmF0b3I6OlN0YXRlOjpCRUdJTjsKICAgIHJldHVybiByZXM7Cn0KCkNlbGxJdGVyYXRvciBUZXJtaW5hbFN1cmZhY2U6OmVuZCgpIGNvbnN0CnsKICAgIHJldHVybiBDZWxsSXRlcmF0b3IodGhpcyk7Cn0KCnN0ZDo6cmV2ZXJzZV9pdGVyYXRvcjxDZWxsSXRlcmF0b3I+IFRlcm1pbmFsU3VyZmFjZTo6cmJlZ2luKCkgY29uc3QKewogICAgcmV0dXJuIHN0ZDo6bWFrZV9yZXZlcnNlX2l0ZXJhdG9yKGVuZCgpKTsKfQoKc3RkOjpyZXZlcnNlX2l0ZXJhdG9yPENlbGxJdGVyYXRvcj4gVGVybWluYWxTdXJmYWNlOjpyZW5kKCkgY29uc3QKewogICAgcmV0dXJuIHN0ZDo6bWFrZV9yZXZlcnNlX2l0ZXJhdG9yKGJlZ2luKCkpOwp9CgpDZWxsSXRlcmF0b3IgVGVybWluYWxTdXJmYWNlOjppdGVyYXRvckF0KFFQb2ludCBwb3MpIGNvbnN0CnsKICAgIHJldHVybiBDZWxsSXRlcmF0b3IodGhpcywgcG9zKTsKfQpDZWxsSXRlcmF0b3IgVGVybWluYWxTdXJmYWNlOjppdGVyYXRvckF0KGludCBwb3MpIGNvbnN0CnsKICAgIHJldHVybiBDZWxsSXRlcmF0b3IodGhpcywgcG9zKTsKfQoKc3RkOjpyZXZlcnNlX2l0ZXJhdG9yPENlbGxJdGVyYXRvcj4gVGVybWluYWxTdXJmYWNlOjpySXRlcmF0b3JBdChRUG9pbnQgcG9zKSBjb25zdAp7CiAgICByZXR1cm4gc3RkOjptYWtlX3JldmVyc2VfaXRlcmF0b3IoaXRlcmF0b3JBdChwb3MpKTsKfQoKc3RkOjpyZXZlcnNlX2l0ZXJhdG9yPENlbGxJdGVyYXRvcj4gVGVybWluYWxTdXJmYWNlOjpySXRlcmF0b3JBdChpbnQgcG9zKSBjb25zdAp7CiAgICByZXR1cm4gc3RkOjptYWtlX3JldmVyc2VfaXRlcmF0b3IoaXRlcmF0b3JBdChwb3MpKTsKfQoKfSAvLyBuYW1lc3BhY2UgVGVybWluYWxTb2x1dGlvbgo=\a\n" + +read -p " ⎆ Press enter to continue " -n1 -s +echo +echo diff --git a/src/plugins/terminal/tests/mouse b/src/plugins/terminal/tests/mouse new file mode 100755 index 00000000000..3134454bb38 --- /dev/null +++ b/src/plugins/terminal/tests/mouse @@ -0,0 +1,67 @@ +#!/bin/sh + + +function cleanup { + stty -echo + printf "\e[?1006;1000l" +} + +trap cleanup EXIT + +stty -echo + +# Enable SGR protocol and button press and release events +printf "\e[?1006;1000h" + +while read -n 1 line +do + printf -v ch "%d" \'$line + if [ "27" != "$ch" ]; then + continue + fi + read -n 1 line + if [ "[" != "$line" ]; then + continue + fi + read -n 1 line + if [ "<" != "$line" ]; then + continue + fi + # Read button state + modifier= + while read -n 1 line + do + if [ ";" = "$line" ]; then + # End + break + fi + printf -v modifier "$modifier$line" + done + # Read column + col= + while read -n 1 line + do + if [ ";" = "$line" ]; then + # End + break + fi + printf -v col "$col$line" + done + # Read row + row= + while read -n 1 line + do + if [ "M" = "$line" ] || [ "m" = "$line" ]; then + # End + btn=$line + break + fi + printf -v row "$row$line" + done + if [ "M" = "$btn" ]; then + echo "You pressed at $col x $row (mods: $modifier)" + else + echo "You released at $col x $row (mods: $modifier)" + fi +done < "${1:-/dev/stdin}" + diff --git a/src/plugins/terminal/tests/title b/src/plugins/terminal/tests/title new file mode 100755 index 00000000000..d6dc5193125 --- /dev/null +++ b/src/plugins/terminal/tests/title @@ -0,0 +1,4 @@ +#!/bin/bash + +echo -e "\033[1m ⎆ The terminal title should have changed to 'Test Title'\033[0m" +echo -e "\033]0;Test Title\007" diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index 2a99947b933..03efc189224 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -16,6 +16,9 @@ add_qtc_plugin(TextEditor behaviorsettingspage.cpp behaviorsettingspage.h behaviorsettingswidget.cpp behaviorsettingswidget.h blockrange.h + bookmark.cpp bookmark.h + bookmarkfilter.cpp bookmarkfilter.h + bookmarkmanager.cpp bookmarkmanager.h circularclipboard.cpp circularclipboard.h circularclipboardassist.cpp circularclipboardassist.h codeassist/assistenums.h @@ -68,6 +71,7 @@ add_qtc_plugin(TextEditor icodestylepreferencesfactory.cpp icodestylepreferencesfactory.h indenter.h ioutlinewidget.h + jsoneditor.cpp jsoneditor.h linenumberfilter.cpp linenumberfilter.h marginsettings.cpp marginsettings.h markdowneditor.cpp markdowneditor.h diff --git a/src/plugins/texteditor/TextEditor.json.in b/src/plugins/texteditor/TextEditor.json.in index a2aefaf146f..5fbf3e3067b 100644 --- a/src/plugins/texteditor/TextEditor.json.in +++ b/src/plugins/texteditor/TextEditor.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"TextEditor\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "TextEditor", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Core\", - \"Description\" : \"Text editor framework and the implementation of the basic text editor.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Core", + "Description" : "Text editor framework and the implementation of the basic text editor.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 285d42fd3d9..4bda6757745 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -13,28 +13,25 @@ #include <coreplugin/documentmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/find/ifindsupport.h> +#include <coreplugin/find/textfindconstants.h> #include <coreplugin/icore.h> #include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/progressmanager.h> #include <utils/algorithm.h> #include <utils/fadingindicator.h> -#include <utils/filesearch.h> #include <utils/futuresynchronizer.h> #include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/stylehelper.h> -#include <QDebug> -#include <QSettings> +#include <QComboBox> +#include <QFutureWatcher> #include <QHash> +#include <QLabel> #include <QPair> +#include <QPointer> #include <QPromise> #include <QStringListModel> -#include <QFutureWatcher> -#include <QPointer> -#include <QComboBox> -#include <QLabel> using namespace Utils; using namespace Core; @@ -142,26 +139,14 @@ public: QString title() const override { return Tr::tr("Internal"); } QString toolTip() const override { return {}; } QWidget *widget() const override { return m_widget; } - QVariant parameters() const override { return {}; } - void readSettings(QSettings * /*settings*/) override {} - void writeSettings(QSettings * /*settings*/) const override {} - QFuture<SearchResultItems> executeSearch(const TextEditor::FileFindParameters ¶meters, - BaseFileFind *baseFileFind) override + void readSettings(QtcSettings * /*settings*/) override {} + void writeSettings(QtcSettings * /*settings*/) const override {} + SearchExecutor searchExecutor() const override { - const auto func = parameters.flags & FindRegularExpression ? Utils::findInFilesRegExp - : Utils::findInFiles; - - return func(parameters.text, - baseFileFind->files(parameters.nameFilters, parameters.exclusionFilters, - parameters.additionalParameters), - textDocumentFlagsForFindFlags(parameters.flags), - TextDocument::openedTextDocumentContents()); - - } - Core::IEditor *openEditor(const SearchResultItem &/*item*/, - const TextEditor::FileFindParameters &/*parameters*/) override - { - return nullptr; + return [](const FileFindParameters ¶meters) { + return Utils::findInFiles(parameters.text, parameters.fileContainerProvider(), + parameters.flags, TextDocument::openedTextDocumentContents()); + }; } private: @@ -199,6 +184,7 @@ public: QVector<SearchEngine *> m_searchEngines; InternalEngine m_internalSearchEngine; int m_currentSearchEngineIndex = -1; + FilePath m_searchDir; }; } // namespace Internal @@ -316,16 +302,20 @@ void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags, tooltip.arg(IFindFilter::descriptionForFindFlags(findFlags)), txt, searchMode, SearchResultWindow::PreserveCaseEnabled, QString::fromLatin1("TextEditor")); + setupSearch(search); search->setTextToReplace(txt); search->setSearchAgainSupported(true); + SearchEngine *searchEngine = currentSearchEngine(); FileFindParameters parameters; parameters.text = txt; parameters.flags = findFlags; parameters.nameFilters = fileNameFilters(); parameters.exclusionFilters = fileExclusionFilters(); - parameters.additionalParameters = additionalParameters(); - parameters.searchEngineParameters = currentSearchEngine()->parameters(); - parameters.searchEngineIndex = d->m_currentSearchEngineIndex; + parameters.searchDir = searchDir(); + parameters.fileContainerProvider = fileContainerProvider(); + parameters.editorOpener = searchEngine->editorOpener(); + parameters.searchExecutor = searchEngine->searchExecutor(); + search->setUserData(QVariant::fromValue(parameters)); connect(search, &SearchResult::activated, this, [this, search](const SearchResultItem &item) { openEditor(search, item); @@ -336,10 +326,6 @@ void BaseFileFind::runNewSearch(const QString &txt, FindFlags findFlags, connect(search, &SearchResult::searchAgainRequested, this, [this, search] { searchAgain(search); }); - connect(this, &BaseFileFind::enabledChanged, search, &SearchResult::requestEnabledCheck); - connect(search, &SearchResult::requestEnabledCheck, this, [this, search] { - recheckEnabled(search); - }); runSearch(search); } @@ -365,12 +351,12 @@ void BaseFileFind::runSearch(SearchResult *search) connect(watcher, &QFutureWatcherBase::finished, search, [watcher, search]() { search->finishSearch(watcher->isCanceled()); }); - QFuture<SearchResultItems> future = executeSearch(parameters); + QFuture<SearchResultItems> future = parameters.searchExecutor(parameters); watcher->setFuture(future); d->m_futureSynchronizer.addFuture(future); FutureProgress *progress = ProgressManager::addTask(future, Tr::tr("Searching"), - Constants::TASK_SEARCH); + Core::Constants::TASK_SEARCH); connect(search, &SearchResult::countChanged, progress, [progress](int c) { progress->setSubtitle(Tr::tr("%n found.", nullptr, c)); }); @@ -400,9 +386,8 @@ void BaseFileFind::doReplace(const QString &text, const SearchResultItems &items { const FilePaths files = replaceAll(text, items, preserveCase); if (!files.isEmpty()) { - Utils::FadingIndicator::showText(ICore::dialogParent(), - Tr::tr("%n occurrences replaced.", nullptr, items.size()), - Utils::FadingIndicator::SmallText); + FadingIndicator::showText(ICore::dialogParent(), + Tr::tr("%n occurrences replaced.", nullptr, items.size()), FadingIndicator::SmallText); DocumentManager::notifyFilesChangedInternally(files); SearchResultWindow::instance()->hide(); } @@ -439,13 +424,26 @@ QList<QPair<QWidget *, QWidget *>> BaseFileFind::createPatternWidgets() syncComboWithSettings(d->m_filterCombo, d->m_filterSetting); QLabel *exclusionLabel = createLabel(msgExclusionPatternLabel()); d->m_exclusionCombo = createCombo(&d->m_exclusionStrings); - d->m_exclusionCombo->setToolTip(msgFilePatternToolTip(Utils::InclusionType::Excluded)); + d->m_exclusionCombo->setToolTip(msgFilePatternToolTip(InclusionType::Excluded)); exclusionLabel->setBuddy(d->m_exclusionCombo); syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting); return {{filterLabel, d->m_filterCombo}, {exclusionLabel, d->m_exclusionCombo}}; } -void BaseFileFind::writeCommonSettings(QSettings *settings) +void BaseFileFind::setSearchDir(const FilePath &dir) +{ + if (dir == d->m_searchDir) + return; + d->m_searchDir = dir; + emit searchDirChanged(d->m_searchDir); +} + +FilePath BaseFileFind::searchDir() const +{ + return d->m_searchDir; +} + +void BaseFileFind::writeCommonSettings(QtcSettings *settings) { const auto fromNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::fromNativeSeparators); @@ -465,7 +463,7 @@ void BaseFileFind::writeCommonSettings(QSettings *settings) settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex); } -void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaultFilter, +void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter) { const auto toNativeSeparators = [](const QStringList &files) -> QStringList { @@ -501,8 +499,8 @@ void BaseFileFind::readCommonSettings(QSettings *settings, const QString &defaul void BaseFileFind::openEditor(SearchResult *result, const SearchResultItem &item) { const FileFindParameters parameters = result->userData().value<FileFindParameters>(); - IEditor *openedEditor = - d->m_searchEngines[parameters.searchEngineIndex]->openEditor(item, parameters); + IEditor *openedEditor = parameters.editorOpener ? parameters.editorOpener(item, parameters) + : nullptr; if (!openedEditor) EditorManager::openEditorAtSearchResult(item, Id(), EditorManager::DoNotSwitchToDesignMode); if (d->m_currentFindSupport) @@ -529,11 +527,11 @@ void BaseFileFind::searchAgain(SearchResult *search) runSearch(search); } -void BaseFileFind::recheckEnabled(SearchResult *search) +void BaseFileFind::setupSearch(SearchResult *search) { - if (!search) - return; - search->setSearchAgainEnabled(isEnabled()); + connect(this, &IFindFilter::enabledChanged, search, [this, search] { + search->setSearchAgainEnabled(isEnabled()); + }); } FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems &items, @@ -572,9 +570,8 @@ FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems QSet<QPair<int, int>> processed; for (const SearchResultItem &item : changeItems) { const QPair<int, int> p{item.mainRange().begin.line, item.mainRange().begin.column}; - if (processed.contains(p)) + if (!Utils::insert(processed, p)) continue; - processed.insert(p); QString replacement; if (item.userData().canConvert<QStringList>() && !item.userData().toStringList().isEmpty()) { @@ -605,16 +602,6 @@ FilePaths BaseFileFind::replaceAll(const QString &text, const SearchResultItems return changes.keys(); } -QVariant BaseFileFind::getAdditionalParameters(SearchResult *search) -{ - return search->userData().value<FileFindParameters>().additionalParameters; -} - -QFuture<SearchResultItems> BaseFileFind::executeSearch(const FileFindParameters ¶meters) -{ - return d->m_searchEngines[parameters.searchEngineIndex]->executeSearch(parameters, this); -} - namespace Internal { } // namespace Internal diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index c8555f03deb..47adae4b866 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -18,10 +18,7 @@ class IEditor; class SearchResult; } // namespace Core -namespace Utils { -class FileIterator; -class Process; -} +namespace Utils { class Process; } namespace TextEditor { @@ -30,16 +27,24 @@ class BaseFileFindPrivate; class SearchEnginePrivate; } // Internal +class FileFindParameters; + +using FileContainerProvider = std::function<Utils::FileContainer()>; +using EditorOpener = std::function<Core::IEditor *(const Utils::SearchResultItem &, + const FileFindParameters &)>; +using SearchExecutor = std::function<QFuture<Utils::SearchResultItems>(const FileFindParameters &)>; + class TEXTEDITOR_EXPORT FileFindParameters { public: QString text; QStringList nameFilters; QStringList exclusionFilters; - QVariant additionalParameters; - QVariant searchEngineParameters; - int searchEngineIndex; - Core::FindFlags flags; + Utils::FilePath searchDir; + Utils::FindFlags flags; + FileContainerProvider fileContainerProvider = {}; + EditorOpener editorOpener = {}; + SearchExecutor searchExecutor = {}; }; using ProcessSetupHandler = std::function<void(Utils::Process &)>; @@ -64,13 +69,10 @@ public: virtual QString title() const = 0; virtual QString toolTip() const = 0; // add %1 placeholder where the find flags should be put virtual QWidget *widget() const = 0; - virtual QVariant parameters() const = 0; - virtual void readSettings(QSettings *settings) = 0; - virtual void writeSettings(QSettings *settings) const = 0; - virtual QFuture<Utils::SearchResultItems> executeSearch( - const FileFindParameters ¶meters, BaseFileFind *baseFileFind) = 0; - virtual Core::IEditor *openEditor(const Utils::SearchResultItem &item, - const FileFindParameters ¶meters) = 0; + virtual void readSettings(Utils::QtcSettings *settings) = 0; + virtual void writeSettings(Utils::QtcSettings *settings) const = 0; + virtual SearchExecutor searchExecutor() const = 0; + virtual EditorOpener editorOpener() const { return {}; } bool isEnabled() const; void setEnabled(bool enabled); @@ -91,27 +93,23 @@ public: bool isEnabled() const override; bool isReplaceSupported() const override { return true; } - void findAll(const QString &txt, Core::FindFlags findFlags) override; - void replaceAll(const QString &txt, Core::FindFlags findFlags) override; + void findAll(const QString &txt, Utils::FindFlags findFlags) override; + void replaceAll(const QString &txt, Utils::FindFlags findFlags) override; void addSearchEngine(SearchEngine *searchEngine); /* returns the list of unique files that were passed in items */ static Utils::FilePaths replaceAll(const QString &txt, const Utils::SearchResultItems &items, bool preserveCase = false); - virtual Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const = 0; protected: - virtual QVariant additionalParameters() const = 0; - static QVariant getAdditionalParameters(Core::SearchResult *search); + void setSearchDir(const Utils::FilePath &dir); + Utils::FilePath searchDir() const; virtual QString label() const = 0; // see Core::SearchResultWindow::startNewSearch virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put - QFuture<Utils::SearchResultItems> executeSearch(const FileFindParameters ¶meters); - void writeCommonSettings(QSettings *settings); - void readCommonSettings(QSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter); + void writeCommonSettings(Utils::QtcSettings *settings); + void readCommonSettings(Utils::QtcSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter); QList<QPair<QWidget *, QWidget *>> createPatternWidgets(); QStringList fileNameFilters() const; QStringList fileExclusionFilters() const; @@ -123,15 +121,17 @@ protected: signals: void currentSearchEngineChanged(); + void searchDirChanged(const Utils::FilePath &dir); private: + virtual FileContainerProvider fileContainerProvider() const = 0; void openEditor(Core::SearchResult *result, const Utils::SearchResultItem &item); void doReplace(const QString &txt, const Utils::SearchResultItems &items, bool preserveCase); void hideHighlightAll(bool visible); void searchAgain(Core::SearchResult *search); - virtual void recheckEnabled(Core::SearchResult *search); + virtual void setupSearch(Core::SearchResult *search); - void runNewSearch(const QString &txt, Core::FindFlags findFlags, + void runNewSearch(const QString &txt, Utils::FindFlags findFlags, Core::SearchResultWindow::SearchMode searchMode); void runSearch(Core::SearchResult *search); diff --git a/src/plugins/texteditor/behaviorsettings.cpp b/src/plugins/texteditor/behaviorsettings.cpp index d04b4824fe6..391e3a60e84 100644 --- a/src/plugins/texteditor/behaviorsettings.cpp +++ b/src/plugins/texteditor/behaviorsettings.cpp @@ -3,10 +3,7 @@ #include "behaviorsettings.h" -#include <utils/settingsutils.h> - -#include <QSettings> -#include <QString> +#include <coreplugin/icore.h> static const char mouseHidingKey[] = "MouseHiding"; static const char mouseNavigationKey[] = "MouseNavigation"; @@ -14,9 +11,10 @@ static const char scrollWheelZoomingKey[] = "ScrollWheelZooming"; static const char constrainTooltips[] = "ConstrainTooltips"; static const char camelCaseNavigationKey[] = "CamelCaseNavigation"; static const char keyboardTooltips[] = "KeyboardTooltips"; -static const char groupPostfix[] = "BehaviorSettings"; static const char smartSelectionChanging[] = "SmartSelectionChanging"; +using namespace Utils; + namespace TextEditor { BehaviorSettings::BehaviorSettings() : @@ -30,18 +28,7 @@ BehaviorSettings::BehaviorSettings() : { } -void BehaviorSettings::toSettings(const QString &category, QSettings *s) const -{ - Utils::toSettings(QLatin1String(groupPostfix), category, s, this); -} - -void BehaviorSettings::fromSettings(const QString &category, QSettings *s) -{ - *this = BehaviorSettings(); - Utils::fromSettings(QLatin1String(groupPostfix), category, s, this); -} - -QVariantMap BehaviorSettings::toMap() const +Store BehaviorSettings::toMap() const { return { {mouseHidingKey, m_mouseHiding}, @@ -54,7 +41,7 @@ QVariantMap BehaviorSettings::toMap() const }; } -void BehaviorSettings::fromMap(const QVariantMap &map) +void BehaviorSettings::fromMap(const Store &map) { m_mouseHiding = map.value(mouseHidingKey, m_mouseHiding).toBool(); m_mouseNavigation = map.value(mouseNavigationKey, m_mouseNavigation).toBool(); diff --git a/src/plugins/texteditor/behaviorsettings.h b/src/plugins/texteditor/behaviorsettings.h index 678ec7b37f7..57f88f1b2c7 100644 --- a/src/plugins/texteditor/behaviorsettings.h +++ b/src/plugins/texteditor/behaviorsettings.h @@ -5,11 +5,7 @@ #include "texteditor_global.h" -#include <QVariantMap> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <utils/store.h> namespace TextEditor { @@ -22,11 +18,8 @@ class TEXTEDITOR_EXPORT BehaviorSettings public: BehaviorSettings(); - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); - - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const BehaviorSettings &bs) const; diff --git a/src/plugins/texteditor/behaviorsettingspage.cpp b/src/plugins/texteditor/behaviorsettingspage.cpp index 73675e2cdb9..4b1fad44bcb 100644 --- a/src/plugins/texteditor/behaviorsettingspage.cpp +++ b/src/plugins/texteditor/behaviorsettingspage.cpp @@ -28,20 +28,24 @@ #include <qmljseditor/qmljseditorconstants.h> #include <qmljstools/qmljstoolsconstants.h> -#include <QCoreApplication> #include <QGridLayout> -#include <QPointer> -#include <QSettings> #include <QSpacerItem> +using namespace Utils; + namespace TextEditor { +const char behaviorGroup[] = "textBehaviorSettings"; +const char storageGroup[] = "textStorageSettings"; +const char typingGroup[] = "textTypingSettings"; +const char extraEncodingGroup[] = "textEditorManager"; + class BehaviorSettingsPagePrivate : public QObject { public: BehaviorSettingsPagePrivate(); - const QString m_settingsPrefix{"text"}; + const Key m_settingsPrefix{"text"}; TextEditor::BehaviorSettingsWidget *m_behaviorWidget = nullptr; CodeStylePool *m_defaultCodeStylePool = nullptr; @@ -64,12 +68,12 @@ BehaviorSettingsPagePrivate::BehaviorSettingsPagePrivate() m_defaultCodeStylePool = new CodeStylePool(nullptr, this); // Any language m_defaultCodeStylePool->addCodeStyle(m_codeStyle); - QSettings * const s = Core::ICore::settings(); - m_codeStyle->fromSettings(m_settingsPrefix, s); - m_typingSettings.fromSettings(m_settingsPrefix, s); - m_storageSettings.fromSettings(m_settingsPrefix, s); - m_behaviorSettings.fromSettings(m_settingsPrefix, s); - m_extraEncodingSettings.fromSettings(m_settingsPrefix, s); + QtcSettings *s = Core::ICore::settings(); + m_codeStyle->fromSettings(m_settingsPrefix); + m_typingSettings.fromMap(storeFromSettings(typingGroup, s)); + m_storageSettings.fromMap(storeFromSettings(storageGroup, s)); + m_behaviorSettings.fromMap(storeFromSettings(behaviorGroup, s)); + m_extraEncodingSettings.fromMap(storeFromSettings(extraEncodingGroup, s)); } class BehaviorSettingsWidgetImpl : public Core::IOptionsPageWidget @@ -143,6 +147,8 @@ void BehaviorSettingsWidgetImpl::apply() if (!d->m_behaviorWidget) // page was never shown return; + QtcSettings *s = Core::ICore::settings(); + TypingSettings newTypingSettings; StorageSettings newStorageSettings; BehaviorSettings newBehaviorSettings; @@ -153,50 +159,47 @@ void BehaviorSettingsWidgetImpl::apply() d->m_behaviorWidget->assignedBehaviorSettings(&newBehaviorSettings); d->m_behaviorWidget->assignedExtraEncodingSettings(&newExtraEncodingSettings); - QSettings *s = Core::ICore::settings(); - QTC_ASSERT(s, return); - if (d->m_codeStyle->tabSettings() != d->m_pageCodeStyle->tabSettings()) { d->m_codeStyle->setTabSettings(d->m_pageCodeStyle->tabSettings()); - d->m_codeStyle->toSettings(d->m_settingsPrefix, s); + d->m_codeStyle->toSettings(d->m_settingsPrefix); } if (d->m_codeStyle->currentDelegate() != d->m_pageCodeStyle->currentDelegate()) { d->m_codeStyle->setCurrentDelegate(d->m_pageCodeStyle->currentDelegate()); - d->m_codeStyle->toSettings(d->m_settingsPrefix, s); + d->m_codeStyle->toSettings(d->m_settingsPrefix); } if (newTypingSettings != d->m_typingSettings) { d->m_typingSettings = newTypingSettings; - d->m_typingSettings.toSettings(d->m_settingsPrefix, s); + storeToSettings(typingGroup, s, d->m_typingSettings.toMap()); emit TextEditorSettings::instance()->typingSettingsChanged(newTypingSettings); } if (newStorageSettings != d->m_storageSettings) { d->m_storageSettings = newStorageSettings; - d->m_storageSettings.toSettings(d->m_settingsPrefix, s); + storeToSettings(storageGroup, s, d->m_storageSettings.toMap()); emit TextEditorSettings::instance()->storageSettingsChanged(newStorageSettings); } if (newBehaviorSettings != d->m_behaviorSettings) { d->m_behaviorSettings = newBehaviorSettings; - d->m_behaviorSettings.toSettings(d->m_settingsPrefix, s); + storeToSettings(behaviorGroup, s, d->m_behaviorSettings.toMap()); emit TextEditorSettings::instance()->behaviorSettingsChanged(newBehaviorSettings); } if (newExtraEncodingSettings != d->m_extraEncodingSettings) { d->m_extraEncodingSettings = newExtraEncodingSettings; - d->m_extraEncodingSettings.toSettings(d->m_settingsPrefix, s); + storeToSettings(extraEncodingGroup, s, d->m_extraEncodingSettings.toMap()); emit TextEditorSettings::instance()->extraEncodingSettingsChanged(newExtraEncodingSettings); } - s->setValue(QLatin1String(Core::Constants::SETTINGS_DEFAULTTEXTENCODING), + s->setValue(Core::Constants::SETTINGS_DEFAULTTEXTENCODING, d->m_behaviorWidget->assignedCodecName()); - s->setValue(QLatin1String(Core::Constants::SETTINGS_DEFAULT_LINE_TERMINATOR), + s->setValue(Core::Constants::SETTINGS_DEFAULT_LINE_TERMINATOR, d->m_behaviorWidget->assignedLineEnding()); } diff --git a/src/plugins/texteditor/behaviorsettingswidget.cpp b/src/plugins/texteditor/behaviorsettingswidget.cpp index 144afa6eb23..b5f8967a832 100644 --- a/src/plugins/texteditor/behaviorsettingswidget.cpp +++ b/src/plugins/texteditor/behaviorsettingswidget.cpp @@ -38,6 +38,7 @@ struct BehaviorSettingsWidgetPrivate QComboBox *smartBackspaceBehavior; QCheckBox *autoIndent; QCheckBox *preferSingleLineComments; + QComboBox *commentPosition; QGroupBox *groupBoxStorageSettings; QGroupBox *groupBoxTyping; QCheckBox *skipTrailingWhitespace; @@ -95,6 +96,48 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent) d->autoIndent = new QCheckBox(Tr::tr("Enable automatic &indentation")); d->preferSingleLineComments = new QCheckBox(Tr::tr("Prefer single line comments")); + d->commentPosition = new QComboBox; + const QString automaticText = Tr::tr("Automatic"); + d->commentPosition->addItem(automaticText); + const QString lineStartText = Tr::tr("At Line Start"); + d->commentPosition->addItem(lineStartText); + const QString afterWhitespaceText = Tr::tr("After Whitespace"); + d->commentPosition->addItem(afterWhitespaceText); + + const QString generalCommentPosition = Tr::tr( + "Specifies where single line comments should be positioned."); + const QString automaticCommentPosition + = Tr::tr( + "%1: The highlight definition for the file determines the position. If no highlight " + "definition is available, the comment is placed after leading whitespaces.") + .arg(automaticText); + const QString lineStartCommentPosition + = Tr::tr("%1: The comment is placed at the start of the line.").arg(lineStartText); + const QString afterWhitespaceCommentPosition + = Tr::tr("%1: The comment is placed after leading whitespaces.").arg(afterWhitespaceText); + + const QString commentPositionToolTip = QString("<html><head/><body>\n" + "%1\n" + "\n" + "<ul>\n" + "<li>%2\n" + "</li>\n" + "\n" + "<li>%3\n" + "</li>\n" + "\n" + "<li>%4\n" + "</li>\n" + "</ul></body></html>\n" + "") + .arg(generalCommentPosition) + .arg(automaticCommentPosition) + .arg(lineStartCommentPosition) + .arg(afterWhitespaceCommentPosition); + d->commentPosition->setToolTip(commentPositionToolTip); + + auto commentPositionLabel = new QLabel(Tr::tr("Preferred comment position:")); + commentPositionLabel->setToolTip(commentPositionToolTip); d->skipTrailingWhitespace = new QCheckBox(Tr::tr("Skip clean whitespace for file types:")); d->skipTrailingWhitespace->setToolTip(Tr::tr("For the file patterns listed, do not trim trailing whitespace.")); @@ -173,7 +216,9 @@ BehaviorSettingsWidget::BehaviorSettingsWidget(QWidget *parent) indent(d->smartBackspaceBehavior), Tr::tr("Tab key performs auto-indent:"), indent(d->tabKeyBehavior), - d->preferSingleLineComments + d->preferSingleLineComments, + commentPositionLabel, + indent(d->commentPosition) }.attachTo(d->groupBoxTyping); Column { @@ -293,6 +338,7 @@ void BehaviorSettingsWidget::setAssignedTypingSettings(const TypingSettings &typ d->tabKeyBehavior->setCurrentIndex(typingSettings.m_tabKeyBehavior); d->preferSingleLineComments->setChecked(typingSettings.m_preferSingleLineComments); + d->commentPosition->setCurrentIndex(typingSettings.m_commentPosition); } void BehaviorSettingsWidget::assignedTypingSettings(TypingSettings *typingSettings) const @@ -304,6 +350,8 @@ void BehaviorSettingsWidget::assignedTypingSettings(TypingSettings *typingSettin (TypingSettings::TabKeyBehavior)(d->tabKeyBehavior->currentIndex()); typingSettings->m_preferSingleLineComments = d->preferSingleLineComments->isChecked(); + typingSettings->m_commentPosition = TypingSettings::CommentPosition( + d->commentPosition->currentIndex()); } void BehaviorSettingsWidget::setAssignedStorageSettings(const StorageSettings &storageSettings) diff --git a/src/plugins/bookmarks/bookmark.cpp b/src/plugins/texteditor/bookmark.cpp similarity index 86% rename from src/plugins/bookmarks/bookmark.cpp rename to src/plugins/texteditor/bookmark.cpp index 27c8365e19e..9d13d5a6b23 100644 --- a/src/plugins/bookmarks/bookmark.cpp +++ b/src/plugins/texteditor/bookmark.cpp @@ -4,8 +4,8 @@ #include "bookmark.h" #include "bookmarkmanager.h" -#include "bookmarks_global.h" -#include "bookmarkstr.h" +#include "texteditor_global.h" +#include "texteditortr.h" #include <utils/utilsicons.h> @@ -13,16 +13,18 @@ using namespace Utils; -namespace Bookmarks::Internal { +namespace TextEditor::Internal { + +const char BOOKMARKS_TEXT_MARK_CATEGORY[] = "Bookmarks.TextMarkCategory"; Bookmark::Bookmark(int lineNumber, BookmarkManager *manager) : - TextMark(FilePath(), lineNumber, {Tr::tr("Bookmark"), Constants::BOOKMARKS_TEXT_MARK_CATEGORY}), + TextMark(FilePath(), lineNumber, {Tr::tr("Bookmark"), BOOKMARKS_TEXT_MARK_CATEGORY}), m_manager(manager) { setColor(Theme::Bookmarks_TextMarkColor); setIcon(Icons::BOOKMARK_TEXTEDITOR.icon()); setDefaultToolTip(Tr::tr("Bookmark")); - setPriority(TextEditor::TextMark::NormalPriority); + setPriority(TextMark::NormalPriority); } void Bookmark::removedFromEditor() diff --git a/src/plugins/bookmarks/bookmark.h b/src/plugins/texteditor/bookmark.h similarity index 88% rename from src/plugins/bookmarks/bookmark.h rename to src/plugins/texteditor/bookmark.h index 44277d19c60..1c8c522363e 100644 --- a/src/plugins/bookmarks/bookmark.h +++ b/src/plugins/texteditor/bookmark.h @@ -5,11 +5,11 @@ #include <texteditor/textmark.h> -namespace Bookmarks::Internal { +namespace TextEditor::Internal { class BookmarkManager; -class Bookmark : public TextEditor::TextMark +class Bookmark : public TextMark { public: Bookmark(int lineNumber, BookmarkManager *manager); @@ -34,4 +34,4 @@ private: QString m_lineText; }; -} // Bookmarks::Internal +} // TextEditor::Internal diff --git a/src/plugins/bookmarks/bookmarkfilter.cpp b/src/plugins/texteditor/bookmarkfilter.cpp similarity index 98% rename from src/plugins/bookmarks/bookmarkfilter.cpp rename to src/plugins/texteditor/bookmarkfilter.cpp index 1cedbade481..2756521cd89 100644 --- a/src/plugins/bookmarks/bookmarkfilter.cpp +++ b/src/plugins/texteditor/bookmarkfilter.cpp @@ -5,14 +5,14 @@ #include "bookmark.h" #include "bookmarkmanager.h" -#include "bookmarkstr.h" +#include "texteditortr.h" #include <utils/algorithm.h> using namespace Core; using namespace Utils; -namespace Bookmarks::Internal { +namespace TextEditor::Internal { BookmarkFilter::BookmarkFilter(BookmarkManager *manager) : m_manager(manager) @@ -112,4 +112,4 @@ LocatorFilterEntries BookmarkFilter::match(const QString &input) const return entries; } -} // Bookmarks::Internal +} // TextEditor::Internal diff --git a/src/plugins/bookmarks/bookmarkfilter.h b/src/plugins/texteditor/bookmarkfilter.h similarity index 89% rename from src/plugins/bookmarks/bookmarkfilter.h rename to src/plugins/texteditor/bookmarkfilter.h index f85eff9b7d7..fca42dd679d 100644 --- a/src/plugins/bookmarks/bookmarkfilter.h +++ b/src/plugins/texteditor/bookmarkfilter.h @@ -5,7 +5,7 @@ #include <coreplugin/locator/ilocatorfilter.h> -namespace Bookmarks::Internal { +namespace TextEditor::Internal { class BookmarkManager; @@ -21,4 +21,4 @@ private: BookmarkManager *m_manager = nullptr; // not owned }; -} // Bookmarks::Internal +} // TextEditor::Internal diff --git a/src/plugins/bookmarks/bookmarkmanager.cpp b/src/plugins/texteditor/bookmarkmanager.cpp similarity index 85% rename from src/plugins/bookmarks/bookmarkmanager.cpp rename to src/plugins/texteditor/bookmarkmanager.cpp index 83a2650b750..ae21a75be88 100644 --- a/src/plugins/bookmarks/bookmarkmanager.cpp +++ b/src/plugins/texteditor/bookmarkmanager.cpp @@ -4,8 +4,8 @@ #include "bookmarkmanager.h" #include "bookmark.h" -#include "bookmarks_global.h" -#include "bookmarkstr.h" +#include "texteditorconstants.h" +#include "texteditortr.h" #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -14,21 +14,18 @@ #include <coreplugin/idocument.h> #include <coreplugin/session.h> -#include <texteditor/texteditor.h> #include <utils/algorithm.h> #include <utils/icon.h> #include <utils/qtcassert.h> #include <utils/checkablemessagebox.h> #include <utils/theme/theme.h> #include <utils/dropsupport.h> -#include <utils/utilsicons.h> #include <QAction> #include <QContextMenuEvent> #include <QDebug> #include <QDialog> #include <QDialogButtonBox> -#include <QDir> #include <QFormLayout> #include <QLineEdit> #include <QMenu> @@ -36,17 +33,30 @@ #include <QSpinBox> #include <QToolButton> -Q_DECLARE_METATYPE(Bookmarks::Internal::Bookmark*) +Q_DECLARE_METATYPE(TextEditor::Internal::Bookmark*) using namespace Core; using namespace Utils; -namespace Bookmarks::Internal { +namespace TextEditor::Internal { -BookmarkDelegate::BookmarkDelegate(QObject *parent) - : QStyledItemDelegate(parent) +const char BOOKMARKS_CONTEXT[] = "Bookmarks"; + +class BookmarkDelegate : public QStyledItemDelegate { -} +public: + BookmarkDelegate(QObject *parent) + : QStyledItemDelegate(parent) + {} + +private: + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const final; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const final; + void generateGradientPixmap(int width, int height, const QColor &color, bool selected) const; + + mutable QPixmap m_normalPixmap; + mutable QPixmap m_selectedPixmap; +}; QSize BookmarkDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { @@ -159,6 +169,31 @@ void BookmarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti painter->restore(); } +// BookmarkView + +class BookmarkView final : public Utils::ListView +{ +public: + explicit BookmarkView(BookmarkManager *manager); + + QList<QToolButton *> createToolBarWidgets(); + + void gotoBookmark(const QModelIndex &index); + + void removeFromContextMenu(); + void removeAll(); + +protected: + void contextMenuEvent(QContextMenuEvent *event) final; + void removeBookmark(const QModelIndex &index); + void keyPressEvent(QKeyEvent *event) final; + +private: + Core::IContext *m_bookmarkContext; + QModelIndex m_contextMenuIndex; + BookmarkManager *m_manager; +}; + BookmarkView::BookmarkView(BookmarkManager *manager) : m_bookmarkContext(new IContext(this)), m_manager(manager) @@ -166,7 +201,7 @@ BookmarkView::BookmarkView(BookmarkManager *manager) : setWindowTitle(Tr::tr("Bookmarks")); m_bookmarkContext->setWidget(this); - m_bookmarkContext->setContext(Context(Constants::BOOKMARKS_CONTEXT)); + m_bookmarkContext->setContext(Context(BOOKMARKS_CONTEXT)); ICore::addContextObject(m_bookmarkContext); @@ -180,16 +215,16 @@ BookmarkView::BookmarkView(BookmarkManager *manager) : setSelectionMode(QAbstractItemView::SingleSelection); setSelectionBehavior(QAbstractItemView::SelectRows); setDragEnabled(true); - setDragDropMode(QAbstractItemView::DragOnly); + setDragDropMode(QAbstractItemView::DragDrop); - connect(this, &QAbstractItemView::clicked, this, &BookmarkView::gotoBookmark); + connect(this, &QAbstractItemView::doubleClicked, this, &BookmarkView::gotoBookmark); connect(this, &QAbstractItemView::activated, this, &BookmarkView::gotoBookmark); } QList<QToolButton *> BookmarkView::createToolBarWidgets() { - Command *prevCmd = ActionManager::command(Constants::BOOKMARKS_PREV_ACTION); - Command *nextCmd = ActionManager::command(Constants::BOOKMARKS_NEXT_ACTION); + Command *prevCmd = ActionManager::command(TextEditor::Constants::BOOKMARKS_PREV_ACTION); + Command *nextCmd = ActionManager::command(TextEditor::Constants::BOOKMARKS_NEXT_ACTION); QTC_ASSERT(prevCmd && nextCmd, return {}); auto prevButton = new QToolButton(this); prevButton->setToolButtonStyle(Qt::ToolButtonIconOnly); @@ -258,7 +293,7 @@ void BookmarkView::removeAll() Tr::tr("Remove All Bookmarks"), Tr::tr("Are you sure you want to remove all bookmarks from " "all files in the current session?"), - QString("RemoveAllBookmarks")) + Key("RemoveAllBookmarks")) != QMessageBox::Yes) return; @@ -360,7 +395,7 @@ Qt::ItemFlags BookmarkManager::flags(const QModelIndex &index) const { if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_bookmarksList.count()) return Qt::NoItemFlags; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } Qt::DropActions BookmarkManager::supportedDragActions() const @@ -381,10 +416,62 @@ QMimeData *BookmarkManager::mimeData(const QModelIndexList &indexes) const continue; Bookmark *bookMark = m_bookmarksList.at(index.row()); data->addFile(bookMark->filePath(), bookMark->lineNumber()); + data->addValue(QVariant::fromValue(bookMark)); } return data; } +Qt::DropActions BookmarkManager::supportedDropActions() const +{ + return Qt::MoveAction; +} + +bool BookmarkManager::canDropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, + const QModelIndex &parent) const +{ + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); + + if (!(action & supportedDropActions())) + return false; + + const DropMimeData* customData = qobject_cast<const DropMimeData*>(data); + if (!customData) + return false; + + return true; +} + +bool BookmarkManager::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + Q_UNUSED(column); + + if (!(action & supportedDropActions())) + return false; + + const DropMimeData* customData = qobject_cast<const DropMimeData*>(data); + if (!customData) + return false; + + row = parent.row(); + if (row >= m_bookmarksList.size()) + row = m_bookmarksList.size() - 1; + if (row == -1) + row = m_bookmarksList.size() - 1; + + const QList<QVariant> values = customData->values(); + for (const QVariant &value : values) { + auto mark = value.value<Bookmark*>(); + if (mark) + move(mark, row); + } + + return true; +} + void BookmarkManager::toggleBookmark(const FilePath &fileName, int lineNumber) { if (lineNumber <= 0 || fileName.isEmpty()) @@ -593,6 +680,25 @@ void BookmarkManager::updateActionStatus() emit updateActions(enableToggle, state()); } +void BookmarkManager::move(Bookmark* mark, int newRow) +{ + int currentRow = m_bookmarksList.indexOf(mark); + if (newRow <= currentRow) + ++currentRow; + else + ++newRow; + m_bookmarksList.insert(newRow, mark); + m_bookmarksList.removeAt(currentRow); + + QModelIndex current = selectionModel()->currentIndex(); + QModelIndex topLeft = current.sibling(std::min(newRow, currentRow), 0); + QModelIndex bottomRight = current.sibling(std::max(newRow, currentRow), 2); + emit dataChanged(topLeft, bottomRight); + selectionModel()->setCurrentIndex(topLeft, QItemSelectionModel::Select | QItemSelectionModel::Clear); + + saveBookmarks(); +} + void BookmarkManager::moveUp() { QModelIndex current = selectionModel()->currentIndex(); @@ -750,14 +856,14 @@ void BookmarkManager::saveBookmarks() for (const Bookmark *bookmark : std::as_const(m_bookmarksList)) list << bookmarkToString(bookmark); - SessionManager::setValue(QLatin1String("Bookmarks"), list); + SessionManager::setValue("Bookmarks", list); } /* Loads the bookmarks from the session settings. */ void BookmarkManager::loadBookmarks() { removeAllBookmarks(); - const QStringList &list = SessionManager::value(QLatin1String("Bookmarks")).toStringList(); + const QStringList &list = SessionManager::value("Bookmarks").toStringList(); for (const QString &bookmarkString : list) addBookmark(bookmarkString); @@ -789,7 +895,8 @@ BookmarkViewFactory::BookmarkViewFactory(BookmarkManager *bm) NavigationView BookmarkViewFactory::createWidget() { auto view = new BookmarkView(m_manager); + view->setActivationMode(Utils::DoubleClickActivation); // QUESTION: is this useful ? return {view, view->createToolBarWidgets()}; } -} // Bookmarks::Internal +} // TextEditor::Internal diff --git a/src/plugins/bookmarks/bookmarkmanager.h b/src/plugins/texteditor/bookmarkmanager.h similarity index 75% rename from src/plugins/bookmarks/bookmarkmanager.h rename to src/plugins/texteditor/bookmarkmanager.h index a4fcf01b58c..c83763d870f 100644 --- a/src/plugins/bookmarks/bookmarkmanager.h +++ b/src/plugins/texteditor/bookmarkmanager.h @@ -16,7 +16,7 @@ namespace Core { class IContext; } -namespace Bookmarks::Internal { +namespace TextEditor::Internal { class Bookmark; class BookmarksPlugin; @@ -50,6 +50,9 @@ public: Qt::DropActions supportedDragActions() const final; QStringList mimeTypes() const final; QMimeData *mimeData(const QModelIndexList &indexes) const final; + Qt::DropActions supportedDropActions() const final; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const final; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) final; // this QItemSelectionModel is shared by all views QItemSelectionModel *selectionModel() const; @@ -69,6 +72,7 @@ public: void prevInDocument(); void next(); void prev(); + void move(Bookmark* mark, int newRow); void moveUp(); void moveDown(); void edit(); @@ -99,29 +103,6 @@ private: QItemSelectionModel *m_selectionModel; }; -class BookmarkView final : public Utils::ListView -{ -public: - explicit BookmarkView(BookmarkManager *manager); - - QList<QToolButton *> createToolBarWidgets(); - - void gotoBookmark(const QModelIndex &index); - - void removeFromContextMenu(); - void removeAll(); - -protected: - void contextMenuEvent(QContextMenuEvent *event) final; - void removeBookmark(const QModelIndex &index); - void keyPressEvent(QKeyEvent *event) final; - -private: - Core::IContext *m_bookmarkContext; - QModelIndex m_contextMenuIndex; - BookmarkManager *m_manager; -}; - class BookmarkViewFactory : public Core::INavigationWidgetFactory { public: @@ -133,18 +114,4 @@ private: BookmarkManager *m_manager; }; -class BookmarkDelegate : public QStyledItemDelegate -{ -public: - BookmarkDelegate(QObject *parent = nullptr); - -private: - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const final; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const final; - void generateGradientPixmap(int width, int height, const QColor &color, bool selected) const; - - mutable QPixmap m_normalPixmap; - mutable QPixmap m_selectedPixmap; -}; - } // Bookmarks::Internal diff --git a/src/plugins/texteditor/circularclipboardassist.cpp b/src/plugins/texteditor/circularclipboardassist.cpp index 4c0956b6c3f..fe569f54e66 100644 --- a/src/plugins/texteditor/circularclipboardassist.cpp +++ b/src/plugins/texteditor/circularclipboardassist.cpp @@ -13,11 +13,13 @@ #include <coreplugin/coreconstants.h> -#include <utils/utilsicons.h> +#include <utils/icon.h> #include <QApplication> #include <QClipboard> +using namespace Utils; + namespace TextEditor { namespace Internal { @@ -65,7 +67,7 @@ class ClipboardAssistProcessor: public IAssistProcessor public: IAssistProposal *perform() override { - QIcon icon = QIcon::fromTheme(QLatin1String("edit-paste"), Utils::Icons::PASTE.icon()).pixmap(16); + QIcon icon = Icon::fromTheme("edit-paste"); CircularClipboard * clipboard = CircularClipboard::instance(); QList<AssistProposalItemInterface *> items; items.reserve(clipboard->size()); diff --git a/src/plugins/texteditor/codeassist/asyncprocessor.h b/src/plugins/texteditor/codeassist/asyncprocessor.h index 9894239af1d..94336fd537d 100644 --- a/src/plugins/texteditor/codeassist/asyncprocessor.h +++ b/src/plugins/texteditor/codeassist/asyncprocessor.h @@ -14,7 +14,7 @@ class TEXTEDITOR_EXPORT AsyncProcessor : public TextEditor::IAssistProcessor public: AsyncProcessor(); - IAssistProposal *perform() final; + IAssistProposal *perform() override; bool running() override; void cancel() override; @@ -23,8 +23,6 @@ public: protected: bool isCanceled() const; - -private: QFutureWatcher<IAssistProposal *> m_watcher; }; diff --git a/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp index 44506678e02..d994238f4a3 100644 --- a/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp +++ b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp @@ -96,11 +96,10 @@ IAssistProposal *DocumentContentCompletionProcessor::performAsync() continue; } - if (!words.contains(word)) { + if (Utils::insert(words, word)) { auto item = new AssistProposalItem(); item->setText(word); items.append(item); - words.insert(word); } } diff --git a/src/plugins/texteditor/codestylepool.cpp b/src/plugins/texteditor/codestylepool.cpp index 573d5ea456e..949b38ab336 100644 --- a/src/plugins/texteditor/codestylepool.cpp +++ b/src/plugins/texteditor/codestylepool.cpp @@ -204,11 +204,11 @@ ICodeStylePreferences *CodeStylePool::loadCodeStyle(const FilePath &fileName) ICodeStylePreferences *codeStyle = nullptr; PersistentSettingsReader reader; reader.load(fileName); - QVariantMap m = reader.restoreValues(); - if (m.contains(QLatin1String(codeStyleDataKey))) { + Store m = reader.restoreValues(); + if (m.contains(codeStyleDataKey)) { const QByteArray id = fileName.completeBaseName().toUtf8(); - const QString displayName = reader.restoreValue(QLatin1String(displayNameKey)).toString(); - const QVariantMap map = reader.restoreValue(QLatin1String(codeStyleDataKey)).toMap(); + const QString displayName = reader.restoreValue(displayNameKey).toString(); + const Store map = storeFromVariant(reader.restoreValue(codeStyleDataKey)); if (d->m_factory) { codeStyle = d->m_factory->createCodeStyle(); codeStyle->setId(id); @@ -242,10 +242,10 @@ void CodeStylePool::saveCodeStyle(ICodeStylePreferences *codeStyle) const void CodeStylePool::exportCodeStyle(const FilePath &fileName, ICodeStylePreferences *codeStyle) const { - const QVariantMap map = codeStyle->toMap(); - const QVariantMap tmp = { + const Store map = codeStyle->toMap(); + const Store tmp = { {displayNameKey, codeStyle->displayName()}, - {codeStyleDataKey, map} + {codeStyleDataKey, variantFromStore(map)} }; PersistentSettingsWriter writer(fileName, QLatin1String(codeStyleDocKey)); writer.save(tmp, Core::ICore::dialogParent()); diff --git a/src/plugins/texteditor/commentssettings.cpp b/src/plugins/texteditor/commentssettings.cpp index 4585d358499..bc6f76cb4bd 100644 --- a/src/plugins/texteditor/commentssettings.cpp +++ b/src/plugins/texteditor/commentssettings.cpp @@ -3,46 +3,176 @@ #include "commentssettings.h" -#include <QSettings> +#include "texteditorconstants.h" +#include "texteditorsettings.h" +#include "texteditortr.h" -using namespace TextEditor; +#include <coreplugin/icore.h> +#include <projectexplorer/project.h> +#include <utils/layoutbuilder.h> + +#include <QCheckBox> +#include <QComboBox> + +using namespace Layouting; +using namespace Utils; + +namespace TextEditor { namespace { - const char kDocumentationCommentsGroup[] = "CppToolsDocumentationComments"; const char kEnableDoxygenBlocks[] = "EnableDoxygenBlocks"; const char kGenerateBrief[] = "GenerateBrief"; const char kAddLeadingAsterisks[] = "AddLeadingAsterisks"; - +const char kCommandPrefix[] = "CommandPrefix"; } +void CommentsSettings::setData(const Data &data) +{ + if (data == instance().m_data) + return; + instance().m_data = data; + instance().save(); +} + +Key CommentsSettings::mainSettingsKey() { return kDocumentationCommentsGroup; } +Key CommentsSettings::enableDoxygenSettingsKey() { return kEnableDoxygenBlocks; } +Key CommentsSettings::generateBriefSettingsKey() { return kGenerateBrief; } +Key CommentsSettings::leadingAsterisksSettingsKey() { return kAddLeadingAsterisks; } +Key CommentsSettings::commandPrefixKey() { return kCommandPrefix; } + CommentsSettings::CommentsSettings() - : m_enableDoxygen(true) - , m_generateBrief(true) - , m_leadingAsterisks(true) -{} - -void CommentsSettings::toSettings(QSettings *s) const { + load(); +} + +CommentsSettings &CommentsSettings::instance() +{ + static CommentsSettings settings; + return settings; +} + +void CommentsSettings::save() const +{ + Utils::QtcSettings * const s = Core::ICore::settings(); s->beginGroup(kDocumentationCommentsGroup); - s->setValue(kEnableDoxygenBlocks, m_enableDoxygen); - s->setValue(kGenerateBrief, m_generateBrief); - s->setValue(kAddLeadingAsterisks, m_leadingAsterisks); + s->setValue(kEnableDoxygenBlocks, m_data.enableDoxygen); + s->setValue(kGenerateBrief, m_data.generateBrief); + s->setValue(kAddLeadingAsterisks, m_data.leadingAsterisks); + s->setValueWithDefault(kCommandPrefix, int(m_data.commandPrefix)); s->endGroup(); } -void CommentsSettings::fromSettings(QSettings *s) +void CommentsSettings::load() { + Utils::QtcSettings * const s = Core::ICore::settings(); s->beginGroup(kDocumentationCommentsGroup); - m_enableDoxygen = s->value(kEnableDoxygenBlocks, true).toBool(); - m_generateBrief = m_enableDoxygen && s->value(kGenerateBrief, true).toBool(); - m_leadingAsterisks = s->value(kAddLeadingAsterisks, true).toBool(); + m_data.enableDoxygen = s->value(kEnableDoxygenBlocks, true).toBool(); + m_data.generateBrief = m_data.enableDoxygen && s->value(kGenerateBrief, true).toBool(); + m_data.leadingAsterisks = s->value(kAddLeadingAsterisks, true).toBool(); + m_data.commandPrefix = static_cast<CommandPrefix>( + s->value(kCommandPrefix, int(m_data.commandPrefix)).toInt()); s->endGroup(); } -bool CommentsSettings::equals(const CommentsSettings &other) const +class CommentsSettingsWidget::Private { - return m_enableDoxygen == other.m_enableDoxygen - && m_generateBrief == other.m_generateBrief - && m_leadingAsterisks == other.m_leadingAsterisks; +public: + QCheckBox m_overwriteClosingChars; + QCheckBox m_enableDoxygenCheckBox; + QCheckBox m_generateBriefCheckBox; + QCheckBox m_leadingAsterisksCheckBox; + QComboBox m_commandPrefixComboBox; +}; + +CommentsSettingsWidget::CommentsSettingsWidget(const CommentsSettings::Data &settings) + : d(new Private) +{ + d->m_enableDoxygenCheckBox.setText(Tr::tr("Enable Doxygen blocks")); + d->m_enableDoxygenCheckBox.setToolTip( + Tr::tr("Automatically creates a Doxygen comment upon pressing " + "enter after a '/**', '/*!', '//!' or '///'.")); + + d->m_generateBriefCheckBox.setText(Tr::tr("Generate brief description")); + d->m_generateBriefCheckBox.setToolTip( + Tr::tr("Generates a <i>brief</i> command with an initial " + "description for the corresponding declaration.")); + + d->m_leadingAsterisksCheckBox.setText(Tr::tr("Add leading asterisks")); + d->m_leadingAsterisksCheckBox.setToolTip( + Tr::tr("Adds leading asterisks when continuing C/C++ \"/*\", Qt \"/*!\" " + "and Java \"/**\" style comments on new lines.")); + + const auto commandPrefixLabel = new QLabel(Tr::tr("Doxygen command prefix:")); + const QString commandPrefixToolTip = Tr::tr(R"(Doxygen allows "@" and "\" to start commands. +By default, "@" is used if the surrounding comment starts with "/**" or "///", and "\" is used +if the comment starts with "/*!" or "//!)"); + commandPrefixLabel->setToolTip(commandPrefixToolTip); + d->m_commandPrefixComboBox.setToolTip(commandPrefixToolTip); + d->m_commandPrefixComboBox.addItem(Tr::tr("Automatic")); + d->m_commandPrefixComboBox.addItem("@"); + d->m_commandPrefixComboBox.addItem("\\"); + + initFromSettings(settings); + + Column { + &d->m_enableDoxygenCheckBox, + Row { Space(30), &d->m_generateBriefCheckBox }, + &d->m_leadingAsterisksCheckBox, + Row { commandPrefixLabel, &d->m_commandPrefixComboBox, st }, + st + }.attachTo(this); + + connect(&d->m_enableDoxygenCheckBox, &QCheckBox::toggled, + &d->m_generateBriefCheckBox, &QCheckBox::setEnabled); + + for (QCheckBox *checkBox : {&d->m_enableDoxygenCheckBox, &d->m_generateBriefCheckBox, + &d->m_leadingAsterisksCheckBox}) { + connect(checkBox, &QCheckBox::clicked, this, &CommentsSettingsWidget::settingsChanged); + } + connect(&d->m_commandPrefixComboBox, &QComboBox::currentIndexChanged, + this, &CommentsSettingsWidget::settingsChanged); } + +CommentsSettingsWidget::~CommentsSettingsWidget() { delete d; } + +CommentsSettings::Data CommentsSettingsWidget::settingsData() const +{ + CommentsSettings::Data settings; + settings.enableDoxygen = d->m_enableDoxygenCheckBox.isChecked(); + settings.generateBrief = d->m_generateBriefCheckBox.isChecked(); + settings.leadingAsterisks = d->m_leadingAsterisksCheckBox.isChecked(); + settings.commandPrefix = static_cast<CommentsSettings::CommandPrefix>( + d->m_commandPrefixComboBox.currentIndex()); + return settings; +} + +void CommentsSettingsWidget::apply() +{ + // This function is only ever called for the global settings page. + CommentsSettings::setData(settingsData()); +} + +void CommentsSettingsWidget::initFromSettings(const CommentsSettings::Data &settings) +{ + d->m_enableDoxygenCheckBox.setChecked(settings.enableDoxygen); + d->m_generateBriefCheckBox.setEnabled(d->m_enableDoxygenCheckBox.isChecked()); + d->m_generateBriefCheckBox.setChecked(settings.generateBrief); + d->m_leadingAsterisksCheckBox.setChecked(settings.leadingAsterisks); + d->m_commandPrefixComboBox.setCurrentIndex(int(settings.commandPrefix)); +} + +namespace Internal { + +CommentsSettingsPage::CommentsSettingsPage() +{ + setId(Constants::TEXT_EDITOR_COMMENTS_SETTINGS); + setDisplayName(Tr::tr("Documentation Comments")); + setCategory(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("Text Editor")); + setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH); + setWidgetCreator([] { return new CommentsSettingsWidget(CommentsSettings::data()); }); +} + +} // namespace Internal +} // namespace TextEditor diff --git a/src/plugins/texteditor/commentssettings.h b/src/plugins/texteditor/commentssettings.h index b5844d3a0f8..fd37a8e5b74 100644 --- a/src/plugins/texteditor/commentssettings.h +++ b/src/plugins/texteditor/commentssettings.h @@ -5,31 +5,82 @@ #include "texteditor_global.h" -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <coreplugin/dialogs/ioptionspage.h> + +namespace ProjectExplorer { class Project; } namespace TextEditor { class TEXTEDITOR_EXPORT CommentsSettings { public: + enum class CommandPrefix { Auto, At, Backslash }; + class Data { + public: + CommandPrefix commandPrefix = CommandPrefix::Auto; + bool enableDoxygen = true; + bool generateBrief = true; + bool leadingAsterisks = true; + }; + + static Data data() { return instance().m_data; } + static void setData(const Data &data); + + static Utils::Key mainSettingsKey(); + static Utils::Key enableDoxygenSettingsKey(); + static Utils::Key generateBriefSettingsKey(); + static Utils::Key leadingAsterisksSettingsKey(); + static Utils::Key commandPrefixKey(); + +private: CommentsSettings(); + static CommentsSettings &instance(); + void save() const; + void load(); - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + Data m_data; +}; +inline bool operator==(const CommentsSettings::Data &a, const CommentsSettings::Data &b) +{ + return a.enableDoxygen == b.enableDoxygen + && a.commandPrefix == b.commandPrefix + && a.generateBrief == b.generateBrief + && a.leadingAsterisks == b.leadingAsterisks; +} +inline bool operator!=(const CommentsSettings::Data &a, const CommentsSettings::Data &b) +{ + return !(a == b); +} - bool equals(const CommentsSettings &other) const; - friend bool operator==(const CommentsSettings &a, const CommentsSettings &b) - { return a.equals(b); } +class TEXTEDITOR_EXPORT CommentsSettingsWidget final : public Core::IOptionsPageWidget +{ + Q_OBJECT +public: + CommentsSettingsWidget(const CommentsSettings::Data &settings); + ~CommentsSettingsWidget(); - friend bool operator!=(const CommentsSettings &a, const CommentsSettings &b) - { return !(a == b); } + CommentsSettings::Data settingsData() const; - bool m_enableDoxygen; - bool m_generateBrief; - bool m_leadingAsterisks; +signals: + void settingsChanged(); + +private: + void apply() override; + + void initFromSettings(const CommentsSettings::Data &settings); + + class Private; + Private * const d; }; +namespace Internal { + +class CommentsSettingsPage : public Core::IOptionsPage +{ +public: + CommentsSettingsPage(); +}; + +} // namespace Internal } // namespace TextEditor diff --git a/src/plugins/texteditor/completionsettings.cpp b/src/plugins/texteditor/completionsettings.cpp index e4d6e76028a..033f322a6e4 100644 --- a/src/plugins/texteditor/completionsettings.cpp +++ b/src/plugins/texteditor/completionsettings.cpp @@ -3,29 +3,31 @@ #include "completionsettings.h" -#include <QSettings> +#include <utils/qtcsettings.h> -static const char settingsGroup[] = "CppTools/Completion"; -static const char caseSensitivityKey[] = "CaseSensitivity"; -static const char completionTriggerKey[] = "CompletionTrigger"; -static const char automaticProposalTimeoutKey[] = "AutomaticProposalTimeout"; -static const char characterThresholdKey[] = "CharacterThreshold"; -static const char autoInsertBracesKey[] = "AutoInsertBraces"; -static const char surroundingAutoBracketsKey[] = "SurroundingAutoBrackets"; -static const char autoInsertQuotesKey[] = "AutoInsertQuotes"; -static const char surroundingAutoQuotesKey[] = "SurroundingAutoQuotes"; -static const char partiallyCompleteKey[] = "PartiallyComplete"; -static const char spaceAfterFunctionNameKey[] = "SpaceAfterFunctionName"; -static const char autoSplitStringsKey[] = "AutoSplitStrings"; -static const char animateAutoCompleteKey[] = "AnimateAutoComplete"; -static const char highlightAutoCompleteKey[] = "HighlightAutoComplete"; -static const char skipAutoCompleteKey[] = "SkipAutoComplete"; -static const char autoRemoveKey[] = "AutoRemove"; -static const char overwriteClosingCharsKey[] = "OverwriteClosingChars"; +using namespace Utils; -using namespace TextEditor; +namespace TextEditor { -void CompletionSettings::toSettings(QSettings *s) const +const char settingsGroup[] = "CppTools/Completion"; +const char caseSensitivityKey[] = "CaseSensitivity"; +const char completionTriggerKey[] = "CompletionTrigger"; +const char automaticProposalTimeoutKey[] = "AutomaticProposalTimeout"; +const char characterThresholdKey[] = "CharacterThreshold"; +const char autoInsertBracesKey[] = "AutoInsertBraces"; +const char surroundingAutoBracketsKey[] = "SurroundingAutoBrackets"; +const char autoInsertQuotesKey[] = "AutoInsertQuotes"; +const char surroundingAutoQuotesKey[] = "SurroundingAutoQuotes"; +const char partiallyCompleteKey[] = "PartiallyComplete"; +const char spaceAfterFunctionNameKey[] = "SpaceAfterFunctionName"; +const char autoSplitStringsKey[] = "AutoSplitStrings"; +const char animateAutoCompleteKey[] = "AnimateAutoComplete"; +const char highlightAutoCompleteKey[] = "HighlightAutoComplete"; +const char skipAutoCompleteKey[] = "SkipAutoComplete"; +const char autoRemoveKey[] = "AutoRemove"; +const char overwriteClosingCharsKey[] = "OverwriteClosingChars"; + +void CompletionSettings::toSettings(QtcSettings *s) const { s->beginGroup(settingsGroup); s->setValue(caseSensitivityKey, (int) m_caseSensitivity); @@ -47,7 +49,7 @@ void CompletionSettings::toSettings(QSettings *s) const s->endGroup(); } -void CompletionSettings::fromSettings(QSettings *s) +void CompletionSettings::fromSettings(QtcSettings *s) { *this = CompletionSettings(); // Assign defaults @@ -107,3 +109,5 @@ bool CompletionSettings::equals(const CompletionSettings &cs) const && m_overwriteClosingChars == cs.m_overwriteClosingChars ; } + +} // TextEditor diff --git a/src/plugins/texteditor/completionsettings.h b/src/plugins/texteditor/completionsettings.h index b2d201e8975..61294e65ad9 100644 --- a/src/plugins/texteditor/completionsettings.h +++ b/src/plugins/texteditor/completionsettings.h @@ -5,10 +5,7 @@ #include "texteditor_global.h" -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE - +namespace Utils { class QtcSettings; } namespace TextEditor { enum CaseSensitivity { @@ -29,8 +26,8 @@ enum CompletionTrigger { class TEXTEDITOR_EXPORT CompletionSettings { public: - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + void toSettings(Utils::QtcSettings *s) const; + void fromSettings(Utils::QtcSettings *s); bool equals(const CompletionSettings &bs) const; diff --git a/src/plugins/texteditor/completionsettingspage.cpp b/src/plugins/texteditor/completionsettingspage.cpp index 167cde25a75..aba86467143 100644 --- a/src/plugins/texteditor/completionsettingspage.cpp +++ b/src/plugins/texteditor/completionsettingspage.cpp @@ -38,7 +38,7 @@ private: CaseSensitivity caseSensitivity() const; CompletionTrigger completionTrigger() const; - void settingsFromUi(CompletionSettings &completion, CommentsSettings &comment) const; + void settingsFromUi(CompletionSettings &completion) const; CompletionSettingsPage *m_owner = nullptr; @@ -58,9 +58,6 @@ private: QCheckBox *m_skipAutoComplete; QCheckBox *m_removeAutoComplete; QCheckBox *m_overwriteClosingChars; - QCheckBox *m_enableDoxygenCheckBox; - QCheckBox *m_generateBriefCheckBox; - QCheckBox *m_leadingAsterisksCheckBox; }; CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPage *owner) @@ -147,19 +144,6 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag m_overwriteClosingChars = new QCheckBox(Tr::tr("Overwrite closing punctuation")); m_overwriteClosingChars->setToolTip(Tr::tr("Automatically overwrite closing parentheses and quotes.")); - m_enableDoxygenCheckBox = new QCheckBox(Tr::tr("Enable Doxygen blocks")); - m_enableDoxygenCheckBox->setToolTip(Tr::tr("Automatically creates a Doxygen comment upon pressing " - "enter after a '/**', '/*!', '//!' or '///'.")); - - m_generateBriefCheckBox = new QCheckBox(Tr::tr("Generate brief description")); - m_generateBriefCheckBox->setToolTip(Tr::tr("Generates a <i>brief</i> command with an initial " - "description for the corresponding declaration.")); - - m_leadingAsterisksCheckBox = new QCheckBox(Tr::tr("Add leading asterisks")); - m_leadingAsterisksCheckBox->setToolTip( - Tr::tr("Adds leading asterisks when continuing C/C++ \"/*\", Qt \"/*!\" " - "and Java \"/**\" style comments on new lines.")); - connect(m_completionTrigger, &QComboBox::currentIndexChanged, this, [this, automaticProposalTimeoutLabel] { const bool enableTimeoutWidgets = completionTrigger() == AutomaticCompletion; @@ -211,11 +195,6 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag m_skipAutoComplete->setChecked(m_owner->m_completionSettings.m_skipAutoCompletedText); m_removeAutoComplete->setChecked(m_owner->m_completionSettings.m_autoRemove); - m_enableDoxygenCheckBox->setChecked(m_owner->m_commentsSettings.m_enableDoxygen); - m_generateBriefCheckBox->setChecked(m_owner->m_commentsSettings.m_generateBrief); - m_leadingAsterisksCheckBox->setChecked(m_owner->m_commentsSettings.m_leadingAsterisks); - - m_generateBriefCheckBox->setEnabled(m_enableDoxygenCheckBox->isChecked()); m_skipAutoComplete->setEnabled(m_highlightAutoComplete->isChecked()); m_removeAutoComplete->setEnabled(m_highlightAutoComplete->isChecked()); @@ -254,18 +233,9 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag } } }, - Group { - title(Tr::tr("Documentation Comments")), - Column { - m_enableDoxygenCheckBox, - indent(m_generateBriefCheckBox), - m_leadingAsterisksCheckBox - } - }, st }.attachTo(this); - connect(m_enableDoxygenCheckBox, &QCheckBox::toggled, m_generateBriefCheckBox, &QCheckBox::setEnabled); connect(m_highlightAutoComplete, &QCheckBox::toggled, m_skipAutoComplete, &QCheckBox::setEnabled); connect(m_highlightAutoComplete, &QCheckBox::toggled, m_removeAutoComplete, &QCheckBox::setEnabled); } @@ -273,21 +243,14 @@ CompletionSettingsPageWidget::CompletionSettingsPageWidget(CompletionSettingsPag void CompletionSettingsPageWidget::apply() { CompletionSettings completionSettings; - CommentsSettings commentsSettings; - settingsFromUi(completionSettings, commentsSettings); + settingsFromUi(completionSettings); if (m_owner->m_completionSettings != completionSettings) { m_owner->m_completionSettings = completionSettings; m_owner->m_completionSettings.toSettings(Core::ICore::settings()); emit TextEditorSettings::instance()->completionSettingsChanged(completionSettings); } - - if (m_owner->m_commentsSettings != commentsSettings) { - m_owner->m_commentsSettings = commentsSettings; - m_owner->m_commentsSettings.toSettings(Core::ICore::settings()); - emit TextEditorSettings::instance()->commentsSettingsChanged(commentsSettings); - } } CaseSensitivity CompletionSettingsPageWidget::caseSensitivity() const @@ -314,8 +277,7 @@ CompletionTrigger CompletionSettingsPageWidget::completionTrigger() const } } -void CompletionSettingsPageWidget::settingsFromUi(CompletionSettings &completion, - CommentsSettings &comment) const +void CompletionSettingsPageWidget::settingsFromUi(CompletionSettings &completion) const { completion.m_caseSensitivity = caseSensitivity(); completion.m_completionTrigger = completionTrigger(); @@ -334,10 +296,6 @@ void CompletionSettingsPageWidget::settingsFromUi(CompletionSettings &completion completion.m_highlightAutoComplete = m_highlightAutoComplete->isChecked(); completion.m_skipAutoCompletedText = m_skipAutoComplete->isChecked(); completion.m_autoRemove = m_removeAutoComplete->isChecked(); - - comment.m_enableDoxygen = m_enableDoxygenCheckBox->isChecked(); - comment.m_generateBrief = m_generateBriefCheckBox->isChecked(); - comment.m_leadingAsterisks = m_leadingAsterisksCheckBox->isChecked(); } const CompletionSettings &CompletionSettingsPage::completionSettings() const @@ -345,10 +303,6 @@ const CompletionSettings &CompletionSettingsPage::completionSettings() const return m_completionSettings; } -const CommentsSettings &CompletionSettingsPage::commentsSettings() const -{ - return m_commentsSettings; -} CompletionSettingsPage::CompletionSettingsPage() { @@ -359,9 +313,8 @@ CompletionSettingsPage::CompletionSettingsPage() setCategoryIconPath(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY_ICON_PATH); setWidgetCreator([this] { return new CompletionSettingsPageWidget(this); }); - QSettings *s = Core::ICore::settings(); + QtcSettings *s = Core::ICore::settings(); m_completionSettings.fromSettings(s); - m_commentsSettings.fromSettings(s); } } // Internal diff --git a/src/plugins/texteditor/completionsettingspage.h b/src/plugins/texteditor/completionsettingspage.h index e3921348a3a..286f2508033 100644 --- a/src/plugins/texteditor/completionsettingspage.h +++ b/src/plugins/texteditor/completionsettingspage.h @@ -3,7 +3,6 @@ #pragma once -#include "commentssettings.h" #include "completionsettings.h" #include <coreplugin/dialogs/ioptionspage.h> @@ -17,12 +16,10 @@ public: CompletionSettingsPage(); const CompletionSettings &completionSettings() const; - const CommentsSettings &commentsSettings() const; private: friend class CompletionSettingsPageWidget; - CommentsSettings m_commentsSettings; CompletionSettings m_completionSettings; }; diff --git a/src/plugins/texteditor/displaysettings.cpp b/src/plugins/texteditor/displaysettings.cpp index fde9774cf3f..5cd8e86c151 100644 --- a/src/plugins/texteditor/displaysettings.cpp +++ b/src/plugins/texteditor/displaysettings.cpp @@ -6,90 +6,92 @@ #include "texteditorconstants.h" #include <coreplugin/icore.h> + +#include <utils/qtcsettings.h> #include <utils/tooltip/tooltip.h> #include <QLabel> -#include <QSettings> -#include <QString> -static const char displayLineNumbersKey[] = "DisplayLineNumbers"; -static const char textWrappingKey[] = "TextWrapping"; -static const char visualizeWhitespaceKey[] = "VisualizeWhitespace"; -static const char visualizeIndentKey[] = "VisualizeIndent"; -static const char displayFoldingMarkersKey[] = "DisplayFoldingMarkers"; -static const char highlightCurrentLineKey[] = "HighlightCurrentLine2Key"; -static const char highlightBlocksKey[] = "HighlightBlocksKey"; -static const char animateMatchingParenthesesKey[] = "AnimateMatchingParenthesesKey"; -static const char highlightMatchingParenthesesKey[] = "HightlightMatchingParenthesesKey"; -static const char markTextChangesKey[] = "MarkTextChanges"; -static const char autoFoldFirstCommentKey[] = "AutoFoldFirstComment"; -static const char centerCursorOnScrollKey[] = "CenterCursorOnScroll"; -static const char openLinksInNextSplitKey[] = "OpenLinksInNextSplitKey"; -static const char displayFileEncodingKey[] = "DisplayFileEncoding"; -static const char displayFileLineEndingKey[] = "DisplayFileLineEnding"; -static const char scrollBarHighlightsKey[] = "ScrollBarHighlights"; -static const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile"; -static const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax"; -static const char displayAnnotationsKey[] = "DisplayAnnotations"; -static const char annotationAlignmentKey[] = "AnnotationAlignment"; -static const char minimalAnnotationContentKey[] = "MinimalAnnotationContent"; -static const char groupPostfix[] = "textDisplaySettings"; +using namespace Utils; namespace TextEditor { -void DisplaySettings::toSettings(QSettings *s) const +const char displayLineNumbersKey[] = "DisplayLineNumbers"; +const char textWrappingKey[] = "TextWrapping"; +const char visualizeWhitespaceKey[] = "VisualizeWhitespace"; +const char visualizeIndentKey[] = "VisualizeIndent"; +const char displayFoldingMarkersKey[] = "DisplayFoldingMarkers"; +const char highlightCurrentLineKey[] = "HighlightCurrentLine2Key"; +const char highlightBlocksKey[] = "HighlightBlocksKey"; +const char animateMatchingParenthesesKey[] = "AnimateMatchingParenthesesKey"; +const char highlightMatchingParenthesesKey[] = "HightlightMatchingParenthesesKey"; +const char markTextChangesKey[] = "MarkTextChanges"; +const char autoFoldFirstCommentKey[] = "AutoFoldFirstComment"; +const char centerCursorOnScrollKey[] = "CenterCursorOnScroll"; +const char openLinksInNextSplitKey[] = "OpenLinksInNextSplitKey"; +const char displayFileEncodingKey[] = "DisplayFileEncoding"; +const char displayFileLineEndingKey[] = "DisplayFileLineEnding"; +const char scrollBarHighlightsKey[] = "ScrollBarHighlights"; +const char animateNavigationWithinFileKey[] = "AnimateNavigationWithinFile"; +const char animateWithinFileTimeMaxKey[] = "AnimateWithinFileTimeMax"; +const char displayAnnotationsKey[] = "DisplayAnnotations"; +const char annotationAlignmentKey[] = "AnnotationAlignment"; +const char minimalAnnotationContentKey[] = "MinimalAnnotationContent"; +const char groupPostfix[] = "textDisplaySettings"; + +void DisplaySettings::toSettings(QtcSettings *s) const { s->beginGroup(groupPostfix); - s->setValue(QLatin1String(displayLineNumbersKey), m_displayLineNumbers); - s->setValue(QLatin1String(textWrappingKey), m_textWrapping); - s->setValue(QLatin1String(visualizeWhitespaceKey), m_visualizeWhitespace); - s->setValue(QLatin1String(visualizeIndentKey), m_visualizeIndent); - s->setValue(QLatin1String(displayFoldingMarkersKey), m_displayFoldingMarkers); - s->setValue(QLatin1String(highlightCurrentLineKey), m_highlightCurrentLine); - s->setValue(QLatin1String(highlightBlocksKey), m_highlightBlocks); - s->setValue(QLatin1String(animateMatchingParenthesesKey), m_animateMatchingParentheses); - s->setValue(QLatin1String(highlightMatchingParenthesesKey), m_highlightMatchingParentheses); - s->setValue(QLatin1String(markTextChangesKey), m_markTextChanges); - s->setValue(QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment); - s->setValue(QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll); - s->setValue(QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit); - s->setValue(QLatin1String(displayFileEncodingKey), m_displayFileEncoding); - s->setValue(QLatin1String(displayFileLineEndingKey), m_displayFileLineEnding); - s->setValue(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights); - s->setValue(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile); - s->setValue(QLatin1String(displayAnnotationsKey), m_displayAnnotations); - s->setValue(QLatin1String(annotationAlignmentKey), static_cast<int>(m_annotationAlignment)); + s->setValue(displayLineNumbersKey, m_displayLineNumbers); + s->setValue(textWrappingKey, m_textWrapping); + s->setValue(visualizeWhitespaceKey, m_visualizeWhitespace); + s->setValue(visualizeIndentKey, m_visualizeIndent); + s->setValue(displayFoldingMarkersKey, m_displayFoldingMarkers); + s->setValue(highlightCurrentLineKey, m_highlightCurrentLine); + s->setValue(highlightBlocksKey, m_highlightBlocks); + s->setValue(animateMatchingParenthesesKey, m_animateMatchingParentheses); + s->setValue(highlightMatchingParenthesesKey, m_highlightMatchingParentheses); + s->setValue(markTextChangesKey, m_markTextChanges); + s->setValue(autoFoldFirstCommentKey, m_autoFoldFirstComment); + s->setValue(centerCursorOnScrollKey, m_centerCursorOnScroll); + s->setValue(openLinksInNextSplitKey, m_openLinksInNextSplit); + s->setValue(displayFileEncodingKey, m_displayFileEncoding); + s->setValue(displayFileLineEndingKey, m_displayFileLineEnding); + s->setValue(scrollBarHighlightsKey, m_scrollBarHighlights); + s->setValue(animateNavigationWithinFileKey, m_animateNavigationWithinFile); + s->setValue(displayAnnotationsKey, m_displayAnnotations); + s->setValue(annotationAlignmentKey, static_cast<int>(m_annotationAlignment)); s->endGroup(); } -void DisplaySettings::fromSettings(QSettings *s) +void DisplaySettings::fromSettings(QtcSettings *s) { s->beginGroup(groupPostfix); *this = DisplaySettings(); // Assign defaults - m_displayLineNumbers = s->value(QLatin1String(displayLineNumbersKey), m_displayLineNumbers).toBool(); - m_textWrapping = s->value(QLatin1String(textWrappingKey), m_textWrapping).toBool(); - m_visualizeWhitespace = s->value(QLatin1String(visualizeWhitespaceKey), m_visualizeWhitespace).toBool(); - m_visualizeIndent = s->value(QLatin1String(visualizeIndentKey), m_visualizeIndent).toBool(); - m_displayFoldingMarkers = s->value(QLatin1String(displayFoldingMarkersKey), m_displayFoldingMarkers).toBool(); - m_highlightCurrentLine = s->value(QLatin1String(highlightCurrentLineKey), m_highlightCurrentLine).toBool(); - m_highlightBlocks = s->value(QLatin1String(highlightBlocksKey), m_highlightBlocks).toBool(); - m_animateMatchingParentheses = s->value(QLatin1String(animateMatchingParenthesesKey), m_animateMatchingParentheses).toBool(); - m_highlightMatchingParentheses = s->value(QLatin1String(highlightMatchingParenthesesKey), m_highlightMatchingParentheses).toBool(); - m_markTextChanges = s->value(QLatin1String(markTextChangesKey), m_markTextChanges).toBool(); - m_autoFoldFirstComment = s->value(QLatin1String(autoFoldFirstCommentKey), m_autoFoldFirstComment).toBool(); - m_centerCursorOnScroll = s->value(QLatin1String(centerCursorOnScrollKey), m_centerCursorOnScroll).toBool(); - m_openLinksInNextSplit = s->value(QLatin1String(openLinksInNextSplitKey), m_openLinksInNextSplit).toBool(); - m_displayFileEncoding = s->value(QLatin1String(displayFileEncodingKey), m_displayFileEncoding).toBool(); - m_displayFileLineEnding = s->value(QLatin1String(displayFileLineEndingKey), m_displayFileLineEnding).toBool(); - m_scrollBarHighlights = s->value(QLatin1String(scrollBarHighlightsKey), m_scrollBarHighlights).toBool(); - m_animateNavigationWithinFile = s->value(QLatin1String(animateNavigationWithinFileKey), m_animateNavigationWithinFile).toBool(); - m_animateWithinFileTimeMax = s->value(QLatin1String(animateWithinFileTimeMaxKey), m_animateWithinFileTimeMax).toInt(); - m_displayAnnotations = s->value(QLatin1String(displayAnnotationsKey), m_displayAnnotations).toBool(); + m_displayLineNumbers = s->value(displayLineNumbersKey, m_displayLineNumbers).toBool(); + m_textWrapping = s->value(textWrappingKey, m_textWrapping).toBool(); + m_visualizeWhitespace = s->value(visualizeWhitespaceKey, m_visualizeWhitespace).toBool(); + m_visualizeIndent = s->value(visualizeIndentKey, m_visualizeIndent).toBool(); + m_displayFoldingMarkers = s->value(displayFoldingMarkersKey, m_displayFoldingMarkers).toBool(); + m_highlightCurrentLine = s->value(highlightCurrentLineKey, m_highlightCurrentLine).toBool(); + m_highlightBlocks = s->value(highlightBlocksKey, m_highlightBlocks).toBool(); + m_animateMatchingParentheses = s->value(animateMatchingParenthesesKey, m_animateMatchingParentheses).toBool(); + m_highlightMatchingParentheses = s->value(highlightMatchingParenthesesKey, m_highlightMatchingParentheses).toBool(); + m_markTextChanges = s->value(markTextChangesKey, m_markTextChanges).toBool(); + m_autoFoldFirstComment = s->value(autoFoldFirstCommentKey, m_autoFoldFirstComment).toBool(); + m_centerCursorOnScroll = s->value(centerCursorOnScrollKey, m_centerCursorOnScroll).toBool(); + m_openLinksInNextSplit = s->value(openLinksInNextSplitKey, m_openLinksInNextSplit).toBool(); + m_displayFileEncoding = s->value(displayFileEncodingKey, m_displayFileEncoding).toBool(); + m_displayFileLineEnding = s->value(displayFileLineEndingKey, m_displayFileLineEnding).toBool(); + m_scrollBarHighlights = s->value(scrollBarHighlightsKey, m_scrollBarHighlights).toBool(); + m_animateNavigationWithinFile = s->value(animateNavigationWithinFileKey, m_animateNavigationWithinFile).toBool(); + m_animateWithinFileTimeMax = s->value(animateWithinFileTimeMaxKey, m_animateWithinFileTimeMax).toInt(); + m_displayAnnotations = s->value(displayAnnotationsKey, m_displayAnnotations).toBool(); m_annotationAlignment = static_cast<TextEditor::AnnotationAlignment>( - s->value(QLatin1String(annotationAlignmentKey), + s->value(annotationAlignmentKey, static_cast<int>(m_annotationAlignment)).toInt()); - m_minimalAnnotationContent = s->value(QLatin1String(minimalAnnotationContentKey), m_minimalAnnotationContent).toInt(); + m_minimalAnnotationContent = s->value(minimalAnnotationContentKey, m_minimalAnnotationContent).toInt(); s->endGroup(); } diff --git a/src/plugins/texteditor/displaysettings.h b/src/plugins/texteditor/displaysettings.h index 635a17b2485..1c8a27f037d 100644 --- a/src/plugins/texteditor/displaysettings.h +++ b/src/plugins/texteditor/displaysettings.h @@ -8,10 +8,11 @@ #include "QMetaType" QT_BEGIN_NAMESPACE -class QSettings; class QLabel; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace TextEditor { enum class AnnotationAlignment @@ -27,8 +28,8 @@ class TEXTEDITOR_EXPORT DisplaySettings public: DisplaySettings() = default; - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + void toSettings(Utils::QtcSettings *s) const; + void fromSettings(Utils::QtcSettings *s); friend bool operator==(const DisplaySettings &t1, const DisplaySettings &t2) { return t1.equals(t2); } friend bool operator!=(const DisplaySettings &t1, const DisplaySettings &t2) { return !t1.equals(t2); } diff --git a/src/plugins/texteditor/extraencodingsettings.cpp b/src/plugins/texteditor/extraencodingsettings.cpp index 349d37a39ef..dab2869423b 100644 --- a/src/plugins/texteditor/extraencodingsettings.cpp +++ b/src/plugins/texteditor/extraencodingsettings.cpp @@ -3,48 +3,28 @@ #include "extraencodingsettings.h" -#include "behaviorsettingswidget.h" #include "texteditortr.h" -#include <utils/settingsutils.h> - -#include <QLatin1String> -#include <QSettings> - // Keep this for compatibility reasons. -static const char kGroupPostfix[] = "EditorManager"; static const char kUtf8BomBehaviorKey[] = "Utf8BomBehavior"; -using namespace TextEditor; +using namespace Utils; + +namespace TextEditor { ExtraEncodingSettings::ExtraEncodingSettings() : m_utf8BomSetting(OnlyKeep) {} ExtraEncodingSettings::~ExtraEncodingSettings() = default; -void ExtraEncodingSettings::toSettings(const QString &category, QSettings *s) const -{ - Q_UNUSED(category) - - Utils::toSettings(QLatin1String(kGroupPostfix), QString(), s, this); -} - -void ExtraEncodingSettings::fromSettings(const QString &category, QSettings *s) -{ - Q_UNUSED(category) - - *this = ExtraEncodingSettings(); - Utils::fromSettings(QLatin1String(kGroupPostfix), QString(), s, this); -} - -QVariantMap ExtraEncodingSettings::toMap() const +Store ExtraEncodingSettings::toMap() const { return { {kUtf8BomBehaviorKey, m_utf8BomSetting} }; } -void ExtraEncodingSettings::fromMap(const QVariantMap &map) +void ExtraEncodingSettings::fromMap(const Store &map) { m_utf8BomSetting = (Utf8BomSetting)map.value(kUtf8BomBehaviorKey, m_utf8BomSetting).toInt(); } @@ -58,3 +38,5 @@ QStringList ExtraEncodingSettings::lineTerminationModeNames() { return {Tr::tr("Unix (LF)"), Tr::tr("Windows (CRLF)")}; } + +} // TextEditor diff --git a/src/plugins/texteditor/extraencodingsettings.h b/src/plugins/texteditor/extraencodingsettings.h index 15fd357a9ff..4d54dc2d7fc 100644 --- a/src/plugins/texteditor/extraencodingsettings.h +++ b/src/plugins/texteditor/extraencodingsettings.h @@ -5,11 +5,7 @@ #include "texteditor_global.h" -#include <QVariantMap> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <utils/store.h> namespace TextEditor { @@ -19,11 +15,8 @@ public: ExtraEncodingSettings(); ~ExtraEncodingSettings(); - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); - - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const ExtraEncodingSettings &s) const; diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index 741225d31e7..b3f749977a0 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -6,14 +6,12 @@ #include "textdocument.h" #include "texteditortr.h" -#include <coreplugin/icore.h> #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/editormanager/editormanager.h> -#include <utils/filesearch.h> -#include <utils/fileutils.h> +#include <utils/qtcsettings.h> -#include <QSettings> +using namespace Utils; namespace TextEditor::Internal { @@ -34,24 +32,15 @@ QString FindInCurrentFile::displayName() const return Tr::tr("Current File"); } -Utils::FileIterator *FindInCurrentFile::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider FindInCurrentFile::fileContainerProvider() const { - Q_UNUSED(nameFilters) - Q_UNUSED(exclusionFilters) - const auto fileName = Utils::FilePath::fromVariant(additionalParameters); - QMap<Utils::FilePath, QTextCodec *> openEditorEncodings - = TextDocument::openedTextDocumentEncodings(); - QTextCodec *codec = openEditorEncodings.value(fileName); - if (!codec) - codec = Core::EditorManager::defaultTextCodec(); - return new Utils::FileListIterator({fileName}, {codec}); -} - -QVariant FindInCurrentFile::additionalParameters() const -{ - return m_currentDocument->filePath().toVariant(); + return [fileName = m_currentDocument->filePath()] { + const QMap<FilePath, QTextCodec *> encodings = TextDocument::openedTextDocumentEncodings(); + QTextCodec *codec = encodings.value(fileName); + if (!codec) + codec = Core::EditorManager::defaultTextCodec(); + return FileListContainer({fileName}, {codec}); + }; } QString FindInCurrentFile::label() const @@ -84,17 +73,16 @@ void FindInCurrentFile::handleFileChange(Core::IEditor *editor) } } - -void FindInCurrentFile::writeSettings(QSettings *settings) +void FindInCurrentFile::writeSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("FindInCurrentFile")); + settings->beginGroup("FindInCurrentFile"); writeCommonSettings(settings); settings->endGroup(); } -void FindInCurrentFile::readSettings(QSettings *settings) +void FindInCurrentFile::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("FindInCurrentFile")); + settings->beginGroup("FindInCurrentFile"); readCommonSettings(settings, "*", ""); settings->endGroup(); } diff --git a/src/plugins/texteditor/findincurrentfile.h b/src/plugins/texteditor/findincurrentfile.h index 3a77e1d30ac..e551db0b7f1 100644 --- a/src/plugins/texteditor/findincurrentfile.h +++ b/src/plugins/texteditor/findincurrentfile.h @@ -25,18 +25,15 @@ public: QString id() const override; QString displayName() const override; bool isEnabled() const override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; - QVariant additionalParameters() const override; QString label() const override; QString toolTip() const override; private: + FileContainerProvider fileContainerProvider() const override; void handleFileChange(Core::IEditor *editor); QPointer<Core::IDocument> m_currentDocument; diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 2f379014dbb..c8a6531ba65 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -9,21 +9,13 @@ #include <coreplugin/find/findplugin.h> #include <coreplugin/icore.h> -#include <utils/filesearch.h> -#include <utils/fileutils.h> #include <utils/historycompleter.h> #include <utils/pathchooser.h> #include <utils/qtcassert.h> -#include <utils/stringutils.h> #include <QComboBox> -#include <QDebug> -#include <QDir> -#include <QFileDialog> #include <QGridLayout> #include <QLabel> -#include <QPushButton> -#include <QSettings> #include <QStackedWidget> using namespace Core; @@ -58,19 +50,13 @@ QString FindInFiles::displayName() const return Tr::tr("Files in File System"); } -FileIterator *FindInFiles::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider FindInFiles::fileContainerProvider() const { - return new SubDirFileIterator({FilePath::fromVariant(additionalParameters)}, - nameFilters, - exclusionFilters, - EditorManager::defaultTextCodec()); -} - -QVariant FindInFiles::additionalParameters() const -{ - return path().toVariant(); + return [nameFilters = fileNameFilters(), exclusionFilters = fileExclusionFilters(), + filePath = searchDir()] { + return SubDirFileContainer({filePath}, nameFilters, exclusionFilters, + EditorManager::defaultTextCodec()); + }; } QString FindInFiles::label() const @@ -78,7 +64,7 @@ QString FindInFiles::label() const QString title = currentSearchEngine()->title(); const QChar slash = QLatin1Char('/'); - const QStringList &nonEmptyComponents = path().toFileInfo().absoluteFilePath() + const QStringList &nonEmptyComponents = searchDir().toFileInfo().absoluteFilePath() .split(slash, Qt::SkipEmptyParts); return Tr::tr("%1 \"%2\":") .arg(title) @@ -89,7 +75,7 @@ QString FindInFiles::toolTip() const { //: the last arg is filled by BaseFileFind::runNewSearch QString tooltip = Tr::tr("Path: %1\nFilter: %2\nExcluding: %3\n%4") - .arg(path().toUserOutput()) + .arg(searchDir().toUserOutput()) .arg(fileNameFilters().join(',')) .arg(fileExclusionFilters().join(',')); @@ -153,13 +139,13 @@ QWidget *FindInFiles::createConfigWidget() m_directory->setExpectedKind(PathChooser::ExistingDirectory); m_directory->setPromptDialogTitle(Tr::tr("Directory to Search")); connect(m_directory.data(), &PathChooser::textChanged, this, - [this] { pathChanged(m_directory->filePath()); }); - m_directory->setHistoryCompleter(QLatin1String(HistoryKey), - /*restoreLastItemFromHistory=*/ true); - if (!HistoryCompleter::historyExistsFor(QLatin1String(HistoryKey))) { + [this] { setSearchDir(m_directory->filePath()); }); + connect(this, &BaseFileFind::searchDirChanged, m_directory, &PathChooser::setFilePath); + m_directory->setHistoryCompleter(HistoryKey, /*restoreLastItemFromHistory=*/ true); + if (!HistoryCompleter::historyExistsFor(HistoryKey)) { auto completer = static_cast<HistoryCompleter *>(m_directory->lineEdit()->completer()); - const QStringList legacyHistory = Core::ICore::settings()->value( - QLatin1String("Find/FindInFiles/directories")).toStringList(); + const QStringList legacyHistory = ICore::settings()->value( + "Find/FindInFiles/directories").toStringList(); for (const QString &dir: legacyHistory) completer->addEntry(dir); } @@ -187,46 +173,31 @@ QWidget *FindInFiles::createConfigWidget() return m_configWidget; } -FilePath FindInFiles::path() const +void FindInFiles::writeSettings(QtcSettings *settings) { - return m_directory->filePath(); -} - -void FindInFiles::writeSettings(QSettings *settings) -{ - settings->beginGroup(QLatin1String("FindInFiles")); + settings->beginGroup("FindInFiles"); writeCommonSettings(settings); settings->endGroup(); } -void FindInFiles::readSettings(QSettings *settings) +void FindInFiles::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("FindInFiles")); + settings->beginGroup("FindInFiles"); readCommonSettings(settings, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave"); settings->endGroup(); } -void FindInFiles::setDirectory(const FilePath &directory) -{ - m_directory->setFilePath(directory); -} - void FindInFiles::setBaseDirectory(const FilePath &directory) { m_directory->setBaseDirectory(directory); } -FilePath FindInFiles::directory() const -{ - return m_directory->filePath(); -} - void FindInFiles::findOnFileSystem(const QString &path) { QTC_ASSERT(m_instance, return); const QFileInfo fi(path); const QString folder = fi.isDir() ? fi.absoluteFilePath() : fi.absolutePath(); - m_instance->setDirectory(FilePath::fromString(folder)); + m_instance->setSearchDir(FilePath::fromString(folder)); Find::openFindDialog(m_instance); } diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h index f9aeb92e317..49c78d2a74b 100644 --- a/src/plugins/texteditor/findinfiles.h +++ b/src/plugins/texteditor/findinfiles.h @@ -5,17 +5,17 @@ #include "basefilefind.h" -#include <utils/fileutils.h> - #include <QPointer> -#include <QStringListModel> QT_BEGIN_NAMESPACE class QComboBox; class QStackedWidget; QT_END_NAMESPACE -namespace Utils { class PathChooser; } +namespace Utils { +class PathChooser; +class QtcSettings; +} // Utils namespace TextEditor { @@ -30,32 +30,24 @@ public: QString id() const override; QString displayName() const override; QWidget *createConfigWidget() override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; bool isValid() const override; void setDirectory(const Utils::FilePath &directory); void setBaseDirectory(const Utils::FilePath &directory); - Utils::FilePath directory() const; static void findOnFileSystem(const QString &path); static FindInFiles *instance(); -signals: - void pathChanged(const Utils::FilePath &directory); - protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; - QVariant additionalParameters() const override; QString label() const override; QString toolTip() const override; void syncSearchEngineCombo(int selectedSearchEngineIndex) override; private: + FileContainerProvider fileContainerProvider() const override; void setValid(bool valid); void searchEnginesSelectionChanged(int index); - Utils::FilePath path() const; QPointer<QWidget> m_configWidget; QPointer<Utils::PathChooser> m_directory; diff --git a/src/plugins/texteditor/findinopenfiles.cpp b/src/plugins/texteditor/findinopenfiles.cpp index 12fb45eb3c9..52c04d3b1d0 100644 --- a/src/plugins/texteditor/findinopenfiles.cpp +++ b/src/plugins/texteditor/findinopenfiles.cpp @@ -6,13 +6,12 @@ #include "textdocument.h" #include "texteditortr.h" -#include <coreplugin/icore.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/documentmodel.h> -#include <utils/filesearch.h> +#include <utils/qtcsettings.h> -#include <QSettings> +using namespace Utils; namespace TextEditor::Internal { @@ -34,35 +33,25 @@ QString FindInOpenFiles::displayName() const return Tr::tr("Open Documents"); } -Utils::FileIterator *FindInOpenFiles::files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const +FileContainerProvider FindInOpenFiles::fileContainerProvider() const { - Q_UNUSED(nameFilters) - Q_UNUSED(exclusionFilters) - Q_UNUSED(additionalParameters) - QMap<Utils::FilePath, QTextCodec *> openEditorEncodings - = TextDocument::openedTextDocumentEncodings(); - Utils::FilePaths fileNames; - QList<QTextCodec *> codecs; - const QList<Core::DocumentModel::Entry *> entries = Core::DocumentModel::entries(); - for (Core::DocumentModel::Entry *entry : entries) { - const Utils::FilePath fileName = entry->filePath(); - if (!fileName.isEmpty()) { - fileNames.append(fileName); - QTextCodec *codec = openEditorEncodings.value(fileName); - if (!codec) - codec = Core::EditorManager::defaultTextCodec(); - codecs.append(codec); + return [] { + const QMap<FilePath, QTextCodec *> encodings = TextDocument::openedTextDocumentEncodings(); + FilePaths fileNames; + QList<QTextCodec *> codecs; + const QList<Core::DocumentModel::Entry *> entries = Core::DocumentModel::entries(); + for (Core::DocumentModel::Entry *entry : entries) { + const FilePath fileName = entry->filePath(); + if (!fileName.isEmpty()) { + fileNames.append(fileName); + QTextCodec *codec = encodings.value(fileName); + if (!codec) + codec = Core::EditorManager::defaultTextCodec(); + codecs.append(codec); + } } - } - - return new Utils::FileListIterator(fileNames, codecs); -} - -QVariant FindInOpenFiles::additionalParameters() const -{ - return QVariant(); + return FileListContainer(fileNames, codecs); + }; } QString FindInOpenFiles::label() const @@ -81,16 +70,16 @@ bool FindInOpenFiles::isEnabled() const return Core::DocumentModel::entryCount() > 0; } -void FindInOpenFiles::writeSettings(QSettings *settings) +void FindInOpenFiles::writeSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("FindInOpenFiles")); + settings->beginGroup("FindInOpenFiles"); writeCommonSettings(settings); settings->endGroup(); } -void FindInOpenFiles::readSettings(QSettings *settings) +void FindInOpenFiles::readSettings(QtcSettings *settings) { - settings->beginGroup(QLatin1String("FindInOpenFiles")); + settings->beginGroup("FindInOpenFiles"); readCommonSettings(settings, "*", ""); settings->endGroup(); } diff --git a/src/plugins/texteditor/findinopenfiles.h b/src/plugins/texteditor/findinopenfiles.h index 244c25f5965..8e2f42695a0 100644 --- a/src/plugins/texteditor/findinopenfiles.h +++ b/src/plugins/texteditor/findinopenfiles.h @@ -18,18 +18,15 @@ public: QString id() const override; QString displayName() const override; bool isEnabled() const override; - void writeSettings(QSettings *settings) override; - void readSettings(QSettings *settings) override; + void writeSettings(Utils::QtcSettings *settings) override; + void readSettings(Utils::QtcSettings *settings) override; protected: - Utils::FileIterator *files(const QStringList &nameFilters, - const QStringList &exclusionFilters, - const QVariant &additionalParameters) const override; - QVariant additionalParameters() const override; QString label() const override; QString toolTip() const override; private: + FileContainerProvider fileContainerProvider() const override; void updateEnabledState(); }; diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp index 873be0ae78a..be550460488 100644 --- a/src/plugins/texteditor/fontsettings.cpp +++ b/src/plugins/texteditor/fontsettings.cpp @@ -17,7 +17,6 @@ #include <QFile> #include <QFont> #include <QFontDatabase> -#include <QSettings> #include <QTextCharFormat> #include <cmath> @@ -56,57 +55,57 @@ void FontSettings::clear() clearCaches(); } -static QString settingsGroup() +static Key settingsGroup() { - return Utils::settingsKey(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY); + return keyFromString(Utils::settingsKey(TextEditor::Constants::TEXT_EDITOR_SETTINGS_CATEGORY)); } -void FontSettings::toSettings(QSettings *s) const +void FontSettings::toSettings(QtcSettings *s) const { s->beginGroup(settingsGroup()); - if (m_family != defaultFixedFontFamily() || s->contains(QLatin1String(fontFamilyKey))) - s->setValue(QLatin1String(fontFamilyKey), m_family); + if (m_family != defaultFixedFontFamily() || s->contains(fontFamilyKey)) + s->setValue(fontFamilyKey, m_family); - if (m_fontSize != defaultFontSize() || s->contains(QLatin1String(fontSizeKey))) - s->setValue(QLatin1String(fontSizeKey), m_fontSize); + if (m_fontSize != defaultFontSize() || s->contains(fontSizeKey)) + s->setValue(fontSizeKey, m_fontSize); - if (m_fontZoom!= 100 || s->contains(QLatin1String(fontZoomKey))) - s->setValue(QLatin1String(fontZoomKey), m_fontZoom); + if (m_fontZoom!= 100 || s->contains(fontZoomKey)) + s->setValue(fontZoomKey, m_fontZoom); - if (m_lineSpacing != 100 || s->contains(QLatin1String(lineSpacingKey))) - s->setValue(QLatin1String(lineSpacingKey), m_lineSpacing); + if (m_lineSpacing != 100 || s->contains(lineSpacingKey)) + s->setValue(lineSpacingKey, m_lineSpacing); - if (m_antialias != DEFAULT_ANTIALIAS || s->contains(QLatin1String(antialiasKey))) - s->setValue(QLatin1String(antialiasKey), m_antialias); + if (m_antialias != DEFAULT_ANTIALIAS || s->contains(antialiasKey)) + s->setValue(antialiasKey, m_antialias); - auto schemeFileNames = s->value(QLatin1String(schemeFileNamesKey)).toMap(); + auto schemeFileNames = s->value(schemeFileNamesKey).toMap(); if (m_schemeFileName != defaultSchemeFileName() || schemeFileNames.contains(Utils::creatorTheme()->id())) { schemeFileNames.insert(Utils::creatorTheme()->id(), m_schemeFileName.toSettings()); - s->setValue(QLatin1String(schemeFileNamesKey), schemeFileNames); + s->setValue(schemeFileNamesKey, schemeFileNames); } s->endGroup(); } -bool FontSettings::fromSettings(const FormatDescriptions &descriptions, const QSettings *s) +bool FontSettings::fromSettings(const FormatDescriptions &descriptions, const QtcSettings *s) { clear(); - QString group = settingsGroup(); - if (!s->childGroups().contains(group)) + Key group = settingsGroup(); + if (!s->childGroups().contains(stringFromKey(group))) return false; - group += QLatin1Char('/'); + group = group + '/'; - m_family = s->value(group + QLatin1String(fontFamilyKey), defaultFixedFontFamily()).toString(); - m_fontSize = s->value(group + QLatin1String(fontSizeKey), m_fontSize).toInt(); - m_fontZoom= s->value(group + QLatin1String(fontZoomKey), m_fontZoom).toInt(); - m_lineSpacing = s->value(group + QLatin1String(lineSpacingKey), m_lineSpacing).toInt(); - m_antialias = s->value(group + QLatin1String(antialiasKey), DEFAULT_ANTIALIAS).toBool(); + m_family = s->value(group + fontFamilyKey, defaultFixedFontFamily()).toString(); + m_fontSize = s->value(group + fontSizeKey, m_fontSize).toInt(); + m_fontZoom= s->value(group + fontZoomKey, m_fontZoom).toInt(); + m_lineSpacing = s->value(group + lineSpacingKey, m_lineSpacing).toInt(); + m_antialias = s->value(group + antialiasKey, DEFAULT_ANTIALIAS).toBool(); - if (s->contains(group + QLatin1String(schemeFileNamesKey))) { + if (s->contains(group + schemeFileNamesKey)) { // Load the selected color scheme for the current theme - auto schemeFileNames = s->value(group + QLatin1String(schemeFileNamesKey)).toMap(); + auto schemeFileNames = s->value(group + schemeFileNamesKey).toMap(); if (schemeFileNames.contains(Utils::creatorTheme()->id())) { const FilePath scheme = FilePath::fromSettings(schemeFileNames.value(Utils::creatorTheme()->id())); loadColorScheme(scheme, descriptions); @@ -333,7 +332,7 @@ void FontSettings::setFontZoom(int zoom) qreal FontSettings::lineSpacing() const { QFont currentFont = font(); - currentFont.setPointSize(m_fontSize * m_fontZoom / 100); + currentFont.setPointSize(std::max(m_fontSize * m_fontZoom / 100, 1)); qreal spacing = QFontMetricsF(currentFont).lineSpacing(); if (m_lineSpacing != 100) spacing *= qreal(m_lineSpacing) / 100; diff --git a/src/plugins/texteditor/fontsettings.h b/src/plugins/texteditor/fontsettings.h index 44cb074c492..c6924128b0e 100644 --- a/src/plugins/texteditor/fontsettings.h +++ b/src/plugins/texteditor/fontsettings.h @@ -17,10 +17,11 @@ #include <QVector> QT_BEGIN_NAMESPACE -class QSettings; class QFont; QT_END_NAMESPACE +namespace Utils { class QtcSettings; } + namespace TextEditor { class FormatDescription; @@ -37,10 +38,10 @@ public: void clear(); inline bool isEmpty() const { return m_scheme.isEmpty(); } - void toSettings(QSettings *s) const; + void toSettings(Utils::QtcSettings *s) const; bool fromSettings(const FormatDescriptions &descriptions, - const QSettings *s); + const Utils::QtcSettings *s); QVector<QTextCharFormat> toTextCharFormats(const QVector<TextStyle> &categories) const; QTextCharFormat toTextCharFormat(TextStyle category) const; diff --git a/src/plugins/texteditor/fontsettingspage.cpp b/src/plugins/texteditor/fontsettingspage.cpp index b57e23f35b8..bdd899b7e04 100644 --- a/src/plugins/texteditor/fontsettingspage.cpp +++ b/src/plugins/texteditor/fontsettingspage.cpp @@ -32,7 +32,6 @@ #include <QPalette> #include <QPointer> #include <QPushButton> -#include <QSettings> #include <QSpacerItem> #include <QSpinBox> #include <QTimer> @@ -202,6 +201,8 @@ public: this, &FontSettingsPageWidget::importScheme); connect(exportButton, &QPushButton::clicked, this, &FontSettingsPageWidget::exportScheme); + connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged, + this, &FontSettingsPageWidget::updateFontZoom); updatePointSizes(); refreshColorSchemeList(); @@ -226,6 +227,7 @@ public: void maybeSaveColorScheme(); void updatePointSizes(); + void updateFontZoom(const FontSettings &fontSettings); QList<int> pointSizesForSelectedFont() const; void refreshColorSchemeList(); @@ -449,6 +451,11 @@ void FontSettingsPageWidget::updatePointSizes() m_sizeComboBox->setCurrentIndex(idx); } +void FontSettingsPageWidget::updateFontZoom(const FontSettings &fontSettings) +{ + m_zoomSpinBox->setValue(fontSettings.fontZoom()); +} + QList<int> FontSettingsPageWidget::pointSizesForSelectedFont() const { QFontDatabase db; @@ -693,7 +700,7 @@ void FontSettingsPageWidget::refreshColorSchemeList() int selected = 0; for (const FilePath &file : std::as_const(schemeList)) { - if (m_value.colorSchemeFileName() == file) + if (m_value.colorSchemeFileName().fileName() == file.fileName()) selected = colorSchemes.size(); colorSchemes.append(ColorSchemeEntry(file, true)); } @@ -703,7 +710,7 @@ void FontSettingsPageWidget::refreshColorSchemeList() const FilePaths files = customStylesPath().dirEntries(FileFilter({"*.xml"}, QDir::Files)); for (const FilePath &file : files) { - if (m_value.colorSchemeFileName() == file) + if (m_value.colorSchemeFileName().fileName() == file.fileName()) selected = colorSchemes.size(); colorSchemes.append(ColorSchemeEntry(file, false)); } @@ -759,7 +766,7 @@ void FontSettingsPageWidget::finish() FontSettingsPage::FontSettingsPage(FontSettings *fontSettings, const FormatDescriptions &fd) { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); if (settings) fontSettings->fromSettings(fd, settings); @@ -774,10 +781,4 @@ FontSettingsPage::FontSettingsPage(FontSettings *fontSettings, const FormatDescr setWidgetCreator([this, fontSettings, fd] { return new FontSettingsPageWidget(this, fd, fontSettings); }); } -void FontSettingsPage::setFontZoom(int zoom) -{ - if (m_widget) - static_cast<FontSettingsPageWidget *>(m_widget.data())->m_zoomSpinBox->setValue(zoom); -} - } // TextEditor diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index 4512b71910f..cc7bc395bd1 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -138,10 +138,10 @@ Highlighter::Definitions Highlighter::definitionsForDocument(const TextDocument return definitions; } -static Highlighter::Definition definitionForSetting(const QString &settingsKey, +static Highlighter::Definition definitionForSetting(const Key &settingsKey, const QString &mapKey) { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(Constants::HIGHLIGHTER_SETTINGS_CATEGORY); const QString &definitionName = settings->value(settingsKey).toMap().value(mapKey).toString(); settings->endGroup(); @@ -188,23 +188,23 @@ void Highlighter::rememberDefinitionForDocument(const Highlighter::Definition &d const QString &mimeType = document->mimeType(); const FilePath &path = document->filePath(); const QString &fileExtension = path.completeSuffix(); - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(Constants::HIGHLIGHTER_SETTINGS_CATEGORY); const Definitions &fileNameDefinitions = definitionsForFileName(path); if (fileNameDefinitions.contains(definition)) { if (!fileExtension.isEmpty()) { - const QString id(kDefinitionForExtension); + const Key id(kDefinitionForExtension); QMap<QString, QVariant> map = settings->value(id).toMap(); map.insert(fileExtension, definition.name()); settings->setValue(id, map); } else if (!path.isEmpty()) { - const QString id(kDefinitionForFilePath); + const Key id(kDefinitionForFilePath); QMap<QString, QVariant> map = settings->value(id).toMap(); map.insert(path.absoluteFilePath().toString(), definition.name()); settings->setValue(id, map); } } else if (!mimeType.isEmpty()) { - const QString id(kDefinitionForMimeType); + const Key id(kDefinitionForMimeType); QMap<QString, QVariant> map = settings->value(id).toMap(); map.insert(mimeType, definition.name()); settings->setValue(id, map); @@ -214,7 +214,7 @@ void Highlighter::rememberDefinitionForDocument(const Highlighter::Definition &d void Highlighter::clearDefinitionForDocumentCache() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(Constants::HIGHLIGHTER_SETTINGS_CATEGORY); settings->remove(kDefinitionForMimeType); settings->remove(kDefinitionForExtension); @@ -227,7 +227,8 @@ void Highlighter::addCustomHighlighterPath(const FilePath &path) highlightRepository()->addCustomSearchPath(path.toString()); } -void Highlighter::downloadDefinitions(std::function<void()> callback) { +void Highlighter::downloadDefinitions(std::function<void()> callback) +{ auto downloader = new KSyntaxHighlighting::DefinitionDownloader(highlightRepository()); connect(downloader, &KSyntaxHighlighting::DefinitionDownloader::done, [downloader, callback]() { diff --git a/src/plugins/texteditor/highlighter_test.cpp b/src/plugins/texteditor/highlighter_test.cpp index 89b5196a864..f38e6a4f854 100644 --- a/src/plugins/texteditor/highlighter_test.cpp +++ b/src/plugins/texteditor/highlighter_test.cpp @@ -49,7 +49,7 @@ using FormatRanges = QList<QTextLayout::FormatRange>; QTextCharFormat toFormat(const TextStyle &style) { - const static FontSettings fontSettings = TextEditorSettings::fontSettings(); + const static FontSettings &fontSettings = TextEditorSettings::fontSettings(); auto format = fontSettings.toTextCharFormat(style); if (style == C_FUNCTION) format.setFontWeight(QFont::Bold); // is explicitly set by the ksyntax format definition diff --git a/src/plugins/texteditor/highlightersettings.cpp b/src/plugins/texteditor/highlightersettings.cpp index 095295162ad..b93faa76105 100644 --- a/src/plugins/texteditor/highlightersettings.cpp +++ b/src/plugins/texteditor/highlightersettings.cpp @@ -7,34 +7,34 @@ #include <coreplugin/icore.h> -#include <QSettings> +#include <utils/qtcsettings.h> using namespace Utils; namespace TextEditor { -const QLatin1String kDefinitionFilesPath("UserDefinitionFilesPath"); -const QLatin1String kIgnoredFilesPatterns("IgnoredFilesPatterns"); +const char kDefinitionFilesPath[] = "UserDefinitionFilesPath"; +const char kIgnoredFilesPatterns[] = "IgnoredFilesPatterns"; -static QString groupSpecifier(const QString &postFix, const QString &category) +static Key groupSpecifier(const Key &postFix, const Key &category) { if (category.isEmpty()) return postFix; - return QString(category + postFix); + return Key(category + postFix); } -void HighlighterSettings::toSettings(const QString &category, QSettings *s) const +void HighlighterSettings::toSettings(const Key &category, QtcSettings *s) const { - const QString &group = groupSpecifier(Constants::HIGHLIGHTER_SETTINGS_CATEGORY, category); + const Key group = groupSpecifier(Constants::HIGHLIGHTER_SETTINGS_CATEGORY, category); s->beginGroup(group); s->setValue(kDefinitionFilesPath, m_definitionFilesPath.toSettings()); s->setValue(kIgnoredFilesPatterns, ignoredFilesPatterns()); s->endGroup(); } -void HighlighterSettings::fromSettings(const QString &category, QSettings *s) +void HighlighterSettings::fromSettings(const Key &category, QtcSettings *s) { - const QString &group = groupSpecifier(Constants::HIGHLIGHTER_SETTINGS_CATEGORY, category); + const Key group = groupSpecifier(Constants::HIGHLIGHTER_SETTINGS_CATEGORY, category); s->beginGroup(group); m_definitionFilesPath = FilePath::fromSettings(s->value(kDefinitionFilesPath)); if (!s->contains(kDefinitionFilesPath)) diff --git a/src/plugins/texteditor/highlightersettings.h b/src/plugins/texteditor/highlightersettings.h index adbe828bfb0..4082901c992 100644 --- a/src/plugins/texteditor/highlightersettings.h +++ b/src/plugins/texteditor/highlightersettings.h @@ -10,9 +10,10 @@ #include <QList> #include <QRegularExpression> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { +class Key; +class QtcSettings; +} // Utils namespace TextEditor { @@ -21,8 +22,8 @@ class HighlighterSettings public: HighlighterSettings() = default; - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); + void toSettings(const Utils::Key &category, Utils::QtcSettings *s) const; + void fromSettings(const Utils::Key &category, Utils::QtcSettings *s); void setDefinitionFilesPath(const Utils::FilePath &path) { m_definitionFilesPath = path; } const Utils::FilePath &definitionFilesPath() const { return m_definitionFilesPath; } diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp index 856a181009d..8b704a7157a 100644 --- a/src/plugins/texteditor/highlightersettingspage.cpp +++ b/src/plugins/texteditor/highlightersettingspage.cpp @@ -51,7 +51,7 @@ public: } bool m_initialized = false; - const QString m_settingsPrefix{"Text"}; + const Key m_settingsPrefix{"Text"}; HighlighterSettings m_settings; diff --git a/src/plugins/texteditor/icodestylepreferences.cpp b/src/plugins/texteditor/icodestylepreferences.cpp index 29a524ebf68..864c3a777ea 100644 --- a/src/plugins/texteditor/icodestylepreferences.cpp +++ b/src/plugins/texteditor/icodestylepreferences.cpp @@ -4,11 +4,10 @@ #include "icodestylepreferences.h" #include "codestylepool.h" #include "tabsettings.h" -#include <utils/settingsutils.h> -#include <QSettings> +#include <coreplugin/icore.h> -using namespace TextEditor; +using namespace Utils; static const char currentPreferencesKey[] = "CurrentPreferences"; @@ -26,7 +25,7 @@ public: bool m_readOnly = false; bool m_temporarilyReadOnly = false; bool m_isAdditionalTabDisabled = false; - QString m_settingsSuffix; + Key m_settingsSuffix; }; } @@ -206,29 +205,29 @@ void ICodeStylePreferences::setCurrentDelegate(const QByteArray &id) setCurrentDelegate(d->m_pool->codeStyle(id)); } -void ICodeStylePreferences::setSettingsSuffix(const QString &suffix) +void ICodeStylePreferences::setSettingsSuffix(const Key &suffix) { d->m_settingsSuffix = suffix; } -void ICodeStylePreferences::toSettings(const QString &category, QSettings *s) const +void ICodeStylePreferences::toSettings(const Key &category) const { - Utils::toSettings(d->m_settingsSuffix, category, s, this); + Utils::storeToSettings(category + d->m_settingsSuffix, Core::ICore::settings(), toMap()); } -void ICodeStylePreferences::fromSettings(const QString &category, QSettings *s) +void ICodeStylePreferences::fromSettings(const Key &category) { - Utils::fromSettings(d->m_settingsSuffix, category, s, this); + fromMap(Utils::storeFromSettings(category + d->m_settingsSuffix, Core::ICore::settings())); } -QVariantMap ICodeStylePreferences::toMap() const +Store ICodeStylePreferences::toMap() const { if (!currentDelegate()) return d->m_tabSettings.toMap(); return {{currentPreferencesKey, currentDelegateId()}}; } -void ICodeStylePreferences::fromMap(const QVariantMap &map) +void ICodeStylePreferences::fromMap(const Store &map) { d->m_tabSettings.fromMap(map); const QByteArray delegateId = map.value(currentPreferencesKey).toByteArray(); diff --git a/src/plugins/texteditor/icodestylepreferences.h b/src/plugins/texteditor/icodestylepreferences.h index 7fd616d5604..450941997f8 100644 --- a/src/plugins/texteditor/icodestylepreferences.h +++ b/src/plugins/texteditor/icodestylepreferences.h @@ -5,11 +5,12 @@ #include "texteditor_global.h" +#include <utils/store.h> + #include <QObject> QT_BEGIN_NAMESPACE class QVariant; -class QSettings; QT_END_NAMESPACE namespace TextEditor { @@ -63,13 +64,13 @@ public: QByteArray currentDelegateId() const; void setCurrentDelegate(const QByteArray &id); - void setSettingsSuffix(const QString &suffix); - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); + void setSettingsSuffix(const Utils::Key &suffix); + void toSettings(const Utils::Key &category) const; + void fromSettings(const Utils::Key &category); // make below 2 protected? - virtual QVariantMap toMap() const; - virtual void fromMap(const QVariantMap &map); + virtual Utils::Store toMap() const; + virtual void fromMap(const Utils::Store &map); signals: void tabSettingsChanged(const TextEditor::TabSettings &settings); diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index 80c2aa5d16f..042724d3bf3 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -3,8 +3,8 @@ #pragma once +#include <utils/changeset.h> #include <utils/fileutils.h> -#include <utils/textutils.h> #include <QMap> #include <QTextBlock> @@ -70,9 +70,11 @@ public: indent(cursor, QChar::Null, tabSettings, cursorPositionInEditor); } - virtual Utils::Text::Replacements format(const RangesInLines & /*rangesInLines*/) + enum class FormattingMode { Forced, Settings }; + virtual Utils::EditOperations format(const RangesInLines &, + FormattingMode = FormattingMode::Forced) { - return Utils::Text::Replacements(); + return Utils::EditOperations(); } virtual bool formatOnSave() const { return false; } diff --git a/src/plugins/texteditor/jsoneditor.cpp b/src/plugins/texteditor/jsoneditor.cpp new file mode 100644 index 00000000000..348df6f4d1f --- /dev/null +++ b/src/plugins/texteditor/jsoneditor.cpp @@ -0,0 +1,166 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "jsoneditor.h" + +#include "autocompleter.h" +#include "textdocument.h" +#include "texteditoractionhandler.h" +#include "texteditortr.h" +#include "textindenter.h" + +namespace TextEditor::Internal { + +const char JSON_EDITOR_ID[] = "Editors.Json"; +const char JSON_MIME_TYPE[] = "application/json"; + +static int startsWith(const QString &line, const QString &closingChars) +{ + int count = 0; + for (const QChar &lineChar : line) { + if (closingChars.contains(lineChar)) + ++count; + else if (!lineChar.isSpace()) + break; + } + return count; +} + +class JsonAutoCompleter : public AutoCompleter +{ + bool contextAllowsElectricCharacters(const QTextCursor &cursor) const override + { + return !isInString(cursor); + } + + bool contextAllowsAutoBrackets(const QTextCursor &cursor, const QString &) const override + { + return !isInString(cursor); + } + QString insertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar lookAhead, + bool skipChars, + int *skippedChars) const override + { + Q_UNUSED(cursor) + if (text.isEmpty()) + return QString(); + const QChar current = text.at(0); + switch (current.unicode()) { + case '{': + return QStringLiteral("}"); + case '[': + return QStringLiteral("]"); + case ']': + case '}': + if (current == lookAhead && skipChars) + ++*skippedChars; + break; + default: + break; + } + + return QString(); + } + + bool contextAllowsAutoQuotes(const QTextCursor &cursor, const QString &) const override + { + return !isInString(cursor); + } + QString insertMatchingQuote(const QTextCursor &cursor, + const QString &text, + QChar lookAhead, + bool skipChars, + int *skippedChars) const override + { + Q_UNUSED(cursor) + static const QChar quote('"'); + if (text.isEmpty() || text != quote) + return QString(); + if (lookAhead == quote && skipChars) { + ++*skippedChars; + return QString(); + } + return quote; + } + + bool isInString(const QTextCursor &cursor) const override + { + bool result = false; + const QString text = cursor.block().text(); + const int position = qMin(cursor.positionInBlock(), text.size()); + + for (int i = 0; i < position; ++i) { + if (text.at(i) == '"') { + if (!result || text[i - 1] != '\\') + result = !result; + } + } + return result; + } +}; + +class JsonIndenter : public TextIndenter +{ +public: + JsonIndenter(QTextDocument *doc) : TextIndenter(doc) {} + + bool isElectricCharacter(const QChar &c) const override + { + static QString echars("{}[]"); + return echars.contains(c); + } + + int indentFor(const QTextBlock &block, + const TabSettings &tabSettings, + int /*cursorPositionInEditor*/) override + { + QTextBlock previous = block.previous(); + if (!previous.isValid()) + return 0; + + QString previousText = previous.text(); + while (previousText.trimmed().isEmpty()) { + previous = previous.previous(); + if (!previous.isValid()) + return 0; + previousText = previous.text(); + } + + int indent = tabSettings.indentationColumn(previousText); + + int adjust = previousText.count('{') + previousText.count('['); + adjust -= previousText.count('}') + previousText.count(']'); + adjust += startsWith(previousText, "}]") - startsWith(block.text(), "}]"); + adjust *= tabSettings.m_indentSize; + + return qMax(0, indent + adjust); + } + + void indentBlock(const QTextBlock &block, + const QChar &typedChar, + const TabSettings &tabSettings, + int cursorPositionInEditor) override + { + Q_UNUSED(typedChar) + tabSettings.indentLine(block, indentFor(block, tabSettings, cursorPositionInEditor)); + } +}; + +JsonEditorFactory::JsonEditorFactory() +{ + setId(JSON_EDITOR_ID); + setDisplayName(Tr::tr("JSON Editor")); + addMimeType(JSON_MIME_TYPE); + + setEditorCreator([] { return new BaseTextEditor; }); + setEditorWidgetCreator([] { return new TextEditorWidget; }); + setDocumentCreator([] { return new TextDocument(JSON_EDITOR_ID); }); + setAutoCompleterCreator([] { return new JsonAutoCompleter; }); + setIndenterCreator([](QTextDocument *doc) { return new JsonIndenter(doc); }); + setEditorActionHandlers(TextEditorActionHandler::Format); + setUseGenericHighlighter(true); +} + +} // namespace TextEditor::Internal diff --git a/src/plugins/texteditor/jsoneditor.h b/src/plugins/texteditor/jsoneditor.h new file mode 100644 index 00000000000..d1f783fed70 --- /dev/null +++ b/src/plugins/texteditor/jsoneditor.h @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "texteditor.h" + +namespace TextEditor::Internal { + +class JsonEditorFactory final : public TextEditorFactory +{ +public: + JsonEditorFactory(); +}; + +} // TextEditor::Internal diff --git a/src/plugins/texteditor/marginsettings.cpp b/src/plugins/texteditor/marginsettings.cpp index 8e58e012b8d..c393d588e18 100644 --- a/src/plugins/texteditor/marginsettings.cpp +++ b/src/plugins/texteditor/marginsettings.cpp @@ -3,17 +3,17 @@ #include "marginsettings.h" -#include <QSettings> -#include <QString> -#include <QVariantMap> +#include <utils/qtcsettings.h> -static const char showWrapColumnKey[] = "ShowMargin"; -static const char wrapColumnKey[] = "MarginColumn"; -static const char groupPostfix[] = "textMarginSettings"; -static const char useIndenterColumnKey[] = "UseIndenter"; -static const char tintMarginAreaColumnKey[] = "tintMarginArea"; +using namespace Utils; -using namespace TextEditor; +namespace TextEditor { + +const char showWrapColumnKey[] = "ShowMargin"; +const char wrapColumnKey[] = "MarginColumn"; +const char groupPostfix[] = "textMarginSettings"; +const char useIndenterColumnKey[] = "UseIndenter"; +const char tintMarginAreaColumnKey[] = "tintMarginArea"; MarginSettings::MarginSettings() : m_showMargin(false) @@ -23,29 +23,29 @@ MarginSettings::MarginSettings() { } -void MarginSettings::toSettings(QSettings *s) const +void MarginSettings::toSettings(QtcSettings *s) const { s->beginGroup(groupPostfix); - s->setValue(QLatin1String(showWrapColumnKey), m_showMargin); - s->setValue(QLatin1String(tintMarginAreaColumnKey), m_tintMarginArea); - s->setValue(QLatin1String(useIndenterColumnKey), m_useIndenter); - s->setValue(QLatin1String(wrapColumnKey), m_marginColumn); + s->setValue(showWrapColumnKey, m_showMargin); + s->setValue(tintMarginAreaColumnKey, m_tintMarginArea); + s->setValue(useIndenterColumnKey, m_useIndenter); + s->setValue(wrapColumnKey, m_marginColumn); s->endGroup(); } -void MarginSettings::fromSettings(QSettings *s) +void MarginSettings::fromSettings(QtcSettings *s) { s->beginGroup(groupPostfix); *this = MarginSettings(); // Assign defaults - m_showMargin = s->value(QLatin1String(showWrapColumnKey), m_showMargin).toBool(); - m_tintMarginArea = s->value(QLatin1String(tintMarginAreaColumnKey), m_tintMarginArea).toBool(); - m_useIndenter = s->value(QLatin1String(useIndenterColumnKey), m_useIndenter).toBool(); - m_marginColumn = s->value(QLatin1String(wrapColumnKey), m_marginColumn).toInt(); + m_showMargin = s->value(showWrapColumnKey, m_showMargin).toBool(); + m_tintMarginArea = s->value(tintMarginAreaColumnKey, m_tintMarginArea).toBool(); + m_useIndenter = s->value(useIndenterColumnKey, m_useIndenter).toBool(); + m_marginColumn = s->value(wrapColumnKey, m_marginColumn).toInt(); s->endGroup(); } -QVariantMap MarginSettings::toMap() const +Store MarginSettings::toMap() const { return { {tintMarginAreaColumnKey, m_tintMarginArea}, @@ -55,7 +55,7 @@ QVariantMap MarginSettings::toMap() const }; } -void MarginSettings::fromMap(const QVariantMap &map) +void MarginSettings::fromMap(const Store &map) { m_showMargin = map.value(showWrapColumnKey, m_showMargin).toBool(); m_tintMarginArea = map.value(tintMarginAreaColumnKey, m_tintMarginArea).toBool(); @@ -71,3 +71,5 @@ bool MarginSettings::equals(const MarginSettings &other) const && m_marginColumn == other.m_marginColumn ; } + +} // TextEditor diff --git a/src/plugins/texteditor/marginsettings.h b/src/plugins/texteditor/marginsettings.h index e98093042fb..903e1c6d00f 100644 --- a/src/plugins/texteditor/marginsettings.h +++ b/src/plugins/texteditor/marginsettings.h @@ -5,11 +5,7 @@ #include "texteditor_global.h" -#include <QVariantMap> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <utils/store.h> namespace TextEditor { @@ -18,11 +14,11 @@ class TEXTEDITOR_EXPORT MarginSettings public: MarginSettings(); - void toSettings(QSettings *s) const; - void fromSettings(QSettings *s); + void toSettings(Utils::QtcSettings *s) const; + void fromSettings(Utils::QtcSettings *s); - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const MarginSettings &other) const; diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp index fdde77e9dbd..935d215f321 100644 --- a/src/plugins/texteditor/markdowneditor.cpp +++ b/src/plugins/texteditor/markdowneditor.cpp @@ -8,11 +8,17 @@ #include "texteditortr.h" #include <aggregation/aggregate.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/actionmanager/commandbutton.h> #include <coreplugin/coreconstants.h> #include <coreplugin/coreplugintr.h> #include <coreplugin/icore.h> #include <coreplugin/minisplitter.h> + +#include <utils/qtcsettings.h> +#include <utils/qtcsettings.h> #include <utils/stringutils.h> +#include <utils/utilsicons.h> #include <QHBoxLayout> #include <QScrollBar> @@ -22,6 +28,9 @@ #include <optional> +using namespace Core; +using namespace Utils; + namespace TextEditor::Internal { const char MARKDOWNVIEWER_ID[] = "Editors.MarkdownViewer"; @@ -33,23 +42,31 @@ const char MARKDOWNVIEWER_SHOW_PREVIEW[] = "Markdown.ShowPreview"; const bool kTextEditorRightDefault = false; const bool kShowEditorDefault = true; const bool kShowPreviewDefault = true; +const char EMPHASIS_ACTION[] = "Markdown.Emphasis"; +const char STRONG_ACTION[] = "Markdown.Strong"; +const char INLINECODE_ACTION[] = "Markdown.InlineCode"; +const char LINK_ACTION[] = "Markdown.Link"; +const char TOGGLEEDITOR_ACTION[] = "Markdown.ToggleEditor"; +const char TOGGLEPREVIEW_ACTION[] = "Markdown.TogglePreview"; +const char SWAPVIEWS_ACTION[] = "Markdown.SwapViews"; -class MarkdownEditor : public Core::IEditor +class MarkdownEditor : public IEditor { + Q_OBJECT public: MarkdownEditor() : m_document(new TextDocument(MARKDOWNVIEWER_ID)) { m_document->setMimeType(MARKDOWNVIEWER_MIME_TYPE); - QSettings *s = Core::ICore::settings(); + QtcSettings *s = ICore::settings(); const bool textEditorRight = s->value(MARKDOWNVIEWER_TEXTEDITOR_RIGHT, kTextEditorRightDefault).toBool(); const bool showPreview = s->value(MARKDOWNVIEWER_SHOW_PREVIEW, kShowPreviewDefault).toBool(); const bool showEditor = s->value(MARKDOWNVIEWER_SHOW_EDITOR, kShowEditorDefault).toBool() || !showPreview; // ensure at least one is visible - m_splitter = new Core::MiniSplitter; + m_splitter = new MiniSplitter; // preview m_previewWidget = new QTextBrowser(); @@ -62,15 +79,15 @@ public: m_textEditorWidget->setTextDocument(m_document); m_textEditorWidget->setupGenericHighlighter(); m_textEditorWidget->setMarksVisible(false); - auto context = new Core::IContext(this); + auto context = new IContext(this); context->setWidget(m_textEditorWidget); - context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT)); - Core::ICore::addContextObject(context); + context->setContext(Context(MARKDOWNVIEWER_TEXT_CONTEXT)); + ICore::addContextObject(context); m_splitter->addWidget(m_textEditorWidget); // sets splitter->focusWidget() on non-Windows m_splitter->addWidget(m_previewWidget); - setContext(Core::Context(MARKDOWNVIEWER_ID)); + setContext(Context(MARKDOWNVIEWER_ID)); auto widget = new QWidget; auto layout = new QVBoxLayout; @@ -87,29 +104,55 @@ public: } agg->add(m_widget.get()); - m_togglePreviewVisible = new QToolButton; - m_togglePreviewVisible->setText(Tr::tr("Show Preview")); + m_togglePreviewVisible = new CommandButton(TOGGLEPREVIEW_ACTION); + m_togglePreviewVisible->setText(m_togglePreviewVisible->toolTipBase()); m_togglePreviewVisible->setCheckable(true); m_togglePreviewVisible->setChecked(showPreview); m_previewWidget->setVisible(showPreview); - m_toggleEditorVisible = new QToolButton; - m_toggleEditorVisible->setText(Tr::tr("Show Editor")); + m_toggleEditorVisible = new CommandButton(TOGGLEEDITOR_ACTION); + m_toggleEditorVisible->setText(m_toggleEditorVisible->toolTipBase()); m_toggleEditorVisible->setCheckable(true); m_toggleEditorVisible->setChecked(showEditor); m_textEditorWidget->setVisible(showEditor); - auto swapViews = new QToolButton; - swapViews->setText(Tr::tr("Swap Views")); - swapViews->setEnabled(showEditor && showPreview); + auto button = new CommandButton(EMPHASIS_ACTION); + button->setText("i"); + button->setFont([button]{ auto f = button->font(); f.setItalic(true); return f; }()); + connect(button, &QToolButton::clicked, this, &MarkdownEditor::triggerEmphasis); + m_markDownButtons.append(button); + button = new CommandButton(STRONG_ACTION); + button->setText("b"); + button->setFont([button]{ auto f = button->font(); f.setBold(true); return f; }()); + connect(button, &QToolButton::clicked, this, &MarkdownEditor::triggerStrong); + m_markDownButtons.append(button); + button = new CommandButton(INLINECODE_ACTION); + button->setText("`"); + connect(button, &QToolButton::clicked, this, &MarkdownEditor::triggerInlineCode); + m_markDownButtons.append(button); + button = new CommandButton(LINK_ACTION); + button->setIcon(Utils::Icons::LINK_TOOLBAR.icon()); + connect(button, &QToolButton::clicked, this, &MarkdownEditor::triggerLink); + m_markDownButtons.append(button); + for (auto button : m_markDownButtons) { + // do not call setVisible(true) at this point, this destroys the hover effect on macOS + if (!showEditor) + button->setVisible(false); + } + + m_swapViews = new CommandButton(SWAPVIEWS_ACTION); + m_swapViews->setText(m_swapViews->toolTipBase()); + m_swapViews->setEnabled(showEditor && showPreview); m_toolbarLayout = new QHBoxLayout(&m_toolbar); m_toolbarLayout->setSpacing(0); m_toolbarLayout->setContentsMargins(0, 0, 0, 0); + for (auto button : m_markDownButtons) + m_toolbarLayout->addWidget(button); m_toolbarLayout->addStretch(); m_toolbarLayout->addWidget(m_togglePreviewVisible); m_toolbarLayout->addWidget(m_toggleEditorVisible); - m_toolbarLayout->addWidget(swapViews); + m_toolbarLayout->addWidget(m_swapViews); setWidgetOrder(textEditorRight); @@ -133,7 +176,7 @@ public: }; const auto viewToggled = - [swapViews](QWidget *view, bool visible, QWidget *otherView, QToolButton *otherButton) { + [this](QWidget *view, bool visible, QWidget *otherView, QToolButton *otherButton) { if (view->isVisible() == visible) return; view->setVisible(visible); @@ -145,10 +188,10 @@ public: // make sure at least one view is visible otherButton->toggle(); } - swapViews->setEnabled(view->isVisible() && otherView->isVisible()); + m_swapViews->setEnabled(view->isVisible() && otherView->isVisible()); }; const auto saveViewSettings = [this] { - Utils::QtcSettings *s = Core::ICore::settings(); + Utils::QtcSettings *s = ICore::settings(); s->setValueWithDefault(MARKDOWNVIEWER_SHOW_PREVIEW, m_togglePreviewVisible->isChecked(), kShowPreviewDefault); @@ -165,6 +208,8 @@ public: visible, m_previewWidget, m_togglePreviewVisible); + for (auto button : m_markDownButtons) + button->setVisible(visible); saveViewSettings(); }); connect(m_togglePreviewVisible, @@ -179,11 +224,11 @@ public: saveViewSettings(); }); - connect(swapViews, &QToolButton::clicked, m_textEditorWidget, [this] { + connect(m_swapViews, &QToolButton::clicked, m_textEditorWidget, [this] { const bool textEditorRight = isTextEditorRight(); setWidgetOrder(!textEditorRight); // save settings - Utils::QtcSettings *s = Core::ICore::settings(); + Utils::QtcSettings *s = ICore::settings(); s->setValueWithDefault(MARKDOWNVIEWER_TEXTEDITOR_RIGHT, !textEditorRight, kTextEditorRightDefault); @@ -204,6 +249,57 @@ public: }); } + void triggerEmphasis() + { + triggerFormatingAction([](QString *selectedText, int *cursorOffset) { + if (selectedText->isEmpty()) { + *selectedText = QStringLiteral("**"); + *cursorOffset = -1; + } else { + *selectedText = QStringLiteral("*%1*").arg(*selectedText); + } + }); + } + void triggerStrong() + { + triggerFormatingAction([](QString *selectedText, int *cursorOffset) { + if (selectedText->isEmpty()) { + *selectedText = QStringLiteral("****"); + *cursorOffset = -2; + } else { + *selectedText = QStringLiteral("**%1**").arg(*selectedText); + } + }); + } + void triggerInlineCode() + { + triggerFormatingAction([](QString *selectedText, int *cursorOffset) { + if (selectedText->isEmpty()) { + *selectedText = QStringLiteral("``"); + *cursorOffset = -1; + } else { + *selectedText = QStringLiteral("`%1`").arg(*selectedText); + } + }); + } + void triggerLink() + { + triggerFormatingAction([](QString *selectedText, int *cursorOffset, int *selectionLength) { + if (selectedText->isEmpty()) { + *selectedText = QStringLiteral("[](https://)"); + *cursorOffset = -11; // ](https://) is 11 chars + } else { + *selectedText = QStringLiteral("[%1](https://)").arg(*selectedText); + *cursorOffset = -1; + *selectionLength = -8; // https:// is 8 chars + } + }); + } + + void toggleEditor() { m_toggleEditorVisible->toggle(); } + void togglePreview() { m_togglePreviewVisible->toggle(); } + void swapViews() { m_swapViews->click(); } + bool isTextEditorRight() const { return m_splitter->widget(0) == m_previewWidget; } void setWidgetOrder(bool textEditorRight) @@ -225,7 +321,7 @@ public: QWidget *toolBar() override { return &m_toolbar; } - Core::IDocument *document() const override { return m_document.data(); } + IDocument *document() const override { return m_document.data(); } TextEditorWidget *textEditorWidget() const { return m_textEditorWidget; } int currentLine() const override { return textEditorWidget()->textCursor().blockNumber() + 1; }; int currentColumn() const override @@ -251,7 +347,7 @@ public: m_splitter->widget(0)->setFocus(); return true; } - return Core::IEditor::eventFilter(obj, ev); + return IEditor::eventFilter(obj, ev); } QByteArray saveState() const override @@ -299,17 +395,57 @@ public: m_toggleEditorVisible->setChecked(textEditorShown || !previewShown); } +private: + void triggerFormatingAction(std::function<void(QString *selectedText, int *cursorOffset)> action) + { + auto formattedText = m_textEditorWidget->selectedText(); + int cursorOffset = 0; + action(&formattedText, &cursorOffset); + format(formattedText, cursorOffset); + } + void triggerFormatingAction(std::function<void(QString *selectedText, int *cursorOffset, int *selectionLength)> action) + { + auto formattedText = m_textEditorWidget->selectedText(); + int cursorOffset = 0; + int selectionLength = 0; + action(&formattedText, &cursorOffset, &selectionLength); + format(formattedText, cursorOffset, selectionLength); + } + void format(const QString &formattedText, int cursorOffset = 0, int selectionLength = 0) + { + auto cursor = m_textEditorWidget->textCursor(); + int start = cursor.selectionStart(); + int end = cursor.selectionEnd(); + cursor.setPosition(start, QTextCursor::MoveAnchor); + cursor.setPosition(end, QTextCursor::KeepAnchor); + + cursor.insertText(formattedText); + if (cursorOffset != 0) { + auto pos = cursor.position(); + cursor.setPosition(pos + cursorOffset); + m_textEditorWidget->setTextCursor(cursor); + } + + if (selectionLength != 0) { + cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor); + cursor.setPosition(cursor.position() + selectionLength, QTextCursor::KeepAnchor); + m_textEditorWidget->setTextCursor(cursor); + } + } + private: QTimer m_previewTimer; bool m_performDelayedUpdate = false; - Core::MiniSplitter *m_splitter; + MiniSplitter *m_splitter; QTextBrowser *m_previewWidget; TextEditorWidget *m_textEditorWidget; TextDocumentPtr m_document; QWidget m_toolbar; QHBoxLayout *m_toolbarLayout; - QToolButton *m_toggleEditorVisible; - QToolButton *m_togglePreviewVisible; + QList<QToolButton *> m_markDownButtons; + CommandButton *m_toggleEditorVisible; + CommandButton *m_togglePreviewVisible; + CommandButton *m_swapViews; std::optional<QPoint> m_previewRestoreScrollPosition; }; @@ -317,7 +453,7 @@ MarkdownEditorFactory::MarkdownEditorFactory() : m_actionHandler(MARKDOWNVIEWER_ID, MARKDOWNVIEWER_TEXT_CONTEXT, TextEditor::TextEditorActionHandler::None, - [](Core::IEditor *editor) { + [](IEditor *editor) { return static_cast<MarkdownEditor *>(editor)->textEditorWidget(); }) { @@ -325,6 +461,62 @@ MarkdownEditorFactory::MarkdownEditorFactory() setDisplayName(::Core::Tr::tr("Markdown Editor")); addMimeType(MARKDOWNVIEWER_MIME_TYPE); setEditorCreator([] { return new MarkdownEditor; }); + + const auto textContext = Context(MARKDOWNVIEWER_TEXT_CONTEXT); + const auto context = Context(MARKDOWNVIEWER_ID); + Command *cmd = nullptr; + cmd = ActionManager::registerAction(&m_emphasisAction, EMPHASIS_ACTION, textContext); + cmd->setDescription(Tr::tr("Emphasis")); + QObject::connect(&m_emphasisAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->triggerEmphasis(); + }); + cmd = ActionManager::registerAction(&m_strongAction, STRONG_ACTION, textContext); + cmd->setDescription(Tr::tr("Strong")); + QObject::connect(&m_strongAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->triggerStrong(); + }); + cmd = ActionManager::registerAction(&m_inlineCodeAction, INLINECODE_ACTION, textContext); + cmd->setDescription(Tr::tr("Inline Code")); + QObject::connect(&m_inlineCodeAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->triggerInlineCode(); + }); + cmd = ActionManager::registerAction(&m_linkAction, LINK_ACTION, textContext); + cmd->setDescription(Tr::tr("Hyperlink")); + QObject::connect(&m_linkAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->triggerLink(); + }); + + cmd = ActionManager::registerAction(&m_toggleEditorAction, TOGGLEEDITOR_ACTION, context); + cmd->setDescription(Tr::tr("Show Editor")); + QObject::connect(&m_toggleEditorAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->toggleEditor(); + }); + cmd = ActionManager::registerAction(&m_togglePreviewAction, TOGGLEPREVIEW_ACTION, context); + cmd->setDescription(Tr::tr("Show Preview")); + QObject::connect(&m_togglePreviewAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->togglePreview(); + }); + cmd = ActionManager::registerAction(&m_swapAction, SWAPVIEWS_ACTION, context); + cmd->setDescription(Tr::tr("Swap Views")); + QObject::connect(&m_swapAction, &QAction::triggered, EditorManager::instance(), [] { + auto editor = qobject_cast<MarkdownEditor *>(EditorManager::currentEditor()); + if (editor) + editor->swapViews(); + }); } } // namespace TextEditor::Internal + +#include "markdowneditor.moc" diff --git a/src/plugins/texteditor/markdowneditor.h b/src/plugins/texteditor/markdowneditor.h index de87c558e1b..92fc18a8b88 100644 --- a/src/plugins/texteditor/markdowneditor.h +++ b/src/plugins/texteditor/markdowneditor.h @@ -7,6 +7,8 @@ #include <texteditor/texteditoractionhandler.h> +#include <QAction> + namespace TextEditor::Internal { class MarkdownEditorFactory final : public Core::IEditorFactory @@ -16,6 +18,13 @@ public: private: TextEditor::TextEditorActionHandler m_actionHandler; + QAction m_emphasisAction; + QAction m_strongAction; + QAction m_inlineCodeAction; + QAction m_linkAction; + QAction m_toggleEditorAction; + QAction m_togglePreviewAction; + QAction m_swapAction; }; } // TextEditor::Internal diff --git a/src/plugins/texteditor/outlinefactory.cpp b/src/plugins/texteditor/outlinefactory.cpp index bc9aa03c8ba..2c462127026 100644 --- a/src/plugins/texteditor/outlinefactory.cpp +++ b/src/plugins/texteditor/outlinefactory.cpp @@ -4,6 +4,7 @@ #include "outlinefactory.h" #include "texteditortr.h" +#include "ioutlinewidget.h" #include <coreplugin/coreconstants.h> #include <coreplugin/icore.h> @@ -12,13 +13,17 @@ #include <utils/utilsicons.h> #include <utils/qtcassert.h> +#include <utils/store.h> #include <utils/stylehelper.h> #include <QDebug> #include <QLabel> +#include <QMenu> #include <QStackedWidget> #include <QToolButton> +using namespace Utils; + namespace TextEditor { static QList<IOutlineWidgetFactory *> g_outlineWidgetFactories; @@ -42,6 +47,37 @@ void IOutlineWidgetFactory::updateOutline() namespace Internal { +class OutlineWidgetStack : public QStackedWidget +{ + Q_OBJECT + +public: + OutlineWidgetStack(OutlineFactory *factory); + ~OutlineWidgetStack() override; + + QList<QToolButton *> toolButtons(); + + void saveSettings(Utils::QtcSettings *settings, int position); + void restoreSettings(Utils::QtcSettings *settings, int position); + +private: + bool isCursorSynchronized() const; + QWidget *dummyWidget() const; + void updateFilterMenu(); + void toggleCursorSynchronization(); + void toggleSort(); + void updateEditor(Core::IEditor *editor); + void updateCurrentEditor(); + + QToolButton *m_toggleSync; + QToolButton *m_filterButton; + QToolButton *m_toggleSort; + QMenu *m_filterMenu; + QVariantMap m_widgetSettings; + bool m_syncWithEditor; + bool m_sorted; +}; + OutlineWidgetStack::OutlineWidgetStack(OutlineFactory *factory) : m_syncWithEditor(true), m_sorted(false) @@ -98,32 +134,33 @@ QList<QToolButton *> OutlineWidgetStack::toolButtons() OutlineWidgetStack::~OutlineWidgetStack() = default; -void OutlineWidgetStack::saveSettings(QSettings *settings, int position) +void OutlineWidgetStack::saveSettings(QtcSettings *settings, int position) { - const QString baseKey = QStringLiteral("Outline.%1.").arg(position); - settings->setValue(baseKey + QLatin1String("SyncWithEditor"), m_toggleSync->isChecked()); + const Key baseKey = numberedKey("Outline.", position) + '.'; + settings->setValue(baseKey + "SyncWithEditor", m_toggleSync->isChecked()); for (auto iter = m_widgetSettings.constBegin(); iter != m_widgetSettings.constEnd(); ++iter) - settings->setValue(baseKey + iter.key(), iter.value()); + settings->setValue(baseKey + keyFromString(iter.key()), iter.value()); } -void OutlineWidgetStack::restoreSettings(QSettings *settings, int position) +void OutlineWidgetStack::restoreSettings(Utils::QtcSettings *settings, int position) { - const QString baseKey = QStringLiteral("Outline.%1.").arg(position); + const Key baseKey = numberedKey("Outline.", position) + '.'; + const QString baseKeyString = stringFromKey(baseKey); bool syncWithEditor = true; m_widgetSettings.clear(); const QStringList longKeys = settings->allKeys(); for (const QString &longKey : longKeys) { - if (!longKey.startsWith(baseKey)) + if (!longKey.startsWith(baseKeyString)) continue; - const QString key = longKey.mid(baseKey.length()); + const QString key = longKey.mid(baseKeyString.length()); if (key == QLatin1String("SyncWithEditor")) { - syncWithEditor = settings->value(longKey).toBool(); + syncWithEditor = settings->value(keyFromString(longKey)).toBool(); continue; } - m_widgetSettings.insert(key, settings->value(longKey)); + m_widgetSettings.insert(key, settings->value(keyFromString(longKey))); } m_toggleSync->setChecked(syncWithEditor); @@ -224,7 +261,7 @@ void OutlineFactory::saveSettings(Utils::QtcSettings *settings, int position, QW widgetStack->saveSettings(settings, position); } -void OutlineFactory::restoreSettings(QSettings *settings, int position, QWidget *widget) +void OutlineFactory::restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) { auto widgetStack = qobject_cast<OutlineWidgetStack *>(widget); Q_ASSERT(widgetStack); @@ -233,3 +270,5 @@ void OutlineFactory::restoreSettings(QSettings *settings, int position, QWidget } // namespace Internal } // namespace TextEditor + +#include "outlinefactory.moc" diff --git a/src/plugins/texteditor/outlinefactory.h b/src/plugins/texteditor/outlinefactory.h index 238c51c364c..eae3efb7fcc 100644 --- a/src/plugins/texteditor/outlinefactory.h +++ b/src/plugins/texteditor/outlinefactory.h @@ -3,62 +3,24 @@ #pragma once -#include <texteditor/ioutlinewidget.h> #include <coreplugin/inavigationwidgetfactory.h> -#include <QStackedWidget> -#include <QMenu> -namespace Core { class IEditor; } - -namespace TextEditor { -namespace Internal { - -class OutlineFactory; - -class OutlineWidgetStack : public QStackedWidget -{ - Q_OBJECT -public: - OutlineWidgetStack(OutlineFactory *factory); - ~OutlineWidgetStack() override; - - QList<QToolButton *> toolButtons(); - - void saveSettings(QSettings *settings, int position); - void restoreSettings(QSettings *settings, int position); - -private: - bool isCursorSynchronized() const; - QWidget *dummyWidget() const; - void updateFilterMenu(); - void toggleCursorSynchronization(); - void toggleSort(); - void updateEditor(Core::IEditor *editor); - void updateCurrentEditor(); - - QToolButton *m_toggleSync; - QToolButton *m_filterButton; - QToolButton *m_toggleSort; - QMenu *m_filterMenu; - QVariantMap m_widgetSettings; - bool m_syncWithEditor; - bool m_sorted; -}; +namespace TextEditor::Internal { class OutlineFactory : public Core::INavigationWidgetFactory { Q_OBJECT + public: OutlineFactory(); // from INavigationWidgetFactory Core::NavigationView createWidget() override; void saveSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; - void restoreSettings(QSettings *settings, int position, QWidget *widget) override; + void restoreSettings(Utils::QtcSettings *settings, int position, QWidget *widget) override; signals: void updateOutline(); }; -} // namespace Internal -} // namespace TextEditor +} // TextEditor::Internal diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp index a28f05e38e2..fd6b8d96b9b 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.cpp +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -48,8 +48,7 @@ PlainTextEditorFactory::PlainTextEditorFactory() setEditorActionHandlers(TextEditorActionHandler::Format | TextEditorActionHandler::UnCommentSelection | - TextEditorActionHandler::UnCollapseAll | - TextEditorActionHandler::FollowSymbolUnderCursor); + TextEditorActionHandler::UnCollapseAll); } PlainTextEditorFactory *PlainTextEditorFactory::instance() diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index ba55628423d..1398d55bcaa 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -113,10 +113,7 @@ TextEditorWidget *RefactoringChanges::openEditor(const FilePath &filePath, } IEditor *editor = EditorManager::openEditorAt(Link{filePath, line, column}, Id(), flags); - if (editor) - return TextEditorWidget::fromEditor(editor); - else - return nullptr; + return TextEditorWidget::fromEditor(editor); } RefactoringFilePtr RefactoringChanges::file(TextEditorWidget *editor) @@ -340,12 +337,15 @@ bool RefactoringFile::apply() RefactoringChanges::rangesToSelections(doc, m_reindentRanges); m_reindentRanges.clear(); - // apply changes and reindent + // apply changes + setupFormattingRanges(m_changes.operationList()); m_changes.apply(&c); m_changes.clear(); + // Do indentation and formatting. indentOrReindent(indentSelections, Indent); indentOrReindent(reindentSelections, Reindent); + doFormatting(); c.endEditBlock(); @@ -391,6 +391,104 @@ void RefactoringFile::indentOrReindent(const RefactoringSelections &ranges, } } +void RefactoringFile::setupFormattingRanges(const QList<ChangeSet::EditOp> &replaceList) +{ + if (!m_editor || !m_formattingEnabled) + return; + + for (const ChangeSet::EditOp &op : replaceList) { + QTextCursor cursor = m_editor->textCursor(); + switch (op.type) { + case ChangeSet::EditOp::Unset: + break; + case ChangeSet::EditOp::Replace: + case ChangeSet::EditOp::Insert: + case ChangeSet::EditOp::Remove: + cursor.setKeepPositionOnInsert(true); + cursor.setPosition(op.pos1 + op.length1); + cursor.setPosition(op.pos1, QTextCursor::KeepAnchor); + m_formattingCursors << cursor; + break; + case ChangeSet::EditOp::Flip: + case ChangeSet::EditOp::Move: + cursor.setKeepPositionOnInsert(true); + cursor.setPosition(op.pos1 + op.length1); + cursor.setPosition(op.pos1, QTextCursor::KeepAnchor); + m_formattingCursors << cursor; + cursor.setPosition(op.pos2 + op.length2); + cursor.setPosition(op.pos2, QTextCursor::KeepAnchor); + m_formattingCursors << cursor; + break; + case ChangeSet::EditOp::Copy: + cursor.setKeepPositionOnInsert(true); + cursor.setPosition(op.pos2, QTextCursor::KeepAnchor); + m_formattingCursors << cursor; + break; + } + } +} + +void RefactoringFile::doFormatting() +{ + if (m_formattingCursors.empty() || !m_editor) + return; + + RangesInLines formattingRanges; + + QTextCursor cursor = m_editor->textCursor(); + auto lineForPosition = [&](int pos) { + cursor.setPosition(pos); + return cursor.blockNumber() + 1; + }; + QList<int> affectedLines; + for (const QTextCursor &formattingCursor : std::as_const(m_formattingCursors)) { + int startLine = lineForPosition(formattingCursor.selectionStart()); + int endLine = lineForPosition(formattingCursor.selectionEnd()); + for (int line = startLine; line <= endLine; ++line) { + const auto it = std::lower_bound(affectedLines.begin(), affectedLines.end(), line); + if (it == affectedLines.end() || *it > line) + affectedLines.insert(it, line); + } + } + + for (int line : std::as_const(affectedLines)) { + if (!formattingRanges.empty() && formattingRanges.back().endLine == line - 1) + formattingRanges.back().endLine = line; + else + formattingRanges.push_back({line, line}); + } + + static const QString clangFormatLineRemovalBlocker("// QTC_TEMP"); + for (const RangeInLines &r : std::as_const(formattingRanges)) { + QTextBlock b = m_editor->document()->findBlockByNumber(r.startLine - 1); + while (true) { + QTC_ASSERT(b.isValid(), break); + if (b.text().simplified().isEmpty()) + QTextCursor(b).insertText(clangFormatLineRemovalBlocker); + if (b.blockNumber() == r.endLine - 1) + break; + b = b.next(); + } + } + + // TODO: The proper solution seems to be to call formatOrIndent() here (and not + // use hardcoded indent regions anymore), but that would require intrusive changes + // to the C++ quickfixes and tests, where we rely on the built-in indenter behavior. + m_editor->textDocument()->indenter()->format(formattingRanges, + Indenter::FormattingMode::Settings); + + for (QTextBlock b = m_editor->document()->findBlockByNumber( + formattingRanges.front().startLine - 1); b.isValid(); b = b.next()) { + QString blockText = b.text(); + if (blockText.remove(clangFormatLineRemovalBlocker) == b.text()) + continue; + QTextCursor c(b); + c.select(QTextCursor::LineUnderCursor); + c.removeSelectedText(); + c.insertText(blockText); + } +} + void RefactoringFile::fileChanged() { if (!m_filePath.isEmpty()) diff --git a/src/plugins/texteditor/refactoringchanges.h b/src/plugins/texteditor/refactoringchanges.h index bbe09d72754..f657095287a 100644 --- a/src/plugins/texteditor/refactoringchanges.h +++ b/src/plugins/texteditor/refactoringchanges.h @@ -3,6 +3,8 @@ #pragma once +#include "indenter.h" + #include <texteditor/texteditor_global.h> #include <utils/changeset.h> #include <utils/fileutils.h> @@ -75,6 +77,9 @@ protected: enum IndentType {Indent, Reindent}; void indentOrReindent(const RefactoringSelections &ranges, IndentType indent); + void setupFormattingRanges(const QList<Utils::ChangeSet::EditOp> &replaceList); + void doFormatting(); + Utils::FilePath m_filePath; QSharedPointer<RefactoringChangesData> m_data; mutable Utils::TextFileFormat m_textFileFormat; @@ -83,10 +88,12 @@ protected: Utils::ChangeSet m_changes; QList<Range> m_indentRanges; QList<Range> m_reindentRanges; + QList<QTextCursor> m_formattingCursors; bool m_openEditor = false; bool m_activateEditor = false; int m_editorCursorPosition = -1; bool m_appliedOnce = false; + bool m_formattingEnabled = false; friend class RefactoringChanges; // access to constructor }; diff --git a/src/plugins/texteditor/snippets/snippetssettings.cpp b/src/plugins/texteditor/snippets/snippetssettings.cpp index c041343dad4..cce25bc7109 100644 --- a/src/plugins/texteditor/snippets/snippetssettings.cpp +++ b/src/plugins/texteditor/snippets/snippetssettings.cpp @@ -2,31 +2,27 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "snippetssettings.h" -#include "reuse.h" -#include <QSettings> +#include <utils/qtcsettings.h> -namespace { +using namespace Utils; -static const QLatin1String kGroupPostfix("SnippetsSettings"); -static const QLatin1String kLastUsedSnippetGroup("LastUsedSnippetGroup"); +namespace TextEditor { -} // Anonymous +const char kGroupPostfix[] = "SnippetsSettings"; +const char kLastUsedSnippetGroup[] = "LastUsedSnippetGroup"; -using namespace TextEditor; -using namespace Internal; - -void SnippetsSettings::toSettings(const QString &category, QSettings *s) const +void SnippetsSettings::toSettings(const Key &category, QtcSettings *s) const { - const QString &group = category + kGroupPostfix; + const Key group = category + kGroupPostfix; s->beginGroup(group); s->setValue(kLastUsedSnippetGroup, m_lastUsedSnippetGroup); s->endGroup(); } -void SnippetsSettings::fromSettings(const QString &category, QSettings *s) +void SnippetsSettings::fromSettings(const Key &category, QtcSettings *s) { - const QString &group = category + kGroupPostfix; + const Key group = category + kGroupPostfix; s->beginGroup(group); m_lastUsedSnippetGroup = s->value(kLastUsedSnippetGroup, QString()).toString(); s->endGroup(); @@ -46,3 +42,5 @@ bool SnippetsSettings::equals(const SnippetsSettings &snippetsSettings) const { return m_lastUsedSnippetGroup == snippetsSettings.m_lastUsedSnippetGroup; } + +} // TextEditor diff --git a/src/plugins/texteditor/snippets/snippetssettings.h b/src/plugins/texteditor/snippets/snippetssettings.h index af74fd960a2..732d51f6716 100644 --- a/src/plugins/texteditor/snippets/snippetssettings.h +++ b/src/plugins/texteditor/snippets/snippetssettings.h @@ -5,9 +5,10 @@ #include <QString> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +namespace Utils { +class Key; +class QtcSettings; +} // Utils namespace TextEditor { @@ -16,8 +17,8 @@ class SnippetsSettings public: SnippetsSettings() = default; - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); + void toSettings(const Utils::Key &category, Utils::QtcSettings *s) const; + void fromSettings(const Utils::Key &category, Utils::QtcSettings *s); void setLastUsedSnippetGroup(const QString &lastUsed); const QString &lastUsedSnippetGroup() const; diff --git a/src/plugins/texteditor/snippets/snippetssettingspage.cpp b/src/plugins/texteditor/snippets/snippetssettingspage.cpp index e4bfdf7df84..51deff5ad1f 100644 --- a/src/plugins/texteditor/snippets/snippetssettingspage.cpp +++ b/src/plugins/texteditor/snippets/snippetssettingspage.cpp @@ -35,6 +35,8 @@ #include <QStackedWidget> #include <QTextStream> +using namespace Utils; + namespace TextEditor::Internal { // SnippetsTableModel @@ -273,7 +275,7 @@ private: bool settingsChanged() const; void writeSettings(); - const QString m_settingsPrefix{QLatin1String("Text")}; + const Key m_settingsPrefix{"Text"}; SnippetsTableModel m_model; bool m_snippetsCollectionChanged = false; SnippetsSettings m_settings; diff --git a/src/plugins/texteditor/storagesettings.cpp b/src/plugins/texteditor/storagesettings.cpp index 1e26119189c..b84011855c8 100644 --- a/src/plugins/texteditor/storagesettings.cpp +++ b/src/plugins/texteditor/storagesettings.cpp @@ -3,12 +3,13 @@ #include "storagesettings.h" +#include <coreplugin/icore.h> + #include <utils/hostosinfo.h> -#include <utils/settingsutils.h> #include <QRegularExpression> -#include <QSettings> -#include <QString> + +using namespace Utils; namespace TextEditor { @@ -18,7 +19,6 @@ static const char addFinalNewLineKey[] = "addFinalNewLine"; static const char cleanIndentationKey[] = "cleanIndentation"; static const char skipTrailingWhitespaceKey[] = "skipTrailingWhitespace"; static const char ignoreFileTypesKey[] = "ignoreFileTypes"; -static const char groupPostfix[] = "StorageSettings"; static const char defaultTrailingWhitespaceBlacklist[] = "*.md, *.MD, Makefile"; StorageSettings::StorageSettings() @@ -31,18 +31,7 @@ StorageSettings::StorageSettings() { } -void StorageSettings::toSettings(const QString &category, QSettings *s) const -{ - Utils::toSettings(QLatin1String(groupPostfix), category, s, this); -} - -void StorageSettings::fromSettings(const QString &category, QSettings *s) -{ - *this = StorageSettings(); - Utils::fromSettings(QLatin1String(groupPostfix), category, s, this); -} - -QVariantMap StorageSettings::toMap() const +Store StorageSettings::toMap() const { return { {cleanWhitespaceKey, m_cleanWhitespace}, @@ -54,7 +43,7 @@ QVariantMap StorageSettings::toMap() const }; } -void StorageSettings::fromMap(const QVariantMap &map) +void StorageSettings::fromMap(const Store &map) { m_cleanWhitespace = map.value(cleanWhitespaceKey, m_cleanWhitespace).toBool(); m_inEntireDocument = map.value(inEntireDocumentKey, m_inEntireDocument).toBool(); diff --git a/src/plugins/texteditor/storagesettings.h b/src/plugins/texteditor/storagesettings.h index 53040f5c7d8..757ad246a25 100644 --- a/src/plugins/texteditor/storagesettings.h +++ b/src/plugins/texteditor/storagesettings.h @@ -5,11 +5,7 @@ #include "texteditor_global.h" -#include <QVariantMap> - -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <utils/store.h> namespace TextEditor { @@ -18,11 +14,8 @@ class TEXTEDITOR_EXPORT StorageSettings public: StorageSettings(); - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); - - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); // calculated based on boolean setting plus file type blacklist examination bool removeTrailingWhitespace(const QString &filePattern) const; diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp index d8b39a5c2c2..24a96522b38 100644 --- a/src/plugins/texteditor/syntaxhighlighter.cpp +++ b/src/plugins/texteditor/syntaxhighlighter.cpp @@ -512,7 +512,6 @@ void SyntaxHighlighter::formatSpaces(const QString &text, int start, int count) void SyntaxHighlighter::setFormatWithSpaces(const QString &text, int start, int count, const QTextCharFormat &format) { - Q_D(const SyntaxHighlighter); const QTextCharFormat visualSpaceFormat = whitespacified(format); const int end = std::min(start + count, int(text.length())); diff --git a/src/plugins/texteditor/tabsettings.cpp b/src/plugins/texteditor/tabsettings.cpp index 07660a55475..e0c5142dcce 100644 --- a/src/plugins/texteditor/tabsettings.cpp +++ b/src/plugins/texteditor/tabsettings.cpp @@ -2,13 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "tabsettings.h" -#include "texteditorplugin.h" - -#include <utils/settingsutils.h> #include <QDebug> -#include <QSettings> -#include <QString> #include <QTextCursor> #include <QTextDocument> @@ -16,9 +11,10 @@ static const char spacesForTabsKey[] = "SpacesForTabs"; static const char autoSpacesForTabsKey[] = "AutoSpacesForTabs"; static const char tabSizeKey[] = "TabSize"; static const char indentSizeKey[] = "IndentSize"; -static const char groupPostfix[] = "TabSettings"; static const char paddingModeKey[] = "PaddingMode"; +using namespace Utils; + namespace TextEditor { TabSettings::TabSettings(TabSettings::TabPolicy tabPolicy, @@ -30,21 +26,9 @@ TabSettings::TabSettings(TabSettings::TabPolicy tabPolicy, , m_indentSize(indentSize) , m_continuationAlignBehavior(continuationAlignBehavior) { - } -void TabSettings::toSettings(const QString &category, QSettings *s) const -{ - Utils::toSettings(QLatin1String(groupPostfix), category, s, this); -} - -void TabSettings::fromSettings(const QString &category, QSettings *s) -{ - *this = TabSettings(); // Assign defaults - Utils::fromSettings(QLatin1String(groupPostfix), category, s, this); -} - -QVariantMap TabSettings::toMap() const +Store TabSettings::toMap() const { return { {spacesForTabsKey, m_tabPolicy != TabsOnlyTabPolicy}, @@ -55,7 +39,7 @@ QVariantMap TabSettings::toMap() const }; } -void TabSettings::fromMap(const QVariantMap &map) +void TabSettings::fromMap(const Store &map) { const bool spacesForTabs = map.value(spacesForTabsKey, true).toBool(); const bool autoSpacesForTabs = map.value(autoSpacesForTabsKey, false).toBool(); diff --git a/src/plugins/texteditor/tabsettings.h b/src/plugins/texteditor/tabsettings.h index e21ab9678b9..9a446738d0c 100644 --- a/src/plugins/texteditor/tabsettings.h +++ b/src/plugins/texteditor/tabsettings.h @@ -5,11 +5,10 @@ #include "texteditor_global.h" -#include <QTextBlock> +#include <utils/store.h> +#include <utils/qtcsettings.h> -QT_BEGIN_NAMESPACE -class QSettings; -QT_END_NAMESPACE +#include <QTextBlock> namespace TextEditor { @@ -36,11 +35,8 @@ public: TabSettings(TabPolicy tabPolicy, int tabSize, int indentSize, ContinuationAlignBehavior continuationAlignBehavior); - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); - - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); int lineIndentPosition(const QString &text) const; int columnAt(const QString &text, int position) const; diff --git a/src/plugins/texteditor/textdocument.cpp b/src/plugins/texteditor/textdocument.cpp index 98a3eda4a1e..ddc25d2a276 100644 --- a/src/plugins/texteditor/textdocument.cpp +++ b/src/plugins/texteditor/textdocument.cpp @@ -662,8 +662,6 @@ bool TextDocument::saveImpl(QString *errorString, const FilePath &filePath, bool cursor.endEditBlock(); } - const Utils::FilePath &savePath = filePath.isEmpty() ? this->filePath() : filePath; - // check if UTF8-BOM has to be added or removed Utils::TextFileFormat saveFormat = format(); if (saveFormat.codec->name() == "UTF-8" && supportsUtf8Bom()) { @@ -679,7 +677,7 @@ bool TextDocument::saveImpl(QString *errorString, const FilePath &filePath, bool } } - const bool ok = write(savePath, saveFormat, plainText(), errorString); + const bool ok = write(filePath, saveFormat, plainText(), errorString); // restore text cursor and scroll bar positions if (autoSave && undos < d->m_document.availableUndoSteps()) { @@ -702,7 +700,7 @@ bool TextDocument::saveImpl(QString *errorString, const FilePath &filePath, bool // inform about the new filename d->m_document.setModified(false); // also triggers update of the block revisions - setFilePath(savePath.absoluteFilePath()); + setFilePath(filePath.absoluteFilePath()); emit changed(); return true; } @@ -717,6 +715,12 @@ bool TextDocument::setContents(const QByteArray &contents) return setPlainText(QString::fromUtf8(contents)); } +void TextDocument::formatContents() +{ + d->m_indenter->format({{document()->firstBlock().blockNumber() + 1, + document()->lastBlock().blockNumber() + 1}}); +} + bool TextDocument::shouldAutoSave() const { return d->m_autoSaveRevision != d->m_document.revision(); diff --git a/src/plugins/texteditor/textdocument.h b/src/plugins/texteditor/textdocument.h index 6527aefd5f4..a4dd3dd45f0 100644 --- a/src/plugins/texteditor/textdocument.h +++ b/src/plugins/texteditor/textdocument.h @@ -103,6 +103,7 @@ public: // IDocument implementation. QByteArray contents() const override; bool setContents(const QByteArray &contents) override; + void formatContents() override; bool shouldAutoSave() const override; bool isModified() const override; bool isSaveAsAllowed() const override; diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp index d16befe9ab5..6efa145aace 100644 --- a/src/plugins/texteditor/textdocumentlayout.cpp +++ b/src/plugins/texteditor/textdocumentlayout.cpp @@ -858,7 +858,7 @@ bool Parenthesis::operator==(const Parenthesis &other) const void insertSorted(Parentheses &list, const Parenthesis &elem) { - const auto it = std::lower_bound(list.begin(), list.end(), elem, + const auto it = std::lower_bound(list.constBegin(), list.constEnd(), elem, [](const auto &p1, const auto &p2) { return p1.pos < p2.pos; }); list.insert(it, elem); } diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index e01938f98ac..4905d745f9a 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -48,12 +48,12 @@ #include <coreplugin/manhattanstyle.h> #include <utils/algorithm.h> +#include <utils/async.h> #include <utils/camelcasecursor.h> #include <utils/dropsupport.h> #include <utils/fadingindicator.h> #include <utils/filesearch.h> #include <utils/fileutils.h> -#include <utils/fixedsizeclicklabel.h> #include <utils/hostosinfo.h> #include <utils/infobar.h> #include <utils/mimeutils.h> @@ -106,6 +106,7 @@ #include <QTimeLine> #include <QTimer> #include <QToolBar> +#include <QToolButton> /*! \namespace TextEditor @@ -148,17 +149,16 @@ using ListTransformationMethod = void(QStringList &); static constexpr char dropProperty[] = "dropProp"; -class LineColumnLabel : public FixedSizeClickLabel +class LineColumnButton : public QToolButton { Q_OBJECT public: - LineColumnLabel(TextEditorWidget *parent) - : FixedSizeClickLabel(parent) + LineColumnButton(TextEditorWidget *parent) + : QToolButton(parent) , m_editor(parent) { - setMaxText(Tr::tr("Line: 9999, Col: 999")); - connect(m_editor, &QPlainTextEdit::cursorPositionChanged, this, &LineColumnLabel::update); - connect(this, &FixedSizeClickLabel::clicked, ActionManager::instance(), [this] { + connect(m_editor, &QPlainTextEdit::cursorPositionChanged, this, &LineColumnButton::update); + connect(this, &QToolButton::pressed, ActionManager::instance(), [this] { emit m_editor->activateEditor(EditorManager::IgnoreNavigationHistory); QMetaObject::invokeMethod(ActionManager::instance(), [] { if (Command *cmd = ActionManager::command(Core::Constants::GOTO)) { @@ -172,20 +172,89 @@ public: private: void update() { - const QTextCursor cursor = m_editor->textCursor(); - const QTextBlock block = cursor.block(); - const int line = block.blockNumber() + 1; - const TabSettings &tabSettings = m_editor->textDocument()->tabSettings(); - const int column = tabSettings.columnAt(block.text(), cursor.positionInBlock()) + 1; - const QString text = Tr::tr("Line: %1, Col: %2"); - setText(text.arg(line).arg(column)); - const QString toolTipText = Tr::tr("Cursor position: %1"); - setToolTip(toolTipText.arg(QString::number(cursor.position()))); - QFont f = font(); - f.setItalic(m_editor->multiTextCursor().hasMultipleCursors()); - setFont(f); + const Utils::MultiTextCursor &cursors = m_editor->multiTextCursor(); + QString text; + if (cursors.hasMultipleCursors()) { + text = Tr::tr("Cursors: %2").arg(cursors.cursorCount()); + } else { + const QTextCursor cursor = cursors.mainCursor(); + const QTextBlock block = cursor.block(); + const int line = block.blockNumber() + 1; + const TabSettings &tabSettings = m_editor->textDocument()->tabSettings(); + const int column = tabSettings.columnAt(block.text(), cursor.positionInBlock()) + 1; + text = Tr::tr("Line: %1, Col: %2").arg(line).arg(column); + const QString toolTipText = Tr::tr("Cursor position: %1"); + setToolTip(toolTipText.arg(cursor.position())); + } + int selection = 0; + for (const QTextCursor &cursor : cursors) + selection += cursor.selectionEnd() - cursor.selectionStart(); + if (selection > 0) + text += " " + Tr::tr("(Sel: %1)").arg(selection); + setText(text); } + bool event(QEvent *event) override + { + if (event->type() != QEvent::ToolTip) + return QToolButton::event(event); + + QString tooltipText = "<table cellpadding='2'>\n"; + + const MultiTextCursor multiCursor = m_editor->multiTextCursor(); + const QList<QTextCursor> cursors = multiCursor.cursors().mid(0, 15); + + tooltipText += "<tr>"; + tooltipText += QString("<th align='left'>%1</th>").arg(Tr::tr("Cursors:")); + tooltipText += QString("<td>%1</td>").arg(multiCursor.cursorCount()); + tooltipText += "</tr>\n"; + + auto addRow = [&](const QString header, auto cellText) { + tooltipText += "<tr>"; + tooltipText += QString("<th align='left'>%1</th>").arg(header); + for (const QTextCursor &c : cursors) + tooltipText += QString("<td>%1</td>").arg(cellText(c)); + if (multiCursor.cursorCount() > cursors.count()) + tooltipText += QString("<td>...</td>"); + tooltipText += "</tr>\n"; + }; + + addRow(Tr::tr("Line:"), [](const QTextCursor &c) { return c.blockNumber() + 1; }); + + const TabSettings &tabSettings = m_editor->textDocument()->tabSettings(); + addRow(Tr::tr("Column:"), [&](const QTextCursor &c) { + return tabSettings.columnAt(c.block().text(), c.positionInBlock()) + 1; + }); + + addRow(Tr::tr("Selection length:"), + [](const QTextCursor &c) { return c.selectionEnd() - c.selectionStart(); }); + + addRow(Tr::tr("Position in document:"), [](const QTextCursor &c) { return c.position(); }); + + addRow(Tr::tr("Anchor:"), [](const QTextCursor &c) { return c.anchor(); }); + + tooltipText += "</table>\n"; + + ToolTip::show(static_cast<const QHelpEvent *>(event)->globalPos(), + tooltipText, + Qt::RichText); + event->accept(); + return true; + } + + QSize sizeHint() const override + { + const QSize size = QToolButton::sizeHint(); + auto wider = [](const QSize &left, const QSize &right) { + return left.width() < right.width(); + }; + if (m_editor->multiTextCursor().hasSelection()) + return std::max(m_maxSize, size, wider); // do not save the size if we have a selection + m_maxSize = std::max(m_maxSize, size, wider); + return m_maxSize; + } + + mutable QSize m_maxSize; TextEditorWidget *m_editor; }; @@ -448,6 +517,7 @@ struct PaintEventData , textCursorBlock(textCursor.block()) , isEditable(!editor->isReadOnly()) , fontSettings(editor->textDocument()->fontSettings()) + , lineSpacing(fontSettings.lineSpacing()) , searchScopeFormat(fontSettings.toTextCharFormat(C_SEARCH_SCOPE)) , searchResultFormat(fontSettings.toTextCharFormat(C_SEARCH_RESULT)) , visualWhitespaceFormat(fontSettings.toTextCharFormat(C_VISUAL_WHITESPACE)) @@ -468,6 +538,7 @@ struct PaintEventData const QTextBlock textCursorBlock; const bool isEditable; const FontSettings fontSettings; + const int lineSpacing; const QTextCharFormat searchScopeFormat; const QTextCharFormat searchResultFormat; const QTextCharFormat visualWhitespaceFormat; @@ -560,6 +631,7 @@ public: QPainter &painter, const PaintEventBlockData &blockData) const; QTextBlock nextVisibleBlock(const QTextBlock &block) const; + void scheduleCleanupAnnotationCache(); void cleanupAnnotationCache(); // extra area paint methods @@ -609,8 +681,6 @@ public: void documentAboutToBeReloaded(); void documentReloadFinished(bool success); void highlightSearchResultsSlot(const QString &txt, FindFlags findFlags); - void searchResultsReady(int beginIndex, int endIndex); - void searchFinished(); void setupScrollBar(); void highlightSearchResultsInScrollBar(); void scheduleUpdateHighlightScrollBar(); @@ -645,6 +715,7 @@ public: KSyntaxHighlighting::Definition currentDefinition(); void rememberCurrentSyntaxDefinition(); void openLinkUnderCursor(bool openInNextSplit); + void openTypeUnderCursor(bool openInNextSplit); qreal charWidth() const; public: @@ -654,12 +725,12 @@ public: QWidget *m_stretchWidget = nullptr; QAction *m_stretchAction = nullptr; QAction *m_toolbarOutlineAction = nullptr; - LineColumnLabel *m_cursorPositionLabel = nullptr; - FixedSizeClickLabel *m_fileEncodingLabel = nullptr; + LineColumnButton *m_cursorPositionButton = nullptr; + QToolButton *m_fileEncodingButton = nullptr; QAction *m_fileEncodingLabelAction = nullptr; BaseTextFind *m_find = nullptr; - QComboBox *m_fileLineEnding = nullptr; + QToolButton *m_fileLineEnding = nullptr; QAction *m_fileLineEndingAction = nullptr; uint m_optionalActionMask = TextEditorActionHandler::None; @@ -705,6 +776,7 @@ public: friend bool operator==(const AnnotationRect &a, const AnnotationRect &b) { return a.mark == b.mark && a.rect == b.rect; } }; + bool cleanupAnnotationRectsScheduled = false; QMap<int, QList<AnnotationRect>> m_annotationRects; QRectF getLastLineLineRect(const QTextBlock &block); @@ -798,7 +870,7 @@ public: QScopedPointer<AutoCompleter> m_autoCompleter; CommentDefinition m_commentDefinition; - QFutureWatcher<SearchResultItems> *m_searchWatcher = nullptr; + QFuture<SearchResultItems> m_searchFuture; QVector<SearchResult> m_searchResults; QTimer m_scrollBarUpdateTimer; HighlightScrollBarController *m_highlightScrollBarController = nullptr; @@ -902,19 +974,9 @@ void TextEditorWidgetFind::selectAll(const QString &txt, FindFlags findFlags) m_editor->setFocus(); }); - const FilePath &fileName = m_editor->textDocument()->filePath(); - QMap<FilePath, QString> fileToContentsMap; - fileToContentsMap[fileName] = m_editor->textDocument()->plainText(); - - FileListIterator *it = new FileListIterator({fileName}, - {const_cast<QTextCodec *>( - m_editor->textDocument()->codec())}); - const QTextDocument::FindFlags findFlags2 = textDocumentFlagsForFindFlags(findFlags); - - if (findFlags & FindRegularExpression) - m_selectWatcher->setFuture(findInFilesRegExp(txt, it, findFlags2, fileToContentsMap)); - else - m_selectWatcher->setFuture(findInFiles(txt, it, findFlags2, fileToContentsMap)); + m_selectWatcher->setFuture(Utils::asyncRun(Utils::searchInContents, txt, findFlags, + m_editor->textDocument()->filePath(), + m_editor->textDocument()->plainText())); } void TextEditorWidgetFind::cancelCurrentSelectAll() @@ -972,22 +1034,21 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) m_stretchAction = m_toolBar->addWidget(m_stretchWidget); m_toolBarWidget->layout()->addWidget(m_toolBar); - m_cursorPositionLabel = new LineColumnLabel(q); const int spacing = q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2; - m_cursorPositionLabel->setContentsMargins(spacing, 0, spacing, 0); - m_toolBarWidget->layout()->addWidget(m_cursorPositionLabel); + m_cursorPositionButton = new LineColumnButton(q); + m_cursorPositionButton->setContentsMargins(spacing, 0, spacing, 0); + m_toolBarWidget->layout()->addWidget(m_cursorPositionButton); - m_fileLineEnding = new QComboBox(); - m_fileLineEnding->addItems(ExtraEncodingSettings::lineTerminationModeNames()); + m_fileLineEnding = new QToolButton(q); m_fileLineEnding->setContentsMargins(spacing, 0, spacing, 0); m_fileLineEndingAction = m_toolBar->addWidget(m_fileLineEnding); updateFileLineEndingVisible(); connect(q, &TextEditorWidget::readOnlyChanged, this, &TextEditorWidgetPrivate::updateFileLineEndingVisible); - m_fileEncodingLabel = new FixedSizeClickLabel; - m_fileEncodingLabel->setContentsMargins(spacing, 0, spacing, 0); - m_fileEncodingLabelAction = m_toolBar->addWidget(m_fileEncodingLabel); + m_fileEncodingButton = new QToolButton; + m_fileEncodingButton->setContentsMargins(spacing, 0, spacing, 0); + m_fileEncodingLabelAction = m_toolBar->addWidget(m_fileEncodingButton); m_extraSelections.reserve(NExtraSelectionKinds); @@ -1028,11 +1089,17 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent) connect(&m_delayedUpdateTimer, &QTimer::timeout, q->viewport(), QOverload<>::of(&QWidget::update)); - connect(m_fileEncodingLabel, &FixedSizeClickLabel::clicked, + connect(m_fileEncodingButton, &QToolButton::clicked, q, &TextEditorWidget::selectEncoding); - connect(m_fileLineEnding, &QComboBox::currentIndexChanged, - q, &TextEditorWidget::selectLineEnding); + connect(m_fileLineEnding, &QToolButton::clicked, ActionManager::instance(), [this] { + QMenu *menu = new QMenu; + menu->addAction(Tr::tr("Unix Line Endings (LF)"), + [this] { q->selectLineEnding(TextFileFormat::LFLineTerminator); }); + menu->addAction(Tr::tr("Windows Line Endings (CRLF)"), + [this] { q->selectLineEnding(TextFileFormat::CRLFLineTerminator); }); + menu->popup(QCursor::pos()); + }); TextEditorSettings *settings = TextEditorSettings::instance(); @@ -1068,6 +1135,8 @@ TextEditorWidgetPrivate::~TextEditorWidgetPrivate() q->disconnect(this); delete m_toolBarWidget; delete m_highlightScrollBarController; + if (m_searchFuture.isRunning()) + m_searchFuture.cancel(); } static QFrame *createSeparator(const QString &styleSheet) @@ -1685,6 +1754,10 @@ void TextEditorWidgetPrivate::insertSuggestion(std::unique_ptr<TextSuggestion> & auto cursor = q->textCursor(); cursor.setPosition(suggestion->position()); + QTextOption option = suggestion->document()->defaultTextOption(); + option.setTabStopDistance(charWidth() * m_document->tabSettings().m_tabSize); + suggestion->document()->setDefaultTextOption(option); + auto options = suggestion->document()->defaultTextOption(); m_suggestionBlock = cursor.block(); m_document->insertSuggestion(std::move(suggestion)); } @@ -1736,25 +1809,30 @@ void TextEditorWidget::selectEncoding() } } -void TextEditorWidget::selectLineEnding(int index) +void TextEditorWidget::selectLineEnding(TextFileFormat::LineTerminationMode lineEnding) { - QTC_CHECK(index >= 0); - const auto newMode = Utils::TextFileFormat::LineTerminationMode(index); - if (d->m_document->lineTerminationMode() != newMode) { - d->m_document->setLineTerminationMode(newMode); + if (d->m_document->lineTerminationMode() != lineEnding) { + d->m_document->setLineTerminationMode(lineEnding); d->q->document()->setModified(true); + updateTextLineEndingLabel(); } } void TextEditorWidget::updateTextLineEndingLabel() { - d->m_fileLineEnding->setCurrentIndex(d->m_document->lineTerminationMode()); + const TextFileFormat::LineTerminationMode lineEnding = d->m_document->lineTerminationMode(); + if (lineEnding == TextFileFormat::LFLineTerminator) + d->m_fileLineEnding->setText(Tr::tr("LF")); + else if (lineEnding == TextFileFormat::CRLFLineTerminator) + d->m_fileLineEnding->setText(Tr::tr("CRLF")); + else + QTC_ASSERT_STRING("Unsupported line ending mode."); } void TextEditorWidget::updateTextCodecLabel() { QString text = QString::fromLatin1(d->m_document->codec()->name()); - d->m_fileEncodingLabel->setText(text, text); + d->m_fileEncodingButton->setText(text); } QString TextEditorWidget::msgTextTooLarge(quint64 size) @@ -2367,6 +2445,16 @@ void TextEditorWidget::openLinkUnderCursorInNextSplit() d->openLinkUnderCursor(!alwaysOpenLinksInNextSplit()); } +void TextEditorWidget::openTypeUnderCursor() +{ + d->openTypeUnderCursor(alwaysOpenLinksInNextSplit()); +} + +void TextEditorWidget::openTypeUnderCursorInNextSplit() +{ + d->openTypeUnderCursor(!alwaysOpenLinksInNextSplit()); +} + void TextEditorWidget::findUsages() { emit requestUsages(textCursor()); @@ -3531,11 +3619,19 @@ void TextEditorWidgetPrivate::configureGenericHighlighter( void TextEditorWidgetPrivate::setupFromDefinition(const KSyntaxHighlighting::Definition &definition) { + const TypingSettings::CommentPosition commentPosition + = m_document->typingSettings().m_commentPosition; + m_commentDefinition.isAfterWhitespace = commentPosition != TypingSettings::StartOfLine; if (!definition.isValid()) return; m_commentDefinition.singleLine = definition.singleLineCommentMarker(); m_commentDefinition.multiLineStart = definition.multiLineCommentMarker().first; m_commentDefinition.multiLineEnd = definition.multiLineCommentMarker().second; + if (commentPosition == TypingSettings::Automatic) { + m_commentDefinition.isAfterWhitespace + = definition.singleLineCommentPosition() + == KSyntaxHighlighting::CommentPosition::AfterWhitespace; + } q->setCodeFoldingSupported(true); } @@ -3555,11 +3651,26 @@ void TextEditorWidgetPrivate::rememberCurrentSyntaxDefinition() void TextEditorWidgetPrivate::openLinkUnderCursor(bool openInNextSplit) { - q->findLinkAt(q->textCursor(), - [openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) { - if (self) - self->openLink(symbolLink, openInNextSplit); - }, true, openInNextSplit); + q->findLinkAt( + q->textCursor(), + [openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) { + if (self) + self->openLink(symbolLink, openInNextSplit); + }, + true, + openInNextSplit); +} + +void TextEditorWidgetPrivate::openTypeUnderCursor(bool openInNextSplit) +{ + q->findTypeAt( + q->textCursor(), + [openInNextSplit, self = QPointer<TextEditorWidget>(q)](const Link &symbolLink) { + if (self) + self->openLink(symbolLink, openInNextSplit); + }, + true, + openInNextSplit); } qreal TextEditorWidgetPrivate::charWidth() const @@ -3918,10 +4029,7 @@ void TextEditorWidgetPrivate::highlightSearchResults(const QTextBlock &block, co const int start = blockPosition + idx; const int end = start + l; - QTextCursor result = cursor; - result.setPosition(start); - result.setPosition(end, QTextCursor::KeepAnchor); - if (!q->inFindScope(result)) + if (!m_find->inScope(start, end)) continue; // check if the result is inside the visibale area for long blocks @@ -4234,6 +4342,9 @@ void TextEditorWidgetPrivate::updateLineAnnotation(const PaintEventData &data, q->viewport()->update(annotationRect.rect.toAlignedRect()); } m_annotationRects[data.block.blockNumber()] = newRects; + const int maxVisibleLines = data.viewportRect.height() / data.lineSpacing; + if (m_annotationRects.size() >= maxVisibleLines * 2) + scheduleCleanupAnnotationCache(); } QColor blendRightMarginColor(const FontSettings &settings, bool areaColor) @@ -4440,9 +4551,8 @@ void TextEditorWidgetPrivate::paintCurrentLineHighlight(const PaintEventData &da QSet<int> seenLines; for (const QTextCursor &cursor : cursorsForBlock) { QTextLine line = data.block.layout()->lineForTextPosition(cursor.positionInBlock()); - if (seenLines.contains(line.lineNumber())) + if (!Utils::insert(seenLines, line.lineNumber())) continue; - seenLines << line.lineNumber(); QRectF lineRect = line.rect(); lineRect.moveTop(lineRect.top() + blockRect.top()); lineRect.setLeft(0); @@ -4902,8 +5012,19 @@ QTextBlock TextEditorWidgetPrivate::nextVisibleBlock(const QTextBlock &block) co return TextEditor::nextVisibleBlock(block, q->document()); } +void TextEditorWidgetPrivate::scheduleCleanupAnnotationCache() +{ + if (cleanupAnnotationRectsScheduled) + return; + QMetaObject::invokeMethod(this, + &TextEditorWidgetPrivate::cleanupAnnotationCache, + Qt::QueuedConnection); + cleanupAnnotationRectsScheduled = true; +} + void TextEditorWidgetPrivate::cleanupAnnotationCache() { + cleanupAnnotationRectsScheduled = false; const int firstVisibleBlock = q->firstVisibleBlockNumber(); const int lastVisibleBlock = q->lastVisibleBlockNumber(); auto lineIsVisble = [&](int blockNumber){ @@ -5025,8 +5146,6 @@ void TextEditorWidget::paintEvent(QPaintEvent *e) } } - d->cleanupAnnotationCache(); - painter.setPen(data.context.palette.text().color()); d->updateAnimator(d->m_bracketsAnimator, painter); @@ -5702,10 +5821,8 @@ void TextEditorWidgetPrivate::slotUpdateBlockNotify(const QTextBlock &block) if (blockContainsFindScope) { QTextBlock b = block.document()->findBlock(scope.selectionStart()); do { - if (!updatedBlocks.contains(b.blockNumber())) { - updatedBlocks << b.blockNumber(); + if (Utils::insert(updatedBlocks, b.blockNumber())) emit q->requestBlockUpdate(b); - } b = b.next(); } while (b.isValid() && b.position() < scope.selectionEnd()); } @@ -6586,6 +6703,14 @@ void TextEditorWidget::findLinkAt(const QTextCursor &cursor, emit requestLinkAt(cursor, callback, resolveTarget, inNextSplit); } +void TextEditorWidget::findTypeAt(const QTextCursor &cursor, + const Utils::LinkHandler &callback, + bool resolveTarget, + bool inNextSplit) +{ + emit requestTypeAt(cursor, callback, resolveTarget, inNextSplit); +} + bool TextEditorWidget::openLink(const Utils::Link &link, bool inNextSplit) { #ifdef WITH_TESTS @@ -6717,27 +6842,6 @@ void TextEditorWidgetPrivate::highlightSearchResultsSlot(const QString &txt, Fin m_scrollBarUpdateTimer.start(50); } -void TextEditorWidgetPrivate::searchResultsReady(int beginIndex, int endIndex) -{ - QVector<SearchResult> results; - for (int index = beginIndex; index < endIndex; ++index) { - const SearchResultItems resultList = m_searchWatcher->resultAt(index); - for (const SearchResultItem &result : resultList) { - SearchResult searchResult; - if (q->inFindScope(selectRange(q->document(), result.mainRange(), &searchResult))) - results << searchResult; - } - } - m_searchResults << results; - addSearchResultsToScrollBar(results); -} - -void TextEditorWidgetPrivate::searchFinished() -{ - delete m_searchWatcher; - m_searchWatcher = nullptr; -} - void TextEditorWidgetPrivate::adjustScrollBarRanges() { if (!m_highlightScrollBarController) @@ -6758,12 +6862,8 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar() m_highlightScrollBarController->removeHighlights(Constants::SCROLL_BAR_SEARCH_RESULT); m_searchResults.clear(); - if (m_searchWatcher) { - m_searchWatcher->disconnect(); - m_searchWatcher->cancel(); - m_searchWatcher->deleteLater(); - m_searchWatcher = nullptr; - } + if (m_searchFuture.isRunning()) + m_searchFuture.cancel(); const QString &txt = m_findText; if (txt.isEmpty()) @@ -6771,25 +6871,28 @@ void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar() adjustScrollBarRanges(); - m_searchWatcher = new QFutureWatcher<SearchResultItems>; - connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::resultsReadyAt, - this, &TextEditorWidgetPrivate::searchResultsReady); - connect(m_searchWatcher, &QFutureWatcher<SearchResultItems>::finished, - this, &TextEditorWidgetPrivate::searchFinished); - m_searchWatcher->setPendingResultsLimit(10); - - const QTextDocument::FindFlags findFlags = textDocumentFlagsForFindFlags(m_findFlags); - - const FilePath &fileName = m_document->filePath(); - FileListIterator *it = - new FileListIterator({fileName} , {const_cast<QTextCodec *>(m_document->codec())}); - QMap<FilePath, QString> fileToContentsMap; - fileToContentsMap[fileName] = m_document->plainText(); - - if (m_findFlags & FindRegularExpression) - m_searchWatcher->setFuture(findInFilesRegExp(txt, it, findFlags, fileToContentsMap)); - else - m_searchWatcher->setFuture(findInFiles(txt, it, findFlags, fileToContentsMap)); + m_searchFuture = Utils::asyncRun(Utils::searchInContents, + txt, + m_findFlags, + m_document->filePath(), + m_document->plainText()); + Utils::onResultReady(m_searchFuture, this, [this](const SearchResultItems &resultList) { + QList<SearchResult> results; + for (const SearchResultItem &result : resultList) { + int start = result.mainRange().begin.toPositionInDocument(m_document->document()); + if (start < 0) + continue; + int end = result.mainRange().end.toPositionInDocument(m_document->document()); + if (end < 0) + continue; + if (start > end) + std::swap(start, end); + if (m_find->inScope(start, end)) + results << SearchResult{start, start - end}; + } + m_searchResults << results; + addSearchResultsToScrollBar(results); + }); } void TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar() @@ -6823,12 +6926,20 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(const QVector<SearchRe for (const SearchResult &result : results) { const QTextBlock &block = q->document()->findBlock(result.start); if (block.isValid() && block.isVisible()) { - const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber(); - const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber(); - for (int line = firstLine; line <= lastLine; ++line) { + if (q->lineWrapMode() == QPlainTextEdit::WidgetWidth) { + const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber(); + const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber(); + for (int line = firstLine; line <= lastLine; ++line) { + m_highlightScrollBarController->addHighlight( + {Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line, + Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority}); + } + } else { m_highlightScrollBarController->addHighlight( - {Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line, - Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority}); + {Constants::SCROLL_BAR_SEARCH_RESULT, + block.blockNumber(), + Theme::TextEditor_SearchResult_ScrollBarColor, + Highlight::HighPriority}); } } } @@ -7325,7 +7436,7 @@ void TextEditorWidgetPrivate::addSelectionNextFindMatch() return; } - const QTextDocument::FindFlags findFlags = textDocumentFlagsForFindFlags(m_findFlags); + const QTextDocument::FindFlags findFlags = Utils::textDocumentFlagsForFindFlags(m_findFlags); int searchFrom = cursors.last().selectionEnd(); while (true) { @@ -7578,7 +7689,7 @@ void TextEditorWidget::rewrapParagraph() } // Find end of paragraph. - const QRegularExpression immovableDoxygenCommand(doxygenPrefix + "[@\\\\].*"); + const QRegularExpression immovableDoxygenCommand(doxygenPrefix + "[@\\\\][a-zA-Z]{2,}"); QTC_CHECK(immovableDoxygenCommand.isValid()); while (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor)) { QString text = cursor.block().text(); @@ -7808,6 +7919,7 @@ void TextEditorWidget::setBehaviorSettings(const BehaviorSettings &bs) void TextEditorWidget::setTypingSettings(const TypingSettings &typingSettings) { d->m_document->setTypingSettings(typingSettings); + d->setupFromDefinition(d->currentDefinition()); } void TextEditorWidget::setStorageSettings(const StorageSettings &storageSettings) @@ -7833,40 +7945,50 @@ void TextEditorWidget::setExtraEncodingSettings(const ExtraEncodingSettings &ext d->m_document->setExtraEncodingSettings(extraEncodingSettings); } -void TextEditorWidget::fold() +void TextEditorWidget::foldCurrentBlock() +{ + fold(textCursor().block()); +} + +void TextEditorWidget::fold(const QTextBlock &block) { QTextDocument *doc = document(); auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout()); QTC_ASSERT(documentLayout, return); - QTextBlock block = textCursor().block(); - if (!(TextDocumentLayout::canFold(block) && block.next().isVisible())) { + QTextBlock b = block; + if (!(TextDocumentLayout::canFold(b) && b.next().isVisible())) { // find the closest previous block which can fold - int indent = TextDocumentLayout::foldingIndent(block); - while (block.isValid() && (TextDocumentLayout::foldingIndent(block) >= indent || !block.isVisible())) - block = block.previous(); + int indent = TextDocumentLayout::foldingIndent(b); + while (b.isValid() && (TextDocumentLayout::foldingIndent(b) >= indent || !b.isVisible())) + b = b.previous(); } - if (block.isValid()) { - TextDocumentLayout::doFoldOrUnfold(block, false); + if (b.isValid()) { + TextDocumentLayout::doFoldOrUnfold(b, false); d->moveCursorVisible(); documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } } -void TextEditorWidget::unfold() +void TextEditorWidget::unfold(const QTextBlock &block) { QTextDocument *doc = document(); auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout()); QTC_ASSERT(documentLayout, return); - QTextBlock block = textCursor().block(); - while (block.isValid() && !block.isVisible()) - block = block.previous(); - TextDocumentLayout::doFoldOrUnfold(block, true); + QTextBlock b = block; + while (b.isValid() && !b.isVisible()) + b = b.previous(); + TextDocumentLayout::doFoldOrUnfold(b, true); d->moveCursorVisible(); documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } +void TextEditorWidget::unfoldCurrentBlock() +{ + unfold(textCursor().block()); +} + void TextEditorWidget::unfoldAll() { QTextDocument *doc = document(); @@ -7916,10 +8038,12 @@ void TextEditorWidget::cut() void TextEditorWidget::selectAll() { - QPlainTextEdit::selectAll(); // Directly update the internal multi text cursor here to prevent calling setTextCursor. - // This would indirectly makes sure the cursor is visible which is not desired for select all. - const_cast<MultiTextCursor &>(d->m_cursors).setCursors({QPlainTextEdit::textCursor()}); + // This would indirectly make sure the cursor is visible which is not desired for select all. + QTextCursor c = QPlainTextEdit::textCursor(); + c.select(QTextCursor::Document); + const_cast<MultiTextCursor &>(d->m_cursors).setCursors({c}); + QPlainTextEdit::selectAll(); } void TextEditorWidget::copy() @@ -8298,15 +8422,30 @@ void TextEditorWidget::setupFallBackEditor(Id id) void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu) { + if (optionalActions() & TextEditorActionHandler::FollowSymbolUnderCursor) { + const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_UNDER_CURSOR)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); + } + if (optionalActions() & TextEditorActionHandler::FollowTypeUnderCursor) { + const auto action = ActionManager::command(Constants::FOLLOW_SYMBOL_TO_TYPE)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); + } if (optionalActions() & TextEditorActionHandler::FindUsage) { - const auto findUsage = ActionManager::command(Constants::FIND_USAGES)->action(); - if (!menu->actions().contains(findUsage)) - menu->addAction(findUsage); + const auto action = ActionManager::command(Constants::FIND_USAGES)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); + } + if (optionalActions() & TextEditorActionHandler::RenameSymbol) { + const auto action = ActionManager::command(Constants::RENAME_SYMBOL)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); } if (optionalActions() & TextEditorActionHandler::CallHierarchy) { - const auto callHierarchy = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action(); - if (!menu->actions().contains(callHierarchy)) - menu->addAction(callHierarchy); + const auto action = ActionManager::command(Constants::OPEN_CALL_HIERARCHY)->action(); + if (!menu->actions().contains(action)) + menu->addAction(action); } menu->addSeparator(); @@ -8673,6 +8812,11 @@ void TextEditorWidgetPrivate::updateTabStops() QTextOption option = q->document()->defaultTextOption(); option.setTabStopDistance(charWidth() * m_document->tabSettings().m_tabSize); q->document()->setDefaultTextOption(option); + if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(m_suggestionBlock)) { + QTextOption option = suggestion->document()->defaultTextOption(); + option.setTabStopDistance(option.tabStopDistance()); + suggestion->document()->setDefaultTextOption(option); + } } void TextEditorWidgetPrivate::applyTabSettings() @@ -8924,6 +9068,17 @@ void TextEditorWidget::configureGenericHighlighter(const Utils::MimeType &mimeTy d->removeSyntaxInfoBar(); } +expected_str<void> TextEditorWidget::configureGenericHighlighter(const QString &definitionName) +{ + Highlighter::Definition definition = TextEditor::Highlighter::definitionForName(definitionName); + if (!definition.isValid()) + return make_unexpected(Tr::tr("Could not find definition.")); + + d->configureGenericHighlighter(definition); + d->removeSyntaxInfoBar(); + return {}; +} + int TextEditorWidget::blockNumberForVisibleRow(int row) const { QTextBlock block = blockForVisibleRow(row); @@ -9189,6 +9344,8 @@ BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentP textEditorWidget->d->m_hoverHandlers = m_hoverHandlers; textEditorWidget->d->m_commentDefinition = m_commentDefinition; + textEditorWidget->d->m_commentDefinition.isAfterWhitespace + = document->typingSettings().m_commentPosition != TypingSettings::StartOfLine; QObject::connect(textEditorWidget, &TextEditorWidget::activateEditor, diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index c05425a5b07..0abe3305639 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -5,7 +5,6 @@ #include "texteditor_global.h" -#include "blockrange.h" #include "codeassist/assistenums.h" #include "indenter.h" #include "refactoroverlay.h" @@ -155,7 +154,6 @@ private: Internal::BaseTextEditorPrivate *d; }; - class TEXTEDITOR_EXPORT TextEditorWidget : public QPlainTextEdit { Q_OBJECT @@ -363,11 +361,13 @@ public: void deleteStartOfWord(); void deleteStartOfWordCamelCase(); void unfoldAll(); - void fold(); - void unfold(); + void fold(const QTextBlock &block); + void foldCurrentBlock(); + void unfold(const QTextBlock &block); + void unfoldCurrentBlock(); void selectEncoding(); void updateTextCodecLabel(); - void selectLineEnding(int index); + void selectLineEnding(Utils::TextFileFormat::LineTerminationMode lineEnding); void updateTextLineEndingLabel(); void addSelectionNextFindMatch(); void addCursorsToLineEnds(); @@ -432,11 +432,13 @@ public: void indent(); void unindent(); - void undo(); - void redo(); + virtual void undo(); + virtual void redo(); void openLinkUnderCursor(); void openLinkUnderCursorInNextSplit(); + void openTypeUnderCursor(); + void openTypeUnderCursorInNextSplit(); virtual void findUsages(); virtual void renameSymbolUnderCursor(); @@ -451,6 +453,9 @@ public: /// Overwrite the current highlighter with a new generic highlighter based on the given mimetype void configureGenericHighlighter(const Utils::MimeType &mimeType); + /// Overwrite the current highlighter with a new generic highlighter based on the given definition + Utils::expected_str<void> configureGenericHighlighter(const QString &definitionName); + Q_INVOKABLE void inSnippetMode(bool *active); // Used by FakeVim. /*! Returns the document line number for the visible \a row. @@ -495,6 +500,8 @@ signals: void requestLinkAt(const QTextCursor &cursor, const Utils::LinkHandler &callback, bool resolveTarget, bool inNextSplit); + void requestTypeAt(const QTextCursor &cursor, const Utils::LinkHandler &callback, + bool resolveTarget, bool inNextSplit); void requestUsages(const QTextCursor &cursor); void requestRename(const QTextCursor &cursor); void requestCallHierarchy(const QTextCursor &cursor); @@ -587,6 +594,11 @@ protected: bool resolveTarget = true, bool inNextSplit = false); + virtual void findTypeAt(const QTextCursor &, + const Utils::LinkHandler &processLinkCallback, + bool resolveTarget = true, + bool inNextSplit = false); + /*! Returns whether the link was opened successfully. */ diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index fa8441f29ef..a041fbcaa66 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -1,235 +1,240 @@ -import qbs 1.0 import qbs.FileInfo import qbs.Environment -Project { +QtcPlugin { name: "TextEditor" - QtcPlugin { - Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "printsupport"] } - Depends { name: "Aggregation" } - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["widgets", "xml", "network", "printsupport"] } + Depends { name: "Aggregation" } + Depends { name: "Utils" } + Depends { name: "KSyntaxHighlighting" } + + Export { Depends { name: "KSyntaxHighlighting" } + } - Export { - Depends { name: "KSyntaxHighlighting" } - } + Depends { name: "Core" } - Depends { name: "Core" } + cpp.enableExceptions: true - cpp.enableExceptions: true + files: [ + "autocompleter.cpp", + "autocompleter.h", + "basefilefind.cpp", + "basefilefind.h", + "basehoverhandler.cpp", + "basehoverhandler.h", + "behaviorsettings.cpp", + "behaviorsettings.h", + "behaviorsettingspage.cpp", + "behaviorsettingspage.h", + "behaviorsettingswidget.cpp", + "behaviorsettingswidget.h", + "blockrange.h", + "bookmark.cpp", + "bookmark.h", + "bookmarkfilter.cpp", + "bookmarkfilter.h", + "bookmarkmanager.cpp", + "bookmarkmanager.h", + "circularclipboard.cpp", + "circularclipboard.h", + "circularclipboardassist.cpp", + "circularclipboardassist.h", + "codecchooser.cpp", + "codecchooser.h", + "codestyleeditor.cpp", + "codestyleeditor.h", + "codestylepool.cpp", + "codestylepool.h", + "codestyleselectorwidget.cpp", + "codestyleselectorwidget.h", + "colorpreviewhoverhandler.cpp", + "colorpreviewhoverhandler.h", + "colorscheme.cpp", + "colorscheme.h", + "colorschemeedit.cpp", + "colorschemeedit.h", + "command.cpp", + "command.h", + "commentssettings.cpp", + "commentssettings.h", + "completionsettings.cpp", + "completionsettings.h", + "completionsettingspage.cpp", + "completionsettingspage.h", + "displaysettings.cpp", + "displaysettings.h", + "displaysettingspage.cpp", + "displaysettingspage.h", + "extraencodingsettings.cpp", + "extraencodingsettings.h", + "findincurrentfile.cpp", + "findincurrentfile.h", + "findinfiles.cpp", + "findinfiles.h", + "findinopenfiles.cpp", + "findinopenfiles.h", + "fontsettings.cpp", + "fontsettings.h", + "fontsettingspage.cpp", + "fontsettingspage.h", + "formatter.h", + "formattexteditor.cpp", + "formattexteditor.h", + "highlighter.cpp", + "highlighter.h", + "highlightersettings.cpp", + "highlightersettings.h", + "highlightersettingspage.cpp", + "highlightersettingspage.h", + "icodestylepreferences.cpp", + "icodestylepreferences.h", + "icodestylepreferencesfactory.cpp", + "icodestylepreferencesfactory.h", + "indenter.h", + "ioutlinewidget.h", + "jsoneditor.cpp", + "jsoneditor.h", + "linenumberfilter.cpp", + "linenumberfilter.h", + "marginsettings.cpp", + "marginsettings.h", + "markdowneditor.cpp", + "markdowneditor.h", + "outlinefactory.cpp", + "outlinefactory.h", + "plaintexteditorfactory.cpp", + "plaintexteditorfactory.h", + "quickfix.cpp", + "quickfix.h", + "refactoringchanges.cpp", + "refactoringchanges.h", + "refactoroverlay.cpp", + "refactoroverlay.h", + "semantichighlighter.cpp", + "semantichighlighter.h", + "simplecodestylepreferences.cpp", + "simplecodestylepreferences.h", + "simplecodestylepreferenceswidget.cpp", + "simplecodestylepreferenceswidget.h", + "storagesettings.cpp", + "storagesettings.h", + "syntaxhighlighter.cpp", + "syntaxhighlighter.h", + "tabsettings.cpp", + "tabsettings.h", + "tabsettingswidget.cpp", + "tabsettingswidget.h", + "textdocument.cpp", + "textdocument.h", + "textdocumentlayout.cpp", + "textdocumentlayout.h", + "texteditor.cpp", + "texteditor.h", + "texteditor.qrc", + "texteditor_global.h", "texteditortr.h", + "texteditor_p.h", + "texteditoractionhandler.cpp", + "texteditoractionhandler.h", + "texteditorconstants.cpp", + "texteditorconstants.h", + "texteditoroverlay.cpp", + "texteditoroverlay.h", + "texteditorplugin.cpp", + "texteditorplugin.h", + "texteditorsettings.cpp", + "texteditorsettings.h", + "textindenter.cpp", + "textindenter.h", + "textmark.cpp", + "textmark.h", + "textstyles.h", + "typingsettings.cpp", + "typingsettings.h", + ] + Group { + name: "CodeAssist" + prefix: "codeassist/" files: [ - "autocompleter.cpp", - "autocompleter.h", - "basefilefind.cpp", - "basefilefind.h", - "basehoverhandler.cpp", - "basehoverhandler.h", - "behaviorsettings.cpp", - "behaviorsettings.h", - "behaviorsettingspage.cpp", - "behaviorsettingspage.h", - "behaviorsettingswidget.cpp", - "behaviorsettingswidget.h", - "blockrange.h", - "circularclipboard.cpp", - "circularclipboard.h", - "circularclipboardassist.cpp", - "circularclipboardassist.h", - "codecchooser.cpp", - "codecchooser.h", - "codestyleeditor.cpp", - "codestyleeditor.h", - "codestylepool.cpp", - "codestylepool.h", - "codestyleselectorwidget.cpp", - "codestyleselectorwidget.h", - "colorpreviewhoverhandler.cpp", - "colorpreviewhoverhandler.h", - "colorscheme.cpp", - "colorscheme.h", - "colorschemeedit.cpp", - "colorschemeedit.h", - "command.cpp", - "command.h", - "commentssettings.cpp", - "commentssettings.h", - "completionsettings.cpp", - "completionsettings.h", - "completionsettingspage.cpp", - "completionsettingspage.h", - "displaysettings.cpp", - "displaysettings.h", - "displaysettingspage.cpp", - "displaysettingspage.h", - "extraencodingsettings.cpp", - "extraencodingsettings.h", - "findincurrentfile.cpp", - "findincurrentfile.h", - "findinfiles.cpp", - "findinfiles.h", - "findinopenfiles.cpp", - "findinopenfiles.h", - "fontsettings.cpp", - "fontsettings.h", - "fontsettingspage.cpp", - "fontsettingspage.h", - "formatter.h", - "formattexteditor.cpp", - "formattexteditor.h", - "highlighter.cpp", - "highlighter.h", - "highlightersettings.cpp", - "highlightersettings.h", - "highlightersettingspage.cpp", - "highlightersettingspage.h", - "icodestylepreferences.cpp", - "icodestylepreferences.h", - "icodestylepreferencesfactory.cpp", - "icodestylepreferencesfactory.h", - "indenter.h", - "ioutlinewidget.h", - "linenumberfilter.cpp", - "linenumberfilter.h", - "marginsettings.cpp", - "marginsettings.h", - "markdowneditor.cpp", - "markdowneditor.h", - "outlinefactory.cpp", - "outlinefactory.h", - "plaintexteditorfactory.cpp", - "plaintexteditorfactory.h", - "quickfix.cpp", - "quickfix.h", - "refactoringchanges.cpp", - "refactoringchanges.h", - "refactoroverlay.cpp", - "refactoroverlay.h", - "semantichighlighter.cpp", - "semantichighlighter.h", - "simplecodestylepreferences.cpp", - "simplecodestylepreferences.h", - "simplecodestylepreferenceswidget.cpp", - "simplecodestylepreferenceswidget.h", - "storagesettings.cpp", - "storagesettings.h", - "syntaxhighlighter.cpp", - "syntaxhighlighter.h", - "tabsettings.cpp", - "tabsettings.h", - "tabsettingswidget.cpp", - "tabsettingswidget.h", - "textdocument.cpp", - "textdocument.h", - "textdocumentlayout.cpp", - "textdocumentlayout.h", - "texteditor.cpp", - "texteditor.h", - "texteditor.qrc", - "texteditor_global.h", "texteditortr.h", - "texteditor_p.h", - "texteditoractionhandler.cpp", - "texteditoractionhandler.h", - "texteditorconstants.cpp", - "texteditorconstants.h", - "texteditoroverlay.cpp", - "texteditoroverlay.h", - "texteditorplugin.cpp", - "texteditorplugin.h", - "texteditorsettings.cpp", - "texteditorsettings.h", - "textindenter.cpp", - "textindenter.h", - "textmark.cpp", - "textmark.h", - "textstyles.h", - "typingsettings.cpp", - "typingsettings.h", + "assistenums.h", + "assistinterface.cpp", + "assistinterface.h", + "assistproposalitem.cpp", + "assistproposalitem.h", + "assistproposaliteminterface.h", + "asyncprocessor.cpp", + "asyncprocessor.h", + "codeassistant.cpp", + "codeassistant.h", + "completionassistprovider.cpp", + "completionassistprovider.h", + "documentcontentcompletion.cpp", + "documentcontentcompletion.h", + "functionhintproposal.cpp", + "functionhintproposal.h", + "functionhintproposalwidget.cpp", + "functionhintproposalwidget.h", + "genericproposal.cpp", + "genericproposal.h", + "genericproposalmodel.cpp", + "genericproposalmodel.h", + "genericproposalwidget.cpp", + "genericproposalwidget.h", + "iassistprocessor.cpp", + "iassistprocessor.h", + "iassistproposal.cpp", + "iassistproposal.h", + "iassistproposalmodel.cpp", + "iassistproposalmodel.h", + "iassistproposalwidget.cpp", + "iassistproposalwidget.h", + "iassistprovider.cpp", + "iassistprovider.h", + "ifunctionhintproposalmodel.cpp", + "ifunctionhintproposalmodel.h", + "keywordscompletionassist.cpp", + "keywordscompletionassist.h", + "textdocumentmanipulator.cpp", + "textdocumentmanipulator.h", + "textdocumentmanipulatorinterface.h", ] + } - Group { - name: "CodeAssist" - prefix: "codeassist/" - files: [ - "assistenums.h", - "assistinterface.cpp", - "assistinterface.h", - "assistproposalitem.cpp", - "assistproposalitem.h", - "assistproposaliteminterface.h", - "asyncprocessor.cpp", - "asyncprocessor.h", - "codeassistant.cpp", - "codeassistant.h", - "completionassistprovider.cpp", - "completionassistprovider.h", - "documentcontentcompletion.cpp", - "documentcontentcompletion.h", - "functionhintproposal.cpp", - "functionhintproposal.h", - "functionhintproposalwidget.cpp", - "functionhintproposalwidget.h", - "genericproposal.cpp", - "genericproposal.h", - "genericproposalmodel.cpp", - "genericproposalmodel.h", - "genericproposalwidget.cpp", - "genericproposalwidget.h", - "iassistprocessor.cpp", - "iassistprocessor.h", - "iassistproposal.cpp", - "iassistproposal.h", - "iassistproposalmodel.cpp", - "iassistproposalmodel.h", - "iassistproposalwidget.cpp", - "iassistproposalwidget.h", - "iassistprovider.cpp", - "iassistprovider.h", - "ifunctionhintproposalmodel.cpp", - "ifunctionhintproposalmodel.h", - "keywordscompletionassist.cpp", - "keywordscompletionassist.h", - "textdocumentmanipulator.cpp", - "textdocumentmanipulator.h", - "textdocumentmanipulatorinterface.h", - ] - } + Group { + name: "Snippets" + prefix: "snippets/" + files: [ + "reuse.h", + "snippet.cpp", + "snippet.h", + "snippetassistcollector.cpp", + "snippetassistcollector.h", + "snippeteditor.cpp", + "snippeteditor.h", + "snippetoverlay.cpp", + "snippetoverlay.h", + "snippetparser.cpp", + "snippetparser.h", + "snippetprovider.cpp", + "snippetprovider.h", + "snippetscollection.cpp", + "snippetscollection.h", + "snippetssettings.cpp", + "snippetssettings.h", + "snippetssettingspage.cpp", + "snippetssettingspage.h", + ] + } - Group { - name: "Snippets" - prefix: "snippets/" - files: [ - "reuse.h", - "snippet.cpp", - "snippet.h", - "snippetassistcollector.cpp", - "snippetassistcollector.h", - "snippeteditor.cpp", - "snippeteditor.h", - "snippetoverlay.cpp", - "snippetoverlay.h", - "snippetparser.cpp", - "snippetparser.h", - "snippetprovider.cpp", - "snippetprovider.h", - "snippetscollection.cpp", - "snippetscollection.h", - "snippetssettings.cpp", - "snippetssettings.h", - "snippetssettingspage.cpp", - "snippetssettingspage.h", - ] - } - - QtcTestFiles { - files: [ - "codeassist/codeassist_test.cpp", - "codeassist/codeassist_test.h", - "highlighter_test.cpp", - "highlighter_test.h", - "texteditor_test.cpp", - ] - } + QtcTestFiles { + files: [ + "codeassist/codeassist_test.cpp", + "codeassist/codeassist_test.h", + "highlighter_test.cpp", + "highlighter_test.h", + "texteditor_test.cpp", + ] } } diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index 00ed94a5ee7..49b83b8a756 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -62,8 +62,18 @@ public: Utils::Id menueGroup = Utils::Id(), Core::ActionContainer *container = nullptr) { - return registerActionHelper(id, scriptable, title, keySequence, menueGroup, container, - [this, slot](bool) { if (m_currentEditorWidget) slot(m_currentEditorWidget); }); + return registerActionHelper(id, + scriptable, + title, + keySequence, + menueGroup, + container, + [this, slot, id](bool) { + if (m_currentEditorWidget) + slot(m_currentEditorWidget); + else if (m_unhandledCallback) + m_unhandledCallback(id, m_currentEditor); + }); } QAction *registerBoolAction(Utils::Id id, @@ -100,6 +110,9 @@ public: void updateCurrentEditor(Core::IEditor *editor); + void setCanUndoCallback(const TextEditorActionHandler::Predicate &callback); + void setCanRedoCallback(const TextEditorActionHandler::Predicate &callback); + public: TextEditorActionHandler::TextEditorWidgetResolver m_findTextWidget; QAction *m_undoAction = nullptr; @@ -115,6 +128,8 @@ public: QAction *m_unfoldAllAction = nullptr; QAction *m_followSymbolAction = nullptr; QAction *m_followSymbolInNextSplitAction = nullptr; + QAction *m_followToTypeAction = nullptr; + QAction *m_followToTypeInNextSplitAction = nullptr; QAction *m_findUsageAction = nullptr; QAction *m_openCallHierarchyAction = nullptr; QAction *m_renameSymbolAction = nullptr; @@ -124,8 +139,14 @@ public: uint m_optionalActions = TextEditorActionHandler::None; QPointer<TextEditorWidget> m_currentEditorWidget; + QPointer<Core::IEditor> m_currentEditor; Utils::Id m_editorId; Utils::Id m_contextId; + + TextEditorActionHandler::Predicate m_canUndoCallback; + TextEditorActionHandler::Predicate m_canRedoCallback; + + TextEditorActionHandler::UnhandledCallback m_unhandledCallback; }; TextEditorActionHandlerPrivate::TextEditorActionHandlerPrivate @@ -217,6 +238,12 @@ void TextEditorActionHandlerPrivate::createActions() m_followSymbolInNextSplitAction = registerAction(FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT, [] (TextEditorWidget *w) { w->openLinkUnderCursorInNextSplit(); }, true, Tr::tr("Follow Symbol Under Cursor in Next Split"), QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, F2") : Tr::tr("Ctrl+E, F2"))); + m_followToTypeAction = registerAction(FOLLOW_SYMBOL_TO_TYPE, + [] (TextEditorWidget *w) { w->openTypeUnderCursor(); }, true, Tr::tr("Follow Type Under Cursor"), + QKeySequence(Tr::tr("Ctrl+Shift+F2"))); + m_followToTypeInNextSplitAction = registerAction(FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT, + [] (TextEditorWidget *w) { w->openTypeUnderCursorInNextSplit(); }, true, Tr::tr("Follow Type Under Cursor in Next Split"), + QKeySequence(Utils::HostOsInfo::isMacHost() ? Tr::tr("Meta+E, Shift+F2") : Tr::tr("Ctrl+E, Ctrl+Shift+F2"))); m_findUsageAction = registerAction(FIND_USAGES, [] (TextEditorWidget *w) { w->findUsages(); }, true, Tr::tr("Find References to Symbol Under Cursor"), QKeySequence(Tr::tr("Ctrl+Shift+U"))); @@ -344,11 +371,11 @@ void TextEditorActionHandlerPrivate::createActions() QKeySequence(Core::useMacShortcuts ? Tr::tr("Meta+Shift+S") : Tr::tr("Alt+Shift+S")), G_EDIT_TEXT, advancedEditMenu); registerAction(FOLD, - [] (TextEditorWidget *w) { w->fold(); }, true, Tr::tr("Fold"), + [] (TextEditorWidget *w) { w->foldCurrentBlock(); }, true, Tr::tr("Fold"), QKeySequence(Tr::tr("Ctrl+<")), G_EDIT_COLLAPSING, advancedEditMenu); registerAction(UNFOLD, - [] (TextEditorWidget *w) { w->unfold(); }, true, Tr::tr("Unfold"), + [] (TextEditorWidget *w) { w->unfoldCurrentBlock(); }, true, Tr::tr("Unfold"), QKeySequence(Tr::tr("Ctrl+>")), G_EDIT_COLLAPSING, advancedEditMenu); m_unfoldAllAction = registerAction(UNFOLD_ALL, @@ -463,9 +490,28 @@ void TextEditorActionHandlerPrivate::updateActions() m_textWrappingAction->setChecked(m_currentEditorWidget->displaySettings().m_textWrapping); } - updateRedoAction(m_currentEditorWidget && m_currentEditorWidget->document()->isRedoAvailable()); - updateUndoAction(m_currentEditorWidget && m_currentEditorWidget->document()->isUndoAvailable()); - updateCopyAction(m_currentEditorWidget && m_currentEditorWidget->textCursor().hasSelection()); + bool canRedo = false; + bool canUndo = false; + bool canCopy = false; + + if (m_currentEditor && m_currentEditor->document() + && m_currentEditor->document()->id() == m_editorId) { + canRedo = m_canRedoCallback ? m_canRedoCallback(m_currentEditor) : false; + canUndo = m_canUndoCallback ? m_canUndoCallback(m_currentEditor) : false; + + if (m_currentEditorWidget) { + canRedo = m_canRedoCallback ? canRedo + : m_currentEditorWidget->document()->isRedoAvailable(); + canUndo = m_canUndoCallback ? canUndo + : m_currentEditorWidget->document()->isUndoAvailable(); + canCopy = m_currentEditorWidget->textCursor().hasSelection(); + } + } + + updateRedoAction(canRedo); + updateUndoAction(canUndo); + updateCopyAction(canCopy); + updateOptionalActions(); } @@ -477,6 +523,10 @@ void TextEditorActionHandlerPrivate::updateOptionalActions() optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); m_followSymbolInNextSplitAction->setEnabled( optionalActions & TextEditorActionHandler::FollowSymbolUnderCursor); + m_followToTypeAction->setEnabled( + optionalActions & TextEditorActionHandler::FollowTypeUnderCursor); + m_followToTypeInNextSplitAction->setEnabled( + optionalActions & TextEditorActionHandler::FollowTypeUnderCursor); m_findUsageAction->setEnabled( optionalActions & TextEditorActionHandler::FindUsage); m_jumpToFileAction->setEnabled( @@ -523,20 +573,22 @@ void TextEditorActionHandlerPrivate::updateCurrentEditor(Core::IEditor *editor) m_currentEditorWidget->disconnect(this); m_currentEditorWidget = nullptr; + m_currentEditor = editor; + if (editor && editor->document()->id() == m_editorId) { - TextEditorWidget *editorWidget = m_findTextWidget(editor); - QTC_ASSERT(editorWidget, return); // editor has our id, so shouldn't happen - m_currentEditorWidget = editorWidget; - connect(editorWidget, &QPlainTextEdit::undoAvailable, - this, &TextEditorActionHandlerPrivate::updateUndoAction); - connect(editorWidget, &QPlainTextEdit::redoAvailable, - this, &TextEditorActionHandlerPrivate::updateRedoAction); - connect(editorWidget, &QPlainTextEdit::copyAvailable, - this, &TextEditorActionHandlerPrivate::updateCopyAction); - connect(editorWidget, &TextEditorWidget::readOnlyChanged, - this, &TextEditorActionHandlerPrivate::updateActions); - connect(editorWidget, &TextEditorWidget::optionalActionMaskChanged, - this, &TextEditorActionHandlerPrivate::updateOptionalActions); + m_currentEditorWidget = m_findTextWidget(editor); + if (m_currentEditorWidget) { + connect(m_currentEditorWidget, &QPlainTextEdit::undoAvailable, + this, &TextEditorActionHandlerPrivate::updateUndoAction); + connect(m_currentEditorWidget, &QPlainTextEdit::redoAvailable, + this, &TextEditorActionHandlerPrivate::updateRedoAction); + connect(m_currentEditorWidget, &QPlainTextEdit::copyAvailable, + this, &TextEditorActionHandlerPrivate::updateCopyAction); + connect(m_currentEditorWidget, &TextEditorWidget::readOnlyChanged, + this, &TextEditorActionHandlerPrivate::updateActions); + connect(m_currentEditorWidget, &TextEditorWidget::optionalActionMaskChanged, + this, &TextEditorActionHandlerPrivate::updateOptionalActions); + } } updateActions(); } @@ -565,4 +617,29 @@ TextEditorActionHandler::~TextEditorActionHandler() delete d; } +void TextEditorActionHandler::updateCurrentEditor() +{ + d->updateCurrentEditor(Core::EditorManager::currentEditor()); +} + +void TextEditorActionHandler::updateActions() +{ + d->updateActions(); +} + +void TextEditorActionHandler::setCanUndoCallback(const Predicate &callback) +{ + d->m_canUndoCallback = callback; +} + +void TextEditorActionHandler::setCanRedoCallback(const Predicate &callback) +{ + d->m_canRedoCallback = callback; +} + +void TextEditorActionHandler::setUnhandledCallback(const UnhandledCallback &callback) +{ + d->m_unhandledCallback = callback; +} + } // namespace TextEditor diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h index 2bdb8efff3e..344a4a553e1 100644 --- a/src/plugins/texteditor/texteditoractionhandler.h +++ b/src/plugins/texteditor/texteditoractionhandler.h @@ -34,10 +34,11 @@ public: UnCommentSelection = 2, UnCollapseAll = 4, FollowSymbolUnderCursor = 8, - JumpToFileUnderCursor = 16, - RenameSymbol = 32, - FindUsage = 64, - CallHierarchy = 128 + FollowTypeUnderCursor = 16, + JumpToFileUnderCursor = 32, + RenameSymbol = 64, + FindUsage = 128, + CallHierarchy = 256 }; using TextEditorWidgetResolver = std::function<TextEditorWidget *(Core::IEditor *)>; @@ -49,6 +50,17 @@ public: uint optionalActions() const; ~TextEditorActionHandler(); + void updateCurrentEditor(); + void updateActions(); + + using Predicate = std::function<bool(Core::IEditor *editor)>; + + void setCanUndoCallback(const Predicate &callback); + void setCanRedoCallback(const Predicate &callback); + + using UnhandledCallback = std::function<void(Utils::Id commandId, Core::IEditor *editor)>; + void setUnhandledCallback(const UnhandledCallback &callback); + private: Internal::TextEditorActionHandlerPrivate *d; }; diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 946b2b2f890..4cac6a9dd7f 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -206,6 +206,8 @@ const char INDENT[] = "TextEditor.Indent"; const char UNINDENT[] = "TextEditor.Unindent"; const char FOLLOW_SYMBOL_UNDER_CURSOR[] = "TextEditor.FollowSymbolUnderCursor"; const char FOLLOW_SYMBOL_UNDER_CURSOR_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolUnderCursorInNextSplit"; +const char FOLLOW_SYMBOL_TO_TYPE[] = "TextEditor.FollowSymbolToType"; +const char FOLLOW_SYMBOL_TO_TYPE_IN_NEXT_SPLIT[] = "TextEditor.FollowSymbolToTypeInNextSplit"; const char FIND_USAGES[] = "TextEditor.FindUsages"; // moved from CppEditor to TextEditor avoid breaking the setting by using the old key const char RENAME_SYMBOL[] = "CppEditor.RenameSymbolUnderCursor"; @@ -226,6 +228,7 @@ const char TEXT_EDITOR_BEHAVIOR_SETTINGS[] = "B.BehaviourSettings"; const char TEXT_EDITOR_DISPLAY_SETTINGS[] = "D.DisplaySettings"; const char TEXT_EDITOR_HIGHLIGHTER_SETTINGS[] = "E.HighlighterSettings"; const char TEXT_EDITOR_SNIPPETS_SETTINGS[] = "F.SnippetsSettings"; +const char TEXT_EDITOR_COMMENTS_SETTINGS[] = "Q.CommentsSettings"; const char HIGHLIGHTER_SETTINGS_CATEGORY[] = "HighlighterSettings"; @@ -235,6 +238,9 @@ const char TEXT_SNIPPET_GROUP_ID[] = "Text"; const char GLOBAL_SETTINGS_ID[] = "Global"; const char GENERIC_PROPOSAL_ID[] = "TextEditor.GenericProposalId"; +const char BOOKMARKS_PREV_ACTION[] = "Bookmarks.Previous"; +const char BOOKMARKS_NEXT_ACTION[] = "Bookmarks.Next"; + /** * Delay before tooltip will be shown near completion assistant proposal */ diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index f82155c0a54..f8de8503937 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -3,12 +3,15 @@ #include "texteditorplugin.h" +#include "bookmarkfilter.h" +#include "bookmarkmanager.h" #include "findincurrentfile.h" #include "findinfiles.h" #include "findinopenfiles.h" #include "fontsettings.h" #include "highlighter.h" #include "icodestylepreferences.h" +#include "jsoneditor.h" #include "linenumberfilter.h" #include "markdowneditor.h" #include "outlinefactory.h" @@ -16,7 +19,9 @@ #include "snippets/snippetprovider.h" #include "tabsettings.h" #include "textdocument.h" +#include "textdocument.h" #include "texteditor.h" +#include "texteditorconstants.h" #include "texteditorsettings.h" #include "texteditortr.h" @@ -28,7 +33,10 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/coreconstants.h> #include <coreplugin/diffservice.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> #include <coreplugin/externaltoolmanager.h> #include <coreplugin/foldernavigationwidget.h> #include <coreplugin/icore.h> @@ -38,12 +46,15 @@ #include <utils/fancylineedit.h> #include <utils/qtcassert.h> #include <utils/macroexpander.h> +#include <utils/utilsicons.h> + +#include <QMenu> using namespace Core; using namespace Utils; +using namespace TextEditor::Constants; -namespace TextEditor { -namespace Internal { +namespace TextEditor::Internal { const char kCurrentDocumentSelection[] = "CurrentDocument:Selection"; const char kCurrentDocumentRow[] = "CurrentDocument:Row"; @@ -56,13 +67,37 @@ const char kCurrentDocumentWordUnderCursor[] = "CurrentDocument:WordUnderCursor" class TextEditorPluginPrivate : public QObject { public: + TextEditorPluginPrivate(); + + void updateActions(bool enableToggle, int stateMask); + void editorOpened(Core::IEditor *editor); + void editorAboutToClose(Core::IEditor *editor); + + void requestContextMenu(TextEditorWidget *widget, int lineNumber, QMenu *menu); + void extensionsInitialized(); - void updateSearchResultsFont(const TextEditor::FontSettings &); - void updateSearchResultsTabWidth(const TextEditor::TabSettings &tabSettings); + void updateSearchResultsFont(const FontSettings &); + void updateSearchResultsTabWidth(const TabSettings &tabSettings); void updateCurrentSelection(const QString &text); void createStandardContextMenu(); + BookmarkManager m_bookmarkManager; + BookmarkFilter m_bookmarkFilter{&m_bookmarkManager}; + BookmarkViewFactory m_bookmarkViewFactory{&m_bookmarkManager}; + + QAction m_toggleAction{Tr::tr("Toggle Bookmark")}; + QAction m_editAction{Tr::tr("Edit Bookmark")}; + QAction m_prevAction{Tr::tr("Previous Bookmark")}; + QAction m_nextAction{Tr::tr("Next Bookmark")}; + QAction m_docPrevAction{Tr::tr("Previous Bookmark in Document")}; + QAction m_docNextAction{Tr::tr("Next Bookmark in Document")}; + QAction m_editBookmarkAction{Tr::tr("Edit Bookmark")}; + QAction m_bookmarkMarginAction{Tr::tr("Toggle Bookmark")}; + + int m_marginActionLineNumber = 0; + FilePath m_marginActionFileName; + TextEditorSettings settings; LineNumberFilter lineNumberFilter; // Goto line functionality for quick open OutlineFactory outlineFactory; @@ -73,8 +108,157 @@ public: PlainTextEditorFactory plainTextEditorFactory; MarkdownEditorFactory markdownEditorFactory; + JsonEditorFactory jsonEditorFactory; }; +TextEditorPluginPrivate::TextEditorPluginPrivate() +{ + ActionContainer *mtools = ActionManager::actionContainer(Core::Constants::M_TOOLS); + ActionContainer *touchBar = ActionManager::actionContainer(Core::Constants::TOUCH_BAR); + ActionContainer *mbm = ActionManager::createMenu(Id("Bookmarks.Menu")); + + mbm->menu()->setTitle(Tr::tr("&Bookmarks")); + mtools->addMenu(mbm); + + const Context editorManagerContext(Core::Constants::C_EDITORMANAGER); + + // Toggle + Command *cmd = ActionManager::registerAction(&m_toggleAction, "Bookmarks.Toggle", + editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M"))); + cmd->setTouchBarIcon(Icons::MACOS_TOUCHBAR_BOOKMARK.icon()); + mbm->addAction(cmd); + touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR); + + cmd = ActionManager::registerAction(&m_editAction, "Bookmarks.Edit", editorManagerContext); + cmd->setDefaultKeySequence( + QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M"))); + mbm->addAction(cmd); + + mbm->addSeparator(); + + // Previous + m_prevAction.setIcon(Icons::PREV_TOOLBAR.icon()); + m_prevAction.setIconVisibleInMenu(false); + cmd = ActionManager::registerAction(&m_prevAction, BOOKMARKS_PREV_ACTION, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+,") + : Tr::tr("Ctrl+,"))); + mbm->addAction(cmd); + + // Next + m_nextAction.setIcon(Icons::NEXT_TOOLBAR.icon()); + m_nextAction.setIconVisibleInMenu(false); + cmd = ActionManager::registerAction(&m_nextAction, BOOKMARKS_NEXT_ACTION, editorManagerContext); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+.") + : Tr::tr("Ctrl+."))); + mbm->addAction(cmd); + + mbm->addSeparator(); + + // Previous Doc + cmd = ActionManager::registerAction(&m_docPrevAction, "Bookmarks.PreviousDocument", + editorManagerContext); + mbm->addAction(cmd); + + // Next Doc + cmd = ActionManager::registerAction(&m_docNextAction, "Bookmarks.NextDocument", + editorManagerContext); + mbm->addAction(cmd); + + connect(&m_toggleAction, &QAction::triggered, this, [this] { + IEditor *editor = EditorManager::currentEditor(); + auto widget = TextEditorWidget::fromEditor(editor); + if (widget && editor && !editor->document()->isTemporary()) + m_bookmarkManager.toggleBookmark(editor->document()->filePath(), editor->currentLine()); + }); + + connect(&m_editAction, &QAction::triggered, this, [this] { + IEditor *editor = EditorManager::currentEditor(); + auto widget = TextEditorWidget::fromEditor(editor); + if (widget && editor && !editor->document()->isTemporary()) { + const FilePath filePath = editor->document()->filePath(); + const int line = editor->currentLine(); + if (!m_bookmarkManager.hasBookmarkInPosition(filePath, line)) + m_bookmarkManager.toggleBookmark(filePath, line); + m_bookmarkManager.editByFileAndLine(filePath, line); + } + }); + + connect(&m_prevAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::prev); + connect(&m_nextAction, &QAction::triggered, &m_bookmarkManager, &BookmarkManager::next); + connect(&m_docPrevAction, &QAction::triggered, + &m_bookmarkManager, &BookmarkManager::prevInDocument); + connect(&m_docNextAction, &QAction::triggered, + &m_bookmarkManager, &BookmarkManager::nextInDocument); + + connect(&m_editBookmarkAction, &QAction::triggered, this, [this] { + m_bookmarkManager.editByFileAndLine(m_marginActionFileName, m_marginActionLineNumber); + }); + + connect(&m_bookmarkManager, &BookmarkManager::updateActions, + this, &TextEditorPluginPrivate::updateActions); + updateActions(false, m_bookmarkManager.state()); + + connect(&m_bookmarkMarginAction, &QAction::triggered, this, [this] { + m_bookmarkManager.toggleBookmark(m_marginActionFileName, m_marginActionLineNumber); + }); + + // EditorManager + connect(EditorManager::instance(), &EditorManager::editorAboutToClose, + this, &TextEditorPluginPrivate::editorAboutToClose); + connect(EditorManager::instance(), &EditorManager::editorOpened, + this, &TextEditorPluginPrivate::editorOpened); +} + +void TextEditorPluginPrivate::updateActions(bool enableToggle, int state) +{ + const bool hasbm = state >= BookmarkManager::HasBookMarks; + const bool hasdocbm = state == BookmarkManager::HasBookmarksInDocument; + + m_toggleAction.setEnabled(enableToggle); + m_editAction.setEnabled(enableToggle); + m_prevAction.setEnabled(hasbm); + m_nextAction.setEnabled(hasbm); + m_docPrevAction.setEnabled(hasdocbm); + m_docNextAction.setEnabled(hasdocbm); +} + +void TextEditorPluginPrivate::editorOpened(IEditor *editor) +{ + if (auto widget = TextEditorWidget::fromEditor(editor)) { + connect(widget, &TextEditorWidget::markRequested, + this, [this, editor](TextEditorWidget *, int line, TextMarkRequestKind kind) { + if (kind == BookmarkRequest && !editor->document()->isTemporary()) + m_bookmarkManager.toggleBookmark(editor->document()->filePath(), line); + }); + + connect(widget, &TextEditorWidget::markContextMenuRequested, + this, &TextEditorPluginPrivate::requestContextMenu); + } +} + +void TextEditorPluginPrivate::editorAboutToClose(IEditor *editor) +{ + if (auto widget = TextEditorWidget::fromEditor(editor)) { + disconnect(widget, &TextEditorWidget::markContextMenuRequested, + this, &TextEditorPluginPrivate::requestContextMenu); + } +} + +void TextEditorPluginPrivate::requestContextMenu(TextEditorWidget *widget, + int lineNumber, QMenu *menu) +{ + if (widget->textDocument()->isTemporary()) + return; + + m_marginActionLineNumber = lineNumber; + m_marginActionFileName = widget->textDocument()->filePath(); + + menu->addAction(&m_bookmarkMarginAction); + if (m_bookmarkManager.hasBookmarkInPosition(m_marginActionFileName, m_marginActionLineNumber)) + menu->addAction(&m_editBookmarkAction); +} + static TextEditorPlugin *m_instance = nullptr; TextEditorPlugin::TextEditorPlugin() @@ -110,9 +294,9 @@ void TextEditorPlugin::initialize() editor->editorWidget()->invokeAssist(Completion); }); connect(command, &Command::keySequenceChanged, [command] { - Utils::FancyLineEdit::setCompletionShortcut(command->keySequence()); + FancyLineEdit::setCompletionShortcut(command->keySequence()); }); - Utils::FancyLineEdit::setCompletionShortcut(command->keySequence()); + FancyLineEdit::setCompletionShortcut(command->keySequence()); // Add shortcut for invoking function hint completion QAction *functionHintAction = new QAction(Tr::tr("Display Function Hint"), this); @@ -160,7 +344,7 @@ void TextEditorPluginPrivate::extensionsInitialized() &FolderNavigationWidgetFactory::aboutToShowContextMenu, this, [](QMenu *menu, const FilePath &filePath, bool isDir) { if (!isDir && Core::DiffService::instance()) { - menu->addAction(TextEditor::TextDocument::createDiffAgainstCurrentFileAction( + menu->addAction(TextDocument::createDiffAgainstCurrentFileAction( menu, [filePath] { return filePath; })); } }); @@ -185,7 +369,7 @@ void TextEditorPlugin::extensionsInitialized() { d->extensionsInitialized(); - Utils::MacroExpander *expander = Utils::globalMacroExpander(); + MacroExpander *expander = Utils::globalMacroExpander(); expander->registerVariable(kCurrentDocumentSelection, Tr::tr("Selected text within the current document."), @@ -334,5 +518,4 @@ void TextEditorPluginPrivate::createStandardContextMenu() add(Constants::SWITCH_UTF8BOM, Constants::G_BOM); } -} // namespace Internal -} // namespace TextEditor +} // namespace TextEditor::Internal diff --git a/src/plugins/texteditor/texteditorsettings.cpp b/src/plugins/texteditor/texteditorsettings.cpp index 5e99fcf582f..d2ac16eb65d 100644 --- a/src/plugins/texteditor/texteditorsettings.cpp +++ b/src/plugins/texteditor/texteditorsettings.cpp @@ -5,6 +5,7 @@ #include "behaviorsettings.h" #include "behaviorsettingspage.h" +#include "commentssettings.h" #include "completionsettings.h" #include "completionsettingspage.h" #include "displaysettings.h" @@ -47,6 +48,7 @@ public: HighlighterSettingsPage m_highlighterSettingsPage; SnippetsSettingsPage m_snippetsSettingsPage; CompletionSettingsPage m_completionSettingsPage; + CommentsSettingsPage m_commentsSettingsPage; QMap<Utils::Id, ICodeStylePreferencesFactory *> m_languageToFactory; @@ -54,6 +56,8 @@ public: QMap<Utils::Id, CodeStylePool *> m_languageToCodeStylePool; QMap<QString, Utils::Id> m_mimeTypeToLanguage; + std::function<CommentsSettings::Data(const Utils::FilePath &)> m_retrieveCommentsSettings; + private: static std::vector<FormatDescription> initialFormats(); }; @@ -501,9 +505,16 @@ const ExtraEncodingSettings &TextEditorSettings::extraEncodingSettings() return d->m_behaviorSettingsPage.extraEncodingSettings(); } -const CommentsSettings &TextEditorSettings::commentsSettings() +void TextEditorSettings::setCommentsSettingsRetriever( + const std::function<CommentsSettings::Data(const Utils::FilePath &)> &retrieve) { - return d->m_completionSettingsPage.commentsSettings(); + d->m_retrieveCommentsSettings = retrieve; +} + +CommentsSettings::Data TextEditorSettings::commentsSettings(const Utils::FilePath &filePath) +{ + QTC_ASSERT(d->m_retrieveCommentsSettings, return CommentsSettings::data()); + return d->m_retrieveCommentsSettings(filePath); } void TextEditorSettings::registerCodeStyleFactory(ICodeStylePreferencesFactory *factory) @@ -583,7 +594,6 @@ Utils::Id TextEditorSettings::languageId(const QString &mimeType) static void setFontZoom(int zoom) { - d->m_fontSettingsPage.setFontZoom(zoom); d->m_fontSettings.setFontZoom(zoom); d->m_fontSettings.toSettings(Core::ICore::settings()); emit m_instance->fontSettingsChanged(d->m_fontSettings); diff --git a/src/plugins/texteditor/texteditorsettings.h b/src/plugins/texteditor/texteditorsettings.h index 62f36980ef4..f5ebccc0cd1 100644 --- a/src/plugins/texteditor/texteditorsettings.h +++ b/src/plugins/texteditor/texteditorsettings.h @@ -3,12 +3,15 @@ #pragma once +#include "commentssettings.h" #include "texteditor_global.h" #include <utils/id.h> #include <QObject> +#include <functional> + QT_BEGIN_NAMESPACE template <typename Key, typename T> class QMap; @@ -54,7 +57,10 @@ public: static const CompletionSettings &completionSettings(); static const HighlighterSettings &highlighterSettings(); static const ExtraEncodingSettings &extraEncodingSettings(); - static const CommentsSettings &commentsSettings(); + + static void setCommentsSettingsRetriever( + const std::function<CommentsSettings::Data(const Utils::FilePath &)> &); + static CommentsSettings::Data commentsSettings(const Utils::FilePath &filePath); static ICodeStylePreferencesFactory *codeStyleFactory(Utils::Id languageId); static const QMap<Utils::Id, ICodeStylePreferencesFactory *> &codeStyleFactories(); @@ -86,7 +92,7 @@ signals: void displaySettingsChanged(const TextEditor::DisplaySettings &); void completionSettingsChanged(const TextEditor::CompletionSettings &); void extraEncodingSettingsChanged(const TextEditor::ExtraEncodingSettings &); - void commentsSettingsChanged(const TextEditor::CommentsSettings &); + void commentsSettingsChanged(); }; } // namespace TextEditor diff --git a/src/plugins/texteditor/typingsettings.cpp b/src/plugins/texteditor/typingsettings.cpp index e5db1a7129f..3f9a42f9f7f 100644 --- a/src/plugins/texteditor/typingsettings.cpp +++ b/src/plugins/texteditor/typingsettings.cpp @@ -3,7 +3,8 @@ #include "typingsettings.h" -#include <utils/settingsutils.h> +#include <coreplugin/icore.h> + #include <QTextCursor> #include <QTextDocument> @@ -11,8 +12,9 @@ static const char autoIndentKey[] = "AutoIndent"; static const char tabKeyBehaviorKey[] = "TabKeyBehavior"; static const char smartBackspaceBehaviorKey[] = "SmartBackspaceBehavior"; static const char preferSingleLineCommentsKey[] = "PreferSingleLineComments"; -static const char groupPostfix[] = "TypingSettings"; +static const char preferAfterWhitespaceCommentsKey[] = "PreferAfterWhitespaceComments"; +using namespace Utils; namespace TextEditor { @@ -24,28 +26,18 @@ TypingSettings::TypingSettings(): { } -void TypingSettings::toSettings(const QString &category, QSettings *s) const -{ - Utils::toSettings(QLatin1String(groupPostfix), category, s, this); -} - -void TypingSettings::fromSettings(const QString &category, QSettings *s) -{ - *this = TypingSettings(); // Assign defaults - Utils::fromSettings(QLatin1String(groupPostfix), category, s, this); -} - -QVariantMap TypingSettings::toMap() const +Store TypingSettings::toMap() const { return { {autoIndentKey, m_autoIndent}, {tabKeyBehaviorKey, m_tabKeyBehavior}, {smartBackspaceBehaviorKey, m_smartBackspaceBehavior}, - {preferSingleLineCommentsKey, m_preferSingleLineComments} + {preferSingleLineCommentsKey, m_preferSingleLineComments}, + {preferAfterWhitespaceCommentsKey, m_commentPosition} }; } -void TypingSettings::fromMap(const QVariantMap &map) +void TypingSettings::fromMap(const Store &map) { m_autoIndent = map.value(autoIndentKey, m_autoIndent).toBool(); m_tabKeyBehavior = (TabKeyBehavior) map.value(tabKeyBehaviorKey, m_tabKeyBehavior).toInt(); @@ -53,14 +45,19 @@ void TypingSettings::fromMap(const QVariantMap &map) smartBackspaceBehaviorKey, m_smartBackspaceBehavior).toInt(); m_preferSingleLineComments = map.value(preferSingleLineCommentsKey, m_preferSingleLineComments).toBool(); + m_commentPosition = CommentPosition( + std::clamp(map.value(preferAfterWhitespaceCommentsKey, m_commentPosition).toInt(), + int(Automatic), + int(AfterWhitespace))); } bool TypingSettings::equals(const TypingSettings &ts) const { return m_autoIndent == ts.m_autoIndent - && m_tabKeyBehavior == ts.m_tabKeyBehavior - && m_smartBackspaceBehavior == ts.m_smartBackspaceBehavior - && m_preferSingleLineComments == ts.m_preferSingleLineComments; + && m_tabKeyBehavior == ts.m_tabKeyBehavior + && m_smartBackspaceBehavior == ts.m_smartBackspaceBehavior + && m_preferSingleLineComments == ts.m_preferSingleLineComments + && m_commentPosition == ts.m_commentPosition; } bool TypingSettings::tabShouldIndent(const QTextDocument *document, diff --git a/src/plugins/texteditor/typingsettings.h b/src/plugins/texteditor/typingsettings.h index 5af7ff3a413..283a665f52c 100644 --- a/src/plugins/texteditor/typingsettings.h +++ b/src/plugins/texteditor/typingsettings.h @@ -5,10 +5,9 @@ #include "texteditor_global.h" -#include <QVariantMap> +#include <utils/store.h> QT_BEGIN_NAMESPACE -class QSettings; class QTextDocument; class QTextCursor; QT_END_NAMESPACE @@ -32,15 +31,18 @@ public: BackspaceUnindents = 2 }; + enum CommentPosition { + Automatic = 0, + StartOfLine = 1, + AfterWhitespace = 2, + }; + TypingSettings(); bool tabShouldIndent(const QTextDocument *document, const QTextCursor &cursor, int *suggestedPosition) const; - void toSettings(const QString &category, QSettings *s) const; - void fromSettings(const QString &category, QSettings *s); - - QVariantMap toMap() const; - void fromMap(const QVariantMap &map); + Utils::Store toMap() const; + void fromMap(const Utils::Store &map); bool equals(const TypingSettings &ts) const; @@ -52,6 +54,7 @@ public: SmartBackspaceBehavior m_smartBackspaceBehavior; bool m_preferSingleLineComments; + CommentPosition m_commentPosition = Automatic; }; } // namespace TextEditor diff --git a/src/plugins/todo/Todo.json.in b/src/plugins/todo/Todo.json.in index 5bc157f3722..2b6227492e8 100644 --- a/src/plugins/todo/Todo.json.in +++ b/src/plugins/todo/Todo.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Todo\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"Vendor\" : \"Dmitry Savchenko\", - \"Copyright\" : \"(C) 2016 Dmitry Savchenko, Vasiliy Sorokin, (C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Todo", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "Vendor" : "Dmitry Savchenko", + "Copyright" : "(C) 2016 Dmitry Savchenko, Vasiliy Sorokin, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Adds pane that lists all TODO, FIXME, etc. entries in comments.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Adds pane that lists all TODO, FIXME, etc. entries in comments.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/todo/lineparser.cpp b/src/plugins/todo/lineparser.cpp index 2dace7a4df2..334bccfae08 100644 --- a/src/plugins/todo/lineparser.cpp +++ b/src/plugins/todo/lineparser.cpp @@ -79,7 +79,7 @@ QList<LineParser::KeywordEntry> LineParser::keywordEntriesFromCandidates( { // Ensure something is found if (candidates.isEmpty()) - return QList<KeywordEntry>(); + return {}; // Convert candidates to entries std::vector<KeywordEntry> tmp; diff --git a/src/plugins/todo/settings.cpp b/src/plugins/todo/settings.cpp index d6def9c58e8..d5aafadd37f 100644 --- a/src/plugins/todo/settings.cpp +++ b/src/plugins/todo/settings.cpp @@ -6,14 +6,16 @@ #include "constants.h" #include <coreplugin/coreconstants.h> + +#include <utils/qtcsettings.h> #include <utils/theme/theme.h> -#include <QSettings> +using namespace Utils; namespace Todo { namespace Internal { -void Settings::save(QSettings *settings) const +void Settings::save(QtcSettings *settings) const { if (!keywordsEdited) return; @@ -23,9 +25,9 @@ void Settings::save(QSettings *settings) const settings->beginWriteArray(Constants::KEYWORDS_LIST); if (const int size = keywords.size()) { - const QString nameKey = "name"; - const QString colorKey = "color"; - const QString iconTypeKey = "iconType"; + const Key nameKey = "name"; + const Key colorKey = "color"; + const Key iconTypeKey = "iconType"; for (int i = 0; i < size; ++i) { settings->setArrayIndex(i); settings->setValue(nameKey, keywords.at(i).name); @@ -39,7 +41,7 @@ void Settings::save(QSettings *settings) const settings->sync(); } -void Settings::load(QSettings *settings) +void Settings::load(QtcSettings *settings) { setDefault(); @@ -53,9 +55,9 @@ void Settings::load(QSettings *settings) KeywordList newKeywords; const int keywordsSize = settings->beginReadArray(Constants::KEYWORDS_LIST); if (keywordsSize > 0) { - const QString nameKey = "name"; - const QString colorKey = "color"; - const QString iconTypeKey = "iconType"; + const Key nameKey = "name"; + const Key colorKey = "color"; + const Key iconTypeKey = "iconType"; for (int i = 0; i < keywordsSize; ++i) { settings->setArrayIndex(i); Keyword keyword; @@ -86,6 +88,11 @@ void Settings::setDefault() keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor); keywords.append(keyword); + keyword.name = R"(\todo)"; + keyword.iconType = IconType::Todo; + keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor); + keywords.append(keyword); + keyword.name = "NOTE"; keyword.iconType = IconType::Info; keyword.color = theme->color(Utils::Theme::OutputPanes_NormalMessageTextColor); diff --git a/src/plugins/todo/settings.h b/src/plugins/todo/settings.h index 0caae568e8f..534b1a1b0ca 100644 --- a/src/plugins/todo/settings.h +++ b/src/plugins/todo/settings.h @@ -6,7 +6,7 @@ #include "keyword.h" -QT_FORWARD_DECLARE_CLASS(QSettings) +namespace Utils { class QtcSettings; } namespace Todo { namespace Internal { @@ -18,14 +18,15 @@ enum ScanningScope { ScanningScopeMax }; -class Settings { +class Settings +{ public: KeywordList keywords; ScanningScope scanningScope = ScanningScopeCurrentFile; bool keywordsEdited = false; - void save(QSettings *settings) const; - void load(QSettings *settings); + void save(Utils::QtcSettings *settings) const; + void load(Utils::QtcSettings *settings); void setDefault(); bool equals(const Settings &other) const; }; diff --git a/src/plugins/todo/todoitemsprovider.cpp b/src/plugins/todo/todoitemsprovider.cpp index 02ba71e2fcc..3ef6b344afa 100644 --- a/src/plugins/todo/todoitemsprovider.cpp +++ b/src/plugins/todo/todoitemsprovider.cpp @@ -69,8 +69,8 @@ void TodoItemsProvider::updateList() // Show only items of the current file if any if (m_settings.scanningScope == ScanningScopeCurrentFile) { - if (m_currentEditor) - m_itemsList = m_itemsHash.value(m_currentEditor->document()->filePath()); + if (auto currentEditor = Core::EditorManager::currentEditor()) + m_itemsList = m_itemsHash.value(currentEditor->document()->filePath()); // Show only items of the current sub-project } else if (m_settings.scanningScope == ScanningScopeSubProject) { if (m_startupProject) @@ -164,9 +164,8 @@ void TodoItemsProvider::projectsFilesChanged() updateList(); } -void TodoItemsProvider::currentEditorChanged(Core::IEditor *editor) +void TodoItemsProvider::currentEditorChanged() { - m_currentEditor = editor; if (m_settings.scanningScope == ScanningScopeCurrentFile || m_settings.scanningScope == ScanningScopeSubProject) { updateList(); @@ -192,7 +191,6 @@ void TodoItemsProvider::setupStartupProjectBinding() void TodoItemsProvider::setupCurrentEditorBinding() { - m_currentEditor = Core::EditorManager::currentEditor(); connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, &TodoItemsProvider::currentEditorChanged); } diff --git a/src/plugins/todo/todoitemsprovider.h b/src/plugins/todo/todoitemsprovider.h index 2843d3df62a..1a7702b9eef 100644 --- a/src/plugins/todo/todoitemsprovider.h +++ b/src/plugins/todo/todoitemsprovider.h @@ -46,7 +46,6 @@ private: QList<TodoItemsScanner *> m_scanners; ProjectExplorer::Project *m_startupProject; - Core::IEditor* m_currentEditor; bool m_shouldUpdateList; @@ -63,7 +62,7 @@ private: void itemsFetched(const QString &fileName, const QList<TodoItem> &items); void startupProjectChanged(ProjectExplorer::Project *project); void projectsFilesChanged(); - void currentEditorChanged(Core::IEditor *editor); + void currentEditorChanged(); void updateListTimeoutElapsed(); }; diff --git a/src/plugins/todo/todooutputpane.cpp b/src/plugins/todo/todooutputpane.cpp index b8197d6b611..3f314e86a99 100644 --- a/src/plugins/todo/todooutputpane.cpp +++ b/src/plugins/todo/todooutputpane.cpp @@ -26,6 +26,10 @@ TodoOutputPane::TodoOutputPane(TodoItemsModel *todoItemsModel, const Settings *s m_todoItemsModel(todoItemsModel), m_settings(settings) { + setId("To-DoEntries"); + setDisplayName(Tr::tr("To-Do Entries")); + setPriorityInStatusBar(10); + createTreeView(); createScopeButtons(); setScanningScope(ScanningScopeCurrentFile); // default @@ -59,16 +63,6 @@ QList<QWidget*> TodoOutputPane::toolBarWidgets() const return widgets; } -QString TodoOutputPane::displayName() const -{ - return Tr::tr("To-Do Entries"); -} - -int TodoOutputPane::priorityInStatusBar() const -{ - return 1; -} - void TodoOutputPane::clearContents() { clearKeywordFilter(); diff --git a/src/plugins/todo/todooutputpane.h b/src/plugins/todo/todooutputpane.h index fa4dda3ddbe..f408a399362 100644 --- a/src/plugins/todo/todooutputpane.h +++ b/src/plugins/todo/todooutputpane.h @@ -35,8 +35,6 @@ public: QWidget *outputWidget(QWidget *parent) override; QList<QWidget*> toolBarWidgets() const override; - QString displayName() const override; - int priorityInStatusBar() const override; void clearContents() override; void setFocus() override; bool hasFocus() const override; diff --git a/src/plugins/todo/todooutputtreeview.cpp b/src/plugins/todo/todooutputtreeview.cpp index 5f20d381de1..a0519d59e94 100644 --- a/src/plugins/todo/todooutputtreeview.cpp +++ b/src/plugins/todo/todooutputtreeview.cpp @@ -7,9 +7,12 @@ #include <coreplugin/icore.h> +#include <utils/qtcsettings.h> + #include <QResizeEvent> #include <QHeaderView> -#include <QSettings> + +using namespace Utils; namespace Todo { namespace Internal { @@ -38,7 +41,7 @@ TodoOutputTreeView::~TodoOutputTreeView() void TodoOutputTreeView::saveDisplaySettings() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(Constants::SETTINGS_GROUP); settings->setValue(Constants::OUTPUT_PANE_TEXT_WIDTH, columnWidth(Constants::OUTPUT_COLUMN_TEXT)); @@ -49,7 +52,7 @@ void TodoOutputTreeView::saveDisplaySettings() void TodoOutputTreeView::loadDisplaySettings() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); settings->beginGroup(Constants::SETTINGS_GROUP); m_textColumnDefaultWidth = settings->value(Constants::OUTPUT_PANE_TEXT_WIDTH, 0).toInt(); m_fileColumnDefaultWidth = settings->value(Constants::OUTPUT_PANE_FILE_WIDTH, 0).toInt(); diff --git a/src/plugins/todo/todoplugin.cpp b/src/plugins/todo/todoplugin.cpp index 764217b76be..dfb0f866fb8 100644 --- a/src/plugins/todo/todoplugin.cpp +++ b/src/plugins/todo/todoplugin.cpp @@ -17,8 +17,6 @@ #include <projectexplorer/projectpanelfactory.h> #include <utils/link.h> -#include <QSettings> - namespace Todo { namespace Internal { diff --git a/src/plugins/updateinfo/CMakeLists.txt b/src/plugins/updateinfo/CMakeLists.txt index 2507f99964b..5049f351f0f 100644 --- a/src/plugins/updateinfo/CMakeLists.txt +++ b/src/plugins/updateinfo/CMakeLists.txt @@ -1,7 +1,6 @@ add_qtc_plugin(UpdateInfo DEPENDS Qt::Xml PLUGIN_DEPENDS Core - PLUGIN_JSON_IN UPDATEINFO_EXPERIMENTAL_STR=true SOURCES settingspage.cpp settingspage.h diff --git a/src/plugins/updateinfo/UpdateInfo.json.in b/src/plugins/updateinfo/UpdateInfo.json.in index 5331281feaf..b7f15e449ba 100644 --- a/src/plugins/updateinfo/UpdateInfo.json.in +++ b/src/plugins/updateinfo/UpdateInfo.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"UpdateInfo\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"DisabledByDefault\" : $$UPDATEINFO_EXPERIMENTAL_STR, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "UpdateInfo", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Description\" : \"Displays Update-Infos for Qt Installer Framework-based Updaters.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Description" : "Displays Update-Infos for Qt Installer Framework-based Updaters.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/updateinfo/updateinfo.qbs b/src/plugins/updateinfo/updateinfo.qbs index fc294e2a132..cdef4fc5105 100644 --- a/src/plugins/updateinfo/updateinfo.qbs +++ b/src/plugins/updateinfo/updateinfo.qbs @@ -9,7 +9,7 @@ QtcPlugin { Depends { name: "Core" } property bool enable: false - pluginJsonReplacements: ({"UPDATEINFO_EXPERIMENTAL_STR": (enable ? "false": "true")}) + pluginjson.replacements: ({"UPDATEINFO_EXPERIMENTAL_STR": (enable ? "false": "true")}) files: [ "settingspage.cpp", diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index 985817720a1..1b631cb82cf 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -118,8 +118,6 @@ void UpdateInfoPlugin::startCheckForUpdates() emit checkForUpdatesRunningChanged(true); - using namespace Tasking; - const auto doSetup = [this](Process &process, const QStringList &args) { process.setCommand({d->m_maintenanceTool, args}); process.setLowPriority(); @@ -340,8 +338,8 @@ bool UpdateInfoPlugin::initialize(const QStringList & /* arguments */, QString * void UpdateInfoPlugin::loadSettings() const { UpdateInfoPluginPrivate::Settings def; - QSettings *settings = ICore::settings(); - const QString updaterKey = QLatin1String(UpdaterGroup) + '/'; + QtcSettings *settings = ICore::settings(); + const Key updaterKey = Key(UpdaterGroup) + '/'; d->m_maintenanceTool = FilePath::fromSettings(settings->value(updaterKey + MaintenanceToolKey)); d->m_lastCheckDate = settings->value(updaterKey + LastCheckDateKey, QDate()).toDate(); d->m_settings.automaticCheck diff --git a/src/plugins/valgrind/CMakeLists.txt b/src/plugins/valgrind/CMakeLists.txt index 3e1f149730f..0efcd67c623 100644 --- a/src/plugins/valgrind/CMakeLists.txt +++ b/src/plugins/valgrind/CMakeLists.txt @@ -24,25 +24,21 @@ add_qtc_plugin(Valgrind callgrindvisualisation.cpp callgrindvisualisation.h memcheckerrorview.cpp memcheckerrorview.h memchecktool.cpp memchecktool.h - suppressiondialog.cpp suppressiondialog.h valgrind.qrc - valgrindconfigwidget.cpp valgrindconfigwidget.h valgrindengine.cpp valgrindengine.h valgrindplugin.cpp - valgrindrunner.cpp valgrindrunner.h + valgrindprocess.cpp valgrindprocess.h valgrindsettings.cpp valgrindsettings.h valgrindtr.h xmlprotocol/announcethread.cpp xmlprotocol/announcethread.h xmlprotocol/error.cpp xmlprotocol/error.h xmlprotocol/errorlistmodel.cpp xmlprotocol/errorlistmodel.h xmlprotocol/frame.cpp xmlprotocol/frame.h - xmlprotocol/modelhelpers.cpp xmlprotocol/modelhelpers.h xmlprotocol/parser.cpp xmlprotocol/parser.h xmlprotocol/stack.cpp xmlprotocol/stack.h xmlprotocol/stackmodel.cpp xmlprotocol/stackmodel.h xmlprotocol/status.cpp xmlprotocol/status.h xmlprotocol/suppression.cpp xmlprotocol/suppression.h - xmlprotocol/threadedparser.cpp xmlprotocol/threadedparser.h ) extend_qtc_plugin(Valgrind diff --git a/src/plugins/valgrind/Valgrind.json.in b/src/plugins/valgrind/Valgrind.json.in index 23b822f2422..1f270b12184 100644 --- a/src/plugins/valgrind/Valgrind.json.in +++ b/src/plugins/valgrind/Valgrind.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"Valgrind\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Valgrind", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Code Analyzer\", - \"Description\" : \"Valgrind Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Code Analyzer", + "Description" : "Valgrind Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/valgrind/callgrind/callgrindcallmodel.cpp b/src/plugins/valgrind/callgrind/callgrindcallmodel.cpp index 31c00067f13..b341e29d5d1 100644 --- a/src/plugins/valgrind/callgrind/callgrindcallmodel.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcallmodel.cpp @@ -10,7 +10,7 @@ #include <utils/qtcassert.h> -#include <QVector> +#include <QList> namespace Valgrind::Callgrind { @@ -18,7 +18,7 @@ class CallModel::Private { public: const ParseData *m_data = nullptr; - QVector<const FunctionCall *> m_calls; + QList<const FunctionCall *> m_calls; int m_event = 0; const Function *m_function = nullptr; }; @@ -41,7 +41,7 @@ void CallModel::clear() endResetModel(); } -void CallModel::setCalls(const QVector<const FunctionCall *> &calls, const Function *function) +void CallModel::setCalls(const QList<const FunctionCall *> &calls, const Function *function) { beginResetModel(); d->m_function = function; @@ -49,7 +49,7 @@ void CallModel::setCalls(const QVector<const FunctionCall *> &calls, const Funct endResetModel(); } -QVector<const FunctionCall *> CallModel::calls() const +QList<const FunctionCall *> CallModel::calls() const { return d->m_calls; } diff --git a/src/plugins/valgrind/callgrind/callgrindcallmodel.h b/src/plugins/valgrind/callgrind/callgrindcallmodel.h index 8a45671de67..4b8e6f631e4 100644 --- a/src/plugins/valgrind/callgrind/callgrindcallmodel.h +++ b/src/plugins/valgrind/callgrind/callgrindcallmodel.h @@ -32,8 +32,8 @@ public: virtual void setParseData(const ParseData *data); virtual const ParseData *parseData() const; - void setCalls(const QVector<const FunctionCall *> &calls, const Function *function); - QVector<const FunctionCall *> calls() const; + void setCalls(const QList<const FunctionCall *> &calls, const Function *function); + QList<const FunctionCall *> calls() const; const Function *function() const; int rowCount(const QModelIndex &parent = QModelIndex()) const override; diff --git a/src/plugins/valgrind/callgrind/callgrindcostitem.cpp b/src/plugins/valgrind/callgrind/callgrindcostitem.cpp index 3bbf71fc419..d6fa4f99013 100644 --- a/src/plugins/valgrind/callgrind/callgrindcostitem.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcostitem.cpp @@ -6,9 +6,7 @@ #include "callgrindparsedata.h" #include "callgrindfunctioncall.h" -#include <QString> #include <QStringList> -#include <QVector> namespace Valgrind::Callgrind { @@ -18,8 +16,8 @@ public: Private(ParseData *data); ~Private(); - QVector<quint64> m_positions; - QVector<quint64> m_events; + QList<quint64> m_positions; + QList<quint64> m_events; const FunctionCall *m_call = nullptr; const ParseData *m_data = nullptr; @@ -60,7 +58,7 @@ void CostItem::setPosition(int posIdx, quint64 position) d->m_positions[posIdx] = position; } -QVector< quint64 > CostItem::positions() const +QList<quint64> CostItem::positions() const { return d->m_positions; } @@ -75,7 +73,7 @@ void CostItem::setCost(int event, quint64 cost) d->m_events[event] = cost; } -QVector< quint64 > CostItem::costs() const +QList<quint64> CostItem::costs() const { return d->m_events; } diff --git a/src/plugins/valgrind/callgrind/callgrindcostitem.h b/src/plugins/valgrind/callgrind/callgrindcostitem.h index 3b69d688925..3cc54df1dab 100644 --- a/src/plugins/valgrind/callgrind/callgrindcostitem.h +++ b/src/plugins/valgrind/callgrind/callgrindcostitem.h @@ -3,7 +3,7 @@ #pragma once -#include <QVector> +#include <QList> namespace Valgrind::Callgrind { @@ -27,7 +27,7 @@ public: */ quint64 position(int posIdx) const; void setPosition(int posIdx, quint64 position); - QVector<quint64> positions() const; + QList<quint64> positions() const; /** * Cost data for the given event-index @p event @@ -35,7 +35,7 @@ public: */ quint64 cost(int event) const; void setCost(int event, quint64 cost); - QVector<quint64> costs() const; + QList<quint64> costs() const; /** * If this cost item represents a function call, this will return the @c Callee. diff --git a/src/plugins/valgrind/callgrind/callgrindcycledetection.cpp b/src/plugins/valgrind/callgrind/callgrindcycledetection.cpp index ef8ab008091..68f6964c344 100644 --- a/src/plugins/valgrind/callgrind/callgrindcycledetection.cpp +++ b/src/plugins/valgrind/callgrind/callgrindcycledetection.cpp @@ -5,13 +5,11 @@ #include "callgrindfunction.h" #include "callgrindfunctioncall.h" -#include "callgrindparsedata.h" #include "callgrindfunctioncycle.h" +#include "callgrindparsedata.h" #include <utils/qtcassert.h> -#include <QDebug> - namespace Valgrind::Callgrind::Internal { CycleDetection::CycleDetection(ParseData *data) @@ -19,7 +17,7 @@ CycleDetection::CycleDetection(ParseData *data) { } -QVector<const Function *> CycleDetection::run(const QVector<const Function *> &input) +QList<const Function *> CycleDetection::run(const QList<const Function *> &input) { for (const Function *function : input) { Node *node = new Node; @@ -45,12 +43,12 @@ void CycleDetection::tarjan(Node *node) m_depth++; m_stack.push(node); - const QVector<const FunctionCall *> calls = node->function->outgoingCalls(); + const QList<const FunctionCall *> calls = node->function->outgoingCalls(); for (const FunctionCall *call : calls) tarjanForChildNode(node, m_nodes.value(call->callee())); if (node->dfs == node->lowlink) { - QVector<const Function *> functions; + QList<const Function *> functions; Node *n; do { n = m_stack.pop(); diff --git a/src/plugins/valgrind/callgrind/callgrindcycledetection.h b/src/plugins/valgrind/callgrind/callgrindcycledetection.h index 68eead96083..f38412fe549 100644 --- a/src/plugins/valgrind/callgrind/callgrindcycledetection.h +++ b/src/plugins/valgrind/callgrind/callgrindcycledetection.h @@ -25,7 +25,7 @@ class CycleDetection { public: explicit CycleDetection(ParseData *data); - QVector<const Function *> run(const QVector<const Function *> &input); + QList<const Function *> run(const QList<const Function *> &input); private: ParseData *m_data; @@ -41,7 +41,7 @@ private: QHash<const Function *, Node *> m_nodes; QStack<Node *> m_stack; - QVector<const Function *> m_ret; + QList<const Function *> m_ret; int m_depth = 0; int m_cycle = 0; diff --git a/src/plugins/valgrind/callgrind/callgrinddatamodel.cpp b/src/plugins/valgrind/callgrind/callgrinddatamodel.cpp index d5e9f370421..a623a6a8c98 100644 --- a/src/plugins/valgrind/callgrind/callgrinddatamodel.cpp +++ b/src/plugins/valgrind/callgrind/callgrinddatamodel.cpp @@ -11,11 +11,7 @@ #include <utils/algorithm.h> #include <utils/qtcassert.h> -#include <QChar> -#include <QDebug> #include <QStringList> -#include <QTextDocument> -#include <QVector> namespace Valgrind { namespace Callgrind { @@ -30,7 +26,7 @@ public: bool m_verboseToolTips = true; bool m_cycleDetection = false; bool m_shortenTemplates = false; - QVector<const Function *> m_functions; + QList<const Function *> m_functions; }; void DataModel::Private::updateFunctions() diff --git a/src/plugins/valgrind/callgrind/callgrinddatamodel.h b/src/plugins/valgrind/callgrind/callgrinddatamodel.h index 89aba60fb97..136ea4a0d7f 100644 --- a/src/plugins/valgrind/callgrind/callgrinddatamodel.h +++ b/src/plugins/valgrind/callgrind/callgrinddatamodel.h @@ -7,8 +7,7 @@ #include <QAbstractItemModel> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class Function; class ParseData; @@ -69,5 +68,4 @@ private: Private *d; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindfunction.cpp b/src/plugins/valgrind/callgrind/callgrindfunction.cpp index ef10ec2ed98..a0d63300fd2 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunction.cpp +++ b/src/plugins/valgrind/callgrind/callgrindfunction.cpp @@ -2,23 +2,18 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "callgrindfunction.h" -#include "callgrindfunction_p.h" #include "callgrindcostitem.h" +#include "callgrindfunction_p.h" #include "callgrindfunctioncall.h" #include "callgrindparsedata.h" #include "../valgrindtr.h" #include <utils/qtcassert.h> -#include <QString> -#include <QStringList> -#include <QDebug> #include <QFileInfo> -#include <QCoreApplication> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { //BEGIN Function::Private @@ -39,7 +34,7 @@ Function::Private::~Private() qDeleteAll(m_outgoingCalls); } -void Function::Private::accumulateCost(QVector<quint64> &base, const QVector<quint64> &add) +void Function::Private::accumulateCost(QList<quint64> &base, const QList<quint64> &add) { if (base.isEmpty()) { base = add; @@ -72,7 +67,7 @@ FunctionCall *Function::Private::accumulateCall(const FunctionCall *call, CallTy accumulatedCall->setCosts(call->costs()); } else { - QVector<quint64> costs = accumulatedCall->costs(); + QList<quint64> costs = accumulatedCall->costs(); accumulateCost(costs, call->costs()); accumulatedCall->setCosts(costs); } @@ -206,7 +201,7 @@ quint64 Function::selfCost(int event) const return d->m_selfCost.at(event); } -QVector< quint64 > Function::selfCosts() const +QList<quint64> Function::selfCosts() const { return d->m_selfCost; } @@ -216,7 +211,7 @@ quint64 Function::inclusiveCost(int event) const return d->m_inclusiveCost.at(event) + d->m_selfCost.at(event); } -QVector<const FunctionCall *> Function::outgoingCalls() const +QList<const FunctionCall *> Function::outgoingCalls() const { return d->m_outgoingCalls; } @@ -228,7 +223,7 @@ void Function::addOutgoingCall(const FunctionCall *call) d->accumulateCall(call, Private::Outgoing); } -QVector<const FunctionCall *> Function::incomingCalls() const +QList<const FunctionCall *> Function::incomingCalls() const { return d->m_incomingCalls; } @@ -245,7 +240,7 @@ quint64 Function::called() const return d->m_called; } -QVector<const CostItem *> Function::costItems() const +QList<const CostItem *> Function::costItems() const { return d->m_costItems; } @@ -281,7 +276,7 @@ void Function::finalize() d->m_inclusiveCost.fill(0); for (const FunctionCall *call : std::as_const(d->m_incomingCalls)) { if (call->caller() != this) { - const QVector<const CostItem *> costItems = call->caller()->costItems(); + const QList<const CostItem *> costItems = call->caller()->costItems(); for (const CostItem *costItem : costItems) { if (costItem->call() && costItem->call()->callee() == this) d->accumulateCost(d->m_inclusiveCost, costItem->costs()); @@ -298,5 +293,4 @@ void Function::finalize() } } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindfunction.h b/src/plugins/valgrind/callgrind/callgrindfunction.h index 57d757cbbd6..a6d314189e5 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunction.h +++ b/src/plugins/valgrind/callgrind/callgrindfunction.h @@ -3,11 +3,10 @@ #pragma once +#include <QList> #include <QMetaType> -#include <QVector> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class FunctionCall; class CostItem; @@ -67,7 +66,7 @@ public: * @see ParseData::events() */ quint64 selfCost(int event) const; - QVector<quint64> selfCosts() const; + QList<quint64> selfCosts() const; /** * total accumulated inclusive cost of @p event @@ -76,7 +75,7 @@ public: quint64 inclusiveCost(int event) const; /// calls from other functions to this function - QVector<const FunctionCall *> incomingCalls() const; + QList<const FunctionCall *> incomingCalls() const; void addIncomingCall(const FunctionCall *call); /// @return how often this function was called in total quint64 called() const; @@ -86,7 +85,7 @@ public: * a detailed view of the function's source code annotated with * cost per line. */ - QVector<const CostItem *> costItems() const; + QList<const CostItem *> costItems() const; /** * Add parsed @c CostItem @p item to this function. @@ -98,7 +97,7 @@ public: /** * Function calls from this function to others. */ - QVector<const FunctionCall *> outgoingCalls() const; + QList<const FunctionCall *> outgoingCalls() const; void addOutgoingCall(const FunctionCall *call); /** @@ -118,7 +117,6 @@ private: Q_DISABLE_COPY(Function) }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind Q_DECLARE_METATYPE(const Valgrind::Callgrind::Function *) diff --git a/src/plugins/valgrind/callgrind/callgrindfunction_p.h b/src/plugins/valgrind/callgrind/callgrindfunction_p.h index bc648cc0249..8ea1ecf0324 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunction_p.h +++ b/src/plugins/valgrind/callgrind/callgrindfunction_p.h @@ -3,16 +3,15 @@ #pragma once -#include "callgrindfunction.h" -#include "callgrindparsedata.h" #include "callgrindcostitem.h" +#include "callgrindfunction.h" #include "callgrindfunctioncall.h" +#include "callgrindparsedata.h" -#include <QVector> #include <QHash> +#include <QList> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class Function::Private { @@ -20,7 +19,7 @@ public: Private(const ParseData *data); virtual ~Private(); - static void accumulateCost(QVector<quint64> &base, const QVector<quint64> &add); + static void accumulateCost(QList<quint64> &base, const QList<quint64> &add); enum CallType { Incoming, Outgoing @@ -33,18 +32,17 @@ public: qint64 m_objectId = -1; qint64 m_nameId = -1; - QVector<quint64> m_selfCost; - QVector<quint64> m_inclusiveCost; + QList<quint64> m_selfCost; + QList<quint64> m_inclusiveCost; - QVector<const CostItem *> m_costItems; + QList<const CostItem *> m_costItems; // used to accumulate, hence values not const QHash<const Function *, FunctionCall *> m_outgoingCallMap; QHash<const Function *, FunctionCall *> m_incomingCallMap; // used in public api, hence const - QVector<const FunctionCall *> m_outgoingCalls; - QVector<const FunctionCall *> m_incomingCalls; + QList<const FunctionCall *> m_outgoingCalls; + QList<const FunctionCall *> m_incomingCalls; quint64 m_called = 0; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindfunctioncall.cpp b/src/plugins/valgrind/callgrind/callgrindfunctioncall.cpp index 190edc23e10..a5ef8e8fabc 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunctioncall.cpp +++ b/src/plugins/valgrind/callgrind/callgrindfunctioncall.cpp @@ -7,10 +7,9 @@ #include <utils/qtcassert.h> -#include <QVector> +#include <QList> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { //BEGIN FunctionCall::Private class FunctionCall::Private @@ -20,8 +19,8 @@ public: const Function *m_caller = nullptr; quint64 m_calls = 0; quint64 m_totalInclusiveCost = 0; - QVector<quint64> m_destinations; - QVector<quint64> m_costs; + QList<quint64> m_destinations; + QList<quint64> m_costs; }; //BEGIN FunctionCall @@ -71,12 +70,12 @@ quint64 FunctionCall::destination(int posIdx) const return d->m_destinations.at(posIdx); } -QVector<quint64> FunctionCall::destinations() const +QList<quint64> FunctionCall::destinations() const { return d->m_destinations; } -void FunctionCall::setDestinations(const QVector<quint64> &destinations) +void FunctionCall::setDestinations(const QList<quint64> &destinations) { d->m_destinations = destinations; } @@ -87,15 +86,14 @@ quint64 FunctionCall::cost(int event) const return d->m_costs.at(event); } -QVector<quint64> FunctionCall::costs() const +QList<quint64> FunctionCall::costs() const { return d->m_costs; } -void FunctionCall::setCosts(const QVector<quint64> &costs) +void FunctionCall::setCosts(const QList<quint64> &costs) { d->m_costs = costs; } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindfunctioncall.h b/src/plugins/valgrind/callgrind/callgrindfunctioncall.h index 961e71cb624..60131d37c60 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunctioncall.h +++ b/src/plugins/valgrind/callgrind/callgrindfunctioncall.h @@ -4,10 +4,9 @@ #pragma once #include <QMetaType> -#include <QVector> +#include <QList> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class Function; @@ -37,16 +36,16 @@ public: * @see ParseData::positions() */ quint64 destination(int posIdx) const; - QVector<quint64> destinations() const; - void setDestinations(const QVector<quint64> &destinations); + QList<quint64> destinations() const; + void setDestinations(const QList<quint64> &destinations); /** * Inclusive cost of the function call. * @see ParseData::events() */ quint64 cost(int event) const; - QVector<quint64> costs() const; - void setCosts(const QVector<quint64> &costs); + QList<quint64> costs() const; + void setCosts(const QList<quint64> &costs); private: Q_DISABLE_COPY(FunctionCall) @@ -55,7 +54,6 @@ private: Private *d; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind Q_DECLARE_METATYPE(const Valgrind::Callgrind::FunctionCall *) diff --git a/src/plugins/valgrind/callgrind/callgrindfunctioncycle.cpp b/src/plugins/valgrind/callgrind/callgrindfunctioncycle.cpp index 92f4938b82e..6a92b13c553 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunctioncycle.cpp +++ b/src/plugins/valgrind/callgrind/callgrindfunctioncycle.cpp @@ -2,16 +2,12 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "callgrindfunctioncycle.h" -#include "callgrindfunction_p.h" +#include "callgrindfunction_p.h" #include "callgrindfunctioncall.h" #include "callgrindparsedata.h" -#include <QStringList> -#include <QDebug> - -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { //BEGIN FunctionCycle::Private @@ -19,7 +15,7 @@ class FunctionCycle::Private : public Function::Private { public: Private(const ParseData *data); - QVector<const Function *> m_functions; + QList<const Function *> m_functions; }; FunctionCycle::Private::Private(const ParseData *data) @@ -39,7 +35,7 @@ FunctionCycle::FunctionCycle(const ParseData *data) // d should be deleted by Function::~Function() FunctionCycle::~FunctionCycle() = default; -void FunctionCycle::setFunctions(const QVector<const Function *> &functions) +void FunctionCycle::setFunctions(const QList<const Function *> &functions) { Private *d = CYCLE_D; @@ -55,13 +51,13 @@ void FunctionCycle::setFunctions(const QVector<const Function *> &functions) // just add up self cost Private::accumulateCost(d->m_selfCost, func->selfCosts()); // add outgoing calls to functions that are not part of the cycle - const QVector<const FunctionCall *> calls = func->outgoingCalls(); + const QList<const FunctionCall *> calls = func->outgoingCalls(); for (const FunctionCall *call : calls) { if (!functions.contains(call->callee())) d->accumulateCall(call, Function::Private::Outgoing); } // add incoming calls from functions that are not part of the cycle - const QVector<const FunctionCall *> inCalls = func->incomingCalls(); + const QList<const FunctionCall *> inCalls = func->incomingCalls(); for (const FunctionCall *call : inCalls) { if (!functions.contains(call->caller())) { d->accumulateCall(call, Function::Private::Incoming); @@ -80,10 +76,9 @@ void FunctionCycle::setFunctions(const QVector<const Function *> &functions) } } -QVector<const Function *> FunctionCycle::functions() const +QList<const Function *> FunctionCycle::functions() const { return CYCLE_D->m_functions; } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindfunctioncycle.h b/src/plugins/valgrind/callgrind/callgrindfunctioncycle.h index d25b54a7660..8ba7a2fe71d 100644 --- a/src/plugins/valgrind/callgrind/callgrindfunctioncycle.h +++ b/src/plugins/valgrind/callgrind/callgrindfunctioncycle.h @@ -5,8 +5,7 @@ #include "callgrindfunction.h" -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { /** * self cost of a function cycle: sum of self costs of functions in the cycle @@ -24,13 +23,12 @@ public: /// sets the list of functions that make up this cycle /// NOTE: ownership is *not* transferred to the cycle - void setFunctions(const QVector<const Function *> &functions); + void setFunctions(const QList<const Function *> &functions); /// @return the functions that make up this cycle - QVector<const Function *> functions() const; + QList<const Function *> functions() const; private: class Private; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindparsedata.cpp b/src/plugins/valgrind/callgrind/callgrindparsedata.cpp index 0f6f8285f64..d58450b9684 100644 --- a/src/plugins/valgrind/callgrind/callgrindparsedata.cpp +++ b/src/plugins/valgrind/callgrind/callgrindparsedata.cpp @@ -3,20 +3,17 @@ #include "callgrindparsedata.h" -#include "callgrindfunction.h" #include "callgrindcycledetection.h" +#include "callgrindfunction.h" #include "callgrindfunctioncycle.h" #include "../valgrindtr.h" #include <utils/qtcassert.h> -#include <QStringList> -#include <QVector> #include <QHash> -#include <QCoreApplication> +#include <QStringList> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class ParseData::Private { @@ -32,8 +29,8 @@ public: QString m_fileName; QStringList m_events; QStringList m_positions; - QVector<quint64> m_totalCosts; - QVector<const Function *> m_functions; + QList<quint64> m_totalCosts; + QList<const Function *> m_functions; QString m_command; quint64 m_pid = 0; int m_lineNumberPositionIndex = -1; @@ -43,7 +40,7 @@ public: QStringList m_descriptions; QString m_creator; - QHash<qint64, QHash<qint64, QVector<Function *> > > functionLookup; + QHash<qint64, QHash<qint64, QList<Function *>>> functionLookup; using NameLookupTable = QHash<qint64, QString>; QString stringForCompression(const NameLookupTable &lookup, qint64 id); @@ -55,7 +52,7 @@ public: void cycleDetection(); void cleanupFunctionCycles(); - QVector<const Function *> m_cycleCache; + QList<const Function *> m_cycleCache; ParseData *m_q; }; @@ -233,7 +230,7 @@ void ParseData::setTotalCost(uint event, quint64 cost) d->m_totalCosts[event] = cost; } -QVector<const Function *> ParseData::functions(bool detectCycles) const +QList<const Function *> ParseData::functions(bool detectCycles) const { if (detectCycles) { d->cycleDetection(); @@ -343,5 +340,4 @@ void ParseData::addCompressedFunction(const QString &function, qint64 &id) d->addCompressedString(d->m_functionCompression, function, id); } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindparsedata.h b/src/plugins/valgrind/callgrind/callgrindparsedata.h index d74efa4b169..e6c7b0b8cb1 100644 --- a/src/plugins/valgrind/callgrind/callgrindparsedata.h +++ b/src/plugins/valgrind/callgrind/callgrindparsedata.h @@ -3,14 +3,13 @@ #pragma once -#include <QVector> +#include <QList> QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class Function; @@ -53,7 +52,7 @@ public: * * @return All functions that where reported in the data file. */ - QVector<const Function *> functions(bool detectCycles = false) const; + QList<const Function *> functions(bool detectCycles = false) const; /// NOTE: The @c ParseData will take ownership. void addFunction(const Function *function); @@ -110,5 +109,4 @@ private: Private *d; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindparser.cpp b/src/plugins/valgrind/callgrind/callgrindparser.cpp index 4d22fcb695f..b2d4b166835 100644 --- a/src/plugins/valgrind/callgrind/callgrindparser.cpp +++ b/src/plugins/valgrind/callgrind/callgrindparser.cpp @@ -3,26 +3,24 @@ #include "callgrindparser.h" -#include "callgrindparsedata.h" -#include "callgrindfunctioncall.h" #include "callgrindcostitem.h" #include "callgrindfunction.h" +#include "callgrindfunctioncall.h" +#include "callgrindparsedata.h" #include <utils/filepath.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> -#include <QHash> -#include <QVector> -#include <QStringList> #include <QDebug> +#include <QHash> +#include <QStringList> // #define DEBUG_PARSER using namespace Utils; -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { static void skipSpace(const char **current, const char *end) { @@ -162,13 +160,13 @@ public: FunctionCall *call = nullptr; }; CallData currentCallData; - QVector<quint64> callDestinations; + QList<quint64> callDestinations; // we can only resolve callees after parsing the whole file so save that data here for now - QVector<CallData> pendingCallees; + QList<CallData> pendingCallees; // id(s) for the ??? file - QVector<quint64> unknownFiles; + QList<quint64> unknownFiles; // functions which call themselves QSet<Function *> recursiveFunctions; @@ -197,7 +195,7 @@ void Parser::Private::parse(const FilePath &filePath) // build fast lookup of functions by their nameId QHash<qint64, QList<const Function *> > functionLookup; - const QVector<const Function *> functions = data->functions(); + const QList<const Function *> functions = data->functions(); for (const Function *function : functions) { functionLookup[function->nameId()].append(function); } @@ -643,5 +641,4 @@ ParseData *Parser::takeData() return data; } -} //Callgrind -} //Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindparser.h b/src/plugins/valgrind/callgrind/callgrindparser.h index 9b5661b0b59..6355a5859e8 100644 --- a/src/plugins/valgrind/callgrind/callgrindparser.h +++ b/src/plugins/valgrind/callgrind/callgrindparser.h @@ -7,8 +7,7 @@ namespace Utils { class FilePath; } -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class ParseData; @@ -42,5 +41,4 @@ private: Private *const d; }; -} // Callgrind -} // Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp b/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp index d13ec0c06e1..d9bad9daefc 100644 --- a/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp +++ b/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp @@ -10,10 +10,7 @@ #include <utils/qtcassert.h> -#include <QDebug> - -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { DataProxyModel::DataProxyModel(QObject *parent) : QSortFilterProxyModel(parent) @@ -108,7 +105,7 @@ bool DataProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_ // check if the function from this index is a child of (called by) the filter function if (m_function) { bool isValid = false; - const QVector<const FunctionCall *> calls = func->incomingCalls(); + const QList<const FunctionCall *> calls = func->incomingCalls(); for (const FunctionCall *call : calls) { if (call->caller() == m_function) { isValid = true; @@ -135,5 +132,4 @@ bool DataProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindproxymodel.h b/src/plugins/valgrind/callgrind/callgrindproxymodel.h index c90b653f1a3..b11186a6c27 100644 --- a/src/plugins/valgrind/callgrind/callgrindproxymodel.h +++ b/src/plugins/valgrind/callgrind/callgrindproxymodel.h @@ -5,8 +5,7 @@ #include <QSortFilterProxyModel> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class DataModel; class Function; @@ -52,5 +51,4 @@ private: double m_minimumInclusiveCostRatio = 0; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindstackbrowser.cpp b/src/plugins/valgrind/callgrind/callgrindstackbrowser.cpp index 7b780d0dfa3..34ce812127e 100644 --- a/src/plugins/valgrind/callgrind/callgrindstackbrowser.cpp +++ b/src/plugins/valgrind/callgrind/callgrindstackbrowser.cpp @@ -3,8 +3,7 @@ #include "callgrindstackbrowser.h" -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { StackBrowser::StackBrowser(QObject *parent) : QObject(parent) @@ -51,5 +50,4 @@ void StackBrowser::goNext() emit currentChanged(); } -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrind/callgrindstackbrowser.h b/src/plugins/valgrind/callgrind/callgrindstackbrowser.h index d9ece6c1ae3..62d829c69e0 100644 --- a/src/plugins/valgrind/callgrind/callgrindstackbrowser.h +++ b/src/plugins/valgrind/callgrind/callgrindstackbrowser.h @@ -6,8 +6,7 @@ #include <QObject> #include <QStack> -namespace Valgrind { -namespace Callgrind { +namespace Valgrind::Callgrind { class Function; @@ -35,5 +34,4 @@ private: QStack<const Function *> m_redoStack; }; -} // namespace Callgrind -} // namespace Valgrind +} // namespace Valgrind::Callgrind diff --git a/src/plugins/valgrind/callgrindcostdelegate.cpp b/src/plugins/valgrind/callgrindcostdelegate.cpp index e6afe5df32f..a280a0edba9 100644 --- a/src/plugins/valgrind/callgrindcostdelegate.cpp +++ b/src/plugins/valgrind/callgrindcostdelegate.cpp @@ -3,9 +3,8 @@ #include "callgrindcostdelegate.h" -#include "callgrindhelper.h" - #include "callgrind/callgrindabstractmodel.h" +#include "callgrindhelper.h" #include <utils/qtcassert.h> @@ -14,8 +13,7 @@ using namespace Valgrind::Callgrind; -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class CostDelegate::Private { @@ -135,5 +133,4 @@ QSize CostDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelInd return size; } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindcostdelegate.h b/src/plugins/valgrind/callgrindcostdelegate.h index b8f7f01d016..94a0940462c 100644 --- a/src/plugins/valgrind/callgrindcostdelegate.h +++ b/src/plugins/valgrind/callgrindcostdelegate.h @@ -5,8 +5,7 @@ #include <QStyledItemDelegate> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class CostDelegate : public QStyledItemDelegate { @@ -37,7 +36,6 @@ private: Private *d; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal Q_DECLARE_METATYPE(Valgrind::Internal::CostDelegate::CostFormat) diff --git a/src/plugins/valgrind/callgrindcostview.cpp b/src/plugins/valgrind/callgrindcostview.cpp index 3d54a99c295..ffc883cb280 100644 --- a/src/plugins/valgrind/callgrindcostview.cpp +++ b/src/plugins/valgrind/callgrindcostview.cpp @@ -3,22 +3,15 @@ #include "callgrindcostview.h" +#include "callgrind/callgrinddatamodel.h" +#include "callgrind/callgrindcallmodel.h" #include "callgrindnamedelegate.h" -#include <valgrind/callgrind/callgrindabstractmodel.h> -#include <valgrind/callgrind/callgrinddatamodel.h> -#include <valgrind/callgrind/callgrindfunction.h> -#include <valgrind/callgrind/callgrindcallmodel.h> - #include <QAbstractProxyModel> -#include <QContextMenuEvent> -#include <QMenu> -#include <QDebug> using namespace Valgrind::Callgrind; -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { CostView::CostView(QWidget *parent) : Utils::BaseTreeView(parent) @@ -72,5 +65,4 @@ CostDelegate::CostFormat CostView::costFormat() const return m_costDelegate->format(); } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindcostview.h b/src/plugins/valgrind/callgrindcostview.h index 48a04e5fb4f..5448db86a6c 100644 --- a/src/plugins/valgrind/callgrindcostview.h +++ b/src/plugins/valgrind/callgrindcostview.h @@ -7,8 +7,7 @@ #include <utils/basetreeview.h> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class CostDelegate; class NameDelegate; @@ -38,5 +37,4 @@ private: NameDelegate *m_nameDelegate; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindengine.cpp b/src/plugins/valgrind/callgrindengine.cpp index 92b13b4f77b..06ff38f5abe 100644 --- a/src/plugins/valgrind/callgrindengine.cpp +++ b/src/plugins/valgrind/callgrindengine.cpp @@ -3,11 +3,8 @@ #include "callgrindengine.h" -#include "valgrindsettings.h" - -#include <valgrind/callgrind/callgrindparser.h> -#include <valgrind/valgrindrunner.h> -#include <valgrind/valgrindtr.h> +#include "callgrind/callgrindparser.h" +#include "valgrindtr.h" #include <debugger/analyzer/analyzermanager.h> @@ -17,16 +14,13 @@ #include <utils/qtcassert.h> #include <utils/temporaryfile.h> -#include <QDebug> - #define CALLGRIND_CONTROL_DEBUG 0 using namespace ProjectExplorer; using namespace Valgrind::Callgrind; using namespace Utils; -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { const char CALLGRIND_CONTROL_BINARY[] = "callgrind_control"; @@ -37,10 +31,10 @@ CallgrindToolRunner::CallgrindToolRunner(RunControl *runControl) { setId("CallgrindToolRunner"); - connect(&m_runner, &ValgrindRunner::valgrindStarted, this, [this](qint64 pid) { + connect(&m_runner, &ValgrindProcess::valgrindStarted, this, [this](qint64 pid) { m_pid = pid; }); - connect(&m_runner, &ValgrindRunner::finished, this, [this] { + connect(&m_runner, &ValgrindProcess::done, this, [this] { triggerParse(); emit parserDataReady(this); }); @@ -274,5 +268,4 @@ void CallgrindToolRunner::cleanupTempFile() m_hostOutputFile.clear(); } -} // Internal -} // Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindengine.h b/src/plugins/valgrind/callgrindengine.h index d5d26e113a5..2379e917374 100644 --- a/src/plugins/valgrind/callgrindengine.h +++ b/src/plugins/valgrind/callgrindengine.h @@ -9,9 +9,9 @@ #include "callgrind/callgrindparser.h" #include <utils/process.h> +#include <utils/processinterface.h> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class CallgrindToolRunner : public ValgrindToolRunner { @@ -74,7 +74,7 @@ private: bool m_markAsPaused = false; std::unique_ptr<Utils::Process> m_controllerProcess; - ProjectExplorer::Runnable m_valgrindRunnable; + Utils::ProcessRunData m_valgrindRunnable; qint64 m_pid = 0; Option m_lastOption = Unknown; @@ -89,5 +89,4 @@ private: QString m_argumentForToggleCollect; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindhelper.cpp b/src/plugins/valgrind/callgrindhelper.cpp index 29a57f5af09..18c25a201fe 100644 --- a/src/plugins/valgrind/callgrindhelper.cpp +++ b/src/plugins/valgrind/callgrindhelper.cpp @@ -10,8 +10,7 @@ #include <QRandomGenerator> #include <QString> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { QColor CallgrindHelper::colorForString(const QString &text) { @@ -21,10 +20,9 @@ QColor CallgrindHelper::colorForString(const QString &text) return colorCache.value(text); // Minimum lightness of 100 to be readable with black text. - const QColor color = QColor::fromHsl( - ((qreal) QRandomGenerator::global()->generate() / RAND_MAX * 359), - ((qreal) QRandomGenerator::global()->generate() / RAND_MAX * 255), - ((qreal) QRandomGenerator::global()->generate() / RAND_MAX * 127) + 128); + const QColor color = QColor::fromHsl(QRandomGenerator::global()->generate() % 360, + QRandomGenerator::global()->generate() % 256, + QRandomGenerator::global()->generate() % 128 + 128); colorCache[text] = color; return color; } @@ -46,5 +44,4 @@ QString CallgrindHelper::toPercent(float costs, const QLocale &locale) return '<' + locale.toString(0.01f) + locale.percent(); } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindhelper.h b/src/plugins/valgrind/callgrindhelper.h index 3287e987265..16432777d85 100644 --- a/src/plugins/valgrind/callgrindhelper.h +++ b/src/plugins/valgrind/callgrindhelper.h @@ -10,8 +10,7 @@ class QColor; class QString; QT_END_NAMESPACE -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { namespace CallgrindHelper { @@ -32,5 +31,4 @@ namespace CallgrindHelper QString toPercent(float costs, const QLocale &locale = QLocale()); } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindnamedelegate.cpp b/src/plugins/valgrind/callgrindnamedelegate.cpp index f5795def31a..614465e3d1e 100644 --- a/src/plugins/valgrind/callgrindnamedelegate.cpp +++ b/src/plugins/valgrind/callgrindnamedelegate.cpp @@ -8,8 +8,7 @@ #include <QApplication> #include <QPainter> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { NameDelegate::NameDelegate(QObject *parent) : QStyledItemDelegate(parent) @@ -57,5 +56,4 @@ void NameDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, painter->restore(); } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindnamedelegate.h b/src/plugins/valgrind/callgrindnamedelegate.h index 97580938dd5..4c6a5699851 100644 --- a/src/plugins/valgrind/callgrindnamedelegate.h +++ b/src/plugins/valgrind/callgrindnamedelegate.h @@ -5,8 +5,7 @@ #include <QStyledItemDelegate> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class NameDelegate : public QStyledItemDelegate { @@ -17,5 +16,4 @@ public: const QModelIndex &index) const override; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindtextmark.cpp b/src/plugins/valgrind/callgrindtextmark.cpp index 07b6af16c24..39cbf3803a5 100644 --- a/src/plugins/valgrind/callgrindtextmark.cpp +++ b/src/plugins/valgrind/callgrindtextmark.cpp @@ -3,18 +3,15 @@ #include "callgrindtextmark.h" -#include "callgrindhelper.h" - #include "callgrind/callgrinddatamodel.h" #include "callgrind/callgrindfunction.h" +#include "callgrindhelper.h" #include "valgrindtr.h" #include <utils/qtcassert.h> -#include <QDebug> #include <QLabel> #include <QLayout> -#include <QPainter> using namespace Utils; using namespace Valgrind::Internal; diff --git a/src/plugins/valgrind/callgrindtextmark.h b/src/plugins/valgrind/callgrindtextmark.h index 3e0c3472f8e..998da19a7e2 100644 --- a/src/plugins/valgrind/callgrindtextmark.h +++ b/src/plugins/valgrind/callgrindtextmark.h @@ -7,11 +7,9 @@ #include <QPersistentModelIndex> -namespace Valgrind { +namespace Valgrind::Callgrind { class Function; } -namespace Callgrind { class Function; } - -namespace Internal { +namespace Valgrind::Internal { class CallgrindTextMark : public TextEditor::TextMark { @@ -34,5 +32,4 @@ private: QPersistentModelIndex m_modelIndex; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp index ab5b0e55602..fe488cc1af8 100644 --- a/src/plugins/valgrind/callgrindtool.cpp +++ b/src/plugins/valgrind/callgrindtool.cpp @@ -3,30 +3,22 @@ #include "callgrindtool.h" +#include "callgrind/callgrindcallmodel.h" +#include "callgrind/callgrinddatamodel.h" +#include "callgrind/callgrindfunction.h" +#include "callgrind/callgrindfunctioncall.h" +#include "callgrind/callgrindparsedata.h" +#include "callgrind/callgrindparser.h" +#include "callgrind/callgrindproxymodel.h" +#include "callgrind/callgrindstackbrowser.h" #include "callgrindcostdelegate.h" #include "callgrindcostview.h" #include "callgrindengine.h" #include "callgrindtextmark.h" #include "callgrindvisualisation.h" +#include "valgrindsettings.h" #include "valgrindtr.h" -#include <valgrind/callgrind/callgrindcallmodel.h> -#include <valgrind/callgrind/callgrindcostitem.h> -#include <valgrind/callgrind/callgrinddatamodel.h> -#include <valgrind/callgrind/callgrindfunction.h> -#include <valgrind/callgrind/callgrindfunctioncall.h> -#include <valgrind/callgrind/callgrindparsedata.h> -#include <valgrind/callgrind/callgrindparser.h> -#include <valgrind/callgrind/callgrindproxymodel.h> -#include <valgrind/callgrind/callgrindstackbrowser.h> -#include <valgrind/valgrindsettings.h> - -#include <debugger/debuggerconstants.h> -#include <debugger/analyzer/analyzerconstants.h> -#include <debugger/analyzer/analyzermanager.h> -#include <debugger/analyzer/analyzerutils.h> -#include <debugger/analyzer/startremotedialog.h> - #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -39,10 +31,10 @@ #include <cppeditor/cppeditorconstants.h> -#include <extensionsystem/pluginmanager.h> - -#include <texteditor/texteditor.h> -#include <texteditor/textdocument.h> +#include <debugger/debuggerconstants.h> +#include <debugger/analyzer/analyzermanager.h> +#include <debugger/analyzer/analyzerutils.h> +#include <debugger/analyzer/startremotedialog.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> @@ -51,23 +43,22 @@ #include <projectexplorer/projecttree.h> #include <projectexplorer/taskhub.h> -#include <utils/fancymainwindow.h> +#include <texteditor/texteditor.h> +#include <texteditor/textdocument.h> + #include <utils/process.h> #include <utils/qtcassert.h> -#include <utils/styledbar.h> #include <utils/utilsicons.h> #include <QAction> #include <QActionGroup> #include <QComboBox> #include <QFile> -#include <QFileDialog> #include <QFileInfo> #include <QLineEdit> #include <QMenu> #include <QSortFilterProxyModel> #include <QTimer> -#include <QToolBar> #include <QToolButton> using namespace Debugger; @@ -187,7 +178,7 @@ public: QTimer m_updateTimer; - QVector<CallgrindTextMark *> m_textMarks; + QList<CallgrindTextMark *> m_textMarks; QAction *m_startAction = nullptr; QAction *m_stopAction = nullptr; @@ -290,7 +281,7 @@ CallgrindToolPrivate::CallgrindToolPrivate() cmd->setAttribute(Command::CA_NonConfigurable); } - QSettings *coreSettings = ICore::settings(); + QtcSettings *coreSettings = ICore::settings(); // // DockWidgets @@ -342,8 +333,6 @@ CallgrindToolPrivate::CallgrindToolPrivate() updateCostFormat(); - ValgrindGlobalSettings *settings = ValgrindGlobalSettings::instance(); - // // Control Widget // @@ -360,8 +349,8 @@ CallgrindToolPrivate::CallgrindToolPrivate() Theme::IconsBaseColor}}); action->setIcon(kCachegrindIcon.icon()); action->setToolTip(Tr::tr("Open results in KCachegrind.")); - connect(action, &QAction::triggered, this, [this, settings] { - Process::startDetached({FilePath::fromString(settings->kcachegrindExecutable.value()), { m_lastFileName }}); + connect(action, &QAction::triggered, this, [this] { + Process::startDetached({globalSettings().kcachegrindExecutable(), { m_lastFileName }}); }); // dump action @@ -468,7 +457,7 @@ CallgrindToolPrivate::CallgrindToolPrivate() } // Filtering - action = m_filterProjectCosts = settings->filterExternalIssues.action(); + action = m_filterProjectCosts = globalSettings().filterExternalIssues.action(); connect(action, &QAction::toggled, this, &CallgrindToolPrivate::handleFilterProjectCosts); // Filter @@ -477,10 +466,10 @@ CallgrindToolPrivate::CallgrindToolPrivate() connect(m_searchFilter, &QLineEdit::textChanged, &m_updateTimer, QOverload<>::of(&QTimer::start)); - setCostFormat(CostDelegate::CostFormat(settings->costFormat.value())); + setCostFormat(CostDelegate::CostFormat(globalSettings().costFormat())); - m_perspective.addToolBarAction(settings->detectCycles.action()); - m_perspective.addToolBarAction(settings->shortenTemplates.action()); + m_perspective.addToolBarAction(globalSettings().detectCycles.action()); + m_perspective.addToolBarAction(globalSettings().shortenTemplates.action()); m_perspective.addToolBarAction(m_filterProjectCosts); m_perspective.addToolBarWidget(m_searchFilter); @@ -625,8 +614,7 @@ void CallgrindToolPrivate::updateCostFormat() m_calleesView->setCostFormat(format); m_callersView->setCostFormat(format); } - if (ValgrindGlobalSettings *settings = ValgrindGlobalSettings::instance()) - settings->costFormat.setValue(format); + globalSettings().costFormat.setValue(format); } void CallgrindToolPrivate::handleFilterProjectCosts() @@ -745,11 +733,11 @@ void CallgrindToolPrivate::setupRunner(CallgrindToolRunner *toolRunner) QTC_ASSERT(m_visualization, return); // apply project settings - ValgrindProjectSettings settings; + ValgrindSettings settings{false}; settings.fromMap(runControl->settingsData(ANALYZER_VALGRIND_SETTINGS)); - m_visualization->setMinimumInclusiveCostRatio(settings.visualizationMinimumInclusiveCostRatio.value() / 100.0); - m_proxyModel.setMinimumInclusiveCostRatio(settings.minimumInclusiveCostRatio.value() / 100.0); - m_dataModel.setVerboseToolTipsEnabled(settings.enableEventToolTips.value()); + m_visualization->setMinimumInclusiveCostRatio(settings.visualizationMinimumInclusiveCostRatio() / 100.0); + m_proxyModel.setMinimumInclusiveCostRatio(settings.minimumInclusiveCostRatio() / 100.0); + m_dataModel.setVerboseToolTipsEnabled(settings.enableEventToolTips()); m_toolBusy = true; updateRunActions(); @@ -770,10 +758,10 @@ void CallgrindToolPrivate::updateRunActions() m_startAction->setToolTip(Tr::tr("A Valgrind Callgrind analysis is still in progress.")); m_stopAction->setEnabled(true); } else { - QString whyNot = Tr::tr("Start a Valgrind Callgrind analysis."); - bool canRun = ProjectExplorerPlugin::canRunStartupProject(CALLGRIND_RUN_MODE, &whyNot); - m_startAction->setToolTip(whyNot); - m_startAction->setEnabled(canRun); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject(CALLGRIND_RUN_MODE); + m_startAction->setToolTip(canRun ? Tr::tr("Start a Valgrind Callgrind analysis.") + : canRun.error()); + m_startAction->setEnabled(bool(canRun)); m_stopAction->setEnabled(false); } } @@ -908,10 +896,9 @@ void CallgrindToolPrivate::takeParserData(ParseData *data) doClear(true); setParseData(data); - const QString kcachegrindExecutable = - ValgrindGlobalSettings::instance()->kcachegrindExecutable.value(); - const bool kcachegrindExists = !Utils::Environment::systemEnvironment().searchInPath( - kcachegrindExecutable).isEmpty(); + const FilePath kcachegrindExecutable = globalSettings().kcachegrindExecutable(); + const FilePath found = kcachegrindExecutable.searchInPath(); + const bool kcachegrindExists = found.isExecutableFile(); m_startKCachegrind->setEnabled(kcachegrindExists && !m_lastFileName.isEmpty()); createTextMarks(); } diff --git a/src/plugins/valgrind/callgrindvisualisation.cpp b/src/plugins/valgrind/callgrindvisualisation.cpp index 4120dc53de2..15bc6b45d19 100644 --- a/src/plugins/valgrind/callgrindvisualisation.cpp +++ b/src/plugins/valgrind/callgrindvisualisation.cpp @@ -3,18 +3,15 @@ #include "callgrindvisualisation.h" +#include "callgrind/callgrinddatamodel.h" +#include "callgrind/callgrindfunction.h" +#include "callgrind/callgrindproxymodel.h" #include "callgrindhelper.h" #include "valgrindtr.h" -#include <valgrind/callgrind/callgrindabstractmodel.h> -#include <valgrind/callgrind/callgrinddatamodel.h> -#include <valgrind/callgrind/callgrindfunction.h> -#include <valgrind/callgrind/callgrindproxymodel.h> - #include <utils/qtcassert.h> #include <QAbstractItemModel> -#include <QDebug> #include <QGraphicsRectItem> #include <QGraphicsScene> #include <QGraphicsSimpleTextItem> @@ -22,7 +19,6 @@ #include <QPair> #include <QStaticText> #include <QStyleOptionGraphicsItem> -#include <QVector> #define VISUALISATION_DEBUG 0 // Margin from hardcoded value in: @@ -33,8 +29,7 @@ static const int FIT_IN_VIEW_MARGIN = 2; using namespace Valgrind::Callgrind; -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class FunctionGraphicsTextItem : public QAbstractGraphicsShapeItem { @@ -345,7 +340,7 @@ void Visualization::populateScene() qreal total = 0; using Pair = QPair<QModelIndex, qreal>; - QVector<Pair> costs; + QList<Pair> costs; for (int row = 0; row < d->m_model->rowCount(); ++row) { const QModelIndex index = d->m_model->index(row, DataModel::InclusiveCostColumn); @@ -419,5 +414,4 @@ void Visualization::resizeEvent(QResizeEvent *event) QGraphicsView::resizeEvent(event); } -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/callgrindvisualisation.h b/src/plugins/valgrind/callgrindvisualisation.h index 7c10ba75cc8..fda14eef487 100644 --- a/src/plugins/valgrind/callgrindvisualisation.h +++ b/src/plugins/valgrind/callgrindvisualisation.h @@ -9,12 +9,9 @@ QT_BEGIN_NAMESPACE class QAbstractItemModel; QT_END_NAMESPACE -namespace Valgrind { -namespace Callgrind { class Function; } -} +namespace Valgrind::Callgrind { class Function; } -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { class Visualization : public QGraphicsView { @@ -51,5 +48,4 @@ private: Private *d; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/memcheckerrorview.cpp b/src/plugins/valgrind/memcheckerrorview.cpp index 48e9afd8018..102a131f62c 100644 --- a/src/plugins/valgrind/memcheckerrorview.cpp +++ b/src/plugins/valgrind/memcheckerrorview.cpp @@ -3,30 +3,56 @@ #include "memcheckerrorview.h" -#include "suppressiondialog.h" #include "valgrindsettings.h" #include "valgrindtr.h" - #include "xmlprotocol/error.h" #include "xmlprotocol/errorlistmodel.h" +#include "xmlprotocol/frame.h" +#include "xmlprotocol/stack.h" #include "xmlprotocol/suppression.h" -#include <coreplugin/editormanager/editormanager.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> +#include <projectexplorer/projectnodes.h> -#include <utils/qtcassert.h> +#include <utils/algorithm.h> #include <utils/icon.h> +#include <utils/pathchooser.h> +#include <utils/qtcassert.h> #include <utils/theme/theme.h> #include <QAction> +#include <QDialog> +#include <QDialogButtonBox> +#include <QFormLayout> +#include <QLabel> +#include <QPlainTextEdit> +#include <QPushButton> using namespace Utils; using namespace Valgrind::XmlProtocol; -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { + +class SuppressionDialog : public QDialog +{ +public: + SuppressionDialog(MemcheckErrorView *view, const QList<XmlProtocol::Error> &errors); + +private: + void validate(); + void accept() override; + void reject() override; + + MemcheckErrorView *m_view; + bool m_cleanupIfCanceled; + QList<XmlProtocol::Error> m_errors; + + Utils::PathChooser *m_fileChooser; + QPlainTextEdit *m_suppressionEdit; + QDialogButtonBox *m_buttonBox; +}; MemcheckErrorView::MemcheckErrorView(QWidget *parent) : Debugger::DetailedErrorView(parent) @@ -58,7 +84,7 @@ FilePath MemcheckErrorView::defaultSuppressionFile() const // slot, can (for now) be invoked either when the settings were modified *or* when the active // settings object has changed. -void MemcheckErrorView::settingsChanged(ValgrindBaseSettings *settings) +void MemcheckErrorView::settingsChanged(ValgrindSettings *settings) { QTC_ASSERT(settings, return); m_settings = settings; @@ -66,7 +92,23 @@ void MemcheckErrorView::settingsChanged(ValgrindBaseSettings *settings) void MemcheckErrorView::suppressError() { - SuppressionDialog::maybeShow(this); + QModelIndexList indices = selectionModel()->selectedRows(); + // Can happen when using arrow keys to navigate and shortcut to trigger suppression: + if (indices.isEmpty() && selectionModel()->currentIndex().isValid()) + indices.append(selectionModel()->currentIndex()); + + QList<XmlProtocol::Error> errors; + for (const QModelIndex &index : std::as_const(indices)) { + Error error = model()->data(index, ErrorListModel::ErrorRole).value<Error>(); + if (!error.suppression().isNull()) + errors.append(error); + } + + if (errors.isEmpty()) + return; + + SuppressionDialog dialog(this, errors); + dialog.exec(); } QList<QAction *> MemcheckErrorView::customActions() const @@ -88,5 +130,189 @@ QList<QAction *> MemcheckErrorView::customActions() const return actions; } -} // namespace Internal -} // namespace Valgrind +static QString suppressionText(const Error &error) +{ + Suppression sup(error.suppression()); + + // workaround: https://bugs.kde.org/show_bug.cgi?id=255822 + if (sup.frames().size() >= 24) + sup.setFrames(sup.frames().mid(0, 23)); + QTC_CHECK(sup.frames().size() < 24); + + // try to set some useful name automatically, instead of "insert_name_here" + // we take the last stack frame and append the suppression kind, e.g.: + // QDebug::operator<<(bool) [Memcheck:Cond] + if (!error.stacks().isEmpty() && !error.stacks().constFirst().frames().isEmpty()) { + const Frame frame = error.stacks().constFirst().frames().constFirst(); + + QString newName; + if (!frame.functionName().isEmpty()) + newName = frame.functionName(); + else if (!frame.object().isEmpty()) + newName = frame.object(); + + if (!newName.isEmpty()) + sup.setName(newName + '[' + sup.kind() + ']'); + } + + return sup.toString(); +} + +/// @p error input error, which might get hidden when it has the same stack +/// @p suppressed the error that got suppressed already +static bool equalSuppression(const Error &error, const Error &suppressed) +{ + if (error.kind() != suppressed.kind() || error.suppression().isNull()) + return false; + + const SuppressionFrames errorFrames = error.suppression().frames(); + const SuppressionFrames suppressedFrames = suppressed.suppression().frames(); + + // limit to 23 frames, see: https://bugs.kde.org/show_bug.cgi?id=255822 + if (qMin(23, suppressedFrames.size()) > errorFrames.size()) + return false; + + int frames = 23; + if (errorFrames.size() < frames) + frames = errorFrames.size(); + + if (suppressedFrames.size() < frames) + frames = suppressedFrames.size(); + + for (int i = 0; i < frames; ++i) + if (errorFrames.at(i) != suppressedFrames.at(i)) + return false; + + return true; +} + +SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error> &errors) + : m_view(view), + m_cleanupIfCanceled(false), + m_errors(errors), + m_fileChooser(new PathChooser(this)), + m_suppressionEdit(new QPlainTextEdit(this)) +{ + setWindowTitle(Tr::tr("Save Suppression")); + + auto fileLabel = new QLabel(Tr::tr("Suppression File:"), this); + + auto suppressionsLabel = new QLabel(Tr::tr("Suppression:"), this); + suppressionsLabel->setBuddy(m_suppressionEdit); + + QFont font; + font.setFamily("Monospace"); + m_suppressionEdit->setFont(font); + + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Save); + + auto formLayout = new QFormLayout(this); + formLayout->addRow(fileLabel, m_fileChooser); + formLayout->addRow(suppressionsLabel); + formLayout->addRow(m_suppressionEdit); + formLayout->addRow(m_buttonBox); + + const FilePath defaultSuppFile = view->defaultSuppressionFile(); + if (!defaultSuppFile.exists() && defaultSuppFile.ensureExistingFile()) + m_cleanupIfCanceled = true; + + m_fileChooser->setExpectedKind(PathChooser::File); + m_fileChooser->setHistoryCompleter("Valgrind.Suppression.History"); + m_fileChooser->setPath(defaultSuppFile.fileName()); + m_fileChooser->setPromptDialogFilter("*.supp"); + m_fileChooser->setPromptDialogTitle(Tr::tr("Select Suppression File")); + + QString suppressions; + for (const Error &error : std::as_const(m_errors)) + suppressions += suppressionText(error); + + m_suppressionEdit->setPlainText(suppressions); + + connect(m_fileChooser, &PathChooser::validChanged, + this, &SuppressionDialog::validate); + connect(m_suppressionEdit->document(), &QTextDocument::contentsChanged, + this, &SuppressionDialog::validate); + connect(m_buttonBox, &QDialogButtonBox::accepted, + this, &SuppressionDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::rejected, + this, &SuppressionDialog::reject); +} + +void SuppressionDialog::accept() +{ + const FilePath path = m_fileChooser->filePath(); + QTC_ASSERT(!path.isEmpty(), return); + QTC_ASSERT(!m_suppressionEdit->toPlainText().trimmed().isEmpty(), return); + + FileSaver saver(path, QIODevice::Append); + if (!saver.hasError()) { + QTextStream stream(saver.file()); + stream << m_suppressionEdit->toPlainText(); + saver.setResult(&stream); + } + if (!saver.finalize(this)) + return; + + // Add file to project if there is a project containing this file on the file system. + if (!ProjectExplorer::ProjectManager::projectForFile(path)) { + for (ProjectExplorer::Project *p : ProjectExplorer::ProjectManager::projects()) { + if (path.startsWith(p->projectDirectory().toString())) { + p->rootProjectNode()->addFiles({path}); + break; + } + } + } + + m_view->settings()->suppressions.addSuppressionFile(path); + + const QModelIndexList indices = Utils::sorted(m_view->selectionModel()->selectedRows(), + [](const QModelIndex &l, const QModelIndex &r) { + return l.row() > r.row(); + }); + QAbstractItemModel *model = m_view->model(); + for (const QModelIndex &index : indices) { + bool removed = model->removeRow(index.row()); + QTC_ASSERT(removed, qt_noop()); + Q_UNUSED(removed) + } + + // One suppression might hide multiple rows, care for that. + for (int row = 0; row < model->rowCount(); ++row ) { + const Error rowError = model->data( + model->index(row, 0), ErrorListModel::ErrorRole).value<Error>(); + + for (const Error &error : std::as_const(m_errors)) { + if (equalSuppression(rowError, error)) { + bool removed = model->removeRow(row); + QTC_CHECK(removed); + // Gets incremented in the for loop again. + --row; + break; + } + } + } + + // Select a new item. + m_view->setCurrentIndex(indices.first()); + + QDialog::accept(); +} + +void SuppressionDialog::reject() +{ + if (m_cleanupIfCanceled) + m_view->defaultSuppressionFile().removeFile(); + + QDialog::reject(); +} + +void SuppressionDialog::validate() +{ + bool valid = m_fileChooser->isValid() + && !m_suppressionEdit->toPlainText().trimmed().isEmpty(); + + m_buttonBox->button(QDialogButtonBox::Save)->setEnabled(valid); +} + +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/memcheckerrorview.h b/src/plugins/valgrind/memcheckerrorview.h index 8de9400d851..025a78ce01b 100644 --- a/src/plugins/valgrind/memcheckerrorview.h +++ b/src/plugins/valgrind/memcheckerrorview.h @@ -9,10 +9,9 @@ #include <QListView> -namespace Valgrind { -namespace Internal { +namespace Valgrind::Internal { -class ValgrindBaseSettings; +class ValgrindSettings; class MemcheckErrorView : public Debugger::DetailedErrorView { @@ -22,8 +21,8 @@ public: void setDefaultSuppressionFile(const Utils::FilePath &suppFile); Utils::FilePath defaultSuppressionFile() const; - ValgrindBaseSettings *settings() const { return m_settings; } - void settingsChanged(ValgrindBaseSettings *settings); + ValgrindSettings *settings() const { return m_settings; } + void settingsChanged(ValgrindSettings *settings); private: void suppressError(); @@ -31,8 +30,7 @@ private: QAction *m_suppressAction; Utils::FilePath m_defaultSuppFile; - ValgrindBaseSettings *m_settings = nullptr; + ValgrindSettings *m_settings = nullptr; }; -} // namespace Internal -} // namespace Valgrind +} // namespace Valgrind::Internal diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index e2921603e98..1cbd522571c 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -5,7 +5,7 @@ #include "memcheckerrorview.h" #include "valgrindengine.h" -#include "valgrindrunner.h" +#include "valgrindprocess.h" #include "valgrindsettings.h" #include "valgrindtr.h" @@ -13,30 +13,8 @@ #include "xmlprotocol/error.h" #include "xmlprotocol/errorlistmodel.h" #include "xmlprotocol/frame.h" +#include "xmlprotocol/parser.h" #include "xmlprotocol/stack.h" -#include "xmlprotocol/threadedparser.h" - -#include <debugger/debuggerkitinformation.h> -#include <debugger/debuggerruncontrol.h> -#include <debugger/analyzer/analyzerconstants.h> -#include <debugger/analyzer/analyzermanager.h> -#include <debugger/analyzer/startremotedialog.h> - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/deploymentdata.h> -#include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/kitinformation.h> -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <projectexplorer/projectmanager.h> -#include <projectexplorer/runconfiguration.h> -#include <projectexplorer/target.h> -#include <projectexplorer/taskhub.h> -#include <projectexplorer/toolchain.h> - -#include <extensionsystem/iplugin.h> -#include <extensionsystem/pluginmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -46,8 +24,26 @@ #include <coreplugin/icore.h> #include <coreplugin/modemanager.h> +#include <debugger/debuggerkitaspect.h> +#include <debugger/debuggerruncontrol.h> +#include <debugger/analyzer/analyzerconstants.h> +#include <debugger/analyzer/analyzermanager.h> +#include <debugger/analyzer/startremotedialog.h> + +#include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/deploymentdata.h> +#include <projectexplorer/devicesupport/devicemanager.h> +#include <projectexplorer/kitaspects.h> +#include <projectexplorer/project.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/projectmanager.h> +#include <projectexplorer/runconfiguration.h> +#include <projectexplorer/target.h> +#include <projectexplorer/taskhub.h> +#include <projectexplorer/toolchain.h> + #include <utils/checkablemessagebox.h> -#include <utils/fancymainwindow.h> #include <utils/pathchooser.h> #include <utils/process.h> #include <utils/qtcassert.h> @@ -55,22 +51,20 @@ #include <utils/utilsicons.h> #include <QAction> +#include <QCheckBox> +#include <QComboBox> #include <QFile> -#include <QFileDialog> #include <QFileInfo> #include <QHostAddress> #include <QInputDialog> #include <QLabel> -#include <QMenu> -#include <QToolButton> -#include <QSortFilterProxyModel> - -#include <QCheckBox> -#include <QComboBox> #include <QLineEdit> +#include <QMenu> #include <QPushButton> #include <QSpinBox> #include <QStandardPaths> +#include <QToolButton> +#include <QSortFilterProxyModel> #include <QVBoxLayout> #ifdef Q_OS_WIN @@ -107,7 +101,6 @@ public: signals: void internalParserError(const QString &errorString); void parserError(const Valgrind::XmlProtocol::Error &error); - void suppressionCount(const QString &name, qint64 count); private: QString progressTitle() const override; @@ -176,7 +169,7 @@ void MemcheckToolRunner::start() void MemcheckToolRunner::stop() { - disconnect(m_runner.parser(), &ThreadedParser::internalError, + disconnect(&m_runner, &ValgrindProcess::internalError, this, &MemcheckToolRunner::internalParserError); ValgrindToolRunner::stop(); } @@ -193,13 +186,13 @@ void MemcheckToolRunner::addToolArguments(CommandLine &cmd) const QString leakCheckValue; switch (m_settings.leakCheckOnFinish()) { - case ValgrindBaseSettings::LeakCheckOnFinishNo: + case ValgrindSettings::LeakCheckOnFinishNo: leakCheckValue = "no"; break; - case ValgrindBaseSettings::LeakCheckOnFinishYes: + case ValgrindSettings::LeakCheckOnFinishYes: leakCheckValue = "full"; break; - case ValgrindBaseSettings::LeakCheckOnFinishSummaryOnly: + case ValgrindSettings::LeakCheckOnFinishSummaryOnly: default: leakCheckValue = "summary"; break; @@ -246,11 +239,11 @@ static ErrorListModel::RelevantFrameFinder makeFrameFinder(const QStringList &pr { return [projectFiles](const Error &error) { const Frame defaultFrame = Frame(); - const QVector<Stack> stacks = error.stacks(); + const QList<Stack> stacks = error.stacks(); if (stacks.isEmpty()) return defaultFrame; const Stack &stack = stacks[0]; - const QVector<Frame> frames = stack.frames(); + const QList<Frame> frames = stack.frames(); if (frames.isEmpty()) return defaultFrame; @@ -349,7 +342,7 @@ bool MemcheckErrorFilterProxyModel::filterAcceptsRow(int sourceRow, const QModel } } - const QVector<Frame> frames = error.stacks().constFirst().frames(); + const QList<Frame> frames = error.stacks().constFirst().frames(); const int framesToLookAt = qMin(6, frames.size()); @@ -420,7 +413,7 @@ private: void heobAction(); private: - ValgrindBaseSettings *m_settings; + ValgrindSettings *m_settings; QMenu *m_filterMenu = nullptr; Valgrind::XmlProtocol::ErrorListModel m_errorModel; @@ -439,6 +432,7 @@ private: QAction *m_goNext; bool m_toolBusy = false; + std::unique_ptr<Parser> m_logParser; QString m_exitMsg; Perspective m_perspective{"Memcheck.Perspective", Tr::tr("Memcheck")}; @@ -519,7 +513,7 @@ private: MemcheckToolPrivate::MemcheckToolPrivate() { - m_settings = ValgrindGlobalSettings::instance(); + m_settings = &globalSettings(); setObjectName("MemcheckTool"); @@ -712,7 +706,7 @@ MemcheckToolPrivate::~MemcheckToolPrivate() void MemcheckToolPrivate::heobAction() { - Runnable sr; + ProcessRunData sr; Abi abi; bool hasLocalRc = false; Kit *kit = nullptr; @@ -784,7 +778,7 @@ void MemcheckToolPrivate::heobAction() // heob executable const QString heob = QString("heob%1.exe").arg(abi.wordWidth()); const QString heobPath = dialog.path() + '/' + heob; - if (!QFile::exists(heobPath)) { + if (!QFileInfo::exists(heobPath)) { QMessageBox::critical( Core::ICore::dialogParent(), Tr::tr("Heob"), @@ -797,7 +791,7 @@ void MemcheckToolPrivate::heobAction() if (abi.osFlavor() == Abi::WindowsMSysFlavor) { const QString dwarfstack = QString("dwarfstack%1.dll").arg(abi.wordWidth()); const QString dwarfstackPath = dialog.path() + '/' + dwarfstack; - if (!QFile::exists(dwarfstackPath) + if (!QFileInfo::exists(dwarfstackPath) && CheckableMessageBox::information( Core::ICore::dialogParent(), Tr::tr("Heob"), @@ -806,7 +800,7 @@ void MemcheckToolPrivate::heobAction() .arg( "<a " "href=\"https://github.com/ssbssa/dwarfstack/releases\">Dwarfstack</a>"), - QString("HeobDwarfstackInfo"), + Key("HeobDwarfstackInfo"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) @@ -888,14 +882,15 @@ void MemcheckToolPrivate::updateRunActions() m_startWithGdbAction->setToolTip(Tr::tr("A Valgrind Memcheck analysis is still in progress.")); m_stopAction->setEnabled(true); } else { - QString whyNot = Tr::tr("Start a Valgrind Memcheck analysis."); - bool canRun = ProjectExplorerPlugin::canRunStartupProject(MEMCHECK_RUN_MODE, &whyNot); - m_startAction->setToolTip(whyNot); - m_startAction->setEnabled(canRun); - whyNot = Tr::tr("Start a Valgrind Memcheck with GDB analysis."); - canRun = ProjectExplorerPlugin::canRunStartupProject(MEMCHECK_WITH_GDB_RUN_MODE, &whyNot); - m_startWithGdbAction->setToolTip(whyNot); - m_startWithGdbAction->setEnabled(canRun); + const auto canRun = ProjectExplorerPlugin::canRunStartupProject(MEMCHECK_RUN_MODE); + m_startAction->setToolTip(canRun ? Tr::tr("Start a Valgrind Memcheck analysis.") + : canRun.error()); + m_startAction->setEnabled(bool(canRun)); + const auto canRunGdb = ProjectExplorerPlugin::canRunStartupProject( + MEMCHECK_WITH_GDB_RUN_MODE); + m_startWithGdbAction->setToolTip( + canRunGdb ? Tr::tr("Start a Valgrind Memcheck with GDB analysis.") : canRunGdb.error()); + m_startWithGdbAction->setEnabled(bool(canRunGdb)); m_stopAction->setEnabled(false); } } @@ -903,19 +898,22 @@ void MemcheckToolPrivate::updateRunActions() void MemcheckToolPrivate::settingsDestroyed(QObject *settings) { QTC_ASSERT(m_settings == settings, return); - m_settings = ValgrindGlobalSettings::instance(); + m_settings = &globalSettings(); } void MemcheckToolPrivate::updateFromSettings() { + const QList<int> stored = m_settings->visibleErrorKinds(); for (QAction *action : std::as_const(m_errorFilterActions)) { bool contained = true; const QList<QVariant> actions = action->data().toList(); for (const QVariant &v : actions) { bool ok; int kind = v.toInt(&ok); - if (ok && !m_settings->visibleErrorKinds().contains(kind)) + if (ok && !stored.contains(kind)) { contained = false; + break; + } } action->setChecked(contained); } @@ -923,27 +921,28 @@ void MemcheckToolPrivate::updateFromSettings() m_filterProjectAction->setChecked(!m_settings->filterExternalIssues()); m_errorView->settingsChanged(m_settings); - connect(&m_settings->visibleErrorKinds, &IntegersAspect::valueChanged, - &m_errorProxyModel, &MemcheckErrorFilterProxyModel::setAcceptedKinds); m_errorProxyModel.setAcceptedKinds(m_settings->visibleErrorKinds()); - - connect(&m_settings->filterExternalIssues, &BoolAspect::valueChanged, - &m_errorProxyModel, &MemcheckErrorFilterProxyModel::setFilterExternalIssues); + connect(&m_settings->visibleErrorKinds, &BaseAspect::changed, &m_errorProxyModel, [this] { + m_errorProxyModel.setAcceptedKinds(m_settings->visibleErrorKinds()); + }); m_errorProxyModel.setFilterExternalIssues(m_settings->filterExternalIssues()); + connect(&m_settings->filterExternalIssues, &BaseAspect::changed, &m_errorProxyModel, [this] { + m_errorProxyModel.setFilterExternalIssues(m_settings->filterExternalIssues()); + }); } void MemcheckToolPrivate::maybeActiveRunConfigurationChanged() { updateRunActions(); - ValgrindBaseSettings *settings = nullptr; + ValgrindSettings *settings = nullptr; if (Project *project = ProjectManager::startupProject()) if (Target *target = project->activeTarget()) if (RunConfiguration *rc = target->activeRunConfiguration()) - settings = rc->currentSettings<ValgrindBaseSettings>(ANALYZER_VALGRIND_SETTINGS); + settings = rc->currentSettings<ValgrindSettings>(ANALYZER_VALGRIND_SETTINGS); if (!settings) // fallback to global settings - settings = ValgrindGlobalSettings::instance(); + settings = &globalSettings(); if (m_settings == settings) return; @@ -957,7 +956,7 @@ void MemcheckToolPrivate::maybeActiveRunConfigurationChanged() // now make the new settings current, update and connect input widgets m_settings = settings; QTC_ASSERT(m_settings, return); - connect(m_settings, &ValgrindBaseSettings::destroyed, + connect(m_settings, &ValgrindSettings::destroyed, this, &MemcheckToolPrivate::settingsDestroyed); updateFromSettings(); @@ -1030,9 +1029,8 @@ void MemcheckToolPrivate::loadExternalXmlLogFile() void MemcheckToolPrivate::loadXmlLogFile(const QString &filePath) { - auto logFile = new QFile(filePath); - if (!logFile->open(QIODevice::ReadOnly | QIODevice::Text)) { - delete logFile; + QFile logFile(filePath); + if (!logFile.open(QIODevice::ReadOnly | QIODevice::Text)) { QString msg = Tr::tr("Memcheck: Failed to open file for reading: %1").arg(filePath); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); @@ -1045,23 +1043,23 @@ void MemcheckToolPrivate::loadXmlLogFile(const QString &filePath) clearErrorView(); m_loadExternalLogFile->setDisabled(true); - if (!m_settings || m_settings != ValgrindGlobalSettings::instance()) { - m_settings = ValgrindGlobalSettings::instance(); + if (!m_settings || m_settings != &globalSettings()) { + m_settings = &globalSettings(); m_errorView->settingsChanged(m_settings); updateFromSettings(); } - auto parser = new ThreadedParser; - connect(parser, &ThreadedParser::error, - this, &MemcheckToolPrivate::parserError); - connect(parser, &ThreadedParser::internalError, - this, &MemcheckToolPrivate::internalParserError); - connect(parser, &ThreadedParser::finished, - this, &MemcheckToolPrivate::loadingExternalXmlLogFileFinished); - connect(parser, &ThreadedParser::finished, - parser, &ThreadedParser::deleteLater); + m_logParser.reset(new Parser); + connect(m_logParser.get(), &Parser::error, this, &MemcheckToolPrivate::parserError); + connect(m_logParser.get(), &Parser::done, this, [this](bool success, const QString &err) { + if (!success) + internalParserError(err); + loadingExternalXmlLogFileFinished(); + m_logParser.release()->deleteLater(); + }); - parser->parse(logFile); // ThreadedParser owns the file + m_logParser->setData(logFile.readAll()); + m_logParser->start(); } void MemcheckToolPrivate::parserError(const Error &error) @@ -1106,6 +1104,7 @@ void MemcheckToolPrivate::updateErrorFilter() } } m_settings->visibleErrorKinds.setValue(errorKinds); + m_settings->visibleErrorKinds.writeToSettingsImmediatly(); } int MemcheckToolPrivate::updateUiAfterFinishedHelper() @@ -1149,19 +1148,15 @@ MemcheckToolRunner::MemcheckToolRunner(RunControl *runControl) m_localServerAddress(QHostAddress::LocalHost) { setId("MemcheckToolRunner"); - connect(m_runner.parser(), &XmlProtocol::ThreadedParser::error, - this, &MemcheckToolRunner::parserError); - connect(m_runner.parser(), &XmlProtocol::ThreadedParser::suppressionCount, - this, &MemcheckToolRunner::suppressionCount); + connect(&m_runner, &ValgrindProcess::error, this, &MemcheckToolRunner::parserError); if (m_withGdb) { - connect(&m_runner, &ValgrindRunner::valgrindStarted, + connect(&m_runner, &ValgrindProcess::valgrindStarted, this, &MemcheckToolRunner::startDebugger); - connect(&m_runner, &ValgrindRunner::logMessageReceived, + connect(&m_runner, &ValgrindProcess::logMessageReceived, this, &MemcheckToolRunner::appendLog); -// m_runner.disableXml(); } else { - connect(m_runner.parser(), &XmlProtocol::ThreadedParser::internalError, + connect(&m_runner, &ValgrindProcess::internalError, this, &MemcheckToolRunner::internalParserError); } @@ -1175,7 +1170,6 @@ MemcheckToolRunner::MemcheckToolRunner(RunControl *runControl) dd->setupRunner(this); } - const char heobProfileC[] = "Heob/Profile"; const char heobProfileNameC[] = "Name"; const char heobXmlC[] = "Xml"; @@ -1193,7 +1187,7 @@ const char heobPathC[] = "Path"; HeobDialog::HeobDialog(QWidget *parent) : QDialog(parent) { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); bool hasSelProfile = settings->contains(heobProfileC); const QString selProfile = hasSelProfile ? settings->value(heobProfileC).toString() : "Heob"; m_profiles = settings->childGroups().filter(QRegularExpression("^Heob\\.Profile\\.")); @@ -1205,7 +1199,7 @@ HeobDialog::HeobDialog(QWidget *parent) : auto profilesLayout = new QHBoxLayout; m_profilesCombo = new QComboBox; for (const auto &profile : std::as_const(m_profiles)) - m_profilesCombo->addItem(settings->value(profile + "/" + heobProfileNameC).toString()); + m_profilesCombo->addItem(settings->value(keyFromString(profile) + "/" + heobProfileNameC).toString()); if (hasSelProfile) { int selIdx = m_profiles.indexOf(selProfile); if (selIdx >= 0) @@ -1408,8 +1402,9 @@ void HeobDialog::keyPressEvent(QKeyEvent *e) void HeobDialog::updateProfile() { - QSettings *settings = Core::ICore::settings(); - const QString selProfile = m_profiles.empty() ? "heob" : m_profiles[m_profilesCombo->currentIndex()]; + QtcSettings *settings = Core::ICore::settings(); + const Key selProfile = + keyFromString(m_profiles.empty() ? "heob" : m_profiles[m_profilesCombo->currentIndex()]); settings->beginGroup(selProfile); const QString xml = settings->value(heobXmlC, "leaks.xml").toString(); @@ -1462,12 +1457,12 @@ void HeobDialog::updateEnabled() void HeobDialog::saveOptions() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); const QString selProfile = m_profiles.at(m_profilesCombo->currentIndex()); settings->setValue(heobProfileC, selProfile); - settings->beginGroup(selProfile); + settings->beginGroup(keyFromString(selProfile)); settings->setValue(heobProfileNameC, m_profilesCombo->currentText()); settings->setValue(heobXmlC, m_xmlEdit->text()); settings->setValue(heobHandleExceptionC, m_handleExceptionCombo->currentIndex()); @@ -1534,11 +1529,11 @@ void HeobDialog::deleteProfileDialog() void HeobDialog::deleteProfile() { - QSettings *settings = Core::ICore::settings(); + QtcSettings *settings = Core::ICore::settings(); int index = m_profilesCombo->currentIndex(); const QString profile = m_profiles.at(index); bool isDefault = settings->value(heobProfileC).toString() == profile; - settings->remove(profile); + settings->remove(keyFromString(profile)); m_profiles.removeAt(index); m_profilesCombo->removeItem(index); if (isDefault) diff --git a/src/plugins/valgrind/suppressiondialog.cpp b/src/plugins/valgrind/suppressiondialog.cpp deleted file mode 100644 index 6273580e82d..00000000000 --- a/src/plugins/valgrind/suppressiondialog.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "suppressiondialog.h" - -#include "memcheckerrorview.h" -#include "valgrindsettings.h" -#include "valgrindtr.h" - -#include "xmlprotocol/suppression.h" -#include "xmlprotocol/errorlistmodel.h" -#include "xmlprotocol/stack.h" -#include "xmlprotocol/frame.h" - -#include <projectexplorer/project.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/projectmanager.h> -#include <projectexplorer/projectnodes.h> - -#include <utils/algorithm.h> -#include <utils/pathchooser.h> -#include <utils/fileutils.h> -#include <utils/qtcassert.h> - -#include <QDialogButtonBox> -#include <QFile> -#include <QFormLayout> -#include <QLabel> -#include <QPlainTextEdit> -#include <QPushButton> - -using namespace Utils; -using namespace Valgrind::XmlProtocol; - -namespace Valgrind { -namespace Internal { - -static QString suppressionText(const Error &error) -{ - Suppression sup(error.suppression()); - - // workaround: https://bugs.kde.org/show_bug.cgi?id=255822 - if (sup.frames().size() >= 24) - sup.setFrames(sup.frames().mid(0, 23)); - QTC_CHECK(sup.frames().size() < 24); - - // try to set some useful name automatically, instead of "insert_name_here" - // we take the last stack frame and append the suppression kind, e.g.: - // QDebug::operator<<(bool) [Memcheck:Cond] - if (!error.stacks().isEmpty() && !error.stacks().constFirst().frames().isEmpty()) { - const Frame frame = error.stacks().constFirst().frames().constFirst(); - - QString newName; - if (!frame.functionName().isEmpty()) - newName = frame.functionName(); - else if (!frame.object().isEmpty()) - newName = frame.object(); - - if (!newName.isEmpty()) - sup.setName(newName + '[' + sup.kind() + ']'); - } - - return sup.toString(); -} - -/// @p error input error, which might get hidden when it has the same stack -/// @p suppressed the error that got suppressed already -static bool equalSuppression(const Error &error, const Error &suppressed) -{ - if (error.kind() != suppressed.kind() || error.suppression().isNull()) - return false; - - const SuppressionFrames errorFrames = error.suppression().frames(); - const SuppressionFrames suppressedFrames = suppressed.suppression().frames(); - - // limit to 23 frames, see: https://bugs.kde.org/show_bug.cgi?id=255822 - if (qMin(23, suppressedFrames.size()) > errorFrames.size()) - return false; - - int frames = 23; - if (errorFrames.size() < frames) - frames = errorFrames.size(); - - if (suppressedFrames.size() < frames) - frames = suppressedFrames.size(); - - for (int i = 0; i < frames; ++i) - if (errorFrames.at(i) != suppressedFrames.at(i)) - return false; - - return true; -} - -SuppressionDialog::SuppressionDialog(MemcheckErrorView *view, const QList<Error> &errors) - : m_view(view), - m_settings(view->settings()), - m_cleanupIfCanceled(false), - m_errors(errors), - m_fileChooser(new PathChooser(this)), - m_suppressionEdit(new QPlainTextEdit(this)) -{ - setWindowTitle(Tr::tr("Save Suppression")); - - auto fileLabel = new QLabel(Tr::tr("Suppression File:"), this); - - auto suppressionsLabel = new QLabel(Tr::tr("Suppression:"), this); - suppressionsLabel->setBuddy(m_suppressionEdit); - - QFont font; - font.setFamily("Monospace"); - m_suppressionEdit->setFont(font); - - m_buttonBox = new QDialogButtonBox(this); - m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Save); - - auto formLayout = new QFormLayout(this); - formLayout->addRow(fileLabel, m_fileChooser); - formLayout->addRow(suppressionsLabel); - formLayout->addRow(m_suppressionEdit); - formLayout->addRow(m_buttonBox); - - const FilePath defaultSuppFile = view->defaultSuppressionFile(); - if (!defaultSuppFile.exists() && defaultSuppFile.ensureExistingFile()) - m_cleanupIfCanceled = true; - - m_fileChooser->setExpectedKind(PathChooser::File); - m_fileChooser->setHistoryCompleter("Valgrind.Suppression.History"); - m_fileChooser->setPath(defaultSuppFile.fileName()); - m_fileChooser->setPromptDialogFilter("*.supp"); - m_fileChooser->setPromptDialogTitle(Tr::tr("Select Suppression File")); - - QString suppressions; - for (const Error &error : std::as_const(m_errors)) - suppressions += suppressionText(error); - - m_suppressionEdit->setPlainText(suppressions); - - connect(m_fileChooser, &PathChooser::validChanged, - this, &SuppressionDialog::validate); - connect(m_suppressionEdit->document(), &QTextDocument::contentsChanged, - this, &SuppressionDialog::validate); - connect(m_buttonBox, &QDialogButtonBox::accepted, - this, &SuppressionDialog::accept); - connect(m_buttonBox, &QDialogButtonBox::rejected, - this, &SuppressionDialog::reject); -} - -void SuppressionDialog::maybeShow(MemcheckErrorView *view) -{ - QModelIndexList indices = view->selectionModel()->selectedRows(); - // Can happen when using arrow keys to navigate and shortcut to trigger suppression: - if (indices.isEmpty() && view->selectionModel()->currentIndex().isValid()) - indices.append(view->selectionModel()->currentIndex()); - - QList<XmlProtocol::Error> errors; - for (const QModelIndex &index : std::as_const(indices)) { - Error error = view->model()->data(index, ErrorListModel::ErrorRole).value<Error>(); - if (!error.suppression().isNull()) - errors.append(error); - } - - if (errors.isEmpty()) - return; - - SuppressionDialog dialog(view, errors); - dialog.exec(); -} - -void SuppressionDialog::accept() -{ - const FilePath path = m_fileChooser->filePath(); - QTC_ASSERT(!path.isEmpty(), return); - QTC_ASSERT(!m_suppressionEdit->toPlainText().trimmed().isEmpty(), return); - - FileSaver saver(path, QIODevice::Append); - if (!saver.hasError()) { - QTextStream stream(saver.file()); - stream << m_suppressionEdit->toPlainText(); - saver.setResult(&stream); - } - if (!saver.finalize(this)) - return; - - // Add file to project if there is a project containing this file on the file system. - if (!ProjectExplorer::ProjectManager::projectForFile(path)) { - for (ProjectExplorer::Project *p : ProjectExplorer::ProjectManager::projects()) { - if (path.startsWith(p->projectDirectory().toString())) { - p->rootProjectNode()->addFiles({path}); - break; - } - } - } - - m_settings->suppressions.addSuppressionFile(path); - - const QModelIndexList indices = Utils::sorted(m_view->selectionModel()->selectedRows(), - [](const QModelIndex &l, const QModelIndex &r) { - return l.row() > r.row(); - }); - QAbstractItemModel *model = m_view->model(); - for (const QModelIndex &index : indices) { - bool removed = model->removeRow(index.row()); - QTC_ASSERT(removed, qt_noop()); - Q_UNUSED(removed) - } - - // One suppression might hide multiple rows, care for that. - for (int row = 0; row < model->rowCount(); ++row ) { - const Error rowError = model->data( - model->index(row, 0), ErrorListModel::ErrorRole).value<Error>(); - - for (const Error &error : std::as_const(m_errors)) { - if (equalSuppression(rowError, error)) { - bool removed = model->removeRow(row); - QTC_CHECK(removed); - // Gets incremented in the for loop again. - --row; - break; - } - } - } - - // Select a new item. - m_view->setCurrentIndex(indices.first()); - - QDialog::accept(); -} - -void SuppressionDialog::reject() -{ - if (m_cleanupIfCanceled) - m_view->defaultSuppressionFile().removeFile(); - - QDialog::reject(); -} - -void SuppressionDialog::validate() -{ - bool valid = m_fileChooser->isValid() - && !m_suppressionEdit->toPlainText().trimmed().isEmpty(); - - m_buttonBox->button(QDialogButtonBox::Save)->setEnabled(valid); -} - -} // namespace Internal -} // namespace Valgrind diff --git a/src/plugins/valgrind/suppressiondialog.h b/src/plugins/valgrind/suppressiondialog.h deleted file mode 100644 index b4aac0d644d..00000000000 --- a/src/plugins/valgrind/suppressiondialog.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "xmlprotocol/error.h" - -#include <utils/pathchooser.h> - -#include <QDialog> - -QT_BEGIN_NAMESPACE -class QPlainTextEdit; -class QDialogButtonBox; -QT_END_NAMESPACE - -namespace Valgrind { -namespace Internal { - -class MemcheckErrorView; -class ValgrindBaseSettings; - -class SuppressionDialog : public QDialog -{ -public: - SuppressionDialog(MemcheckErrorView *view, - const QList<XmlProtocol::Error> &errors); - static void maybeShow(MemcheckErrorView *view); - -private: - void validate(); - void accept() override; - void reject() override; - - MemcheckErrorView *m_view; - ValgrindBaseSettings *m_settings; - bool m_cleanupIfCanceled; - QList<XmlProtocol::Error> m_errors; - - Utils::PathChooser *m_fileChooser; - QPlainTextEdit *m_suppressionEdit; - QDialogButtonBox *m_buttonBox; -}; - -} // namespace Internal -} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrind.qbs b/src/plugins/valgrind/valgrind.qbs index 973b1d7b85a..049f75fc77d 100644 --- a/src/plugins/valgrind/valgrind.qbs +++ b/src/plugins/valgrind/valgrind.qbs @@ -28,12 +28,10 @@ QtcPlugin { "callgrindvisualisation.cpp", "callgrindvisualisation.h", "memcheckerrorview.cpp", "memcheckerrorview.h", "memchecktool.cpp", "memchecktool.h", - "suppressiondialog.cpp", "suppressiondialog.h", "valgrind.qrc", - "valgrindconfigwidget.cpp", "valgrindconfigwidget.h", "valgrindengine.cpp", "valgrindengine.h", "valgrindplugin.cpp", - "valgrindrunner.cpp", "valgrindrunner.h", + "valgrindprocess.cpp", "valgrindprocess.h", "valgrindsettings.cpp", "valgrindsettings.h", "valgrindtr.h", ] @@ -66,13 +64,11 @@ QtcPlugin { "error.cpp", "error.h", "errorlistmodel.cpp", "errorlistmodel.h", "frame.cpp", "frame.h", - "modelhelpers.cpp", "modelhelpers.h", "parser.cpp", "parser.h", "stack.cpp", "stack.h", "stackmodel.cpp", "stackmodel.h", "status.cpp", "status.h", "suppression.cpp", "suppression.h", - "threadedparser.cpp", "threadedparser.h", ] } diff --git a/src/plugins/valgrind/valgrindconfigwidget.cpp b/src/plugins/valgrind/valgrindconfigwidget.cpp deleted file mode 100644 index 2200eb5b254..00000000000 --- a/src/plugins/valgrind/valgrindconfigwidget.cpp +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "valgrindconfigwidget.h" -#include "valgrindsettings.h" -#include "valgrindtr.h" - -#include <debugger/analyzer/analyzericons.h> -#include <debugger/debuggertr.h> - -#include <coreplugin/icore.h> - -#include <utils/algorithm.h> -#include <utils/layoutbuilder.h> -#include <utils/qtcassert.h> - -using namespace Utils; - -namespace Valgrind { -namespace Internal { - -class ValgrindConfigWidget : public Core::IOptionsPageWidget -{ -public: - explicit ValgrindConfigWidget(ValgrindBaseSettings *settings); - - void apply() final - { - ValgrindGlobalSettings::instance()->apply(); - ValgrindGlobalSettings::instance()->writeSettings(); - } - - void finish() final - { - ValgrindGlobalSettings::instance()->finish(); - } -}; - -ValgrindConfigWidget::ValgrindConfigWidget(ValgrindBaseSettings *settings) -{ - using namespace Layouting; - ValgrindBaseSettings &s = *settings; - // clang-format off - Grid generic { - s.valgrindExecutable, br, - s.valgrindArguments, br, - s.selfModifyingCodeDetection, br - }; - - Grid memcheck { - s.memcheckArguments, br, - s.trackOrigins, br, - s.showReachable, br, - s.leakCheckOnFinish, br, - s.numCallers, br, - s.filterExternalIssues, br, - s.suppressions - }; - - Grid callgrind { - s.callgrindArguments, br, - s.kcachegrindExecutable, br, - s.minimumInclusiveCostRatio, br, - s.visualizationMinimumInclusiveCostRatio, br, - s.enableEventToolTips, br, - Span { - 2, - Group { - Column { - s.enableCacheSim, - s.enableBranchSim, - s.collectSystime, - s.collectBusEvents, - } - } - } - }; - - Column { - Group { title(Tr::tr("Valgrind Generic Settings")), generic }, - Group { title(Tr::tr("Memcheck Memory Analysis Options")), memcheck }, - Group { title(Tr::tr("Callgrind Profiling Options")), callgrind }, - st, - }.attachTo(this); - // clang-format on -} - -// ValgrindOptionsPage - -ValgrindOptionsPage::ValgrindOptionsPage() -{ - setId(ANALYZER_VALGRIND_SETTINGS); - setDisplayName(Tr::tr("Valgrind")); - setCategory("T.Analyzer"); - setDisplayCategory(::Debugger::Tr::tr("Analyzer")); - setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); - setWidgetCreator([] { return new ValgrindConfigWidget(ValgrindGlobalSettings::instance()); }); -} - -QWidget *ValgrindOptionsPage::createSettingsWidget(ValgrindBaseSettings *settings) -{ - return new ValgrindConfigWidget(settings); -} - -} // namespace Internal -} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrindconfigwidget.h b/src/plugins/valgrind/valgrindconfigwidget.h deleted file mode 100644 index b6353cc2ef7..00000000000 --- a/src/plugins/valgrind/valgrindconfigwidget.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <coreplugin/dialogs/ioptionspage.h> - -namespace Valgrind::Internal { - -class ValgrindOptionsPage final : public Core::IOptionsPage -{ -public: - ValgrindOptionsPage(); - - static QWidget *createSettingsWidget(class ValgrindBaseSettings *settings); -}; - -} // Valgrind::Internal diff --git a/src/plugins/valgrind/valgrindengine.cpp b/src/plugins/valgrind/valgrindengine.cpp index 61ef89c3041..8254f9284bd 100644 --- a/src/plugins/valgrind/valgrindengine.cpp +++ b/src/plugins/valgrind/valgrindengine.cpp @@ -6,23 +6,19 @@ #include "valgrindsettings.h" #include "valgrindtr.h" -#include <debugger/analyzer/analyzermanager.h> - #include <coreplugin/icore.h> #include <coreplugin/ioutputpane.h> -#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/futureprogress.h> +#include <coreplugin/progressmanager/progressmanager.h> + #include <extensionsystem/pluginmanager.h> #include <projectexplorer/devicesupport/idevice.h> -#include <projectexplorer/kitinformation.h> +#include <projectexplorer/kitaspects.h> #include <projectexplorer/projectexplorericons.h> -#include <projectexplorer/runconfiguration.h> -#include <projectexplorer/runconfigurationaspects.h> #include <QApplication> -using namespace Debugger; using namespace Core; using namespace Utils; using namespace ProjectExplorer; @@ -37,17 +33,11 @@ ValgrindToolRunner::ValgrindToolRunner(RunControl *runControl) m_settings.fromMap(runControl->settingsData(ANALYZER_VALGRIND_SETTINGS)); - connect(&m_runner, - &ValgrindRunner::appendMessage, - this, + connect(&m_runner, &ValgrindProcess::appendMessage, this, [this](const QString &msg, Utils::OutputFormat format) { appendMessage(msg, format); }); - connect(&m_runner, &ValgrindRunner::valgrindExecuted, - this, [this](const QString &commandLine) { - appendMessage(commandLine, NormalMessageFormat); - }); - connect(&m_runner, &ValgrindRunner::processErrorReceived, + connect(&m_runner, &ValgrindProcess::processErrorReceived, this, &ValgrindToolRunner::receiveProcessError); - connect(&m_runner, &ValgrindRunner::finished, + connect(&m_runner, &ValgrindProcess::done, this, &ValgrindToolRunner::runnerFinished); } @@ -103,17 +93,17 @@ QStringList ValgrindToolRunner::genericToolArguments() const { QString smcCheckValue; - switch (m_settings.selfModifyingCodeDetection.value()) { - case ValgrindBaseSettings::DetectSmcNo: + switch (m_settings.selfModifyingCodeDetection()) { + case ValgrindSettings::DetectSmcNo: smcCheckValue = "none"; break; - case ValgrindBaseSettings::DetectSmcEverywhere: + case ValgrindSettings::DetectSmcEverywhere: smcCheckValue = "all"; break; - case ValgrindBaseSettings::DetectSmcEverywhereButFile: + case ValgrindSettings::DetectSmcEverywhereButFile: smcCheckValue = "all-non-file"; break; - case ValgrindBaseSettings::DetectSmcStackOnly: + case ValgrindSettings::DetectSmcStackOnly: default: smcCheckValue = "stack"; break; @@ -144,11 +134,13 @@ void ValgrindToolRunner::runnerFinished() void ValgrindToolRunner::receiveProcessError(const QString &message, QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { - const QString valgrind = m_settings.valgrindExecutable.value(); - if (!valgrind.isEmpty()) - appendMessage(Tr::tr("Error: \"%1\" could not be started: %2").arg(valgrind, message), ErrorMessageFormat); - else + const FilePath valgrind = m_settings.valgrindExecutable(); + if (!valgrind.isEmpty()) { + appendMessage(Tr::tr("Error: \"%1\" could not be started: %2") + .arg(valgrind.toUserOutput(), message), ErrorMessageFormat); + } else { appendMessage(Tr::tr("Error: no Valgrind executable set."), ErrorMessageFormat); + } } else if (m_isStopping && error == QProcess::Crashed) { // process gets killed on stop appendMessage(Tr::tr("Process terminated."), ErrorMessageFormat); } else { diff --git a/src/plugins/valgrind/valgrindengine.h b/src/plugins/valgrind/valgrindengine.h index d491c7a8476..c3d23e7e7ab 100644 --- a/src/plugins/valgrind/valgrindengine.h +++ b/src/plugins/valgrind/valgrindengine.h @@ -3,10 +3,10 @@ #pragma once +#include "valgrindprocess.h" #include "valgrindsettings.h" #include <projectexplorer/runcontrol.h> -#include <valgrind/valgrindrunner.h> #include <QFutureInterface> @@ -24,9 +24,9 @@ protected: virtual QString progressTitle() const = 0; virtual void addToolArguments(Utils::CommandLine &cmd) const = 0; - ValgrindProjectSettings m_settings; + ValgrindSettings m_settings{false}; QFutureInterface<void> m_progress; - ValgrindRunner m_runner; + ValgrindProcess m_runner; private: void handleProgressCanceled(); diff --git a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp index 1d3a8a0e323..45780602d9d 100644 --- a/src/plugins/valgrind/valgrindmemcheckparsertest.cpp +++ b/src/plugins/valgrind/valgrindmemcheckparsertest.cpp @@ -3,23 +3,19 @@ #include "valgrindmemcheckparsertest.h" +#include "valgrindprocess.h" +#include "xmlprotocol/error.h" #include "xmlprotocol/frame.h" #include "xmlprotocol/parser.h" #include "xmlprotocol/stack.h" +#include "xmlprotocol/status.h" #include "xmlprotocol/suppression.h" -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/runconfiguration.h> -#include <projectexplorer/runcontrol.h> +#include <utils/processinterface.h> -#include <QFile> #include <QFileInfo> -#include <QProcess> -#include <QString> -#include <QTest> #include <QTcpServer> -#include <QTcpSocket> -#include <QSignalSpy> +#include <QTest> using namespace Utils; using namespace Valgrind::XmlProtocol; @@ -28,7 +24,7 @@ QT_BEGIN_NAMESPACE namespace QTest { template<> -inline bool qCompare(int const &t1, MemcheckErrorKind const &t2, +inline bool qCompare(int const &t1, MemcheckError const &t2, char const *actual, char const *expected, char const *file, int line) { return qCompare(t1, int(t2), actual, expected, file, line); @@ -43,16 +39,9 @@ inline bool qCompare(const QString &t1, char const *t2, } // namespace QTest QT_END_NAMESPACE -namespace Valgrind { -namespace Test { +namespace Valgrind::Test { -static void dumpFrame(const Frame &f) -{ - qDebug() << f.instructionPointer() << f.directory() << f.fileName() << f.functionName() - << f.line() << f.object(); -} - -void dumpError(const Error &e) +static void dumpError(const Error &e) { qDebug() << e.kind() << e.leakedBlocks() << e.leakedBytes() << e.what() << e.tid() << e.unique(); qDebug() << "stacks:" << e.stacks().size(); @@ -60,15 +49,61 @@ void dumpError(const Error &e) qDebug() << s.auxWhat() << s.directory() << s.file() << s.line() << s.helgrindThreadId(); qDebug() << "frames:"; for (const Frame &f : s.frames()) { - dumpFrame(f); + qDebug() << f.instructionPointer() << f.directory() << f.fileName() << f.functionName() + << f.line() << f.object(); } } } +class Recorder : public QObject +{ +public: + explicit Recorder(Parser *parser) + { + connect(parser, &Parser::error, this, [this](const Error &err) { errors.append(err); }); + connect(parser, &Parser::errorCount, this, [this](qint64 unique, qint64 count) { + errorcounts.push_back({unique, count}); + }); + connect(parser, &Parser::suppressionCount, this, [this](const QString &name, qint64 count) { + suppcounts.push_back({name, count}); + }); + } + + QList<Error> errors; + QList<QPair<qint64, qint64>> errorcounts; + QList<QPair<QString, qint64>> suppcounts; +}; + +class RunnerDumper : public QObject +{ +public: + explicit RunnerDumper(ValgrindProcess *runner) + { + connect(runner, &ValgrindProcess::error, this, [](const Error &err) { + qDebug() << "error received"; + dumpError(err); + }); + connect(runner, &ValgrindProcess::internalError, this, [](const QString &err) { + qDebug() << "internal error received:" << err; + }); + connect(runner, &ValgrindProcess::status, this, [](const Status &status) { + qDebug() << "status received:" << status.state() << status.time(); + }); + connect(runner, &ValgrindProcess::logMessageReceived, this, [](const QByteArray &log) { + qDebug() << "log message received:" << log; + }); + connect(runner, &ValgrindProcess::processErrorReceived, this, [this](const QString &) { + m_errorReceived = true; + }); + } + + bool m_errorReceived = false; +}; + static QString fakeValgrindExecutable() { - QString valgrindFakePath(VALGRIND_FAKE_PATH); - if (Utils::HostOsInfo::isWindowsHost()) { + const QString valgrindFakePath(VALGRIND_FAKE_PATH); + if (HostOsInfo::isWindowsHost()) { QFileInfo fi(QString(valgrindFakePath + "/debug"), "valgrind-fake.exe"); if (fi.exists()) return fi.canonicalFilePath(); @@ -94,12 +129,12 @@ static QString extraDataFile(const QString &file) // Clone test data from: https://git.qt.io/chstenge/creator-test-data static QString prefix = qtcEnvironmentVariable("QTC_TEST_EXTRADATALOCATION"); if (prefix.isEmpty()) - return QString(); + return {}; - QFileInfo fi(QString(prefix + "/valgrind"), file); + const QFileInfo fi(QString(prefix + "/valgrind"), file); if (fi.exists()) return fi.canonicalFilePath(); - return QString(); + return {}; } void ValgrindMemcheckParserTest::initTestCase() @@ -112,36 +147,30 @@ void ValgrindMemcheckParserTest::initTest(const QString &testfile, const QString { QVERIFY(!m_server->hasPendingConnections()); - m_process = new QProcess(m_server); + m_process.reset(new Process); m_process->setProcessChannelMode(QProcess::ForwardedChannels); const QString fakeValgrind = fakeValgrindExecutable(); - QFileInfo fileInfo(fakeValgrind); + const QFileInfo fileInfo(fakeValgrind); QVERIFY2(fileInfo.isExecutable(), qPrintable(fakeValgrind)); QVERIFY2(!fileInfo.isDir(), qPrintable(fakeValgrind)); - m_process->start( - fakeValgrind, - QStringList({QString("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort()), "-i", - testfile}) << otherArgs); + const QStringList args = {QString("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort()), + "-i", testfile}; + m_process->setCommand({FilePath::fromString(fakeValgrind), args + otherArgs}); + m_process->start(); QVERIFY(m_process->waitForStarted(5000)); QCOMPARE(m_process->state(), QProcess::Running); QVERIFY2(m_process->error() == QProcess::UnknownError, qPrintable(m_process->errorString())); QVERIFY(m_server->waitForNewConnection(5000)); - m_socket = m_server->nextPendingConnection(); + m_socket.reset(m_server->nextPendingConnection()); QVERIFY(m_socket); } void ValgrindMemcheckParserTest::cleanup() { - if (m_socket) { - delete m_socket; - m_socket = nullptr; - } - if (m_process) { - delete m_process; - m_process = nullptr; - } + m_socket.reset(); + m_process.reset(); } void ValgrindMemcheckParserTest::testHelgrindSample1() @@ -175,7 +204,7 @@ void ValgrindMemcheckParserTest::testHelgrindSample1() frame12.setDirectory("/home/frank/source/tarballs/qt-4.6.3-build/src/corelib/../../include/QtCore/../../src/corelib/thread"); frame12.setFileName("qmutex.h"); frame12.setLine(120); - stack1.setFrames(QVector<Frame>() << frame11 << frame12); + stack1.setFrames({frame11, frame12}); Stack stack2; stack2.setAuxWhat("Required order was established by acquisition of lock at 0xA39C270"); @@ -193,7 +222,7 @@ void ValgrindMemcheckParserTest::testHelgrindSample1() frame22.setDirectory("/home/frank/source/tarballs/qt-4.6.3-build/src/corelib/../../include/QtCore/../../src/corelib/thread"); frame22.setFileName("qmutex.h"); frame22.setLine(121); - stack2.setFrames(QVector<Frame>() << frame21 << frame22); + stack2.setFrames({frame21, frame22}); Stack stack3; stack3.setAuxWhat("followed by a later acquisition of lock at 0xA3AC010"); @@ -212,15 +241,16 @@ void ValgrindMemcheckParserTest::testHelgrindSample1() frame32.setFileName("qmutex.h"); frame32.setLine(122); - stack3.setFrames(QVector<Frame>() << frame31 << frame32); - error1.setStacks(QVector<Stack>() << stack1 << stack2 << stack3); + stack3.setFrames({frame31, frame32}); + error1.setStacks({stack1, stack2, stack3}); expectedErrors.append(error1); } Parser parser; Recorder rec(&parser); - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -228,7 +258,7 @@ void ValgrindMemcheckParserTest::testHelgrindSample1() QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString())); const QList<Error> actualErrors = rec.errors; - + QVERIFY(!actualErrors.isEmpty()); if (actualErrors.first() != expectedErrors.first()) { dumpError(actualErrors.first()); dumpError(expectedErrors.first()); @@ -279,14 +309,14 @@ void ValgrindMemcheckParserTest::testMemcheckSample1() f4.setLine(4396); Stack s1; s1.setAuxWhat("Address 0x11527cb8 is not stack'd, malloc'd or (recently) free'd"); - s1.setFrames(QVector<Frame>() << f1 << f2 << f3 << f4); - error.setStacks( QVector<Stack>() << s1 ); + s1.setFrames({f1, f2, f3, f4}); + error.setStacks({s1}); expectedErrors << error; } - const QVector<QPair<qint64,qint64>> expectedErrorCounts{{9, 2}}; - const QVector<QPair<QString,qint64>> expectedSuppCounts{ + const QList<QPair<qint64, qint64>> expectedErrorCounts{{9, 2}}; + const QList<QPair<QString, qint64>> expectedSuppCounts{ {QString("X on SUSE11 writev uninit padding"), 12}, {QString("dl-hack3-cond-1"), 2}, {QString("glibc-2.5.x-on-SUSE-10.2-(PPC)-2a"), 2}}; @@ -294,7 +324,8 @@ void ValgrindMemcheckParserTest::testMemcheckSample1() Parser parser; Recorder rec(&parser); - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -302,7 +333,7 @@ void ValgrindMemcheckParserTest::testMemcheckSample1() QVERIFY2(parser.errorString().isEmpty(), qPrintable(parser.errorString())); const QList<Error> actualErrors = rec.errors; - + QVERIFY(!actualErrors.isEmpty()); if (actualErrors.first() != expectedErrors.first()) { dumpError(actualErrors.first()); dumpError(expectedErrors.first()); @@ -327,7 +358,8 @@ void ValgrindMemcheckParserTest::testMemcheckSample2() Parser parser; Recorder rec(&parser); - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -338,7 +370,7 @@ void ValgrindMemcheckParserTest::testMemcheckSample2() //the first auxwhat should be assigned to the _second_ stack. const QList<Error> errors = rec.errors; QCOMPARE(errors.size(), 1); - const QVector<Stack> stacks = errors.first().stacks(); + const QList<Stack> stacks = errors.first().stacks(); QCOMPARE(stacks.size(), 2); QCOMPARE(stacks.first().auxWhat(), QString()); QCOMPARE(stacks.last().auxWhat(), "Address 0x11b66c50 is 0 bytes inside a block of size 16 free'd"); @@ -355,7 +387,8 @@ void ValgrindMemcheckParserTest::testMemcheckSample3() Parser parser; Recorder rec(&parser); - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -367,7 +400,7 @@ void ValgrindMemcheckParserTest::testMemcheckSample3() { const Error error = errors.at(0); - const QVector<Stack> stacks = error.stacks(); + const QList<Stack> stacks = error.stacks(); QCOMPARE(error.unique(), 0x1ll); QCOMPARE(error.what(), "Conditional jump or move depends on uninitialised value(s)"); @@ -410,7 +443,8 @@ void ValgrindMemcheckParserTest::testMemcheckCharm() Parser parser; Recorder rec(&parser); - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -426,14 +460,13 @@ void ValgrindMemcheckParserTest::testValgrindCrash() initTest(dataFile("memcheck-output-sample1.xml"), QStringList("--crash")); Parser parser; - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->state(), QProcess::NotRunning); QCOMPARE(m_process->exitStatus(), QProcess::CrashExit); QVERIFY(!parser.errorString().isEmpty()); - QCOMPARE(m_socket->error(), QAbstractSocket::RemoteHostClosedError); - QCOMPARE(parser.errorString(), m_socket->errorString()); } void ValgrindMemcheckParserTest::testValgrindGarbage() @@ -441,7 +474,8 @@ void ValgrindMemcheckParserTest::testValgrindGarbage() initTest(dataFile("memcheck-output-sample1.xml"), QStringList("--garbage")); Parser parser; - parser.parse(m_socket); + parser.setSocket(m_socket.release()); + parser.runBlocking(); m_process->waitForFinished(); QCOMPARE(m_process->state(), QProcess::NotRunning); QCOMPARE(m_process->exitStatus(), QProcess::NormalExit); @@ -452,7 +486,7 @@ void ValgrindMemcheckParserTest::testValgrindGarbage() void ValgrindMemcheckParserTest::testParserStop() { - ValgrindRunner runner; + ValgrindProcess runner; runner.setValgrindCommand({FilePath::fromString(fakeValgrindExecutable()), {QString("--xml-socket=127.0.0.1:%1").arg(m_server->serverPort()), "-i", dataFile("memcheck-output-sample1.xml"), "--wait", "5" }}); @@ -465,22 +499,22 @@ void ValgrindMemcheckParserTest::testParserStop() void ValgrindMemcheckParserTest::testRealValgrind() { - const Utils::Environment &sysEnv = Utils::Environment::systemEnvironment(); - auto fileName = sysEnv.searchInPath("valgrind"); + const Environment &sysEnv = Environment::systemEnvironment(); + const auto fileName = sysEnv.searchInPath("valgrind"); if (fileName.isEmpty()) QSKIP("This test needs valgrind in PATH"); - QString executable = QProcessEnvironment::systemEnvironment().value("VALGRIND_TEST_BIN", fakeValgrindExecutable()); + QString executable = QProcessEnvironment::systemEnvironment().value("VALGRIND_TEST_BIN", + fakeValgrindExecutable()); qDebug() << "running exe:" << executable << " HINT: set VALGRIND_TEST_BIN to change this"; - ProjectExplorer::Runnable debuggee; + ProcessRunData debuggee; debuggee.command.setExecutable(FilePath::fromString(executable)); debuggee.environment = sysEnv; - ValgrindRunner runner; + ValgrindProcess runner; runner.setValgrindCommand({"valgrind", {}}); runner.setDebuggee(debuggee); RunnerDumper dumper(&runner); - runner.start(); - runner.waitForFinished(); + runner.runBlocking(); } void ValgrindMemcheckParserTest::testValgrindStartError_data() @@ -507,20 +541,18 @@ void ValgrindMemcheckParserTest::testValgrindStartError() QFETCH(QString, debuggee); QFETCH(QString, debuggeeArgs); - ProjectExplorer::Runnable debuggeeExecutable; + ProcessRunData debuggeeExecutable; debuggeeExecutable.command.setExecutable(FilePath::fromString(debuggee)); debuggeeExecutable.command.setArguments(debuggeeArgs); - debuggeeExecutable.environment = Utils::Environment::systemEnvironment(); + debuggeeExecutable.environment = Environment::systemEnvironment(); - ValgrindRunner runner; + ValgrindProcess runner; runner.setValgrindCommand({FilePath::fromString(valgrindExe), valgrindArgs}); runner.setDebuggee(debuggeeExecutable); RunnerDumper dumper(&runner); - runner.start(); - runner.waitForFinished(); + runner.runBlocking(); QVERIFY(dumper.m_errorReceived); // just finish without deadlock and we are fine } -} // namespace Test -} // namespace Valgrind +} // namespace Valgrind::Test diff --git a/src/plugins/valgrind/valgrindmemcheckparsertest.h b/src/plugins/valgrind/valgrindmemcheckparsertest.h index 96fd6ad186d..0d10ac0617f 100644 --- a/src/plugins/valgrind/valgrindmemcheckparsertest.h +++ b/src/plugins/valgrind/valgrindmemcheckparsertest.h @@ -3,108 +3,16 @@ #pragma once -#include <QObject> -#include <QPair> -#include <QStringList> -#include <QVector> -#include <QDebug> +#include <utils/process.h> -#include "xmlprotocol/error.h" -#include "xmlprotocol/status.h" -#include "xmlprotocol/threadedparser.h" -#include "xmlprotocol/parser.h" -#include "valgrindrunner.h" +#include <QStringList> +#include <QTcpSocket> QT_BEGIN_NAMESPACE class QTcpServer; -class QTcpSocket; -class QProcess; QT_END_NAMESPACE -namespace Valgrind { -namespace Test { - -void dumpError(const Valgrind::XmlProtocol::Error &e); - -class Recorder : public QObject -{ -public: - explicit Recorder(XmlProtocol::Parser *parser) - { - connect(parser, &XmlProtocol::Parser::error, - this, &Recorder::error); - connect(parser, &XmlProtocol::Parser::errorCount, - this, &Recorder::errorCount); - connect(parser, &XmlProtocol::Parser::suppressionCount, - this, &Recorder::suppressionCount); - } - - QList<Valgrind::XmlProtocol::Error> errors; - QVector<QPair<qint64,qint64> > errorcounts; - QVector<QPair<QString,qint64> > suppcounts; - -public: - void error(const Valgrind::XmlProtocol::Error &err) - { - errors.append(err); - } - - void errorCount(qint64 unique, qint64 count) - { - errorcounts.push_back({unique, count}); - } - - void suppressionCount(const QString &name, qint64 count) - { - suppcounts.push_back({name, count}); - } - -}; - -class RunnerDumper : public QObject -{ -public: - explicit RunnerDumper(ValgrindRunner *runner) - { - connect(runner->parser(), &XmlProtocol::ThreadedParser::error, - this, &RunnerDumper::error); - connect(runner->parser(), &XmlProtocol::ThreadedParser::internalError, - this, &RunnerDumper::internalError); - connect(runner->parser(), &XmlProtocol::ThreadedParser::status, - this, &RunnerDumper::status); - connect(runner, &ValgrindRunner::logMessageReceived, - this, &RunnerDumper::logMessageReceived); - connect(runner, &ValgrindRunner::processErrorReceived, - this, &RunnerDumper::processErrorReceived); - } - - void error(const Valgrind::XmlProtocol::Error &e) - { - qDebug() << "error received"; - dumpError(e); - } - void internalError(const QString& error) - { - qDebug() << "internal error received:" << error; - } - void status(const Valgrind::XmlProtocol::Status &status) - { - qDebug() << "status received:" << status.state() << status.time(); - } - void logMessageReceived(const QByteArray &log) - { - qDebug() << "log message received:" << log; - } - void processErrorReceived(const QString &s) - { - Q_UNUSED(s) - // qDebug() << "error received:" << s; // this can be a lot of text - m_errorReceived = true; - } - -public: - bool m_errorReceived = false; -}; +namespace Valgrind::Test { class ValgrindMemcheckParserTest : public QObject { @@ -129,12 +37,11 @@ private slots: void testValgrindStartError(); private: - void initTest(const QString &testfile, const QStringList &otherArgs = QStringList()); + void initTest(const QString &testfile, const QStringList &otherArgs = {}); QTcpServer *m_server = nullptr; - QProcess *m_process = nullptr; - QTcpSocket *m_socket = nullptr; + std::unique_ptr<Utils::Process> m_process; + std::unique_ptr<QTcpSocket> m_socket; }; -} // namespace Test -} // namespace Valgrind +} // namespace Valgrind::Test diff --git a/src/plugins/valgrind/valgrindplugin.cpp b/src/plugins/valgrind/valgrindplugin.cpp index fad8db5c980..6840c3c0a1f 100644 --- a/src/plugins/valgrind/valgrindplugin.cpp +++ b/src/plugins/valgrind/valgrindplugin.cpp @@ -3,11 +3,9 @@ #include "callgrindtool.h" #include "memchecktool.h" -#include "valgrindconfigwidget.h" #include "valgrindsettings.h" #include "valgrindtr.h" -#include <coreplugin/dialogs/ioptionspage.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> @@ -33,8 +31,8 @@ class ValgrindRunConfigurationAspect : public GlobalOrProjectAspect public: ValgrindRunConfigurationAspect(Target *) { - setProjectSettings(new ValgrindProjectSettings); - setGlobalSettings(ValgrindGlobalSettings::instance()); + setProjectSettings(new ValgrindSettings(false)); + setGlobalSettings(&globalSettings()); setId(ANALYZER_VALGRIND_SETTINGS); setDisplayName(Tr::tr("Valgrind Settings")); setUsingGlobalSettings(true); @@ -46,10 +44,8 @@ public: class ValgrindPluginPrivate { public: - ValgrindGlobalSettings valgrindGlobalSettings; // Needs to come before the tools. MemcheckTool memcheckTool; CallgrindTool callgrindTool; - ValgrindOptionsPage valgrindOptionsPage; }; class ValgrindPlugin final : public ExtensionSystem::IPlugin diff --git a/src/plugins/valgrind/valgrindprocess.cpp b/src/plugins/valgrind/valgrindprocess.cpp new file mode 100644 index 00000000000..1280014f952 --- /dev/null +++ b/src/plugins/valgrind/valgrindprocess.cpp @@ -0,0 +1,285 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "valgrindprocess.h" + +#include "valgrindtr.h" +#include "xmlprotocol/parser.h" + +#include <solutions/tasking/barrier.h> + +#include <utils/process.h> +#include <utils/processinterface.h> +#include <utils/qtcassert.h> + +#include <QEventLoop> +#include <QTcpServer> +#include <QTcpSocket> +#include <QTimer> + +using namespace Tasking; +using namespace Utils; +using namespace Valgrind::XmlProtocol; + +namespace Valgrind { + +static CommandLine valgrindCommand(const CommandLine &command, + const QTcpServer &xmlServer, + const QTcpServer &logServer) +{ + CommandLine cmd = command; + cmd.addArg("--child-silent-after-fork=yes"); + + // Workaround for valgrind bug when running vgdb with xml output + // https://bugs.kde.org/show_bug.cgi?id=343902 + bool enableXml = true; + + auto handleSocketParameter = [&enableXml, &cmd](const QString &prefix, + const QTcpServer &tcpServer) + { + QHostAddress serverAddress = tcpServer.serverAddress(); + if (serverAddress.protocol() != QAbstractSocket::IPv4Protocol) { + // Report will end up in the Application Output pane, i.e. not have + // clickable items, but that's better than nothing. + qWarning("Need IPv4 for valgrind"); + enableXml = false; + } else { + cmd.addArg(QString("%1=%2:%3").arg(prefix).arg(serverAddress.toString()) + .arg(tcpServer.serverPort())); + } + }; + + handleSocketParameter("--xml-socket", xmlServer); + handleSocketParameter("--log-socket", logServer); + + if (enableXml) + cmd.addArg("--xml=yes"); + return cmd; +} + +class ValgrindProcessPrivate : public QObject +{ +public: + ValgrindProcessPrivate(ValgrindProcess *owner) + : q(owner) + {} + + void setupValgrindProcess(Process *process, const CommandLine &command) const { + CommandLine cmd = command; + cmd.addArgs(m_valgrindCommand.arguments(), CommandLine::Raw); + + // consider appending our options last so they override any interfering user-supplied + // options -q as suggested by valgrind manual + + if (cmd.executable().osType() == OsTypeMac) { + // May be slower to start but without it we get no filenames for symbols. + cmd.addArg("--dsymutil=yes"); + } + + cmd.addCommandLineAsArgs(m_debuggee.command); + + emit q->appendMessage(cmd.toUserOutput(), NormalMessageFormat); + + process->setCommand(cmd); + process->setWorkingDirectory(m_debuggee.workingDirectory); + process->setEnvironment(m_debuggee.environment); + process->setProcessChannelMode(m_channelMode); + process->setTerminalMode(m_useTerminal ? TerminalMode::Run : TerminalMode::Off); + + connect(process, &Process::started, this, [this, process] { + emit q->valgrindStarted(process->processId()); + }); + connect(process, &Process::done, this, [this, process] { + const bool success = process->result() == ProcessResult::FinishedWithSuccess; + if (!success) + emit q->processErrorReceived(process->errorString(), process->error()); + emit q->done(success); + }); + connect(process, &Process::readyReadStandardOutput, this, [this, process] { + emit q->appendMessage(process->readAllStandardOutput(), StdOutFormat); + }); + connect(process, &Process::readyReadStandardError, this, [this, process] { + emit q->appendMessage(process->readAllStandardError(), StdErrFormat); + }); + } + + Group runRecipe() const; + + bool run(); + + ValgrindProcess *q = nullptr; + + CommandLine m_valgrindCommand; + ProcessRunData m_debuggee; + QProcess::ProcessChannelMode m_channelMode = QProcess::SeparateChannels; + QHostAddress m_localServerAddress; + bool m_useTerminal = false; + + std::unique_ptr<TaskTree> m_taskTree; +}; + +Group ValgrindProcessPrivate::runRecipe() const +{ + struct ValgrindStorage { + CommandLine m_valgrindCommand; + std::unique_ptr<QTcpServer> m_xmlServer; + std::unique_ptr<QTcpServer> m_logServer; + std::unique_ptr<QTcpSocket> m_xmlSocket; + }; + + TreeStorage<ValgrindStorage> storage; + SingleBarrier xmlBarrier; + + const auto onSetup = [this, storage, xmlBarrier] { + ValgrindStorage *storagePtr = storage.activeStorage(); + storagePtr->m_valgrindCommand.setExecutable(m_valgrindCommand.executable()); + if (!m_localServerAddress.isNull()) { + Barrier *barrier = xmlBarrier->barrier(); + const QString ip = m_localServerAddress.toString(); + + QTcpServer *xmlServer = new QTcpServer; + storagePtr->m_xmlServer.reset(xmlServer); + connect(xmlServer, &QTcpServer::newConnection, this, [xmlServer, storagePtr, barrier] { + QTcpSocket *socket = xmlServer->nextPendingConnection(); + QTC_ASSERT(socket, return); + xmlServer->close(); + storagePtr->m_xmlSocket.reset(socket); + barrier->advance(); // Release Parser task + }); + if (!xmlServer->listen(m_localServerAddress)) { + emit q->processErrorReceived(Tr::tr("XmlServer on %1:").arg(ip) + ' ' + + xmlServer->errorString(), QProcess::FailedToStart); + return SetupResult::StopWithError; + } + xmlServer->setMaxPendingConnections(1); + + QTcpServer *logServer = new QTcpServer; + storagePtr->m_logServer.reset(logServer); + connect(logServer, &QTcpServer::newConnection, this, [this, logServer] { + QTcpSocket *socket = logServer->nextPendingConnection(); + QTC_ASSERT(socket, return); + connect(socket, &QIODevice::readyRead, this, [this, socket] { + emit q->logMessageReceived(socket->readAll()); + }); + logServer->close(); + }); + if (!logServer->listen(m_localServerAddress)) { + emit q->processErrorReceived(Tr::tr("LogServer on %1:").arg(ip) + ' ' + + logServer->errorString(), QProcess::FailedToStart); + return SetupResult::StopWithError; + } + logServer->setMaxPendingConnections(1); + + storagePtr->m_valgrindCommand = valgrindCommand(storagePtr->m_valgrindCommand, + *xmlServer, *logServer); + } + return SetupResult::Continue; + }; + + const auto onProcessSetup = [this, storage](Process &process) { + setupValgrindProcess(&process, storage->m_valgrindCommand); + }; + + const auto onParserGroupSetup = [this] { + return m_localServerAddress.isNull() ? SetupResult::StopWithDone : SetupResult::Continue; + }; + + const auto onParserSetup = [this, storage](Parser &parser) { + connect(&parser, &Parser::status, q, &ValgrindProcess::status); + connect(&parser, &Parser::error, q, &ValgrindProcess::error); + parser.setSocket(storage->m_xmlSocket.release()); + }; + + const auto onParserError = [this](const Parser &parser) { + emit q->internalError(parser.errorString()); + }; + + const Group root { + parallel, + Storage(storage), + Storage(xmlBarrier), + onGroupSetup(onSetup), + ProcessTask(onProcessSetup), + Group { + onGroupSetup(onParserGroupSetup), + waitForBarrierTask(xmlBarrier), + ParserTask(onParserSetup, {}, onParserError) + } + }; + return root; +} + +bool ValgrindProcessPrivate::run() +{ + m_taskTree.reset(new TaskTree); + m_taskTree->setRecipe(runRecipe()); + const auto finalize = [this](bool success) { + m_taskTree.release()->deleteLater(); + emit q->done(success); + }; + connect(m_taskTree.get(), &TaskTree::done, this, [finalize] { finalize(true); }); + connect(m_taskTree.get(), &TaskTree::errorOccurred, this, [finalize] { finalize(false); }); + m_taskTree->start(); + return bool(m_taskTree); +} + +ValgrindProcess::ValgrindProcess(QObject *parent) + : QObject(parent) + , d(new ValgrindProcessPrivate(this)) +{} + +ValgrindProcess::~ValgrindProcess() = default; + +void ValgrindProcess::setValgrindCommand(const CommandLine &command) +{ + d->m_valgrindCommand = command; +} + +void ValgrindProcess::setDebuggee(const ProcessRunData &debuggee) +{ + d->m_debuggee = debuggee; +} + +void ValgrindProcess::setProcessChannelMode(QProcess::ProcessChannelMode mode) +{ + d->m_channelMode = mode; +} + +void ValgrindProcess::setLocalServerAddress(const QHostAddress &localServerAddress) +{ + d->m_localServerAddress = localServerAddress; +} + +void ValgrindProcess::setUseTerminal(bool on) +{ + d->m_useTerminal = on; +} + +bool ValgrindProcess::start() +{ + return d->run(); +} + +void ValgrindProcess::stop() +{ + d->m_taskTree.reset(); +} + +bool ValgrindProcess::runBlocking() +{ + bool ok = false; + QEventLoop loop; + + const auto finalize = [&loop, &ok](bool success) { + ok = success; + // Refer to the QObject::deleteLater() docs. + QMetaObject::invokeMethod(&loop, [&loop] { loop.quit(); }, Qt::QueuedConnection); + }; + + connect(this, &ValgrindProcess::done, &loop, finalize); + QTimer::singleShot(0, this, &ValgrindProcess::start); + loop.exec(QEventLoop::ExcludeUserInputEvents); + return ok; +} + +} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrindprocess.h b/src/plugins/valgrind/valgrindprocess.h new file mode 100644 index 00000000000..065d4ad47ad --- /dev/null +++ b/src/plugins/valgrind/valgrindprocess.h @@ -0,0 +1,75 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <solutions/tasking/tasktree.h> + +#include <utils/outputformat.h> + +#include <QProcess> + +QT_BEGIN_NAMESPACE +class QHostAddress; +QT_END_NAMESPACE + +namespace Utils { +class CommandLine; +class ProcessRunData; +} + +namespace Valgrind { + +namespace XmlProtocol { +class Error; +class Status; +} + +class ValgrindProcessPrivate; + +class ValgrindProcess : public QObject +{ + Q_OBJECT + +public: + explicit ValgrindProcess(QObject *parent = nullptr); + ~ValgrindProcess() override; + + void setValgrindCommand(const Utils::CommandLine &command); + void setDebuggee(const Utils::ProcessRunData &debuggee); + void setProcessChannelMode(QProcess::ProcessChannelMode mode); + void setLocalServerAddress(const QHostAddress &localServerAddress); + void setUseTerminal(bool on); + + bool start(); + void stop(); + bool runBlocking(); + +signals: + void appendMessage(const QString &, Utils::OutputFormat); + void logMessageReceived(const QByteArray &); + void processErrorReceived(const QString &, QProcess::ProcessError); + void valgrindStarted(qint64 pid); + void done(bool success); + + // Parser's signals + void status(const Valgrind::XmlProtocol::Status &status); + void error(const Valgrind::XmlProtocol::Error &error); + void internalError(const QString &errorString); + +private: + std::unique_ptr<ValgrindProcessPrivate> d; +}; + +class ValgrindProcessTaskAdapter : public Tasking::TaskAdapter<ValgrindProcess> +{ +public: + ValgrindProcessTaskAdapter() { + connect(task(), &ValgrindProcess::done, this, &Tasking::TaskInterface::done); + } + void start() final { task()->start(); } +}; + +using ValgrindProcessTask = Tasking::CustomTask<ValgrindProcessTaskAdapter>; + +} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrindrunner.cpp b/src/plugins/valgrind/valgrindrunner.cpp deleted file mode 100644 index fd265444226..00000000000 --- a/src/plugins/valgrind/valgrindrunner.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "valgrindrunner.h" - -#include "valgrindtr.h" -#include "xmlprotocol/threadedparser.h" - -#include <projectexplorer/runcontrol.h> - -#include <utils/hostosinfo.h> -#include <utils/process.h> -#include <utils/qtcassert.h> - -#include <QEventLoop> -#include <QTcpServer> -#include <QTcpSocket> - -using namespace ProjectExplorer; -using namespace Utils; - -namespace Valgrind { - -class ValgrindRunner::Private : public QObject -{ -public: - Private(ValgrindRunner *owner) : q(owner) { - connect(&m_process, &Process::started, this, [this] { - emit q->valgrindStarted(m_process.processId()); - }); - connect(&m_process, &Process::done, this, [this] { - if (m_process.result() != ProcessResult::FinishedWithSuccess) - emit q->processErrorReceived(m_process.errorString(), m_process.error()); - emit q->finished(); - }); - connect(&m_process, &Process::readyReadStandardOutput, this, [this] { - emit q->appendMessage(m_process.readAllStandardOutput(), StdOutFormat); - }); - connect(&m_process, &Process::readyReadStandardError, this, [this] { - emit q->appendMessage(m_process.readAllStandardError(), StdErrFormat); - }); - - connect(&m_xmlServer, &QTcpServer::newConnection, this, &Private::xmlSocketConnected); - connect(&m_logServer, &QTcpServer::newConnection, this, &Private::logSocketConnected); - } - - void xmlSocketConnected(); - void logSocketConnected(); - bool startServers(); - bool run(); - - ValgrindRunner *q; - Runnable m_debuggee; - - CommandLine m_command; - Process m_process; - - QHostAddress m_localServerAddress; - - QTcpServer m_xmlServer; - XmlProtocol::ThreadedParser m_parser; - QTcpServer m_logServer; -}; - -void ValgrindRunner::Private::xmlSocketConnected() -{ - QTcpSocket *socket = m_xmlServer.nextPendingConnection(); - QTC_ASSERT(socket, return); - m_xmlServer.close(); - m_parser.parse(socket); -} - -void ValgrindRunner::Private::logSocketConnected() -{ - QTcpSocket *logSocket = m_logServer.nextPendingConnection(); - QTC_ASSERT(logSocket, return); - connect(logSocket, &QIODevice::readyRead, this, [this, logSocket] { - emit q->logMessageReceived(logSocket->readAll()); - }); - m_logServer.close(); -} - -bool ValgrindRunner::Private::startServers() -{ - const bool xmlOK = m_xmlServer.listen(m_localServerAddress); - const QString ip = m_localServerAddress.toString(); - if (!xmlOK) { - emit q->processErrorReceived(Tr::tr("XmlServer on %1:").arg(ip) + ' ' - + m_xmlServer.errorString(), QProcess::FailedToStart ); - return false; - } - m_xmlServer.setMaxPendingConnections(1); - const bool logOK = m_logServer.listen(m_localServerAddress); - if (!logOK) { - emit q->processErrorReceived(Tr::tr("LogServer on %1:").arg(ip) + ' ' - + m_logServer.errorString(), QProcess::FailedToStart ); - return false; - } - m_logServer.setMaxPendingConnections(1); - return true; -} - -bool ValgrindRunner::Private::run() -{ - CommandLine cmd; - cmd.setExecutable(m_command.executable()); - - if (!m_localServerAddress.isNull()) { - if (!startServers()) - return false; - - cmd.addArg("--child-silent-after-fork=yes"); - - // Workaround for valgrind bug when running vgdb with xml output - // https://bugs.kde.org/show_bug.cgi?id=343902 - bool enableXml = true; - - auto handleSocketParameter = [&enableXml, &cmd](const QString &prefix, const QTcpServer &tcpServer) - { - QHostAddress serverAddress = tcpServer.serverAddress(); - if (serverAddress.protocol() != QAbstractSocket::IPv4Protocol) { - // Report will end up in the Application Output pane, i.e. not have - // clickable items, but that's better than nothing. - qWarning("Need IPv4 for valgrind"); - enableXml = false; - } else { - cmd.addArg(QString("%1=%2:%3").arg(prefix).arg(serverAddress.toString()) - .arg(tcpServer.serverPort())); - } - }; - - handleSocketParameter("--xml-socket", m_xmlServer); - handleSocketParameter("--log-socket", m_logServer); - - if (enableXml) - cmd.addArg("--xml=yes"); - } - cmd.addArgs(m_command.arguments(), CommandLine::Raw); - - // consider appending our options last so they override any interfering user-supplied options - // -q as suggested by valgrind manual - - if (cmd.executable().osType() == OsTypeMac) { - // May be slower to start but without it we get no filenames for symbols. - cmd.addArg("--dsymutil=yes"); - } - - cmd.addCommandLineAsArgs(m_debuggee.command); - - emit q->valgrindExecuted(cmd.toUserOutput()); - - m_process.setCommand(cmd); - m_process.setWorkingDirectory(m_debuggee.workingDirectory); - m_process.setEnvironment(m_debuggee.environment); - m_process.start(); - return true; -} - -ValgrindRunner::ValgrindRunner(QObject *parent) - : QObject(parent), d(new Private(this)) -{ -} - -ValgrindRunner::~ValgrindRunner() -{ - if (d->m_process.isRunning()) { - // make sure we don't delete the thread while it's still running - waitForFinished(); - } - if (d->m_parser.isRunning()) { - // make sure we don't delete the thread while it's still running - waitForFinished(); - } - delete d; - d = nullptr; -} - -void ValgrindRunner::setValgrindCommand(const CommandLine &command) -{ - d->m_command = command; -} - -void ValgrindRunner::setDebuggee(const Runnable &debuggee) -{ - d->m_debuggee = debuggee; -} - -void ValgrindRunner::setProcessChannelMode(QProcess::ProcessChannelMode mode) -{ - d->m_process.setProcessChannelMode(mode); -} - -void ValgrindRunner::setLocalServerAddress(const QHostAddress &localServerAddress) -{ - d->m_localServerAddress = localServerAddress; -} - -void ValgrindRunner::setUseTerminal(bool on) -{ - d->m_process.setTerminalMode(on ? TerminalMode::Run : TerminalMode::Off); -} - -void ValgrindRunner::waitForFinished() const -{ - if (d->m_process.state() == QProcess::NotRunning) - return; - - QEventLoop loop; - connect(this, &ValgrindRunner::finished, &loop, &QEventLoop::quit); - loop.exec(); -} - -QString ValgrindRunner::errorString() const -{ - return d->m_process.errorString(); -} - -bool ValgrindRunner::start() -{ - return d->run(); -} - -void ValgrindRunner::stop() -{ - d->m_process.stop(); -} - -XmlProtocol::ThreadedParser *ValgrindRunner::parser() const -{ - return &d->m_parser; -} - -} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrindrunner.h b/src/plugins/valgrind/valgrindrunner.h deleted file mode 100644 index c6426fba830..00000000000 --- a/src/plugins/valgrind/valgrindrunner.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <utils/outputformat.h> - -#include <QProcess> - -QT_BEGIN_NAMESPACE -class QHostAddress; -QT_END_NAMESPACE - -namespace Utils { class CommandLine; } -namespace ProjectExplorer { class Runnable; } - -namespace Valgrind { - -namespace XmlProtocol { class ThreadedParser; } - -class ValgrindRunner : public QObject -{ - Q_OBJECT - -public: - explicit ValgrindRunner(QObject *parent = nullptr); - ~ValgrindRunner() override; - - void setValgrindCommand(const Utils::CommandLine &command); - void setDebuggee(const ProjectExplorer::Runnable &debuggee); - void setProcessChannelMode(QProcess::ProcessChannelMode mode); - void setLocalServerAddress(const QHostAddress &localServerAddress); - void setUseTerminal(bool on); - - void waitForFinished() const; - - QString errorString() const; - - bool start(); - void stop(); - - XmlProtocol::ThreadedParser *parser() const; - -signals: - void appendMessage(const QString &, Utils::OutputFormat); - - void logMessageReceived(const QByteArray &); - void processErrorReceived(const QString &, QProcess::ProcessError); - void valgrindExecuted(const QString &); - void valgrindStarted(qint64 pid); - void finished(); - -private: - class Private; - Private *d; -}; - -} // namespace Valgrind diff --git a/src/plugins/valgrind/valgrindsettings.cpp b/src/plugins/valgrind/valgrindsettings.cpp index 4db2e39c3a5..b520f328921 100644 --- a/src/plugins/valgrind/valgrindsettings.cpp +++ b/src/plugins/valgrind/valgrindsettings.cpp @@ -4,23 +4,22 @@ #include "valgrindsettings.h" #include "callgrindcostdelegate.h" -#include "valgrindconfigwidget.h" #include "valgrindtr.h" +#include "xmlprotocol/error.h" -#include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + +#include <debugger/analyzer/analyzericons.h> +#include <debugger/debuggertr.h> #include <utils/algorithm.h> #include <utils/layoutbuilder.h> #include <utils/qtcassert.h> -#include <utils/treemodel.h> #include <utils/utilsicons.h> -#include <valgrind/xmlprotocol/error.h> - -#include <QDebug> #include <QListView> +#include <QMetaEnum> #include <QPushButton> -#include <QSettings> #include <QStandardItemModel> using namespace Utils; @@ -59,18 +58,16 @@ void SuppressionAspect::addSuppressionFile(const FilePath &suppression) void SuppressionAspectPrivate::slotAddSuppression() { - ValgrindGlobalSettings *conf = ValgrindGlobalSettings::instance(); - QTC_ASSERT(conf, return); const FilePaths files = FileUtils::getOpenFilePaths(nullptr, Tr::tr("Valgrind Suppression Files"), - conf->lastSuppressionDirectory.filePath(), + globalSettings().lastSuppressionDirectory(), Tr::tr("Valgrind Suppression File (*.supp);;All Files (*)")); //dialog.setHistory(conf->lastSuppressionDialogHistory()); if (!files.isEmpty()) { for (const FilePath &file : files) m_model.appendRow(new QStandardItem(file.toString())); - conf->lastSuppressionDirectory.setFilePath(files.at(0).absolutePath()); + globalSettings().lastSuppressionDirectory.setValue(files.at(0).absolutePath()); //conf->setLastSuppressionDialogHistory(dialog.history()); if (!isGlobal) q->apply(); @@ -108,7 +105,7 @@ void SuppressionAspectPrivate::slotSuppressionSelectionChanged() // SuppressionAspect::SuppressionAspect(AspectContainer *container, bool global) - : BaseAspect(container) + : TypedAspect(container) { d = new SuppressionAspectPrivate(this, global); setSettingsKey("Analyzer.Valgrind.SuppressionFiles"); @@ -119,16 +116,6 @@ SuppressionAspect::~SuppressionAspect() delete d; } -FilePaths SuppressionAspect::value() const -{ - return FileUtils::toFilePathList(BaseAspect::value().toStringList()); -} - -void SuppressionAspect::setValue(const FilePaths &val) -{ - BaseAspect::setValue(Utils::transform<QStringList>(val, &FilePath::toString)); -} - void SuppressionAspect::addToLayout(Layouting::LayoutItem &parent) { QTC_CHECK(!d->addEntry); @@ -157,36 +144,32 @@ void SuppressionAspect::addToLayout(Layouting::LayoutItem &parent) Column { d->addEntry.data(), d->removeEntry.data(), st } }; parent.addItem(Span { 2, group }); - - setVolatileValue(BaseAspect::value()); } -void SuppressionAspect::fromMap(const QVariantMap &map) +void SuppressionAspect::fromMap(const Store &map) { - BaseAspect::fromMap(map); + BaseAspect::fromMap(map); // FIXME Looks wrong, as it skips the intermediate level } -void SuppressionAspect::toMap(QVariantMap &map) const +void SuppressionAspect::toMap(Store &map) const { BaseAspect::toMap(map); } -QVariant SuppressionAspect::volatileValue() const +bool SuppressionAspect::guiToBuffer() { - QStringList ret; - + const FilePaths old = m_buffer; + m_buffer.clear(); for (int i = 0; i < d->m_model.rowCount(); ++i) - ret << d->m_model.item(i)->text(); - - return ret; + m_buffer.append(FilePath::fromUserInput(d->m_model.item(i)->text())); + return m_buffer != old; } -void SuppressionAspect::setVolatileValue(const QVariant &val) +void SuppressionAspect::bufferToGui() { - const QStringList files = val.toStringList(); d->m_model.clear(); - for (const QString &file : files) - d->m_model.appendRow(new QStandardItem(file)); + for (const FilePath &file : m_buffer) + d->m_model.appendRow(new QStandardItem(file.toUserOutput())); } ////////////////////////////////////////////////////////////////// @@ -195,13 +178,16 @@ void SuppressionAspect::setVolatileValue(const QVariant &val) // ////////////////////////////////////////////////////////////////// -ValgrindBaseSettings::ValgrindBaseSettings(bool global) +ValgrindSettings::ValgrindSettings(bool global) : suppressions(this, global) { + setSettingsGroup("Analyzer"); + setAutoApply(false); + // Note that this is used twice, once for project settings in the .user files // and once for global settings in QtCreator.ini. This uses intentionally // the same key to facilitate copying using fromMap/toMap. - QString base = "Analyzer.Valgrind."; + Key base = "Analyzer.Valgrind."; valgrindExecutable.setSettingsKey(base + "ValgrindExecutable"); valgrindExecutable.setDefaultValue("valgrind"); @@ -263,6 +249,12 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global) numCallers.setDefaultValue(25); numCallers.setLabelText(Tr::tr("Backtrace frame count:")); + lastSuppressionDirectory.setSettingsKey(base + "LastSuppressionDirectory"); + lastSuppressionDirectory.setVisible(global); + + lastSuppressionHistory.setSettingsKey(base + "LastSuppressionHistory"); + lastSuppressionHistory.setVisible(global); + // Callgrind kcachegrindExecutable.setSettingsKey(base + "KCachegrindExecutable"); @@ -329,118 +321,114 @@ ValgrindBaseSettings::ValgrindBaseSettings(bool global) visibleErrorKinds.setSettingsKey(base + "VisibleErrorKinds"); QList<int> defaultErrorKinds; - for (int i = 0; i < Valgrind::XmlProtocol::MemcheckErrorKindCount; ++i) - defaultErrorKinds << i; + const QMetaEnum memcheckErrorEnum = QMetaEnum::fromType<XmlProtocol::MemcheckError>(); + for (int i = 0; i < memcheckErrorEnum.keyCount(); ++i) + defaultErrorKinds << memcheckErrorEnum.value(i); visibleErrorKinds.setDefaultValue(defaultErrorKinds); -} - - -////////////////////////////////////////////////////////////////// -// -// ValgrindGlobalSettings -// -////////////////////////////////////////////////////////////////// - -static ValgrindGlobalSettings *theGlobalSettings = nullptr; - -ValgrindGlobalSettings::ValgrindGlobalSettings() - : ValgrindBaseSettings(true) -{ - theGlobalSettings = this; - - const QString base = "Analyzer.Valgrind"; - - lastSuppressionDirectory.setSettingsKey(base + "LastSuppressionDirectory"); - - lastSuppressionHistory.setSettingsKey(base + "LastSuppressionHistory"); detectCycles.setSettingsKey(base + "Callgrind.CycleDetection"); detectCycles.setDefaultValue(true); detectCycles.setLabelText("O"); // FIXME: Create a real icon detectCycles.setToolTip(Tr::tr("Enable cycle detection to properly handle recursive " "or circular function calls.")); + detectCycles.setVisible(global); costFormat.setSettingsKey(base + "Callgrind.CostFormat"); costFormat.setDefaultValue(CostDelegate::FormatRelative); costFormat.setDisplayStyle(SelectionAspect::DisplayStyle::ComboBox); + costFormat.setVisible(global); shortenTemplates.setSettingsKey(base + "Callgrind.ShortenTemplates"); shortenTemplates.setDefaultValue(true); shortenTemplates.setLabelText("<>"); // FIXME: Create a real icon shortenTemplates.setToolTip(Tr::tr("Remove template parameter lists when displaying function names.")); + shortenTemplates.setVisible(global); - setConfigWidgetCreator([this] { return ValgrindOptionsPage::createSettingsWidget(this); }); - readSettings(); + setLayouter([this] { + using namespace Layouting; - setAutoApply(false); -} + // clang-format off + Grid generic { + valgrindExecutable, br, + valgrindArguments, br, + selfModifyingCodeDetection, br + }; -ValgrindGlobalSettings *ValgrindGlobalSettings::instance() -{ - return theGlobalSettings; -} + Grid memcheck { + memcheckArguments, br, + trackOrigins, br, + showReachable, br, + leakCheckOnFinish, br, + numCallers, br, + filterExternalIssues, br, + suppressions + }; -// -// Memcheck -// + Grid callgrind { + callgrindArguments, br, + kcachegrindExecutable, br, + minimumInclusiveCostRatio, br, + visualizationMinimumInclusiveCostRatio, br, + enableEventToolTips, br, + Span { + 2, + Group { + Column { + enableCacheSim, + enableBranchSim, + collectSystime, + collectBusEvents, + } + } + } + }; -QVariantMap ValgrindBaseSettings::defaultSettings() const -{ - QVariantMap defaults; - forEachAspect([&defaults](BaseAspect *aspect) { - defaults.insert(aspect->settingsKey(), aspect->defaultValue()); + return Column { + Group { title(Tr::tr("Valgrind Generic Settings")), generic }, + Group { title(Tr::tr("Memcheck Memory Analysis Options")), memcheck }, + Group { title(Tr::tr("Callgrind Profiling Options")), callgrind }, + st, + }; + // clang-format on }); - return defaults; + + if (global) { + readSettings(); + } else { + // FIXME: Is this needed? + connect(this, &AspectContainer::fromMapFinished, [this] { + // FIXME: Update project page e.g. on "Restore Global", aspects + // there are 'autoapply', and Aspect::cancel() is normally part of + // the 'manual apply' machinery. + setAutoApply(false); + cancel(); + setAutoApply(true); + }); + } } -static const char groupC[] = "Analyzer"; - -void ValgrindGlobalSettings::readSettings() +ValgrindSettings &globalSettings() { - // Read stored values - QSettings *settings = Core::ICore::settings(); - settings->beginGroup(groupC); - QVariantMap map; - const QStringList childKey = settings->childKeys(); - for (const QString &key : childKey) - map.insert(key, settings->value(key)); - settings->endGroup(); - - fromMap(map); + static ValgrindSettings theSettings{true}; + return theSettings; } -void ValgrindGlobalSettings::writeSettings() const +// ValgrindSettingsPage + +class ValgrindSettingsPage final : public Core::IOptionsPage { - const QVariantMap defaults = defaultSettings(); +public: + ValgrindSettingsPage() + { + setId(ANALYZER_VALGRIND_SETTINGS); + setDisplayName(Tr::tr("Valgrind")); + setCategory("T.Analyzer"); + setDisplayCategory(::Debugger::Tr::tr("Analyzer")); + setCategoryIconPath(Analyzer::Icons::SETTINGSCATEGORY_ANALYZER); + setSettingsProvider([] { return &globalSettings(); }); + } +}; - Utils::QtcSettings *settings = Core::ICore::settings(); - settings->beginGroup(groupC); - QVariantMap map; - toMap(map); - for (QVariantMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) - settings->setValueWithDefault(it.key(), it.value(), defaults.value(it.key())); - settings->endGroup(); -} - -////////////////////////////////////////////////////////////////// -// -// ValgrindProjectSettings -// -////////////////////////////////////////////////////////////////// - -ValgrindProjectSettings::ValgrindProjectSettings() - : ValgrindBaseSettings(false) -{ - setConfigWidgetCreator([this] { return ValgrindOptionsPage::createSettingsWidget(this); }); - - connect(this, &AspectContainer::fromMapFinished, [this] { - // FIXME: Update project page e.g. on "Restore Global", aspects - // there are 'autoapply', and Aspect::cancel() is normally part of - // the 'manual apply' machinery. - setAutoApply(false); - cancel(); - setAutoApply(true); - }); -} +const ValgrindSettingsPage settingsPage; } // Valgrind::Internal diff --git a/src/plugins/valgrind/valgrindsettings.h b/src/plugins/valgrind/valgrindsettings.h index 29506b848f5..93866bae862 100644 --- a/src/plugins/valgrind/valgrindsettings.h +++ b/src/plugins/valgrind/valgrindsettings.h @@ -12,7 +12,7 @@ const char ANALYZER_VALGRIND_SETTINGS[] = "Analyzer.Valgrind.Settings"; class SuppressionAspectPrivate; -class SuppressionAspect final : public Utils::BaseAspect +class SuppressionAspect final : public Utils::TypedAspect<Utils::FilePaths> { Q_OBJECT @@ -20,34 +20,31 @@ public: SuppressionAspect(Utils::AspectContainer *container, bool global); ~SuppressionAspect() final; - Utils::FilePaths operator()() const { return value(); } - Utils::FilePaths value() const; - void setValue(const Utils::FilePaths &val); - void addToLayout(Layouting::LayoutItem &parent) final; - void fromMap(const QVariantMap &map) final; - void toMap(QVariantMap &map) const final; - - QVariant volatileValue() const final; - void setVolatileValue(const QVariant &val) final; + void fromMap(const Utils::Store &map) final; + void toMap(Utils::Store &map) const final; void addSuppressionFile(const Utils::FilePath &suppressionFile); private: - friend class ValgrindBaseSettings; + void bufferToGui() override; + bool guiToBuffer() override; + + friend class ValgrindSettings; SuppressionAspectPrivate *d = nullptr; }; /** * Valgrind settings shared for global and per-project. */ -class ValgrindBaseSettings : public ProjectExplorer::ISettingsAspect +class ValgrindSettings : public Utils::AspectContainer { Q_OBJECT public: - explicit ValgrindBaseSettings(bool global); + // These exists once globally, and once per project + explicit ValgrindSettings(bool global); enum SelfModifyingCodeDetection { DetectSmcNo, @@ -62,20 +59,14 @@ public: LeakCheckOnFinishYes }; -/** - * Base valgrind settings - */ -public: + // Generic valgrind settings Utils::FilePathAspect valgrindExecutable{this}; Utils::StringAspect valgrindArguments{this}; Utils::SelectionAspect selfModifyingCodeDetection{this}; SuppressionAspect suppressions; -/** - * Base memcheck settings - */ -public: + // Memcheck Utils::StringAspect memcheckArguments{this}; Utils::IntegerAspect numCallers{this}; Utils::SelectionAspect leakCheckOnFinish{this}; @@ -84,12 +75,12 @@ public: Utils::BoolAspect filterExternalIssues{this}; Utils::IntegersAspect visibleErrorKinds{this}; + Utils::FilePathAspect lastSuppressionDirectory{this}; // Global only + Utils::StringAspect lastSuppressionHistory{this}; // Global only + void setVisibleErrorKinds(const QList<int> &); -/** - * Base callgrind settings - */ -public: + // Callgrind Utils::StringAspect callgrindArguments{this}; Utils::FilePathAspect kcachegrindExecutable{this}; @@ -101,51 +92,11 @@ public: Utils::DoubleAspect minimumInclusiveCostRatio{this}; Utils::DoubleAspect visualizationMinimumInclusiveCostRatio{this}; - QVariantMap defaultSettings() const; + Utils::SelectionAspect costFormat{this}; // Global only + Utils::BoolAspect detectCycles{this}; // Global only + Utils::BoolAspect shortenTemplates{this}; // Global only }; - -/** - * Global valgrind settings - */ -class ValgrindGlobalSettings : public ValgrindBaseSettings -{ - Q_OBJECT - -public: - ValgrindGlobalSettings(); - - static ValgrindGlobalSettings *instance(); - - /** - * Global memcheck settings - */ - - void writeSettings() const; - void readSettings(); - - Utils::StringAspect lastSuppressionDirectory{this}; - Utils::StringAspect lastSuppressionHistory{this}; - - - /** - * Global callgrind settings - */ - Utils::SelectionAspect costFormat{this}; - Utils::BoolAspect detectCycles{this}; - Utils::BoolAspect shortenTemplates{this}; -}; - - -/** - * Per-project valgrind settings. - */ -class ValgrindProjectSettings : public ValgrindBaseSettings -{ - Q_OBJECT - -public: - ValgrindProjectSettings(); -}; +ValgrindSettings &globalSettings(); } // Valgrind::Internal diff --git a/src/plugins/valgrind/valgrindtestrunnertest.cpp b/src/plugins/valgrind/valgrindtestrunnertest.cpp index 4e42572ce72..73371e09164 100644 --- a/src/plugins/valgrind/valgrindtestrunnertest.cpp +++ b/src/plugins/valgrind/valgrindtestrunnertest.cpp @@ -3,31 +3,23 @@ #include "valgrindtestrunnertest.h" +#include "valgrindprocess.h" #include "xmlprotocol/frame.h" #include "xmlprotocol/stack.h" -#include "xmlprotocol/threadedparser.h" -#include "valgrindrunner.h" - -#include <projectexplorer/devicesupport/devicemanager.h> -#include <projectexplorer/projectexplorer.h> -#include <projectexplorer/runconfiguration.h> -#include <projectexplorer/runcontrol.h> #include <utils/algorithm.h> +#include <utils/processinterface.h> #include <QDebug> -#include <QTest> #include <QDir> -#include <QSignalSpy> +#include <QTest> #define HEADER_LENGTH 3 -using namespace ProjectExplorer; using namespace Valgrind::XmlProtocol; using namespace Utils; -namespace Valgrind { -namespace Test { +namespace Valgrind::Test { //BEGIN Test Helpers and boilerplate code @@ -56,7 +48,7 @@ QString ValgrindTestRunnerTest::runTestBinary(const QString &binary, const QStri if (!binPathFileInfo.isExecutable()) return QString(); - Runnable debuggee; + ProcessRunData debuggee; const QString &binPath = binPathFileInfo.canonicalFilePath(); debuggee.command.setExecutable(Utils::FilePath::fromString(binPath)); debuggee.environment = Utils::Environment::systemEnvironment(); @@ -67,8 +59,7 @@ QString ValgrindTestRunnerTest::runTestBinary(const QString &binary, const QStri m_runner->setLocalServerAddress(QHostAddress::LocalHost); m_runner->setValgrindCommand(valgrind); m_runner->setDebuggee(debuggee); - m_runner->start(); - m_runner->waitForFinished(); + m_runner->runBlocking(); return binPath; } @@ -111,16 +102,14 @@ void ValgrindTestRunnerTest::init() Q_ASSERT(m_logMessages.isEmpty()); Q_ASSERT(!m_runner); - m_runner = new ValgrindRunner; + m_runner = new ValgrindProcess; m_runner->setProcessChannelMode(QProcess::ForwardedChannels); - connect(m_runner, &ValgrindRunner::logMessageReceived, + connect(m_runner, &ValgrindProcess::logMessageReceived, this, &ValgrindTestRunnerTest::logMessageReceived); - connect(m_runner, &ValgrindRunner::processErrorReceived, + connect(m_runner, &ValgrindProcess::processErrorReceived, this, &ValgrindTestRunnerTest::internalError); - connect(m_runner->parser(), &ThreadedParser::internalError, - this, &ValgrindTestRunnerTest::internalError); - connect(m_runner->parser(), &ThreadedParser::error, - this, &ValgrindTestRunnerTest::error); + connect(m_runner, &ValgrindProcess::internalError, this, &ValgrindTestRunnerTest::internalError); + connect(m_runner, &ValgrindProcess::error, this, &ValgrindTestRunnerTest::error); } //BEGIN: Actual test cases @@ -760,5 +749,4 @@ void ValgrindTestRunnerTest::testOverlap() } } -} // namespace Test -} // namespace Valgrind +} // namespace Valgrind::Test diff --git a/src/plugins/valgrind/valgrindtestrunnertest.h b/src/plugins/valgrind/valgrindtestrunnertest.h index 1fb0b3670ad..39ee370f44d 100644 --- a/src/plugins/valgrind/valgrindtestrunnertest.h +++ b/src/plugins/valgrind/valgrindtestrunnertest.h @@ -8,11 +8,9 @@ #include <QObject> #include <QStringList> -namespace Valgrind { +namespace Valgrind { class ValgrindProcess; } -class ValgrindRunner; - -namespace Test { +namespace Valgrind::Test { class ValgrindTestRunnerTest : public QObject { @@ -48,11 +46,10 @@ private slots: private: QString runTestBinary(const QString &binary, const QStringList &vArgs = QStringList()); - ValgrindRunner *m_runner = nullptr; + ValgrindProcess *m_runner = nullptr; QList<QByteArray> m_logMessages; QList<XmlProtocol::Error> m_errors; bool m_expectCrash = false; }; -} // namespace Test -} // namespace Valgrind +} // namespace Valgrind::Test diff --git a/src/plugins/valgrind/xmlprotocol/announcethread.cpp b/src/plugins/valgrind/xmlprotocol/announcethread.cpp index 6d4b33b40ed..97d414904c6 100644 --- a/src/plugins/valgrind/xmlprotocol/announcethread.cpp +++ b/src/plugins/valgrind/xmlprotocol/announcethread.cpp @@ -5,18 +5,17 @@ #include "frame.h" #include <QSharedData> -#include <QVector> +#include <QList> #include <algorithm> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class AnnounceThread::Private : public QSharedData { public: qint64 hThreadId = -1; - QVector<Frame> stack; + QList<Frame> stack; }; AnnounceThread::AnnounceThread() @@ -56,15 +55,14 @@ void AnnounceThread::setHelgrindThreadId(qint64 id) d->hThreadId = id; } -QVector<Frame> AnnounceThread::stack() const +QList<Frame> AnnounceThread::stack() const { return d->stack; } -void AnnounceThread::setStack(const QVector<Frame> &stack) +void AnnounceThread::setStack(const QList<Frame> &stack) { d->stack = stack; } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/announcethread.h b/src/plugins/valgrind/xmlprotocol/announcethread.h index 6e427d4ab20..cf5afc42e01 100644 --- a/src/plugins/valgrind/xmlprotocol/announcethread.h +++ b/src/plugins/valgrind/xmlprotocol/announcethread.h @@ -4,10 +4,9 @@ #pragma once #include <QSharedDataPointer> -#include <QVector> +#include <QList> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Frame; @@ -23,13 +22,12 @@ public: qint64 helgrindThreadId() const; void setHelgrindThreadId(qint64 id); - QVector<Frame> stack() const; - void setStack(const QVector<Frame> &stack); + QList<Frame> stack() const; + void setStack(const QList<Frame> &stack); private: class Private; QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/error.cpp b/src/plugins/valgrind/xmlprotocol/error.cpp index a062eb1693d..4c0707b3726 100644 --- a/src/plugins/valgrind/xmlprotocol/error.cpp +++ b/src/plugins/valgrind/xmlprotocol/error.cpp @@ -6,15 +6,14 @@ #include "stack.h" #include "suppression.h" +#include <QList> #include <QSharedData> #include <QString> #include <QTextStream> -#include <QVector> #include <algorithm> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Error::Private : public QSharedData { @@ -23,7 +22,7 @@ public: qint64 tid = 0; QString what; int kind = 0; - QVector<Stack> stacks; + QList<Stack> stacks; Suppression suppression; quint64 leakedBytes = 0; qint64 leakedBlocks = 0; @@ -144,12 +143,12 @@ void Error::setKind(int k) d->kind = k; } -QVector<Stack> Error::stacks() const +QList<Stack> Error::stacks() const { return d->stacks; } -void Error::setStacks(const QVector<Stack> &stacks) +void Error::setStacks(const QList<Stack> &stacks) { d->stacks = stacks; } @@ -187,7 +186,7 @@ QString Error::toXml() const stream << " <auxwhat>" << stack.auxWhat() << "</auxwhat>\n"; stream << " <stack>\n"; - const QVector<Frame> frames = stack.frames(); + const QList<Frame> frames = stack.frames(); for (const Frame &frame : frames) { stream << " <frame>\n"; stream << " <ip>0x" << QString::number(frame.instructionPointer(), 16) << "</ip>\n"; @@ -212,5 +211,4 @@ QString Error::toXml() const return xml; } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/error.h b/src/plugins/valgrind/xmlprotocol/error.h index c0948e62b5a..a9dd3278b8f 100644 --- a/src/plugins/valgrind/xmlprotocol/error.h +++ b/src/plugins/valgrind/xmlprotocol/error.h @@ -3,16 +3,17 @@ #pragma once +#include <QList> #include <QMetaType> #include <QSharedDataPointer> -#include <QVector> QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { + +Q_NAMESPACE class Stack; class Suppression; @@ -20,7 +21,7 @@ class Suppression; /** * Error kinds, specific to memcheck */ -enum MemcheckErrorKind +enum MemcheckError { InvalidFree, MismatchedFree, @@ -37,18 +38,20 @@ enum MemcheckErrorKind Leak_PossiblyLost, Leak_StillReachable, Leak_IndirectlyLost, - MemcheckErrorKindCount + ReallocSizeZero }; +Q_ENUM_NS(MemcheckError); -enum PtrcheckErrorKind +enum PtrcheckError { SorG, Heap, Arith, SysParam }; +Q_ENUM_NS(PtrcheckError); -enum HelgrindErrorKind +enum HelgrindError { Race, UnlockUnlocked, @@ -58,6 +61,7 @@ enum HelgrindErrorKind LockOrder, Misc }; +Q_ENUM_NS(HelgrindError); class Error { @@ -85,8 +89,8 @@ public: int kind() const; void setKind(int kind); - QVector<Stack> stacks() const; - void setStacks(const QVector<Stack> &stacks); + QList<Stack> stacks() const; + void setStacks(const QList<Stack> &stacks); Suppression suppression() const; void setSuppression(const Suppression &suppression); @@ -109,7 +113,6 @@ private: QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol Q_DECLARE_METATYPE(Valgrind::XmlProtocol::Error) diff --git a/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp b/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp index a44aa73a35a..96bf9023eec 100644 --- a/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp +++ b/src/plugins/valgrind/xmlprotocol/errorlistmodel.cpp @@ -5,20 +5,16 @@ #include "error.h" #include "frame.h" #include "stack.h" -#include "modelhelpers.h" #include "../valgrindtr.h" #include <debugger/analyzer/diagnosticlocation.h> + #include <utils/qtcassert.h> -#include <QCoreApplication> #include <QDir> -#include <QVector> +#include <QList> -#include <cmath> - -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class ErrorItem : public Utils::TreeItem { @@ -72,11 +68,11 @@ Frame ErrorListModel::findRelevantFrame(const Error &error) const { if (m_relevantFrameFinder) return m_relevantFrameFinder(error); - const QVector<Stack> stacks = error.stacks(); + const QList<Stack> stacks = error.stacks(); if (stacks.isEmpty()) return Frame(); const Stack &stack = stacks[0]; - const QVector<Frame> frames = stack.frames(); + const QList<Frame> frames = stack.frames(); if (!frames.isEmpty()) return frames.first(); return Frame(); @@ -95,7 +91,7 @@ static QString makeFrameName(const Frame &frame, bool withLocation) else path = frame.object(); - if (QFile::exists(path)) + if (QFileInfo::exists(path)) path = QFileInfo(path).canonicalFilePath(); if (frame.line() != -1) @@ -142,11 +138,11 @@ ErrorItem::ErrorItem(const ErrorListModel *model, const Error &error) // just annoy the user. // The same goes for the frame level. if (m_error.stacks().count() > 1) { - const QVector<Stack> stacks = m_error.stacks(); + const QList<Stack> stacks = m_error.stacks(); for (const Stack &s : stacks) appendChild(new StackItem(s)); } else if (m_error.stacks().constFirst().frames().count() > 1) { - const QVector<Frame> frames = m_error.stacks().constFirst().frames(); + const QList<Frame> frames = m_error.stacks().constFirst().frames(); for (const Frame &f : frames) appendChild(new FrameItem(f)); } @@ -176,12 +172,12 @@ QVariant ErrorItem::data(int column, int role) const << m_model->errorLocation(m_error) << "\n"; - const QVector<Stack> stacks = m_error.stacks(); + const QList<Stack> stacks = m_error.stacks(); for (const Stack &stack : stacks) { if (!stack.auxWhat().isEmpty()) stream << stack.auxWhat(); int i = 1; - const QVector<Frame> frames = stack.frames(); + const QList<Frame> frames = stack.frames(); for (const Frame &frame : frames) stream << " " << i++ << ": " << makeFrameName(frame, true) << "\n"; } @@ -201,7 +197,7 @@ QVariant ErrorItem::data(int column, int role) const return Tr::tr("%1 in function %2") .arg(m_error.what(), m_error.stacks().constFirst().frames().constFirst().functionName()); case Qt::ToolTipRole: - return toolTipForFrame(m_model->findRelevantFrame(m_error)); + return m_model->findRelevantFrame(m_error).toolTip(); default: return QVariant(); } @@ -210,7 +206,7 @@ QVariant ErrorItem::data(int column, int role) const StackItem::StackItem(const Stack &stack) : m_stack(stack) { - const QVector<Frame> frames = m_stack.frames(); + const QList<Frame> frames = m_stack.frames(); for (const Frame &f : frames) appendChild(new FrameItem(f)); } @@ -228,7 +224,7 @@ QVariant StackItem::data(int column, int role) const case Qt::DisplayRole: return m_stack.auxWhat().isEmpty() ? errorItem->error().what() : m_stack.auxWhat(); case Qt::ToolTipRole: - return toolTipForFrame(errorItem->modelPrivate()->findRelevantFrame(errorItem->error())); + return errorItem->modelPrivate()->findRelevantFrame(errorItem->error()).toolTip(); default: return QVariant(); } @@ -263,7 +259,7 @@ QVariant FrameItem::data(int column, int role) const .arg(makeFrameName(m_frame, false)); } case Qt::ToolTipRole: - return toolTipForFrame(m_frame); + return m_frame.toolTip(); default: return QVariant(); } @@ -280,5 +276,4 @@ const ErrorItem *FrameItem::getErrorItem() const return nullptr; } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/errorlistmodel.h b/src/plugins/valgrind/xmlprotocol/errorlistmodel.h index 0d7840509a6..2136cf4f8b8 100644 --- a/src/plugins/valgrind/xmlprotocol/errorlistmodel.h +++ b/src/plugins/valgrind/xmlprotocol/errorlistmodel.h @@ -8,8 +8,7 @@ #include <functional> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Error; class Frame; @@ -39,5 +38,4 @@ private: RelevantFrameFinder m_relevantFrameFinder; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/frame.cpp b/src/plugins/valgrind/xmlprotocol/frame.cpp index 17c8e951719..a7e157c8fd5 100644 --- a/src/plugins/valgrind/xmlprotocol/frame.cpp +++ b/src/plugins/valgrind/xmlprotocol/frame.cpp @@ -3,12 +3,15 @@ #include "frame.h" +#include "../valgrindtr.h" + +#include <QList> +#include <QPair> #include <QString> #include <algorithm> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Frame::Private : public QSharedData { @@ -129,5 +132,42 @@ void Frame::setLine(int line) d->line = line; } -} // namespace XmlProtocol -} // namespace Valgrind +QString Frame::toolTip() const +{ + QString location; + if (!fileName().isEmpty()) { + location = filePath(); + if (line() > 0) + location += ':' + QString::number(line()); + } + + using StringPair = QPair<QString, QString>; + QList<StringPair> lines; + + if (!functionName().isEmpty()) + lines.append({Tr::tr("Function:"), functionName()}); + if (!location.isEmpty()) + lines.append({Tr::tr("Location:"), location}); + if (instructionPointer()) { + lines.append({Tr::tr("Instruction pointer:"), + QString("0x%1").arg(instructionPointer(), 0, 16)}); + } + if (!object().isEmpty()) + lines.append({Tr::tr("Object:"), object()}); + + QString html = "<html><head>" + "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n" + "</head><body><dl>"; + + for (const StringPair &pair : std::as_const(lines)) { + html += "<dt>"; + html += pair.first; + html += "</dt><dd>"; + html += pair.second; + html += "</dd>\n"; + } + html += "</dl></body></html>"; + return html; +} + +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/frame.h b/src/plugins/valgrind/xmlprotocol/frame.h index 727466e74d1..07c6e9c6576 100644 --- a/src/plugins/valgrind/xmlprotocol/frame.h +++ b/src/plugins/valgrind/xmlprotocol/frame.h @@ -5,8 +5,7 @@ #include <QSharedDataPointer> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Frame { @@ -41,10 +40,11 @@ public: int line() const; void setLine(int line); + QString toolTip() const; + private: class Private; QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/modelhelpers.cpp b/src/plugins/valgrind/xmlprotocol/modelhelpers.cpp deleted file mode 100644 index 9df22ef9a06..00000000000 --- a/src/plugins/valgrind/xmlprotocol/modelhelpers.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "modelhelpers.h" -#include "frame.h" -#include "../valgrindtr.h" - -#include <QString> -#include <QDir> -#include <QPair> - -namespace Valgrind { -namespace XmlProtocol { - -QString toolTipForFrame(const Frame &frame) -{ - QString location; - if (!frame.fileName().isEmpty()) { - location = frame.filePath(); - if (frame.line() > 0) - location += ':' + QString::number(frame.line()); - } - - using StringPair = QPair<QString, QString>; - QList<StringPair> lines; - - if (!frame.functionName().isEmpty()) - lines << qMakePair(Tr::tr("Function:"), frame.functionName()); - if (!location.isEmpty()) - lines << qMakePair(Tr::tr("Location:"), location); - if (frame.instructionPointer()) - lines << qMakePair(Tr::tr("Instruction pointer:"), - QString("0x%1").arg(frame.instructionPointer(), 0, 16)); - if (!frame.object().isEmpty()) - lines << qMakePair(Tr::tr("Object:"), frame.object()); - - QString html = "<html><head>" - "<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n" - "</head><body><dl>"; - - for (const StringPair &pair : std::as_const(lines)) { - html += "<dt>"; - html += pair.first; - html += "</dt><dd>"; - html += pair.second; - html += "</dd>\n"; - } - html += "</dl></body></html>"; - return html; -} - -} // namespace XmlProtocol -} // namespace Valgrind diff --git a/src/plugins/valgrind/xmlprotocol/modelhelpers.h b/src/plugins/valgrind/xmlprotocol/modelhelpers.h deleted file mode 100644 index 000d57c4bf0..00000000000 --- a/src/plugins/valgrind/xmlprotocol/modelhelpers.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QtGlobal> - -QT_BEGIN_NAMESPACE -class QString; -QT_END_NAMESPACE - -namespace Valgrind { -namespace XmlProtocol { - -class Frame; - -QString toolTipForFrame(const Frame &frame); - -} // namespace XmlProtocol -} // namespace Valgrind diff --git a/src/plugins/valgrind/xmlprotocol/parser.cpp b/src/plugins/valgrind/xmlprotocol/parser.cpp index 342277b7f1d..df464e33247 100644 --- a/src/plugins/valgrind/xmlprotocol/parser.cpp +++ b/src/plugins/valgrind/xmlprotocol/parser.cpp @@ -10,66 +10,136 @@ #include "suppression.h" #include "../valgrindtr.h" +#include <extensionsystem/pluginmanager.h> + +#include <utils/async.h> +#include <utils/expected.h> +#include <utils/futuresynchronizer.h> #include <utils/qtcassert.h> #include <QAbstractSocket> #include <QHash> -#include <QIODevice> -#include <QPair> +#include <QMetaEnum> +#include <QMutex> +#include <QPromise> +#include <QWaitCondition> #include <QXmlStreamReader> -namespace { +using namespace Utils; - class ParserException - { - public: - explicit ParserException(const QString &message) - : m_message(message) - {} +namespace Valgrind::XmlProtocol { - ~ParserException() noexcept = default; +struct ParserException +{ + QString message; +}; - QString message() const { return m_message; } +struct XWhat +{ + QString text; + qint64 leakedblocks = 0; + qint64 leakedbytes = 0; + qint64 hthreadid = -1; +}; - private: - QString m_message; +struct XauxWhat +{ + void clear() { *this = XauxWhat(); } + + QString text; + QString file; + QString dir; + qint64 line = -1; + qint64 hthreadid = -1; +}; + +enum class Tool { Unknown, Memcheck, Ptrcheck, Helgrind }; + +const QHash<QString, Tool> &toolByName() +{ + static const QHash<QString, Tool> theHash { + {"memcheck", Tool::Memcheck}, + {"ptrcheck", Tool::Ptrcheck}, + {"exp-ptrcheck", Tool::Ptrcheck}, + {"helgrind", Tool::Helgrind} }; + return theHash; +} - struct XWhat - { - QString text; - qint64 leakedblocks = 0; - qint64 leakedbytes = 0; - qint64 hthreadid = -1; - }; +struct OutputData +{ + std::optional<Status> m_status = {}; + std::optional<Error> m_error = {}; + std::optional<QPair<qint64, qint64>> m_errorCount = {}; + std::optional<QPair<QString, qint64>> m_suppressionCount = {}; + std::optional<AnnounceThread> m_announceThread = {}; + std::optional<QString> m_internalError = {}; +}; - struct XauxWhat - { - void clear() { *this = XauxWhat(); } - - QString text; - QString file; - QString dir; - qint64 line = -1; - qint64 hthreadid = -1; - }; - -} // namespace anon - -namespace Valgrind { -namespace XmlProtocol { - -class Parser::Private +class ParserThread { public: - explicit Private(Parser *qq); + // Called from the other thread, scheduled from the main thread through the queued + // invocation. + void run(QPromise<OutputData> &promise) { + if (promise.isCanceled()) + return; + m_promise = &promise; + start(); + m_promise = nullptr; + } - void parse(QIODevice *device); - QString errorString; + // Called from the main thread exclusively + void cancel() + { + QMutexLocker locker(&m_mutex); + m_state = State::Canceled; + m_waitCondition.wakeOne(); + } + // Called from the main thread exclusively + void finalize() + { + QMutexLocker locker(&m_mutex); + if (m_state != State::Awaiting) + return; + m_state = State::Finalized; + m_waitCondition.wakeOne(); + } + // Called from the main thread exclusively + void addData(const QByteArray &input) + { + if (input.isEmpty()) + return; + QMutexLocker locker(&m_mutex); + if (m_state != State::Awaiting) + return; + m_inputBuffer.append(input); + m_waitCondition.wakeOne(); + } private: + // Called from the separate thread, exclusively by run(). Checks if the new data already + // came before sleeping with wait condition. If so, it doesn't sleep with wait condition, + // but returns the data collected in meantime. Otherwise, it calls wait() on wait condition. + expected_str<QByteArray> waitForData() + { + QMutexLocker locker(&m_mutex); + while (true) { + if (m_state == State::Canceled) + return make_unexpected(Tr::tr("Parsing canceled.")); + if (!m_inputBuffer.isEmpty()) + return std::exchange(m_inputBuffer, {}); + if (m_state == State::Finalized) + return make_unexpected(Tr::tr("Premature end of XML document.")); + m_waitCondition.wait(&m_mutex); + } + QTC_CHECK(false); + return {}; + } + + void start(); void parseError(); - QVector<Frame> parseStack(); + QList<Frame> parseStack(); Suppression parseSuppression(); SuppressionFrame parseSuppressionFrame(); Frame parseFrame(); @@ -81,77 +151,45 @@ private: void checkTool(const QString &tool); XWhat parseXWhat(); XauxWhat parseXauxWhat(); - MemcheckErrorKind parseMemcheckErrorKind(const QString &kind); - HelgrindErrorKind parseHelgrindErrorKind(const QString &kind); - PtrcheckErrorKind parsePtrcheckErrorKind(const QString &kind); int parseErrorKind(const QString &kind); - void reportInternalError(const QString &errorString); QXmlStreamReader::TokenType blockingReadNext(); bool notAtEnd() const; QString blockingReadElementText(); - Tool tool; - QXmlStreamReader reader; - QHash<QString, MemcheckErrorKind> errorKindsByName_memcheck; - QHash<QString, HelgrindErrorKind> errorKindsByName_helgrind; - QHash<QString, PtrcheckErrorKind> errorKindsByName_ptrcheck; - QHash<QString, Parser::Tool> toolsByName; + void emitStatus(const Status &status) { m_promise->addResult(OutputData{{status}}); } + void emitError(const Error &error) { m_promise->addResult(OutputData{{}, {error}}); } + void emitErrorCount(qint64 unique, qint64 count) { + m_promise->addResult(OutputData{{}, {}, {{unique, count}}}); + } + void emitSuppressionCount(const QString &name, qint64 count) { + m_promise->addResult(OutputData{{}, {}, {}, {{name, count}}}); + } + void emitAnnounceThread(const AnnounceThread &announceThread) { + m_promise->addResult(OutputData{{}, {}, {}, {}, {announceThread}}); + } + void emitInternalError(const QString &errorString) { + m_promise->addResult(OutputData{{}, {}, {}, {}, {}, {errorString}}); + } -private: - Parser *const q; + enum class State { Awaiting, Finalized, Canceled }; + + Tool m_tool = Tool::Unknown; // Accessed only from the other thread. + QXmlStreamReader m_reader; // Accessed only from the other thread. + + QMutex m_mutex; + QWaitCondition m_waitCondition; + QPromise<OutputData> *m_promise = nullptr; + State m_state = State::Awaiting; // Accessed from both threads, needs mutex. + QByteArray m_inputBuffer; // Accessed from both threads, needs mutex. }; -#undef ADD_ENUM -#define ADD_ENUM(tool,enumV) { errorKindsByName_##tool.insert(#enumV, enumV); } - - -Parser::Private::Private(Parser *qq) - : q(qq) -{ - tool = Parser::Unknown; - toolsByName.insert("memcheck", Parser::Memcheck); - toolsByName.insert("ptrcheck", Parser::Ptrcheck); - toolsByName.insert("exp-ptrcheck", Parser::Ptrcheck); - toolsByName.insert("helgrind", Parser::Helgrind); - - ADD_ENUM(memcheck, ClientCheck) - ADD_ENUM(memcheck, InvalidFree) - ADD_ENUM(memcheck, InvalidJump) - ADD_ENUM(memcheck, InvalidRead) - ADD_ENUM(memcheck, InvalidWrite) - ADD_ENUM(memcheck, Leak_DefinitelyLost) - ADD_ENUM(memcheck, Leak_PossiblyLost) - ADD_ENUM(memcheck, Leak_StillReachable) - ADD_ENUM(memcheck, Leak_IndirectlyLost) - ADD_ENUM(memcheck, MismatchedFree) - ADD_ENUM(memcheck, Overlap) - ADD_ENUM(memcheck, SyscallParam) - ADD_ENUM(memcheck, UninitCondition) - ADD_ENUM(memcheck, UninitValue) - - ADD_ENUM(helgrind, Race) - ADD_ENUM(helgrind, UnlockUnlocked) - ADD_ENUM(helgrind, UnlockForeign) - ADD_ENUM(helgrind, UnlockBogus) - ADD_ENUM(helgrind, PthAPIerror) - ADD_ENUM(helgrind, LockOrder) - ADD_ENUM(helgrind, Misc) - - ADD_ENUM(ptrcheck, SorG) - ADD_ENUM(ptrcheck, Heap) - ADD_ENUM(ptrcheck, Arith) - ADD_ENUM(ptrcheck, SysParam) -} - -#undef ADD_ENUM - static quint64 parseHex(const QString &str, const QString &context) { bool ok; const quint64 v = str.toULongLong(&ok, 16); if (!ok) - throw ParserException(Tr::tr("Could not parse hex number from \"%1\" (%2)").arg(str, context)); + throw ParserException{Tr::tr("Could not parse hex number from \"%1\" (%2)").arg(str, context)}; return v; } @@ -160,74 +198,56 @@ static qint64 parseInt64(const QString &str, const QString &context) bool ok; const quint64 v = str.toLongLong(&ok); if (!ok) - throw ParserException(Tr::tr("Could not parse hex number from \"%1\" (%2)").arg(str, context)); + throw ParserException{Tr::tr("Could not parse hex number from \"%1\" (%2).").arg(str, context)}; return v; } -QXmlStreamReader::TokenType Parser::Private::blockingReadNext() +QXmlStreamReader::TokenType ParserThread::blockingReadNext() { QXmlStreamReader::TokenType token = QXmlStreamReader::Invalid; - - forever { - token = reader.readNext(); - - if (reader.error() == QXmlStreamReader::PrematureEndOfDocumentError) { - if (reader.device()->waitForReadyRead(1000)) { - // let's try again + while (true) { + token = m_reader.readNext(); + if (m_reader.error() == QXmlStreamReader::PrematureEndOfDocumentError) { + const auto data = waitForData(); + if (data) { + m_reader.addData(*data); continue; } else { - // device error, e.g. remote host closed connection, or timeout - // ### we have no way to know if waitForReadyRead() timed out or failed with a real - // error, and sensible heuristics based on QIODevice fail. - // - error strings are translated and in any case not guaranteed to stay the same, - // so we can't use them. - // - errorString().isEmpty() does not work because errorString() is - // "network timeout error" if the waitFor... timed out. - // - isSequential() [for socket] && isOpen() doesn't work because isOpen() - // returns true if the remote host closed the connection. - // ...so we fall back to knowing it might be a QAbstractSocket. - - QIODevice *dev = reader.device(); - auto sock = qobject_cast<const QAbstractSocket *>(dev); - - if (!sock || sock->state() != QAbstractSocket::ConnectedState) - throw ParserException(dev->errorString()); + throw ParserException{data.error()}; } - } else if (reader.hasError()) { - throw ParserException(reader.errorString()); //TODO add line, column? + } else if (m_reader.hasError()) { + throw ParserException{m_reader.errorString()}; //TODO add line, column? break; } else { // read a valid next token break; } } - return token; } -bool Parser::Private::notAtEnd() const +bool ParserThread::notAtEnd() const { - return !reader.atEnd() - || reader.error() == QXmlStreamReader::PrematureEndOfDocumentError; + return !m_reader.atEnd() || m_reader.error() == QXmlStreamReader::PrematureEndOfDocumentError; } -QString Parser::Private::blockingReadElementText() +QString ParserThread::blockingReadElementText() { //analogous to QXmlStreamReader::readElementText(), but blocking. readElementText() doesn't recover from PrematureEndOfData, //but always returns a null string if isStartElement() is false (which is the case if it already parts of the text) //affects at least Qt <= 4.7.1. Reported as QTBUG-14661. - if (!reader.isStartElement()) - throw ParserException(Tr::tr("trying to read element text although current position is not start of element")); + if (!m_reader.isStartElement()) + throw ParserException{Tr::tr("Trying to read element text although current position is not start of element.")}; QString result; - forever { + while (true) { const QXmlStreamReader::TokenType type = blockingReadNext(); switch (type) { case QXmlStreamReader::Characters: case QXmlStreamReader::EntityReference: - result += reader.text(); + result += m_reader.text(); break; case QXmlStreamReader::EndElement: return result; @@ -235,44 +255,42 @@ QString Parser::Private::blockingReadElementText() case QXmlStreamReader::Comment: break; case QXmlStreamReader::StartElement: - throw ParserException(Tr::tr("Unexpected child element while reading element text")); + throw ParserException{Tr::tr("Unexpected child element while reading element text")}; default: //TODO handle - throw ParserException(Tr::tr("Unexpected token type %1").arg(type)); + throw ParserException{Tr::tr("Unexpected token type %1").arg(type)}; break; } } - return QString(); + return {}; } -void Parser::Private::checkProtocolVersion(const QString &versionStr) +void ParserThread::checkProtocolVersion(const QString &versionStr) { bool ok; const int version = versionStr.toInt(&ok); if (!ok) - throw ParserException(Tr::tr("Could not parse protocol version from \"%1\"").arg(versionStr)); + throw ParserException{Tr::tr("Could not parse protocol version from \"%1\"").arg(versionStr)}; if (version != 4) - throw ParserException(Tr::tr("XmlProtocol version %1 not supported (supported version: 4)").arg(version)); + throw ParserException{Tr::tr("XmlProtocol version %1 not supported (supported version: 4)").arg(version)}; } -void Parser::Private::checkTool(const QString &reportedStr) +void ParserThread::checkTool(const QString &reportedStr) { - const QHash<QString,Parser::Tool>::ConstIterator reported = toolsByName.constFind(reportedStr); - - if (reported == toolsByName.constEnd()) - throw ParserException(Tr::tr("Valgrind tool \"%1\" not supported").arg(reportedStr)); - - tool = reported.value(); + const auto reported = toolByName().constFind(reportedStr); + if (reported == toolByName().constEnd()) + throw ParserException{Tr::tr("Valgrind tool \"%1\" not supported").arg(reportedStr)}; + m_tool = reported.value(); } -XWhat Parser::Private::parseXWhat() +XWhat ParserThread::parseXWhat() { XWhat what; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - const auto name = reader.name(); + const auto name = m_reader.name(); if (name == QLatin1String("text")) what.text = blockingReadElementText(); else if (name == QLatin1String("leakedbytes")) @@ -281,20 +299,20 @@ XWhat Parser::Private::parseXWhat() what.leakedblocks = parseInt64(blockingReadElementText(), "error/xwhat[memcheck]/leakedblocks"); else if (name == QLatin1String("hthreadid")) what.hthreadid = parseInt64(blockingReadElementText(), "error/xwhat[memcheck]/hthreadid"); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } return what; } -XauxWhat Parser::Private::parseXauxWhat() +XauxWhat ParserThread::parseXauxWhat() { XauxWhat what; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - const auto name = reader.name(); + const auto name = m_reader.name(); if (name == QLatin1String("text")) what.text = blockingReadElementText(); else if (name == QLatin1String("file")) @@ -305,55 +323,37 @@ XauxWhat Parser::Private::parseXauxWhat() what.line = parseInt64(blockingReadElementText(), "error/xauxwhat/line"); else if (name == QLatin1String("hthreadid")) what.hthreadid = parseInt64(blockingReadElementText(), "error/xauxwhat/hthreadid"); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } return what; } - - -MemcheckErrorKind Parser::Private::parseMemcheckErrorKind(const QString &kind) +template <typename Enum> +int parseErrorEnum(const QString &kind) { - const QHash<QString,MemcheckErrorKind>::ConstIterator it = errorKindsByName_memcheck.constFind(kind); - if (it != errorKindsByName_memcheck.constEnd()) - return *it; - else - throw ParserException(Tr::tr("Unknown memcheck error kind \"%1\"").arg(kind)); + const QMetaEnum metaEnum = QMetaEnum::fromType<Enum>(); + const int value = metaEnum.keyToValue(kind.toUtf8()); + if (value >= 0) + return value; + throw ParserException{Tr::tr("Unknown %1 kind \"%2\"") + .arg(QString::fromUtf8(metaEnum.enumName()), kind)}; } -HelgrindErrorKind Parser::Private::parseHelgrindErrorKind(const QString &kind) +int ParserThread::parseErrorKind(const QString &kind) { - const QHash<QString,HelgrindErrorKind>::ConstIterator it = errorKindsByName_helgrind.constFind(kind); - if (it != errorKindsByName_helgrind.constEnd()) - return *it; - else - throw ParserException(Tr::tr("Unknown helgrind error kind \"%1\"").arg(kind)); -} - -PtrcheckErrorKind Parser::Private::parsePtrcheckErrorKind(const QString &kind) -{ - const QHash<QString,PtrcheckErrorKind>::ConstIterator it = errorKindsByName_ptrcheck.constFind(kind); - if (it != errorKindsByName_ptrcheck.constEnd()) - return *it; - else - throw ParserException(Tr::tr("Unknown ptrcheck error kind \"%1\"").arg(kind)); -} - -int Parser::Private::parseErrorKind(const QString &kind) -{ - switch (tool) { - case Memcheck: - return parseMemcheckErrorKind(kind); - case Ptrcheck: - return parsePtrcheckErrorKind(kind); - case Helgrind: - return parseHelgrindErrorKind(kind); - case Unknown: + switch (m_tool) { + case Tool::Memcheck: + return parseErrorEnum<MemcheckError>(kind); + case Tool::Ptrcheck: + return parseErrorEnum<PtrcheckError>(kind); + case Tool::Helgrind: + return parseErrorEnum<HelgrindError>(kind); + case Tool::Unknown: default: break; } - throw ParserException(Tr::tr("Could not parse error kind, tool not yet set.")); + throw ParserException{Tr::tr("Could not parse error kind, tool not yet set.")}; } static Status::State parseState(const QString &state) @@ -362,16 +362,10 @@ static Status::State parseState(const QString &state) return Status::Running; if (state == "FINISHED") return Status::Finished; - throw ParserException(Tr::tr("Unknown state \"%1\"").arg(state)); + throw ParserException{Tr::tr("Unknown state \"%1\"").arg(state)}; } -void Parser::Private::reportInternalError(const QString &e) -{ - errorString = e; - emit q->internalError(e); -} - -static Stack makeStack(const XauxWhat &xauxwhat, const QVector<Frame> &frames) +static Stack makeStack(const XauxWhat &xauxwhat, const QList<Frame> &frames) { Stack s; s.setFrames(frames); @@ -383,21 +377,20 @@ static Stack makeStack(const XauxWhat &xauxwhat, const QVector<Frame> &frames) return s; } -void Parser::Private::parseError() +void ParserThread::parseError() { Error e; - QVector<QVector<Frame> > frames; + QList<QList<Frame>> frames; XauxWhat currentAux; - QVector<XauxWhat> auxs; - + QList<XauxWhat> auxs; int lastAuxWhat = -1; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) + if (m_reader.isStartElement()) lastAuxWhat++; - const auto name = reader.name(); + const auto name = m_reader.name(); if (name == QLatin1String("unique")) { e.setUnique(parseHex(blockingReadElementText(), "unique")); } else if (name == QLatin1String("tid")) { @@ -434,8 +427,8 @@ void Parser::Private::parseError() lastAuxWhat = 0; } else if (name == QLatin1String("stack")) { frames.push_back(parseStack()); - } else if (reader.isStartElement()) { - reader.skipCurrentElement(); + } else if (m_reader.isStartElement()) { + m_reader.skipCurrentElement(); } } @@ -449,26 +442,25 @@ void Parser::Private::parseError() //add empty stacks until sizes match while (frames.size() < auxs.size()) - frames.push_back(QVector<Frame>()); + frames.push_back({}); - QVector<Stack> stacks; + QList<Stack> stacks; for (int i = 0; i < auxs.size(); ++i) stacks.append(makeStack(auxs[i], frames[i])); e.setStacks(stacks); - emit q->error(e); + emitError(e); } -Frame Parser::Private::parseFrame() +Frame ParserThread::parseFrame() { Frame frame; - while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("ip")) frame.setInstructionPointer(parseHex(blockingReadElementText(), "error/frame/ip")); else if (name == QLatin1String("obj")) @@ -481,169 +473,160 @@ Frame Parser::Private::parseFrame() frame.setFileName(blockingReadElementText()); else if (name == QLatin1String("line")) frame.setLine(parseInt64(blockingReadElementText(), "error/frame/line")); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - return frame; } -void Parser::Private::parseAnnounceThread() +void ParserThread::parseAnnounceThread() { AnnounceThread at; - while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("hthreadid")) at.setHelgrindThreadId(parseInt64(blockingReadElementText(), "announcethread/hthreadid")); else if (name == QLatin1String("stack")) at.setStack(parseStack()); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - - emit q->announceThread(at); + emitAnnounceThread(at); } -void Parser::Private::parseErrorCounts() +void ParserThread::parseErrorCounts() { while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - if (reader.name() == QLatin1String("pair")) { + if (m_reader.isStartElement()) { + if (m_reader.name() == QLatin1String("pair")) { qint64 unique = 0; qint64 count = 0; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("unique")) unique = parseHex(blockingReadElementText(), "errorcounts/pair/unique"); else if (name == QLatin1String("count")) count = parseInt64(blockingReadElementText(), "errorcounts/pair/count"); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - emit q->errorCount(unique, count); - } else if (reader.isStartElement()) - reader.skipCurrentElement(); + emitErrorCount(unique, count); + } else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } } - -void Parser::Private::parseSuppressionCounts() +void ParserThread::parseSuppressionCounts() { while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - if (reader.name() == QLatin1String("pair")) { + if (m_reader.isStartElement()) { + if (m_reader.name() == QLatin1String("pair")) { QString pairName; qint64 count = 0; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("name")) pairName = blockingReadElementText(); else if (name == QLatin1String("count")) count = parseInt64(blockingReadElementText(), "suppcounts/pair/count"); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - emit q->suppressionCount(pairName, count); - } else if (reader.isStartElement()) - reader.skipCurrentElement(); + emitSuppressionCount(pairName, count); + } else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } } -void Parser::Private::parseStatus() +void ParserThread::parseStatus() { Status s; - while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("state")) s.setState(parseState(blockingReadElementText())); else if (name == QLatin1String("time")) s.setTime(blockingReadElementText()); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - - emit q->status(s); + emitStatus(s); } -QVector<Frame> Parser::Private::parseStack() +QList<Frame> ParserThread::parseStack() { - QVector<Frame> frames; + QList<Frame> frames; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - if (reader.name() == QLatin1String("frame")) + if (m_reader.isStartElement()) { + if (m_reader.name() == QLatin1String("frame")) frames.append(parseFrame()); } } - return frames; } -SuppressionFrame Parser::Private::parseSuppressionFrame() +SuppressionFrame ParserThread::parseSuppressionFrame() { SuppressionFrame frame; - while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("obj")) frame.setObject(blockingReadElementText()); else if (name == QLatin1String("fun")) frame.setFunction( blockingReadElementText()); - else if (reader.isStartElement()) - reader.skipCurrentElement(); + else if (m_reader.isStartElement()) + m_reader.skipCurrentElement(); } } - return frame; } -Suppression Parser::Private::parseSuppression() +Suppression ParserThread::parseSuppression() { Suppression supp; SuppressionFrames frames; while (notAtEnd()) { blockingReadNext(); - if (reader.isEndElement()) + if (m_reader.isEndElement()) break; - if (reader.isStartElement()) { - const auto name = reader.name(); + if (m_reader.isStartElement()) { + const auto name = m_reader.name(); if (name == QLatin1String("sname")) supp.setName(blockingReadElementText()); else if (name == QLatin1String("skind")) @@ -656,20 +639,16 @@ Suppression Parser::Private::parseSuppression() frames.push_back(parseSuppressionFrame()); } } - supp.setFrames(frames); return supp; } -void Parser::Private::parse(QIODevice *device) +void ParserThread::start() { - QTC_ASSERT(device, return); - reader.setDevice(device); - try { while (notAtEnd()) { blockingReadNext(); - const auto name = reader.name(); + const auto name = m_reader.name(); if (name == QLatin1String("error")) parseError(); else if (name == QLatin1String("announcethread")) @@ -686,33 +665,137 @@ void Parser::Private::parse(QIODevice *device) checkTool(blockingReadElementText()); } } catch (const ParserException &e) { - reportInternalError(e.message()); + emitInternalError(e.message); } catch (...) { - reportInternalError(Tr::tr("Unexpected exception caught during parsing.")); + emitInternalError(Tr::tr("Unexpected exception caught during parsing.")); } - emit q->finished(); } +class ParserPrivate +{ +public: + ParserPrivate(Parser *parser) + : q(parser) + {} + + ~ParserPrivate() + { + if (!m_watcher) + return; + m_thread->cancel(); + ExtensionSystem::PluginManager::futureSynchronizer()->addFuture(m_watcher->future()); + } + + void start() + { + QTC_ASSERT(!m_watcher, return); + QTC_ASSERT(m_socket || !m_data.isEmpty(), return); + + m_errorString = {}; + m_thread.reset(new ParserThread); + m_watcher.reset(new QFutureWatcher<OutputData>); + QObject::connect(m_watcher.get(), &QFutureWatcherBase::resultReadyAt, q, [this](int index) { + const OutputData data = m_watcher->resultAt(index); + if (data.m_status) + emit q->status(*data.m_status); + if (data.m_error) + emit q->error(*data.m_error); + if (data.m_errorCount) + emit q->errorCount(data.m_errorCount->first, data.m_errorCount->second); + if (data.m_suppressionCount) + emit q->suppressionCount(data.m_suppressionCount->first, data.m_suppressionCount->second); + if (data.m_announceThread) + emit q->announceThread(*data.m_announceThread); + if (data.m_internalError) + m_errorString = data.m_internalError; + }); + QObject::connect(m_watcher.get(), &QFutureWatcherBase::finished, q, [this] { + emit q->done(!m_errorString, *m_errorString); + m_watcher.release()->deleteLater(); + m_thread.reset(); + m_socket.reset(); + }); + if (m_socket) { + QObject::connect(m_socket.get(), &QIODevice::readyRead, q, [this] { + if (m_thread) + m_thread->addData(m_socket->readAll()); + }); + QObject::connect(m_socket.get(), &QAbstractSocket::disconnected, q, [this] { + if (m_thread) + m_thread->finalize(); + }); + m_thread->addData(m_socket->readAll()); + } else { + m_thread->addData(m_data); + m_thread->finalize(); + } + auto parse = [](QPromise<OutputData> &promise, const std::shared_ptr<ParserThread> &thread) { + thread->run(promise); + }; + m_watcher->setFuture(Utils::asyncRun(parse, m_thread)); + } + + Parser *q = nullptr; + + QByteArray m_data; + std::unique_ptr<QAbstractSocket> m_socket; + std::unique_ptr<QFutureWatcher<OutputData>> m_watcher; + std::shared_ptr<ParserThread> m_thread; + std::optional<QString> m_errorString; +}; + Parser::Parser(QObject *parent) : QObject(parent) - , d(new Private(this)) -{ -} + , d(new ParserPrivate(this)) +{} -Parser::~Parser() -{ - delete d; -} +Parser::~Parser() = default; QString Parser::errorString() const { - return d->errorString; + return d->m_errorString.value_or(QString()); } -void Parser::parse(QIODevice *device) +void Parser::setSocket(QAbstractSocket *socket) { - d->parse(device); + QTC_ASSERT(socket, return); + QTC_ASSERT(socket->isOpen(), return); + QTC_ASSERT(!isRunning(), return); + socket->setParent(nullptr); // Don't delete it together with parent QTcpServer anymore. + d->m_socket.reset(socket); } -} // namespace XmlParser -} // namespace Valgrind +void Parser::setData(const QByteArray &data) +{ + QTC_ASSERT(!isRunning(), return); + d->m_data = data; +} + +void Parser::start() +{ + d->start(); +} + +bool Parser::isRunning() const +{ + return d->m_watcher.get(); +} + +bool Parser::runBlocking() +{ + bool ok = false; + QEventLoop loop; + + const auto finalize = [&loop, &ok](bool success) { + ok = success; + // Refer to the QObject::deleteLater() docs. + QMetaObject::invokeMethod(&loop, [&loop] { loop.quit(); }, Qt::QueuedConnection); + }; + + connect(this, &Parser::done, &loop, finalize); + QTimer::singleShot(0, this, &Parser::start); + loop.exec(QEventLoop::ExcludeUserInputEvents); + return ok; +} + +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/parser.h b/src/plugins/valgrind/xmlprotocol/parser.h index 5be623a3513..df34601bc35 100644 --- a/src/plugins/valgrind/xmlprotocol/parser.h +++ b/src/plugins/valgrind/xmlprotocol/parser.h @@ -3,17 +3,19 @@ #pragma once +#include <solutions/tasking/tasktree.h> + #include <QObject> QT_BEGIN_NAMESPACE -class QIODevice; +class QAbstractSocket; QT_END_NAMESPACE -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class AnnounceThread; class Error; +class ParserPrivate; class Status; /** @@ -24,32 +26,38 @@ class Parser : public QObject Q_OBJECT public: - enum Tool { - Unknown, - Memcheck, - Ptrcheck, - Helgrind - }; - explicit Parser(QObject *parent = nullptr); ~Parser() override; QString errorString() const; - void parse(QIODevice *stream); + // The passed device needs to be open. The parser takes ownership of the passed device. + void setSocket(QAbstractSocket *socket); + // Alternatively, the data to be parsed may be set manually + void setData(const QByteArray &data); + + void start(); + bool isRunning() const; + bool runBlocking(); signals: - void status(const Valgrind::XmlProtocol::Status &status); - void error(const Valgrind::XmlProtocol::Error &error); - void internalError(const QString &errorString); + void status(const Status &status); + void error(const Error &error); void errorCount(qint64 unique, qint64 count); void suppressionCount(const QString &name, qint64 count); - void announceThread(const Valgrind::XmlProtocol::AnnounceThread &announceThread); - void finished(); + void announceThread(const AnnounceThread &announceThread); + void done(bool success, const QString &errorString); private: - class Private; - Private *const d; + std::unique_ptr<ParserPrivate> d; }; -} // XmlProtocol -} // Valgrind +class ParserTaskAdapter : public Tasking::TaskAdapter<Parser> +{ +public: + ParserTaskAdapter() { connect(task(), &Parser::done, this, &Tasking::TaskInterface::done); } + void start() final { task()->start(); } +}; + +using ParserTask = Tasking::CustomTask<ParserTaskAdapter>; + +} // Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/stack.cpp b/src/plugins/valgrind/xmlprotocol/stack.cpp index 5d977bbad12..d7528251020 100644 --- a/src/plugins/valgrind/xmlprotocol/stack.cpp +++ b/src/plugins/valgrind/xmlprotocol/stack.cpp @@ -4,12 +4,11 @@ #include "stack.h" #include "frame.h" +#include <QList> #include <QSharedData> #include <QString> -#include <QVector> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Stack::Private : public QSharedData { @@ -19,7 +18,7 @@ public: QString dir; qint64 line = -1; qint64 hthreadid = -1; - QVector<Frame> frames; + QList<Frame> frames; }; Stack::Stack() @@ -62,12 +61,12 @@ void Stack::setAuxWhat(const QString &auxwhat) d->auxwhat = auxwhat; } -QVector<Frame> Stack::frames() const +QList<Frame> Stack::frames() const { return d->frames; } -void Stack::setFrames(const QVector<Frame> &frames) +void Stack::setFrames(const QList<Frame> &frames) { d->frames = frames; } @@ -112,5 +111,4 @@ void Stack::setHelgrindThreadId(qint64 id) d->hthreadid = id; } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/stack.h b/src/plugins/valgrind/xmlprotocol/stack.h index 8945984186b..a22f23451cd 100644 --- a/src/plugins/valgrind/xmlprotocol/stack.h +++ b/src/plugins/valgrind/xmlprotocol/stack.h @@ -3,11 +3,10 @@ #pragma once +#include <QList> #include <QSharedDataPointer> -#include <QVector> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Frame; @@ -24,8 +23,8 @@ public: QString auxWhat() const; void setAuxWhat(const QString &auxwhat); - QVector<Frame> frames() const; - void setFrames(const QVector<Frame> &frames); + QList<Frame> frames() const; + void setFrames(const QList<Frame> &frames); //memcheck, ptrcheck, helgrind: QString file() const; @@ -46,5 +45,4 @@ private: QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Stack +} // namespace Stack::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/stackmodel.cpp b/src/plugins/valgrind/xmlprotocol/stackmodel.cpp index fceee892bb4..8d96c0ff6e7 100644 --- a/src/plugins/valgrind/xmlprotocol/stackmodel.cpp +++ b/src/plugins/valgrind/xmlprotocol/stackmodel.cpp @@ -5,16 +5,11 @@ #include "error.h" #include "frame.h" #include "stack.h" -#include "modelhelpers.h" #include "../valgrindtr.h" #include <utils/qtcassert.h> -#include <QDir> -#include <QVector> - -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class StackModel::Private { @@ -76,7 +71,7 @@ QVariant StackModel::data(const QModelIndex &index, int role) const } } else { const Stack stack = d->stack(index.parent().row()); - const QVector<Frame> frames = stack.frames(); + const QList<Frame> frames = stack.frames(); const int fidx = index.row(); if (fidx < 0 || fidx >= frames.size()) return QVariant(); @@ -106,7 +101,7 @@ QVariant StackModel::data(const QModelIndex &index, int role) const break; } case Qt::ToolTipRole: - return toolTipForFrame(frame); + return frame.toolTip(); case ObjectRole: return frame.object(); case FunctionNameRole: @@ -205,5 +200,4 @@ void StackModel::clear() endResetModel(); } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/stackmodel.h b/src/plugins/valgrind/xmlprotocol/stackmodel.h index db2613fec60..ff1350108f6 100644 --- a/src/plugins/valgrind/xmlprotocol/stackmodel.h +++ b/src/plugins/valgrind/xmlprotocol/stackmodel.h @@ -5,8 +5,7 @@ #include <QAbstractItemModel> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Error; @@ -51,5 +50,4 @@ private: Private *const d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/status.cpp b/src/plugins/valgrind/xmlprotocol/status.cpp index 3bf310ba5cc..a5f8cf4c871 100644 --- a/src/plugins/valgrind/xmlprotocol/status.cpp +++ b/src/plugins/valgrind/xmlprotocol/status.cpp @@ -6,8 +6,7 @@ #include <QSharedData> #include <QString> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Status::Private : public QSharedData { @@ -62,5 +61,4 @@ QString Status::time() const return d->time; } -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/status.h b/src/plugins/valgrind/xmlprotocol/status.h index 363fd09fcbe..d48748ad702 100644 --- a/src/plugins/valgrind/xmlprotocol/status.h +++ b/src/plugins/valgrind/xmlprotocol/status.h @@ -6,8 +6,7 @@ #include <QMetaType> #include <QSharedDataPointer> -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class Status { @@ -35,7 +34,6 @@ private: QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol Q_DECLARE_METATYPE(Valgrind::XmlProtocol::Status) diff --git a/src/plugins/valgrind/xmlprotocol/suppression.cpp b/src/plugins/valgrind/xmlprotocol/suppression.cpp index 5d86d9a0db1..c76caf7bed5 100644 --- a/src/plugins/valgrind/xmlprotocol/suppression.cpp +++ b/src/plugins/valgrind/xmlprotocol/suppression.cpp @@ -5,7 +5,6 @@ #include <QSharedData> #include <QString> -#include <QVector> #include <QTextStream> #include <algorithm> diff --git a/src/plugins/valgrind/xmlprotocol/suppression.h b/src/plugins/valgrind/xmlprotocol/suppression.h index 328166ca23f..08c8861fd86 100644 --- a/src/plugins/valgrind/xmlprotocol/suppression.h +++ b/src/plugins/valgrind/xmlprotocol/suppression.h @@ -3,15 +3,14 @@ #pragma once +#include <QList> #include <QSharedDataPointer> -#include <QVector> QT_BEGIN_NAMESPACE class QString; QT_END_NAMESPACE -namespace Valgrind { -namespace XmlProtocol { +namespace Valgrind::XmlProtocol { class SuppressionFrame { @@ -40,7 +39,7 @@ private: QSharedDataPointer<Private> d; }; -using SuppressionFrames = QVector<SuppressionFrame>; +using SuppressionFrames = QList<SuppressionFrame>; class Suppression { @@ -77,5 +76,4 @@ private: QSharedDataPointer<Private> d; }; -} // namespace XmlProtocol -} // namespace Valgrind +} // namespace Valgrind::XmlProtocol diff --git a/src/plugins/valgrind/xmlprotocol/threadedparser.cpp b/src/plugins/valgrind/xmlprotocol/threadedparser.cpp deleted file mode 100644 index 788d95aafaf..00000000000 --- a/src/plugins/valgrind/xmlprotocol/threadedparser.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "threadedparser.h" -#include "parser.h" -#include "error.h" -#include "status.h" - -#include <utils/qtcassert.h> - -#include <QIODevice> -#include <QMetaType> -#include <QThread> -#include <QPointer> - -namespace { - -class Thread : public QThread -{ -public: - void run() override - { - QTC_ASSERT(QThread::currentThread() == this, return); - parser->parse(device); - delete parser; - parser = nullptr; - delete device; - device = nullptr; - } - - Valgrind::XmlProtocol::Parser *parser = nullptr; - QIODevice *device = nullptr; -}; - -} // namespace anon - - -namespace Valgrind { -namespace XmlProtocol { - -class ThreadedParser::Private -{ -public: - QPointer<Thread> parserThread; - QString errorString; -}; - - -ThreadedParser::ThreadedParser(QObject *parent) - : QObject(parent), - d(new Private) -{ -} - -ThreadedParser::~ThreadedParser() -{ - delete d; -} - -QString ThreadedParser::errorString() const -{ - return d->errorString; -} - -bool ThreadedParser::isRunning() const -{ - return d->parserThread ? d->parserThread.data()->isRunning() : false; -} - -void ThreadedParser::parse(QIODevice *device) -{ - QTC_ASSERT(!d->parserThread, return); - - auto parser = new Parser; - qRegisterMetaType<Valgrind::XmlProtocol::Status>(); - qRegisterMetaType<Valgrind::XmlProtocol::Error>(); - connect(parser, &Parser::status, - this, &ThreadedParser::status, - Qt::QueuedConnection); - connect(parser, &Parser::error, - this, &ThreadedParser::error, - Qt::QueuedConnection); - connect(parser, &Parser::internalError, - this, &ThreadedParser::slotInternalError, - Qt::QueuedConnection); - connect(parser, &Parser::errorCount, - this, &ThreadedParser::errorCount, - Qt::QueuedConnection); - connect(parser, &Parser::suppressionCount, - this, &ThreadedParser::suppressionCount, - Qt::QueuedConnection); - connect(parser, &Parser::finished, - this, &ThreadedParser::finished, - Qt::QueuedConnection); - - - auto thread = new Thread; - d->parserThread = thread; - connect(thread, &QThread::finished, - thread, &QObject::deleteLater); - device->setParent(nullptr); - device->moveToThread(thread); - parser->moveToThread(thread); - thread->device = device; - thread->parser = parser; - thread->start(); -} - -void ThreadedParser::slotInternalError(const QString &errorString) -{ - d->errorString = errorString; - emit internalError(errorString); -} - -bool ThreadedParser::waitForFinished() -{ - return d->parserThread ? d->parserThread.data()->wait() : true; -} - -} // namespace XmlProtocol -} // namespace Valgrind diff --git a/src/plugins/valgrind/xmlprotocol/threadedparser.h b/src/plugins/valgrind/xmlprotocol/threadedparser.h deleted file mode 100644 index 08d9cd760b9..00000000000 --- a/src/plugins/valgrind/xmlprotocol/threadedparser.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QObject> - -QT_BEGIN_NAMESPACE -class QIODevice; -QT_END_NAMESPACE - -namespace Valgrind { -namespace XmlProtocol { - -class Error; -class Status; - -/** - * ThreadedParser for the Valgrind Output XmlProtocol 4 - */ -class ThreadedParser : public QObject -{ - Q_OBJECT - -public: - explicit ThreadedParser(QObject *parent = nullptr); - ~ThreadedParser() override; - - QString errorString() const; - - /// interface additions relative to Parser because Parser is synchronous and this - /// class parses asynchronously in a non-public secondary thread. - bool waitForFinished(); - bool isRunning() const; - - ///@warning will move @p stream to a different thread and take ownership of it - void parse(QIODevice *stream); - -private: - void slotInternalError(const QString &errorString); - -signals: - void status(const Valgrind::XmlProtocol::Status &status); - void error(const Valgrind::XmlProtocol::Error &error); - void internalError(const QString &errorString); - void errorCount(qint64 unique, qint64 count); - void suppressionCount(const QString &name, qint64 count); - void finished(); - -private: - class Private; - Private *const d; -}; - -} // XmlProtocol -} // Valgrind diff --git a/src/plugins/vcpkg/CMakeLists.txt b/src/plugins/vcpkg/CMakeLists.txt index d3fb8d00ed8..ba5f156a4ee 100644 --- a/src/plugins/vcpkg/CMakeLists.txt +++ b/src/plugins/vcpkg/CMakeLists.txt @@ -4,7 +4,7 @@ add_qtc_plugin(Vcpkg vcpkg.qrc vcpkgconstants.h vcpkgmanifesteditor.cpp vcpkgmanifesteditor.h - vcpkgplugin.cpp vcpkgplugin.h + vcpkgplugin.cpp vcpkgsearch.cpp vcpkgsearch.h vcpkgsettings.cpp vcpkgsettings.h ) diff --git a/src/plugins/vcpkg/Vcpkg.json.in b/src/plugins/vcpkg/Vcpkg.json.in index 7357c9e845b..f3ab4f0a5d7 100644 --- a/src/plugins/vcpkg/Vcpkg.json.in +++ b/src/plugins/vcpkg/Vcpkg.json.in @@ -1,30 +1,30 @@ { - \"Name\" : \"Vcpkg\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Vcpkg", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Experimental\" : true, - \"Description\" : \"vcpkg integration.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList, + "Experimental" : true, + "Description" : "vcpkg integration.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES}, - \"Mimetypes\" : [ - \"<?xml version=\'1.0\' encoding=\'UTF-8\'?>\", - \"<mime-info xmlns=\'http://www.freedesktop.org/standards/shared-mime-info\'>\", - \" <mime-type type=\'application/vcpkg.manifest+json\'>\", - \" <sub-class-of type=\'application/json\'/>\", - \" <comment>Vcpkg Manifest File</comment>\", - \" <glob pattern=\'vcpkg.json\' weight=\'71\'/>\", - \" </mime-type>\", - \"</mime-info>\" + "Mimetypes" : [ + "<?xml version='1.0' encoding='UTF-8'?>", + "<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>", + " <mime-type type='application/vcpkg.manifest+json'>", + " <sub-class-of type='application/json'/>", + " <comment>Vcpkg Manifest File</comment>", + " <glob pattern='vcpkg.json' weight='71'/>", + " </mime-type>", + "</mime-info>" ] } diff --git a/src/plugins/vcpkg/images/vcpkgicon.png b/src/plugins/vcpkg/images/vcpkgicon.png new file mode 100644 index 00000000000..28231853671 Binary files /dev/null and b/src/plugins/vcpkg/images/vcpkgicon.png differ diff --git a/src/plugins/vcpkg/images/vcpkgicon@2x.png b/src/plugins/vcpkg/images/vcpkgicon@2x.png new file mode 100644 index 00000000000..df97ed38b49 Binary files /dev/null and b/src/plugins/vcpkg/images/vcpkgicon@2x.png differ diff --git a/src/plugins/vcpkg/vcpkg.qbs b/src/plugins/vcpkg/vcpkg.qbs index dff796ab917..95bcc1e71d7 100644 --- a/src/plugins/vcpkg/vcpkg.qbs +++ b/src/plugins/vcpkg/vcpkg.qbs @@ -16,7 +16,6 @@ QtcPlugin { "vcpkgmanifesteditor.cpp", "vcpkgmanifesteditor.h", "vcpkgplugin.cpp", - "vcpkgplugin.h", "vcpkgsearch.cpp", "vcpkgsearch.h", "vcpkgsettings.cpp", diff --git a/src/plugins/vcpkg/vcpkg.qrc b/src/plugins/vcpkg/vcpkg.qrc index a377b736db3..e01340cab4d 100644 --- a/src/plugins/vcpkg/vcpkg.qrc +++ b/src/plugins/vcpkg/vcpkg.qrc @@ -2,5 +2,7 @@ <qresource prefix="/vcpkg"> <file>wizards/manifest/vcpkg.json.tpl</file> <file>wizards/manifest/wizard.json</file> + <file>images/vcpkgicon.png</file> + <file>images/vcpkgicon@2x.png</file> </qresource> </RCC> diff --git a/src/plugins/vcpkg/vcpkg_test.cpp b/src/plugins/vcpkg/vcpkg_test.cpp index 65079b96a4e..71e3d50a848 100644 --- a/src/plugins/vcpkg/vcpkg_test.cpp +++ b/src/plugins/vcpkg/vcpkg_test.cpp @@ -3,6 +3,7 @@ #include "vcpkg_test.h" +#include "vcpkgmanifesteditor.h" #include "vcpkgsearch.h" #include <QTest> @@ -21,6 +22,7 @@ void VcpkgSearchTest::testVcpkgJsonParser_data() QTest::addColumn<QString>("name"); QTest::addColumn<QString>("version"); QTest::addColumn<QString>("license"); + QTest::addColumn<QStringList>("dependencies"); QTest::addColumn<QString>("shortDescription"); QTest::addColumn<QStringList>("description"); QTest::addColumn<QUrl>("homepage"); @@ -33,6 +35,7 @@ void VcpkgSearchTest::testVcpkgJsonParser_data() "description": "The CImg Library is a small, open-source, and modern C++ toolkit for image processing", "homepage": "https://github.com/dtschump/CImg", "dependencies": [ + "fmt", { "name": "vcpkg-cmake", "host": true @@ -42,6 +45,7 @@ void VcpkgSearchTest::testVcpkgJsonParser_data() << "cimg" << "2.9.9" << "" + << QStringList({"fmt", "vcpkg-cmake"}) << "The CImg Library is a small, open-source, and modern C++ toolkit for image processing" << QStringList() << QUrl::fromUserInput("https://github.com/dtschump/CImg") @@ -62,6 +66,7 @@ void VcpkgSearchTest::testVcpkgJsonParser_data() << "catch-classic" << "1.12.2" << "" + << QStringList() << "A modern, header-only test framework for unit tests" << QStringList({"This is specifically the legacy 1.x branch provided for compatibility", "with older compilers."}) @@ -77,6 +82,7 @@ void VcpkgSearchTest::testVcpkgJsonParser_data() << "" << "1.0" << "WTFPL" + << QStringList() << "foo" << QStringList() << QUrl() @@ -89,6 +95,7 @@ void VcpkgSearchTest::testVcpkgJsonParser() QFETCH(QString, name); QFETCH(QString, version); QFETCH(QString, license); + QFETCH(QStringList, dependencies); QFETCH(QString, shortDescription); QFETCH(QStringList, description); QFETCH(QUrl, homepage); @@ -101,10 +108,74 @@ void VcpkgSearchTest::testVcpkgJsonParser() QCOMPARE(mf.name, name); QCOMPARE(mf.version, version); QCOMPARE(mf.license, license); + QCOMPARE(mf.dependencies, dependencies); QCOMPARE(mf.shortDescription, shortDescription); QCOMPARE(mf.description, description); QCOMPARE(mf.homepage, homepage); QCOMPARE(ok, success); } +void VcpkgSearchTest::testAddDependency_data() +{ + QTest::addColumn<QString>("originalVcpkgManifestJsonData"); + QTest::addColumn<QString>("addedPackage"); + QTest::addColumn<QString>("modifiedVcpkgManifestJsonData"); + + QTest::newRow("Existing dependencies") + << +R"({ + "name": "foo", + "dependencies": [ + "fmt", + { + "name": "vcpkg-cmake", + "host": true + } + ] +} +)" + << "7zip" + << +R"({ + "dependencies": [ + "fmt", + { + "host": true, + "name": "vcpkg-cmake" + }, + "7zip" + ], + "name": "foo" +} +)"; + + QTest::newRow("Without dependencies") + << +R"({ + "name": "foo" +} +)" + << "7zip" + << +R"({ + "dependencies": [ + "7zip" + ], + "name": "foo" +} +)"; +} + +void VcpkgSearchTest::testAddDependency() +{ + QFETCH(QString, originalVcpkgManifestJsonData); + QFETCH(QString, addedPackage); + QFETCH(QString, modifiedVcpkgManifestJsonData); + + const QByteArray result = addDependencyToManifest(originalVcpkgManifestJsonData.toUtf8(), + addedPackage); + + QCOMPARE(QString::fromUtf8(result), modifiedVcpkgManifestJsonData); +} + } // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkg_test.h b/src/plugins/vcpkg/vcpkg_test.h index 8175e28d594..2bc386e7aa1 100644 --- a/src/plugins/vcpkg/vcpkg_test.h +++ b/src/plugins/vcpkg/vcpkg_test.h @@ -19,6 +19,8 @@ public: private slots: void testVcpkgJsonParser_data(); void testVcpkgJsonParser(); + void testAddDependency_data(); + void testAddDependency(); }; } // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgconstants.h b/src/plugins/vcpkg/vcpkgconstants.h index 644dfab95f9..19136ba9f53 100644 --- a/src/plugins/vcpkg/vcpkgconstants.h +++ b/src/plugins/vcpkg/vcpkgconstants.h @@ -8,6 +8,7 @@ namespace Vcpkg::Constants { const char TOOLSSETTINGSPAGE_ID[] = "Vcpkg.VcpkgSettings"; const char WEBSITE_URL[] = "https://vcpkg.io/"; const char ENVVAR_VCPKG_ROOT[] = "VCPKG_ROOT"; +const char VCPKG_COMMAND[] = "vcpkg"; const char VCPKGMANIFEST_EDITOR_ID[] = "Vcpkg.VcpkgManifestEditor"; const char VCPKGMANIFEST_MIMETYPE[] = "application/vcpkg.manifest+json"; diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.cpp b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp index 40e4837cf77..3e90b0f7076 100644 --- a/src/plugins/vcpkg/vcpkgmanifesteditor.cpp +++ b/src/plugins/vcpkg/vcpkgmanifesteditor.cpp @@ -10,27 +10,112 @@ #include <coreplugin/icore.h> +#include <utils/icon.h> +#include <utils/layoutbuilder.h> +#include <utils/stringutils.h> #include <utils/utilsicons.h> -#include <texteditor/textdocument.h> +#include <projectexplorer/projectexplorericons.h> +#include <texteditor/fontsettings.h> +#include <texteditor/textdocument.h> +#include <texteditor/texteditorsettings.h> + +#include <QDialogButtonBox> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QPlainTextEdit> #include <QToolBar> namespace Vcpkg::Internal { +class CMakeCodeDialog : public QDialog +{ +public: + explicit CMakeCodeDialog(const QStringList &packages, QWidget *parent = nullptr); + +private: + static QString cmakeCodeForPackage(const QString &package); + static QString cmakeCodeForPackages(const QStringList &packages); +}; + +CMakeCodeDialog::CMakeCodeDialog(const QStringList &packages, QWidget *parent) + : QDialog(parent) +{ + resize(600, 600); + + auto codeBrowser = new QPlainTextEdit; + const TextEditor::FontSettings &fs = TextEditor::TextEditorSettings::fontSettings(); + codeBrowser->setFont(fs.font()); + codeBrowser->setPlainText(cmakeCodeForPackages(packages)); + + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + + using namespace Layouting; + Column { + Tr::tr("Copy paste the required lines into your CMakeLists.txt:"), + codeBrowser, + buttonBox, + }.attachTo(this); + + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +QString CMakeCodeDialog::cmakeCodeForPackage(const QString &package) +{ + QString result; + + const Utils::FilePath usageFile = settings().vcpkgRoot() / "ports" / package / "usage"; + if (usageFile.exists()) { + Utils::FileReader reader; + if (reader.fetch(usageFile)) + result = QString::fromUtf8(reader.data()); + } else { + result = QString( +R"(The package %1 provides CMake targets: + + # this is heuristically generated, and may not be correct + find_package(%1 CONFIG REQUIRED) + target_link_libraries(main PRIVATE %1::%1))" + ).arg(package); + } + + return result; +} + +QString CMakeCodeDialog::cmakeCodeForPackages(const QStringList &packages) +{ + QString result; + for (const QString &package : packages) + result.append(cmakeCodeForPackage(package) + "\n\n"); + return result; +} + class VcpkgManifestEditorWidget : public TextEditor::TextEditorWidget { public: VcpkgManifestEditorWidget() { - m_searchPkgAction = toolBar()->addAction(Utils::Icons::ZOOM_TOOLBAR.icon(), - Tr::tr("Search package...")); + const QIcon vcpkgIcon = Utils::Icon({{":/vcpkg/images/vcpkgicon.png", + Utils::Theme::IconsBaseColor}}).icon(); + m_searchPkgAction = toolBar()->addAction(vcpkgIcon, Tr::tr("Add vcpkg Package...")); connect(m_searchPkgAction, &QAction::triggered, this, [this] { - const Search::VcpkgManifest package = Search::showVcpkgPackageSearchDialog(); - if (!package.name.isEmpty()) - textCursor().insertText(package.name); + const Search::VcpkgManifest package = + Search::showVcpkgPackageSearchDialog(documentToManifest()); + if (!package.name.isEmpty()) { + const QByteArray modifiedDocument = + addDependencyToManifest(textDocument()->contents(), package.name); + textDocument()->setContents(modifiedDocument); + } + }); + + const QIcon cmakeIcon = ProjectExplorer::Icons::CMAKE_LOGO_TOOLBAR.icon(); + m_cmakeCodeAction = toolBar()->addAction(cmakeIcon, Tr::tr("CMake Code...")); + connect(m_cmakeCodeAction, &QAction::triggered, this, [this] { + CMakeCodeDialog dlg(documentToManifest().dependencies); + dlg.exec(); }); - updateToolBar(); QAction *optionsAction = toolBar()->addAction(Utils::Icons::SETTINGS_TOOLBAR.icon(), Core::ICore::msgShowOptionsDialog()); @@ -38,6 +123,7 @@ public: Core::ICore::showOptionsDialog(Constants::TOOLSSETTINGSPAGE_ID); }); + updateToolBar(); connect(&settings().vcpkgRoot, &Utils::BaseAspect::changed, this, &VcpkgManifestEditorWidget::updateToolBar); } @@ -45,11 +131,19 @@ public: void updateToolBar() { Utils::FilePath vcpkg = settings().vcpkgRoot().pathAppended("vcpkg").withExecutableSuffix(); - m_searchPkgAction->setEnabled(vcpkg.isExecutableFile()); + const bool vcpkgEncabled = vcpkg.isExecutableFile(); + m_searchPkgAction->setEnabled(vcpkgEncabled); + m_cmakeCodeAction->setEnabled(vcpkgEncabled); } private: + Search::VcpkgManifest documentToManifest() const + { + return Search::parseVcpkgManifest(textDocument()->contents()); + } + QAction *m_searchPkgAction; + QAction *m_cmakeCodeAction; }; static TextEditor::TextDocument *createVcpkgManifestDocument() @@ -69,4 +163,14 @@ VcpkgManifestEditorFactory::VcpkgManifestEditorFactory() setUseGenericHighlighter(true); } +QByteArray addDependencyToManifest(const QByteArray &manifest, const QString &package) +{ + constexpr char dependenciesKey[] = "dependencies"; + QJsonObject jsonObject = QJsonDocument::fromJson(manifest).object(); + QJsonArray dependencies = jsonObject.value(dependenciesKey).toArray(); + dependencies.append(package); + jsonObject.insert(dependenciesKey, dependencies); + return QJsonDocument(jsonObject).toJson(); +} + } // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgmanifesteditor.h b/src/plugins/vcpkg/vcpkgmanifesteditor.h index c7762d69df9..3d88ba79655 100644 --- a/src/plugins/vcpkg/vcpkgmanifesteditor.h +++ b/src/plugins/vcpkg/vcpkgmanifesteditor.h @@ -13,4 +13,6 @@ public: VcpkgManifestEditorFactory(); }; +QByteArray addDependencyToManifest(const QByteArray &manifest, const QString &package); + } // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgplugin.cpp b/src/plugins/vcpkg/vcpkgplugin.cpp index 4ef89499a76..9dc717e08e1 100644 --- a/src/plugins/vcpkg/vcpkgplugin.cpp +++ b/src/plugins/vcpkg/vcpkgplugin.cpp @@ -1,14 +1,14 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "vcpkgplugin.h" - #ifdef WITH_TESTS #include "vcpkg_test.h" #endif // WITH_TESTS #include "vcpkgmanifesteditor.h" #include "vcpkgsettings.h" +#include <extensionsystem/iplugin.h> + #include <projectexplorer/jsonwizard/jsonwizardfactory.h> namespace Vcpkg::Internal { @@ -16,23 +16,31 @@ namespace Vcpkg::Internal { class VcpkgPluginPrivate { public: - VcpkgManifestEditorFactory manifestEditorFactory; - VcpkgSettings settings; + VcpkgManifestEditorFactory vcpkgManifestEditorFactory; }; -VcpkgPlugin::~VcpkgPlugin() +class VcpkgPlugin final : public ExtensionSystem::IPlugin { - delete d; -} + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Vcpkg.json") -void VcpkgPlugin::initialize() -{ - d = new VcpkgPluginPrivate; - ProjectExplorer::JsonWizardFactory::addWizardPath(":/vcpkg/wizards/"); +public: + void initialize() final + { + ProjectExplorer::JsonWizardFactory::addWizardPath(":/vcpkg/wizards/"); + + d = std::make_unique<VcpkgPluginPrivate>(); #ifdef WITH_TESTS - addTest<VcpkgSearchTest>(); + addTest<VcpkgSearchTest>(); #endif -} + } + + virtual void extensionsInitialized() final { settings().setVcpkgRootEnvironmentVariable(); } + + std::unique_ptr<VcpkgPluginPrivate> d; +}; } // namespace Vcpkg::Internal + +#include "vcpkgplugin.moc" diff --git a/src/plugins/vcpkg/vcpkgplugin.h b/src/plugins/vcpkg/vcpkgplugin.h deleted file mode 100644 index 797083ea956..00000000000 --- a/src/plugins/vcpkg/vcpkgplugin.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <extensionsystem/iplugin.h> - -namespace ProjectExplorer { class Project; } - -namespace Vcpkg::Internal { - -class VcpkgPlugin final : public ExtensionSystem::IPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Vcpkg.json") - -public: - ~VcpkgPlugin(); - - void initialize() final; - -private: - class VcpkgPluginPrivate *d = nullptr; -}; - -} // namespace Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgsearch.cpp b/src/plugins/vcpkg/vcpkgsearch.cpp index b9315ec540c..d5e300893f4 100644 --- a/src/plugins/vcpkg/vcpkgsearch.cpp +++ b/src/plugins/vcpkg/vcpkgsearch.cpp @@ -10,6 +10,7 @@ #include <utils/algorithm.h> #include <utils/fancylineedit.h> #include <utils/fileutils.h> +#include <utils/infolabel.h> #include <utils/itemviews.h> #include <utils/layoutbuilder.h> @@ -28,17 +29,20 @@ namespace Vcpkg::Internal::Search { class VcpkgPackageSearchDialog : public QDialog { public: - explicit VcpkgPackageSearchDialog(QWidget *parent); + explicit VcpkgPackageSearchDialog(const VcpkgManifest &preexistingPackages, QWidget *parent); VcpkgManifest selectedPackage() const; private: void listPackages(const QString &filter); void showPackageDetails(const QString &packageName); + void updateStatus(); VcpkgManifests m_allPackages; VcpkgManifest m_selectedPackage; + const VcpkgManifest m_projectManifest; + FancyLineEdit *m_packagesFilter; ListWidget *m_packagesList; QLineEdit *m_vcpkgName; @@ -46,13 +50,17 @@ private: QLabel *m_vcpkgLicense; QTextBrowser *m_vcpkgDescription; QLabel *m_vcpkgHomepage; + InfoLabel *m_infoLabel; QDialogButtonBox *m_buttonBox; }; -VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(QWidget *parent) +VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(const VcpkgManifest &preexistingPackages, + QWidget *parent) : QDialog(parent) + , m_projectManifest(preexistingPackages) { resize(920, 400); + setWindowTitle(Tr::tr("Add vcpkg Package")); m_packagesFilter = new FancyLineEdit; m_packagesFilter->setFiltering(true); @@ -74,29 +82,40 @@ VcpkgPackageSearchDialog::VcpkgPackageSearchDialog(QWidget *parent) m_vcpkgHomepage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); m_vcpkgHomepage->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close); + m_infoLabel = new InfoLabel(Tr::tr("This package is already a project dependency."), + InfoLabel::Information); + m_infoLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); + m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + // clang-format off using namespace Layouting; Column { Row { Column { m_packagesFilter, + Tr::tr("Packages:"), m_packagesList, }, - Form { - Tr::tr("Name:"), m_vcpkgName, br, - Tr::tr("Version:"), m_vcpkgVersion, br, - Tr::tr("License:"), m_vcpkgLicense, br, - Tr::tr("Description:"), m_vcpkgDescription, br, - Tr::tr("Homepage:"), m_vcpkgHomepage, br, - }, + Group { + title(Tr::tr("Package Details")), + Form { + Tr::tr("Name:"), m_vcpkgName, br, + Tr::tr("Version:"), m_vcpkgVersion, br, + Tr::tr("License:"), m_vcpkgLicense, br, + Tr::tr("Description:"), m_vcpkgDescription, br, + Tr::tr("Homepage:"), m_vcpkgHomepage, br, + }, + } }, - m_buttonBox, + Row { m_infoLabel, m_buttonBox }, }.attachTo(this); + // clang-format on m_allPackages = vcpkgManifests(settings().vcpkgRoot()); listPackages({}); + updateStatus(); connect(m_packagesFilter, &FancyLineEdit::filterChanged, this, &VcpkgPackageSearchDialog::listPackages); @@ -146,7 +165,16 @@ void VcpkgPackageSearchDialog::showPackageDetails(const QString &packageName) .arg(manifest.homepage.toDisplayString())); m_selectedPackage = manifest; - m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!manifest.name.isEmpty()); + updateStatus(); +} + +void VcpkgPackageSearchDialog::updateStatus() +{ + const QString package = selectedPackage().name; + const bool isProjectDependency = m_projectManifest.dependencies.contains(package); + m_infoLabel->setVisible(isProjectDependency); + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!(package.isEmpty() + || isProjectDependency)); } VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok) @@ -164,6 +192,15 @@ VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool * } if (const QJsonValue license = jsonObject.value("license"); !license.isUndefined()) result.license = license.toString(); + if (const QJsonValue deps = jsonObject.value("dependencies"); !deps.isUndefined()) { + const QJsonArray dependencies = deps.toArray(); + for (const QJsonValue &dependency : dependencies) { + if (dependency.isString()) + result.dependencies.append(dependency.toString()); + else if (const QJsonValue name = dependency.toObject().value("name"); name.isString()) + result.dependencies.append(name.toString()); + } + } if (const QJsonValue description = jsonObject.value("description"); !description.isUndefined()) { if (description.isArray()) { const QJsonArray descriptionLines = description.toArray(); @@ -205,10 +242,13 @@ VcpkgManifests vcpkgManifests(const FilePath &vcpkgRoot) return result; } -VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent) +VcpkgManifest showVcpkgPackageSearchDialog(const VcpkgManifest &projectManifest, QWidget *parent) { - VcpkgPackageSearchDialog dlg(parent ? parent : Core::ICore::dialogParent()); - return (dlg.exec() == QDialog::Accepted) ? dlg.selectedPackage() : VcpkgManifest(); + QWidget *dlgParent = parent ? parent : Core::ICore::dialogParent(); + VcpkgPackageSearchDialog dlg(projectManifest, dlgParent); + const VcpkgManifest result = (dlg.exec() == QDialog::Accepted) ? dlg.selectedPackage() + : VcpkgManifest(); + return result; } } // namespace Vcpkg::Internal::Search diff --git a/src/plugins/vcpkg/vcpkgsearch.h b/src/plugins/vcpkg/vcpkgsearch.h index bb2d568a00c..912dff00b6a 100644 --- a/src/plugins/vcpkg/vcpkgsearch.h +++ b/src/plugins/vcpkg/vcpkgsearch.h @@ -15,6 +15,7 @@ struct VcpkgManifest QString name; QString version; QString license; + QStringList dependencies; QString shortDescription; QStringList description; QUrl homepage; @@ -24,6 +25,7 @@ using VcpkgManifests = QList<VcpkgManifest>; VcpkgManifest parseVcpkgManifest(const QByteArray &vcpkgManifestJsonData, bool *ok = nullptr); VcpkgManifests vcpkgManifests(const Utils::FilePath &vcpkgRoot); -VcpkgManifest showVcpkgPackageSearchDialog(QWidget *parent = nullptr); +VcpkgManifest showVcpkgPackageSearchDialog(const VcpkgManifest &projectManifest, + QWidget *parent = nullptr); } // namespace Vcpkg::Internal::Search diff --git a/src/plugins/vcpkg/vcpkgsettings.cpp b/src/plugins/vcpkg/vcpkgsettings.cpp index 3542ec47910..4ea4496519d 100644 --- a/src/plugins/vcpkg/vcpkgsettings.cpp +++ b/src/plugins/vcpkg/vcpkgsettings.cpp @@ -6,6 +6,9 @@ #include "vcpkgconstants.h" #include "vcpkgtr.h" +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/icore.h> + #include <cmakeprojectmanager/cmakeprojectconstants.h> #include <utils/environment.h> @@ -15,32 +18,36 @@ #include <QDesktopServices> #include <QToolButton> -namespace Vcpkg::Internal { +using namespace Utils; -static VcpkgSettings *theSettings = nullptr; +namespace Vcpkg::Internal { VcpkgSettings &settings() { - return *theSettings; + static VcpkgSettings theSettings; + return theSettings; } VcpkgSettings::VcpkgSettings() { - theSettings = this; - setSettingsGroup("Vcpkg"); - setId(Constants::TOOLSSETTINGSPAGE_ID); - setDisplayName("Vcpkg"); - setCategory(CMakeProjectManager::Constants::Settings::CATEGORY); + setAutoApply(false); vcpkgRoot.setSettingsKey("VcpkgRoot"); - vcpkgRoot.setExpectedKind(Utils::PathChooser::ExistingDirectory); - vcpkgRoot.setDefaultValue(Utils::qtcEnvironmentVariable(Constants::ENVVAR_VCPKG_ROOT)); + vcpkgRoot.setExpectedKind(PathChooser::ExistingDirectory); + FilePath defaultPath = Environment::systemEnvironment().searchInPath(Constants::VCPKG_COMMAND) + .parentDir(); + if (!defaultPath.isDir()) + defaultPath = FilePath::fromUserInput(qtcEnvironmentVariable(Constants::ENVVAR_VCPKG_ROOT)); + if (defaultPath.isDir()) + vcpkgRoot.setDefaultValue(defaultPath.toUserOutput()); + + connect(this, &AspectContainer::applied, this, &VcpkgSettings::setVcpkgRootEnvironmentVariable); setLayouter([this] { using namespace Layouting; auto websiteButton = new QToolButton; - websiteButton->setIcon(Utils::Icons::ONLINE.icon()); + websiteButton->setIcon(Icons::ONLINE.icon()); websiteButton->setToolTip(Constants::WEBSITE_URL); connect(websiteButton, &QAbstractButton::clicked, [] { @@ -53,7 +60,7 @@ VcpkgSettings::VcpkgSettings() Group { title(Tr::tr("Vcpkg installation")), Form { - Utils::PathChooser::label(), + PathChooser::label(), Span { 2, Row { vcpkgRoot, websiteButton } }, }, }, @@ -65,4 +72,24 @@ VcpkgSettings::VcpkgSettings() readSettings(); } +void VcpkgSettings::setVcpkgRootEnvironmentVariable() +{ + // Set VCPKG_ROOT environment variable so that auto-setup.cmake would pick it up + Environment::modifySystemEnvironment({{Constants::ENVVAR_VCPKG_ROOT, vcpkgRoot.value()}}); +} + +class VcpkgSettingsPage : public Core::IOptionsPage +{ +public: + VcpkgSettingsPage() + { + setId(Constants::TOOLSSETTINGSPAGE_ID); + setDisplayName("Vcpkg"); + setCategory(CMakeProjectManager::Constants::Settings::CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +static const VcpkgSettingsPage settingsPage; + } // Vcpkg::Internal diff --git a/src/plugins/vcpkg/vcpkgsettings.h b/src/plugins/vcpkg/vcpkgsettings.h index 6a00fd506f7..acb41110b40 100644 --- a/src/plugins/vcpkg/vcpkgsettings.h +++ b/src/plugins/vcpkg/vcpkgsettings.h @@ -3,15 +3,17 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace Vcpkg::Internal { -class VcpkgSettings : public Core::PagedSettings +class VcpkgSettings : public Utils::AspectContainer { public: VcpkgSettings(); + void setVcpkgRootEnvironmentVariable(); + Utils::FilePathAspect vcpkgRoot{this}; }; diff --git a/src/plugins/vcpkg/wizards/manifest/wizard.json b/src/plugins/vcpkg/wizards/manifest/wizard.json index 80e8b0fcc52..ae43ceef064 100644 --- a/src/plugins/vcpkg/wizards/manifest/wizard.json +++ b/src/plugins/vcpkg/wizards/manifest/wizard.json @@ -54,7 +54,7 @@ "type": "TextEdit", "data": { - "trText": " \"dependencies\": [\n \"fmt\"\n ]" + "text": " \"dependencies\": [\n \"fmt\"\n ]" } } ] diff --git a/src/plugins/vcsbase/VcsBase.json.in b/src/plugins/vcsbase/VcsBase.json.in index 3167b8d0c20..87e343ff45f 100644 --- a/src/plugins/vcsbase/VcsBase.json.in +++ b/src/plugins/vcsbase/VcsBase.json.in @@ -1,19 +1,19 @@ { - \"Name\" : \"VcsBase\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "VcsBase", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Version Control\", - \"Description\" : \"Version Control System Base Plugin.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Version Control", + "Description" : "Version Control System Base Plugin.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp index e0c4ba4af62..8b9d179c666 100644 --- a/src/plugins/vcsbase/commonvcssettings.cpp +++ b/src/plugins/vcsbase/commonvcssettings.cpp @@ -6,6 +6,8 @@ #include "vcsbaseconstants.h" #include "vcsbasetr.h" +#include <coreplugin/dialogs/ioptionspage.h> +#include <coreplugin/iversioncontrol.h> #include <coreplugin/vcsmanager.h> #include <utils/environment.h> @@ -28,24 +30,16 @@ static QString sshPasswordPromptDefault() return QLatin1String("ssh-askpass"); } -static CommonVcsSettings *s_instance; - CommonVcsSettings &commonSettings() { - return *s_instance; + static CommonVcsSettings settings; + return settings; } CommonVcsSettings::CommonVcsSettings() { - s_instance = this; - + setAutoApply(false); setSettingsGroup("VCS"); - setId(Constants::VCS_COMMON_SETTINGS_ID); - setDisplayName(Tr::tr("General")); - setCategory(Constants::VCS_SETTINGS_CATEGORY); - // The following act as blueprint for other pages in the same category: - setDisplayCategory(Tr::tr("Version Control")); - setCategoryIconPath(":/vcsbase/images/settingscategory_vcs.png"); nickNameMailMap.setSettingsKey("NickNameMailMap"); nickNameMailMap.setExpectedKind(PathChooser::File); @@ -118,4 +112,23 @@ CommonVcsSettings::CommonVcsSettings() readSettings(); } +// CommonVcsSettingsPage + +class CommonVcsSettingsPage final : public Core::IOptionsPage +{ +public: + CommonVcsSettingsPage() + { + setId(Constants::VCS_COMMON_SETTINGS_ID); + setDisplayName(Tr::tr("General")); + setCategory(Constants::VCS_SETTINGS_CATEGORY); + // The following act as blueprint for other pages in the same category: + setDisplayCategory(Tr::tr("Version Control")); + setCategoryIconPath(":/vcsbase/images/settingscategory_vcs.png"); + setSettingsProvider([] { return &commonSettings(); }); + } +}; + +const CommonVcsSettingsPage settingsPage; + } // VcsBase::Internal diff --git a/src/plugins/vcsbase/commonvcssettings.h b/src/plugins/vcsbase/commonvcssettings.h index 0f591b27111..ced68d31b97 100644 --- a/src/plugins/vcsbase/commonvcssettings.h +++ b/src/plugins/vcsbase/commonvcssettings.h @@ -3,12 +3,11 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> -#include <coreplugin/iversioncontrol.h> +#include <utils/aspects.h> namespace VcsBase::Internal { -class CommonVcsSettings : public Core::PagedSettings +class CommonVcsSettings final : public Utils::AspectContainer { public: CommonVcsSettings(); diff --git a/src/plugins/vcsbase/diffandloghighlighter.h b/src/plugins/vcsbase/diffandloghighlighter.h index 85750960426..6318de941f3 100644 --- a/src/plugins/vcsbase/diffandloghighlighter.h +++ b/src/plugins/vcsbase/diffandloghighlighter.h @@ -17,8 +17,6 @@ class DiffAndLogHighlighterPrivate; class VCSBASE_EXPORT DiffAndLogHighlighter : public TextEditor::SyntaxHighlighter { - Q_OBJECT - public: explicit DiffAndLogHighlighter(const QRegularExpression &filePattern, const QRegularExpression &changePattern); diff --git a/src/plugins/vcsbase/nicknamedialog.cpp b/src/plugins/vcsbase/nicknamedialog.cpp index 40d77dee545..39791dc7472 100644 --- a/src/plugins/vcsbase/nicknamedialog.cpp +++ b/src/plugins/vcsbase/nicknamedialog.cpp @@ -215,7 +215,7 @@ QString NickNameDialog::nickName() const if (const QStandardItem *item = m_model->itemFromIndex(sourceIndex)) return NickNameEntry::nickNameOf(item); } - return QString(); + return {}; } QStandardItemModel *NickNameDialog::createModel(QObject *parent) diff --git a/src/plugins/vcsbase/nicknamedialog.h b/src/plugins/vcsbase/nicknamedialog.h index 481633fe16d..9fc0c3d4fa2 100644 --- a/src/plugins/vcsbase/nicknamedialog.h +++ b/src/plugins/vcsbase/nicknamedialog.h @@ -22,8 +22,6 @@ namespace VcsBase::Internal { class NickNameDialog : public QDialog { - Q_OBJECT - public: explicit NickNameDialog(QStandardItemModel *model, QWidget *parent = nullptr); ~NickNameDialog() override; diff --git a/src/plugins/vcsbase/submiteditorfile.cpp b/src/plugins/vcsbase/submiteditorfile.cpp index 7d52d359a4c..8f2e6f0c953 100644 --- a/src/plugins/vcsbase/submiteditorfile.cpp +++ b/src/plugins/vcsbase/submiteditorfile.cpp @@ -69,14 +69,13 @@ void SubmitEditorFile::setModified(bool modified) bool SubmitEditorFile::saveImpl(QString *errorString, const FilePath &filePath, bool autoSave) { - const FilePath &fName = filePath.isEmpty() ? this->filePath() : filePath; - FileSaver saver(fName, QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); + FileSaver saver(filePath, QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text); saver.write(m_editor->fileContents()); if (!saver.finalize(errorString)) return false; if (autoSave) return true; - setFilePath(fName.absoluteFilePath()); + setFilePath(filePath.absoluteFilePath()); setModified(false); if (!errorString->isEmpty()) return false; diff --git a/src/plugins/vcsbase/submitfilemodel.cpp b/src/plugins/vcsbase/submitfilemodel.cpp index 4ee4b35f3cf..02a9b6e359e 100644 --- a/src/plugins/vcsbase/submitfilemodel.cpp +++ b/src/plugins/vcsbase/submitfilemodel.cpp @@ -121,14 +121,14 @@ QList<QStandardItem *> SubmitFileModel::addFile(const QString &fileName, const Q QString SubmitFileModel::state(int row) const { if (row < 0 || row >= rowCount()) - return QString(); + return {}; return item(row)->text(); } QString SubmitFileModel::file(int row) const { if (row < 0 || row >= rowCount()) - return QString(); + return {}; return item(row, FileColumn)->text(); } diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs index 0c2f08abd58..7cf0a8fe487 100644 --- a/src/plugins/vcsbase/vcsbase.qbs +++ b/src/plugins/vcsbase/vcsbase.qbs @@ -75,5 +75,5 @@ QtcPlugin { "wizard/vcsjsextension.h", ] - cpp.defines: base.concat(qtc.testsEnabled ? ['SRC_DIR="' + project.ide_source_tree + '"'] : []) + cpp.defines: base.concat(qtc.withPluginTests ? ['SRC_DIR="' + project.ide_source_tree + '"'] : []) } diff --git a/src/plugins/vcsbase/vcsbaseclient.cpp b/src/plugins/vcsbase/vcsbaseclient.cpp index ffa19b24f78..7460024e225 100644 --- a/src/plugins/vcsbase/vcsbaseclient.cpp +++ b/src/plugins/vcsbase/vcsbaseclient.cpp @@ -57,16 +57,10 @@ namespace VcsBase { VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings) : m_baseSettings(baseSettings) { - m_baseSettings->readSettings(); connect(ICore::instance(), &ICore::saveSettingsRequested, this, &VcsBaseClientImpl::saveSettings); } -VcsBaseSettings &VcsBaseClientImpl::settings() const -{ - return *m_baseSettings; -} - FilePath VcsBaseClientImpl::vcsBinary() const { return m_baseSettings->binaryPath(); @@ -121,7 +115,7 @@ QStringList VcsBaseClientImpl::splitLines(const QString &s) if (output.endsWith(newLine)) output.truncate(output.size() - 1); if (output.isEmpty()) - return QStringList(); + return {}; return output.split(newLine); } @@ -246,7 +240,7 @@ VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Id kind, QString title, void VcsBaseClientImpl::saveSettings() { - m_baseSettings->writeSettings(ICore::settings()); + m_baseSettings->writeSettings(); } VcsBaseClient::VcsBaseClient(VcsBaseSettings *baseSettings) @@ -521,7 +515,7 @@ QString VcsBaseClient::vcsCommandString(VcsCommandTag cmd) const case LogCommand: return QLatin1String("log"); case StatusCommand: return QLatin1String("status"); } - return QString(); + return {}; } ExitCodeInterpreter VcsBaseClient::exitCodeInterpreter(VcsCommandTag cmd) const diff --git a/src/plugins/vcsbase/vcsbaseclient.h b/src/plugins/vcsbase/vcsbaseclient.h index 035e2f00424..ee578a7cd79 100644 --- a/src/plugins/vcsbase/vcsbaseclient.h +++ b/src/plugins/vcsbase/vcsbaseclient.h @@ -38,14 +38,10 @@ using CommandHandler = std::function<void(const CommandResult &)>; class VCSBASE_EXPORT VcsBaseClientImpl : public QObject { - Q_OBJECT - public: explicit VcsBaseClientImpl(VcsBaseSettings *baseSettings); ~VcsBaseClientImpl() override = default; - VcsBaseSettings &settings() const; - virtual Utils::FilePath vcsBinary() const; int vcsTimeoutS() const; diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.cpp b/src/plugins/vcsbase/vcsbaseclientsettings.cpp index 170437864a0..33d9b34a6c1 100644 --- a/src/plugins/vcsbase/vcsbaseclientsettings.cpp +++ b/src/plugins/vcsbase/vcsbaseclientsettings.cpp @@ -38,7 +38,8 @@ VcsBaseSettings::~VcsBaseSettings() = default; FilePaths VcsBaseSettings::searchPathList() const { - return Utils::transform(path.value().split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts), + // FIXME: Filepathify + return Utils::transform(path().split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts), &FilePath::fromUserInput); } diff --git a/src/plugins/vcsbase/vcsbaseclientsettings.h b/src/plugins/vcsbase/vcsbaseclientsettings.h index 6e1f227f218..e998434910f 100644 --- a/src/plugins/vcsbase/vcsbaseclientsettings.h +++ b/src/plugins/vcsbase/vcsbaseclientsettings.h @@ -5,11 +5,11 @@ #include "vcsbase_global.h" -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> namespace VcsBase { -class VCSBASE_EXPORT VcsBaseSettings : public Core::PagedSettings +class VCSBASE_EXPORT VcsBaseSettings : public Utils::AspectContainer { public: VcsBaseSettings(); diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp index b60abaeb5f5..f7372fcd074 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp @@ -9,7 +9,6 @@ #include <utils/environment.h> #include <utils/futuresynchronizer.h> #include <utils/process.h> -#include <utils/qtcassert.h> using namespace DiffEditor; using namespace Tasking; @@ -25,7 +24,6 @@ public: VcsBaseDiffEditorController *q; Environment m_processEnvironment; FilePath m_vcsBinary; - const TreeStorage<QString> m_inputStorage; }; ///////////////////// @@ -40,20 +38,11 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController() delete d; } -TreeStorage<QString> VcsBaseDiffEditorController::inputStorage() const +GroupItem VcsBaseDiffEditorController::postProcessTask(const TreeStorage<QString> &inputStorage) { - return d->m_inputStorage; -} - -GroupItem VcsBaseDiffEditorController::postProcessTask() -{ - const auto setupDiffProcessor = [this](Async<QList<FileData>> &async) { - const QString *storage = inputStorage().activeStorage(); - QTC_ASSERT(storage, qWarning("Using postProcessTask() requires putting inputStorage() " - "into task tree's root group.")); - const QString inputData = storage ? *storage : QString(); + const auto setupDiffProcessor = [inputStorage](Async<QList<FileData>> &async) { async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); - async.setConcurrentCallData(&DiffUtils::readPatchWithPromise, inputData); + async.setConcurrentCallData(&DiffUtils::readPatchWithPromise, *inputStorage); }; const auto onDiffProcessorDone = [this](const Async<QList<FileData>> &async) { setDiffFiles(async.isResultAvailable() ? async.result() : QList<FileData>()); diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h index e4b88805065..2cd2ece26c8 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h @@ -18,8 +18,6 @@ class VcsBaseDiffEditorControllerPrivate; class VCSBASE_EXPORT VcsBaseDiffEditorController : public DiffEditor::DiffEditorController { - Q_OBJECT - public: explicit VcsBaseDiffEditorController(Core::IDocument *document); ~VcsBaseDiffEditorController() override; @@ -28,8 +26,7 @@ public: void setVcsBinary(const Utils::FilePath &path); protected: - Tasking::TreeStorage<QString> inputStorage() const; - Tasking::GroupItem postProcessTask(); + Tasking::GroupItem postProcessTask(const Tasking::TreeStorage<QString> &inputStorage); void setupCommand(Utils::Process &process, const QStringList &args) const; diff --git a/src/plugins/vcsbase/vcsbaseeditor.cpp b/src/plugins/vcsbase/vcsbaseeditor.cpp index 26c19c7f91e..1ad32d4684f 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.cpp +++ b/src/plugins/vcsbase/vcsbaseeditor.cpp @@ -1156,7 +1156,7 @@ void VcsBaseEditorWidget::jumpToChangeFromDiff(QTextCursor cursor) return; const QString fileName = findDiffFile(fileNameFromDiffSpecification(block)); - const bool exists = fileName.isEmpty() ? false : QFile::exists(fileName); + const bool exists = fileName.isEmpty() ? false : QFileInfo::exists(fileName); if (!exists) return; @@ -1454,7 +1454,7 @@ void VcsBaseEditorWidget::slotAnnotateRevision(const QString &change) QStringList VcsBaseEditorWidget::annotationPreviousVersions(const QString &) const { - return QStringList(); + return {}; } void VcsBaseEditorWidget::slotPaste() @@ -1554,7 +1554,7 @@ bool VcsBaseEditorWidget::isValidRevision(const QString &revision) const QString VcsBaseEditorWidget::revisionSubject(const QTextBlock &inBlock) const { Q_UNUSED(inBlock) - return QString(); + return {}; } bool VcsBaseEditorWidget::hasDiff() const diff --git a/src/plugins/vcsbase/vcsbaseeditor.h b/src/plugins/vcsbase/vcsbaseeditor.h index 81028f3b709..1b588b3e6e7 100644 --- a/src/plugins/vcsbase/vcsbaseeditor.h +++ b/src/plugins/vcsbase/vcsbaseeditor.h @@ -60,6 +60,7 @@ public: class VCSBASE_EXPORT VcsBaseEditor : public TextEditor::BaseTextEditor { Q_OBJECT + public: VcsBaseEditor(); diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index b73f97dd79c..29a25e1627b 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -220,7 +220,7 @@ QString StateListener::windowTitleVcsTopic(const FilePath &filePath) searchPath = projects.first()->projectDirectory(); } if (searchPath.isEmpty()) - return QString(); + return {}; FilePath topLevelPath; IVersionControl *vc = VcsManager::findVersionControlForDirectory( searchPath, &topLevelPath); @@ -232,7 +232,7 @@ static inline QString displayNameOfEditor(const FilePath &fileName) IDocument *document = DocumentModel::documentForFilePath(fileName); if (document) return document->displayName(); - return QString(); + return {}; } void StateListener::slotStateChanged() @@ -751,8 +751,10 @@ FilePath source(IDocument *document) void setProcessEnvironment(Environment *e) { const QString prompt = Internal::commonSettings().sshPasswordPrompt().path(); - if (!prompt.isEmpty()) + if (!prompt.isEmpty()) { e->set("SSH_ASKPASS", prompt); + e->set("SSH_ASKPASS_REQUIRE", "force"); + } } } // namespace VcsBase diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 03eaa49c857..ccf56e55d29 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -39,18 +39,19 @@ #include <projectexplorer/project.h> #include <projectexplorer/projectmanager.h> +#include <QAction> +#include <QApplication> +#include <QCompleter> #include <QDir> #include <QFileInfo> +#include <QMessageBox> #include <QPointer> #include <QProcess> +#include <QPushButton> #include <QSet> #include <QStringListModel> #include <QStyle> #include <QToolBar> -#include <QAction> -#include <QApplication> -#include <QMessageBox> -#include <QCompleter> #include <cstring> @@ -174,7 +175,7 @@ void VcsBaseSubmitEditor::setParameters(const VcsBaseSubmitEditorParameters &par const CommonVcsSettings &settings = commonSettings(); // Add additional context menu settings if (!settings.submitMessageCheckScript().isEmpty() - || !settings.nickNameMailMap.value().isEmpty()) { + || !settings.nickNameMailMap().isEmpty()) { auto sep = new QAction(this); sep->setSeparator(true); d->m_widget->addDescriptionEditContextMenuAction(sep); @@ -186,15 +187,15 @@ void VcsBaseSubmitEditor::setParameters(const VcsBaseSubmitEditorParameters &par d->m_widget->addDescriptionEditContextMenuAction(checkAction); } // Insert nick - if (!settings.nickNameMailMap.value().isEmpty()) { + if (!settings.nickNameMailMap().isEmpty()) { auto insertAction = new QAction(Tr::tr("Insert Name..."), this); connect(insertAction, &QAction::triggered, this, &VcsBaseSubmitEditor::slotInsertNickName); d->m_widget->addDescriptionEditContextMenuAction(insertAction); } } // Do we have user fields? - if (!settings.nickNameFieldListFile.value().isEmpty()) - createUserFields(settings.nickNameFieldListFile.value()); + if (!settings.nickNameFieldListFile().isEmpty()) + createUserFields(settings.nickNameFieldListFile()); // wrapping. etc slotUpdateEditorSettings(); @@ -241,14 +242,12 @@ static inline QStringList fieldTexts(const QString &fileContents) return rc; } -void VcsBaseSubmitEditor::createUserFields(const QString &fieldConfigFile) +void VcsBaseSubmitEditor::createUserFields(const FilePath &fieldConfigFile) { FileReader reader; - if (!reader.fetch(FilePath::fromString(fieldConfigFile), - QIODevice::Text, - Core::ICore::dialogParent())) { + if (!reader.fetch(fieldConfigFile, QIODevice::Text, Core::ICore::dialogParent())) return; - } + // Parse into fields const QStringList fields = fieldTexts(QString::fromUtf8(reader.data())); if (fields.empty()) @@ -387,7 +386,7 @@ SubmitFileModel *VcsBaseSubmitEditor::fileModel() const QStringList VcsBaseSubmitEditor::rowsToFiles(const QList<int> &rows) const { if (rows.empty()) - return QStringList(); + return {}; QStringList rc; const SubmitFileModel *model = fileModel(); @@ -474,13 +473,12 @@ bool VcsBaseSubmitEditor::promptSubmit(VcsBasePluginPrivate *plugin) mb.setWindowTitle(plugin->commitAbortTitle()); mb.setIcon(QMessageBox::Warning); mb.setText(plugin->commitAbortMessage()); - mb.setStandardButtons(QMessageBox::Close | QMessageBox::Cancel); - // On Windows there is no mnemonic for Close. Set it explicitly. - mb.button(QMessageBox::Close)->setText(Tr::tr("&Close")); - mb.button(QMessageBox::Cancel)->setText(Tr::tr("&Keep Editing")); - mb.setDefaultButton(QMessageBox::Cancel); + QPushButton *closeButton = mb.addButton(Tr::tr("&Close"), QMessageBox::AcceptRole); + QPushButton *keepButton = mb.addButton(Tr::tr("&Keep Editing"), QMessageBox::RejectRole); + mb.setDefaultButton(keepButton); + mb.setEscapeButton(keepButton); mb.exec(); - return mb.result() == QMessageBox::Close; + return mb.clickedButton() == closeButton; } QString VcsBaseSubmitEditor::promptForNickName() @@ -489,7 +487,7 @@ QString VcsBaseSubmitEditor::promptForNickName() d->m_nickNameDialog = new NickNameDialog(VcsPlugin::instance()->nickNameModel(), d->m_widget); if (d->m_nickNameDialog->exec() == QDialog::Accepted) return d->m_nickNameDialog->nickName(); - return QString(); + return {}; } void VcsBaseSubmitEditor::slotInsertNickName() @@ -521,7 +519,7 @@ void VcsBaseSubmitEditor::slotCheckSubmitMessage() bool VcsBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const { - const QString checkScript = commonSettings().submitMessageCheckScript.value(); + const FilePath checkScript = commonSettings().submitMessageCheckScript(); if (checkScript.isEmpty()) return true; QApplication::setOverrideCursor(Qt::WaitCursor); @@ -530,17 +528,17 @@ bool VcsBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const return rc; } -static QString msgCheckScript(const FilePath &workingDir, const QString &cmd) +static QString msgCheckScript(const FilePath &workingDir, const FilePath &cmd) { - const QString nativeCmd = QDir::toNativeSeparators(cmd); + const QString nativeCmd = cmd.toUserOutput(); return workingDir.isEmpty() ? Tr::tr("Executing %1").arg(nativeCmd) : - Tr::tr("Executing [%1] %2"). - arg(workingDir.toUserOutput(), nativeCmd); + Tr::tr("Executing [%1] %2").arg(workingDir.toUserOutput(), nativeCmd); } -bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript, QString *errorMessage) const +bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const FilePath &checkScript, QString *errorMessage) const { + QTC_ASSERT(!checkScript.needsDevice(), return false); // Not supported below. // Write out message TempFileSaver saver(TemporaryDirectory::masterDirectoryPath() + "/msgXXXXXX.txt"); saver.write(fileContents()); @@ -552,7 +550,7 @@ bool VcsBaseSubmitEditor::runSubmitMessageCheckScript(const QString &checkScript Process checkProcess; if (!d->m_checkScriptWorkingDirectory.isEmpty()) checkProcess.setWorkingDirectory(d->m_checkScriptWorkingDirectory); - checkProcess.setCommand({FilePath::fromString(checkScript), {saver.filePath().toString()}}); + checkProcess.setCommand({checkScript, {saver.filePath().path()}}); checkProcess.start(); const bool succeeded = checkProcess.waitForFinished(); diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 38540386664..2c3123d7368 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -114,9 +114,9 @@ private: void slotSetFieldNickName(int); void slotUpdateEditorSettings(); - void createUserFields(const QString &fieldConfigFile); + void createUserFields(const Utils::FilePath &fieldConfigFile); bool checkSubmitMessage(QString *errorMessage) const; - bool runSubmitMessageCheckScript(const QString &script, QString *errorMessage) const; + bool runSubmitMessageCheckScript(const Utils::FilePath &script, QString *errorMessage) const; QString promptForNickName(); void close(); diff --git a/src/plugins/vcsbase/vcsoutputformatter.h b/src/plugins/vcsbase/vcsoutputformatter.h index a7c25fbacea..d12561d3758 100644 --- a/src/plugins/vcsbase/vcsoutputformatter.h +++ b/src/plugins/vcsbase/vcsoutputformatter.h @@ -14,7 +14,6 @@ namespace VcsBase { class VcsOutputLineParser : public Utils::OutputLineParser { - Q_OBJECT public: VcsOutputLineParser(); void fillLinkContextMenu(QMenu *menu, const Utils::FilePath &workingDirectory, const QString &href); diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index 816aa79f022..10b0ace71ac 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -123,13 +123,13 @@ QString OutputWindowPlainTextEdit::identifierUnderCursor(const QPoint &widgetPos const int cursorDocumentPos = cursor.position(); cursor.select(QTextCursor::BlockUnderCursor); if (!cursor.hasSelection()) - return QString(); + return {}; const QString block = cursor.selectedText(); // Determine cursor position within line and find blank-delimited word const int cursorPos = cursorDocumentPos - cursor.block().position(); const int blockSize = block.size(); if (cursorPos < 0 || cursorPos >= blockSize || block.at(cursorPos).isSpace()) - return QString(); + return {}; // Retrieve repository if desired if (repository) if (QTextBlockUserData *data = cursor.block().userData()) @@ -283,6 +283,10 @@ static VcsOutputWindowPrivate *d = nullptr; VcsOutputWindow::VcsOutputWindow() { + setId("VersionControl"); + setDisplayName(Tr::tr("Version Control")); + setPriorityInStatusBar(-20); + d = new VcsOutputWindowPrivate; Q_ASSERT(d->passwordRegExp.isValid()); m_instance = this; @@ -327,16 +331,6 @@ QWidget *VcsOutputWindow::outputWidget(QWidget *parent) return &d->widget; } -QString VcsOutputWindow::displayName() const -{ - return Tr::tr("Version Control"); -} - -int VcsOutputWindow::priorityInStatusBar() const -{ - return -1; -} - void VcsOutputWindow::clearContents() { d->widget.clear(); diff --git a/src/plugins/vcsbase/vcsoutputwindow.h b/src/plugins/vcsbase/vcsoutputwindow.h index f6023560865..ec2235d7285 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.h +++ b/src/plugins/vcsbase/vcsoutputwindow.h @@ -22,9 +22,6 @@ class VCSBASE_EXPORT VcsOutputWindow : public Core::IOutputPane public: QWidget *outputWidget(QWidget *parent) override; - QString displayName() const override; - - int priorityInStatusBar() const override; void clearContents() override; diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp index c61c8e0bec0..ee6461e603f 100644 --- a/src/plugins/vcsbase/vcsplugin.cpp +++ b/src/plugins/vcsbase/vcsplugin.cpp @@ -40,7 +40,7 @@ public: explicit VcsPluginPrivate(VcsPlugin *plugin) : q(plugin) { - QObject::connect(&m_settings, &AspectContainer::changed, + QObject::connect(&commonSettings(), &AspectContainer::changed, [this] { slotSettingsChanged(); }); slotSettingsChanged(); } @@ -57,7 +57,7 @@ public: void populateNickNameModel() { QString errorMessage; - if (!NickNameDialog::populateModelFromMailCapFile(m_settings.nickNameMailMap(), + if (!NickNameDialog::populateModelFromMailCapFile(commonSettings().nickNameMailMap(), m_nickNameModel, &errorMessage)) { qWarning("%s", qPrintable(errorMessage)); @@ -71,8 +71,10 @@ public: } VcsPlugin *q; - CommonVcsSettings m_settings; QStandardItemModel *m_nickNameModel = nullptr; + + VcsConfigurationPageFactory m_vcsConfigurationPageFactory; + VcsCommandPageFactory m_vcsCommandPageFactory; }; static VcsPlugin *m_instance = nullptr; @@ -101,15 +103,11 @@ void VcsPlugin::initialize() return result; }); - JsonWizardFactory::registerPageFactory(new Internal::VcsConfigurationPageFactory); - JsonWizardFactory::registerPageFactory(new Internal::VcsCommandPageFactory); - JsExpander::registerGlobalObject<VcsJsExtension>("Vcs"); MacroExpander *expander = globalMacroExpander(); expander->registerVariable(Constants::VAR_VCS_NAME, - Tr::tr("Name of the version control system in use by the current project."), - []() -> QString { + Tr::tr("Name of the version control system in use by the current project."), [] { IVersionControl *vc = nullptr; if (Project *project = ProjectTree::currentProject()) vc = VcsManager::findVersionControlForDirectory(project->projectDirectory()); @@ -117,8 +115,8 @@ void VcsPlugin::initialize() }); expander->registerVariable(Constants::VAR_VCS_TOPIC, - Tr::tr("The current version control topic (branch or tag) identification of the current project."), - []() -> QString { + Tr::tr("The current version control topic (branch or tag) identification " + "of the current project."), [] { IVersionControl *vc = nullptr; FilePath topLevel; if (Project *project = ProjectTree::currentProject()) @@ -127,8 +125,7 @@ void VcsPlugin::initialize() }); expander->registerVariable(Constants::VAR_VCS_TOPLEVELPATH, - Tr::tr("The top level path to the repository the current project is in."), - []() -> QString { + Tr::tr("The top level path to the repository the current project is in."), [] { if (Project *project = ProjectTree::currentProject()) return VcsManager::findTopLevelForDirectory(project->projectDirectory()).toString(); return QString(); diff --git a/src/plugins/vcsbase/wizard/vcscommandpage.h b/src/plugins/vcsbase/wizard/vcscommandpage.h index c36e3a76c19..20524184835 100644 --- a/src/plugins/vcsbase/wizard/vcscommandpage.h +++ b/src/plugins/vcsbase/wizard/vcscommandpage.h @@ -33,8 +33,6 @@ public: class VcsCommandPage : public Utils::WizardPage { - Q_OBJECT - public: VcsCommandPage(); ~VcsCommandPage() override; diff --git a/src/plugins/vcsbase/wizard/vcsconfigurationpage.h b/src/plugins/vcsbase/wizard/vcsconfigurationpage.h index d36f4934346..ceceb515079 100644 --- a/src/plugins/vcsbase/wizard/vcsconfigurationpage.h +++ b/src/plugins/vcsbase/wizard/vcsconfigurationpage.h @@ -30,8 +30,6 @@ public: class VCSBASE_EXPORT VcsConfigurationPage : public Utils::WizardPage { - Q_OBJECT - public: VcsConfigurationPage(); ~VcsConfigurationPage() override; diff --git a/src/plugins/webassembly/CMakeLists.txt b/src/plugins/webassembly/CMakeLists.txt index 6609357f015..d9e46bcc4d4 100644 --- a/src/plugins/webassembly/CMakeLists.txt +++ b/src/plugins/webassembly/CMakeLists.txt @@ -10,7 +10,6 @@ add_qtc_plugin(WebAssembly webassemblyplugin.cpp webassemblyplugin.h webassemblyqtversion.cpp webassemblyqtversion.h webassemblyrunconfiguration.cpp webassemblyrunconfiguration.h - webassemblyrunconfigurationaspects.cpp webassemblyrunconfigurationaspects.h webassemblysettings.cpp webassemblysettings.h webassemblytoolchain.cpp webassemblytoolchain.h webassemblytr.h diff --git a/src/plugins/webassembly/WebAssembly.json.in b/src/plugins/webassembly/WebAssembly.json.in index 6245d74456e..f860568cee9 100644 --- a/src/plugins/webassembly/WebAssembly.json.in +++ b/src/plugins/webassembly/WebAssembly.json.in @@ -1,21 +1,21 @@ { - \"Name\" : \"WebAssembly\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, - \"DisabledByDefault\" : true, - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "WebAssembly", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Experimental" : true, + "DisabledByDefault" : true, + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Device Support\", - \"Description\" : \"Helper for WebAssembly projects.\", - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Category" : "Device Support", + "Description" : "Helper for WebAssembly projects.", + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/webassembly/webassembly.qbs b/src/plugins/webassembly/webassembly.qbs index 12b639238cb..75a019ed422 100644 --- a/src/plugins/webassembly/webassembly.qbs +++ b/src/plugins/webassembly/webassembly.qbs @@ -25,8 +25,6 @@ QtcPlugin { "webassemblyqtversion.h", "webassemblyrunconfiguration.cpp", "webassemblyrunconfiguration.h", - "webassemblyrunconfigurationaspects.cpp", - "webassemblyrunconfigurationaspects.h", "webassemblysettings.cpp", "webassemblysettings.h", "webassemblytoolchain.cpp", diff --git a/src/plugins/webassembly/webassembly_test.cpp b/src/plugins/webassembly/webassembly_test.cpp index 6292cd9942a..bc763822c06 100644 --- a/src/plugins/webassembly/webassembly_test.cpp +++ b/src/plugins/webassembly/webassembly_test.cpp @@ -4,7 +4,7 @@ #include "webassembly_test.h" #include "webassemblyemsdk.h" -#include "webassemblyrunconfigurationaspects.h" +#include "webassemblyrunconfiguration.h" #include <utils/environment.h> @@ -77,7 +77,7 @@ void WebAssemblyTest::testEmrunBrowserListParsing() QFETCH(QByteArray, emrunOutput); QFETCH(WebBrowserEntries, expectedBrowsers); - QCOMPARE(WebBrowserSelectionAspect::parseEmrunOutput(emrunOutput), expectedBrowsers); + QCOMPARE(parseEmrunOutput(emrunOutput), expectedBrowsers); } void WebAssemblyTest::testEmrunBrowserListParsing_data() diff --git a/src/plugins/webassembly/webassemblydevice.cpp b/src/plugins/webassembly/webassemblydevice.cpp index 643dea0edbf..c3f16926310 100644 --- a/src/plugins/webassembly/webassemblydevice.cpp +++ b/src/plugins/webassembly/webassemblydevice.cpp @@ -16,7 +16,7 @@ WebAssemblyDevice::WebAssemblyDevice() setupId(IDevice::AutoDetected, Constants::WEBASSEMBLY_DEVICE_DEVICE_ID); setType(Constants::WEBASSEMBLY_DEVICE_TYPE); const QString displayNameAndType = Tr::tr("Web Browser"); - setDefaultDisplayName(displayNameAndType); + settings()->displayName.setDefaultValue(displayNameAndType); setDisplayType(displayNameAndType); setDeviceState(IDevice::DeviceStateUnknown); setMachineType(IDevice::Hardware); diff --git a/src/plugins/webassembly/webassemblyemsdk.cpp b/src/plugins/webassembly/webassemblyemsdk.cpp index ad12f12e62d..4ba3563c2ec 100644 --- a/src/plugins/webassembly/webassemblyemsdk.cpp +++ b/src/plugins/webassembly/webassemblyemsdk.cpp @@ -3,9 +3,9 @@ #include "webassemblyemsdk.h" -#include "webassemblyconstants.h" - #include <coreplugin/icore.h> +#include <coreplugin/settingsdatabase.h> + #include <utils/environment.h> #include <utils/process.h> #include <utils/hostosinfo.h> @@ -16,38 +16,50 @@ using namespace Utils; namespace WebAssembly::Internal::WebAssemblyEmSdk { -using EmSdkEnvCache = QCache<FilePath, QString>; -static EmSdkEnvCache *emSdkEnvCache() -{ - static EmSdkEnvCache cache(10); - return &cache; -} +const char emSdkEnvTSFileKey[] = "WebAssembly/emSdkEnvTimeStampFile"; +const char emSdkEnvTSKey[] = "WebAssembly/emSdkEnvTimeStamp"; +const char emSdkEnvOutputKey[] = "WebAssembly/emSdkEnvOutput1"; -using EmSdkVersionCache = QCache<FilePath, QVersionNumber>; -static EmSdkVersionCache *emSdkVersionCache() +const char emSdkVersionTSFileKey[] = "WebAssembly/emSdkVersionTimeStampFile"; +const char emSdkVersionTSKey[] = "WebAssembly/emSdkVersionTimeStamp"; +const char emSdkVersionKey[] = "WebAssembly/emSdkVersion1"; + +const FilePath timeStampFile(const FilePath &sdkRoot) { - static EmSdkVersionCache cache(10); - return &cache; + return sdkRoot / ".emscripten"; } static QString emSdkEnvOutput(const FilePath &sdkRoot) { - const bool isWindows = sdkRoot.osType() == OsTypeWindows; - if (!emSdkEnvCache()->contains(sdkRoot)) { - const FilePath scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") + - (isWindows ? ".bat" : ".sh")); - Process emSdkEnv; - if (isWindows) { - emSdkEnv.setCommand(CommandLine(scriptFile)); - } else { - // File needs to be source'd, not executed. - emSdkEnv.setCommand({sdkRoot.withNewPath("bash"), {"-c", ". " + scriptFile.path()}}); - } - emSdkEnv.runBlocking(); - const QString output = emSdkEnv.allOutput(); - emSdkEnvCache()->insert(sdkRoot, new QString(output)); + const FilePath tsFile = timeStampFile(sdkRoot); // ts == Timestamp + if (!tsFile.exists()) + return {}; + const QDateTime ts = tsFile.lastModified(); + + namespace DB = Core::SettingsDatabase; + if (DB::value(emSdkEnvTSKey).toDateTime() == ts + && FilePath::fromVariant(DB::value(emSdkEnvTSFileKey)) == tsFile + && DB::contains(emSdkEnvOutputKey)) { + return DB::value(emSdkEnvOutputKey).toString(); } - return *emSdkEnvCache()->object(sdkRoot); + + const bool isWindows = sdkRoot.osType() == OsTypeWindows; + const FilePath scriptFile = sdkRoot.pathAppended(QLatin1String("emsdk_env") + + (isWindows ? ".bat" : ".sh")); + Process emSdkEnv; + if (isWindows) { + emSdkEnv.setCommand(CommandLine(scriptFile)); + } else { + // File needs to be source'd, not executed. + emSdkEnv.setCommand({sdkRoot.withNewPath("bash"), {"-c", ". " + scriptFile.path()}}); + } + emSdkEnv.runBlocking(); + const QString result = emSdkEnv.allOutput(); + DB::setValue(emSdkEnvTSFileKey, tsFile.toVariant()); + DB::setValue(emSdkEnvTSKey, ts); + DB::setValue(emSdkEnvOutputKey, result); + + return result; } void parseEmSdkEnvOutputAndAddToEnv(const QString &output, Environment &env) @@ -86,29 +98,44 @@ void addToEnvironment(const FilePath &sdkRoot, Environment &env) QVersionNumber version(const FilePath &sdkRoot) { - if (!sdkRoot.exists()) + const FilePath tsFile = timeStampFile(sdkRoot); // ts == Timestamp + if (!tsFile.exists()) return {}; - if (!emSdkVersionCache()->contains(sdkRoot)) { - Environment env = sdkRoot.deviceEnvironment(); - addToEnvironment(sdkRoot, env); - QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"}; - FilePath script = sdkRoot.withNewPath(scriptFile).searchInDirectories(env.path()); - const CommandLine command(script, {"-dumpversion"}); - Process emcc; - emcc.setCommand(command); - emcc.setEnvironment(env); - emcc.runBlocking(); - const QString version = emcc.cleanedStdOut(); - emSdkVersionCache()->insert(sdkRoot, - new QVersionNumber(QVersionNumber::fromString(version))); + const QDateTime ts = tsFile.lastModified(); + + namespace DB = Core::SettingsDatabase; + if (DB::value(emSdkVersionTSKey).toDateTime() == ts + && FilePath::fromVariant(DB::value(emSdkVersionTSFileKey)) == tsFile + && DB::contains(emSdkVersionKey)) { + return QVersionNumber::fromString(DB::value(emSdkVersionKey).toString()); } - return *emSdkVersionCache()->object(sdkRoot); + + Environment env = sdkRoot.deviceEnvironment(); + addToEnvironment(sdkRoot, env); + QLatin1String scriptFile{sdkRoot.osType() == OsType::OsTypeWindows ? "emcc.bat" : "emcc"}; + FilePath script = sdkRoot.withNewPath(scriptFile).searchInDirectories(env.path()); + const CommandLine command(script, {"-dumpversion"}); + Process emcc; + emcc.setCommand(command); + emcc.setEnvironment(env); + emcc.runBlocking(); + const QString versionStr = emcc.cleanedStdOut(); + const QVersionNumber result = QVersionNumber::fromString(versionStr); + DB::setValue(emSdkVersionTSFileKey, tsFile.toVariant()); + DB::setValue(emSdkVersionTSKey, ts); + DB::setValue(emSdkVersionKey, result.toString()); + return result; } void clearCaches() { - emSdkEnvCache()->clear(); - emSdkVersionCache()->clear(); + namespace DB = Core::SettingsDatabase; + DB::remove(emSdkEnvTSFileKey); + DB::remove(emSdkEnvTSKey); + DB::remove(emSdkEnvOutputKey); + DB::remove(emSdkVersionTSFileKey); + DB::remove(emSdkVersionTSKey); + DB::remove(emSdkVersionKey); } } // namespace WebAssembly::Internal::WebAssemblyEmSdk diff --git a/src/plugins/webassembly/webassemblyplugin.cpp b/src/plugins/webassembly/webassemblyplugin.cpp index 28227884da7..87b4283e291 100644 --- a/src/plugins/webassembly/webassemblyplugin.cpp +++ b/src/plugins/webassembly/webassemblyplugin.cpp @@ -39,7 +39,6 @@ public: WebAssemblyQtVersionFactory qtVersionFactory; EmrunRunConfigurationFactory emrunRunConfigurationFactory; EmrunRunWorkerFactory emrunRunWorkerFactory; - WebAssemblySettings settings; }; static WebAssemblyPluginPrivate *dd = nullptr; diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.cpp b/src/plugins/webassembly/webassemblyrunconfiguration.cpp index 32f1bf581bb..d3aaf00cd1f 100644 --- a/src/plugins/webassembly/webassemblyrunconfiguration.cpp +++ b/src/plugins/webassembly/webassemblyrunconfiguration.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "webassemblyconstants.h" #include "webassemblyrunconfiguration.h" -#include "webassemblyrunconfigurationaspects.h" + +#include "webassemblyconstants.h" #include "webassemblytr.h" #include <projectexplorer/buildconfiguration.h> @@ -12,14 +12,37 @@ #include <projectexplorer/devicesupport/deviceusedportsgatherer.h> #include <projectexplorer/project.h> #include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/runconfigurationaspects.h> #include <projectexplorer/runcontrol.h> #include <projectexplorer/target.h> +#include <utils/layoutbuilder.h> +#include <utils/process.h> +#include <utils/qtcassert.h> + +#include <QComboBox> +#include <QTextStream> + using namespace ProjectExplorer; using namespace Utils; namespace WebAssembly::Internal { +WebBrowserEntries parseEmrunOutput(const QByteArray &output) +{ + WebBrowserEntries result; + QTextStream ts(output); + QString line; + static const QRegularExpression regExp(R"( - (.*):\s*(.*))"); // ' - firefox: Mozilla Firefox' + // ^__1__^ ^______2______^ + while (ts.readLineInto(&line)) { + const QRegularExpressionMatch match = regExp.match(line); + if (match.hasMatch()) + result.push_back({match.captured(1), match.captured(2)}); + } + return result; +} + static FilePath pythonInterpreter(const Environment &env) { const QString emsdkPythonEnvVarKey("EMSDK_PYTHON"); @@ -63,31 +86,116 @@ static CommandLine emrunCommand(const Target *target, return {}; } +static const char BROWSER_KEY[] = "WASM.WebBrowserSelectionAspect.Browser"; + +static WebBrowserEntries emrunBrowsers(Target *target) +{ + WebBrowserEntries result; + result.append(qMakePair(QString(), Tr::tr("Default Browser"))); + if (auto bc = target->activeBuildConfiguration()) { + const Environment environment = bc->environment(); + const FilePath emrunPath = environment.searchInPath("emrun"); + + Process browserLister; + browserLister.setEnvironment(environment); + browserLister.setCommand({emrunPath, {"--list_browsers"}}); + browserLister.start(); + + if (browserLister.waitForFinished()) + result.append(parseEmrunOutput(browserLister.readAllRawStandardOutput())); + } + return result; +} + +class WebBrowserSelectionAspect : public BaseAspect +{ + Q_OBJECT + +public: + WebBrowserSelectionAspect(AspectContainer *container) + : BaseAspect(container) + {} + + void setTarget(Target *target) + { + m_availableBrowsers = emrunBrowsers(target); + if (!m_availableBrowsers.isEmpty()) { + const int defaultIndex = qBound(0, m_availableBrowsers.count() - 1, 1); + m_currentBrowser = m_availableBrowsers.at(defaultIndex).first; + } + setDisplayName(Tr::tr("Web Browser")); + setId("WebBrowserAspect"); + setSettingsKey("RunConfiguration.WebBrowser"); + + addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser); + } + + void addToLayout(Layouting::LayoutItem &parent) override + { + QTC_CHECK(!m_webBrowserComboBox); + m_webBrowserComboBox = new QComboBox; + for (const WebBrowserEntry &be : m_availableBrowsers) + m_webBrowserComboBox->addItem(be.second, be.first); + m_webBrowserComboBox->setCurrentIndex(m_webBrowserComboBox->findData(m_currentBrowser)); + connect(m_webBrowserComboBox, &QComboBox::currentIndexChanged, this, [this] { + m_currentBrowser = m_webBrowserComboBox->currentData().toString(); + emit changed(); + }); + parent.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox}); + } + + void fromMap(const Store &map) override + { + if (!m_availableBrowsers.isEmpty()) + m_currentBrowser = map.value(BROWSER_KEY, m_availableBrowsers.first().first).toString(); + } + + void toMap(Store &map) const override + { + map.insert(BROWSER_KEY, m_currentBrowser); + } + + QString currentBrowser() const { return m_currentBrowser; } + + struct Data : BaseAspect::Data + { + QString currentBrowser; + }; + +private: + QComboBox *m_webBrowserComboBox = nullptr; + QString m_currentBrowser; + WebBrowserEntries m_availableBrowsers; +}; + // Runs a webassembly application via emscripten's "emrun" tool // https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html -class EmrunRunConfiguration : public ProjectExplorer::RunConfiguration +class EmrunRunConfiguration : public RunConfiguration { public: - EmrunRunConfiguration(Target *target, Utils::Id id) - : RunConfiguration(target, id) + EmrunRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) { - auto webBrowserAspect = addAspect<WebBrowserSelectionAspect>(target); + webBrowser.setTarget(target); - auto effectiveEmrunCall = addAspect<StringAspect>(); - effectiveEmrunCall->setLabelText(Tr::tr("Effective emrun call:")); - effectiveEmrunCall->setDisplayStyle(StringAspect::TextEditDisplay); - effectiveEmrunCall->setReadOnly(true); + effectiveEmrunCall.setLabelText(Tr::tr("Effective emrun call:")); + effectiveEmrunCall.setDisplayStyle(StringAspect::TextEditDisplay); + effectiveEmrunCall.setReadOnly(true); - setUpdater([this, target, effectiveEmrunCall, webBrowserAspect] { - effectiveEmrunCall->setValue(emrunCommand(target, - buildKey(), - webBrowserAspect->currentBrowser(), - "<port>").toUserOutput()); + setUpdater([this, target] { + effectiveEmrunCall.setValue(emrunCommand(target, + buildKey(), + webBrowser.currentBrowser(), + "<port>").toUserOutput()); }); - connect(webBrowserAspect, &BaseAspect::changed, this, &RunConfiguration::update); + connect(&webBrowser, &BaseAspect::changed, this, &RunConfiguration::update); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); } + +private: + WebBrowserSelectionAspect webBrowser{this}; + StringAspect effectiveEmrunCall{this}; }; class EmrunRunWorker : public SimpleTargetRunner @@ -127,3 +235,5 @@ EmrunRunWorkerFactory::EmrunRunWorkerFactory() } } // Webassembly::Internal + +#include "webassemblyrunconfiguration.moc" diff --git a/src/plugins/webassembly/webassemblyrunconfiguration.h b/src/plugins/webassembly/webassemblyrunconfiguration.h index b6ade22e847..9490cc77c86 100644 --- a/src/plugins/webassembly/webassemblyrunconfiguration.h +++ b/src/plugins/webassembly/webassemblyrunconfiguration.h @@ -8,6 +8,12 @@ namespace WebAssembly::Internal { +using WebBrowserEntry = QPair<QString, QString>; // first: id, second: display name +using WebBrowserEntries = QList<WebBrowserEntry>; + +WebBrowserEntries parseEmrunOutput(const QByteArray &output); + + class EmrunRunConfigurationFactory final : public ProjectExplorer::RunConfigurationFactory { public: diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp b/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp deleted file mode 100644 index 05b7e89e821..00000000000 --- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "webassemblyrunconfigurationaspects.h" -#include "webassemblytr.h" - -#include <projectexplorer/buildconfiguration.h> -#include <projectexplorer/target.h> - -#include <utils/layoutbuilder.h> -#include <utils/process.h> -#include <utils/qtcassert.h> - -#include <QComboBox> -#include <QTextStream> - -using namespace Utils; - -namespace WebAssembly { -namespace Internal { - -static const char BROWSER_KEY[] = "WASM.WebBrowserSelectionAspect.Browser"; - -static WebBrowserEntries emrunBrowsers(ProjectExplorer::Target *target) -{ - WebBrowserEntries result; - result.append(qMakePair(QString(), Tr::tr("Default Browser"))); - if (auto bc = target->activeBuildConfiguration()) { - const Utils::Environment environment = bc->environment(); - const Utils::FilePath emrunPath = environment.searchInPath("emrun"); - - Process browserLister; - browserLister.setEnvironment(environment); - browserLister.setCommand({emrunPath, {"--list_browsers"}}); - browserLister.start(); - - if (browserLister.waitForFinished()) - result.append(WebBrowserSelectionAspect::parseEmrunOutput( - browserLister.readAllRawStandardOutput())); - } - return result; -} - -WebBrowserSelectionAspect::WebBrowserSelectionAspect(ProjectExplorer::Target *target) - : m_availableBrowsers(emrunBrowsers(target)) -{ - if (!m_availableBrowsers.isEmpty()) { - const int defaultIndex = qBound(0, m_availableBrowsers.count() - 1, 1); - m_currentBrowser = m_availableBrowsers.at(defaultIndex).first; - } - setDisplayName(Tr::tr("Web Browser")); - setId("WebBrowserAspect"); - setSettingsKey("RunConfiguration.WebBrowser"); - - addDataExtractor(this, &WebBrowserSelectionAspect::currentBrowser, &Data::currentBrowser); -} - -void WebBrowserSelectionAspect::addToLayout(Layouting::LayoutItem &parent) -{ - QTC_CHECK(!m_webBrowserComboBox); - m_webBrowserComboBox = new QComboBox; - for (const WebBrowserEntry &be : m_availableBrowsers) - m_webBrowserComboBox->addItem(be.second, be.first); - m_webBrowserComboBox->setCurrentIndex(m_webBrowserComboBox->findData(m_currentBrowser)); - connect(m_webBrowserComboBox, &QComboBox::currentIndexChanged, this, [this] { - m_currentBrowser = m_webBrowserComboBox->currentData().toString(); - emit changed(); - }); - parent.addItems({Tr::tr("Web browser:"), m_webBrowserComboBox}); -} - -void WebBrowserSelectionAspect::fromMap(const QVariantMap &map) -{ - if (!m_availableBrowsers.isEmpty()) - m_currentBrowser = map.value(BROWSER_KEY, m_availableBrowsers.first().first).toString(); -} - -void WebBrowserSelectionAspect::toMap(QVariantMap &map) const -{ - map.insert(BROWSER_KEY, m_currentBrowser); -} - -QString WebBrowserSelectionAspect::currentBrowser() const -{ - return m_currentBrowser; -} - -WebBrowserEntries WebBrowserSelectionAspect::parseEmrunOutput(const QByteArray &output) -{ - WebBrowserEntries result; - QTextStream ts(output); - QString line; - static const QRegularExpression regExp(R"( - (.*):\s*(.*))"); // ' - firefox: Mozilla Firefox' - // ^__1__^ ^______2______^ - while (ts.readLineInto(&line)) { - const QRegularExpressionMatch match = regExp.match(line); - if (match.hasMatch()) - result.push_back({match.captured(1), match.captured(2)}); - } - return result; -} - -} // namespace Internal -} // namespace Webassembly diff --git a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h b/src/plugins/webassembly/webassemblyrunconfigurationaspects.h deleted file mode 100644 index 8a3cc24bdb2..00000000000 --- a/src/plugins/webassembly/webassemblyrunconfigurationaspects.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <projectexplorer/runconfigurationaspects.h> - -QT_FORWARD_DECLARE_CLASS(QComboBox) - -namespace WebAssembly { -namespace Internal { - -using WebBrowserEntry = QPair<QString, QString>; // first: id, second: display name -using WebBrowserEntries = QList<WebBrowserEntry>; - -class WebBrowserSelectionAspect : public Utils::BaseAspect -{ - Q_OBJECT - -public: - WebBrowserSelectionAspect(ProjectExplorer::Target *target); - - void addToLayout(Layouting::LayoutItem &parent) override; - - void fromMap(const QVariantMap &map) override; - void toMap(QVariantMap &map) const override; - - QString currentBrowser() const; - - static WebBrowserEntries parseEmrunOutput(const QByteArray &output); - - struct Data : BaseAspect::Data - { - QString currentBrowser; - }; - -private: - QComboBox *m_webBrowserComboBox = nullptr; - QString m_currentBrowser; - const WebBrowserEntries m_availableBrowsers; -}; - -} // namespace Internal -} // namespace Webassembly diff --git a/src/plugins/webassembly/webassemblysettings.cpp b/src/plugins/webassembly/webassemblysettings.cpp index fb9293f2c84..88a7cc22130 100644 --- a/src/plugins/webassembly/webassemblysettings.cpp +++ b/src/plugins/webassembly/webassemblysettings.cpp @@ -10,6 +10,9 @@ #include "webassemblytr.h" #include <coreplugin/icore.h> +#include <coreplugin/dialogs/ioptionspage.h> + +#include <projectexplorer/projectexplorerconstants.h> #include <utils/aspects.h> #include <utils/environment.h> @@ -18,19 +21,16 @@ #include <utils/pathchooser.h> #include <utils/utilsicons.h> -#include <QGroupBox> #include <QTextBrowser> #include <QTimer> using namespace Utils; -namespace WebAssembly { -namespace Internal { +namespace WebAssembly::Internal { -static WebAssemblySettings *theSettings = nullptr; - -WebAssemblySettings *WebAssemblySettings::instance() +WebAssemblySettings &settings() { + static WebAssemblySettings theSettings; return theSettings; } @@ -52,18 +52,12 @@ static QString environmentDisplay(const FilePath &sdkRoot) WebAssemblySettings::WebAssemblySettings() { - theSettings = this; - setSettingsGroup("WebAssembly"); + setAutoApply(false); - setId(Id(Constants::SETTINGS_ID)); - setDisplayName(Tr::tr("WebAssembly")); - setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); - - registerAspect(&emSdk); emSdk.setSettingsKey("EmSdk"); emSdk.setExpectedKind(Utils::PathChooser::ExistingDirectory); - emSdk.setDefaultFilePath(FileUtils::homePath()); + emSdk.setDefaultValue(QDir::homePath()); connect(this, &Utils::AspectContainer::applied, &WebAssemblyToolChain::registerToolChains); @@ -164,5 +158,20 @@ void WebAssemblySettings::updateStatus() m_qtVersionDisplay->setVisible(WebAssemblyQtVersion::isUnsupportedQtVersionInstalled()); } -} // Internal -} // WebAssembly +// WebAssemblySettingsPage + +class WebAssemblySettingsPage final : public Core::IOptionsPage +{ +public: + WebAssemblySettingsPage() + { + setId(Id(Constants::SETTINGS_ID)); + setDisplayName(Tr::tr("WebAssembly")); + setCategory(ProjectExplorer::Constants::DEVICE_SETTINGS_CATEGORY); + setSettingsProvider([] { return &settings(); }); + } +}; + +const WebAssemblySettingsPage settingsPage; + +} // WebAssembly::Internal diff --git a/src/plugins/webassembly/webassemblysettings.h b/src/plugins/webassembly/webassemblysettings.h index c9e207c409c..e607e57d8f6 100644 --- a/src/plugins/webassembly/webassemblysettings.h +++ b/src/plugins/webassembly/webassemblysettings.h @@ -3,23 +3,22 @@ #pragma once -#include <coreplugin/dialogs/ioptionspage.h> +#include <utils/aspects.h> QT_BEGIN_NAMESPACE class QTextBrowser; QT_END_NAMESPACE -namespace WebAssembly { -namespace Internal { +namespace Utils { class InfoLabel; } -class WebAssemblySettings final : public Core::PagedSettings +namespace WebAssembly::Internal { + +class WebAssemblySettings final : public Utils::AspectContainer { public: WebAssemblySettings(); - static WebAssemblySettings *instance(); - - Utils::FilePathAspect emSdk; + Utils::FilePathAspect emSdk{this}; private: QWidget *m_emSdkEnvGroupBox = nullptr; @@ -30,5 +29,6 @@ private: void updateStatus(); }; -} // namespace Internal -} // namespace WebAssmbly +WebAssemblySettings &settings(); + +} // WebAssmbly::Internal diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp index c554ee65cd5..925b7f5dbfb 100644 --- a/src/plugins/webassembly/webassemblytoolchain.cpp +++ b/src/plugins/webassembly/webassemblytoolchain.cpp @@ -14,7 +14,7 @@ #include <projectexplorer/projectmacro.h> #include <projectexplorer/toolchainmanager.h> -#include <qtsupport/qtkitinformation.h> +#include <qtsupport/qtkitaspect.h> #include <utils/algorithm.h> #include <utils/environment.h> @@ -42,6 +42,12 @@ static const Abi &toolChainAbi() static void addRegisteredMinGWToEnvironment(Environment &env) { + if (!ToolChainManager::isLoaded()) { + // Avoid querying the ToolChainManager before it is loaded, which is the case during + // toolchain restoration. The compiler version can be determined without MinGW in path. + return; + } + const ToolChain *toolChain = ToolChainManager::toolChain([](const ToolChain *t){ return t->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID; }); @@ -51,10 +57,10 @@ static void addRegisteredMinGWToEnvironment(Environment &env) void WebAssemblyToolChain::addToEnvironment(Environment &env) const { - const FilePath emSdk = WebAssemblySettings::instance()->emSdk(); + const FilePath emSdk = settings().emSdk(); WebAssemblyEmSdk::addToEnvironment(emSdk, env); if (env.osType() == OsTypeWindows) - addRegisteredMinGWToEnvironment(env); + addRegisteredMinGWToEnvironment(env); // qmake based builds require [mingw32-]make.exe } WebAssemblyToolChain::WebAssemblyToolChain() : @@ -95,7 +101,7 @@ const QVersionNumber &WebAssemblyToolChain::minimumSupportedEmSdkVersion() static Toolchains doAutoDetect(const ToolchainDetector &detector) { - const FilePath sdk = WebAssemblySettings::instance()->emSdk(); + const FilePath sdk = settings().emSdk(); if (!WebAssemblyEmSdk::isValid(sdk)) return {}; diff --git a/src/plugins/welcome/Welcome.json.in b/src/plugins/welcome/Welcome.json.in index 8cdf438151d..40f7e1ee3ff 100644 --- a/src/plugins/welcome/Welcome.json.in +++ b/src/plugins/welcome/Welcome.json.in @@ -1,25 +1,25 @@ { - \"Name\" : \"Welcome\", - \"Version\" : \"$$QTCREATOR_VERSION\", - \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Vendor\" : \"The Qt Company Ltd\", - \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", - \"License\" : [ \"Commercial Usage\", - \"\", - \"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.\", - \"\", - \"GNU General Public License Usage\", - \"\", - \"Alternatively, this plugin 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 plugin. 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.\" + "Name" : "Welcome", + "Version" : "${IDE_VERSION}", + "CompatVersion" : "${IDE_VERSION_COMPAT}", + "Vendor" : "The Qt Company Ltd", + "Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd", + "License" : [ "Commercial Usage", + "", + "Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt 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.", + "", + "GNU General Public License Usage", + "", + "Alternatively, this plugin 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 plugin. 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." ], - \"Category\" : \"Core\", - \"Description\" : \"Secondary Welcome Screen Plugin.\", - \"Arguments\" : [ + "Category" : "Core", + "Description" : "Secondary Welcome Screen Plugin.", + "Arguments" : [ { - \"Name\" : \"-notour\", - \"Description\" : \"Do not ask for taking a UI tour on startup\" + "Name" : "-notour", + "Description" : "Do not ask for taking a UI tour on startup" } ], - \"Url\" : \"http://www.qt.io\", - $$dependencyList + "Url" : "http://www.qt.io", + ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp index ef56d9ab3ed..d89805037af 100644 --- a/src/plugins/welcome/introductionwidget.cpp +++ b/src/plugins/welcome/introductionwidget.cpp @@ -29,7 +29,7 @@ namespace Internal { void IntroductionWidget::askUserAboutIntroduction(QWidget *parent) { // CheckableMessageBox for compatibility with Qt Creator < 4.11 - if (!CheckableDecider(QString(kTakeTourSetting)).shouldAskAgain() + if (!CheckableDecider(Key(kTakeTourSetting)).shouldAskAgain() || !Core::ICore::infoBar()->canInfoBeAdded(kTakeTourSetting)) return; diff --git a/src/plugins/welcome/welcome.qbs b/src/plugins/welcome/welcome.qbs index 95acd8fe216..2811856c1f3 100644 --- a/src/plugins/welcome/welcome.qbs +++ b/src/plugins/welcome/welcome.qbs @@ -7,7 +7,6 @@ QtcPlugin { Depends { name: "Utils" } Depends { name: "Core" } - Depends { name: "app_version_header" } files: [ "introductionwidget.cpp", diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp index 915695dafa2..c0a2f25ac94 100644 --- a/src/plugins/welcome/welcomeplugin.cpp +++ b/src/plugins/welcome/welcomeplugin.cpp @@ -7,8 +7,6 @@ #include <extensionsystem/iplugin.h> #include <extensionsystem/pluginmanager.h> -#include <app/app_version.h> - #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> @@ -30,6 +28,7 @@ #include <utils/treemodel.h> #include <QDesktopServices> +#include <QGuiApplication> #include <QLabel> #include <QMouseEvent> #include <QPainter> @@ -187,7 +186,7 @@ public: hbox->addSpacing(8); - auto ideNameLabel = new QLabel(Core::Constants::IDE_DISPLAY_NAME); + auto ideNameLabel = new QLabel(QGuiApplication::applicationDisplayName()); ideNameLabel->setFont(welcomeFont); ideNameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); QPalette pal = palette(); @@ -352,16 +351,14 @@ WelcomeMode::WelcomeMode() m_modeWidget = new ResizeSignallingWidget; m_modeWidget->setPalette(palette); - connect(m_modeWidget, &ResizeSignallingWidget::resized, + connect(m_modeWidget, &ResizeSignallingWidget::resized, this, [this](const QSize &size, const QSize &) { const bool hideSideArea = size.width() <= 750; const bool hideBottomArea = size.width() <= 850; const bool compactVertically = size.height() <= 530; - QTimer::singleShot(0, [this, hideSideArea, hideBottomArea, compactVertically]() { - m_sideArea->setVisible(!hideSideArea); - m_bottomArea->setVisible(!(hideBottomArea || compactVertically)); - m_topArea->setCompact(compactVertically); - }); + m_sideArea->setVisible(!hideSideArea); + m_bottomArea->setVisible(!(hideBottomArea || compactVertically)); + m_topArea->setCompact(compactVertically); }); m_sideArea = new SideArea(m_modeWidget); @@ -399,7 +396,7 @@ WelcomeMode::~WelcomeMode() void WelcomeMode::initPlugins() { - QSettings *settings = ICore::settings(); + QtcSettings *settings = ICore::settings(); m_activePage = Id::fromSetting(settings->value(currentPageSettingsKeyC)); for (IWelcomePage *page : IWelcomePage::allWelcomePages()) diff --git a/src/share/3rdparty/package-manager/auto-setup.cmake b/src/share/3rdparty/package-manager/auto-setup.cmake index 60c21440449..bae90ee6429 100644 --- a/src/share/3rdparty/package-manager/auto-setup.cmake +++ b/src/share/3rdparty/package-manager/auto-setup.cmake @@ -102,7 +102,8 @@ macro(qtc_auto_setup_conan) project(conan-setup) if (${conan_version} VERSION_GREATER_EQUAL 2.0) - include(\"${CMAKE_CURRENT_LIST_DIR}/conan_support.cmake\") + set(CONAN_COMMAND \"${conan_program}\") + include(\"${CMAKE_CURRENT_LIST_DIR}/conan_provider.cmake\") conan_profile_detect_default() detect_host_profile(\"${CMAKE_BINARY_DIR}/conan-dependencies/conan_host_profile\") @@ -111,10 +112,17 @@ macro(qtc_auto_setup_conan) --build=${QT_CREATOR_CONAN_BUILD_POLICY} -s build_type=${CMAKE_BUILD_TYPE} -g CMakeDeps) + + get_property(CONAN_INSTALL_SUCCESS GLOBAL PROPERTY CONAN_INSTALL_SUCCESS) if (CONAN_INSTALL_SUCCESS) + get_property(CONAN_GENERATORS_FOLDER GLOBAL PROPERTY CONAN_GENERATORS_FOLDER) file(WRITE \"${CMAKE_BINARY_DIR}/conan-dependencies/conan_paths.cmake\" \" list(PREPEND CMAKE_PREFIX_PATH \\\"\${CONAN_GENERATORS_FOLDER}\\\") list(PREPEND CMAKE_MODULE_PATH \\\"\${CONAN_GENERATORS_FOLDER}\\\") + list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) + list(REMOVE_DUPLICATES CMAKE_MODULE_PATH) + set(CMAKE_PREFIX_PATH \\\"\\\${CMAKE_PREFIX_PATH}\\\" CACHE STRING \\\"\\\" FORCE) + set(CMAKE_MODULE_PATH \\\"\\\${CMAKE_MODULE_PATH}\\\" CACHE STRING \\\"\\\" FORCE) \") endif() else() @@ -161,7 +169,7 @@ macro(qtc_auto_setup_vcpkg) if (EXISTS "${CMAKE_SOURCE_DIR}/vcpkg.json" AND NOT QT_CREATOR_SKIP_VCPKG_SETUP) option(QT_CREATOR_SKIP_VCPKG_SETUP "Skip Qt Creator's vcpkg package manager auto-setup" OFF) - find_program(vcpkg_program vcpkg) + find_program(vcpkg_program vcpkg $ENV{VCPKG_ROOT} ${CMAKE_SOURCE_DIR}/vcpkg) if (NOT vcpkg_program) message(WARNING "Qt Creator: vcpkg executable not found. " "Package manager auto-setup will be skipped. " @@ -218,6 +226,14 @@ macro(qtc_auto_setup_vcpkg) endif() set(CMAKE_TOOLCHAIN_FILE "${CMAKE_BINARY_DIR}/vcpkg-dependencies/toolchain.cmake" CACHE PATH "" FORCE) + + # Save CMAKE_PREFIX_PATH and CMAKE_MODULE_PATH as cache variables + if (CMAKE_VERSION GREATER_EQUAL "3.19") + cmake_language(DEFER CALL list REMOVE_DUPLICATES CMAKE_PREFIX_PATH) + cmake_language(DEFER CALL list REMOVE_DUPLICATES CMAKE_MODULE_PATH) + cmake_language(DEFER CALL set CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}" CACHE STRING "" FORCE) + cmake_language(DEFER CALL set CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" CACHE STRING "" FORCE) + endif() endif() endmacro() qtc_auto_setup_vcpkg() diff --git a/src/share/3rdparty/package-manager/conan_provider.cmake b/src/share/3rdparty/package-manager/conan_provider.cmake new file mode 100644 index 00000000000..d94a9d811f8 --- /dev/null +++ b/src/share/3rdparty/package-manager/conan_provider.cmake @@ -0,0 +1,544 @@ +# https://github.com/conan-io/cmake-conan/blob/develop2/conan_provider.cmake +# commit: 451fa97d2c59c07b13fb4812a64b2a6391f9e781 +# +# The MIT License (MIT) +# +# Copyright (c) 2019 JFrog +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +set(CONAN_MINIMUM_VERSION 2.0.5) + + +function(detect_os OS OS_API_LEVEL OS_SDK OS_SUBSYSTEM OS_VERSION) + # it could be cross compilation + message(STATUS "CMake-Conan: cmake_system_name=${CMAKE_SYSTEM_NAME}") + if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") + if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + set(${OS} Macos PARENT_SCOPE) + elseif(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") + set(${OS} Neutrino PARENT_SCOPE) + elseif(${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN") + set(${OS} Windows PARENT_SCOPE) + set(${OS_SUBSYSTEM} cygwin PARENT_SCOPE) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "^MSYS") + set(${OS} Windows PARENT_SCOPE) + set(${OS_SUBSYSTEM} msys2 PARENT_SCOPE) + else() + set(${OS} ${CMAKE_SYSTEM_NAME} PARENT_SCOPE) + endif() + if(${CMAKE_SYSTEM_NAME} STREQUAL "Android") + string(REGEX MATCH "[0-9]+" _OS_API_LEVEL ${ANDROID_PLATFORM}) + message(STATUS "CMake-Conan: android_platform=${ANDROID_PLATFORM}") + set(${OS_API_LEVEL} ${_OS_API_LEVEL} PARENT_SCOPE) + endif() + if(CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS") + # CMAKE_OSX_SYSROOT contains the full path to the SDK for MakeFile/Ninja + # generators, but just has the original input string for Xcode. + if(NOT IS_DIRECTORY ${CMAKE_OSX_SYSROOT}) + set(_OS_SDK ${CMAKE_OSX_SYSROOT}) + else() + if(CMAKE_OSX_SYSROOT MATCHES Simulator) + set(apple_platform_suffix simulator) + else() + set(apple_platform_suffix os) + endif() + if(CMAKE_OSX_SYSROOT MATCHES AppleTV) + set(_OS_SDK "appletv${apple_platform_suffix}") + elseif(CMAKE_OSX_SYSROOT MATCHES iPhone) + set(_OS_SDK "iphone${apple_platform_suffix}") + elseif(CMAKE_OSX_SYSROOT MATCHES Watch) + set(_OS_SDK "watch${apple_platform_suffix}") + endif() + endif() + if(DEFINED _OS_SDK) + message(STATUS "CMake-Conan: cmake_osx_sysroot=${CMAKE_OSX_SYSROOT}") + set(${OS_SDK} ${_OS_SDK} PARENT_SCOPE) + endif() + if(DEFINED CMAKE_OSX_DEPLOYMENT_TARGET) + message(STATUS "CMake-Conan: cmake_osx_deployment_target=${CMAKE_OSX_DEPLOYMENT_TARGET}") + set(${OS_VERSION} ${CMAKE_OSX_DEPLOYMENT_TARGET} PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + + +function(detect_arch ARCH) + # CMAKE_OSX_ARCHITECTURES can contain multiple architectures, but Conan only supports one. + # Therefore this code only finds one. If the recipes support multiple architectures, the + # build will work. Otherwise, there will be a linker error for the missing architecture(s). + if(DEFINED CMAKE_OSX_ARCHITECTURES) + string(REPLACE " " ";" apple_arch_list "${CMAKE_OSX_ARCHITECTURES}") + list(LENGTH apple_arch_list apple_arch_count) + if(apple_arch_count GREATER 1) + message(WARNING "CMake-Conan: Multiple architectures detected, this will only work if Conan recipe(s) produce fat binaries.") + endif() + endif() + if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64|arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES arm64) + set(_ARCH armv8) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "armv7-a|armv7l" OR CMAKE_OSX_ARCHITECTURES MATCHES armv7) + set(_ARCH armv7) + elseif(CMAKE_OSX_ARCHITECTURES MATCHES armv7s) + set(_ARCH armv7s) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686" OR CMAKE_OSX_ARCHITECTURES MATCHES i386) + set(_ARCH x86) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|amd64|x86_64" OR CMAKE_OSX_ARCHITECTURES MATCHES x86_64) + set(_ARCH x86_64) + endif() + message(STATUS "CMake-Conan: cmake_system_processor=${_ARCH}") + set(${ARCH} ${_ARCH} PARENT_SCOPE) +endfunction() + + +function(detect_cxx_standard CXX_STANDARD) + set(${CXX_STANDARD} ${CMAKE_CXX_STANDARD} PARENT_SCOPE) + if(CMAKE_CXX_EXTENSIONS) + set(${CXX_STANDARD} "gnu${CMAKE_CXX_STANDARD}" PARENT_SCOPE) + endif() +endfunction() + +macro(detect_gnu_libstdcxx) + # _CONAN_IS_GNU_LIBSTDCXX true if GNU libstdc++ + check_cxx_source_compiles(" + #include <cstddef> + #if !defined(__GLIBCXX__) && !defined(__GLIBCPP__) + static_assert(false); + #endif + int main(){}" _CONAN_IS_GNU_LIBSTDCXX) + + # _CONAN_GNU_LIBSTDCXX_IS_CXX11_ABI true if C++11 ABI + check_cxx_source_compiles(" + #include <string> + static_assert(sizeof(std::string) != sizeof(void*), \"using libstdc++\"); + int main () {}" _CONAN_GNU_LIBSTDCXX_IS_CXX11_ABI) + + set(_CONAN_GNU_LIBSTDCXX_SUFFIX "") + if(_CONAN_GNU_LIBSTDCXX_IS_CXX11_ABI) + set(_CONAN_GNU_LIBSTDCXX_SUFFIX "11") + endif() + unset (_CONAN_GNU_LIBSTDCXX_IS_CXX11_ABI) +endmacro() + +macro(detect_libcxx) + # _CONAN_IS_LIBCXX true if LLVM libc++ + check_cxx_source_compiles(" + #include <cstddef> + #if !defined(_LIBCPP_VERSION) + static_assert(false); + #endif + int main(){}" _CONAN_IS_LIBCXX) +endmacro() + + +function(detect_lib_cxx OS LIB_CXX) + if(${OS} STREQUAL "Android") + message(STATUS "CMake-Conan: android_stl=${ANDROID_STL}") + set(${LIB_CXX} ${ANDROID_STL} PARENT_SCOPE) + return() + endif() + + include(CheckCXXSourceCompiles) + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + detect_gnu_libstdcxx() + set(${LIB_CXX} "libstdc++${_CONAN_GNU_LIBSTDCXX_SUFFIX}" PARENT_SCOPE) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") + set(${LIB_CXX} "libc++" PARENT_SCOPE) + elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT CMAKE_SYSTEM_NAME MATCHES "Windows") + # Check for libc++ + detect_libcxx() + if(_CONAN_IS_LIBCXX) + set(${LIB_CXX} "libc++" PARENT_SCOPE) + return() + endif() + + # Check for libstdc++ + detect_gnu_libstdcxx() + if(_CONAN_IS_GNU_LIBSTDCXX) + set(${LIB_CXX} "libstdc++${_CONAN_GNU_LIBSTDCXX_SUFFIX}" PARENT_SCOPE) + return() + endif() + + # TODO: it would be an error if we reach this point + elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # Do nothing - compiler.runtime and compiler.runtime_type + # should be handled separately: https://github.com/conan-io/cmake-conan/pull/516 + return() + else() + # TODO: unable to determine, ask user to provide a full profile file instead + endif() +endfunction() + + +function(detect_compiler COMPILER COMPILER_VERSION COMPILER_RUNTIME COMPILER_RUNTIME_TYPE) + if(DEFINED CMAKE_CXX_COMPILER_ID) + set(_COMPILER ${CMAKE_CXX_COMPILER_ID}) + set(_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) + else() + if(NOT DEFINED CMAKE_C_COMPILER_ID) + message(FATAL_ERROR "C or C++ compiler not defined") + endif() + set(_COMPILER ${CMAKE_C_COMPILER_ID}) + set(_COMPILER_VERSION ${CMAKE_C_COMPILER_VERSION}) + endif() + + message(STATUS "CMake-Conan: CMake compiler=${_COMPILER}") + message(STATUS "CMake-Conan: CMake compiler version=${_COMPILER_VERSION}") + + if(_COMPILER MATCHES MSVC) + set(_COMPILER "msvc") + string(SUBSTRING ${MSVC_VERSION} 0 3 _COMPILER_VERSION) + # Configure compiler.runtime and compiler.runtime_type settings for MSVC + if(CMAKE_MSVC_RUNTIME_LIBRARY) + set(_KNOWN_MSVC_RUNTIME_VALUES "") + list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded MultiThreadedDLL) + list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreadedDebug MultiThreadedDebugDLL) + list(APPEND _KNOWN_MSVC_RUNTIME_VALUES MultiThreaded$<$<CONFIG:Debug>:Debug> MultiThreaded$<$<CONFIG:Debug>:Debug>DLL) + + # only accept the 6 possible values, otherwise we don't don't know to map this + if(NOT CMAKE_MSVC_RUNTIME_LIBRARY IN_LIST _KNOWN_MSVC_RUNTIME_VALUES) + message(FATAL_ERROR "CMake-Conan: unable to map MSVC runtime: ${CMAKE_MSVC_RUNTIME_LIBRARY} to Conan settings") + endif() + + # Runtime is "dynamic" in all cases if it ends in DLL + if(CMAKE_MSVC_RUNTIME_LIBRARY MATCHES ".*DLL$") + set(_COMPILER_RUNTIME "dynamic") + else() + set(_COMPILER_RUNTIME "static") + endif() + + # Only define compiler.runtime_type when explicitly requested + # If a generator expression is used, let Conan handle it conditional on build_type + get_property(_IS_MULTI_CONFIG_GENERATOR GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(NOT CMAKE_MSVC_RUNTIME_LIBRARY MATCHES "<CONFIG:Debug>:Debug>") + if(CMAKE_MSVC_RUNTIME_LIBRARY MATCHES "Debug") + set(_COMPILER_RUNTIME_TYPE "Debug") + else() + set(_COMPILER_RUNTIME_TYPE "Release") + endif() + endif() + + unset(_KNOWN_MSVC_RUNTIME_VALUES) + unset(_IS_MULTI_CONFIG_GENERATOR) + endif() + elseif(_COMPILER MATCHES AppleClang) + set(_COMPILER "apple-clang") + string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) + list(GET VERSION_LIST 0 _COMPILER_VERSION) + elseif(_COMPILER MATCHES Clang) + set(_COMPILER "clang") + string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) + list(GET VERSION_LIST 0 _COMPILER_VERSION) + elseif(_COMPILER MATCHES GNU) + set(_COMPILER "gcc") + string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) + list(GET VERSION_LIST 0 _COMPILER_VERSION) + endif() + + message(STATUS "CMake-Conan: [settings] compiler=${_COMPILER}") + message(STATUS "CMake-Conan: [settings] compiler.version=${_COMPILER_VERSION}") + if (_COMPILER_RUNTIME) + message(STATUS "CMake-Conan: [settings] compiler.runtime=${_COMPILER_RUNTIME}") + endif() + if (_COMPILER_RUNTIME_TYPE) + message(STATUS "CMake-Conan: [settings] compiler.runtime_type=${_COMPILER_RUNTIME_TYPE}") + endif() + + set(${COMPILER} ${_COMPILER} PARENT_SCOPE) + set(${COMPILER_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) + set(${COMPILER_RUNTIME} ${_COMPILER_RUNTIME} PARENT_SCOPE) + set(${COMPILER_RUNTIME_TYPE} ${_COMPILER_RUNTIME_TYPE} PARENT_SCOPE) +endfunction() + +function(detect_build_type BUILD_TYPE) + get_property(_MULTICONFIG_GENERATOR GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(NOT _MULTICONFIG_GENERATOR) + # Only set when we know we are in a single-configuration generator + # Note: we may want to fail early if `CMAKE_BUILD_TYPE` is not defined + set(${BUILD_TYPE} ${CMAKE_BUILD_TYPE} PARENT_SCOPE) + endif() +endfunction() + +macro(append_compiler_executables_configuration) + set(_conan_c_compiler "") + set(_conan_cpp_compiler "") + if(CMAKE_C_COMPILER) + set(_conan_c_compiler "\"c\":\"${CMAKE_C_COMPILER}\",") + else() + message(WARNING "CMake-Conan: The C compiler is not defined. " + "Please define CMAKE_C_COMPILER or enable the C language.") + endif() + if(CMAKE_CXX_COMPILER) + set(_conan_cpp_compiler "\"cpp\":\"${CMAKE_CXX_COMPILER}\"") + else() + message(WARNING "CMake-Conan: The C++ compiler is not defined. " + "Please define CMAKE_CXX_COMPILER or enable the C++ language.") + endif() + + string(APPEND PROFILE "tools.build:compiler_executables={${_conan_c_compiler}${_conan_cpp_compiler}}\n") + unset(_conan_c_compiler) + unset(_conan_cpp_compiler) +endmacro() + + +function(detect_host_profile output_file) + detect_os(MYOS MYOS_API_LEVEL MYOS_SDK MYOS_SUBSYSTEM MYOS_VERSION) + detect_arch(MYARCH) + detect_compiler(MYCOMPILER MYCOMPILER_VERSION MYCOMPILER_RUNTIME MYCOMPILER_RUNTIME_TYPE) + detect_cxx_standard(MYCXX_STANDARD) + detect_lib_cxx(MYOS MYLIB_CXX) + detect_build_type(MYBUILD_TYPE) + + set(PROFILE "") + string(APPEND PROFILE "[settings]\n") + if(MYARCH) + string(APPEND PROFILE arch=${MYARCH} "\n") + endif() + if(MYOS) + string(APPEND PROFILE os=${MYOS} "\n") + endif() + if(MYOS_API_LEVEL) + string(APPEND PROFILE os.api_level=${MYOS_API_LEVEL} "\n") + endif() + if(MYOS_VERSION) + string(APPEND PROFILE os.version=${MYOS_VERSION} "\n") + endif() + if(MYOS_SDK) + string(APPEND PROFILE os.sdk=${MYOS_SDK} "\n") + endif() + if(MYOS_SUBSYSTEM) + string(APPEND PROFILE os.subsystem=${MYOS_SUBSYSTEM} "\n") + endif() + if(MYCOMPILER) + string(APPEND PROFILE compiler=${MYCOMPILER} "\n") + endif() + if(MYCOMPILER_VERSION) + string(APPEND PROFILE compiler.version=${MYCOMPILER_VERSION} "\n") + endif() + if(MYCOMPILER_RUNTIME) + string(APPEND PROFILE compiler.runtime=${MYCOMPILER_RUNTIME} "\n") + endif() + if(MYCOMPILER_RUNTIME_TYPE) + string(APPEND PROFILE compiler.runtime_type=${MYCOMPILER_RUNTIME_TYPE} "\n") + endif() + if(MYCXX_STANDARD) + string(APPEND PROFILE compiler.cppstd=${MYCXX_STANDARD} "\n") + endif() + if(MYLIB_CXX) + string(APPEND PROFILE compiler.libcxx=${MYLIB_CXX} "\n") + endif() + if(MYBUILD_TYPE) + string(APPEND PROFILE "build_type=${MYBUILD_TYPE}\n") + endif() + + if(NOT DEFINED output_file) + set(_FN "${CMAKE_BINARY_DIR}/profile") + else() + set(_FN ${output_file}) + endif() + + string(APPEND PROFILE "[conf]\n") + string(APPEND PROFILE "tools.cmake.cmaketoolchain:generator=${CMAKE_GENERATOR}\n") + + # propagate compilers via profile + append_compiler_executables_configuration() + + if(${MYOS} STREQUAL "Android") + string(APPEND PROFILE "tools.android:ndk_path=${CMAKE_ANDROID_NDK}\n") + endif() + + message(STATUS "CMake-Conan: Creating profile ${_FN}") + file(WRITE ${_FN} ${PROFILE}) + message(STATUS "CMake-Conan: Profile: \n${PROFILE}") +endfunction() + + +function(conan_profile_detect_default) + message(STATUS "CMake-Conan: Checking if a default profile exists") + execute_process(COMMAND ${CONAN_COMMAND} profile path default + RESULT_VARIABLE return_code + OUTPUT_VARIABLE conan_stdout + ERROR_VARIABLE conan_stderr + ECHO_ERROR_VARIABLE # show the text output regardless + ECHO_OUTPUT_VARIABLE + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + if(NOT ${return_code} EQUAL "0") + message(STATUS "CMake-Conan: The default profile doesn't exist, detecting it.") + execute_process(COMMAND ${CONAN_COMMAND} profile detect + RESULT_VARIABLE return_code + OUTPUT_VARIABLE conan_stdout + ERROR_VARIABLE conan_stderr + ECHO_ERROR_VARIABLE # show the text output regardless + ECHO_OUTPUT_VARIABLE + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() +endfunction() + + +function(conan_install) + cmake_parse_arguments(ARGS CONAN_ARGS ${ARGN}) + set(CONAN_OUTPUT_FOLDER ${CMAKE_BINARY_DIR}/conan) + # Invoke "conan install" with the provided arguments + set(CONAN_ARGS ${CONAN_ARGS} -of=${CONAN_OUTPUT_FOLDER}) + message(STATUS "CMake-Conan: conan install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN}") + execute_process(COMMAND ${CONAN_COMMAND} install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN} --format=json + RESULT_VARIABLE return_code + OUTPUT_VARIABLE conan_stdout + ERROR_VARIABLE conan_stderr + ECHO_ERROR_VARIABLE # show the text output regardless + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + if(NOT "${return_code}" STREQUAL "0") + message(FATAL_ERROR "Conan install failed='${return_code}'") + else() + # the files are generated in a folder that depends on the layout used, if + # one is specified, but we don't know a priori where this is. + # TODO: this can be made more robust if Conan can provide this in the json output + string(JSON CONAN_GENERATORS_FOLDER GET ${conan_stdout} graph nodes 0 generators_folder) + # message("conan stdout: ${conan_stdout}") + message(STATUS "CMake-Conan: CONAN_GENERATORS_FOLDER=${CONAN_GENERATORS_FOLDER}") + set_property(GLOBAL PROPERTY CONAN_GENERATORS_FOLDER "${CONAN_GENERATORS_FOLDER}") + # reconfigure on conanfile changes + string(JSON CONANFILE GET ${conan_stdout} graph nodes 0 label) + message(STATUS "CMake-Conan: CONANFILE=${CMAKE_SOURCE_DIR}/${CONANFILE}") + set_property(DIRECTORY ${CMAKE_SOURCE_DIR} APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/${CONANFILE}") + # success + set_property(GLOBAL PROPERTY CONAN_INSTALL_SUCCESS TRUE) + endif() +endfunction() + + +function(conan_get_version conan_command conan_current_version) + execute_process( + COMMAND ${conan_command} --version + OUTPUT_VARIABLE conan_output + RESULT_VARIABLE conan_result + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(conan_result) + message(FATAL_ERROR "CMake-Conan: Error when trying to run Conan") + endif() + + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" conan_version ${conan_output}) + set(${conan_current_version} ${conan_version} PARENT_SCOPE) +endfunction() + + +function(conan_version_check) + set(options ) + set(oneValueArgs MINIMUM CURRENT) + set(multiValueArgs ) + cmake_parse_arguments(CONAN_VERSION_CHECK + "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT CONAN_VERSION_CHECK_MINIMUM) + message(FATAL_ERROR "CMake-Conan: Required parameter MINIMUM not set!") + endif() + if(NOT CONAN_VERSION_CHECK_CURRENT) + message(FATAL_ERROR "CMake-Conan: Required parameter CURRENT not set!") + endif() + + if(CONAN_VERSION_CHECK_CURRENT VERSION_LESS CONAN_VERSION_CHECK_MINIMUM) + message(FATAL_ERROR "CMake-Conan: Conan version must be ${CONAN_VERSION_CHECK_MINIMUM} or later") + endif() +endfunction() + +macro(construct_profile_argument argument_variable profile_list) + set(${argument_variable} "") + if("${profile_list}" STREQUAL "CONAN_HOST_PROFILE") + set(_arg_flag "--profile:host=") + elseif("${profile_list}" STREQUAL "CONAN_BUILD_PROFILE") + set(_arg_flag "--profile:build=") + endif() + + set(_profile_list "${${profile_list}}") + list(TRANSFORM _profile_list REPLACE "auto-cmake" "${CMAKE_BINARY_DIR}/conan_host_profile") + list(TRANSFORM _profile_list PREPEND ${_arg_flag}) + set(${argument_variable} ${_profile_list}) + + unset(_arg_flag) + unset(_profile_list) +endmacro() + + +macro(conan_provide_dependency method package_name) + set_property(GLOBAL PROPERTY CONAN_PROVIDE_DEPENDENCY_INVOKED TRUE) + get_property(CONAN_INSTALL_SUCCESS GLOBAL PROPERTY CONAN_INSTALL_SUCCESS) + if(NOT CONAN_INSTALL_SUCCESS) + find_program(CONAN_COMMAND "conan" REQUIRED) + conan_get_version(${CONAN_COMMAND} CONAN_CURRENT_VERSION) + conan_version_check(MINIMUM ${CONAN_MINIMUM_VERSION} CURRENT ${CONAN_CURRENT_VERSION}) + message(STATUS "CMake-Conan: first find_package() found. Installing dependencies with Conan") + if("default" IN_LIST CONAN_HOST_PROFILE OR "default" IN_LIST CONAN_BUILD_PROFILE) + conan_profile_detect_default() + endif() + if("auto-cmake" IN_LIST CONAN_HOST_PROFILE) + detect_host_profile(${CMAKE_BINARY_DIR}/conan_host_profile) + endif() + construct_profile_argument(_host_profile_flags CONAN_HOST_PROFILE) + construct_profile_argument(_build_profile_flags CONAN_BUILD_PROFILE) + get_property(_MULTICONFIG_GENERATOR GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(NOT _MULTICONFIG_GENERATOR) + message(STATUS "CMake-Conan: Installing single configuration ${CMAKE_BUILD_TYPE}") + conan_install(${_host_profile_flags} ${_build_profile_flags} --build=missing -g CMakeDeps) + else() + message(STATUS "CMake-Conan: Installing both Debug and Release") + conan_install(${_host_profile_flags} ${_build_profile_flags} -s build_type=Release --build=missing -g CMakeDeps) + conan_install(${_host_profile_flags} ${_build_profile_flags} -s build_type=Debug --build=missing -g CMakeDeps) + endif() + unset(_MULTICONFIG_GENERATOR) + else() + message(STATUS "CMake-Conan: find_package(${ARGV1}) found, 'conan install' already ran") + endif() + + get_property(CONAN_GENERATORS_FOLDER GLOBAL PROPERTY CONAN_GENERATORS_FOLDER) + + # Ensure that we consider Conan-provided packages ahead of any other, + # irrespective of other settings that modify the search order or search paths + # This follows the guidelines from the find_package documentation + # (https://cmake.org/cmake/help/latest/command/find_package.html): + # find_package (<PackageName> PATHS paths... NO_DEFAULT_PATH) + # find_package (<PackageName>) + + # Filter out `REQUIRED` from the argument list, as the first call may fail + set(_find_args "${ARGN}") + list(REMOVE_ITEM _find_args "REQUIRED") + if(NOT "MODULE" IN_LIST _find_args) + find_package(${package_name} ${_find_args} BYPASS_PROVIDER PATHS "${CONAN_GENERATORS_FOLDER}" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) + endif() + + # Invoke find_package a second time - if the first call succeeded, + # this will simply reuse the result. If not, fall back to CMake default search + # behaviour, also allowing modules to be searched. + set(_cmake_module_path_orig "${CMAKE_MODULE_PATH}") + list(PREPEND CMAKE_MODULE_PATH "${CONAN_GENERATORS_FOLDER}") + if(NOT ${package_name}_FOUND) + find_package(${package_name} ${ARGN} BYPASS_PROVIDER) + endif() + + set(CMAKE_MODULE_PATH "${_cmake_module_path_orig}") + unset(_find_args) + unset(_cmake_module_path_orig) + unset(_host_profile_flags) + unset(_build_profile_flags) +endmacro() + +# Configurable variables for Conan profiles +set(CONAN_HOST_PROFILE "default;auto-cmake" CACHE STRING "Conan host profile") +set(CONAN_BUILD_PROFILE "default" CACHE STRING "Conan build profile") diff --git a/src/share/3rdparty/package-manager/conan_support.cmake b/src/share/3rdparty/package-manager/conan_support.cmake deleted file mode 100644 index f1dbccf2aaf..00000000000 --- a/src/share/3rdparty/package-manager/conan_support.cmake +++ /dev/null @@ -1,211 +0,0 @@ -# https://github.com/conan-io/cmake-conan/blob/develop2/conan_support.cmake -# commit: 3e088cd3e1d9d69e04b5250d565c1b8b55b0400b -# -# The MIT License (MIT) -# -# Copyright (c) 2019 JFrog -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -function(detect_os OS) - # it could be cross compilation - message(STATUS "Conan-cmake: cmake_system_name=${CMAKE_SYSTEM_NAME}") - if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") - if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - set(${OS} Macos PARENT_SCOPE) - elseif(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") - set(${OS} Neutrino PARENT_SCOPE) - else() - set(${OS} ${CMAKE_SYSTEM_NAME} PARENT_SCOPE) - endif() - endif() -endfunction() - - -function(detect_cxx_standard CXX_STANDARD) - set(${CXX_STANDARD} ${CMAKE_CXX_STANDARD} PARENT_SCOPE) - if (CMAKE_CXX_EXTENSIONS) - set(${CXX_STANDARD} "gnu${CMAKE_CXX_STANDARD}" PARENT_SCOPE) - endif() -endfunction() - - -function(detect_compiler COMPILER COMPILER_VERSION) - if(DEFINED CMAKE_CXX_COMPILER_ID) - set(_COMPILER ${CMAKE_CXX_COMPILER_ID}) - set(_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) - else() - if(NOT DEFINED CMAKE_C_COMPILER_ID) - message(FATAL_ERROR "C or C++ compiler not defined") - endif() - set(_COMPILER ${CMAKE_C_COMPILER_ID}) - set(_COMPILER_VERSION ${CMAKE_C_COMPILER_VERSION}) - endif() - - message(STATUS "Conan-cmake: CMake compiler=${_COMPILER}") - message(STATUS "Conan-cmake: CMake cmpiler version=${_COMPILER_VERSION}") - - if(_COMPILER MATCHES MSVC) - set(_COMPILER "msvc") - string(SUBSTRING ${MSVC_VERSION} 0 3 _COMPILER_VERSION) - elseif(_COMPILER MATCHES AppleClang) - set(_COMPILER "apple-clang") - string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) - list(GET VERSION_LIST 0 _COMPILER_VERSION) - elseif(_COMPILER MATCHES Clang) - set(_COMPILER "clang") - string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) - list(GET VERSION_LIST 0 _COMPILER_VERSION) - elseif(_COMPILER MATCHES GNU) - set(_COMPILER "gcc") - string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) - list(GET VERSION_LIST 0 _COMPILER_VERSION) - endif() - - message(STATUS "Conan-cmake: [settings] compiler=${_COMPILER}") - message(STATUS "Conan-cmake: [settings] compiler.version=${_COMPILER_VERSION}") - - set(${COMPILER} ${_COMPILER} PARENT_SCOPE) - set(${COMPILER_VERSION} ${_COMPILER_VERSION} PARENT_SCOPE) -endfunction() - -function(detect_build_type BUILD_TYPE) - if(NOT CMAKE_CONFIGURATION_TYPES) - # Only set when we know we are in a single-configuration generator - # Note: we may want to fail early if `CMAKE_BUILD_TYPE` is not defined - set(${BUILD_TYPE} ${CMAKE_BUILD_TYPE} PARENT_SCOPE) - endif() -endfunction() - - -function(detect_host_profile output_file) - detect_os(MYOS) - detect_compiler(MYCOMPILER MYCOMPILER_VERSION) - detect_cxx_standard(MYCXX_STANDARD) - detect_build_type(MYBUILD_TYPE) - - set(PROFILE "") - string(APPEND PROFILE "include(default)\n") - string(APPEND PROFILE "[settings]\n") - if(MYOS) - string(APPEND PROFILE os=${MYOS} "\n") - endif() - if(MYCOMPILER) - string(APPEND PROFILE compiler=${MYCOMPILER} "\n") - endif() - if(MYCOMPILER_VERSION) - string(APPEND PROFILE compiler.version=${MYCOMPILER_VERSION} "\n") - endif() - if(MYCXX_STANDARD) - string(APPEND PROFILE compiler.cppstd=${MYCXX_STANDARD} "\n") - endif() - if(MYBUILD_TYPE) - string(APPEND PROFILE "build_type=${MYBUILD_TYPE}\n") - endif() - - if(NOT DEFINED output_file) - set(_FN "${CMAKE_BINARY_DIR}/profile") - else() - set(_FN ${output_file}) - endif() - - string(APPEND PROFILE "[conf]\n") - string(APPEND PROFILE "tools.cmake.cmaketoolchain:generator=${CMAKE_GENERATOR}\n") - - message(STATUS "Conan-cmake: Creating profile ${_FN}") - file(WRITE ${_FN} ${PROFILE}) - message(STATUS "Conan-cmake: Profile: \n${PROFILE}") -endfunction() - - -function(conan_profile_detect_default) - message(STATUS "Conan-cmake: Checking if a default profile exists") - execute_process(COMMAND conan profile path default - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - ECHO_OUTPUT_VARIABLE - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - if(NOT ${return_code} EQUAL "0") - message(STATUS "Conan-cmake: The default profile doesn't exist, detecting it.") - execute_process(COMMAND conan profile detect - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - ECHO_OUTPUT_VARIABLE - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() -endfunction() - - -function(conan_install) - cmake_parse_arguments(ARGS CONAN_ARGS ${ARGN}) - set(CONAN_OUTPUT_FOLDER ${CMAKE_BINARY_DIR}/conan) - # Invoke "conan install" with the provided arguments - set(CONAN_ARGS ${CONAN_ARGS} -of=${CONAN_OUTPUT_FOLDER}) - message(STATUS "CMake-conan: conan install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN}") - execute_process(COMMAND conan install ${CMAKE_SOURCE_DIR} ${CONAN_ARGS} ${ARGN} --format=json - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_stdout - ERROR_VARIABLE conan_stderr - ECHO_ERROR_VARIABLE # show the text output regardless - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan install failed='${return_code}'") - else() - # the files are generated in a folder that depends on the layout used, if - # one if specified, but we don't know a priori where this is. - # TODO: this can be made more robust if Conan can provide this in the json output - string(JSON CONAN_GENERATORS_FOLDER GET ${conan_stdout} graph nodes 0 generators_folder) - # message("conan stdout: ${conan_stdout}") - message(STATUS "CMake-conan: CONAN_GENERATORS_FOLDER=${CONAN_GENERATORS_FOLDER}") - set(CONAN_GENERATORS_FOLDER "${CONAN_GENERATORS_FOLDER}" PARENT_SCOPE) - set(CONAN_INSTALL_SUCCESS TRUE CACHE BOOL "Conan install has been invoked and was successful") - endif() -endfunction() - - -function(conan_provide_dependency package_name) - if(NOT CONAN_INSTALL_SUCCESS) - message(STATUS "CMake-conan: first find_package() found. Installing dependencies with Conan") - conan_profile_detect_default() - detect_host_profile(${CMAKE_BINARY_DIR}/conan_host_profile) - if(NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "CMake-conan: Installing single configuration ${CMAKE_BUILD_TYPE}") - conan_install(-pr ${CMAKE_BINARY_DIR}/conan_host_profile --build=missing -g CMakeDeps) - else() - message(STATUS "CMake-conan: Installing both Debug and Release") - conan_install(-pr ${CMAKE_BINARY_DIR}/conan_host_profile -s build_type=Release --build=missing -g CMakeDeps) - conan_install(-pr ${CMAKE_BINARY_DIR}/conan_host_profile -s build_type=Debug --build=missing -g CMakeDeps) - endif() - if (CONAN_INSTALL_SUCCESS) - set(CONAN_GENERATORS_FOLDER "${CONAN_GENERATORS_FOLDER}" CACHE PATH "Conan generators folder") - endif() - else() - message(STATUS "CMake-conan: find_package(${package_name}) found, 'conan install' aready ran") - endif() - - if (CONAN_GENERATORS_FOLDER) - list(PREPEND CMAKE_PREFIX_PATH "${CONAN_GENERATORS_FOLDER}") - endif() - - find_package(${ARGN} BYPASS_PROVIDER) -endfunction() diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 93409b0bf45..b945e8faa92 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -1,7 +1,6 @@ add_subdirectory(designerintegrationv2) add_subdirectory(proparser) add_subdirectory(qtsingleapplication) -add_subdirectory(qtlockedfile) add_subdirectory(help) add_subdirectory(registryaccess) @@ -19,7 +18,11 @@ if (BUILD_QBS) set(QBS_LIB_INSTALL_DIR "${IDE_LIBRARY_PATH}" CACHE STRING "" FORCE) set(QBS_DLL_INSTALL_DIR "${IDE_BIN_PATH}" CACHE STRING "" FORCE) set(QBS_LIBEXEC_INSTALL_DIR "${IDE_LIBEXEC_PATH}" CACHE STRING "" FORCE) - set(QBS_PLUGINS_INSTALL_BASE "${IDE_PLUGIN_PATH}" CACHE STRING "" FORCE) + if (APPLE) + set(QBS_PLUGINS_INSTALL_BASE "${IDE_PLUGIN_PATH}/../" CACHE STRING "" FORCE) + else() + set(QBS_PLUGINS_INSTALL_BASE "${_IDE_LIBRARY_BASE_PATH}/" CACHE STRING "" FORCE) + endif() set(QBS_RESOURCES_INSTALL_BASE "${IDE_DATA_PATH}/qbs" CACHE STRING "" FORCE) set(QBS_DOC_INSTALL_DIR "${IDE_DOC_PATH}" CACHE STRING "" FORCE) set(QBS_HEADERS_INSTALL_DIR "${IDE_DATA_PATH}/qbs/include/qbs" CACHE STRING "" FORCE) diff --git a/src/shared/help/bookmarkmanager.cpp b/src/shared/help/bookmarkmanager.cpp index 72bd5db3214..523b7521c22 100644 --- a/src/shared/help/bookmarkmanager.cpp +++ b/src/shared/help/bookmarkmanager.cpp @@ -600,7 +600,7 @@ void BookmarkManager::saveBookmarks() QDataStream stream(&bookmarks, QIODevice::WriteOnly); readBookmarksRecursive(treeModel->invisibleRootItem(), stream, 0); - Core::ICore::settings()->setValue(QLatin1String(kBookmarksKey), bookmarks); + Core::ICore::settings()->setValue(kBookmarksKey, bookmarks); } QStringList BookmarkManager::bookmarkFolders() const @@ -720,8 +720,8 @@ void BookmarkManager::setupBookmarkModels() QList<QStandardItem*> parents; QByteArray ba; - QSettings *settings = Core::ICore::settings(); - ba = settings->value(QLatin1String(kBookmarksKey)).toByteArray(); + Utils::QtcSettings *settings = Core::ICore::settings(); + ba = settings->value(kBookmarksKey).toByteArray(); QDataStream stream(ba); while (!stream.atEnd()) { stream >> depth >> name >> type >> expanded; diff --git a/src/shared/help/contentwindow.cpp b/src/shared/help/contentwindow.cpp index 52c27a60180..216a63b3efe 100644 --- a/src/shared/help/contentwindow.cpp +++ b/src/shared/help/contentwindow.cpp @@ -27,6 +27,7 @@ ContentWindow::ContentWindow() { m_contentModel = (&LocalHelpManager::helpEngine())->contentModel(); m_contentWidget = new Utils::NavigationTreeView; + m_contentWidget->setObjectName("helpContentWidget"); // used by Squish m_contentWidget->setModel(m_contentModel); m_contentWidget->setActivationMode(Utils::SingleClickActivation); m_contentWidget->installEventFilter(this); diff --git a/src/shared/help/topicchooser.cpp b/src/shared/help/topicchooser.cpp index 93a4bf47be9..43d24cd1364 100644 --- a/src/shared/help/topicchooser.cpp +++ b/src/shared/help/topicchooser.cpp @@ -9,6 +9,8 @@ #include <utils/layoutbuilder.h> #include <utils/utilstr.h> +#include <coreplugin/icore.h> + #include <QMap> #include <QUrl> @@ -18,12 +20,18 @@ #include <QStandardItemModel> #include <QSortFilterProxyModel> +const int kInitialWidth = 400; +const int kInitialHeight = 220; +const char kPreferenceDialogSize[] = "Core/TopicChooserSize"; + TopicChooser::TopicChooser(QWidget *parent, const QString &keyword, const QMultiMap<QString, QUrl> &links) : QDialog(parent) , m_filterModel(new QSortFilterProxyModel(this)) { - resize(400, 220); + const QSize initialSize(kInitialWidth, kInitialHeight); + resize(Core::ICore::settings()->value(kPreferenceDialogSize, initialSize).toSize()); + setWindowTitle(::Help::Tr::tr("Choose Topic")); QStandardItemModel *model = new QStandardItemModel(this); @@ -70,6 +78,13 @@ TopicChooser::TopicChooser(QWidget *parent, const QString &keyword, this, &TopicChooser::setFilter); } +TopicChooser::~TopicChooser() +{ + Core::ICore::settings()->setValueWithDefault(kPreferenceDialogSize, + size(), + QSize(kInitialWidth, kInitialHeight)); +} + QUrl TopicChooser::link() const { if (m_activedIndex.isValid()) diff --git a/src/shared/help/topicchooser.h b/src/shared/help/topicchooser.h index 53954cad09f..feca98c85fd 100644 --- a/src/shared/help/topicchooser.h +++ b/src/shared/help/topicchooser.h @@ -24,6 +24,7 @@ class TopicChooser : public QDialog public: TopicChooser(QWidget *parent, const QString &keyword, const QMultiMap<QString, QUrl> &links); + ~TopicChooser() override; QUrl link() const; diff --git a/src/shared/pch_files.qbs b/src/shared/pch_files.qbs deleted file mode 100644 index 237f8456db8..00000000000 --- a/src/shared/pch_files.qbs +++ /dev/null @@ -1,16 +0,0 @@ -import qbs -import qbs.FileInfo - -Product { - name: "precompiled headers" - condition: qtc.make_dev_package - Depends { name: "qtc" } - Group { - files: [ - "qtcreator_pch.h", - "qtcreator_gui_pch.h", - ] - qbs.install: true - qbs.installDir: qtc.ide_shared_sources_path - } -} diff --git a/src/shared/proparser/proparser.qbs b/src/shared/proparser/proparser.qbs index dd9ce7cc64b..290ffdff5d8 100644 --- a/src/shared/proparser/proparser.qbs +++ b/src/shared/proparser/proparser.qbs @@ -6,13 +6,13 @@ Product { Export { Depends { name: "cpp" } - cpp.defines: base.concat([ + cpp.defines: [ "QMAKE_AS_LIBRARY", "PROPARSER_THREAD_SAFE", "PROEVALUATOR_THREAD_SAFE", "PROEVALUATOR_CUMULATIVE", "PROEVALUATOR_SETENV", - ]) - cpp.includePaths: base.concat([exportingProduct.sourceDirectory + "/.."]) + ] + cpp.includePaths: exportingProduct.sourceDirectory + "/.." } } diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 87707106e61..505b4cf9895 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -441,6 +441,13 @@ QMAKE_EXPORT std::function<void(ProcessData *data)> &theProcessRunner() return runner; } +QMAKE_EXPORT std::function<std::optional<QString>(const QString &, const QStringList &)> + &thePrompter() +{ + static std::function<std::optional<QString>(const QString &, const QStringList &)> prompter; + return prompter; +} + void QMakeEvaluator::runProcessHelper(ProcessData *data) const { const QString root = deviceRoot(); @@ -1161,12 +1168,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( } } break; -#ifdef PROEVALUATOR_FULL +//#ifdef PROEVALUATOR_FULL case E_PROMPT: { if (args.count() != 1 && args.count() != 2) { evalError(fL1S("prompt(question, [decorate=true]) requires one or two arguments.")); -// } else if (currentFileName() == QLatin1String("-")) { -// evalError(fL1S("prompt(question) cannot be used when '-o -' is used")); + } else if (currentFileName() == QLatin1String("-")) { + evalError(fL1S("prompt(question) cannot be used when '-o -' is used")); } else { QString msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp1)); bool decorate = true; @@ -1179,20 +1186,32 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( } else { fputs(qPrintable(msg), stderr); } - QFile qfile; - if (qfile.open(stdin, QIODevice::ReadOnly)) { - QTextStream t(&qfile); - const QString &line = t.readLine(); - if (t.atEnd()) { + + if (thePrompter()) { + std::optional<QString> line = thePrompter()(msg, m_logBuffer); + m_logBuffer.clear(); + if (!line) { fputs("\n", stderr); evalError(fL1S("Unexpected EOF.")); return ReturnError; } - ret = split_value_list(QStringView(line)); - } + ret = split_value_list(QStringView(*line)); + } else { + QFile qfile; + if (qfile.open(stdin, QIODevice::ReadOnly)) { + QTextStream t(&qfile); + const QString &line = t.readLine(); + if (t.atEnd()) { + fputs("\n", stderr); + evalError(fL1S("Unexpected EOF.")); + return ReturnError; + } + ret = split_value_list(QStringView(line)); + } + } } break; } -#endif +//#endif case E_REPLACE: if (args.count() != 3 ) { evalError(fL1S("replace(var, before, after) requires three arguments.")); @@ -1827,6 +1846,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( const QString &msg = m_option->expandEnvVars(args.at(0).toQString(m_tmp2)); if (!m_skipLevel) { if (func_t == T_LOG) { + m_logBuffer.append(msg); + if (m_logBuffer.size() > 15) + m_logBuffer.takeFirst(); #ifdef PROEVALUATOR_FULL fputs(msg.toLatin1().constData(), stderr); #endif diff --git a/src/shared/proparser/qmakeevaluator.h b/src/shared/proparser/qmakeevaluator.h index da681b3bbe6..a3d7438d829 100644 --- a/src/shared/proparser/qmakeevaluator.h +++ b/src/shared/proparser/qmakeevaluator.h @@ -48,6 +48,10 @@ public: QT_BEGIN_NAMESPACE QMAKE_EXPORT std::function<void(ProcessData *data)> &theProcessRunner(); + +QMAKE_EXPORT std::function<std::optional<QString>(const QString &, const QStringList &)> & + thePrompter(); + QMAKE_EXPORT QString removeHostAndScheme(const QString &remotePath); class QMakeGlobals; @@ -296,6 +300,7 @@ public: QStringList m_qmakepath; QStringList m_qmakefeatures; QStringList m_mkspecPaths; + QStringList m_logBuffer; QExplicitlySharedDataPointer<QMakeFeatureRoots> m_featureRoots; ProString m_dirSep; ProFunctionDefs m_functionDefs; diff --git a/src/shared/proparser/qmakeglobals.cpp b/src/shared/proparser/qmakeglobals.cpp index 03e0fd60559..7fa8915920f 100644 --- a/src/shared/proparser/qmakeglobals.cpp +++ b/src/shared/proparser/qmakeglobals.cpp @@ -79,7 +79,7 @@ QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &s QString ret = QDir::cleanPath(spec); if (ret.contains(QLatin1Char('/'))) { QString absRet = IoUtils::resolvePath(device_root, state.pwd, ret); - if (QFile::exists(absRet)) + if (QFileInfo::exists(absRet)) ret = absRet; } return ret; diff --git a/src/shared/qbs b/src/shared/qbs index 40d30480847..d99256dd794 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 40d304808479ea72c1d02dacf007ab8b05851e2e +Subproject commit d99256dd79460628aafb5fa34a8dde7761ff7b1c diff --git a/src/shared/qtlockedfile/CMakeLists.txt b/src/shared/qtlockedfile/CMakeLists.txt deleted file mode 100644 index f7f8a1c6662..00000000000 --- a/src/shared/qtlockedfile/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -if (WIN32) - set(OS_SOURCES qtlockedfile_win.cpp) -else() - set(OS_SOURCES qtlockedfile_unix.cpp) -endif() - -add_library(shared_qtlockedfile STATIC ${OS_SOURCES} qtlockedfile.cpp qtlockedfile.h) -target_link_libraries(shared_qtlockedfile Qt::Core) -target_include_directories(shared_qtlockedfile PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -if (WIN32) - target_compile_definitions(shared_qtlockedfile PRIVATE - "QT_QTLOCKEDFILE_EXPORT=__declspec(dllexport)" _UNICODE UNICODE) -endif() - -if (WITH_SANITIZE) - qtc_enable_sanitize(shared_qtlockedfile ${SANITIZE_FLAGS}) -endif() diff --git a/src/shared/qtlockedfile/README.txt b/src/shared/qtlockedfile/README.txt deleted file mode 100644 index 6fcf2fd2954..00000000000 --- a/src/shared/qtlockedfile/README.txt +++ /dev/null @@ -1,10 +0,0 @@ -This is the src directory of the QtLockedFile -solution integrated over from addons/main/utils/qtlockedfile/src . - -namespace.patch was applied to introduce the SharedTools namespace. - -It is required by the QtSingleApplication solution. - -History: - -16.05.2008 Integrated diff --git a/src/shared/qtlockedfile/namespace.patch b/src/shared/qtlockedfile/namespace.patch deleted file mode 100644 index 301cae8cff7..00000000000 --- a/src/shared/qtlockedfile/namespace.patch +++ /dev/null @@ -1,70 +0,0 @@ - ---- qtlockedfile.cpp 1970-01-01 01:00:00.000000000 -+++ qtlockedfile.cpp 2008/05/16 10:51:19.000000000 -@@ -1,5 +1,7 @@ - #include "qtlockedfile.h" - -+namespace SharedTools { -+ - /*! - \class QtLockedFile - -@@ -123,3 +125,5 @@ - - Destroys the \e QtLockedFile object. If any locks were held, they are released. - */ -+ -+} - ---- qtlockedfile.h 1970-01-01 01:00:00.000000000 -+++ qtlockedfile.h 2008/05/16 10:51:19.000000000 -@@ -19,6 +19,8 @@ - # define QT_QTLOCKEDFILE_EXPORT - #endif - -+namespace SharedTools { -+ - class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile - { - public: -@@ -41,4 +43,6 @@ - LockMode m_lock_mode; - }; - -+} -+ - #endif - ---- qtlockedfile_unix.cpp 1970-01-01 01:00:00.000000000 -+++ qtlockedfile_unix.cpp 2008/05/16 10:51:19.000000000 -@@ -5,6 +5,8 @@ - - #include "qtlockedfile.h" - -+namespace SharedTools { -+ - bool QtLockedFile::lock(LockMode mode, bool block) - { - if (!isOpen()) { -@@ -73,3 +75,4 @@ - unlock(); - } - -+} - ---- qtlockedfile_win.cpp 1970-01-01 01:00:00.000000000 -+++ qtlockedfile_win.cpp 2008/05/16 10:51:19.000000000 -@@ -2,6 +2,8 @@ - #include <qt_windows.h> - #include <QtCore/QFileInfo> - -+namespace SharedTools { -+ - #define SEMAPHORE_PREFIX "QtLockedFile semaphore " - #define MUTEX_PREFIX "QtLockedFile mutex " - #define SEMAPHORE_MAX 100 -@@ -168,3 +170,4 @@ - } - } - -+} diff --git a/src/shared/qtlockedfile/qtlockedfile.cpp b/src/shared/qtlockedfile/qtlockedfile.cpp deleted file mode 100644 index a14c7e14b04..00000000000 --- a/src/shared/qtlockedfile/qtlockedfile.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qtlockedfile.h" - -namespace SharedTools { - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QtLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor behaves in the same way - as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - m_semaphore_hnd = 0; - m_mutex_hnd = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This constructor behaves in - the same way as \e QFile::QFile(const QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - m_semaphore_hnd = 0; - m_mutex_hnd = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. - - If \a block is true, this - function will block until the lock is acquired. If \a block is - false, this function returns \e false immediately if the lock cannot - be acquired. - - If this object already has a lock of type \a mode, this function returns \e true immediately. If this object has a lock of a different type than \a mode, the lock - is first released and then a new lock is obtained. - - This function returns \e true if, after it executes, the file is locked by this object, - and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is not locked by - this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they are released. -*/ - -} // namespace SharedTools diff --git a/src/shared/qtlockedfile/qtlockedfile.h b/src/shared/qtlockedfile/qtlockedfile.h deleted file mode 100644 index d4aa9a59c0d..00000000000 --- a/src/shared/qtlockedfile/qtlockedfile.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include <QFile> - -#if defined(Q_OS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace SharedTools { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE m_semaphore_hnd; - Qt::HANDLE m_mutex_hnd; -#endif - LockMode m_lock_mode; -}; - -} // namespace SharedTools diff --git a/src/shared/qtlockedfile/qtlockedfile_unix.cpp b/src/shared/qtlockedfile/qtlockedfile_unix.cpp deleted file mode 100644 index e189f31472d..00000000000 --- a/src/shared/qtlockedfile/qtlockedfile_unix.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qtlockedfile.h" - -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> - -namespace SharedTools { - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - remove(); - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - -} // namespace SharedTools diff --git a/src/shared/qtlockedfile/qtlockedfile_win.cpp b/src/shared/qtlockedfile/qtlockedfile_win.cpp deleted file mode 100644 index 2f35635b55e..00000000000 --- a/src/shared/qtlockedfile/qtlockedfile_win.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "qtlockedfile.h" - -#include <qt_windows.h> -#include <QFileInfo> - -namespace SharedTools { - -#define SEMAPHORE_PREFIX "QtLockedFile semaphore " -#define MUTEX_PREFIX "QtLockedFile mutex " -#define SEMAPHORE_MAX 100 - -static QString errorCodeToString(DWORD errorCode) -{ - QString result; - char *data = 0; - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - 0, errorCode, 0, - (char*)&data, 0, 0); - result = QString::fromLocal8Bit(data); - if (data != 0) - LocalFree(data); - - if (result.endsWith(QLatin1Char('\n'))) - result.truncate(result.length() - 1); - - return result; -} - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != 0) - unlock(); - - if (m_semaphore_hnd == 0) { - QFileInfo fi(*this); - QString sem_name = QString::fromLatin1(SEMAPHORE_PREFIX) - + fi.absoluteFilePath().toLower(); - - m_semaphore_hnd = CreateSemaphoreW(0, SEMAPHORE_MAX, SEMAPHORE_MAX, - (TCHAR*)sem_name.utf16()); - - if (m_semaphore_hnd == 0) { - qWarning("QtLockedFile::lock(): CreateSemaphore: %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - return false; - } - } - - bool gotMutex = false; - int decrement; - if (mode == ReadLock) { - decrement = 1; - } else { - decrement = SEMAPHORE_MAX; - if (m_mutex_hnd == 0) { - QFileInfo fi(*this); - QString mut_name = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - - m_mutex_hnd = CreateMutexW(NULL, FALSE, (TCHAR*)mut_name.utf16()); - - if (m_mutex_hnd == 0) { - qWarning("QtLockedFile::lock(): CreateMutex: %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - return false; - } - } - DWORD res = WaitForSingleObject(m_mutex_hnd, block ? INFINITE : 0); - if (res == WAIT_TIMEOUT) - return false; - if (res == WAIT_FAILED) { - qWarning("QtLockedFile::lock(): WaitForSingleObject (mutex): %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - return false; - } - gotMutex = true; - } - - for (int i = 0; i < decrement; ++i) { - DWORD res = WaitForSingleObject(m_semaphore_hnd, block ? INFINITE : 0); - if (res == WAIT_TIMEOUT) { - if (i) { - // A failed nonblocking rw locking. Undo changes to semaphore. - if (ReleaseSemaphore(m_semaphore_hnd, i, NULL) == 0) { - qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - // Fall through - } - } - if (gotMutex) - ReleaseMutex(m_mutex_hnd); - return false; - } - if (res != WAIT_OBJECT_0) { - if (gotMutex) - ReleaseMutex(m_mutex_hnd); - qWarning("QtLockedFile::lock(): WaitForSingleObject (semaphore): %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - return false; - } - } - - m_lock_mode = mode; - if (gotMutex) - ReleaseMutex(m_mutex_hnd); - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - int increment; - if (m_lock_mode == ReadLock) - increment = 1; - else - increment = SEMAPHORE_MAX; - - DWORD ret = ReleaseSemaphore(m_semaphore_hnd, increment, 0); - if (ret == 0) { - qWarning("QtLockedFile::unlock(): ReleaseSemaphore: %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - return false; - } - - m_lock_mode = QtLockedFile::NoLock; - remove(); - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (m_mutex_hnd != 0) { - DWORD ret = CloseHandle(m_mutex_hnd); - if (ret == 0) { - qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (mutex): %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - } - m_mutex_hnd = 0; - } - if (m_semaphore_hnd != 0) { - DWORD ret = CloseHandle(m_semaphore_hnd); - if (ret == 0) { - qWarning("QtLockedFile::~QtLockedFile(): CloseHandle (semaphore): %s", - errorCodeToString(GetLastError()).toLatin1().constData()); - } - m_semaphore_hnd = 0; - } -} - -} // namespace SharedTools diff --git a/src/shared/qtsingleapplication/CMakeLists.txt b/src/shared/qtsingleapplication/CMakeLists.txt index fae6a085454..5b8e9c02713 100644 --- a/src/shared/qtsingleapplication/CMakeLists.txt +++ b/src/shared/qtsingleapplication/CMakeLists.txt @@ -2,7 +2,7 @@ add_library(shared_qtsingleapplication STATIC qtsingleapplication.cpp qtsingleapplication.h qtlocalpeer.cpp qtlocalpeer.h ) -target_link_libraries(shared_qtsingleapplication shared_qtlockedfile Qt::Core Qt::Network Qt::Widgets) +target_link_libraries(shared_qtsingleapplication Qt::Core Qt::Network Qt::Widgets) target_include_directories(shared_qtsingleapplication PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") if (WIN32) target_compile_definitions(shared_qtsingleapplication PRIVATE "QT_QTSINGLEAPPLICATION_EXPORT=__declspec(dllexport)") diff --git a/src/shared/qtsingleapplication/qtlocalpeer.cpp b/src/shared/qtsingleapplication/qtlocalpeer.cpp index af72ece772a..47dd3805bad 100644 --- a/src/shared/qtsingleapplication/qtlocalpeer.cpp +++ b/src/shared/qtsingleapplication/qtlocalpeer.cpp @@ -59,16 +59,15 @@ QtLocalPeer::QtLocalPeer(QObject *parent, const QString &appId) QString lockName = QDir(QDir::tempPath()).absolutePath() + QLatin1Char('/') + socketName + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); + lockFile.reset(new QLockFile(lockName)); } bool QtLocalPeer::isClient() { - if (lockFile.isLocked()) + if (lockFile->isLocked()) return false; - if (!lockFile.lock(QtLockedFile::WriteLock, false)) + if (!lockFile->tryLock()) return true; if (!QLocalServer::removeServer(socketName)) diff --git a/src/shared/qtsingleapplication/qtlocalpeer.h b/src/shared/qtsingleapplication/qtlocalpeer.h index 67a0d42e2c9..27578a7acac 100644 --- a/src/shared/qtsingleapplication/qtlocalpeer.h +++ b/src/shared/qtsingleapplication/qtlocalpeer.h @@ -3,11 +3,12 @@ #pragma once -#include <qtlockedfile.h> +#include <QDir> #include <QLocalServer> #include <QLocalSocket> -#include <QDir> +#include <QLockFile> +#include <QScopedPointer> namespace SharedTools { @@ -31,8 +32,8 @@ protected: QString id; QString socketName; - QLocalServer* server; - QtLockedFile lockFile; + QLocalServer* server{nullptr}; + QScopedPointer<QLockFile> lockFile; }; } // namespace SharedTools diff --git a/src/shared/qtsingleapplication/qtsingleapplication.cpp b/src/shared/qtsingleapplication/qtsingleapplication.cpp index 0f8fa8b6d13..c38d568004c 100644 --- a/src/shared/qtsingleapplication/qtsingleapplication.cpp +++ b/src/shared/qtsingleapplication/qtsingleapplication.cpp @@ -4,10 +4,10 @@ #include "qtsingleapplication.h" #include "qtlocalpeer.h" -#include <qtlockedfile.h> - #include <QDir> #include <QFileOpenEvent> +#include <QLockFile> +#include <QPointer> #include <QSharedMemory> #include <QWidget> @@ -50,11 +50,10 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char * } } - // QtLockedFile is used to workaround QTBUG-10364 - QtLockedFile lockfile(instancesLockFilename(appSessionId)); + // QLockFile is used to workaround QTBUG-10364 + QLockFile lockfile(instancesLockFilename(appSessionId)); - lockfile.open(QtLockedFile::ReadWrite); - lockfile.lock(QtLockedFile::WriteLock); + lockfile.lock(); qint64 *pids = static_cast<qint64 *>(instances->data()); if (!created) { // Find the first instance that it still running @@ -79,9 +78,8 @@ QtSingleApplication::~QtSingleApplication() if (!instances) return; const qint64 appPid = QCoreApplication::applicationPid(); - QtLockedFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId))); - lockfile.open(QtLockedFile::ReadWrite); - lockfile.lock(QtLockedFile::WriteLock); + QLockFile lockfile(instancesLockFilename(QtLocalPeer::appSessionId(appId))); + lockfile.lock(); // Rewrite array, removing current pid and previously crashed ones qint64 *pids = static_cast<qint64 *>(instances->data()); qint64 *newpids = pids; diff --git a/src/src.qbs b/src/src.qbs index b59d8843236..fb7532b03c9 100644 --- a/src/src.qbs +++ b/src/src.qbs @@ -8,12 +8,12 @@ Project { references: [ "app/app.qbs", "app/app_version_header.qbs", + "app/images/logo/logo.qbs", "libs/libs.qbs", "plugins/plugins.qbs", "tools/tools.qbs", project.sharedSourcesDir + "/json", project.sharedSourcesDir + "/proparser", - project.sharedSourcesDir + "/pch_files.qbs", ] property bool qbsSubModuleExists: File.exists(qbsProject.qbsBaseDir + "/qbs.qbs") diff --git a/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp index 5fe89323a90..e38889f5ea5 100644 --- a/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp +++ b/src/tools/cplusplus-ast2png/cplusplus-ast2png.cpp @@ -554,7 +554,7 @@ int main(int argc, char *argv[]) // Process files const QStringList files = args; for (const QString &fileName : files) { - if (! QFile::exists(fileName)) { + if (! QFileInfo::exists(fileName)) { std::cerr << "Error: File \"" << qPrintable(fileName) << "\" does not exist." << std::endl; exit(EXIT_FAILURE); diff --git a/src/tools/cplusplus-shared/utils.cpp b/src/tools/cplusplus-shared/utils.cpp index 562cd96d65b..b7feb7023c8 100644 --- a/src/tools/cplusplus-shared/utils.cpp +++ b/src/tools/cplusplus-shared/utils.cpp @@ -76,7 +76,7 @@ SystemPreprocessor::SystemPreprocessor(bool verbose) void SystemPreprocessor::check() const { QTextStream out(stderr); - if (!QFile::exists(QLatin1String(PATH_PREPROCESSOR_CONFIG))) { + if (!QFileInfo::exists(QLatin1String(PATH_PREPROCESSOR_CONFIG))) { out << QString::fromLatin1("Error: File \"%1\" does not exist.") .arg(QLatin1String(PATH_PREPROCESSOR_CONFIG)) << Qt::endl; @@ -94,7 +94,7 @@ void SystemPreprocessor::check() const void SystemPreprocessor::preprocessFile(const QString &inputFile, const QString &outputFile) const { check(); - if (!QFile::exists(inputFile)) { + if (!QFileInfo::exists(inputFile)) { QTextStream out(stderr); out << QString::fromLatin1("Error: File \"%1\" does not exist.").arg(inputFile) << Qt::endl; exit(EXIT_FAILURE); diff --git a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp index 9661081091b..f202b8b8946 100644 --- a/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp +++ b/src/tools/cplusplus-update-frontend/cplusplus-update-frontend.cpp @@ -126,7 +126,7 @@ static ASTNodes astNodes; static QTextCursor createCursor(TranslationUnit *unit, AST *ast, QTextDocument *document) { int startLine, startColumn, endLine, endColumn; - unit->getTokenStartPosition(ast->firstToken(), &startLine, &startColumn); + unit->getTokenPosition(ast->firstToken(), &startLine, &startColumn); unit->getTokenEndPosition(ast->lastToken() - 1, &endLine, &endColumn); QTextCursor tc(document); @@ -1058,7 +1058,7 @@ void generateAST_cpp(const Snapshot &snapshot, const QDir &cplusplusDir) QTextCursor cursor(&cpp_document); int line = 0, column = 0; - AST_cpp_document->translationUnit()->getTokenStartPosition(funDef->firstToken(), &line, &column); + AST_cpp_document->translationUnit()->getTokenPosition(funDef->firstToken(), &line, &column); const int start = cpp_document.findBlockByNumber(line - 1).position() + column - 1; cursor.setPosition(start); int doxyStart = start; @@ -1606,7 +1606,7 @@ int main(int argc, char *argv[]) } QDir cplusplusDir(pathCppFrontend); - if (!QFile::exists(pathCppFrontend)) { + if (!QFileInfo::exists(pathCppFrontend)) { std::cerr << "Error: Directory \"" << qPrintable(cplusplusDir.absolutePath()) << "\" does not exist." << std::endl; return EXIT_FAILURE; @@ -1616,7 +1616,7 @@ int main(int argc, char *argv[]) << "\"." << std::endl; return EXIT_FAILURE; } - if (!QFile::exists(pathDumpersFile)) { + if (!QFileInfo::exists(pathDumpersFile)) { std::cerr << "Error: File \"" << qPrintable(pathDumpersFile) << "\" does not exist." << std::endl; return EXIT_FAILURE; diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 13a4fe5b918..60accd46bb6 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -1200,6 +1200,27 @@ y="0" x="0" /> </g> + <use + x="0" + y="0" + xlink:href="#src/libs/utils/images/progressindicator_big" + id="src/libs/solutions/spinner/icons/spinner_large" + transform="translate(97,-29)" + style="display:inline" /> + <use + x="0" + y="0" + xlink:href="#src/libs/utils/images/progressindicator_medium" + id="src/libs/solutions/spinner/icons/spinner_medium" + transform="translate(98,-61)" + style="display:inline" /> + <use + x="0" + y="0" + xlink:href="#src/libs/utils/images/progressindicator_small" + id="src/libs/solutions/spinner/icons/spinner_small" + transform="translate(243,-177)" + style="display:inline" /> <g id="src/plugins/coreplugin/find/images/wrapindicator"> <rect @@ -3145,15 +3166,18 @@ height="100%" transform="translate(1090,64)" style="display:inline;fill:none" /> - <path - id="path4544-1" - style="fill:#000000" - d="m 1078,478.85 -8.5,16.15 h 17 z m 0.7724,8.61732 0.4208,4.69491 -4.1948,-1.62916 z" /> - <path - id="path6034" - style="fill:none;stroke:#ffffff" - d="m 1078,478.85469 1.189,13.30208 m -4.1861,-1.62746 L 1086.5,495 m -17,0 9.272,-7.52758" - sodipodi:nodetypes="cccccc" /> + <g + id="CMakeLogo"> + <path + id="path4544-1" + style="fill:#000000" + d="m 1078,478.85 -8.5,16.15 h 17 z m 0.7724,8.61732 0.4208,4.69491 -4.1948,-1.62916 z" /> + <path + id="path6034" + style="fill:none;stroke:#ffffff;stroke-width:1.2" + d="m 1078,478.85469 1.189,13.30208 m -4.1861,-1.62746 L 1086.5,495 m -17,0 9.272,-7.52758" + sodipodi:nodetypes="cccccc" /> + </g> </g> <g id="src/plugins/terminal/images/settingscategory_terminal"> @@ -3640,6 +3664,83 @@ inkscape:connector-curvature="0" sodipodi:nodetypes="cczczcc" /> </g> + <g + id="src/plugins/projectexplorer/images/cmakeicon" + transform="translate(862,88.5)" + style="display:inline"> + <use + style="display:inline" + transform="translate(1137,43.5)" + height="100%" + width="100%" + id="use1952-7-6" + xlink:href="#backgroundRect" + y="0" + x="0" /> + <use + x="0" + y="0" + xlink:href="#CMakeLogo" + id="use2008" + style="display:inline" + transform="matrix(0.89482687,0,0,0.89482687,164.39232,51.559627)" /> + </g> + <g + id="src/plugins/vcpkg/images/vcpkgicon"> + <use + style="display:inline" + transform="translate(2016,132)" + height="100%" + width="100%" + id="use1952-7-6-1" + xlink:href="#backgroundRect" + y="0" + x="0" /> + <g + id="g6088"> + <path + style="fill:#4d4d4d" + d="m 2003.1348,577.71875 c 1.2848,0.24885 2.6819,-1.48291 2.4355,-3.76367 -0.7916,-0.0764 -1.3258,0.0765 -1.7637,0.36133 -0.6852,0.44589 -1.1449,1.30413 -1.7226,2.23047 0.1588,0.64898 0.5375,1.07247 1.0508,1.17187 z" + id="path2102" + sodipodi:nodetypes="scscs" /> + <path + style="fill:#000000" + d="m 2006.0376,573.24147 c 1.8615,0.51984 1.6429,0.7072 2.8995,1.08292 1.2566,0.37573 2.3556,-0.12796 2.7719,-1.74627 0.3958,-1.53873 -1.3327,-2.57841 -3.4317,-2.56695 -2.0989,0.0114 -4.5685,1.07289 -5.6543,3.36578 -0.3182,0.67202 -0.5017,1.29842 -0.5781,1.85743 0.9246,-1.47431 2.1312,-2.51275 3.9927,-1.99291 z" + id="path5378" + sodipodi:nodetypes="zzssscz" /> + </g> + <use + x="0" + y="0" + xlink:href="#g6088" + id="use6090" + transform="rotate(180,2008,576)" /> + </g> + <g + id="src/libs/solutions/terminal/images/passwordlock" + transform="translate(-5,4)"> + <use + x="0" + y="0" + xlink:href="#transparentBackgroundRect" + id="use1" + transform="matrix(2.5,0,0,4,586,-1405)" /> + <path + style="opacity:0.75;fill:#ffffff" + d="m 582,423 v -5 c 0,-4 -4,-15 -16,-15 -12,-0.0312 -16,11 -16,15 0,4 0,5 0,5 h -4 v 36 h 40 v -36 z" + id="path167" + sodipodi:nodetypes="czzzcccccc" /> + <path + style="display:inline;fill:#000000;fill-opacity:1;stroke-width:4" + inkscape:connector-curvature="0" + d="m 566,407 c -6.624,0 -12,5.372 -12,12 v 12 h 4 v -12 c 0,-4.412 3.584,-8 8,-8 4.412,0 8,3.588 8,8 v 12 h 4 v -12 c 0,-6.628 -5.372,-12 -12,-12 z" + id="lockbow-1" /> + <path + style="display:inline;fill:#000000;fill-opacity:1" + inkscape:connector-curvature="0" + d="m 550,427 v 28 h 32 v -28 z m 18,15.388 V 447 h -4 v -4.612 c -1.172,-0.7 -2,-1.92 -2,-3.388 0,-2.212 1.792,-4 4,-4 2.208,0 4,1.788 4,4 0,1.464 -0.828,2.688 -2,3.388 z" + id="lockbody-1" /> + </g> </g> <g inkscape:groupmode="layer" @@ -4525,7 +4626,7 @@ sodipodi:nodetypes="cc" /> </g> <g - id="src/plugins/projectexplorer/images/debugger_overlay_small" + id="src/libs/utils/images/debugger_overlay_small" transform="translate(16)"> <rect style="fill:#ffffff;fill-opacity:1;stroke:none" @@ -5262,7 +5363,7 @@ <g style="display:inline" transform="translate(144)" - id="src/plugins/projectexplorer/images/continue_1_small"> + id="src/libs/utils/images/continue_1_small"> <rect style="fill:#ffffff;fill-opacity:1;stroke:none" x="265" @@ -5282,7 +5383,7 @@ <g style="display:inline" transform="translate(160)" - id="src/plugins/projectexplorer/images/continue_2_small"> + id="src/libs/utils/images/continue_2_small"> <rect style="fill:#ffffff;fill-opacity:1;stroke:none" x="265" diff --git a/src/tools/iostool/iostool.qbs b/src/tools/iostool/iostool.qbs index d45172e23f2..0da4b451c7c 100644 --- a/src/tools/iostool/iostool.qbs +++ b/src/tools/iostool/iostool.qbs @@ -8,7 +8,6 @@ QtcTool { Depends { name: "Qt.widgets" } Depends { name: "Qt.xml" } Depends { name: "Qt.network" } - Depends { name: "app_version_header" } files: [ "cfutils.h", diff --git a/src/tools/perfparser b/src/tools/perfparser index 5444f962076..ca9077a4ae9 160000 --- a/src/tools/perfparser +++ b/src/tools/perfparser @@ -1 +1 @@ -Subproject commit 5444f96207616f922f3093e9d64bd6000f168c51 +Subproject commit ca9077a4ae9d301383fc2f3021eadfede5d48804 diff --git a/src/tools/processlauncher/processlauncher.qbs b/src/tools/processlauncher/processlauncher.qbs index 2f03f7fba57..750276396c2 100644 --- a/src/tools/processlauncher/processlauncher.qbs +++ b/src/tools/processlauncher/processlauncher.qbs @@ -11,9 +11,7 @@ QtcTool { Properties { condition: qbs.targetOS.contains("windows") - cpp.dynamicLibraries: { - return qbs.toolchainType === "msvc" ? ["user32", "dbghelp"] : ["user32"]; - } + cpp.dynamicLibraries: qbs.toolchain.contains("msvc") ? ["user32", "dbghelp"] : ["user32"] } files: [ diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt index 2ffebbc59db..9244a9454db 100644 --- a/src/tools/qml2puppet/CMakeLists.txt +++ b/src/tools/qml2puppet/CMakeLists.txt @@ -16,6 +16,7 @@ if (NOT QT_CREATOR_API_DEFINED) set(DESTINATION DESTINATION .) include(QtCreatorIDEBranding) include(QtCreatorAPI) + qtc_handle_compiler_cache_support() find_package(Qt6 COMPONENTS Concurrent Core Gui Network PrintSupport Qml Quick Sql Widgets Xml diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp index 4af31d072f0..616b904e9e3 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/gridgeometry.cpp @@ -80,8 +80,6 @@ void GridGeometry::doUpdateGeometry() setVertexData(vertexData); - int lastIndex = (vertexData.size() - 1) / int(sizeof(QVector3D)); - // Set bounds based on main grid size instead of actual mesh size to make all parts of the // grid have consistent bounds. const float extent = float(m_lines) * m_step; diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 77de3a5661b..e66a55e82f2 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -704,12 +704,12 @@ QString ObjectNodeInstance::instanceType(const PropertyName &name) const QList<ServerNodeInstance> ObjectNodeInstance::childItems() const { - return QList<ServerNodeInstance>(); + return {}; } QList<QQuickItem *> ObjectNodeInstance::allItemsRecursive() const { - return QList<QQuickItem *>(); + return {}; } QList<ServerNodeInstance> ObjectNodeInstance::stateInstances() const @@ -724,7 +724,7 @@ QList<ServerNodeInstance> ObjectNodeInstance::stateInstances() const return instanceList; } - return QList<ServerNodeInstance>(); + return {}; } void ObjectNodeInstance::setNodeSource(const QString & /*source*/) diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index 785357fe624..2f905d21cf7 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -185,7 +185,7 @@ const QList<QQuickItem*> Qt5NodeInstanceServer::allItems() const if (rootNodeInstance().isValid()) return rootNodeInstance().allItemsRecursive(); - return QList<QQuickItem*>(); + return {}; } bool Qt5NodeInstanceServer::rootIsRenderable3DObject() const diff --git a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp index 40aa4585971..86dce8cb90d 100644 --- a/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp +++ b/src/tools/qml2puppet/qml2puppet/qmlpuppet.cpp @@ -105,14 +105,14 @@ void QmlPuppet::initQmlRunner() if (m_argParser.isSet("readcapturedstream") && m_coreApp->arguments().count() > 2) { QString fileName = m_argParser.value("readcapturedstream"); - if (!QFile::exists(fileName)) { + if (!QFileInfo::exists(fileName)) { qDebug() << "Input stream does not exist:" << fileName; exit(-1); } if (m_coreApp->arguments().count() > 3) { fileName = m_coreApp->arguments().at(3); - if (!QFile::exists(fileName)) { + if (!QFileInfo::exists(fileName)) { qDebug() << "Output stream does not exist:" << fileName; exit(-1); } diff --git a/src/tools/qtc-askpass/qtc-askpass-main.cpp b/src/tools/qtc-askpass/qtc-askpass-main.cpp index 5a527e18db1..d45fe564fb7 100644 --- a/src/tools/qtc-askpass/qtc-askpass-main.cpp +++ b/src/tools/qtc-askpass/qtc-askpass-main.cpp @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); - QTimer::singleShot(0, [] { + QTimer::singleShot(0, &app, [] { QInputDialog dlg; const QStringList appArgs = qApp->arguments(); QString labelText = QCoreApplication::translate("qtc-askpass", @@ -18,8 +18,10 @@ int main(int argc, char *argv[]) labelText.append('\n').append(appArgs.at(1)); dlg.setLabelText(labelText); dlg.setTextEchoMode(QLineEdit::Password); - if (dlg.exec() == QDialog::Accepted) + const bool accepted = dlg.exec() == QDialog::Accepted; + if (accepted) std::cout << qPrintable(dlg.textValue()) << std::endl; + qApp->exit(accepted ? 0 : 1); }); return app.exec(); } diff --git a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp index c8853f53459..77084cab5ce 100644 --- a/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp +++ b/src/tools/qtcreatorcrashhandler/crashhandlerdialog.cpp @@ -10,6 +10,7 @@ #include <utils/checkablemessagebox.h> #include <utils/layoutbuilder.h> +#include <utils/qtcsettings.h> #include <utils/stringutils.h> #include <QApplication> @@ -205,10 +206,10 @@ CrashHandlerDialog::~CrashHandlerDialog() bool CrashHandlerDialog::runDebuggerWhileBacktraceNotFinished() { // Check settings. - QSettings settings(QSettings::IniFormat, - QSettings::UserScope, - QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), - QLatin1String(SettingsApplication)); + Utils::QtcSettings settings(QSettings::IniFormat, + QSettings::UserScope, + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), + QLatin1String(SettingsApplication)); Utils::CheckableMessageBox::initialize(&settings); // Ask user. @@ -224,7 +225,7 @@ bool CrashHandlerDialog::runDebuggerWhileBacktraceNotFinished() = Utils::CheckableMessageBox::question(this, title, message, - QString(SettingsKeySkipWarningAbortingBacktrace), + Utils::Key(SettingsKeySkipWarningAbortingBacktrace), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt index 937aa04087a..19b8571f562 100644 --- a/src/tools/sdktool/CMakeLists.txt +++ b/src/tools/sdktool/CMakeLists.txt @@ -16,6 +16,7 @@ if (NOT QT_CREATOR_API_DEFINED) set(DESTINATION DESTINATION .) include(QtCreatorIDEBranding) include(QtCreatorAPI) + qtc_handle_compiler_cache_support() find_package(QT NAMES Qt6 Qt5 COMPONENTS Core diff --git a/src/tools/sdktool/adddeviceoperation.cpp b/src/tools/sdktool/adddeviceoperation.cpp index 938ea13cc6d..3c439e3e536 100644 --- a/src/tools/sdktool/adddeviceoperation.cpp +++ b/src/tools/sdktool/adddeviceoperation.cpp @@ -58,6 +58,7 @@ QString AddDeviceOperation::argumentsHelpText() const " --dockerRepo <STRING> Docker image repo.\n" " --dockerTag <STRING> Docker image tag.\n" " --dockerMappedPaths <STRING> Docker mapped paths (semi-colon separated).\n" + " --dockerClangdExecutable <STRING> Path to clangd inside the docker.\n" " <KEY> <TYPE:VALUE> extra key value pairs\n"); } @@ -219,6 +220,14 @@ bool AddDeviceOperation::setArguments(const QStringList &args) continue; } + if (current == QLatin1String("--dockerClangdExecutable")) { + if (next.isNull()) + return false; + ++i; // skip next; + m_clangdExecutable = next; + continue; + } + if (current == QLatin1String("--dockerRepo")) { if (next.isNull()) return false; @@ -292,14 +301,14 @@ void AddDeviceOperation::unittest() devData.m_dockerMappedPaths = QStringList{"/opt", "/data"}; devData.m_dockerRepo = "repo"; devData.m_dockerTag = "tag"; - + devData.m_clangdExecutable = "clangdexe"; QVariantMap result = devData.addDevice(map); QVariantMap data = result.value(QLatin1String(DEVICEMANAGER_ID)).toMap(); QVariantList devList = data.value(QLatin1String(DEVICE_LIST_ID)).toList(); QCOMPARE(devList.count(), 1); QVariantMap dev = devList.at(0).toMap(); - QCOMPARE(dev.count(), 20); + QCOMPARE(dev.count(), 21); QCOMPARE(dev.value(QLatin1String("Authentication")).toInt(), 2); QCOMPARE(dev.value(QLatin1String("DebugServerKey")).toString(), QLatin1String("debugServer")); QCOMPARE(dev.value(QLatin1String("FreePortsSpec")).toString(), QLatin1String("ports")); @@ -317,6 +326,7 @@ void AddDeviceOperation::unittest() QCOMPARE(dev.value(QLatin1String("Version")).toInt(), 6); QCOMPARE(dev.value(QLatin1String("DockerDeviceDataRepo")).toString(), "repo"); QCOMPARE(dev.value(QLatin1String("DockerDeviceDataTag")).toString(), "tag"); + QCOMPARE(dev.value(QLatin1String("DockerDeviceClangDExecutable")).toString(), "clangdexe"); const QStringList paths = dev.value(QLatin1String("DockerDeviceMappedPaths")).toStringList(); QCOMPARE(paths, QStringList({"/opt", "/data"})); @@ -353,6 +363,7 @@ QVariantMap AddDeviceData::addDevice(const QVariantMap &map) const dev.append(KeyValuePair(QLatin1String("Uname"), QVariant(m_uname))); dev.append(KeyValuePair(QLatin1String("Version"), QVariant(m_version))); dev.append(KeyValuePair(QLatin1String("DockerDeviceMappedPaths"), QVariant(m_dockerMappedPaths))); + dev.append(KeyValuePair(QLatin1String("DockerDeviceClangDExecutable"), QVariant(m_clangdExecutable))); dev.append(KeyValuePair(QLatin1String("DockerDeviceDataRepo"), QVariant(m_dockerRepo))); dev.append(KeyValuePair(QLatin1String("DockerDeviceDataTag"), QVariant(m_dockerTag))); dev.append(m_extra); diff --git a/src/tools/sdktool/adddeviceoperation.h b/src/tools/sdktool/adddeviceoperation.h index d7ae62d235c..8ddbf0069d2 100644 --- a/src/tools/sdktool/adddeviceoperation.h +++ b/src/tools/sdktool/adddeviceoperation.h @@ -43,6 +43,7 @@ public: QStringList m_dockerMappedPaths; QString m_dockerRepo; QString m_dockerTag; + QString m_clangdExecutable; KeyValuePairList m_extra; }; diff --git a/src/tools/sdktool/sdkpersistentsettings.cpp b/src/tools/sdktool/sdkpersistentsettings.cpp index b72fffa4c57..43b92d7d470 100644 --- a/src/tools/sdktool/sdkpersistentsettings.cpp +++ b/src/tools/sdktool/sdkpersistentsettings.cpp @@ -173,7 +173,7 @@ bool SdkSaveFile::commit() if (!result) { DWORD replaceErrorCode = GetLastError(); QString errorStr; - if (!QFile::exists(finalFileName)) { + if (!QFileInfo::exists(finalFileName)) { // Replace failed because finalFileName does not exist, try rename. if (!(result = rename(finalFileName))) errorStr = errorString(); @@ -210,7 +210,7 @@ bool SdkSaveFile::commit() // Back up current file. // If it's opened by another application, the lock follows the move. - if (QFile::exists(finalFileName)) { + if (QFileInfo::exists(finalFileName)) { // Kill old backup. Might be useful if creator crashed before removing backup. QFile::remove(backupName); QFile finalFile(finalFileName); diff --git a/src/tools/sdktool/sdktool.qbs b/src/tools/sdktool/sdktool.qbs index a1da4f5df3e..268b6db40c2 100644 --- a/src/tools/sdktool/sdktool.qbs +++ b/src/tools/sdktool/sdktool.qbs @@ -1,11 +1,8 @@ -import qbs 1.0 - QtcTool { name: "sdktool" Depends { name: "Qt.core" } Depends { name: "app_version_header" } - Depends { name: "Qt.testlib"; condition: project.withAutotests } Depends { name: "sdktoolLib" } cpp.defines: base.concat([ diff --git a/src/tools/sdktool/sdktoollib.qbs b/src/tools/sdktool/sdktoollib.qbs index ca40590d8d1..d179a2eaa87 100644 --- a/src/tools/sdktool/sdktoollib.qbs +++ b/src/tools/sdktool/sdktoollib.qbs @@ -1,5 +1,3 @@ -import qbs 1.0 - QtcLibrary { name: "sdktoolLib" @@ -7,7 +5,7 @@ QtcLibrary { Depends { name: "Qt.core" } Depends { name: "app_version_header" } - Depends { name: "Qt.testlib"; condition: project.withAutotests } + Depends { name: "Qt.testlib"; condition: qtc.withPluginTests } property string libsDir: path + "/../../libs" @@ -19,7 +17,7 @@ QtcLibrary { ? 'DATA_PATH="."' : qbs.targetOS.contains("windows") ? 'DATA_PATH="../share/qtcreator"' : 'DATA_PATH="../../share/qtcreator"'); - if (project.withAutotests) + if (qtc.withPluginTests) defines.push("WITH_TESTS"); return defines; } @@ -27,7 +25,7 @@ QtcLibrary { var libs = []; if (qbs.targetOS.contains("windows")) libs.push("user32", "shell32"); - if (qbs.toolchainType === "msvc") + if (qbs.toolchain.contains("msvc")) libs.push("dbghelp"); return libs; } diff --git a/src/tools/wininterrupt/CMakeLists.txt b/src/tools/wininterrupt/CMakeLists.txt index 4a41fa7b08f..ee40ff769b8 100644 --- a/src/tools/wininterrupt/CMakeLists.txt +++ b/src/tools/wininterrupt/CMakeLists.txt @@ -12,6 +12,7 @@ if (NOT QT_CREATOR_API_DEFINED) # standalone build include(QtCreatorIDEBranding) include(QtCreatorAPI) + qtc_handle_compiler_cache_support() endif() if (NOT WIN32) @@ -23,6 +24,10 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(Arch 64) endif() +if (MSVC_CXX_ARCHITECTURE_ID MATCHES "^ARM") + set(Arch "arm${Arch}") +endif() + add_qtc_executable(win${Arch}interrupt COMPONENT wininterrupt SOURCES wininterrupt.c diff --git a/src/tools/wininterrupt/wininterrupt.c b/src/tools/wininterrupt/wininterrupt.c index 8053d6db3cc..47559a30fa7 100644 --- a/src/tools/wininterrupt/wininterrupt.c +++ b/src/tools/wininterrupt/wininterrupt.c @@ -11,6 +11,7 @@ #include <windows.h> #include <stdio.h> +#include <stdlib.h> /* To debug break a 64bit application under Windows, you must call * DebugBreakProcess() from an 64bit apllication. Therefore: diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 5dcd026a7af..7b53a20004d 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -13,11 +13,9 @@ add_subdirectory(filesearch) add_subdirectory(haskell) add_subdirectory(json) add_subdirectory(languageserverprotocol) -add_subdirectory(mapreduce) add_subdirectory(pointeralgorithm) add_subdirectory(profilewriter) add_subdirectory(qml) -add_subdirectory(runextensions) add_subdirectory(sdktool) add_subdirectory(solutions) add_subdirectory(texteditor) diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs index 603b5cd1b15..4df8e3118f3 100644 --- a/tests/auto/auto.qbs +++ b/tests/auto/auto.qbs @@ -2,7 +2,6 @@ import qbs Project { name: "QtcAutotests" - condition: project.withAutotests references: [ "aggregation/aggregation.qbs", "algorithm/algorithm.qbs", @@ -20,7 +19,6 @@ Project { "languageserverprotocol/languageserverprotocol.qbs", "profilewriter/profilewriter.qbs", "qml/qml.qbs", - "runextensions/runextensions.qbs", "sdktool/sdktool.qbs", "solutions/solutions.qbs", "texteditor/texteditor.qbs", diff --git a/tests/auto/cplusplus/CMakeLists.txt b/tests/auto/cplusplus/CMakeLists.txt index 1c85a988cc9..ab83eb9b542 100644 --- a/tests/auto/cplusplus/CMakeLists.txt +++ b/tests/auto/cplusplus/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(checksymbols) add_subdirectory(codeformatter) add_subdirectory(cppselectionchanger) add_subdirectory(cxx11) +add_subdirectory(declarationcomments) add_subdirectory(fileiterationorder) add_subdirectory(findusages) add_subdirectory(lexer) diff --git a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp index e73468db799..0e0705c5e56 100644 --- a/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp +++ b/tests/auto/cplusplus/checksymbols/tst_checksymbols.cpp @@ -144,7 +144,7 @@ public: if (result.column == column) return result; } - return Use(); + return {}; } CheckSymbols::Future future; diff --git a/tests/auto/cplusplus/cplusplus.qbs b/tests/auto/cplusplus/cplusplus.qbs index e50b6881ef5..44b6bde7fae 100644 --- a/tests/auto/cplusplus/cplusplus.qbs +++ b/tests/auto/cplusplus/cplusplus.qbs @@ -9,6 +9,7 @@ Project { "codeformatter/codeformatter.qbs", "cppselectionchanger/cppselectionchanger.qbs", "cxx11/cxx11.qbs", + "declarationcomments/declarationcomments.qbs", "fileiterationorder/fileiterationorder.qbs", "findusages/findusages.qbs", "lexer/lexer.qbs", diff --git a/tests/auto/cplusplus/declarationcomments/CMakeLists.txt b/tests/auto/cplusplus/declarationcomments/CMakeLists.txt new file mode 100644 index 00000000000..bb36a8e306d --- /dev/null +++ b/tests/auto/cplusplus/declarationcomments/CMakeLists.txt @@ -0,0 +1,4 @@ +add_qtc_test(tst_cplusplus_declarationcomments + DEPENDS CppEditor Utils + SOURCES tst_declarationcomments.cpp +) diff --git a/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs b/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs new file mode 100644 index 00000000000..601eb30ec0b --- /dev/null +++ b/tests/auto/cplusplus/declarationcomments/declarationcomments.qbs @@ -0,0 +1,6 @@ +import "../cplusplusautotest.qbs" as CPlusPlusAutotest + +CPlusPlusAutotest { + name: "declaration comments autotest" + files: "tst_declarationcomments.cpp" +} diff --git a/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp b/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp new file mode 100644 index 00000000000..27caff6c451 --- /dev/null +++ b/tests/auto/cplusplus/declarationcomments/tst_declarationcomments.cpp @@ -0,0 +1,225 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "cplusplus/Overview.h" +#include <cplusplus/declarationcomments.h> +#include <cplusplus/CppDocument.h> +#include <cplusplus/SymbolVisitor.h> + +#include <QtTest> +#include <QTextDocument> + +using namespace CPlusPlus; +using namespace Utils; + +const char testSource[] = R"TheFile( +//! Unrelated comment, even though it mentions MyEnum. + +//! Related comment because of proximity. +//! Related comment that mentions MyEnum. +//! Related comment that mentions MyEnumA. + +enum MyEnum { MyEnumA, MyEnumB }; + +// Unrelated comment because of different comment type. +//! Related comment that mentions MyEnumClass::A. + +enum class MyEnumClass { A, B }; + +// Related comment because of proximity. +void myFunc(); + +/* + * Related comment because of proximity. + */ +template<typename T> class MyClassTemplate {}; + +/* + * Related comment, because it mentions MyOtherClass. + */ + +class MyOtherClass { + /// Related comment for function and parameter a2, but not parameter a. + /// @param a2 + void memberFunc(int a, int a2); +}; + +//! Comment for member function at implementation site. +void MyOtherClass::memberFunc(int, int) {} + +//! An unrelated comment + +void funcWithoutComment(); + +)TheFile"; + +class SymbolFinder : public SymbolVisitor +{ +public: + SymbolFinder(const QString &name, Document *doc, int expectedOccurrence) + : m_name(name), m_doc(doc), m_expectedOccurrence(expectedOccurrence) {} + const Symbol *find(); + +private: + bool preVisit(Symbol *) override { return !m_symbol; } + bool visit(Namespace *ns) override { return visitScope(ns); } + bool visit(Enum *e) override { return visitScope(e); } + bool visit(Class *c) override { return visitScope(c); } + bool visit(Function *f) override { return visitScope(f); } + bool visit(Argument *a) override; + bool visit(Declaration *d) override; + + bool visitScope(Scope *scope); + + const QString &m_name; + Document * const m_doc; + const Symbol *m_symbol = nullptr; + const int m_expectedOccurrence; + int m_tokenLocation = -1; +}; + +class TestDeclarationComments : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + + void commentsForDecl_data(); + void commentsForDecl(); + +private: + Snapshot m_snapshot; + Document::Ptr m_cppDoc; + QTextDocument m_textDoc; +}; + +void TestDeclarationComments::initTestCase() +{ + m_cppDoc = m_snapshot.preprocessedDocument(testSource, "dummy.cpp"); + m_cppDoc->check(); + const bool hasErrors = !m_cppDoc->diagnosticMessages().isEmpty(); + if (hasErrors) { + for (const Document::DiagnosticMessage &msg : m_cppDoc->diagnosticMessages()) + qDebug() << '\t' << msg.text(); + } + QVERIFY(!hasErrors); + m_textDoc.setPlainText(testSource); +} + +void TestDeclarationComments::commentsForDecl_data() +{ + QTest::addColumn<QString>("symbolName"); + QTest::addColumn<QString>("expectedCommentPrefix"); + QTest::addColumn<int>("occurrence"); + + QTest::newRow("enum type") << "MyEnum" << "//! Related comment because of proximity" << 1; + QTest::newRow("enum value (positive)") << "MyEnumA" + << "//! Related comment because of proximity" << 1; + QTest::newRow("enum value (negative") << "MyEnumB" << QString() << 1; + + QTest::newRow("enum class type") << "MyEnumClass" + << "//! Related comment that mentions MyEnumClass::A" << 1; + QTest::newRow("enum class value (positive)") + << "A" << "//! Related comment that mentions MyEnumClass::A" << 1; + QTest::newRow("enum class value (negative") << "B" << QString() << 1; + + QTest::newRow("function declaration with comment") + << "myFunc" << "// Related comment because of proximity" << 1; + + QTest::newRow("class template") + << "MyClassTemplate" << "/*\n * Related comment because of proximity." << 1; + + QTest::newRow("class") + << "MyOtherClass" << "/*\n * Related comment, because it mentions MyOtherClass." << 1; + QTest::newRow("member function declaration") + << "memberFunc" << "/// Related comment for function and parameter a2, but not parameter a." + << 1; + QTest::newRow("function parameter (negative)") << "a" << QString() << 1; + QTest::newRow("function parameter (positive)") << "a2" << "/// Related comment for function " + "and parameter a2, but not parameter a." << 1; + + QTest::newRow("member function definition") << "memberFunc" << + "//! Comment for member function at implementation site." << 2; + + QTest::newRow("function declaration without comment") << "funcWithoutComment" << QString() << 1; +} + +void TestDeclarationComments::commentsForDecl() +{ + QFETCH(QString, symbolName); + QFETCH(QString, expectedCommentPrefix); + QFETCH(int, occurrence); + + SymbolFinder finder(symbolName, m_cppDoc.get(), occurrence); + const Symbol * const symbol = finder.find(); + QVERIFY(symbol); + + const QList<Token> commentTokens = commentsForDeclaration(symbol, m_textDoc, m_cppDoc); + if (expectedCommentPrefix.isEmpty()) { + QVERIFY(commentTokens.isEmpty()); + return; + } + QVERIFY(!commentTokens.isEmpty()); + + const int firstCommentPos = m_cppDoc->translationUnit()->getTokenPositionInDocument( + commentTokens.first(), &m_textDoc); + const QString actualCommentPrefix = m_textDoc.toPlainText().mid(firstCommentPos, + expectedCommentPrefix.length()); + QCOMPARE(actualCommentPrefix, expectedCommentPrefix); +} + + +const Symbol *SymbolFinder::find() +{ + for (int i = 0, occurrences = 0; i < m_doc->translationUnit()->tokenCount(); ++i) { + const Token &tok = m_doc->translationUnit()->tokenAt(i); + if (tok.kind() != T_IDENTIFIER) + continue; + if (tok.spell() != m_name) + continue; + if (++occurrences == m_expectedOccurrence) { + m_tokenLocation = i; + break; + } + } + if (m_tokenLocation == -1) + return nullptr; + + visit(m_doc->globalNamespace()); + return m_symbol; +} + +bool SymbolFinder::visit(Argument *a) +{ + if (a->sourceLocation() == m_tokenLocation) + m_symbol = a; + return false; +} + +bool SymbolFinder::visit(Declaration *d) +{ + if (d->sourceLocation() == m_tokenLocation) { + m_symbol = d; + return false; + } + if (const auto func = d->type().type()->asFunctionType()) + return visit(func); + return true; +} + +bool SymbolFinder::visitScope(Scope *scope) +{ + for (int i = 0; i < scope->memberCount(); ++i) { + Symbol * const s = scope->memberAt(i); + if (s->sourceLocation() == m_tokenLocation) { + m_symbol = s; + return false; + } + accept(s); + } + return false; +} + +QTEST_APPLESS_MAIN(TestDeclarationComments) +#include "tst_declarationcomments.moc" diff --git a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp index d9fce5e2f23..54ceed08ca0 100644 --- a/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp +++ b/tests/auto/cplusplus/preprocessor/tst_preprocessor.cpp @@ -21,7 +21,7 @@ QByteArray loadSource(const QString &fileName) QFile inf(QLatin1String(SRCDIR) + QLatin1Char('/') + fileName); if (!inf.open(QIODevice::ReadOnly | QIODevice::Text)) { qDebug("Cannot open \"%s\"", fileName.toUtf8().constData()); - return QByteArray(); + return {}; } QTextStream ins(&inf); @@ -194,11 +194,7 @@ public: else dir = QFileInfo(currentFileName).dir(); const QFileInfo inc(dir, includedFileName); - if (inc.exists()) { - return inc.filePath(); - } else { - return QString(); - } + return inc.exists() ? inc.filePath() : QString(); } QString resolveGlobally(const QString ¤tFileName) const @@ -208,8 +204,7 @@ public: if (f.exists()) return f.filePath(); } - - return QString(); + return {}; } void setIncludePaths(const QStringList &includePaths) diff --git a/tests/auto/debugger/CMakeLists.txt b/tests/auto/debugger/CMakeLists.txt index 4abe98c05e8..2f06a6b2fc7 100644 --- a/tests/auto/debugger/CMakeLists.txt +++ b/tests/auto/debugger/CMakeLists.txt @@ -24,6 +24,7 @@ if (NOT QT_CREATOR_API_DEFINED) include(QtCreatorIDEBranding) include(QtCreatorAPI) + qtc_handle_compiler_cache_support() set(WITH_TESTS ON) diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index a1df2ae70c5..ac175bffd29 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -1203,6 +1203,7 @@ void tst_Dumpers::initTestCase() QProcess debugger; debugger.start(m_debuggerBinary, {"-i", "mi", "-quiet", "-nx"}); bool ok = debugger.waitForStarted(); + QVERIFY(ok); debugger.write("set confirm off\npython print 43\nshow version\nquit\n"); ok = debugger.waitForFinished(); QVERIFY(ok); @@ -1251,7 +1252,7 @@ void tst_Dumpers::initTestCase() QString cdbextPath = qEnvironmentVariable("QTC_CDBEXT_PATH"); if (cdbextPath.isEmpty()) cdbextPath = QString(CDBEXT_PATH "\\qtcreatorcdbext64"); - QVERIFY(QFile::exists(cdbextPath + "\\qtcreatorcdbext.dll")); + QVERIFY(QFileInfo::exists(cdbextPath + "\\qtcreatorcdbext.dll")); env.set("_NT_DEBUGGER_EXTENSION_PATH", cdbextPath); env.prependOrSetPath(Utils::FilePath::fromString(m_qmakeBinary).parentDir()); m_makeBinary = env.searchInPath("nmake.exe").toString(); @@ -2259,7 +2260,10 @@ void tst_Dumpers::dumper_data() QTest::newRow("QDateTime") - << Data("#include <QDateTime>", + << Data("#include <QDateTime>\n" + "#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n" + "#include <QTimeZone>\n" + "#endif", "QDate d0;\n" "QDate d1;\n" @@ -2269,9 +2273,12 @@ void tst_Dumpers::dumper_data() "QTime t1(13, 15, 32);\n" "QDateTime dt0;\n" - "QDateTime dt1(QDate(1980, 1, 1), QTime(13, 15, 32), Qt::UTC);", + "QDateTime dt1(QDate(1980, 1, 1), QTime(13, 15, 32), Qt::UTC);\n" + "#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)\n" + "QDateTime dt2(QDate(1980, 1, 1), QTime(13, 15, 32), QTimeZone(60 * 60));\n" + "#endif\n", - "&d0, &d1, &t0, &t1, &dt0, &dt1") + "&d0, &d1, &t0, &t1, &dt0, &dt1, &dt2") + CoreProfile() @@ -2296,6 +2303,7 @@ void tst_Dumpers::dumper_data() + Check("dt0", "(invalid)", "@QDateTime") + Check("dt1", Value4("Tue Jan 1 13:15:32 1980"), "@QDateTime") + Check("dt1", Value5("Tue Jan 1 13:15:32 1980 GMT"), "@QDateTime") + + Check("dt1", Value6("Tue Jan 1 13:15:32 1980 GMT"), "@QDateTime") + Check("dt1.(ISO)", "\"1980-01-01T13:15:32Z\"", "@QString") % NeedsInferiorCall + Check("dt1.(Locale)", AnyValue, "@QString") % NeedsInferiorCall @@ -2305,11 +2313,13 @@ void tst_Dumpers::dumper_data() + Check("dt1.toString", Value4("\"Tue Jan 1 13:15:32 1980\""), "@QString") % NeedsInferiorCall + Check("dt1.toString", - Value5("\"Tue Jan 1 13:15:32 1980 GMT\""), "@QString") % NeedsInferiorCall; + Value5("\"Tue Jan 1 13:15:32 1980 GMT\""), "@QString") % NeedsInferiorCall //+ Check("dt1.toUTC", // Value4("Tue Jan 1 13:15:32 1980"), "@QDateTime") % Optional() //+ Check("dt1.toUTC", // Value5("Tue Jan 1 13:15:32 1980 GMT"), "@QDateTime") % Optional(); + + Check("dt2", Value5("Tue Jan 1 13:15:32 1980 UTC+01:00"), "@QDateTime") + + Check("dt2", Value6("Tue Jan 1 13:15:32 1980 UTC+01:00"), "@QDateTime"); QTest::newRow("QFileInfo") @@ -5014,16 +5024,18 @@ void tst_Dumpers::dumper_data() "#include <string>\n" + fooData + "static Foo *alloc_foo() { return new Foo; }\n" - "static void free_foo(Foo *f) { delete f; }\n", + "static void free_foo(Foo *f) { delete f; }\n" + "class Bar : public Foo { public: int bar = 42;};\n", "std::unique_ptr<int> p0;\n\n" "std::unique_ptr<int> p1(new int(32));\n\n" "std::unique_ptr<Foo> p2(new Foo);\n\n" "std::unique_ptr<std::string> p3(new std::string(\"ABC\"));\n" - "std::unique_ptr<Foo, void(*)(Foo*)> p4{alloc_foo(), free_foo};", + "std::unique_ptr<Foo, void(*)(Foo*)> p4{alloc_foo(), free_foo};\n" + "std::unique_ptr<Foo> p5(new Bar);", - "&p0, &p1, &p2, &p3, &p4") + "&p0, &p1, &p2, &p3, &p4, &p5") + CoreProfile() + Cxx11Profile() @@ -5033,7 +5045,8 @@ void tst_Dumpers::dumper_data() + Check("p1", "32", "std::unique_ptr<int, std::default_delete<int> >") + Check("p2", Pointer(), "std::unique_ptr<Foo, std::default_delete<Foo> >") + Check("p3", "\"ABC\"", "std::unique_ptr<std::string, std::default_delete<std::string> >") - + Check("p4.b", "2", "int"); + + Check("p4.b", "2", "int") + + Check("p5.bar", "42", "int"); QTest::newRow("StdOnce") diff --git a/tests/auto/examples/tst_examples.cpp b/tests/auto/examples/tst_examples.cpp index d6639663adf..5d30c8710b5 100644 --- a/tests/auto/examples/tst_examples.cpp +++ b/tests/auto/examples/tst_examples.cpp @@ -90,9 +90,11 @@ void tst_Examples::parsing_data() QTest::addColumn<QStringList>("platforms"); QTest::addColumn<MetaData>("metaData"); QTest::addColumn<QStringList>("categories"); + QTest::addColumn<QStringList>("categoryOrder"); QTest::addRow("example") << QByteArray(R"raw( + <instructionals module="Qt"> <examples> <example docUrl="qthelp://org.qt-project.qtwidgets.660/qtwidgets/qtwidgets-widgets-analogclock-example.html" imageUrl="qthelp://org.qt-project.qtwidgets.660/qtwidgets/images/analogclock-example.png" @@ -110,6 +112,13 @@ void tst_Examples::parsing_data() </meta> </example> </examples> + <categories> + <category>Application Examples</category> + <category>Desktop</category> + <category>Mobile</category> + <category>Embedded</category> + </categories> + </instructionals> )raw") << /*isExamples=*/true << "Analog Clock" << "The Analog Clock example shows how to draw the contents of a custom widget." @@ -126,20 +135,53 @@ void tst_Examples::parsing_data() << FilePaths() << Example << true << false << false << "" << "" << QStringList() << MetaData({{"category", {"Graphics", "Graphics", "Foobar"}}, {"tags", {"widgets"}}}) - << QStringList{"Foobar", "Graphics"}; + << QStringList{"Foobar", "Graphics"} + << QStringList{"Application Examples", "Desktop", "Mobile", "Embedded"}; QTest::addRow("no category, highlighted") << QByteArray(R"raw( + <instructionals module="Qt"> <examples> <example name="No Category, highlighted" isHighlighted="true"> </example> </examples> + </instructionals> )raw") << /*isExamples=*/true << "No Category, highlighted" << QString() << QString() << QStringList() << FilePath("examples") << QString() << FilePaths() << FilePath() << FilePaths() << Example << /*hasSourceCode=*/false << false << /*isHighlighted=*/true << "" - << "" << QStringList() << MetaData() << QStringList{"Featured"}; + << "" << QStringList() << MetaData() << QStringList{"Featured"} << QStringList(); + + QTest::addRow("tutorial with category") + << QByteArray(R"raw( + <instructionals module="Qt"> + <categories> + <category>Help</category> + <category>Learning</category> + <category>Online</category> + <category>Talk</category> + </categories> + <tutorials> + <tutorial imageUrl=":qtsupport/images/icons/tutorialicon.png" difficulty="" docUrl="qthelp://org.qt-project.qtcreator/doc/dummytutorial.html" projectPath="" name="A tutorial"> + <description><![CDATA[A dummy tutorial.]]></description> + <tags>qt creator,build,compile,help</tags> + <meta> + <entry name="category">Help</entry> + </meta> + </tutorial> + </tutorials> + </instructionals> +)raw") << /*isExamples=*/false + << "A tutorial" + << "A dummy tutorial." + << ":qtsupport/images/icons/tutorialicon.png" + << QStringList{"qt creator", "build", "compile", "help"} << FilePath() + << "qthelp://org.qt-project.qtcreator/doc/dummytutorial.html" << FilePaths() << FilePath() + << FilePaths() << Tutorial << /*hasSourceCode=*/false << /*isVideo=*/false + << /*isHighlighted=*/false << QString() << QString() << QStringList() + << MetaData({{"category", {"Help"}}}) << QStringList("Help") + << QStringList{"Help", "Learning", "Online", "Talk"}; } void tst_Examples::parsing() @@ -147,16 +189,18 @@ void tst_Examples::parsing() QFETCH(QByteArray, data); QFETCH(bool, isExamples); QFETCH(QStringList, categories); + QFETCH(QStringList, categoryOrder); const ExampleItem expected = fetchItem(); - const expected_str<QList<ExampleItem *>> result - = parseExamples(data, - FilePath("manifest/examples-manifest.xml"), - FilePath("examples"), - FilePath("demos"), - isExamples); + const expected_str<ParsedExamples> result = parseExamples(data, + FilePath( + "manifest/examples-manifest.xml"), + FilePath("examples"), + FilePath("demos"), + isExamples); QVERIFY(result); - QCOMPARE(result->size(), 1); - const ExampleItem item = *result->at(0); + QCOMPARE(result->categoryOrder, categoryOrder); + QCOMPARE(result->items.size(), 1); + const ExampleItem item = *result->items.at(0); QCOMPARE(item.name, expected.name); QCOMPARE(item.description, expected.description); QCOMPARE(item.imageUrl, expected.imageUrl); @@ -175,8 +219,8 @@ void tst_Examples::parsing() QCOMPARE(item.platforms, expected.platforms); QCOMPARE(item.metaData, expected.metaData); - const QList<std::pair<Section, QList<ExampleItem *>>> resultCategories = getCategories(*result, - true); + const QList<std::pair<Section, QList<ExampleItem *>>> resultCategories + = getCategories(result->items, true, {}, true); QCOMPARE(resultCategories.size(), categories.size()); for (int i = 0; i < resultCategories.size(); ++i) { QCOMPARE(resultCategories.at(i).first.name, categories.at(i)); diff --git a/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp b/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp index f3618841c8d..762142b95ee 100644 --- a/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp +++ b/tests/auto/extensionsystem/pluginspec/tst_pluginspec.cpp @@ -20,20 +20,20 @@ static QJsonObject metaData(const QString &fileName) QFile f(fileName); if (!f.open(QIODevice::ReadOnly)) { qWarning() << "Could not open" << fileName; - return QJsonObject(); + return {}; } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(f.readAll(), &error); if (error.error != QJsonParseError::NoError) { qWarning() << "Could not parse" << fileName << ":" << error.errorString(); - return QJsonObject(); + return {}; } return doc.object(); } static QString libraryName(const QString &basename) { -#if defined(Q_OS_OSX) +#if defined(Q_OS_MACOS) return QLatin1String("lib") + basename + QLatin1String("_debug.dylib"); #elif defined(Q_OS_UNIX) return QLatin1String("lib") + basename + QLatin1String(".so"); diff --git a/tests/auto/filesearch/tst_filesearch.cpp b/tests/auto/filesearch/tst_filesearch.cpp index 95bec53e099..b2c09064c92 100644 --- a/tests/auto/filesearch/tst_filesearch.cpp +++ b/tests/auto/filesearch/tst_filesearch.cpp @@ -11,12 +11,6 @@ using namespace Utils; class tst_FileSearch : public QObject { Q_OBJECT -public: - enum RegExpFlag { - NoRegExp, - RegExp - }; - private slots: void multipleResults(); void caseSensitive(); @@ -41,16 +35,12 @@ SearchResultItem searchResult(const FilePath &fileName, const QString &matchingL } void test_helper(const FilePath &filePath, const SearchResultItems &expectedResults, - const QString &term, QTextDocument::FindFlags flags = {}, - tst_FileSearch::RegExpFlag regexp = tst_FileSearch::NoRegExp) + const QString &term, Utils::FindFlags flags = {}) { - FileIterator *it = new FileListIterator({filePath}, {QTextCodec::codecForLocale()}); + const FileListContainer container({filePath}, {QTextCodec::codecForLocale()}); QFutureWatcher<SearchResultItems> watcher; QSignalSpy ready(&watcher, &QFutureWatcherBase::resultsReadyAt); - if (regexp == tst_FileSearch::NoRegExp) - watcher.setFuture(Utils::findInFiles(term, it, flags)); - else - watcher.setFuture(Utils::findInFilesRegExp(term, it, flags)); + watcher.setFuture(Utils::findInFiles(term, container, flags, {})); watcher.future().waitForFinished(); QTest::qWait(100); // process events QCOMPARE(ready.count(), 1); @@ -84,7 +74,7 @@ void tst_FileSearch::multipleResults() expectedResults << searchResult(m_filePath, "aaaaaaaa this line has 2 results for four a in a row", 5, 4, 4, {"aaaa"}); - test_helper(m_filePath, expectedResults, "aaaa", {}, RegExp); + test_helper(m_filePath, expectedResults, "aaaa", FindRegularExpression); } void tst_FileSearch::caseSensitive() @@ -92,7 +82,7 @@ void tst_FileSearch::caseSensitive() SearchResultItems expectedResults; expectedResults << searchResult(m_filePath, "search CaseSensitively for casesensitive", 3, 7, 13); - test_helper(m_filePath, expectedResults, "CaseSensitive", QTextDocument::FindCaseSensitively); + test_helper(m_filePath, expectedResults, "CaseSensitive", FindCaseSensitively); } void tst_FileSearch::caseInSensitive() diff --git a/tests/auto/mapreduce/CMakeLists.txt b/tests/auto/mapreduce/CMakeLists.txt deleted file mode 100644 index 5a57f3c6761..00000000000 --- a/tests/auto/mapreduce/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_qtc_test(tst_mapreduce - DEPENDS Utils - SOURCES tst_mapreduce.cpp -) diff --git a/tests/auto/mapreduce/mapreduce.qbs b/tests/auto/mapreduce/mapreduce.qbs deleted file mode 100644 index 63502117b02..00000000000 --- a/tests/auto/mapreduce/mapreduce.qbs +++ /dev/null @@ -1,10 +0,0 @@ -import qbs - -QtcAutotest { - name: "Map reduce autotest" - Depends { name: "Utils" } - - files: [ - "tst_mapreduce.cpp", - ] -} diff --git a/tests/auto/mapreduce/tst_mapreduce.cpp b/tests/auto/mapreduce/tst_mapreduce.cpp deleted file mode 100644 index df1f8e1986b..00000000000 --- a/tests/auto/mapreduce/tst_mapreduce.cpp +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <utils/algorithm.h> -#include <utils/mapreduce.h> - -#include <QThreadPool> -#include <QtTest> - -#if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 // MSVC2015 -#define SUPPORTS_MOVE -#endif - -class tst_MapReduce : public QObject -{ - Q_OBJECT - -private slots: - void mapReduce(); - void mapReduceRvalueContainer(); - void map(); - void orderedMapReduce(); -#ifdef SUPPORTS_MOVE - void moveOnlyType(); -#endif -}; - -static int returnxx(int x) -{ - return x*x; -} - -static void returnxxThroughFutureInterface(QFutureInterface<int> &fi, int x) -{ - fi.reportResult(x*x); -} - -void tst_MapReduce::mapReduce() -{ - QThreadPool pool; - const auto initWithFutureInterface = [](QFutureInterface<double> &fi) -> double { - fi.reportResult(0.); - return 0.; - }; - const auto reduceWithFutureInterface = [](QFutureInterface<double> &fi, double &state, int value) { - state += value; - fi.reportResult(value); - }; - const auto reduceWithReturn = [](double &state, int value) -> double { - state += value; - return value; - }; - const auto cleanupWithFutureInterface = [](QFutureInterface<double> &fi, double &state) { - state /= 2.; - fi.reportResult(state); - }; - - { - // map without future interface - QList<double> results = Utils::mapReduce(QVector<int>({1, 2, 3, 4, 5}), - initWithFutureInterface, - returnxx, - reduceWithFutureInterface, - cleanupWithFutureInterface) - .results(); - Utils::sort(results); // mapping order is undefined - QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // map with future interface - QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}), - initWithFutureInterface, returnxxThroughFutureInterface, - reduceWithFutureInterface, cleanupWithFutureInterface) - .results(); - Utils::sort(results); // mapping order is undefined - QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // reduce without future interface - QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}), - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface) - .results(); - Utils::sort(results); // mapping order is undefined - QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // reduce with threadpool - QList<double> results = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}), - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface, - Utils::MapReduceOption::Unordered, &pool) - .results(); - Utils::sort(results); // mapping order is undefined - QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // lvalue ref container - QList<int> container({1, 2, 3, 4, 5}); - QList<double> results = Utils::mapReduce(container, - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface) - .results(); - Utils::sort(results); // mapping order is undefined - QCOMPARE(results, QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // std::cref - QList<int> container({1, 2, 3, 4, 5}); - QCOMPARE(Utils::mapReduce(std::cref(container), - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface, - Utils::MapReduceOption::Ordered).results(), - QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // std::cref with threadpool - QList<int> container({1, 2, 3, 4, 5}); - QCOMPARE(Utils::mapReduce(std::cref(container), - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface, - Utils::MapReduceOption::Ordered, &pool).results(), - QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // std::ref - QList<int> container({1, 2, 3, 4, 5}); - QCOMPARE(Utils::mapReduce(std::ref(container), - initWithFutureInterface, returnxx, - reduceWithReturn, cleanupWithFutureInterface, - Utils::MapReduceOption::Ordered).results(), - QList<double>({0., 1., 4., 9., 16., 25., 27.5})); - } - { - // init and cleanup without future interface - QCOMPARE(Utils::mapReduce(QList<int>({1, 2, 3}), - []() { return 0.; }, - [](int v) { return v*2; }, - [](double &state, int v) { return state += v/4.; }, - [](double &) { }, - Utils::MapReduceOption::Ordered).results(), - QList<double>({.5, 1.5, 3.})); - } - { - // simplified map reduce without init and cleanup - QCOMPARE(Utils::mapReduce(QList<QString>({QLatin1String("blubb"), QLatin1String("foo"), QLatin1String("blah")}), - [](const QString &val) { return val.size(); }, - 90., - [](double &state, int val) { - state /= double(val); - }, - Utils::MapReduceOption::Ordered).result(), - 1.5); - } - { - // simplified map reduce without init and cleanup with threadpool - QCOMPARE(Utils::mapReduce(QList<QString>({QLatin1String("blubb"), QLatin1String("foo"), QLatin1String("blah")}), - [](const QString &val) { return val.size(); }, - 90., - [](double &state, int val) { - state /= double(val); - }, - Utils::MapReduceOption::Ordered, &pool).result(), - 1.5); - } - { - // simplified map reduce - // std::cref - QList<int> container({1, 2, 3}); - QCOMPARE(Utils::mapReduce(std::cref(container), [](int val) { return 2*val; }, 10, - [](int &state, int val) { state += val; }).result(), - 22); - } - { - // simplified map reduce - // std::cref with threadpool - QList<int> container({1, 2, 3}); - QCOMPARE(Utils::mapReduce(std::cref(container), [](int val) { return 2*val; }, 10, - [](int &state, int val) { state += val; }, - Utils::MapReduceOption::Unordered, &pool).result(), - 22); - } - { - // simplified map reduce - // std::ref - QList<int> container({1, 2, 3}); - QCOMPARE(Utils::mapReduce(std::ref(container), [](int &val) { return 2*val; }, 10, - [](int &state, int val) { state += val; }).result(), - 22); - } - { - // blocking mapReduce = mappedReduced - QCOMPARE(Utils::mappedReduced(QList<int>({1, 2, 3}), [](int &val) { return 2*val; }, 10, - [](int &state, int val) { state += val; }), - 22); - } - { - // blocking mapReduce = mappedReduced - // with threadpool - QCOMPARE(Utils::mappedReduced(QList<int>({1, 2, 3}), [](int &val) { return 2*val; }, 10, - [](int &state, int val) { state += val; }, - Utils::MapReduceOption::Unordered, &pool), - 22); - } -} - -void tst_MapReduce::mapReduceRvalueContainer() -{ - { - QFuture<int> future = Utils::mapReduce(QList<int>({1, 2, 3, 4, 5}), - []() { return 0; }, - [](int value) { return value; }, - [](QFutureInterface<int> &, int &state, int value) { state += value; }, - [](QFutureInterface<int> &fi, int &state) { fi.reportResult(state); }); - // here, lifetime of the QList temporary ends - QCOMPARE(future.results(), QList<int>({15})); - } -} - -void tst_MapReduce::map() -{ - QCOMPARE(Utils::map(QList<int>({2, 5, 1}), [](int x) { return x*2.5; }).results(), - QList<double>({5., 12.5, 2.5})); - { - // void result - QList<int> results; - QMutex mutex; - Utils::map( - // container - QList<int>({2, 5, 1}), - // map - [&mutex, &results](int x) { QMutexLocker l(&mutex); results.append(x); } - ).waitForFinished(); - // Utils::map is "ordered" by default, but that means that result reporting is ordered, - // the map function is still called out-of-order - Utils::sort(results); - QCOMPARE(results, QList<int>({1, 2, 5})); - } - { - // inplace editing - QList<int> container({2, 5, 1}); - Utils::map(std::ref(container), [](int &x) { x *= 2; }).waitForFinished(); - QCOMPARE(container, QList<int>({4, 10, 2})); - - Utils::map(container.begin(), container.end(), [](int &x) { x *= 2; }, - Utils::MapReduceOption::Unordered, - nullptr, QThread::InheritPriority, 3).waitForFinished(); - QCOMPARE(container, QList<int>({8, 20, 4})); - } - - // blocking map = mapped - { - const QSet<qsizetype> sizes = Utils::mapped<QSet>( - QStringList({QLatin1String("foo"), QLatin1String("bar"), QLatin1String("blah")}), - [](const QString &s) { return s.size(); }); - QList<qsizetype> vals = sizes.values(); - Utils::sort(vals); - QCOMPARE(vals, QList<qsizetype>({3, 4})); - } - { - const QStringList list({QLatin1String("foo"), QLatin1String("bar"), QLatin1String("blah")}); - const QSet<qsizetype> sizes = Utils::mapped<QSet>(list.cbegin(), - list.cend(), - [](const QString &s) { - return s.size(); - }); - QList<qsizetype> vals = sizes.values(); - Utils::sort(vals); - QCOMPARE(vals, QList<qsizetype>({3, 4})); - } -} - -void tst_MapReduce::orderedMapReduce() -{ - QCOMPARE(Utils::mapReduce(QList<int>({1, 2, 3, 4}), - []() { return 0; }, - [](int i) { return i*2; }, - [](int &state, int val) { state += val; return state; }, - [](int &) { }, - Utils::MapReduceOption::Ordered).results(), - QList<int>({2, 6, 12, 20})); -} - -#ifdef SUPPORTS_MOVE - -class MoveOnlyType -{ -public: - MoveOnlyType() noexcept {} // <- with GCC 5 the defaulted one is noexcept(false) - MoveOnlyType(const MoveOnlyType &) = delete; - MoveOnlyType(MoveOnlyType &&) = default; - MoveOnlyType &operator=(const MoveOnlyType &) = delete; - MoveOnlyType &operator=(MoveOnlyType &&) = default; -}; - -class MoveOnlyState : public MoveOnlyType -{ -public: - int count = 0; -}; - -class MoveOnlyInit : public MoveOnlyType -{ -public: - MoveOnlyState operator()(QFutureInterface<int> &) const { return MoveOnlyState(); } -}; - -class MoveOnlyMap : public MoveOnlyType -{ -public: - int operator()(const MoveOnlyType &) const { return 1; } -}; - -class MoveOnlyReduce : public MoveOnlyType -{ -public: - void operator()(QFutureInterface<int> &, MoveOnlyState &state, int) { ++state.count; } -}; - -class MoveOnlyList : public std::vector<MoveOnlyType> -{ -public: - MoveOnlyList() { emplace_back(MoveOnlyType()); emplace_back(MoveOnlyType()); } - MoveOnlyList(const MoveOnlyList &) = delete; - MoveOnlyList(MoveOnlyList &&) = default; - MoveOnlyList &operator=(const MoveOnlyList &) = delete; - MoveOnlyList &operator=(MoveOnlyList &&) = default; -}; - -void tst_MapReduce::moveOnlyType() -{ - QCOMPARE(Utils::mapReduce(MoveOnlyList(), - MoveOnlyInit(), - MoveOnlyMap(), - MoveOnlyReduce(), - [](QFutureInterface<int> &fi, MoveOnlyState &state) { fi.reportResult(state.count); } - ).results(), - QList<int>({2})); -} - -#endif - -QTEST_GUILESS_MAIN(tst_MapReduce) - -#include "tst_mapreduce.moc" diff --git a/tests/auto/qml/codemodel/check/equality-checks.qml b/tests/auto/qml/codemodel/check/equality-checks.qml index 9355142df40..e24e8afad99 100644 --- a/tests/auto/qml/codemodel/check/equality-checks.qml +++ b/tests/auto/qml/codemodel/check/equality-checks.qml @@ -12,42 +12,30 @@ Rectangle { if (s == s) {} if (s == n) {} // 126 15 16 - if (s == N) {} // 325 15 16 - if (s == u) {} // 325 15 16 if (s == b) {} // 126 15 16 if (s == o) {} // 126 15 16 if (s == k) {} // 126 15 16 if (n == s) {} // 126 15 16 if (n == n) {} - if (n == N) {} // 325 15 16 - if (n == u) {} // 325 15 16 if (n == b) {} // 126 15 16 if (n == o) {} // 126 15 16 if (n == k) {} // 126 15 16 - if (N == s) {} // 325 15 16 - if (N == n) {} // 325 15 16 if (N == N) {} if (N == u) {} - if (N == b) {} // 325 15 16 if (N == o) {} if (N == k) {} // 126 15 16 - if (u == s) {} // 325 15 16 - if (u == n) {} // 325 15 16 if (u == N) {} if (u == u) {} - if (u == b) {} // 325 15 16 if (u == o) {} if (u == k) {} // 126 15 16 if (b == s) {} // 126 15 16 if (b == n) {} // 126 15 16 - if (b == N) {} // 325 15 16 - if (b == u) {} // 325 15 16 if (b == b) {} if (b == o) {} // 126 15 16 if (b == k) {} // 126 15 16 @@ -69,35 +57,19 @@ Rectangle { if (k == k) {} // 126 15 16 if (s === s) {} - if (s === n) {} // 325 15 17 - if (s === N) {} // 325 15 17 if (s === u) {} - if (s === b) {} // 325 15 17 - if (s === o) {} // 325 15 17 if (s === k) {} if (s !== s) {} - if (s !== n) {} // 325 15 17 - if (s !== N) {} // 325 15 17 if (s !== u) {} - if (s !== b) {} // 325 15 17 - if (s !== o) {} // 325 15 17 if (s !== k) {} - if (n === s) {} // 325 15 17 if (n === n) {} - if (n === N) {} // 325 15 17 if (n === u) {} - if (n === b) {} // 325 15 17 - if (n === o) {} // 325 15 17 if (n === k) {} - if (N === s) {} // 325 15 17 - if (N === n) {} // 325 15 17 if (N === N) {} if (N === u) {} - if (N === b) {} // 325 15 17 - if (N === o) {} // 325 15 17 if (N === k) {} if (u === s) {} @@ -108,21 +80,11 @@ Rectangle { if (u === o) {} if (u === k) {} - if (b === s) {} // 325 15 17 - if (b === n) {} // 325 15 17 - - if (b === N) {} // 325 15 17 if (b === u) {} if (b === b) {} - if (b === o) {} // 325 15 17 if (b === k) {} - if (o === s) {} // 325 15 17 - if (o === n) {} // 325 15 17 - if (o === N) {} // 325 15 17 if (o === u) {} - if (o === b) {} // 325 15 17 - if (o === o) {} // 325 15 17 if (o === k) {} if (k === s) {} diff --git a/tests/auto/qml/codemodel/check/tst_check.cpp b/tests/auto/qml/codemodel/check/tst_check.cpp index befb363ae4c..611f2cefd94 100644 --- a/tests/auto/qml/codemodel/check/tst_check.cpp +++ b/tests/auto/qml/codemodel/check/tst_check.cpp @@ -64,7 +64,8 @@ void tst_Check::initTestCase() // the resource path is wrong, have to load things manually QFileInfo builtins(resourcePath() + "/qml-type-descriptions/builtins.qmltypes"); QStringList errors, warnings; - CppQmlTypesLoader::defaultQtObjects = CppQmlTypesLoader::loadQmlTypes(QFileInfoList() << builtins, &errors, &warnings); + CppQmlTypesLoader::defaultQtObjects() + = CppQmlTypesLoader::loadQmlTypes(QFileInfoList() << builtins, &errors, &warnings); if (!ModelManagerInterface::instance()) new ModelManagerInterface; diff --git a/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp b/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp index 003630ab0bf..ac0611aa85c 100644 --- a/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp +++ b/tests/auto/qml/codemodel/ecmascript7/tst_ecmascript7.cpp @@ -71,7 +71,7 @@ static QStringList readSkipList(const QDir &dir, const QString &filename) QStringList result; if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) - return QStringList(); + return {}; while (!f.atEnd()) { const QString s = QString::fromUtf8(f.readLine().trimmed()); diff --git a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp index 8e1cd0ed6cb..06b8f142653 100644 --- a/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp +++ b/tests/auto/qml/connectioneditor/tst_connectioneditor.cpp @@ -4,15 +4,14 @@ #include "qmldesigner/components/connectioneditor/connectioneditorevaluator.h" #include "qmljs/parser/qmljsast_p.h" #include "qmljs/qmljsdocument.h" -#include <algorithm> + #include <QApplication> -#include <QFileInfo> #include <QGraphicsObject> -#include <QLatin1String> #include <QScopedPointer> -#include <QSettings> #include <QtTest> +#include <algorithm> + using namespace QmlJS; using namespace QmlJS::AST; using namespace QmlDesigner; diff --git a/tests/auto/qml/qml.qbs b/tests/auto/qml/qml.qbs index 76659a5256b..46b3557c68a 100644 --- a/tests/auto/qml/qml.qbs +++ b/tests/auto/qml/qml.qbs @@ -7,7 +7,6 @@ Project { // "qmldesigner/qmldesigner.qbs", "qmleditor/qmleditor.qbs", "qmljssimplereader/qmljssimplereader.qbs", - "qmlprojectmanager/qmlprojectmanager.qbs", "qrcparser/qrcparser.qbs", "reformatter/reformatter.qbs", "persistenttrie/persistenttrie.qbs" diff --git a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt index 66ac125a450..7cb456d03fc 100644 --- a/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt +++ b/tests/auto/qml/qmldesigner/coretests/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_qml_testcore + NEEDS_GUI EXCLUDE_FROM_PRECHECK CONDITION TARGET QmlProjectManager DEPENDS diff --git a/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp b/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp index 0f708da693e..4dc37a3b691 100644 --- a/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp +++ b/tests/auto/qml/qmldesigner/coretests/testrewriterview.cpp @@ -57,8 +57,7 @@ VariantProperty TestModelToTextMerger::findAddedVariantProperty(const VariantPro return property.toVariantProperty(); } } - - return VariantProperty(); + return {}; } TestRewriterView::TestRewriterView(ExternalDependenciesInterface &externalDependencies, diff --git a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp index 8d80f7e547a..92ff4f9b2b2 100644 --- a/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp +++ b/tests/auto/qml/qmldesigner/coretests/tst_testcore.cpp @@ -3,13 +3,6 @@ #include "tst_testcore.h" -#include <QScopedPointer> -#include <QLatin1String> -#include <QGraphicsObject> -#include <QQueue> -#include <QTest> -#include <QVariant> - #include <designersettings.h> #include <externaldependenciesinterface.h> #include <invalididexception.h> @@ -21,7 +14,6 @@ #include <nodeinstanceview.h> #include <rewritingexception.h> #include <stylesheetmerger.h> -#include <QDebug> #include <qmlanchors.h> #include <qmlmodelnodefacade.h> @@ -43,13 +35,21 @@ #include <bytearraymodifier.h> #include "testrewriterview.h" -#include <utils/fileutils.h> #include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljssimplereader.h> #include <extensionsystem/pluginmanager.h> +#include <utils/fileutils.h> +#include <utils/qtcsettings.h> + +#include <QDebug> +#include <QGraphicsObject> #include <QPlainTextEdit> +#include <QQueue> +#include <QScopedPointer> +#include <QTest> +#include <QVariant> //TESTED_COMPONENT=src/plugins/qmldesigner/designercore @@ -167,7 +167,7 @@ public: Utils::FilePath resourcePath(const QString &) const override { return {}; } public: - QSettings qsettings; + Utils::QtcSettings qsettings; QmlDesigner::DesignerSettings settings{&qsettings}; Model *model; }; @@ -268,7 +268,8 @@ void tst_TestCore::initTestCase() QFileInfo builtins(IDE_DATA_PATH "/qml-type-descriptions/builtins.qmltypes"); QStringList errors, warnings; - QmlJS::CppQmlTypesLoader::defaultQtObjects = QmlJS::CppQmlTypesLoader::loadQmlTypes(QFileInfoList{builtins}, &errors, &warnings); + QmlJS::CppQmlTypesLoader::defaultQtObjects() + = QmlJS::CppQmlTypesLoader::loadQmlTypes(QFileInfoList{builtins}, &errors, &warnings); } void tst_TestCore::cleanupTestCase() diff --git a/tests/auto/qml/reformatter/tst_reformatter.cpp b/tests/auto/qml/reformatter/tst_reformatter.cpp index 68b192fa600..36c8f2a83e8 100644 --- a/tests/auto/qml/reformatter/tst_reformatter.cpp +++ b/tests/auto/qml/reformatter/tst_reformatter.cpp @@ -1,21 +1,19 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <QScopedPointer> -#include <QLatin1String> -#include <QGraphicsObject> -#include <QApplication> -#include <QSettings> -#include <QFileInfo> - #include <qmljs/qmljsdocument.h> #include <qmljs/qmljsmodelmanagerinterface.h> #include <qmljs/qmljsreformatter.h> #include <qmljs/parser/qmljsast_p.h> #include <qmljs/parser/qmljsengine_p.h> + #include <utils/filepath.h> +#include <QApplication> +#include <QGraphicsObject> +#include <QScopedPointer> #include <QtTest> + #include <algorithm> using namespace QmlJS; diff --git a/tests/auto/runextensions/CMakeLists.txt b/tests/auto/runextensions/CMakeLists.txt deleted file mode 100644 index 73455f75185..00000000000 --- a/tests/auto/runextensions/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_qtc_test(tst_runextensions - DEPENDS Utils - SOURCES tst_runextensions.cpp -) diff --git a/tests/auto/runextensions/runextensions.qbs b/tests/auto/runextensions/runextensions.qbs deleted file mode 100644 index 26e6bf5d84a..00000000000 --- a/tests/auto/runextensions/runextensions.qbs +++ /dev/null @@ -1,10 +0,0 @@ -import qbs - -QtcAutotest { - name: "Run extensions autotest" - Depends { name: "Utils" } - - files: [ - "tst_runextensions.cpp", - ] -} diff --git a/tests/auto/runextensions/tst_runextensions.cpp b/tests/auto/runextensions/tst_runextensions.cpp deleted file mode 100644 index 4ffee270025..00000000000 --- a/tests/auto/runextensions/tst_runextensions.cpp +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include <utils/runextensions.h> - -#include <QtTest> - -#if !defined(Q_CC_MSVC) || _MSC_VER >= 1900 // MSVC2015 -#define SUPPORTS_MOVE -#endif - -class tst_RunExtensions : public QObject -{ - Q_OBJECT - -private slots: - void runAsync(); - void runInThreadPool(); -#ifdef SUPPORTS_MOVE - void moveOnlyType(); -#endif - void threadPriority(); - void runAsyncNoFutureInterface(); - void crefFunction(); -}; - -void report3(QFutureInterface<int> &fi) -{ - fi.reportResults({0, 2, 1}); -} - -void reportN(QFutureInterface<double> &fi, int n) -{ - fi.reportResults(QVector<double>(n, 0)); -} - -void reportString1(QFutureInterface<QString> &fi, const QString &s) -{ - fi.reportResult(s); -} - -void reportString2(QFutureInterface<QString> &fi, QString s) -{ - fi.reportResult(s); -} - -class Callable { -public: - void operator()(QFutureInterface<double> &fi, int n) const - { - fi.reportResults(QVector<double>(n, 0)); - } -}; - -class MyObject { -public: - static void staticMember0(QFutureInterface<double> &fi) - { - fi.reportResults({0, 2, 1}); - } - - static void staticMember1(QFutureInterface<double> &fi, int n) - { - fi.reportResults(QVector<double>(n, 0)); - } - - void member0(QFutureInterface<double> &fi) const - { - fi.reportResults({0, 2, 1}); - } - - void member1(QFutureInterface<double> &fi, int n) const - { - fi.reportResults(QVector<double>(n, 0)); - } - - void memberString1(QFutureInterface<QString> &fi, const QString &s) const - { - fi.reportResult(s); - } - - void memberString2(QFutureInterface<QString> &fi, QString s) const - { - fi.reportResult(s); - } - - void nonConstMember(QFutureInterface<double> &fi) - { - fi.reportResults({0, 2, 1}); - } -}; - -void voidFunction(bool *value) // can be useful to get QFuture for watching when it is finished -{ - *value = true; -} - -int one() -{ - return 1; -} - -int identity(int input) -{ - return input; -} - -QString stringIdentity1(const QString &s) -{ - return s; -} - -QString stringIdentity2(QString s) -{ - return s; -} - -class CallableWithoutQFutureInterface { -public: - void operator()(bool *value) const - { - *value = true; - } -}; - -class MyObjectWithoutQFutureInterface { -public: - static void staticMember0(bool *value) - { - *value = true; - } - - static double staticMember1(int n) - { - return n; - } - - void member0(bool *value) const - { - *value = true; - } - - double member1(int n) const - { - return n; - } - - QString memberString1(const QString &s) const - { - return s; - } - - QString memberString2(QString s) const - { - return s; - } - - double nonConstMember(int n) - { - return n; - } -}; - -void tst_RunExtensions::runAsync() -{ - // free function pointer - QCOMPARE(Utils::runAsync(&report3).results(), - QList<int>({0, 2, 1})); - QCOMPARE(Utils::runAsync(report3).results(), - QList<int>({0, 2, 1})); - - QCOMPARE(Utils::runAsync(reportN, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(Utils::runAsync(reportN, 2).results(), - QList<double>({0, 0})); - - QString s = QLatin1String("string"); - const QString &crs = QLatin1String("cr string"); - const QString cs = QLatin1String("c string"); - - QCOMPARE(Utils::runAsync(reportString1, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(reportString1, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(reportString1, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(reportString1, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - - QCOMPARE(Utils::runAsync(reportString2, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(reportString2, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(reportString2, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(reportString2, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - - // lambda - QCOMPARE(Utils::runAsync([](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }, 3).results(), - QList<double>({0, 0, 0})); - - // std::function - const std::function<void(QFutureInterface<double>&,int)> fun = [](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }; - QCOMPARE(Utils::runAsync(fun, 2).results(), - QList<double>({0, 0})); - - // operator() - QCOMPARE(Utils::runAsync(Callable(), 3).results(), - QList<double>({0, 0, 0})); - const Callable c{}; - QCOMPARE(Utils::runAsync(c, 2).results(), - QList<double>({0, 0})); - - // static member functions - QCOMPARE(Utils::runAsync(&MyObject::staticMember0).results(), - QList<double>({0, 2, 1})); - QCOMPARE(Utils::runAsync(&MyObject::staticMember1, 2).results(), - QList<double>({0, 0})); - - // member functions - const MyObject obj{}; - QCOMPARE(Utils::runAsync(&MyObject::member0, &obj).results(), - QList<double>({0, 2, 1})); - QCOMPARE(Utils::runAsync(&MyObject::member1, &obj, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(&MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(&MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - MyObject nonConstObj{}; - QCOMPARE(Utils::runAsync(&MyObject::nonConstMember, &nonConstObj).results(), - QList<double>({0, 2, 1})); -} - -void tst_RunExtensions::runInThreadPool() -{ - QScopedPointer<QThreadPool> pool(new QThreadPool); - // free function pointer - QCOMPARE(Utils::runAsync(pool.data(), &report3).results(), - QList<int>({0, 2, 1})); - QCOMPARE(Utils::runAsync(pool.data(), report3).results(), - QList<int>({0, 2, 1})); - - QCOMPARE(Utils::runAsync(pool.data(), reportN, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(Utils::runAsync(pool.data(), reportN, 2).results(), - QList<double>({0, 0})); - - QString s = QLatin1String("string"); - const QString &crs = QLatin1String("cr string"); - const QString cs = QLatin1String("c string"); - - QCOMPARE(Utils::runAsync(pool.data(), reportString1, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(pool.data(), reportString1, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(pool.data(), reportString1, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(pool.data(), reportString1, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - - QCOMPARE(Utils::runAsync(pool.data(), reportString2, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(pool.data(), reportString2, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(pool.data(), reportString2, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(pool.data(), reportString2, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - - // lambda - QCOMPARE(Utils::runAsync(pool.data(), [](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }, 3).results(), - QList<double>({0, 0, 0})); - - // std::function - const std::function<void(QFutureInterface<double>&,int)> fun = [](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }; - QCOMPARE(Utils::runAsync(pool.data(), fun, 2).results(), - QList<double>({0, 0})); - - // operator() - QCOMPARE(Utils::runAsync(pool.data(), Callable(), 3).results(), - QList<double>({0, 0, 0})); - const Callable c{}; - QCOMPARE(Utils::runAsync(pool.data(), c, 2).results(), - QList<double>({0, 0})); - - // static member functions - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember0).results(), - QList<double>({0, 2, 1})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::staticMember1, 2).results(), - QList<double>({0, 0})); - - // member functions - const MyObject obj{}; - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member0, &obj).results(), - QList<double>({0, 2, 1})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::member1, &obj, 4).results(), - QList<double>({0, 0, 0, 0})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(pool.data(), &MyObject::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); -} - -#ifdef SUPPORTS_MOVE - -class MoveOnlyType -{ -public: - MoveOnlyType() = default; - MoveOnlyType(const MoveOnlyType &) = delete; - MoveOnlyType(MoveOnlyType &&) = default; - MoveOnlyType &operator=(const MoveOnlyType &) = delete; - MoveOnlyType &operator=(MoveOnlyType &&) = default; -}; - -class MoveOnlyCallable : public MoveOnlyType -{ -public: - void operator()(QFutureInterface<int> &fi, const MoveOnlyType &) - { - fi.reportResult(1); - } -}; - -void tst_RunExtensions::moveOnlyType() -{ - QCOMPARE(Utils::runAsync(MoveOnlyCallable(), MoveOnlyType()).results(), - QList<int>({1})); -} - -#endif - -void tst_RunExtensions::threadPriority() -{ - QScopedPointer<QThreadPool> pool(new QThreadPool); - // with pool - QCOMPARE(Utils::runAsync(pool.data(), QThread::LowestPriority, &report3).results(), - QList<int>({0, 2, 1})); - - // without pool - QCOMPARE(Utils::runAsync(QThread::LowestPriority, report3).results(), - QList<int>({0, 2, 1})); -} - -void tst_RunExtensions::runAsyncNoFutureInterface() -{ - // free function pointer - bool value = false; - Utils::runAsync(voidFunction, &value).waitForFinished(); - QCOMPARE(value, true); - - QCOMPARE(Utils::runAsync(one).results(), - QList<int>({1})); - QCOMPARE(Utils::runAsync(identity, 5).results(), - QList<int>({5})); - - QString s = QLatin1String("string"); - const QString &crs = QLatin1String("cr string"); - const QString cs = QLatin1String("c string"); - - QCOMPARE(Utils::runAsync(stringIdentity1, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(stringIdentity1, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(stringIdentity1, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(stringIdentity1, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(Utils::runAsync(stringIdentity2, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(stringIdentity2, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(stringIdentity2, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(stringIdentity2, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - - // lambda - QCOMPARE(Utils::runAsync([](int n) -> double { - return n + 1; - }, 3).results(), - QList<double>({4})); - - // std::function - const std::function<double(int)> fun = [](int n) { - return n + 1; - }; - QCOMPARE(Utils::runAsync(fun, 2).results(), - QList<double>({3})); - - // operator() - value = false; - Utils::runAsync(CallableWithoutQFutureInterface(), &value).waitForFinished(); - QCOMPARE(value, true); - value = false; - const CallableWithoutQFutureInterface c{}; - Utils::runAsync(c, &value).waitForFinished(); - QCOMPARE(value, true); - - // static member functions - value = false; - Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember0, &value).waitForFinished(); - QCOMPARE(value, true); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::staticMember1, 2).results(), - QList<double>({2})); - - // member functions - const MyObjectWithoutQFutureInterface obj{}; - value = false; - Utils::runAsync(&MyObjectWithoutQFutureInterface::member0, &obj, &value).waitForFinished(); - QCOMPARE(value, true); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::member1, &obj, 4).results(), - QList<double>({4})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString1, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, s).results(), - QList<QString>({s})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, crs).results(), - QList<QString>({crs})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, cs).results(), - QList<QString>({cs})); - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::memberString2, &obj, QString(QLatin1String("rvalue"))).results(), - QList<QString>({QString(QLatin1String("rvalue"))})); - MyObjectWithoutQFutureInterface nonConstObj{}; - QCOMPARE(Utils::runAsync(&MyObjectWithoutQFutureInterface::nonConstMember, &nonConstObj, 4).results(), - QList<double>({4})); -} - -void tst_RunExtensions::crefFunction() -{ - // free function pointer with future interface - auto fun = &report3; - QCOMPARE(Utils::runAsync(std::cref(fun)).results(), - QList<int>({0, 2, 1})); - - // lambda with future interface - auto lambda = [](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }; - QCOMPARE(Utils::runAsync(std::cref(lambda), 3).results(), - QList<double>({0, 0, 0})); - - // std::function with future interface - const std::function<void(QFutureInterface<double>&,int)> funObj = [](QFutureInterface<double> &fi, int n) { - fi.reportResults(QVector<double>(n, 0)); - }; - QCOMPARE(Utils::runAsync(std::cref(funObj), 2).results(), - QList<double>({0, 0})); - - // callable with future interface - const Callable c{}; - QCOMPARE(Utils::runAsync(std::cref(c), 2).results(), - QList<double>({0, 0})); - - // member functions with future interface - auto member = &MyObject::member0; - const MyObject obj{}; - QCOMPARE(Utils::runAsync(std::cref(member), &obj).results(), - QList<double>({0, 2, 1})); - - // free function pointer without future interface - bool value = false; - auto voidFun = &voidFunction; - Utils::runAsync(std::cref(voidFun), &value).waitForFinished(); - QCOMPARE(value, true); - - auto oneFun = &one; - QCOMPARE(Utils::runAsync(std::cref(oneFun)).results(), - QList<int>({1})); - - // lambda without future interface - auto lambda2 = [](int n) -> double { - return n + 1; - }; - QCOMPARE(Utils::runAsync(std::cref(lambda2), 3).results(), - QList<double>({4})); - - // std::function - const std::function<double(int)> funObj2 = [](int n) { - return n + 1; - }; - QCOMPARE(Utils::runAsync(std::cref(funObj2), 2).results(), - QList<double>({3})); - - // callable without future interface - const CallableWithoutQFutureInterface c2{}; - Utils::runAsync(std::cref(c2), &value).waitForFinished(); - QCOMPARE(value, true); - - // member functions without future interface - const MyObjectWithoutQFutureInterface obj2{}; - auto member2 = &MyObjectWithoutQFutureInterface::member0; - value = false; - Utils::runAsync(std::cref(member2), &obj2, &value).waitForFinished(); - QCOMPARE(value, true); -} - -QTEST_GUILESS_MAIN(tst_RunExtensions) - -#include "tst_runextensions.moc" diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 5f25008228b..cd476670c8f 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -50,7 +50,6 @@ private: }; int CustomStorage::s_count = 0; -static const char s_taskIdProperty[] = "__taskId"; struct TestData { TreeStorage<CustomStorage> storage; @@ -68,6 +67,8 @@ private slots: void validConstructs(); // compile test void testTree_data(); void testTree(); + void storageIO_data(); + void storageIO(); void storageOperators(); void storageDestructor(); }; @@ -194,7 +195,7 @@ public: void start() final { task()->start(); } }; -TASKING_DECLARE_TASK(TickAndDoneTask, TickAndDoneTaskAdapter); +using TickAndDoneTask = CustomTask<TickAndDoneTaskAdapter>; template <typename SharedBarrierType> GroupItem createBarrierAdvance(const TreeStorage<CustomStorage> &storage, @@ -550,8 +551,8 @@ void tst_Tasking::testTree_data() }; taskTree.setRecipe(nestedRoot); CustomStorage *activeStorage = storage.activeStorage(); - auto collectSubLog = [activeStorage](CustomStorage *subTreeStorage){ - activeStorage->m_log += subTreeStorage->m_log; + const auto collectSubLog = [activeStorage](const CustomStorage &subTreeStorage){ + activeStorage->m_log += subTreeStorage.m_log; }; taskTree.onStorageDone(storage, collectSubLog); }; @@ -1846,7 +1847,7 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 1), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(2), createSuccessTask(3) } @@ -1871,7 +1872,7 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 1), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(2), createSuccessTask(3) } @@ -1902,7 +1903,7 @@ void tst_Tasking::testTree_data() parallel, Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(2), createSuccessTask(3) }, @@ -1928,12 +1929,12 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 1), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(4) }, Group { groupSetup(3), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(5) } }; @@ -1963,8 +1964,8 @@ void tst_Tasking::testTree_data() Group { parallel, groupSetup(1), - WaitForBarrierTask(barrier), - WaitForBarrierTask(barrier2) + waitForBarrierTask(barrier), + waitForBarrierTask(barrier2) }, createSuccessTask(3) }, @@ -2006,7 +2007,7 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 2), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(2), createSuccessTask(3) } @@ -2034,7 +2035,7 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 2), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(3), createSuccessTask(4) } @@ -2067,7 +2068,7 @@ void tst_Tasking::testTree_data() parallel, Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(3), createSuccessTask(4) }, @@ -2097,12 +2098,12 @@ void tst_Tasking::testTree_data() createBarrierAdvance(storage, barrier, 2), Group { groupSetup(2), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(3) }, Group { groupSetup(3), - WaitForBarrierTask(barrier), + waitForBarrierTask(barrier), createSuccessTask(4) } }; @@ -2244,7 +2245,9 @@ void tst_Tasking::testTree() TaskTree taskTree({testData.root.withTimeout(1000ms)}); QCOMPARE(taskTree.taskCount() - 1, testData.taskCount); // -1 for the timeout task above Log actualLog; - const auto collectLog = [&actualLog](CustomStorage *storage) { actualLog = storage->m_log; }; + const auto collectLog = [&actualLog](const CustomStorage &storage) { + actualLog = storage.m_log; + }; taskTree.onStorageDone(testData.storage, collectLog); const OnDone result = taskTree.runBlocking() ? OnDone::Success : OnDone::Failure; QCOMPARE(taskTree.isRunning(), false); @@ -2256,6 +2259,52 @@ void tst_Tasking::testTree() QCOMPARE(result, testData.onDone); } +struct StorageIO +{ + int value = 0; +}; + +static Group inputOutputRecipe(const TreeStorage<StorageIO> &storage) +{ + return Group { + Storage(storage), + onGroupSetup([storage] { ++storage->value; }), + onGroupDone([storage] { storage->value *= 2; }) + }; +} + +void tst_Tasking::storageIO_data() +{ + QTest::addColumn<int>("input"); + QTest::addColumn<int>("output"); + + QTest::newRow("-1 -> 0") << -1 << 0; + QTest::newRow("0 -> 2") << 0 << 2; + QTest::newRow("1 -> 4") << 1 << 4; + QTest::newRow("2 -> 6") << 2 << 6; +} + +void tst_Tasking::storageIO() +{ + QFETCH(int, input); + QFETCH(int, output); + + int actualOutput = 0; + + const TreeStorage<StorageIO> storage; + TaskTree taskTree(inputOutputRecipe(storage)); + + const auto setInput = [input](StorageIO &storage) { storage.value = input; }; + const auto getOutput = [&actualOutput](const StorageIO &storage) { actualOutput = storage.value; }; + + taskTree.onStorageSetup(storage, setInput); + taskTree.onStorageDone(storage, getOutput); + taskTree.runBlocking(); + + QCOMPARE(taskTree.isRunning(), false); + QCOMPARE(actualOutput, output); +} + void tst_Tasking::storageOperators() { TreeStorageBase storage1 = TreeStorage<CustomStorage>(); @@ -2274,11 +2323,11 @@ void tst_Tasking::storageOperators() void tst_Tasking::storageDestructor() { bool setupCalled = false; - const auto setupHandler = [&setupCalled](CustomStorage *) { + const auto setupHandler = [&setupCalled](CustomStorage &) { setupCalled = true; }; bool doneCalled = false; - const auto doneHandler = [&doneCalled](CustomStorage *) { + const auto doneHandler = [&doneCalled](const CustomStorage &) { doneCalled = true; }; QCOMPARE(CustomStorage::instanceCount(), 0); diff --git a/tests/auto/texteditor/highlighter/CMakeLists.txt b/tests/auto/texteditor/highlighter/CMakeLists.txt index 76370bf8255..99fb136b320 100644 --- a/tests/auto/texteditor/highlighter/CMakeLists.txt +++ b/tests/auto/texteditor/highlighter/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_highlighter + NEEDS_GUI DEPENDS TextEditor Utils Qt::Widgets SOURCES tst_highlighter.cpp ) diff --git a/tests/auto/toolchaincache/toolchaincache.qbs b/tests/auto/toolchaincache/toolchaincache.qbs index 7f1a2adbf19..7384b21a88c 100644 --- a/tests/auto/toolchaincache/toolchaincache.qbs +++ b/tests/auto/toolchaincache/toolchaincache.qbs @@ -3,7 +3,7 @@ import qbs QtcAutotest { name: "ToolChainCache autotest" Depends { name: "ProjectExplorer" } - Depends { name: "Qt.gui" } // For QIcon in Task + Depends { name: "Qt"; submodules: ["gui", "widgets"] } // For QIcon in Task, QLabel in ElidingLabel Group { name: "Test sources" files: "tst_toolchaincache.cpp" diff --git a/tests/auto/tracing/flamegraph/CMakeLists.txt b/tests/auto/tracing/flamegraph/CMakeLists.txt index ae894557af4..8618fdf7d5e 100644 --- a/tests/auto/tracing/flamegraph/CMakeLists.txt +++ b/tests/auto/tracing/flamegraph/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_tracing_flamegraph + NEEDS_GUI DEPENDS Tracing Qt::Quick SOURCES tst_flamegraph.cpp ) diff --git a/tests/auto/tracing/flamegraphview/CMakeLists.txt b/tests/auto/tracing/flamegraphview/CMakeLists.txt index b1c53558a3a..22e5c9471d6 100644 --- a/tests/auto/tracing/flamegraphview/CMakeLists.txt +++ b/tests/auto/tracing/flamegraphview/CMakeLists.txt @@ -4,6 +4,7 @@ set(TSTFLAMEGRAPHVIEW_CPP_SOURCES ) add_qtc_test(tst_tracing_flamegraphview + NEEDS_GUI EXCLUDE_FROM_PRECHECK DEPENDS Tracing Qt::QuickWidgets Qt::Quick Utils ) diff --git a/tests/auto/tracing/timelineitemsrenderpass/CMakeLists.txt b/tests/auto/tracing/timelineitemsrenderpass/CMakeLists.txt index 5af1116952e..228fed76e68 100644 --- a/tests/auto/tracing/timelineitemsrenderpass/CMakeLists.txt +++ b/tests/auto/tracing/timelineitemsrenderpass/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_tracing_timelineitemsrenderpass + NEEDS_GUI DEPENDS Tracing Qt::Quick SOURCES tst_timelineitemsrenderpass.cpp ) diff --git a/tests/auto/tracing/timelineitemsrenderpass/tst_timelineitemsrenderpass.cpp b/tests/auto/tracing/timelineitemsrenderpass/tst_timelineitemsrenderpass.cpp index 10bc5c85e1a..0c42367a613 100644 --- a/tests/auto/tracing/timelineitemsrenderpass/tst_timelineitemsrenderpass.cpp +++ b/tests/auto/tracing/timelineitemsrenderpass/tst_timelineitemsrenderpass.cpp @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <tracing/runscenegraphtest.h> #include <tracing/timelineitemsrenderpass.h> #include <tracing/timelinemodelaggregator.h> #include <tracing/timelinerenderstate.h> @@ -124,8 +123,8 @@ void tst_TimelineItemsRenderPass::update() parentState.setPassState(0, result); parentState.assembleNodeTree(&model, 1, 1); - runSceneGraphTest(parentState.collapsedRowRoot()); - runSceneGraphTest(parentState.expandedRowRoot()); + QVERIFY(parentState.collapsedRowRoot()); + QVERIFY(parentState.expandedRowRoot()); } QTEST_MAIN(tst_TimelineItemsRenderPass) diff --git a/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp b/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp index 559b827de2e..72f16510bce 100644 --- a/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp +++ b/tests/auto/tracing/timelinemodel/tst_timelinemodel.cpp @@ -57,6 +57,7 @@ private slots: void rowCount(); void prevNext(); void parentingOfEqualStarts(); + void rows(); private: TimelineModelAggregator aggregator; @@ -463,6 +464,18 @@ void tst_TimelineModel::parentingOfEqualStarts() QCOMPARE(dummy.lastIndex(2), 1); } +void tst_TimelineModel::rows() +{ + DummyModel dummy(&aggregator); + dummy.loadData(); + int maxlevel; + const QList<int> levels = dummy.computeRows(&maxlevel); + QCOMPARE(levels.at(0), 0); + QCOMPARE(levels.at(7), 7); + QCOMPARE(levels.at(10), 2); + QCOMPARE(maxlevel, 15); +} + QTEST_GUILESS_MAIN(tst_TimelineModel) #include "tst_timelinemodel.moc" diff --git a/tests/auto/tracing/timelinenotesrenderpass/CMakeLists.txt b/tests/auto/tracing/timelinenotesrenderpass/CMakeLists.txt index f0d6f0397ad..6355651f13e 100644 --- a/tests/auto/tracing/timelinenotesrenderpass/CMakeLists.txt +++ b/tests/auto/tracing/timelinenotesrenderpass/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_tracing_timelinenotesrenderpass + NEEDS_GUI DEPENDS Tracing Qt::Quick SOURCES tst_timelinenotesrenderpass.cpp ) diff --git a/tests/auto/tracing/timelinenotesrenderpass/tst_timelinenotesrenderpass.cpp b/tests/auto/tracing/timelinenotesrenderpass/tst_timelinenotesrenderpass.cpp index ea08ed7d823..77447f53ed3 100644 --- a/tests/auto/tracing/timelinenotesrenderpass/tst_timelinenotesrenderpass.cpp +++ b/tests/auto/tracing/timelinenotesrenderpass/tst_timelinenotesrenderpass.cpp @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <tracing/runscenegraphtest.h> #include <tracing/timelinemodelaggregator.h> #include <tracing/timelinenotesrenderpass.h> #include <tracing/timelinerenderstate.h> @@ -110,8 +109,8 @@ void tst_TimelineNotesRenderPass::update() parentState.setPassState(0, result); parentState.assembleNodeTree(&model, 1, 1); - runSceneGraphTest(parentState.collapsedOverlayRoot()); - runSceneGraphTest(parentState.expandedRowRoot()); + QVERIFY(parentState.collapsedOverlayRoot()); + QVERIFY(parentState.expandedRowRoot()); } QTEST_MAIN(tst_TimelineNotesRenderPass) diff --git a/tests/auto/tracing/timelineselectionrenderpass/CMakeLists.txt b/tests/auto/tracing/timelineselectionrenderpass/CMakeLists.txt index acb8f54220c..163400a4554 100644 --- a/tests/auto/tracing/timelineselectionrenderpass/CMakeLists.txt +++ b/tests/auto/tracing/timelineselectionrenderpass/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_tracing_timelineselectionrenderpass + NEEDS_GUI DEPENDS Tracing Qt::Quick SOURCES tst_timelineselectionrenderpass.cpp ) diff --git a/tests/auto/tracing/timelineselectionrenderpass/tst_timelineselectionrenderpass.cpp b/tests/auto/tracing/timelineselectionrenderpass/tst_timelineselectionrenderpass.cpp index 411d42a7173..94a49d5fdba 100644 --- a/tests/auto/tracing/timelineselectionrenderpass/tst_timelineselectionrenderpass.cpp +++ b/tests/auto/tracing/timelineselectionrenderpass/tst_timelineselectionrenderpass.cpp @@ -1,7 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include <tracing/runscenegraphtest.h> #include <tracing/timelineselectionrenderpass.h> #include <tracing/timelinerenderstate.h> #include <tracing/timelineabstractrenderer_p.h> @@ -150,8 +149,8 @@ void tst_TimelineSelectionRenderPass::update() parentState.setPassState(0, result); parentState.assembleNodeTree(&model, 1, 1); - runSceneGraphTest(parentState.collapsedOverlayRoot()); - runSceneGraphTest(parentState.expandedOverlayRoot()); + QVERIFY(parentState.collapsedOverlayRoot()); + QVERIFY(parentState.expandedOverlayRoot()); } QTEST_MAIN(tst_TimelineSelectionRenderPass) diff --git a/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp b/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp index 3e1c936c0d5..ea26071401d 100644 --- a/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp +++ b/tests/auto/tracing/timelinezoomcontrol/tst_timelinezoomcontrol.cpp @@ -55,15 +55,14 @@ void tst_TimelineZoomControl::window() QTimer timer; timer.setSingleShot(true); - connect(&timer, &QTimer::timeout, [&] { + connect(&timer, &QTimer::timeout, this, [&zoomControl] { QVERIFY(zoomControl.windowLocked()); zoomControl.setWindowLocked(false); }); int numWindowChanges = 0; - connect(&zoomControl, &TimelineZoomControl::windowChanged, - [&](qint64, qint64) { + connect(&zoomControl, &TimelineZoomControl::windowChanged, this, [&](qint64, qint64) { verifyWindow(zoomControl); QVERIFY(!timer.isActive()); @@ -100,7 +99,7 @@ void tst_TimelineZoomControl::window() zoomControl.setRange(152000, 152005); // move right QMetaObject::Connection connection = connect( - &zoomControl, &TimelineZoomControl::windowMovingChanged, [&](bool moving) { + &zoomControl, &TimelineZoomControl::windowMovingChanged, this, [&](bool moving) { if (moving) return; diff --git a/tests/auto/treeviewfind/CMakeLists.txt b/tests/auto/treeviewfind/CMakeLists.txt index 6ce09aa2097..00ac4a52f31 100644 --- a/tests/auto/treeviewfind/CMakeLists.txt +++ b/tests/auto/treeviewfind/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_treeviewfind + NEEDS_GUI DEPENDS Core SOURCES tst_treeviewfind.cpp ) diff --git a/tests/auto/treeviewfind/tst_treeviewfind.cpp b/tests/auto/treeviewfind/tst_treeviewfind.cpp index 301c9084d3c..a3a8b11545e 100644 --- a/tests/auto/treeviewfind/tst_treeviewfind.cpp +++ b/tests/auto/treeviewfind/tst_treeviewfind.cpp @@ -48,13 +48,13 @@ void tst_treeviewfind::wrapping() QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("FOO2")); // forward - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0)); // backward tree->setCurrentItem(toplevelitems.at(0)->child(0)); QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("FOO1")); - findSupport->findStep(QLatin1String("FOO"), Core::FindBackward); + findSupport->findStep(QLatin1String("FOO"), Utils::FindBackward); QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0)); // clean up @@ -93,31 +93,31 @@ void tst_treeviewfind::columns() QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("HEADER1")); // find in first column - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0)); // find in second column of node with children - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(1)); // again find in first column - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(1)->child(0)); // don't stay in item if multiple columns match, and find in second column - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0)); // wrap - findSupport->findStep(QLatin1String("FOO"), Core::FindFlags()); + findSupport->findStep(QLatin1String("FOO"), {}); QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0)); // backwards tree->setCurrentItem(toplevelitems.at(2)->child(0)); QCOMPARE(tree->currentItem()->text(0), QString::fromLatin1("A")); - findSupport->findStep(QLatin1String("FOO"), Core::FindBackward); + findSupport->findStep(QLatin1String("FOO"), Utils::FindBackward); QCOMPARE(tree->currentItem(), toplevelitems.at(1)->child(0)); - findSupport->findStep(QLatin1String("FOO"), Core::FindBackward); + findSupport->findStep(QLatin1String("FOO"), Utils::FindBackward); QCOMPARE(tree->currentItem(), toplevelitems.at(1)); - findSupport->findStep(QLatin1String("FOO"), Core::FindBackward); + findSupport->findStep(QLatin1String("FOO"), Utils::FindBackward); QCOMPARE(tree->currentItem(), toplevelitems.at(0)->child(0)); - findSupport->findStep(QLatin1String("FOO"), Core::FindBackward); + findSupport->findStep(QLatin1String("FOO"), Utils::FindBackward); QCOMPARE(tree->currentItem(), toplevelitems.at(2)->child(0)); // clean up diff --git a/tests/auto/utils/expected/tst_expected.cpp b/tests/auto/utils/expected/tst_expected.cpp index 43e58fb4f90..ae084036bdc 100644 --- a/tests/auto/utils/expected/tst_expected.cpp +++ b/tests/auto/utils/expected/tst_expected.cpp @@ -26,12 +26,31 @@ private slots: return expected_str<QByteArray>( make_unexpected(QString("Error: " + error))); }) - .transform_or([](auto error) -> QString { + .transform_error([](auto error) -> QString { return QString(QString("More Info: ") + error); }); QVERIFY(!result); } + + void tryCompareVoid() + { + tl::expected<void, QString> e1; + QVERIFY(e1 == e1); + + tl::expected<void, QString> e2 = make_unexpected("error"); + QVERIFY(e1 != e2); + + e1 = make_unexpected(QString("error")); + QVERIFY(e1 == e2); + + e2 = {}; + QVERIFY(e1 != e2); + + e1 = {}; + QVERIFY(e1 == e2); + QVERIFY(!(e1 != e2)); + } }; QTEST_GUILESS_MAIN(tst_expected) diff --git a/tests/auto/utils/filepath/tst_filepath.cpp b/tests/auto/utils/filepath/tst_filepath.cpp index 6a269af7712..0cd0629609e 100644 --- a/tests/auto/utils/filepath/tst_filepath.cpp +++ b/tests/auto/utils/filepath/tst_filepath.cpp @@ -625,15 +625,15 @@ void tst_filepath::toString_data() << "c:/__qtc_devices__/docker" << "c:/__qtc_devices__/docker"; QTest::newRow("qtc-root-folder") << "docker" - << "alpine:latest" + << "alpine.latest" << "/" - << "docker://alpine:latest/" - << "docker://alpine:latest/"; + << "docker://alpine.latest/" + << "docker://alpine.latest/"; QTest::newRow("qtc-root-folder-rel") << "docker" - << "alpine:latest" + << "alpine.latest" << "" - << "docker://alpine:latest" - << "docker://alpine:latest"; + << "docker://alpine.latest" + << "docker://alpine.latest"; } void tst_filepath::toString() @@ -692,14 +692,14 @@ void tst_filepath::toFSPathString_data() << QDir::rootPath() + "__qtc_devices__/docker"; QTest::newRow("qtc-root-folder") << "docker" - << "alpine:latest" - << "/" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest/" - << "docker://alpine:latest/"; + << "alpine.latest" + << "/" << QDir::rootPath() + "__qtc_devices__/docker/alpine.latest/" + << "docker://alpine.latest/"; QTest::newRow("qtc-root-folder-rel") << "docker" - << "alpine:latest" - << "" << QDir::rootPath() + "__qtc_devices__/docker/alpine:latest" - << "docker://alpine:latest"; + << "alpine.latest" + << "" << QDir::rootPath() + "__qtc_devices__/docker/alpine.latest" + << "docker://alpine.latest"; } void tst_filepath::toFSPathString() diff --git a/tests/auto/utils/multicursor/CMakeLists.txt b/tests/auto/utils/multicursor/CMakeLists.txt index 7b8be63f836..9300e38b632 100644 --- a/tests/auto/utils/multicursor/CMakeLists.txt +++ b/tests/auto/utils/multicursor/CMakeLists.txt @@ -1,4 +1,5 @@ add_qtc_test(tst_utils_multicursor + NEEDS_GUI DEPENDS Utils SOURCES tst_multicursor.cpp ) diff --git a/tests/auto/utils/multicursor/tst_multicursor.cpp b/tests/auto/utils/multicursor/tst_multicursor.cpp index 4068d47f1fe..f873af143c7 100644 --- a/tests/auto/utils/multicursor/tst_multicursor.cpp +++ b/tests/auto/utils/multicursor/tst_multicursor.cpp @@ -57,7 +57,7 @@ public: QTextCursor toTextCursor(QTextDocument *doc) { if (p.first < 0) - return QTextCursor(); + return {}; QTextCursor c(doc); c.setPosition(p.second); c.setPosition(p.first, QTextCursor::KeepAnchor); diff --git a/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp b/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp index d64867aef09..e2fde299096 100644 --- a/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp +++ b/tests/auto/utils/persistentsettings/tst_persistentsettings.cpp @@ -19,9 +19,9 @@ private slots: void tst_readwrite(); }; -static const QVariantMap generateData() +static const Store generateData() { - QVariantMap result; + Store result; QByteArray barr("I am a byte array."); QString str("I am a string."); QColor color("#8b00d1"); @@ -44,7 +44,7 @@ void PersistentSettingsTest::tst_readwrite() qDebug() << "using" << fi.absoluteFilePath(); const FilePath filePath = FilePath::fromFileInfo(fi); PersistentSettingsWriter writer(filePath, "Narf"); - const QVariantMap originalData = generateData(); + const Store originalData = generateData(); QString error; bool success = writer.save(originalData, &error); QVERIFY2(success, error.toLocal8Bit()); @@ -54,7 +54,7 @@ void PersistentSettingsTest::tst_readwrite() success = reader.load(filePath); QVERIFY(success); - const QVariantMap restored = reader.restoreValues(); + const Store restored = reader.restoreValues(); QCOMPARE(restored.size(), originalData.size()); auto restoredEnd = restored.end(); for (auto it = originalData.cbegin(), end = originalData.cend(); it != end; ++it) { diff --git a/tests/auto/utils/process/processtestapp/processtestapp.cpp b/tests/auto/utils/process/processtestapp/processtestapp.cpp index 6cf7510eb78..77f36a362ec 100644 --- a/tests/auto/utils/process/processtestapp/processtestapp.cpp +++ b/tests/auto/utils/process/processtestapp/processtestapp.cpp @@ -161,10 +161,16 @@ int ProcessTestApp::BlockingProcess::main() { std::cout << "Blocking process successfully executed." << std::endl; const BlockType blockType = BlockType(qEnvironmentVariableIntValue(envVar())); + bool dummy = true; switch (blockType) { case BlockType::EndlessLoop: - while (true) - ; + while (true) { + if (dummy) { + // Note: Keep these lines, otherwise the compiler may optimize out the empty loop. + std::cout << "EndlessLoop started" << std::endl; + dummy = false; + } + } break; case BlockType::InfiniteSleep: QThread::sleep(INT_MAX); diff --git a/tests/auto/utils/process/tst_process.cpp b/tests/auto/utils/process/tst_process.cpp index 589adbb6fa8..52e4658778f 100644 --- a/tests/auto/utils/process/tst_process.cpp +++ b/tests/auto/utils/process/tst_process.cpp @@ -1216,7 +1216,6 @@ void tst_Process::mergedChannels_data() << false << false << false << true; QTest::newRow("ForwardedErrorChannel") << QProcess::ForwardedErrorChannel << true << false << false << false; - } void tst_Process::mergedChannels() @@ -1265,6 +1264,7 @@ void tst_Process::destroyBlockingProcess() subConfig.setupSubProcess(&process); process.start(); QVERIFY(process.waitForStarted()); + QVERIFY(process.isRunning()); QVERIFY(!process.waitForFinished(1000)); } diff --git a/tests/auto/utils/settings/tst_settings.cpp b/tests/auto/utils/settings/tst_settings.cpp index d45d673e4d3..7ce338025b0 100644 --- a/tests/auto/utils/settings/tst_settings.cpp +++ b/tests/auto/utils/settings/tst_settings.cpp @@ -13,9 +13,9 @@ using namespace Utils; const char TESTACCESSOR_APPLICATION_DN[] = "SettingsAccessor Test (Basic)"; const char TESTACCESSOR_DEFAULT_ID[] = "testId"; -QVariantMap generateExtraData() +Store generateExtraData() { - QVariantMap extra; + Store extra; extra.insert("Foo", "Bar"); extra.insert("Int", 42); return extra; @@ -28,14 +28,14 @@ QVariantMap generateExtraData() class TestVersionUpgrader : public Utils::VersionUpgrader { public: - TestVersionUpgrader(int version) : - Utils::VersionUpgrader(version, QString("v") + QString::number(version)) + TestVersionUpgrader(int version) + : VersionUpgrader(version, "v" + QString::number(version)) { } - QVariantMap upgrade(const QVariantMap &data) final + Store upgrade(const Store &data) final { - QVariantMap result = data; - result.insert(QString("VERSION_") + QString::number(version()), version()); + Store result = data; + result.insert(numberedKey("VERSION_", version()), version()); return result; } }; @@ -52,10 +52,10 @@ public: using Utils::MergingSettingsAccessor::addVersionUpgrader; - QHash<Utils::FilePath, QVariantMap> files() const { return m_files; } - void addFile(const Utils::FilePath &path, const QVariantMap &data) const { m_files.insert(path, data); } + QHash<Utils::FilePath, Store> files() const { return m_files; } + void addFile(const Utils::FilePath &path, const Store &data) const { m_files.insert(path, data); } Utils::FilePaths fileNames() const { return m_files.keys(); } - QVariantMap fileContents(const Utils::FilePath &path) const { return m_files.value(path); } + Store fileContents(const Utils::FilePath &path) const { return m_files.value(path); } protected: RestoreData readFile(const Utils::FilePath &path) const override @@ -71,7 +71,7 @@ protected: { Q_UNUSED(global) - const QString key = local.key; + const Key key = local.key; const QVariant main = local.main.value(key); const QVariant secondary = local.secondary.value(key); @@ -85,7 +85,7 @@ protected: return qMakePair(key, secondary); } - std::optional<Issue> writeFile(const Utils::FilePath &path, const QVariantMap &data) const override + std::optional<Issue> writeFile(const Utils::FilePath &path, const Store &data) const override { if (data.isEmpty()) { return Issue("Failed to Write File", "There was nothing to write.", @@ -97,7 +97,7 @@ protected: } private: - mutable QHash<Utils::FilePath, QVariantMap> m_files; + mutable QHash<Utils::FilePath, Store> m_files; }; // -------------------------------------------------------------------- @@ -200,10 +200,9 @@ private slots: void loadSettings_pickBest(); }; -static QVariantMap versionedMap(int version, const QByteArray &id = QByteArray(), - const QVariantMap &extra = QVariantMap()) +static Store versionedMap(int version, const QByteArray &id = {}, const Store &extra = {}) { - QVariantMap result; + Store result; result.insert("Version", version); if (!id.isEmpty()) result.insert("EnvironmentId", id); @@ -213,13 +212,13 @@ static QVariantMap versionedMap(int version, const QByteArray &id = QByteArray() } static Utils::SettingsAccessor::RestoreData restoreData(const Utils::FilePath &path, - const QVariantMap &data) + const Store &data) { return Utils::SettingsAccessor::RestoreData(path, data); } //static Utils::SettingsAccessor::RestoreData restoreData(const QByteArray &path, -// const QVariantMap &data) +// const Store &data) //{ // return restoreData(Utils::FilePath::fromUtf8(path), data); //} @@ -366,7 +365,7 @@ void tst_SettingsAccessor::RestoreDataCompare_emptyMap() { const TestSettingsAccessor accessor; - Utils::SettingsAccessor::RestoreData a = restoreData("/foo/bar", QVariantMap()); + Utils::SettingsAccessor::RestoreData a = restoreData("/foo/bar", Store()); Utils::SettingsAccessor::RestoreData b = restoreData("/foo/baz", versionedMap(7, TESTACCESSOR_DEFAULT_ID)); QCOMPARE(accessor.strategy()->compare(a, b), 1); @@ -377,8 +376,8 @@ void tst_SettingsAccessor::RestoreDataCompare_twoEmptyMaps() { const TestSettingsAccessor accessor; - Utils::SettingsAccessor::RestoreData a = restoreData("/foo/bar", QVariantMap()); - Utils::SettingsAccessor::RestoreData b = restoreData("/foo/baz", QVariantMap()); + Utils::SettingsAccessor::RestoreData a = restoreData("/foo/bar", Store()); + Utils::SettingsAccessor::RestoreData b = restoreData("/foo/baz", Store()); QCOMPARE(accessor.strategy()->compare(a, b), 0); QCOMPARE(accessor.strategy()->compare(b, a), 0); @@ -544,7 +543,7 @@ void tst_SettingsAccessor::upgradeSettings_partialUpdate() void tst_SettingsAccessor::upgradeSettings_targetVersionTooOld() { const TestSettingsAccessor accessor; - const QVariantMap extra = generateExtraData(); + const Store extra = generateExtraData(); const int startVersion = 6; const Utils::SettingsAccessor::RestoreData input = restoreData(accessor.baseFilePath(), @@ -560,7 +559,7 @@ void tst_SettingsAccessor::upgradeSettings_targetVersionTooOld() void tst_SettingsAccessor::upgradeSettings_targetVersionTooNew() { const TestSettingsAccessor accessor; - const QVariantMap extra = generateExtraData(); + const Store extra = generateExtraData(); const int startVersion = 6; const Utils::SettingsAccessor::RestoreData input = restoreData(accessor.baseFilePath(), @@ -578,7 +577,7 @@ void tst_SettingsAccessor::upgradeSettings_targetVersionTooNew() void tst_SettingsAccessor::findIssues_ok() { const TestSettingsAccessor accessor; - const QVariantMap data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); + const Store data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); const Utils::FilePath path = "/foo/baz.user"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -589,7 +588,7 @@ void tst_SettingsAccessor::findIssues_ok() void tst_SettingsAccessor::findIssues_emptyData() { const TestSettingsAccessor accessor; - const QVariantMap data; + const Store data; const Utils::FilePath path = "/foo/bar.user"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -600,7 +599,7 @@ void tst_SettingsAccessor::findIssues_emptyData() void tst_SettingsAccessor::findIssues_tooNew() { const TestSettingsAccessor accessor; - const QVariantMap data = versionedMap(42, TESTACCESSOR_DEFAULT_ID); + const Store data = versionedMap(42, TESTACCESSOR_DEFAULT_ID); const Utils::FilePath path = "/foo/bar.user"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -611,7 +610,7 @@ void tst_SettingsAccessor::findIssues_tooNew() void tst_SettingsAccessor::findIssues_tooOld() { const TestSettingsAccessor accessor; - const QVariantMap data = versionedMap(2, TESTACCESSOR_DEFAULT_ID); + const Store data = versionedMap(2, TESTACCESSOR_DEFAULT_ID); const Utils::FilePath path = "/foo/bar.user"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -622,7 +621,7 @@ void tst_SettingsAccessor::findIssues_tooOld() void tst_SettingsAccessor::findIssues_wrongId() { const TestSettingsAccessor accessor; - const QVariantMap data = versionedMap(6, "foo"); + const Store data = versionedMap(6, "foo"); const Utils::FilePath path = "/foo/bar.user"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -633,7 +632,7 @@ void tst_SettingsAccessor::findIssues_wrongId() void tst_SettingsAccessor::findIssues_nonDefaultPath() { const TestSettingsAccessor accessor; - const QVariantMap data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); + const Store data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); const Utils::FilePath path = "/foo/bar.user.foobar"; const std::optional<Utils::SettingsAccessor::Issue> info = accessor.findIssues(data, path); @@ -646,12 +645,12 @@ void tst_SettingsAccessor::saveSettings() { const FilePath baseFile = "/tmp/foo/saveSettings"; const TestSettingsAccessor accessor(baseFile); - const QVariantMap data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); + const Store data = versionedMap(6, TESTACCESSOR_DEFAULT_ID); QVERIFY(accessor.saveSettings(data, nullptr)); QCOMPARE(accessor.files().count(), 1); - const QVariantMap read = accessor.fileContents(baseFile); + const Store read = accessor.fileContents(baseFile); QVERIFY(!read.isEmpty()); for (auto it = read.cbegin(); it != read.cend(); ++it) { @@ -667,13 +666,13 @@ void tst_SettingsAccessor::saveSettings() void tst_SettingsAccessor::loadSettings() { - const QVariantMap data = versionedMap(6, "loadSettings", generateExtraData()); + const Store data = versionedMap(6, "loadSettings", generateExtraData()); const FilePath path = "/tmp/foo/loadSettings"; const TestSettingsAccessor accessor(path, "loadSettings"); accessor.addFile(path, data); QCOMPARE(accessor.files().count(), 1); // Catch changes early:-) - const QVariantMap read = accessor.restoreSettings(nullptr); + const Store read = accessor.restoreSettings(nullptr); QCOMPARE(accessor.files().count(), 1); // no files were created QVERIFY(!read.isEmpty()); @@ -700,7 +699,7 @@ void tst_SettingsAccessor::loadSettings_pickBest() const TestSettingsAccessor accessor(path, "loadSettings"); accessor.addFile(path, versionedMap(10, "loadSettings", generateExtraData())); // too new - const QVariantMap data = versionedMap(7, "loadSettings", generateExtraData()); + const Store data = versionedMap(7, "loadSettings", generateExtraData()); accessor.addFile("/tmp/foo/loadSettings.foo", data); // pick this! accessor.addFile("/tmp/foo/loadSettings.foo1", versionedMap(8, "fooSettings", generateExtraData())); // wrong environment @@ -710,7 +709,7 @@ void tst_SettingsAccessor::loadSettings_pickBest() versionedMap(1, "loadSettings", generateExtraData())); // much too old QCOMPARE(accessor.files().count(), 5); // Catch changes early:-) - const QVariantMap read = accessor.restoreSettings(nullptr); + const Store read = accessor.restoreSettings(nullptr); QCOMPARE(accessor.files().count(), 5); // no new files QVERIFY(!read.isEmpty()); diff --git a/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp b/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp index 3e62be9147d..5eba8122241 100644 --- a/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp +++ b/tests/auto/utils/unixdevicefileaccess/tst_unixdevicefileaccess.cpp @@ -33,6 +33,7 @@ public: virtual RunResult runInShell(const CommandLine &cmdLine, const QByteArray &inputData = {}) const override { + // Note: Don't convert into Utils::Process. See more comments in this change in gerrit. QProcess p; p.setProgram(cmdLine.executable().toString()); p.setArguments(cmdLine.splitArguments()); diff --git a/tests/auto/valgrind/memcheck/modeldemo.cpp b/tests/auto/valgrind/memcheck/modeldemo.cpp index 7739b441307..4fe18f1bf8b 100644 --- a/tests/auto/valgrind/memcheck/modeldemo.cpp +++ b/tests/auto/valgrind/memcheck/modeldemo.cpp @@ -9,7 +9,6 @@ #include <valgrind/xmlprotocol/parser.h> #include <valgrind/xmlprotocol/stack.h> #include <valgrind/xmlprotocol/status.h> -#include <valgrind/xmlprotocol/threadedparser.h> #include "modeldemo.h" @@ -30,15 +29,18 @@ int main(int argc, char *argv[]) qRegisterMetaType<Error>(); - ValgrindRunner runner; + ValgrindProcess runner; runner.setValgrindCommand({VALGRIND_FAKE_PATH, {"-i", PARSERTESTS_DATA_DIR "/memcheck-output-sample1.xml"}}); ModelDemo demo(&runner); - QObject::connect(&runner, &ValgrindRunner::finished, - &demo, &ModelDemo::finished); + QObject::connect(&runner, &ValgrindProcess::processErrorReceived, &app, [](const QString &err) { + qDebug() << err; + }); + QObject::connect(&runner, &ValgrindProcess::done, &app, [](bool success) { + qApp->exit(success ? 0 : 1); + }); ErrorListModel model; - QObject::connect(runner.parser(), &ThreadedParser::error, - &model, &ErrorListModel::addError, + QObject::connect(&runner, &ValgrindProcess::error, &model, &ErrorListModel::addError, Qt::QueuedConnection); QTreeView errorview; diff --git a/tests/auto/valgrind/memcheck/modeldemo.h b/tests/auto/valgrind/memcheck/modeldemo.h index 0950c66d53c..6694bc57fe1 100644 --- a/tests/auto/valgrind/memcheck/modeldemo.h +++ b/tests/auto/valgrind/memcheck/modeldemo.h @@ -12,13 +12,13 @@ #include <valgrind/xmlprotocol/error.h> #include <valgrind/xmlprotocol/errorlistmodel.h> #include <valgrind/xmlprotocol/stackmodel.h> -#include <valgrind/valgrindrunner.h> +#include <valgrind/valgrindprocess.h> class ModelDemo : public QObject { Q_OBJECT public: - explicit ModelDemo(Valgrind::ValgrindRunner *r, QObject *parent = 0) + explicit ModelDemo(Valgrind::ValgrindProcess *r, QObject *parent = 0) : QObject(parent) , runner(r) { @@ -27,11 +27,6 @@ public: Valgrind::XmlProtocol::StackModel* stackModel; public Q_SLOTS: - void finished() { - qDebug() << runner->errorString(); - qApp->exit(!runner->errorString().isEmpty()); - } - void selectionChanged(const QItemSelection &sel, const QItemSelection &) { if (sel.indexes().isEmpty()) return; @@ -41,7 +36,6 @@ public Q_SLOTS: stackModel->setError(err); } - private: - Valgrind::ValgrindRunner *runner; + Valgrind::ValgrindProcess *runner; }; diff --git a/tests/auto/valgrind/valgrind.cmake b/tests/auto/valgrind/valgrind.cmake index fa7d8b4b60b..e502c0c69d4 100644 --- a/tests/auto/valgrind/valgrind.cmake +++ b/tests/auto/valgrind/valgrind.cmake @@ -14,17 +14,15 @@ function(extend_valgrind_test targetName) callgrind/callgrindparser.h callgrind/callgrindparser.cpp callgrind/callgrindproxymodel.h callgrind/callgrindproxymodel.cpp callgrind/callgrindstackbrowser.h callgrind/callgrindstackbrowser.cpp - valgrindrunner.h valgrindrunner.cpp + valgrindprocess.h valgrindprocess.cpp xmlprotocol/announcethread.h xmlprotocol/announcethread.cpp xmlprotocol/error.h xmlprotocol/error.cpp xmlprotocol/errorlistmodel.h xmlprotocol/errorlistmodel.cpp xmlprotocol/frame.h xmlprotocol/frame.cpp - xmlprotocol/modelhelpers.h xmlprotocol/modelhelpers.cpp xmlprotocol/parser.h xmlprotocol/parser.cpp xmlprotocol/stack.h xmlprotocol/stack.cpp xmlprotocol/stackmodel.h xmlprotocol/stackmodel.cpp xmlprotocol/status.h xmlprotocol/status.cpp xmlprotocol/suppression.h xmlprotocol/suppression.cpp - xmlprotocol/threadedparser.h xmlprotocol/threadedparser.cpp ) endfunction() diff --git a/tests/auto/valgrind/valgrindautotest.qbs b/tests/auto/valgrind/valgrindautotest.qbs index 88b52b61393..92b695241de 100644 --- a/tests/auto/valgrind/valgrindautotest.qbs +++ b/tests/auto/valgrind/valgrindautotest.qbs @@ -26,7 +26,7 @@ QtcAutotest { name: "Other files from plugin" prefix: product.pluginDir + "/" files: [ - "valgrindrunner.h", "valgrindrunner.cpp", + "valgrindprocess.h", "valgrindprocess.cpp", ] } cpp.includePaths: base.concat([project.ide_source_tree + "/src/plugins"]) diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index 37d302c4424..de324714a2b 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -17,6 +17,6 @@ add_subdirectory(proparser) # add_subdirectory(search) add_subdirectory(shootout) add_subdirectory(spinner) -add_subdirectory(subdirfileiterator) +add_subdirectory(subdirfilecontainer) add_subdirectory(tasking) add_subdirectory(widgets) diff --git a/tests/manual/cmakepresets/CMakeLists.txt b/tests/manual/cmakeprojectmanager/cmakepresets/CMakeLists.txt similarity index 100% rename from tests/manual/cmakepresets/CMakeLists.txt rename to tests/manual/cmakeprojectmanager/cmakepresets/CMakeLists.txt diff --git a/tests/manual/cmakepresets/CMakePresets.json b/tests/manual/cmakeprojectmanager/cmakepresets/CMakePresets.json similarity index 100% rename from tests/manual/cmakepresets/CMakePresets.json rename to tests/manual/cmakeprojectmanager/cmakepresets/CMakePresets.json diff --git a/tests/manual/cmakepresets/main.cpp b/tests/manual/cmakeprojectmanager/cmakepresets/main.cpp similarity index 100% rename from tests/manual/cmakepresets/main.cpp rename to tests/manual/cmakeprojectmanager/cmakepresets/main.cpp diff --git a/tests/manual/cmakepresets/mainwindow.cpp b/tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.cpp similarity index 100% rename from tests/manual/cmakepresets/mainwindow.cpp rename to tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.cpp diff --git a/tests/manual/cmakepresets/mainwindow.h b/tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.h similarity index 100% rename from tests/manual/cmakepresets/mainwindow.h rename to tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.h diff --git a/tests/manual/cmakepresets/mainwindow.ui b/tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.ui similarity index 100% rename from tests/manual/cmakepresets/mainwindow.ui rename to tests/manual/cmakeprojectmanager/cmakepresets/mainwindow.ui diff --git a/tests/manual/cmakepresets/mingw.json b/tests/manual/cmakeprojectmanager/cmakepresets/mingw.json similarity index 100% rename from tests/manual/cmakepresets/mingw.json rename to tests/manual/cmakeprojectmanager/cmakepresets/mingw.json diff --git a/tests/manual/cmakepresets/msvc-toolchain.cmake b/tests/manual/cmakeprojectmanager/cmakepresets/msvc-toolchain.cmake similarity index 100% rename from tests/manual/cmakepresets/msvc-toolchain.cmake rename to tests/manual/cmakeprojectmanager/cmakepresets/msvc-toolchain.cmake diff --git a/tests/manual/cmakepresets/msvc.json b/tests/manual/cmakeprojectmanager/cmakepresets/msvc.json similarity index 100% rename from tests/manual/cmakepresets/msvc.json rename to tests/manual/cmakeprojectmanager/cmakepresets/msvc.json diff --git a/tests/manual/cmakeprojectmanager/completion/.gitignore b/tests/manual/cmakeprojectmanager/completion/.gitignore new file mode 100644 index 00000000000..4a0b530afd2 --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/.gitignore @@ -0,0 +1,74 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/tests/manual/cmakeprojectmanager/completion/CMakeLists.txt b/tests/manual/cmakeprojectmanager/completion/CMakeLists.txt new file mode 100644 index 00000000000..ae03231e90d --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/CMakeLists.txt @@ -0,0 +1,92 @@ +# Only parameters for the function should be mentioned, +# In the case for cmake_minimum_required should be +# VERSION and FATAL_ERROR +cmake_minimum_required(VERSION 3.15) + +# Mouse hover over "project" should display a help popup +project(completion LANGUAGES CXX) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +# F2 on the URL should open the url in the web browser +# https://doc.qt.io/qt-6/cmake-get-started.html + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# here code completion for "find_package(Qt|" should popup +# Qt, Qt3, Qt4 and Qt6 +#find_package(Qt + +find_package(Qt6 REQUIRED COMPONENTS Widgets) + +# here code completion "find_package(std|" should popup stdvector +#find_package(std + +qt_standard_project_setup() + +set(SOURCE_FILES + mainwindow.ui + mainwindow.cpp mainwindow.h + main.cpp + + # here code completion for "main|" should popup the source files + #main +) + +# here code completion for "if (SOU|" should popup SOURCE_FILES +#if (SOU + +if (SOURCE_FILES) + # here code completion for "print_v|" should popup "print_variables" + #print_v + + # here code completion for "include(CMakePrint|" should popup "CMakePrintHelpers" + #include(CMakePrint + + # + # F2 on CMakePrintHelpers would open the CMake module + # on cmake_print_variables would open the cursor at the function + # on SOURCE_FILES would jump above to the set(SOURCE_FILES + # + include(CMakePrintHelpers) + cmake_print_variables(SOURCE_FILES) +endif() + +# here code completion for "if (ENV{|" should popup the CMake supported environment variables +# also deleting { and typing again { should trigger the menu +#if (ENV{ + +# hover over CXX should display the help for the CXX environment variable +# and F1 should open the help page +message(STATUS $ENV{CXX}) + +qt_add_executable(${PROJECT_NAME} ${SOURCE_FILES}) + +# here code completino on "target_link_libraries(comp|" should poupup "completion" +# with a hammer icon, which suggests a project target +#target_link_libraries(comp + +# here code completion after "target_link_libraries(completion PRIVATE Qt6::Wid|" +# should complete with "Qt6::Widgets" having a grayish hammer icon + +#target_link_libraries(completion PRIVATE Qt6::Wid + + +# F2 on "completion" would jump above to qt_add_executable +target_link_libraries(completion PRIVATE Qt6::Widgets) + +# F2 on ${PROJECT_NAME} would jump above to qt_add_executable +set_target_properties(${PROJECT_NAME} PROPERTIES + # F1 on WIN32_EXECUTABLE should open the help + WIN32_EXECUTABLE ON + MACOSX_BUNDLE ON + + # here completion for "WIN32|" should popup WIN32_EXECUTABLE + #WIN32 +) + +include(JustACacheVariable) +# here code completion for "if (JUST|" should complete with if (JUST_A_CACHE_VARIABLE +# as a tooltip containing the description +#if (JUST diff --git a/tests/manual/cmakeprojectmanager/completion/README.md b/tests/manual/cmakeprojectmanager/completion/README.md new file mode 100644 index 00000000000..7d96bd3f17a --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/README.md @@ -0,0 +1 @@ +Qt6 project to test code completion and navigation in CMakeLists.txt diff --git a/tests/manual/cmakeprojectmanager/completion/cmake/Findstdvector.cmake b/tests/manual/cmakeprojectmanager/completion/cmake/Findstdvector.cmake new file mode 100644 index 00000000000..7409b65b0fd --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/cmake/Findstdvector.cmake @@ -0,0 +1,61 @@ +# https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#a-sample-find-module + +#[=======================================================================[.rst: +Findstdvector +------- + +Finds the stdvector library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``stdvector::stdvector`` +The stdvector library + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``stdvector_FOUND`` +True if the system has the stdvector library. +``stdvector_INCLUDE_DIRS`` +Include directories needed to use stdvector. + +Cache Variables +^^^^^^^^^^^^^^^ + +The following cache variables may also be set: + +``stdvector_INCLUDE_DIR`` +The directory containing ``vector``. + +#]=======================================================================] + +find_path(stdvector_INCLUDE_DIR + NAMES vector +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(stdvector + FOUND_VAR stdvector_FOUND + REQUIRED_VARS + stdvector_INCLUDE_DIR +) + +if(stdvector_FOUND) + set(stdvector_INCLUDE_DIRS ${stdvector_INCLUDE_DIR}) +endif() + +if(stdvector_FOUND AND NOT TARGET stdvector::stdvector) + add_library(stdvector::stdvector UNKNOWN IMPORTED) + set_target_properties(stdvector::stdvector PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${stdvector_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced( + stdvector_INCLUDE_DIR +) diff --git a/tests/manual/cmakeprojectmanager/completion/cmake/JustACacheVariable.cmake b/tests/manual/cmakeprojectmanager/completion/cmake/JustACacheVariable.cmake new file mode 100644 index 00000000000..ce9ed4aab42 --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/cmake/JustACacheVariable.cmake @@ -0,0 +1 @@ +set(JUST_A_CACHE_VARIABLE ON CACHE BOOL "This was set from cmake/JustACacheVariable" FORCE) diff --git a/tests/manual/cmakeprojectmanager/completion/main.cpp b/tests/manual/cmakeprojectmanager/completion/main.cpp new file mode 100644 index 00000000000..b368a389e34 --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/main.cpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" + +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/tests/manual/cmakeprojectmanager/completion/mainwindow.cpp b/tests/manual/cmakeprojectmanager/completion/mainwindow.cpp new file mode 100644 index 00000000000..5e056ab090f --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/mainwindow.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/manual/cmakeprojectmanager/completion/mainwindow.h b/tests/manual/cmakeprojectmanager/completion/mainwindow.h new file mode 100644 index 00000000000..f877d77cd1b --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/mainwindow.h @@ -0,0 +1,25 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/tests/manual/cmakeprojectmanager/completion/mainwindow.ui b/tests/manual/cmakeprojectmanager/completion/mainwindow.ui new file mode 100644 index 00000000000..0e5f87509e6 --- /dev/null +++ b/tests/manual/cmakeprojectmanager/completion/mainwindow.ui @@ -0,0 +1,24 @@ +<ui version="4.0"> + <author/> + <comment/> + <exportmacro/> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QMenuBar" name="menubar"/> + <widget class="QWidget" name="centralwidget"/> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <pixmapfunction/> + <connections/> +</ui> diff --git a/tests/manual/conan/CMakeLists.txt b/tests/manual/cmakeprojectmanager/conan/CMakeLists.txt similarity index 100% rename from tests/manual/conan/CMakeLists.txt rename to tests/manual/cmakeprojectmanager/conan/CMakeLists.txt diff --git a/tests/manual/conan/conanfile.txt b/tests/manual/cmakeprojectmanager/conan/conanfile.txt similarity index 100% rename from tests/manual/conan/conanfile.txt rename to tests/manual/cmakeprojectmanager/conan/conanfile.txt diff --git a/tests/manual/conan/main.cpp b/tests/manual/cmakeprojectmanager/conan/main.cpp similarity index 100% rename from tests/manual/conan/main.cpp rename to tests/manual/cmakeprojectmanager/conan/main.cpp diff --git a/tests/manual/vcpkg/CMakeLists.txt b/tests/manual/cmakeprojectmanager/vcpkg/CMakeLists.txt similarity index 100% rename from tests/manual/vcpkg/CMakeLists.txt rename to tests/manual/cmakeprojectmanager/vcpkg/CMakeLists.txt diff --git a/tests/manual/vcpkg/README.md b/tests/manual/cmakeprojectmanager/vcpkg/README.md similarity index 100% rename from tests/manual/vcpkg/README.md rename to tests/manual/cmakeprojectmanager/vcpkg/README.md diff --git a/tests/manual/vcpkg/main.cpp b/tests/manual/cmakeprojectmanager/vcpkg/main.cpp similarity index 100% rename from tests/manual/vcpkg/main.cpp rename to tests/manual/cmakeprojectmanager/vcpkg/main.cpp diff --git a/tests/manual/vcpkg/vcpkg.json b/tests/manual/cmakeprojectmanager/vcpkg/vcpkg.json similarity index 100% rename from tests/manual/vcpkg/vcpkg.json rename to tests/manual/cmakeprojectmanager/vcpkg/vcpkg.json diff --git a/tests/manual/debugger/gui/CMakeLists.txt b/tests/manual/debugger/gui/CMakeLists.txt index 15c0aeb9491..412b0cdc429 100644 --- a/tests/manual/debugger/gui/CMakeLists.txt +++ b/tests/manual/debugger/gui/CMakeLists.txt @@ -5,7 +5,7 @@ project(manual_test_debugger_gui LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets) diff --git a/tests/manual/debugger/gui/gui.qbs b/tests/manual/debugger/gui/gui.qbs index 88e9bd53071..caa73bc211e 100644 --- a/tests/manual/debugger/gui/gui.qbs +++ b/tests/manual/debugger/gui/gui.qbs @@ -1,6 +1,6 @@ QtApplication { name: "Manual debugger gui test" - Depends { name: "qtc" } + Depends { name: "Qt.widgets" } files: [ diff --git a/tests/manual/debugger/gui/mainwindow.cpp b/tests/manual/debugger/gui/mainwindow.cpp index 831f266ca0b..bea62f56a5e 100644 --- a/tests/manual/debugger/gui/mainwindow.cpp +++ b/tests/manual/debugger/gui/mainwindow.cpp @@ -306,9 +306,8 @@ void MainWindow::on_actionExtTypes_triggered() void MainWindow::on_actionForeach_triggered() { - QStringList sl; - sl << "1" << "2" << "3"; - foreach(const QString &s, sl) + QStringList sl {"1", "2", "3"}; + for (const QString &s : std::as_const(sl)) qDebug() << s; sl.clear(); qDebug() << sl; diff --git a/tests/manual/debugger/simple/CMakeLists.txt b/tests/manual/debugger/simple/CMakeLists.txt index 3701d58bbd7..abe4c5b7812 100644 --- a/tests/manual/debugger/simple/CMakeLists.txt +++ b/tests/manual/debugger/simple/CMakeLists.txt @@ -5,7 +5,7 @@ project(simple_test_app LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if (WINDOWS) diff --git a/tests/manual/debugger/simple/simple.qbs b/tests/manual/debugger/simple/simple.qbs index c05ef68ac8a..9f2b2c03e8b 100644 --- a/tests/manual/debugger/simple/simple.qbs +++ b/tests/manual/debugger/simple/simple.qbs @@ -1,11 +1,6 @@ -import qbs -import qbs.FileInfo - Project { name: "Manual debugger simple tests" - condition: project.withAutotests - references: [ "simple_test_app.qbs", "simple_test_plugin.qbs" diff --git a/tests/manual/debugger/simple/simple_test_app.cpp b/tests/manual/debugger/simple/simple_test_app.cpp index 5b38d85f3ee..f32c1baaf15 100644 --- a/tests/manual/debugger/simple/simple_test_app.cpp +++ b/tests/manual/debugger/simple/simple_test_app.cpp @@ -5657,8 +5657,8 @@ namespace basic { quint64 c = std::numeric_limits<quint64>::max() - quint64(1); BREAK_HERE; // Check a -1143861252567568256 qint64. - // Check b -1143861252567568256 quint64. - // Check c -2 quint64. + // Check b 17302882821141983360 quint64. + // Check c 18446744073709551614 quint64. // Continue. dummyStatement(&a, &b, &c); } diff --git a/tests/manual/debugger/simple/simple_test_app.qbs b/tests/manual/debugger/simple/simple_test_app.qbs index ca2c56229e4..59cf3fe3074 100644 --- a/tests/manual/debugger/simple/simple_test_app.qbs +++ b/tests/manual/debugger/simple/simple_test_app.qbs @@ -5,7 +5,6 @@ CppApplication { name: "Manual Test Simple Application" targetName: "simple_test_app" - Depends { name: "qtc" } Depends { name: "Qt.core" } Depends { name: "Qt.core-private"; required: false; condition: Qt.core.versionMajor > 4 } Depends { name: "Qt.core5compat"; condition: Qt.core.versionMajor > 5 } diff --git a/tests/manual/debugger/simple/simple_test_plugin.qbs b/tests/manual/debugger/simple/simple_test_plugin.qbs index eca6791c02c..20ff3609fcf 100644 --- a/tests/manual/debugger/simple/simple_test_plugin.qbs +++ b/tests/manual/debugger/simple/simple_test_plugin.qbs @@ -1,10 +1,11 @@ import qbs.FileInfo DynamicLibrary { + condition: !qtc.present || qtc.withAutotests name: "Manual Test Simple Plugin" targetName: "simple_test_plugin" - Depends { name: "qtc" } + Depends { name: "qtc"; required: false } Depends { name: "Qt.core" } files: [ "simple_test_plugin.cpp" ] diff --git a/tests/manual/deviceshell/deviceshell.qbs b/tests/manual/deviceshell/deviceshell.qbs index 9a4409793fe..205c83a11f6 100644 --- a/tests/manual/deviceshell/deviceshell.qbs +++ b/tests/manual/deviceshell/deviceshell.qbs @@ -1,22 +1,19 @@ import qbs.FileInfo -Project { - QtcManualtest { - name: "DeviceShell manualtest" +QtcManualTest { + name: "DeviceShell manualtest" - Depends { name: "Utils" } - Depends { name: "app_version_header" } + Depends { name: "Utils" } + Depends { name: "app_version_header" } - files: [ - "tst_deviceshell.cpp", - ] - cpp.defines: { - var defines = base; - var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, - qtc.ide_libexec_path); - var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath); - defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"'); - return defines; - } + cpp.defines: { + var defines = base; + var absLibExecPath = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, + qtc.ide_libexec_path); + var relLibExecPath = FileInfo.relativePath(destinationDirectory, absLibExecPath); + defines.push('TEST_RELATIVE_LIBEXEC_PATH="' + relLibExecPath + '"'); + return defines; } + + files: "tst_deviceshell.cpp" } diff --git a/tests/manual/fakevim/fakevim.qbs b/tests/manual/fakevim/fakevim.qbs index 77e7253830a..051f1a04fc6 100644 --- a/tests/manual/fakevim/fakevim.qbs +++ b/tests/manual/fakevim/fakevim.qbs @@ -1,6 +1,6 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Manual FakeVim test" type: ["application"] @@ -11,9 +11,7 @@ QtcManualtest { cpp.defines: base.concat(["FAKEVIM_STANDALONE"]) cpp.includePaths: fakeVimDir - files: [ - "main.cpp" - ] + files: "main.cpp" Group { name: "FakeVim files" diff --git a/tests/manual/layoutbuilder/demo/main.cpp b/tests/manual/layoutbuilder/demo/main.cpp index 2dd95dfddbf..586a572a9b4 100644 --- a/tests/manual/layoutbuilder/demo/main.cpp +++ b/tests/manual/layoutbuilder/demo/main.cpp @@ -17,9 +17,17 @@ int main(int argc, char *argv[]) title("Hello World"), Column { - TextEdit { - id(textId), - text("Hallo") + If { false, { + TextEdit { + id(textId), + text("Hallo") + }, + }, { + TextEdit { + id(textId), + text("Och noe") + }, + } }, Row { diff --git a/tests/manual/layoutbuilder/experimental/CMakeLists.txt b/tests/manual/layoutbuilder/experimental/CMakeLists.txt new file mode 100644 index 00000000000..6d2568d7cf4 --- /dev/null +++ b/tests/manual/layoutbuilder/experimental/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) + +project(experimental LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(experimental main.cpp) diff --git a/tests/manual/layoutbuilder/experimental/main.cpp b/tests/manual/layoutbuilder/experimental/main.cpp new file mode 100644 index 00000000000..b9c9a2bb8a4 --- /dev/null +++ b/tests/manual/layoutbuilder/experimental/main.cpp @@ -0,0 +1,170 @@ +// Copyright (C) 2023 André Pönitz +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <array> +#include <initializer_list> +#include <tuple> + +#include <stdio.h> +#include <string.h> + +using namespace std; + +// std::tuple implementations are quite fat, we might want to use a cut-down version of it. +template <typename ...Args> +using Tuple = std::tuple<Args...>; + + +// The main dispatcher(s) + +auto doit_tuple(auto x, auto t); // Tuple-based + +auto doit(auto x, auto ...a); // Args expanded. + + +// Convenience for the setter implementors. Removes the need to use get<n> +// in the setters explicitly. +template <typename X, typename Id, typename Arg1> +void doit_tuple(X *x, const Tuple<Id, Arg1> *a) +{ + doit(x, get<0>(*a), get<1>(*a)); +} + +template <typename X, typename Id, typename Arg1, typename Arg2> +void doit_tuple(X *x, const Tuple<Id, Arg1, Arg2> *a) +{ + doit(x, get<0>(*a), get<1>(*a), get<2>(*a)); +} + + +// "Builder base". + +template <typename X> +struct B +{ +private: + template <typename ...Args> + struct Doer + { + static void doit(X *x, const Tuple<Args...> *args) + { + doit_tuple(x, args); + } + }; + +public: + struct I + { + template <typename ...Args> + I(const Tuple<Args...> &p) + { + auto f = &Doer<Args...>::doit; + memcpy(&f_, &f, sizeof(f_)); + p_ = &p; + } + + void (*f_)(X *, const void *); + const void *p_; + }; + + B(initializer_list<I> ps) { + for (auto && p : ps) + (*p.f_)(static_cast<X *>(this), p.p_); + } +}; + + +////////////////////////////////////////////// + + +// Builder Classes preparation + +// We need one class for each user visible Builder type. +// This is usually wrappeng one "backend" type, e.g. there +// could be a PushButton wrapping QPushButton. + +struct D : B<D> { using B<D>::B; }; + +struct E : B<E> { using B<E>::B; }; + + +// "Setters" preparation + +// We need one 'Id' (and a corresponding function wrapping arguments into a +// tuple marked by this id) per 'name' of "backend" setter member function, +// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText. +// The name of the Id does not have to match the backend names as it +// is mapped per-backend-type in the respective setter implementation +// but we assume that it generally makes sense to stay close to the +// wrapped API name-wise. + +struct TextId {}; +auto text(auto ...x) { return Tuple{TextId{}, x...}; } + +struct SumId {}; +auto sum(auto ...x) { return Tuple{SumId{}, x...}; } + +struct OtherId {}; +auto other(auto ...x) { return Tuple{OtherId{}, x...}; } + +struct LargeId {}; +auto large(auto ...x) { return Tuple{LargeId{}, x...}; } + +struct Large { + Large() { for (int i = 0; i < 100; ++i) x[i] = i; } + int x[100]; +}; + + +// Setter implementation + +// These are free functions overloaded on the type of builder object +// and setter id. The function implementations are independent, but +// the base expectation is that they will forwards to the backend +// type's setter. + +void doit(D *, TextId, const char *t) +{ + printf("D text: %s\n", t); +} + +void doit(D *, OtherId, int t) +{ + printf("D other: %d\n", t); +} + +void doit(D *, OtherId, double t) +{ + printf("D other: %f\n", t); +} + +void doit(D *, LargeId, Large t) +{ + printf("D other: %d\n", t.x[50]); +} + +void doit(E *, OtherId, double t) +{ + printf("E other: %f\n", t); +} + +void doit(E *, SumId, int a, int b) +{ + printf("E sum: %d + %d = %d\n", a, b, a + b); +} + + +int main() +{ + E { + other(4.55), + sum(1, 2) + }; + D { + //other(5), + other(5.5), // - does not compile as there is no doit(D*, OtherId, double) + text("abc"), + large(Large()) + }; +} diff --git a/tests/manual/manual.qbs b/tests/manual/manual.qbs index 923f0fe5f0e..ba774823c12 100644 --- a/tests/manual/manual.qbs +++ b/tests/manual/manual.qbs @@ -1,9 +1,7 @@ -import qbs - Project { - name: "QtcManualtests" + name: "QtcManualTests" - condition: project.withAutotests + qbsSearchPaths: "qbs" references: [ "debugger/gui/gui.qbs", @@ -14,7 +12,7 @@ Project { "proparser/testreader.qbs", "shootout/shootout.qbs", "spinner/spinner.qbs", - "subdirfileiterator/subdirfileiterator.qbs", + "subdirfilecontainer/subdirfilecontainer.qbs", "tasking/demo/demo.qbs", "tasking/imagescaling/imagescaling.qbs", "widgets/widgets.qbs", diff --git a/tests/manual/meson/mesonsampleproject/meson.build b/tests/manual/meson/mesonsampleproject/meson.build index 901285a587e..8edea0d4d53 100644 --- a/tests/manual/meson/mesonsampleproject/meson.build +++ b/tests/manual/meson/mesonsampleproject/meson.build @@ -4,7 +4,7 @@ project('mesonsampleproject', 'cpp',default_options : ['cpp_std=c++11']) qt5 = import('qt5') qt5dep = dependency('qt5', modules : ['Core', 'Widgets']) -translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true) +#translations = qt5.compile_translations(ts_files : 'mesonsampleproject_fr_FR.ts', build_by_default : true) generated_files = qt5.preprocess( moc_headers : 'mesonsampleproject.h', diff --git a/tests/manual/pluginview/plugins/plugin1/plugin1.qbs b/tests/manual/pluginview/plugins/plugin1/plugin1.qbs index e709f0208ae..1698504808d 100644 --- a/tests/manual/pluginview/plugins/plugin1/plugin1.qbs +++ b/tests/manual/pluginview/plugins/plugin1/plugin1.qbs @@ -1,6 +1,6 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Manual test plugin1" targetName: "plugin1" type: [ "dynamiclibrary" ] diff --git a/tests/manual/pluginview/plugins/plugin2/plugin2.qbs b/tests/manual/pluginview/plugins/plugin2/plugin2.qbs index 4c97412990c..c1dad909b0a 100644 --- a/tests/manual/pluginview/plugins/plugin2/plugin2.qbs +++ b/tests/manual/pluginview/plugins/plugin2/plugin2.qbs @@ -1,6 +1,6 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Manual test plugin2" targetName: "plugin2" type: [ "dynamiclibrary" ] diff --git a/tests/manual/pluginview/plugins/plugin3/plugin3.qbs b/tests/manual/pluginview/plugins/plugin3/plugin3.qbs index 659fcda5063..69f309bfcda 100644 --- a/tests/manual/pluginview/plugins/plugin3/plugin3.qbs +++ b/tests/manual/pluginview/plugins/plugin3/plugin3.qbs @@ -1,6 +1,6 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Manual test plugin3" targetName: "plugin3" type: [ "dynamiclibrary" ] diff --git a/tests/manual/pluginview/pluginview.qbs b/tests/manual/pluginview/pluginview.qbs index e7ea64bf5be..ba6baa53f86 100644 --- a/tests/manual/pluginview/pluginview.qbs +++ b/tests/manual/pluginview/pluginview.qbs @@ -1,7 +1,7 @@ Project { name: "Manual plugin test" - QtcManualtest { + QtcManualTest { name: "Manual plugin view test" Depends { name: "ExtensionSystem" } diff --git a/tests/manual/proparser/testreader.qbs b/tests/manual/proparser/testreader.qbs index 4d71ff0f21a..daf1ae94930 100644 --- a/tests/manual/proparser/testreader.qbs +++ b/tests/manual/proparser/testreader.qbs @@ -1,4 +1,4 @@ -QtcManualtest { +QtcManualTest { name: "Manual ProParser test" Depends { name: "Qt.core" } @@ -18,9 +18,7 @@ QtcManualtest { cpp.dynamicLibraries: "advapi32" } - files: [ - "main.cpp", - ] + files: "main.cpp" Group { name: "ProParser files" diff --git a/tests/manual/qbs/imports/CppApplication.qbs b/tests/manual/qbs/imports/CppApplication.qbs new file mode 100644 index 00000000000..e72e8fb2d06 --- /dev/null +++ b/tests/manual/qbs/imports/CppApplication.qbs @@ -0,0 +1 @@ +QtcManualTest {} diff --git a/tests/manual/qbs/imports/QtApplication.qbs b/tests/manual/qbs/imports/QtApplication.qbs new file mode 100644 index 00000000000..e72e8fb2d06 --- /dev/null +++ b/tests/manual/qbs/imports/QtApplication.qbs @@ -0,0 +1 @@ +QtcManualTest {} diff --git a/tests/manual/shootout/shootout.qbs b/tests/manual/shootout/shootout.qbs index 85626c5b0d8..6a89a374899 100644 --- a/tests/manual/shootout/shootout.qbs +++ b/tests/manual/shootout/shootout.qbs @@ -1,7 +1,5 @@ -QtcManualtest { +QtcManualTest { name: "Manual debugger shootout test" - files: [ - "tst_codesize.cpp", - ] + files: "tst_codesize.cpp" } diff --git a/tests/manual/spinner/spinner.qbs b/tests/manual/spinner/spinner.qbs index 39b644da067..4cb1b386146 100644 --- a/tests/manual/spinner/spinner.qbs +++ b/tests/manual/spinner/spinner.qbs @@ -1,13 +1,11 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Spinner example" type: ["application"] Depends { name: "Qt"; submodules: ["widgets"] } Depends { name: "Spinner" } - files: [ - "main.cpp", - ] + files: "main.cpp" } diff --git a/tests/manual/subdirfileiterator/CMakeLists.txt b/tests/manual/subdirfilecontainer/CMakeLists.txt similarity index 80% rename from tests/manual/subdirfileiterator/CMakeLists.txt rename to tests/manual/subdirfilecontainer/CMakeLists.txt index 9613e197b77..ace5958396c 100644 --- a/tests/manual/subdirfileiterator/CMakeLists.txt +++ b/tests/manual/subdirfilecontainer/CMakeLists.txt @@ -1,10 +1,10 @@ file(RELATIVE_PATH RELATIVE_TEST_PATH "${PROJECT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") file(RELATIVE_PATH TEST_RELATIVE_LIBEXEC_PATH "/${RELATIVE_TEST_PATH}" "/${IDE_LIBEXEC_PATH}") -add_qtc_test(tst_manual_subdirfileiterator +add_qtc_test(tst_manual_subdirfilecontainer MANUALTEST DEFINES "TEST_RELATIVE_LIBEXEC_PATH=\"${TEST_RELATIVE_LIBEXEC_PATH}\"" DEPENDS Utils app_version SOURCES - tst_subdirfileiterator.cpp + tst_subdirfilecontainer.cpp ) diff --git a/tests/manual/subdirfileiterator/subdirfileiterator.qbs b/tests/manual/subdirfilecontainer/subdirfilecontainer.qbs similarity index 81% rename from tests/manual/subdirfileiterator/subdirfileiterator.qbs rename to tests/manual/subdirfilecontainer/subdirfilecontainer.qbs index 852fb9b545b..2fade953d35 100644 --- a/tests/manual/subdirfileiterator/subdirfileiterator.qbs +++ b/tests/manual/subdirfilecontainer/subdirfilecontainer.qbs @@ -1,15 +1,13 @@ import qbs.FileInfo -QtcManualtest { - name: "Manual SubDirFileIterator test" +QtcManualTest { + name: "Manual SubDirFileContainer test" type: ["application"] Depends { name: "Utils" } Depends { name: "app_version_header" } - files: [ - "tst_subdirfileiterator.cpp", - ] + files: "tst_subdirfilecontainer.cpp" cpp.defines: { var defines = base; diff --git a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp b/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp similarity index 62% rename from tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp rename to tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp index 50bbd222c9a..89993b6a2b6 100644 --- a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp +++ b/tests/manual/subdirfilecontainer/tst_subdirfilecontainer.cpp @@ -19,9 +19,10 @@ using namespace Tasking; using namespace Utils; -static const int s_subDirsCount = 8; -static const int s_subFilesCount = 8; -static const int s_treeDepth = 4; +static const int s_topLevelSubDirsCount = 128; +static const int s_subDirsCount = 4; +static const int s_subFilesCount = 4; +static const int s_treeDepth = 5; static const QDir::Filters s_filters = QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot; @@ -29,6 +30,16 @@ static const QDir::Filters s_filters = QDir::Dirs | QDir::Files | QDir::Hidden static const char s_dirPrefix[] = "dir_"; static const char s_filePrefix[] = "file_"; +static QString dirName(int suffix) +{ + return QString("%1%2").arg(s_dirPrefix).arg(suffix); +} + +static QString fileName(int suffix) +{ + return QString("%1%2.txt").arg(s_filePrefix).arg(suffix); +} + static int expectedDirsCountHelper(int depth) { if (depth == 1) @@ -36,9 +47,9 @@ static int expectedDirsCountHelper(int depth) return expectedDirsCountHelper(depth - 1) * s_subDirsCount + 1; // +1 -> dir itself } -static int expectedDirsCount(int tasksCount) +static int expectedDirsCount() { - return expectedDirsCountHelper(s_treeDepth) * tasksCount + 1; // +1 -> root temp dir + return expectedDirsCountHelper(s_treeDepth) * s_topLevelSubDirsCount; } static int expectedFilesCountHelper(int depth) @@ -48,48 +59,52 @@ static int expectedFilesCountHelper(int depth) return expectedFilesCountHelper(depth - 1) * s_subDirsCount + s_subFilesCount; } -static int expectedFilesCount(int tasksCount) +static int expectedFilesCount() { - return expectedFilesCountHelper(s_treeDepth) * tasksCount + 1; // +1 -> fileTemplate.txt + return expectedFilesCountHelper(s_treeDepth) * s_topLevelSubDirsCount + 1; // +1 -> fileTemplate.txt } -static void generate(QPromise<void> &promise, const QString &parentPath, - const QString &templateFile, int depth) +static int threadsCount() +{ + return qMax(QThread::idealThreadCount() - 1, 1); // "- 1" -> left for main thread +} + +static bool generateOriginal(const QString &parentPath, const QString &templateFile, int depth) { const QDir parentDir(parentPath); for (int i = 0; i < s_subFilesCount; ++i) - QFile::copy(templateFile, parentDir.filePath(QString("%1%2").arg(s_filePrefix).arg(i))); + QFile::copy(templateFile, parentDir.filePath(fileName(i))); if (depth == 0) - return; + return true; - if (promise.isCanceled()) - return; + const QString originalDirName = dirName(0); + if (!parentDir.mkdir(originalDirName)) + return false; - const QString templateDir("dirTemplate"); - if (!parentDir.mkdir(templateDir)) { - promise.future().cancel(); - return; - } - - const QString templateDirPath = parentDir.filePath(templateDir); - generate(promise, templateDirPath, templateFile, depth - 1); - - if (promise.isCanceled()) - return; - - for (int i = 0; i < s_subDirsCount - 1; ++i) { - const QString dirName = QString("%1%2").arg(s_dirPrefix).arg(i); - const FilePath source = FilePath::fromString(templateDirPath); - const FilePath destination = FilePath::fromString(parentDir.filePath(dirName)); - if (!source.copyRecursively(destination)) { - promise.future().cancel(); - return; - } + const QString originalDirPath = parentDir.filePath(originalDirName); + if (!generateOriginal(originalDirPath, templateFile, depth - 1)) + return false; + + for (int i = 1; i < s_subDirsCount; ++i) { + const FilePath source = FilePath::fromString(originalDirPath); + const FilePath destination = FilePath::fromString(parentDir.filePath(dirName(i))); + if (!source.copyRecursively(destination)) + return false; } + return true; } -class tst_SubDirFileIterator : public QObject +static void generateCopy(QPromise<void> &promise, const QString &sourcePath, + const QString &destPath) +{ + const FilePath source = FilePath::fromString(sourcePath); + const FilePath destination = FilePath::fromString(destPath); + if (!source.copyRecursively(destination)) + promise.future().cancel(); +} + +class tst_SubDirFileContainer : public QObject { Q_OBJECT @@ -103,14 +118,14 @@ private slots: + QLatin1String(TEST_RELATIVE_LIBEXEC_PATH)); LauncherInterface::setPathToLauncher(libExecPath); - qDebug() << "This manual test compares the performance of the SubDirFileIterator with " - "a manually written iterator using QDir::entryInfoList()."; + qDebug() << "This manual test compares the performance of the SubDirFileContainer with a " + "manually written iterator using QDir::entryInfoList() and with QDirIterator."; QTC_SCOPED_TIMER("GENERATING TEMPORARY FILES TREE"); - const int tasksCount = QThread::idealThreadCount(); - m_filesCount = expectedFilesCount(tasksCount); - m_dirsCount = expectedDirsCount(tasksCount); + m_threadsCount = threadsCount(); + m_filesCount = expectedFilesCount(); + m_dirsCount = expectedDirsCount(); m_tempDir.reset(new QTemporaryDir); - qDebug() << "Generating on" << tasksCount << "cores..."; + qDebug() << "Generating on" << m_threadsCount << "cores..."; qDebug() << "Generating" << m_filesCount << "files..."; qDebug() << "Generating" << m_dirsCount << "dirs..."; qDebug() << "Generating inside" << m_tempDir->path() << "dir..."; @@ -121,20 +136,25 @@ private slots: file.write("X"); } - // Parallelize tree generation const QDir parentDir(m_tempDir->path()); - const auto onSetup = [](const QString &parentPath, const QString &templateFile) { - return [parentPath, templateFile](Async<void> &async) { - async.setConcurrentCallData(generate, parentPath, templateFile, s_treeDepth); + const QString sourceDirName = dirName(0); + QVERIFY(parentDir.mkdir(sourceDirName)); + QVERIFY(generateOriginal(parentDir.filePath(sourceDirName), templateFile, s_treeDepth)); + + const auto onCopySetup = [](const QString &sourcePath, const QString &destPath) { + return [sourcePath, destPath](Async<void> &async) { + async.setConcurrentCallData(generateCopy, sourcePath, destPath); }; }; - QList<GroupItem> tasks {parallel}; - for (int i = 0; i < tasksCount; ++i) { - const QString dirName = QString("%1%2").arg(s_dirPrefix).arg(i); - QVERIFY(parentDir.mkdir(dirName)); - tasks.append(AsyncTask<void>(onSetup(parentDir.filePath(dirName), templateFile))); - } + // Parallelize tree generation + QList<GroupItem> tasks{parallelLimit(m_threadsCount)}; + for (int i = 1; i < s_topLevelSubDirsCount; ++i) { + const QString destDirName = dirName(i); + QVERIFY(parentDir.mkdir(destDirName)); + tasks.append(AsyncTask<void>(onCopySetup(parentDir.filePath(sourceDirName), + parentDir.filePath(destDirName)))); + } QVERIFY(TaskTree::runBlocking(tasks)); } @@ -147,18 +167,15 @@ private slots: FilePath::fromString(parentPath).removeRecursively(); }; - const QDir parentDir(m_tempDir->path()); const auto onSetup = [removeTree](const QString &parentPath) { return [parentPath, removeTree](Async<void> &async) { async.setConcurrentCallData(removeTree, parentPath); }; }; - QList<GroupItem> tasks {parallel}; - const int tasksCount = QThread::idealThreadCount(); - for (int i = 0; i < tasksCount; ++i) { - const QString dirName = QString("%1%2").arg(s_dirPrefix).arg(i); - tasks.append(AsyncTask<void>(onSetup(parentDir.filePath(dirName)))); - } + const QDir parentDir(m_tempDir->path()); + QList<GroupItem> tasks {parallelLimit(m_threadsCount)}; + for (int i = 0; i < s_topLevelSubDirsCount; ++i) + tasks.append(AsyncTask<void>(onSetup(parentDir.filePath(dirName(i))))); QVERIFY(TaskTree::runBlocking(tasks)); @@ -166,23 +183,23 @@ private slots: Singleton::deleteAll(); } - void testSubDirFileIterator() + void testSubDirFileContainer() { - QTC_SCOPED_TIMER("ITERATING with SubDirFileIterator"); + QTC_SCOPED_TIMER("ITERATING with FileContainer"); int filesCount = 0; { const FilePath root(FilePath::fromString(m_tempDir->path())); - SubDirFileIterator it({root}, {}, {}); - auto i = it.begin(); - const auto e = it.end(); - while (i != e) { + FileContainer container = SubDirFileContainer({root}, {}, {}); + auto it = container.begin(); + const auto end = container.end(); + while (it != end) { ++filesCount; - ++i; + ++it; if (filesCount % 100000 == 0) qDebug() << filesCount << '/' << m_filesCount << "files visited so far..."; } } - qDebug() << "Visited" << filesCount << "files."; + QCOMPARE(filesCount, m_filesCount); } void testManualIterator() @@ -196,7 +213,6 @@ private slots: std::unordered_set<QString> visitedDirs; visitedDirs.emplace(root.absolutePath()); QFileInfoList workingList = root.entryInfoList(s_filters); - ++dirsCount; // for root itself while (!workingList.isEmpty()) { const QFileInfo fi = workingList.takeLast(); const QString absoluteFilePath = fi.absoluteFilePath(); @@ -218,7 +234,8 @@ private slots: } } } - qDebug() << "Visited" << filesCount << "files and" << dirsCount << "directories."; + QCOMPARE(filesCount, m_filesCount); + QCOMPARE(dirsCount, m_dirsCount); } void testQDirIterator() @@ -227,7 +244,6 @@ private slots: int filesCount = 0; int dirsCount = 0; { - ++dirsCount; // for root itself QDirIterator it(m_tempDir->path(), s_filters, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); while (it.hasNext()) { @@ -246,15 +262,17 @@ private slots: } } } - qDebug() << "Visited" << filesCount << "files and" << dirsCount << "directories."; + QCOMPARE(filesCount, m_filesCount); + QCOMPARE(dirsCount, m_dirsCount); } private: + int m_threadsCount = 1; int m_dirsCount = 0; int m_filesCount = 0; std::unique_ptr<QTemporaryDir> m_tempDir; }; -QTEST_GUILESS_MAIN(tst_SubDirFileIterator) +QTEST_GUILESS_MAIN(tst_SubDirFileContainer) -#include "tst_subdirfileiterator.moc" +#include "tst_subdirfilecontainer.moc" diff --git a/tests/manual/tasking/demo/demo.qbs b/tests/manual/tasking/demo/demo.qbs index 9cf856bca21..3da7c13b056 100644 --- a/tests/manual/tasking/demo/demo.qbs +++ b/tests/manual/tasking/demo/demo.qbs @@ -1,6 +1,6 @@ import qbs.FileInfo -QtcManualtest { +QtcManualTest { name: "Tasking demo" type: ["application"] diff --git a/tests/manual/tasking/demo/main.cpp b/tests/manual/tasking/demo/main.cpp index 807def8e454..c48b894580f 100644 --- a/tests/manual/tasking/demo/main.cpp +++ b/tests/manual/tasking/demo/main.cpp @@ -163,29 +163,22 @@ int main(int argc, char *argv[]) std::unique_ptr<TaskTree> taskTree; - const auto createTask = [](TaskWidget *widget) -> GroupItem { - const auto setupTask = [](TaskWidget *widget) { - return [widget](milliseconds &taskObject) { - taskObject = milliseconds{widget->busyTime() * 1000}; - widget->setState(State::Running); - }; + const auto createTask = [](TaskWidget *widget) { + const milliseconds timeout{widget->busyTime() * 1000}; + const auto setupTask = [widget, timeout](milliseconds &taskObject) { + taskObject = timeout; + widget->setState(State::Running); }; - if (widget->isSuccess()) { - return TimeoutTask(setupTask(widget), - [widget](const milliseconds &) { widget->setState(State::Done); }, - [widget](const milliseconds &) { widget->setState(State::Error); }); - } const Group root { - finishAllAndError, - TimeoutTask(setupTask(widget)), + widget->isSuccess() ? stopOnError : finishAllAndError, + TimeoutTask(setupTask), onGroupDone([widget] { widget->setState(State::Done); }), onGroupError([widget] { widget->setState(State::Error); }) }; return root; }; - auto treeRoot = [&] { - + const auto recipe = [&] { const Group root { rootGroup->executeMode(), rootGroup->workflowPolicy(), @@ -237,21 +230,21 @@ int main(int argc, char *argv[]) // Non-task GUI handling - auto createTaskTree = [&] { - TaskTree *taskTree = new TaskTree(treeRoot()); + const auto createTaskTree = [&] { + TaskTree *taskTree = new TaskTree(recipe()); progressBar->setMaximum(taskTree->progressMaximum()); QObject::connect(taskTree, &TaskTree::progressValueChanged, progressBar, &QProgressBar::setValue); return taskTree; }; - auto stopTaskTree = [&] { + const auto stopTaskTree = [&] { if (taskTree) taskTree->stop(); // TODO: unlock GUI controls? }; - auto resetTaskTree = [&] { + const auto resetTaskTree = [&] { if (!taskTree) return; diff --git a/tests/manual/tasking/imagescaling/imagescaling.qbs b/tests/manual/tasking/imagescaling/imagescaling.qbs index ea0698c7292..b2287ffad8f 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.qbs +++ b/tests/manual/tasking/imagescaling/imagescaling.qbs @@ -1,6 +1,4 @@ -import qbs.FileInfo - -QtcManualtest { +QtcManualTest { name: "Tasking imagescaling" type: ["application"] diff --git a/tests/manual/widgets/common/themeselector.cpp b/tests/manual/widgets/common/themeselector.cpp index 2fbdd2424b8..8d3b3bf2db1 100644 --- a/tests/manual/widgets/common/themeselector.cpp +++ b/tests/manual/widgets/common/themeselector.cpp @@ -45,7 +45,7 @@ ThemeSelector::ThemeSelector(QWidget *parent) setCurrentText(appSettings.value(themeNameKey, "Flat Dark").toString()); setTheme(currentData().toString()); - connect(this, &QComboBox::currentTextChanged, [this]{ + connect(this, &QComboBox::currentTextChanged, this, [this] { setTheme(currentData().toString()); QSettings appSettings; appSettings.setValue(themeNameKey, currentText()); diff --git a/tests/manual/widgets/crumblepath/crumblepath.qbs b/tests/manual/widgets/crumblepath/crumblepath.qbs index c9b32a0810f..1f6b68e8b60 100644 --- a/tests/manual/widgets/crumblepath/crumblepath.qbs +++ b/tests/manual/widgets/crumblepath/crumblepath.qbs @@ -3,13 +3,10 @@ import "../common/common.qbs" as Common CppApplication { name: "Manual Test Utils CrumblePath" - Depends { name: "qtc" } Depends { name: "Core" } Depends { name: "Utils" } - files: [ - "tst_manual_widgets_crumblepath.cpp", - ] + files: "tst_manual_widgets_crumblepath.cpp" Common {} } diff --git a/tests/manual/widgets/infolabel/infolabel.qbs b/tests/manual/widgets/infolabel/infolabel.qbs index dfba3bd9ef7..df4aaf1a329 100644 --- a/tests/manual/widgets/infolabel/infolabel.qbs +++ b/tests/manual/widgets/infolabel/infolabel.qbs @@ -3,13 +3,10 @@ import "../common/common.qbs" as Common CppApplication { name: "Manual Test Utils InfoLabel" - Depends { name: "qtc" } Depends { name: "Core" } Depends { name: "Utils" } - files: [ - "tst_manual_widgets_infolabel.cpp", - ] + files: "tst_manual_widgets_infolabel.cpp" Common {} } diff --git a/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp b/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp index 288c1fb4a97..7e323ed167a 100644 --- a/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp +++ b/tests/manual/widgets/infolabel/tst_manual_widgets_infolabel.cpp @@ -6,7 +6,6 @@ #include <QApplication> #include <QFormLayout> #include <QMessageBox> -#include <QSettings> #include <utils/infolabel.h> @@ -58,7 +57,7 @@ int main(int argc, char *argv[]) auto withLink = new Utils::InfoLabel("With <a href=\"link clicked!\">link</a>", InfoLabel::Error); withLink->setElideMode(Qt::ElideNone); - QObject::connect(withLink, &QLabel::linkActivated, [widget](const QString& link){ + QObject::connect(withLink, &QLabel::linkActivated, &app, [widget](const QString& link) { QMessageBox::information(widget, {}, link); }); mainLayout->addWidget(withLink); diff --git a/tests/manual/widgets/layoutbuilder/layoutbuilder.qbs b/tests/manual/widgets/layoutbuilder/layoutbuilder.qbs index f51ad6883d8..6ebe176fb9b 100644 --- a/tests/manual/widgets/layoutbuilder/layoutbuilder.qbs +++ b/tests/manual/widgets/layoutbuilder/layoutbuilder.qbs @@ -1,12 +1,8 @@ - CppApplication { name: "Manual Test Utils LayoutBuilder" - Depends { name: "qtc" } Depends { name: "Core" } Depends { name: "Utils" } - files: [ - "tst_manual_widgets_layoutbuilder.cpp", - ] + files: "tst_manual_widgets_layoutbuilder.cpp" } diff --git a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp index 03a1cbc4bef..343c500c287 100644 --- a/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp +++ b/tests/manual/widgets/layoutbuilder/tst_manual_widgets_layoutbuilder.cpp @@ -38,5 +38,23 @@ int main(int argc, char *argv[]) }, }.emerge()->show(); + Group { + windowTitle("Group without parent layout"), + title("This group was emerged without parent layout"), + Column { + Splitter { + new QTextEdit("First Widget"), + new QTextEdit("Second Widget"), + }, + } + }.emerge()->show(); + + + Splitter { + windowTitle("Splitter with sub layouts"), + Column {"First Widget"}, + Row {"Second Widget"}, + }.emerge()->show(); + return app.exec(); } diff --git a/tests/manual/widgets/manhattanstyle/manhattanstyle.qbs b/tests/manual/widgets/manhattanstyle/manhattanstyle.qbs index c04bd03e89f..ffa13e98a06 100644 --- a/tests/manual/widgets/manhattanstyle/manhattanstyle.qbs +++ b/tests/manual/widgets/manhattanstyle/manhattanstyle.qbs @@ -3,13 +3,10 @@ import "../common/common.qbs" as Common CppApplication { name: "Manual Test Utils ManhattanStyle" - Depends { name: "qtc" } Depends { name: "Core" } Depends { name: "Utils" } - files: [ - "tst_manual_widgets_manhattanstyle.cpp", - ] + files: "tst_manual_widgets_manhattanstyle.cpp" Common {} } diff --git a/tests/manual/widgets/manhattanstyle/tst_manual_widgets_manhattanstyle.cpp b/tests/manual/widgets/manhattanstyle/tst_manual_widgets_manhattanstyle.cpp index 30ec55a4015..0189fe6a631 100644 --- a/tests/manual/widgets/manhattanstyle/tst_manual_widgets_manhattanstyle.cpp +++ b/tests/manual/widgets/manhattanstyle/tst_manual_widgets_manhattanstyle.cpp @@ -15,7 +15,6 @@ #include <QMainWindow> #include <QMessageBox> #include <QScrollBar> -#include <QSettings> #include <QSpinBox> #include <utils/utilsicons.h> diff --git a/tests/manual/widgets/tracing/tracing.qbs b/tests/manual/widgets/tracing/tracing.qbs index 95e661910ce..3dcde3acdcd 100644 --- a/tests/manual/widgets/tracing/tracing.qbs +++ b/tests/manual/widgets/tracing/tracing.qbs @@ -3,15 +3,12 @@ import "../common/common.qbs" as Common CppApplication { name: "Manual Test Tracing" - Depends { name: "qtc" } Depends { name: "Qt.quick" } Depends { name: "Tracing" } Depends { name: "Utils" } Depends { name: "Core" } - files: [ - "tst_manual_widgets_tracing.cpp", - ] + files: "tst_manual_widgets_tracing.cpp" Common {} } diff --git a/tests/manual/widgets/widgets.qbs b/tests/manual/widgets/widgets.qbs index 518ea823b1b..d6b6cd633b1 100644 --- a/tests/manual/widgets/widgets.qbs +++ b/tests/manual/widgets/widgets.qbs @@ -1,10 +1,6 @@ -import qbs - Project { name: "Widgets manualtests" - condition: project.withAutotests - references: [ "crumblepath/crumblepath.qbs", "infolabel/infolabel.qbs", diff --git a/tests/system/README b/tests/system/README index a23c5001fdd..9e5843da25b 100644 --- a/tests/system/README +++ b/tests/system/README @@ -2,7 +2,8 @@ Prerequisites - general information ----------------------------------- Squish tests inside this folder have several prerequisites to get them running. -First - and most important - you have to own a valid Squish license. At least Squish 6.0 is required. +First - and most important - you have to own a valid Squish license. At least Squish 6.0 is +required. It must be configured to run tests written in Python3. Second - some of the test suites/test cases expect Qt versions to be installed in their default locations. For Qt5, this is ~/Qt5.x.1 on Linux/macOS or C:\Qt\Qt5.x.1 on Windows. For Qt6, this is diff --git a/tests/system/objects.map b/tests/system/objects.map index cc359d6b7fc..66681c6b056 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -12,7 +12,7 @@ :*Qt Creator.findEdit_Utils::FilterLineEdit {name='findEdit' type='Utils::FancyLineEdit' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :*Qt Creator_Core::Internal::FancyToolButton {name='KitSelector.Button' type='Core::Internal::FancyToolButton' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :*Qt Creator_Utils::FilterLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:*Qt Creator_Utils::IconButton {occurrence='4' type='Utils::IconButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:*Qt Creator_Utils::IconButton {occurrence='5' type='Utils::FancyIconButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :About Qt Creator_Core::Internal::VersionDialog {type='Core::Internal::VersionDialog' unnamed='1' visible='1' windowTitle='About Qt Creator'} :Activate completion:_QComboBox {buddy=':Behavior.Activate completion:_QLabel' type='QComboBox' unnamed='1' visible='1'} :Add Bookmark.ExpandBookmarksList_QToolButton {text='+' type='QToolButton' unnamed='1' visible='1' window=':Add Bookmark_BookmarkDialog'} @@ -53,7 +53,7 @@ :DebugModeWidget_QComboBox {container=':Qt Creator_Core::Internal::MainWindow' occurrence='3' type='QComboBox' unnamed='1' visible='1'} :Debugger Toolbar.Continue_QToolButton {container=':DebugModeWidget.Toolbar_QDockWidget' toolTip?='Continue*' type='QToolButton' unnamed='1' visible='1'} :Debugger Toolbar.Exit Debugger_QToolButton {container=':DebugModeWidget.Toolbar_QDockWidget' text='Stop Debugger' type='QToolButton' unnamed='1' visible='1'} -:Debugger Toolbar.StatusText_Utils::StatusLabel {container=':DebugModeWidget.Toolbar_QDockWidget' type='Utils::StatusLabel' unnamed='1'} +:Debugger Toolbar.StatusText_Utils::StatusLabel {container=':DebugModeWidget.Toolbar_QDockWidget' name='DebuggerStatusLabel' type='QLabel'} :Debugger.Docks.BreakDockWidget.Debugger.Docks.Break_QFrame {container=':DebugModeWidget.Debugger.Docks.BreakDockWidget_QDockWidget' name='Debugger.Docks.BreakpointManager' type='QFrame' visible='1'} :Debugger.Docks.LocalsAndWatchersDockWidget.Inspector_QFrame {container=':DebugModeWidget.Debugger.Docks.LocalsAndInspectorDockWidget_QDockWidget' name~='Debugger\\\\.Dock\\\\.Inspector\\\\.\\\\d+' type='QFrame' visible='1'} :Debugger::Internal::ConsoleEdit {columnIndex='0' container=':DebugModeWidget_Debugger::Internal::ConsoleView' rowIndex='0' type='Debugger::Internal::ConsoleEdit' unnamed='1' visible='1'} @@ -126,7 +126,7 @@ :Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator.Issues_QListView {type='Utils::TreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'} +:Qt Creator.Issues_QListView {type='QTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'} :Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'} :Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'} :Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} @@ -147,7 +147,7 @@ :Qt Creator_CloseButton {type='CloseButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_CompileOutput_Core::Internal::OutputPaneToggleButton {occurrence='4' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Core::Internal::CommandComboBox {type='Core::Internal::CommandComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_Core::Internal::MainWindow {type='Core::Internal::MainWindow' visible='1' windowTitle?='*Qt Creator'} +:Qt Creator_Core::Internal::MainWindow {type='Utils::AppMainWindow' visible='1' windowTitle?='*Qt Creator'} :Qt Creator_Core::Internal::NavComboBox {type='Core::Internal::NavComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Core::Internal::ProgressBar {type='Core::Internal::ProgressBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Core::OutputWindow {type='Core::OutputWindow' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} @@ -160,13 +160,13 @@ :Qt Creator_Git::Internal::GitEditor {type='Git::Internal::GitEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_HelpSelector_QComboBox {occurrence='3' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Issues_Core::Internal::OutputPaneToggleButton {occurrence='1' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator_QHelpContentWidget {type='Utils::NavigationTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:Qt Creator_QHelpContentWidget {name='helpContentWidget' type='QTreeView' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_QmlJSEditor::Internal::QmlJSOutlineTreeView {type='QmlJSEditor::Internal::QmlJSOutlineTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_QmlJSEditor::QmlJSTextEditorWidget {type='QmlJSEditor::QmlJSEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_SearchResult_Core::Internal::OutputPaneToggleButton {occurrence='2' type='Core::Internal::OutputPaneToggleButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_TextEditor::TextEditorWidget {type='TextEditor::TextEditorWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::BuildDirectoryLineEdit {name='LineEdit' 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 {name='projectTreeView' type='QTreeView' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::NavigationTreeView::QExpandingLineEdit {container=':Qt Creator_Utils::NavigationTreeView' type='QExpandingLineEdit' 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'} @@ -206,7 +206,7 @@ :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'} -:projectComboBox_Utils::TreeViewComboBox {buddy=':New Text File.Add to project:_QLabel' name='projectComboBox' type='Utils::TreeViewComboBox' visible='1'} +:projectComboBox_Utils::TreeViewComboBox {buddy=':New Text File.Add to project:_QLabel' name='projectComboBox' type='QComboBox' visible='1'} :qdesigner_internal::WidgetBoxCategoryListView {container=':Widget Box_qdesigner_internal::WidgetBoxTreeWidget' occurrence='3' type='qdesigner_internal::WidgetBoxCategoryListView' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget_QScrollArea {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} diff --git a/tests/system/shared/debugger.py b/tests/system/shared/debugger.py index b878f9efef6..d409d5b0668 100644 --- a/tests/system/shared/debugger.py +++ b/tests/system/shared/debugger.py @@ -53,8 +53,7 @@ def setBreakpointsForCurrentProject(filesAndLines): if not filesAndLines or not isinstance(filesAndLines, (list,tuple)): test.fatal("This function only takes a non-empty list/tuple holding dicts.") return None - waitForObject("{type='Utils::NavigationTreeView' unnamed='1' visible='1' " - "window=':Qt Creator_Core::Internal::MainWindow'}") + waitForObject(":Qt Creator_Utils::NavigationTreeView") breakPointList = [] for current in filesAndLines: for curFile,curLine in current.items(): diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index b39ef8060ee..317067142a4 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -81,8 +81,8 @@ def __createProjectOrFileSelectType__(category, template, fromWelcome = False, i return __getSupportedPlatforms__(str(text), template)[0] def __createProjectSetNameAndPath__(path, projectName = None, checks = True): - directoryEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' " - "toolTip~='Full path: .*'}") + pathChooser = waitForObject("{type='Utils::PathChooser' name='baseFolder' visible='1'}") + directoryEdit = getChildByClass(pathChooser, "Utils::FancyLineEdit") replaceEditorContent(directoryEdit, path) projectNameEdit = waitForObject("{name='nameLineEdit' visible='1' " "type='Utils::FancyLineEdit'}") diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py index 8639c6e2651..77b493b32a0 100644 --- a/tests/system/shared/utils.py +++ b/tests/system/shared/utils.py @@ -190,8 +190,7 @@ def invokeMenuItem(menu, item, *subItems): "Function arguments: '%s', '%s', %s" % (menu, item, str(subItems))) break # we failed to trigger - no need to process subItems further else: - noAmpersandItem = item.replace('&', '') - waitForObject("{type='QMenu' title='%s'}" % noAmpersandItem, 2000) + waitForObject("{type='QMenu' title='%s'}" % str(itemObject.text), 2000) itemObject = waitForObjectItem(itemObject, subItem) waitFor("itemObject.enabled", 2000) activateItem(itemObject) diff --git a/tests/system/shared/welcome.py b/tests/system/shared/welcome.py index e945956b6e6..8066f271318 100644 --- a/tests/system/shared/welcome.py +++ b/tests/system/shared/welcome.py @@ -44,7 +44,15 @@ def switchToSubMode(subModeLabel): return frameAndLabelFound def findExampleOrTutorial(tableView, regex, verbose=False): - children = __childrenOfType__(tableView, 'QModelIndex') + filterModel = __childrenOfType__(tableView, 'QSortFilterProxyModel') + if len(filterModel) != 1: + test.fatal("Something's wrong - could not find filter proxy model.") + return None + filterModel = filterModel[0] + if filterModel.rowCount() == 0: + return None + + children = dumpIndices(filterModel) for child in children: if re.match(regex, str(child.text)): if verbose: diff --git a/tests/system/suite_CSUP/tst_CSUP01/test.py b/tests/system/suite_CSUP/tst_CSUP01/test.py index 5ca6d9500ab..0e443d4d89c 100644 --- a/tests/system/suite_CSUP/tst_CSUP01/test.py +++ b/tests/system/suite_CSUP/tst_CSUP01/test.py @@ -19,6 +19,15 @@ def triggerCompletion(editorWidget): else: type(editorWidget, "<Ctrl+Space>") + +def proposalItemFrom(baseText, useClang, isFunction): + if not useClang: + return baseText + if isFunction: + return ' ' + baseText + '(*' + return ' ' + baseText + + # entry of test def main(): for useClang in [False, True]: @@ -48,8 +57,9 @@ def main(): functionName = "realpath" if platform.system() in ('Windows', 'Microsoft'): functionName = "realloc" - waitForObjectItem(":popupFrame_Proposal_QListView", functionName) - doubleClickItem(":popupFrame_Proposal_QListView", functionName, 5, 5, 0, Qt.LeftButton) + proposalItem = proposalItemFrom(functionName, useClang, True) + waitForObjectItem(":popupFrame_Proposal_QListView", proposalItem) + doubleClickItem(":popupFrame_Proposal_QListView", proposalItem, 5, 5, 0, Qt.LeftButton) test.compare(str(lineUnderCursor(editorWidget)).strip(), functionName + "()", "Step 3: Verifying if: The list of suggestions is opened. It is " "possible to select one of the suggestions.") @@ -58,11 +68,11 @@ def main(): type(editorWidget, "unsig") try: proposalListView = waitForObject(":popupFrame_Proposal_QListView") - waitForObjectItem(proposalListView, "unsigned") + waitForObjectItem(proposalListView, proposalItemFrom("unsigned", useClang, False)) model = proposalListView.model() if test.verify(model.rowCount() >= 1, 'At least one proposal for "unsi"?'): - test.compare(dumpItems(model)[0], 'unsigned', + test.compare(dumpItems(model)[0].strip(), 'unsigned', '"unsigned" is the first proposal for "unsi"?') type(proposalListView, "<Tab>") test.compare(str(lineUnderCursor(editorWidget)).strip(), "unsigned", @@ -90,8 +100,8 @@ def main(): try: proposal = "return" if useClang: - # clang adds a whitespace because the function needs to return a value - proposal += " ;" + # clang adds more because the function needs to return a value + proposal = " return expression;" waitForObjectItem(":popupFrame_Proposal_QListView", proposal) except: test.fail("Could not find proposal popup.") diff --git a/tests/system/suite_CSUP/tst_CSUP06/test.py b/tests/system/suite_CSUP/tst_CSUP06/test.py index 7682d1a6723..b1b890b6b36 100644 --- a/tests/system/suite_CSUP/tst_CSUP06/test.py +++ b/tests/system/suite_CSUP/tst_CSUP06/test.py @@ -87,7 +87,7 @@ def checkSymbolCompletion(editor, isClangCodeModel): "internal.o":"internal.one", "freefunc2":"freefunc2(", "using namespace st":"using namespace std", "afun":"afunc()"} if isClangCodeModel: - missing = ["dummy.bla("] + missing = ["dummy.bla(", "freefunc2("] expectedSuggestion["internal.o"] = ["one"] if platform.system() in ('Microsoft', 'Windows'): expectedSuggestion["using namespace st"] = ["std", "stdext"] @@ -118,6 +118,16 @@ def checkSymbolCompletion(editor, isClangCodeModel): suggestionToClick = " " + suggestionToClick doubleClickItem(':popupFrame_Proposal_QListView', suggestionToClick, 5, 5, 0, Qt.LeftButton) + + try: + multiSuggestionToolTip = waitForObject( + "{leftWidget={text~='[0-9]+ of [0-9]+' type='QLabel'} type='QToolButton'}", 1000) + if multiSuggestionToolTip is not None: + test.log("Closing tooltip containing overloaded function completion.") + type(editor, "<Esc>") + except LookupError: # no proposal or tool tip for unambiguous stuff - direct completion + pass + changedLine = str(lineUnderCursor(editor)).strip() if symbol in expectedRes: exp = expectedRes[symbol] diff --git a/tests/system/suite_WELP/tst_WELP01/test.py b/tests/system/suite_WELP/tst_WELP01/test.py index b64edcd2b7f..0bc65d1f001 100644 --- a/tests/system/suite_WELP/tst_WELP01/test.py +++ b/tests/system/suite_WELP/tst_WELP01/test.py @@ -137,7 +137,7 @@ def main(): for (qType, prop, info) in expect: test.verify(checkIfObjectExists(search % (qType, prop)), "Verifying whether %s is shown" % info) - checkTableViewForContent(search % (expect[0][0], expect[0][1]), "Help: Creating .*", "Tutorials", + checkTableViewForContent(search % (expect[0][0], expect[0][1]), "Creating .*", "Tutorials", "Verifying that at least one tutorial is displayed.") # exit Qt Creator invokeMenuItem("File", "Exit") diff --git a/tests/system/suite_WELP/tst_WELP03/test.py b/tests/system/suite_WELP/tst_WELP03/test.py index 7c672464eb3..b09fa9e2a90 100644 --- a/tests/system/suite_WELP/tst_WELP03/test.py +++ b/tests/system/suite_WELP/tst_WELP03/test.py @@ -21,19 +21,21 @@ def handlePackagingMessageBoxes(): def openExample(examplesLineEdit, input, exampleRegex, exampleName, waitForChildCount=0): replaceEditorContent(examplesLineEdit, input) - listView = waitForObject("{type='QListView' unnamed='1' visible='1' " + listView = waitForObject("{type='QListView' name='AllItemsView' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}") + filterModel = __childrenOfType__(listView, 'QSortFilterProxyModel') + if len(filterModel) != 1: + test.fatal("Failed to find filter proxy model.") + return None + + filterModel = filterModel[0] if waitForChildCount > 0: - - def childCount(view): - return len(__childrenOfType__(view, 'QModelIndex')) - - waitFor("childCount(listView) == waitForChildCount", 3000) + waitFor("filterModel.rowCount() == waitForChildCount", 3000) waitFor('findExampleOrTutorial(listView, exampleRegex) is not None', 3000) example = findExampleOrTutorial(listView, exampleRegex, True) if test.verify(example is not None, "Verifying: Example (%s) is shown." % exampleName): - mouseClick(example) + mouseClick(waitForObjectItem(listView, str(example.text))) handlePackagingMessageBoxes() helpWidget = waitForObject(":Help Widget_Help::Internal::HelpWidget") test.verify(waitFor('exampleName in str(helpWidget.windowTitle)', 5000), @@ -74,7 +76,7 @@ def main(): combo = waitForObject(search % (expect[2][0], expect[2][1])) test.log("Using examples from Kit %s." % str(combo.currentText)) replaceEditorContent(examplesLineEdit, "qwerty") - listView = waitForObject(search % (expect[0][0], expect[0][1])) + listView = waitForObject("{type='QListView' name='AllItemsView'}") waitFor('findExampleOrTutorial(listView, ".*") is None', 3000) example = findExampleOrTutorial(listView, ".*", True) test.verify(example is None, "Verifying: No example is shown.") diff --git a/tests/system/suite_WELP/tst_WELP04/test.py b/tests/system/suite_WELP/tst_WELP04/test.py index 557059a3309..c9a18066693 100644 --- a/tests/system/suite_WELP/tst_WELP04/test.py +++ b/tests/system/suite_WELP/tst_WELP04/test.py @@ -6,7 +6,7 @@ source("../../shared/qtcreator.py") def __waitForListView__(): listView = waitForObject("{container=':Qt Creator.WelcomeScreenStackedWidget' " - "type='QListView' unnamed='1' visible='1'}") + "type='QListView' name='AllItemsView' visible='1'}") return listView @@ -34,7 +34,7 @@ def main(): tutorial = findExampleOrTutorial(listView, ".*", True) test.verify(tutorial is None, "Verifying: 'Tutorials' topic is opened and nothing is shown.") - bnr = "Help: Building and Running an Example" + bnr = "Building and Running an Example" replaceEditorContent(searchTutorials, bnr.lower()) listView = __waitForListView__() waitFor('findExampleOrTutorial(listView, "%s.*") is not None' % bnr, 3000) @@ -43,7 +43,7 @@ def main(): # clicking before documentation was updated will open the tutorial in browser progressBarWait(warn=False) # select a text tutorial - mouseClick(tutorial) + mouseClick(waitForObjectItem(listView, str(tutorial.text))) test.verify("Building and Running an Example" in str(waitForObject(":Help Widget_Help::Internal::HelpWidget").windowTitle), "Verifying: The tutorial is opened inside Help.") @@ -52,7 +52,7 @@ def main(): # check a demonstration video link mouseClick(searchTutorials) replaceEditorContent(searchTutorials, "embedded device") - embeddedTutorial = "Online: How to install and set up Qt for Device Creation.*" + embeddedTutorial = "How to install and set up Qt for Device Creation.*" listView = __waitForListView__() waitFor('findExampleOrTutorial(listView, embeddedTutorial) is not None', 3000) tutorial = findExampleOrTutorial(listView, embeddedTutorial, True) diff --git a/tests/system/suite_debugger/tst_debug_empty_main/test.py b/tests/system/suite_debugger/tst_debug_empty_main/test.py index 5e981b7b494..019b55e5f00 100644 --- a/tests/system/suite_debugger/tst_debug_empty_main/test.py +++ b/tests/system/suite_debugger/tst_debug_empty_main/test.py @@ -8,8 +8,8 @@ def addFileToProject(projectPath, category, fileTemplate, fileName): nameLineEdit = waitForObject("{name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' " "visible='1'}") replaceEditorContent(nameLineEdit, fileName) - pathLineEdit = waitForObject("{type='Utils::FancyLineEdit' unnamed='1' visible='1' " - "toolTip?='Full path: *'}") + pathChooser = waitForObject("{type='Utils::PathChooser' name='fullPathChooser'}") + pathLineEdit = getChildByClass(pathChooser, "Utils::FancyLineEdit") if not test.compare(pathLineEdit.text, projectPath, "Verifying whether path is correct."): replaceEditorContent(pathLineEdit, projectPath) diff --git a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt6.2.4.tsv b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt6.2.4.tsv index 7ab9ac87edb..c52d83b338c 100644 --- a/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt6.2.4.tsv +++ b/tests/system/suite_debugger/tst_simple_analyze/testdata/events_qt6.2.4.tsv @@ -1,10 +1,10 @@ "0" "1" "6" "11" "<program>" "" "1" "Main program" -"Main.qml:15" "Handling Signal" "2" "onTriggered: { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" -"Main.qml:15" "JavaScript" "2" "expression for onTriggered" -"Main.qml:4" "Creating" "2" "QtQuick/Window" +"Main.qml:14" "Handling Signal" "2" "onTriggered: { runCount += 1; var i; for (i = 1; i < 2500; ++i) { var j = i * i; console.log(j); } }" +"Main.qml:14" "JavaScript" "2" "expression for onTriggered" +"Main.qml:3" "Creating" "2" "QtQuick/Window" "Main.qml:0" "Compiling" "1" "Main.qml" -"Main.qml:10" "Creating" "2" "QtQml/Timer" -"Main.qml:14" "Binding" "3" "running: runCount < 2" -"Main.qml:14" "JavaScript" "3" "expression for running" +"Main.qml:9" "Creating" "2" "QtQml/Timer" +"Main.qml:13" "Binding" "3" "running: runCount < 2" +"Main.qml:13" "JavaScript" "3" "expression for running" "<bytecode>" "Binding" "1" "Source code not available" diff --git a/tests/system/suite_editors/tst_clean_whitespaces/test.py b/tests/system/suite_editors/tst_clean_whitespaces/test.py index 5c9e7aa6a65..7803027f869 100644 --- a/tests/system/suite_editors/tst_clean_whitespaces/test.py +++ b/tests/system/suite_editors/tst_clean_whitespaces/test.py @@ -89,7 +89,7 @@ def prepareFileExternal(fileName, content): test.log("Added trailing whitespace.") modifiedContent += currentLine - with open(fileName, "w") as f: + with open(fileName, "w", encoding="utf-8") as f: # used only with text files, so okay f.write(modifiedContent) if not emptyLine or not trailingWS: diff --git a/tests/system/suite_editors/tst_rename_macros/test.py b/tests/system/suite_editors/tst_rename_macros/test.py index 49737f3b492..574310940cb 100644 --- a/tests/system/suite_editors/tst_rename_macros/test.py +++ b/tests/system/suite_editors/tst_rename_macros/test.py @@ -98,8 +98,7 @@ def testRenameMacroAfterSourceMoving(): def performMacroRenaming(newMacroName): for _ in range(10): type(cppEditorStr, "<Left>") - invokeContextMenuItem(waitForObject(cppEditorStr), "Refactor", - "Rename Symbol Under Cursor") + invokeContextMenuItem(waitForObject(cppEditorStr), "Rename Symbol Under Cursor") waitForSearchResults() validateSearchResult(2) replaceLineEdit = waitForObject("{leftWidget={text='Replace with:' type='QLabel' " diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index d521f2092dd..4da9d39d732 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -33,8 +33,8 @@ def main(): # skip non-configurable if "Import" in category: continue - # FIXME - if "Qt for Python" in category: + # FIXME special or complicated + if "Qt for Python" in category or "Test Project" in category: continue mouseClick(waitForObjectItem(categoriesView, "Projects." + category)) templatesView = waitForObject("{name='templatesView' type='QListView' visible='1'}") @@ -45,7 +45,7 @@ def main(): if template == "Qt Quick 2 Extension Plugin": continue # skip non-configurable - if template not in ["Qt Quick UI Prototype", "Auto Test Project", "Qt Creator Plugin"]: + if template not in ["Qt Quick UI Prototype", "Qt Creator Plugin"]: availableProjectTypes.append({category:template}) safeClickButton("Cancel") for current in availableProjectTypes: diff --git a/tests/system/suite_general/tst_default_settings/test.py b/tests/system/suite_general/tst_default_settings/test.py index 57d7ae02cb1..95391864c24 100644 --- a/tests/system/suite_general/tst_default_settings/test.py +++ b/tests/system/suite_general/tst_default_settings/test.py @@ -39,6 +39,7 @@ def __checkKits__(): internalClangExe = os.path.join(llvmForBuild, "bin", "clang") if platform.system() in ("Microsoft", "Windows"): internalClangExe += ".exe" + internalClangExe = os.path.realpath(internalClangExe) # clean symlinks if os.path.exists(internalClangExe): expectedCompilers.append(internalClangExe) foundCompilers = [] @@ -268,7 +269,9 @@ def __getExpectedDebuggers__(): for debugger in ["gdb", "lldb"]: result.extend(findAllFilesInPATH(debugger + exeSuffix)) if platform.system() == 'Linux': - result.extend(filter(lambda s: not ("lldb-platform" in s or "lldb-gdbserver" in s), + explicitlyOmitted = ("lldb-platform", "lldb-gdbserver", "lldb-instr", "lldb-argdumper", + "lldb-server", "lldb-vscode") + result.extend(filter(lambda s: not (any(omitted in s for omitted in explicitlyOmitted)), findAllFilesInPATH("lldb-*"))) if platform.system() == 'Darwin': xcodeLLDB = getOutputFromCmdline(["xcrun", "--find", "lldb"]).strip("\n") diff --git a/tests/system/suite_general/tst_new_class/test.py b/tests/system/suite_general/tst_new_class/test.py index d63c6d4a582..d45b5918e08 100644 --- a/tests/system/suite_general/tst_new_class/test.py +++ b/tests/system/suite_general/tst_new_class/test.py @@ -35,10 +35,18 @@ def main(): editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget") editorText = str(editor.plainText) includeGuard = newClassName.upper().replace(".", "_") - test.verify("#ifndef " + includeGuard in editorText, - "Include guard check in header file?") - test.verify("#define " + includeGuard in editorText, - "Include guard definition in header file?") + + guardCount = 0 + if "#pragma once" in editorText: + guardCount += 1 + test.passes("Include guard in header file.") + if "#ifndef " + includeGuard in editorText: + guardCount += 1 + test.passes("Include guard check in header file.") + test.verify("#define " + includeGuard in editorText, + "Include guard definition in header file.") + test.compare(guardCount, 1, "Either pragma once or conventional include guard present.") + test.verify("class " + newClassName in editorText, "Class definition in header file?") test.verify(" " + newClassName + "();" in editorText, diff --git a/tests/tests.qbs b/tests/tests.qbs index fc634c93d08..7605c427366 100644 --- a/tests/tests.qbs +++ b/tests/tests.qbs @@ -1,4 +1,4 @@ -import qbs +import qbs.FileInfo Project { name: "Tests" @@ -8,4 +8,32 @@ Project { "tools/qml-ast2dot/qml-ast2dot.qbs", "unit/unit.qbs", ] + + AutotestRunner { + Depends { name: "Qt.core" } + Depends { name: "qtc" } + environment: { + var env = base; + if (!qbs.hostOS.contains("windows") || !qbs.targetOS.contains("windows")) + return env; + var path = ""; + for (var i = 0; i < env.length; ++i) { + if (env[i].startsWith("PATH=")) { + path = env[i].substring(5); + break; + } + } + var fullQtcInstallDir = FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix); + var fullLibInstallDir = FileInfo.joinPaths(fullQtcInstallDir, qtc.ide_library_path); + var fullPluginInstallDir = FileInfo.joinPaths(fullQtcInstallDir, qtc.ide_plugin_path); + path = Qt.core.binPath + ";" + fullLibInstallDir + ";" + fullPluginInstallDir + + ";" + path; + var arrayElem = "PATH=" + path; + if (i < env.length) + env[i] = arrayElem; + else + env.push(arrayElem); + return env; + } + } } diff --git a/tests/unit/.clang-format b/tests/unit/.clang-format index 968dfdec7d1..87648a20579 100644 --- a/tests/unit/.clang-format +++ b/tests/unit/.clang-format @@ -110,7 +110,6 @@ SpaceAfterCStyleCast: true SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true diff --git a/tests/unit/tests/CMakeLists.txt b/tests/unit/tests/CMakeLists.txt index fe1f0c84b32..bd7ec6ce3d6 100644 --- a/tests/unit/tests/CMakeLists.txt +++ b/tests/unit/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate) +find_package(Qt6 COMPONENTS QmlDomPrivate QmlCompilerPrivate QUIET) if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") diff --git a/tests/unit/tests/printers/gtest-creator-printing.cpp b/tests/unit/tests/printers/gtest-creator-printing.cpp index f8a9fc48a68..1bff6df492c 100644 --- a/tests/unit/tests/printers/gtest-creator-printing.cpp +++ b/tests/unit/tests/printers/gtest-creator-printing.cpp @@ -226,6 +226,7 @@ std::ostream &operator<<(std::ostream &out, const ValueView &value) } namespace { +#if 0 [[maybe_unused]] Utils::SmallStringView operationText(int operation) { @@ -241,7 +242,6 @@ Utils::SmallStringView operationText(int operation) return {}; } -#if 0 std::ostream &operator<<(std::ostream &out, [[maybe_unused]] sqlite3_changeset_iter *iter) { out << "("; @@ -323,6 +323,7 @@ const char *toText(LockingMode lockingMode) std::ostream &operator<<(std::ostream &out, [[maybe_unused]] const SessionChangeSet &changeset) { + Q_UNUSED(changeset) #if 0 sqlite3_changeset_iter *iter = nullptr; sqlite3changeset_start(&iter, changeset.size(), const_cast<void *>(changeset.data())); diff --git a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/Helpers/qmldir b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/Helpers/qmldir index 01802761653..74780bb7d60 100644 --- a/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/Helpers/qmldir +++ b/tests/unit/tests/unittests/projectstorage/data/qml/QtQuick3D/Physics/Helpers/qmldir @@ -3,6 +3,8 @@ linktarget Qt6::qtquick3dphysicshelpersplugin optional plugin qtquick3dphysicshelpersplugin classname QtQuick3DPhysicsHelpersPlugin typeinfo plugins.qmltypes -depends QtQuick3DPhysics auto +depends QtQuick3D.Physics auto +depends QtQuick3D auto +depends QtQuick auto prefer :/qt-project.org/imports/QtQuick3D/Physics/Helpers/ diff --git a/tests/unit/unit.qbs b/tests/unit/unit.qbs new file mode 100644 index 00000000000..b263b933f8f --- /dev/null +++ b/tests/unit/unit.qbs @@ -0,0 +1,4 @@ +Product { + name: "QmlDesigner unit tests" + files: ["*", "**/*"] +}